@civ-clone/base-unit-lifecycle 0.1.0 → 0.1.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.
- package/README.md +29 -1
- package/lib/defeated.d.ts +5 -0
- package/lib/defeated.js +4 -1
- package/lib/defeated.js.map +1 -1
- package/lib/defeated.ts +11 -0
- package/lib/detonatedRules.d.ts +56 -0
- package/lib/detonatedRules.js +95 -0
- package/lib/detonatedRules.js.map +1 -0
- package/lib/detonatedRules.ts +196 -0
- package/lib/diplomatRules.d.ts +27 -0
- package/lib/diplomatRules.js +41 -0
- package/lib/diplomatRules.js.map +1 -0
- package/lib/diplomatRules.ts +83 -0
- package/lib/lifecycleRules.d.ts +121 -0
- package/lib/lifecycleRules.js +128 -0
- package/lib/lifecycleRules.js.map +1 -0
- package/lib/lifecycleRules.ts +386 -0
- package/lib/movedRules.d.ts +44 -0
- package/lib/movedRules.js +126 -0
- package/lib/movedRules.js.map +1 -0
- package/lib/movedRules.ts +300 -0
- package/lib/treatyPartner.d.ts +26 -0
- package/lib/treatyPartner.js +38 -0
- package/lib/treatyPartner.js.map +1 -0
- package/lib/treatyPartner.ts +78 -0
- package/package.json +20 -2
- package/tests/fixtures/civ1-unit-rules.json +89 -0
- package/tests/lifecycle.test.ts +20 -0
- package/tests/movedRules.test.ts +256 -0
- package/tests/rules.test.ts +484 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { CityRegistry } from '@civ-clone/core-city/CityRegistry';
|
|
2
|
+
import { Engine } from '@civ-clone/core-engine/Engine';
|
|
3
|
+
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
4
|
+
import { TransportRegistry } from '@civ-clone/core-unit-transport/TransportRegistry';
|
|
5
|
+
import { UnitImprovementRegistry } from '@civ-clone/core-unit-improvement/UnitImprovementRegistry';
|
|
6
|
+
import { UnitLossRegistry } from '@civ-clone/core-unit/UnitLossRegistry';
|
|
7
|
+
import { UnitPlayerActionType } from './playerAction';
|
|
8
|
+
import { UnitRegistry } from '@civ-clone/core-unit/UnitRegistry';
|
|
9
|
+
import { SparesStack } from './defeated';
|
|
10
|
+
import Action from '@civ-clone/core-player/Rules/Action';
|
|
11
|
+
import Activate from '@civ-clone/core-unit/Rules/Activate';
|
|
12
|
+
import CanStow from '@civ-clone/core-unit-transport/Rules/CanStow';
|
|
13
|
+
import Created from '@civ-clone/core-unit/Rules/Created';
|
|
14
|
+
import Defeated from '@civ-clone/core-unit/Rules/Defeated';
|
|
15
|
+
import Destroyed from '@civ-clone/core-unit/Rules/Destroyed';
|
|
16
|
+
import ExpectedMovementCost from '@civ-clone/core-world-path/Rules/ExpectedMovementCost';
|
|
17
|
+
import Halted from '@civ-clone/base-unit-action-goto/Rules/Halted';
|
|
18
|
+
import Impassable from '@civ-clone/core-world-path/Rules/Impassable';
|
|
19
|
+
import LostAtSea from '@civ-clone/core-unit-transport/Rules/LostAtSea';
|
|
20
|
+
import Player from '@civ-clone/core-player/Player';
|
|
21
|
+
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
22
|
+
import Stowed from '@civ-clone/core-unit-transport/Rules/Stowed';
|
|
23
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
24
|
+
import Transferred from '@civ-clone/core-unit/Rules/Transferred';
|
|
25
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
26
|
+
import UnitImprovement from '@civ-clone/core-unit-improvement/UnitImprovement';
|
|
27
|
+
import Unsupported from '@civ-clone/core-unit/Rules/Unsupported';
|
|
28
|
+
import ValidateMove from '@civ-clone/core-unit/Rules/ValidateMove';
|
|
29
|
+
export interface LifecycleDependencies {
|
|
30
|
+
cityRegistry?: CityRegistry;
|
|
31
|
+
engine?: Engine;
|
|
32
|
+
randomNumberGenerator?: () => number;
|
|
33
|
+
ruleRegistry?: RuleRegistry;
|
|
34
|
+
transportRegistry?: TransportRegistry;
|
|
35
|
+
unitImprovementRegistry?: UnitImprovementRegistry;
|
|
36
|
+
unitLossRegistry?: UnitLossRegistry;
|
|
37
|
+
unitRegistry?: UnitRegistry;
|
|
38
|
+
}
|
|
39
|
+
export interface LifecycleBaseOptions {
|
|
40
|
+
ActiveUnit: UnitPlayerActionType<PlayerAction>;
|
|
41
|
+
InactiveUnit: UnitPlayerActionType<PlayerAction>;
|
|
42
|
+
removedOnActivate: (typeof UnitImprovement)[];
|
|
43
|
+
stowable: (typeof Unit)[];
|
|
44
|
+
partnerOf?: (unit: Unit, tile: Tile) => Player | null;
|
|
45
|
+
sparesStack?: SparesStack;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* How a unit short of a step's moves fares: Civ1's `shortfallFactor` (`spendShort`), or a ruleset's own `shortStep`,
|
|
49
|
+
* which then needs no factor.
|
|
50
|
+
*/
|
|
51
|
+
export type LifecycleOptions = LifecycleBaseOptions &
|
|
52
|
+
(
|
|
53
|
+
| {
|
|
54
|
+
shortfallFactor: number;
|
|
55
|
+
shortStep?: undefined;
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
shortfallFactor?: number;
|
|
59
|
+
shortStep: ShortStep;
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
/**
|
|
63
|
+
* A ruleset's own short step: whether `unit`, with `remaining` of its `movement` moves, gets onto a square costing
|
|
64
|
+
* `cost` (one draw of `randomNumberGenerator` at most), and what such a step is worth on average to route planning.
|
|
65
|
+
* The unit's moves are spent either way.
|
|
66
|
+
*/
|
|
67
|
+
export interface ShortStep {
|
|
68
|
+
succeeds: (
|
|
69
|
+
unit: Unit,
|
|
70
|
+
remaining: number,
|
|
71
|
+
movement: number,
|
|
72
|
+
cost: number,
|
|
73
|
+
randomNumberGenerator: () => number
|
|
74
|
+
) => boolean;
|
|
75
|
+
expectedCost: (movement: number, cost: number) => number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A ruleset's unit lifecycle rules, by the rule type they are, in the order each type's rules run. Their ids are
|
|
79
|
+
* `<prefix>:unit/<type>/<name>` (`<prefix>:player/action/<name>` for the player actions), as civ1-unit's are, so a
|
|
80
|
+
* ruleset registers each list where it wants it and keeps the ids another package may name.
|
|
81
|
+
*/
|
|
82
|
+
export declare const lifecycleRules: (
|
|
83
|
+
prefix: string,
|
|
84
|
+
{
|
|
85
|
+
cityRegistry,
|
|
86
|
+
engine,
|
|
87
|
+
randomNumberGenerator,
|
|
88
|
+
ruleRegistry,
|
|
89
|
+
transportRegistry,
|
|
90
|
+
unitImprovementRegistry,
|
|
91
|
+
unitLossRegistry,
|
|
92
|
+
unitRegistry,
|
|
93
|
+
}: LifecycleDependencies,
|
|
94
|
+
{
|
|
95
|
+
ActiveUnit,
|
|
96
|
+
InactiveUnit,
|
|
97
|
+
removedOnActivate,
|
|
98
|
+
stowable,
|
|
99
|
+
partnerOf,
|
|
100
|
+
shortfallFactor,
|
|
101
|
+
shortStep,
|
|
102
|
+
sparesStack,
|
|
103
|
+
}: LifecycleOptions
|
|
104
|
+
) => {
|
|
105
|
+
activate: Activate[];
|
|
106
|
+
canStow: CanStow[];
|
|
107
|
+
created: Created[];
|
|
108
|
+
defeated: Defeated[];
|
|
109
|
+
destroyed: Destroyed[];
|
|
110
|
+
expectedMovementCost: ExpectedMovementCost[];
|
|
111
|
+
halted: Halted[];
|
|
112
|
+
impassable: Impassable[];
|
|
113
|
+
lostAtSea: LostAtSea[];
|
|
114
|
+
playerAction: Action[];
|
|
115
|
+
stowed: Stowed[];
|
|
116
|
+
transferred: Transferred[];
|
|
117
|
+
unsupported: Unsupported[];
|
|
118
|
+
validateMove: ValidateMove[];
|
|
119
|
+
};
|
|
120
|
+
export type LifecycleRules = ReturnType<typeof lifecycleRules>;
|
|
121
|
+
export default lifecycleRules;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lifecycleRules = void 0;
|
|
4
|
+
const CityRegistry_1 = require("@civ-clone/core-city/CityRegistry");
|
|
5
|
+
const Engine_1 = require("@civ-clone/core-engine/Engine");
|
|
6
|
+
const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
|
|
7
|
+
const TransportRegistry_1 = require("@civ-clone/core-unit-transport/TransportRegistry");
|
|
8
|
+
const UnitImprovementRegistry_1 = require("@civ-clone/core-unit-improvement/UnitImprovementRegistry");
|
|
9
|
+
const UnitLossRegistry_1 = require("@civ-clone/core-unit/UnitLossRegistry");
|
|
10
|
+
const playerAction_1 = require("./playerAction");
|
|
11
|
+
const UnitRegistry_1 = require("@civ-clone/core-unit/UnitRegistry");
|
|
12
|
+
const created_1 = require("./created");
|
|
13
|
+
const destroyed_1 = require("./destroyed");
|
|
14
|
+
const canStow_1 = require("./canStow");
|
|
15
|
+
const activate_1 = require("./activate");
|
|
16
|
+
const lostAtSea_1 = require("./lostAtSea");
|
|
17
|
+
const defeated_1 = require("./defeated");
|
|
18
|
+
const impassable_1 = require("./impassable");
|
|
19
|
+
const validateMove_1 = require("./validateMove");
|
|
20
|
+
const Action_1 = require("@civ-clone/core-player/Rules/Action");
|
|
21
|
+
const Activate_1 = require("@civ-clone/core-unit/Rules/Activate");
|
|
22
|
+
const CanStow_1 = require("@civ-clone/core-unit-transport/Rules/CanStow");
|
|
23
|
+
const Created_1 = require("@civ-clone/core-unit/Rules/Created");
|
|
24
|
+
const Criterion_1 = require("@civ-clone/core-rule/Criterion");
|
|
25
|
+
const Defeated_1 = require("@civ-clone/core-unit/Rules/Defeated");
|
|
26
|
+
const Destroyed_1 = require("@civ-clone/core-unit/Rules/Destroyed");
|
|
27
|
+
const Effect_1 = require("@civ-clone/core-rule/Effect");
|
|
28
|
+
const ExpectedMovementCost_1 = require("@civ-clone/core-world-path/Rules/ExpectedMovementCost");
|
|
29
|
+
const Halted_1 = require("@civ-clone/base-unit-action-goto/Rules/Halted");
|
|
30
|
+
const Impassable_1 = require("@civ-clone/core-world-path/Rules/Impassable");
|
|
31
|
+
const LostAtSea_1 = require("@civ-clone/core-unit-transport/Rules/LostAtSea");
|
|
32
|
+
const Stowed_1 = require("@civ-clone/core-unit-transport/Rules/Stowed");
|
|
33
|
+
const Transferred_1 = require("@civ-clone/core-unit/Rules/Transferred");
|
|
34
|
+
const Unsupported_1 = require("@civ-clone/core-unit/Rules/Unsupported");
|
|
35
|
+
const ValidateMove_1 = require("@civ-clone/core-unit/Rules/ValidateMove");
|
|
36
|
+
const halted_1 = require("./halted");
|
|
37
|
+
const unsupported_1 = require("./unsupported");
|
|
38
|
+
const moveShortfall_1 = require("@civ-clone/base-unit-action-rules/lib/moveShortfall");
|
|
39
|
+
const core_random_1 = require("@civ-clone/core-random");
|
|
40
|
+
const stowed_1 = require("./stowed");
|
|
41
|
+
/**
|
|
42
|
+
* A ruleset's unit lifecycle rules, by the rule type they are, in the order each type's rules run. Their ids are
|
|
43
|
+
* `<prefix>:unit/<type>/<name>` (`<prefix>:player/action/<name>` for the player actions), as civ1-unit's are, so a
|
|
44
|
+
* ruleset registers each list where it wants it and keeps the ids another package may name.
|
|
45
|
+
*/
|
|
46
|
+
const lifecycleRules = (prefix, { cityRegistry = CityRegistry_1.instance, engine = Engine_1.instance, randomNumberGenerator = core_random_1.instance, ruleRegistry = RuleRegistry_1.instance, transportRegistry = TransportRegistry_1.instance, unitImprovementRegistry = UnitImprovementRegistry_1.instance, unitLossRegistry = UnitLossRegistry_1.instance, unitRegistry = UnitRegistry_1.instance, }, { ActiveUnit, InactiveUnit, removedOnActivate, stowable, partnerOf, shortfallFactor, shortStep, sparesStack, }) => {
|
|
47
|
+
const id = (name) => `${prefix}:unit/${name}`;
|
|
48
|
+
return {
|
|
49
|
+
activate: [
|
|
50
|
+
new Activate_1.default(id('activate/has-moves'), new Criterion_1.default(activate_1.hasMoves), new Effect_1.default(activate_1.setActive)),
|
|
51
|
+
new Activate_1.default(id('activate/clear-busy'), new Criterion_1.default(activate_1.isBusy), new Effect_1.default(activate_1.clearBusy)),
|
|
52
|
+
...removedOnActivate.map((UnitImprovementType) => new Activate_1.default(id(`activate/remove-improvement/${UnitImprovementType.name}`), new Criterion_1.default((0, activate_1.hasImprovement)(unitImprovementRegistry, UnitImprovementType)), new Effect_1.default((0, activate_1.removeImprovement)(unitImprovementRegistry, UnitImprovementType)))),
|
|
53
|
+
],
|
|
54
|
+
canStow: stowable.map((StowableUnitType) => new CanStow_1.default(id(`can-stow/${StowableUnitType.name}`), new Criterion_1.default((0, canStow_1.carriesDomain)(StowableUnitType)), new Effect_1.default((0, canStow_1.carriesUnit)(StowableUnitType)))),
|
|
55
|
+
created: [
|
|
56
|
+
new Created_1.default(id('created/register'), new Effect_1.default((0, created_1.registerUnit)(unitRegistry))),
|
|
57
|
+
new Created_1.default(id('created/apply-visibility'), new Effect_1.default(created_1.applyVisibility)),
|
|
58
|
+
new Created_1.default(id('created/set-moves'), new Effect_1.default(created_1.setMoves)),
|
|
59
|
+
new Created_1.default(id('created/emit'), new Effect_1.default((0, created_1.emitCreated)(engine))),
|
|
60
|
+
],
|
|
61
|
+
defeated: [
|
|
62
|
+
new Defeated_1.default(id('defeated/emit-and-destroy'), new Effect_1.default((0, defeated_1.emitAndDestroy)(engine, ruleRegistry))),
|
|
63
|
+
new Defeated_1.default(
|
|
64
|
+
// `Attack` processes `Defeated` for a losing attacker too; only a defeated defender takes its stack with it.
|
|
65
|
+
id('defeated/destroy-stack'), new Criterion_1.default(defeated_1.defended), new Criterion_1.default((0, defeated_1.notInCity)(cityRegistry)), ...(sparesStack === undefined
|
|
66
|
+
? []
|
|
67
|
+
: [new Criterion_1.default((0, defeated_1.notSpared)(sparesStack))]), new Criterion_1.default((0, defeated_1.hasStack)(unitRegistry)), new Effect_1.default((0, defeated_1.destroyStack)(unitRegistry, ruleRegistry))),
|
|
68
|
+
],
|
|
69
|
+
destroyed: [
|
|
70
|
+
new Destroyed_1.default(id('destroyed/emit'), new Effect_1.default((0, destroyed_1.emitDestroyed)(engine))),
|
|
71
|
+
new Destroyed_1.default(
|
|
72
|
+
// A loss, once, for a unit another player destroyed.
|
|
73
|
+
id('destroyed/count-loss'), new Criterion_1.default(destroyed_1.destroyedByAnother), new Criterion_1.default(destroyed_1.notYetDestroyed), new Effect_1.default((0, destroyed_1.countLoss)(unitLossRegistry))),
|
|
74
|
+
new Destroyed_1.default(id('destroyed/deactivate'), new Effect_1.default(destroyed_1.deactivate)),
|
|
75
|
+
new Destroyed_1.default(id('destroyed/remove-improvements'), new Effect_1.default((0, destroyed_1.removeImprovements)(unitImprovementRegistry))),
|
|
76
|
+
new Destroyed_1.default(id('destroyed/lose-cargo'), new Criterion_1.default((0, destroyed_1.carriesCargo)(transportRegistry)), new Effect_1.default((0, destroyed_1.loseCargo)(transportRegistry))),
|
|
77
|
+
new Destroyed_1.default(id('destroyed/leave-transport'), new Criterion_1.default((0, destroyed_1.isAboard)(transportRegistry)), new Effect_1.default((0, destroyed_1.leaveOwnTransport)(transportRegistry))),
|
|
78
|
+
],
|
|
79
|
+
expectedMovementCost: [
|
|
80
|
+
new ExpectedMovementCost_1.default(
|
|
81
|
+
// A step costing more than the unit's full moves is worth `max(m, c × shortfallFactor)` on average.
|
|
82
|
+
id('expected-movement-cost/shortfall'), new Criterion_1.default((unit, movementCost, movement) => movement > 0), new Effect_1.default((unit, movementCost, movement) => shortStep === undefined
|
|
83
|
+
? (0, moveShortfall_1.expectedCost)(movement, movementCost, shortfallFactor)
|
|
84
|
+
: shortStep.expectedCost(movement, movementCost))),
|
|
85
|
+
],
|
|
86
|
+
halted: [new Halted_1.default(id('halted/emit'), new Effect_1.default((0, halted_1.emitHalted)(engine)))],
|
|
87
|
+
impassable: partnerOf === undefined
|
|
88
|
+
? []
|
|
89
|
+
: [
|
|
90
|
+
new Impassable_1.default(
|
|
91
|
+
// Routes go round a treaty partner's land, so following one never breaks a treaty by accident.
|
|
92
|
+
id('impassable/treaty-partners-land'), new Criterion_1.default(impassable_1.fromLand), new Criterion_1.default((0, impassable_1.toPartnersLand)(partnerOf)), new Effect_1.default(impassable_1.impassable)),
|
|
93
|
+
],
|
|
94
|
+
lostAtSea: [
|
|
95
|
+
new LostAtSea_1.default(id('lost-at-sea/emit'), new Effect_1.default((0, lostAtSea_1.emitLostAtSea)(engine))),
|
|
96
|
+
new LostAtSea_1.default(
|
|
97
|
+
// Before `destroy`, while the manifests still say what's aboard.
|
|
98
|
+
id('lost-at-sea/count-loss'), new Effect_1.default((0, lostAtSea_1.countLosses)(unitLossRegistry, transportRegistry))),
|
|
99
|
+
new LostAtSea_1.default(id('lost-at-sea/destroy'), new Effect_1.default(lostAtSea_1.sink)),
|
|
100
|
+
],
|
|
101
|
+
playerAction: [
|
|
102
|
+
new Action_1.default(`${prefix}:player/action/active-units`, new Criterion_1.default((0, playerAction_1.hasActiveUnits)(unitRegistry)), new Effect_1.default((0, playerAction_1.activeUnits)(unitRegistry, ActiveUnit))),
|
|
103
|
+
new Action_1.default(`${prefix}:player/action/inactive-units`, new Criterion_1.default((0, playerAction_1.hasInactiveUnits)(unitRegistry)), new Effect_1.default((0, playerAction_1.inactiveUnits)(unitRegistry, InactiveUnit))),
|
|
104
|
+
],
|
|
105
|
+
stowed: [new Stowed_1.default(id('stowed/sleep'), new Effect_1.default(stowed_1.sleepAboard))],
|
|
106
|
+
transferred: [
|
|
107
|
+
new Transferred_1.default(id('transferred/emit'), new Effect_1.default((unit, player, previousPlayer) => {
|
|
108
|
+
engine.emit('unit:transferred', unit, player, previousPlayer);
|
|
109
|
+
})),
|
|
110
|
+
],
|
|
111
|
+
unsupported: [
|
|
112
|
+
new Unsupported_1.default(id('unsupported/emit'), new Effect_1.default((0, unsupported_1.emitUnsupported)(engine))),
|
|
113
|
+
],
|
|
114
|
+
validateMove: [
|
|
115
|
+
new ValidateMove_1.default(id('validate-move/enough-moves'), new Criterion_1.default(validateMove_1.hasMovesFor), new Effect_1.default(validateMove_1.spendMoves)),
|
|
116
|
+
new ValidateMove_1.default(id('validate-move/not-enough-moves'), new Criterion_1.default(validateMove_1.shortOfMovesFor), new Effect_1.default(shortStep === undefined
|
|
117
|
+
? (0, validateMove_1.spendShort)(shortfallFactor, randomNumberGenerator)
|
|
118
|
+
: (unit, movementCost) => {
|
|
119
|
+
const remaining = unit.moves().value(), movement = unit.movement().value();
|
|
120
|
+
unit.moves().set(0);
|
|
121
|
+
return shortStep.succeeds(unit, remaining, movement, movementCost, randomNumberGenerator);
|
|
122
|
+
})),
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
exports.lifecycleRules = lifecycleRules;
|
|
127
|
+
exports.default = exports.lifecycleRules;
|
|
128
|
+
//# sourceMappingURL=lifecycleRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycleRules.js","sourceRoot":"","sources":["lifecycleRules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,386 @@
|
|
|
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
|
+
SparesStack,
|
|
66
|
+
defended,
|
|
67
|
+
destroyStack,
|
|
68
|
+
emitAndDestroy,
|
|
69
|
+
hasStack,
|
|
70
|
+
notInCity,
|
|
71
|
+
notSpared,
|
|
72
|
+
} from './defeated';
|
|
73
|
+
import { fromLand, impassable, toPartnersLand } from './impassable';
|
|
74
|
+
import {
|
|
75
|
+
hasMovesFor,
|
|
76
|
+
shortOfMovesFor,
|
|
77
|
+
spendMoves,
|
|
78
|
+
spendShort,
|
|
79
|
+
} from './validateMove';
|
|
80
|
+
import Action from '@civ-clone/core-player/Rules/Action';
|
|
81
|
+
import Activate from '@civ-clone/core-unit/Rules/Activate';
|
|
82
|
+
import CanStow from '@civ-clone/core-unit-transport/Rules/CanStow';
|
|
83
|
+
import Created from '@civ-clone/core-unit/Rules/Created';
|
|
84
|
+
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
85
|
+
import Defeated from '@civ-clone/core-unit/Rules/Defeated';
|
|
86
|
+
import Destroyed from '@civ-clone/core-unit/Rules/Destroyed';
|
|
87
|
+
import Effect from '@civ-clone/core-rule/Effect';
|
|
88
|
+
import ExpectedMovementCost from '@civ-clone/core-world-path/Rules/ExpectedMovementCost';
|
|
89
|
+
import Halted from '@civ-clone/base-unit-action-goto/Rules/Halted';
|
|
90
|
+
import Impassable from '@civ-clone/core-world-path/Rules/Impassable';
|
|
91
|
+
import LostAtSea from '@civ-clone/core-unit-transport/Rules/LostAtSea';
|
|
92
|
+
import Player from '@civ-clone/core-player/Player';
|
|
93
|
+
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
94
|
+
import Stowed from '@civ-clone/core-unit-transport/Rules/Stowed';
|
|
95
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
96
|
+
import Transferred from '@civ-clone/core-unit/Rules/Transferred';
|
|
97
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
98
|
+
import UnitImprovement from '@civ-clone/core-unit-improvement/UnitImprovement';
|
|
99
|
+
import Unsupported from '@civ-clone/core-unit/Rules/Unsupported';
|
|
100
|
+
import ValidateMove from '@civ-clone/core-unit/Rules/ValidateMove';
|
|
101
|
+
import { emitHalted } from './halted';
|
|
102
|
+
import { emitUnsupported } from './unsupported';
|
|
103
|
+
import { expectedCost } from '@civ-clone/base-unit-action-rules/lib/moveShortfall';
|
|
104
|
+
import { instance as rngInstance } from '@civ-clone/core-random';
|
|
105
|
+
import { sleepAboard } from './stowed';
|
|
106
|
+
|
|
107
|
+
export interface LifecycleDependencies {
|
|
108
|
+
cityRegistry?: CityRegistry;
|
|
109
|
+
engine?: Engine;
|
|
110
|
+
randomNumberGenerator?: () => number;
|
|
111
|
+
ruleRegistry?: RuleRegistry;
|
|
112
|
+
transportRegistry?: TransportRegistry;
|
|
113
|
+
unitImprovementRegistry?: UnitImprovementRegistry;
|
|
114
|
+
unitLossRegistry?: UnitLossRegistry;
|
|
115
|
+
unitRegistry?: UnitRegistry;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface LifecycleBaseOptions {
|
|
119
|
+
// The player actions a unit that can take orders, and one that can't, is offered as.
|
|
120
|
+
ActiveUnit: UnitPlayerActionType<PlayerAction>;
|
|
121
|
+
InactiveUnit: UnitPlayerActionType<PlayerAction>;
|
|
122
|
+
// The unit improvements activating a unit takes off it (a Fortified state), each with a rule of its own.
|
|
123
|
+
removedOnActivate: (typeof UnitImprovement)[];
|
|
124
|
+
// The domains a transport may carry, each with a `CanStow` rule of its own (`Carries` traits).
|
|
125
|
+
stowable: (typeof Unit)[];
|
|
126
|
+
// Whose land `tile` is among `unit`'s treaty partners, or `null`: routes go round it. Without it, no rule is made.
|
|
127
|
+
partnerOf?: (unit: Unit, tile: Tile) => Player | null;
|
|
128
|
+
// Squares where a defeated defender's stack survives it, besides a city. Without it, only a city spares the stack.
|
|
129
|
+
sparesStack?: SparesStack;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* How a unit short of a step's moves fares: Civ1's `shortfallFactor` (`spendShort`), or a ruleset's own `shortStep`,
|
|
134
|
+
* which then needs no factor.
|
|
135
|
+
*/
|
|
136
|
+
export type LifecycleOptions = LifecycleBaseOptions &
|
|
137
|
+
(
|
|
138
|
+
| { shortfallFactor: number; shortStep?: undefined }
|
|
139
|
+
| { shortfallFactor?: number; shortStep: ShortStep }
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* A ruleset's own short step: whether `unit`, with `remaining` of its `movement` moves, gets onto a square costing
|
|
144
|
+
* `cost` (one draw of `randomNumberGenerator` at most), and what such a step is worth on average to route planning.
|
|
145
|
+
* The unit's moves are spent either way.
|
|
146
|
+
*/
|
|
147
|
+
export interface ShortStep {
|
|
148
|
+
succeeds: (
|
|
149
|
+
unit: Unit,
|
|
150
|
+
remaining: number,
|
|
151
|
+
movement: number,
|
|
152
|
+
cost: number,
|
|
153
|
+
randomNumberGenerator: () => number
|
|
154
|
+
) => boolean;
|
|
155
|
+
expectedCost: (movement: number, cost: number) => number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A ruleset's unit lifecycle rules, by the rule type they are, in the order each type's rules run. Their ids are
|
|
160
|
+
* `<prefix>:unit/<type>/<name>` (`<prefix>:player/action/<name>` for the player actions), as civ1-unit's are, so a
|
|
161
|
+
* ruleset registers each list where it wants it and keeps the ids another package may name.
|
|
162
|
+
*/
|
|
163
|
+
export const lifecycleRules = (
|
|
164
|
+
prefix: string,
|
|
165
|
+
{
|
|
166
|
+
cityRegistry = cityRegistryInstance,
|
|
167
|
+
engine = engineInstance,
|
|
168
|
+
randomNumberGenerator = rngInstance,
|
|
169
|
+
ruleRegistry = ruleRegistryInstance,
|
|
170
|
+
transportRegistry = transportRegistryInstance,
|
|
171
|
+
unitImprovementRegistry = unitImprovementRegistryInstance,
|
|
172
|
+
unitLossRegistry = unitLossRegistryInstance,
|
|
173
|
+
unitRegistry = unitRegistryInstance,
|
|
174
|
+
}: LifecycleDependencies,
|
|
175
|
+
{
|
|
176
|
+
ActiveUnit,
|
|
177
|
+
InactiveUnit,
|
|
178
|
+
removedOnActivate,
|
|
179
|
+
stowable,
|
|
180
|
+
partnerOf,
|
|
181
|
+
shortfallFactor,
|
|
182
|
+
shortStep,
|
|
183
|
+
sparesStack,
|
|
184
|
+
}: LifecycleOptions
|
|
185
|
+
) => {
|
|
186
|
+
const id = (name: string): string => `${prefix}:unit/${name}`;
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
activate: [
|
|
190
|
+
new Activate(
|
|
191
|
+
id('activate/has-moves'),
|
|
192
|
+
new Criterion(hasMoves),
|
|
193
|
+
new Effect(setActive)
|
|
194
|
+
),
|
|
195
|
+
new Activate(
|
|
196
|
+
id('activate/clear-busy'),
|
|
197
|
+
new Criterion(isBusy),
|
|
198
|
+
new Effect(clearBusy)
|
|
199
|
+
),
|
|
200
|
+
...removedOnActivate.map(
|
|
201
|
+
(UnitImprovementType: typeof UnitImprovement): Activate =>
|
|
202
|
+
new Activate(
|
|
203
|
+
id(`activate/remove-improvement/${UnitImprovementType.name}`),
|
|
204
|
+
new Criterion(
|
|
205
|
+
hasImprovement(unitImprovementRegistry, UnitImprovementType)
|
|
206
|
+
),
|
|
207
|
+
new Effect(
|
|
208
|
+
removeImprovement(unitImprovementRegistry, UnitImprovementType)
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
),
|
|
212
|
+
],
|
|
213
|
+
|
|
214
|
+
canStow: stowable.map(
|
|
215
|
+
(StowableUnitType: typeof Unit): CanStow =>
|
|
216
|
+
new CanStow(
|
|
217
|
+
id(`can-stow/${StowableUnitType.name}`),
|
|
218
|
+
new Criterion(carriesDomain(StowableUnitType)),
|
|
219
|
+
new Effect(carriesUnit(StowableUnitType))
|
|
220
|
+
)
|
|
221
|
+
),
|
|
222
|
+
|
|
223
|
+
created: [
|
|
224
|
+
new Created(
|
|
225
|
+
id('created/register'),
|
|
226
|
+
new Effect(registerUnit(unitRegistry))
|
|
227
|
+
),
|
|
228
|
+
new Created(id('created/apply-visibility'), new Effect(applyVisibility)),
|
|
229
|
+
new Created(id('created/set-moves'), new Effect(setMoves)),
|
|
230
|
+
new Created(id('created/emit'), new Effect(emitCreated(engine))),
|
|
231
|
+
],
|
|
232
|
+
|
|
233
|
+
defeated: [
|
|
234
|
+
new Defeated(
|
|
235
|
+
id('defeated/emit-and-destroy'),
|
|
236
|
+
new Effect(emitAndDestroy(engine, ruleRegistry))
|
|
237
|
+
),
|
|
238
|
+
new Defeated(
|
|
239
|
+
// `Attack` processes `Defeated` for a losing attacker too; only a defeated defender takes its stack with it.
|
|
240
|
+
id('defeated/destroy-stack'),
|
|
241
|
+
new Criterion(defended),
|
|
242
|
+
new Criterion(notInCity(cityRegistry)),
|
|
243
|
+
...(sparesStack === undefined
|
|
244
|
+
? []
|
|
245
|
+
: [new Criterion(notSpared(sparesStack))]),
|
|
246
|
+
new Criterion(hasStack(unitRegistry)),
|
|
247
|
+
new Effect(destroyStack(unitRegistry, ruleRegistry))
|
|
248
|
+
),
|
|
249
|
+
],
|
|
250
|
+
|
|
251
|
+
destroyed: [
|
|
252
|
+
new Destroyed(id('destroyed/emit'), new Effect(emitDestroyed(engine))),
|
|
253
|
+
new Destroyed(
|
|
254
|
+
// A loss, once, for a unit another player destroyed.
|
|
255
|
+
id('destroyed/count-loss'),
|
|
256
|
+
new Criterion(destroyedByAnother),
|
|
257
|
+
new Criterion(notYetDestroyed),
|
|
258
|
+
new Effect(countLoss(unitLossRegistry))
|
|
259
|
+
),
|
|
260
|
+
new Destroyed(id('destroyed/deactivate'), new Effect(deactivate)),
|
|
261
|
+
new Destroyed(
|
|
262
|
+
id('destroyed/remove-improvements'),
|
|
263
|
+
new Effect(removeImprovements(unitImprovementRegistry))
|
|
264
|
+
),
|
|
265
|
+
new Destroyed(
|
|
266
|
+
id('destroyed/lose-cargo'),
|
|
267
|
+
new Criterion(carriesCargo(transportRegistry)),
|
|
268
|
+
new Effect(loseCargo(transportRegistry))
|
|
269
|
+
),
|
|
270
|
+
new Destroyed(
|
|
271
|
+
id('destroyed/leave-transport'),
|
|
272
|
+
new Criterion(isAboard(transportRegistry)),
|
|
273
|
+
new Effect(leaveOwnTransport(transportRegistry))
|
|
274
|
+
),
|
|
275
|
+
],
|
|
276
|
+
|
|
277
|
+
expectedMovementCost: [
|
|
278
|
+
new ExpectedMovementCost(
|
|
279
|
+
// A step costing more than the unit's full moves is worth `max(m, c × shortfallFactor)` on average.
|
|
280
|
+
id('expected-movement-cost/shortfall'),
|
|
281
|
+
new Criterion(
|
|
282
|
+
(unit: Unit, movementCost: number, movement: number): boolean =>
|
|
283
|
+
movement > 0
|
|
284
|
+
),
|
|
285
|
+
new Effect(
|
|
286
|
+
(unit: Unit, movementCost: number, movement: number): number =>
|
|
287
|
+
shortStep === undefined
|
|
288
|
+
? expectedCost(movement, movementCost, shortfallFactor as number)
|
|
289
|
+
: shortStep.expectedCost(movement, movementCost)
|
|
290
|
+
)
|
|
291
|
+
),
|
|
292
|
+
],
|
|
293
|
+
|
|
294
|
+
halted: [new Halted(id('halted/emit'), new Effect(emitHalted(engine)))],
|
|
295
|
+
|
|
296
|
+
impassable:
|
|
297
|
+
partnerOf === undefined
|
|
298
|
+
? []
|
|
299
|
+
: [
|
|
300
|
+
new Impassable(
|
|
301
|
+
// Routes go round a treaty partner's land, so following one never breaks a treaty by accident.
|
|
302
|
+
id('impassable/treaty-partners-land'),
|
|
303
|
+
new Criterion(fromLand),
|
|
304
|
+
new Criterion(toPartnersLand(partnerOf)),
|
|
305
|
+
new Effect(impassable)
|
|
306
|
+
),
|
|
307
|
+
],
|
|
308
|
+
|
|
309
|
+
lostAtSea: [
|
|
310
|
+
new LostAtSea(id('lost-at-sea/emit'), new Effect(emitLostAtSea(engine))),
|
|
311
|
+
new LostAtSea(
|
|
312
|
+
// Before `destroy`, while the manifests still say what's aboard.
|
|
313
|
+
id('lost-at-sea/count-loss'),
|
|
314
|
+
new Effect(countLosses(unitLossRegistry, transportRegistry))
|
|
315
|
+
),
|
|
316
|
+
new LostAtSea(id('lost-at-sea/destroy'), new Effect(sink)),
|
|
317
|
+
],
|
|
318
|
+
|
|
319
|
+
playerAction: [
|
|
320
|
+
new Action(
|
|
321
|
+
`${prefix}:player/action/active-units`,
|
|
322
|
+
new Criterion(hasActiveUnits(unitRegistry)),
|
|
323
|
+
new Effect(activeUnits(unitRegistry, ActiveUnit))
|
|
324
|
+
),
|
|
325
|
+
new Action(
|
|
326
|
+
`${prefix}:player/action/inactive-units`,
|
|
327
|
+
new Criterion(hasInactiveUnits(unitRegistry)),
|
|
328
|
+
new Effect(inactiveUnits(unitRegistry, InactiveUnit))
|
|
329
|
+
),
|
|
330
|
+
],
|
|
331
|
+
|
|
332
|
+
stowed: [new Stowed(id('stowed/sleep'), new Effect(sleepAboard))],
|
|
333
|
+
|
|
334
|
+
transferred: [
|
|
335
|
+
new Transferred(
|
|
336
|
+
id('transferred/emit'),
|
|
337
|
+
new Effect(
|
|
338
|
+
(unit: Unit, player: Player, previousPlayer: Player): void => {
|
|
339
|
+
engine.emit('unit:transferred', unit, player, previousPlayer);
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
),
|
|
343
|
+
],
|
|
344
|
+
|
|
345
|
+
unsupported: [
|
|
346
|
+
new Unsupported(
|
|
347
|
+
id('unsupported/emit'),
|
|
348
|
+
new Effect(emitUnsupported(engine))
|
|
349
|
+
),
|
|
350
|
+
],
|
|
351
|
+
|
|
352
|
+
validateMove: [
|
|
353
|
+
new ValidateMove(
|
|
354
|
+
id('validate-move/enough-moves'),
|
|
355
|
+
new Criterion(hasMovesFor),
|
|
356
|
+
new Effect(spendMoves)
|
|
357
|
+
),
|
|
358
|
+
new ValidateMove(
|
|
359
|
+
id('validate-move/not-enough-moves'),
|
|
360
|
+
new Criterion(shortOfMovesFor),
|
|
361
|
+
new Effect(
|
|
362
|
+
shortStep === undefined
|
|
363
|
+
? spendShort(shortfallFactor as number, randomNumberGenerator)
|
|
364
|
+
: (unit: Unit, movementCost: number): boolean => {
|
|
365
|
+
const remaining = unit.moves().value(),
|
|
366
|
+
movement = unit.movement().value();
|
|
367
|
+
|
|
368
|
+
unit.moves().set(0);
|
|
369
|
+
|
|
370
|
+
return shortStep.succeeds(
|
|
371
|
+
unit,
|
|
372
|
+
remaining,
|
|
373
|
+
movement,
|
|
374
|
+
movementCost,
|
|
375
|
+
randomNumberGenerator
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
)
|
|
379
|
+
),
|
|
380
|
+
],
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export type LifecycleRules = ReturnType<typeof lifecycleRules>;
|
|
385
|
+
|
|
386
|
+
export default lifecycleRules;
|