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,154 @@
|
|
1
|
+
## BEGIN_IMPORTS
|
2
|
+
from base import DomInspect
|
3
|
+
from map import ZTMap
|
4
|
+
from piece import ZTPiece
|
5
|
+
from countersheet import ZTCounterSheet
|
6
|
+
from dicehand import ZTDiceHand
|
7
|
+
from common import VerboseGuard
|
8
|
+
from scenario import ZTScenario
|
9
|
+
## END_IMPORTS
|
10
|
+
|
11
|
+
# --------------------------------------------------------------------
|
12
|
+
class ZTGameBox(DomInspect):
|
13
|
+
|
14
|
+
def __init__(self,file):
|
15
|
+
from zipfile import ZipFile
|
16
|
+
from xml.dom.minidom import parse
|
17
|
+
|
18
|
+
with VerboseGuard('Reading gamebox') as v:
|
19
|
+
self._zip = ZipFile(file,'r')
|
20
|
+
|
21
|
+
with self._zip.open('game-box.xml','r') as xml:
|
22
|
+
self._gamebox =parse(xml)
|
23
|
+
|
24
|
+
self._root = self._gamebox.firstChild
|
25
|
+
self._name = self._get_attr(self._root,'name')
|
26
|
+
self._desc = self._get_attr(self._root,'description')
|
27
|
+
self._copy = self._get_attr(self._root,'copyright')
|
28
|
+
self._start = self._get_attr(self._root,'startup-scenario')
|
29
|
+
self._icon = self._get_attr(self._root,'icon',None)
|
30
|
+
self._splash = None
|
31
|
+
self._sheets_map = {}
|
32
|
+
|
33
|
+
if not self._icon and 'icon.bmp' in self._zip.namelist():
|
34
|
+
self._icon = 'icon.bmp'
|
35
|
+
v(f'Icon is {self._icon}')
|
36
|
+
|
37
|
+
if self._icon:
|
38
|
+
self._splash = self._read_image(self._zip, self._icon)
|
39
|
+
v(f'Got splash screen {self._icon}: {self._splash}')
|
40
|
+
|
41
|
+
self._dice_hands = [
|
42
|
+
self.parse_dice_hand(dh)
|
43
|
+
for dh in self._root.getElementsByTagName('dice-hand')]
|
44
|
+
# IF no dice was defined add a 1d6
|
45
|
+
if len(self._dice_hands) <= 0:
|
46
|
+
self._dice_hands = [ ZTDiceHand(None) ]
|
47
|
+
|
48
|
+
self._maps = [
|
49
|
+
self.parse_map(mp)
|
50
|
+
for mp in self._root.getElementsByTagName('map')]
|
51
|
+
|
52
|
+
self._scenarios = [
|
53
|
+
self.parse_scenario(fn)
|
54
|
+
for fn in self._zip.namelist() if fn.endswith('.zts')]
|
55
|
+
|
56
|
+
# counter-sheet or terrain-sheet
|
57
|
+
self._counter_sheets = [
|
58
|
+
self.parse_counter_sheet(sh)
|
59
|
+
for sh in self._find_children(self._root,
|
60
|
+
'counter-sheet',
|
61
|
+
'terrain-sheet')]
|
62
|
+
|
63
|
+
self._pieces = sum([c._piece for c in self._counter_sheets],[])
|
64
|
+
self._cards = sum([c._card for c in self._counter_sheets],[])
|
65
|
+
self.map_pieces()
|
66
|
+
|
67
|
+
def parse_dice_hand(self,elem):
|
68
|
+
return ZTDiceHand(elem)
|
69
|
+
|
70
|
+
def parse_map(self,elem):
|
71
|
+
m = ZTMap(elem)
|
72
|
+
m.read_image(self._zip)
|
73
|
+
# self._master[m._name] = m
|
74
|
+
return m
|
75
|
+
|
76
|
+
def parse_counter_sheet(self,elem):
|
77
|
+
s = ZTCounterSheet(elem)
|
78
|
+
s.read_image(self._zip)
|
79
|
+
s.make_pieces()
|
80
|
+
self._sheets_map[s._name] = s
|
81
|
+
return s
|
82
|
+
|
83
|
+
def parse_scenario(self,fn):
|
84
|
+
with self._zip.open(fn,'r') as inp:
|
85
|
+
return ZTScenario(inp)
|
86
|
+
|
87
|
+
def write_image(self,dir,name,image):
|
88
|
+
if image is None:
|
89
|
+
return
|
90
|
+
|
91
|
+
fn = dir / f'{name}.{image.format.lower()}'
|
92
|
+
print(f'Writing {fn}')
|
93
|
+
image.save(filename=fn)
|
94
|
+
|
95
|
+
def map_pieces(self):
|
96
|
+
with VerboseGuard(f'Mapping pieces to ID and back'):
|
97
|
+
# Cards and pieces are intermingled
|
98
|
+
tmp = sum([c._piece+c._card for c in self._counter_sheets],[])
|
99
|
+
self._piece_map = {
|
100
|
+
i: p for i,p in enumerate(tmp) }
|
101
|
+
self._piece_id = {
|
102
|
+
v: k for k,v in self._piece_map.items()}
|
103
|
+
|
104
|
+
|
105
|
+
def write_images(self):
|
106
|
+
from pathlib import Path
|
107
|
+
|
108
|
+
dir = Path('images')
|
109
|
+
dir.mkdir(0o755,parents=True,exist_ok=True)
|
110
|
+
|
111
|
+
for m in self._maps:
|
112
|
+
if not m._image:
|
113
|
+
print(f'Missing image for map {m._name}')
|
114
|
+
continue
|
115
|
+
|
116
|
+
self.write_image(dir,m._name,m._image._image)
|
117
|
+
|
118
|
+
for i,p in enumerate(self._pieces):
|
119
|
+
for img,nam in zip([p._front,p._back],
|
120
|
+
['front','back']):
|
121
|
+
|
122
|
+
self.write_image(dir,f'{i:04d}_{nam}',img)
|
123
|
+
|
124
|
+
for i,p in enumerate(self._cards):
|
125
|
+
for img,nam in zip([p._front,p._back],
|
126
|
+
['front','back']):
|
127
|
+
|
128
|
+
self.write_image(dir,f'c{i:04d}_{nam}',img)
|
129
|
+
|
130
|
+
for s in self._counter_sheets:
|
131
|
+
name = s._name
|
132
|
+
for fac,nam in zip([s._front,s._back],
|
133
|
+
['front','back']):
|
134
|
+
if not fac:
|
135
|
+
continue
|
136
|
+
|
137
|
+
self.write_image(dir,f'{name}_{nam}',fac._image)
|
138
|
+
|
139
|
+
|
140
|
+
def __str__(self):
|
141
|
+
return (f'{self._name}'+'\n'+
|
142
|
+
f' Description: {self._desc}'+'\n'+
|
143
|
+
f' Copyright: {self._copy}'+'\n'+
|
144
|
+
f' Start: {self._start}'+'\n'+
|
145
|
+
f' Icon: {self._icon}'+
|
146
|
+
('\n' if len(self._dice_hands)>0 else '')+
|
147
|
+
'\n'.join([str(d) for d in self._dice_hands])+
|
148
|
+
('\n' if len(self._maps)>0 else '')+
|
149
|
+
'\n'.join([str(m) for m in self._maps])+
|
150
|
+
('\n' if len(self._counter_sheets)>0 else '')+
|
151
|
+
'\n'.join([str(s) for s in self._counter_sheets]))
|
152
|
+
#
|
153
|
+
# EOF
|
154
|
+
#
|
pywargame/zuntzu/map.py
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
## BEGIN_IMPORTS
|
2
|
+
from base import DomInspect, ZTImage
|
3
|
+
from common import VerboseGuard
|
4
|
+
## END_IMPORTS
|
5
|
+
|
6
|
+
# --------------------------------------------------------------------
|
7
|
+
class ZTMap(DomInspect):
|
8
|
+
def __init__(self,elem):
|
9
|
+
with VerboseGuard(f'Got a map'):
|
10
|
+
self._name = self._get_attr(elem,'name')
|
11
|
+
self._image = ZTImage(elem)
|
12
|
+
|
13
|
+
def read_image(self,zf):
|
14
|
+
with VerboseGuard(f'Reading map ({self._name}) image') as v:
|
15
|
+
self._image.read_image(zf)
|
16
|
+
sc = ZTImage.target_dpi / self._image._reso
|
17
|
+
v(f'Scale by {sc}={ZTImage.target_dpi}/{self._image._reso} '
|
18
|
+
f'from {self._image._image.width}x{self._image._image.height}')
|
19
|
+
self._image._image.resize(int(sc*self._image._image.width),
|
20
|
+
int(sc*self._image._image.height))
|
21
|
+
v(f'Scaled by {sc} to {self._image._image.width}x'
|
22
|
+
f'{self._image._image.height}')
|
23
|
+
|
24
|
+
@property
|
25
|
+
def filename(self):
|
26
|
+
return f'{self._name.replace(" ","_")}.{self._image._image.format.lower()}'
|
27
|
+
|
28
|
+
@property
|
29
|
+
def size(self):
|
30
|
+
return self._image._image.width,self._image._image.height
|
31
|
+
|
32
|
+
def __str__(self):
|
33
|
+
return f' Map: name={self._name}'+'\n'+str(self._image)
|
34
|
+
#
|
35
|
+
# EOF
|
36
|
+
#
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# --------------------------------------------------------------------
|
2
|
+
class ZTPiece:
|
3
|
+
def __init__(self,front_image,back_image,x,y,terrain,card):
|
4
|
+
self._front = front_image
|
5
|
+
self._back = back_image
|
6
|
+
self._terrain = terrain
|
7
|
+
self._card = card
|
8
|
+
self._x = x
|
9
|
+
self._y = y
|
10
|
+
|
11
|
+
@property
|
12
|
+
def two_sides(self):
|
13
|
+
return (self._front is not None and
|
14
|
+
self._back is not None)
|
15
|
+
|
16
|
+
@property
|
17
|
+
def size(self):
|
18
|
+
return self.width,self.height
|
19
|
+
|
20
|
+
@property
|
21
|
+
def width(self):
|
22
|
+
return self._front.width
|
23
|
+
|
24
|
+
@property
|
25
|
+
def height(self):
|
26
|
+
return self._front.height
|
27
|
+
|
28
|
+
|
29
|
+
def __str__(self):
|
30
|
+
return (f' Piece: '+
|
31
|
+
f'{"card" if self._card else "piece"} '+
|
32
|
+
f'{"terrain" if self._terrain else "piece"} '+
|
33
|
+
f'front={self._front} back={self._back}')
|
34
|
+
|
35
|
+
#
|
36
|
+
# EOF
|
37
|
+
#
|
@@ -0,0 +1,208 @@
|
|
1
|
+
## BEGIN_IMPORTS
|
2
|
+
from common import VerboseGuard
|
3
|
+
from base import DomInspect
|
4
|
+
## END_IMPORTS
|
5
|
+
|
6
|
+
# --------------------------------------------------------------------
|
7
|
+
class ZTStack:
|
8
|
+
def __init__(self,x,y,ids,flp,rot):
|
9
|
+
self._x = x
|
10
|
+
self._y = y
|
11
|
+
self._ids = ids
|
12
|
+
self._flip = flp
|
13
|
+
self._rot = rot
|
14
|
+
|
15
|
+
def translate(self,dx,dy):
|
16
|
+
self._x += dx
|
17
|
+
self._y += dy
|
18
|
+
|
19
|
+
def scale(self,f):
|
20
|
+
self._x *= f
|
21
|
+
self._y *= f
|
22
|
+
|
23
|
+
|
24
|
+
# --------------------------------------------------------------------
|
25
|
+
class ZTLayout(DomInspect):
|
26
|
+
def __init__(self,elem):
|
27
|
+
self._name = self._get_attr(elem,'board')
|
28
|
+
self._left = float(self._get_attr(elem,'left', -1))
|
29
|
+
self._top = float(self._get_attr(elem,'top', -1))
|
30
|
+
self._right = float(self._get_attr(elem,'right', -1))
|
31
|
+
self._bottom = float(self._get_attr(elem,'bottom',-1))
|
32
|
+
self._visible = self._get_attr(elem,'visible','true')=='true'
|
33
|
+
|
34
|
+
self._stacks = [self.parse_stack(s)
|
35
|
+
for s in self._find_children(elem,'counter','stack')]
|
36
|
+
|
37
|
+
@property
|
38
|
+
def ulx(self):
|
39
|
+
return self._left
|
40
|
+
|
41
|
+
@property
|
42
|
+
def lrx(self):
|
43
|
+
return self._right
|
44
|
+
|
45
|
+
@property
|
46
|
+
def uly(self):
|
47
|
+
return self._top
|
48
|
+
|
49
|
+
@property
|
50
|
+
def lry(self):
|
51
|
+
return self._bottom
|
52
|
+
|
53
|
+
@property
|
54
|
+
def left(self):
|
55
|
+
return int(self._left)
|
56
|
+
|
57
|
+
@property
|
58
|
+
def top(self):
|
59
|
+
return int(self._top)
|
60
|
+
|
61
|
+
@property
|
62
|
+
def width(self):
|
63
|
+
return int(self._right - self._left+.5)
|
64
|
+
|
65
|
+
@property
|
66
|
+
def height(self):
|
67
|
+
return int(self._bottom - self._top+.5)
|
68
|
+
|
69
|
+
def parse_stack(self,elem):
|
70
|
+
x = float(self._get_attr(elem,'x',0))
|
71
|
+
y = float(self._get_attr(elem,'y',0))
|
72
|
+
# We ignore the `rot` and `Side` attributes for now
|
73
|
+
# More difficult to propagate to the states, which are handled
|
74
|
+
# in VSav. Could perhaps add a mapping from traits to states
|
75
|
+
# when the traits are flattened there.
|
76
|
+
ids = []
|
77
|
+
flp = []
|
78
|
+
rot = []
|
79
|
+
if elem.tagName == 'counter':
|
80
|
+
ids.append(int(self._get_attr(elem,'id',-1)))
|
81
|
+
flp.append(self._get_attr(elem,'side','')=='Back')
|
82
|
+
rot.append(int(self._get_attr(elem,'rot',0)))
|
83
|
+
else:
|
84
|
+
ids.extend([int(self._get_attr(c,'id',-1))
|
85
|
+
for c in self._find_children(elem,'counter')])
|
86
|
+
flp.extend([self._get_attr(c,'side','')=='Back'
|
87
|
+
for c in self._find_children(elem,'counter')])
|
88
|
+
rot.extend([int(self._get_attr(c,'rot',0))
|
89
|
+
for c in self._find_children(elem,'counter')])
|
90
|
+
|
91
|
+
return ZTStack(x,y,ids,flp,rot)
|
92
|
+
|
93
|
+
def translate(self,dx,dy):
|
94
|
+
self._left += dx
|
95
|
+
self._right += dx
|
96
|
+
self._top += dy
|
97
|
+
self._bottom += dy
|
98
|
+
for s in self._stacks:
|
99
|
+
s.translate(dx,dy)
|
100
|
+
|
101
|
+
def scale(self,f):
|
102
|
+
self._left *= f
|
103
|
+
self._right *= f
|
104
|
+
self._top *= f
|
105
|
+
self._bottom *= f
|
106
|
+
for s in self._stacks:
|
107
|
+
s.scale(f)
|
108
|
+
|
109
|
+
def __str__(self):
|
110
|
+
return f'Layout "{self._name}" ({len(self._stacks)}: {self.ulx},{self.uly},{self.lrx},{self.lry}'
|
111
|
+
|
112
|
+
# --------------------------------------------------------------------
|
113
|
+
class ZTScenario(DomInspect):
|
114
|
+
def __init__(self,file):
|
115
|
+
from xml.dom.minidom import parse
|
116
|
+
from pathlib import Path
|
117
|
+
|
118
|
+
with VerboseGuard(f'Got a scenario {file}') as v:
|
119
|
+
self._dom = parse(file)
|
120
|
+
|
121
|
+
p = Path(file if isinstance(file,str) else file.name)
|
122
|
+
|
123
|
+
self._file = p.stem
|
124
|
+
self._root = self._dom.firstChild
|
125
|
+
self._game = self._get_attr(self._root,'game-box')
|
126
|
+
self._name = self._get_attr(self._root,'scenario-name')
|
127
|
+
self._desc = self._get_attr(self._root,'scenario-description','')
|
128
|
+
self._copy = self._get_attr(self._root,'scenario-copyright','')
|
129
|
+
|
130
|
+
self._layouts = [self.parse_layout(elem)
|
131
|
+
for elem in
|
132
|
+
self._root.getElementsByTagName('layout')]
|
133
|
+
|
134
|
+
self.ulx = None
|
135
|
+
self.uly = None
|
136
|
+
self.lrx = None
|
137
|
+
self.lry = None
|
138
|
+
|
139
|
+
v(f'Bounding box: {self.bounding_box}')
|
140
|
+
|
141
|
+
def parse_layout(self,elem):
|
142
|
+
return ZTLayout(elem)
|
143
|
+
|
144
|
+
def translate(self,dx,dy):
|
145
|
+
with VerboseGuard(f'Translating scenario by {dx},{dy}'):
|
146
|
+
self.ulx = None
|
147
|
+
self.uly = None
|
148
|
+
self.lrx = None
|
149
|
+
self.lry = None
|
150
|
+
|
151
|
+
for l in self._layouts:
|
152
|
+
l.translate(dx,dy)
|
153
|
+
|
154
|
+
def scale(self,f):
|
155
|
+
with VerboseGuard(f'Scaling scenario by {f}'):
|
156
|
+
self.ulx = None
|
157
|
+
self.uly = None
|
158
|
+
self.lrx = None
|
159
|
+
self.lry = None
|
160
|
+
|
161
|
+
for l in self._layouts:
|
162
|
+
l.scale(f)
|
163
|
+
|
164
|
+
def on_map(self,name):
|
165
|
+
return any([l._name == name for l in self._layouts])
|
166
|
+
|
167
|
+
def map_layout(self,name):
|
168
|
+
return [l for l in self._layouts if l._name == name]
|
169
|
+
|
170
|
+
def calc_bounding_box(self,name=None):
|
171
|
+
layouts = self._layouts if name is None else self.map_layout(name)
|
172
|
+
ulx = int(min([l.ulx for l in layouts])-.5)
|
173
|
+
uly = int(min([l.uly for l in layouts])-.5)
|
174
|
+
lrx = int(max([l.lrx for l in layouts])+.5)
|
175
|
+
lry = int(max([l.lry for l in layouts])+.5)
|
176
|
+
return ulx,uly,lrx,lry
|
177
|
+
|
178
|
+
@property
|
179
|
+
def bounding_box(self):
|
180
|
+
'''Cache calculations'''
|
181
|
+
if self.ulx is None or \
|
182
|
+
self.uly is None or \
|
183
|
+
self.lrx is None or \
|
184
|
+
self.lry is None:
|
185
|
+
self.ulx = int(min([l.ulx for l in self._layouts])-.5)
|
186
|
+
self.uly = int(min([l.uly for l in self._layouts])-.5)
|
187
|
+
self.lrx = int(max([l.lrx for l in self._layouts])+.5)
|
188
|
+
self.lry = int(max([l.lry for l in self._layouts])+.5)
|
189
|
+
|
190
|
+
return self.ulx,self.uly,self.lrx,self.lry
|
191
|
+
|
192
|
+
def __str__(self):
|
193
|
+
return f'Scenario "{self._name}": {self._desc}'+'\n'+\
|
194
|
+
'\n'.join([str(l) for l in self._layouts])
|
195
|
+
|
196
|
+
@property
|
197
|
+
def width(self):
|
198
|
+
x1,_,x2,_ = self.bounding_box
|
199
|
+
return x2-x1
|
200
|
+
@property
|
201
|
+
def height(self):
|
202
|
+
_,y1,_,y2 = self.bounding_box
|
203
|
+
return y2-y1
|
204
|
+
|
205
|
+
|
206
|
+
#
|
207
|
+
# EOF
|
208
|
+
#
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
## BEGIN_IMPORTS
|
3
|
+
from sys import path
|
4
|
+
path.append('..')
|
5
|
+
|
6
|
+
from gamebox import ZTGameBox
|
7
|
+
from exporter import ZTExporter
|
8
|
+
from common import Verbose
|
9
|
+
from base import ZTImage
|
10
|
+
## END_IMPORTS
|
11
|
+
|
12
|
+
if __name__ == '__main__':
|
13
|
+
from argparse import ArgumentParser, FileType, \
|
14
|
+
RawDescriptionHelpFormatter
|
15
|
+
from pathlib import Path
|
16
|
+
from textwrap import wrap, dedent
|
17
|
+
|
18
|
+
w = lambda s : '\n\n'.join(['\n'.join(wrap(p))
|
19
|
+
for p in dedent(s).split('\n\n')])
|
20
|
+
e = '''
|
21
|
+
If the script fails with "cache resources exhausted",
|
22
|
+
try reducing the resolution (-r).
|
23
|
+
|
24
|
+
If the ZunTzu game box does not have selectable
|
25
|
+
boards, use the -a option to add all boards to
|
26
|
+
the generated VASSAL module.
|
27
|
+
|
28
|
+
Pass a Python script with the option -p to do
|
29
|
+
user post-processing with that script. Use the
|
30
|
+
pywargame VASSAL API to do all sorts of manipulations.
|
31
|
+
'''
|
32
|
+
|
33
|
+
ap = ArgumentParser(description='Read in a ZunTzu GameBox',
|
34
|
+
formatter_class= RawDescriptionHelpFormatter,
|
35
|
+
epilog=w(e))
|
36
|
+
ap.add_argument('input', type=FileType('rb'),
|
37
|
+
help='Input ZunTzu GameBox')
|
38
|
+
ap.add_argument('--output', '-o', type=str, nargs='?',
|
39
|
+
default='', help='Output VASSAL module')
|
40
|
+
ap.add_argument('-p','--patch',
|
41
|
+
help='A python script to patch generated module',
|
42
|
+
type=FileType('r'))
|
43
|
+
ap.add_argument('-V','--verbose',
|
44
|
+
help='Be verbose',
|
45
|
+
action='store_true')
|
46
|
+
ap.add_argument('-r','--resolution',help='Set target DPI',type=int,
|
47
|
+
choices=[75,150,300,600],default=150)
|
48
|
+
ap.add_argument('-a','--all-maps',action='store_true',
|
49
|
+
help='Generate all maps')
|
50
|
+
ap.add_argument('-1','--one-map',action='store_false',dest='all_maps',
|
51
|
+
help='All maps are different versions of main map')
|
52
|
+
ap.add_argument('-R','--n-rotations',type=int,
|
53
|
+
help='Number of rotations to allow. Set to 1 for '
|
54
|
+
'arbitrary rotations',default=12)
|
55
|
+
ap.add_argument('-W','--vassal-version',
|
56
|
+
help='Vassal version number',
|
57
|
+
type=str,
|
58
|
+
default='3.7.0')
|
59
|
+
ap.add_argument('-S','--symbolic-dice',
|
60
|
+
help='Use symbolic dice',
|
61
|
+
action='store_true')
|
62
|
+
ap.add_argument('-T','--text-dice',
|
63
|
+
help='Use normal text dice',
|
64
|
+
dest='symbolic_dice',
|
65
|
+
action='store_false')
|
66
|
+
ap.add_argument('-v','--version',
|
67
|
+
help='Override version',
|
68
|
+
type=str,
|
69
|
+
default='0.0.1')
|
70
|
+
ap.set_defaults(symbolic_dice=True,
|
71
|
+
all_maps=False)
|
72
|
+
|
73
|
+
|
74
|
+
args = ap.parse_args()
|
75
|
+
|
76
|
+
Verbose().setVerbose(args.verbose)
|
77
|
+
|
78
|
+
ZTImage.target_dpi = args.resolution
|
79
|
+
output = args.output
|
80
|
+
patch = args.patch
|
81
|
+
if output == '':
|
82
|
+
p = Path(args.input.name)
|
83
|
+
output = p.with_suffix('.vmod')
|
84
|
+
if patch is not None:
|
85
|
+
tmp = patch.name
|
86
|
+
patch.close()
|
87
|
+
patch = tmp
|
88
|
+
|
89
|
+
gb = ZTGameBox(args.input)
|
90
|
+
# gb.write_images()
|
91
|
+
|
92
|
+
try:
|
93
|
+
exporter = ZTExporter(gb,
|
94
|
+
version=args.version,
|
95
|
+
vassalVersion=args.vassal_version,
|
96
|
+
allmaps=args.all_maps,
|
97
|
+
nrotations=args.n_rotations,
|
98
|
+
symbolic_dice=args.symbolic_dice)
|
99
|
+
exporter.run(output,patch)
|
100
|
+
except Exception as e:
|
101
|
+
from sys import stderr
|
102
|
+
from os import unlink
|
103
|
+
|
104
|
+
print(f'Failed to build {output}: {e}',file=stderr)
|
105
|
+
|
106
|
+
try:
|
107
|
+
unlink(vmodname)
|
108
|
+
except:
|
109
|
+
pass
|
110
|
+
|
111
|
+
raise e
|
112
|
+
|
113
|
+
#
|
114
|
+
# EOF
|
115
|
+
#
|