From 10bd9121a231961fa7d07ade24d8506ffffea56f Mon Sep 17 00:00:00 2001 From: Adrian Gaudebert Date: Thu, 7 Nov 2024 17:15:41 +0100 Subject: [PATCH] Add quests that give resources as reward. --- src/app.css | 3 --- src/board/Outside.svelte | 1 - src/board/Village.svelte | 1 - src/board/Worldmap.svelte | 4 ++- src/create.ts | 2 +- src/hud/Game.svelte | 22 +++++++++++++--- src/hud/Quests.svelte | 50 ++++++++++++++++++++++++++++++++++++ src/hud/Resources.svelte | 4 +-- src/hud/Reward.svelte | 53 +++++++++++++++++++++++++++++++++++++++ src/moves/index.ts | 6 +++-- src/moves/startQuest.ts | 13 ++++++++++ src/quests.ts | 34 +++++++++++++++++++++++++ src/types.ts | 15 +++++++++++ src/update.ts | 21 ++++++++++++++++ src/utils.ts | 15 +++++++++++ src/village.ts | 14 ++++++++++- 16 files changed, 242 insertions(+), 16 deletions(-) create mode 100644 src/hud/Quests.svelte create mode 100644 src/hud/Reward.svelte create mode 100644 src/moves/startQuest.ts create mode 100644 src/quests.ts diff --git a/src/app.css b/src/app.css index b92576c..37629e7 100644 --- a/src/app.css +++ b/src/app.css @@ -37,9 +37,6 @@ h1 { } #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; text-align: center; } diff --git a/src/board/Outside.svelte b/src/board/Outside.svelte index ec618df..ce43ecf 100644 --- a/src/board/Outside.svelte +++ b/src/board/Outside.svelte @@ -40,7 +40,6 @@ .outside-map { display: grid; height: 100%; - margin-top: 0.8em; position: relative; } diff --git a/src/board/Village.svelte b/src/board/Village.svelte index b5bbba0..85b45af 100644 --- a/src/board/Village.svelte +++ b/src/board/Village.svelte @@ -43,7 +43,6 @@ .village-map { display: grid; height: 100%; - margin-top: 0.8em; position: relative; } diff --git a/src/board/Worldmap.svelte b/src/board/Worldmap.svelte index 0c8d33d..ee9caec 100644 --- a/src/board/Worldmap.svelte +++ b/src/board/Worldmap.svelte @@ -17,7 +17,9 @@ diff --git a/src/hud/Resources.svelte b/src/hud/Resources.svelte index f475c55..522c044 100644 --- a/src/hud/Resources.svelte +++ b/src/hud/Resources.svelte @@ -38,8 +38,8 @@ diff --git a/src/moves/index.ts b/src/moves/index.ts index 25d916f..64ad62f 100644 --- a/src/moves/index.ts +++ b/src/moves/index.ts @@ -2,9 +2,10 @@ import { produce } from 'immer'; import village, { type VillageState } from '../village'; import build from './build'; -import upgradeBuilding from './upgradeBuilding'; -import recruitUnits from './recruitUnits'; import pillage from './pillage'; +import recruitUnits from './recruitUnits'; +import startQuest from './startQuest'; +import upgradeBuilding from './upgradeBuilding'; // Encapsulates a move function into a store update, where the data is made @@ -34,4 +35,5 @@ export default { upgradeBuilding: makeMove(upgradeBuilding), pillage: makeMove(pillage), recruitUnits: makeMove(recruitUnits), + startQuest: makeMove(startQuest), }; diff --git a/src/moves/startQuest.ts b/src/moves/startQuest.ts new file mode 100644 index 0000000..7656d79 --- /dev/null +++ b/src/moves/startQuest.ts @@ -0,0 +1,13 @@ +import type { VillageState } from "../village"; + +export default function startQuest(V: VillageState, questId: number) { + const quest = V.quests.find(q => q.id === questId); + if (!quest) { + return false; + } + + quest.started = true; + quest.remainingTime = quest.duration * 1000; + + return true; +} diff --git a/src/quests.ts b/src/quests.ts new file mode 100644 index 0000000..6b60503 --- /dev/null +++ b/src/quests.ts @@ -0,0 +1,34 @@ +import type { QuestType, ResourcesType } from "./types"; +import { getEmptyResources, random } from "./utils"; + + +let uid = 0; + + +export function createQuest(level: number): QuestType { + const reward = getEmptyResources(); + const adjustedLevel = level * 2 + 5; + const duration = random(adjustedLevel - 2, adjustedLevel + 2); + Object.keys(reward).forEach(r => { + const resource = r as keyof ResourcesType; + reward[resource] = random( + duration * 5 - level * 10, + duration * 5 + level * 10, + ); + + if (resource === 'food') { + reward[resource] = Math.round(reward[resource] / 3); + } + else if (resource === 'culture') { + reward[resource] = Math.round(reward[resource] / 20); + } + }); + + return { + id: uid++, + duration, + reward, + level, + started: false, + }; +} diff --git a/src/types.ts b/src/types.ts index 976af09..71c27c3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,3 +95,18 @@ export interface BourgadeType extends BaseRegionType { export type RegionType = OasisType | BourgadeType; + + +export interface HeroType { + id: number; +} + + +export interface QuestType { + id: number; + duration: number; + reward: ResourcesType; + level: number; + started: boolean; + remainingTime?: number; +} diff --git a/src/update.ts b/src/update.ts index c00fdbe..6ea9aa0 100644 --- a/src/update.ts +++ b/src/update.ts @@ -5,6 +5,7 @@ import { resolveMission } from './missions'; import type { ProductionType } from './types'; import { getProduction, getStorage, shuffle } from './utils'; import village, { type VillageState } from "./village"; +import { createQuest } from './quests'; let lastFrame: number; @@ -46,6 +47,26 @@ export default function update(timestamp: number) { } }); + // Advance quests. + V.quests.filter(q => q.started).forEach(quest => { + if (!quest.remainingTime) { + return; + } + + quest.remainingTime -= delta; + if (quest.remainingTime <= 0) { + V.resources.wood += quest.reward.wood; + V.resources.stone += quest.reward.stone; + V.resources.iron += quest.reward.iron; + V.resources.food += quest.reward.food; + V.resources.culture += quest.reward.culture; + + // Replace the finished quest with a new one. + V.quests = V.quests.filter(q => q.id !== quest.id); + V.quests.push(createQuest(quest.level + 1)); + } + }); + // Make all buildings and units produce and consume. const productionPerMinute = getProduction(V); const storage = getStorage(V); diff --git a/src/utils.ts b/src/utils.ts index 814b27a..16d97fa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -130,6 +130,21 @@ export function shuffle(array: Array): Array { } +/** + * Return a random integer in the range [ min, max ] (min and max can be returned). + * @param min integer + * @param max integer + * @returns integer + */ +export function random(min: number, max?: number) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1.0)); +} + + export function getBuildingUpgradeCost(_V: VillageState, building: BuildingType): CostType { const level = building.level + 1; return building.cost(level); diff --git a/src/village.ts b/src/village.ts index e9b4952..ada6414 100644 --- a/src/village.ts +++ b/src/village.ts @@ -3,8 +3,9 @@ import { writable } from "svelte/store"; import { createBuilding } from "./create"; import worldmap from "./data/worldmap"; import { getTilesAtDistance, Hex } from "./hexgrid"; -import type { BuildingType, RegionType, ResourcesType } from "./types"; +import type { BuildingType, QuestType, RegionType, ResourcesType } from "./types"; import { getKeysAsNumbers, shuffle } from "./utils"; +import { createQuest } from "./quests"; type Board = { @@ -23,6 +24,7 @@ export interface VillageState { villageTiles: Board; outsideTiles: Board; worldmap: RegionType[]; + quests: QuestType[]; victory: boolean; } @@ -78,6 +80,15 @@ function getInitialWorldmap(): RegionType[] { } +function getInitialQuests(): QuestType[] { + return [ + createQuest(1), + createQuest(1), + createQuest(1), + ]; +} + + function getInitialState() { const state: VillageState = { buildings: [], @@ -92,6 +103,7 @@ function getInitialState() { villageTiles: getInitialVillageBoard(), outsideTiles: getInitialOutsideBoard(), worldmap: getInitialWorldmap(), + quests: getInitialQuests(), victory: false, };