diff --git a/src/board/OasisRegion.svelte b/src/board/OasisRegion.svelte deleted file mode 100644 index a3a40ef..0000000 --- a/src/board/OasisRegion.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - -
-

- { region.resource } - Oasis - (→ { region.distance }) -

- { #if region.state.mission } -
-

{ region.state.mission.unitCount } soldiers are on a mission here.

-

Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }

- -
- { :else } -
- - { numberOfUnits } - - - -
- { /if } -
- - diff --git a/src/board/OasisRegionPanel.svelte b/src/board/OasisRegionPanel.svelte new file mode 100644 index 0000000..d4bd4cb --- /dev/null +++ b/src/board/OasisRegionPanel.svelte @@ -0,0 +1,112 @@ + + +{ #if region !== null } +
+
+
+

+ { region.resource } + Oasis +

+ + + +
+ { #if region.state.mission } +
+

{ region.state.mission.unitCount } soldiers are on a mission here.

+

Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }

+ +
+ { :else } +
+ + { numberOfUnits } + + + +
+ { /if } +
+
+{ /if } + + diff --git a/src/board/Worldmap.svelte b/src/board/Worldmap.svelte index 60de20a..1eddf67 100644 --- a/src/board/Worldmap.svelte +++ b/src/board/Worldmap.svelte @@ -1,14 +1,32 @@ -
+
{ #each $village.worldmap as region } -
- { #if region.type === 'oasis' } - +
+ { #if region.type === WORLDMAP_TYPES.BOURGADE } + + { :else if region.type === WORLDMAP_TYPES.OASIS } + + { :else } + { region.id } { /if }
{ /each } @@ -16,15 +34,35 @@ diff --git a/src/constants.ts b/src/constants.ts index 782c886..fc87389 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1 +1,3 @@ export const CULTURE_TO_WIN = 20000; +export const WORLD_MAP_HEIGHT = 9; +export const WORLD_MAP_WIDTH = 9; diff --git a/src/hud/BuildingPanel.svelte b/src/hud/BuildingPanel.svelte index e7db7b1..c59a496 100644 --- a/src/hud/BuildingPanel.svelte +++ b/src/hud/BuildingPanel.svelte @@ -6,11 +6,11 @@ import BuildingRecruitment from "./BuildingRecruitment.svelte"; import Cost from "./Cost.svelte"; + function close() { showBuildingPanel.set(null); } - function upgrade() { if ($showBuildingPanel === null) { return; diff --git a/src/hud/Game.svelte b/src/hud/Game.svelte index 1e84f35..49c2687 100644 --- a/src/hud/Game.svelte +++ b/src/hud/Game.svelte @@ -14,6 +14,7 @@ import Units from "./Units.svelte"; import Victory from "./Victory.svelte"; import Quests from "./Quests.svelte"; + import OasisRegionPanel from "../board/OasisRegionPanel.svelte"; onMount(() => { @@ -57,6 +58,7 @@
+
diff --git a/src/missions.ts b/src/missions.ts index e688355..090a2d2 100644 --- a/src/missions.ts +++ b/src/missions.ts @@ -1,9 +1,11 @@ -import type { OasisType, RegionType } from "./types"; -import { getUnitSource } from "./utils"; +import { WORLDMAP_TYPES, type OasisType, type RegionType } from "./types"; +import { assert, getUnitSource } from "./utils"; import type { VillageState } from "./village"; export function resolveMission(V: VillageState, region: RegionType) { + assert(region.type === WORLDMAP_TYPES.OASIS); + const mission = region.state.mission; if (!mission) { return; @@ -11,7 +13,7 @@ export function resolveMission(V: VillageState, region: RegionType) { switch (mission.type) { case 'pillage': - if (region.type === 'oasis') { + if (region.type === WORLDMAP_TYPES.OASIS) { resolvePillageOasis(V, region); } break; @@ -20,7 +22,7 @@ export function resolveMission(V: VillageState, region: RegionType) { } if (mission.repeat) { - mission.remainingTime = region.distance * 10 * 1000; + mission.remainingTime = 1 * 10 * 1000; } else { delete region.state.mission; diff --git a/src/moves/pillage.ts b/src/moves/pillage.ts index a649f0f..47731b1 100644 --- a/src/moves/pillage.ts +++ b/src/moves/pillage.ts @@ -1,4 +1,5 @@ -import { getRemainingUnitCount } from "../utils"; +import { WORLDMAP_TYPES } from "../types"; +import { assert, getRemainingUnitCount } from "../utils"; import type { VillageState } from "../village"; @@ -9,6 +10,8 @@ export default function pillage(V: VillageState, regionIndex: number, soldiersCo const region = V.worldmap[regionIndex]; + assert(region.type === WORLDMAP_TYPES.OASIS); + if (region.state.mission) { return false; } @@ -17,7 +20,7 @@ export default function pillage(V: VillageState, regionIndex: number, soldiersCo type: 'pillage', unitType: 'soldier', unitCount: soldiersCount, - remainingTime: region.distance * 10 * 1000, + remainingTime: 1 * 10 * 1000, repeat, }; diff --git a/src/moves/toggleMissionRepeat.ts b/src/moves/toggleMissionRepeat.ts index 2f763ca..a55403f 100644 --- a/src/moves/toggleMissionRepeat.ts +++ b/src/moves/toggleMissionRepeat.ts @@ -1,9 +1,13 @@ +import { WORLDMAP_TYPES } from "../types"; +import { assert } from "../utils"; import type { VillageState } from "../village"; export default function toggleMissionRepeat(V: VillageState, regionIndex: number) { const region = V.worldmap[regionIndex]; + assert(region.type === WORLDMAP_TYPES.OASIS); + if (!region.state.mission) { return false; } diff --git a/src/stores/showOasisPanel.ts b/src/stores/showOasisPanel.ts new file mode 100644 index 0000000..b4e581f --- /dev/null +++ b/src/stores/showOasisPanel.ts @@ -0,0 +1,4 @@ +import { writable } from "svelte/store"; + + +export default writable(null); diff --git a/src/types.ts b/src/types.ts index a904e0c..a82ceba 100644 --- a/src/types.ts +++ b/src/types.ts @@ -81,28 +81,40 @@ export interface MissionType { repeat: boolean; } + +export enum WORLDMAP_TYPES { + EMPTY, + OASIS, + BOURGADE, +} + + interface BaseRegionType { - distance: number; + id: number; + type: WORLDMAP_TYPES; +} + + +export interface EmptyRegionType extends BaseRegionType { + type: WORLDMAP_TYPES.EMPTY; +} + + +export interface OasisType extends BaseRegionType { + type: WORLDMAP_TYPES.OASIS; + resource: keyof CostType; state: { - index: number; mission?: MissionType; }; } -export interface OasisType extends BaseRegionType { - type: 'oasis'; - resource: keyof CostType; -} - - export interface BourgadeType extends BaseRegionType { - type: 'bourgade'; - distance: number; + type: WORLDMAP_TYPES.BOURGADE; } -export type RegionType = OasisType | BourgadeType; +export type RegionType = EmptyRegionType | OasisType | BourgadeType; export interface HeroType { diff --git a/src/update.ts b/src/update.ts index f56ec3e..5f9eb4c 100644 --- a/src/update.ts +++ b/src/update.ts @@ -4,7 +4,7 @@ import { CULTURE_TO_WIN } from './constants'; import { createQuest } from './create'; import { resolveMission } from './missions'; import type { ProductionType } from './types'; -import { getProduction, getStorage, shuffle } from './utils'; +import { getProduction, getRegionsWithMissions, getStorage, shuffle } from './utils'; import village, { type VillageState } from "./village"; @@ -36,7 +36,7 @@ export default function update(timestamp: number) { }); // Advance missions. - V.worldmap.forEach(region => { + getRegionsWithMissions(V).forEach(region => { if (!region.state.mission) { return; } diff --git a/src/utils.ts b/src/utils.ts index d2f20f2..a3f7b32 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ +import { WORLD_MAP_HEIGHT, WORLD_MAP_WIDTH } from "./constants"; import units from "./data/units"; -import type { BuildingType, CostType, MissionType, ProductionType, ResourcesType } from "./types"; +import { WORLDMAP_TYPES, type BuildingType, type CostType, type OasisType, type ProductionType, type ResourcesType } from "./types"; import type { VillageState } from "./village"; @@ -88,9 +89,16 @@ export function getUnitSource(unitType: string) { } +export function getRegionsWithMissions(V: VillageState): OasisType[] { + return V.worldmap.filter(r => r.type === WORLDMAP_TYPES.OASIS); +} + + 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); + const missions = getRegionsWithMissions(V) + .filter(r => r.state.mission?.unitType === unitType) + .map(r => r.state.mission); missions.forEach(m => total -= m?.unitCount || 0); return total; } @@ -170,3 +178,25 @@ export function getTownhall(V: VillageState): BuildingType { } return townhall; } + +export function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new Error(msg); + } +} + + +export function getAdjacentWorldmapCells(cellIndex: number) { + const cells = [ + cellIndex - WORLD_MAP_WIDTH - 1, + cellIndex - WORLD_MAP_WIDTH, + cellIndex - WORLD_MAP_WIDTH + 1, + cellIndex - 1, + cellIndex + 1, + cellIndex + WORLD_MAP_WIDTH - 1, + cellIndex + WORLD_MAP_WIDTH, + cellIndex + WORLD_MAP_WIDTH + 1, + ]; + + return cells.filter(c => c >= 0 && c < WORLD_MAP_WIDTH * WORLD_MAP_HEIGHT); +} diff --git a/src/village.ts b/src/village.ts index 1278435..c54d642 100644 --- a/src/village.ts +++ b/src/village.ts @@ -3,8 +3,9 @@ import { writable } from "svelte/store"; import { createBuilding, createHero, createQuest } from "./create"; import worldmap from "./data/worldmap"; import { getTilesAtDistance, Hex } from "./hexgrid"; -import type { BuildingType, HeroType, QuestType, RegionType, ResourcesType } from "./types"; -import { getKeysAsNumbers, shuffle } from "./utils"; +import { WORLDMAP_TYPES, type BuildingType, type CostType, type HeroType, type QuestType, type RegionType, type ResourcesType } from "./types"; +import { getAdjacentWorldmapCells, getKeysAsNumbers, shuffle } from "./utils"; +import { WORLD_MAP_HEIGHT, WORLD_MAP_WIDTH } from "./constants"; type Board = { @@ -68,15 +69,28 @@ function getInitialOutsideBoard() { function getInitialWorldmap(): RegionType[] { - return worldmap.map((r, index) => { - const region = r as RegionType; - return { - ...region, - state: { - index, - }, + const board: RegionType[] = [] + for (let i = 0; i < WORLD_MAP_WIDTH * WORLD_MAP_HEIGHT; i++) { + board[i] = { + id: i, + type: WORLDMAP_TYPES.EMPTY, + }; + } + const centerIndex = Math.floor((WORLD_MAP_WIDTH * WORLD_MAP_HEIGHT) / 2); + board[centerIndex].type = WORLDMAP_TYPES.BOURGADE; + + const adj = shuffle(getAdjacentWorldmapCells(centerIndex)); + worldmap.forEach(c => { + const cellIndex = adj.pop() || 0; + board[cellIndex] = { + type: WORLDMAP_TYPES.OASIS, + id: cellIndex, + resource: c.resource as keyof CostType, + state: {}, }; }); + + return board; }