2024-10-25 17:03:32 +00:00
|
|
|
import units from "./data/units";
|
2024-11-05 10:59:25 +00:00
|
|
|
import type { BuildingType, CostType, ProductionType, ResourcesType } from "./types";
|
2024-10-22 15:15:35 +00:00
|
|
|
import type { VillageState } from "./village";
|
|
|
|
|
|
|
|
|
2024-11-05 10:59:25 +00:00
|
|
|
function _reduceResources(acc: ResourcesType, item: ResourcesType): ResourcesType {
|
2024-10-22 15:15:35 +00:00
|
|
|
return {
|
|
|
|
wood: acc.wood + item.wood,
|
|
|
|
stone: acc.stone + item.stone,
|
|
|
|
iron: acc.iron + item.iron,
|
|
|
|
food: acc.food + item.food,
|
2024-11-05 10:59:25 +00:00
|
|
|
culture: acc.culture + item.culture,
|
2024-10-22 15:15:35 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-11-05 10:59:25 +00:00
|
|
|
export function getEmptyResources(): ResourcesType {
|
2024-10-22 15:15:35 +00:00
|
|
|
return {
|
|
|
|
wood: 0,
|
|
|
|
stone: 0,
|
|
|
|
iron: 0,
|
|
|
|
food: 0,
|
2024-11-05 10:59:25 +00:00
|
|
|
culture: 0,
|
2024-10-22 15:15:35 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-11-05 10:59:25 +00:00
|
|
|
export function getProduction(villageState: VillageState): ResourcesType {
|
2024-11-05 10:35:32 +00:00
|
|
|
let production = getEmptyResources();
|
|
|
|
|
|
|
|
// Add buildings production and intake.
|
|
|
|
production = villageState.buildings
|
2024-10-24 17:27:33 +00:00
|
|
|
.filter(b => b.behavior.production && b.level > 0)
|
2024-10-22 15:15:35 +00:00
|
|
|
.map(b => {
|
|
|
|
if (b.behavior.production) {
|
|
|
|
return b.behavior.production(villageState, b);
|
|
|
|
}
|
|
|
|
})
|
2024-11-05 10:35:32 +00:00
|
|
|
.reduce(_reduceResources, production);
|
|
|
|
|
2024-11-05 10:59:25 +00:00
|
|
|
// Add units production and intake.
|
2024-11-05 10:35:32 +00:00
|
|
|
['philosopher'].forEach(type => {
|
|
|
|
const unit = getUnitSource(type);
|
|
|
|
const unitCount = villageState.units[type] || 0;
|
2024-11-05 10:59:25 +00:00
|
|
|
|
|
|
|
// Add food intake.
|
2024-11-05 10:35:32 +00:00
|
|
|
const intakePerMinute = unit.behavior.foodIntakePerMinute * unitCount;
|
|
|
|
production.food -= intakePerMinute;
|
2024-11-05 10:59:25 +00:00
|
|
|
|
|
|
|
// Add culture production for Philosophers.
|
|
|
|
if (type === 'philosopher') {
|
|
|
|
const outputPerMinute = unit.behavior.culturePerMinute;
|
|
|
|
production.culture += outputPerMinute * unitCount;
|
|
|
|
}
|
2024-11-05 10:35:32 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return production;
|
2024-10-22 15:15:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-25 17:03:32 +00:00
|
|
|
export function getStorage(villageState: VillageState): ProductionType {
|
2024-10-22 15:15:35 +00:00
|
|
|
return villageState.buildings
|
2024-10-24 17:27:33 +00:00
|
|
|
.filter(b => b.behavior.storage && b.level > 0)
|
2024-10-22 15:15:35 +00:00
|
|
|
.map(b => {
|
|
|
|
if (b.behavior.storage) {
|
|
|
|
return b.behavior.storage(villageState, b);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.reduce(_reduceResources, getEmptyResources());
|
|
|
|
}
|
2024-10-24 09:32:31 +00:00
|
|
|
|
|
|
|
|
2024-10-25 17:03:32 +00:00
|
|
|
export function getBuilding(V: VillageState, buildingId: number): BuildingType {
|
2024-10-24 13:12:21 +00:00
|
|
|
const building = V.buildings.find(b => b.id === buildingId);
|
|
|
|
if (!building) {
|
|
|
|
throw new Error(`Cannot find building with id "${buildingId}"`);
|
|
|
|
}
|
|
|
|
return building;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-25 17:03:32 +00:00
|
|
|
export function getUnitSource(unitType: string) {
|
|
|
|
const unit = units.find(u => u.type === unitType);
|
|
|
|
if (unit === undefined) {
|
|
|
|
throw new Error(`Unknown unit type: "${unitType}"`);
|
|
|
|
}
|
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-24 09:32:31 +00:00
|
|
|
export function getKeysAsNumbers(dict: Object): Array<number> {
|
|
|
|
return Object.keys(dict).map(i => parseInt(i)).sort((a, b) => a - b);
|
|
|
|
}
|
2024-10-24 13:31:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an array of shuffled values, using a version of the
|
|
|
|
* [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
|
|
|
|
*
|
|
|
|
* @since 0.1.0
|
|
|
|
* @category Array
|
|
|
|
* @param {Array} array The array to shuffle.
|
|
|
|
* @returns {Array} Returns the new shuffled array.
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* shuffle([1, 2, 3, 4])
|
|
|
|
* // => [4, 1, 3, 2]
|
|
|
|
*/
|
|
|
|
export function shuffle<T>(array: Array<T>): Array<T> {
|
|
|
|
const length = array.length;
|
|
|
|
if (!length) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
let index = -1;
|
|
|
|
const lastIndex = length - 1;
|
|
|
|
const result = [ ...array ];
|
|
|
|
while (++index < length) {
|
|
|
|
const rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
|
|
|
|
[ result[rand], result[index] ] = [ result[index], result[rand] ];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2024-10-24 17:27:33 +00:00
|
|
|
|
|
|
|
|
2024-10-25 17:03:32 +00:00
|
|
|
export function enqueueBuilding(V: VillageState, building: BuildingType) {
|
2024-10-24 17:27:33 +00:00
|
|
|
const ongoingUpgrades = V.queue.filter(q => q.id === building.id);
|
|
|
|
const level = building.level + 1 + ongoingUpgrades.length;
|
|
|
|
const remainingTime = 1000 * level;
|
|
|
|
|
|
|
|
V.queue.push({
|
|
|
|
id: building.id,
|
|
|
|
remainingTime,
|
|
|
|
});
|
|
|
|
}
|
2024-11-04 18:04:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
export function getBuildingUpgradeCost(V: VillageState, building: BuildingType): CostType {
|
|
|
|
const ongoingUpgrades = V.queue.filter(q => q.id === building.id);
|
|
|
|
const level = building.level + ongoingUpgrades.length + 1;
|
|
|
|
return building.cost(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function canPayBuildingCost(V: VillageState, building: BuildingType): boolean {
|
|
|
|
const cost = getBuildingUpgradeCost(V, building);
|
|
|
|
|
|
|
|
return !(
|
|
|
|
cost.wood > V.resources.wood
|
|
|
|
|| cost.stone > V.resources.stone
|
|
|
|
|| cost.iron > V.resources.iron
|
|
|
|
|| cost.food > V.resources.food
|
|
|
|
);
|
|
|
|
}
|