Browse Source

add customer loading

master
Stephanie Gredell 3 weeks ago
parent
commit
3454df077c
  1. 57
      canvasController.js
  2. 21
      game.js
  3. 18
      index.html
  4. 73
      index.js
  5. 2
      style.css

57
canvasController.js

@ -55,6 +55,51 @@ export const sprites = { @@ -55,6 +55,51 @@ export const sprites = {
}),
seesaw: createSprite({
source: 'seesaw.png'
}),
customer1: createSprite({
source: 'customer_1.png'
}),
customer2: createSprite({
source: 'customer_2.png'
}),
customer3: createSprite({
source: 'customer_3.png'
}),
customer4: createSprite({
source: 'customer_4.png'
}),
customer5: createSprite({
source: 'customer_5.png'
}),
customer6: createSprite({
source: 'customer_6.png'
}),
customer7: createSprite({
source: 'customer_7.png'
}),
customer8: createSprite({
source: 'customer_8.png'
}),
customer9: createSprite({
source: 'customer_9.png'
}),
customer10: createSprite({
source: 'customer_10.png'
}),
customer11: createSprite({
source: 'customer_11.png'
}),
customer12: createSprite({
source: 'customer_12.png'
}),
customer13: createSprite({
source: 'customer_13.png'
}),
customer14: createSprite({
source: 'customer_14.png'
}),
customer15: createSprite({
source: 'customer_15.png'
})
};
@ -136,7 +181,7 @@ export function render() { @@ -136,7 +181,7 @@ export function render() {
// Background
drawGround(sprites.grass);
// Background trees
drawSprite(sprites.tree, 600, 50, 0.25);
drawSprite(sprites.tree, 200, 50, 0.2);
@ -149,13 +194,17 @@ export function render() { @@ -149,13 +194,17 @@ export function render() {
drawShadow(500, 360, 180, 50);
// Lemonade stand
drawSprite(sprites.maker, 430, 160, 0.6);
drawSprite(sprites.stand, 350, 50, 0.7);
drawSprite(sprites.maker, 430, 160, 0.55);
drawSprite(sprites.stand, 350, 50, 0.6);
drawSprite(sprites.pitcher, 620, 290, 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);
// Invoke callback if set
if (onRenderCallback) onRenderCallback();
}
}

21
game.js

@ -443,3 +443,24 @@ export function calculate_cost_per_cup(game_state, recipe) { @@ -443,3 +443,24 @@ export function calculate_cost_per_cup(game_state, recipe) {
cost_per_cup: Math.round(cost * 100) / 100,
}
}
/**
* Calculate the maximum cups that can be produced from available supplies.
* @param {Supplies} supplies - Available supplies
* @param {Recipe} recipe - Recipe per cup
* @returns {number} Maximum cups producible (floored)
*/
export function calculate_maximum_cups_available(supplies, recipe) {
console.assert(supplies, 'supplies must be defined');
console.assert(recipe, 'recipe must be defined');
// Can't make lemonade without lemons
if (recipe.lemons === 0) return 0;
return Math.floor(Math.min(
recipe.lemons > 0 ? supplies.lemons / recipe.lemons : Infinity,
recipe.sugar > 0 ? supplies.sugar / recipe.sugar : Infinity,
recipe.ice > 0 ? supplies.ice / recipe.ice : Infinity,
supplies.cups
));
}

18
index.html

@ -16,6 +16,12 @@ @@ -16,6 +16,12 @@
</header>
<div class="dashboard">
<section class="game_section weather_section">
<h2 class="section_title">Weather</h2>
<img class="weather_icon" src="sunny.png" alt="Weather">
<p class="weather_label" data-bind="weather">sunny</p>
</section>
<section class="game_section">
<h2 class="section_title">Supplies</h2>
@ -51,18 +57,6 @@ @@ -51,18 +57,6 @@
<button class="change_price_button">Change Price</button>
</section>
<section class="game_section">
<h2 class="section_title">Total Earnings</h2>
<p data-bind="total_earnings" data-format="currency">$0.00</p>
</section>
<section class="game_section weather_section">
<h2 class="section_title">Weather</h2>
<img class="weather_icon" src="sunny.png" alt="Weather">
<p class="weather_label" data-bind="weather">sunny</p>
</section>
<section class="game_section day_section">
<h2 class="section_title">Day <span data-bind="current_day">1</span></h2>
<p class="section_hint">Cups sold: <span data-bind="cups_sold">0</span></p>

73
index.js

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
* Initializes game state, wires up UI events, and coordinates modules.
*/
import { init_game, set_price_per_cup, calculate_supply_cost, calculate_cost_per_cup, make_lemonade, set_weather } from './game.js';
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 { createReactiveState, updateBindings } from './binding.js';
@ -25,23 +25,55 @@ updateWeatherIcon(); @@ -25,23 +25,55 @@ updateWeatherIcon();
let isAnimating = false;
function animateCupFills(count, onComplete) {
let filled = 0;
sprites.maker.frameIndex = 1; // Maker active
render();
function generateSalesAttempts(maxCups, cupsSold) {
const attempts = Array(maxCups).fill(false);
for (let i = 0; i < cupsSold; i++) attempts[i] = true;
// Fisher-Yates shuffle
for (let i = attempts.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[attempts[i], attempts[j]] = [attempts[j], attempts[i]];
}
return attempts;
}
const interval = setInterval(() => {
if (filled < count && filled < cups.length) {
cups[filled].fill();
render();
filled++;
} else {
clearInterval(interval);
function animateSales(attempts, onComplete) {
let i = 0;
let cupIndex = 0;
function nextAttempt() {
if (i >= attempts.length) {
sprites.maker.frameIndex = 0; // Maker idle
render();
onComplete();
return;
}
}, 400);
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();
render();
cupIndex = (cupIndex + 1) % cups.length;
i++;
nextAttempt();
}, 400);
} else {
// Pass: skip instantly
i++;
nextAttempt();
}
}
nextAttempt();
}
function fillAllCups() {
cups.forEach(cup => cup.fill());
render();
}
function resetCups() {
@ -230,22 +262,25 @@ function startDay() { @@ -230,22 +262,25 @@ function startDay() {
}
const { recipe } = gameState;
if (recipe.lemons <= 0 && recipe.sugar <= 0 && recipe.ice <= 0) {
alert('Set a recipe with at least one ingredient!');
if (recipe.lemons <= 0) {
alert('Your recipe needs lemons!');
return;
}
if (isAnimating) return; // Prevent double-click
if (isAnimating) return;
isAnimating = true;
startDayBtn.disabled = true;
resetCups();
// Fill all cups at start
fillAllCups();
const maxCups = calculate_maximum_cups_available(gameState.supplies, gameState.recipe);
const result = make_lemonade(gameState);
const cupsSold = result.cups_sold;
// Animate cup fills
animateCupFills(cupsSold, () => {
const attempts = generateSalesAttempts(maxCups, cupsSold);
animateSales(attempts, () => {
setState(result);
// Randomize weather for next day

2
style.css

@ -4,6 +4,8 @@ body { @@ -4,6 +4,8 @@ body {
background: linear-gradient(180deg, #cfefff 0%, #f7ffe5 100%);
min-height: 100vh;
font-family: 'Inter', sans-serif;
padding: 0;
margin: 0;
}
.game_header {

Loading…
Cancel
Save