pywargame 0.3.1__py3-none-any.whl
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.
- pywargame/__init__.py +2 -0
- pywargame/common/__init__.py +3 -0
- pywargame/common/collector.py +87 -0
- pywargame/common/dicedraw.py +363 -0
- pywargame/common/drawdice.py +40 -0
- pywargame/common/singleton.py +22 -0
- pywargame/common/test.py +25 -0
- pywargame/common/verbose.py +59 -0
- pywargame/common/verboseguard.py +53 -0
- pywargame/cyberboard/__init__.py +18 -0
- pywargame/cyberboard/archive.py +283 -0
- pywargame/cyberboard/base.py +63 -0
- pywargame/cyberboard/board.py +462 -0
- pywargame/cyberboard/cell.py +200 -0
- pywargame/cyberboard/collect.py +49 -0
- pywargame/cyberboard/collectgbx0pwd.py +30 -0
- pywargame/cyberboard/collectgbxext.py +30 -0
- pywargame/cyberboard/collectgsnexp.py +32 -0
- pywargame/cyberboard/collectgsnext.py +30 -0
- pywargame/cyberboard/draw.py +396 -0
- pywargame/cyberboard/exporter.py +1132 -0
- pywargame/cyberboard/extractor.py +240 -0
- pywargame/cyberboard/features.py +17 -0
- pywargame/cyberboard/gamebox.py +81 -0
- pywargame/cyberboard/gbxexp.py +76 -0
- pywargame/cyberboard/gbxext.py +64 -0
- pywargame/cyberboard/gsnexp.py +147 -0
- pywargame/cyberboard/gsnext.py +59 -0
- pywargame/cyberboard/head.py +111 -0
- pywargame/cyberboard/image.py +76 -0
- pywargame/cyberboard/main.py +47 -0
- pywargame/cyberboard/mark.py +102 -0
- pywargame/cyberboard/palette.py +36 -0
- pywargame/cyberboard/piece.py +169 -0
- pywargame/cyberboard/player.py +36 -0
- pywargame/cyberboard/scenario.py +115 -0
- pywargame/cyberboard/testgrid.py +156 -0
- pywargame/cyberboard/tile.py +121 -0
- pywargame/cyberboard/tray.py +68 -0
- pywargame/cyberboard/windows.py +41 -0
- pywargame/cyberboard/zeropwd.py +45 -0
- pywargame/cyberboard.py +2728 -0
- pywargame/gbx0pwd.py +2776 -0
- pywargame/gbxextract.py +2795 -0
- pywargame/gsnexport.py +16499 -0
- pywargame/gsnextract.py +2790 -0
- pywargame/latex/__init__.py +2 -0
- pywargame/latex/collect.py +34 -0
- pywargame/latex/latexexporter.py +4010 -0
- pywargame/latex/main.py +184 -0
- pywargame/vassal/__init__.py +66 -0
- pywargame/vassal/base.py +139 -0
- pywargame/vassal/board.py +243 -0
- pywargame/vassal/buildfile.py +60 -0
- pywargame/vassal/chart.py +79 -0
- pywargame/vassal/chessclock.py +197 -0
- pywargame/vassal/collect.py +98 -0
- pywargame/vassal/collectpatch.py +28 -0
- pywargame/vassal/command.py +21 -0
- pywargame/vassal/documentation.py +322 -0
- pywargame/vassal/dumpcollect.py +28 -0
- pywargame/vassal/dumpvsav.py +28 -0
- pywargame/vassal/element.py +439 -0
- pywargame/vassal/exporter.py +89 -0
- pywargame/vassal/extension.py +101 -0
- pywargame/vassal/folder.py +103 -0
- pywargame/vassal/game.py +940 -0
- pywargame/vassal/gameelements.py +1091 -0
- pywargame/vassal/globalkey.py +127 -0
- pywargame/vassal/globalproperty.py +433 -0
- pywargame/vassal/grid.py +573 -0
- pywargame/vassal/map.py +1061 -0
- pywargame/vassal/mapelements.py +1020 -0
- pywargame/vassal/merge.py +57 -0
- pywargame/vassal/merger.py +460 -0
- pywargame/vassal/moduledata.py +275 -0
- pywargame/vassal/mrgcollect.py +31 -0
- pywargame/vassal/patch.py +44 -0
- pywargame/vassal/patchcollect.py +28 -0
- pywargame/vassal/player.py +83 -0
- pywargame/vassal/save.py +495 -0
- pywargame/vassal/skel.py +380 -0
- pywargame/vassal/trait.py +224 -0
- pywargame/vassal/traits/__init__.py +36 -0
- pywargame/vassal/traits/area.py +50 -0
- pywargame/vassal/traits/basic.py +35 -0
- pywargame/vassal/traits/calculatedproperty.py +22 -0
- pywargame/vassal/traits/cargo.py +29 -0
- pywargame/vassal/traits/click.py +41 -0
- pywargame/vassal/traits/clone.py +28 -0
- pywargame/vassal/traits/delete.py +24 -0
- pywargame/vassal/traits/deselect.py +32 -0
- pywargame/vassal/traits/dynamicproperty.py +112 -0
- pywargame/vassal/traits/globalcommand.py +55 -0
- pywargame/vassal/traits/globalhotkey.py +26 -0
- pywargame/vassal/traits/globalproperty.py +54 -0
- pywargame/vassal/traits/hide.py +67 -0
- pywargame/vassal/traits/label.py +76 -0
- pywargame/vassal/traits/layer.py +105 -0
- pywargame/vassal/traits/mark.py +20 -0
- pywargame/vassal/traits/mask.py +85 -0
- pywargame/vassal/traits/mat.py +26 -0
- pywargame/vassal/traits/moved.py +35 -0
- pywargame/vassal/traits/movefixed.py +51 -0
- pywargame/vassal/traits/nonrect.py +95 -0
- pywargame/vassal/traits/nostack.py +55 -0
- pywargame/vassal/traits/place.py +104 -0
- pywargame/vassal/traits/prototype.py +20 -0
- pywargame/vassal/traits/report.py +34 -0
- pywargame/vassal/traits/restrictaccess.py +28 -0
- pywargame/vassal/traits/restrictcommand.py +32 -0
- pywargame/vassal/traits/return.py +40 -0
- pywargame/vassal/traits/rotate.py +62 -0
- pywargame/vassal/traits/sendto.py +59 -0
- pywargame/vassal/traits/sheet.py +129 -0
- pywargame/vassal/traits/skel.py +9 -0
- pywargame/vassal/traits/stack.py +28 -0
- pywargame/vassal/traits/submenu.py +27 -0
- pywargame/vassal/traits/trail.py +61 -0
- pywargame/vassal/traits/trigger.py +72 -0
- pywargame/vassal/turn.py +272 -0
- pywargame/vassal/upgrade.py +191 -0
- pywargame/vassal/vmod.py +323 -0
- pywargame/vassal/vsav.py +100 -0
- pywargame/vassal/widget.py +358 -0
- pywargame/vassal/withtraits.py +634 -0
- pywargame/vassal/xml.py +4 -0
- pywargame/vassal/zone.py +399 -0
- pywargame/vassal.py +12500 -0
- pywargame/vmodpatch.py +12548 -0
- pywargame/vsavdump.py +12533 -0
- pywargame/vslmerge.py +13015 -0
- pywargame/wgexport.py +16689 -0
- pywargame/ztexport.py +14351 -0
- pywargame/zuntzu/__init__.py +5 -0
- pywargame/zuntzu/base.py +82 -0
- pywargame/zuntzu/collect.py +38 -0
- pywargame/zuntzu/countersheet.py +250 -0
- pywargame/zuntzu/dicehand.py +48 -0
- pywargame/zuntzu/exporter.py +936 -0
- pywargame/zuntzu/gamebox.py +154 -0
- pywargame/zuntzu/map.py +36 -0
- pywargame/zuntzu/piece.py +37 -0
- pywargame/zuntzu/scenario.py +208 -0
- pywargame/zuntzu/ztexp.py +115 -0
- pywargame-0.3.1.dist-info/METADATA +353 -0
- pywargame-0.3.1.dist-info/RECORD +150 -0
- pywargame-0.3.1.dist-info/WHEEL +5 -0
- pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
- pywargame-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,936 @@
|
|
1
|
+
## BEGIN_IMPORTS
|
2
|
+
from common import VerboseGuard, Verbose
|
3
|
+
from vassal.buildfile import BuildFile
|
4
|
+
from vassal.documentation import Documentation
|
5
|
+
from vassal.traits import *
|
6
|
+
from vassal.base import *
|
7
|
+
from vassal.moduledata import ModuleData
|
8
|
+
from vassal.exporter import Exporter
|
9
|
+
from vassal.mapelements import LayerControl
|
10
|
+
from vassal.vsav import VSav
|
11
|
+
from base import ZTImage
|
12
|
+
## END_IMPORTS
|
13
|
+
|
14
|
+
|
15
|
+
class ZTExporter(Exporter):
|
16
|
+
def __init__(self,
|
17
|
+
gamebox,
|
18
|
+
version = None,
|
19
|
+
vassalVersion = '3.6',
|
20
|
+
allmaps = False,
|
21
|
+
mainMap = None,
|
22
|
+
nrotations = 6,
|
23
|
+
symbolic_dice = True):
|
24
|
+
self._gamebox = gamebox
|
25
|
+
self._version = '0.0.1' if version is None else version
|
26
|
+
self._vassalVersion = vassalVersion
|
27
|
+
self._allmaps = allmaps
|
28
|
+
self._main_map = mainMap
|
29
|
+
self._n_rotations = nrotations
|
30
|
+
self._symbolic_dice = symbolic_dice
|
31
|
+
|
32
|
+
# ----------------------------------------------------------------
|
33
|
+
def setup(self):
|
34
|
+
with VerboseGuard('Saving images'):
|
35
|
+
from pathlib import Path
|
36
|
+
|
37
|
+
|
38
|
+
# ----------------------------------------------------------------
|
39
|
+
def createModuleData(self):
|
40
|
+
with VerboseGuard('Creating module data'):
|
41
|
+
self._moduleData = ModuleData()
|
42
|
+
data = self._moduleData.addData()
|
43
|
+
data.addVersion (version='0.0.1')
|
44
|
+
data.addVASSALVersion(version=self._vassalVersion)
|
45
|
+
data.addName (name =self._gamebox._name)
|
46
|
+
data.addDescription (description=self._gamebox._desc)
|
47
|
+
data.addDateSaved ()
|
48
|
+
|
49
|
+
# ----------------------------------------------------------------
|
50
|
+
def createBuildFile(self,
|
51
|
+
ignores = ['all',
|
52
|
+
'common',
|
53
|
+
'marker',
|
54
|
+
'markers',
|
55
|
+
' ']):
|
56
|
+
with VerboseGuard(f'Creating build file') as v:
|
57
|
+
self._build = BuildFile() # 'buildFile.xml')
|
58
|
+
self._game = self._build.addGame(
|
59
|
+
name = self._gamebox._name,
|
60
|
+
version = self._version,
|
61
|
+
description = self._gamebox._desc)
|
62
|
+
|
63
|
+
v(f'Adding description')
|
64
|
+
self.addDocumentation()
|
65
|
+
|
66
|
+
v(f'Adding command encoder')
|
67
|
+
self._game.addBasicCommandEncoder()
|
68
|
+
|
69
|
+
v(f'Adding Global options')
|
70
|
+
go = self._game.addGlobalOptions(
|
71
|
+
autoReport = 'Use Preferences Setting',
|
72
|
+
centerOnMove = 'Use Preferences Setting',
|
73
|
+
nonOwnerUnmaskable = 'Use Preferences Setting',
|
74
|
+
playerIdFormat = '$playerName$')
|
75
|
+
go.addOption(name='undoHotKey',value=key('Z'))
|
76
|
+
|
77
|
+
v(f'Adding dice hands')
|
78
|
+
self.addDiceHands()
|
79
|
+
|
80
|
+
v(f'Adding prototypes')
|
81
|
+
self.addPrototypes()
|
82
|
+
|
83
|
+
v(f'Adding pieces')
|
84
|
+
self.addPieces()
|
85
|
+
|
86
|
+
v(f'Fixing up map locations')
|
87
|
+
self.fixMaps()
|
88
|
+
|
89
|
+
v(f'Adding maps')
|
90
|
+
self.addMaps()
|
91
|
+
|
92
|
+
v(f'Adding counter sheets')
|
93
|
+
self.addCounterSheets()
|
94
|
+
|
95
|
+
v(f'Adding scenarios')
|
96
|
+
self.addScenarios()
|
97
|
+
|
98
|
+
def fixMaps(self):
|
99
|
+
# Scale all scenarios to target resolution before anything else
|
100
|
+
for sc in self._gamebox._scenarios:
|
101
|
+
sc.scale(ZTImage.target_dpi / 600)
|
102
|
+
|
103
|
+
for m in self._gamebox._maps:
|
104
|
+
self.fixMap(m)
|
105
|
+
|
106
|
+
def fixMap(self,map):
|
107
|
+
from wand.image import Image, Color
|
108
|
+
from wand.drawing import Drawing
|
109
|
+
|
110
|
+
with VerboseGuard(f'Fixing up map {map._name}') as v:
|
111
|
+
tgt = [s for s in self._gamebox._scenarios if s.on_map(map._name)]
|
112
|
+
if not tgt:
|
113
|
+
v(f'No target scenario found for map')
|
114
|
+
return
|
115
|
+
|
116
|
+
# if len(tgt) > 1:
|
117
|
+
# v(f'More than 1 target scenario found for map')
|
118
|
+
# return
|
119
|
+
dxs = []
|
120
|
+
dys = []
|
121
|
+
ws = []
|
122
|
+
hs = []
|
123
|
+
for sc in tgt:
|
124
|
+
v(f'Target scenario for map "{map._name}" is "{sc._name}"')
|
125
|
+
v(f'Map "{map._name}" image is '
|
126
|
+
f'{map._image._image.width}x{map._image._image.height}')
|
127
|
+
|
128
|
+
ly = sc.map_layout(map._name)[0]
|
129
|
+
v(f'Layout at {ly.left},{ly.top} {ly.lrx},{ly.lry}')
|
130
|
+
|
131
|
+
bb = sc.calc_bounding_box(map._name if self._allmaps
|
132
|
+
else None)
|
133
|
+
v(f'Bounding box for map: {bb}')
|
134
|
+
|
135
|
+
ulx = min([s._x - self._gamebox._piece_map[p]._front.width/2
|
136
|
+
for s in ly._stacks for p in s._ids]+[0])
|
137
|
+
lrx = max([s._x + self._gamebox._piece_map[p]._front.width/2
|
138
|
+
for s in ly._stacks for p in s._ids]+
|
139
|
+
[map._image._image.width])
|
140
|
+
uly = min([s._y - self._gamebox._piece_map[p]._front.height/2
|
141
|
+
for s in ly._stacks for p in s._ids]+[0])
|
142
|
+
lry = max([s._y + self._gamebox._piece_map[p]._front.height/2
|
143
|
+
for s in ly._stacks for p in s._ids]+
|
144
|
+
[map._image._image.height])
|
145
|
+
v(f'Bounding box for map: {bb} {ulx},{uly},{lrx},{lry}')
|
146
|
+
|
147
|
+
# Translate the layout
|
148
|
+
mar = 10
|
149
|
+
sdx = -int(ulx-.5)+mar
|
150
|
+
sdy = -int(uly-.5)+mar
|
151
|
+
sw = int(lrx+sdx+.5)+2*mar
|
152
|
+
sh = int(lry+sdy+.5)+2*mar
|
153
|
+
dxs.append(sdx)
|
154
|
+
dys.append(sdy)
|
155
|
+
ws .append(sw)
|
156
|
+
hs .append(sh)
|
157
|
+
v(f'{sc._name}: dx={sdx} dy={sdy} w={sw} h={sh}')
|
158
|
+
|
159
|
+
# Find the smallest translation, and largest size
|
160
|
+
dx = max(dxs)
|
161
|
+
dy = max(dys)
|
162
|
+
w = max(ws)
|
163
|
+
h = max(hs)
|
164
|
+
v(f'All scenarios translated by {dx},{dy}')
|
165
|
+
for sc in tgt:
|
166
|
+
sc.translate(dx,dy)
|
167
|
+
# v(f'{sc}')
|
168
|
+
|
169
|
+
v(f'Background image {w}x{h}')
|
170
|
+
bg = Image(width=w,height=h,
|
171
|
+
colorspace='truecolor',
|
172
|
+
background=Color('#404040'))
|
173
|
+
bg.format = 'png'
|
174
|
+
#map._image._image.save(filename=map.filename)
|
175
|
+
|
176
|
+
v(f'Compose over at {dx},{dy}')
|
177
|
+
bg.composite(map._image._image,left=dx,top=dy)
|
178
|
+
|
179
|
+
# Replace with new image
|
180
|
+
#bg.save(filename='bg_'+map.filename)
|
181
|
+
map._image._image = bg #.save(filename=map.filename)
|
182
|
+
|
183
|
+
# ----------------------------------------------------------------
|
184
|
+
def saveImages(self,vmod):
|
185
|
+
pass
|
186
|
+
|
187
|
+
# ----------------------------------------------------------------
|
188
|
+
def savePNG(self,vmod,filename,img):
|
189
|
+
#with VerboseGuard(f'Saving PNG {filename}'):
|
190
|
+
from io import BytesIO
|
191
|
+
|
192
|
+
stream = BytesIO()
|
193
|
+
img.save(stream)
|
194
|
+
vmod.addFile('images/'+filename,stream.getvalue())
|
195
|
+
|
196
|
+
# ----------------------------------------------------------------
|
197
|
+
def addDocumentation(self):
|
198
|
+
'''Add documentation to the module. This includes rules,
|
199
|
+
key-bindings, and about elements.
|
200
|
+
'''
|
201
|
+
with VerboseGuard('Adding documentation') as v:
|
202
|
+
doc = self._game.addDocumentation()
|
203
|
+
|
204
|
+
desc = f'''<html><body>
|
205
|
+
<h1>Module</h1>
|
206
|
+
<p>
|
207
|
+
This module was created from a ZunTzu gamebox
|
208
|
+
{self._gamebox._name} by the Python script
|
209
|
+
<code>ztexport.py</code> available from
|
210
|
+
</p>
|
211
|
+
<p>
|
212
|
+
Ctrl-click to select terrain pieces.
|
213
|
+
</p>
|
214
|
+
<pre>
|
215
|
+
https://gitlab.com/wargames_tex/pywargames
|
216
|
+
</pre>
|
217
|
+
<h1>Game</h1>
|
218
|
+
<p>{self._gamebox._desc}</p>
|
219
|
+
<h1>Scenarios</h1>
|
220
|
+
<ul>
|
221
|
+
{'\n'.join(['<li>'+s._name+': '+s._desc+'</li>'
|
222
|
+
for s in self._gamebox._scenarios])}
|
223
|
+
</ul>
|
224
|
+
<h1>Copyright</h1>
|
225
|
+
<p>© {self._gamebox._copy}</p>
|
226
|
+
</body></html>;'''
|
227
|
+
|
228
|
+
self._vmod.addFile('help/description.html',desc)
|
229
|
+
doc.addHelpFile(title='Description',
|
230
|
+
fileName='help/description.html')
|
231
|
+
|
232
|
+
if self._gamebox._splash:
|
233
|
+
v(f'Adding splash screen')
|
234
|
+
fname = 'splash.'+self._gamebox._splash.format.lower()
|
235
|
+
self.savePNG(self._vmod,fname,self._gamebox._splash)
|
236
|
+
self._gamebox._splash = None
|
237
|
+
doc.addAboutScreen(title=self._gamebox._name,
|
238
|
+
fileName=fname)
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
# ----------------------------------------------------------------
|
243
|
+
def addDiceHands(self):
|
244
|
+
with VerboseGuard(f'Adding dice hands: '
|
245
|
+
f'{self._gamebox._dice_hands}') as v:
|
246
|
+
seen = set()
|
247
|
+
for no,hand in enumerate(self._gamebox._dice_hands):
|
248
|
+
name = f'{len(hand._dice)}d{hand._type}'
|
249
|
+
if name in seen:
|
250
|
+
name += f'_{no}'
|
251
|
+
|
252
|
+
seen.add(name)
|
253
|
+
# v(f'Prepare to add dice {name}')
|
254
|
+
self.addDiceHand(name,hand)
|
255
|
+
|
256
|
+
# ----------------------------------------------------------------
|
257
|
+
def addDiceHand(self,name,hand):
|
258
|
+
with VerboseGuard(f'Adding dice hand {name} '
|
259
|
+
f'{len(hand._dice)}d{hand._type}') as v:
|
260
|
+
|
261
|
+
cnt = len(hand._dice)
|
262
|
+
sid = hand._type
|
263
|
+
|
264
|
+
rep = '$name$ = '
|
265
|
+
if cnt > 1:
|
266
|
+
rep += ' + '.join([f'$result{i+1}$' for i in range(cnt)])+' = '
|
267
|
+
rep += '$result$'
|
268
|
+
ky = {4: '4',
|
269
|
+
6: '6',
|
270
|
+
8: '8',
|
271
|
+
10: '0',
|
272
|
+
12: '2',
|
273
|
+
20: '3'}[hand._type]
|
274
|
+
#v(f'Dice defined: {hand._dice}')
|
275
|
+
if not self._symbolic_dice:
|
276
|
+
self._game.addDiceButton(name = name,
|
277
|
+
hotkey = key(ky,ALT),
|
278
|
+
tooltip = f'Roll {cnt}d{sid}',
|
279
|
+
text = name,
|
280
|
+
nDice = cnt,
|
281
|
+
nSides = sid,
|
282
|
+
reportFormat = rep,
|
283
|
+
reportTotal = True)
|
284
|
+
return
|
285
|
+
|
286
|
+
|
287
|
+
# Could be used instead if we knew we had images
|
288
|
+
#
|
289
|
+
v(f'Adding symbolic dice')
|
290
|
+
|
291
|
+
# Make icon. A nicer way would be to combine the images
|
292
|
+
# created below and then scale to the appropriate height.
|
293
|
+
#
|
294
|
+
# For another time.
|
295
|
+
# drawer = DiceDrawer(hand._type,20,20,
|
296
|
+
# fg='white',
|
297
|
+
# bg='black')
|
298
|
+
# image = drawer.draw(hand._type//2)
|
299
|
+
# image.format = 'png'
|
300
|
+
# fn = f'{name}-logo.png'
|
301
|
+
# self.savePNG(self._vmod,fn,image)
|
302
|
+
|
303
|
+
rep = '{name+": "+'
|
304
|
+
def img(i,d):
|
305
|
+
return (f'"<img src=\'{name}{i}_"+result{i}+".png\' '
|
306
|
+
f'width=\'24\' height=\'24\'>"')
|
307
|
+
def txt(i,d):
|
308
|
+
bg = d['pips']
|
309
|
+
fg = d['color']
|
310
|
+
bg = 0xFFFFFF
|
311
|
+
if fg == 0xFFFFFF:
|
312
|
+
fg = 0
|
313
|
+
|
314
|
+
return (f'"<span style=\'color:#{fg:06x}; '
|
315
|
+
f'background-color:#{bg:06x}; '
|
316
|
+
f'font-weight:bold; '
|
317
|
+
f'padding: 5px;\'>"+'
|
318
|
+
f'result{i}+"</span>"')
|
319
|
+
|
320
|
+
rep += '+'.join([img(i+1,d)
|
321
|
+
for i,d in enumerate(hand._dice)])+\
|
322
|
+
'+" = "+'
|
323
|
+
rep += '+" + "+'.join([txt(i+1,d)
|
324
|
+
for i,d in enumerate(hand._dice)])
|
325
|
+
if cnt > 1:
|
326
|
+
rep += '+" = <b>"+numericalTotal+"</b>"}'
|
327
|
+
else:
|
328
|
+
rep += '}'
|
329
|
+
|
330
|
+
|
331
|
+
diceW = 80
|
332
|
+
diceH = 80
|
333
|
+
iconfn = f'{name}-icon.png'
|
334
|
+
but = self._game.addSymbolicDice(
|
335
|
+
name = name,
|
336
|
+
icon = iconfn,
|
337
|
+
hotkey = key(str(hand._type),ALT),
|
338
|
+
tooltip = f'Roll {cnt}d{sid}',
|
339
|
+
text = name,
|
340
|
+
doReport = True,
|
341
|
+
resultWindow = True,
|
342
|
+
format = rep,
|
343
|
+
windowX = (diceW+10)*len(hand._dice),
|
344
|
+
windowY = diceH+10)
|
345
|
+
v(f'Report format: {rep}')
|
346
|
+
|
347
|
+
from wand.image import Image as WImage
|
348
|
+
from wand.color import Color
|
349
|
+
from random import randint
|
350
|
+
|
351
|
+
icon = WImage(format='png',width=diceW,height=diceH)
|
352
|
+
icon.alpha_channel = True
|
353
|
+
icon.background_color = Color('transparent')
|
354
|
+
|
355
|
+
for i, dice in enumerate(hand._dice):
|
356
|
+
# v(f'- {dice}')
|
357
|
+
dien = f'{name}{i+1}'
|
358
|
+
die = but.addDie(name=dien)
|
359
|
+
drawer = DiceDrawer(hand._type,80,80,
|
360
|
+
fg=dice['pips'],
|
361
|
+
bg=dice['color'])
|
362
|
+
|
363
|
+
imgs = []
|
364
|
+
for num in range(1,hand._type+1):
|
365
|
+
if hand._type == 10 and num == 10:
|
366
|
+
num = 0
|
367
|
+
image = drawer.draw(num)
|
368
|
+
image.format = 'png'
|
369
|
+
imgs.append(image)
|
370
|
+
fn = f'{dien}_{num}.png'
|
371
|
+
self.savePNG(self._vmod,fn,image)
|
372
|
+
|
373
|
+
die.addFace(icon = fn,
|
374
|
+
text = f'{num}',
|
375
|
+
value = num)
|
376
|
+
|
377
|
+
icon.sequence.append(imgs[randint(0,hand._type-1)])
|
378
|
+
|
379
|
+
icon.smush(stacked=False,offset=0)
|
380
|
+
sc = 20 / diceH
|
381
|
+
icon.resize(int(sc * icon.width),int(sc * icon.height))
|
382
|
+
icon.format = 'png'
|
383
|
+
self.savePNG(self._vmod,iconfn,icon)
|
384
|
+
|
385
|
+
|
386
|
+
|
387
|
+
# ----------------------------------------------------------------
|
388
|
+
def addPrototypes(self):
|
389
|
+
with VerboseGuard(f'Creating prototypes'):
|
390
|
+
protos = self._game.addPrototypes()
|
391
|
+
|
392
|
+
traits = [TrailTrait(),
|
393
|
+
RotateTrait(nangles = self._n_rotations),
|
394
|
+
DeleteTrait(),
|
395
|
+
BasicTrait()]
|
396
|
+
protos.addPrototype(name = 'Basic prototype',
|
397
|
+
description = 'Prototype for most',
|
398
|
+
traits = traits)
|
399
|
+
|
400
|
+
|
401
|
+
traits = [PrototypeTrait(name = 'Basic prototype'),
|
402
|
+
NoStackTrait(
|
403
|
+
select = NoStackTrait.CTRL_SELECT,
|
404
|
+
bandSelect = NoStackTrait.NEVER_BAND_SELECT,
|
405
|
+
move = NoStackTrait.SELECT_MOVE,
|
406
|
+
canStack = False,
|
407
|
+
description = 'Terrain does not stack'),
|
408
|
+
MarkTrait(name = 'PieceLayer',
|
409
|
+
value = 'Terrains'),
|
410
|
+
BasicTrait()]
|
411
|
+
protos.addPrototype(name = 'Terrain prototype',
|
412
|
+
description = 'Prototype for terrain',
|
413
|
+
traits = traits)
|
414
|
+
|
415
|
+
traits = [PrototypeTrait(name = 'Basic prototype'),
|
416
|
+
MarkTrait(name = 'PieceLayer',
|
417
|
+
value = 'Pieces'),
|
418
|
+
BasicTrait()]
|
419
|
+
|
420
|
+
protos.addPrototype(name = 'Piece prototype',
|
421
|
+
description = 'Prototype for pieces',
|
422
|
+
traits = traits)
|
423
|
+
|
424
|
+
traits = [PrototypeTrait(name = 'Basic prototype'),
|
425
|
+
MarkTrait(name = 'PieceLayer',
|
426
|
+
value = 'Cards'),
|
427
|
+
BasicTrait()]
|
428
|
+
|
429
|
+
protos.addPrototype(name = 'Card prototype',
|
430
|
+
description = 'Prototype for cards',
|
431
|
+
traits = traits)
|
432
|
+
|
433
|
+
# ----------------------------------------------------------------
|
434
|
+
def addPieces(self):
|
435
|
+
with VerboseGuard(f'Adding pieces') as v:
|
436
|
+
window = self._game.addPieceWindow(name = 'Pieces',
|
437
|
+
hotkey = key('C',ALT))
|
438
|
+
|
439
|
+
combo = window.addCombo(entryName = 'Pieces')
|
440
|
+
|
441
|
+
self._piece_slots = {}
|
442
|
+
|
443
|
+
for sheet in self._gamebox._counter_sheets:
|
444
|
+
v(sheet._name)
|
445
|
+
if len(sheet._piece) <= 0 and len(sheet._card) <= 0:
|
446
|
+
continue
|
447
|
+
plist = self.addPieceContainer(combo, sheet._name)
|
448
|
+
self.addSheetPieces(plist,sheet._piece,sheet._card)
|
449
|
+
|
450
|
+
# ----------------------------------------------------------------
|
451
|
+
def addSheetPieces(self,container,pieces,cards):
|
452
|
+
with VerboseGuard(f'Adding pieces to {container["entryName"]} '+
|
453
|
+
f'{len(pieces)} pieces, {len(cards)} cards'):
|
454
|
+
for piece in pieces+cards:
|
455
|
+
boardName = container['entryName']
|
456
|
+
board = self._game.getBoards(asdict=True).get(boardName,None)
|
457
|
+
mapName = board.getMap()['mapName'] if board else ''
|
458
|
+
no = self._gamebox._piece_id[piece]
|
459
|
+
traits = [
|
460
|
+
PrototypeTrait(
|
461
|
+
name = (('Terrain' if piece._terrain else
|
462
|
+
('Card' if piece._card else 'Piece'))+
|
463
|
+
' prototype')),
|
464
|
+
SendtoTrait(
|
465
|
+
mapName = boardName,
|
466
|
+
boardName = mapName,
|
467
|
+
name = f'Return to {boardName}',
|
468
|
+
key = key('R',CTRL_SHIFT),
|
469
|
+
restoreName = '',
|
470
|
+
restoreKey = '',
|
471
|
+
x = piece._x,
|
472
|
+
y = piece._y,
|
473
|
+
description = 'Return to where it came from',
|
474
|
+
destination = SendtoTrait.LOCATION)
|
475
|
+
]
|
476
|
+
name = f'{no:06d}'
|
477
|
+
front_name = f'{name}_front.{piece._front.format.lower()}'
|
478
|
+
back_name = (f'{name}_back.{piece._back.format.lower()}'
|
479
|
+
if piece.two_sides else None)
|
480
|
+
if back_name is not None:
|
481
|
+
traits.extend([
|
482
|
+
LayerTrait(
|
483
|
+
images = [front_name,back_name],
|
484
|
+
newNames = ['','+ reversed'],
|
485
|
+
activateName = '',
|
486
|
+
decreaseName = '',
|
487
|
+
increaseName = 'Flip',
|
488
|
+
increaseKey = key('F'),
|
489
|
+
decreaseKey = '',
|
490
|
+
name = 'Step'),
|
491
|
+
ReportTrait(key('F'))])
|
492
|
+
gpid = self._game.nextPieceSlotId()
|
493
|
+
size = piece.size
|
494
|
+
traits.append(
|
495
|
+
BasicTrait(name = name,
|
496
|
+
filename = front_name,
|
497
|
+
gpid = gpid))
|
498
|
+
|
499
|
+
self._piece_slots[piece] = container.addPieceSlot(
|
500
|
+
entryName = name,
|
501
|
+
gpid = gpid,
|
502
|
+
traits = traits,
|
503
|
+
width = piece.width,
|
504
|
+
height = piece.height)
|
505
|
+
|
506
|
+
for img,fname in zip([piece._front,piece._back],
|
507
|
+
[front_name,back_name]):
|
508
|
+
if img is None:
|
509
|
+
continue
|
510
|
+
fnameext = fname
|
511
|
+
self.savePNG(self._vmod,fnameext,img)
|
512
|
+
#piece._front = None
|
513
|
+
#piece._back = None
|
514
|
+
|
515
|
+
# self.saveImages(self,vmod)
|
516
|
+
# ----------------------------------------------------------------
|
517
|
+
def addPieceContainer(self,container,name):
|
518
|
+
#panel = container.addPanel(entryName = name,
|
519
|
+
# fixed = False,
|
520
|
+
# vert = True)
|
521
|
+
#return panel
|
522
|
+
panel = container.addPanel(entryName = name, fixed = False)
|
523
|
+
plist = panel.addList(entryName = f'{name}',
|
524
|
+
width = 300,
|
525
|
+
height = 300,
|
526
|
+
divider = 150)
|
527
|
+
return plist
|
528
|
+
|
529
|
+
# ----------------------------------------------------------------
|
530
|
+
def addMapLayers(self,map):
|
531
|
+
layerDesc = {'Terrains': {'t': 'Terrain', 'i': ''},
|
532
|
+
'Cards': {'t': 'Cards', 'i': ''},
|
533
|
+
'Pieces': {'t': 'Pieces', 'i': ''}}
|
534
|
+
layers = map.addLayers(property = 'PieceLayer',
|
535
|
+
layerOrder = layerDesc)
|
536
|
+
for layerTitle, layerData in layerDesc.items():
|
537
|
+
layers.addControl(name = layerTitle,
|
538
|
+
tooltip = f'Toggle display of {layerTitle}',
|
539
|
+
text = f'Toggle {layerData["t"]}',
|
540
|
+
icon = layerData['i'],
|
541
|
+
layers = [layerTitle])
|
542
|
+
layers.addControl(name = 'Show all',
|
543
|
+
tooltip = 'Show all',
|
544
|
+
text = 'Show all',
|
545
|
+
command = LayerControl.ENABLE,
|
546
|
+
layers = list(layerDesc.keys()))
|
547
|
+
map.addMenu(description = 'Toggle layers',
|
548
|
+
text = '',
|
549
|
+
tooltip = 'Toggle display of layers',
|
550
|
+
icon = '/images/inventory.gif',
|
551
|
+
menuItems = ([f'Toggle {ln["t"]}'
|
552
|
+
for ln in layerDesc.values()]
|
553
|
+
+['Show all']))
|
554
|
+
|
555
|
+
# ----------------------------------------------------------------
|
556
|
+
def addMapDefaults(self,map):
|
557
|
+
s = ZTImage.target_dpi / 150
|
558
|
+
z = [0.05,0.1,0.2,0.25,0.333,0.4,0.5,
|
559
|
+
0.555,0.625,0.75,1.0,1.25,1.6,1.8,2]
|
560
|
+
if s > 4: z = z[:-3]
|
561
|
+
if s < 2: z = z[2:]
|
562
|
+
z0 = 6
|
563
|
+
map.addGlobalMap()
|
564
|
+
# Basics
|
565
|
+
map.addStackMetrics(
|
566
|
+
exSepX = int(s * 12 + .5),
|
567
|
+
exSepY = int(s * 16 + .5),
|
568
|
+
unexSepX = int(s * 8 + .5),
|
569
|
+
unexSepY = int(s * 10 + .5)
|
570
|
+
)
|
571
|
+
map.addImageSaver()
|
572
|
+
map.addTextSaver()
|
573
|
+
map.addForwardToChatter()
|
574
|
+
map.addMenuDisplayer()
|
575
|
+
map.addMapCenterer()
|
576
|
+
map.addStackExpander()
|
577
|
+
map.addPieceMover()
|
578
|
+
map.addKeyBufferer()
|
579
|
+
map.addForwardKeys()
|
580
|
+
map.addSelectionHighlighters()
|
581
|
+
map.addHighlightLastMoved()
|
582
|
+
map.addScroller()
|
583
|
+
map.addZoomer(zoomLevels = z, zoomStart = z0)
|
584
|
+
|
585
|
+
# ----------------------------------------------------------------
|
586
|
+
def addMaps(self):
|
587
|
+
with VerboseGuard(f'Adding maps') as v:
|
588
|
+
if not self._allmaps:
|
589
|
+
self.addMap(*self._gamebox._maps)
|
590
|
+
return
|
591
|
+
|
592
|
+
mapNames = [self.addMap(m) for m in self._gamebox._maps]
|
593
|
+
|
594
|
+
filtered = [n for n in mapNames if n != '' and n != self._main_map]
|
595
|
+
v(f'Filtered set of maps: {filtered}')
|
596
|
+
if len(filtered) > 0:
|
597
|
+
self._game.addMenu(description = 'Maps',
|
598
|
+
text = 'Maps',
|
599
|
+
tooltip = 'Show or hide maps',
|
600
|
+
icon = '/images/map.gif',
|
601
|
+
menuItems = filtered)
|
602
|
+
|
603
|
+
|
604
|
+
# ----------------------------------------------------------------
|
605
|
+
def addMap(self,*maps):
|
606
|
+
with VerboseGuard(f'Adding maps') as v:
|
607
|
+
isMain = False
|
608
|
+
mapName = maps[0]._name
|
609
|
+
if not self._main_map or self._main_map == mapName:
|
610
|
+
self._main_map = mapName
|
611
|
+
isMain = True
|
612
|
+
|
613
|
+
map = self._game.addMap(mapName = mapName,
|
614
|
+
buttonName = '' if isMain else mapName,
|
615
|
+
hotkey = '' if isMain else key('M',ALT),
|
616
|
+
launch = not isMain
|
617
|
+
)
|
618
|
+
self.addMapDefaults(map)
|
619
|
+
map.addLOS()
|
620
|
+
map.addCounterDetailViewer()
|
621
|
+
self.addMapLayers(map)
|
622
|
+
|
623
|
+
picker = map.addBoardPicker()
|
624
|
+
self._picker = picker
|
625
|
+
for m in maps:
|
626
|
+
imgname = m.filename
|
627
|
+
size = m.size
|
628
|
+
brd = picker.addBoard(name = m._name,
|
629
|
+
image = imgname,
|
630
|
+
width = size[0],
|
631
|
+
height = size[1])
|
632
|
+
zoned = brd.addZonedGrid()
|
633
|
+
zoned.addHighlighter()
|
634
|
+
zone = zoned.addZone(name = 'Full',
|
635
|
+
useParentGrid = False,
|
636
|
+
path=(f'{0},{0};' +
|
637
|
+
f'{size[0]},{0};' +
|
638
|
+
f'{size[0]},{size[1]};' +
|
639
|
+
f'{0},{size[1]}'))
|
640
|
+
|
641
|
+
self.savePNG(self._vmod,imgname,m._image._image)
|
642
|
+
m._image._image = None
|
643
|
+
|
644
|
+
return mapName
|
645
|
+
|
646
|
+
# ----------------------------------------------------------------
|
647
|
+
def addCounterSheets(self):
|
648
|
+
with VerboseGuard(f'Adding Charts') as v:
|
649
|
+
window = \
|
650
|
+
self._game.addChartWindow(name='Charts',
|
651
|
+
hotkey = key('A',ALT),
|
652
|
+
description = 'Charts',
|
653
|
+
text = '',
|
654
|
+
icon = '/images/chart.gif',
|
655
|
+
tooltip = 'Show/hide Charts')
|
656
|
+
tabs = window.addTabs(entryName='Charts')
|
657
|
+
|
658
|
+
for sheet in self._gamebox._counter_sheets:
|
659
|
+
widget = tabs.addMapWidget(entryName=sheet._name)
|
660
|
+
self.addCounterSheet(widget,sheet)
|
661
|
+
|
662
|
+
# ----------------------------------------------------------------
|
663
|
+
def addCounterSheet(self,widget,sheet):
|
664
|
+
with VerboseGuard(f'Adding counter sheet {sheet._name}') as v:
|
665
|
+
map = widget.addWidgetMap(mapName = sheet._name,
|
666
|
+
markMoved = 'Never',
|
667
|
+
hotkey = '')
|
668
|
+
self.addMapDefaults(map)
|
669
|
+
self.addMapLayers(map)
|
670
|
+
|
671
|
+
size = sheet.size
|
672
|
+
picker = map.addPicker()
|
673
|
+
brd = picker.addBoard(name = sheet._name,
|
674
|
+
image = sheet.filename)
|
675
|
+
zoned = brd.addZonedGrid()
|
676
|
+
zoned.addHighlighter()
|
677
|
+
zone = zoned.addZone(name = 'Full',
|
678
|
+
useParentGrid = False,
|
679
|
+
path=(f'{0},{0};' +
|
680
|
+
f'{size[0]},{0};' +
|
681
|
+
f'{size[0]},{size[1]};' +
|
682
|
+
f'{0},{size[1]}'))
|
683
|
+
|
684
|
+
self.savePNG(self._vmod,sheet.filename,sheet._front._image)
|
685
|
+
self.addAtStart(map,sheet._piece+sheet._card)
|
686
|
+
sheet._front._image = None
|
687
|
+
|
688
|
+
# ----------------------------------------------------------------
|
689
|
+
def addAtStart(self,map,pieces):
|
690
|
+
with VerboseGuard(f'Adding at-start to {map["mapName"]} '+
|
691
|
+
f'{len(pieces)} pieces and cards') as g:
|
692
|
+
toAdd = {}
|
693
|
+
|
694
|
+
for piece in pieces:
|
695
|
+
x = piece._x
|
696
|
+
y = piece._y
|
697
|
+
grid = (x,y)
|
698
|
+
slot = self._piece_slots[piece]
|
699
|
+
#g(f'Adding piece at {x},{y}')
|
700
|
+
if grid not in toAdd:
|
701
|
+
toAdd[grid] = {'center': (x,y),
|
702
|
+
'pieces': [] }
|
703
|
+
toAdd[grid]['pieces'].append(slot)
|
704
|
+
|
705
|
+
for grid, dpieces in toAdd.items():
|
706
|
+
center = dpieces['center']
|
707
|
+
name = f'{grid[0]:06d}{grid[1]:06d}'
|
708
|
+
atstart = map.addAtStart(name = name,
|
709
|
+
useGridLocation = False,
|
710
|
+
owningBoard = map["mapName"],
|
711
|
+
x = center[0],
|
712
|
+
y = center[1])
|
713
|
+
atstart.addPieces(*dpieces['pieces'])
|
714
|
+
|
715
|
+
# ----------------------------------------------------------------
|
716
|
+
def addDeadMap(self,sides):
|
717
|
+
pass
|
718
|
+
|
719
|
+
# ----------------------------------------------------------------
|
720
|
+
def addScenarios(self):
|
721
|
+
menu = self._game.addPredefinedSetup(name = 'Setups',
|
722
|
+
isMenu = True)
|
723
|
+
|
724
|
+
menu.addPredefinedSetup(name = 'No setup',
|
725
|
+
file = '',
|
726
|
+
useFile = False,
|
727
|
+
description = 'No initial setup')
|
728
|
+
|
729
|
+
for sc in self._gamebox._scenarios:
|
730
|
+
self.addScenario(sc,menu)
|
731
|
+
|
732
|
+
# ----------------------------------------------------------------
|
733
|
+
def remapIds(self,scenario):
|
734
|
+
from pprint import pprint
|
735
|
+
with VerboseGuard(f'Remapping piece ID for scenario '
|
736
|
+
f'"{scenario._name}"') as v:
|
737
|
+
tmp = []
|
738
|
+
for layout in scenario._layouts:
|
739
|
+
cs = self._gamebox._sheets_map.get(layout._name,None)
|
740
|
+
if not cs:
|
741
|
+
v(f'Missing counter sheet {layout._name} - maybe map')
|
742
|
+
continue
|
743
|
+
|
744
|
+
tmp.extend(cs._piece+cs._card)
|
745
|
+
|
746
|
+
|
747
|
+
# Map from ID to piece - only used counter sheets in this scenario
|
748
|
+
# pprint(tmp)
|
749
|
+
piece_map = {i: p for i,p in enumerate(tmp)}
|
750
|
+
# print(piece_map)
|
751
|
+
# piece_id = {v: k for k,v in piece_map.items()}
|
752
|
+
|
753
|
+
return piece_map
|
754
|
+
|
755
|
+
# ----------------------------------------------------------------
|
756
|
+
def changeSlot(self,slot,flipped,angle,changed):
|
757
|
+
if angle == 0 and not flipped:
|
758
|
+
return
|
759
|
+
|
760
|
+
# Valid angles are
|
761
|
+
#
|
762
|
+
# for i in range(nangles):
|
763
|
+
# angle[i] = - i * 360 // nangles
|
764
|
+
#
|
765
|
+
# validAngles = [-i * 360 // nangles for i in range(nangles)]
|
766
|
+
changed[slot['entryName']] = {'angle':angle,
|
767
|
+
'step': 2 if flipped else 1}
|
768
|
+
|
769
|
+
# ----------------------------------------------------------------
|
770
|
+
def updateSlot(self,name,traits,changed):
|
771
|
+
changes = changed.get(name,None)
|
772
|
+
if not changes:
|
773
|
+
return
|
774
|
+
|
775
|
+
with VerboseGuard(f'Updating rotation and step state of '
|
776
|
+
f'{name} to {changes}') as v:
|
777
|
+
rot = Trait.findTrait(traits, RotateTrait.ID)
|
778
|
+
lyr = Trait.findTrait(traits, LayerTrait.ID,
|
779
|
+
key='name', value='Step')
|
780
|
+
if not rot and not lyr:
|
781
|
+
v(f'No traits to modify!')
|
782
|
+
return
|
783
|
+
|
784
|
+
angle = changes['angle']
|
785
|
+
step = changes['step']
|
786
|
+
nangle = int(rot['nangles'])
|
787
|
+
fangle = angle
|
788
|
+
if nangle != 1:
|
789
|
+
# Funny calculation in VASSAL
|
790
|
+
a = ((angle % 360) - 360) % 360
|
791
|
+
e = (-a / 360) * nangle
|
792
|
+
fangle = int(round(e) % nangle)
|
793
|
+
# v(f'nangle={nangle} fangle={fangle}')
|
794
|
+
if rot: rot.setState(angle = fangle)
|
795
|
+
if lyr: lyr.setState(level = step)
|
796
|
+
|
797
|
+
|
798
|
+
# ----------------------------------------------------------------
|
799
|
+
def restoreSlot(self,slot,step,angle):
|
800
|
+
# traits = slot.getTraits()
|
801
|
+
# rot = Trait.findTrait(traits, RotateTrait.ID)
|
802
|
+
# lyr = Trait.findTrait(traits, LayerTrait.ID,
|
803
|
+
# key='name', value='Step')
|
804
|
+
#
|
805
|
+
# rot.setState(angle=rot)
|
806
|
+
# lyr.setState(level=step)
|
807
|
+
#
|
808
|
+
# slot.setTraits(*traits)
|
809
|
+
pass
|
810
|
+
|
811
|
+
# ----------------------------------------------------------------
|
812
|
+
def addScenario(self,scenario,menu):
|
813
|
+
from sys import stderr
|
814
|
+
from pprint import pprint
|
815
|
+
|
816
|
+
with VerboseGuard(f'Adding a scenario {scenario._name}') as v:
|
817
|
+
vsav = VSav(self._build,self._vmod)
|
818
|
+
save = vsav.addSaveFile()
|
819
|
+
changed = {}
|
820
|
+
|
821
|
+
# ZunTzu reads in the counter sheets specified in a given
|
822
|
+
# scenario, and then builds and array of those counters.
|
823
|
+
# However, since we already have read in the counters, we
|
824
|
+
# need to simply remap the index here.
|
825
|
+
piece_map = self.remapIds(scenario)#self._gamebox._piece_map
|
826
|
+
# pprint(piece_map)
|
827
|
+
|
828
|
+
selectedBoard = None
|
829
|
+
selectedMap = None
|
830
|
+
# First, loop over layouts and see if we can find the map
|
831
|
+
# we're looking for.
|
832
|
+
for layout in scenario._layouts:
|
833
|
+
boardName = layout._name
|
834
|
+
board = self._game.getBoards(asdict=True).get(boardName,
|
835
|
+
None)
|
836
|
+
if not board:
|
837
|
+
# Try the next layout
|
838
|
+
continue
|
839
|
+
|
840
|
+
map = board.getMap()
|
841
|
+
if not map:
|
842
|
+
# A board without a map?
|
843
|
+
continue
|
844
|
+
|
845
|
+
if not isinstance(map.getParent(),Game):
|
846
|
+
# Parent of map is not the game
|
847
|
+
continue
|
848
|
+
|
849
|
+
selectedMap = map
|
850
|
+
selectedBoard = board
|
851
|
+
break
|
852
|
+
|
853
|
+
if not selectedMap:
|
854
|
+
print(f'No map found for scenario {scenario._name}')
|
855
|
+
|
856
|
+
mapName = selectedMap['mapName']
|
857
|
+
boardName = selectedBoard['name']
|
858
|
+
|
859
|
+
for layout in scenario._layouts:
|
860
|
+
v(f'Layout {layout._name} with {len(layout._stacks)}')
|
861
|
+
if len(layout._stacks) <= 0:
|
862
|
+
continue
|
863
|
+
|
864
|
+
thisMap = mapName
|
865
|
+
if self._allmaps:
|
866
|
+
boardName = layout._name
|
867
|
+
board = self._game.getBoards(asdict=True)\
|
868
|
+
.get(boardName, None)
|
869
|
+
|
870
|
+
map = board.getMap() if board else None
|
871
|
+
if map:
|
872
|
+
# A board without a map?
|
873
|
+
thisMap = map['mapName']
|
874
|
+
|
875
|
+
# In principle, it could be done like this, but to be
|
876
|
+
# on the safe side, we do it a tad more laboriously.
|
877
|
+
#
|
878
|
+
# mapped = {(s._x,s._y):
|
879
|
+
# [self._piece_slots[self._gamebox._piece_map[i]]
|
880
|
+
# for i in s._ids]
|
881
|
+
# for s in layout._stacks}
|
882
|
+
mapped = {}
|
883
|
+
v(f'Layout has {len(layout._stacks)}')
|
884
|
+
for s in layout._stacks:
|
885
|
+
slots = []
|
886
|
+
for i,f,r in zip(s._ids,s._flip,s._rot):
|
887
|
+
v(f'Piece {i}')
|
888
|
+
piece = piece_map.get(i, None)
|
889
|
+
if not piece:
|
890
|
+
print(f'Could not find piece for id {i}',
|
891
|
+
file=stderr)
|
892
|
+
continue
|
893
|
+
slot = self._piece_slots.get(piece, None)
|
894
|
+
if not slot:
|
895
|
+
print(f'Could not find slot for piece {piece}',
|
896
|
+
file=stderr)
|
897
|
+
continue
|
898
|
+
|
899
|
+
# Set states
|
900
|
+
self.changeSlot(slot, f, r, changed)
|
901
|
+
|
902
|
+
# v(f'id={i} -> piece={piece} -> slot={slot}')
|
903
|
+
slots.append(slot)
|
904
|
+
|
905
|
+
mapped[(int(s._x+.5),int(s._y+.5))] = slots
|
906
|
+
|
907
|
+
# self._picker.getMap()['mapName']
|
908
|
+
v(f'Add pieces to map: {thisMap}')
|
909
|
+
save.addNoGrid(thisMap, mapped)
|
910
|
+
|
911
|
+
v(f'Selected map/board {mapName}/{boardName}')
|
912
|
+
|
913
|
+
picker = selectedMap.getBoardPicker(single=True)[0]
|
914
|
+
|
915
|
+
if picker:
|
916
|
+
picker.selectBoard(boardName)
|
917
|
+
v(f'Selected map: {picker["selected"]}')
|
918
|
+
|
919
|
+
# print('\n'.join(save.getLines()))
|
920
|
+
# lines = []
|
921
|
+
# save._otherLines(lines)
|
922
|
+
# print('\n'.join(lines))
|
923
|
+
vsav.run(savename = scenario._file+'.vsav',
|
924
|
+
description = scenario._desc,
|
925
|
+
update = lambda n,t : self.updateSlot(n,t,changed))
|
926
|
+
|
927
|
+
if picker:
|
928
|
+
picker.selectBoard(None)
|
929
|
+
|
930
|
+
menu.addPredefinedSetup(name=scenario._name,
|
931
|
+
useFile=True,
|
932
|
+
file=scenario._file+'.vsav',
|
933
|
+
description=scenario._desc)
|
934
|
+
#
|
935
|
+
# EOF
|
936
|
+
#
|