powergrid-viewer 1.9.3 → 1.9.5

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.9.3",
3
+ "version": "1.9.5",
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.13.3"
21
+ "powergrid-engine": "1.13.4"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/assert": "^1.4.7",
@@ -59,6 +59,40 @@
59
59
  @build="build($event)"
60
60
  />
61
61
 
62
+ <!-- Japan: Free Jump indicator -->
63
+ <g v-if="G.map.name === 'Japan'">
64
+ <text x="330" y="93" font-size="20" font-weight="bold" fill="black">Free Jump:</text>
65
+ <template v-for="(fjPlayer, i) in G.players">
66
+ <g
67
+ :key="'fj_' + i"
68
+ :transform="`translate(${480 + i * 30}, 80) scale(0.045)`"
69
+ :opacity="playerHasUsedFreeJump(i) ? 0.2 : 1"
70
+ >
71
+ <path
72
+ d="M187.698 263.636V456.017L3 341.204V169.522L80.8579 108.141L187.698 263.636Z"
73
+ :fill="playerColors[i]"
74
+ stroke="#010101"
75
+ stroke-width="12"
76
+ stroke-miterlimit="10"
77
+ />
78
+ <path
79
+ d="M395.724 136.361V300.164L187.698 456.017V263.636L395.724 136.361Z"
80
+ :fill="playerColors[i]"
81
+ stroke="#010101"
82
+ stroke-width="12"
83
+ stroke-miterlimit="10"
84
+ />
85
+ <path
86
+ d="M395.724 136.361L187.698 263.636L80.8579 108.141L304.771 4L395.724 136.361Z"
87
+ :fill="playerColors[i]"
88
+ stroke="#010101"
89
+ stroke-width="12"
90
+ stroke-miterlimit="10"
91
+ />
92
+ </g>
93
+ </template>
94
+ </g>
95
+
62
96
  <Resources
63
97
  ref="resources"
64
98
  :transform="`translate(${G.map.supplyPosition[0]}, ${G.map.supplyPosition[1]})`"
@@ -168,6 +202,45 @@
168
202
  </div>
169
203
  </div>
170
204
 
205
+ <div v-if="G && freeJumpCity" :class="['modal', { visible: freeJumpVisible }]">
206
+ <div class="modal-content">
207
+ <span
208
+ class="close"
209
+ @click="
210
+ freeJumpVisible = false;
211
+ freeJumpCity = null;
212
+ "
213
+ >&times;</span
214
+ >
215
+ <div class="modal-title">Free Jump — {{ freeJumpCity.name }}</div>
216
+ <div class="confirm-message" v-if="freeJumpNormalPrice !== null">
217
+ Use your <b>Free Jump</b> to build here for ${{ freeJumpSlotPrice }} (slot cost only), or pay the
218
+ full connection price of ${{ freeJumpNormalPrice }} and save the jump for later.
219
+ </div>
220
+ <div class="confirm-message" v-else>
221
+ Use your <b>Free Jump</b> to build here for ${{ freeJumpSlotPrice }} (slot cost only)? This city is
222
+ not reachable from your network otherwise.
223
+ </div>
224
+ <div class="confirm-buttons">
225
+ <button class="confirm-button" @click="confirmFreeJump(true)">
226
+ Use Free Jump (${{ freeJumpSlotPrice }})
227
+ </button>
228
+ <button v-if="freeJumpNormalPrice !== null" class="confirm-button" @click="confirmFreeJump(false)">
229
+ Pay Full Price (${{ freeJumpNormalPrice }})
230
+ </button>
231
+ <button
232
+ class="confirm-button"
233
+ @click="
234
+ freeJumpVisible = false;
235
+ freeJumpCity = null;
236
+ "
237
+ >
238
+ Cancel
239
+ </button>
240
+ </div>
241
+ </div>
242
+ </div>
243
+
171
244
  <div v-if="G && discardedPowerPlant" :class="['modal', { visible: discardVisible }]">
172
245
  <div class="modal-content">
173
246
  <div class="modal-title">Discard Resources</div>
