Add a Palace that gives new heroes, and allow different heroes to be sent on quests at the same time.
This commit is contained in:
parent
620bfb7581
commit
a21585280a
BIN
public/img/buildings/palace.png
Normal file
BIN
public/img/buildings/palace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
@ -1,6 +1,8 @@
|
||||
import buildings from "./data/buildings";
|
||||
import { NAMES } from "./data/heroes";
|
||||
import { Hex } from "./hexgrid";
|
||||
import type { BuildingSource, BuildingType } from "./types";
|
||||
import type { BuildingSource, BuildingType, HeroType, QuestType, ResourcesType } from "./types";
|
||||
import { getEmptyResources, random, shuffle } from "./utils";
|
||||
|
||||
|
||||
let uid = 0;
|
||||
@ -33,3 +35,37 @@ export function createBuilding(buildingType: string): BuildingType {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function createQuest(level: number): QuestType {
|
||||
const reward = getEmptyResources();
|
||||
const adjustedLevel = level * level + 3;
|
||||
const duration = random(adjustedLevel - 2, adjustedLevel + 2);
|
||||
Object.keys(reward).forEach(r => {
|
||||
const resource = r as keyof ResourcesType;
|
||||
reward[resource] = random(
|
||||
Math.round((duration * 5 - level * 10) * (1 + level / 3)),
|
||||
Math.round((duration * 5 + level * 10) * (1 + level / 3)),
|
||||
);
|
||||
|
||||
if (resource === 'culture') {
|
||||
reward[resource] = Math.round(reward[resource] / 20);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
id: uid++,
|
||||
duration,
|
||||
reward,
|
||||
level,
|
||||
started: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function createHero(): HeroType {
|
||||
return {
|
||||
id: uid++,
|
||||
name: shuffle(NAMES)[0],
|
||||
};
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { createHero } from "../create";
|
||||
import type { BuildingType } from "../types";
|
||||
import { getEmptyResources } from "../utils";
|
||||
import type { VillageState } from "../village";
|
||||
@ -31,10 +32,10 @@ export default [
|
||||
maxLevel: 20,
|
||||
cost: (level: number) => {
|
||||
return {
|
||||
wood: level * 10,
|
||||
stone: level * 10,
|
||||
iron: level * 10,
|
||||
food: 0,
|
||||
wood: getResourceBuildingCost(level, 10),
|
||||
stone: getResourceBuildingCost(level, 15),
|
||||
iron: getResourceBuildingCost(level, 20),
|
||||
food: getResourceBuildingCost(level, 5),
|
||||
};
|
||||
},
|
||||
timeToBuild: getStandardTimeToBuild,
|
||||
@ -258,4 +259,34 @@ export default [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'palace',
|
||||
name: 'Palace',
|
||||
maxLevel: 20,
|
||||
unique: true,
|
||||
cost: (level: number) => {
|
||||
return {
|
||||
wood: getStorageBuildingCost(level, 200),
|
||||
stone: getStorageBuildingCost(level, 240),
|
||||
iron: getStorageBuildingCost(level, 110),
|
||||
food: getStorageBuildingCost(level, 70),
|
||||
};
|
||||
},
|
||||
timeToBuild: getStandardTimeToBuild,
|
||||
behavior: {
|
||||
production: (V: VillageState, self: BuildingType) => {
|
||||
const prod = getEmptyResources();
|
||||
prod.food = self.level * -30;
|
||||
return prod;
|
||||
},
|
||||
triggers: {
|
||||
onLevelUp: (V: VillageState, self: BuildingType) => {
|
||||
if (self.level === 10 || self.level === 20) {
|
||||
// Create a new hero.
|
||||
V.heroes.push(createHero());
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
12
src/data/heroes.ts
Normal file
12
src/data/heroes.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export const NAMES = [
|
||||
'Adosinda',
|
||||
'Garsendis',
|
||||
'Amalfriede',
|
||||
'Seda',
|
||||
'Amalgard',
|
||||
'Samocenus',
|
||||
'Licno',
|
||||
'Vercombogious',
|
||||
'Elusconos',
|
||||
'Excingullus',
|
||||
];
|
@ -4,7 +4,7 @@
|
||||
import moves from "../moves";
|
||||
import showBuildingCreator from "../stores/showBuildingCreator";
|
||||
import { canPayBuildingCost } from "../utils";
|
||||
import village from "../village";
|
||||
import village, { type VillageState } from "../village";
|
||||
import Cost from "./Cost.svelte";
|
||||
|
||||
function close() {
|
||||
@ -22,7 +22,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
const constructible = buildings.filter(b => !b.autoBuilt).map(b =>{
|
||||
$: constructible = buildings
|
||||
.filter(b => !b.autoBuilt)
|
||||
.filter(b => !(b.unique && $village.buildings.some(b2 => b2.type === b.type)))
|
||||
.map(b =>{
|
||||
const building = createBuilding(b.type);
|
||||
building.level = 0;
|
||||
return building;
|
||||
|
@ -7,10 +7,10 @@
|
||||
import { flip } from "svelte/animate";
|
||||
|
||||
|
||||
$: isQuestStarted = $village.quests.some(q => q.started);
|
||||
$: availableHeroes = $village.heroes.filter(h => !$village.quests.some(q => q.hero === h.id));
|
||||
|
||||
function startQuest(id: number) {
|
||||
moves.startQuest(id);
|
||||
function startQuest(id: number, heroId: number) {
|
||||
moves.startQuest(id, heroId);
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -31,12 +31,15 @@
|
||||
{ Math.ceil((quest.remainingTime || 0) / 1000) }
|
||||
{ :else }
|
||||
{ quest.duration }
|
||||
{ #each availableHeroes as hero }
|
||||
<button
|
||||
on:click={ () => startQuest(quest.id) }
|
||||
disabled={ isQuestStarted }
|
||||
on:click={ () => startQuest(quest.id, hero.id) }
|
||||
>
|
||||
Start quest
|
||||
Send { hero.name }
|
||||
</button>
|
||||
{ :else }
|
||||
<span>No heroes available</span>
|
||||
{ /each }
|
||||
{ /if }
|
||||
</p>
|
||||
</div>
|
||||
|
@ -17,6 +17,9 @@
|
||||
</script>
|
||||
|
||||
<section>
|
||||
{ #each $village.heroes as hero }
|
||||
<p>{ hero.name }</p>
|
||||
{ /each }
|
||||
{ #each currentUnits as unit }
|
||||
<p>{ unit.name }: { getRemainingUnitCount($village, unit.type) } / { unit.count }</p>
|
||||
{ /each }
|
||||
|
@ -1,12 +1,23 @@
|
||||
import type { VillageState } from "../village";
|
||||
|
||||
export default function startQuest(V: VillageState, questId: number) {
|
||||
export default function startQuest(V: VillageState, questId: number, heroId: number) {
|
||||
const quest = V.quests.find(q => q.id === questId);
|
||||
if (!quest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hero = V.heroes.find(h => h.id === heroId);
|
||||
if (!hero) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the hero is not already on a quest.
|
||||
if (V.quests.some(q => q.hero === heroId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
quest.started = true;
|
||||
quest.hero = heroId;
|
||||
quest.remainingTime = quest.duration * 1000;
|
||||
|
||||
return true;
|
||||
|
@ -24,6 +24,7 @@ export interface BuildingSource {
|
||||
name: string;
|
||||
type: string;
|
||||
autoBuilt?: boolean;
|
||||
unique?: boolean;
|
||||
maxLevel: number;
|
||||
cost: (level: number) => CostType;
|
||||
timeToBuild: (level: number) => number;
|
||||
@ -35,6 +36,9 @@ export interface BuildingSource {
|
||||
recruitmentTime: Function;
|
||||
};
|
||||
constructionTimeReductionPerLevel?: number;
|
||||
triggers?: {
|
||||
onLevelUp?: Function;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -101,6 +105,7 @@ export type RegionType = OasisType | BourgadeType;
|
||||
|
||||
export interface HeroType {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
|
||||
@ -110,5 +115,6 @@ export interface QuestType {
|
||||
reward: ResourcesType;
|
||||
level: number;
|
||||
started: boolean;
|
||||
hero?: number;
|
||||
remainingTime?: number;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ export default function update(timestamp: number) {
|
||||
if (b.state.upgrade.remainingTime <= 0) {
|
||||
b.level++;
|
||||
b.state.upgrade.isUpgrading = false;
|
||||
b.behavior.triggers?.onLevelUp?.(V, b);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
import { createBuilding } from "./create";
|
||||
import { createBuilding, createHero, createQuest } from "./create";
|
||||
import worldmap from "./data/worldmap";
|
||||
import { getTilesAtDistance, Hex } from "./hexgrid";
|
||||
import type { BuildingType, QuestType, RegionType, ResourcesType } from "./types";
|
||||
import type { BuildingType, HeroType, QuestType, RegionType, ResourcesType } from "./types";
|
||||
import { getKeysAsNumbers, shuffle } from "./utils";
|
||||
import { createQuest } from "./quests";
|
||||
|
||||
|
||||
type Board = {
|
||||
@ -24,6 +23,7 @@ export interface VillageState {
|
||||
villageTiles: Board;
|
||||
outsideTiles: Board;
|
||||
worldmap: RegionType[];
|
||||
heroes: HeroType[];
|
||||
quests: QuestType[];
|
||||
victory: boolean;
|
||||
}
|
||||
@ -103,6 +103,9 @@ function getInitialState() {
|
||||
villageTiles: getInitialVillageBoard(),
|
||||
outsideTiles: getInitialOutsideBoard(),
|
||||
worldmap: getInitialWorldmap(),
|
||||
heroes: [
|
||||
createHero(),
|
||||
],
|
||||
quests: getInitialQuests(),
|
||||
victory: false,
|
||||
};
|
||||
@ -131,7 +134,7 @@ function getInitialState() {
|
||||
}
|
||||
const newBuilding = createBuilding(type);
|
||||
newBuilding.tile = new Hex(x, y);
|
||||
newBuilding.level = 1; //newBuilding.type === 'field' ? 1 : 10;
|
||||
newBuilding.level = 20; //newBuilding.type === 'field' ? 1 : 10;
|
||||
state.outsideTiles[y][x] = newBuilding.id;
|
||||
state.buildings.push(newBuilding);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user