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/__init__.py
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from . verboseguard import VerboseGuard
|
3
|
+
from . verbose import Verbose
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
|
7
|
+
class Collector:
|
8
|
+
def __init__(self,executable=True):
|
9
|
+
'''This could be done with SED, but we don't need to rely on that'''
|
10
|
+
self._executable = executable
|
11
|
+
|
12
|
+
def readFile(self,filename):
|
13
|
+
'''Reads lines from a file and returns them.
|
14
|
+
|
15
|
+
Lines fenced by with `##` are not returned.
|
16
|
+
|
17
|
+
Lines starting with `#!` are not returned
|
18
|
+
'''
|
19
|
+
with VerboseGuard(f'Reading lines from {filename}') as g:
|
20
|
+
ret = [f'# {"="*68}\n'
|
21
|
+
f'# From {filename}\n' ]
|
22
|
+
with open(filename,'r') as file:
|
23
|
+
lines = file.readlines()
|
24
|
+
nlines = len(lines)
|
25
|
+
lineno = 0
|
26
|
+
|
27
|
+
g(f'{filename} has {nlines} lines')
|
28
|
+
while lineno < nlines:
|
29
|
+
line = lines[lineno]
|
30
|
+
if line.strip().startswith('##'):
|
31
|
+
start = line
|
32
|
+
startno = lineno
|
33
|
+
|
34
|
+
while lineno < nlines:
|
35
|
+
lineno += 1
|
36
|
+
if lineno >= nlines:
|
37
|
+
raise RuntimeError(f'At {filename}:{lineno}:'
|
38
|
+
f' No end to {start} in '
|
39
|
+
f'line {startno}')
|
40
|
+
|
41
|
+
line = lines[lineno]
|
42
|
+
if line.strip().startswith('##'):
|
43
|
+
g(f'Skipped lines {startno} to {lineno}')
|
44
|
+
break
|
45
|
+
lineno += 1
|
46
|
+
continue
|
47
|
+
|
48
|
+
if line.startswith('#!'):
|
49
|
+
g(f'Skipping line {lineno}')
|
50
|
+
lineno += 1
|
51
|
+
continue
|
52
|
+
|
53
|
+
ret.append(line)
|
54
|
+
lineno += 1
|
55
|
+
|
56
|
+
return ret
|
57
|
+
|
58
|
+
def run(self,output,*filenames):
|
59
|
+
lines = ['#!/usr/bin/env python\n',
|
60
|
+
'# Script collected from other scripts\n',
|
61
|
+
'#\n']
|
62
|
+
lines.extend([f'# {filename}\n' for filename in filenames])
|
63
|
+
lines.append('#\n')
|
64
|
+
|
65
|
+
|
66
|
+
for filename in filenames:
|
67
|
+
lines.extend(self.readFile(filename))
|
68
|
+
|
69
|
+
lines.extend(['##\n',
|
70
|
+
'# End of generated script\n'
|
71
|
+
'##\n'])
|
72
|
+
|
73
|
+
|
74
|
+
output.writelines(lines)
|
75
|
+
|
76
|
+
if not self._executable:
|
77
|
+
return
|
78
|
+
|
79
|
+
from os import chmod
|
80
|
+
chmod(output.name,0o755)
|
81
|
+
|
82
|
+
|
83
|
+
#
|
84
|
+
# EOF
|
85
|
+
#
|
86
|
+
|
87
|
+
|
@@ -0,0 +1,363 @@
|
|
1
|
+
# --------------------------------------------------------------------
|
2
|
+
class DiceContext:
|
3
|
+
def __init__(self,draw,darker=None):
|
4
|
+
self._draw = draw
|
5
|
+
self._darker = darker
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
def __enter__(self):
|
10
|
+
from wand.color import Color
|
11
|
+
self._draw._draw.push()
|
12
|
+
|
13
|
+
if self._darker:
|
14
|
+
r, g, b = (self._draw._draw.fill_color.red_int8,
|
15
|
+
self._draw._draw.fill_color.green_int8,
|
16
|
+
self._draw._draw.fill_color.blue_int8)
|
17
|
+
r *= self._darker
|
18
|
+
g *= self._darker
|
19
|
+
b *= self._darker
|
20
|
+
#print(r,g,b)
|
21
|
+
self._draw._draw.fill_color = \
|
22
|
+
Color(f'srgb({int(r)},{int(g)},{int(b)})')
|
23
|
+
|
24
|
+
return self._draw
|
25
|
+
|
26
|
+
def __exit__(self,*e):
|
27
|
+
self._draw._draw.pop()
|
28
|
+
|
29
|
+
# --------------------------------------------------------------------
|
30
|
+
class DicePath:
|
31
|
+
def __init__(self,draw):
|
32
|
+
self._draw = draw
|
33
|
+
|
34
|
+
def __enter__(self):
|
35
|
+
self._draw._draw.path_start()
|
36
|
+
return self
|
37
|
+
|
38
|
+
def __exit__(self,*e):
|
39
|
+
self._draw._draw.path_finish()
|
40
|
+
|
41
|
+
def move(self,to):
|
42
|
+
self._draw._draw.path_move(to=(self._draw.x(to[0]),
|
43
|
+
self._draw.y(to[1])))
|
44
|
+
|
45
|
+
def line(self,to):
|
46
|
+
self._draw._draw.path_line(to=(self._draw.x(to[0]),
|
47
|
+
self._draw.y(to[1])))
|
48
|
+
|
49
|
+
def arc(self,to,r,cw=True):
|
50
|
+
self._draw._draw.path_elliptic_arc(to=(self._draw.x(to[0]),
|
51
|
+
self._draw.y(to[1])),
|
52
|
+
radius=(self._draw.x(r),
|
53
|
+
self._draw.y(r)),
|
54
|
+
clockwise=cw)
|
55
|
+
|
56
|
+
# --------------------------------------------------------------------
|
57
|
+
class DiceDraw:
|
58
|
+
def __init__(self,width=100,height=100,fg='black',bg='white'):
|
59
|
+
from wand.drawing import Drawing
|
60
|
+
from wand.color import Color
|
61
|
+
|
62
|
+
self._width = width
|
63
|
+
self._height = height
|
64
|
+
self._fg = fg if isinstance(fg,Color) else Color(fg)
|
65
|
+
self._bg = bg if isinstance(bg,Color) else Color(bg)
|
66
|
+
self._draw = Drawing()
|
67
|
+
self._size = min(self._width,self._height)
|
68
|
+
|
69
|
+
@property
|
70
|
+
def size(self):
|
71
|
+
return self._size
|
72
|
+
|
73
|
+
def x(self,xx):
|
74
|
+
return int((xx + 0.5) * self.size)
|
75
|
+
|
76
|
+
def y(self,yy):
|
77
|
+
return int((0.5 - yy) * self.size)
|
78
|
+
|
79
|
+
def __enter__(self):
|
80
|
+
self._draw.stroke_width = max(1,self.size//75)
|
81
|
+
self._draw.stroke_color = self._fg
|
82
|
+
self._draw.fill_color = self._bg
|
83
|
+
return self
|
84
|
+
|
85
|
+
def number(self,num,yoff=0,scale=1):
|
86
|
+
if num is None or num == '':
|
87
|
+
return
|
88
|
+
|
89
|
+
with DiceContext(self):
|
90
|
+
self._draw.stroke_width = 1
|
91
|
+
self._draw.stroke_color = self._fg
|
92
|
+
self._draw.fill_color = self._fg
|
93
|
+
self._draw.text_alignment = 'center'
|
94
|
+
self._draw.font_size = scale * self.size / 2
|
95
|
+
self._draw.text(self.x(0),
|
96
|
+
int(self.y(yoff)+self._draw.font_size//2),
|
97
|
+
f'{num}')
|
98
|
+
|
99
|
+
def image(self):
|
100
|
+
from wand.image import Image as WImage
|
101
|
+
|
102
|
+
image = WImage(width=self._width,height=self._height,format='png')
|
103
|
+
image.alpha_channel = True
|
104
|
+
self._draw(image)
|
105
|
+
|
106
|
+
off = min(self._width,self._height) // 30
|
107
|
+
copy = image.clone()
|
108
|
+
copy.shadow(50,off,0,0)
|
109
|
+
copy.negate(channel='rgb')
|
110
|
+
copy.composite(image,int(off/4),int(off/4))
|
111
|
+
|
112
|
+
#copy.save(filename='d4.png')
|
113
|
+
return copy
|
114
|
+
|
115
|
+
def __exit__(self,*e):
|
116
|
+
pass
|
117
|
+
|
118
|
+
# --------------------------------------------------------------------
|
119
|
+
class DiceDrawer:
|
120
|
+
def __init__(self,nsides,width,height,fg='red',bg='white'):
|
121
|
+
from wand.color import Color
|
122
|
+
self._nsides = nsides
|
123
|
+
self._width = width
|
124
|
+
self._height = height
|
125
|
+
self._fg = Color(fg if isinstance(fg,str) else f'#{fg:06x}')
|
126
|
+
self._bg = Color(bg if isinstance(bg,str) else f'#{bg:06x}')
|
127
|
+
if self._nsides not in [4,6,8,10,12,20]:
|
128
|
+
raise RuntimeError(f'Unknown number of sides: {self._nsides}')
|
129
|
+
|
130
|
+
|
131
|
+
def draw(self,num):
|
132
|
+
if self._nsides == 4: return self.draw_d4 (num)
|
133
|
+
if self._nsides == 6: return self.draw_d6 (num)
|
134
|
+
if self._nsides == 8: return self.draw_d8 (num)
|
135
|
+
if self._nsides == 10: return self.draw_d10(num)
|
136
|
+
if self._nsides == 12: return self.draw_d12(num)
|
137
|
+
if self._nsides == 20: return self.draw_d20(num)
|
138
|
+
return None
|
139
|
+
|
140
|
+
def draw_d4(self,num):
|
141
|
+
with DiceDraw(self._width,self._height,
|
142
|
+
fg=self._fg,bg=self._bg) as draw:
|
143
|
+
with DicePath(draw) as path:
|
144
|
+
path.move(to=( 0.000, 0.40))
|
145
|
+
path.line(to=( 0.433,-0.35))
|
146
|
+
path.line(to=(-0.433,-0.35))
|
147
|
+
path.line(to=( 0.000, 0.40))
|
148
|
+
|
149
|
+
draw.number(num)
|
150
|
+
return draw.image()
|
151
|
+
|
152
|
+
def draw_d6(self,num):
|
153
|
+
with DiceDraw(self._width,self._height,
|
154
|
+
fg=self._fg,bg=self._bg) as draw:
|
155
|
+
with DicePath(draw) as path:
|
156
|
+
r = 0.05
|
157
|
+
w = .4 - r
|
158
|
+
path.move(to=( w, 0.40))
|
159
|
+
path.arc (to=( 0.40, w), r=r)
|
160
|
+
path.line(to=( 0.40,- w))
|
161
|
+
path.arc (to=( w,-0.40), r=r)
|
162
|
+
path.line(to=(- w,-0.40))
|
163
|
+
path.arc (to=(-0.40,- w), r=r)
|
164
|
+
path.line(to=(-0.40, w))
|
165
|
+
path.arc (to=(- w, 0.40), r=r)
|
166
|
+
path.line(to=( w, 0.40))
|
167
|
+
|
168
|
+
draw.number(num,yoff=.1)
|
169
|
+
return draw.image()
|
170
|
+
|
171
|
+
def draw_d8(self,num):
|
172
|
+
with DiceDraw(self._width,self._height,
|
173
|
+
fg=self._fg,bg=self._bg) as draw:
|
174
|
+
with DiceContext(draw,darker=.9):
|
175
|
+
with DicePath(draw) as path:
|
176
|
+
path.move(to=(0.0000,0.5000))
|
177
|
+
path.line(to=(0.4330,0.2500))
|
178
|
+
path.line(to=(0.4330,-0.2500))
|
179
|
+
path.line(to=(0.0000,-0.5000))
|
180
|
+
path.line(to=(-0.4330,-0.2500))
|
181
|
+
path.line(to=(-0.4330,0.2500))
|
182
|
+
path.line(to=(0.0000,0.5000))
|
183
|
+
|
184
|
+
with DicePath(draw) as path:
|
185
|
+
path.move(to=(0.0000,0.5000))
|
186
|
+
path.line(to=(0.4330,-0.2500))
|
187
|
+
path.line(to=(-0.4330,-0.2500))
|
188
|
+
path.line(to=(0.0000,0.5000))
|
189
|
+
|
190
|
+
draw.number(num,yoff=.1)
|
191
|
+
return draw.image()
|
192
|
+
|
193
|
+
def draw_d10(self,num):
|
194
|
+
with DiceDraw(self._width,self._height,
|
195
|
+
fg=self._fg,bg=self._bg) as draw:
|
196
|
+
with DiceContext(draw,darker=.9):
|
197
|
+
with DicePath(draw) as path:
|
198
|
+
path.move(to=(0.0000,0.5000))
|
199
|
+
path.line(to=(0.4750,0.1000))
|
200
|
+
path.line(to=(0.4750,-0.1000))
|
201
|
+
path.line(to=(0.0000,-0.5000))
|
202
|
+
path.line(to=(-0.4750,-0.1000))
|
203
|
+
path.line(to=(-0.4750,0.1000))
|
204
|
+
path.line(to=(0.0000,0.5000))
|
205
|
+
path.move(to=(0.2940,-0.1540))
|
206
|
+
path.line(to=(0.4750,-0.1000))
|
207
|
+
path.move(to=(-0.4750,-0.1000))
|
208
|
+
path.line(to=(-0.2940,-0.1540))
|
209
|
+
path.move(to=(0.0000,-0.5000))
|
210
|
+
path.line(to=(0.0000,-0.3000))
|
211
|
+
with DicePath(draw) as path:
|
212
|
+
path.move(to=(0.0000,0.5000))
|
213
|
+
path.line(to=(0.2940,-0.1540))
|
214
|
+
path.line(to=(0.0000,-0.3000))
|
215
|
+
path.line(to=(-0.2940,-0.1540))
|
216
|
+
path.line(to=(0.0000,0.5000))
|
217
|
+
|
218
|
+
draw.number(num,yoff=.1)
|
219
|
+
return draw.image()
|
220
|
+
|
221
|
+
def draw_d12(self,num):
|
222
|
+
with DiceDraw(self._width,self._height,
|
223
|
+
fg=self._fg,bg=self._bg) as draw:
|
224
|
+
with DiceContext(draw,darker=.9):
|
225
|
+
with DicePath(draw) as path:
|
226
|
+
path.move(to=( 0.0000, 0.5000))
|
227
|
+
path.line(to=( 0.2940, 0.4050))
|
228
|
+
path.line(to=( 0.4750, 0.1730))
|
229
|
+
path.line(to=( 0.4750,-0.1730))
|
230
|
+
path.line(to=( 0.2940,-0.4050))
|
231
|
+
path.line(to=( 0.0000,-0.5000))
|
232
|
+
path.line(to=(-0.2940,-0.4050))
|
233
|
+
path.line(to=(-0.4750,-0.1730))
|
234
|
+
path.line(to=(-0.4750, 0.1730))
|
235
|
+
path.line(to=(-0.2940, 0.4050))
|
236
|
+
path.line(to=( 0.0000, 0.5000))
|
237
|
+
path.line(to=( 0.0000, 0.3490))
|
238
|
+
path.move(to=( 0.4750, 0.1730))
|
239
|
+
path.line(to=( 0.3320, 0.1080))
|
240
|
+
path.move(to=( 0.2940,-0.4050))
|
241
|
+
path.line(to=( 0.2050,-0.2820))
|
242
|
+
path.move(to=(-0.2940,-0.4050))
|
243
|
+
path.line(to=(-0.2050,-0.2820))
|
244
|
+
path.move(to=(-0.4750, 0.1730))
|
245
|
+
path.line(to=(-0.3320, 0.1080))
|
246
|
+
with DicePath(draw) as path:
|
247
|
+
path.move(to=(0.0000,0.3490))
|
248
|
+
path.line(to=(0.3320,0.1080))
|
249
|
+
path.line(to=(0.2050,-0.2820))
|
250
|
+
path.line(to=(-0.2050,-0.2820))
|
251
|
+
path.line(to=(-0.3320,0.1080))
|
252
|
+
path.line(to=(0.0000,0.3490))
|
253
|
+
|
254
|
+
|
255
|
+
draw.number(num,yoff=.1)
|
256
|
+
return draw.image()
|
257
|
+
|
258
|
+
def draw_d20(self,num):
|
259
|
+
with DiceDraw(self._width,self._height,
|
260
|
+
fg=self._fg,bg=self._bg) as draw:
|
261
|
+
with DiceContext(draw,darker=.85):
|
262
|
+
with DicePath(draw) as path:
|
263
|
+
path.move(to=(0.0000,0.5000))
|
264
|
+
path.line(to=(0.4540,0.2620))
|
265
|
+
path.line(to=(0.4540,-0.2620))
|
266
|
+
path.line(to=(0.0000,-0.5000))
|
267
|
+
path.line(to=(-0.4540,-0.2620))
|
268
|
+
path.line(to=(-0.4540,0.2620))
|
269
|
+
path.line(to=(0.0000,0.5000))
|
270
|
+
path.line(to=(0.0000,0.2920))
|
271
|
+
with DiceContext(draw,darker=.95):
|
272
|
+
with DicePath(draw) as path:
|
273
|
+
path.move(to=(0.0000,0.2920))
|
274
|
+
path.line(to=(0.4540,0.2620))
|
275
|
+
path.line(to=(0.2530,-0.1460))
|
276
|
+
path.line(to=(0.0000,-0.5000))
|
277
|
+
path.line(to=(-0.2530,-0.1460))
|
278
|
+
path.line(to=(-0.4540,0.2620))
|
279
|
+
path.line(to=(0.0000,0.2920))
|
280
|
+
path.move(to=(0.4540,-0.2620))
|
281
|
+
path.line(to=(0.2530,-0.1460))
|
282
|
+
path.move(to=(-0.4540,-0.2620))
|
283
|
+
path.line(to=(-0.2530,-0.1460))
|
284
|
+
with DicePath(draw) as path:
|
285
|
+
path.move(to=(0.0000,0.2920))
|
286
|
+
path.line(to=(0.2530,-0.1460))
|
287
|
+
path.line(to=(-0.2530,-0.1460))
|
288
|
+
path.line(to=(0.0000,0.2920))
|
289
|
+
|
290
|
+
|
291
|
+
draw.number(num,yoff=.07,scale=.7)
|
292
|
+
return draw.image()
|
293
|
+
|
294
|
+
|
295
|
+
# --------------------------------------------------------------------
|
296
|
+
class DiceAnimator:
|
297
|
+
def __init__(self,nsides,width,height,fg='red',bg='white'):
|
298
|
+
drawer = DiceDrawer(nsides = nsides,
|
299
|
+
width = width,
|
300
|
+
height = height,
|
301
|
+
fg = fg,
|
302
|
+
bg = bg)
|
303
|
+
self._pool = [drawer.draw(v) for v in range(1,nsides+1)]
|
304
|
+
|
305
|
+
def draw(self,num,n,dt=10):
|
306
|
+
|
307
|
+
'''
|
308
|
+
delay : int
|
309
|
+
1 / 100 of a second as base delay
|
310
|
+
'''
|
311
|
+
from random import sample
|
312
|
+
from wand.image import Image
|
313
|
+
|
314
|
+
if num > len(self._pool):
|
315
|
+
raise RuntimeError(f'Final value {num} not possible for '
|
316
|
+
f'd{len(self._pool)}')
|
317
|
+
|
318
|
+
if n >= len(self._pool) - 1:
|
319
|
+
raise RuntimeError(f'Pre-steps {n} not possible for '
|
320
|
+
f'd{len(self._pool)}')
|
321
|
+
|
322
|
+
end = self._pool[num-1]
|
323
|
+
|
324
|
+
# Round-about, but for debug
|
325
|
+
nums = list(range(1,len(self._pool)+1))
|
326
|
+
nums.remove(num)
|
327
|
+
pool = sample(nums, k=n)
|
328
|
+
other = [self._pool[i-1] for i in pool]
|
329
|
+
print(num,pool)
|
330
|
+
|
331
|
+
# More direct
|
332
|
+
# pool = self._pool[:num] + self._pool[num+1:]
|
333
|
+
# other = sample(pool, k=n)
|
334
|
+
|
335
|
+
faces = other + [end]
|
336
|
+
dang = 360 / (len(faces))
|
337
|
+
img = Image(format='gif')
|
338
|
+
ang = 360
|
339
|
+
wait = dt
|
340
|
+
for i, face in enumerate(faces):
|
341
|
+
ang -= dang
|
342
|
+
wait += dt
|
343
|
+
|
344
|
+
with face.clone() as rotated:
|
345
|
+
w, h = rotated.size
|
346
|
+
rotated.rotate(ang, reset_coords=False)
|
347
|
+
rotated.crop(0,0,w,h)
|
348
|
+
rotated.dispose = 'previous'
|
349
|
+
rotated.delay = wait # dt * (i + 1)
|
350
|
+
rotated.loop = 1
|
351
|
+
img.sequence.append(rotated)
|
352
|
+
|
353
|
+
# Make sure we dispose of the previous image altogether
|
354
|
+
#img.dispose = 'previous'
|
355
|
+
img.coalesce()
|
356
|
+
#img.deconstruct()
|
357
|
+
|
358
|
+
|
359
|
+
return img
|
360
|
+
|
361
|
+
#
|
362
|
+
# EOF
|
363
|
+
#
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
if __name__ == '__main__':
|
4
|
+
from sys import path
|
5
|
+
|
6
|
+
from argparse import ArgumentParser
|
7
|
+
from dicedraw import DiceDrawer
|
8
|
+
|
9
|
+
ap = ArgumentParser(description='Make a series of dice images')
|
10
|
+
ap.add_argument('-n','--sides', choices=[4,6,8,10,12,20],
|
11
|
+
default=6, type=int,
|
12
|
+
help='Number of sides')
|
13
|
+
ap.add_argument('-f','--foreground', type=str, default='white',
|
14
|
+
help='Foreground color')
|
15
|
+
ap.add_argument('-b','--background', type=str, default='#333333',
|
16
|
+
help='Background color')
|
17
|
+
ap.add_argument('-W','--width',type=int,default=75,
|
18
|
+
help='Width of images')
|
19
|
+
ap.add_argument('-H','--height',type=int,default=75,
|
20
|
+
help='Height of images')
|
21
|
+
ap.add_argument('-B','--base',type=str,default='d{sides}-{value}.png',
|
22
|
+
help='Format of file names')
|
23
|
+
|
24
|
+
args = ap.parse_args()
|
25
|
+
|
26
|
+
dd = DiceDrawer(args.sides,
|
27
|
+
args.width,
|
28
|
+
args.height,
|
29
|
+
fg = args.foreground,
|
30
|
+
bg = args.background)
|
31
|
+
|
32
|
+
base = args.base
|
33
|
+
|
34
|
+
vals = list(range(0,args.sides) if args.sides == 10 else
|
35
|
+
range(1,args.sides+1))
|
36
|
+
for val in vals:
|
37
|
+
dd.draw(val).save(filename=base.format(sides=args.sides,value=val))
|
38
|
+
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# ====================================================================
|
2
|
+
class Singleton(type):
|
3
|
+
'''Meta base class for singletons'''
|
4
|
+
_instances = {}
|
5
|
+
def __call__(cls, *args, **kwargs):
|
6
|
+
'''Create the singleton object or returned existing
|
7
|
+
|
8
|
+
Parameters
|
9
|
+
----------
|
10
|
+
args : tuple
|
11
|
+
Arguments
|
12
|
+
kwargs : dict
|
13
|
+
Keyword arguments
|
14
|
+
'''
|
15
|
+
if cls not in cls._instances:
|
16
|
+
cls._instances[cls] = \
|
17
|
+
super(Singleton, cls).__call__(*args, **kwargs)
|
18
|
+
return cls._instances[cls]
|
19
|
+
|
20
|
+
#
|
21
|
+
# EOF
|
22
|
+
#
|
pywargame/common/test.py
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
from verboseguard import VerboseGuard
|
4
|
+
from verbose import Verbose
|
5
|
+
|
6
|
+
def inner():
|
7
|
+
with VerboseGuard('Entering inner') as g:
|
8
|
+
g('Message from inner')
|
9
|
+
|
10
|
+
def test():
|
11
|
+
with VerboseGuard('Entering test'):
|
12
|
+
inner()
|
13
|
+
|
14
|
+
|
15
|
+
if __name__ == '__main__':
|
16
|
+
from argparse import ArgumentParser
|
17
|
+
|
18
|
+
ap = ArgumentParser('test verbose')
|
19
|
+
ap.add_argument('-v','--verbose',help='Be verbose',action='store_true')
|
20
|
+
|
21
|
+
args = ap.parse_args()
|
22
|
+
|
23
|
+
Verbose().setVerbose(args.verbose)
|
24
|
+
|
25
|
+
test()
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# --------------------------------------------------------------------
|
2
|
+
## BEGIN_IMPORT
|
3
|
+
from . singleton import Singleton
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
class Verbose(metaclass=Singleton):
|
7
|
+
def __init__(self,verbose=False):
|
8
|
+
'''Singleton for writing message to screen, contigent on setting
|
9
|
+
|
10
|
+
Parameters
|
11
|
+
----------
|
12
|
+
verbose : bool
|
13
|
+
Whether to show messages or not
|
14
|
+
'''
|
15
|
+
self._indent = ''
|
16
|
+
self._verbose = verbose
|
17
|
+
|
18
|
+
def setVerbose(self,verbose):
|
19
|
+
'''Set whether to print or not
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
verbose : bool
|
24
|
+
Whether to show messages or not
|
25
|
+
'''
|
26
|
+
self._verbose = verbose
|
27
|
+
|
28
|
+
@property
|
29
|
+
def verbose(self):
|
30
|
+
'''Test if this is verbose'''
|
31
|
+
return self._verbose
|
32
|
+
|
33
|
+
def message(self,*args,**kwargs):
|
34
|
+
'''Write messsage if verbose
|
35
|
+
|
36
|
+
Parameters
|
37
|
+
----------
|
38
|
+
args : tuple
|
39
|
+
Arguments
|
40
|
+
kwargs : dict
|
41
|
+
Keyword arguments
|
42
|
+
'''
|
43
|
+
if not self._verbose: return
|
44
|
+
if not kwargs.pop('noindent', False):
|
45
|
+
print(self._indent,end='')
|
46
|
+
print(*args,**kwargs)
|
47
|
+
|
48
|
+
def incr(self):
|
49
|
+
'''Increment indention'''
|
50
|
+
self._indent += ' '
|
51
|
+
|
52
|
+
def decr(self):
|
53
|
+
'''Decrement indention'''
|
54
|
+
if len(self._indent) > 0:
|
55
|
+
self._indent = self._indent[:-1]
|
56
|
+
|
57
|
+
#
|
58
|
+
# EOF
|
59
|
+
#
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# --------------------------------------------------------------------
|
2
|
+
## BEGIN_IMPORT
|
3
|
+
from . verbose import Verbose
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
class VerboseGuard:
|
7
|
+
def __init__(self,*args,**kwargs):
|
8
|
+
'''A guard pattern that increases verbose indention
|
9
|
+
|
10
|
+
This is a context manager. The arguments passed are used for
|
11
|
+
an initial message, before increasinig indention.
|
12
|
+
|
13
|
+
Parameters
|
14
|
+
----------
|
15
|
+
args : tuple
|
16
|
+
Arguments
|
17
|
+
kwargs : dict
|
18
|
+
Keyword arguments
|
19
|
+
'''
|
20
|
+
Verbose().message(*args,**kwargs)
|
21
|
+
|
22
|
+
def __bool_(self):
|
23
|
+
'''Test if verbose'''
|
24
|
+
return Verbose().verbose
|
25
|
+
|
26
|
+
def __enter__(self):
|
27
|
+
'''Enter context'''
|
28
|
+
Verbose().incr()
|
29
|
+
return self
|
30
|
+
|
31
|
+
def __exit__(self,*args):
|
32
|
+
'''Exit context'''
|
33
|
+
Verbose().decr()
|
34
|
+
|
35
|
+
@property
|
36
|
+
def i(self):
|
37
|
+
return Verbose()._indent
|
38
|
+
|
39
|
+
def __call__(self,*args,**kwargs):
|
40
|
+
'''Write a message at current indention level
|
41
|
+
|
42
|
+
Parameters
|
43
|
+
----------
|
44
|
+
args : tuple
|
45
|
+
Arguments
|
46
|
+
kwargs : dict
|
47
|
+
Keyword arguments
|
48
|
+
'''
|
49
|
+
Verbose().message(*args,**kwargs)
|
50
|
+
|
51
|
+
#
|
52
|
+
# EOF
|
53
|
+
#
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from . archive import *
|
2
|
+
from . base import *
|
3
|
+
from . head import *
|
4
|
+
from . image import *
|
5
|
+
from . tile import *
|
6
|
+
from . piece import *
|
7
|
+
from . mark import *
|
8
|
+
from . draw import *
|
9
|
+
from . cell import *
|
10
|
+
from . board import *
|
11
|
+
from . gamebox import *
|
12
|
+
from . scenario import *
|
13
|
+
from . player import *
|
14
|
+
from . windows import *
|
15
|
+
from . palette import *
|
16
|
+
from . tray import *
|
17
|
+
from . extractor import *
|
18
|
+
|