Add an oasis pillaging move.

This commit is contained in:
Adrian 2024-11-07 14:20:34 +01:00
parent 149a6b47f9
commit 2dffb2dcac
9 changed files with 138 additions and 9 deletions

View File

@ -1,7 +1,27 @@
<script lang="ts"> <script lang="ts">
import moves from "../moves";
import type { OasisType } from "../types"; import type { OasisType } from "../types";
import { getRemainingUnitCount } from "../utils";
import village from "../village";
export let region: OasisType; export let region: OasisType;
let numberOfUnits = 0;
$: maximumUnits = getRemainingUnitCount($village, 'soldier');
$: if (numberOfUnits > maximumUnits) {
numberOfUnits = maximumUnits || 0;
}
function setMaxUnits() {
numberOfUnits = maximumUnits;
}
function pillage() {
moves.pillage(region.state.index, numberOfUnits);
}
</script> </script>
<div> <div>
@ -10,10 +30,31 @@
Oasis Oasis
(→ { region.distance }) (→ { region.distance })
</h2> </h2>
{ #if region.state.mission }
<div> <div>
<button>Pillage</button> <p>{ region.state.mission.unitCount } soldiers are on a mission here.</p>
<button>Conquer</button> <p>Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }</p>
</div> </div>
{ :else }
<div>
<input
type="range"
name="units"
min="0"
max={ maximumUnits }
bind:value={ numberOfUnits }
/>
<input
type="number"
name="units"
min="0"
max={ maximumUnits }
bind:value={ numberOfUnits }
/>
<button on:click={ setMaxUnits }>↑</button>
<button on:click={ pillage } disabled={ numberOfUnits === 0 }>Pillage</button>
</div>
{ /if }
</div> </div>
<style> <style>

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import units from "../data/units"; import units from "../data/units";
import { getRemainingUnitCount } from "../utils";
import village from "../village"; import village from "../village";
$: currentUnits = Object.entries($village.units).map(([type, count]) => { $: currentUnits = Object.entries($village.units).map(([type, count]) => {
@ -17,6 +18,6 @@
<section> <section>
{ #each currentUnits as unit } { #each currentUnits as unit }
<p>{ unit.name }: { unit.count }</p> <p>{ unit.name }: { getRemainingUnitCount($village, unit.type) } / { unit.count }</p>
{ /each } { /each }
</section> </section>

31
src/missions.ts Normal file
View File

@ -0,0 +1,31 @@
import type { OasisType, RegionType } from "./types";
import type { VillageState } from "./village";
export function resolveMission(V: VillageState, region: RegionType) {
const mission = region.state.mission;
if (!mission) {
return;
}
switch (mission.type) {
case 'pillage':
if (region.type === 'oasis') {
resolvePillageOasis(V, region);
}
break;
default:
throw new Error(`Unknown mission type: "${ mission.type }"`);
}
}
function resolvePillageOasis(V: VillageState, region: OasisType) {
const mission = region.state.mission;
if (!mission) {
return;
}
V.resources[region.resource] += mission.unitCount * 40;
delete region.state.mission;
}

View File

@ -4,6 +4,7 @@ import village, { type VillageState } from '../village';
import build from './build'; import build from './build';
import upgradeBuilding from './upgradeBuilding'; import upgradeBuilding from './upgradeBuilding';
import recruitUnits from './recruitUnits'; import recruitUnits from './recruitUnits';
import pillage from './pillage';
// Encapsulates a move function into a store update, where the data is made // Encapsulates a move function into a store update, where the data is made
@ -31,5 +32,6 @@ export function makeMove(move: (...args: any[]) => boolean) {
export default { export default {
build: makeMove(build), build: makeMove(build),
upgradeBuilding: makeMove(upgradeBuilding), upgradeBuilding: makeMove(upgradeBuilding),
pillage: makeMove(pillage),
recruitUnits: makeMove(recruitUnits), recruitUnits: makeMove(recruitUnits),
}; };

24
src/moves/pillage.ts Normal file
View File

@ -0,0 +1,24 @@
import { getRemainingUnitCount } from "../utils";
import type { VillageState } from "../village";
export default function pillage(V: VillageState, regionIndex: number, soldiersCount: number) {
if (soldiersCount > getRemainingUnitCount(V, 'soldier')) {
return false;
}
const region = V.worldmap[regionIndex];
if (region.state.mission) {
return false;
}
region.state.mission = {
type: 'pillage',
unitType: 'soldier',
unitCount: soldiersCount,
remainingTime: region.distance * 10 * 1000,
};
return true;
}

View File

@ -64,12 +64,18 @@ export interface UnitType {
} }
export interface MissionType {
type: string;
unitType: string;
unitCount: number;
remainingTime: number;
}
interface BaseRegionType { interface BaseRegionType {
distance: number; distance: number;
state: { state: {
mission?: { index: number;
type: string; mission?: MissionType;
};
}; };
} }

View File

@ -1,6 +1,7 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { CULTURE_TO_WIN } from './constants'; import { CULTURE_TO_WIN } from './constants';
import { resolveMission } from './missions';
import type { ProductionType } from './types'; import type { ProductionType } from './types';
import { getProduction, getStorage, shuffle } from './utils'; import { getProduction, getStorage, shuffle } from './utils';
import village, { type VillageState } from "./village"; import village, { type VillageState } from "./village";
@ -32,6 +33,19 @@ export default function update(timestamp: number) {
} }
}); });
// Advance missions.
V.worldmap.forEach(region => {
if (!region.state.mission) {
return;
}
const mission = region.state.mission;
mission.remainingTime -= delta;
if (mission.remainingTime <= 0) {
resolveMission(V, region);
}
});
// Make all buildings and units produce and consume. // Make all buildings and units produce and consume.
const productionPerMinute = getProduction(V); const productionPerMinute = getProduction(V);
const storage = getStorage(V); const storage = getStorage(V);

View File

@ -1,5 +1,5 @@
import units from "./data/units"; import units from "./data/units";
import type { BuildingType, CostType, ProductionType, ResourcesType } from "./types"; import type { BuildingType, CostType, MissionType, ProductionType, ResourcesType } from "./types";
import type { VillageState } from "./village"; import type { VillageState } from "./village";
@ -88,6 +88,14 @@ export function getUnitSource(unitType: string) {
} }
export function getRemainingUnitCount(V: VillageState, unitType: string) {
let total = V.units[unitType] || 0;
const missions = V.worldmap.filter(r => r.state.mission?.unitType === unitType).map(r => r.state.mission);
missions.forEach(m => total -= m?.unitCount || 0);
return total;
}
export function getKeysAsNumbers(dict: Object): Array<number> { export function getKeysAsNumbers(dict: Object): Array<number> {
return Object.keys(dict).map(i => parseInt(i)).sort((a, b) => a - b); return Object.keys(dict).map(i => parseInt(i)).sort((a, b) => a - b);
} }

View File

@ -66,11 +66,13 @@ function getInitialOutsideBoard() {
function getInitialWorldmap(): RegionType[] { function getInitialWorldmap(): RegionType[] {
return worldmap.map(r => { return worldmap.map((r, index) => {
const region = r as RegionType; const region = r as RegionType;
return { return {
...region, ...region,
state: {}, state: {
index,
},
}; };
}); });
} }