@civ-clone/base-city-happiness-status 0.1.0 → 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.
- package/README.md +5 -0
- package/lib/statusTurn.d.ts +70 -0
- package/lib/statusTurn.js +50 -0
- package/lib/statusTurn.js.map +1 -0
- package/lib/statusTurn.ts +142 -0
- package/package.json +5 -2
- package/tests/statusTurn.test.ts +187 -0
package/README.md
CHANGED
|
@@ -11,6 +11,11 @@ is a `core-pending-effect` `PendingEffect` on the city, named by the ruleset's o
|
|
|
11
11
|
- `registerCityStatusHandler(handler, event, pendingEffects?, engine?)`: emits `event` with the city when its status
|
|
12
12
|
ends. Register it when the rules are created, so a status loaded from a save can still end.
|
|
13
13
|
- `forgetCityStatuses(city, handlers, pendingEffects?)`: drops statuses without ending them, for a city that's gone.
|
|
14
|
+
- `lib/statusTurn`: `cityStatusTurn(cities, options)` checks each city's `CivilDisorder` and `CelebrateLeader` rules
|
|
15
|
+
on one set of yields, emits `city:civil-disorder` and `city:leader-celebration`, and records or ends both statuses.
|
|
16
|
+
The ruleset passes its handlers and its own turns of the rule through hooks: `onDisorderContinues` (a second turn of
|
|
17
|
+
disorder, such as a Democracy falling), `onCelebrationChecked` (with whether the city celebrates again, for growth),
|
|
18
|
+
`forgetCelebration` (drop rather than end) and `celebrationSurvivesDisorder`.
|
|
14
19
|
|
|
15
20
|
A library: it registers no rules. See `civ1-city-happiness` for Civilization's civil disorder and "We Love the King
|
|
16
21
|
Day".
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PendingEffect,
|
|
3
|
+
PendingEffectRegistry,
|
|
4
|
+
} from '@civ-clone/core-pending-effect';
|
|
5
|
+
import City from '@civ-clone/core-city/City';
|
|
6
|
+
import { Engine } from '@civ-clone/core-engine/Engine';
|
|
7
|
+
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
8
|
+
import Yield from '@civ-clone/core-yield/Yield';
|
|
9
|
+
/** What a city's check found this turn, and what was recorded before it. */
|
|
10
|
+
export type CityStatusCheck = {
|
|
11
|
+
city: City;
|
|
12
|
+
/** The yields the city was judged on, worked out once for both checks. */
|
|
13
|
+
yields: Yield[];
|
|
14
|
+
/** Whether the city is in civil disorder now. */
|
|
15
|
+
disorder: boolean;
|
|
16
|
+
/** Whether the city celebrates now. */
|
|
17
|
+
celebrating: boolean;
|
|
18
|
+
/** The city's disorder as recorded before this turn's check, or `null`. */
|
|
19
|
+
recordedDisorder: PendingEffect | null;
|
|
20
|
+
/** The city's celebration as recorded before this turn's check, or `null`. */
|
|
21
|
+
recordedCelebration: PendingEffect | null;
|
|
22
|
+
};
|
|
23
|
+
/** How a ruleset keeps its cities' disorder and celebrations from one turn to the next. */
|
|
24
|
+
export type CityStatusTurn = {
|
|
25
|
+
/** The status `handler`s the ruleset records disorder and celebration under (see `cityStatus`). */
|
|
26
|
+
disorderHandler: string;
|
|
27
|
+
celebrationHandler: string;
|
|
28
|
+
pendingEffects: PendingEffectRegistry;
|
|
29
|
+
ruleRegistry: RuleRegistry;
|
|
30
|
+
engine: Engine;
|
|
31
|
+
/**
|
|
32
|
+
* Whether a city that celebrated before, and is in disorder now without celebrating, keeps its celebration (Civ1
|
|
33
|
+
* keeps it, without a word, until order returns); otherwise its celebration is ended or forgotten as usual.
|
|
34
|
+
*/
|
|
35
|
+
celebrationSurvivesDisorder?: boolean;
|
|
36
|
+
/** Called for a city in disorder for a second turn running (or more), before its status is updated. */
|
|
37
|
+
onDisorderContinues?: (check: CityStatusCheck) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Called for each city whose celebration is updated, before it is, with whether it celebrates for a second turn
|
|
40
|
+
* running (or more): what a ruleset grows a city for.
|
|
41
|
+
*/
|
|
42
|
+
onCelebrationChecked?: (check: CityStatusCheck, again: boolean) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a celebration that has stopped is dropped without its end being told (no event), rather than ended.
|
|
45
|
+
* Ended, by default.
|
|
46
|
+
*/
|
|
47
|
+
forgetCelebration?: (check: CityStatusCheck) => boolean;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Checks each of `cities` for civil disorder and for celebrating, at the start of their player's turn, emitting
|
|
51
|
+
* `city:civil-disorder` for each city in disorder, and records the statuses that start and ends those that stop
|
|
52
|
+
* (`updateCityStatus`), emitting `city:leader-celebration` as a celebration starts. The handlers' events (registered
|
|
53
|
+
* with `registerCityStatusHandler`) tell of order restored and celebrations ended. The cities are taken in order, and a
|
|
54
|
+
* hook may change the player's government part-way through them.
|
|
55
|
+
*/
|
|
56
|
+
export declare const cityStatusTurn: (
|
|
57
|
+
cities: City[],
|
|
58
|
+
{
|
|
59
|
+
disorderHandler,
|
|
60
|
+
celebrationHandler,
|
|
61
|
+
pendingEffects,
|
|
62
|
+
ruleRegistry,
|
|
63
|
+
engine,
|
|
64
|
+
celebrationSurvivesDisorder,
|
|
65
|
+
onDisorderContinues,
|
|
66
|
+
onCelebrationChecked,
|
|
67
|
+
forgetCelebration,
|
|
68
|
+
}: CityStatusTurn
|
|
69
|
+
) => void;
|
|
70
|
+
export default cityStatusTurn;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cityStatusTurn = void 0;
|
|
4
|
+
const cityStatus_1 = require("./cityStatus");
|
|
5
|
+
const CelebrateLeader_1 = require("@civ-clone/core-city-happiness/Rules/CelebrateLeader");
|
|
6
|
+
const CivilDisorder_1 = require("@civ-clone/core-city-happiness/Rules/CivilDisorder");
|
|
7
|
+
/**
|
|
8
|
+
* Checks each of `cities` for civil disorder and for celebrating, at the start of their player's turn, emitting
|
|
9
|
+
* `city:civil-disorder` for each city in disorder, and records the statuses that start and ends those that stop
|
|
10
|
+
* (`updateCityStatus`), emitting `city:leader-celebration` as a celebration starts. The handlers' events (registered
|
|
11
|
+
* with `registerCityStatusHandler`) tell of order restored and celebrations ended. The cities are taken in order, and a
|
|
12
|
+
* hook may change the player's government part-way through them.
|
|
13
|
+
*/
|
|
14
|
+
const cityStatusTurn = (cities, { disorderHandler, celebrationHandler, pendingEffects, ruleRegistry, engine, celebrationSurvivesDisorder = false, onDisorderContinues = () => { }, onCelebrationChecked = () => { }, forgetCelebration = () => false, }) => cities.forEach((city) => {
|
|
15
|
+
// Both rules read the same yields, and working them out isn't cheap (civ-clone/web-renderer#315).
|
|
16
|
+
const yields = city.yields(), check = {
|
|
17
|
+
city,
|
|
18
|
+
yields,
|
|
19
|
+
disorder: ruleRegistry
|
|
20
|
+
.process(CivilDisorder_1.default, city, yields)
|
|
21
|
+
.some((result) => result),
|
|
22
|
+
celebrating: ruleRegistry
|
|
23
|
+
.process(CelebrateLeader_1.default, city, yields)
|
|
24
|
+
.some((result) => result),
|
|
25
|
+
recordedDisorder: (0, cityStatus_1.cityStatus)(disorderHandler, city, pendingEffects),
|
|
26
|
+
recordedCelebration: (0, cityStatus_1.cityStatus)(celebrationHandler, city, pendingEffects),
|
|
27
|
+
};
|
|
28
|
+
if (check.disorder) {
|
|
29
|
+
engine.emit('city:civil-disorder', city);
|
|
30
|
+
}
|
|
31
|
+
if (check.disorder && check.recordedDisorder !== null) {
|
|
32
|
+
onDisorderContinues(check);
|
|
33
|
+
}
|
|
34
|
+
onCelebrationChecked(check, check.celebrating && check.recordedCelebration !== null);
|
|
35
|
+
// Records disorder that has started, and ends disorder that has stopped.
|
|
36
|
+
(0, cityStatus_1.updateCityStatus)(disorderHandler, city, check.disorder, check.recordedDisorder, pendingEffects);
|
|
37
|
+
if (celebrationSurvivesDisorder && check.disorder && !check.celebrating) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!check.celebrating &&
|
|
41
|
+
check.recordedCelebration !== null &&
|
|
42
|
+
forgetCelebration(check)) {
|
|
43
|
+
(0, cityStatus_1.forgetCityStatuses)(city, [celebrationHandler], pendingEffects);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
(0, cityStatus_1.updateCityStatus)(celebrationHandler, city, check.celebrating, check.recordedCelebration, pendingEffects, () => engine.emit('city:leader-celebration', city));
|
|
47
|
+
});
|
|
48
|
+
exports.cityStatusTurn = cityStatusTurn;
|
|
49
|
+
exports.default = exports.cityStatusTurn;
|
|
50
|
+
//# sourceMappingURL=statusTurn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statusTurn.js","sourceRoot":"","sources":["statusTurn.ts"],"names":[],"mappings":";;;AAIA,6CAAgF;AAChF,0FAAmF;AAEnF,sFAA+E;AA+C/E;;;;;;GAMG;AACI,MAAM,cAAc,GAAG,CAC5B,MAAc,EACd,EACE,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,MAAM,EACN,2BAA2B,GAAG,KAAK,EACnC,mBAAmB,GAAG,GAAG,EAAE,GAAE,CAAC,EAC9B,oBAAoB,GAAG,GAAG,EAAE,GAAE,CAAC,EAC/B,iBAAiB,GAAG,GAAG,EAAE,CAAC,KAAK,GAChB,EACX,EAAE,CACR,MAAM,CAAC,OAAO,CAAC,CAAC,IAAU,EAAQ,EAAE;IAClC,kGAAkG;IAClG,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,EAC1B,KAAK,GAAoB;QACvB,IAAI;QACJ,MAAM;QACN,QAAQ,EAAE,YAAY;aACnB,OAAO,CAAC,uBAAa,EAAE,IAAI,EAAE,MAAM,CAAC;aACpC,IAAI,CAAC,CAAC,MAAe,EAAW,EAAE,CAAC,MAAM,CAAC;QAC7C,WAAW,EAAE,YAAY;aACtB,OAAO,CAAC,yBAAe,EAAE,IAAI,EAAE,MAAM,CAAC;aACtC,IAAI,CAAC,CAAC,MAAe,EAAW,EAAE,CAAC,MAAM,CAAC;QAC7C,gBAAgB,EAAE,IAAA,uBAAU,EAAC,eAAe,EAAE,IAAI,EAAE,cAAc,CAAC;QACnE,mBAAmB,EAAE,IAAA,uBAAU,EAC7B,kBAAkB,EAClB,IAAI,EACJ,cAAc,CACf;KACF,CAAC;IAEJ,IAAI,KAAK,CAAC,QAAQ,EAAE;QAClB,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;KAC1C;IAED,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,EAAE;QACrD,mBAAmB,CAAC,KAAK,CAAC,CAAC;KAC5B;IAED,oBAAoB,CAClB,KAAK,EACL,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,mBAAmB,KAAK,IAAI,CACxD,CAAC;IAEF,yEAAyE;IACzE,IAAA,6BAAgB,EACd,eAAe,EACf,IAAI,EACJ,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,gBAAgB,EACtB,cAAc,CACf,CAAC;IAEF,IAAI,2BAA2B,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;QACvE,OAAO;KACR;IAED,IACE,CAAC,KAAK,CAAC,WAAW;QAClB,KAAK,CAAC,mBAAmB,KAAK,IAAI;QAClC,iBAAiB,CAAC,KAAK,CAAC,EACxB;QACA,IAAA,+BAAkB,EAAC,IAAI,EAAE,CAAC,kBAAkB,CAAC,EAAE,cAAc,CAAC,CAAC;QAE/D,OAAO;KACR;IAED,IAAA,6BAAgB,EACd,kBAAkB,EAClB,IAAI,EACJ,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,mBAAmB,EACzB,cAAc,EACd,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,IAAI,CAAC,CACnD,CAAC;AACJ,CAAC,CAAC,CAAC;AA9EQ,QAAA,cAAc,kBA8EtB;AAEL,kBAAe,sBAAc,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PendingEffect,
|
|
3
|
+
PendingEffectRegistry,
|
|
4
|
+
} from '@civ-clone/core-pending-effect';
|
|
5
|
+
import { cityStatus, forgetCityStatuses, updateCityStatus } from './cityStatus';
|
|
6
|
+
import CelebrateLeader from '@civ-clone/core-city-happiness/Rules/CelebrateLeader';
|
|
7
|
+
import City from '@civ-clone/core-city/City';
|
|
8
|
+
import CivilDisorder from '@civ-clone/core-city-happiness/Rules/CivilDisorder';
|
|
9
|
+
import { Engine } from '@civ-clone/core-engine/Engine';
|
|
10
|
+
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
11
|
+
import Yield from '@civ-clone/core-yield/Yield';
|
|
12
|
+
|
|
13
|
+
/** What a city's check found this turn, and what was recorded before it. */
|
|
14
|
+
export type CityStatusCheck = {
|
|
15
|
+
city: City;
|
|
16
|
+
/** The yields the city was judged on, worked out once for both checks. */
|
|
17
|
+
yields: Yield[];
|
|
18
|
+
/** Whether the city is in civil disorder now. */
|
|
19
|
+
disorder: boolean;
|
|
20
|
+
/** Whether the city celebrates now. */
|
|
21
|
+
celebrating: boolean;
|
|
22
|
+
/** The city's disorder as recorded before this turn's check, or `null`. */
|
|
23
|
+
recordedDisorder: PendingEffect | null;
|
|
24
|
+
/** The city's celebration as recorded before this turn's check, or `null`. */
|
|
25
|
+
recordedCelebration: PendingEffect | null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** How a ruleset keeps its cities' disorder and celebrations from one turn to the next. */
|
|
29
|
+
export type CityStatusTurn = {
|
|
30
|
+
/** The status `handler`s the ruleset records disorder and celebration under (see `cityStatus`). */
|
|
31
|
+
disorderHandler: string;
|
|
32
|
+
celebrationHandler: string;
|
|
33
|
+
pendingEffects: PendingEffectRegistry;
|
|
34
|
+
ruleRegistry: RuleRegistry;
|
|
35
|
+
engine: Engine;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a city that celebrated before, and is in disorder now without celebrating, keeps its celebration (Civ1
|
|
38
|
+
* keeps it, without a word, until order returns); otherwise its celebration is ended or forgotten as usual.
|
|
39
|
+
*/
|
|
40
|
+
celebrationSurvivesDisorder?: boolean;
|
|
41
|
+
/** Called for a city in disorder for a second turn running (or more), before its status is updated. */
|
|
42
|
+
onDisorderContinues?: (check: CityStatusCheck) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Called for each city whose celebration is updated, before it is, with whether it celebrates for a second turn
|
|
45
|
+
* running (or more): what a ruleset grows a city for.
|
|
46
|
+
*/
|
|
47
|
+
onCelebrationChecked?: (check: CityStatusCheck, again: boolean) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Whether a celebration that has stopped is dropped without its end being told (no event), rather than ended.
|
|
50
|
+
* Ended, by default.
|
|
51
|
+
*/
|
|
52
|
+
forgetCelebration?: (check: CityStatusCheck) => boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks each of `cities` for civil disorder and for celebrating, at the start of their player's turn, emitting
|
|
57
|
+
* `city:civil-disorder` for each city in disorder, and records the statuses that start and ends those that stop
|
|
58
|
+
* (`updateCityStatus`), emitting `city:leader-celebration` as a celebration starts. The handlers' events (registered
|
|
59
|
+
* with `registerCityStatusHandler`) tell of order restored and celebrations ended. The cities are taken in order, and a
|
|
60
|
+
* hook may change the player's government part-way through them.
|
|
61
|
+
*/
|
|
62
|
+
export const cityStatusTurn = (
|
|
63
|
+
cities: City[],
|
|
64
|
+
{
|
|
65
|
+
disorderHandler,
|
|
66
|
+
celebrationHandler,
|
|
67
|
+
pendingEffects,
|
|
68
|
+
ruleRegistry,
|
|
69
|
+
engine,
|
|
70
|
+
celebrationSurvivesDisorder = false,
|
|
71
|
+
onDisorderContinues = () => {},
|
|
72
|
+
onCelebrationChecked = () => {},
|
|
73
|
+
forgetCelebration = () => false,
|
|
74
|
+
}: CityStatusTurn
|
|
75
|
+
): void =>
|
|
76
|
+
cities.forEach((city: City): void => {
|
|
77
|
+
// Both rules read the same yields, and working them out isn't cheap (civ-clone/web-renderer#315).
|
|
78
|
+
const yields = city.yields(),
|
|
79
|
+
check: CityStatusCheck = {
|
|
80
|
+
city,
|
|
81
|
+
yields,
|
|
82
|
+
disorder: ruleRegistry
|
|
83
|
+
.process(CivilDisorder, city, yields)
|
|
84
|
+
.some((result: boolean): boolean => result),
|
|
85
|
+
celebrating: ruleRegistry
|
|
86
|
+
.process(CelebrateLeader, city, yields)
|
|
87
|
+
.some((result: boolean): boolean => result),
|
|
88
|
+
recordedDisorder: cityStatus(disorderHandler, city, pendingEffects),
|
|
89
|
+
recordedCelebration: cityStatus(
|
|
90
|
+
celebrationHandler,
|
|
91
|
+
city,
|
|
92
|
+
pendingEffects
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (check.disorder) {
|
|
97
|
+
engine.emit('city:civil-disorder', city);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (check.disorder && check.recordedDisorder !== null) {
|
|
101
|
+
onDisorderContinues(check);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onCelebrationChecked(
|
|
105
|
+
check,
|
|
106
|
+
check.celebrating && check.recordedCelebration !== null
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Records disorder that has started, and ends disorder that has stopped.
|
|
110
|
+
updateCityStatus(
|
|
111
|
+
disorderHandler,
|
|
112
|
+
city,
|
|
113
|
+
check.disorder,
|
|
114
|
+
check.recordedDisorder,
|
|
115
|
+
pendingEffects
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (celebrationSurvivesDisorder && check.disorder && !check.celebrating) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
!check.celebrating &&
|
|
124
|
+
check.recordedCelebration !== null &&
|
|
125
|
+
forgetCelebration(check)
|
|
126
|
+
) {
|
|
127
|
+
forgetCityStatuses(city, [celebrationHandler], pendingEffects);
|
|
128
|
+
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
updateCityStatus(
|
|
133
|
+
celebrationHandler,
|
|
134
|
+
city,
|
|
135
|
+
check.celebrating,
|
|
136
|
+
check.recordedCelebration,
|
|
137
|
+
pendingEffects,
|
|
138
|
+
() => engine.emit('city:leader-celebration', city)
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export default cityStatusTurn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civ-clone/base-city-happiness-status",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Keeps a city's status, such as disorder or celebrating, from one turn to the next and in the save.",
|
|
5
5
|
"repository": "git@github.com:civ-clone/base-city-happiness-status.git",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@civ-clone/core-city": "^0.1.24",
|
|
32
|
+
"@civ-clone/core-city-happiness": "^0.1.2",
|
|
32
33
|
"@civ-clone/core-engine": "^0.1.6",
|
|
33
|
-
"@civ-clone/core-pending-effect": "^0.1.2"
|
|
34
|
+
"@civ-clone/core-pending-effect": "^0.1.2",
|
|
35
|
+
"@civ-clone/core-rule": "^0.1.9",
|
|
36
|
+
"@civ-clone/core-yield": "^0.1.6"
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { CityStatusCheck, cityStatusTurn } from '../lib/statusTurn';
|
|
2
|
+
import { cityStatus, registerCityStatusHandler } from '../lib/cityStatus';
|
|
3
|
+
import CelebrateLeader from '@civ-clone/core-city-happiness/Rules/CelebrateLeader';
|
|
4
|
+
import City from '@civ-clone/core-city/City';
|
|
5
|
+
import CivilDisorder from '@civ-clone/core-city-happiness/Rules/CivilDisorder';
|
|
6
|
+
import Effect from '@civ-clone/core-rule/Effect';
|
|
7
|
+
import Engine from '@civ-clone/core-engine/Engine';
|
|
8
|
+
import { PendingEffectRegistry } from '@civ-clone/core-pending-effect';
|
|
9
|
+
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
10
|
+
import { expect } from 'chai';
|
|
11
|
+
|
|
12
|
+
const UPSET = 'test:upset',
|
|
13
|
+
CHEERFUL = 'test:cheerful';
|
|
14
|
+
|
|
15
|
+
describe('cityStatusTurn', (): void => {
|
|
16
|
+
const setUp = () => {
|
|
17
|
+
const pendingEffects = new PendingEffectRegistry(),
|
|
18
|
+
ruleRegistry = new RuleRegistry(),
|
|
19
|
+
events: [string, unknown][] = [],
|
|
20
|
+
engine = {
|
|
21
|
+
emit: (event: string, target: unknown) => events.push([event, target]),
|
|
22
|
+
} as unknown as Engine,
|
|
23
|
+
// What each city's check finds this turn, by name.
|
|
24
|
+
state: { [name: string]: { disorder: boolean; celebrating: boolean } } =
|
|
25
|
+
{},
|
|
26
|
+
yieldsAsked: string[] = [],
|
|
27
|
+
city = (name: string) =>
|
|
28
|
+
({
|
|
29
|
+
name,
|
|
30
|
+
yields: () => {
|
|
31
|
+
yieldsAsked.push(name);
|
|
32
|
+
|
|
33
|
+
return [name];
|
|
34
|
+
},
|
|
35
|
+
} as unknown as City),
|
|
36
|
+
a = city('a'),
|
|
37
|
+
b = city('b');
|
|
38
|
+
|
|
39
|
+
ruleRegistry.register(
|
|
40
|
+
new CivilDisorder(
|
|
41
|
+
new Effect(
|
|
42
|
+
(city: City, yields: unknown[]): boolean =>
|
|
43
|
+
yields[0] === (city as any).name &&
|
|
44
|
+
state[(city as any).name].disorder
|
|
45
|
+
)
|
|
46
|
+
),
|
|
47
|
+
new CelebrateLeader(
|
|
48
|
+
new Effect(
|
|
49
|
+
(city: City, yields: unknown[]): boolean =>
|
|
50
|
+
yields[0] === (city as any).name &&
|
|
51
|
+
state[(city as any).name].celebrating
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
registerCityStatusHandler(
|
|
57
|
+
UPSET,
|
|
58
|
+
'city:order-restored',
|
|
59
|
+
pendingEffects,
|
|
60
|
+
engine
|
|
61
|
+
);
|
|
62
|
+
registerCityStatusHandler(
|
|
63
|
+
CHEERFUL,
|
|
64
|
+
'city:celebration-ended',
|
|
65
|
+
pendingEffects,
|
|
66
|
+
engine
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const turn = (extra: object = {}) =>
|
|
70
|
+
cityStatusTurn([a, b], {
|
|
71
|
+
disorderHandler: UPSET,
|
|
72
|
+
celebrationHandler: CHEERFUL,
|
|
73
|
+
pendingEffects,
|
|
74
|
+
ruleRegistry,
|
|
75
|
+
engine,
|
|
76
|
+
...extra,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return { pendingEffects, events, state, yieldsAsked, a, b, turn };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
it('works out the yields once per city, records disorder and celebrations as they start and ends them as they stop', (): void => {
|
|
83
|
+
const { pendingEffects, events, state, yieldsAsked, a, b, turn } = setUp();
|
|
84
|
+
|
|
85
|
+
state.a = { disorder: true, celebrating: false };
|
|
86
|
+
state.b = { disorder: false, celebrating: true };
|
|
87
|
+
|
|
88
|
+
turn();
|
|
89
|
+
|
|
90
|
+
expect(yieldsAsked).eql(['a', 'b']);
|
|
91
|
+
expect(events).eql([
|
|
92
|
+
['city:civil-disorder', a],
|
|
93
|
+
['city:leader-celebration', b],
|
|
94
|
+
]);
|
|
95
|
+
expect(cityStatus(UPSET, a, pendingEffects)).not.null;
|
|
96
|
+
expect(cityStatus(CHEERFUL, b, pendingEffects)).not.null;
|
|
97
|
+
|
|
98
|
+
events.length = 0;
|
|
99
|
+
state.a = { disorder: false, celebrating: false };
|
|
100
|
+
state.b = { disorder: false, celebrating: false };
|
|
101
|
+
|
|
102
|
+
turn();
|
|
103
|
+
|
|
104
|
+
expect(events).eql([
|
|
105
|
+
['city:order-restored', a],
|
|
106
|
+
['city:celebration-ended', b],
|
|
107
|
+
]);
|
|
108
|
+
expect(pendingEffects.entries()).empty;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('calls the hooks with the check, telling a second turn running from a first', (): void => {
|
|
112
|
+
const { state, a, b, turn } = setUp(),
|
|
113
|
+
continued: City[] = [],
|
|
114
|
+
checked: [City, boolean][] = [];
|
|
115
|
+
|
|
116
|
+
state.a = { disorder: true, celebrating: false };
|
|
117
|
+
state.b = { disorder: false, celebrating: true };
|
|
118
|
+
|
|
119
|
+
const hooks = {
|
|
120
|
+
onDisorderContinues: ({ city }: CityStatusCheck) => continued.push(city),
|
|
121
|
+
onCelebrationChecked: ({ city }: CityStatusCheck, again: boolean) =>
|
|
122
|
+
checked.push([city, again]),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
turn(hooks);
|
|
126
|
+
turn(hooks);
|
|
127
|
+
|
|
128
|
+
expect(continued).eql([a]);
|
|
129
|
+
expect(checked).eql([
|
|
130
|
+
[a, false],
|
|
131
|
+
[b, false],
|
|
132
|
+
[a, false],
|
|
133
|
+
[b, true],
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('keeps a celebration through disorder only when the ruleset says so', (): void => {
|
|
138
|
+
const { pendingEffects, events, state, a, turn } = setUp();
|
|
139
|
+
|
|
140
|
+
state.a = { disorder: false, celebrating: true };
|
|
141
|
+
state.b = { disorder: false, celebrating: false };
|
|
142
|
+
|
|
143
|
+
turn();
|
|
144
|
+
|
|
145
|
+
state.a = { disorder: true, celebrating: false };
|
|
146
|
+
events.length = 0;
|
|
147
|
+
|
|
148
|
+
turn({ celebrationSurvivesDisorder: true });
|
|
149
|
+
|
|
150
|
+
expect(cityStatus(CHEERFUL, a, pendingEffects)).not.null;
|
|
151
|
+
expect(events).eql([['city:civil-disorder', a]]);
|
|
152
|
+
|
|
153
|
+
turn();
|
|
154
|
+
|
|
155
|
+
expect(cityStatus(CHEERFUL, a, pendingEffects)).null;
|
|
156
|
+
expect(events).eql([
|
|
157
|
+
['city:civil-disorder', a],
|
|
158
|
+
['city:civil-disorder', a],
|
|
159
|
+
['city:celebration-ended', a],
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('drops a celebration without a word when the ruleset forgets it', (): void => {
|
|
164
|
+
const { pendingEffects, events, state, a, turn } = setUp(),
|
|
165
|
+
asked: boolean[] = [];
|
|
166
|
+
|
|
167
|
+
state.a = { disorder: false, celebrating: true };
|
|
168
|
+
state.b = { disorder: false, celebrating: false };
|
|
169
|
+
|
|
170
|
+
turn();
|
|
171
|
+
|
|
172
|
+
state.a = { disorder: true, celebrating: false };
|
|
173
|
+
events.length = 0;
|
|
174
|
+
|
|
175
|
+
turn({
|
|
176
|
+
forgetCelebration: ({ disorder }: CityStatusCheck) => {
|
|
177
|
+
asked.push(disorder);
|
|
178
|
+
|
|
179
|
+
return disorder;
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(asked).eql([true]);
|
|
184
|
+
expect(cityStatus(CHEERFUL, a, pendingEffects)).null;
|
|
185
|
+
expect(events).eql([['city:civil-disorder', a]]);
|
|
186
|
+
});
|
|
187
|
+
});
|