@civ-clone/base-city-lifecycle 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 +14 -3
- package/lib/assignWorkers.d.ts +114 -0
- package/lib/assignWorkers.js +184 -0
- package/lib/assignWorkers.js.map +1 -0
- package/lib/assignWorkers.ts +372 -0
- package/lib/captured.d.ts +12 -0
- package/lib/captured.js +30 -0
- package/lib/captured.js.map +1 -0
- package/lib/captured.ts +31 -0
- package/lib/created.d.ts +41 -0
- package/lib/created.js +52 -0
- package/lib/created.js.map +1 -0
- package/lib/created.ts +110 -0
- package/lib/destroyed.d.ts +23 -0
- package/lib/destroyed.js +29 -0
- package/lib/destroyed.js.map +1 -0
- package/lib/destroyed.ts +39 -0
- package/lib/events.d.ts +8 -0
- package/lib/events.js +8 -1
- package/lib/events.js.map +1 -1
- package/lib/events.ts +11 -0
- package/lib/occupiedTiles.d.ts +14 -0
- package/lib/occupiedTiles.js +16 -1
- package/lib/occupiedTiles.js.map +1 -1
- package/lib/occupiedTiles.ts +32 -0
- package/package.json +6 -1
- package/tests/r3b.test.ts +341 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
// Lifted from library-city 0.1.4 (`lib/assignWorkers.ts`), unchanged but for where the yields come from, so a game's
|
|
2
|
+
// rules can place a city's citizens without depending on library-city (civ-clone/web-renderer Civ2 M2a, R3b).
|
|
3
|
+
// library-city re-exports this in R3b.
|
|
4
|
+
import {
|
|
5
|
+
AvailableSpecialistRegistry,
|
|
6
|
+
instance as availableSpecialistRegistryInstance,
|
|
7
|
+
} from '@civ-clone/core-city/AvailableSpecialistRegistry';
|
|
8
|
+
import {
|
|
9
|
+
CityGrowthRegistry,
|
|
10
|
+
instance as cityGrowthRegistryInstance,
|
|
11
|
+
} from '@civ-clone/core-city-growth/CityGrowthRegistry';
|
|
12
|
+
import Food from '@civ-clone/base-terrain-yield-food/Food';
|
|
13
|
+
import Production from '@civ-clone/base-terrain-yield-production/Production';
|
|
14
|
+
import Trade from '@civ-clone/base-terrain-yield-trade/Trade';
|
|
15
|
+
import {
|
|
16
|
+
PlayerWorldRegistry,
|
|
17
|
+
instance as playerWorldRegistryInstance,
|
|
18
|
+
} from '@civ-clone/core-player-world/PlayerWorldRegistry';
|
|
19
|
+
import {
|
|
20
|
+
SpecialistRegistry,
|
|
21
|
+
instance as specialistRegistryInstance,
|
|
22
|
+
} from '@civ-clone/core-city/SpecialistRegistry';
|
|
23
|
+
import {
|
|
24
|
+
WorkedTileRegistry,
|
|
25
|
+
instance as workedTileRegistryInstance,
|
|
26
|
+
} from '@civ-clone/core-city/WorkedTileRegistry';
|
|
27
|
+
import City from '@civ-clone/core-city/City';
|
|
28
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
29
|
+
import Yield from '@civ-clone/core-yield/Yield';
|
|
30
|
+
import { reduceYield } from '@civ-clone/core-yield/lib/reduceYields';
|
|
31
|
+
import WorkedTile from '@civ-clone/core-city/WorkedTile';
|
|
32
|
+
import CityGrowth from '@civ-clone/core-city-growth/CityGrowth';
|
|
33
|
+
import Specialist from '@civ-clone/core-city/Specialist';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* How many citizens `city` has placed: one per worked tile, the city centre included, and one per `Specialist`. A city
|
|
37
|
+
* of size `n` has `n + 1`.
|
|
38
|
+
*/
|
|
39
|
+
export const citizenCount = (
|
|
40
|
+
city: City,
|
|
41
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
42
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance
|
|
43
|
+
): number =>
|
|
44
|
+
workedTileRegistry.getByCity(city).length +
|
|
45
|
+
specialistRegistry.getByCity(city).length;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Makes a citizen of `city` a `Specialist` of the first available kind (an Entertainer, in Civ1). Returns `null` when
|
|
49
|
+
* the ruleset offers no specialists.
|
|
50
|
+
*/
|
|
51
|
+
export const addSpecialist = (
|
|
52
|
+
city: City,
|
|
53
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
54
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
55
|
+
): Specialist | null => {
|
|
56
|
+
const [SpecialistType] = availableSpecialistRegistry.entries();
|
|
57
|
+
|
|
58
|
+
if (!SpecialistType) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const specialist = new SpecialistType(city);
|
|
63
|
+
|
|
64
|
+
specialistRegistry.register(specialist);
|
|
65
|
+
|
|
66
|
+
return specialist;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Turns `specialist` into the next available kind, wrapping back to the first. Returns the `Specialist` that replaces
|
|
71
|
+
* it, or `specialist` itself when there is nothing to change it to.
|
|
72
|
+
*/
|
|
73
|
+
export const changeSpecialist = (
|
|
74
|
+
specialist: Specialist,
|
|
75
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
76
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
77
|
+
): Specialist => {
|
|
78
|
+
const city = specialist.city(),
|
|
79
|
+
types = availableSpecialistRegistry.entries(),
|
|
80
|
+
NextType =
|
|
81
|
+
types[
|
|
82
|
+
(types.indexOf(specialist.constructor as typeof Specialist) + 1) %
|
|
83
|
+
types.length
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
if (!NextType || specialist instanceof NextType) {
|
|
87
|
+
return specialist;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const replacement = new NextType(city);
|
|
91
|
+
|
|
92
|
+
specialistRegistry.unregister(specialist);
|
|
93
|
+
specialistRegistry.register(replacement);
|
|
94
|
+
|
|
95
|
+
return replacement;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Puts one of `city`'s specialists back to work, preferring the first available kind (Entertainers), and returns
|
|
100
|
+
* whether there was one to remove.
|
|
101
|
+
*/
|
|
102
|
+
const removeSpecialist = (
|
|
103
|
+
city: City,
|
|
104
|
+
specialistRegistry: SpecialistRegistry,
|
|
105
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry
|
|
106
|
+
): boolean => {
|
|
107
|
+
const specialists = specialistRegistry.getByCity(city),
|
|
108
|
+
[DefaultType] = availableSpecialistRegistry.entries(),
|
|
109
|
+
specialist =
|
|
110
|
+
specialists.find(
|
|
111
|
+
(specialist) => DefaultType && specialist instanceof DefaultType
|
|
112
|
+
) ?? specialists[specialists.length - 1];
|
|
113
|
+
|
|
114
|
+
if (!specialist) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
specialistRegistry.unregister(specialist);
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const getHighestValueCityTiles = (city: City) =>
|
|
124
|
+
sortTiles(city.tiles().entries(), city);
|
|
125
|
+
|
|
126
|
+
export const sortTiles = (
|
|
127
|
+
tiles: Tile[],
|
|
128
|
+
city: City,
|
|
129
|
+
weights: [typeof Yield, number][] = [
|
|
130
|
+
[Food, 8],
|
|
131
|
+
[
|
|
132
|
+
Production,
|
|
133
|
+
3 *
|
|
134
|
+
(reduceYield(city.tilesWorked().yields(city.player()), Production) === 0
|
|
135
|
+
? 3
|
|
136
|
+
: 1),
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
Trade,
|
|
140
|
+
1 *
|
|
141
|
+
(reduceYield(city.tilesWorked().yields(city.player()), Trade) === 0
|
|
142
|
+
? 2
|
|
143
|
+
: 1),
|
|
144
|
+
],
|
|
145
|
+
]
|
|
146
|
+
): Tile[] =>
|
|
147
|
+
tiles.sort(
|
|
148
|
+
(a: Tile, b: Tile) =>
|
|
149
|
+
b.score(city.player(), weights) - a.score(city.player(), weights)
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
export const getHighestValueAvailableCityTiles = (
|
|
153
|
+
city: City,
|
|
154
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
155
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance
|
|
156
|
+
) =>
|
|
157
|
+
getHighestValueCityTiles(city).filter(
|
|
158
|
+
(tile) =>
|
|
159
|
+
workedTileRegistry.tileCanBeWorkedBy(tile, city) &&
|
|
160
|
+
playerWorldRegistry.getByPlayer(city.player()).includes(tile)
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Rearranges the tiles `city` works from scratch. Its specialists stay as they are.
|
|
165
|
+
*/
|
|
166
|
+
export const reassignWorkers = (
|
|
167
|
+
city: City,
|
|
168
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
169
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
170
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
171
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
172
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
173
|
+
) => {
|
|
174
|
+
workedTileRegistry
|
|
175
|
+
.getBy('city', city)
|
|
176
|
+
.forEach((workedTile: WorkedTile) =>
|
|
177
|
+
workedTileRegistry.unregister(workedTile)
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
workedTileRegistry.register(new WorkedTile(city.tile(), city));
|
|
181
|
+
|
|
182
|
+
assignWorkers(
|
|
183
|
+
city,
|
|
184
|
+
playerWorldRegistry,
|
|
185
|
+
cityGrowthRegistry,
|
|
186
|
+
workedTileRegistry,
|
|
187
|
+
specialistRegistry,
|
|
188
|
+
availableSpecialistRegistry
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Places each of `city`'s citizens that has no job on the best tile available, or makes them a specialist if there is
|
|
194
|
+
* no tile left to work.
|
|
195
|
+
*/
|
|
196
|
+
export const assignWorkers: (
|
|
197
|
+
city: City,
|
|
198
|
+
playerWorldRegistry?: PlayerWorldRegistry,
|
|
199
|
+
cityGrowthRegistry?: CityGrowthRegistry,
|
|
200
|
+
workedTileRegistry?: WorkedTileRegistry,
|
|
201
|
+
specialistRegistry?: SpecialistRegistry,
|
|
202
|
+
availableSpecialistRegistry?: AvailableSpecialistRegistry
|
|
203
|
+
) => void = (
|
|
204
|
+
city: City,
|
|
205
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
206
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
207
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
208
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
209
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
210
|
+
): void => {
|
|
211
|
+
const cityGrowth = cityGrowthRegistry.getByCity(city),
|
|
212
|
+
placed = () =>
|
|
213
|
+
citizenCount(city, workedTileRegistry, specialistRegistry) >=
|
|
214
|
+
cityGrowth.size() + 1;
|
|
215
|
+
|
|
216
|
+
getHighestValueAvailableCityTiles(
|
|
217
|
+
city,
|
|
218
|
+
playerWorldRegistry,
|
|
219
|
+
workedTileRegistry
|
|
220
|
+
).some((tile) => {
|
|
221
|
+
if (placed()) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
workedTileRegistry.register(new WorkedTile(tile, city));
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
while (
|
|
231
|
+
!placed() &&
|
|
232
|
+
addSpecialist(city, specialistRegistry, availableSpecialistRegistry)
|
|
233
|
+
) {}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Places one new citizen of `city` on the best tile available, or makes them a specialist if there is no tile left.
|
|
238
|
+
*/
|
|
239
|
+
export const assignWorker: (
|
|
240
|
+
city: City,
|
|
241
|
+
playerWorldRegistry?: PlayerWorldRegistry,
|
|
242
|
+
cityGrowthRegistry?: CityGrowthRegistry,
|
|
243
|
+
workedTileRegistry?: WorkedTileRegistry,
|
|
244
|
+
specialistRegistry?: SpecialistRegistry,
|
|
245
|
+
availableSpecialistRegistry?: AvailableSpecialistRegistry
|
|
246
|
+
) => void = (
|
|
247
|
+
city: City,
|
|
248
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
249
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
250
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
251
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
252
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
253
|
+
): void => {
|
|
254
|
+
const [newTile] = getHighestValueAvailableCityTiles(
|
|
255
|
+
city,
|
|
256
|
+
playerWorldRegistry,
|
|
257
|
+
workedTileRegistry
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (!newTile) {
|
|
261
|
+
addSpecialist(city, specialistRegistry, availableSpecialistRegistry);
|
|
262
|
+
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
workedTileRegistry.register(new WorkedTile(newTile, city));
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export type WorkedTileChange = 'removed' | 'added' | 'reassigned' | 'none';
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Changes whether `tile` is worked by `city`, as a player clicking it on the city map would.
|
|
273
|
+
*
|
|
274
|
+
* A tile the city works stops being worked, and its worker becomes a specialist. Any other tile is worked by a
|
|
275
|
+
* specialist, or by a citizen with no job, if the city has one and is allowed to work it. With every citizen already
|
|
276
|
+
* working a tile, the city's workers are reassigned instead. The city centre is always worked.
|
|
277
|
+
*/
|
|
278
|
+
export const changeWorkedTile: (
|
|
279
|
+
city: City,
|
|
280
|
+
tile: Tile,
|
|
281
|
+
playerWorldRegistry?: PlayerWorldRegistry,
|
|
282
|
+
cityGrowthRegistry?: CityGrowthRegistry,
|
|
283
|
+
workedTileRegistry?: WorkedTileRegistry,
|
|
284
|
+
specialistRegistry?: SpecialistRegistry,
|
|
285
|
+
availableSpecialistRegistry?: AvailableSpecialistRegistry
|
|
286
|
+
) => WorkedTileChange = (
|
|
287
|
+
city: City,
|
|
288
|
+
tile: Tile,
|
|
289
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
290
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
291
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
292
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
293
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
294
|
+
): WorkedTileChange => {
|
|
295
|
+
if (tile === city.tile() || !city.tiles().includes(tile)) {
|
|
296
|
+
return 'none';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (workedTileRegistry.getByTile(tile)?.city() === city) {
|
|
300
|
+
workedTileRegistry.unregisterByTile(tile);
|
|
301
|
+
|
|
302
|
+
addSpecialist(city, specialistRegistry, availableSpecialistRegistry);
|
|
303
|
+
|
|
304
|
+
return 'removed';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const size = cityGrowthRegistry.getByCity(city).size();
|
|
308
|
+
|
|
309
|
+
if (workedTileRegistry.getByCity(city).length >= size + 1) {
|
|
310
|
+
reassignWorkers(
|
|
311
|
+
city,
|
|
312
|
+
playerWorldRegistry,
|
|
313
|
+
cityGrowthRegistry,
|
|
314
|
+
workedTileRegistry,
|
|
315
|
+
specialistRegistry,
|
|
316
|
+
availableSpecialistRegistry
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
return 'reassigned';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (
|
|
323
|
+
!workedTileRegistry.tileCanBeWorkedBy(tile, city) ||
|
|
324
|
+
!playerWorldRegistry.getByPlayer(city.player()).includes(tile)
|
|
325
|
+
) {
|
|
326
|
+
return 'none';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (citizenCount(city, workedTileRegistry, specialistRegistry) >= size + 1) {
|
|
330
|
+
removeSpecialist(city, specialistRegistry, availableSpecialistRegistry);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
workedTileRegistry.register(new WorkedTile(tile, city));
|
|
334
|
+
|
|
335
|
+
return 'added';
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Takes jobs away from `city`'s citizens until it has no more than its size allows: specialists first, then the
|
|
340
|
+
* workers on the least valuable tiles.
|
|
341
|
+
*/
|
|
342
|
+
export const releaseCitizens = (
|
|
343
|
+
city: City,
|
|
344
|
+
cityGrowth: CityGrowth,
|
|
345
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
346
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance
|
|
347
|
+
): void => {
|
|
348
|
+
const specialists = specialistRegistry.getByCity(city);
|
|
349
|
+
|
|
350
|
+
while (
|
|
351
|
+
specialists.length > 0 &&
|
|
352
|
+
citizenCount(city, workedTileRegistry, specialistRegistry) >
|
|
353
|
+
cityGrowth.size() + 1
|
|
354
|
+
) {
|
|
355
|
+
specialistRegistry.unregister(specialists.pop()!);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
reduceWorkers(city, cityGrowth).forEach((tile: Tile): void =>
|
|
359
|
+
workedTileRegistry.unregisterByTile(tile)
|
|
360
|
+
);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
export const reduceWorkers = (city: City, cityGrowth: CityGrowth): Tile[] =>
|
|
364
|
+
sortTiles(
|
|
365
|
+
city
|
|
366
|
+
.tilesWorked()
|
|
367
|
+
.entries()
|
|
368
|
+
.filter((tile: Tile) => tile !== city.tile()),
|
|
369
|
+
city
|
|
370
|
+
).slice(cityGrowth.size());
|
|
371
|
+
|
|
372
|
+
export default assignWorkers;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import CityBuild from '@civ-clone/core-city-build/CityBuild';
|
|
2
|
+
/**
|
|
3
|
+
* Clears what a captured city was building, so its new owner chooses: the item was its previous owner's choice, at
|
|
4
|
+
* its previous owner's price (civ-clone/web-renderer#670, #500). The city is asked for as a captured city's even if
|
|
5
|
+
* its previous owner had chosen nothing (civ-clone/web-renderer#716).
|
|
6
|
+
*/
|
|
7
|
+
export declare const clearCapturedBuild: (cityBuild: CityBuild) => void;
|
|
8
|
+
/**
|
|
9
|
+
* A captured city keeps what it was building, at its new owner's price, without its previous owner's queue
|
|
10
|
+
* (civ-clone/web-renderer#670). Asked for as a captured city's if nothing was being built (civ-clone/web-renderer#716).
|
|
11
|
+
*/
|
|
12
|
+
export declare const keepCapturedBuild: (cityBuild: CityBuild) => void;
|
package/lib/captured.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.keepCapturedBuild = exports.clearCapturedBuild = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Clears what a captured city was building, so its new owner chooses: the item was its previous owner's choice, at
|
|
6
|
+
* its previous owner's price (civ-clone/web-renderer#670, #500). The city is asked for as a captured city's even if
|
|
7
|
+
* its previous owner had chosen nothing (civ-clone/web-renderer#716).
|
|
8
|
+
*/
|
|
9
|
+
const clearCapturedBuild = (cityBuild) => {
|
|
10
|
+
if (cityBuild.building() === null) {
|
|
11
|
+
cityBuild.setCleared('captured');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
cityBuild.cancel('captured');
|
|
15
|
+
};
|
|
16
|
+
exports.clearCapturedBuild = clearCapturedBuild;
|
|
17
|
+
/**
|
|
18
|
+
* A captured city keeps what it was building, at its new owner's price, without its previous owner's queue
|
|
19
|
+
* (civ-clone/web-renderer#670). Asked for as a captured city's if nothing was being built (civ-clone/web-renderer#716).
|
|
20
|
+
*/
|
|
21
|
+
const keepCapturedBuild = (cityBuild) => {
|
|
22
|
+
if (cityBuild.building() === null) {
|
|
23
|
+
cityBuild.setCleared('captured');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
cityBuild.clearQueue();
|
|
27
|
+
cityBuild.reprice();
|
|
28
|
+
};
|
|
29
|
+
exports.keepCapturedBuild = keepCapturedBuild;
|
|
30
|
+
//# sourceMappingURL=captured.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captured.js","sourceRoot":"","sources":["captured.ts"],"names":[],"mappings":";;;AAEA;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,SAAoB,EAAQ,EAAE;IAC/D,IAAI,SAAS,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACjC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAEjC,OAAO;KACR;IAED,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC/B,CAAC,CAAC;AARW,QAAA,kBAAkB,sBAQ7B;AAEF;;;GAGG;AACI,MAAM,iBAAiB,GAAG,CAAC,SAAoB,EAAQ,EAAE;IAC9D,IAAI,SAAS,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACjC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAEjC,OAAO;KACR;IAED,SAAS,CAAC,UAAU,EAAE,CAAC;IACvB,SAAS,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC;AATW,QAAA,iBAAiB,qBAS5B"}
|
package/lib/captured.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import CityBuild from '@civ-clone/core-city-build/CityBuild';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clears what a captured city was building, so its new owner chooses: the item was its previous owner's choice, at
|
|
5
|
+
* its previous owner's price (civ-clone/web-renderer#670, #500). The city is asked for as a captured city's even if
|
|
6
|
+
* its previous owner had chosen nothing (civ-clone/web-renderer#716).
|
|
7
|
+
*/
|
|
8
|
+
export const clearCapturedBuild = (cityBuild: CityBuild): void => {
|
|
9
|
+
if (cityBuild.building() === null) {
|
|
10
|
+
cityBuild.setCleared('captured');
|
|
11
|
+
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
cityBuild.cancel('captured');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A captured city keeps what it was building, at its new owner's price, without its previous owner's queue
|
|
20
|
+
* (civ-clone/web-renderer#670). Asked for as a captured city's if nothing was being built (civ-clone/web-renderer#716).
|
|
21
|
+
*/
|
|
22
|
+
export const keepCapturedBuild = (cityBuild: CityBuild): void => {
|
|
23
|
+
if (cityBuild.building() === null) {
|
|
24
|
+
cityBuild.setCleared('captured');
|
|
25
|
+
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
cityBuild.clearQueue();
|
|
30
|
+
cityBuild.reprice();
|
|
31
|
+
};
|
package/lib/created.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AvailableSpecialistRegistry } from '@civ-clone/core-city/AvailableSpecialistRegistry';
|
|
2
|
+
import { CityGrowthRegistry } from '@civ-clone/core-city-growth/CityGrowthRegistry';
|
|
3
|
+
import { PlayerWorldRegistry } from '@civ-clone/core-player-world/PlayerWorldRegistry';
|
|
4
|
+
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
5
|
+
import { SpecialistRegistry } from '@civ-clone/core-city/SpecialistRegistry';
|
|
6
|
+
import { WorkedTileRegistry } from '@civ-clone/core-city/WorkedTileRegistry';
|
|
7
|
+
import { AvailableCityBuildItemsRegistry } from '@civ-clone/core-city-build/AvailableCityBuildItemsRegistry';
|
|
8
|
+
import City from '@civ-clone/core-city/City';
|
|
9
|
+
import CityBuild from '@civ-clone/core-city-build/CityBuild';
|
|
10
|
+
import { CityBuildRegistry } from '@civ-clone/core-city-build/CityBuildRegistry';
|
|
11
|
+
import CityGrowth from '@civ-clone/core-city-growth/CityGrowth';
|
|
12
|
+
/**
|
|
13
|
+
* Gives a new city its build, whose first item is asked for as a new city's (civ-clone/web-renderer#716).
|
|
14
|
+
*/
|
|
15
|
+
export declare const registerCityBuild: (
|
|
16
|
+
city: City,
|
|
17
|
+
cityBuildRegistry: CityBuildRegistry,
|
|
18
|
+
availableBuildItemsRegistry: AvailableCityBuildItemsRegistry,
|
|
19
|
+
ruleRegistry?: RuleRegistry
|
|
20
|
+
) => CityBuild;
|
|
21
|
+
/**
|
|
22
|
+
* Gives a new city its growth: its size and food store.
|
|
23
|
+
*/
|
|
24
|
+
export declare const registerCityGrowth: (
|
|
25
|
+
city: City,
|
|
26
|
+
cityGrowthRegistry?: CityGrowthRegistry,
|
|
27
|
+
ruleRegistry?: RuleRegistry
|
|
28
|
+
) => CityGrowth;
|
|
29
|
+
/**
|
|
30
|
+
* A new city works its own centre, taking it from any city that worked it, which its `TileReassigned` rules then let
|
|
31
|
+
* choose another, and puts its citizens to work.
|
|
32
|
+
*/
|
|
33
|
+
export declare const workCityTile: (
|
|
34
|
+
city: City,
|
|
35
|
+
ruleRegistry?: RuleRegistry,
|
|
36
|
+
playerWorldRegistry?: PlayerWorldRegistry,
|
|
37
|
+
cityGrowthRegistry?: CityGrowthRegistry,
|
|
38
|
+
workedTileRegistry?: WorkedTileRegistry,
|
|
39
|
+
specialistRegistry?: SpecialistRegistry,
|
|
40
|
+
availableSpecialistRegistry?: AvailableSpecialistRegistry
|
|
41
|
+
) => void;
|
package/lib/created.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.workCityTile = exports.registerCityGrowth = exports.registerCityBuild = void 0;
|
|
4
|
+
const AvailableSpecialistRegistry_1 = require("@civ-clone/core-city/AvailableSpecialistRegistry");
|
|
5
|
+
const CityGrowthRegistry_1 = require("@civ-clone/core-city-growth/CityGrowthRegistry");
|
|
6
|
+
const PlayerWorldRegistry_1 = require("@civ-clone/core-player-world/PlayerWorldRegistry");
|
|
7
|
+
const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
|
|
8
|
+
const SpecialistRegistry_1 = require("@civ-clone/core-city/SpecialistRegistry");
|
|
9
|
+
const WorkedTileRegistry_1 = require("@civ-clone/core-city/WorkedTileRegistry");
|
|
10
|
+
const CityBuild_1 = require("@civ-clone/core-city-build/CityBuild");
|
|
11
|
+
const CityGrowth_1 = require("@civ-clone/core-city-growth/CityGrowth");
|
|
12
|
+
const TileReassigned_1 = require("@civ-clone/core-city/Rules/TileReassigned");
|
|
13
|
+
const WorkedTile_1 = require("@civ-clone/core-city/WorkedTile");
|
|
14
|
+
const assignWorkers_1 = require("./assignWorkers");
|
|
15
|
+
/**
|
|
16
|
+
* Gives a new city its build, whose first item is asked for as a new city's (civ-clone/web-renderer#716).
|
|
17
|
+
*/
|
|
18
|
+
const registerCityBuild = (city, cityBuildRegistry, availableBuildItemsRegistry, ruleRegistry = RuleRegistry_1.instance) => {
|
|
19
|
+
const cityBuild = new CityBuild_1.default(city, availableBuildItemsRegistry, ruleRegistry);
|
|
20
|
+
cityBuild.setCleared('created');
|
|
21
|
+
cityBuildRegistry.register(cityBuild);
|
|
22
|
+
return cityBuild;
|
|
23
|
+
};
|
|
24
|
+
exports.registerCityBuild = registerCityBuild;
|
|
25
|
+
/**
|
|
26
|
+
* Gives a new city its growth: its size and food store.
|
|
27
|
+
*/
|
|
28
|
+
const registerCityGrowth = (city, cityGrowthRegistry = CityGrowthRegistry_1.instance, ruleRegistry = RuleRegistry_1.instance) => {
|
|
29
|
+
const cityGrowth = new CityGrowth_1.default(city, ruleRegistry);
|
|
30
|
+
cityGrowthRegistry.register(cityGrowth);
|
|
31
|
+
return cityGrowth;
|
|
32
|
+
};
|
|
33
|
+
exports.registerCityGrowth = registerCityGrowth;
|
|
34
|
+
/**
|
|
35
|
+
* A new city works its own centre, taking it from any city that worked it, which its `TileReassigned` rules then let
|
|
36
|
+
* choose another, and puts its citizens to work.
|
|
37
|
+
*/
|
|
38
|
+
const workCityTile = (city, ruleRegistry = RuleRegistry_1.instance, playerWorldRegistry = PlayerWorldRegistry_1.instance, cityGrowthRegistry = CityGrowthRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
|
|
39
|
+
const existingWorkedTile = workedTileRegistry.getByTile(city.tile());
|
|
40
|
+
if (existingWorkedTile !== null) {
|
|
41
|
+
workedTileRegistry.unregister(existingWorkedTile);
|
|
42
|
+
}
|
|
43
|
+
workedTileRegistry.register(new WorkedTile_1.default(city.tile(), city));
|
|
44
|
+
// Give the existing City the chance to reassign its worker before...
|
|
45
|
+
if (existingWorkedTile !== null) {
|
|
46
|
+
ruleRegistry.process(TileReassigned_1.default, existingWorkedTile.city(), existingWorkedTile.tile());
|
|
47
|
+
}
|
|
48
|
+
// ...assigning the remaining workers.
|
|
49
|
+
(0, assignWorkers_1.default)(city, playerWorldRegistry, cityGrowthRegistry, workedTileRegistry, specialistRegistry, availableSpecialistRegistry);
|
|
50
|
+
};
|
|
51
|
+
exports.workCityTile = workCityTile;
|
|
52
|
+
//# sourceMappingURL=created.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"created.js","sourceRoot":"","sources":["created.ts"],"names":[],"mappings":";;;AAAA,kGAG0D;AAC1D,uFAGwD;AACxD,0FAG0D;AAC1D,oEAG2C;AAC3C,gFAGiD;AACjD,gFAGiD;AAGjD,oEAA6D;AAE7D,uEAAgE;AAChE,8EAAuE;AACvE,gEAAyD;AACzD,mDAA4C;AAE5C;;GAEG;AACI,MAAM,iBAAiB,GAAG,CAC/B,IAAU,EACV,iBAAoC,EACpC,2BAA4D,EAC5D,eAA6B,uBAAoB,EACtC,EAAE;IACb,MAAM,SAAS,GAAG,IAAI,mBAAS,CAC7B,IAAI,EACJ,2BAA2B,EAC3B,YAAY,CACb,CAAC;IAEF,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAEtC,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAjBW,QAAA,iBAAiB,qBAiB5B;AAEF;;GAEG;AACI,MAAM,kBAAkB,GAAG,CAChC,IAAU,EACV,qBAAyC,6BAA0B,EACnE,eAA6B,uBAAoB,EACrC,EAAE;IACd,MAAM,UAAU,GAAG,IAAI,oBAAU,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAEtD,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAExC,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAVW,QAAA,kBAAkB,sBAU7B;AAEF;;;GAGG;AACI,MAAM,YAAY,GAAG,CAC1B,IAAU,EACV,eAA6B,uBAAoB,EACjD,sBAA2C,8BAA2B,EACtE,qBAAyC,6BAA0B,EACnE,qBAAyC,6BAA0B,EACnE,qBAAyC,6BAA0B,EACnE,8BAA2D,sCAAmC,EACxF,EAAE;IACR,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAErE,IAAI,kBAAkB,KAAK,IAAI,EAAE;QAC/B,kBAAkB,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;KACnD;IAED,kBAAkB,CAAC,QAAQ,CAAC,IAAI,oBAAU,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAE/D,qEAAqE;IACrE,IAAI,kBAAkB,KAAK,IAAI,EAAE;QAC/B,YAAY,CAAC,OAAO,CAClB,wBAAc,EACd,kBAAkB,CAAC,IAAI,EAAE,EACzB,kBAAkB,CAAC,IAAI,EAAE,CAC1B,CAAC;KACH;IAED,sCAAsC;IACtC,IAAA,uBAAa,EACX,IAAI,EACJ,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,CAC5B,CAAC;AACJ,CAAC,CAAC;AAnCW,QAAA,YAAY,gBAmCvB"}
|
package/lib/created.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AvailableSpecialistRegistry,
|
|
3
|
+
instance as availableSpecialistRegistryInstance,
|
|
4
|
+
} from '@civ-clone/core-city/AvailableSpecialistRegistry';
|
|
5
|
+
import {
|
|
6
|
+
CityGrowthRegistry,
|
|
7
|
+
instance as cityGrowthRegistryInstance,
|
|
8
|
+
} from '@civ-clone/core-city-growth/CityGrowthRegistry';
|
|
9
|
+
import {
|
|
10
|
+
PlayerWorldRegistry,
|
|
11
|
+
instance as playerWorldRegistryInstance,
|
|
12
|
+
} from '@civ-clone/core-player-world/PlayerWorldRegistry';
|
|
13
|
+
import {
|
|
14
|
+
RuleRegistry,
|
|
15
|
+
instance as ruleRegistryInstance,
|
|
16
|
+
} from '@civ-clone/core-rule/RuleRegistry';
|
|
17
|
+
import {
|
|
18
|
+
SpecialistRegistry,
|
|
19
|
+
instance as specialistRegistryInstance,
|
|
20
|
+
} from '@civ-clone/core-city/SpecialistRegistry';
|
|
21
|
+
import {
|
|
22
|
+
WorkedTileRegistry,
|
|
23
|
+
instance as workedTileRegistryInstance,
|
|
24
|
+
} from '@civ-clone/core-city/WorkedTileRegistry';
|
|
25
|
+
import { AvailableCityBuildItemsRegistry } from '@civ-clone/core-city-build/AvailableCityBuildItemsRegistry';
|
|
26
|
+
import City from '@civ-clone/core-city/City';
|
|
27
|
+
import CityBuild from '@civ-clone/core-city-build/CityBuild';
|
|
28
|
+
import { CityBuildRegistry } from '@civ-clone/core-city-build/CityBuildRegistry';
|
|
29
|
+
import CityGrowth from '@civ-clone/core-city-growth/CityGrowth';
|
|
30
|
+
import TileReassigned from '@civ-clone/core-city/Rules/TileReassigned';
|
|
31
|
+
import WorkedTile from '@civ-clone/core-city/WorkedTile';
|
|
32
|
+
import assignWorkers from './assignWorkers';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gives a new city its build, whose first item is asked for as a new city's (civ-clone/web-renderer#716).
|
|
36
|
+
*/
|
|
37
|
+
export const registerCityBuild = (
|
|
38
|
+
city: City,
|
|
39
|
+
cityBuildRegistry: CityBuildRegistry,
|
|
40
|
+
availableBuildItemsRegistry: AvailableCityBuildItemsRegistry,
|
|
41
|
+
ruleRegistry: RuleRegistry = ruleRegistryInstance
|
|
42
|
+
): CityBuild => {
|
|
43
|
+
const cityBuild = new CityBuild(
|
|
44
|
+
city,
|
|
45
|
+
availableBuildItemsRegistry,
|
|
46
|
+
ruleRegistry
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
cityBuild.setCleared('created');
|
|
50
|
+
|
|
51
|
+
cityBuildRegistry.register(cityBuild);
|
|
52
|
+
|
|
53
|
+
return cityBuild;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gives a new city its growth: its size and food store.
|
|
58
|
+
*/
|
|
59
|
+
export const registerCityGrowth = (
|
|
60
|
+
city: City,
|
|
61
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
62
|
+
ruleRegistry: RuleRegistry = ruleRegistryInstance
|
|
63
|
+
): CityGrowth => {
|
|
64
|
+
const cityGrowth = new CityGrowth(city, ruleRegistry);
|
|
65
|
+
|
|
66
|
+
cityGrowthRegistry.register(cityGrowth);
|
|
67
|
+
|
|
68
|
+
return cityGrowth;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A new city works its own centre, taking it from any city that worked it, which its `TileReassigned` rules then let
|
|
73
|
+
* choose another, and puts its citizens to work.
|
|
74
|
+
*/
|
|
75
|
+
export const workCityTile = (
|
|
76
|
+
city: City,
|
|
77
|
+
ruleRegistry: RuleRegistry = ruleRegistryInstance,
|
|
78
|
+
playerWorldRegistry: PlayerWorldRegistry = playerWorldRegistryInstance,
|
|
79
|
+
cityGrowthRegistry: CityGrowthRegistry = cityGrowthRegistryInstance,
|
|
80
|
+
workedTileRegistry: WorkedTileRegistry = workedTileRegistryInstance,
|
|
81
|
+
specialistRegistry: SpecialistRegistry = specialistRegistryInstance,
|
|
82
|
+
availableSpecialistRegistry: AvailableSpecialistRegistry = availableSpecialistRegistryInstance
|
|
83
|
+
): void => {
|
|
84
|
+
const existingWorkedTile = workedTileRegistry.getByTile(city.tile());
|
|
85
|
+
|
|
86
|
+
if (existingWorkedTile !== null) {
|
|
87
|
+
workedTileRegistry.unregister(existingWorkedTile);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
workedTileRegistry.register(new WorkedTile(city.tile(), city));
|
|
91
|
+
|
|
92
|
+
// Give the existing City the chance to reassign its worker before...
|
|
93
|
+
if (existingWorkedTile !== null) {
|
|
94
|
+
ruleRegistry.process(
|
|
95
|
+
TileReassigned,
|
|
96
|
+
existingWorkedTile.city(),
|
|
97
|
+
existingWorkedTile.tile()
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ...assigning the remaining workers.
|
|
102
|
+
assignWorkers(
|
|
103
|
+
city,
|
|
104
|
+
playerWorldRegistry,
|
|
105
|
+
cityGrowthRegistry,
|
|
106
|
+
workedTileRegistry,
|
|
107
|
+
specialistRegistry,
|
|
108
|
+
availableSpecialistRegistry
|
|
109
|
+
);
|
|
110
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SpecialistRegistry } from '@civ-clone/core-city/SpecialistRegistry';
|
|
2
|
+
import City from '@civ-clone/core-city/City';
|
|
3
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
4
|
+
import { WorkedTileRegistry } from '@civ-clone/core-city/WorkedTileRegistry';
|
|
5
|
+
/**
|
|
6
|
+
* Destroys each of `units` (a city's own, say) that isn't destroyed already: when the city was its player's last, the
|
|
7
|
+
* first unit to go defeats the player, which destroys the rest (civ-clone/web-renderer#600).
|
|
8
|
+
*/
|
|
9
|
+
export declare const destroyUnits: (units: Unit[]) => void;
|
|
10
|
+
/**
|
|
11
|
+
* The tiles a city that's gone worked are free for another to work.
|
|
12
|
+
*/
|
|
13
|
+
export declare const releaseWorkedTiles: (
|
|
14
|
+
city: City,
|
|
15
|
+
workedTileRegistry: WorkedTileRegistry
|
|
16
|
+
) => void;
|
|
17
|
+
/**
|
|
18
|
+
* A city that's gone has no specialists.
|
|
19
|
+
*/
|
|
20
|
+
export declare const releaseSpecialists: (
|
|
21
|
+
city: City,
|
|
22
|
+
specialistRegistry: SpecialistRegistry
|
|
23
|
+
) => void;
|