Browse Source

wire up shopping

master
Stephanie Gredell 3 weeks ago
parent
commit
660b372dd5
  1. 50
      game.js
  2. 55
      index.html
  3. 47
      index.js
  4. 72
      style.css

50
game.js

@ -74,6 +74,56 @@ const IdealRecipe = { @@ -74,6 +74,56 @@ const IdealRecipe = {
}
}
/**
* Tiered pricing structure for supplies.
* Price per unit decreases with larger quantities.
* @type {Object.<string, Array.<{min: number, max: number, price: number}>>}
*/
const SupplyPricing = {
lemons: [
{ min: 1, max: 50, price: 0.02 },
{ min: 51, max: 100, price: 0.018 },
{ min: 101, max: Infinity, price: 0.015 }
],
sugar: [
{ min: 1, max: 50, price: 0.01 },
{ min: 51, max: 100, price: 0.009 },
{ min: 101, max: Infinity, price: 0.008 }
],
ice: [
{ min: 1, max: 100, price: 0.01 },
{ min: 101, max: 300, price: 0.009 },
{ min: 301, max: Infinity, price: 0.008 }
],
cups: [
{ min: 1, max: 100, price: 0.01 },
{ min: 101, max: Infinity, price: 0.009 }
]
};
/**
* Calculate the cost of purchasing a supply item based on tiered pricing.
* @param {string} item - The supply type (lemons, sugar, ice, cups)
* @param {number} quantity - The quantity to purchase
* @returns {number} The total cost
*/
export function calculate_supply_cost(item, quantity) {
if (quantity <= 0) return 0;
const tiers = SupplyPricing[item];
if (!tiers) return 0;
const tier = tiers.find(t => quantity >= t.min && quantity <= t.max);
return tier ? Math.round(quantity * tier.price * 100) / 100 : 0;
}
/**
* Get the pricing tiers for a supply item.
* @param {string} item - The supply type
* @returns {Array.<{min: number, max: number, price: number}>} The pricing tiers
*/
export function get_supply_pricing(item) {
return SupplyPricing[item] || [];
}
/**
* Probability weights for each weather type.
* Used to randomly determine the day's weather.

55
index.html

@ -60,24 +60,55 @@ @@ -60,24 +60,55 @@
</div>
<div class="shopping_modal_body">
<div class="shop_item">
<span class="shop_item_name">Lemons</span>
<span class="shop_item_price">$4.80 / 12</span>
<button class="shop_item_btn">Buy</button>
<div class="shop_item_info">
<span class="shop_item_name">Lemons</span>
<div class="shop_tiers">
<span class="shop_tier">1-50: $0.02</span>
<span class="shop_tier">51-100: $0.018</span>
<span class="shop_tier best">101+: $0.015</span>
</div>
</div>
<input type="number" class="shop_qty_input" data-item="lemons" min="1" value="10">
<span class="shop_item_price" data-price="lemons">$0.20</span>
<button class="shop_item_btn" data-item="lemons">Buy</button>
</div>
<div class="shop_item">
<span class="shop_item_name">Sugar</span>
<span class="shop_item_price">$4.80 / 12</span>
<button class="shop_item_btn">Buy</button>
<div class="shop_item_info">
<span class="shop_item_name">Sugar</span>
<div class="shop_tiers">
<span class="shop_tier">1-50: $0.01</span>
<span class="shop_tier">51-100: $0.009</span>
<span class="shop_tier best">101+: $0.008</span>
</div>
</div>
<input type="number" class="shop_qty_input" data-item="sugar" min="1" value="10">
<span class="shop_item_price" data-price="sugar">$0.10</span>
<button class="shop_item_btn" data-item="sugar">Buy</button>
</div>
<div class="shop_item">
<span class="shop_item_name">Ice</span>
<span class="shop_item_price">$1.00 / 50</span>
<button class="shop_item_btn">Buy</button>
<div class="shop_item_info">
<span class="shop_item_name">Ice</span>
<div class="shop_tiers">
<span class="shop_tier">1-100: $0.01</span>
<span class="shop_tier">101-300: $0.009</span>
<span class="shop_tier best">301+: $0.008</span>
</div>
</div>
<input type="number" class="shop_qty_input" data-item="ice" min="1" value="50">
<span class="shop_item_price" data-price="ice">$0.50</span>
<button class="shop_item_btn" data-item="ice">Buy</button>
</div>
<div class="shop_item">
<span class="shop_item_name">Cups</span>
<span class="shop_item_price">$1.00 / 75</span>
<button class="shop_item_btn">Buy</button>
<div class="shop_item_info">
<span class="shop_item_name">Cups</span>
<div class="shop_tiers">
<span class="shop_tier">1-100: $0.01</span>
<span class="shop_tier best">101+: $0.009</span>
</div>
</div>
<input type="number" class="shop_qty_input" data-item="cups" min="1" value="25">
<span class="shop_item_price" data-price="cups">$0.25</span>
<button class="shop_item_btn" data-item="cups">Buy</button>
</div>
</div>
</div>

47
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 } from './game.js';
import { init_game, set_price_per_cup, calculate_supply_cost } from './game.js';
import { sprites, cups, render, whenSpritesReady } from './canvasController.js';
import { createReactiveState, updateBindings } from './binding.js';
@ -35,10 +35,53 @@ const priceInput = document.querySelector('.price_input'); @@ -35,10 +35,53 @@ const priceInput = document.querySelector('.price_input');
const priceSaveBtn = document.querySelector('.price_change_save_btn');
// Shopping modal - quantity inputs and dynamic pricing
const shopQtyInputs = document.querySelectorAll('.shop_qty_input');
const shopBuyBtns = document.querySelectorAll('.shop_item_btn');
function updateShopPrice(item) {
const input = document.querySelector(`.shop_qty_input[data-item="${item}"]`);
const priceDisplay = document.querySelector(`.shop_item_price[data-price="${item}"]`);
const qty = parseInt(input.value) || 0;
const cost = calculate_supply_cost(item, qty);
priceDisplay.textContent = '$' + cost.toFixed(2);
}
// Update prices when quantity changes
shopQtyInputs.forEach(input => {
input.addEventListener('input', () => {
updateShopPrice(input.dataset.item);
});
});
// Buy button handlers
shopBuyBtns.forEach(btn => {
btn.addEventListener('click', () => {
const item = btn.dataset.item;
const input = document.querySelector(`.shop_qty_input[data-item="${item}"]`);
const qty = parseInt(input.value) || 0;
const cost = calculate_supply_cost(item, qty);
if (cost > gameState.player_money) {
alert("Not enough money!");
return;
}
setState({
player_money: gameState.player_money - cost,
supplies: {
...gameState.supplies,
[item]: gameState.supplies[item] + qty
}
});
});
});
// Event handlers
if (goShoppingBtn) {
goShoppingBtn.addEventListener('click', () => {
console.log('hey');
// Update all prices when modal opens
['lemons', 'sugar', 'ice', 'cups'].forEach(updateShopPrice);
shoppingModal.classList.add('open');
});

72
style.css

@ -115,7 +115,7 @@ canvas { @@ -115,7 +115,7 @@ canvas {
border: 4px solid #8fd16a;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
width: 400px;
width: 580px;
max-width: 90%;
}
@ -165,17 +165,81 @@ canvas { @@ -165,17 +165,81 @@ canvas {
border-bottom: none;
}
.shop_item_info {
flex: 1;
min-width: 0;
}
.shop_item_name {
font-family: 'Fredoka', sans-serif;
font-size: 18px;
color: #5C4632;
flex: 1;
display: block;
margin-bottom: 4px;
}
.shop_item_price {
.shop_tiers {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.shop_tier {
font-family: 'Inter', sans-serif;
font-size: 14px;
font-size: 11px;
color: #7A6146;
background: #f5f0e6;
padding: 2px 6px;
border-radius: 4px;
}
.shop_tier.best {
background: #d4f0c4;
color: #3f7a33;
font-weight: 500;
}
.shop_qty_input {
font-family: 'Fredoka', sans-serif;
font-size: 18px;
font-weight: 500;
color: #5C4632;
background: linear-gradient(180deg, #fff 0%, #f9f9f5 100%);
border: 2px solid #c5e8a8;
border-radius: 10px;
padding: 8px 12px;
width: 80px;
text-align: center;
margin-right: 12px;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
transition: all 0.15s ease;
-moz-appearance: textfield;
}
.shop_qty_input::-webkit-outer-spin-button,
.shop_qty_input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.shop_qty_input:hover {
border-color: #8fd16a;
background: linear-gradient(180deg, #fff 0%, #f5f5f0 100%);
}
.shop_qty_input:focus {
outline: none;
border-color: #3f7a33;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06), 0 0 0 3px rgba(143, 209, 106, 0.25);
}
.shop_item_price {
font-family: 'Fredoka', sans-serif;
font-size: 16px;
font-weight: 600;
color: #5C4632;
min-width: 60px;
text-align: right;
margin-right: 16px;
}

Loading…
Cancel
Save