Compare commits

...

3 Commits

10 changed files with 145 additions and 25 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

View File

@ -1,8 +1,57 @@
<script lang="ts"> <script lang="ts">
import moves from "../moves";
import type { BuildingType } from "../types"; import type { BuildingType } from "../types";
import { canPayBuildingCost } from "../utils";
import village from "../village";
export let building: BuildingType; export let building: BuildingType;
$: isUpgrading = $village.queue.find(q => q.id === building.id);
$: canUpgrade = canPayBuildingCost($village, building);
function upgradeBuilding() {
moves.upgradeBuilding(building.id);
return false;
}
</script> </script>
<p>{ building.name }</p> <div>
<p>{ building.level }</p> <p>{ building.name }</p>
<p>
<button
class="level"
class:can-upgrade={ canUpgrade }
class:is-upgrading={ isUpgrading }
on:click|stopPropagation={ upgradeBuilding }
>
{ building.level }
</button>
</p>
</div>
<style>
.level {
aspect-ratio: 1;
background-color: hsl(208, 100%, 97%);
border: 0.4em solid hsl(0, 0%, 45%);
border-radius: 100%;
box-sizing: border-box;
box-shadow: 0 0 0.2em black;
color: hsl(0, 0%, 10%);
font-size: 1.2em;
}
.level.can-upgrade {
border-color: hsl(90, 99%, 36%);
}
.level.is-upgrading {
border-color: hsl(56, 99%, 43%);
}
.level:hover.can-upgrade {
border-color: hsl(90, 99%, 36%);
}
</style>

View File

