diff --git a/src/board/BuildingTile.svelte b/src/board/BuildingTile.svelte
index 1465665..f8e8da8 100644
--- a/src/board/BuildingTile.svelte
+++ b/src/board/BuildingTile.svelte
@@ -1,7 +1,7 @@
{ building.name }
diff --git a/src/create.ts b/src/create.ts
index 20713dd..7ba2388 100644
--- a/src/create.ts
+++ b/src/create.ts
@@ -1,6 +1,6 @@
-import buildings from "./buildings";
+import buildings from "./data/buildings";
import { Hex } from "./hexgrid";
-import type { Building, BuildingSource } from "./types";
+import type { BuildingType, BuildingSource } from "./types";
let uid = 0;
@@ -17,7 +17,7 @@ export function getBuildingSource(buildingType: string): BuildingSource {
}
-export function createBuilding(buildingType: string): Building {
+export function createBuilding(buildingType: string): BuildingType {
const source: BuildingSource = getBuildingSource(buildingType);
return {
@@ -25,5 +25,6 @@ export function createBuilding(buildingType: string): Building {
id: uid++,
level: 1,
tile: new Hex(0, 0),
+ state: {},
};
}
diff --git a/src/buildings.ts b/src/data/buildings.ts
similarity index 75%
rename from src/buildings.ts
rename to src/data/buildings.ts
index 4ca610b..27fbe75 100644
--- a/src/buildings.ts
+++ b/src/data/buildings.ts
@@ -1,6 +1,6 @@
-import type { Building } from "./types";
-import { getEmptyResources } from "./utils";
-import type { VillageState } from "./village";
+import type { BuildingType } from "../types";
+import { getEmptyResources } from "../utils";
+import type { VillageState } from "../village";
export default [
@@ -17,7 +17,7 @@ export default [
};
},
behavior: {
- storage: (_V: VillageState, _self: Building) => {
+ storage: (_V: VillageState, _self: BuildingType) => {
return {
'wood': 100,
'stone': 100,
@@ -40,7 +40,7 @@ export default [
};
},
behavior: {
- production: (V: VillageState, self: Building) => {
+ production: (V: VillageState, self: BuildingType) => {
const prod = getEmptyResources();
const outputPerMinute = 5 * (self.level * self.level);
prod.wood = outputPerMinute;
@@ -65,7 +65,7 @@ export default [
};
},
behavior: {
- production: (V: VillageState, self: Building) => {
+ production: (V: VillageState, self: BuildingType) => {
const prod = getEmptyResources();
const outputPerMinute = 5 * (self.level * self.level);
prod.iron = outputPerMinute;
@@ -90,7 +90,7 @@ export default [
};
},
behavior: {
- production: (V: VillageState, self: Building) => {
+ production: (V: VillageState, self: BuildingType) => {
const prod = getEmptyResources();
const outputPerMinute = 5 * (self.level * self.level);
prod.stone = outputPerMinute;
@@ -115,7 +115,7 @@ export default [
};
},
behavior: {
- production: (V: VillageState, self: Building) => {
+ production: (V: VillageState, self: BuildingType) => {
const prod = getEmptyResources();
const outputPerMinute = 5 * (self.level * self.level);
prod.food = outputPerMinute;
@@ -135,7 +135,7 @@ export default [
};
},
behavior: {
- storage: (V: VillageState, self: Building) => {
+ storage: (V: VillageState, self: BuildingType) => {
const x = self.level;
const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25;
return {
@@ -159,7 +159,7 @@ export default [
};
},
behavior: {
- storage: (V: VillageState, self: Building) => {
+ storage: (V: VillageState, self: BuildingType) => {
const x = self.level;
const capacity = ( ( ( x + ( x * x ) ) / 2 ) + 3 ) * 25;
return {
@@ -171,4 +171,28 @@ export default [
},
},
},
+ {
+ type: 'university',
+ name: 'University',
+ cost: (level: number) => {
+ return {
+ wood: level * 100,
+ stone: level * 100,
+ iron: level * 100,
+ food: 0,
+ };
+ },
+ behavior: {
+ production: (V: VillageState, self: BuildingType) => {
+ const prod = getEmptyResources();
+ const intakePerMinute = self.level * 2;
+ prod.food = -intakePerMinute;
+ return prod;
+ },
+ units: {
+ type: 'philosopher',
+ recruitmentTime: (V: VillageState, self: BuildingType) => 2 - 0.06 * self.level,
+ },
+ },
+ }
];
diff --git a/src/data/units.ts b/src/data/units.ts
new file mode 100644
index 0000000..49e996c
--- /dev/null
+++ b/src/data/units.ts
@@ -0,0 +1,16 @@
+export default [
+ {
+ type: 'philosopher',
+ name: 'Philosopher',
+ cost: {
+ wood: 20,
+ stone: 50,
+ iron: 0,
+ food: 0,
+ },
+ behavior: {
+ culturePerMinute: 1,
+ foodIntakePerMinute: 2,
+ },
+ },
+];
diff --git a/src/hud/BuildingPanel.svelte b/src/hud/BuildingPanel.svelte
index 6e9721b..57da6da 100644
--- a/src/hud/BuildingPanel.svelte
+++ b/src/hud/BuildingPanel.svelte
@@ -1,9 +1,9 @@
+
+
+
+
+ { cost.wood }
+
+
+
+ { cost.stone }
+
+
+
+ { cost.iron }
+
+
+
+ { cost.food }
+
+
+
+
diff --git a/src/hud/Game.svelte b/src/hud/Game.svelte
index adf574f..7370fcb 100644
--- a/src/hud/Game.svelte
+++ b/src/hud/Game.svelte
@@ -9,8 +9,9 @@
import BuildingCreator from "./BuildingCreator.svelte";
import BuildingPanel from "./BuildingPanel.svelte";
import Navigation from "./Navigation.svelte";
- import Resources from "./Resources.svelte";
import Queue from "./Queue.svelte";
+ import Resources from "./Resources.svelte";
+ import Units from "./Units.svelte";
onMount(() => {
@@ -34,6 +35,7 @@
diff --git a/src/hud/Units.svelte b/src/hud/Units.svelte
new file mode 100644
index 0000000..f21bec6
--- /dev/null
+++ b/src/hud/Units.svelte
@@ -0,0 +1,18 @@
+
+
+
+ { #each currentUnits as unit }
+ { unit.name }: { unit.count }
+ { /each }
+
diff --git a/src/hud/UniversityPanel.svelte b/src/hud/UniversityPanel.svelte
new file mode 100644
index 0000000..1d00585
--- /dev/null
+++ b/src/hud/UniversityPanel.svelte
@@ -0,0 +1,72 @@
+
+
+
+
Create Philosophers
+
+
+
+
+
+
+
+
diff --git a/src/moves/build.ts b/src/moves/build.ts
index ce77338..8818359 100644
--- a/src/moves/build.ts
+++ b/src/moves/build.ts
@@ -1,4 +1,4 @@
-import buildings from "../buildings";
+import buildings from "../data/buildings";
import { createBuilding, getBuildingSource } from "../create";
import type { Hex } from "../hexgrid";
import { enqueueBuilding } from "../utils";
diff --git a/src/moves/index.ts b/src/moves/index.ts
index 7521b68..c7350e1 100644
--- a/src/moves/index.ts
+++ b/src/moves/index.ts
@@ -3,6 +3,7 @@ import { produce } from 'immer';
import village, { type VillageState } from '../village';
import build from './build';
import upgradeBuilding from './upgradeBuilding';
+import recruitUnits from './recruitUnits';
// Encapsulates a move function into a store update, where the data is made
@@ -30,4 +31,5 @@ export function makeMove(move: (...args: any[]) => boolean) {
export default {
build: makeMove(build),
upgradeBuilding: makeMove(upgradeBuilding),
+ recruitUnits: makeMove(recruitUnits),
};
diff --git a/src/moves/recruitUnits.ts b/src/moves/recruitUnits.ts
new file mode 100644
index 0000000..b3ba0a7
--- /dev/null
+++ b/src/moves/recruitUnits.ts
@@ -0,0 +1,46 @@
+import units from "../data/units";
+import { getBuilding } from "../utils";
+import type { VillageState } from "../village";
+
+
+export default function recruitUnits(
+ V: VillageState, buildingId: number, unitType: string, unitNumber: number
+) {
+ const unit = units.find(u => u.type === unitType);
+ if (!unit) {
+ return false;
+ }
+
+ const cost = {
+ wood: unit.cost.wood * unitNumber,
+ stone: unit.cost.stone * unitNumber,
+ iron: unit.cost.iron * unitNumber,
+ food: unit.cost.food * unitNumber,
+ };
+
+ if (
+ cost.wood > V.resources.wood
+ || cost.stone > V.resources.stone
+ || cost.iron > V.resources.iron
+ || cost.food > V.resources.food
+ ) {
+ return false;
+ }
+
+ V.resources.wood -= cost.wood;
+ V.resources.stone -= cost.stone;
+ V.resources.iron -= cost.iron;
+ V.resources.food -= cost.food;
+
+ const building = getBuilding(V, buildingId);
+ if (!building.state.recruitment) {
+ building.state.recruitment = {
+ count: 0,
+ elapsedTime: 0,
+ };
+ }
+
+ building.state.recruitment.count += unitNumber;
+
+ return true;
+}
diff --git a/src/types.ts b/src/types.ts
index d8f61ac..259a6be 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -4,7 +4,7 @@ import type { Hex } from "./hexgrid";
export type GameTab = 'village' | 'resources';
-export interface Cost {
+export interface CostType {
wood: number;
stone: number;
iron: number;
@@ -12,23 +12,44 @@ export interface Cost {
}
-export type Production = Cost;
+export type ProductionType = CostType;
export interface BuildingSource {
name: string;
type: string;
autoBuilt?: boolean;
- cost: (level: number) => Cost;
+ cost: (level: number) => CostType;
behavior: {
production?: Function;
storage?: Function;
+ units?: {
+ type: string;
+ recruitmentTime: Function;
+ };
};
}
-export interface Building extends BuildingSource {
+export interface BuildingType extends BuildingSource {
id: number;
level: number;
tile: Hex;
+ state: {
+ recruitment?: {
+ count: number;
+ elapsedTime: number;
+ };
+ };
+}
+
+
+export interface UnitType {
+ type: string;
+ name: string;
+ cost: CostType;
+ behavior: {
+ foodIntakePerMinute: number;
+ culturePerMinute?: number;
+ }
}
diff --git a/src/update.ts b/src/update.ts
index 75fc22a..6b3a3cc 100644
--- a/src/update.ts
+++ b/src/update.ts
@@ -2,7 +2,7 @@ import { produce } from 'immer';
import { getBuilding, getProduction, getStorage } from './utils';
import village, { type VillageState } from "./village";
-import type { Production } from './types';
+import type { ProductionType } from './types';
let lastFrame: number;
@@ -33,7 +33,7 @@ export default function update(timestamp: number) {
const storage = getStorage(V);
Object.keys(productionPerMinute).forEach((key) => {
- const resource = key as keyof Production;
+ const resource = key as keyof ProductionType;
const outputPerMinute = productionPerMinute[resource];
const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0;
V.resources[resource] += outputPerMilisecond * delta;
@@ -46,6 +46,27 @@ export default function update(timestamp: number) {
}
});
+ // Recruit units.
+ V.buildings.forEach(b => {
+ if (!b.state.recruitment || !b.state.recruitment.count) {
+ return;
+ }
+
+ const recruitment = b.state.recruitment;
+ recruitment.elapsedTime += delta;
+
+ const timeToRecruit = b.behavior.units?.recruitmentTime(V, b) * 1000;
+ if (recruitment.elapsedTime >= timeToRecruit) {
+ const unitType = b.behavior.units?.type || '';
+ if (!V.units.hasOwnProperty(unitType)) {
+ V.units[unitType] = 0;
+ }
+ V.units[unitType]++;
+ recruitment.count--;
+ recruitment.elapsedTime = (recruitment.count === 0) ? 0 : timeToRecruit - recruitment.elapsedTime;
+ }
+ });
+
return V;
});
});
diff --git a/src/utils.ts b/src/utils.ts
index 7ef51bb..b9cbe76 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,8 +1,9 @@
-import type { Building, Production } from "./types";
+import units from "./data/units";
+import type { BuildingType, ProductionType } from "./types";
import type { VillageState } from "./village";
-function _reduceResources(acc: Production, item: Production): Production {
+function _reduceResources(acc: ProductionType, item: ProductionType): ProductionType {
return {
wood: acc.wood + item.wood,
stone: acc.stone + item.stone,
@@ -12,7 +13,7 @@ function _reduceResources(acc: Production, item: Production): Production {
}
-export function getEmptyResources(): Production {
+export function getEmptyResources(): ProductionType {
return {
wood: 0,
stone: 0,
@@ -22,7 +23,7 @@ export function getEmptyResources(): Production {
}
-export function getProduction(villageState: VillageState): Production {
+export function getProduction(villageState: VillageState): ProductionType {
return villageState.buildings
.filter(b => b.behavior.production && b.level > 0)
.map(b => {
@@ -34,7 +35,7 @@ export function getProduction(villageState: VillageState): Production {
}
-export function getStorage(villageState: VillageState): Production {
+export function getStorage(villageState: VillageState): ProductionType {
return villageState.buildings
.filter(b => b.behavior.storage && b.level > 0)
.map(b => {
@@ -46,7 +47,7 @@ export function getStorage(villageState: VillageState): Production {
}
-export function getBuilding(V: VillageState, buildingId: number): Building {
+export function getBuilding(V: VillageState, buildingId: number): BuildingType {
const building = V.buildings.find(b => b.id === buildingId);
if (!building) {
throw new Error(`Cannot find building with id "${buildingId}"`);
@@ -55,6 +56,15 @@ export function getBuilding(V: VillageState, buildingId: number): Building {
}
+export function getUnitSource(unitType: string) {
+ const unit = units.find(u => u.type === unitType);
+ if (unit === undefined) {
+ throw new Error(`Unknown unit type: "${unitType}"`);
+ }
+ return unit;
+}
+
+
export function getKeysAsNumbers(dict: Object): Array {
return Object.keys(dict).map(i => parseInt(i)).sort((a, b) => a - b);
}
@@ -89,7 +99,7 @@ export function shuffle(array: Array): Array {
}
-export function enqueueBuilding(V: VillageState, building: Building) {
+export function enqueueBuilding(V: VillageState, building: BuildingType) {
const ongoingUpgrades = V.queue.filter(q => q.id === building.id);
const level = building.level + 1 + ongoingUpgrades.length;
const remainingTime = 1000 * level;
diff --git a/src/village.ts b/src/village.ts
index 069b080..95b7c63 100644
--- a/src/village.ts
+++ b/src/village.ts
@@ -1,9 +1,8 @@
import { writable } from "svelte/store";
-import buildings from "./buildings";
import { createBuilding } from "./create";
-import type { Building } from "./types";
import { getTilesAtDistance, Hex } from "./hexgrid";
+import type { BuildingType } from "./types";
import { getKeysAsNumbers, shuffle } from "./utils";
@@ -21,7 +20,10 @@ interface QueuedBuilding {
export interface VillageState {
- buildings: Building[];
+ buildings: BuildingType[];
+ units: {
+ [key: string]: number;
+ },
resources: {
wood: number;
stone: number;
@@ -76,6 +78,7 @@ function getInitialOutsideBoard() {
function getInitialState() {
const state: VillageState = {
buildings: [],
+ units: {},
resources: {
wood: 60,
stone: 60,
@@ -112,6 +115,7 @@ function getInitialState() {
}
const newBuilding = createBuilding(type);
newBuilding.tile = new Hex(x, y);
+ newBuilding.level = 10;
state.outsideTiles[y][x] = newBuilding.id;
state.buildings.push(newBuilding);
});