@its-not-rocket-science/ananke 0.1.44 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -5
- package/dist/src/bridge/bridge-engine.js +0 -1
- package/dist/src/competence/acoustic.d.ts +1 -1
- package/dist/src/competence/acoustic.js +1 -7
- package/dist/src/competence/catalogue.js +1 -1
- package/dist/src/competence/engineering.js +0 -2
- package/dist/src/competence/framework.js +2 -4
- package/dist/src/competence/interspecies.js +0 -2
- package/dist/src/competence/language.js +0 -2
- package/dist/src/competence/naturalist.js +1 -1
- package/dist/src/crafting/index.d.ts +2 -2
- package/dist/src/crafting/index.js +6 -7
- package/dist/src/crafting/manufacturing.d.ts +11 -15
- package/dist/src/crafting/manufacturing.js +7 -11
- package/dist/src/crafting/materials.d.ts +1 -1
- package/dist/src/crafting/materials.js +12 -4
- package/dist/src/crafting/recipes.d.ts +1 -1
- package/dist/src/crafting/recipes.js +1 -1
- package/dist/src/item-durability.d.ts +1 -1
- package/dist/src/item-durability.js +1 -1
- package/dist/src/legend.d.ts +1 -1
- package/dist/src/legend.js +1 -1
- package/dist/src/modding.d.ts +11 -11
- package/dist/src/modding.js +10 -10
- package/dist/src/monetary.d.ts +104 -0
- package/dist/src/monetary.js +158 -0
- package/dist/src/narrative-render.js +3 -3
- package/dist/src/quest-generators.js +4 -4
- package/dist/src/settlement-services.js +2 -2
- package/dist/src/settlement.d.ts +1 -1
- package/dist/src/settlement.js +2 -2
- package/dist/src/sim/ai/behavior-trees.d.ts +2 -2
- package/dist/src/sim/ai/behavior-trees.js +2 -2
- package/dist/src/sim/bodyplan.d.ts +1 -1
- package/dist/src/sim/kernel.js +1 -1
- package/dist/src/sim/step/morale.js +1 -1
- package/dist/src/wonders.d.ts +125 -0
- package/dist/src/wonders.js +264 -0
- package/package.json +9 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// src/wonders.ts — Phase 100: Wonders & Monuments
|
|
2
|
+
//
|
|
3
|
+
// Unique prestige constructions that define great civilisations — pyramids,
|
|
4
|
+
// colosseums, grand libraries, great walls, harbours, aqueducts, temples.
|
|
5
|
+
// Unlike Phase-89 functional infrastructure (roads, markets, apothecaries),
|
|
6
|
+
// wonders are one-of-a-kind, take years to complete, and give civilisation-
|
|
7
|
+
// level bonuses to stability, morale, research, defence, trade, and health.
|
|
8
|
+
//
|
|
9
|
+
// Design:
|
|
10
|
+
// - Pure data layer — no Entity fields, no kernel changes.
|
|
11
|
+
// - `WonderProject` tracks construction progress; `Wonder` is the completed record.
|
|
12
|
+
// - Progress = investedCost_cu / WONDER_BASE_COST_CU; completion when progress_Q = SCALE.Q.
|
|
13
|
+
// - `WonderEffects` is an advisory bundle; callers pass fields into Phases 88–93.
|
|
14
|
+
// - Damaged wonders (Phase-96 earthquake / Phase-93 siege) provide half effects
|
|
15
|
+
// until repaired at half the base cost.
|
|
16
|
+
// - Only one wonder of each type per polity (enforced by naming convention;
|
|
17
|
+
// host is responsible for uniqueness).
|
|
18
|
+
//
|
|
19
|
+
// Integration:
|
|
20
|
+
// Phase 88 (Epidemic): epidemicResistance_Q adds to healthCapacity_Q (aqueduct).
|
|
21
|
+
// Phase 90 (Unrest): unrestReduction_Q reduces computeUnrestLevel (colosseum, temple).
|
|
22
|
+
// Phase 91 (Research): researchPointBonus adds daily research points (library).
|
|
23
|
+
// Phase 92 (Taxation): tradeIncomeBonus_Q scales trade income (harbour).
|
|
24
|
+
// Phase 93 (Military Camp): defenseBonus_Q adds to defender strength (great_wall).
|
|
25
|
+
// Phase 96 (Climate): earthquake → caller calls damageWonder().
|
|
26
|
+
import { q, SCALE, clampQ, mulDiv } from "./units.js";
|
|
27
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Total treasury cost to construct each wonder type [cu].
|
|
30
|
+
* Grand library is fastest; great pyramid is a generational project.
|
|
31
|
+
*/
|
|
32
|
+
export const WONDER_BASE_COST_CU = {
|
|
33
|
+
grand_library: 150_000,
|
|
34
|
+
aqueduct_system: 200_000,
|
|
35
|
+
grand_temple: 250_000,
|
|
36
|
+
grand_harbour: 300_000,
|
|
37
|
+
colosseum: 500_000,
|
|
38
|
+
great_wall: 600_000,
|
|
39
|
+
great_pyramid: 1_000_000,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Estimated build time in days at average investment rate.
|
|
43
|
+
* Informational only — actual duration depends on how fast the host invests.
|
|
44
|
+
*/
|
|
45
|
+
export const WONDER_TYPICAL_DAYS = {
|
|
46
|
+
grand_library: 180,
|
|
47
|
+
aqueduct_system: 365,
|
|
48
|
+
grand_temple: 365,
|
|
49
|
+
grand_harbour: 365,
|
|
50
|
+
colosseum: 730,
|
|
51
|
+
great_wall: 1095,
|
|
52
|
+
great_pyramid: 3650,
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Full effects for each wonder type at q(1.0) effectiveness.
|
|
56
|
+
* Damaged wonders multiply each field by `WONDER_DAMAGED_EFFECT_MUL`.
|
|
57
|
+
*/
|
|
58
|
+
export const WONDER_BASE_EFFECTS = {
|
|
59
|
+
great_pyramid: {
|
|
60
|
+
stabilityBonus_Q: q(0.08), // generational prestige
|
|
61
|
+
moraleBonus_Q: q(0.05),
|
|
62
|
+
researchPointBonus: 0,
|
|
63
|
+
unrestReduction_Q: 0,
|
|
64
|
+
tradeIncomeBonus_Q: 0,
|
|
65
|
+
defenseBonus_Q: 0,
|
|
66
|
+
epidemicResistance_Q: 0,
|
|
67
|
+
},
|
|
68
|
+
colosseum: {
|
|
69
|
+
stabilityBonus_Q: q(0.03),
|
|
70
|
+
moraleBonus_Q: q(0.10), // entertainment
|
|
71
|
+
researchPointBonus: 0,
|
|
72
|
+
unrestReduction_Q: q(0.12), // bread and circuses
|
|
73
|
+
tradeIncomeBonus_Q: 0,
|
|
74
|
+
defenseBonus_Q: 0,
|
|
75
|
+
epidemicResistance_Q: 0,
|
|
76
|
+
},
|
|
77
|
+
grand_library: {
|
|
78
|
+
stabilityBonus_Q: q(0.03),
|
|
79
|
+
moraleBonus_Q: q(0.02),
|
|
80
|
+
researchPointBonus: 3, // +3 RP/day
|
|
81
|
+
unrestReduction_Q: 0,
|
|
82
|
+
tradeIncomeBonus_Q: 0,
|
|
83
|
+
defenseBonus_Q: 0,
|
|
84
|
+
epidemicResistance_Q: 0,
|
|
85
|
+
},
|
|
86
|
+
great_wall: {
|
|
87
|
+
stabilityBonus_Q: q(0.05),
|
|
88
|
+
moraleBonus_Q: 0,
|
|
89
|
+
researchPointBonus: 0,
|
|
90
|
+
unrestReduction_Q: 0,
|
|
91
|
+
tradeIncomeBonus_Q: 0,
|
|
92
|
+
defenseBonus_Q: q(0.20), // major defensive advantage
|
|
93
|
+
epidemicResistance_Q: 0,
|
|
94
|
+
},
|
|
95
|
+
grand_harbour: {
|
|
96
|
+
stabilityBonus_Q: 0,
|
|
97
|
+
moraleBonus_Q: q(0.03),
|
|
98
|
+
researchPointBonus: 0,
|
|
99
|
+
unrestReduction_Q: 0,
|
|
100
|
+
tradeIncomeBonus_Q: q(0.25), // major trade multiplier
|
|
101
|
+
defenseBonus_Q: 0,
|
|
102
|
+
epidemicResistance_Q: 0,
|
|
103
|
+
},
|
|
104
|
+
aqueduct_system: {
|
|
105
|
+
stabilityBonus_Q: q(0.02),
|
|
106
|
+
moraleBonus_Q: q(0.04), // quality of life
|
|
107
|
+
researchPointBonus: 0,
|
|
108
|
+
unrestReduction_Q: 0,
|
|
109
|
+
tradeIncomeBonus_Q: 0,
|
|
110
|
+
defenseBonus_Q: 0,
|
|
111
|
+
epidemicResistance_Q: q(0.15), // clean water reduces disease
|
|
112
|
+
},
|
|
113
|
+
grand_temple: {
|
|
114
|
+
stabilityBonus_Q: q(0.06), // divine legitimacy
|
|
115
|
+
moraleBonus_Q: q(0.08),
|
|
116
|
+
researchPointBonus: 0,
|
|
117
|
+
unrestReduction_Q: q(0.08),
|
|
118
|
+
tradeIncomeBonus_Q: 0,
|
|
119
|
+
defenseBonus_Q: 0,
|
|
120
|
+
epidemicResistance_Q: 0,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Effect multiplier for a damaged wonder [0, SCALE.Q].
|
|
125
|
+
* Damaged wonders still provide partial benefit; repair restores full effects.
|
|
126
|
+
*/
|
|
127
|
+
export const WONDER_DAMAGED_EFFECT_MUL = q(0.50);
|
|
128
|
+
/**
|
|
129
|
+
* Treasury cost to repair a damaged wonder, as a fraction of `WONDER_BASE_COST_CU`.
|
|
130
|
+
* Repair = `round(baseCost × WONDER_REPAIR_COST_FRAC / SCALE.Q)`.
|
|
131
|
+
*/
|
|
132
|
+
export const WONDER_REPAIR_COST_FRAC = q(0.25);
|
|
133
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
134
|
+
/** Create a new wonder construction project. */
|
|
135
|
+
export function createWonderProject(projectId, polityId, type, startTick) {
|
|
136
|
+
return {
|
|
137
|
+
projectId,
|
|
138
|
+
polityId,
|
|
139
|
+
type,
|
|
140
|
+
progress_Q: 0,
|
|
141
|
+
investedCost_cu: 0,
|
|
142
|
+
startTick,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// ── Construction ──────────────────────────────────────────────────────────────
|
|
146
|
+
/**
|
|
147
|
+
* Invest treasury into a wonder project.
|
|
148
|
+
*
|
|
149
|
+
* Deducts up to `contribution_cu` from `polity.treasury_cu` (capped by available
|
|
150
|
+
* treasury and remaining cost), advances `progress_Q`, and returns the new progress.
|
|
151
|
+
*
|
|
152
|
+
* Does not auto-complete — call `isWonderProjectComplete` then `completeWonder`.
|
|
153
|
+
*/
|
|
154
|
+
export function contributeToWonder(project, polity, contribution_cu) {
|
|
155
|
+
const totalCost = WONDER_BASE_COST_CU[project.type];
|
|
156
|
+
const remaining = Math.max(0, totalCost - project.investedCost_cu);
|
|
157
|
+
const actual = Math.min(contribution_cu, polity.treasury_cu, remaining);
|
|
158
|
+
polity.treasury_cu -= actual;
|
|
159
|
+
project.investedCost_cu += actual;
|
|
160
|
+
project.progress_Q = clampQ(Math.round(project.investedCost_cu * SCALE.Q / totalCost), 0, SCALE.Q);
|
|
161
|
+
return project.progress_Q;
|
|
162
|
+
}
|
|
163
|
+
/** Return `true` when the project has reached full completion. */
|
|
164
|
+
export function isWonderProjectComplete(project) {
|
|
165
|
+
return project.progress_Q >= SCALE.Q;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Finalise a completed project into a standing `Wonder`.
|
|
169
|
+
*
|
|
170
|
+
* The caller is responsible for checking `isWonderProjectComplete` first.
|
|
171
|
+
*/
|
|
172
|
+
export function completeWonder(project, tick) {
|
|
173
|
+
return {
|
|
174
|
+
wonderId: project.projectId,
|
|
175
|
+
polityId: project.polityId,
|
|
176
|
+
type: project.type,
|
|
177
|
+
completedAtTick: tick,
|
|
178
|
+
damaged: false,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// ── Damage & repair ───────────────────────────────────────────────────────────
|
|
182
|
+
/**
|
|
183
|
+
* Mark a wonder as damaged (earthquake, siege).
|
|
184
|
+
* Damaged wonders yield `WONDER_DAMAGED_EFFECT_MUL` fraction of full effects.
|
|
185
|
+
*/
|
|
186
|
+
export function damageWonder(wonder) {
|
|
187
|
+
wonder.damaged = true;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Repair a damaged wonder, spending `WONDER_REPAIR_COST_FRAC` of base cost.
|
|
191
|
+
*
|
|
192
|
+
* Mutates `polity.treasury_cu` and clears `wonder.damaged`.
|
|
193
|
+
* Returns `true` if repaired; `false` if the polity lacked funds.
|
|
194
|
+
* No-op if wonder is not damaged.
|
|
195
|
+
*/
|
|
196
|
+
export function repairWonder(wonder, polity) {
|
|
197
|
+
if (!wonder.damaged)
|
|
198
|
+
return true;
|
|
199
|
+
const baseCost = WONDER_BASE_COST_CU[wonder.type];
|
|
200
|
+
const repairCost = Math.round(mulDiv(baseCost, WONDER_REPAIR_COST_FRAC, SCALE.Q));
|
|
201
|
+
if (polity.treasury_cu < repairCost)
|
|
202
|
+
return false;
|
|
203
|
+
polity.treasury_cu -= repairCost;
|
|
204
|
+
wonder.damaged = false;
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
// ── Effect computation ────────────────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Compute the `WonderEffects` advisory bundle for a single wonder.
|
|
210
|
+
*
|
|
211
|
+
* Damaged wonders: each numeric field is scaled by `WONDER_DAMAGED_EFFECT_MUL / SCALE.Q`.
|
|
212
|
+
*/
|
|
213
|
+
export function computeWonderEffects(wonder) {
|
|
214
|
+
const base = WONDER_BASE_EFFECTS[wonder.type];
|
|
215
|
+
if (!wonder.damaged)
|
|
216
|
+
return { ...base };
|
|
217
|
+
const m = WONDER_DAMAGED_EFFECT_MUL;
|
|
218
|
+
return {
|
|
219
|
+
stabilityBonus_Q: mulDiv(base.stabilityBonus_Q, m, SCALE.Q),
|
|
220
|
+
moraleBonus_Q: mulDiv(base.moraleBonus_Q, m, SCALE.Q),
|
|
221
|
+
researchPointBonus: Math.round(base.researchPointBonus * m / SCALE.Q),
|
|
222
|
+
unrestReduction_Q: mulDiv(base.unrestReduction_Q, m, SCALE.Q),
|
|
223
|
+
tradeIncomeBonus_Q: mulDiv(base.tradeIncomeBonus_Q, m, SCALE.Q),
|
|
224
|
+
defenseBonus_Q: mulDiv(base.defenseBonus_Q, m, SCALE.Q),
|
|
225
|
+
epidemicResistance_Q: mulDiv(base.epidemicResistance_Q, m, SCALE.Q),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Aggregate effects from multiple wonders.
|
|
230
|
+
*
|
|
231
|
+
* Q fields are summed and clamped to SCALE.Q.
|
|
232
|
+
* `researchPointBonus` is summed without capping.
|
|
233
|
+
*/
|
|
234
|
+
export function aggregateWonderEffects(wonders) {
|
|
235
|
+
let stab = 0, morale = 0, rp = 0, unrest = 0, trade = 0, def = 0, epi = 0;
|
|
236
|
+
for (const w of wonders) {
|
|
237
|
+
const fx = computeWonderEffects(w);
|
|
238
|
+
stab += fx.stabilityBonus_Q;
|
|
239
|
+
morale += fx.moraleBonus_Q;
|
|
240
|
+
rp += fx.researchPointBonus;
|
|
241
|
+
unrest += fx.unrestReduction_Q;
|
|
242
|
+
trade += fx.tradeIncomeBonus_Q;
|
|
243
|
+
def += fx.defenseBonus_Q;
|
|
244
|
+
epi += fx.epidemicResistance_Q;
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
stabilityBonus_Q: clampQ(stab, 0, SCALE.Q),
|
|
248
|
+
moraleBonus_Q: clampQ(morale, 0, SCALE.Q),
|
|
249
|
+
researchPointBonus: rp,
|
|
250
|
+
unrestReduction_Q: clampQ(unrest, 0, SCALE.Q),
|
|
251
|
+
tradeIncomeBonus_Q: clampQ(trade, 0, SCALE.Q),
|
|
252
|
+
defenseBonus_Q: clampQ(def, 0, SCALE.Q),
|
|
253
|
+
epidemicResistance_Q: clampQ(epi, 0, SCALE.Q),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
257
|
+
/** Return `true` when the wonder is standing and undamaged. */
|
|
258
|
+
export function isWonderIntact(wonder) {
|
|
259
|
+
return !wonder.damaged;
|
|
260
|
+
}
|
|
261
|
+
/** Compute treasury cost to repair a damaged wonder [cu]. */
|
|
262
|
+
export function computeRepairCost(type) {
|
|
263
|
+
return Math.round(mulDiv(WONDER_BASE_COST_CU[type], WONDER_REPAIR_COST_FRAC, SCALE.Q));
|
|
264
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@its-not-rocket-science/ananke",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deterministic lockstep-friendly SI-units RPG/physics core (fixed-point TS)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -162,6 +162,14 @@
|
|
|
162
162
|
"./mercenaries": {
|
|
163
163
|
"import": "./dist/src/mercenaries.js",
|
|
164
164
|
"types": "./dist/src/mercenaries.d.ts"
|
|
165
|
+
},
|
|
166
|
+
"./wonders": {
|
|
167
|
+
"import": "./dist/src/wonders.js",
|
|
168
|
+
"types": "./dist/src/wonders.d.ts"
|
|
169
|
+
},
|
|
170
|
+
"./monetary": {
|
|
171
|
+
"import": "./dist/src/monetary.js",
|
|
172
|
+
"types": "./dist/src/monetary.d.ts"
|
|
165
173
|
}
|
|
166
174
|
},
|
|
167
175
|
"files": [
|