powergrid-viewer 1.9.4 → 1.9.6
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
|
+
"version": "1.9.6",
|
|
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.
|
|
21
|
+
"powergrid-engine": "1.13.5"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/assert": "^1.4.7",
|
package/src/components/Game.vue
CHANGED
|
@@ -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
|
+
>×</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>
|
|
@@ -385,6 +458,7 @@ import Resources from './boards/Resources.vue';
|
|
|
385
458
|
import { LogMove } from 'powergrid-engine/src/log';
|
|
386
459
|
import { Phase, PowerPlant, PowerPlantType, ResourceType } from 'powergrid-engine/src/gamestate';
|
|
387
460
|
import { City } from 'powergrid-engine/src/maps';
|
|
461
|
+
import { countNetworks } from 'powergrid-engine/src/available-moves';
|
|
388
462
|
|
|
389
463
|
@Component({
|
|
390
464
|
created(this: Game) {
|
|
@@ -488,6 +562,11 @@ export default class Game extends Vue {
|
|
|
488
562
|
discardVisible: boolean = false;
|
|
489
563
|
resourcesToDiscard: { name: string, max: number, value: string }[] = [];
|
|
490
564
|
|
|
565
|
+
freeJumpCity: City | null = null;
|
|
566
|
+
freeJumpNormalPrice: number | null = null;
|
|
567
|
+
freeJumpSlotPrice: number = 0;
|
|
568
|
+
freeJumpVisible: boolean = false;
|
|
569
|
+
|
|
491
570
|
disablePass: boolean = false;
|
|
492
571
|
|
|
493
572
|
@Ref() powerPlantMarket!: PowerPlantMarket;
|
|
@@ -651,8 +730,41 @@ export default class Game extends Vue {
|
|
|
651
730
|
build(city: City) {
|
|
652
731
|
const currentPlayer = this.G!.players[this.player!];
|
|
653
732
|
const availableMoves = currentPlayer.availableMoves!;
|
|
654
|
-
const
|
|
655
|
-
|
|
733
|
+
const buildMoves = availableMoves[MoveName.Build]!;
|
|
734
|
+
const freeJumpMove = buildMoves.find((c) => c.name === city.name && c.freeJump);
|
|
735
|
+
const normalMove = buildMoves.find((c) => c.name === city.name && !c.freeJump);
|
|
736
|
+
|
|
737
|
+
if (freeJumpMove && normalMove) {
|
|
738
|
+
// Player can choose: use the free jump or pay full price
|
|
739
|
+
this.freeJumpCity = city;
|
|
740
|
+
this.freeJumpSlotPrice = freeJumpMove.price;
|
|
741
|
+
this.freeJumpNormalPrice = normalMove.price;
|
|
742
|
+
this.freeJumpVisible = true;
|
|
743
|
+
} else if (freeJumpMove && !normalMove) {
|
|
744
|
+
// Only reachable via free jump — confirm before using it
|
|
745
|
+
this.freeJumpCity = city;
|
|
746
|
+
this.freeJumpSlotPrice = freeJumpMove.price;
|
|
747
|
+
this.freeJumpNormalPrice = null;
|
|
748
|
+
this.freeJumpVisible = true;
|
|
749
|
+
} else {
|
|
750
|
+
// Normal build, no free jump involved
|
|
751
|
+
this.sendMove({ name: MoveName.Build, data: { name: city.name, price: normalMove!.price } });
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
confirmFreeJump(useJump: boolean) {
|
|
756
|
+
const city = this.freeJumpCity!;
|
|
757
|
+
const currentPlayer = this.G!.players[this.player!];
|
|
758
|
+
const buildMoves = currentPlayer.availableMoves![MoveName.Build]!;
|
|
759
|
+
this.freeJumpVisible = false;
|
|
760
|
+
this.freeJumpCity = null;
|
|
761
|
+
if (useJump) {
|
|
762
|
+
const freeJumpMove = buildMoves.find((c) => c.name === city.name && c.freeJump)!;
|
|
763
|
+
this.sendMove({ name: MoveName.Build, data: { name: city.name, price: freeJumpMove.price, freeJump: true } });
|
|
764
|
+
} else {
|
|
765
|
+
const normalMove = buildMoves.find((c) => c.name === city.name && !c.freeJump)!;
|
|
766
|
+
this.sendMove({ name: MoveName.Build, data: { name: city.name, price: normalMove.price } });
|
|
767
|
+
}
|
|
656
768
|
}
|
|
657
769
|
|
|
658
770
|
confirmDiscard() {
|
|
@@ -924,6 +1036,19 @@ export default class Game extends Vue {
|
|
|
924
1036
|
return availableMoves[MoveName.Build] && availableMoves[MoveName.Build]!.map((c) => c.name) || [];
|
|
925
1037
|
}
|
|
926
1038
|
|
|
1039
|
+
playerHasUsedFreeJump(playerIndex: number): boolean {
|
|
1040
|
+
const player = this.G?.players[playerIndex];
|
|
1041
|
+
if (!player) return false;
|
|
1042
|
+
// Primary: trust the engine flag (set for all new games).
|
|
1043
|
+
if (player.usedFreeJump) return true;
|
|
1044
|
+
// Fallback: detect from network topology for game states where
|
|
1045
|
+
// usedFreeJump wasn't tracked (games started before that field existed).
|
|
1046
|
+
if (player.cities.length >= 2) {
|
|
1047
|
+
return countNetworks(this.G!.map.connections, player.cities.map(c => c.name)) >= 2;
|
|
1048
|
+
}
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
927
1052
|
canBuild(city: City) {
|
|
928
1053
|
if (!this.canMove()) return false;
|
|
929
1054
|
|
|
@@ -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
|