Browse Source

add customer animations

master
Stephanie Gredell 3 weeks ago
parent
commit
a49761cb4c
  1. 16
      canvas.js
  2. 57
      canvasController.js
  3. 2
      index.html
  4. 80
      index.js
  5. 2
      style.css

16
canvas.js

@ -118,6 +118,22 @@ export function drawSprite(spriteObject, x, y, scale) { @@ -118,6 +118,22 @@ export function drawSprite(spriteObject, x, y, scale) {
);
}
/**
* Draws a sprite with alpha transparency.
*
* @param {Sprite} sprite - The sprite to draw
* @param {number} x - X position
* @param {number} y - Y position
* @param {number} scale - Scale factor
* @param {number} alpha - Alpha transparency (0-1)
*/
export function drawSpriteWithAlpha(sprite, x, y, scale, alpha) {
ctx.save();
ctx.globalAlpha = alpha;
drawSprite(sprite, x, y, scale);
ctx.restore();
}
/**
* Draws a cup (or any object with sprite, x, y, scale, frameIndex).
*

57
canvasController.js

@ -8,6 +8,7 @@ import { @@ -8,6 +8,7 @@ import {
ctx,
createSprite,
drawSprite,
drawSpriteWithAlpha,
drawInstance,
drawGround,
drawShadow,
@ -103,6 +104,44 @@ export const sprites = { @@ -103,6 +104,44 @@ export const sprites = {
})
};
// Customer positions (alternating left/right)
const customerPositions = [
{ x: 300, y: 180 }, // left
{ x: 700, y: 180 } // right
];
// Active customer state
export let activeCustomer = {
spriteKey: null,
positionIndex: 0,
alpha: 0
};
// All customer sprite keys
const customerKeys = [
'customer1', 'customer2', 'customer3', 'customer4', 'customer5',
'customer6', 'customer7', 'customer8', 'customer9', 'customer10',
'customer11', 'customer12', 'customer13', 'customer14', 'customer15'
];
/**
* Sets the active customer for rendering.
* @param {string|null} spriteKey - Customer sprite key or null
* @param {number} positionIndex - 0 for left, 1 for right
* @param {number} alpha - Transparency (0-1)
*/
export function setActiveCustomer(spriteKey, positionIndex, alpha) {
activeCustomer = { spriteKey, positionIndex, alpha };
}
/**
* Gets a random customer sprite key.
* @returns {string} Random customer key
*/
export function getRandomCustomerKey() {
return customerKeys[Math.floor(Math.random() * customerKeys.length)];
}
/**
* Checks if all sprites are loaded.
* @returns {boolean}
@ -167,10 +206,10 @@ export function createCup(x, y, scale = 0.1) { @@ -167,10 +206,10 @@ export function createCup(x, y, scale = 0.1) {
* @type {Array<import('./canvas.js').Cup>}
*/
export const cups = [
createCup(455, 325, 0.15),
createCup(495, 325, 0.15),
createCup(535, 325, 0.15),
createCup(575, 325, 0.15),
createCup(455, 285, 0.15),
createCup(495, 285, 0.15),
createCup(535, 285, 0.15),
createCup(575, 285, 0.15),
];
/**
@ -196,14 +235,16 @@ export function render() { @@ -196,14 +235,16 @@ export function render() {
// Lemonade stand
drawSprite(sprites.maker, 430, 160, 0.55);
drawSprite(sprites.stand, 350, 50, 0.6);
drawSprite(sprites.pitcher, 620, 290, 0.3);
drawSprite(sprites.pitcher, 620, 260, 0.3);
// Draw all cup instances
cups.forEach(cup => drawInstance(cup));
// Customers
drawSprite(sprites.customer2, 300, 180, 0.45);
drawSprite(sprites.customer5, 700, 180, 0.45);
// Active customer (with fade)
if (activeCustomer.spriteKey && activeCustomer.alpha > 0) {
const pos = customerPositions[activeCustomer.positionIndex];
drawSpriteWithAlpha(sprites[activeCustomer.spriteKey], pos.x, pos.y, 0.45, activeCustomer.alpha);
}
// Invoke callback if set
if (onRenderCallback) onRenderCallback();

2
index.html

@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
</section>
<section class="game_section day_section">
<h2 class="section_title">Day <span data-bind="current_day">1</span></h2>
<h2 class="section_title">Game</h2>
<p class="section_hint">Cups sold: <span data-bind="cups_sold">0</span></p>
<p class="section_hint">Earnings: <span data-bind="total_earnings" data-format="currency">$0.00</span></p>
<button class="start_day_btn">Start Day</button>

80
index.js

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
*/
import { init_game, set_price_per_cup, calculate_supply_cost, calculate_cost_per_cup, make_lemonade, set_weather, calculate_maximum_cups_available } from './game.js';
import { sprites, cups, render, whenSpritesReady } from './canvasController.js';
import { sprites, cups, render, whenSpritesReady, setActiveCustomer, getRandomCustomerKey } from './canvasController.js';
import { createReactiveState, updateBindings } from './binding.js';
// Initialize game state
@ -36,36 +36,78 @@ function generateSalesAttempts(maxCups, cupsSold) { @@ -36,36 +36,78 @@ function generateSalesAttempts(maxCups, cupsSold) {
return attempts;
}
/**
* Fades a customer in or out using requestAnimationFrame.
* @param {string} spriteKey - Customer sprite key
* @param {number} posIndex - Position index (0=left, 1=right)
* @param {number} fromAlpha - Starting alpha
* @param {number} toAlpha - Ending alpha
* @param {number} duration - Duration in ms
* @param {Function} onComplete - Callback when complete
*/
function fadeCustomer(spriteKey, posIndex, fromAlpha, toAlpha, duration, onComplete) {
const startTime = performance.now();
function step() {
const elapsed = performance.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const alpha = fromAlpha + (toAlpha - fromAlpha) * progress;
setActiveCustomer(spriteKey, posIndex, alpha);
render();
if (progress < 1) {
requestAnimationFrame(step);
} else {
onComplete();
}
}
step();
}
function animateSales(attempts, onComplete) {
let i = 0;
let cupIndex = 0;
let posIndex = 0;
function nextAttempt() {
if (i >= attempts.length) {
sprites.maker.frameIndex = 0; // Maker idle
setActiveCustomer(null, 0, 0);
sprites.maker.frameIndex = 0;
render();
onComplete();
return;
}
if (attempts[i]) {
// Buy: maker active, empty cup, wait, refill, maker idle
sprites.maker.frameIndex = 1;
cups[cupIndex].empty();
render();
setTimeout(() => {
sprites.maker.frameIndex = 0;
cups[cupIndex].fill();
// Pick random customer, alternate position
const customerKey = getRandomCustomerKey();
posIndex = (posIndex + 1) % 2;
// Fade in customer
fadeCustomer(customerKey, posIndex, 0, 1, 500, () => {
if (attempts[i]) {
// Buy: maker active, empty cup, wait, refill, maker idle, fade out
sprites.maker.frameIndex = 1;
cups[cupIndex].empty();
render();
cupIndex = (cupIndex + 1) % cups.length;
i++;
nextAttempt();
}, 400);
} else {
// Pass: skip instantly
i++;
nextAttempt();
}
setTimeout(() => {
sprites.maker.frameIndex = 0;
cups[cupIndex].fill();
render();
cupIndex = (cupIndex + 1) % cups.length;
// Fade out customer
fadeCustomer(customerKey, posIndex, 1, 0, 500, () => {
i++;
nextAttempt();
});
}, 600);
} else {
// Pass: brief pause then fade out
setTimeout(() => {
fadeCustomer(customerKey, posIndex, 1, 0, 500, () => {
i++;
nextAttempt();
});
}, 500);
}
});
}
nextAttempt();

2
style.css

@ -113,7 +113,7 @@ body { @@ -113,7 +113,7 @@ body {
}
.game_section .section_hint {
font-size: 11px;
font-size: 13px;
margin: 2px 0 8px;
}

Loading…
Cancel
Save