powergrid-viewer 1.5.4 → 1.7.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/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powergrid-viewer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/boardgamers/powergrid.git",
|
|
7
|
+
"directory": "viewer"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/boardgamers/powergrid#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/boardgamers/powergrid/issues"
|
|
12
|
+
},
|
|
4
13
|
"dependencies": {
|
|
5
14
|
"core-js": "^3.6.5",
|
|
6
15
|
"lodash": "^4.17.19",
|
|
@@ -9,7 +18,7 @@
|
|
|
9
18
|
"vue": "^2.6.11",
|
|
10
19
|
"vue-class-component": "^7.2.3",
|
|
11
20
|
"vue-property-decorator": "^8.4.2",
|
|
12
|
-
"powergrid-engine": "1.
|
|
21
|
+
"powergrid-engine": "1.11.0"
|
|
13
22
|
},
|
|
14
23
|
"devDependencies": {
|
|
15
24
|
"@types/assert": "^1.4.7",
|
|
@@ -41,8 +50,8 @@
|
|
|
41
50
|
"public/audio"
|
|
42
51
|
],
|
|
43
52
|
"scripts": {
|
|
44
|
-
"serve": "vue-cli-service serve",
|
|
45
|
-
"build": "vue-cli-service build",
|
|
53
|
+
"serve": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
|
|
54
|
+
"build": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
|
|
46
55
|
"test:unit": "vue-cli-service test:unit",
|
|
47
56
|
"package": "vue-cli-service build --target lib --name powergrid-viewer src/wrapper.ts"
|
|
48
57
|
}
|
package/src/components/Game.vue
CHANGED
|
@@ -287,6 +287,9 @@
|
|
|
287
287
|
<li><strong>One</strong> player per city</li>
|
|
288
288
|
<li>
|
|
289
289
|
Resource Resupply: <strong>{{ G.resourceResupply[0] }}</strong>
|
|
290
|
+
<template v-if="G.resourceResupplyNorth">
|
|
291
|
+
(S), <strong>{{ G.resourceResupplyNorth[0] }}</strong> (N)
|
|
292
|
+
</template>
|
|
290
293
|
</li>
|
|
291
294
|
<li>Bureaucracy: remove <strong>highest</strong> power plant from market</li>
|
|
292
295
|
</ul>
|
|
@@ -301,6 +304,9 @@
|
|
|
301
304
|
<li><strong>Two</strong> players per city</li>
|
|
302
305
|
<li>
|
|
303
306
|
Resource Resupply: <strong>{{ G.resourceResupply[1] }}</strong>
|
|
307
|
+
<template v-if="G.resourceResupplyNorth">
|
|
308
|
+
(S), <strong>{{ G.resourceResupplyNorth[1] }}</strong> (N)
|
|
309
|
+
</template>
|
|
304
310
|
</li>
|
|
305
311
|
<li>Bureaucracy: remove <strong>highest</strong> power plant from market</li>
|
|
306
312
|
</ul>
|
|
@@ -312,6 +318,9 @@
|
|
|
312
318
|
<li><strong>Three</strong> players per city</li>
|
|
313
319
|
<li>
|
|
314
320
|
Resource Resupply: <strong>{{ G.resourceResupply[2] }}</strong>
|
|
321
|
+
<template v-if="G.resourceResupplyNorth">
|
|
322
|
+
(S), <strong>{{ G.resourceResupplyNorth[2] }}</strong> (N)
|
|
323
|
+
</template>
|
|
315
324
|
</li>
|
|
316
325
|
<li>Bureaucracy: remove <strong>lowest</strong> power plant from market</li>
|
|
317
326
|
<li>All power plants available for auction</li>
|
|
@@ -329,7 +338,7 @@
|
|
|
329
338
|
<br />
|
|
330
339
|
<div>
|
|
331
340
|
<strong>Map Specific Rules:</strong><br />
|
|
332
|
-
<span style="white-space: pre">{{ G.map.mapSpecificRules }}</span>
|
|
341
|
+
<span style="white-space: pre-wrap">{{ G.map.mapSpecificRules }}</span>
|
|
333
342
|
</div>
|
|
334
343
|
</template>
|
|
335
344
|
<br />
|
|
@@ -621,8 +630,12 @@ export default class Game extends Vue {
|
|
|
621
630
|
}
|
|
622
631
|
}
|
|
623
632
|
|
|
624
|
-
buyResource(resource: ResourceType) {
|
|
625
|
-
|
|
633
|
+
buyResource(payload: { resource: ResourceType, side?: 'north' | 'south' }) {
|
|
634
|
+
const data: { resource: ResourceType, side?: 'north' | 'south' } = { resource: payload.resource };
|
|
635
|
+
if (payload.side) {
|
|
636
|
+
data.side = payload.side;
|
|
637
|
+
}
|
|
638
|
+
this.sendMove({ name: MoveName.BuyResource, data });
|
|
626
639
|
}
|
|
627
640
|
|
|
628
641
|
bid(bid: number) {
|
|
@@ -862,13 +875,13 @@ export default class Game extends Vue {
|
|
|
862
875
|
return !!availableMoves[MoveName.ChoosePowerPlant];
|
|
863
876
|
}
|
|
864
877
|
|
|
865
|
-
buyableResources() {
|
|
878
|
+
buyableResources(): { resource: ResourceType, side?: 'north' | 'south' }[] {
|
|
866
879
|
if (!this.canMove()) return [];
|
|
867
880
|
|
|
868
881
|
const currentPlayer = this.G!.players[this.player!];
|
|
869
882
|
const availableMoves = currentPlayer.availableMoves!;
|
|
870
883
|
|
|
871
|
-
return
|
|
884
|
+
return availableMoves[MoveName.BuyResource] || [];
|
|
872
885
|
}
|
|
873
886
|
|
|
874
887
|
canBuyResource(resource?: ResourceType) {
|
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<g>
|
|
3
|
+
<!-- Korea: North resource market (above the standard market block).
|
|
4
|
+
The South market re-uses the existing rendering below; Korea-specific
|
|
5
|
+
cube positions are populated in createPieces. -->
|
|
6
|
+
<template v-if="isKorea">
|
|
7
|
+
<text x="20" y="-110" font-weight="700" fill="black" style="font-size: 22px">North Market</text>
|
|
8
|
+
<rect width="760" height="80" x="20" y="-100" rx="3" fill="#c89c3a" />
|
|
9
|
+
<template v-for="index in 8">
|
|
10
|
+
<rect
|
|
11
|
+
:key="'north_resources' + index"
|
|
12
|
+
width="70"
|
|
13
|
+
height="70"
|
|
14
|
+
:x="25 + 85 * (index - 1)"
|
|
15
|
+
y="-95"
|
|
16
|
+
rx="2"
|
|
17
|
+
fill="darkgoldenrod"
|
|
18
|
+
/>
|
|
19
|
+
<circle
|
|
20
|
+
:key="'north_resourcesCircle' + index"
|
|
21
|
+
r="10"
|
|
22
|
+
:cx="92 + 85 * (index - 1)"
|
|
23
|
+
cy="-92"
|
|
24
|
+
fill="yellow"
|
|
25
|
+
/>
|
|
26
|
+
<text
|
|
27
|
+
:key="'north_resourcesText' + index"
|
|
28
|
+
text-anchor="middle"
|
|
29
|
+
style="font-size: 16px; font-family: monospace"
|
|
30
|
+
:x="92 + 85 * (index - 1)"
|
|
31
|
+
y="-92"
|
|
32
|
+
fill="darkgoldenrod"
|
|
33
|
+
>
|
|
34
|
+
{{ index }}
|
|
35
|
+
</text>
|
|
36
|
+
</template>
|
|
37
|
+
<template v-for="coal in coalsNorth">
|
|
38
|
+
<Coal
|
|
39
|
+
:key="coal.id"
|
|
40
|
+
:pieceId="coal.id"
|
|
41
|
+
:targetState="{ x: coal.x, y: coal.y }"
|
|
42
|
+
:canClick="!coal.transparent && canBuyResource('coal', coal.side)"
|
|
43
|
+
:transparent="coal.transparent"
|
|
44
|
+
:scale="0.08"
|
|
45
|
+
@click="buyResource('coal', coal.side)"
|
|
46
|
+
/>
|
|
47
|
+
</template>
|
|
48
|
+
<template v-for="oil in oilsNorth">
|
|
49
|
+
<Oil
|
|
50
|
+
:key="oil.id"
|
|
51
|
+
:pieceId="oil.id"
|
|
52
|
+
:targetState="{ x: oil.x, y: oil.y }"
|
|
53
|
+
:canClick="!oil.transparent && canBuyResource('oil', oil.side)"
|
|
54
|
+
:transparent="oil.transparent"
|
|
55
|
+
@click="buyResource('oil', oil.side)"
|
|
56
|
+
/>
|
|
57
|
+
</template>
|
|
58
|
+
<template v-for="garbage in garbagesNorth">
|
|
59
|
+
<Garbage
|
|
60
|
+
:key="garbage.id"
|
|
61
|
+
:pieceId="garbage.id"
|
|
62
|
+
:targetState="{ x: garbage.x, y: garbage.y }"
|
|
63
|
+
:canClick="!garbage.transparent && canBuyResource('garbage', garbage.side)"
|
|
64
|
+
:transparent="garbage.transparent"
|
|
65
|
+
@click="buyResource('garbage', garbage.side)"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
68
|
+
<text x="20" y="35" font-weight="700" fill="black" style="font-size: 22px">South Market</text>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
3
71
|
<text v-if="resourceResupply[0] < 10" x="30" y="20" font-weight="600" fill="black" style="font-size: 24px"
|
|
4
72
|
>Resource Resupply:</text
|
|
5
73
|
>
|
|
@@ -164,10 +232,10 @@
|
|
|
164
232
|
:key="coal.id"
|
|
165
233
|
:pieceId="coal.id"
|
|
166
234
|
:targetState="{ x: coal.x, y: coal.y }"
|
|
167
|
-
:canClick="!coal.transparent && canBuyResource('coal')"
|
|
235
|
+
:canClick="!coal.transparent && canBuyResource('coal', coal.side)"
|
|
168
236
|
:transparent="coal.transparent"
|
|
169
237
|
:scale="isIndiaResourceMarket ? 0.06 : 0.08"
|
|
170
|
-
@click="buyResource('coal')"
|
|
238
|
+
@click="buyResource('coal', coal.side)"
|
|
171
239
|
/>
|
|
172
240
|
</template>
|
|
173
241
|
|
|
@@ -176,9 +244,9 @@
|
|
|
176
244
|
:key="oil.id"
|
|
177
245
|
:pieceId="oil.id"
|
|
178
246
|
:targetState="{ x: oil.x, y: oil.y }"
|
|
179
|
-
:canClick="!oil.transparent && canBuyResource('oil')"
|
|
247
|
+
:canClick="!oil.transparent && canBuyResource('oil', oil.side)"
|
|
180
248
|
:transparent="oil.transparent"
|
|
181
|
-
@click="buyResource('oil')"
|
|
249
|
+
@click="buyResource('oil', oil.side)"
|
|
182
250
|
/>
|
|
183
251
|
</template>
|
|
184
252
|
|
|
@@ -187,10 +255,10 @@
|
|
|
187
255
|
:key="garbage.id"
|
|
188
256
|
:pieceId="garbage.id"
|
|
189
257
|
:targetState="{ x: garbage.x, y: garbage.y }"
|
|
190
|
-
:canClick="!garbage.transparent && canBuyResource('garbage')"
|
|
258
|
+
:canClick="!garbage.transparent && canBuyResource('garbage', garbage.side)"
|
|
191
259
|
:transparent="garbage.transparent"
|
|
192
260
|
:scale="isIndiaResourceMarket ? 0.8 : 1"
|
|
193
|
-
@click="buyResource('garbage')"
|
|
261
|
+
@click="buyResource('garbage', garbage.side)"
|
|
194
262
|
/>
|
|
195
263
|
</template>
|
|
196
264
|
|
|
@@ -199,9 +267,9 @@
|
|
|
199
267
|
:key="uranium.id"
|
|
200
268
|
:pieceId="uranium.id"
|
|
201
269
|
:targetState="{ x: uranium.x, y: uranium.y }"
|
|
202
|
-
:canClick="!uranium.transparent && canBuyResource('uranium')"
|
|
270
|
+
:canClick="!uranium.transparent && canBuyResource('uranium', uranium.side)"
|
|
203
271
|
:transparent="uranium.transparent"
|
|
204
|
-
@click="buyResource('uranium')"
|
|
272
|
+
@click="buyResource('uranium', uranium.side)"
|
|
205
273
|
/>
|
|
206
274
|
</template>
|
|
207
275
|
|
|
@@ -222,7 +290,29 @@
|
|
|
222
290
|
|
|
223
291
|
<template v-if="!preferences.disableHelp">
|
|
224
292
|
<rect
|
|
225
|
-
v-if="buyableResources.length > 0"
|
|
293
|
+
v-if="!isKorea && buyableResources.length > 0"
|
|
294
|
+
x="15"
|
|
295
|
+
y="35"
|
|
296
|
+
width="770"
|
|
297
|
+
height="90"
|
|
298
|
+
rx="2"
|
|
299
|
+
fill="none"
|
|
300
|
+
stroke="blue"
|
|
301
|
+
stroke-width="2px"
|
|
302
|
+
/>
|
|
303
|
+
<rect
|
|
304
|
+
v-if="isKorea && hasBuyableNorth"
|
|
305
|
+
x="15"
|
|
306
|
+
y="-105"
|
|
307
|
+
width="770"
|
|
308
|
+
height="90"
|
|
309
|
+
rx="2"
|
|
310
|
+
fill="none"
|
|
311
|
+
stroke="blue"
|
|
312
|
+
stroke-width="2px"
|
|
313
|
+
/>
|
|
314
|
+
<rect
|
|
315
|
+
v-if="isKorea && hasBuyableSouth"
|
|
226
316
|
x="15"
|
|
227
317
|
y="35"
|
|
228
318
|
width="770"
|
|
@@ -254,7 +344,7 @@ export default class Resources extends Vue {
|
|
|
254
344
|
@Prop() isMiddleEast?: boolean;
|
|
255
345
|
@Prop() isIndiaResourceMarket?: boolean;
|
|
256
346
|
@Prop() availableSurplusOil?: number;
|
|
257
|
-
@Prop() buyableResources?: string[];
|
|
347
|
+
@Prop() buyableResources?: { resource: string, side?: 'north' | 'south' }[];
|
|
258
348
|
|
|
259
349
|
@Inject() preferences!: Preferences;
|
|
260
350
|
|
|
@@ -263,8 +353,118 @@ export default class Resources extends Vue {
|
|
|
263
353
|
garbages: Piece[] = [];
|
|
264
354
|
uraniums: Piece[] = [];
|
|
265
355
|
|
|
356
|
+
isKorea: boolean = false;
|
|
357
|
+
coalsNorth: Piece[] = [];
|
|
358
|
+
oilsNorth: Piece[] = [];
|
|
359
|
+
garbagesNorth: Piece[] = [];
|
|
360
|
+
|
|
361
|
+
// Korea: lay out cubes within 8 price spaces ($1..$8) using the prices array
|
|
362
|
+
// to determine slot count per space. Cubes are indexed cheap→expensive in the
|
|
363
|
+
// prices array, so the cheap end empties as the market depletes.
|
|
364
|
+
// Skips entries with price > 8 (uranium corner $10/$12/$14/$16) — caller renders
|
|
365
|
+
// those separately.
|
|
366
|
+
private buildKoreaMainRowPieces(
|
|
367
|
+
prices: number[],
|
|
368
|
+
market: number,
|
|
369
|
+
idPrefix: string,
|
|
370
|
+
side: 'north' | 'south',
|
|
371
|
+
y: number,
|
|
372
|
+
): Piece[] {
|
|
373
|
+
const groups: { price: number; slots: number; firstIdx: number }[] = [];
|
|
374
|
+
prices.forEach((p, idx) => {
|
|
375
|
+
if (p > 8) return;
|
|
376
|
+
const last = groups[groups.length - 1];
|
|
377
|
+
if (last && last.price === p) last.slots++;
|
|
378
|
+
else groups.push({ price: p, slots: 1, firstIdx: idx });
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const pieces: Piece[] = [];
|
|
382
|
+
// Width within a price space across which cubes are distributed. Bigger
|
|
383
|
+
// value → cubes within the same price space appear farther apart.
|
|
384
|
+
const cubeAreaW = 64;
|
|
385
|
+
groups.forEach((g) => {
|
|
386
|
+
const psCenter = 60 + 85 * (g.price - 1);
|
|
387
|
+
for (let s = 0; s < g.slots; s++) {
|
|
388
|
+
const i = g.firstIdx + s;
|
|
389
|
+
const x = g.slots === 1
|
|
390
|
+
? psCenter
|
|
391
|
+
: psCenter - cubeAreaW / 2 + (cubeAreaW * (s + 0.5)) / g.slots;
|
|
392
|
+
pieces.push({
|
|
393
|
+
id: `${idPrefix}_${i}`,
|
|
394
|
+
x,
|
|
395
|
+
y,
|
|
396
|
+
transparent: i < (prices.length - market),
|
|
397
|
+
side,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return pieces;
|
|
402
|
+
}
|
|
403
|
+
|
|
266
404
|
createPieces(gameState: GameState) {
|
|
267
|
-
if (gameState)
|
|
405
|
+
if (!gameState) return;
|
|
406
|
+
|
|
407
|
+
const isKorea = gameState.coalMarketNorth !== undefined;
|
|
408
|
+
this.isKorea = isKorea;
|
|
409
|
+
|
|
410
|
+
if (isKorea) {
|
|
411
|
+
// SOUTH market — main row coal/oil/garbage at the existing y positions.
|
|
412
|
+
this.coals = this.buildKoreaMainRowPieces(
|
|
413
|
+
gameState.coalPrices!, gameState.coalMarket, 'coal', 'south', 48,
|
|
414
|
+
);
|
|
415
|
+
this.oils = this.buildKoreaMainRowPieces(
|
|
416
|
+
gameState.oilPrices!, gameState.oilMarket, 'oil', 'south', 70,
|
|
417
|
+
);
|
|
418
|
+
this.garbages = this.buildKoreaMainRowPieces(
|
|
419
|
+
gameState.garbagePrices!, gameState.garbageMarket, 'garbage', 'south', 94,
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
// SOUTH uranium: $1..$8 sit between the oil (y=70) and garbage (y=94) rows
|
|
423
|
+
// so they don't visually overlap with oil. $10/$12/$14/$16 go in the corner
|
|
424
|
+
// 2x2 grid (top row at y=52, bottom row at y=91).
|
|
425
|
+
this.uraniums = [];
|
|
426
|
+
const uPrices = gameState.uraniumPrices!;
|
|
427
|
+
uPrices.forEach((p, i) => {
|
|
428
|
+
let x: number, y: number;
|
|
429
|
+
if (p <= 8) {
|
|
430
|
+
x = 60 + 85 * (p - 1);
|
|
431
|
+
y = 85;
|
|
432
|
+
} else {
|
|
433
|
+
// $10 → (710, 52), $12 → (750, 52), $14 → (710, 91), $16 → (750, 91)
|
|
434
|
+
const cornerX = (p === 10 || p === 14) ? 710 : 750;
|
|
435
|
+
const cornerY = (p === 10 || p === 12) ? 52 : 91;
|
|
436
|
+
x = cornerX;
|
|
437
|
+
y = cornerY;
|
|
438
|
+
}
|
|
439
|
+
this.uraniums.push({
|
|
440
|
+
id: `uranium_${i}`,
|
|
441
|
+
x, y,
|
|
442
|
+
transparent: i < (uPrices.length - gameState.uraniumMarket),
|
|
443
|
+
side: 'south',
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// NORTH market — placed above the South so its rect sits at y=-100..-20.
|
|
448
|
+
// Cube rows match the South layout's offsets within the rect (top/mid/bottom).
|
|
449
|
+
this.coalsNorth = this.buildKoreaMainRowPieces(
|
|
450
|
+
gameState.coalPricesNorth!, gameState.coalMarketNorth!, 'coal_north', 'north', -92,
|
|
451
|
+
);
|
|
452
|
+
this.oilsNorth = this.buildKoreaMainRowPieces(
|
|
453
|
+
gameState.oilPricesNorth!, gameState.oilMarketNorth!, 'oil_north', 'north', -70,
|
|
454
|
+
);
|
|
455
|
+
this.garbagesNorth = this.buildKoreaMainRowPieces(
|
|
456
|
+
gameState.garbagePricesNorth!, gameState.garbageMarketNorth!, 'garbage_north', 'north', -46,
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Non-Korea: original logic below (unchanged).
|
|
463
|
+
this.coalsNorth = [];
|
|
464
|
+
this.oilsNorth = [];
|
|
465
|
+
this.garbagesNorth = [];
|
|
466
|
+
|
|
467
|
+
{
|
|
268
468
|
this.coals = [];
|
|
269
469
|
if (gameState.map?.name == 'India') {
|
|
270
470
|
Array(24)
|
|
@@ -435,12 +635,20 @@ export default class Resources extends Vue {
|
|
|
435
635
|
}
|
|
436
636
|
}
|
|
437
637
|
|
|
438
|
-
canBuyResource(resource: string) {
|
|
439
|
-
return !!this.buyableResources!.find(r => r == resource);
|
|
638
|
+
canBuyResource(resource: string, side?: 'north' | 'south') {
|
|
639
|
+
return !!this.buyableResources!.find(r => r.resource == resource && r.side == side);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
get hasBuyableNorth() {
|
|
643
|
+
return !!this.buyableResources?.some(r => r.side === 'north');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
get hasBuyableSouth() {
|
|
647
|
+
return !!this.buyableResources?.some(r => r.side === 'south');
|
|
440
648
|
}
|
|
441
649
|
|
|
442
|
-
buyResource(resource: string) {
|
|
443
|
-
this.$emit('buyResource', resource);
|
|
650
|
+
buyResource(resource: string, side?: 'north' | 'south') {
|
|
651
|
+
this.$emit('buyResource', { resource, side });
|
|
444
652
|
}
|
|
445
653
|
}
|
|
446
654
|
</script>
|
package/src/self-contained.ts
CHANGED
|
@@ -10,7 +10,7 @@ function launchSelfContained(selector = '#app') {
|
|
|
10
10
|
|
|
11
11
|
const emitter = launch(selector);
|
|
12
12
|
|
|
13
|
-
let gameState = setup(
|
|
13
|
+
let gameState = setup(6, { map: 'Korea', variant: 'recharged', showMoney: true, randomizeMap: false }, '3');
|
|
14
14
|
|
|
15
15
|
// gameState.map.viewBox = [1480, 1060];
|
|
16
16
|
// gameState.map.playerOrderPosition = [1160, 140];
|