@datamoc/mw_games 0.5.0 → 0.5.2

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.
Files changed (52) hide show
  1. package/dist/actors/EntityTemplate.d.ts +127 -0
  2. package/dist/actors/EntityTemplate.d.ts.map +1 -0
  3. package/dist/actors/EntityTemplate.js +117 -0
  4. package/dist/actors/EntityTemplate.js.map +1 -0
  5. package/dist/actors/index.d.ts +2 -0
  6. package/dist/actors/index.d.ts.map +1 -1
  7. package/dist/actors/index.js +1 -0
  8. package/dist/actors/index.js.map +1 -1
  9. package/dist/board/HexSkirmish.d.ts +213 -0
  10. package/dist/board/HexSkirmish.d.ts.map +1 -0
  11. package/dist/board/HexSkirmish.js +271 -0
  12. package/dist/board/HexSkirmish.js.map +1 -0
  13. package/dist/board/index.d.ts +2 -0
  14. package/dist/board/index.d.ts.map +1 -1
  15. package/dist/board/index.js +1 -0
  16. package/dist/board/index.js.map +1 -1
  17. package/dist/core/Csv.d.ts +64 -0
  18. package/dist/core/Csv.d.ts.map +1 -0
  19. package/dist/core/Csv.js +164 -0
  20. package/dist/core/Csv.js.map +1 -0
  21. package/dist/core/Registry.d.ts +31 -0
  22. package/dist/core/Registry.d.ts.map +1 -0
  23. package/dist/core/Registry.js +46 -0
  24. package/dist/core/Registry.js.map +1 -0
  25. package/dist/core/SceneComponents.d.ts +67 -0
  26. package/dist/core/SceneComponents.d.ts.map +1 -0
  27. package/dist/core/SceneComponents.js +76 -0
  28. package/dist/core/SceneComponents.js.map +1 -0
  29. package/dist/core/index.d.ts +5 -0
  30. package/dist/core/index.d.ts.map +1 -1
  31. package/dist/core/index.js +3 -0
  32. package/dist/core/index.js.map +1 -1
  33. package/dist/mw_games.global.js +91 -90
  34. package/dist/rpg/Quest.d.ts +45 -0
  35. package/dist/rpg/Quest.d.ts.map +1 -1
  36. package/dist/rpg/Quest.js +76 -0
  37. package/dist/rpg/Quest.js.map +1 -1
  38. package/dist/rpg/index.d.ts +2 -2
  39. package/dist/rpg/index.d.ts.map +1 -1
  40. package/dist/rpg/index.js +1 -1
  41. package/dist/rpg/index.js.map +1 -1
  42. package/dist/two-d/render/Shape2D.d.ts +23 -1
  43. package/dist/two-d/render/Shape2D.d.ts.map +1 -1
  44. package/dist/two-d/render/Shape2D.js +23 -1
  45. package/dist/two-d/render/Shape2D.js.map +1 -1
  46. package/dist/two-d/render/index.d.ts +1 -1
  47. package/dist/two-d/render/index.d.ts.map +1 -1
  48. package/dist/two-d/render/index.js +1 -1
  49. package/dist/two-d/render/index.js.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { StatBlock, type Stats } from './StatBlock.ts';
