@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,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":";;;AAAA,oEAG2C;AAC3C,0DAGuC;AACvC,uFAGuD;AACvD,oEAG2C;AAC3C,wFAG0D;AAC1D,oEAG2C;AAC3C,4EAA4E;AAE5E,2DAAoD;AACpD,+FAAwF;AACxF,kFAA2E;AAC3E,8DAAuD;AACvD,wDAAiD;AACjD,+DAAwD;AAExD,8EAAuE;AACvE,4DAAqD;AACrD,6FAAsF;AACtF,oEAA6D;AAE7D,+EAAwE;AACxE,2EAAoE;AAGpE,kEAAqE;AACrE,wDAAiE;AAyBjE;;;;GAIG;AACI,MAAM,UAAU,GAAG,CACxB,MAAc,EACd,EACE,YAAY,GAAG,uBAAoB,EACnC,MAAM,GAAG,iBAAc,EACvB,mBAAmB,GAAG,8BAA2B,EACjD,qBAAqB,GAAG,sBAAW,EACnC,YAAY,GAAG,uBAAoB,EACnC,iBAAiB,GAAG,4BAAyB,EAC7C,YAAY,GAAG,uBAAoB,GACjB,EACpB,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAgB,EAC7D,EAAE;IACX,MAAM,EAAE,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,GAAG,MAAM,eAAe,IAAI,EAAE;IACjE,iGAAiG;IACjG,OAAO,GAAG,CAAC,SAAyB,EAAE,KAAa,EAAQ,EAAE,CAC3D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAU,EAAQ,EAAE;QACjC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACxD,OAAO;SACR;QAED,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAElC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAM,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC;IACJ,sGAAsG;IACtG,cAAc,GAAG,CAAC,SAAe,EAAE,OAAa,SAAS,CAAC,IAAI,EAAE,EAAU,EAAE,CAC1E,YAAY;SACT,SAAS,CAAC,IAAI,CAAC;SACf,MAAM,CACL,CAAC,IAAU,EAAW,EAAE,CACtB,IAAI,KAAK,SAAS;QAClB,IAAI,CAAC,MAAM,EAAE,KAAK,SAAS,CAAC,MAAM,EAAE;QACpC,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CACnC;IACL,4GAA4G;IAC5G,cAAc,GAAG;QACf,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,YAAY,wBAAc,CAAC;QACtE,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,MAAM,YAAY,IAAI,CAChE;QACD,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE,CACrE;KACF,EACD,SAAS,GAAG,CAAC,IAAU,EAAW,EAAE,WAClC,OAAA,CAAA,MAAA,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,0CAAE,MAAM,EAAE,MAAK,IAAI,CAAC,MAAM,EAAE,CAAA,EAAA,CAAC;IAEpE,OAAO;QACL,IAAI,eAAK,CACP,EAAE,CAAC,MAAM,CAAC,EACV,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAE,MAAc,EAAQ,EAAE;YAC9C,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC,CAAC,CACH;QACD,IAAI,eAAK,CACP,EAAE,CAAC,kBAAkB,CAAC,EACtB,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CACzD;QACD,IAAI,eAAK,CACP,EAAE,CAAC,0BAA0B,CAAC,EAC9B,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,EAClE,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE;YAC9B,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CACH;QACD,IAAI,eAAK;QACP,gHAAgH;QAChH,wFAAwF;QACxF,EAAE,CAAC,YAAY,CAAC,EAChB,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,YAAY,wBAAc,CAAC,EACtE,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,MAAM,YAAY,IAAI,CAChE,EACD,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CACnC,IAAuB,CAAC,QAAQ,EAAE,CACpC,EACD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAE,MAAc,EAAQ,EAAE;;YAC9C,MAAM,SAAS,GAAG,IAAsB,EACtC,QAAQ,GACN,CAAA,MAAA,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,0CAAE,MAAM,EAAE,MAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAEtE,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,KAAW,EAAQ,EAAE;gBAC9C,IACE,QAAQ;oBACR,CAAC,CAAC,KAAK,YAAY,aAAG,CAAC;oBACvB,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,gBAAM,CAAC;oBACjC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,kBAAQ,CAAC,EACnC;oBACA,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAExB,OAAO;iBACR;gBAED,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CACH;QACD,wGAAwG;QACxG,IAAI,eAAK;QACP,gFAAgF;QAChF,EAAE,CAAC,kBAAkB,CAAC,EACtB,GAAG,cAAc,EACjB,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EACxD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE,CAC9B,OAAO,CACL,IAAsB,EACtB,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,CACzB,CAAC,QAAc,EAAW,EAAE,CAAC,QAAQ,YAAY,aAAG,CACrD,CACF,CACF,CACF;QACD,IAAI,eAAK;QACP,4FAA4F;QAC5F,EAAE,CAAC,kBAAkB,CAAC,EACtB,GAAG,cAAc,EACjB,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EACvD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE,CAC9B,OAAO,CACL,IAAsB,EACtB,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,CACzB,CAAC,QAAc,EAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,kBAAQ,CACjE,CACF,CACF,CACF;QACD,IAAI,eAAK,CACP,EAAE,CAAC,WAAW,CAAC,EACf,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,MAAM,YAAY,SAAS,CACrE,EACD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE;YAC9B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAEnD,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAElC,iBAAiB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CACH;QACD,IAAI,eAAK;QACP,kHAAkH;QAClH,wFAAwF;QACxF,EAAE,CAAC,UAAU,CAAC,EACd,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,MAAM,YAAY,IAAI,CAChE,EACD,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CAAC,CAAC,CAAC,MAAM,YAAY,SAAS,CAAC,CACxE,EACD,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EACvE,IAAI,mBAAS,CACX,CAAC,IAAU,EAAW,EAAE,CACtB,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE,CACvE,EACD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE;YAC9B,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAE3D,IAAI,IAAI,CAAC,IAAI,EAAE,YAAY,gBAAM,EAAE;gBACjC,IAAI,CAAC,OAAO,EAAE,CAAC;aAChB;QACH,CAAC,CAAC,CACH;QACD,IAAI,eAAK,CACP,EAAE,CAAC,qBAAqB,CAAC,EACzB,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAU,CAAC,CAAC,EACjE,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAClE,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,EAC9D,IAAI,mBAAS,CAAC,GAAY,EAAE,CAAC,qBAAqB,EAAE,IAAI,cAAc,CAAC,EACvE,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE;YAC9B,YAAY,CAAC,OAAO,CAAC,mBAAS,EAAE,IAA6B,CAAC,CAAC;QACjE,CAAC,CAAC,CACH;QACD,IAAI,eAAK;QACP,mEAAmE;QACnE,EAAE,CAAC,8BAA8B,CAAC,EAClC,IAAI,cAAI,EAAE,EACV,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,wBAAc,CAAC,CAAC,EACrE,IAAI,mBAAS,CAAC,CAAC,IAAU,EAAE,MAAc,EAAW,EAAE,CACpD,OAAO,CAAC,IAAI,CACV,CAAC,UAAsB,EAAW,EAAE,CAAC,MAAM,YAAY,UAAU,CAClE,CACF,EACD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAQ,EAAE;YAC9B,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CACH;QACD,IAAI,eAAK,CACP,EAAE,CAAC,oBAAoB,CAAC;QACxB,oFAAoF;QACpF,IAAI,mBAAS,CACX,CAAC,IAAU,EAAE,MAAc,EAAE,EAAE,CAC7B,OAAQ,MAAyC,CAAC,KAAK,KAAK,UAAU,CACzE,EACD,IAAI,gBAAM,CAAC,CAAC,IAAU,EAAE,MAAc,EAAE,EAAE;YACxC,MAAM,KAAK,GAAI,MAAyC,CAAC,KAAK,EAAE,EAC9D,aAAa,GAAG,mBAAmB;iBAChC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC;iBAClC,MAAM,CACL,CAAC,WAAW,EAAwB,EAAE,CACpC,WAAW,YAAY,oBAAK,IAAI,WAAW,CAAC,MAAM,EAAE,CACvD,CAAC;YAEN,aAAa,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CACpC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAClC,CAAC;YAEF,gHAAgH;YAChH,IACE,aAAa,CAAC,MAAM,GAAG,CAAC;gBACxB,CAAC,UAAU,CAAC,IAAI,CACd,CAAC,UAAsB,EAAW,EAAE,CAAC,MAAM,YAAY,UAAU,CAClE;gBACD,CAAC,IAAA,mBAAW,EAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,EACvD;gBACA,mBAAmB,CAAC,QAAQ,CAC1B,IAAI,uBAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,eAAK,EAAE,EAAE,YAAY,CAAC,CAC9D,CAAC;aACH;QACH,CAAC,CAAC,CACH;KACF,CAAC;AACJ,CAAC,CAAC;AAhOW,QAAA,UAAU,cAgOrB;AAEF,kBAAe,kBAAU,CAAC"}
|
|
@@ -0,0 +1,300 @@
|
|
|
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
|
+
InteractionRegistry,
|
|
11
|
+
instance as interactionRegistryInstance,
|
|
12
|
+
} from '@civ-clone/core-diplomacy/InteractionRegistry';
|
|
13
|
+
import {
|
|
14
|
+
RuleRegistry,
|
|
15
|
+
instance as ruleRegistryInstance,
|
|
16
|
+
} from '@civ-clone/core-rule/RuleRegistry';
|
|
17
|
+
import {
|
|
18
|
+
TransportRegistry,
|
|
19
|
+
instance as transportRegistryInstance,
|
|
20
|
+
} from '@civ-clone/core-unit-transport/TransportRegistry';
|
|
21
|
+
import {
|
|
22
|
+
UnitRegistry,
|
|
23
|
+
instance as unitRegistryInstance,
|
|
24
|
+
} from '@civ-clone/core-unit/UnitRegistry';
|
|
25
|
+
import { Peace, Vendetta } from '@civ-clone/library-diplomacy/Declarations';
|
|
26
|
+
import Action from '@civ-clone/core-unit/Action';
|
|
27
|
+
import Air from '@civ-clone/base-unit-type-air/Air';
|
|
28
|
+
import AttackEndsTurn from '@civ-clone/base-unit-trait-attack-ends-turn/AttackEndsTurn';
|
|
29
|
+
import CoastBound from '@civ-clone/base-unit-trait-coast-bound/CoastBound';
|
|
30
|
+
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
31
|
+
import Effect from '@civ-clone/core-rule/Effect';
|
|
32
|
+
import High from '@civ-clone/core-rule/Priorities/High';
|
|
33
|
+
import { ITransport } from '@civ-clone/core-unit-transport/Transport';
|
|
34
|
+
import LostAtSea from '@civ-clone/core-unit-transport/Rules/LostAtSea';
|
|
35
|
+
import Moved from '@civ-clone/core-unit/Rules/Moved';
|
|
36
|
+
import NavalTransport from '@civ-clone/base-unit-type-naval-transport/NavalTransport';
|
|
37
|
+
import Never from '@civ-clone/core-diplomacy/Expiries/Never';
|
|
38
|
+
import Player from '@civ-clone/core-player/Player';
|
|
39
|
+
import Sleeping from '@civ-clone/base-unit-action-sleep/Rules/Sleeping';
|
|
40
|
+
import Stowed from '@civ-clone/base-unit-action-embark/Busy/Stowed';
|
|
41
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
42
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
43
|
+
import { hasVendetta } from '@civ-clone/library-diplomacy/lib/holds';
|
|
44
|
+
import { instance as rngInstance } from '@civ-clone/core-random';
|
|
45
|
+
|
|
46
|
+
type ActionType = abstract new (...args: any[]) => Action;
|
|
47
|
+
|
|
48
|
+
export interface MovedDependencies {
|
|
49
|
+
cityRegistry?: CityRegistry;
|
|
50
|
+
engine?: Engine;
|
|
51
|
+
interactionRegistry?: InteractionRegistry;
|
|
52
|
+
randomNumberGenerator?: () => number;
|
|
53
|
+
ruleRegistry?: RuleRegistry;
|
|
54
|
+
transportRegistry?: TransportRegistry;
|
|
55
|
+
unitRegistry?: UnitRegistry;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface MovedOptions {
|
|
59
|
+
// The ruleset's own action classes: a plain move, leaving a transport, and the attacks.
|
|
60
|
+
Move: ActionType;
|
|
61
|
+
Disembark: ActionType;
|
|
62
|
+
attacks: ActionType[];
|
|
63
|
+
// Actions with an enemy that end a peace treaty without a grudge (walking onto, or pillaging, the partner's land).
|
|
64
|
+
noVendetta: ActionType[];
|
|
65
|
+
// The chance a `CoastBound` unit that ends its move out of sight of land is lost.
|
|
66
|
+
coastBoundLoss: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A ruleset's `Moved` rules, in order, with ids `<prefix>:unit/moved/<name>` as civ1-unit's are: telling the client,
|
|
71
|
+
* vision, ending a unit's moves, transports carrying and collecting their cargo, leaving a transport, a `CoastBound`
|
|
72
|
+
* unit lost at sea, an `AttackEndsTurn` unit's turn ending, and a broken peace treaty.
|
|
73
|
+
*/
|
|
74
|
+
export const movedRules = (
|
|
75
|
+
prefix: string,
|
|
76
|
+
{
|
|
77
|
+
cityRegistry = cityRegistryInstance,
|
|
78
|
+
engine = engineInstance,
|
|
79
|
+
interactionRegistry = interactionRegistryInstance,
|
|
80
|
+
randomNumberGenerator = rngInstance,
|
|
81
|
+
ruleRegistry = ruleRegistryInstance,
|
|
82
|
+
transportRegistry = transportRegistryInstance,
|
|
83
|
+
unitRegistry = unitRegistryInstance,
|
|
84
|
+
}: MovedDependencies,
|
|
85
|
+
{ Move, Disembark, attacks, noVendetta, coastBoundLoss }: MovedOptions
|
|
86
|
+
): Moved[] => {
|
|
87
|
+
const id = (name: string): string => `${prefix}:unit/moved/${name}`,
|
|
88
|
+
// Stows each of `units` aboard `transport`, as `Embark` and `LandAircraft` do, until it is full.
|
|
89
|
+
collect = (transport: NavalTransport, units: Unit[]): void =>
|
|
90
|
+
units.forEach((unit: Unit): void => {
|
|
91
|
+
if (!transport.hasCapacity() || !transport.canStow(unit)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
transport.stow(unit, unit.tile());
|
|
96
|
+
|
|
97
|
+
unit.setBusy(new Stowed());
|
|
98
|
+
}),
|
|
99
|
+
// The transport's own units on the tile it has just moved onto, that aren't already aboard something.
|
|
100
|
+
unitsToCollect = (transport: Unit, tile: Tile = transport.tile()): Unit[] =>
|
|
101
|
+
unitRegistry
|
|
102
|
+
.getByTile(tile)
|
|
103
|
+
.filter(
|
|
104
|
+
(unit: Unit): boolean =>
|
|
105
|
+
unit !== transport &&
|
|
106
|
+
unit.player() === transport.player() &&
|
|
107
|
+
!transportRegistry.hasUnit(unit)
|
|
108
|
+
),
|
|
109
|
+
// A transport's own move that took it onto a new tile (not one it was carried along on, and not an attack).
|
|
110
|
+
transportMoved = [
|
|
111
|
+
new Criterion((unit: Unit): boolean => unit instanceof NavalTransport),
|
|
112
|
+
new Criterion(
|
|
113
|
+
(unit: Unit, action: Action): boolean => action instanceof Move
|
|
114
|
+
),
|
|
115
|
+
new Criterion(
|
|
116
|
+
(unit: Unit, action: Action): boolean => action.to() === unit.tile()
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
isOwnCity = (unit: Unit): boolean =>
|
|
120
|
+
cityRegistry.getByTile(unit.tile())?.player() === unit.player();
|
|
121
|
+
|
|
122
|
+
return [
|
|
123
|
+
new Moved(
|
|
124
|
+
id('emit'),
|
|
125
|
+
new Effect((unit: Unit, action: Action): void => {
|
|
126
|
+
engine.emit('unit:moved', unit, action);
|
|
127
|
+
})
|
|
128
|
+
),
|
|
129
|
+
new Moved(
|
|
130
|
+
id('apply-visibility'),
|
|
131
|
+
new Effect((unit: Unit): void => unit.applyVisibility())
|
|
132
|
+
),
|
|
133
|
+
new Moved(
|
|
134
|
+
id('end-moves-when-exhausted'),
|
|
135
|
+
new Criterion((unit: Unit): boolean => unit.moves().value() < 0.3),
|
|
136
|
+
new Effect((unit: Unit): void => {
|
|
137
|
+
unit.moves().set(0);
|
|
138
|
+
unit.setActive(false);
|
|
139
|
+
})
|
|
140
|
+
),
|
|
141
|
+
new Moved(
|
|
142
|
+
// A transport leaving one of its player's cities takes only the units sentried aboard (`Stowed` or `Sleeping`);
|
|
143
|
+
// at sea, all of its cargo goes with it, and so does an aircraft aboard, awake or not.
|
|
144
|
+
id('move-cargo'),
|
|
145
|
+
new Criterion((unit: Unit): boolean => unit instanceof NavalTransport),
|
|
146
|
+
new Criterion(
|
|
147
|
+
(unit: Unit, action: Action): boolean => action instanceof Move
|
|
148
|
+
),
|
|
149
|
+
new Criterion((unit: Unit): boolean =>
|
|
150
|
+
(unit as NavalTransport).hasCargo()
|
|
151
|
+
),
|
|
152
|
+
new Effect((unit: Unit, action: Action): void => {
|
|
153
|
+
const transport = unit as NavalTransport,
|
|
154
|
+
leftPort =
|
|
155
|
+
cityRegistry.getByTile(action.from())?.player() === unit.player();
|
|
156
|
+
|
|
157
|
+
transport.cargo().forEach((cargo: Unit): void => {
|
|
158
|
+
if (
|
|
159
|
+
leftPort &&
|
|
160
|
+
!(cargo instanceof Air) &&
|
|
161
|
+
!(cargo.busy() instanceof Stowed) &&
|
|
162
|
+
!(cargo.busy() instanceof Sleeping)
|
|
163
|
+
) {
|
|
164
|
+
transport.unload(cargo);
|
|
165
|
+
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
cargo.action(action.forUnit(cargo));
|
|
170
|
+
});
|
|
171
|
+
})
|
|
172
|
+
),
|
|
173
|
+
// These two come after `move-cargo`, so a unit collected here isn't also sent along the move just made.
|
|
174
|
+
new Moved(
|
|
175
|
+
// A carrier that moves onto its player's aircraft outside a city picks them up.
|
|
176
|
+
id('collect-aircraft'),
|
|
177
|
+
...transportMoved,
|
|
178
|
+
new Criterion((unit: Unit): boolean => !isOwnCity(unit)),
|
|
179
|
+
new Effect((unit: Unit): void =>
|
|
180
|
+
collect(
|
|
181
|
+
unit as NavalTransport,
|
|
182
|
+
unitsToCollect(unit).filter(
|
|
183
|
+
(tileUnit: Unit): boolean => tileUnit instanceof Air
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
),
|
|
188
|
+
new Moved(
|
|
189
|
+
// A transport that moves into one of its player's cities picks up the units sleeping there.
|
|
190
|
+
id('collect-sleeping'),
|
|
191
|
+
...transportMoved,
|
|
192
|
+
new Criterion((unit: Unit): boolean => isOwnCity(unit)),
|
|
193
|
+
new Effect((unit: Unit): void =>
|
|
194
|
+
collect(
|
|
195
|
+
unit as NavalTransport,
|
|
196
|
+
unitsToCollect(unit).filter(
|
|
197
|
+
(tileUnit: Unit): boolean => tileUnit.busy() instanceof Sleeping
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
),
|
|
202
|
+
new Moved(
|
|
203
|
+
id('disembark'),
|
|
204
|
+
new Criterion(
|
|
205
|
+
(unit: Unit, action: Action): boolean => action instanceof Disembark
|
|
206
|
+
),
|
|
207
|
+
new Effect((unit: Unit): void => {
|
|
208
|
+
const manifest = transportRegistry.getByUnit(unit);
|
|
209
|
+
|
|
210
|
+
manifest.transport().unload(unit);
|
|
211
|
+
|
|
212
|
+
transportRegistry.unregister(manifest);
|
|
213
|
+
})
|
|
214
|
+
),
|
|
215
|
+
new Moved(
|
|
216
|
+
// A unit that leaves its transport's tile with a plain move (an aircraft taking off, a unit walking off a ship in
|
|
217
|
+
// a city) is no longer aboard. A `Disembark` is a move too, which `disembark` unloads.
|
|
218
|
+
id('take-off'),
|
|
219
|
+
new Criterion(
|
|
220
|
+
(unit: Unit, action: Action): boolean => action instanceof Move
|
|
221
|
+
),
|
|
222
|
+
new Criterion(
|
|
223
|
+
(unit: Unit, action: Action): boolean => !(action instanceof Disembark)
|
|
224
|
+
),
|
|
225
|
+
new Criterion((unit: Unit): boolean => transportRegistry.hasUnit(unit)),
|
|
226
|
+
new Criterion(
|
|
227
|
+
(unit: Unit): boolean =>
|
|
228
|
+
transportRegistry.getByUnit(unit).transport().tile() !== unit.tile()
|
|
229
|
+
),
|
|
230
|
+
new Effect((unit: Unit): void => {
|
|
231
|
+
transportRegistry.getByUnit(unit).transport().unload(unit);
|
|
232
|
+
|
|
233
|
+
if (unit.busy() instanceof Stowed) {
|
|
234
|
+
unit.setBusy();
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
),
|
|
238
|
+
new Moved(
|
|
239
|
+
id('trireme-lost-at-sea'),
|
|
240
|
+
new Criterion((unit: Unit): boolean => unit.hasTrait(CoastBound)),
|
|
241
|
+
new Criterion((unit: Unit): boolean => unit.moves().value() === 0),
|
|
242
|
+
new Criterion((unit: Unit): boolean => !unit.tile().isCoast()),
|
|
243
|
+
new Criterion((): boolean => randomNumberGenerator() <= coastBoundLoss),
|
|
244
|
+
new Effect((unit: Unit): void => {
|
|
245
|
+
ruleRegistry.process(LostAtSea, unit as unknown as ITransport);
|
|
246
|
+
})
|
|
247
|
+
),
|
|
248
|
+
new Moved(
|
|
249
|
+
// A unit that `AttackEndsTurn` can't attack again until next turn.
|
|
250
|
+
id('bomber/end-turn-after-attack'),
|
|
251
|
+
new High(),
|
|
252
|
+
new Criterion((unit: Unit): boolean => unit.hasTrait(AttackEndsTurn)),
|
|
253
|
+
new Criterion((unit: Unit, action: Action): boolean =>
|
|
254
|
+
attacks.some(
|
|
255
|
+
(AttackType: ActionType): boolean => action instanceof AttackType
|
|
256
|
+
)
|
|
257
|
+
),
|
|
258
|
+
new Effect((unit: Unit): void => {
|
|
259
|
+
unit.moves().set(0);
|
|
260
|
+
unit.setActive(false);
|
|
261
|
+
})
|
|
262
|
+
),
|
|
263
|
+
new Moved(
|
|
264
|
+
id('break-peace-treaty'),
|
|
265
|
+
// Any action with an enemy: a sneak attack or capture, or a covert action at peace.
|
|
266
|
+
new Criterion(
|
|
267
|
+
(unit: Unit, action: Action) =>
|
|
268
|
+
typeof (action as unknown as { enemy?: unknown }).enemy === 'function'
|
|
269
|
+
),
|
|
270
|
+
new Effect((unit: Unit, action: Action) => {
|
|
271
|
+
const enemy = (action as unknown as { enemy(): Player }).enemy(),
|
|
272
|
+
peaceTreaties = interactionRegistry
|
|
273
|
+
.getByPlayers(unit.player(), enemy)
|
|
274
|
+
.filter(
|
|
275
|
+
(interaction): interaction is Peace =>
|
|
276
|
+
interaction instanceof Peace && interaction.active()
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
peaceTreaties.forEach((peaceTreaty) =>
|
|
280
|
+
peaceTreaty.expire(unit.player())
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// The victim of an attack in peacetime holds a grudge; walking onto or pillaging its land only ends the treaty.
|
|
284
|
+
if (
|
|
285
|
+
peaceTreaties.length > 0 &&
|
|
286
|
+
!noVendetta.some(
|
|
287
|
+
(ActionType: ActionType): boolean => action instanceof ActionType
|
|
288
|
+
) &&
|
|
289
|
+
!hasVendetta(enemy, unit.player(), interactionRegistry)
|
|
290
|
+
) {
|
|
291
|
+
interactionRegistry.register(
|
|
292
|
+
new Vendetta(enemy, unit.player(), new Never(), ruleRegistry)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
),
|
|
297
|
+
];
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export default movedRules;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { InteractionRegistry } from '@civ-clone/core-diplomacy/InteractionRegistry';
|
|
2
|
+
import { TileImprovementRegistry } from '@civ-clone/core-tile-improvement/TileImprovementRegistry';
|
|
3
|
+
import { WorkedTileRegistry } from '@civ-clone/core-city/WorkedTileRegistry';
|
|
4
|
+
import Player from '@civ-clone/core-player/Player';
|
|
5
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
6
|
+
import TileImprovement from '@civ-clone/core-tile-improvement/TileImprovement';
|
|
7
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
8
|
+
export interface TreatyPartnerDependencies {
|
|
9
|
+
interactionRegistry?: InteractionRegistry;
|
|
10
|
+
tileImprovementRegistry?: TileImprovementRegistry;
|
|
11
|
+
workedTileRegistry?: WorkedTileRegistry;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Whose land a tile is, among a unit's treaty partners, or `null`: a tile that one of the partner's cities is working
|
|
15
|
+
* and that has one of `improvements` on it, for a land unit that isn't a `NonCombatant`. Moving onto or pillaging such
|
|
16
|
+
* a tile breaks the treaty. A tile counts as land while it's worked (Civ1's territory model, simplified).
|
|
17
|
+
*/
|
|
18
|
+
export declare const treatyPartner: (
|
|
19
|
+
improvements: (typeof TileImprovement)[],
|
|
20
|
+
{
|
|
21
|
+
interactionRegistry,
|
|
22
|
+
tileImprovementRegistry,
|
|
23
|
+
workedTileRegistry,
|
|
24
|
+
}?: TreatyPartnerDependencies
|
|
25
|
+
) => (unit: Unit, tile: Tile) => Player | null;
|
|
26
|
+
export default treatyPartner;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.treatyPartner = void 0;
|
|
4
|
+
const InteractionRegistry_1 = require("@civ-clone/core-diplomacy/InteractionRegistry");
|
|
5
|
+
const TileImprovementRegistry_1 = require("@civ-clone/core-tile-improvement/TileImprovementRegistry");
|
|
6
|
+
const WorkedTileRegistry_1 = require("@civ-clone/core-city/WorkedTileRegistry");
|
|
7
|
+
const Land_1 = require("@civ-clone/base-unit-type-land/Land");
|
|
8
|
+
const NonCombatant_1 = require("@civ-clone/base-unit-trait-non-combatant/NonCombatant");
|
|
9
|
+
const Declarations_1 = require("@civ-clone/library-diplomacy/Declarations");
|
|
10
|
+
const traits_1 = require("@civ-clone/core-unit/traits");
|
|
11
|
+
/**
|
|
12
|
+
* Whose land a tile is, among a unit's treaty partners, or `null`: a tile that one of the partner's cities is working
|
|
13
|
+
* and that has one of `improvements` on it, for a land unit that isn't a `NonCombatant`. Moving onto or pillaging such
|
|
14
|
+
* a tile breaks the treaty. A tile counts as land while it's worked (Civ1's territory model, simplified).
|
|
15
|
+
*/
|
|
16
|
+
const treatyPartner = (improvements, { interactionRegistry = InteractionRegistry_1.instance, tileImprovementRegistry = TileImprovementRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, } = {}) => (unit, tile) => {
|
|
17
|
+
if (!(0, traits_1.isA)(unit, Land_1.default) || unit.hasTrait(NonCombatant_1.default)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const workedTile = workedTileRegistry.getByTile(tile);
|
|
21
|
+
if (workedTile === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const partner = workedTile.city().player();
|
|
25
|
+
if (partner === unit.player() ||
|
|
26
|
+
!interactionRegistry
|
|
27
|
+
.getByPlayers(unit.player(), partner)
|
|
28
|
+
.some((interaction) => interaction instanceof Declarations_1.Peace && interaction.active()) ||
|
|
29
|
+
!tileImprovementRegistry
|
|
30
|
+
.getByTile(tile)
|
|
31
|
+
.some((improvement) => improvements.some((Improvement) => improvement instanceof Improvement))) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return partner;
|
|
35
|
+
};
|
|
36
|
+
exports.treatyPartner = treatyPartner;
|
|
37
|
+
exports.default = exports.treatyPartner;
|
|
38
|
+
//# sourceMappingURL=treatyPartner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treatyPartner.js","sourceRoot":"","sources":["treatyPartner.ts"],"names":[],"mappings":";;;AAAA,uFAGuD;AACvD,sGAGkE;AAClE,gFAGiD;AACjD,8DAAuD;AACvD,wFAAiF;AACjF,4EAAkE;AAKlE,wDAAkD;AAQlD;;;;GAIG;AACI,MAAM,aAAa,GACxB,CACE,YAAwC,EACxC,EACE,mBAAmB,GAAG,8BAA2B,EACjD,uBAAuB,GAAG,kCAA+B,EACzD,kBAAkB,GAAG,6BAA0B,MAClB,EAAE,EACjC,EAAE,CACJ,CAAC,IAAU,EAAE,IAAU,EAAiB,EAAE;IACxC,IAAI,CAAC,IAAA,YAAG,EAAC,IAAI,EAAE,cAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,sBAAY,CAAC,EAAE;QACnD,OAAO,IAAI,CAAC;KACb;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEtD,IAAI,UAAU,KAAK,IAAI,EAAE;QACvB,OAAO,IAAI,CAAC;KACb;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;IAE3C,IACE,OAAO,KAAK,IAAI,CAAC,MAAM,EAAE;QACzB,CAAC,mBAAmB;aACjB,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC;aACpC,IAAI,CACH,CAAC,WAAW,EAAwB,EAAE,CACpC,WAAW,YAAY,oBAAK,IAAI,WAAW,CAAC,MAAM,EAAE,CACvD;QACH,CAAC,uBAAuB;aACrB,SAAS,CAAC,IAAI,CAAC;aACf,IAAI,CAAC,CAAC,WAA4B,EAAW,EAAE,CAC9C,YAAY,CAAC,IAAI,CACf,CAAC,WAAmC,EAAW,EAAE,CAC/C,WAAW,YAAY,WAAW,CACrC,CACF,EACH;QACA,OAAO,IAAI,CAAC;KACb;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AA3CS,QAAA,aAAa,iBA2CtB;AAEJ,kBAAe,qBAAa,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InteractionRegistry,
|
|
3
|
+
instance as interactionRegistryInstance,
|
|
4
|
+
} from '@civ-clone/core-diplomacy/InteractionRegistry';
|
|
5
|
+
import {
|
|
6
|
+
TileImprovementRegistry,
|
|
7
|
+
instance as tileImprovementRegistryInstance,
|
|
8
|
+
} from '@civ-clone/core-tile-improvement/TileImprovementRegistry';
|
|
9
|
+
import {
|
|
10
|
+
WorkedTileRegistry,
|
|
11
|
+
instance as workedTileRegistryInstance,
|
|
12
|
+
} from '@civ-clone/core-city/WorkedTileRegistry';
|
|
13
|
+
import Land from '@civ-clone/base-unit-type-land/Land';
|
|
14
|
+
import NonCombatant from '@civ-clone/base-unit-trait-non-combatant/NonCombatant';
|
|
15
|
+
import { Peace } from '@civ-clone/library-diplomacy/Declarations';
|
|
16
|
+
import Player from '@civ-clone/core-player/Player';
|
|
17
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
18
|
+
import TileImprovement from '@civ-clone/core-tile-improvement/TileImprovement';
|
|
19
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
20
|
+
import { isA } from '@civ-clone/core-unit/traits';
|
|
21
|
+
|
|
22
|
+
export interface TreatyPartnerDependencies {
|
|
23
|
+
interactionRegistry?: InteractionRegistry;
|
|
24
|
+
tileImprovementRegistry?: TileImprovementRegistry;
|
|
25
|
+
workedTileRegistry?: WorkedTileRegistry;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whose land a tile is, among a unit's treaty partners, or `null`: a tile that one of the partner's cities is working
|
|
30
|
+
* and that has one of `improvements` on it, for a land unit that isn't a `NonCombatant`. Moving onto or pillaging such
|
|
31
|
+
* a tile breaks the treaty. A tile counts as land while it's worked (Civ1's territory model, simplified).
|
|
32
|
+
*/
|
|
33
|
+
export const treatyPartner =
|
|
34
|
+
(
|
|
35
|
+
improvements: (typeof TileImprovement)[],
|
|
36
|
+
{
|
|
37
|
+
interactionRegistry = interactionRegistryInstance,
|
|
38
|
+
tileImprovementRegistry = tileImprovementRegistryInstance,
|
|
39
|
+
workedTileRegistry = workedTileRegistryInstance,
|
|
40
|
+
}: TreatyPartnerDependencies = {}
|
|
41
|
+
) =>
|
|
42
|
+
(unit: Unit, tile: Tile): Player | null => {
|
|
43
|
+
if (!isA(unit, Land) || unit.hasTrait(NonCombatant)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const workedTile = workedTileRegistry.getByTile(tile);
|
|
48
|
+
|
|
49
|
+
if (workedTile === null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const partner = workedTile.city().player();
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
partner === unit.player() ||
|
|
57
|
+
!interactionRegistry
|
|
58
|
+
.getByPlayers(unit.player(), partner)
|
|
59
|
+
.some(
|
|
60
|
+
(interaction): interaction is Peace =>
|
|
61
|
+
interaction instanceof Peace && interaction.active()
|
|
62
|
+
) ||
|
|
63
|
+
!tileImprovementRegistry
|
|
64
|
+
.getByTile(tile)
|
|
65
|
+
.some((improvement: TileImprovement): boolean =>
|
|
66
|
+
improvements.some(
|
|
67
|
+
(Improvement: typeof TileImprovement): boolean =>
|
|
68
|
+
improvement instanceof Improvement
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return partner;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default treatyPartner;
|