Compare commits
3 Commits
6c18870b6b
...
fbe48f925d
Author | SHA1 | Date | |
---|---|---|---|
fbe48f925d | |||
dd50d9b80d | |||
04a37732d9 |
BIN
public/img/icons/culture.png
Normal file
BIN
public/img/icons/culture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 873 B |
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
30
src/hud/Victory.svelte
Normal 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>
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
21
src/utils.ts
21
src/utils.ts
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user