Compare commits

..

No commits in common. "10bd9121a231961fa7d07ade24d8506ffffea56f" and "f427f4e2b6d360c07d25242719a892827cc45aef" have entirely different histories.

30 changed files with 31 additions and 602 deletions

View File

@ -1 +0,0 @@
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M253.47 17.406C123.76 17.406 18.437 122.76 18.437 252.47c0 129.707 105.324 235.06 235.03 235.06 129.707 0 235.063-105.353 235.063-235.06 0-129.71-105.355-235.064-235.06-235.064zM367.874 68.75c61.246 38.19 101.97 106.14 101.97 183.72 0 17.143-1.993 33.823-5.75 49.81l-34.25-18.06 22 54.874c-9.454 21.647-22.362 41.432-38 58.687l-43.158-30.936-64.625 47.72-61.656 6.967-13.906-41.78-49.72 26.844-68.093-18.938 9.157 36.594c-28.41-21.793-51.23-50.466-66-83.563L81.25 304.47l32.25 17.124 59.22-9.875 2.843-40.908-37.344-1.718 4.905-17.844 30.78-25.313-25.093-15.625 67.22-38.593-45.345-29.657-66.625 40.187-49.437-15.28c13.812-32.14 35.21-60.22 61.906-82.064l-3.75 44.375 43.376-34.124 72 22.22-22.5-27.407L233 75.562l26.813 28.468 71 9.845-3.5-34.47 41.468 12.657-.905-23.312zm1.156 120.03L278 199.47l28.906 43.218 3.156 64.468L339.25 321l11.438-28.375 62.656 48.656L395.78 294l6.408-48.344-43.75-22.72 10.593-34.155zM221 192.438l-31.594 21.188 36.47 14.78 16.686-14.78L221 192.437zm22.188 144.688l18.687 52.594 19.78-42.564-38.467-10.03z" fill="#ffffff" fill-opacity="1"></path></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -37,6 +37,9 @@ h1 {
} }
#app { #app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center; text-align: center;
} }

View File

