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
pywargame/vassal/save.py
ADDED
@@ -0,0 +1,495 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from . moduledata import ModuleData
|
3
|
+
from . withtraits import PieceSlot, DummyWithTraits
|
4
|
+
from . trait import Trait
|
5
|
+
from . traits import BasicTrait, StackTrait
|
6
|
+
from . element import DummyElement
|
7
|
+
from . mapelements import AtStart
|
8
|
+
## END_IMPORT
|
9
|
+
|
10
|
+
# ====================================================================
|
11
|
+
class SaveIO:
|
12
|
+
'''Wrapper around a save file
|
13
|
+
|
14
|
+
Save file is
|
15
|
+
|
16
|
+
"!VCSK" KEY content
|
17
|
+
|
18
|
+
Key is two bytes drawn as a random number in 0-255. Content is
|
19
|
+
two bytes per character. Content characters are encoded with the
|
20
|
+
random key.
|
21
|
+
|
22
|
+
Save file (.vsav) content is
|
23
|
+
|
24
|
+
"begin_save" ESC
|
25
|
+
"\" ESC
|
26
|
+
[commands]* ESC
|
27
|
+
"PLAYER" name password side ESC
|
28
|
+
[map+"BoardPicker" name x y ESC]+
|
29
|
+
"SETUP_STACK" ESC
|
30
|
+
"TURN"+name state ESC
|
31
|
+
"end_save"
|
32
|
+
|
33
|
+
Commands are
|
34
|
+
|
35
|
+
"+/" id "/" body "\"
|
36
|
+
|
37
|
+
where body are
|
38
|
+
|
39
|
+
"stack" "/" mapName ; x ; y ; ids "\"
|
40
|
+
piece_type "/" piece_state (x and y set here too) "\"
|
41
|
+
|
42
|
+
x and y are pixel coordinates (sigh!). This means we have to know
|
43
|
+
|
44
|
+
- the pixel location of a hex
|
45
|
+
- the hex coordinates of that hex
|
46
|
+
- whether rows and columns are descending
|
47
|
+
- if even hexes are higher or not
|
48
|
+
|
49
|
+
The two first items _must_ be user supplied (I think). When we
|
50
|
+
add stacks or pieces, we must then get the numerical hex
|
51
|
+
coordinates - not what the user specified in the VASSAL editor or
|
52
|
+
the like. Of course, this means opening the module before writing
|
53
|
+
the patch.py script.
|
54
|
+
|
55
|
+
It seems like every piece is added in a stack.
|
56
|
+
|
57
|
+
The id is a numerical value. Rather big (e.g., 1268518000806). It
|
58
|
+
is the current number of miliseconds since epoch, with offset to
|
59
|
+
disambiguate.
|
60
|
+
|
61
|
+
The ID is the current time, taken from a milisecond clock,
|
62
|
+
possibly adjusted up if there is a clash. This is all managed by
|
63
|
+
the GameState class.
|
64
|
+
|
65
|
+
'''
|
66
|
+
VCS_HEADER = b'!VCSK'
|
67
|
+
VK_ESC = chr(27)
|
68
|
+
DEC_MAP = {
|
69
|
+
# 0-9
|
70
|
+
0x30: 0x30,
|
71
|
+
0x31: 0x30,
|
72
|
+
0x32: 0x30,
|
73
|
+
0x33: 0x30,
|
74
|
+
0x34: 0x30,
|
75
|
+
0x35: 0x30,
|
76
|
+
0x36: 0x30,
|
77
|
+
0x37: 0x30,
|
78
|
+
0x38: 0x30,
|
79
|
+
0x39: 0x30,
|
80
|
+
# A-F
|
81
|
+
0x41: 0x37,
|
82
|
+
0x42: 0x37,
|
83
|
+
0x43: 0x37,
|
84
|
+
0x44: 0x37,
|
85
|
+
0x45: 0x37,
|
86
|
+
0x46: 0x37,
|
87
|
+
# a-f
|
88
|
+
0x61: 0x57,
|
89
|
+
0x62: 0x57,
|
90
|
+
0x63: 0x57,
|
91
|
+
0x64: 0x57,
|
92
|
+
0x65: 0x57,
|
93
|
+
0x66: 0x57
|
94
|
+
}
|
95
|
+
ENC_MAP = [b'0',b'1',b'2',b'3',b'4',b'5',b'6',b'7',b'8',b'9',
|
96
|
+
b'a',b'b',b'c',b'd',b'e',b'f']
|
97
|
+
|
98
|
+
# ----------------------------------------------------------------
|
99
|
+
@classmethod
|
100
|
+
def decHex(cls,b):
|
101
|
+
'''Decode a single char into a number
|
102
|
+
|
103
|
+
If the encoded number is b, then the decoded number is
|
104
|
+
|
105
|
+
b - off
|
106
|
+
|
107
|
+
where off is an offset that depends on b
|
108
|
+
|
109
|
+
off = 0x30 if 0x30 <= b <= 0x39
|
110
|
+
0x37 if 0x41 <= b <= 0x46
|
111
|
+
0x57 if 0x61 <= b <= 0x66
|
112
|
+
'''
|
113
|
+
return b - cls.DEC_MAP[b]
|
114
|
+
# --------------------------------------------------------------------
|
115
|
+
@classmethod
|
116
|
+
def readByte(cls,inp,key):
|
117
|
+
'''Read a single byte of information from input stream
|
118
|
+
|
119
|
+
Two characters (c1 and c2) are read from input stream, and the
|
120
|
+
decoded byte is then
|
121
|
+
|
122
|
+
((dechex(c1) << 4 | dechex(c2)) ^ key) & 0xFF
|
123
|
+
|
124
|
+
Parameters
|
125
|
+
----------
|
126
|
+
inp : stream
|
127
|
+
Input to read from
|
128
|
+
key : int
|
129
|
+
Key to decode the input
|
130
|
+
|
131
|
+
Returns
|
132
|
+
-------
|
133
|
+
b : int
|
134
|
+
The read byte
|
135
|
+
'''
|
136
|
+
try:
|
137
|
+
pair = inp.read(2)
|
138
|
+
except Exception as e:
|
139
|
+
from sys import stderr
|
140
|
+
print(e,file=stderr)
|
141
|
+
return None
|
142
|
+
|
143
|
+
if len(pair) < 2:
|
144
|
+
return None
|
145
|
+
|
146
|
+
return ((cls.decHex(pair[0]) << 4 | cls.decHex(pair[1])) ^ key) & 0xFF
|
147
|
+
# --------------------------------------------------------------------
|
148
|
+
@classmethod
|
149
|
+
def readSave(cls,file,alsometa=False):
|
150
|
+
'''Read data from save file. The data is read into lines
|
151
|
+
returned as a list.
|
152
|
+
|
153
|
+
'''
|
154
|
+
from zipfile import ZipFile
|
155
|
+
|
156
|
+
# We open the save file as a zip file
|
157
|
+
with ZipFile(file,'r') as z:
|
158
|
+
# open the save file in the archive
|
159
|
+
save = z.open('savedGame','r')
|
160
|
+
|
161
|
+
# First, we check the header
|
162
|
+
head = save.read(len(cls.VCS_HEADER))
|
163
|
+
assert head == cls.VCS_HEADER, \
|
164
|
+
f'Read header {head} is not {cls.VCS_HEADER}'
|
165
|
+
|
166
|
+
# Then, read the key
|
167
|
+
pair = save.read(2)
|
168
|
+
key = (cls.decHex(pair[0]) << 4 | cls.decHex(pair[1]))
|
169
|
+
|
170
|
+
# Now read content, one byte at a time
|
171
|
+
content = ''
|
172
|
+
while True:
|
173
|
+
byte = cls.readByte(save,key)
|
174
|
+
if byte is None:
|
175
|
+
break
|
176
|
+
|
177
|
+
# Convert byte to character
|
178
|
+
content += chr(byte)
|
179
|
+
|
180
|
+
lines = content.split(cls.VK_ESC)
|
181
|
+
|
182
|
+
if alsometa:
|
183
|
+
savedata = z.read(VSav.SAVE_DATA)
|
184
|
+
moduledata = z.read(VMod.MODULE_DATA)
|
185
|
+
|
186
|
+
if not alsometa:
|
187
|
+
return key, lines
|
188
|
+
|
189
|
+
return key,lines,savedata,moduledata
|
190
|
+
|
191
|
+
# --------------------------------------------------------------------
|
192
|
+
@classmethod
|
193
|
+
def writeByte(cls,out,byte,key):
|
194
|
+
'''Write a single byte
|
195
|
+
|
196
|
+
Parameters
|
197
|
+
----------
|
198
|
+
out : IOStream
|
199
|
+
Stream to write to
|
200
|
+
byte : char
|
201
|
+
Single byte to write
|
202
|
+
key : int
|
203
|
+
Key to encode with (defaults to 0xAA - alternating 0's and 1's)
|
204
|
+
'''
|
205
|
+
b = ord(byte) ^ key
|
206
|
+
pair = cls.ENC_MAP[(b & 0xF0) >> 4], cls.ENC_MAP[b & 0x0F]
|
207
|
+
out.write(pair[0])
|
208
|
+
out.write(pair[1])
|
209
|
+
|
210
|
+
# --------------------------------------------------------------------
|
211
|
+
@classmethod
|
212
|
+
def writeInZip(cls,z,key,lines,filename='savedGame'):
|
213
|
+
'''Write a save file in a zip file (VMod)'''
|
214
|
+
# open the save file in the archive
|
215
|
+
with z.open(filename,'w') as save:
|
216
|
+
# Write header
|
217
|
+
save.write(cls.VCS_HEADER)
|
218
|
+
|
219
|
+
# Split key
|
220
|
+
pair = cls.ENC_MAP[(key & 0xF0) >> 4], cls.ENC_MAP[(key & 0x0F)]
|
221
|
+
save.write(pair[0])
|
222
|
+
save.write(pair[1])
|
223
|
+
|
224
|
+
# Form content
|
225
|
+
content = cls.VK_ESC.join(lines)
|
226
|
+
|
227
|
+
# Write each character as two
|
228
|
+
for c in content:
|
229
|
+
cls.writeByte(save, c, key)
|
230
|
+
|
231
|
+
# --------------------------------------------------------------------
|
232
|
+
@classmethod
|
233
|
+
def writeSave(cls,file,key,lines,savedata=None,moduledata=None):
|
234
|
+
'''Write a save file'''
|
235
|
+
from zipfile import ZipFile, ZIP_DEFLATED
|
236
|
+
|
237
|
+
# We open the save file as a zip file
|
238
|
+
with ZipFile(file,'w',ZIP_DEFLATED) as z:
|
239
|
+
cls.writeInZip(z,key,lines,filename='savedGame')
|
240
|
+
|
241
|
+
if savedata is not None:
|
242
|
+
z.writestr(VSav.SAVE_DATA,savedata)
|
243
|
+
z.writestr(VMod.MODULE_DATA,moduledata)
|
244
|
+
|
245
|
+
# ====================================================================
|
246
|
+
#
|
247
|
+
# VSave file
|
248
|
+
#
|
249
|
+
class SaveFile:
|
250
|
+
def __init__(self,game,firstid=None):
|
251
|
+
'''Creates a save file to add positions to'''
|
252
|
+
from time import time
|
253
|
+
self._game = game
|
254
|
+
self._counters = {}
|
255
|
+
self._stacks = {}
|
256
|
+
self._pieces = self._game.getPieces(asdict=True)
|
257
|
+
self._nextId = (int(time()*1000) - 360000
|
258
|
+
if firstid is None else firstid)
|
259
|
+
|
260
|
+
def add(self,grid,mapname,**kwargs):
|
261
|
+
'''Add pieces to the save.
|
262
|
+
|
263
|
+
Parameters
|
264
|
+
----------
|
265
|
+
grid : BaseGrid
|
266
|
+
Grid to add pieces to
|
267
|
+
kwargs : dict
|
268
|
+
Either a map from piece name to hex position,
|
269
|
+
Or a map from hex position to list of pieces
|
270
|
+
'''
|
271
|
+
for k,v in kwargs.items():
|
272
|
+
# print('Add to save',k,v)
|
273
|
+
self._add(grid,mapname,k,v)
|
274
|
+
|
275
|
+
def addNoGrid(self,mapName,mapping):
|
276
|
+
for k,v in mapping.items():
|
277
|
+
# print('Add to save',k,v)
|
278
|
+
self._add(None,mapName,k,v)
|
279
|
+
|
280
|
+
|
281
|
+
def _add(self,grid,mapName,k,v):
|
282
|
+
|
283
|
+
'''Add to the save'''
|
284
|
+
with VerboseGuard(f'Adding piece(s) to save: {len(k)}') as vg:
|
285
|
+
# print(f'Adding {k} -> {v}')
|
286
|
+
loc = None
|
287
|
+
piece = self._pieces.get(k,None)
|
288
|
+
pieces = []
|
289
|
+
boardName = (grid.getMap()['mapName']
|
290
|
+
if mapName is None else mapName)
|
291
|
+
# print(f'Map name: {mapName}')
|
292
|
+
vg(f'Adding to {boardName}')
|
293
|
+
if piece is not None:
|
294
|
+
vg(f'Key {k} is a piece')
|
295
|
+
#print(f'Key is piece: {k}->{piece}')
|
296
|
+
pieces.append(piece)
|
297
|
+
loc = v
|
298
|
+
else:
|
299
|
+
vg(f'Key {k} is a location')
|
300
|
+
# Key is not a piece name, so a location
|
301
|
+
loc = k
|
302
|
+
# Convert value to iterable
|
303
|
+
try:
|
304
|
+
iter(v)
|
305
|
+
except:
|
306
|
+
v = list(v)
|
307
|
+
|
308
|
+
for vv in v:
|
309
|
+
if isinstance(vv,PieceSlot):
|
310
|
+
pieces.append(vv)
|
311
|
+
continue
|
312
|
+
if isinstance(vv,str):
|
313
|
+
piece = self._pieces.get(vv,None)
|
314
|
+
if piece is None:
|
315
|
+
continue
|
316
|
+
pieces.append(piece)
|
317
|
+
|
318
|
+
vg(f'Loc: {loc} -> {pieces}')
|
319
|
+
if len(pieces) < 1:
|
320
|
+
return
|
321
|
+
|
322
|
+
if (mapName,loc) not in self._stacks:
|
323
|
+
vg(f'Adding stack {mapName},{loc}')
|
324
|
+
coord = grid.getLocation(loc) if grid is not None else loc
|
325
|
+
if coord is None:
|
326
|
+
print(f'did not get coordinates from {loc}')
|
327
|
+
return
|
328
|
+
self._stacks[(mapName,loc)] = {
|
329
|
+
'x': coord[0],
|
330
|
+
'y': coord[1],
|
331
|
+
'pids': [] }
|
332
|
+
|
333
|
+
place = self._stacks[(mapName,loc)]
|
334
|
+
for piece in pieces:
|
335
|
+
name = piece['entryName']
|
336
|
+
gpid = piece['gpid']
|
337
|
+
counter = self._counters.get((name,gpid),None)
|
338
|
+
vg(f'Got counter {counter} for {name},{gpid}')
|
339
|
+
|
340
|
+
if counter is None:
|
341
|
+
if gpid == 0:
|
342
|
+
print(f'making new counter with pid={self._nextId}: '
|
343
|
+
f'{gpid}')
|
344
|
+
gpid = self._nextId
|
345
|
+
self._nextId += 1
|
346
|
+
|
347
|
+
|
348
|
+
vg(f'Save adding counter with pid={gpid}')
|
349
|
+
counter = {'pid': gpid,
|
350
|
+
'piece': piece,
|
351
|
+
'board': mapName,
|
352
|
+
'x': place['x'],
|
353
|
+
'y': place['y'],
|
354
|
+
}
|
355
|
+
self._counters[(name,gpid)] = counter
|
356
|
+
|
357
|
+
vg(f'Adding to stack {mapName},{loc}: {counter}')
|
358
|
+
place['pids'].append(counter['pid'])
|
359
|
+
|
360
|
+
def getLines(self,update=None):
|
361
|
+
'''Get the final lines of code'''
|
362
|
+
key = 0xAA # fixed key
|
363
|
+
|
364
|
+
lines = ['begin_save',
|
365
|
+
'',
|
366
|
+
'\\']
|
367
|
+
|
368
|
+
self._pieceLines(lines,update=update)
|
369
|
+
self._otherLines(lines)
|
370
|
+
|
371
|
+
lines.append('end_save')
|
372
|
+
return lines
|
373
|
+
|
374
|
+
def _pieceLines(self,lines,update=lambda t:t):
|
375
|
+
'''Add piece lines to save file
|
376
|
+
|
377
|
+
Parameters
|
378
|
+
----------
|
379
|
+
lines : list
|
380
|
+
The lines to add
|
381
|
+
'''
|
382
|
+
# print(self._counters)
|
383
|
+
for (name,gpid),counter in self._counters.items():
|
384
|
+
iden = counter['pid']
|
385
|
+
piece = counter['piece']
|
386
|
+
traits = piece.getTraits()
|
387
|
+
traits = Trait.flatten(traits,self._game)
|
388
|
+
# Get last - trait (basic piece), and modify coords
|
389
|
+
basic = traits[-1]
|
390
|
+
basic['map'] = counter['board']
|
391
|
+
basic['x'] = counter['x']
|
392
|
+
basic['y'] = counter['y']
|
393
|
+
# Set old location if possible
|
394
|
+
parent = piece.getParent(DummyElement,checkTag=False)
|
395
|
+
if parent is not None and parent._node.nodeName == AtStart.TAG:
|
396
|
+
oldLoc = parent['location']
|
397
|
+
oldBoard = parent['owningBoard']
|
398
|
+
oldMap = self._game.getBoards()[oldBoard].getMap()['mapName']
|
399
|
+
oldX = parent['x']
|
400
|
+
oldY = parent['y']
|
401
|
+
oldZone = None
|
402
|
+
zones = self._game.getBoards()[oldBoard].getZones()
|
403
|
+
for zone in zones.values():
|
404
|
+
grid = zone.getGrids()[0]
|
405
|
+
if grid is None: continue
|
406
|
+
|
407
|
+
coord = grid.getLocation(oldLoc)
|
408
|
+
if coord is None: continue
|
409
|
+
|
410
|
+
oldZone = zone['name']
|
411
|
+
oldX = coord[0]
|
412
|
+
oldY = coord[1]
|
413
|
+
break
|
414
|
+
|
415
|
+
if oldZone is not None:
|
416
|
+
basic['properties'] = \
|
417
|
+
f'8;'+\
|
418
|
+
f'UniqueID;{iden};'+\
|
419
|
+
f'OldZone;{oldZone};'+\
|
420
|
+
f'OldLocationName;{oldLoc};'+\
|
421
|
+
f'OldDeckName;;'+\
|
422
|
+
f'OldX;{oldX};'+\
|
423
|
+
f'OldY;{oldY};'+\
|
424
|
+
f'OldBoard;{oldBoard};'+\
|
425
|
+
f'OldMap;{oldMap}'
|
426
|
+
else:
|
427
|
+
basic['properties'] = \
|
428
|
+
f'7;'+\
|
429
|
+
f'UniqueID;{iden};'+\
|
430
|
+
f'OldLocationName;{oldLoc};'+\
|
431
|
+
f'OldDeckName;;'+\
|
432
|
+
f'OldX;{oldX};'+\
|
433
|
+
f'OldY;{oldY};'+\
|
434
|
+
f'OldBoard;{oldBoard};'+\
|
435
|
+
f'OldMap;{oldMap}'
|
436
|
+
|
437
|
+
for trait in traits:
|
438
|
+
if trait.ID == TrailTrait.ID:
|
439
|
+
trait['map'] = oldMap
|
440
|
+
trait['points'] = f'1;{oldX},{oldY}'
|
441
|
+
trait['init'] = True
|
442
|
+
|
443
|
+
# Let user code update the flattened traits
|
444
|
+
if update is not None:
|
445
|
+
update(name,traits)
|
446
|
+
# Wrapper
|
447
|
+
wrap = DummyWithTraits(self._game,traits=[])
|
448
|
+
wrap.setTraits(*traits,iden=str(iden))
|
449
|
+
lines.append(wrap._node.childNodes[0].nodeValue+'\\')
|
450
|
+
|
451
|
+
layer = -1
|
452
|
+
for key,dat in self._stacks.items():
|
453
|
+
pids = dat.get('pids',None)
|
454
|
+
x = dat['x']
|
455
|
+
y = dat['y']
|
456
|
+
if pids is None or len(pids) < 1:
|
457
|
+
print(f'No pieces at {key[0]},{key[1]}')
|
458
|
+
continue
|
459
|
+
|
460
|
+
iden = self._nextId
|
461
|
+
self._nextId += 1
|
462
|
+
stack = StackTrait(board=key[0],x=x,y=y,pieceIds=pids,layer=layer)
|
463
|
+
layer = 1
|
464
|
+
wrap = DummyWithTraits(self._game,traits=[])
|
465
|
+
wrap.setTraits(stack,iden=iden)
|
466
|
+
lines.append(wrap._node.childNodes[0].nodeValue+'\\')
|
467
|
+
|
468
|
+
def _otherLines(self,lines):
|
469
|
+
'''Add other lines to save'''
|
470
|
+
lines.append('UNMASK\tnull')
|
471
|
+
if self._game.getPlayerRoster():
|
472
|
+
for r in self._game.getPlayerRoster():
|
473
|
+
lines.extend(r.encode())
|
474
|
+
if self._game.getNotes(single=False):
|
475
|
+
for n in self._game.getNotes(single=False):
|
476
|
+
lines.extend(n.encode())
|
477
|
+
setupStack = False
|
478
|
+
for m in self._game.getMaps(asdict=False):
|
479
|
+
for bp in m.getBoardPicker(single=False):
|
480
|
+
lines.extend(bp.encode())
|
481
|
+
if not setupStack:
|
482
|
+
atstart = m.getAtStarts(single=False)
|
483
|
+
if atstart and len(atstart) > 0:
|
484
|
+
lines.append('SETUP_STACK')
|
485
|
+
setupStack = True
|
486
|
+
|
487
|
+
# for tk,tt in self._game.getTurnTracks(asdict=True):
|
488
|
+
# lines.extend(tt.encode())
|
489
|
+
|
490
|
+
|
491
|
+
# --------------------------------------------------------------------
|
492
|
+
class SaveData(ModuleData):
|
493
|
+
def __init__(self,root=None):
|
494
|
+
'''Convinience wrapper'''
|
495
|
+
super(SaveData,self).__init__(root=root)
|