Show a map of the outside of the village, with all the resource farms.

This commit is contained in:
Adrian 2024-10-24 16:24:39 +02:00
parent a47486973c
commit 756f2bc152
12 changed files with 145 additions and 31 deletions

View File

@ -5,3 +5,4 @@
</script> </script>
<p>{ building.name }</p> <p>{ building.name }</p>
<p>{ building.level }</p>

48
src/board/Outside.svelte Normal file
View File

@ -0,0 +1,48 @@
<script lang="ts">
import gameTab from "../stores/gameTab";
import showBuildingPanel from "../stores/showBuildingPanel";
import { getBuilding, getKeysAsNumbers } from "../utils";
import village, { VILLAGE_TILE } from "../village";
import BuildingTile from "./BuildingTile.svelte";
import Tile from "./Tile.svelte";
function openBuildingPanel(buildingId: number) {
showBuildingPanel.set(buildingId);
}
</script>
<section class="outside-map">
<div>
{ #each getKeysAsNumbers($village.outsideTiles) as y }
<div>
{ #each getKeysAsNumbers($village.outsideTiles[y]) as x }
{ #if $village.outsideTiles[y][x] >= 0 }
<Tile
onTileClick={ () => openBuildingPanel($village.outsideTiles[y][x]) }
>
<BuildingTile building={ getBuilding($village, $village.outsideTiles[y][x]) } />
</Tile>
{ :else if $village.outsideTiles[y][x] === VILLAGE_TILE }
<Tile
onTileClick={ () => gameTab.set('village') }
/>
{ /if }
{ /each }
</div>
{ /each }
</div>
</section>
<style>
.outside-map {
display: grid;
height: 100%;
margin-top: 0.8em;
position: relative;
}
.outside-map > div {
margin: auto;
}
</style>

View File

@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Hex } from "../hexgrid"; import { Hex } from "../hexgrid";
import moves from "../moves";
import showBuildingCreator from "../stores/showBuildingCreator"; import showBuildingCreator from "../stores/showBuildingCreator";
import showBuildingPanel from "../stores/showBuildingPanel"; import showBuildingPanel from "../stores/showBuildingPanel";
import { getBuilding, getKeysAsNumbers } from "../utils"; import { getBuilding, getKeysAsNumbers } from "../utils";
@ -9,10 +8,6 @@
import Tile from "./Tile.svelte"; import Tile from "./Tile.svelte";
function upgradeBuilding(id: number) {
moves.upgradeBuilding(id);
}
function openBuildingCreator(tile: Hex) { function openBuildingCreator(tile: Hex) {
showBuildingCreator.set(tile); showBuildingCreator.set(tile);
} }
@ -48,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,11 +1,13 @@
import type { Building } from "./types"; import type { Building, BuildingSource } from "./types";
import { getEmptyResources } from "./utils"; import { getEmptyResources } from "./utils";
import type { VillageState } from "./village"; import type { VillageState } from "./village";
export default { const buildings = [
'townhall': { {
type: 'townhall',
name: 'Town Hall', name: 'Town Hall',
autoBuilt: true,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -25,8 +27,10 @@ export default {
}, },
}, },
}, },
'woodcutter': { {
type: 'woodcutter',
name: 'Woodcutter', name: 'Woodcutter',
autoBuilt: true,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -48,8 +52,10 @@ export default {
}, },
}, },
}, },
'mine': { {
type: 'mine',
name: 'Mine', name: 'Mine',
autoBuilt: true,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -71,8 +77,10 @@ export default {
}, },
}, },
}, },
'pit': { {
type: 'pit',
name: 'Pit', name: 'Pit',
autoBuilt: true,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -94,8 +102,10 @@ export default {
}, },
}, },
}, },
'field': { {
type: 'field',
name: 'Field', name: 'Field',
autoBuilt: true,
cost: (level: number) => { cost: (level: number) => {
return { return {
wood: level * 10, wood: level * 10,
@ -113,7 +123,8 @@ export default {
}, },
}, },
}, },
'warehouse': { {
type: 'warehouse',
name: 'Warehouse', name: 'Warehouse',
cost: (level: number) => { cost: (level: number) => {
return { return {
@ -136,7 +147,8 @@ export default {
}, },
}, },
}, },
'granary': { {
type: 'granary',
name: 'Granary', name: 'Granary',
cost: (level: number) => { cost: (level: number) => {
return { return {
@ -159,4 +171,5 @@ export default {
}, },
}, },
}, },
}; ];
export default buildings;

View File

@ -1,3 +1,4 @@
import buildings from "./buildings";
import { Hex } from "./hexgrid"; import { Hex } from "./hexgrid";
import type { Building, BuildingSource } from "./types"; import type { Building, BuildingSource } from "./types";
@ -5,9 +6,22 @@ import type { Building, BuildingSource } from "./types";
let uid = 0; let uid = 0;
export function createBuilding(building: BuildingSource): Building { export function getBuildingSource(buildingType: string): BuildingSource {
const source: BuildingSource | undefined = buildings.find(b => b.type === buildingType);
if (source === undefined) {
throw new Error(`Unknown building type: "${buildingType}"`);
}
return source
}
export function createBuilding(buildingType: string): Building {
const source: BuildingSource = getBuildingSource(buildingType);
return { return {
...building, ...source,
id: uid++, id: uid++,
level: 1, level: 1,
tile: new Hex(0, 0), tile: new Hex(0, 0),

View File

@ -17,6 +17,8 @@
close(); close();
} }
} }
const constructible = buildings.filter(b => !b.autoBuilt);
</script> </script>
{ #if $showBuildingCreator !== null } { #if $showBuildingCreator !== null }
@ -29,10 +31,10 @@
</span> </span>
</header> </header>
<div class="buildings"> <div class="buildings">
{ #each Object.entries(buildings) as [type, building] } { #each constructible as building }
<div> <div>
<p>{ building.name }</p> <p>{ building.name }</p>
<button on:click={ () => build(type) }>Build</button> <button on:click={ () => build(building.type) }>Build</button>
</div> </div>
{ /each } { /each }
</div> </div>

View File

@ -6,6 +6,10 @@
import BuildingCreator from "./BuildingCreator.svelte"; import BuildingCreator from "./BuildingCreator.svelte";
import Resources from "./Resources.svelte"; import Resources from "./Resources.svelte";
import BuildingPanel from "./BuildingPanel.svelte"; import BuildingPanel from "./BuildingPanel.svelte";
import Outside from "../board/Outside.svelte";
import Navigation from "./Navigation.svelte";
import type { GameTab } from "../types";
import gameTab from "../stores/gameTab";
onMount(() => { onMount(() => {
@ -21,14 +25,23 @@
cancelAnimationFrame(frame); cancelAnimationFrame(frame);
} }
}); });
function setTab(newTab: GameTab) {
gameTab.set(newTab);
}
</script> </script>
<section class="hud"> <section class="hud">
<header> <header>
<Resources /> <Resources />
<Navigation { setTab } />
</header> </header>
<div class="buildings"> <div class="">
{ #if $gameTab === 'village' }
<Village /> <Village />
{ :else if $gameTab === 'resources' }
<Outside />
{ /if }
</div> </div>
</section> </section>
<section class="overlay"> <section class="overlay">
@ -37,10 +50,6 @@
</section> </section>
<style> <style>
.buildings {
margin-top: 2em;
}
.overlay { .overlay {
left: 0; left: 0;
position: absolute; position: absolute;

20
src/hud/Navigation.svelte Normal file
View File

@ -0,0 +1,20 @@
<script lang="ts">
import type { GameTab } from "../types";
export let setTab: (tab: GameTab) => void;
</script>
<nav>
<button
class="invisible"
on:click={ () => setTab('village') }
>
Village
</button>
<button
class="invisible"
on:click={ () => setTab('resources') }
>
Resources
</button>
</nav>

View File

@ -1,11 +1,11 @@
import buildings from "../buildings"; import buildings from "../buildings";
import { createBuilding } from "../create"; import { createBuilding, getBuildingSource } from "../create";
import type { Hex } from "../hexgrid"; import type { Hex } from "../hexgrid";
import { DEFAULT_TILE, type VillageState } from "../village"; import { DEFAULT_TILE, type VillageState } from "../village";
export default function build(V: VillageState, buildingType: keyof typeof buildings, tile: Hex) { export default function build(V: VillageState, buildingType: string, tile: Hex) {
const building = buildings[buildingType]; const building = getBuildingSource(buildingType);
const cost = building.cost(1); const cost = building.cost(1);
if ( if (
@ -26,7 +26,7 @@ export default function build(V: VillageState, buildingType: keyof typeof buildi
V.resources.iron -= cost.iron; V.resources.iron -= cost.iron;
V.resources.food -= cost.food; V.resources.food -= cost.food;
const newBuilding = createBuilding(building); const newBuilding = createBuilding(buildingType);
newBuilding.tile = tile; newBuilding.tile = tile;
V.buildings.push(newBuilding); V.buildings.push(newBuilding);

5
src/stores/gameTab.ts Normal file
View File

@ -0,0 +1,5 @@
import { writable } from "svelte/store";
import type { GameTab } from "../types";
export default writable<GameTab>('village');

View File

@ -1,5 +1,9 @@
import type { Hex } from "./hexgrid"; import type { Hex } from "./hexgrid";
export type GameTab = 'village' | 'resources';
export interface Cost { export interface Cost {
wood: number; wood: number;
stone: number; stone: number;
@ -13,6 +17,8 @@ export type Production = Cost;
export interface BuildingSource { export interface BuildingSource {
name: string; name: string;
type: string;
autoBuilt?: boolean;
cost: (level: number) => Cost; cost: (level: number) => Cost;
behavior: { behavior: {
production?: Function; production?: Function;

View File

@ -81,12 +81,12 @@ function getInitialState() {
}; };
// Create the Town hall. // Create the Town hall.
const townhall = createBuilding(buildings.townhall); const townhall = createBuilding('townhall');
state.villageTiles[0][0] = townhall.id; state.villageTiles[0][0] = townhall.id;
state.buildings.push(townhall); state.buildings.push(townhall);
// Create all the resource buildings. // Create all the resource buildings.
const resourceBuildingTypes: Array<keyof typeof buildings> = shuffle([ const resourceBuildingTypes: Array<string> = shuffle([
'woodcutter', 'woodcutter', 'woodcutter', 'woodcutter', 'woodcutter', 'woodcutter', 'woodcutter', 'woodcutter',
'mine', 'mine', 'mine', 'mine', 'mine', 'mine', 'mine', 'mine',
'pit', 'pit', 'pit', 'pit', 'pit', 'pit', 'pit', 'pit',
@ -102,7 +102,7 @@ function getInitialState() {
if (type === undefined) { if (type === undefined) {
throw new Error("Not enough building types for outside resource buildings"); throw new Error("Not enough building types for outside resource buildings");
} }
const newBuilding = createBuilding(buildings[type]); const newBuilding = createBuilding(type);
newBuilding.tile = new Hex(x, y); newBuilding.tile = new Hex(x, y);
state.outsideTiles[y][x] = newBuilding.id; state.outsideTiles[y][x] = newBuilding.id;
state.buildings.push(newBuilding); state.buildings.push(newBuilding);