@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.
@@ -6,10 +6,34 @@ jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
8
  steps:
9
- - uses: actions/checkout@v1
10
- - uses: actions/setup-node@v1
9
+ - uses: actions/checkout@v4
10
+ - uses: pnpm/action-setup@v4
11
11
  with:
12
- node-version: 12
13
- - run: yarn install
14
- - run: yarn prettier:check
15
- # - run: yarn test
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
- This is a template repo.
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 Naval from '@civ-clone/base-unit-type-naval/Naval';
2
- export declare class Submarine extends Naval {}
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 Naval_1 = require("@civ-clone/base-unit-type-naval/Naval");
5
- class Submarine extends Naval_1.default {
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,iEAA0D;AAE1D,MAAa,SAAU,SAAQ,eAAK;CAAG;AAAvC,8BAAuC;AAEvC,kBAAe,SAAS,CAAC"}
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 Naval from '@civ-clone/base-unit-type-naval/Naval';
1
+ import NavalTransport from '@civ-clone/base-unit-type-naval-transport/NavalTransport';
2
2
 
3
- export class Submarine extends Naval {}
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.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
- "@types/node": "^14.11.2",
20
- "prettier": "^2.1.2",
21
- "typescript": "^4.0.3"
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.0"
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
+ });