powergrid-viewer 1.10.1 → 1.11.1
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.
|
|
3
|
+
"version": "1.11.1",
|
|
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.
|
|
21
|
+
"powergrid-engine": "1.15.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/assert": "^1.4.7",
|
|
@@ -22,10 +22,26 @@
|
|
|
22
22
|
</template> -->
|
|
23
23
|
|
|
24
24
|
<template v-for="city in cities">
|
|
25
|
-
<circle
|
|
25
|
+
<circle
|
|
26
|
+
v-if="city.connectionCost == null"
|
|
27
|
+
:key="city.name + '_region'"
|
|
28
|
+
r="25"
|
|
29
|
+
:cx="city.x"
|
|
30
|
+
:cy="city.y"
|
|
31
|
+
:fill="city.region"
|
|
32
|
+
stroke="black"
|
|
33
|
+
>
|
|
26
34
|
<title>{{ city.name }}</title>
|
|
27
35
|
</circle>
|
|
28
|
-
<circle
|
|
36
|
+
<circle
|
|
37
|
+
v-if="city.connectionCost == null"
|
|
38
|
+
:key="city.name + '_circle'"
|
|
39
|
+
r="20"
|
|
40
|
+
:cx="city.x"
|
|
41
|
+
:cy="city.y"
|
|
42
|
+
fill="gray"
|
|
43
|
+
stroke="black"
|
|
44
|
+
>
|
|
29
45
|
<title>{{ city.name }}</title>
|
|
30
46
|
</circle>
|
|
31
47
|
<!-- South Africa cross-border spaces: render a small red pennant
|
|
@@ -72,8 +88,65 @@
|
|
|
72
88
|
/>
|
|
73
89
|
</template>
|
|
74
90
|
|
|
91
|
+
<!-- Bremen-style node-weighted districts: clean labeled tiles (entry-cost
|
|
92
|
+
number + 8/14/20 house slots), drawn AFTER the links so the gray lines
|
|
93
|
+
sit behind. Gated on connectionCost; other maps keep circular nodes. -->
|
|
94
|
+
<template v-for="city in cities">
|
|
95
|
+
<g v-if="city.connectionCost != null" :key="city.name + '_tile'">
|
|
96
|
+
<rect
|
|
97
|
+
:class="[{ canClick: canBuild(city) }]"
|
|
98
|
+
:x="city.x - 23"
|
|
99
|
+
:y="city.y - 23"
|
|
100
|
+
width="46"
|
|
101
|
+
height="46"
|
|
102
|
+
rx="7"
|
|
103
|
+
fill="gray"
|
|
104
|
+
:stroke="city.region"
|
|
105
|
+
stroke-width="4"
|
|
106
|
+
:transform="`rotate(45, ${city.x}, ${city.y})`"
|
|
107
|
+
@click="canBuild(city) && build(city)"
|
|
108
|
+
>
|
|
109
|
+
<title>{{ city.name }} — connect for {{ city.connectionCost }} (entry) + house</title>
|
|
110
|
+
</rect>
|
|
111
|
+
<!-- house slots laid out like the board: 8 top, 14 left, 20 right -->
|
|
112
|
+
<template v-for="(sc, i) in city.slotCosts">
|
|
113
|
+
<rect
|
|
114
|
+
:key="city.name + '_slot' + i"
|
|
115
|
+
:x="city.x + nodeSlotPos(i).dx - 6.5"
|
|
116
|
+
:y="city.y + nodeSlotPos(i).dy - 6.5"
|
|
117
|
+
width="13"
|
|
118
|
+
height="13"
|
|
119
|
+
rx="1"
|
|
120
|
+
fill="gray"
|
|
121
|
+
stroke="black"
|
|
122
|
+
stroke-width="1"
|
|
123
|
+
:transform="`rotate(45, ${city.x + nodeSlotPos(i).dx}, ${city.y + nodeSlotPos(i).dy})`"
|
|
124
|
+
/>
|
|
125
|
+
<text
|
|
126
|
+
:key="city.name + '_slotlbl' + i"
|
|
127
|
+
:x="city.x + nodeSlotPos(i).dx"
|
|
128
|
+
:y="city.y + nodeSlotPos(i).dy"
|
|
129
|
+
font-size="10"
|
|
130
|
+
text-anchor="middle"
|
|
131
|
+
dominant-baseline="central"
|
|
132
|
+
fill="black"
|
|
133
|
+
:transform="
|
|
134
|
+
mapRotation
|
|
135
|
+
? `rotate(${-mapRotation}, ${city.x + nodeSlotPos(i).dx}, ${
|
|
136
|
+
city.y + nodeSlotPos(i).dy
|
|
137
|
+
})`
|
|
138
|
+
: undefined
|
|
139
|
+
"
|
|
140
|
+
>
|
|
141
|
+
{{ sc }}
|
|
142
|
+
</text>
|
|
143
|
+
</template>
|
|
144
|
+
</g>
|
|
145
|
+
</template>
|
|
146
|
+
|
|
75
147
|
<template v-for="city in cities">
|
|
76
148
|
<circle
|
|
149
|
+
v-if="city.connectionCost == null"
|
|
77
150
|
:key="city.name + '_circle2'"
|
|
78
151
|
:class="[{ canClick: canBuild(city) }]"
|
|
79
152
|
r="20"
|
|
@@ -135,6 +208,33 @@
|
|
|
135
208
|
/>
|
|
136
209
|
</template>
|
|
137
210
|
|
|
211
|
+
<!-- Bremen entry-cost number, on top so it stays readable over houses -->
|
|
212
|
+
<template v-for="city in cities">
|
|
213
|
+
<g v-if="city.connectionCost != null" :key="city.name + '_cc'">
|
|
214
|
+
<circle
|
|
215
|
+
:cx="city.x"
|
|
216
|
+
:cy="city.y + 15"
|
|
217
|
+
r="11"
|
|
218
|
+
fill="goldenrod"
|
|
219
|
+
stroke="darkgoldenrod"
|
|
220
|
+
stroke-width="2"
|
|
221
|
+
/>
|
|
222
|
+
<circle :cx="city.x" :cy="city.y + 15" r="7.5" fill="gray" stroke="darkgoldenrod" stroke-width="1" />
|
|
223
|
+
<text
|
|
224
|
+
:x="city.x"
|
|
225
|
+
:y="city.y + 15"
|
|
226
|
+
font-size="11"
|
|
227
|
+
font-weight="bold"
|
|
228
|
+
text-anchor="middle"
|
|
229
|
+
dominant-baseline="central"
|
|
230
|
+
fill="black"
|
|
231
|
+
:transform="mapRotation ? `rotate(${-mapRotation}, ${city.x}, ${city.y + 15})` : undefined"
|
|
232
|
+
>
|
|
233
|
+
{{ city.connectionCost }}
|
|
234
|
+
</text>
|
|
235
|
+
</g>
|
|
236
|
+
</template>
|
|
237
|
+
|
|
138
238
|
<template v-for="city in cities">
|
|
139
239
|
<!-- [10,15] city: X at bottom-right -->
|
|
140
240
|
<template v-if="city.slotCosts && city.slotCosts.length === 2 && city.slotCosts[0] === 10">
|
|
@@ -251,8 +351,16 @@ export default class Map extends Vue {
|
|
|
251
351
|
player.cities.forEach((cityPiece) => {
|
|
252
352
|
const city = gameState.map.cities.find((city) => city.name == cityPiece.name)!;
|
|
253
353
|
let offsetX, offsetY;
|
|
354
|
+
const isNodeWeighted = city.connectionCost != null;
|
|
254
355
|
const is1520 = city.slotCosts?.length === 2 && city.slotCosts[0] === 15;
|
|
255
|
-
if (
|
|
356
|
+
if (isNodeWeighted) {
|
|
357
|
+
// Bremen tiles: 8 top, 14 left, 20 right — matches the board layout.
|
|
358
|
+
// The House piece is anchored top-left (~14×16 after its scale), so
|
|
359
|
+
// offset by half its size to center it on the slot.
|
|
360
|
+
const slot = this.nodeSlotPos(cityPiece.position);
|
|
361
|
+
offsetX = slot.dx - 7;
|
|
362
|
+
offsetY = slot.dy - 8;
|
|
363
|
+
} else if (is1520) {
|
|
256
364
|
// [15,20] city: slot 0 (cost 15) → bottom-left, slot 1 (cost 20) → bottom-right
|
|
257
365
|
if (cityPiece.position == 0) {
|
|
258
366
|
offsetX = -15;
|
|
@@ -303,6 +411,13 @@ export default class Map extends Vue {
|
|
|
303
411
|
return city.y;
|
|
304
412
|
}
|
|
305
413
|
|
|
414
|
+
nodeSlotPos(i: number) {
|
|
415
|
+
// House slots laid out like the printed board: 0 = 8 (top), 1 = 14 (left), 2 = 20 (right).
|
|
416
|
+
if (i === 0) return { dx: 0, dy: -15 };
|
|
417
|
+
if (i === 1) return { dx: -15, dy: 0 };
|
|
418
|
+
return { dx: 15, dy: 0 };
|
|
419
|
+
}
|
|
420
|
+
|
|
306
421
|
canBuild(city: City) {
|
|
307
422
|
return !!this.buildableCities!.find((cityName) => cityName == city.name);
|
|
308
423
|
}
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
{{ resourceResupply[2] }}
|
|
124
124
|
</text>
|
|
125
125
|
<Garbage :pieceId="-1" :targetState="{ x: 382, y: 11 }" :canClick="false" :transparent="false" />
|
|
126
|
-
<!-- Australia
|
|
127
|
-
<template v-if="!isAustraliaMarket">
|
|
126
|
+
<!-- Australia and Bremen have no main-market uranium row, so their resupply indicators omit uranium. -->
|
|
127
|
+
<template v-if="!isAustraliaMarket && !isBremenMarket">
|
|
128
128
|
<text x="414" y="20" font-weight="600" fill="black" style="font-size: 24px">
|
|
129
129
|
{{ resourceResupply[3] }}
|
|
130
130
|
</text>
|
|
@@ -164,7 +164,10 @@
|
|
|
164
164
|
>
|
|
165
165
|
{{ index }}
|
|
166
166
|
</text>
|
|
167
|
-
<g
|
|
167
|
+
<g
|
|
168
|
+
:key="'lines' + index"
|
|
169
|
+
v-if="!isIndiaResourceMarket && !isNinePriceMarket && !isAustraliaMarket && !isBremenMarket"
|
|
170
|
+
>
|
|
168
171
|
<line :x1="25 + 85 * (index - 1)" y1="68" :x2="95 + 85 * (index - 1)" y2="68" stroke="goldenrod" />
|
|
169
172
|
<line :x1="25 + 85 * (index - 1)" y1="92" :x2="95 + 85 * (index - 1)" y2="92" stroke="goldenrod" />
|
|
170
173
|
|
|
@@ -216,7 +219,7 @@
|
|
|
216
219
|
</g>
|
|
217
220
|
</template>
|
|
218
221
|
|
|
219
|
-
<template v-if="!isIndiaResourceMarket && !isNinePriceMarket && !isAustraliaMarket">
|
|
222
|
+
<template v-if="!isIndiaResourceMarket && !isNinePriceMarket && !isAustraliaMarket && !isBremenMarket">
|
|
220
223
|
<rect width="30" height="30" x="705" y="45" rx="2" fill="darkgoldenrod" />
|
|
221
224
|
<circle r="10" cx="732" cy="48" fill="yellow" />
|
|
222
225
|
<text
|
|
@@ -453,6 +456,9 @@ export default class Resources extends Vue {
|
|
|
453
456
|
// Australia: coal/oil/garbage-only market that can reach $10 after the Step 3
|
|
454
457
|
// CO2 tax. co2TaxActive closes the $1/$2 columns once that shift has happened.
|
|
455
458
|
isAustraliaMarket: boolean = false;
|
|
459
|
+
// Bremen: coal/oil/garbage-only market (no uranium row, like Korea's North
|
|
460
|
+
// table) where the $7/$8 spaces hold two cubes each.
|
|
461
|
+
isBremenMarket: boolean = false;
|
|
456
462
|
co2TaxActive: boolean = false;
|
|
457
463
|
coalsNorth: Piece[] = [];
|
|
458
464
|
oilsNorth: Piece[] = [];
|
|
@@ -507,6 +513,7 @@ export default class Resources extends Vue {
|
|
|
507
513
|
|
|
508
514
|
this.isAustraliaMarket = gameState.map?.name === 'Australia';
|
|
509
515
|
this.co2TaxActive = this.isAustraliaMarket && gameState.step >= 3;
|
|
516
|
+
this.isBremenMarket = gameState.map?.name === 'Bremen';
|
|
510
517
|
|
|
511
518
|
const isKorea = gameState.coalMarketNorth !== undefined;
|
|
512
519
|
this.isKorea = isKorea;
|
|
@@ -606,6 +613,20 @@ export default class Resources extends Vue {
|
|
|
606
613
|
return;
|
|
607
614
|
}
|
|
608
615
|
|
|
616
|
+
// Bremen: three-resource market rendered like Korea's North table — no
|
|
617
|
+
// uranium row or $10–$16 corner. Slot counts per price space come from
|
|
618
|
+
// the price arrays ($7/$8 hold two cubes each).
|
|
619
|
+
if (this.isBremenMarket) {
|
|
620
|
+
this.coals = this.buildMainRowPieces(gameState.coalPrices!, gameState.coalMarket, 'coal', 48);
|
|
621
|
+
this.oils = this.buildMainRowPieces(gameState.oilPrices!, gameState.oilMarket, 'oil', 70);
|
|
622
|
+
this.garbages = this.buildMainRowPieces(gameState.garbagePrices!, gameState.garbageMarket, 'garbage', 94);
|
|
623
|
+
this.uraniums = [];
|
|
624
|
+
this.coalsNorth = [];
|
|
625
|
+
this.oilsNorth = [];
|
|
626
|
+
this.garbagesNorth = [];
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
609
630
|
this.isNinePriceMarket = false;
|
|
610
631
|
|
|
611
632
|
// Non-Korea: original logic below (unchanged).
|
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(6, { map: '
|
|
13
|
+
let gameState = setup(6, { map: 'Bremen', variant: 'recharged', showMoney: true, randomizeMap: false }, '0');
|
|
14
14
|
|
|
15
15
|
// Dev coord-picker example (commented; uncomment + drop a board photo into
|
|
16
16
|
// viewer/public/ to author city coordinates for a new map). See the picker
|