@civ-clone/base-unit-lifecycle 0.0.0-stage → 0.1.1

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 (79) hide show
  1. package/.gitattributes +4 -0
  2. package/.github/workflows/build-on-push.yml +39 -0
  3. package/.prettierrc +4 -0
  4. package/LICENSE +21 -0
  5. package/README.md +61 -2
  6. package/lib/activate.d.ts +15 -0
  7. package/lib/activate.js +27 -0
  8. package/lib/activate.js.map +1 -0
  9. package/lib/activate.ts +47 -0
  10. package/lib/canStow.d.ts +8 -0
  11. package/lib/canStow.js +17 -0
  12. package/lib/canStow.js.map +1 -0
  13. package/lib/canStow.ts +25 -0
  14. package/lib/created.d.ts +9 -0
  15. package/lib/created.js +19 -0
  16. package/lib/created.js.map +1 -0
  17. package/lib/created.ts +24 -0
  18. package/lib/defeated.d.ts +25 -0
  19. package/lib/defeated.js +33 -0
  20. package/lib/defeated.js.map +1 -0
  21. package/lib/defeated.ts +55 -0
  22. package/lib/destroyed.d.ts +37 -0
  23. package/lib/destroyed.js +74 -0
  24. package/lib/destroyed.js.map +1 -0
  25. package/lib/destroyed.ts +106 -0
  26. package/lib/detonatedRules.d.ts +56 -0
  27. package/lib/detonatedRules.js +95 -0
  28. package/lib/detonatedRules.js.map +1 -0
  29. package/lib/detonatedRules.ts +196 -0
  30. package/lib/diplomatRules.d.ts +27 -0
  31. package/lib/diplomatRules.js +41 -0
  32. package/lib/diplomatRules.js.map +1 -0
  33. package/lib/diplomatRules.ts +83 -0
  34. package/lib/halted.d.ts +6 -0
  35. package/lib/halted.js +10 -0
  36. package/lib/halted.js.map +1 -0
  37. package/lib/halted.ts +11 -0
  38. package/lib/impassable.d.ts +8 -0
  39. package/lib/impassable.js +15 -0
  40. package/lib/impassable.js.map +1 -0
  41. package/lib/impassable.ts +19 -0
  42. package/lib/lifecycleRules.d.ts +118 -0
  43. package/lib/lifecycleRules.js +126 -0
  44. package/lib/lifecycleRules.js.map +1 -0
  45. package/lib/lifecycleRules.ts +378 -0
  46. package/lib/lostAtSea.d.ts +12 -0
  47. package/lib/lostAtSea.js +26 -0
  48. package/lib/lostAtSea.js.map +1 -0
  49. package/lib/lostAtSea.ts +37 -0
  50. package/lib/movedRules.d.ts +44 -0
  51. package/lib/movedRules.js +126 -0
  52. package/lib/movedRules.js.map +1 -0
  53. package/lib/movedRules.ts +300 -0
  54. package/lib/playerAction.d.ts +22 -0
  55. package/lib/playerAction.js +28 -0
  56. package/lib/playerAction.js.map +1 -0
  57. package/lib/playerAction.ts +64 -0
  58. package/lib/stowed.d.ts +2 -0
  59. package/lib/stowed.js +8 -0
  60. package/lib/stowed.js.map +1 -0
  61. package/lib/stowed.ts +6 -0
  62. package/lib/treatyPartner.d.ts +26 -0
  63. package/lib/treatyPartner.js +38 -0
  64. package/lib/treatyPartner.js.map +1 -0
  65. package/lib/treatyPartner.ts +78 -0
  66. package/lib/unsupported.d.ts +6 -0
  67. package/lib/unsupported.js +9 -0
  68. package/lib/unsupported.js.map +1 -0
  69. package/lib/unsupported.ts +10 -0
  70. package/lib/validateMove.d.ts +11 -0
  71. package/lib/validateMove.js +26 -0
  72. package/lib/validateMove.js.map +1 -0
  73. package/lib/validateMove.ts +36 -0
  74. package/package.json +61 -4
  75. package/tests/fixtures/civ1-unit-rules.json +89 -0
  76. package/tests/lifecycle.test.ts +657 -0
  77. package/tests/movedRules.test.ts +256 -0
  78. package/tests/rules.test.ts +434 -0
  79. package/tsconfig.json +16 -0
