@civ-clone/base-unit-fuel 0.0.0-stage → 0.1.0

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/.gitattributes ADDED
@@ -0,0 +1,4 @@
1
+ **/*.d.ts linguist-generated=true
2
+ **/*.js linguist-generated=true
3
+ **/*.js.map linguist-generated=true
4
+
@@ -0,0 +1,39 @@
1
+ name: Build and test application on push to remote
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - uses: pnpm/action-setup@v4
11
+ with:
12
+ version: 11
13
+ - uses: actions/setup-node@v4
14
+ with:
15
+ node-version: 22
16
+ # No lock file, so this tests what a consumer resolves. The settings match
17
+ # web-renderer's pnpm-workspace.yaml: several @civ-clone packages depend on
18
+ # each other as `github:` URLs, this project's own releases are often only
19
+ # minutes old, and nothing here needs a dependency's build script.
20
+ - run: >-
21
+ pnpm install --no-frozen-lockfile
22
+ --config.block-exotic-subdeps=false
23
+ --config.minimum-release-age=0
24
+ --config.strict-dep-builds=false
25
+ - run: npm run --if-present prettier:check
26
+ # The tests load the committed `.js`, so compile to prove the source
27
+ # type-checks, and fail if the committed output is stale. `--force`
28
+ # because a fresh checkout's timestamps can make `tsc --build` treat the
29
+ # committed output as up to date and skip the compile entirely.
30
+ - run: npm run --if-present ts:compile -- --force
31
+ - run: npm run --if-present prettier:format
32
+ # Stale *or never committed* output fails: `git diff` alone misses a new
33
+ # module whose `.js` was never added, which the published package would
34
+ # then omit. Only the compiled extensions, so the lock file the install
35
+ # writes cannot fail it; `node_modules` is ignored, so it stays out.
36
+ - run: |
37
+ git status --short --untracked-files=all -- '*.js' '*.d.ts' '*.js.map'
38
+ test -z "$(git status --porcelain --untracked-files=all -- '*.js' '*.d.ts' '*.js.map')"
39
+ - run: npm run --if-present test
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true
4
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Dom Hastings
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,58 @@
1
- # Temporary Holding Version
1
+ # base-unit-fuel
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Fuel for units that must return to base: a range, the turns a unit has ended out, where it refuels, and running out. A
4
+ library: nothing registers on import, and a game that registers none of it has no fuel.
5
+
6
+ ## API
7
+
8
+ Deep-import the modules; there is no index.
9
+
10
+ - `lib/Fuel`: `Fuel`, the unit yield that gives a unit's range: how many of its player's turns it can end away from a
11
+ refuelling point, counting the one it set out in (1: it must refuel every turn; 2: it can stay out one turn). 0, as
12
+ with no `Yield` rule for it, needs no fuel.
13
+ - `lib/fuelRules`: `fuelRules({ ruleRegistry, unitImprovementRegistry, unitRegistry }, { prefix?, range?, countOn?,
14
+ countsAsMove?, isAttack? })`:
15
+ - a rule that counts turns out, as `countOn` says (see below);
16
+ - an `OutOfFuel` rule that destroys the unit. A game adds its own `OutOfFuel` rules for anything else (a notice).
17
+ - `range(unit)` is the unit's `Fuel` yield by default (`fuelRange`); a game can read its own table instead.
18
+ - helpers: `refuels(ruleRegistry, unit, tile?)`, `turnsAloft(unitImprovementRegistry, unit)`, `aloftOf(...)`,
19
+ `checkFuel(...)` and `checkFuelAfterMove(...)`.
20
+ - Rule ids `<prefix>:player/turn-end/fuel` (or `<prefix>:unit/moved/fuel`) and `<prefix>:unit/out-of-fuel/destroy`.
21
+ - `lib/Rules`: `Refuel(unit, tile) → boolean` (a unit refuels on a square if any rule's criteria match and its effect
22
+ answers `true`) and `OutOfFuel(unit)`.
23
+ - `lib/refuel`, `Refuel` rules a game registers, each with an optional `applies(unit)` filter and id:
24
+ `refuelInOwnCity(cityRegistry)`, `refuelOnTileImprovement(Improvement, tileImprovementRegistry)`,
25
+ `refuelInStackWith(isBase(base, unit), unitRegistry)` (another of the player's units on the square, such as one that
26
+ carries it) and `refuelAboard(transportRegistry)`.
27
+ - `lib/reach`, for computer players:
28
+ - `fuelState(unitImprovementRegistry, unit, range?)`: `{ range, turnsAloft, turnsLeft, reach }`, `turnsLeft` counting
29
+ this turn, and `reach` the moves before the end of the last of them (moves left, then full movement each turn);
30
+ `null` for a unit with no range.
31
+ - `refuelInReach(ruleRegistry, unitImprovementRegistry, unit, { range?, distance?, candidates? })`: the nearest
32
+ square within reach where the unit refuels, or `null`.
33
+
34
+ ## When fuel is counted
35
+
36
+ `countOn` chooses:
37
+
38
+ - `'turn-end'` (the default): a `TurnEnd` rule. At the end of each of the player's turns, a unit with a range that
39
+ refuels where it is loses its count; any other has ended one more turn out, and runs out of fuel (`OutOfFuel`) when
40
+ that reaches its range. It doesn't matter whether the unit spent its moves. This is how civ1-unit's own fuel rule
41
+ counts, so that rule can move onto this package later with the same behaviour.
42
+ - `'out-of-moves'`: a `Moved` rule, as OpenCiv2 (github.com/richardpenman/civ2, read at 49f26ec,
43
+ `unit_move_dir_air_fuel`) counts. After a move (`countsAsMove`, by default an action to another square), a unit that
44
+ refuels where it is has landed: it loses its count and its remaining moves. The exception is a unit with a range of 1
45
+ that attacked (`isAttack`, by default it didn't end up on its target) and still has moves, which hasn't landed. A unit
46
+ anywhere else, left with no moves, has spent one more turn out, and runs out of fuel when that reaches its range. A
47
+ unit that ends its turn with moves unspent isn't counted, as in the original.
48
+
49
+ The places the original refuels (a city, an airbase, a stack with a unit that carries it) are the rules in
50
+ `lib/refuel`.
51
+
52
+ ## The count is saved
53
+
54
+ `Aloft` (a `UnitImprovement` holding the count) exists only for a unit that is out, and goes when it refuels, so a
55
+ unit at its base carries and saves nothing extra. A game that saves registers it in its
56
+ `AvailableUnitImprovementRegistry` (`tests/save.test.ts` round-trips it through core-save-game; without that, loading
57
+ fails with "Unknown entity type 'Aloft'"). It is also sent to clients in a unit's `improvements`, so a client should leave it
58
+ out of what it shows (or show the count).
package/lib/Aloft.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import Unit from '@civ-clone/core-unit/Unit';
2
+ import UnitImprovement from '@civ-clone/core-unit-improvement/UnitImprovement';
3
+ /**
4
+ * How many of its player's turns a fuelled unit has ended away from somewhere it refuels. Only a unit that is out has
5
+ * one, and it goes when the unit refuels, so a unit at its base carries and saves nothing extra. A game that saves
6
+ * needs it in its `AvailableUnitImprovementRegistry`.
7
+ */
8
+ export declare class Aloft extends UnitImprovement {
9
+ private _turns;
10
+ constructor(unit: Unit, turns?: number);
11
+ turns(): number;
12
+ setTurns(turns: number): void;
13
+ }
14
+ export default Aloft;
package/lib/Aloft.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Aloft = void 0;
4
+ const UnitImprovement_1 = require("@civ-clone/core-unit-improvement/UnitImprovement");
5
+ /**
6
+ * How many of its player's turns a fuelled unit has ended away from somewhere it refuels. Only a unit that is out has
7
+ * one, and it goes when the unit refuels, so a unit at its base carries and saves nothing extra. A game that saves
8
+ * needs it in its `AvailableUnitImprovementRegistry`.
9
+ */
10
+ class Aloft extends UnitImprovement_1.default {
11
+ constructor(unit, turns = 1) {
12
+ super(unit);
13
+ this._turns = turns;
14
+ this.addKey('turns');
15
+ }
16
+ turns() {
17
+ return this._turns;
18
+ }
19
+ setTurns(turns) {
20
+ this._turns = turns;
21
+ }
22
+ }
23
+ exports.Aloft = Aloft;
24
+ exports.default = Aloft;
25
+ //# sourceMappingURL=Aloft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Aloft.js","sourceRoot":"","sources":["Aloft.ts"],"names":[],"mappings":";;;AACA,sFAA+E;AAE/E;;;;GAIG;AACH,MAAa,KAAM,SAAQ,yBAAe;IAGxC,YAAY,IAAU,EAAE,QAAgB,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEZ,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAEpB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;CACF;AAlBD,sBAkBC;AAED,kBAAe,KAAK,CAAC"}
package/lib/Aloft.ts ADDED
@@ -0,0 +1,29 @@
1
+ import Unit from '@civ-clone/core-unit/Unit';
2
+ import UnitImprovement from '@civ-clone/core-unit-improvement/UnitImprovement';
3
+
4
+ /**
5
+ * How many of its player's turns a fuelled unit has ended away from somewhere it refuels. Only a unit that is out has
6
+ * one, and it goes when the unit refuels, so a unit at its base carries and saves nothing extra. A game that saves
7
+ * needs it in its `AvailableUnitImprovementRegistry`.
8
+ */
9
+ export class Aloft extends UnitImprovement {
10
+ private _turns: number;
11
+
12
+ constructor(unit: Unit, turns: number = 1) {
13
+ super(unit);
14
+
15
+ this._turns = turns;
16
+
17
+ this.addKey('turns');
18
+ }
19
+
20
+ turns(): number {
21
+ return this._turns;
22
+ }
23
+
24
+ setTurns(turns: number): void {
25
+ this._turns = turns;
26
+ }
27
+ }
28
+
29
+ export default Aloft;
package/lib/Fuel.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import Yield from '@civ-clone/core-yield/Yield';
2
+ /**
3
+ * How many of its player's turns a unit can end away from somewhere it refuels (see `Refuel`), counting the one it set
4
+ * out in: at 1 it must refuel by the end of each turn, at 2 it can stay out for one turn. A unit with none (0, as a
5
+ * game that registers no `Yield` rule for it leaves every unit) needs no fuel.
6
+ */
7
+ export declare class Fuel extends Yield {}
8
+ export default Fuel;
package/lib/Fuel.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Fuel = void 0;
4
+ const Yield_1 = require("@civ-clone/core-yield/Yield");
5
+ /**
6
+ * How many of its player's turns a unit can end away from somewhere it refuels (see `Refuel`), counting the one it set
7
+ * out in: at 1 it must refuel by the end of each turn, at 2 it can stay out for one turn. A unit with none (0, as a
8
+ * game that registers no `Yield` rule for it leaves every unit) needs no fuel.
9
+ */
10
+ class Fuel extends Yield_1.default {
11
+ }
12
+ exports.Fuel = Fuel;
13
+ exports.default = Fuel;
14
+ //# sourceMappingURL=Fuel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fuel.js","sourceRoot":"","sources":["Fuel.ts"],"names":[],"mappings":";;;AAAA,uDAAgD;AAEhD;;;;GAIG;AACH,MAAa,IAAK,SAAQ,eAAK;CAAG;AAAlC,oBAAkC;AAElC,kBAAe,IAAI,CAAC"}
package/lib/Fuel.ts ADDED
@@ -0,0 +1,10 @@
1
+ import Yield from '@civ-clone/core-yield/Yield';
2
+
3
+ /**
4
+ * How many of its player's turns a unit can end away from somewhere it refuels (see `Refuel`), counting the one it set
5
+ * out in: at 1 it must refuel by the end of each turn, at 2 it can stay out for one turn. A unit with none (0, as a
6
+ * game that registers no `Yield` rule for it leaves every unit) needs no fuel.
7
+ */
8
+ export class Fuel extends Yield {}
9
+
10
+ export default Fuel;
package/lib/Rules.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import Rule from '@civ-clone/core-rule/Rule';
2
+ import Tile from '@civ-clone/core-world/Tile';
3
+ import Unit from '@civ-clone/core-unit/Unit';
4
+ /**
5
+ * Whether `unit` refuels on `tile`: it does if any rule's criteria match and its effect returns `true`. A game says
6
+ * where with its criteria (see `refuelInOwnCity`, `refuelOnTileImprovement`, `refuelInStackWith`, `refuelAboard`).
7
+ */
8
+ export declare class Refuel extends Rule<[unit: Unit, tile: Tile], boolean> {}
9
+ /** `unit` ran out of fuel away from anywhere it refuels. `fuelRules` adds one that destroys it. */
10
+ export declare class OutOfFuel extends Rule<[unit: Unit], void> {}
package/lib/Rules.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OutOfFuel = exports.Refuel = void 0;
4
+ const Rule_1 = require("@civ-clone/core-rule/Rule");
5
+ /**
6
+ * Whether `unit` refuels on `tile`: it does if any rule's criteria match and its effect returns `true`. A game says
7
+ * where with its criteria (see `refuelInOwnCity`, `refuelOnTileImprovement`, `refuelInStackWith`, `refuelAboard`).
8
+ */
9
+ class Refuel extends Rule_1.default {
10
+ }
11
+ exports.Refuel = Refuel;
12
+ /** `unit` ran out of fuel away from anywhere it refuels. `fuelRules` adds one that destroys it. */
13
+ class OutOfFuel extends Rule_1.default {
14
+ }
15
+ exports.OutOfFuel = OutOfFuel;
16
+ //# sourceMappingURL=Rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Rules.js","sourceRoot":"","sources":["Rules.ts"],"names":[],"mappings":";;;AAAA,oDAA6C;AAI7C;;;GAGG;AACH,MAAa,MAAO,SAAQ,cAAuC;CAAG;AAAtE,wBAAsE;AAEtE,mGAAmG;AACnG,MAAa,SAAU,SAAQ,cAAwB;CAAG;AAA1D,8BAA0D"}
package/lib/Rules.ts ADDED
@@ -0,0 +1,12 @@
1
+ import Rule from '@civ-clone/core-rule/Rule';
2
+ import Tile from '@civ-clone/core-world/Tile';
3
+ import Unit from '@civ-clone/core-unit/Unit';
4
+
5
+ /**
6
+ * Whether `unit` refuels on `tile`: it does if any rule's criteria match and its effect returns `true`. A game says
7
+ * where with its criteria (see `refuelInOwnCity`, `refuelOnTileImprovement`, `refuelInStackWith`, `refuelAboard`).
8
+ */
9
+ export class Refuel extends Rule<[unit: Unit, tile: Tile], boolean> {}
10
+
11
+ /** `unit` ran out of fuel away from anywhere it refuels. `fuelRules` adds one that destroys it. */
12
+ export class OutOfFuel extends Rule<[unit: Unit], void> {}
@@ -0,0 +1,95 @@
1
+ import { OutOfFuel } from './Rules';
2
+ import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
3
+ import { UnitImprovementRegistry } from '@civ-clone/core-unit-improvement/UnitImprovementRegistry';
4
+ import { UnitRegistry } from '@civ-clone/core-unit/UnitRegistry';
5
+ import Action from '@civ-clone/core-unit/Action';
6
+ import Aloft from './Aloft';
7
+ import Moved from '@civ-clone/core-unit/Rules/Moved';
8
+ import Player from '@civ-clone/core-player/Player';
9
+ import Tile from '@civ-clone/core-world/Tile';
10
+ import TurnEnd from '@civ-clone/core-player/Rules/TurnEnd';
11
+ import Unit from '@civ-clone/core-unit/Unit';
12
+ export interface FuelDependencies {
13
+ ruleRegistry?: RuleRegistry;
14
+ unitImprovementRegistry?: UnitImprovementRegistry;
15
+ unitRegistry?: UnitRegistry;
16
+ }
17
+ /** A unit's range: how many turns it can end away from a refuelling point. */
18
+ export type Range = (unit: Unit) => number;
19
+ /**
20
+ * When a unit's turns out are counted:
21
+ *
22
+ * - `'turn-end'`: at the end of each of its player's turns, wherever it is (`checkFuel`);
23
+ * - `'out-of-moves'`: after each move, as the original does (`checkFuelAfterMove`).
24
+ */
25
+ export type CountOn = 'turn-end' | 'out-of-moves';
26
+ export interface FuelOptions {
27
+ /**
28
+ * The rules' ids are `<prefix>:player/turn-end/fuel` (or `<prefix>:unit/moved/fuel`) and
29
+ * `<prefix>:unit/out-of-fuel/destroy`. Unnamed without.
30
+ */
31
+ prefix?: string;
32
+ /** A unit's range: its `Fuel` yield by default. */
33
+ range?: Range;
34
+ /** When turns out are counted: `'turn-end'` by default. */
35
+ countOn?: CountOn;
36
+ /** For `'out-of-moves'`, whether a `Moved` action is a move: by default, one to another square (attacks included). */
37
+ countsAsMove?: (unit: Unit, action: Action) => boolean;
38
+ /** For `'out-of-moves'`, whether the action was an attack: by default, the unit didn't end up on its target. */
39
+ isAttack?: (unit: Unit, action: Action) => boolean;
40
+ }
41
+ /** A unit's `Fuel` yield. */
42
+ export declare const fuelRange: Range;
43
+ /** `unit`'s `Aloft` mark, if it has one. */
44
+ export declare const aloftOf: (
45
+ unitImprovementRegistry: UnitImprovementRegistry,
46
+ unit: Unit
47
+ ) => Aloft | undefined;
48
+ /** How many turns `unit` has ended away from a refuelling point since it last refuelled. */
49
+ export declare const turnsAloft: (
50
+ unitImprovementRegistry: UnitImprovementRegistry,
51
+ unit: Unit
52
+ ) => number;
53
+ /** Whether `unit` refuels on `tile` (see `Refuel`). */
54
+ export declare const refuels: (
55
+ ruleRegistry: RuleRegistry,
56
+ unit: Unit,
57
+ tile?: Tile
58
+ ) => boolean;
59
+ /**
60
+ * At the end of `player`'s turn, for each of its units with a range (above 0): one that refuels where it is loses its
61
+ * `Aloft` mark; any other has ended one more turn out, and runs out of fuel (`OutOfFuel`) once that reaches its range.
62
+ */
63
+ export declare const checkFuel: (
64
+ ruleRegistry: RuleRegistry,
65
+ unitImprovementRegistry: UnitImprovementRegistry,
66
+ unitRegistry: UnitRegistry,
67
+ range: Range
68
+ ) => (player: Player) => void;
69
+ /**
70
+ * After `unit` moves (OpenCiv2, `unit_move_dir_air_fuel`), for a unit with a range (above 0):
71
+ *
72
+ * - where it refuels, it loses its `Aloft` mark and its remaining moves (it has landed), except that a unit with a
73
+ * range of 1 that attacked and still has moves left hasn't landed;
74
+ * - anywhere else, once it has no moves left, it has spent one more turn out, and runs out of fuel (`OutOfFuel`) when
75
+ * that reaches its range.
76
+ */
77
+ export declare const checkFuelAfterMove: (
78
+ ruleRegistry: RuleRegistry,
79
+ unitImprovementRegistry: UnitImprovementRegistry,
80
+ range: Range,
81
+ isAttack: (unit: Unit, action: Action) => boolean
82
+ ) => (unit: Unit, action: Action) => void;
83
+ /**
84
+ * A ruleset's fuel rules: one that counts the turns each unit with a range spends away from a refuelling point, and an
85
+ * `OutOfFuel` rule that destroys a unit that runs out. A game adds its own `Refuel` rules (see `lib/refuel`), and its
86
+ * own `OutOfFuel` rules for anything else that should happen (a notice, say).
87
+ *
88
+ * With `countOn: 'turn-end'` (the default) the count is a `TurnEnd` rule (`checkFuel`): it doesn't matter whether a
89
+ * unit spent its moves. With `'out-of-moves'` it is a `Moved` rule (`checkFuelAfterMove`), as the original counts it.
90
+ */
91
+ export declare const fuelRules: (
92
+ { ruleRegistry, unitImprovementRegistry, unitRegistry }: FuelDependencies,
93
+ { prefix, range, countOn, countsAsMove, isAttack }?: FuelOptions
94
+ ) => (TurnEnd | Moved | OutOfFuel)[];
95
+ export default fuelRules;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fuelRules = exports.checkFuelAfterMove = exports.checkFuel = exports.refuels = exports.turnsAloft = exports.aloftOf = exports.fuelRange = void 0;
4
+ const Rules_1 = require("./Rules");
5
+ const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
6
+ const UnitImprovementRegistry_1 = require("@civ-clone/core-unit-improvement/UnitImprovementRegistry");
7
+ const UnitRegistry_1 = require("@civ-clone/core-unit/UnitRegistry");
8
+ const Aloft_1 = require("./Aloft");
9
+ const Criterion_1 = require("@civ-clone/core-rule/Criterion");
10
+ const Effect_1 = require("@civ-clone/core-rule/Effect");
11
+ const Fuel_1 = require("./Fuel");
12
+ const Moved_1 = require("@civ-clone/core-unit/Rules/Moved");
13
+ const TurnEnd_1 = require("@civ-clone/core-player/Rules/TurnEnd");
14
+ /** A unit's `Fuel` yield. */
15
+ const fuelRange = (unit) => {
16
+ const [range] = unit.yield(new Fuel_1.default());
17
+ return range.value();
18
+ };
19
+ exports.fuelRange = fuelRange;
20
+ /** `unit`'s `Aloft` mark, if it has one. */
21
+ const aloftOf = (unitImprovementRegistry, unit) => unitImprovementRegistry
22
+ .getByUnit(unit)
23
+ .find((unitImprovement) => unitImprovement instanceof Aloft_1.default);
24
+ exports.aloftOf = aloftOf;
25
+ /** How many turns `unit` has ended away from a refuelling point since it last refuelled. */
26
+ const turnsAloft = (unitImprovementRegistry, unit) => { var _a, _b; return (_b = (_a = (0, exports.aloftOf)(unitImprovementRegistry, unit)) === null || _a === void 0 ? void 0 : _a.turns()) !== null && _b !== void 0 ? _b : 0; };
27
+ exports.turnsAloft = turnsAloft;
28
+ /** Whether `unit` refuels on `tile` (see `Refuel`). */
29
+ const refuels = (ruleRegistry, unit, tile = unit.tile()) => ruleRegistry
30
+ .process(Rules_1.Refuel, unit, tile)
31
+ .some((answer) => answer === true);
32
+ exports.refuels = refuels;
33
+ /**
34
+ * At the end of `player`'s turn, for each of its units with a range (above 0): one that refuels where it is loses its
35
+ * `Aloft` mark; any other has ended one more turn out, and runs out of fuel (`OutOfFuel`) once that reaches its range.
36
+ */
37
+ const checkFuel = (ruleRegistry, unitImprovementRegistry, unitRegistry, range) => (player) => unitRegistry.getByPlayer(player).forEach((unit) => {
38
+ var _a;
39
+ if (unit.destroyed()) {
40
+ return;
41
+ }
42
+ const unitRange = range(unit);
43
+ if (!(unitRange > 0)) {
44
+ return;
45
+ }
46
+ const aloft = (0, exports.aloftOf)(unitImprovementRegistry, unit);
47
+ if ((0, exports.refuels)(ruleRegistry, unit)) {
48
+ if (aloft !== undefined) {
49
+ unitImprovementRegistry.unregister(aloft);
50
+ }
51
+ return;
52
+ }
53
+ const turns = ((_a = aloft === null || aloft === void 0 ? void 0 : aloft.turns()) !== null && _a !== void 0 ? _a : 0) + 1;
54
+ if (turns < unitRange) {
55
+ if (aloft === undefined) {
56
+ unitImprovementRegistry.register(new Aloft_1.default(unit, turns));
57
+ return;
58
+ }
59
+ aloft.setTurns(turns);
60
+ return;
61
+ }
62
+ if (aloft !== undefined) {
63
+ unitImprovementRegistry.unregister(aloft);
64
+ }
65
+ ruleRegistry.process(Rules_1.OutOfFuel, unit);
66
+ });
67
+ exports.checkFuel = checkFuel;
68
+ /**
69
+ * After `unit` moves (OpenCiv2, `unit_move_dir_air_fuel`), for a unit with a range (above 0):
70
+ *
71
+ * - where it refuels, it loses its `Aloft` mark and its remaining moves (it has landed), except that a unit with a
72
+ * range of 1 that attacked and still has moves left hasn't landed;
73
+ * - anywhere else, once it has no moves left, it has spent one more turn out, and runs out of fuel (`OutOfFuel`) when
74
+ * that reaches its range.
75
+ */
76
+ const checkFuelAfterMove = (ruleRegistry, unitImprovementRegistry, range, isAttack) => (unit, action) => {
77
+ var _a;
78
+ if (unit.destroyed()) {
79
+ return;
80
+ }
81
+ const unitRange = range(unit);
82
+ if (!(unitRange > 0)) {
83
+ return;
84
+ }
85
+ const aloft = (0, exports.aloftOf)(unitImprovementRegistry, unit), landed = (0, exports.refuels)(ruleRegistry, unit) &&
86
+ !(unitRange === 1 &&
87
+ unit.moves().value() > 0 &&
88
+ isAttack(unit, action));
89
+ if (landed) {
90
+ if (aloft !== undefined) {
91
+ unitImprovementRegistry.unregister(aloft);
92
+ }
93
+ unit.moves().set(0);
94
+ return;
95
+ }
96
+ if (unit.moves().value() > 0) {
97
+ return;
98
+ }
99
+ const turns = ((_a = aloft === null || aloft === void 0 ? void 0 : aloft.turns()) !== null && _a !== void 0 ? _a : 0) + 1;
100
+ if (turns < unitRange) {
101
+ if (aloft === undefined) {
102
+ unitImprovementRegistry.register(new Aloft_1.default(unit, turns));
103
+ return;
104
+ }
105
+ aloft.setTurns(turns);
106
+ return;
107
+ }
108
+ if (aloft !== undefined) {
109
+ unitImprovementRegistry.unregister(aloft);
110
+ }
111
+ ruleRegistry.process(Rules_1.OutOfFuel, unit);
112
+ };
113
+ exports.checkFuelAfterMove = checkFuelAfterMove;
114
+ /**
115
+ * A ruleset's fuel rules: one that counts the turns each unit with a range spends away from a refuelling point, and an
116
+ * `OutOfFuel` rule that destroys a unit that runs out. A game adds its own `Refuel` rules (see `lib/refuel`), and its
117
+ * own `OutOfFuel` rules for anything else that should happen (a notice, say).
118
+ *
119
+ * With `countOn: 'turn-end'` (the default) the count is a `TurnEnd` rule (`checkFuel`): it doesn't matter whether a
120
+ * unit spent its moves. With `'out-of-moves'` it is a `Moved` rule (`checkFuelAfterMove`), as the original counts it.
121
+ */
122
+ const fuelRules = ({ ruleRegistry = RuleRegistry_1.instance, unitImprovementRegistry = UnitImprovementRegistry_1.instance, unitRegistry = UnitRegistry_1.instance, }, { prefix, range = exports.fuelRange, countOn = 'turn-end', countsAsMove = (unit, action) => action.from() !== action.to(), isAttack = (unit, action) => unit.tile() !== action.to(), } = {}) => {
123
+ const id = (name) => prefix === undefined ? [] : [`${prefix}:${name}`];
124
+ return [
125
+ countOn === 'out-of-moves'
126
+ ? new Moved_1.default(...id('unit/moved/fuel'), new Criterion_1.default(countsAsMove), new Effect_1.default((0, exports.checkFuelAfterMove)(ruleRegistry, unitImprovementRegistry, range, isAttack)))
127
+ : new TurnEnd_1.default(...id('player/turn-end/fuel'), new Effect_1.default((0, exports.checkFuel)(ruleRegistry, unitImprovementRegistry, unitRegistry, range))),
128
+ new Rules_1.OutOfFuel(...id('unit/out-of-fuel/destroy'), new Effect_1.default((unit) => unit.destroy())),
129
+ ];
130
+ };
131
+ exports.fuelRules = fuelRules;
132
+ exports.default = exports.fuelRules;
133
+ //# sourceMappingURL=fuelRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuelRules.js","sourceRoot":"","sources":["fuelRules.ts"],"names":[],"mappings":"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"}