diff options
Diffstat (limited to 'Master/texmf-dist/tex/latex/wargame/wgexport.py')
-rwxr-xr-x | Master/texmf-dist/tex/latex/wargame/wgexport.py | 1620 |
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 |