@civ-clone/base-unit-submarine 0.1.1 → 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/.github/workflows/build-on-push.yml +30 -6
- package/README.md +3 -1
- package/Submarine.d.ts +2 -2
- package/Submarine.js +6 -2
- package/Submarine.js.map +1 -1
- package/Submarine.ts +6 -2
- package/package.json +22 -6
- package/tests/NavalTransport.test.ts +167 -0
- package/tests/Submarine.test.ts +41 -0
|
@@ -6,10 +6,34 @@ jobs:
|
|
|
6
6
|
build:
|
|
7
7
|
runs-on: ubuntu-latest
|
|
8
8
|
steps:
|
|
9
|
-
- uses: actions/checkout@
|
|
10
|
-
- uses:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- uses: pnpm/action-setup@v4
|
|
11
11
|
with:
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
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/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# base-unit-submarine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Submarine` unit type, a naval unit. What it can do (that it is submerged, what it attacks, what it carries) is
|
|
4
|
+
up to the ruleset's rules and traits.
|
|
4
5
|
|
|
6
|
+
A library: no `main`, so it registers nothing on its own. Import it as `@civ-clone/base-unit-submarine/Submarine`.
|
package/Submarine.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
export declare class Submarine extends
|
|
1
|
+
import NavalTransport from '@civ-clone/base-unit-type-naval-transport/NavalTransport';
|
|
2
|
+
export declare class Submarine extends NavalTransport {}
|
|
3
3
|
export default Submarine;
|
package/Submarine.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Submarine = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const NavalTransport_1 = require("@civ-clone/base-unit-type-naval-transport/NavalTransport");
|
|
5
|
+
// A transport, so a ruleset can give it a hold (for missiles, say). It carries nothing until the ruleset's rules say
|
|
6
|
+
// so: with no `Capacity` yield rule for it, its capacity is 0, so it never has room and `stow` refuses. That is the
|
|
7
|
+
// only guard. With no `Carries` trait, no `CanStow` rule applies to it, and `canStow()` (every applicable rule agrees)
|
|
8
|
+
// is then true for any unit, so anything asking whether it can carry a unit must also ask `hasCapacity()`.
|
|
9
|
+
class Submarine extends NavalTransport_1.default {
|
|
6
10
|
}
|
|
7
11
|
exports.Submarine = Submarine;
|
|
8
12
|
exports.default = Submarine;
|
package/Submarine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Submarine.js","sourceRoot":"","sources":["Submarine.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"Submarine.js","sourceRoot":"","sources":["Submarine.ts"],"names":[],"mappings":";;;AAAA,6FAAsF;AAEtF,qHAAqH;AACrH,qHAAqH;AACrH,wHAAwH;AACxH,4GAA4G;AAC5G,MAAa,SAAU,SAAQ,wBAAc;CAAG;AAAhD,8BAAgD;AAEhD,kBAAe,SAAS,CAAC"}
|
package/Submarine.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import NavalTransport from '@civ-clone/base-unit-type-naval-transport/NavalTransport';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// A transport, so a ruleset can give it a hold (for missiles, say). It carries nothing until the ruleset's rules say
|
|
4
|
+
// so: with no `Capacity` yield rule for it, its capacity is 0, so it never has room and `stow` refuses. That is the
|
|
5
|
+
// only guard. With no `Carries` trait, no `CanStow` rule applies to it, and `canStow()` (every applicable rule agrees)
|
|
6
|
+
// is then true for any unit, so anything asking whether it can carry a unit must also ask `hasCapacity()`.
|
|
7
|
+
export class Submarine extends NavalTransport {}
|
|
4
8
|
|
|
5
9
|
export default Submarine;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civ-clone/base-unit-submarine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "The Submarine unit type.",
|
|
4
5
|
"repository": "git@github.com:civ-clone/base-unit-submarine.git",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"typescript",
|
|
@@ -11,16 +12,31 @@
|
|
|
11
12
|
"private": false,
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "npm run ts:compile && npm run prettier:format",
|
|
14
|
-
"prettier:check": "prettier --config .prettierrc '**/*.ts'",
|
|
15
|
+
"prettier:check": "prettier --config .prettierrc --check '**/*.ts'",
|
|
15
16
|
"prettier:format": "prettier --config .prettierrc '**/*.ts' --write",
|
|
17
|
+
"test": "ts-mocha ./tests/*.test.ts",
|
|
16
18
|
"ts:compile": "tsc --build tsconfig.json"
|
|
17
19
|
},
|
|
18
20
|
"devDependencies": {
|
|
19
|
-
"@
|
|
20
|
-
"
|
|
21
|
-
"
|
|
21
|
+
"@civ-clone/base-unit-type-naval": "^0.1.0",
|
|
22
|
+
"@civ-clone/core-data-object": "^0.1.19",
|
|
23
|
+
"@civ-clone/core-game": "^0.1.17",
|
|
24
|
+
"@civ-clone/core-player": "^0.1.10",
|
|
25
|
+
"@civ-clone/core-rule": "^0.1.9",
|
|
26
|
+
"@civ-clone/core-unit": "^0.1.32",
|
|
27
|
+
"@civ-clone/core-unit-transport": "^0.1.10",
|
|
28
|
+
"@civ-clone/core-world": "^0.1.24",
|
|
29
|
+
"@civ-clone/core-yield": "^0.1.0",
|
|
30
|
+
"@types/chai": "^4.0.0",
|
|
31
|
+
"@types/mocha": "^10.0.0",
|
|
32
|
+
"@types/node": "^16.0.0",
|
|
33
|
+
"chai": "^4.0.0",
|
|
34
|
+
"mocha": "^10.0.0",
|
|
35
|
+
"prettier": "^2.8.8",
|
|
36
|
+
"ts-mocha": "^10.0.0",
|
|
37
|
+
"typescript": "^4.0.0"
|
|
22
38
|
},
|
|
23
39
|
"dependencies": {
|
|
24
|
-
"@civ-clone/base-unit-type-naval": "^0.1.
|
|
40
|
+
"@civ-clone/base-unit-type-naval-transport": "^0.1.3"
|
|
25
41
|
}
|
|
26
42
|
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Capacity, CargoWeight } from '@civ-clone/core-unit-transport/Yields';
|
|
2
|
+
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
3
|
+
import Effect from '@civ-clone/core-rule/Effect';
|
|
4
|
+
import { Game } from '@civ-clone/core-game/Game';
|
|
5
|
+
import { ITransport } from '@civ-clone/core-unit-transport/Transport';
|
|
6
|
+
import Naval from '@civ-clone/base-unit-type-naval/Naval';
|
|
7
|
+
import NavalTransport from '@civ-clone/base-unit-type-naval-transport/NavalTransport';
|
|
8
|
+
import Player from '@civ-clone/core-player/Player';
|
|
9
|
+
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
10
|
+
import Submarine from '../Submarine';
|
|
11
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
12
|
+
import TransportRegistry from '@civ-clone/core-unit-transport/TransportRegistry';
|
|
13
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
14
|
+
import UnitYield from '@civ-clone/core-unit/Rules/Yield';
|
|
15
|
+
import YieldValue from '@civ-clone/core-yield/Yield';
|
|
16
|
+
import { expect } from 'chai';
|
|
17
|
+
import generateInheritance from '@civ-clone/core-data-object/lib/generateInheritance';
|
|
18
|
+
import CanStow from '@civ-clone/core-unit-transport/Rules/CanStow';
|
|
19
|
+
|
|
20
|
+
// The Submarine as a `NavalTransport` (civ-clone/web-renderer#1007, M2b): Civ2's carries missiles, Civ1's carries
|
|
21
|
+
// nothing. A ruleset that gives it no hold (Civ1) must see the same unit as before.
|
|
22
|
+
|
|
23
|
+
// What the Submarine was before it became a transport.
|
|
24
|
+
class NavalSubmarine extends Naval {}
|
|
25
|
+
class Passenger extends Unit {}
|
|
26
|
+
|
|
27
|
+
const setup = () => {
|
|
28
|
+
const ruleRegistry = new RuleRegistry(),
|
|
29
|
+
transportRegistry = new TransportRegistry(),
|
|
30
|
+
args: [null, Player, Tile, RuleRegistry, TransportRegistry] = [
|
|
31
|
+
null,
|
|
32
|
+
null as unknown as Player,
|
|
33
|
+
null as unknown as Tile,
|
|
34
|
+
ruleRegistry,
|
|
35
|
+
transportRegistry,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// As civ1-unit's: the cargo weight is what is aboard, for every unit.
|
|
39
|
+
ruleRegistry.register(
|
|
40
|
+
new UnitYield(
|
|
41
|
+
new Criterion(
|
|
42
|
+
(unit: Unit, unitYield: YieldValue): unitYield is CargoWeight =>
|
|
43
|
+
unitYield instanceof CargoWeight
|
|
44
|
+
),
|
|
45
|
+
new Effect((unit: Unit, unitYield: YieldValue): void =>
|
|
46
|
+
unitYield.set(
|
|
47
|
+
transportRegistry.getByTransport(unit as unknown as ITransport).length
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return { args, ruleRegistry, transportRegistry };
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe('Submarine as a NavalTransport', (): void => {
|
|
57
|
+
it('is a naval transport', (): void => {
|
|
58
|
+
const { args } = setup(),
|
|
59
|
+
submarine = new Submarine(...args);
|
|
60
|
+
|
|
61
|
+
expect(submarine).instanceOf(NavalTransport);
|
|
62
|
+
expect(submarine).instanceOf(Naval);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('has no room and stows nothing when the ruleset gives it no hold', (): void => {
|
|
66
|
+
const { args, transportRegistry } = setup(),
|
|
67
|
+
submarine = new Submarine(...args),
|
|
68
|
+
passenger = new Passenger(
|
|
69
|
+
null,
|
|
70
|
+
null as unknown as Player,
|
|
71
|
+
null as unknown as Tile
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(submarine.capacity().value()).equal(0);
|
|
75
|
+
expect(submarine.hasCapacity()).false;
|
|
76
|
+
expect(submarine.hasCargo()).false;
|
|
77
|
+
expect(submarine.cargo()).deep.equal([]);
|
|
78
|
+
// No `CanStow` rule applies, so `canStow` agrees to anything: capacity 0 is what keeps it empty.
|
|
79
|
+
expect(submarine.canStow(passenger)).true;
|
|
80
|
+
expect(submarine.stow(passenger, null as unknown as Tile)).false;
|
|
81
|
+
expect(transportRegistry.entries()).deep.equal([]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('carries what the ruleset gives it room for', (): void => {
|
|
85
|
+
const { args, ruleRegistry } = setup(),
|
|
86
|
+
submarine = new Submarine(...args),
|
|
87
|
+
passenger = new Passenger(
|
|
88
|
+
null,
|
|
89
|
+
null as unknown as Player,
|
|
90
|
+
null as unknown as Tile
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
ruleRegistry.register(
|
|
94
|
+
new UnitYield(
|
|
95
|
+
new Criterion(
|
|
96
|
+
(unit: Unit, unitYield: YieldValue): unitYield is Capacity =>
|
|
97
|
+
unitYield instanceof Capacity
|
|
98
|
+
),
|
|
99
|
+
new Criterion((unit: Unit): boolean => unit instanceof Submarine),
|
|
100
|
+
new Effect((unit: Unit, unitYield: YieldValue): void =>
|
|
101
|
+
unitYield.set(8)
|
|
102
|
+
)
|
|
103
|
+
),
|
|
104
|
+
new CanStow(
|
|
105
|
+
new Effect(
|
|
106
|
+
(transport: ITransport, unit: Unit): boolean =>
|
|
107
|
+
unit instanceof Passenger
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
expect(submarine.stow(passenger, null as unknown as Tile)).true;
|
|
113
|
+
expect(submarine.cargo()).deep.equal([passenger]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('saves exactly what it did as a plain naval unit, and sends the same fields', (): void => {
|
|
117
|
+
const { args } = setup(),
|
|
118
|
+
submarine = new Submarine(...args),
|
|
119
|
+
before = new NavalSubmarine(...args);
|
|
120
|
+
|
|
121
|
+
expect(submarine.stateKeys()).deep.equal(before.stateKeys());
|
|
122
|
+
expect(submarine.keys()).deep.equal(before.keys());
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("names its new ancestors in the client payload's class chain", (): void => {
|
|
126
|
+
const { args } = setup();
|
|
127
|
+
|
|
128
|
+
// `toPlainObject` sends `keys()` (unchanged, above) and the class chain as `__`. A client asking whether it is a
|
|
129
|
+
// `Naval` or a `Unit` gets the same answer; one asking about `NavalTransport` or `Transport` now finds them.
|
|
130
|
+
expect(generateInheritance(new Submarine(...args))).deep.equal(
|
|
131
|
+
generateInheritance(new NavalSubmarine(...args)).flatMap(
|
|
132
|
+
(name: string): string[] =>
|
|
133
|
+
name === 'NavalSubmarine'
|
|
134
|
+
? ['Submarine', 'NavalTransport', 'Transport']
|
|
135
|
+
: [name]
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('gets its transport collaborators from Game.inject when loaded from a save made before it was a transport', (): void => {
|
|
141
|
+
const game = new Game(),
|
|
142
|
+
{ args } = setup(),
|
|
143
|
+
before = new NavalSubmarine(...args) as unknown as Record<
|
|
144
|
+
string,
|
|
145
|
+
unknown
|
|
146
|
+
>,
|
|
147
|
+
// What a save written before this change holds for a Submarine: its saved fields, restored onto the class as it
|
|
148
|
+
// is now without running the constructor, as core-save-game's `hydrate` does.
|
|
149
|
+
loaded = Object.create(Submarine.prototype) as Submarine;
|
|
150
|
+
|
|
151
|
+
new NavalSubmarine(...args).stateKeys().forEach((key: string): void => {
|
|
152
|
+
(loaded as unknown as Record<string, unknown>)[key] = before[key];
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
game.inject(loaded);
|
|
156
|
+
|
|
157
|
+
expect(
|
|
158
|
+
(loaded as unknown as { _transportRegistry: unknown })._transportRegistry
|
|
159
|
+
).equal(game.transports);
|
|
160
|
+
expect(
|
|
161
|
+
(loaded as unknown as { _transportRuleRegistry: unknown })
|
|
162
|
+
._transportRuleRegistry
|
|
163
|
+
).equal(game.rules);
|
|
164
|
+
expect(loaded.hasCargo()).false;
|
|
165
|
+
expect(loaded.cargo()).deep.equal([]);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Naval from '@civ-clone/base-unit-type-naval/Naval';
|
|
2
|
+
import Player from '@civ-clone/core-player/Player';
|
|
3
|
+
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
4
|
+
import Submarine from '../Submarine';
|
|
5
|
+
import Tile from '@civ-clone/core-world/Tile';
|
|
6
|
+
import Unit from '@civ-clone/core-unit/Unit';
|
|
7
|
+
import { expect } from 'chai';
|
|
8
|
+
|
|
9
|
+
// A naval unit with nothing of its own, to compare the Submarine with: what a game saves of one is what it saves of
|
|
10
|
+
// any naval unit.
|
|
11
|
+
class PlainNaval extends Naval {}
|
|
12
|
+
|
|
13
|
+
const unitArgs = (
|
|
14
|
+
ruleRegistry: RuleRegistry = new RuleRegistry()
|
|
15
|
+
): [null, Player, Tile, RuleRegistry] => [
|
|
16
|
+
null,
|
|
17
|
+
null as unknown as Player,
|
|
18
|
+
null as unknown as Tile,
|
|
19
|
+
ruleRegistry,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
describe('Submarine', (): void => {
|
|
23
|
+
it('is a naval unit', (): void => {
|
|
24
|
+
const submarine = new Submarine(...unitArgs());
|
|
25
|
+
|
|
26
|
+
expect(submarine).instanceOf(Naval);
|
|
27
|
+
expect(submarine).instanceOf(Unit);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('saves exactly what any naval unit saves', (): void => {
|
|
31
|
+
expect(new Submarine(...unitArgs()).stateKeys()).deep.equal(
|
|
32
|
+
new PlainNaval(...unitArgs()).stateKeys()
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('sends the client exactly what any naval unit sends', (): void => {
|
|
37
|
+
expect(new Submarine(...unitArgs()).keys()).deep.equal(
|
|
38
|
+
new PlainNaval(...unitArgs()).keys()
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|