2
+ import { Progression, type GrowthCurve } from './Progression.ts';
3
+ import { type AffixDef } from './Affix.ts';
4
+ import type { InventoryItem } from './Inventory.ts';
5
+ import { ReactionTable } from '../core/Reactions.ts';
6
+ /**
7
+ * One row of a hero or monster "file" - a `core.parseCSV` result, typically - turned into a
8
+ * fully wired entity: a `StatBlock` of base stats, an optional `Progression` against a named
9
+ * growth curve, an optional starting item carrying a named starting affix, and a
10
+ * `ReactionTable` already holding a low-HP rule if the row names one. The single call a game
11
+ * makes once per row, in place of the per-field glue code every one of those systems would
12
+ * otherwise need written out by hand.
13
+ *
14
+ * A handful of column names are reserved (`id`, `growth`, `level`, `startingAffix`,
15
+ * `startingItem`, `lowHpReaction`); every other column in the row becomes a base stat, so a
16
+ * game's own stat names (`attack`, `speed`, `armor`, whatever it calls them) need no
17
+ * declaring here. What each named growth curve, affix, and item actually *is* stays the
18
+ * game's own `EntityTemplateCatalog` - `mwg` resolves the reference, never invents the
19
+ * content behind it, the same boundary `AffixDef.id` already draws.
20
+ *
21
+ * Deliberately narrow: one low-HP threshold per row, not an arbitrary list of reactions -
22
+ * add a second column and a second `ReactionRule` in a game's own code the moment a second
23
+ * one is actually needed, rather than a general reaction-list column format speculatively
24
+ * built ahead of that need.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { parseCSV } from '@datamoc/mw_games/core';
29
+ * import { buildEntities, type EntityTemplateRow } from '@datamoc/mw_games/actors';
30
+ *
31
+ * const csv = `id,attack,speed,growth,startingAffix,startingItem,lowHpReaction
32
+ * fireling,10,8,steep,blazing,ember_charm,0.25`;
33
+ *
34
+ * const rows = parseCSV<EntityTemplateRow>(csv, {
35
+ * columns: { attack: 'number', speed: 'number', lowHpReaction: 'number' },
36
+ * });
37
+ *
38
+ * const monsters = buildEntities(rows, {
39
+ * growthCurves: { steep: { maxLevel: 20, experienceFor: (level) => level * level * 10 } },
40
+ * affixes: { blazing: { id: 'blazing', trigger: 'strike', weight: 1 } },
41
+ * items: { ember_charm: () => ({ id: 'ember_charm', quantity: 1 }) },
42
+ * }, (entity) => console.log(`${entity.id} panics at low hp`));
43
+ *
44
+ * monsters[0].reactions.check({ hp: 2, maxHp: 10 }); // fires the callback above
45
+ * ```
46
+ */
47
+ export interface EntityTemplateRow {
48
+ id: string;
49
+ growth?: string;
50
+ level?: number;
51
+ startingAffix?: string;
52
+ startingItem?: string;
53
+ lowHpReaction?: number;
54
+ /** any other column is a base stat by that name; must be a number */
55
+ [stat: string]: unknown;
56
+ }
57
+ /** what each row's named references actually mean - entirely the game's own content */
58
+ export interface EntityTemplateCatalog {
59
+ growthCurves?: Record<string, GrowthCurve>;
60
+ affixes?: Record<string, AffixDef>;
61
+ /** a factory, not a shared instance, so every built entity gets its own item object */
62
+ items?: Record<string, () => InventoryItem>;
63
+ }
64
+ export interface BuiltEntity {
65
+ id: string;
66
+ stats: StatBlock;
67
+ progression?: Progression;
68
+ item?: InventoryItem;
69
+ reactions: ReactionTable<{
70
+ hp: number;
71
+ maxHp: number;
72
+ }>;
73
+ }
74
+ /** what a `BuiltEntity` actually needs saved - everything else is the row/catalog, definitions supplied fresh on load */
75
+ export interface EntitySaveState {
76
+ stats: {
77
+ base: Stats;
78
+ };
79
+ progression?: {
80
+ level: number;
81
+ experience: number;
82
+ };
83
+ item?: InventoryItem;
84
+ }
85
+ /**
86
+ * @example
87
+ * ```ts
88
+ * import { buildEntity } from '@datamoc/mw_games/actors';
89
+ *
90
+ * const goblin = buildEntity({ id: 'goblin', attack: 4, speed: 3 }, {});
91
+ * console.log(goblin.stats.get('attack')); // 4
92
+ * ```
93
+ */
94
+ export declare function buildEntity(row: EntityTemplateRow, catalog?: EntityTemplateCatalog, onLowHp?: (entity: BuiltEntity) => void): BuiltEntity;
95
+ /** `buildEntity` over every row - the usual shape once a whole file's worth loads at once */
96
+ export declare function buildEntities(rows: readonly EntityTemplateRow[], catalog?: EntityTemplateCatalog, onLowHp?: (entity: BuiltEntity) => void): BuiltEntity[];
97
+ /**
98
+ * A `BuiltEntity`'s mutable state, in the same "definitions supplied fresh on load" shape
99
+ * `StatBlock.toJSON`/`Progression.toJSON` already draw: base stat values, level and
100
+ * experience, and the carried item, never the row or catalog that produced them.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * import { buildEntity, toEntitySaveState, fromEntitySaveState, type EntityTemplateCatalog } from '@datamoc/mw_games/actors';
105
+ *
106
+ * const catalog: EntityTemplateCatalog = {
107
+ * growthCurves: { steep: { maxLevel: 20, experienceFor: (level) => level * level * 10 } },
108
+ * };
109
+ *
110
+ * const row = { id: 'fireling', growth: 'steep', attack: 10 };
111
+ * const fireling = buildEntity(row, catalog);
112
+ * fireling.stats.setBase('attack', 12);
113
+ * fireling.progression?.addExperience(500);
114
+ *
115
+ * const saved = toEntitySaveState(fireling); // JSON.stringify this into a save slot
116
+ * const restored = fromEntitySaveState(row, catalog, saved);
117
+ * restored.stats.get('attack'); // 12, not the row's original 10
118
+ * ```
119
+ */
120
+ export declare function toEntitySaveState(entity: BuiltEntity): EntitySaveState;
121
+ /**
122
+ * Rebuilds a `BuiltEntity` the same way `buildEntity` does - same row, same catalog, so the
123
+ * reactions and item lookups stay validated the same way - then overlays the saved base
124
+ * stats, level/experience, and carried item on top of the fresh one.
125
+ */
126
+ export declare function fromEntitySaveState(row: EntityTemplateRow, catalog: EntityTemplateCatalog, data: EntitySaveState, onLowHp?: (entity: BuiltEntity) => void): BuiltEntity;
127
+ //# sourceMappingURL=EntityTemplate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityTemplate.d.ts","sourceRoot":"","sources":["../../src/actors/EntityTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,qEAAqE;IACrE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,uFAAuF;AACvF,MAAM,WAAW,qBAAqB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEnC,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,aAAa,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,yHAAyH;AACzH,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC;IACvB,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,IAAI,CAAC,EAAE,aAAa,CAAC;CACrB;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,qBAA0B,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,WAAW,CAgC7I;AAED,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,EAAE,OAAO,GAAE,qBAA0B,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,WAAW,EAAE,CAE7J;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,CAKtE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,qBAAqB,EAC9B,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACrC,WAAW,CAab"}
@@ -0,0 +1,117 @@
1
+ import { StatBlock } from './StatBlock.js';
2
+ import { Progression } from './Progression.js';
3
+ import { applyAffix } from './Affix.js';
4
+ import { ReactionTable } from '../core/Reactions.js';
5
+ const RESERVED_KEYS = new Set(['id', 'growth', 'level', 'startingAffix', 'startingItem', 'lowHpReaction']);
6
+ /**
7
+ * @example
8
+ * ```ts
9
+ * import { buildEntity } from '@datamoc/mw_games/actors';
10
+ *
11
+ * const goblin = buildEntity({ id: 'goblin', attack: 4, speed: 3 }, {});
12
+ * console.log(goblin.stats.get('attack')); // 4
13
+ * ```
14
+ */
15
+ export function buildEntity(row, catalog = {}, onLowHp) {
16
+ const stats = new StatBlock({ base: statsFrom(row) });
17
+ let progression;
18
+ if (row.growth !== undefined) {
19
+ progression = new Progression(growthCurveFor(row, catalog), { level: row.level ?? 1 });
20
+ }
21
+ let item;
22
+ if (row.startingItem !== undefined) {
23
+ const factory = catalog.items?.[row.startingItem];
24
+ if (!factory)
25
+ throw new Error(`entity template "${row.id}": unknown starting item "${row.startingItem}"`);
26
+ item = factory();
27
+ if (row.startingAffix !== undefined) {
28
+ const affix = catalog.affixes?.[row.startingAffix];
29
+ if (!affix)
30
+ throw new Error(`entity template "${row.id}": unknown affix "${row.startingAffix}"`);
31
+ applyAffix(item, affix);
32
+ }
33
+ }
34
+ else if (row.startingAffix !== undefined) {
35
+ throw new Error(`entity template "${row.id}": a starting affix needs a starting item to carry it`);
36
+ }
37
+ const reactions = new ReactionTable();
38
+ const entity = { id: row.id, stats, progression, item, reactions };
39
+ if (row.lowHpReaction !== undefined) {
40
+ if (!onLowHp)
41
+ throw new Error(`entity template "${row.id}": names a lowHpReaction threshold but no onLowHp callback was given`);
42
+ const threshold = row.lowHpReaction;
43
+ reactions.add({ id: 'low-hp', when: (s) => s.hp / s.maxHp <= threshold, action: () => onLowHp(entity) });
44
+ }
45
+ return entity;
46
+ }
47
+ /** `buildEntity` over every row - the usual shape once a whole file's worth loads at once */
48
+ export function buildEntities(rows, catalog = {}, onLowHp) {
49
+ return rows.map((row) => buildEntity(row, catalog, onLowHp));
50
+ }
51
+ /**
52
+ * A `BuiltEntity`'s mutable state, in the same "definitions supplied fresh on load" shape
53
+ * `StatBlock.toJSON`/`Progression.toJSON` already draw: base stat values, level and
54
+ * experience, and the carried item, never the row or catalog that produced them.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { buildEntity, toEntitySaveState, fromEntitySaveState, type EntityTemplateCatalog } from '@datamoc/mw_games/actors';
59
+ *
60
+ * const catalog: EntityTemplateCatalog = {
61
+ * growthCurves: { steep: { maxLevel: 20, experienceFor: (level) => level * level * 10 } },
62
+ * };
63
+ *
64
+ * const row = { id: 'fireling', growth: 'steep', attack: 10 };
65
+ * const fireling = buildEntity(row, catalog);
66
+ * fireling.stats.setBase('attack', 12);
67
+ * fireling.progression?.addExperience(500);
68
+ *
69
+ * const saved = toEntitySaveState(fireling); // JSON.stringify this into a save slot
70
+ * const restored = fromEntitySaveState(row, catalog, saved);
71
+ * restored.stats.get('attack'); // 12, not the row's original 10
72
+ * ```
73
+ */
74
+ export function toEntitySaveState(entity) {
75
+ const state = { stats: entity.stats.toJSON() };
76
+ if (entity.progression)
77
+ state.progression = entity.progression.toJSON();
78
+ if (entity.item)
79
+ state.item = entity.item;
80
+ return state;
81
+ }
82
+ /**
83
+ * Rebuilds a `BuiltEntity` the same way `buildEntity` does - same row, same catalog, so the
84
+ * reactions and item lookups stay validated the same way - then overlays the saved base
85
+ * stats, level/experience, and carried item on top of the fresh one.
86
+ */
87
+ export function fromEntitySaveState(row, catalog, data, onLowHp) {
88
+ const entity = buildEntity(row, catalog, onLowHp);
89
+ entity.stats = StatBlock.fromJSON({ base: statsFrom(row) }, data.stats);
90
+ if (data.progression !== undefined) {
91
+ if (!entity.progression) {
92
+ throw new Error(`entity template "${row.id}": save data has progression but the row names no growth curve`);
93
+ }
94
+ entity.progression = Progression.fromJSON(growthCurveFor(row, catalog), data.progression);
95
+ }
96
+ if (data.item !== undefined)
97
+ entity.item = data.item;
98
+ return entity;
99
+ }
100
+ function growthCurveFor(row, catalog) {
101
+ const curve = catalog.growthCurves?.[row.growth ?? ''];
102
+ if (!curve)
103
+ throw new Error(`entity template "${row.id}": unknown growth curve "${row.growth}"`);
104
+ return curve;
105
+ }
106
+ function statsFrom(row) {
107
+ const stats = {};
108
+ for (const [key, value] of Object.entries(row)) {
109
+ if (RESERVED_KEYS.has(key))
110
+ continue;
111
+ if (typeof value !== 'number')
112
+ throw new Error(`entity template "${row.id}": stat "${key}" must be a number, got ${typeof value}`);
113
+ stats[key] = value;
114
+ }
115
+ return stats;
116
+ }
117
+ //# sourceMappingURL=EntityTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityTemplate.js","sourceRoot":"","sources":["../../src/actors/EntityTemplate.ts"],"names":[],"mappings":"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"}
@@ -41,4 +41,6 @@ export { AuraField } from './Aura.ts';
41
41
  export type { AuraDef, AuraParticipant } from './Aura.ts';
