powergrid-viewer 1.5.4
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/README.md +35 -0
- package/dist/img/help.ba7779cc.svg +1 -0
- package/dist/img/log.04ef6981.svg +1 -0
- package/dist/img/pass.da9065dc.svg +3 -0
- package/dist/img/rules.64f9aae5.svg +28 -0
- package/dist/img/sound-off.72ada995.svg +3 -0
- package/dist/img/sound-on.c55edd90.svg +3 -0
- package/dist/img/undo.208666d2.svg +3 -0
- package/dist/media/notification.55fa47dd.ogg +0 -0
- package/dist/media/notification.ac905963.mp3 +0 -0
- package/dist/media/piece-drop.eef5f607.mp3 +0 -0
- package/dist/powergrid-viewer.css +1 -0
- package/dist/powergrid-viewer.umd.min.js +35 -0
- package/package.json +49 -0
- package/src/audio/notification.mp3 +0 -0
- package/src/audio/notification.ogg +0 -0
- package/src/audio/piece-drop.mp3 +0 -0
- package/src/components/Calculator.vue +62 -0
- package/src/components/Game.vue +1354 -0
- package/src/components/PlayerBoard.vue +230 -0
- package/src/components/boards/CityCount.vue +82 -0
- package/src/components/boards/Map.vue +196 -0
- package/src/components/boards/PlayerOrder.vue +68 -0
- package/src/components/boards/PowerPlantMarket.vue +184 -0
- package/src/components/boards/Resources.vue +446 -0
- package/src/components/buttons/Button.vue +26 -0
- package/src/components/buttons/HelpButton.vue +18 -0
- package/src/components/buttons/LogButton.vue +15 -0
- package/src/components/buttons/PassButton.vue +18 -0
- package/src/components/buttons/RulesButton.vue +14 -0
- package/src/components/buttons/SoundButton.vue +18 -0
- package/src/components/buttons/UndoButton.vue +17 -0
- package/src/components/buttons/index.js +9 -0
- package/src/components/pieces/Card.vue +131 -0
- package/src/components/pieces/Coal.vue +40 -0
- package/src/components/pieces/Garbage.vue +40 -0
- package/src/components/pieces/House.vue +51 -0
- package/src/components/pieces/Hybrid.vue +37 -0
- package/src/components/pieces/Oil.vue +40 -0
- package/src/components/pieces/Piece.vue +104 -0
- package/src/components/pieces/Uranium.vue +32 -0
- package/src/components/pieces/index.js +10 -0
- package/src/icons/help.svg +1 -0
- package/src/icons/log.svg +1 -0
- package/src/icons/pass.svg +3 -0
- package/src/icons/rules.svg +28 -0
- package/src/icons/sound-off.svg +3 -0
- package/src/icons/sound-on.svg +3 -0
- package/src/icons/undo.svg +3 -0
- package/src/launch.ts +87 -0
- package/src/main.ts +3 -0
- package/src/self-contained.ts +97 -0
- package/src/shims-tsx.d.ts +13 -0
- package/src/shims-vue.d.ts +4 -0
- package/src/types/ui-data.ts +34 -0
- package/src/wrapper.ts +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<g class="player-board">
|
|
3
|
+
<rect width="350" height="100" x="0" y="0" fill="gold" :stroke="color" />
|
|
4
|
+
<rect width="350" height="25" x="0" y="0" :fill="color" />
|
|
5
|
+
<foreignObject height="30" width="30" x="-30" y="-5">
|
|
6
|
+
<img
|
|
7
|
+
:src="avatar || `https://avatars.dicebear.com/api/pixel-art/${player.name}.svg`"
|
|
8
|
+
width="100%"
|
|
9
|
+
height="100%"
|
|
10
|
+
style="border-radius: 50%"
|
|
11
|
+
/>
|
|
12
|
+
</foreignObject>
|
|
13
|
+
<text x="5" y="13" font-weight="600" fill="black">
|
|
14
|
+
{{ getPlayerName() }}
|
|
15
|
+
</text>
|
|
16
|
+
<text v-if="showMoney" x="250" y="13" font-weight="600" fill="black">Money: ${{ player.money }}</text>
|
|
17
|
+
|
|
18
|
+
<Card
|
|
19
|
+
v-for="(powerPlant, i) in player.powerPlants"
|
|
20
|
+
:key="'powerPlant_' + powerPlant.number"
|
|
21
|
+
:targetState="{ x: player.powerPlants.length < 5 ? 20 + 80 * i : 5 + 70 * i, y: 30 }"
|
|
22
|
+
:owner="owner"
|
|
23
|
+
:powerPlant="powerPlant"
|
|
24
|
+
:canClick="canUse(powerPlant)"
|
|
25
|
+
@click="powerPlantClick(powerPlant)"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<template v-if="!preferences.disableHelp">
|
|
29
|
+
<template v-for="(powerPlant, i) in player.powerPlants">
|
|
30
|
+
<rect
|
|
31
|
+
v-if="canUse(powerPlant)"
|
|
32
|
+
:key="i + '_' + i + '_helper'"
|
|
33
|
+
:x="player.powerPlants.length < 5 ? 20 + 80 * i : 5 + 70 * i"
|
|
34
|
+
:y="30"
|
|
35
|
+
width="60"
|
|
36
|
+
height="40"
|
|
37
|
+
fill="none"
|
|
38
|
+
stroke="blue"
|
|
39
|
+
stroke-width="4px"
|
|
40
|
+
rx="2px"
|
|
41
|
+
/>
|
|
42
|
+
</template>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<g v-if="canClickResources">
|
|
46
|
+
<rect width="30" height="30" x="0" y="71" fill="none" stroke="blue" stroke-width="2px" rx="2px" />
|
|
47
|
+
<rect width="30" height="30" x="70" y="71" fill="none" stroke="blue" stroke-width="2px" rx="2px" />
|
|
48
|
+
</g>
|
|
49
|
+
|
|
50
|
+
<Coal
|
|
51
|
+
:pieceId="'Coal' + player.id"
|
|
52
|
+
:targetState="{ x: 5, y: 76 }"
|
|
53
|
+
:canClick="canClickResources"
|
|
54
|
+
@click="clickResource('coal')"
|
|
55
|
+
/>
|
|
56
|
+
<text text-anchor="middle" x="40" y="85" fill="black">{{ player.coalLeft }}</text>
|
|
57
|
+
|
|
58
|
+
<Oil
|
|
59
|
+
:pieceId="'Oil' + player.id"
|
|
60
|
+
:targetState="{ x: 75, y: 76 }"
|
|
61
|
+
:canClick="canClickResources"
|
|
62
|
+
@click="clickResource('oil')"
|
|
63
|
+
/>
|
|
64
|
+
<text text-anchor="middle" x="110" y="85" fill="black">{{ player.oilLeft }}</text>
|
|
65
|
+
|
|
66
|
+
<Garbage :pieceId="'Garbage' + player.id" :targetState="{ x: 145, y: 76 }" />
|
|
67
|
+
<text text-anchor="middle" x="180" y="85" fill="black">{{ player.garbageLeft }}</text>
|
|
68
|
+
|
|
69
|
+
<Uranium :pieceId="'Uranium' + player.id" :targetState="{ x: 215, y: 76 }" />
|
|
70
|
+
<text text-anchor="middle" x="250" y="85" fill="black">{{ player.uraniumLeft }}</text>
|
|
71
|
+
</g>
|
|
72
|
+
</template>
|
|
73
|
+
<script lang="ts">
|
|
74
|
+
import { MoveName, Player } from 'powergrid-engine';
|
|
75
|
+
import { Phase, PowerPlant, PowerPlantType, ResourceType } from 'powergrid-engine/src/gamestate';
|
|
76
|
+
import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
|
|
77
|
+
import { Preferences } from '../types/ui-data';
|
|
78
|
+
import { Coal, Oil, Garbage, Uranium, Card } from './pieces';
|
|
79
|
+
|
|
80
|
+
@Component({
|
|
81
|
+
components: {
|
|
82
|
+
Coal,
|
|
83
|
+
Oil,
|
|
84
|
+
Garbage,
|
|
85
|
+
Uranium,
|
|
86
|
+
Card,
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
export default class PlayerBoard extends Vue {
|
|
90
|
+
@Prop() color?: string;
|
|
91
|
+
@Prop() avatar?: string;
|
|
92
|
+
@Prop() player!: Player;
|
|
93
|
+
@Prop() isCurrentPlayer?: boolean;
|
|
94
|
+
@Prop() owner?: number;
|
|
95
|
+
@Prop() ended?: boolean;
|
|
96
|
+
@Prop() isPlayer?: boolean;
|
|
97
|
+
@Prop() ranking?: number;
|
|
98
|
+
@Prop() showMoney?: boolean;
|
|
99
|
+
@Prop() showBid?: boolean;
|
|
100
|
+
@Prop() phase?: Phase;
|
|
101
|
+
|
|
102
|
+
@Inject() preferences!: Preferences;
|
|
103
|
+
|
|
104
|
+
powerPlantClicked?: PowerPlant;
|
|
105
|
+
|
|
106
|
+
getPlayerName() {
|
|
107
|
+
let name = '';
|
|
108
|
+
if (this.isCurrentPlayer) {
|
|
109
|
+
name += '• ';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
name += this.player.name;
|
|
113
|
+
|
|
114
|
+
if (this.ended) {
|
|
115
|
+
switch (this.ranking) {
|
|
116
|
+
case 1:
|
|
117
|
+
name += ' (1st)';
|
|
118
|
+
break;
|
|
119
|
+
case 2:
|
|
120
|
+
name += ' (2nd)';
|
|
121
|
+
break;
|
|
122
|
+
case 3:
|
|
123
|
+
name += ' (3rd)';
|
|
124
|
+
break;
|
|
125
|
+
case 4:
|
|
126
|
+
case 5:
|
|
127
|
+
case 6:
|
|
128
|
+
name += ` (${this.ranking}th)`;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
} else if (this.player.skipAuction) {
|
|
132
|
+
name += ' (Skipping)';
|
|
133
|
+
} else if (this.player.passed) {
|
|
134
|
+
name += ' (Passed)';
|
|
135
|
+
} else if (this.player.bid && !this.isCurrentPlayer && this.showBid) {
|
|
136
|
+
name += ' (Bid: $' + this.player.bid + ')';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return name;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
canUse(powerPlant) {
|
|
143
|
+
if (!this.isCurrentPlayer) return false;
|
|
144
|
+
|
|
145
|
+
if (this.player.availableMoves?.[MoveName.DiscardPowerPlant]) {
|
|
146
|
+
return powerPlant.number !== this.player.powerPlants[this.player.powerPlants.length - 1].number;
|
|
147
|
+
} else {
|
|
148
|
+
if (!this.player.availableMoves?.[MoveName.UsePowerPlant]) return false;
|
|
149
|
+
|
|
150
|
+
if (!this.player.powerPlantsNotUsed.includes(powerPlant.number) || this.player.resourcesUsed.length > 0) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
switch (powerPlant.type) {
|
|
155
|
+
case PowerPlantType.Coal: {
|
|
156
|
+
return this.player.coalLeft >= powerPlant.cost;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case PowerPlantType.Oil: {
|
|
160
|
+
return this.player.oilLeft >= powerPlant.cost;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case PowerPlantType.Garbage: {
|
|
164
|
+
return this.player.garbageLeft >= powerPlant.cost;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case PowerPlantType.Uranium: {
|
|
168
|
+
return this.player.uraniumLeft >= powerPlant.cost;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
case PowerPlantType.Hybrid: {
|
|
172
|
+
return this.player.coalLeft + this.player.oilLeft >= powerPlant.cost;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case PowerPlantType.Wind:
|
|
176
|
+
case PowerPlantType.Nuclear:
|
|
177
|
+
return this.player.coalLeft + this.player.oilLeft >= powerPlant.cost;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
powerPlantClick(powerPlant: PowerPlant) {
|
|
183
|
+
if (this.phase == Phase.Bureaucracy && powerPlant.type == PowerPlantType.Hybrid) {
|
|
184
|
+
if (
|
|
185
|
+
this.player.coalLeft == 0 ||
|
|
186
|
+
this.player.oilLeft == 0 ||
|
|
187
|
+
this.player.coalLeft + this.player.oilLeft == powerPlant.cost
|
|
188
|
+
) {
|
|
189
|
+
this.player.resourcesUsed = Array(Math.min(powerPlant.cost, this.player.coalLeft))
|
|
190
|
+
.fill(ResourceType.Coal)
|
|
191
|
+
.concat(
|
|
192
|
+
Array(powerPlant.cost - Math.min(powerPlant.cost, this.player.coalLeft)).fill(ResourceType.Oil)
|
|
193
|
+
);
|
|
194
|
+
this.$emit('powerPlantClick', powerPlant);
|
|
195
|
+
} else {
|
|
196
|
+
this.powerPlantClicked = powerPlant;
|
|
197
|
+
this.player.resourcesUsed = Array(powerPlant.cost).fill(null);
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
this.$emit('powerPlantClick', powerPlant);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get canClickResources() {
|
|
205
|
+
return (
|
|
206
|
+
(this.player.availableMoves && this.player.availableMoves['DiscardResources']) ||
|
|
207
|
+
this.player.resourcesUsed.some((r) => r == null)
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
clickResource(resourceType) {
|
|
212
|
+
if (this.player.availableMoves && this.player.availableMoves['DiscardResources']) {
|
|
213
|
+
this.$emit('discardResource', resourceType);
|
|
214
|
+
} else if (this.player.resourcesUsed.some((r) => r == null)) {
|
|
215
|
+
const index = this.player.resourcesUsed.findIndex((r) => r == null)!;
|
|
216
|
+
this.player.resourcesUsed[index] = resourceType;
|
|
217
|
+
|
|
218
|
+
if (resourceType == 'coal') {
|
|
219
|
+
this.player.coalLeft--;
|
|
220
|
+
} else if (resourceType == 'oil') {
|
|
221
|
+
this.player.oilLeft--;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!this.player.resourcesUsed.some((r) => r == null)) {
|
|
225
|
+
this.$emit('powerPlantClick', this.powerPlantClicked);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
</script>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<g>
|
|
3
|
+
<rect width="730" height="50" x="10" y="10" rx="3" fill="goldenrod" />
|
|
4
|
+
<template v-for="index in 21">
|
|
5
|
+
<rect
|
|
6
|
+
:key="'playerCityCount' + index"
|
|
7
|
+
width="28"
|
|
8
|
+
height="38"
|
|
9
|
+
:x="46 + 33 * (index - 1)"
|
|
10
|
+
y="16"
|
|
11
|
+
rx="2"
|
|
12
|
+
fill="darkgoldenrod"
|
|
13
|
+
:stroke="index == citiesToStep2 || index == citiesToEndGame ? '#916a08' : 'darkgoldenrod'"
|
|
14
|
+
stroke-width="2px"
|
|
15
|
+
/>
|
|
16
|
+
<text
|
|
17
|
+
:key="'playerCityCountText' + index"
|
|
18
|
+
text-anchor="middle"
|
|
19
|
+
style="font-size: 24px; font-family: monospace"
|
|
20
|
+
letter-spacing="-2"
|
|
21
|
+
:x="59 + 33 * (index - 1)"
|
|
22
|
+
y="35"
|
|
23
|
+
fill="gold"
|
|
24
|
+
>
|
|
25
|
+
{{ index }}
|
|
26
|
+
</text>
|
|
27
|
+
</template>
|
|
28
|
+
<template v-for="house in houses">
|
|
29
|
+
<House
|
|
30
|
+
:key="house.id"
|
|
31
|
+
:pieceId="house.id"
|
|
32
|
+
:targetState="{ x: house.x, y: house.y }"
|
|
33
|
+
:owner="house.owner"
|
|
34
|
+
:ownerName="house.ownerName"
|
|
35
|
+
:color="house.color"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
38
|
+
</g>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import type { GameState } from 'powergrid-engine';
|
|
43
|
+
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
44
|
+
import { House } from '../pieces';
|
|
45
|
+
import { Piece } from '../../types/ui-data';
|
|
46
|
+
|
|
47
|
+
@Component({
|
|
48
|
+
components: {
|
|
49
|
+
House
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
export default class CityCount extends Vue {
|
|
53
|
+
@Prop() citiesToStep2?: number;
|
|
54
|
+
@Prop() citiesToEndGame?: number;
|
|
55
|
+
@Prop() playerColors?: string[];
|
|
56
|
+
|
|
57
|
+
houses: Piece[] = [];
|
|
58
|
+
|
|
59
|
+
createPieces(gameState: GameState) {
|
|
60
|
+
this.houses = [];
|
|
61
|
+
const adjustCityCount: number[][] = [];
|
|
62
|
+
for (let i = 0; i < 22; i++) adjustCityCount[i] = [];
|
|
63
|
+
|
|
64
|
+
gameState.players.forEach((player, pi) => adjustCityCount[player.cities.length].push(pi));
|
|
65
|
+
gameState.players.forEach((player, pi) => {
|
|
66
|
+
let x = (adjustCityCount[player.cities.length].length == 1 ? 23 : 20) + 33 * player.cities.length;
|
|
67
|
+
x += (adjustCityCount[player.cities.length].indexOf(pi) % 2) * 6;
|
|
68
|
+
|
|
69
|
+
this.houses.push({
|
|
70
|
+
id: pi + '_cityCount',
|
|
71
|
+
x: x,
|
|
72
|
+
y:
|
|
73
|
+
15 +
|
|
74
|
+
(adjustCityCount[player.cities.length].indexOf(pi) * 30) /
|
|
75
|
+
adjustCityCount[player.cities.length].length,
|
|
76
|
+
color: this.playerColors![pi],
|
|
77
|
+
owner: pi,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<g>
|
|
3
|
+
<!-- <template v-for="polygon in polygons">
|
|
4
|
+
<polygon
|
|
5
|
+
:key="'pol_ ' + polygon.region"
|
|
6
|
+
:points="polygon.points.map((p) => `${p[0]},${p[1]}`).join(' ')"
|
|
7
|
+
:fill="polygon.region"
|
|
8
|
+
opacity="0.8"
|
|
9
|
+
stroke="black"
|
|
10
|
+
></polygon>
|
|
11
|
+
</template> -->
|
|
12
|
+
|
|
13
|
+
<template v-for="city in cities">
|
|
14
|
+
<circle :key="city.name + '_region'" r="25" :cx="city.x" :cy="city.y" :fill="city.region" stroke="black">
|
|
15
|
+
<title>{{ city.name }}</title>
|
|
16
|
+
</circle>
|
|
17
|
+
<circle :key="city.name + '_circle'" r="20" :cx="city.x" :cy="city.y" fill="gray" stroke="black">
|
|
18
|
+
<title>{{ city.name }}</title>
|
|
19
|
+
</circle>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<template v-for="connection in connections">
|
|
23
|
+
<line
|
|
24
|
+
:key="connection.nodes[0] + '_' + connection.nodes[1] + '_line1'"
|
|
25
|
+
:x1="getX1(connection)"
|
|
26
|
+
:y1="getY1(connection)"
|
|
27
|
+
:x2="getX2(connection)"
|
|
28
|
+
:y2="getY2(connection)"
|
|
29
|
+
stroke-width="10"
|
|
30
|
+
stroke="black"
|
|
31
|
+
/>
|
|
32
|
+
<line
|
|
33
|
+
:key="connection.nodes[0] + '_' + connection.nodes[1] + '_line2'"
|
|
34
|
+
:x1="getX1(connection)"
|
|
35
|
+
:y1="getY1(connection)"
|
|
36
|
+
:x2="getX2(connection)"
|
|
37
|
+
:y2="getY2(connection)"
|
|
38
|
+
stroke-width="9"
|
|
39
|
+
stroke="gray"
|
|
40
|
+
/>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<template v-for="city in cities">
|
|
44
|
+
<circle
|
|
45
|
+
:key="city.name + '_circle2'"
|
|
46
|
+
:class="[{ canClick: canBuild(city) }]"
|
|
47
|
+
r="20"
|
|
48
|
+
:cx="city.x"
|
|
49
|
+
:cy="city.y"
|
|
50
|
+
fill="gray"
|
|
51
|
+
@click="canBuild(city) && build(city)"
|
|
52
|
+
>
|
|
53
|
+
<title>{{ city.name }}</title>
|
|
54
|
+
</circle>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<template v-for="connection in connections">
|
|
58
|
+
<circle
|
|
59
|
+
v-if="connection.cost > 0"
|
|
60
|
+
:key="connection.nodes[0] + '_' + connection.nodes[1] + '_border'"
|
|
61
|
+
stroke="black"
|
|
62
|
+
:r="10 + (connection.cost * 10) / 28"
|
|
63
|
+
:cx="(getX1(connection) + getX2(connection)) / 2"
|
|
64
|
+
:cy="(getY1(connection) + getY2(connection)) / 2"
|
|
65
|
+
:fill="connection.cost > 15 ? 'gold' : connection.cost > 10 ? 'lightgray' : 'tan'"
|
|
66
|
+
/>
|
|
67
|
+
<circle
|
|
68
|
+
v-if="connection.cost > 0"
|
|
69
|
+
:key="connection.nodes[0] + '_' + connection.nodes[1] + '_circle'"
|
|
70
|
+
:r="6 + (connection.cost * 10) / 28"
|
|
71
|
+
:cx="(getX1(connection) + getX2(connection)) / 2"
|
|
72
|
+
:cy="(getY1(connection) + getY2(connection)) / 2"
|
|
73
|
+
fill="gray"
|
|
74
|
+
stroke="black"
|
|
75
|
+
/>
|
|
76
|
+
<text
|
|
77
|
+
v-if="connection.cost > 0"
|
|
78
|
+
:key="connection.nodes[0] + '_' + connection.nodes[1] + '_text'"
|
|
79
|
+
text-anchor="middle"
|
|
80
|
+
:style="`font-size: ${12 + (connection.cost * 6) / 28}px`"
|
|
81
|
+
fill="black"
|
|
82
|
+
:x="(getX1(connection) + getX2(connection)) / 2"
|
|
83
|
+
:y="(getY1(connection) + getY2(connection)) / 2"
|
|
84
|
+
>
|
|
85
|
+
{{ connection.cost }}
|
|
86
|
+
</text>
|
|
87
|
+
</template>
|
|
88
|
+
<template v-for="house in houses">
|
|
89
|
+
<House
|
|
90
|
+
:key="house.id"
|
|
91
|
+
:pieceId="house.id"
|
|
92
|
+
:targetState="{ x: house.x, y: house.y }"
|
|
93
|
+
:owner="house.owner"
|
|
94
|
+
:ownerName="house.ownerName"
|
|
95
|
+
:color="house.color"
|
|
96
|
+
/>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<template v-if="!preferences.disableHelp">
|
|
100
|
+
<template v-for="city in cities">
|
|
101
|
+
<circle
|
|
102
|
+
v-if="canBuild(city)"
|
|
103
|
+
:key="city.name + '_circleHelp'"
|
|
104
|
+
:class="[{ canClick: canBuild(city) }]"
|
|
105
|
+
r="18"
|
|
106
|
+
:cx="city.x"
|
|
107
|
+
:cy="city.y"
|
|
108
|
+
fill="none"
|
|
109
|
+
stroke="blue"
|
|
110
|
+
stroke-width="4px"
|
|
111
|
+
/>
|
|
112
|
+
</template>
|
|
113
|
+
</template>
|
|
114
|
+
</g>
|
|
115
|
+
</template>
|
|
116
|
+
|
|
117
|
+
<script lang="ts">
|
|
118
|
+
import type { GameState } from 'powergrid-engine';
|
|
119
|
+
import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
|
|
120
|
+
import { House } from '../pieces';
|
|
121
|
+
import { Piece, Preferences } from '../../types/ui-data';
|
|
122
|
+
import { City, Connection, Polygon } from 'powergrid-engine/src/maps';
|
|
123
|
+
|
|
124
|
+
@Component({
|
|
125
|
+
components: {
|
|
126
|
+
House
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
export default class Map extends Vue {
|
|
130
|
+
@Prop() polygons?: Polygon[];
|
|
131
|
+
@Prop() cities?: City[];
|
|
132
|
+
@Prop() connections?: Connection[];
|
|
133
|
+
@Prop() playerColors?: string[];
|
|
134
|
+
@Prop() buildableCities?: string[];
|
|
135
|
+
|
|
136
|
+
@Inject() preferences!: Preferences;
|
|
137
|
+
|
|
138
|
+
houses: Piece[] = [];
|
|
139
|
+
|
|
140
|
+
createPieces(gameState: GameState) {
|
|
141
|
+
this.houses = [];
|
|
142
|
+
gameState.players.forEach((player, pi) => {
|
|
143
|
+
player.cities.forEach((cityPiece) => {
|
|
144
|
+
const city = gameState.map.cities.find((city) => city.name == cityPiece.name)!;
|
|
145
|
+
let offsetX, offsetY;
|
|
146
|
+
if (cityPiece.position == 0) {
|
|
147
|
+
offsetX = -6;
|
|
148
|
+
offsetY = -18;
|
|
149
|
+
} else if (cityPiece.position == 1) {
|
|
150
|
+
offsetX = -15;
|
|
151
|
+
offsetY = -4;
|
|
152
|
+
} else {
|
|
153
|
+
offsetX = 1;
|
|
154
|
+
offsetY = -4;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.houses.push({
|
|
158
|
+
id: pi + '_' + cityPiece.name,
|
|
159
|
+
x: city.x + offsetX,
|
|
160
|
+
y: city.y + offsetY,
|
|
161
|
+
color: this.playerColors![pi],
|
|
162
|
+
owner: pi,
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getX1(connection) {
|
|
169
|
+
const city = this.cities!.find((city) => city.name == connection.nodes[0])!;
|
|
170
|
+
return city.x;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getY1(connection) {
|
|
174
|
+
const city = this.cities!.find((city) => city.name == connection.nodes[0])!;
|
|
175
|
+
return city.y;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getX2(connection) {
|
|
179
|
+
const city = this.cities!.find((city) => city.name == connection.nodes[1])!;
|
|
180
|
+
return city.x;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getY2(connection) {
|
|
184
|
+
const city = this.cities!.find((city) => city.name == connection.nodes[1])!;
|
|
185
|
+
return city.y;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
canBuild(city: City) {
|
|
189
|
+
return !!this.buildableCities!.find((cityName) => cityName == city.name);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
build(city: City) {
|
|
193
|
+
this.$emit('build', city);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
</script>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<g>
|
|
3
|
+
<rect x="10" y="10" width="185" height="40" rx="3" fill="goldenrod" />
|
|
4
|
+
<template v-for="index in 6">
|
|
5
|
+
<rect
|
|
6
|
+
:key="'playerOrder' + index"
|
|
7
|
+
:x="15 + 30 * (index - 1)"
|
|
8
|
+
y="15"
|
|
9
|
+
width="24"
|
|
10
|
+
height="30"
|
|
11
|
+
rx="2"
|
|
12
|
+
fill="darkgoldenrod"
|
|
13
|
+
/>
|
|
14
|
+
<text
|
|
15
|
+
:key="'playerOrderText' + index"
|
|
16
|
+
text-anchor="middle"
|
|
17
|
+
style="font-size: 32px; font-family: monospace"
|
|
18
|
+
:x="27 + 30 * (index - 1)"
|
|
19
|
+
y="30"
|
|
20
|
+
fill="gold"
|
|
21
|
+
>
|
|
22
|
+
{{ index }}
|
|
23
|
+
</text>
|
|
24
|
+
</template>
|
|
25
|
+
<template v-for="house in houses">
|
|
26
|
+
<House
|
|
27
|
+
:key="house.id"
|
|
28
|
+
:pieceId="house.id"
|
|
29
|
+
:targetState="{ x: house.x, y: house.y }"
|
|
30
|
+
:owner="house.owner"
|
|
31
|
+
:ownerName="house.ownerName"
|
|
32
|
+
:color="house.color"
|
|
33
|
+
/>
|
|
34
|
+
</template>
|
|
35
|
+
</g>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script lang="ts">
|
|
39
|
+
import type { GameState } from 'powergrid-engine';
|
|
40
|
+
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
41
|
+
import { House } from '../pieces';
|
|
42
|
+
import { Piece } from '../../types/ui-data';
|
|
43
|
+
|
|
44
|
+
@Component({
|
|
45
|
+
components: {
|
|
46
|
+
House
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
export default class PlayerOrder extends Vue {
|
|
50
|
+
@Prop() playerColors?: string[];
|
|
51
|
+
|
|
52
|
+
houses: Piece[] = [];
|
|
53
|
+
|
|
54
|
+
createPieces(gameState: GameState) {
|
|
55
|
+
this.houses = [];
|
|
56
|
+
gameState.playerOrder.forEach((p, i) => {
|
|
57
|
+
this.houses.push({
|
|
58
|
+
id: p + '_order',
|
|
59
|
+
x: 20 + 30 * i,
|
|
60
|
+
y: 22,
|
|
61
|
+
color: this.playerColors![p],
|
|
62
|
+
owner: p,
|
|
63
|
+
ownerName: gameState.players[p].name
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
</script>
|