@@ -0,0 +1,378 @@
1
+ import {
2
+ CityRegistry,
3
+ instance as cityRegistryInstance,
4
+ } from '@civ-clone/core-city/CityRegistry';
5
+ import {
6
+ Engine,
7
+ instance as engineInstance,
8
+ } from '@civ-clone/core-engine/Engine';
9
+ import {
10
+ RuleRegistry,
11
+ instance as ruleRegistryInstance,
12
+ } from '@civ-clone/core-rule/RuleRegistry';
13
+ import {
14
+ TransportRegistry,
15
+ instance as transportRegistryInstance,
16
+ } from '@civ-clone/core-unit-transport/TransportRegistry';
17
+ import {
18
+ UnitImprovementRegistry,
19
+ instance as unitImprovementRegistryInstance,
20
+ } from '@civ-clone/core-unit-improvement/UnitImprovementRegistry';
21
+ import {
22
+ UnitLossRegistry,
23
+ instance as unitLossRegistryInstance,
24
+ } from '@civ-clone/core-unit/UnitLossRegistry';
25
+ import {
26
+ UnitPlayerActionType,
27
+ activeUnits,
28
+ hasActiveUnits,
29
+ hasInactiveUnits,
30
+ inactiveUnits,
31
+ } from './playerAction';
32
+ import {
33
+ UnitRegistry,
34
+ instance as unitRegistryInstance,
35
+ } from '@civ-clone/core-unit/UnitRegistry';
36
+ import {
37
+ applyVisibility,
38
+ emitCreated,
39
+ registerUnit,
40
+ setMoves,
41
+ } from './created';
42
+ import {
43
+ carriesCargo,
44
+ countLoss,
45
+ deactivate,
46
+ destroyedByAnother,
47
+ emitDestroyed,
48
+ isAboard,
49
+ leaveOwnTransport,
50
+ loseCargo,
51
+ notYetDestroyed,
52
+ removeImprovements,
53
+ } from './destroyed';
54
+ import { carriesDomain, carriesUnit } from './canStow';
55
+ import {
56
+ clearBusy,
57
+ hasImprovement,
58
+ hasMoves,
59
+ isBusy,
60
+ removeImprovement,
61
+ setActive,
62
+ } from './activate';
63
+ import { countLosses, emitLostAtSea, sink } from './lostAtSea';
64
+ import {
65
+ defended,
66
+ destroyStack,
67
+ emitAndDestroy,
68
+ hasStack,
69
+ notInCity,
70
+ } from './defeated';
71
+ import { fromLand, impassable, toPartnersLand } from './impassable';
72
+ import {
73
+ hasMovesFor,
74
+ shortOfMovesFor,
75
+ spendMoves,
76
+ spendShort,
77
+ } from './validateMove';
78
+ import Action from '@civ-clone/core-player/Rules/Action';
79
+ import Activate from '@civ-clone/core-unit/Rules/Activate';
80
+ import CanStow from '@civ-clone/core-unit-transport/Rules/CanStow';
81
+ import Created from '@civ-clone/core-unit/Rules/Created';
82
+ import Criterion from '@civ-clone/core-rule/Criterion';
83
+ import Defeated from '@civ-clone/core-unit/Rules/Defeated';
84
+ import Destroyed from '@civ-clone/core-unit/Rules/Destroyed';
85
+ import Effect from '@civ-clone/core-rule/Effect';
86
+ import ExpectedMovementCost from '@civ-clone/core-world-path/Rules/ExpectedMovementCost';
87
+ import Halted from '@civ-clone/base-unit-action-goto/Rules/Halted';
88
+ import Impassable from '@civ-clone/core-world-path/Rules/Impassable';
89
+ import LostAtSea from '@civ-clone/core-unit-transport/Rules/LostAtSea';
90
+ import Player from '@civ-clone/core-player/Player';
91
+ import PlayerAction from '@civ-clone/core-player/PlayerAction';
92
+ import Stowed from '@civ-clone/core-unit-transport/Rules/Stowed';
93
+ import Tile from '@civ-clone/core-world/Tile';
94
+ import Transferred from '@civ-clone/core-unit/Rules/Transferred';
95
+ import Unit from '@civ-clone/core-unit/Unit';
96
+ import UnitImprovement from '@civ-clone/core-unit-improvement/UnitImprovement';
97
+ import Unsupported from '@civ-clone/core-unit/Rules/Unsupported';
98
+ import ValidateMove from '@civ-clone/core-unit/Rules/ValidateMove';
99
+ import { emitHalted } from './halted';
100
+ import { emitUnsupported } from './unsupported';
101
+ import { expectedCost } from '@civ-clone/base-unit-action-rules/lib/moveShortfall';
102
+ import { instance as rngInstance } from '@civ-clone/core-random';
103
+ import { sleepAboard } from './stowed';
104
+
105
+ export interface LifecycleDependencies {
106
+ cityRegistry?: CityRegistry;
107
+ engine?: Engine;
108
+ randomNumberGenerator?: () => number;
109
+ ruleRegistry?: RuleRegistry;
110
+ transportRegistry?: TransportRegistry;
111
+ unitImprovementRegistry?: UnitImprovementRegistry;
112
+ unitLossRegistry?: UnitLossRegistry;
113
+ unitRegistry?: UnitRegistry;
114
+ }
115
+
116
+ export interface LifecycleBaseOptions {
117
+ // The player actions a unit that can take orders, and one that can't, is offered as.
118
+ ActiveUnit: UnitPlayerActionType<PlayerAction>;
119
+ InactiveUnit: UnitPlayerActionType<PlayerAction>;
120
+ // The unit improvements activating a unit takes off it (a Fortified state), each with a rule of its own.
121
+ removedOnActivate: (typeof UnitImprovement)[];
122
+ // The domains a transport may carry, each with a `CanStow` rule of its own (`Carries` traits).
123
+ stowable: (typeof Unit)[];
124
+ // Whose land `tile` is among `unit`'s treaty partners, or `null`: routes go round it. Without it, no rule is made.
125
+ partnerOf?: (unit: Unit, tile: Tile) => Player | null;
126
+ }
127
+
128
+ /**
129
+ * How a unit short of a step's moves fares: Civ1's `shortfallFactor` (`spendShort`), or a ruleset's own `shortStep`,
130
+ * which then needs no factor.
131
+ */
132
+ export type LifecycleOptions = LifecycleBaseOptions &
133
+ (
134
+ | { shortfallFactor: number; shortStep?: undefined }
135
+ | { shortfallFactor?: number; shortStep: ShortStep }
136
+ );
137
+
138
+ /**
139
+ * A ruleset's own short step: whether `unit`, with `remaining` of its `movement` moves, gets onto a square costing
140
+ * `cost` (one draw of `randomNumberGenerator` at most), and what such a step is worth on average to route planning.
141
+ * The unit's moves are spent either way.
142
+ */
143
+ export interface ShortStep {
144
+ succeeds: (
145
+ unit: Unit,
146
+ remaining: number,
147
+ movement: number,
148
+ cost: number,
149
+ randomNumberGenerator: () => number
150
+ ) => boolean;
151
+ expectedCost: (movement: number, cost: number) => number;
152
+ }
153
+
154
+ /**
155
+ * A ruleset's unit lifecycle rules, by the rule type they are, in the order each type's rules run. Their ids are
156
+ * `<prefix>:unit/<type>/<name>` (`<prefix>:player/action/<name>` for the player actions), as civ1-unit's are, so a
157
+ * ruleset registers each list where it wants it and keeps the ids another package may name.
158
+ */
159
+ export const lifecycleRules = (
160
+ prefix: string,
161
+ {
162
+ cityRegistry = cityRegistryInstance,
163
+ engine = engineInstance,
164
+ randomNumberGenerator = rngInstance,
165
+ ruleRegistry = ruleRegistryInstance,
166
+ transportRegistry = transportRegistryInstance,
167
+ unitImprovementRegistry = unitImprovementRegistryInstance,
168
+ unitLossRegistry = unitLossRegistryInstance,
169
+ unitRegistry = unitRegistryInstance,
170
+ }: LifecycleDependencies,
171
+ {
172
+ ActiveUnit,
173
+ InactiveUnit,
174
+ removedOnActivate,
175
+ stowable,
176
+ partnerOf,
177
+ shortfallFactor,
178
+ shortStep,
179
+ }: LifecycleOptions
180
+ ) => {
181
+ const id = (name: string): string => `${prefix}:unit/${name}`;
182
+
183
+ return {
184
+ activate: [
185
+ new Activate(
186
+ id('activate/has-moves'),
187
+ new Criterion(hasMoves),
188
+ new Effect(setActive)
189
+ ),
190
+ new Activate(
191
+ id('activate/clear-busy'),
192
+ new Criterion(isBusy),
193
+ new Effect(clearBusy)
194
+ ),
195
+ ...removedOnActivate.map(
196
+ (UnitImprovementType: typeof UnitImprovement): Activate =>
197
+ new Activate(
198
+ id(`activate/remove-improvement/${UnitImprovementType.name}`),
199
+ new Criterion(
200
+ hasImprovement(unitImprovementRegistry, UnitImprovementType)
201
+ ),
202
+ new Effect(
203
+ removeImprovement(unitImprovementRegistry, UnitImprovementType)
204
+ )
205
+ )
206
+ ),
207
+ ],
208
+
209
+ canStow: stowable.map(
210
+ (StowableUnitType: typeof Unit): CanStow =>
211
+ new CanStow(
212
+ id(`can-stow/${StowableUnitType.name}`),
213
+ new Criterion(carriesDomain(StowableUnitType)),
214
+ new Effect(carriesUnit(StowableUnitType))
215
+ )
216
+ ),
217
+
218
+ created: [
219
+ new Created(
220
+ id('created/register'),
221
+ new Effect(registerUnit(unitRegistry))
222
+ ),
223
+ new Created(id('created/apply-visibility'), new Effect(applyVisibility)),
224
+ new Created(id('created/set-moves'), new Effect(setMoves)),
225
+ new Created(id('created/emit'), new Effect(emitCreated(engine))),
226
+ ],
227
+
228
+ defeated: [
229
+ new Defeated(
230
+ id('defeated/emit-and-destroy'),
231
+ new Effect(emitAndDestroy(engine, ruleRegistry))
232
+ ),
233
+ new Defeated(
234
+ // `Attack` processes `Defeated` for a losing attacker too; only a defeated defender takes its stack with it.
235
+ id('defeated/destroy-stack'),
236
+ new Criterion(defended),
237
+ new Criterion(notInCity(cityRegistry)),
238
+ new Criterion(hasStack(unitRegistry)),
239
+ new Effect(destroyStack(unitRegistry, ruleRegistry))
240
+ ),
241
+ ],
242
+
243
+ destroyed: [
244
+ new Destroyed(id('destroyed/emit'), new Effect(emitDestroyed(engine))),
245
+ new Destroyed(
246
+ // A loss, once, for a unit another player destroyed.
247
+ id('destroyed/count-loss'),
248
+ new Criterion(destroyedByAnother),
249
+ new Criterion(notYetDestroyed),
250
+ new Effect(countLoss(unitLossRegistry))
251
+ ),
252
+ new Destroyed(id('destroyed/deactivate'), new Effect(deactivate)),
253
+ new Destroyed(
254
+ id('destroyed/remove-improvements'),
255
+ new Effect(removeImprovements(unitImprovementRegistry))
256
+ ),
257
+ new Destroyed(
258
+ id('destroyed/lose-cargo'),
259
+ new Criterion(carriesCargo(transportRegistry)),
260
+ new Effect(loseCargo(transportRegistry))
261
+ ),
262
+ new Destroyed(
263
+ id('destroyed/leave-transport'),
264
+ new Criterion(isAboard(transportRegistry)),
265
+ new Effect(leaveOwnTransport(transportRegistry))
266
+ ),
267
+ ],
268
+
269
+ expectedMovementCost: [
270
+ new ExpectedMovementCost(
271
+ // A step costing more than the unit's full moves is worth `max(m, c × shortfallFactor)` on average.
272
+ id('expected-movement-cost/shortfall'),
273
+ new Criterion(
274
+ (unit: Unit, movementCost: number, movement: number): boolean =>
275
+ movement > 0
276
+ ),
277
+ new Effect(
278
+ (unit: Unit, movementCost: number, movement: number): number =>
279
+ shortStep === undefined
280
+ ? expectedCost(movement, movementCost, shortfallFactor as number)
281
+ : shortStep.expectedCost(movement, movementCost)
282
+ )
283
+ ),
284
+ ],
285
+
286
+ halted: [new Halted(id('halted/emit'), new Effect(emitHalted(engine)))],
287
+
288
+ impassable:
289
+ partnerOf === undefined
290
+ ? []
291
+ : [
292
+ new Impassable(
293
+ // Routes go round a treaty partner's land, so following one never breaks a treaty by accident.
294
+ id('impassable/treaty-partners-land'),
295
+ new Criterion(fromLand),
296
+ new Criterion(toPartnersLand(partnerOf)),
297
+ new Effect(impassable)
298
+ ),
299
+ ],
300
+
301
+ lostAtSea: [
302
+ new LostAtSea(id('lost-at-sea/emit'), new Effect(emitLostAtSea(engine))),
303
+ new LostAtSea(
304
+ // Before `destroy`, while the manifests still say what's aboard.
305
+ id('lost-at-sea/count-loss'),
306
+ new Effect(countLosses(unitLossRegistry, transportRegistry))
307
+ ),
308
+ new LostAtSea(id('lost-at-sea/destroy'), new Effect(sink)),
309
+ ],
310
+
311
+ playerAction: [
312
+ new Action(
313
+ `${prefix}:player/action/active-units`,
314
+ new Criterion(hasActiveUnits(unitRegistry)),
315
+ new Effect(activeUnits(unitRegistry, ActiveUnit))
316
+ ),
317
+ new Action(
318
+ `${prefix}:player/action/inactive-units`,
319
+ new Criterion(hasInactiveUnits(unitRegistry)),
320
+ new Effect(inactiveUnits(unitRegistry, InactiveUnit))
321
+ ),
322
+ ],
323
+
324
+ stowed: [new Stowed(id('stowed/sleep'), new Effect(sleepAboard))],
325
+
326
+ transferred: [
327
+ new Transferred(
328
+ id('transferred/emit'),
329
+ new Effect(
330
+ (unit: Unit, player: Player, previousPlayer: Player): void => {
331
+ engine.emit('unit:transferred', unit, player, previousPlayer);
332
+ }
333
+ )
334
+ ),
335
+ ],
336
+
337
+ unsupported: [
338
+ new Unsupported(
339
+ id('unsupported/emit'),
340
+ new Effect(emitUnsupported(engine))
341
+ ),
342
+ ],
343
+
344
+ validateMove: [
345
+ new ValidateMove(
346
+ id('validate-move/enough-moves'),
347
+ new Criterion(hasMovesFor),
348
+ new Effect(spendMoves)
349
+ ),
350
+ new ValidateMove(
351
+ id('validate-move/not-enough-moves'),
352
+ new Criterion(shortOfMovesFor),
353
+ new Effect(
354
+ shortStep === undefined
355
+ ? spendShort(shortfallFactor as number, randomNumberGenerator)
356
+ : (unit: Unit, movementCost: number): boolean => {
357
+ const remaining = unit.moves().value(),
358
+ movement = unit.movement().value();
359
+
360
+ unit.moves().set(0);
361
+
362
+ return shortStep.succeeds(
363
+ unit,
364
+ remaining,
365
+ movement,
366
+ movementCost,
367
+ randomNumberGenerator
368
+ );
369
+ }
370
+ )
371
+ ),
372
+ ],
373
+ };
374
+ };
375
+
376
+ export type LifecycleRules = ReturnType<typeof lifecycleRules>;
377
+
378
+ export default lifecycleRules;
@@ -0,0 +1,12 @@
1
+ import Engine from '@civ-clone/core-engine/Engine';
2
+ import { ITransport } from '@civ-clone/core-unit-transport/Transport';
3
+ import TransportRegistry from '@civ-clone/core-unit-transport/TransportRegistry';
4
+ import UnitLossRegistry from '@civ-clone/core-unit/UnitLossRegistry';
5
+ export declare const emitLostAtSea: (
6
+ engine: Engine
7
+ ) => (unit: ITransport) => void;
8
+ export declare const countLosses: (
9
+ unitLossRegistry: UnitLossRegistry,
10
+ transportRegistry: TransportRegistry
11
+ ) => (transport: ITransport) => void;
12
+ export declare const sink: (unit: ITransport) => void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sink = exports.countLosses = exports.emitLostAtSea = void 0;
4
+ // What a ruleset's `LostAtSea` rules do when a transport is lost at sea.
5
+ // Emits `unit:lost-at-sea` with the transport.
6
+ const emitLostAtSea = (engine) => (unit) => {
7
+ engine.emit('unit:lost-at-sea', unit);
8
+ };
9
+ exports.emitLostAtSea = emitLostAtSea;
10
+ // Records the transport and everything aboard that isn't destroyed yet as losses of their players, though no one
11
+ // destroyed them. Run it before `sink`, while the manifests still say what's aboard.
12
+ const countLosses = (unitLossRegistry, transportRegistry) => (transport) => [
13
+ transport,
14
+ ...transportRegistry
15
+ .getByTransport(transport)
16
+ .map((manifest) => manifest.unit()),
17
+ ]
18
+ .filter((unit) => !unit.destroyed())
19
+ .forEach((unit) => {
20
+ unitLossRegistry.record(unit.player(), unit.sourceClass());
21
+ });
22
+ exports.countLosses = countLosses;
23
+ // Destroys the transport, by no one.
24
+ const sink = (unit) => unit.destroy(null);
25
+ exports.sink = sink;
26
+ //# sourceMappingURL=lostAtSea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lostAtSea.js","sourceRoot":"","sources":["lostAtSea.ts"],"names":[],"mappings":";;;AAOA,yEAAyE;AAEzE,+CAA+C;AACxC,MAAM,aAAa,GACxB,CAAC,MAAc,EAAE,EAAE,CACnB,CAAC,IAAgB,EAAQ,EAAE;IACzB,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC;AAJS,QAAA,aAAa,iBAItB;AAEJ,iHAAiH;AACjH,sFAAsF;AAC/E,MAAM,WAAW,GACtB,CAAC,gBAAkC,EAAE,iBAAoC,EAAE,EAAE,CAC7E,CAAC,SAAqB,EAAQ,EAAE,CAC9B;IACE,SAA4B;IAC5B,GAAG,iBAAiB;SACjB,cAAc,CAAC,SAAS,CAAC;SACzB,GAAG,CAAC,CAAC,QAA2B,EAAQ,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;CAC/D;KACE,MAAM,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;KAClD,OAAO,CAAC,CAAC,IAAU,EAAQ,EAAE;IAC5B,gBAAgB,CAAC,MAAM,CACrB,IAAI,CAAC,MAAM,EAAE,EACb,IAAI,CAAC,WAAW,EAAiB,CAClC,CAAC;AACJ,CAAC,CAAC,CAAC;AAfI,QAAA,WAAW,eAef;AAET,qCAAqC;AAC9B,MAAM,IAAI,GAAG,CAAC,IAAgB,EAAQ,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAAtD,QAAA,IAAI,QAAkD"}
@@ -0,0 +1,37 @@
1
+ import Engine from '@civ-clone/core-engine/Engine';
2
+ import { ITransport } from '@civ-clone/core-unit-transport/Transport';
3
+ import TransportManifest from '@civ-clone/core-unit-transport/TransportManifest';
4
+ import TransportRegistry from '@civ-clone/core-unit-transport/TransportRegistry';
5
+ import Unit from '@civ-clone/core-unit/Unit';
6
+ import UnitLossRegistry from '@civ-clone/core-unit/UnitLossRegistry';
7
+
8
+ // What a ruleset's `LostAtSea` rules do when a transport is lost at sea.
9
+
10
+ // Emits `unit:lost-at-sea` with the transport.
11
+ export const emitLostAtSea =
12
+ (engine: Engine) =>
13
+ (unit: ITransport): void => {
14
+ engine.emit('unit:lost-at-sea', unit);
15
+ };
16
+
17
+ // Records the transport and everything aboard that isn't destroyed yet as losses of their players, though no one
18
+ // destroyed them. Run it before `sink`, while the manifests still say what's aboard.
19
+ export const countLosses =
20
+ (unitLossRegistry: UnitLossRegistry, transportRegistry: TransportRegistry) =>
21
+ (transport: ITransport): void =>
22
+ [
23
+ transport as unknown as Unit,
24
+ ...transportRegistry
25
+ .getByTransport(transport)
26
+ .map((manifest: TransportManifest): Unit => manifest.unit()),
27
+ ]
28
+ .filter((unit: Unit): boolean => !unit.destroyed())
29
+ .forEach((unit: Unit): void => {
30
+ unitLossRegistry.record(
31
+ unit.player(),
32
+ unit.sourceClass() as typeof Unit
33
+ );
34
+ });
35
+
36
+ // Destroys the transport, by no one.
37
+ export const sink = (unit: ITransport): void => unit.destroy(null);
@@ -0,0 +1,44 @@
1
+ import { CityRegistry } from '@civ-clone/core-city/CityRegistry';
2
+ import { Engine } from '@civ-clone/core-engine/Engine';
3
+ import { InteractionRegistry } from '@civ-clone/core-diplomacy/InteractionRegistry';
4
+ import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
5
+ import { TransportRegistry } from '@civ-clone/core-unit-transport/TransportRegistry';
6
+ import { UnitRegistry } from '@civ-clone/core-unit/UnitRegistry';
7
+ import Action from '@civ-clone/core-unit/Action';
8
+ import Moved from '@civ-clone/core-unit/Rules/Moved';
9
+ type ActionType = abstract new (...args: any[]) => Action;
10
+ export interface MovedDependencies {
11
+ cityRegistry?: CityRegistry;
12
+ engine?: Engine;
13
+ interactionRegistry?: InteractionRegistry;
14
+ randomNumberGenerator?: () => number;
15
+ ruleRegistry?: RuleRegistry;
16
+ transportRegistry?: TransportRegistry;
17
+ unitRegistry?: UnitRegistry;
18
+ }
19
+ export interface MovedOptions {
20
+ Move: ActionType;
21
+ Disembark: ActionType;
22
+ attacks: ActionType[];
23
+ noVendetta: ActionType[];
24
+ coastBoundLoss: number;
25
+ }
26
+ /**
27
+ * A ruleset's `Moved` rules, in order, with ids `<prefix>:unit/moved/<name>` as civ1-unit's are: telling the client,
28
+ * vision, ending a unit's moves, transports carrying and collecting their cargo, leaving a transport, a `CoastBound`
29
+ * unit lost at sea, an `AttackEndsTurn` unit's turn ending, and a broken peace treaty.
30
+ */
31
+ export declare const movedRules: (
32
+ prefix: string,
33
+ {
34
+ cityRegistry,
35
+ engine,
36
+ interactionRegistry,
37
+ randomNumberGenerator,
38
+ ruleRegistry,
39
+ transportRegistry,
40
+ unitRegistry,
41
+ }: MovedDependencies,
42
+ { Move, Disembark, attacks, noVendetta, coastBoundLoss }: MovedOptions
43
+ ) => Moved[];
44
+ export default movedRules;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.movedRules = void 0;
4
+ const CityRegistry_1 = require("@civ-clone/core-city/CityRegistry");
5
+ const Engine_1 = require("@civ-clone/core-engine/Engine");
6
+ const InteractionRegistry_1 = require("@civ-clone/core-diplomacy/InteractionRegistry");
7
+ const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
8
+ const TransportRegistry_1 = require("@civ-clone/core-unit-transport/TransportRegistry");
9
+ const UnitRegistry_1 = require("@civ-clone/core-unit/UnitRegistry");
10
+ const Declarations_1 = require("@civ-clone/library-diplomacy/Declarations");
11
+ const Air_1 = require("@civ-clone/base-unit-type-air/Air");
12
+ const AttackEndsTurn_1 = require("@civ-clone/base-unit-trait-attack-ends-turn/AttackEndsTurn");
13
+ const CoastBound_1 = require("@civ-clone/base-unit-trait-coast-bound/CoastBound");
14
+ const Criterion_1 = require("@civ-clone/core-rule/Criterion");
15
+ const Effect_1 = require("@civ-clone/core-rule/Effect");
16
+ const High_1 = require("@civ-clone/core-rule/Priorities/High");
17
+ const LostAtSea_1 = require("@civ-clone/core-unit-transport/Rules/LostAtSea");
18
+ const Moved_1 = require("@civ-clone/core-unit/Rules/Moved");
19
+ const NavalTransport_1 = require("@civ-clone/base-unit-type-naval-transport/NavalTransport");
20
+ const Never_1 = require("@civ-clone/core-diplomacy/Expiries/Never");
21
+ const Sleeping_1 = require("@civ-clone/base-unit-action-sleep/Rules/Sleeping");
22
+ const Stowed_1 = require("@civ-clone/base-unit-action-embark/Busy/Stowed");
23
+ const holds_1 = require("@civ-clone/library-diplomacy/lib/holds");
24
+ const core_random_1 = require("@civ-clone/core-random");
25
+ /**
26
+ * A ruleset's `Moved` rules, in order, with ids `<prefix>:unit/moved/<name>` as civ1-unit's are: telling the client,
27
+ * vision, ending a unit's moves, transports carrying and collecting their cargo, leaving a transport, a `CoastBound`
28
+ * unit lost at sea, an `AttackEndsTurn` unit's turn ending, and a broken peace treaty.
29
+ */
30
+ const movedRules = (prefix, { cityRegistry = CityRegistry_1.instance, engine = Engine_1.instance, interactionRegistry = InteractionRegistry_1.instance, randomNumberGenerator = core_random_1.instance, ruleRegistry = RuleRegistry_1.instance, transportRegistry = TransportRegistry_1.instance, unitRegistry = UnitRegistry_1.instance, }, { Move, Disembark, attacks, noVendetta, coastBoundLoss }) => {
31
+ const id = (name) => `${prefix}:unit/moved/${name}`,
32
+ // Stows each of `units` aboard `transport`, as `Embark` and `LandAircraft` do, until it is full.
33
+ collect = (transport, units) => units.forEach((unit) => {
34
+ if (!transport.hasCapacity() || !transport.canStow(unit)) {
35
+ return;
36
+ }
37
+ transport.stow(unit, unit.tile());
38
+ unit.setBusy(new Stowed_1.default());
39
+ }),
40
+ // The transport's own units on the tile it has just moved onto, that aren't already aboard something.
41
+ unitsToCollect = (transport, tile = transport.tile()) => unitRegistry
42
+ .getByTile(tile)
43
+ .filter((unit) => unit !== transport &&
44
+ unit.player() === transport.player() &&
45
+ !transportRegistry.hasUnit(unit)),
46
+ // A transport's own move that took it onto a new tile (not one it was carried along on, and not an attack).
47
+ transportMoved = [
48
+ new Criterion_1.default((unit) => unit instanceof NavalTransport_1.default),
49
+ new Criterion_1.default((unit, action) => action instanceof Move),
50
+ new Criterion_1.default((unit, action) => action.to() === unit.tile()),
51
+ ], isOwnCity = (unit) => { var _a; return ((_a = cityRegistry.getByTile(unit.tile())) === null || _a === void 0 ? void 0 : _a.player()) === unit.player(); };
52
+ return [
53
+ new Moved_1.default(id('emit'), new Effect_1.default((unit, action) => {
54
+ engine.emit('unit:moved', unit, action);
55
+ })),
56
+ new Moved_1.default(id('apply-visibility'), new Effect_1.default((unit) => unit.applyVisibility())),
57
+ new Moved_1.default(id('end-moves-when-exhausted'), new Criterion_1.default((unit) => unit.moves().value() < 0.3), new Effect_1.default((unit) => {
58
+ unit.moves().set(0);
59
+ unit.setActive(false);
60
+ })),
61
+ new Moved_1.default(
62
+ // A transport leaving one of its player's cities takes only the units sentried aboard (`Stowed` or `Sleeping`);
63
+ // at sea, all of its cargo goes with it, and so does an aircraft aboard, awake or not.
64
+ id('move-cargo'), new Criterion_1.default((unit) => unit instanceof NavalTransport_1.default), new Criterion_1.default((unit, action) => action instanceof Move), new Criterion_1.default((unit) => unit.hasCargo()), new Effect_1.default((unit, action) => {
65
+ var _a;
66
+ const transport = unit, leftPort = ((_a = cityRegistry.getByTile(action.from())) === null || _a === void 0 ? void 0 : _a.player()) === unit.player();
67
+ transport.cargo().forEach((cargo) => {
68
+ if (leftPort &&
69
+ !(cargo instanceof Air_1.default) &&
70
+ !(cargo.busy() instanceof Stowed_1.default) &&
71
+ !(cargo.busy() instanceof Sleeping_1.default)) {
72
+ transport.unload(cargo);
73
+ return;
74
+ }
75
+ cargo.action(action.forUnit(cargo));
76
+ });
77
+ })),
78
+ // These two come after `move-cargo`, so a unit collected here isn't also sent along the move just made.
79
+ new Moved_1.default(
80
+ // A carrier that moves onto its player's aircraft outside a city picks them up.
81
+ id('collect-aircraft'), ...transportMoved, new Criterion_1.default((unit) => !isOwnCity(unit)), new Effect_1.default((unit) => collect(unit, unitsToCollect(unit).filter((tileUnit) => tileUnit instanceof Air_1.default)))),
82
+ new Moved_1.default(
83
+ // A transport that moves into one of its player's cities picks up the units sleeping there.
84
+ id('collect-sleeping'), ...transportMoved, new Criterion_1.default((unit) => isOwnCity(unit)), new Effect_1.default((unit) => collect(unit, unitsToCollect(unit).filter((tileUnit) => tileUnit.busy() instanceof Sleeping_1.default)))),
85
+ new Moved_1.default(id('disembark'), new Criterion_1.default((unit, action) => action instanceof Disembark), new Effect_1.default((unit) => {
86
+ const manifest = transportRegistry.getByUnit(unit);
87
+ manifest.transport().unload(unit);
88
+ transportRegistry.unregister(manifest);
89
+ })),
90
+ new Moved_1.default(
91
+ // A unit that leaves its transport's tile with a plain move (an aircraft taking off, a unit walking off a ship in
92
+ // a city) is no longer aboard. A `Disembark` is a move too, which `disembark` unloads.
93
+ id('take-off'), new Criterion_1.default((unit, action) => action instanceof Move), new Criterion_1.default((unit, action) => !(action instanceof Disembark)), new Criterion_1.default((unit) => transportRegistry.hasUnit(unit)), new Criterion_1.default((unit) => transportRegistry.getByUnit(unit).transport().tile() !== unit.tile()), new Effect_1.default((unit) => {
94
+ transportRegistry.getByUnit(unit).transport().unload(unit);
95
+ if (unit.busy() instanceof Stowed_1.default) {
96
+ unit.setBusy();
97
+ }
98
+ })),
99
+ new Moved_1.default(id('trireme-lost-at-sea'), new Criterion_1.default((unit) => unit.hasTrait(CoastBound_1.default)), new Criterion_1.default((unit) => unit.moves().value() === 0), new Criterion_1.default((unit) => !unit.tile().isCoast()), new Criterion_1.default(() => randomNumberGenerator() <= coastBoundLoss), new Effect_1.default((unit) => {
100
+ ruleRegistry.process(LostAtSea_1.default, unit);
101
+ })),
102
+ new Moved_1.default(
103
+ // A unit that `AttackEndsTurn` can't attack again until next turn.
104
+ id('bomber/end-turn-after-attack'), new High_1.default(), new Criterion_1.default((unit) => unit.hasTrait(AttackEndsTurn_1.default)), new Criterion_1.default((unit, action) => attacks.some((AttackType) => action instanceof AttackType)), new Effect_1.default((unit) => {
105
+ unit.moves().set(0);
106
+ unit.setActive(false);
107
+ })),
108
+ new Moved_1.default(id('break-peace-treaty'),
109
+ // Any action with an enemy: a sneak attack or capture, or a covert action at peace.
110
+ new Criterion_1.default((unit, action) => typeof action.enemy === 'function'), new Effect_1.default((unit, action) => {
111
+ const enemy = action.enemy(), peaceTreaties = interactionRegistry
112
+ .getByPlayers(unit.player(), enemy)
113
+ .filter((interaction) => interaction instanceof Declarations_1.Peace && interaction.active());
114
+ peaceTreaties.forEach((peaceTreaty) => peaceTreaty.expire(unit.player()));
115
+ // The victim of an attack in peacetime holds a grudge; walking onto or pillaging its land only ends the treaty.
116
+ if (peaceTreaties.length > 0 &&
117
+ !noVendetta.some((ActionType) => action instanceof ActionType) &&
118
+ !(0, holds_1.hasVendetta)(enemy, unit.player(), interactionRegistry)) {
119
+ interactionRegistry.register(new Declarations_1.Vendetta(enemy, unit.player(), new Never_1.default(), ruleRegistry));
120
+ }
121
+ })),
122
+ ];
123
+ };
124
+ exports.movedRules = movedRules;
125
+ exports.default = exports.movedRules;
126
+ //# sourceMappingURL=movedRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"movedRules.js","sourceRoot":"","sources":["movedRules.ts"],"names":[],"mappings":"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"}