42
42
  export { SupportLedger } from './Support.ts';
43
43
  export type { SupportLevel, SupportChange, SupportSave } from './Support.ts';
44
+ export { buildEntity, buildEntities, toEntitySaveState, fromEntitySaveState } from './EntityTemplate.ts';
45
+ export type { EntityTemplateRow, EntityTemplateCatalog, BuiltEntity, EntitySaveState } from './EntityTemplate.ts';
44
46
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3D,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC1H,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3E,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpG,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACzG,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
@@ -20,4 +20,5 @@ export { Barrier } from './Barrier.js';
20
20
  export { assignTraits } from './Traits.js';
21
21
  export { AuraField } from './Aura.js';
22
22
  export { SupportLedger } from './Support.js';
23
+ export { buildEntity, buildEntities, toEntitySaveState, fromEntitySaveState } from './EntityTemplate.js';
23
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEpG,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEpG,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * A Wesnoth-style hex army-game rules layer: terrain that costs different movement points and
3
+ * grants a defence bonus per terrain kind, adjacent attacks that always let a surviving
4
+ * defender strike back, capturable villages that grant income and heal whoever stands on one,
5
+ * and per-side turn income built on both. Distinct from `board.Tactics` (an X-COM-style
6
+ * action-point/cover/overwatch layer, ranged attacks, no retaliation) - this is the other
7
+ * shape a hex army game needs, adjacency-only combat with a hit chance and no zone of control.
8
+ *
9
+ * `mwg` supplies the mechanism only: `SkirmishTerrain`'s move cost and defence numbers, unit
10
+ * attack/hit-chance/hp values, and income rates are entirely the game's own data, the same
11
+ * policy `board.Army`'s `armyIncome` already follows.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { startingSkirmish, setSkirmishTerrain, addSkirmishUnit, skirmishMoves, moveSkirmishUnit, skirmishAttack, endSkirmishTurn, skirmishIncome } from '@datamoc/mw_games/board';
16
+ *
17
+ * const terrain = { plain: { moveCost: 1, defense: 0 }, forest: { moveCost: 2, defense: 0.3 } };
18
+ * const state = startingSkirmish(6, 6, terrain);
19
+ * setSkirmishTerrain(state, 2, 2, 'forest');
20
+ * setSkirmishTerrain(state, 0, 0, 'plain', true); // a village at the corner
21
+ *
22
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
23
+ * addSkirmishUnit(state, { id: 'raider', owner: 'red', x: 1, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 3, hitChance: 0.6 });
24
+ *
25
+ * const moves = skirmishMoves(state, 'axeman'); // reachable cells, weighted by terrain cost
26
+ * const exchange = skirmishAttack(state, 'axeman', 'raider'); // both strike if raider survives
27
+ *
28
+ * endSkirmishTurn(state); // hands the turn to red, refills moves, heals units on owned villages
29
+ * const income = skirmishIncome(state, 'blue', 2, 1); // base 2, +1 per village blue owns
30
+ * ```
31
+ */
32
+ export interface SkirmishTerrain {
33
+ /** movement points spent entering this terrain kind; `Infinity` makes it impassable */
34
+ moveCost: number;
35
+ /** 0-1 defence bonus for whoever is standing here while defending - what it multiplies is `skirmishAttack`'s own formula, not this terrain's own business */
36
+ defense: number;
37
+ }
38
+ export interface SkirmishCell {
39
+ terrain: string;
40
+ /** a capturable village - `moveSkirmishUnit` sets `owner` to whoever last stood here */
41
+ village?: boolean;
42
+ owner?: string;
43
+ }
44
+ export interface SkirmishUnit {
45
+ id: string;
46
+ owner: string;
47
+ x: number;
48
+ y: number;
49
+ hp: number;
50
+ maxHp: number;
51
+ /** movement points available this turn; refilled to `moves` by `endSkirmishTurn` */
52
+ moves: number;
53
+ remainingMoves?: number;
54
+ attack: number;
55
+ /** 0-1 base chance to land a hit, before the defender's terrain defence reduces it */
56
+ hitChance: number;
57
+ }
58
+ export interface SkirmishState {
59
+ width: number;
60
+ height: number;
61
+ terrainTable: Record<string, SkirmishTerrain>;
62
+ cells: SkirmishCell[];
63
+ units: SkirmishUnit[];
64
+ turn: string;
65
+ round: number;
66
+ }
67
+ export interface SkirmishMove {
68
+ unit: string;
69
+ x: number;
70
+ y: number;
71
+ cost: number;
72
+ }
73
+ export interface SkirmishStrike {
74
+ attacker: string;
75
+ defender: string;
76
+ hit: boolean;
77
+ damage: number;
78
+ killed: boolean;
79
+ }
80
+ export interface SkirmishExchange {
81
+ strikes: SkirmishStrike[];
82
+ }
83
+ /**
84
+ * @example
85
+ * ```ts
86
+ * import { startingSkirmish } from '@datamoc/mw_games/board';
87
+ *
88
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
89
+ * console.log(state.width, state.round); // 6 1
90
+ * ```
91
+ */
92
+ export declare function startingSkirmish(width: number, height: number, terrainTable: Record<string, SkirmishTerrain>, defaultTerrain?: string): SkirmishState;
93
+ /**
94
+ * @example
95
+ * ```ts
96
+ * import { startingSkirmish, setSkirmishTerrain } from '@datamoc/mw_games/board';
97
+ *
98
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 }, forest: { moveCost: 2, defense: 0.3 } });
99
+ * setSkirmishTerrain(state, 2, 2, 'forest', true);
100
+ * console.log(state.cells[2 * 6 + 2]); // { terrain: 'forest', village: true }
101
+ * ```
102
+ */
103
+ export declare function setSkirmishTerrain(state: SkirmishState, x: number, y: number, terrain: string, village?: boolean): void;
104
+ /**
105
+ * whether a unit could be placed at (x, y) right now - on the map, passable, and unoccupied
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * import { startingSkirmish, addSkirmishUnit, canPlaceSkirmishUnit } from '@datamoc/mw_games/board';
110
+ *
111
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
112
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
113
+ *
114
+ * console.log(canPlaceSkirmishUnit(state, 1, 0)); // true
115
+ * console.log(canPlaceSkirmishUnit(state, 0, 0)); // false - axeman is already there
116
+ * ```
117
+ */
118
+ export declare function canPlaceSkirmishUnit(state: SkirmishState, x: number, y: number): boolean;
119
+ /**
120
+ * Places a unit at full remaining movement; the first unit added decides whose turn it is.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * import { startingSkirmish, addSkirmishUnit } from '@datamoc/mw_games/board';
125
+ *
126
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
127
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
128
+ * console.log(state.turn); // 'blue'
129
+ * ```
130
+ */
131
+ export declare function addSkirmishUnit(state: SkirmishState, unit: SkirmishUnit): void;
132
+ /**
133
+ * Every cell a unit can reach with its remaining movement points this turn, weighted by each
134
+ * terrain kind's own `moveCost` - no zone of control, unlike `board.Tactics`.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * import { startingSkirmish, addSkirmishUnit, skirmishMoves } from '@datamoc/mw_games/board';
139
+ *
140
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
141
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 2, attack: 4, hitChance: 0.7 });
142
+ *
143
+ * console.log(skirmishMoves(state, 'axeman').length); // 6 - every hex within 2 plain steps
144
+ * ```
145
+ */
146
+ export declare function skirmishMoves(state: SkirmishState, unitId: string): SkirmishMove[];
147
+ /**
148
+ * @example
149
+ * ```ts
150
+ * import { startingSkirmish, addSkirmishUnit, skirmishMoves, moveSkirmishUnit } from '@datamoc/mw_games/board';
151
+ *
152
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
153
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 2, attack: 4, hitChance: 0.7 });
154
+ *
155
+ * moveSkirmishUnit(state, skirmishMoves(state, 'axeman')[0]);
156
+ * console.log(state.units[0].remainingMoves); // 1 - one plain hex spent
157
+ * ```
158
+ */
159
+ export declare function moveSkirmishUnit(state: SkirmishState, move: SkirmishMove): void;
160
+ /**
161
+ * An attacker's strike against an adjacent enemy, followed by the defender's own retaliation
162
+ * if it survives - both use the same formula: `hitChance` reduced by the defender's terrain
163
+ * defence, `Random.chance` deciding whether it lands, `attack` as the flat damage on a hit.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * import { startingSkirmish, addSkirmishUnit, skirmishAttack } from '@datamoc/mw_games/board';
168
+ * import { Random } from '@datamoc/mw_games/core';
169
+ *
170
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
171
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 1 });
172
+ * addSkirmishUnit(state, { id: 'raider', owner: 'red', x: 1, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 3, hitChance: 1 });
173
+ *
174
+ * const exchange = Random.withSeed(1, () => skirmishAttack(state, 'axeman', 'raider'));
175
+ * console.log(exchange.strikes.map((strike: { attacker: string }) => strike.attacker)); // ['axeman', 'raider'] - raider survived to retaliate
176
+ * ```
177
+ */
178
+ export declare function skirmishAttack(state: SkirmishState, attackerId: string, defenderId: string): SkirmishExchange;
179
+ /**
180
+ * Net income for one side, from a flat base plus every village it currently owns - purely a
181
+ * function of the map's own village ownership, no formula beyond that belongs here, the same
182
+ * policy `board.Army`'s `armyIncome` already follows.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * import { startingSkirmish, setSkirmishTerrain, skirmishIncome } from '@datamoc/mw_games/board';
187
+ *
188
+ * const state = startingSkirmish(4, 1, { plain: { moveCost: 1, defense: 0 } });
189
+ * setSkirmishTerrain(state, 0, 0, 'plain', true);
190
+ * state.cells[0].owner = 'blue';
191
+ *
192
+ * console.log(skirmishIncome(state, 'blue', 2, 1)); // 3 - base 2, plus 1 for the one village
193
+ * ```
194
+ */
195
+ export declare function skirmishIncome(state: SkirmishState, owner: string, baseIncome: number, perVillage: number): number;
196
+ /**
197
+ * Hands the turn to the next owner with units on the board, refilling their movement and
198
+ * healing every one of their units standing on a village they own.
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * import { startingSkirmish, addSkirmishUnit, endSkirmishTurn } from '@datamoc/mw_games/board';
203
+ *
204
+ * const state = startingSkirmish(6, 6, { plain: { moveCost: 1, defense: 0 } });
205
+ * addSkirmishUnit(state, { id: 'axeman', owner: 'blue', x: 0, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 4, hitChance: 0.7 });
206
+ * addSkirmishUnit(state, { id: 'raider', owner: 'red', x: 3, y: 0, hp: 10, maxHp: 10, moves: 4, attack: 3, hitChance: 0.6 });
207
+ *
208
+ * endSkirmishTurn(state, 2);
209
+ * console.log(state.turn, state.round); // 'red' 1
210
+ * ```
211
+ */
212
+ export declare function endSkirmishTurn(state: SkirmishState, healPerVillage?: number): void;
213
+ //# sourceMappingURL=HexSkirmish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HexSkirmish.d.ts","sourceRoot":"","sources":["../../src/board/HexSkirmish.ts"],"names":[],"mappings":"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"}