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.
Files changed (150) hide show
  1. pywargame/__init__.py +2 -0
  2. pywargame/common/__init__.py +3 -0
  3. pywargame/common/collector.py +87 -0
  4. pywargame/common/dicedraw.py +363 -0
  5. pywargame/common/drawdice.py +40 -0
  6. pywargame/common/singleton.py +22 -0
  7. pywargame/common/test.py +25 -0
  8. pywargame/common/verbose.py +59 -0
  9. pywargame/common/verboseguard.py +53 -0
  10. pywargame/cyberboard/__init__.py +18 -0
  11. pywargame/cyberboard/archive.py +283 -0
  12. pywargame/cyberboard/base.py +63 -0
  13. pywargame/cyberboard/board.py +462 -0
  14. pywargame/cyberboard/cell.py +200 -0
  15. pywargame/cyberboard/collect.py +49 -0
  16. pywargame/cyberboard/collectgbx0pwd.py +30 -0
  17. pywargame/cyberboard/collectgbxext.py +30 -0
  18. pywargame/cyberboard/collectgsnexp.py +32 -0
  19. pywargame/cyberboard/collectgsnext.py +30 -0
  20. pywargame/cyberboard/draw.py +396 -0
  21. pywargame/cyberboard/exporter.py +1132 -0
  22. pywargame/cyberboard/extractor.py +240 -0
  23. pywargame/cyberboard/features.py +17 -0
  24. pywargame/cyberboard/gamebox.py +81 -0
  25. pywargame/cyberboard/gbxexp.py +76 -0
  26. pywargame/cyberboard/gbxext.py +64 -0
  27. pywargame/cyberboard/gsnexp.py +147 -0
  28. pywargame/cyberboard/gsnext.py +59 -0
  29. pywargame/cyberboard/head.py +111 -0
  30. pywargame/cyberboard/image.py +76 -0
  31. pywargame/cyberboard/main.py +47 -0
  32. pywargame/cyberboard/mark.py +102 -0
  33. pywargame/cyberboard/palette.py +36 -0
  34. pywargame/cyberboard/piece.py +169 -0
  35. pywargame/cyberboard/player.py +36 -0
  36. pywargame/cyberboard/scenario.py +115 -0
  37. pywargame/cyberboard/testgrid.py +156 -0
  38. pywargame/cyberboard/tile.py +121 -0
  39. pywargame/cyberboard/tray.py +68 -0
  40. pywargame/cyberboard/windows.py +41 -0
  41. pywargame/cyberboard/zeropwd.py +45 -0
  42. pywargame/cyberboard.py +2728 -0
  43. pywargame/gbx0pwd.py +2776 -0
  44. pywargame/gbxextract.py +2795 -0
  45. pywargame/gsnexport.py +16499 -0
  46. pywargame/gsnextract.py +2790 -0
  47. pywargame/latex/__init__.py +2 -0
  48. pywargame/latex/collect.py +34 -0
  49. pywargame/latex/latexexporter.py +4010 -0
  50. pywargame/latex/main.py +184 -0
  51. pywargame/vassal/__init__.py +66 -0
  52. pywargame/vassal/base.py +139 -0
  53. pywargame/vassal/board.py +243 -0
  54. pywargame/vassal/buildfile.py +60 -0
  55. pywargame/vassal/chart.py +79 -0
  56. pywargame/vassal/chessclock.py +197 -0
  57. pywargame/vassal/collect.py +98 -0
  58. pywargame/vassal/collectpatch.py +28 -0
  59. pywargame/vassal/command.py +21 -0
  60. pywargame/vassal/documentation.py +322 -0
  61. pywargame/vassal/dumpcollect.py +28 -0
  62. pywargame/vassal/dumpvsav.py +28 -0
  63. pywargame/vassal/element.py +439 -0
  64. pywargame/vassal/exporter.py +89 -0
  65. pywargame/vassal/extension.py +101 -0
  66. pywargame/vassal/folder.py +103 -0
  67. pywargame/vassal/game.py +940 -0
  68. pywargame/vassal/gameelements.py +1091 -0
  69. pywargame/vassal/globalkey.py +127 -0
  70. pywargame/vassal/globalproperty.py +433 -0
  71. pywargame/vassal/grid.py +573 -0
  72. pywargame/vassal/map.py +1061 -0
  73. pywargame/vassal/mapelements.py +1020 -0
  74. pywargame/vassal/merge.py +57 -0
  75. pywargame/vassal/merger.py +460 -0
  76. pywargame/vassal/moduledata.py +275 -0
  77. pywargame/vassal/mrgcollect.py +31 -0
  78. pywargame/vassal/patch.py +44 -0
  79. pywargame/vassal/patchcollect.py +28 -0
  80. pywargame/vassal/player.py +83 -0
  81. pywargame/vassal/save.py +495 -0
  82. pywargame/vassal/skel.py +380 -0
  83. pywargame/vassal/trait.py +224 -0
  84. pywargame/vassal/traits/__init__.py +36 -0
  85. pywargame/vassal/traits/area.py +50 -0
  86. pywargame/vassal/traits/basic.py +35 -0
  87. pywargame/vassal/traits/calculatedproperty.py +22 -0
  88. pywargame/vassal/traits/cargo.py +29 -0
  89. pywargame/vassal/traits/click.py +41 -0
  90. pywargame/vassal/traits/clone.py +28 -0
  91. pywargame/vassal/traits/delete.py +24 -0
  92. pywargame/vassal/traits/deselect.py +32 -0
  93. pywargame/vassal/traits/dynamicproperty.py +112 -0
  94. pywargame/vassal/traits/globalcommand.py +55 -0
  95. pywargame/vassal/traits/globalhotkey.py +26 -0
  96. pywargame/vassal/traits/globalproperty.py +54 -0
  97. pywargame/vassal/traits/hide.py +67 -0
  98. pywargame/vassal/traits/label.py +76 -0
  99. pywargame/vassal/traits/layer.py +105 -0
  100. pywargame/vassal/traits/mark.py +20 -0
  101. pywargame/vassal/traits/mask.py +85 -0
  102. pywargame/vassal/traits/mat.py +26 -0
  103. pywargame/vassal/traits/moved.py +35 -0
  104. pywargame/vassal/traits/movefixed.py +51 -0
  105. pywargame/vassal/traits/nonrect.py +95 -0
  106. pywargame/vassal/traits/nostack.py +55 -0
  107. pywargame/vassal/traits/place.py +104 -0
  108. pywargame/vassal/traits/prototype.py +20 -0
  109. pywargame/vassal/traits/report.py +34 -0
  110. pywargame/vassal/traits/restrictaccess.py +28 -0
  111. pywargame/vassal/traits/restrictcommand.py +32 -0
  112. pywargame/vassal/traits/return.py +40 -0
  113. pywargame/vassal/traits/rotate.py +62 -0
  114. pywargame/vassal/traits/sendto.py +59 -0
  115. pywargame/vassal/traits/sheet.py +129 -0
  116. pywargame/vassal/traits/skel.py +9 -0
  117. pywargame/vassal/traits/stack.py +28 -0
  118. pywargame/vassal/traits/submenu.py +27 -0
  119. pywargame/vassal/traits/trail.py +61 -0
  120. pywargame/vassal/traits/trigger.py +72 -0
  121. pywargame/vassal/turn.py +272 -0
  122. pywargame/vassal/upgrade.py +191 -0
  123. pywargame/vassal/vmod.py +323 -0
  124. pywargame/vassal/vsav.py +100 -0
  125. pywargame/vassal/widget.py +358 -0
  126. pywargame/vassal/withtraits.py +634 -0
  127. pywargame/vassal/xml.py +4 -0
  128. pywargame/vassal/zone.py +399 -0
  129. pywargame/vassal.py +12500 -0
  130. pywargame/vmodpatch.py +12548 -0
  131. pywargame/vsavdump.py +12533 -0
  132. pywargame/vslmerge.py +13015 -0
  133. pywargame/wgexport.py +16689 -0
  134. pywargame/ztexport.py +14351 -0
  135. pywargame/zuntzu/__init__.py +5 -0
  136. pywargame/zuntzu/base.py +82 -0
  137. pywargame/zuntzu/collect.py +38 -0
  138. pywargame/zuntzu/countersheet.py +250 -0
  139. pywargame/zuntzu/dicehand.py +48 -0
  140. pywargame/zuntzu/exporter.py +936 -0
  141. pywargame/zuntzu/gamebox.py +154 -0
  142. pywargame/zuntzu/map.py +36 -0
  143. pywargame/zuntzu/piece.py +37 -0
  144. pywargame/zuntzu/scenario.py +208 -0
  145. pywargame/zuntzu/ztexp.py +115 -0
  146. pywargame-0.3.1.dist-info/METADATA +353 -0
  147. pywargame-0.3.1.dist-info/RECORD +150 -0
  148. pywargame-0.3.1.dist-info/WHEEL +5 -0
  149. pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
  150. pywargame-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,462 @@