@@ -488,6 +561,11 @@ export default class Game extends Vue {
488
561
  discardVisible: boolean = false;
489
562
  resourcesToDiscard: { name: string, max: number, value: string }[] = [];
490
563
 
564
+ freeJumpCity: City | null = null;
565
+ freeJumpNormalPrice: number | null = null;
566
+ freeJumpSlotPrice: number = 0;
567
+ freeJumpVisible: boolean = false;
568
+
491
569
  disablePass: boolean = false;
492
570
 
493
571
  @Ref() powerPlantMarket!: PowerPlantMarket;
@@ -651,8 +729,41 @@ export default class Game extends Vue {
651
729
  build(city: City) {
652
730
  const currentPlayer = this.G!.players[this.player!];
653
731
  const availableMoves = currentPlayer.availableMoves!;
654
- const move = availableMoves[MoveName.Build]!.find((c) => c.name == city.name)!;
655
- this.sendMove({ name: MoveName.Build, data: { name: city.name, price: move.price } });
732
+ const buildMoves = availableMoves[MoveName.Build]!;
733
+ const freeJumpMove = buildMoves.find((c) => c.name === city.name && c.freeJump);
734
+ const normalMove = buildMoves.find((c) => c.name === city.name && !c.freeJump);
735
+
736
+ if (freeJumpMove && normalMove) {
737
+ // Player can choose: use the free jump or pay full price
738
+ this.freeJumpCity = city;
739
+ this.freeJumpSlotPrice = freeJumpMove.price;
740
+ this.freeJumpNormalPrice = normalMove.price;
741
+ this.freeJumpVisible = true;
742
+ } else if (freeJumpMove && !normalMove) {
743
+ // Only reachable via free jump — confirm before using it
744
+ this.freeJumpCity = city;
745
+ this.freeJumpSlotPrice = freeJumpMove.price;
746
+ this.freeJumpNormalPrice = null;
747
+ this.freeJumpVisible = true;
748
+ } else {
749
+ // Normal build, no free jump involved
750
+ this.sendMove({ name: MoveName.Build, data: { name: city.name, price: normalMove!.price } });
751
+ }
752
+ }
753
+
754
+ confirmFreeJump(useJump: boolean) {
755
+ const city = this.freeJumpCity!;
756
+ const currentPlayer = this.G!.players[this.player!];
757
+ const buildMoves = currentPlayer.availableMoves![MoveName.Build]!;
758
+ this.freeJumpVisible = false;
759
+ this.freeJumpCity = null;
760
+ if (useJump) {
761
+ const freeJumpMove = buildMoves.find((c) => c.name === city.name && c.freeJump)!;
762
+ this.sendMove({ name: MoveName.Build, data: { name: city.name, price: freeJumpMove.price, freeJump: true } });
763
+ } else {
764
+ const normalMove = buildMoves.find((c) => c.name === city.name && !c.freeJump)!;
765
+ this.sendMove({ name: MoveName.Build, data: { name: city.name, price: normalMove.price } });
766
+ }
656
767
  }
657
768
 
658
769
  confirmDiscard() {
@@ -924,6 +1035,10 @@ export default class Game extends Vue {
924
1035
  return availableMoves[MoveName.Build] && availableMoves[MoveName.Build]!.map((c) => c.name) || [];
925
1036
  }
926
1037
 
1038
+ playerHasUsedFreeJump(playerIndex: number): boolean {
1039
+ return !!this.G?.players[playerIndex]?.usedFreeJump;
1040
+ }
1041
+
927
1042
  canBuild(city: City) {
928
1043
  if (!this.canMove()) return false;
929
1044
 
@@ -152,6 +152,23 @@
152
152
  stroke-linecap="round"
153
153
  />
154
154
  </template>
155
+ <!-- [10,10,20] city: "10" label at bottom-left (two 10-Elektro slots) -->
156
+ <text
157
+ v-if="
158
+ city.slotCosts &&
159
+ city.slotCosts.length === 3 &&
160
+ city.slotCosts[0] === 10 &&
161
+ city.slotCosts[1] === 10
162
+ "
163
+ :key="city.name + '_10label'"
164
+ :x="city.x - 20"
165
+ :y="city.y + 19"
166
+ font-size="17"
167
+ font-weight="bold"
168
+ fill="black"
169
+ text-anchor="middle"
170
+ >10</text
171
+ >
155
172
  <!-- [15,20] city: X at top-middle -->
156
173
  <template v-if="city.slotCosts && city.slotCosts.length === 2 && city.slotCosts[0] === 15">
157
174
  <line