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,323 @@
1
+ # ====================================================================
2
+ #
3
+ # Wrapper around a module
4
+ #
5
+ ## BEGIN_IMPORT
6
+ from . xml import xmlns
7
+ ## END_IMPORT
8
+
9
+ class VMod:
10
+ BUILD_FILE = 'buildFile.xml'
11
+ BUILD_FILE_SANS = 'buildFile'
12
+ MODULE_DATA = 'moduledata'
13
+ EXTENSION_DATA = 'extensiondata'
14
+
15
+ def __init__(self,filename,mode):
16
+ '''Interface to VASSAL Module (a Zip file)'''
17
+ self._mode = mode
18
+ self._vmod = self._open(filename,mode)
19
+
20
+ def __enter__(self):
21
+ '''Enter context'''
22
+ return self
23
+
24
+ def __exit__(self,*e):
25
+ '''Exit context'''
26
+ self._vmod.close()
27
+ return None
28
+
29
+ def _open(self,filename,mode):
30
+ '''Open a file in VMod'''
31
+ from zipfile import ZipFile, ZIP_DEFLATED
32
+
33
+ return ZipFile(filename,mode,compression=ZIP_DEFLATED)
34
+
35
+ def removeFiles(self,*filenames):
36
+ '''Open a temporary zip file, and copy content from there to
37
+ that file, excluding filenames mentioned in the arguments.
38
+ Then close current file, rename the temporary file to this,
39
+ and reopen in 'append' mode. The deleted files are returned
40
+ as a dictionary.
41
+
42
+ Parameters
43
+ ----------
44
+ filenames : tuple
45
+ List of files to remove from the VMOD
46
+
47
+ Returns
48
+ -------
49
+ files : dict
50
+ Dictionary from filename to content of the removed files.
51
+
52
+ Note, the VMOD is re-opened in append mode after this
53
+ '''
54
+ from tempfile import mkdtemp
55
+ from zipfile import ZipFile
56
+ from shutil import move, rmtree
57
+ from os import path
58
+
59
+ tempdir = mkdtemp()
60
+ ret = {}
61
+
62
+ try:
63
+ tempname = path.join(tempdir, 'new.zip')
64
+ with self._open(tempname, 'w') as tmp:
65
+
66
+ for item in self._vmod.infolist():
67
+ data = self._vmod.read(item.filename)
68
+
69
+ if item.filename not in filenames:
70
+ tmp.writestr(item, data)
71
+ else:
72
+ ret[item.filename] = data
73
+
74
+ name = self._vmod.filename
75
+ self._vmod.close()
76
+ move(tempname, name)
77
+
78
+ self._mode = 'a'
79
+ self._vmod = self._open(name,'a')
80
+ finally:
81
+ rmtree(tempdir)
82
+
83
+ # Return the removed files
84
+ return ret
85
+
86
+ def clone(self,newname,mode='a',filter=lambda f:False):
87
+ '''Clones the VMod and returns new object.
88
+
89
+ This is done by first opening a temporary ZIP file, and then
90
+ copy all files of this module to that tempoary. Then the
91
+ temporary ZIP is closed and moved to its `newname`. After
92
+ that, we open it up as a VMod (write-enabled).
93
+
94
+ '''
95
+ from tempfile import mkdtemp
96
+ from zipfile import ZipFile
97
+ from shutil import move, rmtree
98
+ from os import path
99
+
100
+ tempdir = mkdtemp()
101
+ ret = None
102
+
103
+ try:
104
+ tempname = path.join(tempdir, 'new.zip')
105
+ with self._open(tempname, 'w') as tmp:
106
+
107
+ for item in self._vmod.infolist():
108
+ # Ignore some files, a given by filter functoin
109
+ if filter(item.filename):
110
+ continue
111
+
112
+ data = self._vmod.read(item.filename)
113
+
114
+ tmp.writestr(item, data)
115
+
116
+ move(tempname, newname)
117
+
118
+ ret = VMod(newname,mode)
119
+ finally:
120
+ rmtree(tempdir)
121
+
122
+ return ret
123
+
124
+
125
+ def fileName(self):
126
+ '''Get name of VMod file'''
127
+ return self._vmod.filename
128
+
129
+ def replaceFiles(self,**files):
130
+ '''Replace existing files with new files
131
+
132
+ Parameters
133
+ ----------
134
+ files : dict
135
+ Dictionary that maps file name to content
136
+ '''
137
+ self.removeFiles(*list(files.keys()))
138
+
139
+ self.addFiles(**files);
140
+
141
+ def addFiles(self,**files):
142
+ '''Add a set of files to this
143
+
144
+ Parameters
145
+ ----------
146
+ files : dict
147
+ Dictionary that maps file name to content.
148
+ '''
149
+ for filename,data in files.items():
150
+ self.addFile(filename,data)
151
+
152
+ def addFile(self,filename,content):
153
+ '''Add a file to this
154
+
155
+ Parameters
156
+ ----------
157
+ filename : str
158
+ File name in module
159
+ content : str
160
+ File content
161
+
162
+ Returns
163
+ -------
164
+ element : File
165
+ The added element
166
+ '''
167
+ self._vmod.writestr(filename,content)
168
+
169
+ def addExternalFile(self,filename,target=None):
170
+ '''Add an external file element to this
171
+
172
+ Parameters
173
+ ----------
174
+ kwargs : dict
175
+ Dictionary of attribute key-value pairs
176
+
177
+ Returns
178
+ -------
179
+ element : ExternalFile
180
+ The added element
181
+ '''
182
+ if target is None: target = filename
183
+ self._vmod.write(filename,target)
184
+
185
+ def getFileNames(self):
186
+ '''Get all filenames in module'''
187
+ return self._vmod.namelist()
188
+
189
+ def getFileMapping(self):
190
+ '''Get mapping from short name to full archive name'''
191
+ from pathlib import Path
192
+
193
+ names = self.getFileNames()
194
+
195
+ return {Path(p).stem: str(p) for p in names}
196
+
197
+ def getFiles(self,*filenames):
198
+ '''Return named files as a dictionary.
199
+
200
+ Parameters
201
+ ----------
202
+ filenames : tuple
203
+ The files to get
204
+
205
+ Returns
206
+ -------
207
+ files : dict
208
+ Mapping of file name to file content
209
+ '''
210
+ fn = self.getFileNames()
211
+ ret = {}
212
+ for f in filenames:
213
+ if f not in fn:
214
+ continue
215
+
216
+ ret[f] = self._vmod.read(f)
217
+
218
+ return ret
219
+
220
+ def getDOM(self,filename):
221
+ '''Get content of a file decoded as XML DOM
222
+
223
+ Parameters
224
+ ----------
225
+ filename : str
226
+ Name of file in module
227
+ '''
228
+ #from xmlns import parseString
229
+
230
+ r = self.getFiles(filename)
231
+ if filename not in r:
232
+ raise RuntimeError(f'No {filename} found!')
233
+
234
+ return xmlns.parseString(r[filename])
235
+
236
+ def getBuildFile(self):
237
+ '''Get the buildFile.xml decoded as a DOM tree'''
238
+ try:
239
+ return self.getDOM(VMod.BUILD_FILE)
240
+ except Exception as e:
241
+ print(e)
242
+ try:
243
+ return self.getDOM(VMod.BUILD_FILE_SANS)
244
+ except:
245
+ raise
246
+
247
+ def getModuleData(self):
248
+ '''Get the moduledata decoded as a DOM tree'''
249
+ return self.getDOM(VMod.MODULE_DATA)
250
+
251
+ def getExtensionData(self):
252
+ '''Get the moduledata decoded as a DOM tree'''
253
+ return self.getDOM(VMod.EXTENSION_DATA)
254
+
255
+ def isExtension(self):
256
+ return VMod.EXTENSION_DATA in self.getFileNames()
257
+
258
+ def getInternalFile(self,filename,mode):
259
+ return self._vmod.open(filename,mode)
260
+
261
+ def addVSav(self,build):
262
+ '''Add a `VSav` element to this
263
+
264
+ Parameters
265
+ ----------
266
+ kwargs : dict
267
+ Dictionary of attribute key-value pairs
268
+
269
+ Returns
270
+ -------
271
+ element : VSav
272
+ The added element
273
+ '''
274
+ return VSav(build=build,vmod=self)
275
+
276
+ @classmethod
277
+ def patch(cls,vmod_filename,patch_name,verbose):
278
+ '''Patch a module with a Python script
279
+
280
+ Parameters
281
+ ----------
282
+ vmod_filename : str
283
+ File name of module to patch. Will be overwritten
284
+ patch_name : str
285
+ File name of Python script to patch with
286
+ verbose : bool
287
+ Whether to be verbose or not
288
+ '''
289
+ ## BEGIN_IMPORT
290
+ from common import VerboseGuard, Verbose
291
+ ## END_IMPORT
292
+
293
+ with cls(vmod_filename,'r') as vmod:
294
+ buildFile = BuildFile(vmod.getBuildFile())
295
+ moduleData = ModuleData(vmod.getModuleData())
296
+
297
+ from importlib.util import spec_from_file_location, module_from_spec
298
+ from pathlib import Path
299
+ from sys import modules
300
+
301
+ p = Path(patch_name)
302
+
303
+ spec = spec_from_file_location(p.stem, p.absolute())
304
+ module = module_from_spec(spec)
305
+ spec.loader.exec_module(module)
306
+
307
+ modules[p.stem] = module
308
+
309
+ with cls(vmod_filename,'a') as vmod:
310
+ module.patch(buildFile,
311
+ moduleData,
312
+ vmod,
313
+ verbose)
314
+
315
+ vmod.replaceFiles(**{VMod.BUILD_FILE :
316
+ buildFile.encode(),
317
+ VMod.MODULE_DATA :
318
+ moduleData.encode()})
319
+
320
+
321
+ #
322
+ # EOF
323
+ #
@@ -0,0 +1,100 @@
1
+ ## BEGIN_IMPORT
2
+ from . save import SaveData, SaveFile, SaveIO
3
+ from . moduledata import ModuleData
4
+ from . vmod import VMod
5
+ ## END_IMPORT
6
+
7
+ # --------------------------------------------------------------------
8
+ class VSav:
9
+ SAVE_DATA = 'savedata'
10
+
11
+ def __init__(self,build,vmod):
12
+ '''Create a VASSAL save file programmatically
13
+
14
+ Parameters
15
+ ----------
16
+ build : xml.dom.Document
17
+ `buildFile.xml` as XML
18
+ vmod : VMod
19
+ Module file
20
+ '''
21
+ from time import time
22
+ self._vmod = vmod
23
+ self._game = build.getGame()
24
+ self._start = int(time()*1000)
25
+
26
+
27
+ def createSaveData(self,description=None):
28
+ '''Create `savedgame`'''
29
+ desc = (self._game['description']
30
+ if description is None else description)
31
+ self._saveData = SaveData(root=None)
32
+ data = self._saveData.addData()
33
+ data.addVersion (version =self._game['version'])
34
+ data.addVASSALVersion(version =self._game['VassalVersion'])
35
+ data.addDescription (description=desc)
36
+ data.addDateSaved (milisecondsSinceEpoch=self._start)
37
+ return self._saveData
38
+
39
+ def createModuleData(self):
40
+ '''Create `moduleData`'''
41
+ self._moduleData = ModuleData()
42
+ data = self._moduleData.addData()
43
+ data.addVersion (version =self._game['version'])
44
+ data.addVASSALVersion(version =self._game['VassalVersion'])
45
+ data.addName (name =self._game['name'])
46
+ data.addDescription (description=self._game['description'])
47
+ data.addDateSaved (milisecondsSinceEpoch=self._start)
48
+ return self._moduleData
49
+
50
+ def addSaveFile(self):
51
+ '''Add a save file to the module
52
+
53
+ Returns
54
+ -------
55
+ vsav : SaveFile
56
+ Save file to add content to
57
+ '''
58
+ self._saveFile = SaveFile(game=self._game,firstid=self._start)
59
+ return self._saveFile
60
+
61
+ def run(self,
62
+ savename = 'Save.vsav',
63
+ description = None,
64
+ update = None):
65
+ '''Run this to generate the save file
66
+
67
+ Parameters
68
+ ----------
69
+ savename : str
70
+ Name of save file to write
71
+ description : str
72
+ Short description of the save file
73
+ update : callable or None
74
+ A callable that can update trait states after the piece
75
+ traits have been fully flattened. The callable should
76
+ adhere to the interface
77
+
78
+ update(name,traits)
79
+
80
+ where `name` is the name of the piece (entryName) and
81
+ `traits` is a list of unrolled traits.
82
+
83
+ '''
84
+ from zipfile import ZipFile, ZIP_DEFLATED
85
+
86
+ self.createSaveData(description=description)
87
+ self.createModuleData()
88
+
89
+ with self._vmod.getInternalFile(savename,'w') as vsav:
90
+ with ZipFile(vsav,'w',ZIP_DEFLATED) as zvsav:
91
+ # The key is set to 0xAA (alternating ones and zeros)
92
+ SaveIO.writeInZip(zvsav,0xAA,
93
+ self._saveFile.getLines(update=update))
94
+
95
+ zvsav.writestr(VMod.MODULE_DATA, self._moduleData.encode())
96
+ zvsav.writestr(VSav.SAVE_DATA, self._saveData.encode())
97
+
98
+ #
99
+ # EOF
100
+ #