@ -9,7 +9,6 @@
$: isUpgrading = building.state.upgrade.isUpgrading; $: isUpgrading = building.state.upgrade.isUpgrading;
$: canUpgrade = !isUpgrading && canPayBuildingCost($village, building); $: canUpgrade = !isUpgrading && canPayBuildingCost($village, building);
$: maxLevelReached = building.level === building.maxLevel;
function upgradeBuilding() { function upgradeBuilding() {
@ -25,7 +24,6 @@
class="level" class="level"
class:can-upgrade={ canUpgrade } class:can-upgrade={ canUpgrade }
class:is-upgrading={ isUpgrading } class:is-upgrading={ isUpgrading }
class:max-level={ maxLevelReached }
on:click|stopPropagation={ upgradeBuilding } on:click|stopPropagation={ upgradeBuilding }
> >
{ building.level } { building.level }
@ -61,8 +59,4 @@
.level.is-upgrading { .level.is-upgrading {
border-color: hsl(56, 99%, 43%); border-color: hsl(56, 99%, 43%);
} }
.level.max-level {
border-color: hsl(209, 70%, 52%);
}
</style> </style>

View File

@ -1,66 +0,0 @@
<script lang="ts">
import moves from "../moves";
import type { OasisType } from "../types";
import { getRemainingUnitCount } from "../utils";
import village from "../village";
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>
<div>
<h2>
<img src="/img/icons/{ region.resource }.png" alt="{ region.resource }">
Oasis
(→ { region.distance })
</h2>
{ #if region.state.mission }
<div>
<p>{ region.state.mission.unitCount } soldiers are on a mission here.</p>
<p>Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }</p>
</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>
<style>
h2 {
align-items: center;
display: flex;
gap: 0.4em;
}
</style>

View File

@ -40,6 +40,7 @@
.outside-map { .outside-map {
display: grid; display: grid;
height: 100%; height: 100%;
margin-top: 0.8em;
position: relative; position: relative;
} }

View File

@ -43,6 +43,7 @@
.village-map { .village-map {
display: grid; display: grid;
height: 100%; height: 100%;
margin-top: 0.8em;
position: relative; position: relative;
} }

View File

@ -1,29 +0,0 @@
<script lang="ts">
import village from "../village";
import OasisRegion from "./OasisRegion.svelte";
</script>
<section class="worldmap">
{ #each $village.worldmap as region }
<div class="region">
{ #if region.type === 'oasis' }
<OasisRegion { region } />
{ /if }
</div>
{ /each }
</section>
<style>
.worldmap {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 1em;
}
.region {
border: 1px solid white;
padding: 1em;
}
</style>

View File

@ -1,6 +1,6 @@
import buildings from "./data/buildings"; import buildings from "./data/buildings";
import { Hex } from "./hexgrid"; import { Hex } from "./hexgrid";
import type { BuildingSource, BuildingType } from "./types"; import type { BuildingType, BuildingSource } from "./types";
let uid = 0; let uid = 0;

View File

@ -8,7 +8,6 @@ export default [
type: 'townhall', type: 'townhall',
name: 'Town Hall', name: 'Town Hall',
autoBuilt: true, autoBuilt: true,
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -32,7 +31,6 @@ export default [
type: 'woodcutter', type: 'woodcutter',
name: 'Woodcutter', name: 'Woodcutter',
autoBuilt: true, autoBuilt: true,
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -58,7 +56,6 @@ export default [
type: 'mine', type: 'mine',
name: 'Mine', name: 'Mine',
autoBuilt: true, autoBuilt: true,
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -84,7 +81,6 @@ export default [
type: 'pit', type: 'pit',
name: 'Pit', name: 'Pit',
autoBuilt: true, autoBuilt: true,
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -110,7 +106,6 @@ export default [
type: 'field', type: 'field',
name: 'Field', name: 'Field',
autoBuilt: true, autoBuilt: true,
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -131,7 +126,6 @@ export default [
{ {
type: 'warehouse', type: 'warehouse',
name: 'Warehouse', name: 'Warehouse',
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -156,7 +150,6 @@ export default [
{ {
type: 'granary', type: 'granary',
name: 'Granary', name: 'Granary',
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -181,7 +174,6 @@ export default [
{ {
type: 'university', type: 'university',
name: 'University', name: 'University',
maxLevel: 20,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 100, wood: level * 100,
@ -202,30 +194,5 @@ export default [
recruitmentTime: (V: VillageState, self: BuildingType) => 2 - 0.06 * self.level, recruitmentTime: (V: VillageState, self: BuildingType) => 2 - 0.06 * self.level,
}, },
}, },
}, }
{
type: 'barracks',
name: 'Barracks',
maxLevel: 20,
cost: (level: number) => {
return {
wood: level * 100,
stone: level * 100,
iron: level * 100,
food: 0,
};
},
behavior: {
production: (V: VillageState, self: BuildingType) => {
const prod = getEmptyResources();
const intakePerMinute = Math.ceil(self.level / 3) * 3;
prod.food = -intakePerMinute;
return prod;
},
units: {
type: 'soldier',
recruitmentTime: (V: VillageState, self: BuildingType) => 1 - 0.03 * self.level,
},
},
},
]; ];

View File

@ -9,23 +9,8 @@ export default [
food: 0, food: 0,
}, },
behavior: { behavior: {
caryingCapacity: 2,
culturePerMinute: 1, culturePerMinute: 1,
foodIntakePerMinute: 2, foodIntakePerMinute: 2,
}, },
}, },
{
type: 'soldier',
name: 'Soldier',
cost: {
wood: 15,
stone: 5,
iron: 40,
food: 0,
},
behavior: {
caryingCapacity: 4,
foodIntakePerMinute: 1,
},
},
]; ];

View File

@ -1,22 +0,0 @@
export default [
{
type: 'oasis',
resource: 'food',
distance: 1,
},
{
type: 'oasis',
resource: 'wood',
distance: 1,
},
{
type: 'oasis',
resource: 'stone',
distance: 1,
},
{
type: 'oasis',
resource: 'iron',
distance: 1,
},
];

View File

@ -1,11 +1,7 @@
<script lang="ts"> <script lang="ts">
import { createBuilding } from "../create";
import buildings from "../data/buildings"; import buildings from "../data/buildings";
import moves from "../moves"; import moves from "../moves";
import showBuildingCreator from "../stores/showBuildingCreator"; import showBuildingCreator from "../stores/showBuildingCreator";
import { canPayBuildingCost } from "../utils";
import village from "../village";
import Cost from "./Cost.svelte";
function close() { function close() {
showBuildingCreator.set(null); showBuildingCreator.set(null);
@ -22,11 +18,7 @@
} }
} }
const constructible = buildings.filter(b => !b.autoBuilt).map(b =>{ const constructible = buildings.filter(b => !b.autoBuilt);
const building = createBuilding(b.type);
building.level = 0;
return building;
});
</script> </script>
{ #if $showBuildingCreator !== null } { #if $showBuildingCreator !== null }
@ -42,13 +34,7 @@
{ #each constructible as building } { #each constructible as building }
<div> <div>
<p>{ building.name }</p> <p>{ building.name }</p>
<Cost cost={ building.cost(1) } /> <button on:click={ () => build(building.type) }>Build</button>
<button
on:click={ () => build(building.type) }
disabled={ !canPayBuildingCost($village, building) }
>
Build
</button>
</div> </div>
{ /each } { /each }
</div> </div>

View File

@ -1,10 +1,9 @@
<script lang="ts"> <script lang="ts">
import moves from "../moves"; import moves from "../moves";
import showBuildingPanel from "../stores/showBuildingPanel"; import showBuildingPanel from "../stores/showBuildingPanel";
import { getBuilding, getBuildingUpgradeCost } from "../utils"; import { getBuilding } from "../utils";
import village from "../village"; import village from "../village";
import BuildingRecruitment from "./BuildingRecruitment.svelte"; import UniversityPanel from "./UniversityPanel.svelte";
import Cost from "./Cost.svelte";
function close() { function close() {
showBuildingPanel.set(null); showBuildingPanel.set(null);
@ -36,24 +35,14 @@
<div class="content"> <div class="content">
{ #if building.level === 0 } { #if building.level === 0 }
<p>Building in construction…</p> <p>Building in construction…</p>
{ :else if building.behavior.units } { :else if building.type === 'university' }
<BuildingRecruitment { building } /> <UniversityPanel { building } />
{ /if } { /if }
</div> </div>
<div class="upgrade"> <div class="upgrade">
{ #if building.state.upgrade.isUpgrading }
<p>
Upgrading to level { building.level + 1 }
({ Math.ceil(building.state.upgrade.remainingTime / 1000) })
</p>
{ :else if building.level === building.maxLevel }
<p>Max level reached!</p>
{ :else }
<div> <div>
<Cost cost={ getBuildingUpgradeCost($village, building) } />
<button on:click={ () => upgrade() }>Upgrade</button> <button on:click={ () => upgrade() }>Upgrade</button>
</div> </div>
{ /if }
</div> </div>
</div> </div>
</section> </section>

View File

@ -3,7 +3,6 @@
import Outside from "../board/Outside.svelte"; import Outside from "../board/Outside.svelte";
import Village from "../board/Village.svelte"; import Village from "../board/Village.svelte";
import Worldmap from "../board/Worldmap.svelte";
import gameTab from "../stores/gameTab"; import gameTab from "../stores/gameTab";
import type { GameTab } from "../types"; import type { GameTab } from "../types";
import update from "../update"; import update from "../update";
@ -13,7 +12,6 @@
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"; import Victory from "./Victory.svelte";
import Quests from "./Quests.svelte";
onMount(() => { onMount(() => {
@ -37,20 +35,15 @@
<section class="hud"> <section class="hud">
<header> <header>
<Navigation { setTab } />
<Resources />
</header>
<div class="side">
<Units /> <Units />
<Quests /> <Resources />
</div> <Navigation { setTab } />
</header>
<div class="board"> <div class="board">
{ #if $gameTab === 'village' } { #if $gameTab === 'village' }
<Village /> <Village />
{ :else if $gameTab === 'resources' } { :else if $gameTab === 'resources' }
<Outside /> <Outside />
{ :else if $gameTab === 'world' }
<Worldmap />
{ /if } { /if }
</div> </div>
</section> </section>
@ -61,19 +54,9 @@
</section> </section>
<style> <style>
.hud { header {
display: grid;
grid-template-columns: 20vw 1fr 20vw;
grid-template-rows: 20vh 1fr;
height: 100vh;
width: 100vw;
}
.hud header {
display: flex; display: flex;
justify-content: center; justify-content: space-around;
flex-direction: column;
grid-area: 1 / span 3;
} }
.overlay { .overlay {

View File

@ -19,13 +19,6 @@
<img src="/img/icons/field.svg" alt=""> <img src="/img/icons/field.svg" alt="">
<span>Resources</span> <span>Resources</span>
</button> </button>
<button
class="invisible"
on:click={ () => setTab('world') }
>
<img src="/img/icons/world.svg" alt="">
<span>Worldmap</span>
</button>
</nav> </nav>
<style> <style>

View File

@ -1,50 +0,0 @@
<script lang="ts">
import { fly } from "svelte/transition";
import moves from "../moves";
import village from "../village";
import Reward from "./Reward.svelte";
import { flip } from "svelte/animate";
$: isQuestStarted = $village.quests.some(q => q.started);
function startQuest(id: number) {
moves.startQuest(id);
}
</script>
<section>
{ #each $village.quests as quest (quest.id) }
<div
class="quest"
in:fly={{ duration: 200, delay: 100, x: -500 }}
out:fly={{ duration: 200, x: 500 }}
animate:flip={{ duration: 100 }}
>
<p>
<Reward reward={ quest.reward } />
</p>
<p>
<img src="/img/icons/time.png" alt="Duration">
{ #if quest.started }
{ Math.ceil((quest.remainingTime || 0) / 1000) }
{ :else }
{ quest.duration }
<button
on:click={ () => startQuest(quest.id) }
disabled={ isQuestStarted }
>
Start quest
</button>
{ /if }
</p>
</div>
{ /each }
</section>
<style>
.quest {
text-align: left;
}
</style>

View File

@ -38,8 +38,8 @@
<style> <style>
.resources { .resources {
display: flex; display: flex;
justify-content: center; justify-content: space-around;
gap: 2em; gap: 1em;
} }
.resources div { .resources div {

View File

@ -1,53 +0,0 @@
<script lang="ts">
import type { ResourcesType } from "../types";
export let reward: ResourcesType;
</script>
<div class="reward">
{ #if reward.wood > 0 }
<div>
<img src="/img/icons/wood.png" alt="Wood" />
{ reward.wood }
</div>
{ /if }
{ #if reward.stone > 0 }
<div>
<img src="/img/icons/stone.png" alt="Stone" />
{ reward.stone }
</div>
{ /if }
{ #if reward.iron > 0 }
<div>
<img src="/img/icons/iron.png" alt="Iron" />
{ reward.iron }
</div>
{ /if }
{ #if reward.food > 0 }
<div>
<img src="/img/icons/food.png" alt="Food" />
{ reward.food }
</div>
{ /if }
{ #if reward.culture > 0 }
<div>
<img src="/img/icons/culture.png" alt="Culture" />
{ reward.culture }
</div>
{ /if }
</div>
<style>
.reward {
align-items: center;
display: flex;
gap: 1em;
justify-content: center;
}
.reward div {
display: flex;
align-items: center;
gap: 0.2em;
}
</style>

View File

@ -1,14 +1,9 @@
<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]) => {
const unit = units.find(u => u.type === type); const unit = units.find(u => u.type === type);
if (!unit) {
throw new Error(`Unable to find unit: "${type}"`);
}
return { return {
...unit, ...unit,
count, count,
@ -18,6 +13,6 @@
<section> <section>
{ #each currentUnits as unit } { #each currentUnits as unit }
<p>{ unit.name }: { getRemainingUnitCount($village, unit.type) } / { unit.count }</p> <p>{ unit.name }: { unit.count }</p>
{ /each } { /each }
</section> </section>

View File

@ -49,8 +49,8 @@
} }
</script> </script>
<div class="recruitment"> <div class="university">
<p>Recruit { unit.name }s</p> <p>Create Philosophers</p>
<div class="cost"> <div class="cost">
<Cost { cost } duration={ timeToRecruit } /> <Cost { cost } duration={ timeToRecruit } />
</div> </div>
@ -77,7 +77,7 @@
</div> </div>
<style> <style>
.recruitment .cost { .university .cost {
margin: 1em; margin: 1em;
} }
</style> </style>

View File

@ -1,34 +0,0 @@
import type { OasisType, RegionType } from "./types";
import { getUnitSource } from "./utils";
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;
}
const unit = getUnitSource('soldier');
V.resources[region.resource] += mission.unitCount * unit.behavior.caryingCapacity;
delete region.state.mission;
}

View File

@ -2,10 +2,8 @@ import { produce } from 'immer';
import village, { type VillageState } from '../village'; import village, { type VillageState } from '../village';
import build from './build'; import build from './build';
import pillage from './pillage';
import recruitUnits from './recruitUnits';
import startQuest from './startQuest';
import upgradeBuilding from './upgradeBuilding'; import upgradeBuilding from './upgradeBuilding';
import recruitUnits from './recruitUnits';
// 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
@ -33,7 +31,5 @@ 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),
startQuest: makeMove(startQuest),
}; };

View File

@ -1,24 +0,0 @@
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

@ -1,13 +0,0 @@
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;
}

View File

@ -12,10 +12,6 @@ export default function upgradeBuilding(V: VillageState, buildingId: number) {
return false; return false;
} }
if (building.level >= building.maxLevel) {
return false;
}
if (!canPayBuildingCost(V, building)) { if (!canPayBuildingCost(V, building)) {
return false; return false;
} }

View File

@ -1,34 +0,0 @@
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,
};
}

View File

@ -1,7 +1,7 @@
import type { Hex } from "./hexgrid"; import type { Hex } from "./hexgrid";
export type GameTab = 'village' | 'resources' | 'world'; export type GameTab = 'village' | 'resources';
export interface CostType { export interface CostType {
@ -24,7 +24,6 @@ export interface BuildingSource {
name: string; name: string;
type: string; type: string;
autoBuilt?: boolean; autoBuilt?: boolean;
maxLevel: number;
cost: (level: number) => CostType; cost: (level: number) => CostType;
behavior: { behavior: {
production?: Function; production?: Function;
@ -59,54 +58,7 @@ export interface UnitType {
name: string; name: string;
cost: CostType; cost: CostType;
behavior: { behavior: {
caryingCapacity: number;
foodIntakePerMinute: number; foodIntakePerMinute: number;
culturePerMinute?: number; culturePerMinute?: number;
} }
} }
export interface MissionType {
type: string;
unitType: string;
unitCount: number;
remainingTime: number;
}
interface BaseRegionType {
distance: number;
state: {
index: number;
mission?: MissionType;
};
}
export interface OasisType extends BaseRegionType {
type: 'oasis';
resource: keyof CostType;
}
export interface BourgadeType extends BaseRegionType {
type: 'bourgade';
distance: number;
}
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;
}

View File

@ -1,11 +1,9 @@
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";
import { createQuest } from './quests';
let lastFrame: number; let lastFrame: number;
@ -34,39 +32,6 @@ 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);
}
});
// 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. // 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, MissionType, ProductionType, ResourcesType } from "./types"; import type { BuildingType, CostType, ProductionType, ResourcesType } from "./types";
import type { VillageState } from "./village"; import type { VillageState } from "./village";
@ -39,7 +39,7 @@ export function getProduction(villageState: VillageState): ResourcesType {
.reduce(_reduceResources, production); .reduce(_reduceResources, production);
// Add units production and intake. // Add units production and intake.
['philosopher', 'soldier'].forEach(type => { ['philosopher'].forEach(type => {
const unit = getUnitSource(type); const unit = getUnitSource(type);
const unitCount = villageState.units[type] || 0; const unitCount = villageState.units[type] || 0;
@ -49,7 +49,7 @@ export function getProduction(villageState: VillageState): ResourcesType {
// Add culture production for Philosophers. // Add culture production for Philosophers.
if (type === 'philosopher') { if (type === 'philosopher') {
const outputPerMinute = unit.behavior.culturePerMinute || 0; const outputPerMinute = unit.behavior.culturePerMinute;
production.culture += outputPerMinute * unitCount; production.culture += outputPerMinute * unitCount;
} }
}); });
@ -88,14 +88,6 @@ 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);
} }
@ -130,21 +122,6 @@ export function shuffle<T>(array: Array<T>): Array<T> {
} }
/**
* 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 { export function getBuildingUpgradeCost(_V: VillageState, building: BuildingType): CostType {
const level = building.level + 1; const level = building.level + 1;
return building.cost(level); return building.cost(level);

View File

@ -1,11 +1,9 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { createBuilding } from "./create"; import { createBuilding } from "./create";
import worldmap from "./data/worldmap";
import { getTilesAtDistance, Hex } from "./hexgrid"; import { getTilesAtDistance, Hex } from "./hexgrid";
import type { BuildingType, QuestType, RegionType, ResourcesType } from "./types"; import type { BuildingType, ResourcesType } from "./types";
import { getKeysAsNumbers, shuffle } from "./utils"; import { getKeysAsNumbers, shuffle } from "./utils";
import { createQuest } from "./quests";
type Board = { type Board = {
@ -23,8 +21,6 @@ export interface VillageState {
resources: ResourcesType; resources: ResourcesType;
villageTiles: Board; villageTiles: Board;
outsideTiles: Board; outsideTiles: Board;
worldmap: RegionType[];
quests: QuestType[];
victory: boolean; victory: boolean;
} }
@ -67,28 +63,6 @@ function getInitialOutsideBoard() {
} }
function getInitialWorldmap(): RegionType[] {
return worldmap.map((r, index) => {
const region = r as RegionType;
return {
...region,
state: {
index,
},
};
});
}
function getInitialQuests(): QuestType[] {
return [
createQuest(1),
createQuest(1),
createQuest(1),
];
}
function getInitialState() { function getInitialState() {
const state: VillageState = { const state: VillageState = {
buildings: [], buildings: [],
@ -102,8 +76,6 @@ function getInitialState() {
}, },
villageTiles: getInitialVillageBoard(), villageTiles: getInitialVillageBoard(),
outsideTiles: getInitialOutsideBoard(), outsideTiles: getInitialOutsideBoard(),
worldmap: getInitialWorldmap(),
quests: getInitialQuests(),
victory: false, victory: false,
}; };
@ -131,7 +103,7 @@ function getInitialState() {
} }
const newBuilding = createBuilding(type); const newBuilding = createBuilding(type);
newBuilding.tile = new Hex(x, y); newBuilding.tile = new Hex(x, y);
newBuilding.level = 1; //newBuilding.type === 'field' ? 1 : 10; newBuilding.level = newBuilding.type === 'field' ? 1 : 10;
state.outsideTiles[y][x] = newBuilding.id; state.outsideTiles[y][x] = newBuilding.id;
state.buildings.push(newBuilding); state.buildings.push(newBuilding);
}); });