1
+ ## BEGIN_IMPORT
2
+ from common import VerboseGuard
3
+ from . draw import GBXDrawList
4
+ from . cell import GBXCell, GBXCellGeometry
5
+ from . base import CbManager, CbFont
6
+ from . head import num_version
7
+ ## END_IMPORT
8
+
9
+ # --------------------------------------------------------------------
10
+ class GBXBoardCalculator:
11
+ def __init__(self,board):
12
+ self._geometry = board._full
13
+ self._nRows = board._nRows
14
+ self._nCols = board._nCols
15
+ self._rowOffset = board._rowOffset
16
+ self._colOffset = board._colOffset
17
+ self._rowInvert = board._rowInvert
18
+ self._colInvert = board._colInvert
19
+
20
+ def __call__(self,x,y):
21
+ # Shift depending on grid type and stagger
22
+ row, col = self._geometry.inverse(x,y)
23
+ if self._rowInvert:
24
+ row = self._nRows - row - 1
25
+ if self._colInvert:
26
+ col = self._nCols - col - 1
27
+
28
+ return row+self._rowOffset, col+self._colOffset
29
+
30
+ # --------------------------------------------------------------------
31
+ class GBXBoard:
32
+ def __init__(self,ar):
33
+ '''A board'''
34
+ with VerboseGuard(f'Reading board') as g:
35
+ self._serial = ar.iden()
36
+ self._visible = ar.word()
37
+ self._snap = ar.word()
38
+ self._xSnap = ar.dword()
39
+ self._ySnap = ar.dword()
40
+ self._xSnapOffset = ar.dword()
41
+ self._ySnapOffset = ar.dword()
42
+ self._maxLayer = ar.word()
43
+ self._background = ar.dword()
44
+ self._name = ar.str()
45
+ hasDraw = ar.word()
46
+ self._baseDraw = GBXDrawList(ar) if hasDraw else None
47
+
48
+ self._showCellBorder = ar.word()
49
+ self._topCellBorder = ar.word()
50
+ self._reserved = [ar.word() for _ in range(4)]
51
+ self._reserved2 = None
52
+ self._rowOffset = 0
53
+ self._colOffset = 0
54
+ self._rowInvert = False
55
+ self._colInvert = False
56
+ self._nRows = 0
57
+ self._nCols = 0
58
+ self._transparent = False
59
+ self._numbers = 0
60
+ self._trackCell = False
61
+ self._frameColor = 0xFF000000
62
+ self._full = None
63
+ self._half = None
64
+ self._small = None
65
+ self._map = []
66
+ self._topDraw = None
67
+
68
+ hasArray = ar.word()
69
+ if hasArray != 0:
70
+ self._reserved2 = [ar.word() for _ in range(4)]
71
+ self._rowOffset = ar.word()
72
+ self._colOffset = ar.word()
73
+ self._rowInvert = ar.word()
74
+ self._colInvert = ar.word()
75
+ self._nRows = ar.int(Features().sub_size)
76
+ self._nCols = ar.int(Features().sub_size)
77
+ self._transparent = ar.word()
78
+ self._numbers = ar.word()
79
+ self._trackCell = ar.word()
80
+ self._frameColor = ar.dword()
81
+
82
+ self._full = GBXCellGeometry(ar)
83
+ self._half = GBXCellGeometry(ar)
84
+ self._small = GBXCellGeometry(ar)
85
+
86
+ self._map = [[GBXCell(ar,row,col) for col in range(self._nCols)]
87
+ for row in range(self._nRows)]
88
+
89
+ hasDraw = ar.word()
90
+ self._topDraw = GBXDrawList(ar) if hasDraw else None
91
+ g(f'Board background read: {self._background:06x}, frame color: {self._frameColor:06x}')
92
+
93
+ def toDict(self,tileManager,markManager,strings,no,boardDigits,
94
+ alsoMap=True):
95
+ from io import StringIO
96
+
97
+ with VerboseGuard(f'Making dict of board {self._name}') as g:
98
+ sav = f'board_{no:0{boardDigits}d}.svg'
99
+ g(f'File to save in: {sav}')
100
+ dct = {'name': self._name,
101
+ 'serial': self._serial,
102
+ 'visible': self._visible,
103
+ 'snap': {
104
+ 'enable': self._snap,
105
+ 'x': {
106
+ 'distance': self._xSnap,
107
+ 'offset': self._xSnapOffset
108
+ },
109
+ 'y': {
110
+ 'distance': self._ySnap,
111
+ 'offset': self._ySnapOffset
112
+ }
113
+ },
114
+ 'max layer': self._maxLayer,
115
+ 'cell border': {
116
+ 'visible': self._showCellBorder,
117
+ 'on top layer': self._topCellBorder,
118
+ },
119
+ 'rows': {
120
+ 'size': self._nRows,
121
+ 'offset': self._rowOffset,
122
+ 'inverted': self._rowInvert
123
+ },
124
+ 'columns': {
125
+ 'size': self._nCols,
126
+ 'offset': self._colOffset,
127
+ 'inverted': self._colInvert
128
+ },
129
+ 'cells': {
130
+ 'transparent': self._transparent,
131
+ 'foreground': self._frameColor,
132
+ },
133
+ 'numbering': {
134
+ 'order': 'V' if self._numbers % 2 == 0 else 'H',
135
+ 'padding': self._numbers in [2,3],
136
+ 'first': 'A' if self._numbers in [4,5] else 'N'
137
+ }
138
+ }
139
+ if self._full is not None:
140
+ dct['cells']['geometry'] = self._full.toDict()
141
+ if alsoMap and self._map is not None:
142
+ dct['cells']['list'] = [[c.toDict(tileManager,self._full)
143
+ for c in row]
144
+ for row in self._map]
145
+
146
+
147
+ sav = f'board_{no:0{boardDigits}d}.svg'
148
+ img = self.drawing(sav,
149
+ tileManager=tileManager,
150
+ markManager=markManager)
151
+ # img.save(pretty=True)
152
+
153
+ stream = StringIO()
154
+ img.write(stream,pretty=True)
155
+
156
+ dct['filename'] = sav
157
+ dct['image'] = stream.getvalue()#img.tostring()
158
+ dct['size'] = self._full.boardSize(self._nRows,self._nCols)
159
+
160
+ return dct
161
+
162
+ def drawing(self,sav,tileManager,markManager,*args,**kwargs):
163
+ from svgwrite import Drawing
164
+ from svgwrite.base import Title
165
+ with VerboseGuard(f'Making SVG of board {self._name}') as g:
166
+ size = self._full.boardSize(self._nRows,self._nCols)
167
+ dwg = Drawing(filename=sav,size=size)
168
+ frame, defMap, patMap = self.defs(dwg,tileManager,markManager)
169
+
170
+ # Draw background
171
+ g(f'Board background: {self._background:06x} {GBXDraw.hex(self._background)}')
172
+ dwg.add(Title(self._name))
173
+ dwg.add(dwg.rect(id='background',
174
+ insert=(0,0),
175
+ size=size,
176
+ fill=GBXDraw.hex(self._background)
177
+ #f'#{self._background:06x}')
178
+ ))
179
+ # GBXDraw.hex(self._background)
180
+
181
+ g('Drawing base layer')
182
+ bse = self.base (dwg, 0, defMap)
183
+ g('Drawing cells')
184
+ grd = self.cells(dwg, frame, patMap)
185
+ if self._showCellBorder and not self._topCellBorder:
186
+ self.borders(dwg,frame)
187
+ g('Drawing top layer')
188
+ top1 = self.top (dwg, 1, defMap)
189
+ if self._showCellBorder and self._topCellBorder:
190
+ self.borders(dwg,frame)
191
+ top2 = self.top (dwg, 2, defMap)
192
+
193
+ return dwg
194
+
195
+ def defs(self,dwg,tileManager,markManager):
196
+ defMap = {}
197
+ patMap = {}
198
+ frame = self._full.svgDef(dwg)
199
+ defMap['cell'] = frame
200
+
201
+ # Get defininitions from base layer
202
+ if self._baseDraw:
203
+ self._baseDraw.svgDefs(dwg,
204
+ tileManager,
205
+ markManager,
206
+ defMap)
207
+ # Get definitions from cell layer
208
+ for row in self._map:
209
+ for cell in row:
210
+ cell.svgDef(dwg,tileManager,patMap)
211
+
212
+ # Get definitions from top layer
213
+ if self._topDraw:
214
+ self._topDraw.svgDefs(dwg,
215
+ tileManager,
216
+ markManager,
217
+ defMap)
218
+
219
+ return frame, defMap, patMap
220
+
221
+ def base(self,dwg,passNo,defMap):
222
+ bse = dwg.add(dwg.g(id=f'base_{passNo:02d}'))
223
+ if self._baseDraw:
224
+ self._baseDraw.svg(dwg,bse,passNo,defMap)
225
+
226
+ return bse
227
+
228
+ def cells(self,dwg,frame,patMap):
229
+ grd = dwg.add(dwg.g(id='grid'))
230
+ for row in self._map:
231
+ for cell in row:
232
+ cell.svg(dwg,grd,frame,self._full,patMap)
233
+
234
+ return grd
235
+
236
+ def borders(self,dwg,frame):
237
+ brd = dwg.add(dwg.g(id='borders'))
238
+ for row in self._map:
239
+ for cell in row:
240
+ cell.svgFrame(dwg,brd,frame,self._full,self._frameColor)
241
+
242
+ return brd
243
+
244
+ def top(self,dwg,passNo,defMap):
245
+ top = dwg.add(dwg.g(id=f'top_{passNo:02d}'))
246
+ if self._topDraw:
247
+ self._topDraw.svg (dwg,top,passNo,defMap)
248
+
249
+ return top
250
+
251
+ def __str__(self):
252
+ return (f'GBXBoard: {self._name}\n'
253
+ f' serial: {self._serial}\n'
254
+ f' visible: {self._visible}\n'
255
+ f' snap: {self._snap}\n'
256
+ f' xSnap: {self._xSnap}\n'
257
+ f' ySnap: {self._ySnap}\n'
258
+ f' xSnapOffset: {self._xSnapOffset}\n'
259
+ f' ySnapOffset: {self._ySnapOffset}\n'
260
+ f' maxLayer: {self._maxLayer}\n'
261
+ f' background: {self._background:08x}\n'
262
+ f' Base draws: {self._baseDraw}\n'
263
+ f' Show cell border: {self._showCellBorder}\n'
264
+ f' Top cell border: {self._topCellBorder}\n'
265
+ f' Reserved: {self._reserved}\n'
266
+ f' Reserved2: {self._reserved}\n'
267
+ f' Row offset: {self._rowOffset}\n'
268
+ f' Column offset: {self._colOffset}\n'
269
+ f' Row invert: {self._rowInvert}\n'
270
+ f' Colunn invert: {self._colInvert}\n'
271
+ f' # Rows: {self._nRows}\n'
272
+ f' # Cols: {self._nCols}\n'
273
+ f' Transparent: {self._transparent}\n'
274
+ f' Numbers: {self._numbers}\n'
275
+ f' Track cells: {self._trackCell}\n'
276
+ f' Frame color: {self._frameColor:08x}\n'
277
+ f' Full geometry: {self._full}\n'
278
+ f' Half geometry: {self._half}\n'
279
+ f' Small geometry: {self._small}\n'
280
+ f' Top draws: {self._topDraw}'
281
+ )
282
+
283
+
284
+
285
+
286
+ # --------------------------------------------------------------------
287
+ class GBXBoardManager(CbManager):
288
+ def __init__(self,ar):
289
+ '''Manager of boards'''
290
+ with VerboseGuard(f'Reading board manager'):
291
+ self._nextSerial = ar.iden()
292
+ super(GBXBoardManager,self).__init__(ar)
293
+ # print(Features().id_size)
294
+ self._boards = self._readSub(ar,GBXBoard)
295
+
296
+ def __len__(self):
297
+ return len(self._boards)
298
+
299
+ def bySerial(self,serial):
300
+ for b in self._boards:
301
+ if b._serial == serial:
302
+ return b
303
+
304
+ return None
305
+
306
+ def toDict(self,tileManager,markManager,strings):
307
+ from math import log10, ceil
308
+ with VerboseGuard(f'Making dict board manager'):
309
+ boardDigits = int(ceil(log10(len(self)+.5)))
310
+
311
+ return {b._serial:
312
+ b.toDict(tileManager,markManager,strings,no,boardDigits)
313
+ for no, b in enumerate(self._boards)}
314
+
315
+ def __str__(self):
316
+ return ('GBXBoard manager:\n'
317
+ + f' Next serial: {self._nextSerial:08x}\n'
318
+ + super(GBXBoardManager,self).__str__()
319
+ + self._strSub('boards',self._boards))
320
+
321
+ # --------------------------------------------------------------------
322
+ class GSNGeomorphicElement:
323
+ def __init__(self,ar):
324
+ with VerboseGuard('Reading geomorphic element'):
325
+ self._serial = ar.word()
326
+
327
+ def __str__(self):
328
+ return f'GSNGeomorphicElement: {self._serial}'
329
+
330
+ # --------------------------------------------------------------------
331
+ class GSNGeomorphicBoard:
332
+ def __init__(self,ar):
333
+ with VerboseGuard('Reading geomorphic board'):
334
+ self._name = ar.str()
335
+ self._nRows = ar.word()
336
+ self._nCols = ar.word()
337
+ n = ar.word()
338
+ self._elements = [GSNGeomorphicElement(ar) for _ in range(n)]
339
+
340
+ def __str__(self):
341
+ pl = '\n '.join([str(s) for s in self._elements])
342
+ return (f'GeomorphicBoard: {self._name}\n'
343
+ f' Size: {self._nRows}x{self._nCols}\n'
344
+ f' Elements:\n {pl}\n')
345
+
346
+ # --------------------------------------------------------------------
347
+ class GSNBoard:
348
+ def __init__(self,ar,vers):
349
+ with VerboseGuard(f'Reading scenario board {vers//256}.{vers%256}'):
350
+ hasGeo = ar.byte()
351
+ self._geo = GSNGeomorphicBoard(ar) if hasGeo else None
352
+ self._serial = ar.iden()
353
+ self._snap = ar.word()
354
+ self._xSnap = ar.dword()
355
+ self._ySnap = ar.dword()
356
+ self._xSnapOffset = ar.dword()
357
+ self._ySnapOffset = ar.dword()
358
+ self._xStagger = ar.word()
359
+ self._yStagger = ar.word()
360
+ self._piecesVisible = ar.word()
361
+ self._blockBeneath = ar.word()
362
+ self._rotate180 = ar.word()
363
+ self._showTiny = ar.word()
364
+ self._indicatorsVisible= ar.word()
365
+ self._cellBorders = ar.word()
366
+ self._smallCellBorders = ar.word()
367
+ self._enforceLocks = ar.word()
368
+ self._plotLineColor = ar.dword()
369
+ self._plotLineWidth = ar.word()
370
+ self._lineColor = ar.dword()
371
+ self._lineWidth = ar.word()
372
+ self._textColor = ar.dword()
373
+ self._textBoxColor = ar.dword()
374
+ self._font = CbFont(ar)
375
+ self._gridCenters = ar.word()
376
+ self._snapMove = ar.word()
377
+ self._indactorsTop = ar.word()
378
+ self._openOnLoad = ar.word()
379
+ self._prevPlotMode = ar.word()
380
+ self._prevPlotX = ar.word()
381
+ self._prevPlotY = ar.word()
382
+ self._ownerMask = (ar.word() if vers < num_version(3,10) else
383
+ ar.dword())
384
+ self._restrictToOwner = ar.word()
385
+ self._pieces = GBXDrawList(ar)
386
+ self._indicators = GBXDrawList(ar)
387
+
388
+
389
+ def toDict(self,boardManager):
390
+ board = (None if boardManager is None else
391
+ boardManager.bySerial(self._serial))
392
+ geom = None if board is None else board._full
393
+ calc = None if geom is None else GBXBoardCalculator(board)
394
+
395
+ return {
396
+ 'onload': self._openOnLoad != 0,
397
+ 'snap': {
398
+ 'enable': self._snap,
399
+ 'onmove': self._snapMove != 0,
400
+ 'gridCenter': self._gridCenters != 0,
401
+ 'x': {
402
+ 'distance': self._xSnap,
403
+ 'offset': self._xSnapOffset
404
+ },
405
+ 'y': {
406
+ 'distance': self._ySnap,
407
+ 'offset': self._ySnapOffset
408
+ }
409
+ },
410
+ 'moves': {
411
+ 'color': self._plotLineColor,
412
+ 'width': self._plotLineWidth
413
+ },
414
+ 'stacking': [self._xStagger, self._yStagger],
415
+ 'owner': self._ownerMask,
416
+ 'restrict': self._restrictToOwner != 0,
417
+ 'grid': {
418
+ 'show': self._cellBorders != 0,
419
+ 'color': self._lineColor,
420
+ 'width': self._lineWidth
421
+ },
422
+ 'pieces': self._pieces.toDict(calc),
423
+ 'indicators': self._indicators.toDict(calc)
424
+ }
425
+
426
+ def __str__(self):
427
+ return (f'ScenarioBoard: {self._serial}'
428
+ f' Geomorphic: {"None" if self._geo is None else self._geo}\n'
429
+ f' Font: {self._font}\n'
430
+ f' Pieces:\n{str(self._pieces)}\n'
431
+ f' Indicators:\n{str(self._indicators)}')
432
+
433
+ # --------------------------------------------------------------------
434
+ class GSNBoardManager:
435
+ def __init__(self,ar,vers):
436
+ with VerboseGuard(f'Reading scenario board manager') as g:
437
+ self._nextGeoSerial = ar.iden()
438
+ self._reserved = [ar.word() for _ in range(3)]
439
+ n = ar.sub_size()
440
+ g(f'Got {n} boards to read')
441
+ self._boards = [GSNBoard(ar,vers) for _ in range(n)]
442
+
443
+ def toDict(self,boardManager):
444
+ hasStart = False
445
+ for b in self._boards:
446
+ if b._openOnLoad:
447
+ hasStart = True
448
+
449
+ # Make sure at least one map is loaded
450
+ if not hasStart and len(self._boards) > 0:
451
+ self._boards[0]._openOnLoad = True
452
+
453
+ return {b._serial: b.toDict(boardManager) for b in self._boards }
454
+
455
+ def __str__(self):
456
+ pl = '\n '.join([str(s) for s in self._boards])
457
+ return f'GSNBoardManager: {self._nextGeoSerial}\n {pl}\n'
458
+
459
+ #
460
+ # EOF
461
+ #
462
+
@@ -0,0 +1,200 @@
1
+ ## BEGIN_IMPORT
2
+ from common import VerboseGuard
3
+ from . image import GBXImage
4
+ from . draw import GBXDraw
5
+ ## END_IMPORT
6
+
7
+ # ====================================================================
8
+ class GBXCellGeometry:
9
+ RECTANGLE = 0
10
+ HORIZONTAL_BRICK = 1
11
+ VERTICAL_BRICK = 2
12
+ HEXAGON = 3
13
+ SIDEWAYS_HEXAGON = 4
14
+ STAGGER_OUT = 0
15
+ STAGGER_IN = 1
16
+ TYPES = {
17
+ RECTANGLE : 'rectangle',
18
+ HORIZONTAL_BRICK: 'horizontal brick',
19
+ VERTICAL_BRICK : 'vertical brick',
20
+ HEXAGON : 'hexagon',
21
+ SIDEWAYS_HEXAGON: 'sideways hexagon'
22
+ }
23
+ STAGGERS = {
24
+ STAGGER_OUT: 'out',
25
+ STAGGER_IN: 'in'
26
+ }
27
+
28
+ def __init__(self,ar):
29
+ '''The geometry of cells'''
30
+ with VerboseGuard('Reading cell geometry'):
31
+ from numpy import max
32
+
33
+ self._type = ar.word()
34
+ self._stagger = ar.word()
35
+ self._left = ar.word()
36
+ self._top = ar.word()
37
+ self._right = ar.word()
38
+ self._bottom = ar.word()
39
+ n = 7 if self._type > 2 else 5
40
+ self._points = [[ar.word(),ar.word()] for _ in range(n)]
41
+ size = max(self._points,axis=0)
42
+ self._dx = int(size[0])
43
+ self._dy = int(size[1])
44
+ self._size = [self._dx,self._dy]
45
+
46
+ if self._type == self.HEXAGON:
47
+ self._dx = int(0.75 * self._dx)
48
+ elif self._type == self.SIDEWAYS_HEXAGON:
49
+ self._dy = int(0.75 * self._dy)
50
+
51
+ def toDict(self):
52
+ from numpy import max
53
+ return {'shape': self.TYPES.get(self._type,''),
54
+ 'stagger': self.STAGGERS.get(self._stagger,''),
55
+ 'size': self._size,
56
+ 'bounding box (ltrb)':
57
+ (self._left, self._top, self._right, self._bottom),
58
+ 'points': self._points }
59
+
60
+ def svgDef(self,dwg):
61
+ with VerboseGuard('Defining SVG cell geometry'):
62
+ if self._type in [0,1,2]:
63
+ return dwg.defs.add(dwg.rect(id='cell',
64
+ size=(self._right-self._left,
65
+ self._bottom-self._top)))
66
+
67
+ return dwg.defs.add(dwg.polygon(self._points,id='cell'))
68
+
69
+ def translate(self,row,col,center=False):
70
+ x = col * self._dx
71
+ y = row * self._dy
72
+ if self._type == self.RECTANGLE: # No offset for rectangles
73
+ return x,y
74
+ if self._type in [self.HORIZONTAL_BRICK,self.SIDEWAYS_HEXAGON]:
75
+ x += self._dx//2 if (row % 2) != self._stagger else 0
76
+ if self._type in [self.VERTICAL_BRICK,self.HEXAGON]:
77
+ y += self._dy//2 if (col % 2) != self._stagger else 0
78
+ if center:
79
+ x += self._size[0]//2
80
+ y += self._size[1]//2
81
+ return x,y
82
+
83
+ def inverse(self,x,y):
84
+ col = x / self._dx
85
+ row = y / self._dy
86
+ if self._type in [self.HORIZONTAL_BRICK,self.SIDEWAYS_HEXAGON]:
87
+ col -= .5 if (int(row) % 2) != self._stagger else 0
88
+ if self._type in [self.VERTICAL_BRICK,self.HEXAGON]:
89
+ row -= .5 if (int(col) % 2) != self._stagger else 0
90
+
91
+ # CyberBoard start at 1
92
+ return int(row)+1, int(col)+1
93
+
94
+
95
+ def boardSize(self,nrows,ncols):
96
+ w = ncols * self._dx
97
+ h = nrows * self._dy
98
+
99
+ if self._type in [2,3]:
100
+ h += self._dy // 2
101
+ if self._type in [1,4]:
102
+ w += self._dx // 2
103
+ if self._type == 3:
104
+ w += self._dx // 3
105
+ if self._type == 4:
106
+ h += self._dy // 3
107
+
108
+ return w+1,h+1
109
+
110
+ def __str__(self):
111
+ return (f'type: {self.TYPES.get(self._type,"")} '
112
+ + f'stagger: {self.STAGGERS.get(self._stagger,"")} '
113
+ + f'({self._left},{self._top})x({self._right},{self._bottom}) '
114
+ + f': [{self._points}]')
115
+
116
+
117
+ # --------------------------------------------------------------------
118
+ class GBXCell:
119
+ def __init__(self,ar,row,column):
120
+ '''A single cell'''
121
+ with VerboseGuard(f'Reading cell row={row} column={column}'):
122
+ self._row = row
123
+ self._column = column
124
+ if Features().id_size == 4:
125
+ self._is_tile = ar.byte();
126
+ self._tile = ar.dword()
127
+ if Features().id_size != 4:
128
+ self._is_tile = (self._tile >> 16) == 0xFFFF;
129
+ if self._is_tile:
130
+ self._tile = self._tile & 0xFFFF
131
+
132
+ def tileID(self):
133
+ if not self._is_tile:
134
+ return None
135
+ return self._tile
136
+
137
+ def color(self):
138
+ if self._is_tile:
139
+ return None
140
+ return GBXDraw.hex(self._tile)
141
+
142
+ def toDict(self,tileManager,calc=None):
143
+ d = {'row': self._row,
144
+ 'column': self._column}
145
+ if not self._is_tile:
146
+ d['color'] = GBXDraw.hex(self._tile)
147
+ else:
148
+ d['tile'] = tileManager.store(self._tile)
149
+ if calc is not None:
150
+ d['pixel'] = calc.translate(self._row,self._column,True)
151
+ return d
152
+
153
+
154
+ def svgDef(self,dwg,tileManager,ptm):
155
+ tileID = self.tileID()
156
+ if tileID is None:
157
+ return
158
+
159
+ if tileID in ptm: # Have it
160
+ return
161
+
162
+ with VerboseGuard(f'Defining SVG pattern'):
163
+ img = tileManager.image(tileID)
164
+ data = GBXImage.b64encode(img)
165
+ if data is None:
166
+ return
167
+
168
+ iden = f'terrain_{tileID:04x}'
169
+ pat = dwg.defs.add(dwg.pattern(id=iden,
170
+ size=(img.width,img.height)))
171
+ pat.add(dwg.image(href=(data)))
172
+ ptm[tileID] = pat
173
+
174
+ def svg(self,dwg,g,cell,geom,ptm):
175
+ tileID = self.tileID()
176
+ if tileID is not None:
177
+ fill = ptm[tileID].get_paint_server()
178
+ else:
179
+ fill = self.color()
180
+
181
+ trans = geom.translate(self._row,self._column)
182
+ iden = f'cell_bg_{self._column:03d}{self._row:03d}'
183
+ g.add(dwg.use(cell,insert=trans,fill=fill,id=iden))
184
+
185
+ def svgFrame(self,dwg,g,cell,geom,color):
186
+ trans = geom.translate(self._row,self._column)
187
+ iden = f'cell_fg_{self._column:03d}{self._row:03d}'
188
+ g.add(dwg.use(cell,
189
+ insert=trans,
190
+ stroke=GBXDraw.hex(color),
191
+ fill='none',
192
+ id=iden))
193
+
194
+
195
+ def __str__(self):
196
+ return f'({self._row:02d},{self._column:02d}): {self._tile:08x}'
197
+
198
+ #
199
+ # EOF
200
+ #