@ -12,6 +12,7 @@
import Queue from "./Queue.svelte"; import Queue from "./Queue.svelte";
import Resources from "./Resources.svelte"; import Resources from "./Resources.svelte";
import Units from "./Units.svelte"; import Units from "./Units.svelte";
import Victory from "./Victory.svelte";
onMount(() => { onMount(() => {
@ -53,6 +54,7 @@
<section class="overlay"> <section class="overlay">
<BuildingCreator /> <BuildingCreator />
<BuildingPanel /> <BuildingPanel />
<Victory />
</section> </section>
<style> <style>

View File

@ -28,6 +28,10 @@
{ Math.floor($village.resources.food) } / { capacity.food } { Math.floor($village.resources.food) } / { capacity.food }
({ production.food >= 0 ? '+' : '' }{ production.food }) ({ production.food >= 0 ? '+' : '' }{ production.food })
</div> </div>
<div>
<img src="/img/icons/culture.png" alt="Culture" />
{ Math.floor($village.resources.culture) }
</div>
</div> </div>
<style> <style>

30
src/hud/Victory.svelte Normal file
View File

@ -0,0 +1,30 @@
<script lang="ts">
import village from "../village";
function restart() {
village.reset();
}
</script>
{ #if $village.victory }
<section>
<h1>Victory!</h1>
<div>
<button on:click={ restart }>Play again</button>
</div>
</section>
{ /if }
<style>
section {
background-color: hsl(0, 0%, 10%, 0.9);
display: grid;
place-items: center;
height: 100vh;
left: 0;
position: absolute;
top: 0;
width: 100vw;
}
</style>

View File

@ -1,4 +1,4 @@
import { enqueueBuilding } from "../utils"; import { canPayBuildingCost, enqueueBuilding, getBuildingUpgradeCost } from "../utils";
import type { VillageState } from "../village"; import type { VillageState } from "../village";
@ -8,20 +8,12 @@ export default function upgradeBuilding(V: VillageState, buildingId: number) {
return false; return false;
} }
const ongoingUpgrades = V.queue.filter(q => q.id === building.id); if (!canPayBuildingCost(V, building)) {
const level = building.level + 1 + ongoingUpgrades.length;
const cost = building.cost(level);
if (
cost.wood > V.resources.wood
|| cost.stone > V.resources.stone
|| cost.iron > V.resources.iron
|| cost.food > V.resources.food
) {
return false; return false;
} }
const cost = getBuildingUpgradeCost(V, building);
V.resources.wood -= cost.wood; V.resources.wood -= cost.wood;
V.resources.stone -= cost.stone; V.resources.stone -= cost.stone;
V.resources.iron -= cost.iron; V.resources.iron -= cost.iron;

View File

@ -15,6 +15,11 @@ export interface CostType {
export type ProductionType = CostType; export type ProductionType = CostType;
export interface ResourcesType extends CostType {
culture: number;
}
export interface BuildingSource { export interface BuildingSource {
name: string; name: string;
type: string; type: string;

View File

@ -1,6 +1,6 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { getBuilding, getProduction, getStorage } from './utils'; import { getBuilding, getProduction, getStorage, getUnitSource } from './utils';
import village, { type VillageState } from "./village"; import village, { type VillageState } from "./village";
import type { ProductionType } from './types'; import type { ProductionType } from './types';
@ -17,6 +17,10 @@ export default function update(timestamp: number) {
const delta = timestamp - lastFrame; const delta = timestamp - lastFrame;
village.update(state => { village.update(state => {
if (state.victory) {
return state;
}
return produce(state, (V: VillageState) => { return produce(state, (V: VillageState) => {
// Advance building construction. // Advance building construction.
if (V.queue.length) { if (V.queue.length) {
@ -67,6 +71,17 @@ export default function update(timestamp: number) {
} }
}); });
// Make philosophers produce culture.
const philosopher = getUnitSource('philosopher');
const outputPerMinute = philosopher.behavior.culturePerMinute;
const outputPerMilisecond = outputPerMinute / 60.0 / 1000.0;
V.resources.culture += outputPerMilisecond * delta * (V.units.philosopher || 0);
// Check if the game is won.
if (V.resources.culture >= 2) {
V.victory = true;
}
return V; return V;
}); });
}); });

View File

@ -1,5 +1,5 @@
import units from "./data/units"; import units from "./data/units";
import type { BuildingType, ProductionType } from "./types"; import type { BuildingType, CostType, ProductionType } from "./types";
import type { VillageState } from "./village"; import type { VillageState } from "./village";
@ -109,3 +109,22 @@ export function enqueueBuilding(V: VillageState, building: BuildingType) {
remainingTime, remainingTime,
}); });
} }
export function getBuildingUpgradeCost(V: VillageState, building: BuildingType): CostType {
const ongoingUpgrades = V.queue.filter(q => q.id === building.id);
const level = building.level + ongoingUpgrades.length + 1;
return building.cost(level);
}
export function canPayBuildingCost(V: VillageState, building: BuildingType): boolean {
const cost = getBuildingUpgradeCost(V, building);
return !(
cost.wood > V.resources.wood
|| cost.stone > V.resources.stone
|| cost.iron > V.resources.iron
|| cost.food > V.resources.food
);
}

View File

@ -2,7 +2,7 @@ import { writable } from "svelte/store";
import { createBuilding } from "./create"; import { createBuilding } from "./create";
import { getTilesAtDistance, Hex } from "./hexgrid"; import { getTilesAtDistance, Hex } from "./hexgrid";
import type { BuildingType } from "./types"; import type { BuildingType, ResourcesType } from "./types";
import { getKeysAsNumbers, shuffle } from "./utils"; import { getKeysAsNumbers, shuffle } from "./utils";
@ -24,16 +24,11 @@ export interface VillageState {
units: { units: {
[key: string]: number; [key: string]: number;
}, },
resources: { resources: ResourcesType;
wood: number;
stone: number;
iron: number;
food: number;
culture: number;
};
villageTiles: Board; villageTiles: Board;
outsideTiles: Board; outsideTiles: Board;
queue: QueuedBuilding[]; queue: QueuedBuilding[];
victory: boolean;
} }
@ -89,6 +84,7 @@ function getInitialState() {
villageTiles: getInitialVillageBoard(), villageTiles: getInitialVillageBoard(),
outsideTiles: getInitialOutsideBoard(), outsideTiles: getInitialOutsideBoard(),
queue: [], queue: [],
victory: false,
}; };
// Create the Town hall. // Create the Town hall.
@ -128,4 +124,12 @@ function getInitialState() {
const village = writable<VillageState>(getInitialState()); const village = writable<VillageState>(getInitialState());
export default village; function reset() {
village.set(getInitialState());
}
export default {
...village,
reset,
};