powergrid-viewer 1.11.5 → 1.11.7
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powergrid-viewer",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.7",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/boardgamers/powergrid.git",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"vue": "^2.6.11",
|
|
19
19
|
"vue-class-component": "^7.2.3",
|
|
20
20
|
"vue-property-decorator": "^8.4.2",
|
|
21
|
-
"powergrid-engine": "1.15.
|
|
21
|
+
"powergrid-engine": "1.15.7"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/assert": "^1.4.7",
|
package/src/components/Game.vue
CHANGED
|
@@ -55,9 +55,12 @@
|
|
|
55
55
|
:connections="G.map.connections"
|
|
56
56
|
:polygons="G.map.polygons"
|
|
57
57
|
:buildableCities="getBuildableCities()"
|
|
58
|
+
:blockedCities="G.blockedCities"
|
|
59
|
+
:pickableRegions="getPickableRegions()"
|
|
58
60
|
:devBackdrop="G.map.devBackdrop"
|
|
59
61
|
:mapRotation="G.map.mapRotation || 0"
|
|
60
62
|
@build="build($event)"
|
|
63
|
+
@pickRegion="pickRegion($event)"
|
|
61
64
|
/>
|
|
62
65
|
|
|
63
66
|
<!-- Japan: Free Jump indicator -->
|
|
@@ -1114,6 +1117,19 @@ export default class Game extends Vue {
|
|
|
1114
1117
|
return availableMoves[MoveName.Build] && availableMoves[MoveName.Build]!.map((c) => c.name) || [];
|
|
1115
1118
|
}
|
|
1116
1119
|
|
|
1120
|
+
getPickableRegions(): string[] {
|
|
1121
|
+
if (!this.canMove()) return [];
|
|
1122
|
+
|
|
1123
|
+
const currentPlayer = this.G!.players[this.player!];
|
|
1124
|
+
const availableMoves = currentPlayer.availableMoves!;
|
|
1125
|
+
|
|
1126
|
+
return availableMoves[MoveName.ChooseRegion] || [];
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
pickRegion(region: string) {
|
|
1130
|
+
this.sendMove({ name: MoveName.ChooseRegion, data: region });
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1117
1133
|
playerHasUsedFreeJump(playerIndex: number): boolean {
|
|
1118
1134
|
const player = this.G?.players[playerIndex];
|
|
1119
1135
|
if (!player) return false;
|
|
@@ -1222,6 +1238,20 @@ export default class Game extends Vue {
|
|
|
1222
1238
|
}
|
|
1223
1239
|
|
|
1224
1240
|
getStatusMessage() {
|
|
1241
|
+
// Region draft (chooseRegions): show the pick prompt to the current picker
|
|
1242
|
+
// even on the very first turn, before any moves are in the log.
|
|
1243
|
+
if (
|
|
1244
|
+
this.G &&
|
|
1245
|
+
this.G.phase == Phase.RegionSelection &&
|
|
1246
|
+
this.player !== undefined &&
|
|
1247
|
+
this.G.currentPlayers.includes(this.player)
|
|
1248
|
+
) {
|
|
1249
|
+
const draft = this.G.regionDraft;
|
|
1250
|
+
return draft
|
|
1251
|
+
? `Choose a region to play in (${draft.picked.length + 1} of ${draft.regionsNeeded}).`
|
|
1252
|
+
: 'Choose a region to play in.';
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1225
1255
|
if (!this.G || this.G.log.length == 1) {
|
|
1226
1256
|
return 'Game Start!';
|
|
1227
1257
|
} else if (this.G.currentPlayers == []) {
|
|
@@ -92,9 +92,12 @@
|
|
|
92
92
|
number + 8/14/20 house slots), drawn AFTER the links so the gray lines
|
|
93
93
|
sit behind. Gated on connectionCost; other maps keep circular nodes. -->
|
|
94
94
|
<template v-for="city in cities">
|
|
95
|
-
<g
|
|
95
|
+
<g
|
|
96
|
+
v-if="city.connectionCost != null && city.slotCosts && city.slotCosts.length >= 2"
|
|
97
|
+
:key="city.name + '_tile'"
|
|
98
|
+
>
|
|
96
99
|
<rect
|
|
97
|
-
:class="[{ canClick: canBuild(city) }]"
|
|
100
|
+
:class="[{ canClick: canBuild(city) || canPickRegion(city) }]"
|
|
98
101
|
:x="city.x - 23"
|
|
99
102
|
:y="city.y - 23"
|
|
100
103
|
width="46"
|
|
@@ -104,7 +107,7 @@
|
|
|
104
107
|
:stroke="city.region"
|
|
105
108
|
stroke-width="4"
|
|
106
109
|
:transform="`rotate(45, ${city.x}, ${city.y})`"
|
|
107
|
-
@click="
|
|
110
|
+
@click="onCityClick(city)"
|
|
108
111
|
>
|
|
109
112
|
<title>{{ city.name }} — connect for {{ city.connectionCost }} (entry) + house</title>
|
|
110
113
|
</rect>
|
|
@@ -144,16 +147,58 @@
|
|
|
144
147
|
</g>
|
|
145
148
|
</template>
|
|
146
149
|
|
|
150
|
+
<!-- Manhattan single-house spaces: one house per space at its printed price.
|
|
151
|
+
Small, semi-transparent tile (no entry-cost circle — connection is a flat
|
|
152
|
+
5 everywhere, stated in the rules) so the price reads cleanly and tiles
|
|
153
|
+
never hide the board beneath when they overlap. -->
|
|
154
|
+
<template v-for="city in cities">
|
|
155
|
+
<g
|
|
156
|
+
v-if="city.connectionCost != null && city.slotCosts && city.slotCosts.length === 1"
|
|
157
|
+
:key="city.name + '_mtile'"
|
|
158
|
+
>
|
|
159
|
+
<rect
|
|
160
|
+
:class="[{ canClick: canBuild(city) }]"
|
|
161
|
+
:x="city.x - 13"
|
|
162
|
+
:y="city.y - 13"
|
|
163
|
+
width="26"
|
|
164
|
+
height="26"
|
|
165
|
+
rx="5"
|
|
166
|
+
:fill="isBlocked(city) ? '#555555' : 'white'"
|
|
167
|
+
:fill-opacity="isBlocked(city) ? 0.88 : 0.82"
|
|
168
|
+
stroke="black"
|
|
169
|
+
stroke-width="1.5"
|
|
170
|
+
@click="canBuild(city) && build(city)"
|
|
171
|
+
>
|
|
172
|
+
<title>
|
|
173
|
+
{{ city.name }}<template v-if="isBlocked(city)"> — blocked for this player count (transit
|
|
174
|
+
only)</template ><template v-else> — build for {{ city.slotCosts[0] }} (+ flat 5 per transited
|
|
175
|
+
space)</template>
|
|
176
|
+
</title>
|
|
177
|
+
</rect>
|
|
178
|
+
<text
|
|
179
|
+
:x="city.x"
|
|
180
|
+
:y="city.y"
|
|
181
|
+
font-size="17"
|
|
182
|
+
font-weight="bold"
|
|
183
|
+
text-anchor="middle"
|
|
184
|
+
dominant-baseline="central"
|
|
185
|
+
:fill="isBlocked(city) ? '#dddddd' : 'black'"
|
|
186
|
+
>
|
|
187
|
+
{{ city.slotCosts[0] }}
|
|
188
|
+
</text>
|
|
189
|
+
</g>
|
|
190
|
+
</template>
|
|
191
|
+
|
|
147
192
|
<template v-for="city in cities">
|
|
148
193
|
<circle
|
|
149
194
|
v-if="city.connectionCost == null"
|
|
150
195
|
:key="city.name + '_circle2'"
|
|
151
|
-
:class="[{ canClick: canBuild(city) }]"
|
|
196
|
+
:class="[{ canClick: canBuild(city) || canPickRegion(city) }]"
|
|
152
197
|
r="20"
|
|
153
198
|
:cx="city.x"
|
|
154
199
|
:cy="city.y"
|
|
155
200
|
fill="gray"
|
|
156
|
-
@click="
|
|
201
|
+
@click="onCityClick(city)"
|
|
157
202
|
>
|
|
158
203
|
<title>{{ city.name }}</title>
|
|
159
204
|
</circle>
|
|
@@ -205,12 +250,16 @@
|
|
|
205
250
|
:owner="house.owner"
|
|
206
251
|
:ownerName="house.ownerName"
|
|
207
252
|
:color="house.color"
|
|
253
|
+
:scale="house.scale"
|
|
208
254
|
/>
|
|
209
255
|
</template>
|
|
210
256
|
|
|
211
257
|
<!-- Bremen entry-cost number, on top so it stays readable over houses -->
|
|
212
258
|
<template v-for="city in cities">
|
|
213
|
-
<g
|
|
259
|
+
<g
|
|
260
|
+
v-if="city.connectionCost != null && city.slotCosts && city.slotCosts.length >= 2"
|
|
261
|
+
:key="city.name + '_cc'"
|
|
262
|
+
>
|
|
214
263
|
<circle
|
|
215
264
|
:cx="city.x"
|
|
216
265
|
:cy="city.y + 15"
|
|
@@ -317,6 +366,25 @@
|
|
|
317
366
|
/>
|
|
318
367
|
</template>
|
|
319
368
|
</template>
|
|
369
|
+
|
|
370
|
+
<!-- chooseRegions draft: outline every city in a pickable region so the
|
|
371
|
+
current player can see and click the regions they may draft. Shown
|
|
372
|
+
regardless of the help preference since the draft can't proceed
|
|
373
|
+
without it. -->
|
|
374
|
+
<template v-for="city in cities">
|
|
375
|
+
<circle
|
|
376
|
+
v-if="canPickRegion(city)"
|
|
377
|
+
:key="city.name + '_pickRegion'"
|
|
378
|
+
class="canClick"
|
|
379
|
+
r="18"
|
|
380
|
+
:cx="city.x"
|
|
381
|
+
:cy="city.y"
|
|
382
|
+
fill="none"
|
|
383
|
+
stroke="gold"
|
|
384
|
+
stroke-width="5px"
|
|
385
|
+
@click="onCityClick(city)"
|
|
386
|
+
/>
|
|
387
|
+
</template>
|
|
320
388
|
</g>
|
|
321
389
|
</template>
|
|
322
390
|
|
|
@@ -329,7 +397,7 @@ import { City, Connection, Polygon } from 'powergrid-engine/src/maps';
|
|
|
329
397
|
|
|
330
398
|
@Component({
|
|
331
399
|
components: {
|
|
332
|
-
House
|
|
400
|
+
House,
|
|
333
401
|
},
|
|
334
402
|
})
|
|
335
403
|
export default class Map extends Vue {
|
|
@@ -338,6 +406,10 @@ export default class Map extends Vue {
|
|
|
338
406
|
@Prop() connections?: Connection[];
|
|
339
407
|
@Prop() playerColors?: string[];
|
|
340
408
|
@Prop() buildableCities?: string[];
|
|
409
|
+
// Manhattan: spaces blocked for this player count — transitable but never buildable.
|
|
410
|
+
@Prop() blockedCities?: string[];
|
|
411
|
+
// chooseRegions draft: region names the current player may pick this turn.
|
|
412
|
+
@Prop() pickableRegions?: string[];
|
|
341
413
|
@Prop() devBackdrop?: { src: string; width: number; height: number; opacity?: number };
|
|
342
414
|
@Prop({ default: 0 }) mapRotation!: number;
|
|
343
415
|
|
|
@@ -351,9 +423,20 @@ export default class Map extends Vue {
|
|
|
351
423
|
player.cities.forEach((cityPiece) => {
|
|
352
424
|
const city = gameState.map.cities.find((city) => city.name == cityPiece.name)!;
|
|
353
425
|
let offsetX, offsetY;
|
|
426
|
+
let pieceScale: number | undefined;
|
|
354
427
|
const isNodeWeighted = city.connectionCost != null;
|
|
428
|
+
// Manhattan = a node-weighted map with exactly one house per space.
|
|
429
|
+
const isManhattan = isNodeWeighted && city.slotCosts?.length === 1;
|
|
355
430
|
const is1520 = city.slotCosts?.length === 2 && city.slotCosts[0] === 15;
|
|
356
|
-
if (
|
|
431
|
+
if (isManhattan) {
|
|
432
|
+
// One house per space, enlarged and centered so the house silhouette
|
|
433
|
+
// slightly overflows the (smaller) price tile and covers it — no white
|
|
434
|
+
// corners showing. The House shape's bbox center is ~(199, 230) in its
|
|
435
|
+
// own units, so at scale S it centers on the space when offset -199S/-230S.
|
|
436
|
+
pieceScale = 0.085;
|
|
437
|
+
offsetX = -199 * pieceScale;
|
|
438
|
+
offsetY = -230 * pieceScale;
|
|
439
|
+
} else if (isNodeWeighted) {
|
|
357
440
|
// Bremen tiles: 8 top, 14 left, 20 right — matches the board layout.
|
|
358
441
|
// The House piece is anchored top-left (~14×16 after its scale), so
|
|
359
442
|
// offset by half its size to center it on the slot.
|
|
@@ -386,6 +469,7 @@ export default class Map extends Vue {
|
|
|
386
469
|
y: city.y + offsetY,
|
|
387
470
|
color: this.playerColors![pi],
|
|
388
471
|
owner: pi,
|
|
472
|
+
scale: pieceScale,
|
|
389
473
|
});
|
|
390
474
|
});
|
|
391
475
|
});
|
|
@@ -422,10 +506,31 @@ export default class Map extends Vue {
|
|
|
422
506
|
return !!this.buildableCities!.find((cityName) => cityName == city.name);
|
|
423
507
|
}
|
|
424
508
|
|
|
509
|
+
isBlocked(city: City) {
|
|
510
|
+
return !!this.blockedCities && this.blockedCities.includes(city.name);
|
|
511
|
+
}
|
|
512
|
+
|
|
425
513
|
build(city: City) {
|
|
426
514
|
this.$emit('build', city);
|
|
427
515
|
}
|
|
428
516
|
|
|
517
|
+
canPickRegion(city: City) {
|
|
518
|
+
return !!this.pickableRegions && this.pickableRegions.includes(city.region);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
pickRegion(city: City) {
|
|
522
|
+
this.$emit('pickRegion', city.region);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// During the region draft a city click picks its region; otherwise it builds.
|
|
526
|
+
onCityClick(city: City) {
|
|
527
|
+
if (this.canBuild(city)) {
|
|
528
|
+
this.build(city);
|
|
529
|
+
} else if (this.canPickRegion(city)) {
|
|
530
|
+
this.pickRegion(city);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
429
534
|
onPickCoord(e: MouseEvent) {
|
|
430
535
|
if (!this.devBackdrop) return;
|
|
431
536
|
const g = e.currentTarget as SVGGElement;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<g :id="elId" :class="['piece']" :transform="`translate(${currentX}, ${currentY}) scale(
|
|
2
|
+
<g :id="elId" :class="['piece']" :transform="`translate(${currentX}, ${currentY}) scale(${scale})`">
|
|
3
3
|
<!-- <path d="M15 0 L30 10 L30 30 L0 30 L0 10Z" :fill="color" stroke="black" /> -->
|
|
4
4
|
<path
|
|
5
5
|
d="M187.698 263.636V456.017L3 341.204V169.522L80.8579 108.141L187.698 263.636Z"
|
|
@@ -42,6 +42,9 @@ export default class House extends Mixins(Piece) {
|
|
|
42
42
|
@Prop()
|
|
43
43
|
color?: string;
|
|
44
44
|
|
|
45
|
+
@Prop({ default: 0.035 })
|
|
46
|
+
scale!: number;
|
|
47
|
+
|
|
45
48
|
@Prop()
|
|
46
49
|
owner?: number;
|
|
47
50
|
|
package/src/types/ui-data.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface Piece {
|
|
|
21
21
|
color?: string;
|
|
22
22
|
powerPlant?: PowerPlant;
|
|
23
23
|
transparent?: boolean;
|
|
24
|
+
// Per-piece render scale override (House defaults to 0.035; Manhattan enlarges).
|
|
25
|
+
scale?: number;
|
|
24
26
|
side?: 'north' | 'south';
|
|
25
27
|
// South Africa: this coal cube sits in the storage pool below the market.
|
|
26
28
|
// Clicking emits buyResource with fromStorage:true (flat $8 buy).
|