diff --git a/src/App.svelte b/src/App.svelte index 72933a1..4802de8 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,6 +1,7 @@ - - - Wood: { Math.floor($village.resources.wood) } - Stone: { Math.floor($village.resources.stone) } - Iron: { Math.floor($village.resources.iron) } - Food: { Math.floor($village.resources.food) } - - + + + { #each $village.buildings as building } - { building.name } - Level: { building.level } + { building.name } ({ building.level }) upgradeBuilding(building.id) }> Upgrade @@ -52,7 +47,8 @@ diff --git a/src/app.css b/src/app.css index 0baf90b..b584976 100644 --- a/src/app.css +++ b/src/app.css @@ -24,9 +24,6 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; min-height: 100vh; } diff --git a/src/buildings.ts b/src/buildings.ts index 07b77dc..3e7645c 100644 --- a/src/buildings.ts +++ b/src/buildings.ts @@ -1,10 +1,11 @@ +import type { Building } from "./types"; +import { getEmptyResources } from "./utils"; import type { VillageState } from "./village"; export default { 'woodcutter': { name: 'Woodcutter', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -14,16 +15,16 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.wood += outputPerMilisecond * delta; + prod.wood = outputPerMinute; + return prod; } } }, 'mine': { name: 'Mine', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -33,16 +34,16 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.iron += outputPerMilisecond * delta; + prod.iron = outputPerMinute; + return prod; } } }, 'pit': { name: 'Pit', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -52,11 +53,58 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.stone += outputPerMilisecond * delta; + prod.stone = outputPerMinute; + return prod; } } }, + 'warehouse': { + name: 'Warehouse', + cost: (level: number) => { + return { + wood: level * 10, + stone: level * 10, + iron: level * 10, + food: 0, + }; + }, + behavior: { + storage: (V: VillageState, self: Building) => { + const x = self.level; + const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25; + return { + 'wood': capacity, + 'stone': capacity, + 'iron': capacity, + 'food': 0, + } + } + }, + }, + 'granary': { + name: 'Granary', + cost: (level: number) => { + return { + wood: level * 10, + stone: level * 10, + iron: level * 10, + food: 0, + }; + }, + behavior: { + storage: (V: VillageState, self: Building) => { + const x = self.level; + const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25; + return { + 'wood': 0, + 'stone': 0, + 'iron': 0, + 'food': capacity, + } + } + }, + }, }; diff --git a/src/hud/Resources.svelte b/src/hud/Resources.svelte new file mode 100644 index 0000000..3c2997e --- /dev/null +++ b/src/hud/Resources.svelte @@ -0,0 +1,21 @@ + + + + Wood: { Math.floor($village.resources.wood) } / { capacity.wood } + Stone: { Math.floor($village.resources.stone) } / { capacity.stone } + Iron: { Math.floor($village.resources.iron) } / { capacity.iron } + Food: { Math.floor($village.resources.food) } / { capacity.food } + + + diff --git a/src/moves/upgradeBuilding.ts b/src/moves/upgradeBuilding.ts index 3843b66..27294cc 100644 --- a/src/moves/upgradeBuilding.ts +++ b/src/moves/upgradeBuilding.ts @@ -7,7 +7,7 @@ export default function upgradeBuilding(V: VillageState, buildingId: number) { return false; } - const cost = building.cost(building.level); + const cost = building.cost(building.level + 1); if ( cost.wood > V.resources.wood diff --git a/src/types.ts b/src/types.ts index 1096336..fb9e2fd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,12 +6,16 @@ export interface Cost { } +export type Production = Cost; + + export interface BuildingSource { name: string; level: number; cost: (level: number) => Cost; behavior: { production?: Function; + storage?: Function; }; } diff --git a/src/update.ts b/src/update.ts index 14e8f45..437e8a1 100644 --- a/src/update.ts +++ b/src/update.ts @@ -1,6 +1,8 @@ import { produce } from 'immer'; +import { getProduction, getStorage } from './utils'; import village, { type VillageState } from "./village"; +import type { Production } from './types'; let lastFrame: number; @@ -16,11 +18,20 @@ export default function update(timestamp: number) { village.update(state => { return produce(state, (V: VillageState) => { - V.buildings.forEach(b => { - if (b.behavior.production) { - b.behavior.production(V, b, delta); + const productionPerMinute = getProduction(V); + const storage = getStorage(V); + + Object.keys(productionPerMinute).forEach((key) => { + const resource = key as keyof Production; + const outputPerMinute = productionPerMinute[resource]; + const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; + V.resources[resource] += outputPerMilisecond * delta; + + if (V.resources[resource] > storage[resource]) { + V.resources[resource] = storage[resource]; } }); + return V; }); }); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..48d7f73 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,46 @@ +import type { Production } from "./types"; +import type { VillageState } from "./village"; + + +function _reduceResources(acc: Production, item: Production): Production { + return { + wood: acc.wood + item.wood, + stone: acc.stone + item.stone, + iron: acc.iron + item.iron, + food: acc.food + item.food, + }; +} + + +export function getEmptyResources(): Production { + return { + wood: 0, + stone: 0, + iron: 0, + food: 0, + }; +} + + +export function getProduction(villageState: VillageState): Production { + return villageState.buildings + .filter(b => b.behavior.production) + .map(b => { + if (b.behavior.production) { + return b.behavior.production(villageState, b); + } + }) + .reduce(_reduceResources, getEmptyResources()); +} + + +export function getStorage(villageState: VillageState): Production { + return villageState.buildings + .filter(b => b.behavior.storage) + .map(b => { + if (b.behavior.storage) { + return b.behavior.storage(villageState, b); + } + }) + .reduce(_reduceResources, getEmptyResources()); +} diff --git a/src/village.ts b/src/village.ts index f8726b8..727404d 100644 --- a/src/village.ts +++ b/src/village.ts @@ -11,25 +11,27 @@ export interface VillageState { iron: number; food: number; culture: number; - } + }; } let uid = 0; const village = writable({ buildings: [ - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.warehouse, level: 1, id: uid++ }, + { ...buildings.granary, level: 1, id: uid++ }, ], resources: { wood: 100,
{ building.name }
Level: { building.level }
{ building.name } ({ building.level })
upgradeBuilding(building.id) }> Upgrade @@ -52,7 +47,8 @@ diff --git a/src/app.css b/src/app.css index 0baf90b..b584976 100644 --- a/src/app.css +++ b/src/app.css @@ -24,9 +24,6 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; min-height: 100vh; } diff --git a/src/buildings.ts b/src/buildings.ts index 07b77dc..3e7645c 100644 --- a/src/buildings.ts +++ b/src/buildings.ts @@ -1,10 +1,11 @@ +import type { Building } from "./types"; +import { getEmptyResources } from "./utils"; import type { VillageState } from "./village"; export default { 'woodcutter': { name: 'Woodcutter', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -14,16 +15,16 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.wood += outputPerMilisecond * delta; + prod.wood = outputPerMinute; + return prod; } } }, 'mine': { name: 'Mine', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -33,16 +34,16 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.iron += outputPerMilisecond * delta; + prod.iron = outputPerMinute; + return prod; } } }, 'pit': { name: 'Pit', - level: 1, cost: (level: number) => { return { wood: level * 10, @@ -52,11 +53,58 @@ export default { }; }, behavior: { - production: (V: VillageState, self: any, delta: number) => { + production: (V: VillageState, self: Building) => { + const prod = getEmptyResources(); const outputPerMinute = 5 * (self.level * self.level); - const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; - V.resources.stone += outputPerMilisecond * delta; + prod.stone = outputPerMinute; + return prod; } } }, + 'warehouse': { + name: 'Warehouse', + cost: (level: number) => { + return { + wood: level * 10, + stone: level * 10, + iron: level * 10, + food: 0, + }; + }, + behavior: { + storage: (V: VillageState, self: Building) => { + const x = self.level; + const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25; + return { + 'wood': capacity, + 'stone': capacity, + 'iron': capacity, + 'food': 0, + } + } + }, + }, + 'granary': { + name: 'Granary', + cost: (level: number) => { + return { + wood: level * 10, + stone: level * 10, + iron: level * 10, + food: 0, + }; + }, + behavior: { + storage: (V: VillageState, self: Building) => { + const x = self.level; + const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25; + return { + 'wood': 0, + 'stone': 0, + 'iron': 0, + 'food': capacity, + } + } + }, + }, }; diff --git a/src/hud/Resources.svelte b/src/hud/Resources.svelte new file mode 100644 index 0000000..3c2997e --- /dev/null +++ b/src/hud/Resources.svelte @@ -0,0 +1,21 @@ + + + + Wood: { Math.floor($village.resources.wood) } / { capacity.wood } + Stone: { Math.floor($village.resources.stone) } / { capacity.stone } + Iron: { Math.floor($village.resources.iron) } / { capacity.iron } + Food: { Math.floor($village.resources.food) } / { capacity.food } + + + diff --git a/src/moves/upgradeBuilding.ts b/src/moves/upgradeBuilding.ts index 3843b66..27294cc 100644 --- a/src/moves/upgradeBuilding.ts +++ b/src/moves/upgradeBuilding.ts @@ -7,7 +7,7 @@ export default function upgradeBuilding(V: VillageState, buildingId: number) { return false; } - const cost = building.cost(building.level); + const cost = building.cost(building.level + 1); if ( cost.wood > V.resources.wood diff --git a/src/types.ts b/src/types.ts index 1096336..fb9e2fd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,12 +6,16 @@ export interface Cost { } +export type Production = Cost; + + export interface BuildingSource { name: string; level: number; cost: (level: number) => Cost; behavior: { production?: Function; + storage?: Function; }; } diff --git a/src/update.ts b/src/update.ts index 14e8f45..437e8a1 100644 --- a/src/update.ts +++ b/src/update.ts @@ -1,6 +1,8 @@ import { produce } from 'immer'; +import { getProduction, getStorage } from './utils'; import village, { type VillageState } from "./village"; +import type { Production } from './types'; let lastFrame: number; @@ -16,11 +18,20 @@ export default function update(timestamp: number) { village.update(state => { return produce(state, (V: VillageState) => { - V.buildings.forEach(b => { - if (b.behavior.production) { - b.behavior.production(V, b, delta); + const productionPerMinute = getProduction(V); + const storage = getStorage(V); + + Object.keys(productionPerMinute).forEach((key) => { + const resource = key as keyof Production; + const outputPerMinute = productionPerMinute[resource]; + const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0; + V.resources[resource] += outputPerMilisecond * delta; + + if (V.resources[resource] > storage[resource]) { + V.resources[resource] = storage[resource]; } }); + return V; }); }); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..48d7f73 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,46 @@ +import type { Production } from "./types"; +import type { VillageState } from "./village"; + + +function _reduceResources(acc: Production, item: Production): Production { + return { + wood: acc.wood + item.wood, + stone: acc.stone + item.stone, + iron: acc.iron + item.iron, + food: acc.food + item.food, + }; +} + + +export function getEmptyResources(): Production { + return { + wood: 0, + stone: 0, + iron: 0, + food: 0, + }; +} + + +export function getProduction(villageState: VillageState): Production { + return villageState.buildings + .filter(b => b.behavior.production) + .map(b => { + if (b.behavior.production) { + return b.behavior.production(villageState, b); + } + }) + .reduce(_reduceResources, getEmptyResources()); +} + + +export function getStorage(villageState: VillageState): Production { + return villageState.buildings + .filter(b => b.behavior.storage) + .map(b => { + if (b.behavior.storage) { + return b.behavior.storage(villageState, b); + } + }) + .reduce(_reduceResources, getEmptyResources()); +} diff --git a/src/village.ts b/src/village.ts index f8726b8..727404d 100644 --- a/src/village.ts +++ b/src/village.ts @@ -11,25 +11,27 @@ export interface VillageState { iron: number; food: number; culture: number; - } + }; } let uid = 0; const village = writable({ buildings: [ - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.woodcutter, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.mine, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, - { ...buildings.pit, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.woodcutter, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.mine, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.pit, level: 1, id: uid++ }, + { ...buildings.warehouse, level: 1, id: uid++ }, + { ...buildings.granary, level: 1, id: uid++ }, ], resources: { wood: 100,