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.10.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.14.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 :key="city.name + '_region'" r="25" :cx="city.x" :cy="city.y" :fill="city.region" stroke="black">
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 :key="city.name + '_circle'" r="20" :cx="city.x" :cy="city.y" fill="gray" stroke="black">
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 (is1520) {
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 has no main-market uranium row, so its resupply indicator omits uranium. -->
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 :key="'lines' + index" v-if="!isIndiaResourceMarket && !isNinePriceMarket && !isAustraliaMarket">
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).
@@ -10,7 +10,7 @@ function launchSelfContained(selector = '#app') {
10
10
 
11
11
  const emitter = launch(selector);
12
12
 
13
- let gameState = setup(6, { map: 'Australia', variant: 'recharged', showMoney: true, randomizeMap: false }, '7');
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