summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/wargame/wgexport.py
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/latex/wargame/wgexport.py')
-rwxr-xr-xMaster/texmf-dist/tex/latex/wargame/wgexport.py1620
1 files changed, 1388 insertions, 232 deletions
diff --git a/Master/texmf-dist/tex/latex/wargame/wgexport.py b/Master/texmf-dist/tex/latex/wargame/wgexport.py
index d573b63d36d..2e5dfd243de 100755
--- a/Master/texmf-dist/tex/latex/wargame/wgexport.py
+++ b/Master/texmf-dist/tex/latex/wargame/wgexport.py
@@ -31,6 +31,7 @@
# command.py
# trait.py
# withtraits.py
+# traits/area.py
# traits/dynamicproperty.py
# traits/globalproperty.py
# traits/prototype.py
@@ -65,6 +66,7 @@
# save.py
# vsav.py
# vmod.py
+# upgrade.py
# exporter.py
#
# ====================================================================
@@ -766,6 +768,7 @@ class PredefinedSetup(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PredefinedSetup
@@ -779,6 +782,7 @@ class PredefinedSetup(GameElement):
----------
asdict : bool
If `True`, return a dictonary that maps key to `PredefinedSetup` elements. If `False`, return a list of all PredefinedSetup` children.
+
Returns
-------
children : dict or list
@@ -948,6 +952,7 @@ class GlobalOptions(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Option
@@ -1266,6 +1271,7 @@ class Prototypes(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Prototype
@@ -1279,6 +1285,7 @@ class Prototypes(GameElement):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Prototype` elements. If `False`, return a list of all Prototype` children.
+
Returns
-------
children : dict or list
@@ -1491,7 +1498,25 @@ class SymbolicDice(GameElement):
backgroundColor = rgb(0xdd,0xdd,0xdd), # Window background
windowTitleResultFormat = "$name$", # Window title
windowX = '67', # Window size
- windowY = '65'):
+ windowY = '65',
+ doHotkey = False,
+ doLoop = False,
+ doReport = False,
+ doSound = False,
+ hideWhenDisabled = False,
+ hotkeys = '',
+ index = False,
+ indexProperty = '',
+ indexStart = 1,
+ indexStep = 1,
+ loopCount = 1,
+ loopType = 'counted',
+ postLoopKey = '',
+ reportFormat = '',
+ soundClip = '',
+ untilExpression = '',
+ whileExpression = ''
+ ):
super(SymbolicDice,self).\
__init__(game,
self.TAG,
@@ -1511,7 +1536,25 @@ class SymbolicDice(GameElement):
backgroundColor = backgroundColor,
windowTitleResultFormat = windowTitleResultFormat,
windowX = windowX,
- windowY = windowY)
+ windowY = windowY,
+ doHotkey = doHotkey,
+ doLoop = doLoop,
+ doReport = doReport,
+ doSound = doSound,
+ hideWhenDisabled = hideWhenDisabled,
+ hotkeys = hotkeys,
+ index = index,
+ indexProperty = indexProperty,
+ indexStart = indexStart,
+ indexStep = indexStep,
+ loopCount = loopCount,
+ loopType = loopType,
+ postLoopKey = postLoopKey,
+ reportFormat = reportFormat,
+ soundClip = soundClip,
+ untilExpression = untilExpression,
+ whileExpression = whileExpression)
+
def addDie(self,**kwargs):
return self.add(SpecialDie,**kwargs)
@@ -1549,6 +1592,9 @@ class SpecialDie(GameElement):
def getSymbolicDice(self):
return self.getParent(SymbolicDice)
+
+ def getFaces(self):
+ return self.getAllElements(DieFace,single=False)
registerElement(SpecialDie)
@@ -1622,6 +1668,7 @@ class PieceLayers(MapElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : LayerControl
@@ -1977,10 +2024,11 @@ class CounterDetailViewer(MapElement):
emptyHexReportForma = '$LocationName$',
enableHTML = True,
extraTextPadding = 0,
+ bgColor = None,
fgColor = rgb(0,0,0),
- fontSize = 9,
+ fontSize = 11,
graphicsZoom = 1.0,# Zoom on counters
- hotkey = key('\n',0),
+ hotkey = key('\n'),
layerList = '',
minDisplayPieces = 2,
propertyFilter = '',
@@ -1993,15 +2041,19 @@ class CounterDetailViewer(MapElement):
showOverlap = False,
showgraph = True,
showgraphsingle = False,
- showtext = False,
+ showtext = True,
showtextsingle = False,
stretchWidthSummary = False,
summaryReportFormat = '$LocationName$',
unrotatePieces = False,
version = 3,
- verticalOffset = 0,
- verticalTopText = 5,
- zoomlevel = 1.0): # showTerrain attributes
+ verticalOffset = 2,
+ verticalTopText = 0,
+ zoomlevel = 1.0,
+ stopAfterShowing = False): # showTerrain attributes
+
+ bg = '' if bgColor is None else bgColor
+ fg = '' if fgColor is None else fgColor
super(CounterDetailViewer,self)\
.__init__(map,self.TAG,node=node,
borderWidth = borderWidth,
@@ -2015,7 +2067,8 @@ class CounterDetailViewer(MapElement):
emptyHexReportForma = emptyHexReportForma,
enableHTML = enableHTML,
extraTextPadding = extraTextPadding,
- fgColor = fgColor,
+ bgColor = bg,
+ fgColor = fg,
fontSize = fontSize,
graphicsZoom = graphicsZoom,
hotkey = hotkey,
@@ -2039,7 +2092,8 @@ class CounterDetailViewer(MapElement):
version = version,
verticalOffset = verticalOffset,
verticalTopText = verticalTopText,
- zoomlevel = zoomlevel)
+ zoomlevel = zoomlevel,
+ stopAfterShowing = stopAfterShowing)
registerElement(CounterDetailViewer)
@@ -2198,7 +2252,26 @@ class AtStart(MapElement):
owningBoard = '',
x = 0,
y = 0):
- '''Pieces are existing PieceSlot elements'''
+ '''Pieces are existing PieceSlot elements
+
+
+ Parameters
+ ----------
+ node : xml.minidom.Node
+ Existing node or None
+ name : str
+ Name of node
+ location : str
+ Where the at-start element is put if `useGridLocation`
+ useGridLocation : bool
+ If true, use maps grid
+ owningBoard : str
+ Board that owns the at-start (can be empty)
+ x : float
+ Coordinate (ignored if `useGridLocation`)
+ y : float
+ Coordinate (ignored if `useGridLocation`)
+ '''
super(AtStart,self).\
__init__(map,self.TAG,node=node,
name = name,
@@ -2215,6 +2288,7 @@ class AtStart(MapElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Pieces
@@ -2235,6 +2309,7 @@ class AtStart(MapElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Piece
@@ -2264,7 +2339,7 @@ class AtStart(MapElement):
Dictionary or list of `Piece` children
'''
- return self.getElementsWithKey(PieceSlot,'entryName',asdict)
+ return self.getElementsByKey(PieceSlot,'entryName',asdict)
registerElement(AtStart)
@@ -2292,6 +2367,7 @@ class GlobalProperties(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Property
@@ -2347,6 +2423,7 @@ class TurnLevel(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Level
@@ -2374,6 +2451,7 @@ class TurnLevel(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Counter
@@ -2387,6 +2465,7 @@ class TurnLevel(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : List
@@ -2495,6 +2574,7 @@ class TurnTrack(TurnLevel):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Hotkey
@@ -2626,6 +2706,7 @@ class Documentation(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : AboutScreen
@@ -2639,6 +2720,7 @@ class Documentation(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : HelpFile
@@ -2652,6 +2734,7 @@ class Documentation(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : BrowserHelpFile
@@ -2665,6 +2748,7 @@ class Documentation(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : BrowserPDFFile
@@ -2678,6 +2762,7 @@ class Documentation(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Tutorial
@@ -2909,6 +2994,7 @@ class PlayerRoster(GameElement):
buttonText='Retire',
buttonToolTip='Switch sides, become observer, or release faction'):
'''Add a player roster to the module
+
Parameters
----------
doc : Element
@@ -2931,6 +3017,7 @@ class PlayerRoster(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Side
@@ -3150,6 +3237,7 @@ class ChessClockControl(GameElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : AboutScreen
@@ -3180,6 +3268,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Tabs
@@ -3192,6 +3281,7 @@ class WidgetElement:
Parameters
----------
Dictionary of attribute key-value pairs
+
Returns
-------
element : Combo
@@ -3205,6 +3295,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Panel
@@ -3218,6 +3309,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : List
@@ -3231,6 +3323,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : MapWidget
@@ -3244,6 +3337,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Chart
@@ -3257,6 +3351,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PieceSlot
@@ -3270,6 +3365,7 @@ class WidgetElement:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Piece
@@ -3288,6 +3384,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `Tab` elements. If `False`, return a list of all Tab` children.
+
Returns
-------
children : dict or list
@@ -3301,6 +3398,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `Tab` elements. If `False`, return a list of all Tab` children.
+
Returns
-------
children : dict or list
@@ -3314,6 +3412,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `List` elements. If `False`, return a list of all List` children.
+
Returns
-------
children : dict or list
@@ -3327,6 +3426,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `Panel` elements. If `False`, return a list of all Panel` children.
+
Returns
-------
children : dict or list
@@ -3340,6 +3440,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `MapWidget` elements. If `False`, return a list of all MapWidget` children.
+
Returns
-------
children : dict or list
@@ -3353,6 +3454,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `Chart` elements. If `False`, return a list of all Chart` children.
+
Returns
-------
children : dict or list
@@ -3366,6 +3468,7 @@ class WidgetElement:
----------
asdict : bool
If `True`, return a dictonary that maps key to `PieceSlot` elements. If `False`, return a list of all PieceSlot` children.
+
Returns
-------
children : dict or list
@@ -3470,6 +3573,7 @@ class MapWidget(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : WidgetMap
@@ -3483,6 +3587,7 @@ class MapWidget(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `WidgetMap` elements. If `False`, return a list of all WidgetMap` children.
+
Returns
-------
children : dict or list
@@ -3679,6 +3784,7 @@ class HexGrid(BaseGrid):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Numbering
@@ -3720,8 +3826,8 @@ class SquareGrid(BaseGrid):
dx = dx,
dy = dy,
edgesLegal = edgesLegal,
- x0 = 0,
- y0 = int(0.4*RECT_HEIGHT),
+ x0 = x0,
+ y0 = y0,
**kwargs)
def addNumbering(self,**kwargs):
'''Add a `Numbering` element to this
@@ -3730,6 +3836,7 @@ class SquareGrid(BaseGrid):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Numbering
@@ -3892,6 +3999,7 @@ class RegionGrid(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Region
@@ -4017,6 +4125,7 @@ class ZonedGrid(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Highlighter
@@ -4031,6 +4140,7 @@ class ZonedGrid(Element):
single : bool
If `True`, there can be only one `Highligter` child, otherwise fail.
If `False` return all `Highligter` children in this element
+
Returns
-------
children : list
@@ -4044,6 +4154,7 @@ class ZonedGrid(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Zone
@@ -4057,6 +4168,7 @@ class ZonedGrid(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children.
+
Returns
-------
children : dict or list
@@ -4080,6 +4192,7 @@ class ZonedGridHighlighter(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ZoneHighlight
@@ -4093,6 +4206,7 @@ class ZonedGridHighlighter(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children.
+
Returns
-------
children : dict or list
@@ -4210,6 +4324,7 @@ class Zone(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : HexGrid
@@ -4223,6 +4338,7 @@ class Zone(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : SquareGrid
@@ -4236,6 +4352,7 @@ class Zone(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : RegionGrid
@@ -4249,6 +4366,7 @@ class Zone(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Property
@@ -4263,6 +4381,7 @@ class Zone(Element):
single : bool
If `True`, there can be only one `HexGrid` child, otherwise fail.
If `False` return all `HexGrid` children in this element
+
Returns
-------
children : list
@@ -4277,6 +4396,7 @@ class Zone(Element):
single : bool
If `True`, there can be only one `SquareGrid` child, otherwise fail.
If `False` return all `SquareGrid` children in this element
+
Returns
-------
children : list
@@ -4291,6 +4411,7 @@ class Zone(Element):
single : bool
If `True`, there can be only one `RegionGrid` child, otherwise fail.
If `False` return all `RegionGrid` children in this element
+
Returns
-------
children : list
@@ -4305,6 +4426,7 @@ class Zone(Element):
single : bool
If `True`, there can be only one `Grid` child, otherwise fail.
If `False` return all `Grid` children in this element
+
Returns
-------
children : list
@@ -4323,8 +4445,6 @@ class Zone(Element):
def getProperties(self):
'''Get all `Property` element from this
- Parameters
- ----------
Returns
-------
children : dict
@@ -4339,6 +4459,7 @@ class Zone(Element):
c = pp.split(',')
r.append([int(c[0]),int(c[1])])
return r
+
def getBB(self):
from functools import reduce
path = self.getPath()
@@ -4393,6 +4514,7 @@ class BoardPicker(MapElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Setup
@@ -4411,6 +4533,7 @@ class BoardPicker(MapElement):
single : bool
If `True`, there can be only one `Setup` child, otherwise fail.
If `False` return all `Setup` children in this element
+
Returns
-------
children : list
@@ -4424,6 +4547,7 @@ class BoardPicker(MapElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Board
@@ -4437,6 +4561,7 @@ class BoardPicker(MapElement):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Board` elements. If `False`, return a list of all Board` children.
+
Returns
-------
children : dict or list
@@ -4512,6 +4637,7 @@ class Board(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ZonedGrid
@@ -4526,6 +4652,7 @@ class Board(Element):
single : bool
If `True`, there can be only one `ZonedGrid` child, otherwise fail.
If `False` return all `ZonedGrid` children in this element
+
Returns
-------
children : list
@@ -4539,6 +4666,7 @@ class Board(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Zone` elements. If `False`, return a list of all Zone` children.
+
Returns
-------
children : dict or list
@@ -4596,6 +4724,65 @@ class BaseMap(Element):
moveWithinFormat = '$pieceName$ moves $previousLocation$ → $location$ *',
showKey = '',
thickness = '3'):
+ '''Create a map
+
+ Parameters
+ ----------
+ doc : xml.minidom.Document
+ Parent document
+ tag : str
+ XML tag
+ node : xml.minidom.Node or None
+ Existing node or None
+ mapName : str
+ Name of map
+ allowMultiple : bool
+ Allow multiple boards
+ backgroundcolor : color
+ Bckground color
+ buttonName : str
+ Name on button to show map = '',
+ changeFormat :
+ Message format to show on changes
+ color : color
+ Color of selected pieces
+ createFormat : str
+ Message format when creating a piece
+ edgeHeight : int
+ Height of edge (margin)
+ edgeWidth : int
+ Width of edge (margin)
+ hideKey : Key
+ Hot-key or key-command to hide map
+ hotkey : Key
+ Hot-key or key-command to show map
+ icon : path
+ Icon image
+ launch : bool
+ Show on launch
+ markMoved : str
+ Show moved
+ markUnmovedHotkey : key
+ Remove moved markers
+ markUnmovedIcon : path
+ Icon for unmoved
+ markUnmovedReport : str
+ Message when marking as unmoved
+ markUnmovedText : str
+ Text on button
+ markUnmovedTooltip : str
+ Tooltip on button
+ moveKey : key
+ Key to set moved marker
+ moveToFormat : str
+ Message format when moving
+ moveWithinFormat : str
+ Message when moving within map
+ showKey : str,
+ Key to show map
+ thickness : int
+ Thickness of line around selected pieces
+ '''
super(BaseMap,self).__init__(doc,tag,node=node,
allowMultiple = allowMultiple,
backgroundcolor = backgroundcolor,
@@ -4632,6 +4819,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Picker
@@ -4646,6 +4834,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `BoardPicker` child, otherwise fail.
If `False` return all `BoardPicker` children in this element
+
Returns
-------
children : list
@@ -4660,6 +4849,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `BoardPicker` child, otherwise fail.
If `False` return all `BoardPicker` children in this element
+
Returns
-------
children : list
@@ -4674,6 +4864,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `StackMetric` child, otherwise fail.
If `False` return all `StackMetric` children in this element
+
Returns
-------
children : list
@@ -4688,6 +4879,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `ImageSaver` child, otherwise fail.
If `False` return all `ImageSaver` children in this element
+
Returns
-------
children : list
@@ -4702,6 +4894,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `TextSaver` child, otherwise fail.
If `False` return all `TextSaver` children in this element
+
Returns
-------
children : list
@@ -4716,6 +4909,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `ForwardToChatter` child, otherwise fail.
If `False` return all `ForwardToChatter` children in this element
+
Returns
-------
children : list
@@ -4730,6 +4924,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `MenuDi` child, otherwise fail.
If `False` return all `MenuDi` children in this element
+
Returns
-------
children : list
@@ -4744,6 +4939,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `MapCenterer` child, otherwise fail.
If `False` return all `MapCenterer` children in this element
+
Returns
-------
children : list
@@ -4758,6 +4954,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `StackExpander` child, otherwise fail.
If `False` return all `StackExpander` children in this element
+
Returns
-------
children : list
@@ -4772,6 +4969,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `PieceMover` child, otherwise fail.
If `False` return all `PieceMover` children in this element
+
Returns
-------
children : list
@@ -4786,6 +4984,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `SelectionHighlighter` child, otherwise fail.
If `False` return all `SelectionHighlighter` children in this element
+
Returns
-------
children : list
@@ -4802,6 +5001,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `HighlightLa` child, otherwise fail.
If `False` return all `HighlightLa` children in this element
+
Returns
-------
children : list
@@ -4816,6 +5016,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `CounterDetailViewer` child, otherwise fail.
If `False` return all `CounterDetailViewer` children in this element
+
Returns
-------
children : list
@@ -4830,6 +5031,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `GlobalMap` child, otherwise fail.
If `False` return all `GlobalMap` children in this element
+
Returns
-------
children : list
@@ -4844,6 +5046,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `Zoomer` child, otherwise fail.
If `False` return all `Zoomer` children in this element
+
Returns
-------
children : list
@@ -4858,6 +5061,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `HidePiece` child, otherwise fail.
If `False` return all `HidePiece` children in this element
+
Returns
-------
children : list
@@ -4871,6 +5075,7 @@ class BaseMap(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `MassKey` elements. If `False`, return a list of all MassKey` children.
+
Returns
-------
children : dict or list
@@ -4885,6 +5090,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `Flare` child, otherwise fail.
If `False` return all `Flare` children in this element
+
Returns
-------
children : list
@@ -4899,6 +5105,7 @@ class BaseMap(Element):
single : bool
If `True`, there can be only one `AtStart` child, otherwise fail.
If `False` return all `AtStart` children in this element
+
Returns
-------
children : list
@@ -4912,6 +5119,7 @@ class BaseMap(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Board` elements. If `False`, return a list of all Board` children.
+
Returns
-------
children : dict or list
@@ -4947,6 +5155,7 @@ class BaseMap(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -4964,6 +5173,7 @@ class BaseMap(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -4978,6 +5188,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : BoardPicker
@@ -4991,6 +5202,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : StackMetrics
@@ -5004,6 +5216,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ImageSaver
@@ -5017,6 +5230,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : TextSaver
@@ -5030,6 +5244,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ForwardToChatter
@@ -5043,6 +5258,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : MenuDisplayer
@@ -5056,6 +5272,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : MapCenterer
@@ -5069,6 +5286,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : StackExpander
@@ -5082,6 +5300,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PieceMover
@@ -5095,6 +5314,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : SelectionHighlighters
@@ -5108,6 +5328,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : KeyBufferer
@@ -5121,6 +5342,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : HighlightLastMoved
@@ -5134,6 +5356,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : CounterDetailViewer
@@ -5147,6 +5370,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : GlobalMap
@@ -5160,6 +5384,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Zoomer
@@ -5173,6 +5398,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : HidePiecesButton
@@ -5186,6 +5412,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : MassKey
@@ -5199,6 +5426,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : StartupMassKey
@@ -5212,6 +5440,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Flare
@@ -5225,6 +5454,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : AtStart
@@ -5240,6 +5470,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PieceLayers
@@ -5253,6 +5484,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Menu
@@ -5266,6 +5498,7 @@ class BaseMap(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Menu
@@ -5377,6 +5610,7 @@ class ChartWindow(GameElement,WidgetElement):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Chart
@@ -5388,12 +5622,16 @@ class ChartWindow(GameElement,WidgetElement):
Parameters
----------
- asdict : bool
- If `True`, return a dictonary that maps key to `Chart` elements. If `False`, return a list of all Chart` children.
+ asdict : bool
+ If `True`, return a dictonary that maps key to `Chart`
+ elements. If `False`, return a list of all Chart`
+ children.
+
Returns
-------
children : dict or list
Dictionary or list of `Chart` children
+
'''
return self.getElementsById(Chart,'chartName',asdict=asdict)
@@ -5440,11 +5678,12 @@ class AddCommand(Command):
class Trait:
known_traits = []
def __init__(self):
- '''Base class for trait capture. Unlike the Element classes,
- this actually holds state that isn't reflected elsewhere in
- the DOM. This means that the data here is local to the
- object. So when we do
-
+ '''Base class for trait capture.
+
+ Unlike the Element classes, this actually holds state that
+ isn't reflected elsewhere in the DOM. This means that the
+ data here is local to the object. So when we do
+
piece = foo.getPieceSlots()[0]
traits = p.getTraits()
for trait in traits:
@@ -5467,6 +5706,9 @@ class Trait:
traits.append(MarkTrait('Hello','World;)
piece.setTraits(traits)
+ .. include:: ../../vassal/traits/README.md
+ :parser: myst_parser.sphinx_
+
'''
self._type = None
self._state = None
@@ -5479,6 +5721,7 @@ class Trait:
'''
self._type = list(kwargs.values())
self._tnames = list(kwargs.keys())
+
def setState(self,**kwargs):
'''Set states. Dictionary of names and values. Dictonary keys
defines how we access the fields, which is internal here.
@@ -5487,7 +5730,6 @@ class Trait:
self._state = list(kwargs.values())
self._snames = list(kwargs.keys())
-
def __getitem__(self,key):
'''Look up item in either type or state'''
try:
@@ -5574,39 +5816,78 @@ class Trait:
return [k.replace('\\'+f'{sep}',f'{sep}') for k in ks]
@classmethod
- def flatten(cls,traits,game=None,prototypes=None):
+ def flatten(cls,traits,game=None,prototypes=None,verbose=False):
if prototypes is None:
if game is None:
print(f'Warning: Game or prototypes not passed')
return None
prototypes = game.getPrototypes()[0].getPrototypes()
- ret = []
- return cls._flatten(traits,ret,prototypes,False)
+ if len(traits) < 1: return None
+
+ basic = None
+ if traits[-1].ID == BasicTrait.ID:
+ basic = traits.pop()
+
+ if verbose:
+ print(f'Piece {basic["name"]}')
+
+ ret = cls._flatten(traits,prototypes,' ',verbose)
+ ret.append(basic)
+
+ return ret
@classmethod
- def _flatten(cls,traits,ret,prototypes,nobasic=True):
+ def _flatten(cls,traits,prototypes,ind,verbose):
'''Expand all prototype traits in traits'''
+ ret = []
for trait in traits:
# Ignore recursive basic traits
- if nobasic and trait.ID == BasicTrait.ID:
+ if trait.ID == BasicTrait.ID:
continue
# Add normal traits
if trait.ID != PrototypeTrait.ID:
+ if verbose:
+ print(f'{ind}Adding trait "{trait.ID}"')
+
ret.append(trait)
continue
# Find prototype
- proto = prototypes.get(trait['name'],None)
+ name = trait['name']
+ proto = prototypes.get(name,None)
if proto is None:
- print(f'Warning, prototype {trait["name"]} not found')
+ if name != ' prototype':
+ print(f'{ind}Warning, prototype {name} not found')
continue
+ if verbose:
+ print(f'{ind}Expanding prototype "{name}"')
+
# Recursive call to add prototype traits (and possibly
# more recursive calls
- cls._flatten(proto.getTraits(), ret, prototypes,not nobasic)
+ ret.extend(cls._flatten(proto.getTraits(), prototypes,
+ ind+' ',verbose))
return ret
+
+ def print(self,file=None):
+ if file is None:
+ from sys import stdout
+ file = stdout
+
+ nt = max([len(i) for i in self._tnames])
+ ns = max([len(i) for i in self._snames])
+ nw = max(nt,ns)
+
+ print(f'Trait ID={self.ID}',file=file)
+ print(f' Type:', file=file)
+ for n,v in zip(self._tnames,self._type):
+ print(f' {n:<{nw}s}: {v}',file=file)
+ print(f' State:', file=file)
+ for n,v in zip(self._snames,self._state):
+ print(f' {n:<{nw}s}: {v}',file=file)
+
#
# EOF
#
@@ -5660,6 +5941,7 @@ class WithTraits(Element):
traits.append(trait)
self.setTraits(*traits)
+
def getTraits(self):
'''Get all element traits as objects. This decodes the trait
definitions. This is useful if we have read the element from
@@ -5680,9 +5962,109 @@ class WithTraits(Element):
The decoded traits
'''
+ code = self._node.childNodes[0].nodeValue
+ return self.decodeAdd(code)
+
+ def encodedStates(self):
+ from re import split
+
+ code = self._node.childNodes[0].nodeValue
+ cmd, iden, typ, sta = split(fr'(?<!\\)/',code) #code.split('/')
+
+ return sta
+
+ def decodeStates(self,code,verbose=False):
+ from re import split
+
+ newstates, oldstates = split(fr'(?<!\\)/',code)#code.split('/')
+
+ splitit = lambda l : \
+ [s.strip('\\').split(';') for s in l.split(r' ')]
+
+ newstates = splitit(newstates)
+ oldstates = splitit(oldstates)
+
+ traits = self.getTraits()
+
+ if len(traits) != len(newstates):
+ print(f'Piece has {len(traits)} traits but got '
+ f'{len(newstates)} states')
+
+ for trait, state in zip(traits,newstates):
+ trait._state = state;
+ # print(trait.ID)
+ # for n,s in zip(trait._snames,trait._state):
+ # print(f' {n:30s}: {s}')
+
+ self.setTraits(*traits)
+
+ def copyStates(self,other,verbose=False):
+ straits = other.getTraits()
+ dtraits = self.getTraits()
+
+ matches = 0
+ for strait in straits:
+ if len(strait._state) < 1:
+ continue
+
+ cand = []
+ ttrait = None
+ for dtrait in dtraits:
+ if dtrait.ID == strait.ID:
+ cand.append(dtrait)
+
+ if verbose and len(cand) < 1:
+ print(f'Could not find candidate for {strait.ID}')
+ continue
+
+ if len(cand) == 1:
+ ttrait = cand[0]
+
+ else:
+ # print(f'Got {len(cand)} candidiate targets {strait.ID}')
+
+ best = None
+ count = 0
+ types = strait._type
+ for c in cand:
+ cnt = sum([ct == t for ct,t in zip(c._type, types)])
+ if cnt > count:
+ best = c
+ count = cnt
+
+ if verbose and best is None:
+ print(f'No candidate for {strait.ID} {len(again)}')
+
+ if verbose and count+2 < len(types):
+ print(f'Ambigious candidate for {strait.ID} '
+ f'({count} match out of {len(types)})')
+ #print(best._type)
+ #print(types)
+
+ ttrait = best
+
+ if ttrait is None:
+ continue
+
+ ttrait._state = strait._state
+ matches += 1
+ # print(ttrait.ID)
+ # for n,s in zip(ttrait._snames,ttrait._state):
+ # print(f' {n:30s}: {s}')
+
+ if verbose:
+ print(f'Got {matches} matches out of {len(dtraits)}')
+
+ self.setTraits(*dtraits)
+
+
+ def decodeAdd(self,code,verbose=False):
+ '''Try to decode make a piece from a piece of state code'''
from re import split
- code = self._node.childNodes[0].nodeValue
+
cmd, iden, typ, sta = split(fr'(?<!\\)/',code) #code.split('/')
+ # print(cmd,iden,typ,sta)
+
types = typ.split(r' ')
states = sta.split(r' ')
types = [t.strip('\\').split(';') for t in types]
@@ -5705,19 +6087,11 @@ class WithTraits(Element):
print(f'Warning: Unknown trait {tid}')
return traits
-
- def setTraits(self,*traits,iden='null'):
- '''Set traits on this element. This encodes the traits into
- this object.
-
- Parameters
- ----------
- traits : tuple of Trait objects
- The traits to set on this object.
- iden : str
- Identifier
- '''
+ def encodeAdd(self,*traits,iden='null',verbose=False):
+ '''Encodes type and states'''
+ if len(traits) < 1: return ''
+
last = traits[-1]
# A little hackish to use the name of the class, but needed
# because of imports into patch scripts.
@@ -5740,9 +6114,30 @@ class WithTraits(Element):
tpe = WithTraits.encodeParts(*types)
state = WithTraits.encodeParts(*states)
add = AddCommand(str(iden),tpe,state)
+ return add.cmd
+
+
+ def setTraits(self,*traits,iden='null'):
+ '''Set traits on this element. This encodes the traits into
+ this object.
+
+ Parameters
+ ----------
+ traits : tuple of Trait objects
+ The traits to set on this object.
+ iden : str
+ Identifier
+
+ '''
+ add = self.encodeAdd(*traits,iden=iden)
+ if self._node is None:
+ from xml.dom.minidom import Element, Text
+ self._node = Element(self.TAG)
+ self._node.appendChild(Text())
+
if len(self._node.childNodes) < 1:
self.addText('')
- self._node.childNodes[0].nodeValue = add.cmd
+ self._node.childNodes[0].nodeValue = add
def removeTrait(self,ID,key=None,value=None,verbose=False):
'''Remove a trait from this object.
@@ -5896,6 +6291,7 @@ class PieceSlot(WithTraits):
width = width,
icon = icon)
+
def clone(self,parent):
'''Adds copy of self to parent, possibly with new GPID'''
game = self.getParentOfClass([Game])
@@ -5951,6 +6347,53 @@ registerElement(Prototype)
# EOF
#
# ====================================================================
+# From traits/area.py
+
+class AreaTrait(Trait):
+ ID = 'AreaOfEffect'
+ def __init__(self,
+ transparancyColor = rgb(0x77,0x77,0x77),
+ transparancyLevel = 30,
+ radius = 1,
+ alwaysActive = False,
+ activateCommand = 'Toggle area of effect',
+ activateKey = key('A'), # Ctrl-A
+ mapShaderName = '',
+ fixedRadius = True,
+ radiusMarker = '', # Property
+ description = 'Show area of effect',
+ name = 'EffectArea',
+ onMenuText = '', # Show area of effect
+ onKey = '', # key('A')
+ offMenuText = '', # Hide area of effect
+ offKey = '', # key(A,SHIFT)
+ globallyVisible = True):
+ super(AreaTrait,self).__init__()
+ self.setType(
+ transparancyColor = transparancyColor,
+ transparancyLevel = int(transparancyLevel),
+ radius = radius,
+ alwaysActive = alwaysActive,
+ activateCommand = activateCommand,
+ activateKey = activateKey,
+ mapShaderName = mapShaderName,
+ fixedRadius = fixedRadius,
+ radiusMarker = radiusMarker,
+ description = description,
+ name = name,
+ onMenuText = onMenuText,
+ onKey = onKey,
+ offMenuText = offMenuText,
+ offKey = offKey,
+ globallyVisible = globallyVisible
+ )
+ self.setState(active = alwaysActive or not globallyVisible)
+
+Trait.known_traits.append(AreaTrait)
+#
+# EOF
+#
+# ====================================================================
# From traits/dynamicproperty.py
# --------------------------------------------------------------------
@@ -5990,9 +6433,9 @@ class ChangePropertyTrait(Trait):
# print(cmd)
com = cmd[0] + ':' + cmd[1].replace(',',r'\,') + ':' + cmd[2]
if cmd[2] == self.DIRECT:
- com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:')
+ com += r'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:')
elif cmd[2] == self.INCREMENT:
- com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:')
+ com += r'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:')
cmds.append(com)
# print(cmds)
return ','.join(cmds)
@@ -6758,7 +7201,7 @@ class MaskTrait(Trait):
autoPeek = True,
dealKey = '',
dealExpr = ''):
- '''Create a mark trait (static property)'''
+ '''Create a masking trait'''
super(MaskTrait,self).__init__()
disp = displayStyle
if displayStyle == self.PEEK:
@@ -6822,25 +7265,32 @@ class TrailTrait(Trait):
inactiveOpacity = 50,
edgesBuffer = 20,
displayBuffer = 30,
- lineWidth = 1,
+ lineWidth = 5,
turnOn = '',
turnOff = '',
reset = '',
- description = ''):
+ description = 'Enable or disable movement trail'):
''' Create a movement trail trait ( VASSAL.counters.Footprint)'''
super(TrailTrait,self).__init__()
+ lw = (lineWidth
+ if isinstance(lineWidth,str) and lineWidth.startswith('{') else
+ int(lineWidth))
+ ra = (radius
+ if isinstance(radius,str) and radius.startswith('{') else
+ int(radius))
+
self.setType(key = key,# ENABLE KEY
name = name,# MENU
localVisible = localVisible,# LOCAL VISABLE
globalVisible = globalVisible,# GLOBAL VISABLE
- radius = radius,# RADIUS
+ radius = ra,# RADIUS
fillColor = fillColor,# FILL COLOR
lineColor = lineColor,# LINE COLOR
activeOpacity = activeOpacity,# ACTIVE OPACITY
inactiveOpacity = inactiveOpacity,# INACTIVE OPACITY
edgesBuffer = edgesBuffer,# EDGES BUFFER
displayBuffer = displayBuffer,# DISPLAY BUFFER
- lineWidth = int(lineWidth),# LINE WIDTH
+ lineWidth = lw,# LINE WIDTH
turnOn = turnOn,# TURN ON KEY
turnOff = turnOff,# TURN OFF KEY
reset = reset,# RESET KEY
@@ -7127,13 +7577,19 @@ class NonRectangleTrait(Trait):
self.setState()
@classmethod
- def getShape(cls,image):
- if image is None:
+ def getShape(cls,buffer):
+ if buffer is None:
return []
- from PIL import Image
from io import BytesIO
+ image = buffer
+ if image[:5] == b'<?xml':
+ from cairosvg import svg2png
+ image = svg2png(image)
+
+ from PIL import Image
+
code = []
with Image.open(BytesIO(image)) as img:
alp = img.getchannel('A') # Alpha channel
@@ -7277,6 +7733,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : BasicCommandEncoder
@@ -7290,6 +7747,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : GlobalTranslatableMessages
@@ -7303,6 +7761,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PlayerRoster
@@ -7316,6 +7775,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PlayerRoster
@@ -7329,6 +7789,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Language
@@ -7342,6 +7803,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Chatter
@@ -7355,6 +7817,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : KeyNamer
@@ -7368,6 +7831,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Notes
@@ -7381,6 +7845,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Language
@@ -7394,6 +7859,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Chatter
@@ -7407,6 +7873,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : KeyNamer
@@ -7420,6 +7887,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : GlobalProperties
@@ -7433,6 +7901,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : GlobalOptions
@@ -7446,6 +7915,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : TurnTrack
@@ -7459,6 +7929,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Documentation
@@ -7472,6 +7943,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Prototypes
@@ -7485,6 +7957,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PieceWindow
@@ -7498,6 +7971,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ChartWindow
@@ -7511,6 +7985,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Inventory
@@ -7524,6 +7999,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Map
@@ -7537,6 +8013,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : DiceButton
@@ -7550,6 +8027,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : PredefinedSetup
@@ -7563,6 +8041,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : GameMassKey
@@ -7576,6 +8055,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : StartupMassKey
@@ -7589,6 +8069,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Menu
@@ -7602,6 +8083,7 @@ class Game(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : SymbolicDice
@@ -7619,6 +8101,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `GlobalPropertie` child, otherwise fail.
If `False` return all `GlobalPropertie` children in this element
+
Returns
-------
children : list
@@ -7633,6 +8116,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Ba` child, otherwise fail.
If `False` return all `Ba` children in this element
+
Returns
-------
children : list
@@ -7647,6 +8131,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `GlobalTranslatableMessage` child, otherwise fail.
If `False` return all `GlobalTranslatableMessage` children in this element
+
Returns
-------
children : list
@@ -7661,6 +8146,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Language` child, otherwise fail.
If `False` return all `Language` children in this element
+
Returns
-------
children : list
@@ -7675,6 +8161,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Language` child, otherwise fail.
If `False` return all `Language` children in this element
+
Returns
-------
children : list
@@ -7689,6 +8176,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Chatter` child, otherwise fail.
If `False` return all `Chatter` children in this element
+
Returns
-------
children : list
@@ -7703,6 +8191,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `KeyNamer` child, otherwise fail.
If `False` return all `KeyNamer` children in this element
+
Returns
-------
children : list
@@ -7717,6 +8206,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Documentation` child, otherwise fail.
If `False` return all `Documentation` children in this element
+
Returns
-------
children : list
@@ -7733,6 +8223,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Prototypes` child, otherwise fail.
If `False` return all `Prototypes` children in this element
+
Returns
-------
children : list
@@ -7748,6 +8239,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `PlayerRo` child, otherwise fail.
If `False` return all `PlayerRo` children in this element
+
Returns
-------
children : list
@@ -7762,6 +8254,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `GlobalOption` child, otherwise fail.
If `False` return all `GlobalOption` children in this element
+
Returns
-------
children : list
@@ -7775,6 +8268,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Inventorie` elements. If `False`, return a list of all Inventorie` children.
+
Returns
-------
children : dict or list
@@ -7788,6 +8282,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `PieceWindow` elements. If `False`, return a list of all PieceWindow` children.
+
Returns
-------
children : dict or list
@@ -7801,6 +8296,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `ChartWindow` elements. If `False`, return a list of all ChartWindow` children.
+
Returns
-------
children : dict or list
@@ -7814,6 +8310,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `DiceButton` elements. If `False`, return a list of all DiceButton` children.
+
Returns
-------
children : dict or list
@@ -7827,6 +8324,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `PredefinedSetup` elements. If `False`, return a list of all PredefinedSetup` children.
+
Returns
-------
children : dict or list
@@ -7841,6 +8339,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `Note` child, otherwise fail.
If `False` return all `Note` children in this element
+
Returns
-------
children : list
@@ -7854,6 +8353,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `TurnTrack` elements. If `False`, return a list of all TurnTrack` children.
+
Returns
-------
children : dict or list
@@ -7867,6 +8367,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Piece` elements. If `False`, return a list of all Piece` children.
+
Returns
-------
children : dict or list
@@ -7880,6 +8381,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `SpecificPiece` elements. If `False`, return a list of all SpecificPiece` children.
+
Returns
-------
children : dict or list
@@ -7896,6 +8398,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `WidgetMap` elements. If `False`, return a list of all WidgetMap` children.
+
Returns
-------
children : dict or list
@@ -7909,6 +8412,7 @@ class Game(Element):
----------
asdict : bool
If `True`, return a dictonary that maps key to `Map` elements. If `False`, return a list of all Map` children.
+
Returns
-------
children : dict or list
@@ -7931,6 +8435,7 @@ class Game(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -7948,6 +8453,7 @@ class Game(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -7965,6 +8471,7 @@ class Game(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -7982,6 +8489,7 @@ class Game(Element):
elements. If `False`, return a list of all Board`
children.
+
Returns
-------
children : dict or list
@@ -8014,6 +8522,7 @@ class Game(Element):
single : bool
If `True`, there can be only one `AtStart` child, otherwise fail.
If `False` return all `AtStart` children in this element
+
Returns
-------
children : list
@@ -8057,6 +8566,7 @@ class BuildFile(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Game
@@ -8096,6 +8606,7 @@ class Data(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Version
@@ -8109,6 +8620,7 @@ class Data(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : VASSALVersion
@@ -8122,6 +8634,7 @@ class Data(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Name
@@ -8135,6 +8648,7 @@ class Data(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Description
@@ -8148,6 +8662,7 @@ class Data(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : DateSaved
@@ -8162,6 +8677,7 @@ class Data(Element):
single : bool
If `True`, there can be only one `Version` child, otherwise fail.
If `False` return all `Version` children in this element
+
Returns
-------
children : list
@@ -8177,6 +8693,7 @@ class Data(Element):
If `True`, there can be only one `VASSALVersion` child,
otherwise fail. If `False` return all `VASSALVersion`
children in this element
+
Returns
-------
children : list
@@ -8195,6 +8712,7 @@ class Data(Element):
If `True`, there can be only one `Description` child,
otherwise fail. If `False` return all `De` children in
this element
+
Returns
-------
children : list
@@ -8210,6 +8728,7 @@ class Data(Element):
single : bool
If `True`, there can be only one `DateSaved` child, otherwise fail.
If `False` return all `DateSaved` children in this element
+
Returns
-------
children : list
@@ -8289,6 +8808,7 @@ class ModuleData(Element):
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Data
@@ -8928,6 +9448,18 @@ class VMod:
def fileName(self):
'''Get name of VMod file'''
return self._vmod.filename
+
+ def replaceFiles(self,**files):
+ '''Replace existing files with new files
+
+ Parameters
+ ----------
+ files : dict
+ Dictionary that maps file name to content
+ '''
+ self.removeFiles(*list(files.keys()))
+
+ self.addFiles(**files);
def addFiles(self,**files):
'''Add a set of files to this
@@ -8949,6 +9481,7 @@ class VMod:
File name in module
content : str
File content
+
Returns
-------
element : File
@@ -8963,6 +9496,7 @@ class VMod:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : ExternalFile
@@ -8974,6 +9508,14 @@ class VMod:
def getFileNames(self):
'''Get all filenames in module'''
return self._vmod.namelist()
+
+ def getFileMapping(self):
+ '''Get mapping from short name to full archive name'''
+ from pathlib import Path
+
+ names = self.getFileNames()
+
+ return {Path(p).stem: str(p) for p in names}
def getFiles(self,*filenames):
'''Return named files as a dictionary.
@@ -8982,6 +9524,7 @@ class VMod:
----------
filenames : tuple
The files to get
+
Returns
-------
files : dict
@@ -9009,7 +9552,7 @@ class VMod:
r = self.getFiles(filename)
if filename not in r:
- raise RuntimeException(f'No {filename} found!')
+ raise RuntimeError(f'No {filename} found!')
return parseString(r[filename])
@@ -9031,6 +9574,7 @@ class VMod:
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : VSav
@@ -9043,6 +9587,187 @@ class VMod:
# EOF
#
# ====================================================================
+# From upgrade.py
+
+
+class VLogUpgrader:
+ def __init__(self,
+ vmodFileName,
+ vlogFileName,
+ verbose=False):
+ self._readVModFile(vmodFileName,verbose)
+ self._readVLogFile(vlogFileName,verbose)
+
+ def _readVModFile(self,vmodFileName,verbose=False):
+ with VMod(vmodFileName, 'r') as vmod:
+ self._build = BuildFile(vmod.getBuildFile())
+ self._game = self._build.getGame()
+
+ self._vmod_pieces = {}
+ for piece in self._game.getPieces():
+ name, piece = self._expandPiece(piece,verbose)
+ self._vmod_pieces[name] = piece
+
+ def _expandPiece(self,piece,verbose=False):
+ traits = piece.getTraits();
+ newTraits = Trait.flatten(traits, game=self._game,verbose=verbose)
+
+ piece.setTraits(*newTraits)
+
+ name = newTraits[-1]['name']
+
+ return name, piece
+
+ def _readVLogFile(self,vlogFileName,verbose=False):
+ key, lines, sdata, mdata = SaveIO.readSave(vlogFileName,
+ alsometa=True)
+
+ self._key = key
+ self._lines = lines
+ self._save_data = sdata
+ self._meta_data = mdata
+ self._vlog_pieces = {}
+
+ for line in self._lines:
+ iden, name, piece = self._vlogPiece(line,verbose)
+ if piece is None:
+ continue
+
+ vmod_piece = self._vmod_pieces.get(name,None)
+ if vmod_piece is None:
+ print(f'Did not find piece "{name}" in vmod')
+ vmod_piece = piece
+
+ vmod_piece.copyStates(piece)
+ self._vlog_pieces[iden] = {'name': name,
+ 'vlog': piece,
+ 'vmod': vmod_piece}
+
+
+ def _vlogPiece(self,line,verbose=False):
+ from re import match
+
+ m = match(r'^\+/([0-9]+)/.*;([a-z0-9_]+)\.png.*',line)
+ if m is None:
+ return None,None,None
+
+ iden = int(m.group(1))
+ piece = PieceSlot(None)
+ piece.setTraits(*piece.decodeAdd(line,verbose),iden=iden)
+ basic = piece.getTraits()[-1]
+
+ return iden,basic['name'],piece
+
+
+ def _newLine(self,line,verbose):
+ self._new_lines.append(line)
+ if verbose:
+ print(line)
+
+ def upgrade(self,shownew=False,verbose=False):
+ self._new_lines = []
+ for line in self._lines:
+ add_line = self.newDefine(line,verbose)
+ if add_line:
+ self._newLine(add_line,shownew)
+ continue
+
+ cmd_line = self.newCommand(line,verbose)
+ if cmd_line:
+ self._newLine(cmd_line,shownew)
+ continue
+
+ oth_line = self.other(line,verbose)
+ if oth_line:
+ self._newLine(oth_line,shownew)
+ continue
+
+ self._newLine(line,shownew)
+
+ def newCommand(self,line,verbose=False):
+ from re import match
+
+ m = match(r'LOG\s+([+MD])/([0-9]+)/([^/]+)(.*)',line)
+ if not m:
+ return None
+
+ cmd = m.group(1)
+ iden = int(m.group(2))
+ more = m.group(3)
+
+ if more == 'stack':
+ return None
+
+ vp = self._vlog_pieces.get(iden,None)
+ if vp is None:
+ print(f'Piece {iden} not found: "{line}"')
+ return None
+
+ if cmd == '+' or cmd == 'M':
+ return None
+
+ # Get the code
+ code = more + m.group(4)
+
+ # Decode the states from the code into the old piece
+ vp['vlog'].decodeStates(code,verbose)
+
+ # Get the previsous state from the new piece
+ old = vp['vmod'].encodedStates()
+
+ # Copy states from the old piece to the new piece
+ vp['vmod'].copyStates(vp['vlog'],verbose)
+
+ # Get the new state code from the new piece
+ new = vp['vmod'].encodedStates()
+
+ newline = 'LOG\t'+cmd+'/'+str(iden)+'/'+new+'/'+old+'\\\\'
+ # print('WAS',line)
+ # print('NOW',newline)
+ return newline
+
+ def newDefine(self,line,verbose):
+ from re import match
+
+ m = match(r'\+/([0-9]+)/([^/]+).*',line)
+
+ if not m:
+ return False
+
+ iden = int(m.group(1))
+ more = m.group(2)
+ if more == 'stack':
+ return False
+
+ vp = self._vlog_pieces.get(iden,None)
+ if vp is None:
+ print(f'Piece {iden} not known')
+
+ old = vp['vlog']
+ new = vp['vmod']
+
+ old_add = old._node.childNodes[0].nodeValue;
+ new_add = new.encodeAdd(*new.getTraits(),iden=iden,verbose=verbose);
+
+ return new_add
+
+ def other(self,line,verbose=False):
+ return None
+
+
+ def write(self,outFileName,verbose=False):
+ SaveIO.writeSave(outFileName,
+ key = 0xAA,
+ lines = self._new_lines,
+ savedata = self._save_data,
+ moduledata = self._meta_data)
+
+
+
+#
+# EOF
+#
+# ====================================================================
# From exporter.py
class Exporter:
@@ -9137,12 +9862,87 @@ class Exporter:
# Exporter class
#
class LaTeXExporter(Exporter):
+ class Specials:
+ BATTLE_MARK = 'wgBattleMarker'
+ BATTLE_CTRL = 'wgBattleCtrl'
+ BATTLE_CALC = 'wgBattleCalc'
+ BATTLE_UNIT = 'wgBattleUnit'
+ ODDS_MARK = 'wgOddsMarker'
+ HIDDEN_NAME = 'wg hidden unit'
+
+ class Keys:
+ MARK_BATTLE = key(NONE,0)+',wgMarkBattle'
+ CLEAR_BATTLE = key(NONE,0)+',wgClearBattle'
+ CLEAR_ALL_BATTLE = key(NONE,0)+',wgClearAllBattle'
+ ZERO_BATTLE = key(NONE,0)+',wgZeroBattle'
+ INCR_BATTLE = key(NONE,0)+',wgIncrBattle'
+ SET_BATTLE = key(NONE,0)+',wgSetBattle'
+ GET_BATTLE = key(NONE,0)+',wgGetBattle'
+ MARK_ODDS = key(NONE,0)+',wgMarkOdds'
+ MARK_RESULT = key(NONE,0)+',wgMarkResult'
+ CLEAR_MOVED = key(NONE,0)+',wgClearMoved'
+ ZERO_BATTLE_AF = key(NONE,0)+',wgZeroBattleAF'
+ ZERO_BATTLE_DF = key(NONE,0)+',wgZeroBattleDF'
+ ZERO_BATTLE_FRAC = key(NONE,0)+',wgZeroBattleFrac'
+ ZERO_BATTLE_ODDS = key(NONE,0)+',wgZeroBattleOdds'
+ ZERO_BATTLE_SHFT = key(NONE,0)+',wgZeroBattleShift'
+ ZERO_BATTLE_IDX = key(NONE,0)+',wgZeroBattleIdx'
+ CALC_BATTLE_AF = key(NONE,0)+',wgCalcBattleAF'
+ CALC_BATTLE_DF = key(NONE,0)+',wgCalcBattleDF'
+ CALC_BATTLE_FRAC = key(NONE,0)+',wgCalcBattleFrac'
+ CALC_BATTLE_ODDS = key(NONE,0)+',wgCalcBattleOdds'
+ CALC_BATTLE_SHFT = key(NONE,0)+',wgCalcBattleShift'
+ CALC_BATTLE_IDX = key(NONE,0)+',wgCalcBattleIdx'
+ CALC_BATTLE_RES = key(NONE,0)+',wgCalcBattleResult'
+ CLEAR_BATTLE_PHS = key(NONE,0)+',wgClearBattlePhs'
+ RESOLVE_BATTLE = key(NONE,0)+',wgResolveBattle'
+ ROLL_DICE = key(NONE,0)+',wgRollDice'
+ DICE_INIT_KEY = key(NONE,0)+',wgInitDice'
+ CLEAR_KEY = key('C')
+ CLEAR_ALL_KEY = key('C',CTRL_SHIFT)
+ DELETE_KEY = key('D')
+ ELIMINATE_KEY = key('E')
+ FLIP_KEY = key('F')
+ TRAIL_KEY = key('T')
+ RESTORE_KEY = key('R')
+ MARK_KEY = key('X')
+ RESOLVE_KEY = key('Y')
+ ROTATE_CCWKey = key('[')
+ ROTATE_CWKey = key(']')
+ CHARTS_KEY = key('A',ALT)
+ OOB_KEY = key('B',ALT)
+ COUNTERS_KEY = key('C',ALT)
+ DEAD_KEY = key('E',ALT)
+ DICE_KEY = key('6',ALT)
+ RECALC_ODDS = key('X',CTRL_SHIFT)
+
+ class Globals:
+ BATTLE_COUNTER = 'wgBattleCounter'
+ CURRENT_BATTLE = 'wgCurrentBattle'
+ CURRENT_ATTACKER = 'wgCurrentAttacker'
+ BATTLE_NO = 'wgBattleNo'
+ BATTLE_AF = 'wgBattleAF'
+ BATTLE_DF = 'wgBattleDF'
+ BATTLE_FRAC = 'wgBattleFrac'
+ BATTLE_IDX = 'wgBattleIdx'
+ BATTLE_ODDS = 'wgBattleOdds'
+ BATTLE_ODDSM = 'wgBattleOddsMarker'
+ BATTLE_SHIFT = 'wgBattleShift'
+ BATTLE_RESULT = 'wgBattleResult'
+ AUTO_ODDS = 'wgAutoOdds'
+ AUTO_RESULTS = 'wgAutoResults'
+ NO_CLEAR_MOVES = 'wgNoClearMoves'
+ NO_CLEAR_BATTLES = 'wgNoClearBattles'
+ DEBUG = 'wgDebug'
+ VERBOSE = 'wgVerbose'
+
def __init__(self,
vmodname = 'Draft.vmod',
pdfname = 'export.pdf',
infoname = 'export.json',
title = 'Draft',
version = 'draft',
+ imageFormat = 'png',
description = '',
rules = None,
tutorial = None,
@@ -9197,76 +9997,80 @@ class LaTeXExporter(Exporter):
self._nochit = nochit
self._resolution = resolution
self._counterScale = counterScale
- self._battleMark = 'wgBattleMarker'
+ self._img_format = imageFormat.lower()
+
+ self._battleMark = LaTeXExporter.Specials.BATTLE_MARK
+ self._oddsMark = LaTeXExporter.Specials.ODDS_MARK
+ self._battleCtrl = LaTeXExporter.Specials.BATTLE_CTRL
+ self._battleCalc = LaTeXExporter.Specials.BATTLE_CALC
+ self._battleUnit = LaTeXExporter.Specials.BATTLE_UNIT
+ self._hiddenName = LaTeXExporter.Specials.HIDDEN_NAME
+ self._markBattle = LaTeXExporter.Keys.MARK_BATTLE
+ self._clearBattle = LaTeXExporter.Keys.CLEAR_BATTLE
+ self._clearAllBattle = LaTeXExporter.Keys.CLEAR_ALL_BATTLE
+ self._zeroBattle = LaTeXExporter.Keys.ZERO_BATTLE
+ self._incrBattle = LaTeXExporter.Keys.INCR_BATTLE
+ self._setBattle = LaTeXExporter.Keys.SET_BATTLE
+ self._getBattle = LaTeXExporter.Keys.GET_BATTLE
+ self._markOdds = LaTeXExporter.Keys.MARK_ODDS
+ self._markResult = LaTeXExporter.Keys.MARK_RESULT
+ self._clearMoved = LaTeXExporter.Keys.CLEAR_MOVED
+ self._zeroBattleAF = LaTeXExporter.Keys.ZERO_BATTLE_AF
+ self._zeroBattleDF = LaTeXExporter.Keys.ZERO_BATTLE_DF
+ self._zeroBattleFrac = LaTeXExporter.Keys.ZERO_BATTLE_FRAC
+ self._zeroBattleOdds = LaTeXExporter.Keys.ZERO_BATTLE_ODDS
+ self._zeroBattleShft = LaTeXExporter.Keys.ZERO_BATTLE_SHFT
+ self._zeroBattleIdx = LaTeXExporter.Keys.ZERO_BATTLE_IDX
+ self._calcBattleAF = LaTeXExporter.Keys.CALC_BATTLE_AF
+ self._calcBattleDF = LaTeXExporter.Keys.CALC_BATTLE_DF
+ self._calcBattleFrac = LaTeXExporter.Keys.CALC_BATTLE_FRAC
+ self._calcBattleOdds = LaTeXExporter.Keys.CALC_BATTLE_ODDS
+ self._calcBattleShft = LaTeXExporter.Keys.CALC_BATTLE_SHFT
+ self._calcBattleIdx = LaTeXExporter.Keys.CALC_BATTLE_IDX
+ self._calcBattleRes = LaTeXExporter.Keys.CALC_BATTLE_RES
+ self._clearBattlePhs = LaTeXExporter.Keys.CLEAR_BATTLE_PHS
+ self._resolveBattle = LaTeXExporter.Keys.RESOLVE_BATTLE
+ self._rollDice = LaTeXExporter.Keys.ROLL_DICE
+ self._diceInitKey = LaTeXExporter.Keys.DICE_INIT_KEY
+ self._clearKey = LaTeXExporter.Keys.CLEAR_KEY
+ self._clearAllKey = LaTeXExporter.Keys.CLEAR_ALL_KEY
+ self._deleteKey = LaTeXExporter.Keys.DELETE_KEY
+ self._eliminateKey = LaTeXExporter.Keys.ELIMINATE_KEY
+ self._flipKey = LaTeXExporter.Keys.FLIP_KEY
+ self._trailKey = LaTeXExporter.Keys.TRAIL_KEY
+ self._restoreKey = LaTeXExporter.Keys.RESTORE_KEY
+ self._markKey = LaTeXExporter.Keys.MARK_KEY
+ self._resolveKey = LaTeXExporter.Keys.RESOLVE_KEY
+ self._rotateCCWKey = LaTeXExporter.Keys.ROTATE_CCWKey
+ self._rotateCWKey = LaTeXExporter.Keys.ROTATE_CWKey
+ self._chartsKey = LaTeXExporter.Keys.CHARTS_KEY
+ self._oobKey = LaTeXExporter.Keys.OOB_KEY
+ self._countersKey = LaTeXExporter.Keys.COUNTERS_KEY
+ self._deadKey = LaTeXExporter.Keys.DEAD_KEY
+ self._diceKey = LaTeXExporter.Keys.DICE_KEY
+ self._recalcOdds = LaTeXExporter.Keys.RECALC_ODDS
+ self._battleCounter = LaTeXExporter.Globals.BATTLE_COUNTER
+ self._currentBattle = LaTeXExporter.Globals.CURRENT_BATTLE
+ self._currentAttacker = LaTeXExporter.Globals.CURRENT_ATTACKER
+ self._battleNo = LaTeXExporter.Globals.BATTLE_NO
+ self._battleAF = LaTeXExporter.Globals.BATTLE_AF
+ self._battleDF = LaTeXExporter.Globals.BATTLE_DF
+ self._battleFrac = LaTeXExporter.Globals.BATTLE_FRAC
+ self._battleIdx = LaTeXExporter.Globals.BATTLE_IDX
+ self._battleOdds = LaTeXExporter.Globals.BATTLE_ODDS
+ self._battleOddsM = LaTeXExporter.Globals.BATTLE_ODDSM
+ self._battleShift = LaTeXExporter.Globals.BATTLE_SHIFT
+ self._battleResult = LaTeXExporter.Globals.BATTLE_RESULT
+ self._autoOdds = LaTeXExporter.Globals.AUTO_ODDS
+ self._autoResults = LaTeXExporter.Globals.AUTO_RESULTS
+ self._noClearMoves = LaTeXExporter.Globals.NO_CLEAR_MOVES
+ self._noClearBattles = LaTeXExporter.Globals.NO_CLEAR_BATTLES
+ self._debug = LaTeXExporter.Globals.DEBUG
+ self._verbose = LaTeXExporter.Globals.VERBOSE
self._battleMarks = []
self._oddsMarks = []
self._resultMarks = []
- self._markBattle = key(NONE,0)+',wgMarkBattle'
- self._clearBattle = key(NONE,0)+',wgClearBattle'
- self._clearAllBattle = key(NONE,0)+',wgClearAllBattle'
- self._zeroBattle = key(NONE,0)+',wgZeroBattle'
- self._incrBattle = key(NONE,0)+',wgIncrBattle'
- self._setBattle = key(NONE,0)+',wgSetBattle'
- self._getBattle = key(NONE,0)+',wgGetBattle'
- self._markOdds = key(NONE,0)+',wgMarkOdds'
- self._markResult = key(NONE,0)+',wgMarkResult'
- self._clearMoved = key(NONE,0)+',wgClearMoved'
- self._zeroBattleAF = key(NONE,0)+',wgZeroBattleAF'
- self._zeroBattleDF = key(NONE,0)+',wgZeroBattleDF'
- self._zeroBattleFrac = key(NONE,0)+',wgZeroBattleFrac'
- self._zeroBattleOdds = key(NONE,0)+',wgZeroBattleOdds'
- self._zeroBattleShft = key(NONE,0)+',wgZeroBattleShift'
- self._zeroBattleIdx = key(NONE,0)+',wgZeroBattleIdx'
- self._calcBattleAF = key(NONE,0)+',wgCalcBattleAF'
- self._calcBattleDF = key(NONE,0)+',wgCalcBattleDF'
- self._calcBattleFrac = key(NONE,0)+',wgCalcBattleFrac'
- self._calcBattleOdds = key(NONE,0)+',wgCalcBattleOdds'
- self._calcBattleShft = key(NONE,0)+',wgCalcBattleShift'
- self._calcBattleIdx = key(NONE,0)+',wgCalcBattleIdx'
- self._calcBattleRes = key(NONE,0)+',wgCalcBattleResult'
- self._clearBattlePhs = key(NONE,0)+',wgClearBattlePhs'
- self._resolveBattle = key(NONE,0)+',wgResolveBattle'
- self._rollDice = key(NONE,0)+',wgRollDice'
- self._diceInitKey = key(NONE,0)+',wgInitDice'
- self._clearKey = key('C')
- self._clearAllKey = key('C',CTRL_SHIFT)
- self._deleteKey = key('D')
- self._eliminateKey = key('E')
- self._flipKey = key('F')
- self._trailKey = key('M')
- self._restoreKey = key('R')
- self._markKey = key('X')
- self._resolveKey = key('Y')
- self._rotateCCWKey = key('[')
- self._rotateCWKey = key(']')
- self._chartsKey = key('A',ALT)
- self._oobKey = key('B',ALT)
- self._countersKey = key('C',ALT)
- self._deadKey = key('E',ALT)
- self._diceKey = key('6',ALT)
- self._battleCounter = 'wgBattleCounter'
- self._currentBattle = 'wgCurrentBattle'
- self._currentAttacker = 'wgCurrentAttacker'
- self._battleNo = 'wgBattleNo'
- self._battleAF = 'wgBattleAF'
- self._battleDF = 'wgBattleDF'
- self._battleFrac = 'wgBattleFrac'
- self._battleIdx = 'wgBattleIdx'
- self._battleOdds = 'wgBattleOdds'
- self._battleOddsM = 'wgBattleOddsMarker'
- self._battleShift = 'wgBattleShift'
- self._battleCtrl = 'wgBattleCtrl'
- self._battleCalc = 'wgBattleCalc'
- self._battleUnit = 'wgBattleUnit'
- self._battleResult = 'wgBattleResult'
- self._autoOdds = 'wgAutoOdds'
- self._autoResults = 'wgAutoResults'
- self._noClearMoves = 'wgNoClearMoves'
- self._noClearBattles = 'wgNoClearBattles'
- self._debug = 'wgDebug'
- self._verbose = 'wgVerbose'
self._hidden = None
- self._hiddenName = 'wg hidden unit'
self._dice = {}
self._diceInit = None
@@ -9283,6 +10087,8 @@ class LaTeXExporter(Exporter):
v(f'Visible grids: {self._visible}')
v(f'Resolution: {self._resolution}')
v(f'Scale of counters: {self._counterScale}')
+ v(f'Image format: {self._img_format}')
+
def setup(self):
# Start the processing
@@ -9306,6 +10112,7 @@ class LaTeXExporter(Exporter):
----------
args : list
List of process command line elements
+
Returns
-------
pipe : subprocess.Pipe
@@ -9320,16 +10127,18 @@ class LaTeXExporter(Exporter):
def addPws(self,opw=None,upw=None):
'''Add a `Pws` element to arguments
+ Add password options
+
Parameters
----------
kwargs : dict
Dictionary of attribute key-value pairs
+
Returns
-------
element : Pws
The added element
'''
- '''Add password options'''
args = []
if upw is not None: args.extend(['-upw',upw])
if opw is not None: args.extend(['-opw',opw])
@@ -9392,6 +10201,92 @@ class LaTeXExporter(Exporter):
return info
# ================================================================
+ @classmethod
+ def parseLength(cls,value,def_unit='px'):
+ from re import match
+
+ scales = {
+ 'px': 1,
+ 'pt': 1.25,
+ 'pc': 15,
+ 'in': 90,
+ 'mm': 3.543307,
+ 'cm': 35.43307,
+ '%': -1/100
+ }
+
+ if not value:
+ return 0
+
+ parts = match(r'^\s*(-?\d+(?:\.\d+)?)\s*(px|in|cm|mm|pt|pc|%)?', value)
+ if not parts:
+ raise RuntimeError(f'Unknown length format: "{value}"')
+
+ number = float(parts.group(1))
+ unit = parts.group(2) or def_unit
+ factor = scales.get(unit,None)
+
+ if not factor:
+ raise RuntimeError(f'Unknown unit: "{unit}"')
+
+ return factor * number
+
+ # ----------------------------------------------------------------
+ @classmethod
+ def scaleSVG(cls,buffer,factor):
+ '''Buffer is bytes'''
+ from xml.dom.minidom import parse
+ from re import split
+ from io import StringIO, BytesIO
+
+ if not LaTeXExporter.isSVG(buffer):
+ return buffer
+
+ with BytesIO(buffer) as stream:
+ doc = parse(stream)
+
+ if not doc:
+ raise RuntimeError('Failed to parse buffer as XML')
+
+ root = doc.childNodes[0]
+ getA = lambda e,n,d=None : \
+ e.getAttribute(n) if e.hasAttribute(n) else d
+ setA = lambda e,n,v : e.setAttribute(n,v)
+ leng = LaTeXExporter.parseLength
+
+ width = leng(getA(root,'width', '0'))
+ height = leng(getA(root,'height','0'))
+ vport = getA(root,'viewBox','0 0 0 0').strip()
+ vp = [leng(v) for v in split('[ \t,]',vport)]
+ # print(f'Input WxH: {width}x{height} ({vp})')
+
+ width *= factor
+ height *= factor
+ vp = [factor * v for v in vp]
+
+ # print(f'Scaled WxH: {width}x{height} ({vp})')
+
+ if width <= 0 and vp:
+ width = vp[2] - vp[0]
+
+ if height <= 0 and vp:
+ height = vp[3] - vp[1]
+
+ if not vp:
+ vp = [0, 0, width, height]
+
+ setA(root,'transform',f'scale({factor})')
+ setA(root,'width', f'{width}')
+ setA(root,'height',f'{height}')
+ setA(root,'viewBox',' '.join([f'{v}' for v in vp]))
+
+
+ with StringIO() as out:
+ doc.writexml(out)
+ return out.getvalue().encode()
+
+
+ # ================================================================
def convertPage(self,page,opw=None,upw=None,timeout=None):
'''Convert a page from PDF into an image (bytes)
@@ -9411,17 +10306,22 @@ class LaTeXExporter(Exporter):
info : dict
Image information
'''
- args = ['pdftocairo',
+ args = ['pdftocairo']
+ if self._img_format != 'svg':
+ args.extend([
'-transp',
- '-singlefile',
+ '-singlefile'])
+
+ args.extend([
'-r', str(self._resolution),
'-f', str(page),
'-l', str(page),
- '-png' ]
+ f'-{self._img_format}' ])
args.extend(self.addPws(opw=opw,upw=upw))
args.append(self._pdfname)
args.append('-')
-
+
+ # print(f'Conversion command',' '.join(args))
proc = self.createProcess(args)
try:
@@ -9432,7 +10332,16 @@ class LaTeXExporter(Exporter):
raise RuntimeError(f'Failed to convert page {page} of '
f'{self._pdfname}: {e}')
- # Just return the bytes
+ if len(out) <= 0:
+ raise RuntimeError(f'Failed to convert page {page} of '
+ f'{self._pdfname}: {err}')
+
+ # This does not seem to work - VASSAL (and Inkscape) does not
+ # apply the 'scale' transformation to the image!
+ #
+ # if self._img_format == 'svg':
+ # out = LaTeXExporter.scaleSVG(out,2)
+
return out
@@ -9501,6 +10410,11 @@ class LaTeXExporter(Exporter):
return imgsinfo
# ----------------------------------------------------------------
+ @classmethod
+ def isSVG(cls,buffer):
+ return buffer[:5] == b'<?xml'
+
+ # ----------------------------------------------------------------
def getBB(self,buffer):
'''Get bounding box of image
@@ -9514,11 +10428,26 @@ class LaTeXExporter(Exporter):
ulx, uly, lrx, lry : tuple
The coordinates of the bounding box
'''
- from PIL import Image
from io import BytesIO
+
+ with BytesIO(buffer) as inp:
+ if LaTeXExporter.isSVG(buffer):
+ from svgelements import SVG
+
+ svg = SVG.parse(inp)
+ # bb = svg.bbox()
+ # if bb is None:
+ # print(f'No bounding box!')
+ # bb = [0, 0, 1, 1]
+ # else:
+ # bb = [int(b) for b in bb]
+ x, y, w, h = svg.x, svg.y, svg.width, svg.height
+ bb = (x,y,x+w,y+h)
+ else:
+ from PIL import Image
- with Image.open(BytesIO(buffer)) as img:
- bb = img.getbbox()
+ with Image.open(inp) as img:
+ bb = img.getbbox()
return bb
@@ -9536,11 +10465,21 @@ class LaTeXExporter(Exporter):
ulx, uly, lrx, lry : tuple
The coordinates of the bounding box
'''
- from PIL import Image
from io import BytesIO
-
- with Image.open(BytesIO(buffer)) as img:
- w, h = img.width, img.height
+
+ with BytesIO(buffer) as inp:
+ if LaTeXExporter.isSVG(buffer):
+ from svgelements import SVG
+
+ svg = SVG.parse(inp)
+ w, h = svg.x, svg.y, svg.width, svg.height
+ # bb = svg.bbox()
+ # w, h = int(bb[2]-bb[0]),int(bb[3]-bb[1])
+ else:
+ from PIL import Image
+
+ with Image.open(inp) as img:
+ w, h = img.width, img.height
return w,h
@@ -9591,11 +10530,13 @@ class LaTeXExporter(Exporter):
typ = info.get('category','counter')
sub = info.get('subcategory','all')
-
- info['filename'] = info['name'].replace(' ','_') + '.png'
+ nam = info['name']
+ num = info['number']
+
+ info['filename'] = f'{nam.replace(" ","_")}.{self._img_format}'
imgfn = 'images/'+info['filename']
if imgfn not in self._vmod.getFileNames():
- if typ == 'counter':
+ if typ == 'counter' and self._img_format != 'svg':
# print(f'Possibly scale file {imgfn}')
info['img'] = self.scaleImage(info['img'],
counterScale)
@@ -9621,9 +10562,19 @@ class LaTeXExporter(Exporter):
cat[sub] = {}
tgt = cat[sub]
- v(f'[{info["name"]}]',end=' ',flush=True,noindent=True)
+ v(f'[{nam}]',end=' ',flush=True,noindent=True)
#self.message(f'Adding "{info["name"]}" to catalogue')
- tgt[info['name']] = info
+ #
+ # Here we could handle multiple info's with the same
+ # name by adding a unique postfix - e.g., for dices
+ # what have non-uniform PMFs.
+ #
+ # if info['name'] in tgt:
+ # n = len([i for k,i in tgt.items() if k.startswith(info['name'])])
+ # info['name'] += '_' + str(n)
+ # info['filename'] = info['name'].replace(' ','_') + '.png'
+ unam = f'{nam}'
+ tgt[unam] = info
if self._nonato: continue
@@ -9854,7 +10805,7 @@ class LaTeXExporter(Exporter):
v(f'We have symbolic dice')
self._diceInit = []
# from pprint import pprint
- # pprint(self._dice,depth=3)
+ # pprint(self._dice,depth=2)
for die, faces in self._dice.items():
ico = self.getIcon(die+'-die-icon','')
# print(f'Die {die} icon="{ico}"')
@@ -9866,7 +10817,9 @@ class LaTeXExporter(Exporter):
text = die if ico == '' else '',
icon = ico,
tooltip = f'{die} die roll',
- format = (f'{{"{die} die roll: "+result1'
+ format = (f'{{"<b>"+PlayerSide+"</b> "+'
+ f'"(<i>"+PlayerName+"</i>): "+'+
+ f'"{die} die roll: "+result1'
# f'+" <img src=\'{die}-"+result1'
# f'+".png\' width=24 height=24>"'
f'}}'),
@@ -9876,7 +10829,8 @@ class LaTeXExporter(Exporter):
sdie = symb.addDie(name = die);
for face, fdata in faces.items():
fn = fdata['filename']
- val = int(fn.replace('.png','').replace(die+'-',''))
+ val = int(fn.replace(f'.{self._img_format}','')
+ .replace(die+'-',''))
dmin = min(dmin,val)
dmax = min(dmax,val)
sdie.addFace(icon = fn,
@@ -9988,6 +10942,7 @@ class LaTeXExporter(Exporter):
[['Ctrl-C', 'Counter', 'Clear battle'],
['Ctrl-Shift-C','Board', 'Clear all battle'],
['Ctrl-X', 'Board,Counter','Mark battle'],
+ ['Ctrl-Shift-X','Board,Counter','Recalculate Odds'],
['Ctrl-Y', 'Board,Counter','Resolve battle'],
]):
ks = [k[0] for k in keys]
@@ -10092,6 +11047,7 @@ class LaTeXExporter(Exporter):
wrap = True,
description = 'Zero battle counter',
),
+ # Set global property combat # from this
GlobalPropertyTrait(
['',self._setBattle,GlobalPropertyTrait.DIRECT,
f'{{{self._battleCounter}}}'],
@@ -10411,6 +11367,8 @@ class LaTeXExporter(Exporter):
# for the current battle
#
traits = [
+ # getBattle retrieves the battle number from the global property.
+ # clearBattle sets piece battle to -1
DynamicPropertyTrait(['',self._getBattle,
DynamicPropertyTrait.DIRECT,
f'{{{self._currentBattle}}}'],
@@ -10421,7 +11379,9 @@ class LaTeXExporter(Exporter):
numeric = True,
value = f'{{-1}}',
description = 'Set battle number'),
- GlobalPropertyTrait(['',self._setBattle, GlobalPropertyTrait.DIRECT,
+ # This setBattle sets current attacker in global property
+ GlobalPropertyTrait(['',self._setBattle,
+ GlobalPropertyTrait.DIRECT,
'{IsAttacker}'],
name = self._currentAttacker,
numeric = True,
@@ -10457,6 +11417,9 @@ class LaTeXExporter(Exporter):
description = f'Add battle marker {i+1}',
placement = PlaceTrait.ABOVE,
above = False))
+ # Get current global battle number
+ # Set current battle
+ # Filtered on current global battle # is equal to
trig.append(
TriggerTrait(command = '',#Mark battle',
key = self._markBattle,
@@ -10758,7 +11721,9 @@ class LaTeXExporter(Exporter):
def oddsMarkerTraits(self,c=None):
'''Derives from the CurrentBattle prototype and adds a submenu
to replace odds counter with result marker'''
+ gpid = self._game.nextPieceSlotId()
traits = [PrototypeTrait(name=self._currentBattle),
+ MarkTrait(self._oddsMark,'true'),
NonRectangleTrait(filename = c['filename'],
image = c['img']),
DynamicPropertyTrait(
@@ -10773,13 +11738,53 @@ class LaTeXExporter(Exporter):
TriggerTrait(command = '',
key = self._getBattle+'Details',
actionKeys = [self._getBattle,
- self._getBattle+'More'])]
+ self._getBattle+'More']),
+ # DeleteTrait('',self._recalcOdds+'Delete'),
+ # ReplaceTrait(command = '',
+ # key = self._recalcOdds+'Replace',
+ # markerSpec = '',
+ # markerText = 'null',
+ # xOffset = 0,
+ # yOffset = 0,
+ # matchRotation = False,
+ # afterKey = '',
+ # gpid = gpid,
+ # description = f'Replace with nothing'),
+ GlobalHotkeyTrait(name = '',
+ key = self._calcBattleOdds+'Start',
+ globalHotkey = self._calcBattleOdds+'ReAuto',
+ description = 'Trampoline to global'),
+ # Recalculate odds
+ # First setBatle to make battle No global
+ # Then delete this
+ # Then send global key command
+ TriggerTrait(command = 'Recalculate',
+ key = self._recalcOdds,
+ actionKeys = [self._setBattle,
+ self._calcBattleOdds+'Start',
+ self._clearBattle,
+ ]),
+ ReportTrait(self._recalcOdds+'Delete',
+ report=(f'{{{self._debug}?'
+ f'("~ "+BasicName+'
+ f'": Deleting self"):""}}')),
+ ReportTrait(self._clearBattle,
+ report=(f'{{{self._debug}?'
+ f'("~ "+BasicName+'
+ f'": Remove"):""}}')),
+ ReportTrait(self._recalcOdds,
+ report=(f'{{{self._debug}?'
+ f'("! Recalculate Odds"):""}}')),
+ ReportTrait(self._calcBattleOdds+'Start',
+ report=(f'{{{self._debug}?'
+ f'("~ Start auto recalculate Odds"):""}}')),
+ ]
subs = []
place = []
trig = []
rept = []
- ukeys = []
+ ukeys = [self._recalcOdds]
first = ''
for i, result in enumerate(self._resultMarks):
r = result.replace('result marker','').strip()
@@ -10910,7 +11915,9 @@ class LaTeXExporter(Exporter):
traits = [#ReportTrait(self._eliminateKey,
# self._restoreKey,
# self._trailKey),
- TrailTrait(),
+ TrailTrait(lineWidth = 5,
+ radius = 10,
+ key = self._trailKey),
RotateTrait(),
MovedTrait(xoff = int(offX),yoff = int(offY)),
DeleteTrait(),
@@ -10921,6 +11928,8 @@ class LaTeXExporter(Exporter):
restoreName = 'Restore',
restoreKey = self._restoreKey,
description = 'Eliminate unit'),
+ # ReportTrait(self._trailKey,
+ # f'{{"Enabling trail on "+BasicName}}'),
PrototypeTrait(name=self._battleUnit),
MarkTrait(name='Faction',value=faction)]
@@ -10966,6 +11975,7 @@ class LaTeXExporter(Exporter):
bb = self.getBB(c['img'])
height = bb[3]-bb[1] if bb is not None else 1
width = bb[2]-bb[0] if bb is not None else 1
+
cf = subc.get(cn + ' flipped', None)
traits = [PrototypeTrait(name=f'{subn} prototype')]
@@ -10974,10 +11984,14 @@ class LaTeXExporter(Exporter):
if not self._nonato:
mains = c.get('mains','')
- m = set([clean(mains)] +
- [clean(m) for m in mains.split(',')])
- traits.extend([PrototypeTrait(name=f'{m.strip()} prototype')
- for m in set(m)])
+ mm = clean(mains).strip()
+ traits.append(PrototypeTrait(name=f'{mm} prototype'))
+ # Commented code adds all 'main' types as prototypes, which
+ # doesn't make so much sense
+ #
+ # m = set([clean(m) for m in mains.split(',')])
+ # traits.extend([PrototypeTrait(name=f'{m.strip()} prototype')
+ # for m in set(m)])
for p in ['echelon','command']:
val = c.get(p,None)
if val is not None:
@@ -11187,10 +12201,17 @@ class LaTeXExporter(Exporter):
True if any piece can be flipped
'''
with VerboseGuard(f'Adding board {name}') as v:
+ # from pprint import pprint
+ # pprint(info)
map = self._game.addMap(mapName=name,
markUnmovedHotkey=self._clearMoved)
map.addCounterDetailViewer(
- propertyFilter=f'{{{self._battleMark}!=true}}')
+ propertyFilter=f'{{{self._battleMark}!=true}}',
+ fontSize = 14,
+ summaryReportFormat = '<b>$LocationName$</b>',
+ hotkey = key('\n'),
+ stopAfterShowing = True
+ )
map.addHidePiecesButton()
map.addGlobalMap()
# Basics
@@ -11207,18 +12228,23 @@ class LaTeXExporter(Exporter):
map.addHighlightLastMoved()
map.addZoomer()
- map.addMassKey(name='Eliminate',
+ map.addMassKey(name = 'Eliminate',
buttonHotkey = self._eliminateKey,
hotkey = self._eliminateKey,
icon = self.getIcon('eliminate-icon',
'/icons/16x16/edit-undo.png'),
tooltip = 'Eliminate selected units')
- map.addMassKey(name='Delete',
+ map.addMassKey(name = 'Delete',
buttonHotkey = self._deleteKey,
hotkey = self._deleteKey,
icon = self.getIcon('delete-icon',
'/icons/16x16/no.png'),
tooltip = 'Delete selected units')
+ map.addMassKey(name = 'Trail',
+ buttonHotkey = self._trailKey,
+ hotkey = self._trailKey,
+ icon = '',
+ tooltip = '')
map.addMassKey(name='Rotate CW',
buttonHotkey = self._rotateCWKey,
hotkey = self._rotateCWKey,
@@ -11244,7 +12270,7 @@ class LaTeXExporter(Exporter):
f'{self._noClearMoves})'
f':""}}'))
if hasFlipped:
- map.addMassKey(name='Flip',
+ map.addMassKey(name = 'Flip',
buttonHotkey = self._flipKey,
hotkey = self._flipKey,
icon = self.getIcon('flip-icon',
@@ -11266,14 +12292,15 @@ class LaTeXExporter(Exporter):
curUnt = (f'{{{self._battleNo}=={self._currentBattle}&&'
f'{self._battleUnit}==true}}')
markSel = (f'{{{self._battleNo}=={self._currentBattle}&&'
- f'{self._battleMark}==true}}')
+ f'{self._battleMark}==true&&'
+ f'{self._oddsMark}!=true}}')
# ctrlSel = '{BasicName=="wg hidden unit"}'
map.addMassKey(name = 'User mark battle',
buttonHotkey = self._markKey,
buttonText = '',
hotkey = self._markBattle,
- icon = 'battle-marker-icon.png',
+ icon = f'battle-marker-icon.{self._img_format}',
tooltip = 'Mark battle',
target = '',
singleMap = False,
@@ -11336,7 +12363,7 @@ class LaTeXExporter(Exporter):
buttonText = '',
buttonHotkey = self._clearAllKey,
hotkey = self._clearAllBattle,
- icon = 'clear-battles-icon.png',
+ icon = f'clear-battles-icon.{self._img_format}',
tooltip = 'Clear all battles',
target = '',
singleMap = False,
@@ -11364,7 +12391,7 @@ class LaTeXExporter(Exporter):
map.addMassKey(name = 'Selected resolve battle',
buttonHotkey = self._resolveKey,
hotkey = self._resolveKey,
- icon = 'resolve-battles-icon.png',
+ icon = f'resolve-battles-icon.{self._img_format}',
tooltip = 'Resolve battle',
singleMap = False,
filter = oddsSel,
@@ -11433,15 +12460,45 @@ class LaTeXExporter(Exporter):
reportFormat = (f'{{{self._debug}?'
f'("~ {name}: '
f'Auto calculate odds"):""}}'))
+ map.addMassKey(name = 'User recalc',
+ buttonHotkey = self._recalcOdds,
+ buttonText = '',
+ hotkey = self._recalcOdds,
+ icon = '',
+ tooltip = 'Recalculate odds',
+ singleMap = False,
+ filter = '',
+ reportFormat = (f'{{{self._debug}?'
+ f'("~ {name}: '
+ f'Recalculate odds"):""}}'))
+ map.addMassKey(name = 'Auto recalc battle odds',
+ buttonText = '',
+ buttonHotkey = self._calcBattleOdds+'ReAuto',
+ hotkey = self._calcBattleOdds+'Start',
+ icon = '',
+ tooltip = '',
+ target = '',
+ singleMap = False,
+ filter = markSel,
+ reportFormat = (f'{{{self._debug}?'
+ f'("~ {name}: '
+ f'Auto re-calculate odds"):""}}'))
+ v(f'Getting the board dimensions')
ulx,uly,lrx,lry = self.getBB(info['img'])
- width = abs(ulx - lrx)
- height = abs(uly - lry)
- width, height = self.getWH(info['img'])
+ width = int(abs(ulx - lrx))
+ height = int(abs(uly - lry))
+ # Why is it we take the width and height like this?
+ # Do they every differ from the above?
+ # This is the only place that we actually use this
+ #
+ # width, height = self.getWH(info['img'])
height += 20
width += 5
-
+ # v(f'{ulx},{uly},{lrx},{lry}')
+
+ v(f'Board BB=({lrx},{lry})x({ulx},{uly}) {width}x{height}')
picker = map.addBoardPicker()
board = picker.addBoard(name = name,
image = info['filename'],
@@ -11451,13 +12508,14 @@ class LaTeXExporter(Exporter):
zoned.addHighlighter()
if not 'zones' in info:
- full = zoned.addZone(name = full,
+ color = rgb(255,0,0)
+ full = zoned.addZone(name = 'full',
useParentGrid = False,
path=(f'{ulx},{uly};' +
f'{lrx},{uly};' +
f'{lrx},{lry};' +
f'{ulx},{lry}'))
- grid = zone.addHexGrid(color = color,
+ grid = full.addHexGrid(color = color,
dx = HEX_WIDTH,
dy = HEX_HEIGHT,
visible = self._visible)
@@ -11489,7 +12547,7 @@ class LaTeXExporter(Exporter):
'''Add a "Dead Map" element to the module
'''
name = 'DeadMap'
- with VerboseGuard(f'Adding board {name}') as v:
+ with VerboseGuard(f'Adding deadmap {name}') as v:
map = self._game.addMap(mapName = name,
buttonName = '',
markMoved = 'Never',
@@ -12009,7 +13067,7 @@ class LaTeXExporter(Exporter):
# Do not add grids to pools
if ispool:
- v('Board {n} is pool with no points')
+ v(f'Board {n} is pool with no points')
continue
targs = {'color':rgb(255,0,0),'visible':self._visible}
@@ -12227,118 +13285,216 @@ class LaTeXExporter(Exporter):
self._game.addDiceButton(name = '1d6',
hotkey = self._diceKey)
+# ====================================================================
+def patchVmod(vmod_filename,patch_name,verbose):
+
+ with VMod(vmod_filename,'r') as vmod:
+ buildFile = BuildFile(vmod.getBuildFile())
+ moduleData = ModuleData(vmod.getModuleData())
+
+ from importlib.util import spec_from_file_location, module_from_spec
+ from pathlib import Path
+ from sys import modules
+
+ p = Path(patch_name)
+
+ spec = spec_from_file_location(p.stem, p.absolute())
+ module = module_from_spec(spec)
+ spec.loader.exec_module(module)
+
+ modules[p.stem] = module
+
+ with VMod(vmod_filename,'a') as vmod:
+ module.patch(buildFile,
+ moduleData,
+ vmod,
+ verbose)
+
+ vmod.replaceFiles(**{VMod.BUILD_FILE :
+ buildFile.encode(),
+ VMod.MODULE_DATA :
+ moduleData.encode()})
+
+
#
# EOF
#
# ====================================================================
# From main.py
+from argparse import ArgumentParser
+
+class DefaultSubcommandArgParse(ArgumentParser):
+ _default_subparser = None
+
+ def set_default_subparser(self, name):
+ self._default_subparser = name
+
+ def _parse_known_args(self, arg_strings, *args, **kwargs):
+ from argparse import _SubParsersAction
+ in_args = set(arg_strings)
+ d_sp = self._default_subparser
+ if d_sp is not None and not {'-h', '--help'}.intersection(in_args):
+ for x in self._subparsers._actions:
+ subparser_found = (
+ isinstance(x, _SubParsersAction) and
+ in_args.intersection(x._name_parser_map.keys())
+ )
+ if subparser_found:
+ break
+ else:
+ # insert default in first position, this implies no
+ # global options without a sub_parsers specified
+ arg_strings = [d_sp] + arg_strings
+ return super(DefaultSubcommandArgParse, self)._parse_known_args(
+ arg_strings, *args, **kwargs
+ )
+# ====================================================================
+def patchIt(args):
+ vmodname = args.output.name
+ patchname = args.patch.name
+ args.output.close()
+ args.patch .close()
+
+ patchVmod(vmodname, patchname, args.verbose)
+
+# ====================================================================
+def exportIt(args):
+
+ vmodname = args.output.name
+ patchname = args.patch.name if args.patch is not None else None
+
+ args.output.close()
+ if args.patch is not None:
+ args.patch.close()
+
+ Verbose().setVerbose(args.verbose)
+
+ try:
+ if args.version.lower() == 'draft':
+ args.visible_grids = True
+
+ rulesname = args.rules.name if args.rules is not None else None
+ tutname = args.tutorial.name if args.tutorial is not None else None
+
+ exporter = LaTeXExporter(vmodname = vmodname,
+ pdfname = args.pdffile.name,
+ infoname = args.infofile.name,
+ title = args.title,
+ version = args.version,
+ description = args.description,
+ rules = rulesname,
+ tutorial = tutname,
+ patch = patchname,
+ visible = args.visible_grids,
+ vassalVersion = args.vassal_version,
+ nonato = args.no_nato_prototypes,
+ nochit = args.no_chit_information,
+ resolution = args.resolution,
+ counterScale = args.counter_scale,
+ imageFormat = args.image_format)
+ exporter.run()
+ except Exception as e:
+ from sys import stderr
+ print(f'Failed to build {vmodname}: {e}',file=stderr)
+ from os import unlink
+ try:
+ unlink(vmodname)
+ except:
+ pass
+
+ raise e
+
+
# ====================================================================
if __name__ == '__main__':
from argparse import ArgumentParser, FileType
- ap = ArgumentParser(description='Create draft VASSAL module')
- ap.add_argument('pdffile',
+ ap = DefaultSubcommandArgParse(description='Create draft VASSAL module')
+ ap.set_default_subparser('export')
+ sp = ap.add_subparsers(dest='mode')
+
+ pp = sp.add_parser('patch',help='Patch VMod')
+ pp.add_argument('output',
+ help='Module to patch',
+ type=FileType('r'),
+ default='Draft.vmod')
+ pp.add_argument('patch',
+ help='A python script to patch generated module',
+ type=FileType('r'),
+ default='patch.py')
+ pp.add_argument('-V','--verbose',
+ help='Be verbose',
+ action='store_true')
+
+
+ ep = sp.add_parser('export',help='Export from PDF and JSON to VMod')
+ ep.add_argument('pdffile',
help='The PDF file to read images from',
type=FileType('r'),
default='export.pdf',
nargs='?')
- ap.add_argument('infofile',
+ ep.add_argument('infofile',
help='The JSON file to read image information from',
type=FileType('r'),
default='export.json',
nargs='?')
- ap.add_argument('-p','--patch',
- help='A python script to patch generated module',
- type=FileType('r'))
- ap.add_argument('-o','--output',
+ ep.add_argument('-o','--output',
help='Output file to write module to',
type=FileType('w'),
default='Draft.vmod')
- ap.add_argument('-t','--title',
+ ep.add_argument('-p','--patch',
+ help='A python script to patch generated module',
+ type=FileType('r'))
+ ep.add_argument('-V','--verbose',
+ help='Be verbose',
+ action='store_true')
+ ep.add_argument('-t','--title',
help='Module title', default='Draft',
type=str)
- ap.add_argument('-v','--version',
+ ep.add_argument('-v','--version',
help='Module version',
type=str,
default='draft')
- ap.add_argument('-r','--rules',
+ ep.add_argument('-r','--rules',
help='Rules PDF file',
type=FileType('r'))
- ap.add_argument('-T','--tutorial',
+ ep.add_argument('-T','--tutorial',
help='Tutorial (v)log file',
type=FileType('r'))
- ap.add_argument('-d','--description',
+ ep.add_argument('-d','--description',
help='Short description of module',
type=str,
default='draft of module')
- ap.add_argument('-W','--vassal-version',
+ ep.add_argument('-W','--vassal-version',
help='Vassal version number',
type=str,
default='3.6.7')
- ap.add_argument('-V','--verbose',
- help='Be verbose',
- action='store_true')
- ap.add_argument('-G','--visible-grids',
+ ep.add_argument('-G','--visible-grids',
action='store_true',
help='Make grids visible in the module')
- ap.add_argument('-N','--no-nato-prototypes',
+ ep.add_argument('-N','--no-nato-prototypes',
action='store_true',
help='Do not make prototypes for types,echelons,commands')
- ap.add_argument('-C','--no-chit-information',
+ ep.add_argument('-C','--no-chit-information',
action='store_true',
help='Do not make properties from chit information')
- ap.add_argument('-S','--counter-scale',
+ ep.add_argument('-S','--counter-scale',
type=float, default=1,
help='Scale counters by factor')
- ap.add_argument('-R','--resolution',
+ ep.add_argument('-R','--resolution',
type=int, default=150,
help='Resolution of images')
-
-
+ ep.add_argument('-I','--image-format',
+ choices = ['png','svg'], default='png',
+ help='Image format to use')
+
args = ap.parse_args()
- vmodname = args.output.name
- rulesname = args.rules.name if args.rules is not None else None
- tutname = args.tutorial.name if args.tutorial is not None else None
- args.output.close()
-
- patchname = args.patch.name if args.patch is not None else None
- if args.patch is not None:
- args.patch.close()
-
- if args.version.lower() == 'draft':
- args.visible_grids = True
-
- Verbose().setVerbose(args.verbose)
-
- try:
- exporter = LaTeXExporter(vmodname = vmodname,
- pdfname = args.pdffile.name,
- infoname = args.infofile.name,
- title = args.title,
- version = args.version,
- description = args.description,
- rules = rulesname,
- tutorial = tutname,
- patch = patchname,
- visible = args.visible_grids,
- vassalVersion = args.vassal_version,
- nonato = args.no_nato_prototypes,
- nochit = args.no_chit_information,
- resolution = args.resolution,
- counterScale = args.counter_scale)
- exporter.run()
- except Exception as e:
- from sys import stderr
- print(f'Failed to build {vmodname}: {e}',file=stderr)
- from os import unlink
- try:
- unlink(vmodname)
- except:
- pass
-
- raise e
+ if args.mode == 'patch':
+ patchIt(args)
+ else:
+ exportIt(args)
#
# EOF