@thegraid/hexlib 1.0.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/dist/choosers.js +59 -0
- package/dist/counters.js +155 -0
- package/dist/game-play.js +455 -0
- package/dist/game-setup.js +280 -0
- package/dist/game-state.js +112 -0
- package/dist/hex-intfs.js +82 -0
- package/dist/hex.js +714 -0
- package/dist/image-loader.js +69 -0
- package/dist/meeple.js +152 -0
- package/dist/plan-proxy.js +33 -0
- package/dist/player-panel.js +125 -0
- package/dist/player.js +112 -0
- package/dist/scenario-parser.js +67 -0
- package/dist/shapes.js +304 -0
- package/dist/stream-writer.js +192 -0
- package/dist/table-params.js +48 -0
- package/dist/table.js +707 -0
- package/dist/text-log.js +53 -0
- package/dist/tile-source.js +115 -0
- package/dist/tile.js +312 -0
- package/dist/types.js +15 -0
- package/package.json +45 -0
package/dist/text-log.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { stime } from '@thegraid/easeljs-lib';
|
|
2
|
+
import { Container, Text } from '@thegraid/easeljs-module';
|
|
3
|
+
import { F } from '@thegraid/common-lib';
|
|
4
|
+
export class TextLog extends Container {
|
|
5
|
+
Aname;
|
|
6
|
+
size;
|
|
7
|
+
lead;
|
|
8
|
+
constructor(Aname, nlines = 6, size = 30, lead = 3) {
|
|
9
|
+
super();
|
|
10
|
+
this.Aname = Aname;
|
|
11
|
+
this.size = size;
|
|
12
|
+
this.lead = lead;
|
|
13
|
+
this.lines = new Array(nlines);
|
|
14
|
+
for (let ndx = 0; ndx < nlines; ndx++)
|
|
15
|
+
this.lines[ndx] = this.newText(`//0:`);
|
|
16
|
+
this.addChild(...this.lines);
|
|
17
|
+
}
|
|
18
|
+
lines;
|
|
19
|
+
lastLine = '';
|
|
20
|
+
nReps = 0;
|
|
21
|
+
height(n = this.lines.length) {
|
|
22
|
+
return (this.size + this.lead) * n;
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
this.lines.forEach(tline => tline.text = '');
|
|
26
|
+
this.stage?.update();
|
|
27
|
+
}
|
|
28
|
+
newText(line = '') {
|
|
29
|
+
const text = new Text(line, F.fontSpec(this.size));
|
|
30
|
+
text.textAlign = 'left';
|
|
31
|
+
text.mouseEnabled = false;
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
spaceLines(cy = 0, lead = this.lead) {
|
|
35
|
+
this.lines.forEach(tline => (tline.y = cy, cy += tline.getMeasuredLineHeight() + lead));
|
|
36
|
+
}
|
|
37
|
+
log(line, from = '', toConsole = true) {
|
|
38
|
+
line = line.replace('/\n/g', '-');
|
|
39
|
+
toConsole && console.log(stime(`${from}:`), line);
|
|
40
|
+
if (line === this.lastLine) {
|
|
41
|
+
this.lines[this.lines.length - 1].text = `[${++this.nReps}] ${line}`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.removeChild(this.lines.shift());
|
|
45
|
+
this.lines.push(this.addChild(this.newText(line)));
|
|
46
|
+
this.spaceLines();
|
|
47
|
+
this.lastLine = line;
|
|
48
|
+
this.nReps = 0;
|
|
49
|
+
}
|
|
50
|
+
this.stage?.update();
|
|
51
|
+
return line;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ValueEvent } from "@thegraid/easeljs-lib";
|
|
2
|
+
import { NumCounter } from "./counters";
|
|
3
|
+
import { H } from "./hex-intfs";
|
|
4
|
+
import { TP } from "./table-params";
|
|
5
|
+
/** a Dispenser of a set of Tiles.
|
|
6
|
+
*
|
|
7
|
+
* Source.hex.tile or Source.hex.meep holds an available Tile, placed by moveTo()
|
|
8
|
+
*/
|
|
9
|
+
export class TileSource {
|
|
10
|
+
type;
|
|
11
|
+
player;
|
|
12
|
+
hex;
|
|
13
|
+
static update = 'update';
|
|
14
|
+
Aname;
|
|
15
|
+
allUnits = new Array();
|
|
16
|
+
available = new Array();
|
|
17
|
+
counter; // counter of available units.
|
|
18
|
+
constructor(type, player, hex, counter) {
|
|
19
|
+
this.type = type;
|
|
20
|
+
this.player = player;
|
|
21
|
+
this.hex = hex;
|
|
22
|
+
this.Aname = `${type.name}Source`;
|
|
23
|
+
if (counter === undefined) {
|
|
24
|
+
const cont = hex.map.mapCont.counterCont; // GP.gamePlay.hexMap.mapCont.counterCont;
|
|
25
|
+
const { x, y } = hex.cont.localToLocal(0, TP.hexRad / H.sqrt3, cont);
|
|
26
|
+
counter = this.makeCounter(`${type.name}:${player?.index ?? 'any'}`, this.numAvailable, `lightblue`, TP.hexRad / 2);
|
|
27
|
+
counter.attachToContainer(cont, { x: counter.x + x, y: counter.y + y });
|
|
28
|
+
}
|
|
29
|
+
this.counter = counter;
|
|
30
|
+
}
|
|
31
|
+
/** can override */
|
|
32
|
+
makeCounter(name, initValue, color, fontSize, fontName, textColor) {
|
|
33
|
+
return new NumCounter(name, initValue, color, fontSize, fontName, textColor);
|
|
34
|
+
}
|
|
35
|
+
/** length of available[] plus unit on this.hex */
|
|
36
|
+
get numAvailable() { return this.available.length + (this.hex?.tile || this.hex?.meep ? 1 : 0); }
|
|
37
|
+
/** mark unit available for later deployment */
|
|
38
|
+
availUnit(unit) {
|
|
39
|
+
if (!this.allUnits.includes(unit)) {
|
|
40
|
+
this.allUnits.push(unit);
|
|
41
|
+
unit.source = this;
|
|
42
|
+
}
|
|
43
|
+
if (!this.available.includes(unit)) {
|
|
44
|
+
this.available.push(unit);
|
|
45
|
+
unit.hex = undefined;
|
|
46
|
+
unit.visible = false;
|
|
47
|
+
unit.x = unit.y = 0;
|
|
48
|
+
}
|
|
49
|
+
this.updateCounter();
|
|
50
|
+
}
|
|
51
|
+
/** is the top available unit, next to be picked. */
|
|
52
|
+
isAvailable(unit) {
|
|
53
|
+
return this.hex.tile === unit;
|
|
54
|
+
}
|
|
55
|
+
/** move unit to undefined, remove from parent container, remove from available and allUnits. */
|
|
56
|
+
deleteUnit(unit) {
|
|
57
|
+
if (unit && this.isAvailable(unit)) {
|
|
58
|
+
unit.moveTo(undefined); // --> this.nextUnit();
|
|
59
|
+
unit.parent?.removeChild(unit);
|
|
60
|
+
}
|
|
61
|
+
const ndx = this.allUnits.indexOf(unit);
|
|
62
|
+
if (ndx >= 0)
|
|
63
|
+
this.allUnits.splice(ndx, 1);
|
|
64
|
+
const adx = this.available.indexOf(unit);
|
|
65
|
+
if (adx > 0) {
|
|
66
|
+
this.available.splice(adx, 1);
|
|
67
|
+
this.updateCounter();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** move all units to undefined, and remove from parent container.
|
|
71
|
+
* remove all from available (and allUnits)
|
|
72
|
+
* @return number of units deleted (previous length of allUnits).
|
|
73
|
+
*/
|
|
74
|
+
deleteAll(doAlso) {
|
|
75
|
+
const n = this.allUnits.length;
|
|
76
|
+
this.allUnits.forEach(unit => {
|
|
77
|
+
unit.moveTo(undefined); // --> this.nextUnit();
|
|
78
|
+
unit.parent?.removeChild(unit);
|
|
79
|
+
doAlso(unit);
|
|
80
|
+
});
|
|
81
|
+
this.allUnits.length = 0;
|
|
82
|
+
this.available.length = 0;
|
|
83
|
+
this.updateCounter();
|
|
84
|
+
return n;
|
|
85
|
+
}
|
|
86
|
+
filterUnits(pred) { return this.allUnits.filter(pred); }
|
|
87
|
+
get sourceHexUnit() {
|
|
88
|
+
return (this.hex.tile || this.hex.meep); // moveTo puts it somewhere...
|
|
89
|
+
}
|
|
90
|
+
/** programmatic, vs Table.dragStart */
|
|
91
|
+
takeUnit() {
|
|
92
|
+
const unit = this.sourceHexUnit;
|
|
93
|
+
unit?.moveTo(undefined);
|
|
94
|
+
this.nextUnit();
|
|
95
|
+
return unit;
|
|
96
|
+
}
|
|
97
|
+
/** move next available unit to source.hex, make visible */
|
|
98
|
+
nextUnit(unit = this.available.shift()) {
|
|
99
|
+
if (unit) {
|
|
100
|
+
unit.visible = true;
|
|
101
|
+
unit.moveTo(this.hex); // and try push to available
|
|
102
|
+
}
|
|
103
|
+
this.updateCounter();
|
|
104
|
+
return unit;
|
|
105
|
+
}
|
|
106
|
+
updateCounter() {
|
|
107
|
+
this.counter.parent?.setChildIndex(this.counter, this.counter.parent.numChildren - 1);
|
|
108
|
+
this.counter.setValue(this.numAvailable);
|
|
109
|
+
ValueEvent.dispatchValueEvent(this.counter, TileSource.update, this.numAvailable);
|
|
110
|
+
this.hex?.cont?.updateCache(); // updateCache of counter on hex
|
|
111
|
+
this.hex?.map?.update(); // updateCache of hexMap with hex & counter
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class UnitSource extends TileSource {
|
|
115
|
+
}
|
package/dist/tile.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { C, S, className, stime } from "@thegraid/common-lib";
|
|
2
|
+
import { Bitmap, Container } from "@thegraid/easeljs-module";
|
|
3
|
+
import { ImageLoader } from "./image-loader";
|
|
4
|
+
import { C1, HexShape, TileShape } from "./shapes";
|
|
5
|
+
import { TP } from "./table-params";
|
|
6
|
+
import { CenterText } from "@thegraid/easeljs-lib";
|
|
7
|
+
class TileLoader {
|
|
8
|
+
Uname = ['Univ0', 'Univ1']; // from citymap
|
|
9
|
+
aliases = { Monument1: 'arc_de_triomphe3', Monument2: 'Statue-of-liberty' };
|
|
10
|
+
fromAlias(names) {
|
|
11
|
+
return names.map(name => this.aliases[name] ?? name);
|
|
12
|
+
}
|
|
13
|
+
imageArgs = {
|
|
14
|
+
root: 'assets/images/',
|
|
15
|
+
fnames: this.fromAlias(['Recycle']),
|
|
16
|
+
ext: 'png',
|
|
17
|
+
};
|
|
18
|
+
imageLoader;
|
|
19
|
+
/** use ImageLoader to load images, THEN invoke callback. */
|
|
20
|
+
loadImages(cb) {
|
|
21
|
+
this.imageLoader = new ImageLoader(this.imageArgs, (imap) => cb?.());
|
|
22
|
+
}
|
|
23
|
+
getImage(name) {
|
|
24
|
+
return this.imageLoader.imap.get(this.aliases[name] ?? name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Someday refactor: all the cardboard bits (Tiles, Meeples & Coins) */
|
|
28
|
+
class Tile0 extends Container {
|
|
29
|
+
static gamePlay;
|
|
30
|
+
static loader = new TileLoader();
|
|
31
|
+
// constructor() { super(); }
|
|
32
|
+
gamePlay = Tile.gamePlay;
|
|
33
|
+
player;
|
|
34
|
+
get pColor() { return this.player?.color; }
|
|
35
|
+
get recycleVerb() { return 'demolished'; }
|
|
36
|
+
/** name in set of filenames loaded in GameSetup */
|
|
37
|
+
addImageBitmap(name, at = this.numChildren - 1) {
|
|
38
|
+
const img = Tile0.loader.getImage(name), bm = new Bitmap(img);
|
|
39
|
+
const width = TP.hexRad, scale = width / Math.max(img.height, img.width);
|
|
40
|
+
bm.scaleX = bm.scaleY = scale;
|
|
41
|
+
const sw = img.width * scale, sh = img.height * scale;
|
|
42
|
+
bm.x = -sw / 2;
|
|
43
|
+
bm.y = -sh / 2;
|
|
44
|
+
bm.y -= Tile.textSize / 2;
|
|
45
|
+
this.addChildAt(bm, at);
|
|
46
|
+
return bm;
|
|
47
|
+
}
|
|
48
|
+
get radius() { return TP.hexRad; }
|
|
49
|
+
;
|
|
50
|
+
baseShape = this.makeShape();
|
|
51
|
+
/** Default is TileShape; a HexShape with translucent disk.
|
|
52
|
+
* add more graphics with paint(colorn)
|
|
53
|
+
* also: addBitmapImage()
|
|
54
|
+
*/
|
|
55
|
+
makeShape() {
|
|
56
|
+
return new TileShape(this.radius);
|
|
57
|
+
}
|
|
58
|
+
/** paint with PlayerColor; updateCache()
|
|
59
|
+
* @param pColor the 'short' PlayerColor
|
|
60
|
+
* @param colorn the actual color (default = TP.colorScheme[pColor])
|
|
61
|
+
*/
|
|
62
|
+
paint(pColor = this.player?.color, colorn = pColor ?? C1.grey) {
|
|
63
|
+
this.baseShape.paint(colorn); // set or update baseShape.graphics
|
|
64
|
+
this.updateCache(); // push graphics to bitmapCache
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** all the [Hexagonal] game pieces that appear; can be dragged/dropped.
|
|
68
|
+
*
|
|
69
|
+
* Two subspecies: MapTile are 'stationary' on the HexMap, Meeple are 'mobile'.
|
|
70
|
+
*/
|
|
71
|
+
export class Tile extends Tile0 {
|
|
72
|
+
Aname;
|
|
73
|
+
static allTiles = [];
|
|
74
|
+
static textSize = TP.hexRad / 3;
|
|
75
|
+
// static source: any[] = [];
|
|
76
|
+
static makeSource0(unitSource,
|
|
77
|
+
// IF (per-player) static source: TileSource[] ELSE static source: TileSource
|
|
78
|
+
type, player, hex, n = 0) {
|
|
79
|
+
const source = new unitSource(type, player, hex);
|
|
80
|
+
if (player) {
|
|
81
|
+
type.source[player.index] = source;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
type.source = source;
|
|
85
|
+
}
|
|
86
|
+
// Create initial Tile/Units:
|
|
87
|
+
for (let i = 0; i < n; i++) {
|
|
88
|
+
const unit = new type(player, i + 1);
|
|
89
|
+
source.availUnit(unit);
|
|
90
|
+
}
|
|
91
|
+
source.nextUnit(); // unit.moveTo(source.hex)
|
|
92
|
+
return source;
|
|
93
|
+
}
|
|
94
|
+
source;
|
|
95
|
+
// Tile
|
|
96
|
+
constructor(
|
|
97
|
+
/** typically: className-serial; may be supplied as 'name' or undefined */
|
|
98
|
+
Aname,
|
|
99
|
+
/** the owning Player. */
|
|
100
|
+
player) {
|
|
101
|
+
super();
|
|
102
|
+
this.Aname = Aname;
|
|
103
|
+
Tile.allTiles.push(this);
|
|
104
|
+
const cName = Aname?.split('-')[0] ?? className(this); // className is subject to uglification!
|
|
105
|
+
this.name = cName; // used for saveState!
|
|
106
|
+
if (!Aname)
|
|
107
|
+
this.Aname = `${cName}-${Tile.allTiles.length}`;
|
|
108
|
+
const rad = this.radius;
|
|
109
|
+
if (TP.cacheTiles > 0)
|
|
110
|
+
this.cache(-rad, -rad, 2 * rad, 2 * rad, TP.cacheTiles);
|
|
111
|
+
this.addChild(this.baseShape);
|
|
112
|
+
this.setPlayerAndPaint(player);
|
|
113
|
+
this.nameText = this.addTextChild(rad / 2);
|
|
114
|
+
}
|
|
115
|
+
nameText;
|
|
116
|
+
setNameText(name) {
|
|
117
|
+
this.nameText.text = name.replace(/-/g, '\n');
|
|
118
|
+
const nlines = this.nameText.text.split('\n').length - 1;
|
|
119
|
+
this.nameText.y = (nlines == 0) ? 0 : -nlines * this.nameText.getMeasuredHeight() / 4;
|
|
120
|
+
this.updateCache();
|
|
121
|
+
}
|
|
122
|
+
// for BalMark:
|
|
123
|
+
// get nB() { return 0; }
|
|
124
|
+
// get nR() { return 0; }
|
|
125
|
+
// get fB() { return 0; }
|
|
126
|
+
// get fR() { return 0; }
|
|
127
|
+
/** location at start-of-game & after-Recycle; Meeple & Civic; Policy: sendHome -> sendToBag */
|
|
128
|
+
homeHex;
|
|
129
|
+
/** location at start-of-drag */
|
|
130
|
+
fromHex;
|
|
131
|
+
isDragable(ctx) { return true; }
|
|
132
|
+
_hex;
|
|
133
|
+
/** the map Hex on which this Tile sits. */
|
|
134
|
+
get hex() { return this._hex; }
|
|
135
|
+
/** only one Tile on a Hex, Tile on only one Hex */
|
|
136
|
+
set hex(hex) {
|
|
137
|
+
if (this.hex?.tile === this)
|
|
138
|
+
this.hex.tile = undefined;
|
|
139
|
+
this._hex = hex;
|
|
140
|
+
if (hex !== undefined)
|
|
141
|
+
hex.tile = this;
|
|
142
|
+
}
|
|
143
|
+
updateCache(compositeOperation) {
|
|
144
|
+
if (!this.cacheID)
|
|
145
|
+
return;
|
|
146
|
+
super.updateCache(compositeOperation);
|
|
147
|
+
}
|
|
148
|
+
setPlayerAndPaint(player) {
|
|
149
|
+
this.player = player;
|
|
150
|
+
this.paint(undefined, player?.color);
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
toString() {
|
|
154
|
+
return `${this.Aname}@${this.hex?.Aname ?? this.fromHex?.Aname ?? '?'}`;
|
|
155
|
+
}
|
|
156
|
+
/** name in set of filenames loaded in GameSetup
|
|
157
|
+
* @param at = 2; above HexShape
|
|
158
|
+
*/
|
|
159
|
+
addImageBitmap(name, at = 2) {
|
|
160
|
+
let bm = super.addImageBitmap(name, at);
|
|
161
|
+
this.updateCache();
|
|
162
|
+
return bm;
|
|
163
|
+
}
|
|
164
|
+
addTextChild(y0 = this.radius / 2, text = this.Aname?.replace(/-/g, '\n'), size = Tile.textSize, vis = false) {
|
|
165
|
+
const nameText = new CenterText(text, size);
|
|
166
|
+
nameText.y = y0; // Meeple overrides in constructor!
|
|
167
|
+
nameText.visible = vis;
|
|
168
|
+
this.addChild(nameText);
|
|
169
|
+
return nameText;
|
|
170
|
+
}
|
|
171
|
+
textVis(vis = !this.nameText.visible) {
|
|
172
|
+
this.nameText.visible = vis;
|
|
173
|
+
this.updateCache();
|
|
174
|
+
}
|
|
175
|
+
rightClickable() {
|
|
176
|
+
const ifRightClick = (evt) => {
|
|
177
|
+
const nevt = evt.nativeEvent;
|
|
178
|
+
if (nevt.button === 2) {
|
|
179
|
+
this.onRightClick(evt);
|
|
180
|
+
nevt.preventDefault(); // evt is non-cancelable, but stop the native event...
|
|
181
|
+
nevt.stopImmediatePropagation(); // TODO: prevent Dragger.clickToDrag() when button !== 0
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
this.on(S.click, ifRightClick, this, false, {}, true); // TS fails with overload
|
|
185
|
+
}
|
|
186
|
+
onRightClick(evt) {
|
|
187
|
+
console.log(stime(this, `.rightclick: ${this}`), this);
|
|
188
|
+
}
|
|
189
|
+
overSet(tile) {
|
|
190
|
+
tile.parent && console.log(stime(this, `.overSet: removeChild: ${tile}`), tile);
|
|
191
|
+
tile.parent?.removeChild(tile); // moveBonusTo/sendHome may do this.
|
|
192
|
+
}
|
|
193
|
+
// Tile
|
|
194
|
+
/** Post-condition: tile.hex == hex; low-level, physical move.
|
|
195
|
+
*
|
|
196
|
+
* calls this.source.nextUnit() if tile was dragged from this.source.
|
|
197
|
+
*/
|
|
198
|
+
moveTo(hex) {
|
|
199
|
+
const fromHex = this.fromHex;
|
|
200
|
+
this.hex = hex; // may collide with source.hex.meep, setUnit, overSet?
|
|
201
|
+
if (this.source && fromHex === this.source.hex && fromHex !== hex) {
|
|
202
|
+
this.source.nextUnit(); // shift; moveTo(source.hex); update source counter
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Tile.dropFunc() --> placeTile (to Map, reserve, ~>auction; not Recycle); semantic move/action. */
|
|
206
|
+
placeTile(toHex, payCost = false) {
|
|
207
|
+
this.gamePlay.placeEither(this, toHex, payCost);
|
|
208
|
+
}
|
|
209
|
+
resetTile() {
|
|
210
|
+
this.x = this.y = 0;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* After Capture or Recycle/Replace.
|
|
214
|
+
* Post-condition: !tile.hex.isOnMap; tile.hex = this.homeHex may be undefined [UnitSource, AuctionTile, BonusTile]
|
|
215
|
+
*/
|
|
216
|
+
sendHome() {
|
|
217
|
+
this.resetTile();
|
|
218
|
+
this.moveTo(this.homeHex); // override for AuctionTile.tileBag & UnitSource<Meeple>
|
|
219
|
+
if (!this.homeHex)
|
|
220
|
+
this.parent?.removeChild(this);
|
|
221
|
+
const source = this.source;
|
|
222
|
+
if (source) {
|
|
223
|
+
source.availUnit(this);
|
|
224
|
+
if (!source.hex.tile)
|
|
225
|
+
source.nextUnit();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
showTargetMark(hex, ctx) {
|
|
229
|
+
ctx.targetHex = hex?.isLegal ? hex : this.fromHex;
|
|
230
|
+
ctx.targetHex?.map.showMark(ctx.targetHex);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Augment Table.dragFunc0().
|
|
234
|
+
*
|
|
235
|
+
* isLegal already set;
|
|
236
|
+
* record ctx.targetHex & showMark() when Tile is over a legal targetHex.
|
|
237
|
+
*/
|
|
238
|
+
dragFunc0(hex, ctx) {
|
|
239
|
+
this.showTargetMark(hex, ctx);
|
|
240
|
+
}
|
|
241
|
+
/** entry point from Table.dropFunc; delegate to this.dropFunc() */
|
|
242
|
+
dropFunc0(hex, ctx) {
|
|
243
|
+
this.dropFunc(ctx.targetHex, ctx);
|
|
244
|
+
ctx.targetHex?.map.showMark(undefined); // if (this.fromHex === undefined)
|
|
245
|
+
}
|
|
246
|
+
cantBeMovedBy(player, ctx) {
|
|
247
|
+
return (ctx?.lastShift || this.player === undefined || this.player === player) ? undefined : "Not your Tile";
|
|
248
|
+
}
|
|
249
|
+
/** override as necessary. */
|
|
250
|
+
dragStart(ctx) { }
|
|
251
|
+
/** state of shiftKey has changed during drag */
|
|
252
|
+
dragShift(shiftKey, ctx) { }
|
|
253
|
+
markLegal(table, setLegal = (hex) => { hex.isLegal = false; }, ctx) {
|
|
254
|
+
table.newHexes.forEach(setLegal);
|
|
255
|
+
table.hexMap.forEachHex(setLegal);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Override in AuctionTile, Civic, Meeple/Leader
|
|
259
|
+
* @param toHex a potential targetHex (table.hexUnderObj(dragObj.xy))
|
|
260
|
+
*/
|
|
261
|
+
isLegalTarget(toHex, ctx) {
|
|
262
|
+
if (!toHex)
|
|
263
|
+
return false;
|
|
264
|
+
if (!!toHex.tile)
|
|
265
|
+
return false; // note: from AuctionHexes to Reserve overrides this.
|
|
266
|
+
if (toHex.meep && !(toHex.meep.player === this.gamePlay.curPlayer))
|
|
267
|
+
return false; // QQQ: can place on non-player meep?
|
|
268
|
+
if (this.hex?.isOnMap && !ctx?.lastShift)
|
|
269
|
+
return false;
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
isLegalRecycle(ctx) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Tile.dropFunc; Override in AuctionTile, Civic, Meeple/Leader.
|
|
277
|
+
* @param targetHex Hex2 this Tile is over when dropped (may be undefined; see also: ctx.targetHex)
|
|
278
|
+
* @param ctx DragContext
|
|
279
|
+
*/
|
|
280
|
+
dropFunc(targetHex, ctx) {
|
|
281
|
+
this.placeTile(targetHex);
|
|
282
|
+
}
|
|
283
|
+
noLegal() {
|
|
284
|
+
// const cause = this.gamePlay.failToBalance(this) ?? '';
|
|
285
|
+
// const [infR, coinR] = this.gamePlay.getInfR(this);
|
|
286
|
+
// this.gamePlay.logText(`No placement for ${this.andInfStr} ${cause} infR=${infR} coinR=${coinR}`, 'Tile.noLegal')
|
|
287
|
+
}
|
|
288
|
+
logRecycle(verb) {
|
|
289
|
+
const cp = this.gamePlay.curPlayer;
|
|
290
|
+
const loc = this.hex?.isOnMap ? 'onMap' : 'offMap';
|
|
291
|
+
const info = { Aname: this.Aname, fromHex: this.fromHex?.Aname, cp: cp.colorn, tile: { ...this } };
|
|
292
|
+
console.log(stime(this, `.recycleTile[${loc}]: ${verb}`), info);
|
|
293
|
+
this.gamePlay.logText(`${cp.Aname} ${verb} ${this}`, `GamePlay.recycle`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** A plain WHITE tile; for Debt */
|
|
297
|
+
export class WhiteTile extends Tile {
|
|
298
|
+
// TileShape does not work here:
|
|
299
|
+
makeShape() { return new HexShape(this.radius); }
|
|
300
|
+
paint(pColor, colorn) {
|
|
301
|
+
super.paint(pColor, C.WHITE); // TODO: using cgf
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** a half-sized Tile. */
|
|
305
|
+
export class Token extends Tile {
|
|
306
|
+
makeShape() {
|
|
307
|
+
return new HexShape(this.radius * .5);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/** Tiles that can be played to the Map: AuctionTile, Civic, Monument, BonusTile */
|
|
311
|
+
export class MapTile extends Tile {
|
|
312
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** drill down through value of inner fields. */
|
|
2
|
+
export function findFieldValue(obj, ...names) {
|
|
3
|
+
let next;
|
|
4
|
+
const n = names.shift();
|
|
5
|
+
if (!n)
|
|
6
|
+
return obj; // return obj when no more field accessors
|
|
7
|
+
if (typeof (n) == 'string') {
|
|
8
|
+
next = obj[n];
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
const nn = n.find(n => !!obj[n]);
|
|
12
|
+
next = !!nn ? obj[nn] : !!n[0] ? undefined : obj; // [null, foo, bar] -> next = obj
|
|
13
|
+
}
|
|
14
|
+
return !!next ? findFieldValue(next, ...names) : undefined;
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thegraid/hexlib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "library for hex-based games",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"newlib": "npm i ../../ts/createjs-lib/thegraid-easeljs-lib-*.tgz ; rm -rf .angular/cache",
|
|
12
|
+
"note:": "remember to rm -rf .angular/cache when [re-]installing a tgz",
|
|
13
|
+
"tgz": "rm -rf *.tgz; npm pack",
|
|
14
|
+
"pub": "npm run build; npm version ${1:-patch}; npm publish --access public",
|
|
15
|
+
"testEdit": "test/testEditBox.sh",
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
17
|
+
"clean": "rm -f dist/*",
|
|
18
|
+
"build": "npm run clean; npm run tsc; date",
|
|
19
|
+
"tsc": "tsc"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/jackpunt/hexlib.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"hexagon",
|
|
27
|
+
"game"
|
|
28
|
+
],
|
|
29
|
+
"author": "jack punt",
|
|
30
|
+
"license": "ISC",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/jackpunt/hexlib/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/jackpunt/hexlib#readme",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@thegraid/common-lib": "^1.3.40",
|
|
37
|
+
"@thegraid/easeljs-lib": "^1.3.8",
|
|
38
|
+
"json5": "^2.2.3"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^18.15.11",
|
|
42
|
+
"@types/wicg-file-system-access": "^2020.9.8",
|
|
43
|
+
"typescript": "~4.9.4"
|
|
44
|
+
}
|
|
45
|
+
}
|