texlive[72903] Master/texmf-dist: wargame (19nov24)

commits+karl at tug.org commits+karl at tug.org
Tue Nov 19 21:38:27 CET 2024


Revision: 72903
          https://tug.org/svn/texlive?view=revision&revision=72903
Author:   karl
Date:     2024-11-19 21:38:26 +0100 (Tue, 19 Nov 2024)
Log Message:
-----------
wargame (19nov24)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/wargame/README.md
    trunk/Master/texmf-dist/doc/latex/wargame/compat.pdf
    trunk/Master/texmf-dist/doc/latex/wargame/symbols.pdf
    trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.pdf
    trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.sty
    trunk/Master/texmf-dist/doc/latex/wargame/wargame.pdf
    trunk/Master/texmf-dist/source/latex/wargame/Makefile
    trunk/Master/texmf-dist/source/latex/wargame/chit/battle.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/dice.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/elements.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/misc.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/oob.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/shape.dtx
    trunk/Master/texmf-dist/source/latex/wargame/chit/table.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/board.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/coord.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/labels.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/terrain.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/tile.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/compat/seasurface.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/friendly.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/hostile.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/neutral.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/unknown.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/shape.dtx
    trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/symbols.dtx
    trunk/Master/texmf-dist/source/latex/wargame/util/export.dtx
    trunk/Master/texmf-dist/source/latex/wargame/util/misc.dtx
    trunk/Master/texmf-dist/source/latex/wargame/utils/wgexport.py
    trunk/Master/texmf-dist/source/latex/wargame/wargame.dtx
    trunk/Master/texmf-dist/source/latex/wargame/wargame.ins
    trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex
    trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex
    trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex
    trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wgexport.cls
    trunk/Master/texmf-dist/tex/latex/wargame/wgexport.py

Added Paths:
-----------
    trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/fields.dtx
    trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/speckle.dtx
    trunk/Master/texmf-dist/source/latex/wargame/utils/wgmakenato.py
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf
    trunk/Master/texmf-dist/tex/latex/wargame/wgmakenato.py

Modified: trunk/Master/texmf-dist/doc/latex/wargame/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/wargame/README.md	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/doc/latex/wargame/README.md	2024-11-19 20:38:26 UTC (rev 72903)
@@ -1,6 +1,6 @@
 # A package to make Hex'n'Counter wargames in LaTeX
 
-## Version 0.6
+## Version 0.8
 
 This package can help make classic [Hex'n'Counter
 wargames](https://en.wikipedia.org/wiki/Wargame) using LaTeX. The
@@ -28,33 +28,44 @@
 The sources of the package are kept at
 [GitLab](https://gitlab.com/wargames_tex/wargame_tex)
 
-## Download from GitLab
+## Downloads available at GitLab
 
 -   [Zip file of package and support
     files](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/download?job=dist)
 -   [Browse content of
     package](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/browse?job=dist)
--   [Documentation](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/public/doc/latex/wargame/wargame.pdf?job=dist)
--   [Tutorial](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/public/doc/latex/wargame/tutorial.pdf?job=dist)
+-   [Documentation](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/doc/latex/wargame/wargame.pdf?job=dist)
+-   [Tutorial](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/doc/latex/wargame/tutorial.pdf?job=dist)
     (and associated VASSAL
-    [module](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/public/doc/latex/wargame/Game.vmod?job=dist))
+    [module](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/doc/latex/wargame/Game.vmod?job=dist))
 -   [Table of
-    symbols](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/public/doc/latex/wargame/symbols.pdf?job=dist)
--   [Compatibility](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/public/doc/latex/wargame/compat.pdf?job=dist)
+    symbols](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/doc/latex/wargame/symbols.pdf?job=dist)
+-   [Compatibility](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/file/doc/latex/wargame/compat.pdf?job=dist)
 
+## Installation
+
+The instructions below are geared towards Un\*x-like systems, for
+example GNU/Linux and MacOSX. For more information on setting the
+prerequisites and this package on various platforms, please see [this
+page](https://wargames_tex.gitlab.io/wargame_www/build.html).
+
+### From ZIP file
+
 To install, get the ([zip
 file](https://gitlab.com/wargames_tex/wargame_tex/-/jobs/artifacts/master/download?job=dist))
 and unzip into your TeX tree, for example
 
     mkdir -p ~/texmf
-    (cd ~/texmf && unzip ../artifacts.zip)
+    (cd ~/texmf && unzip ../wargame_tex.zip)
 
-on Un\*x like systems.
+### From git clone
 
 If you clone from GitLab
-(`git clone https://gitlab.com/wargames_tex/wargame_tex.git`) to get the
-sources, then you can do
 
+    git clone https://gitlab.com/wargames_tex/wargame_tex.git
+
+to get the sources, then you can do,
+
     cd wargame
     make install 
 
@@ -78,6 +89,10 @@
 
     unzip wargame.tds.zip -d ~/texmf
 
+or
+
+    sudo unzip wargame.tds.zip -d /usr/local/share/texmf 
+
 ### From sources
 
 If you get the source (`wargame.ins`, all `.dtx`,`.py`, and `Makefile`)
@@ -111,12 +126,12 @@
 Then copy the relevant files to your TeX tree (e.g., `~/texmf/`) as
 
     mkdir ~/texmf/tex/latex/wargame
-    cp tikzlibrary*.tex         ~/texmf/tex/latex/wargame/
-    cp wargame.sty              ~/texmf/tex/latex/wargame/
-    cp wgexport.cls             ~/texmf/tex/latex/wargame/
-    cp wargame.*.pdf            ~/texmf/tex/latex/wargame/
-    cp wgexport.py              ~/texmf/tex/latex/wargame/
-    cp wgsvg2tikz.py            ~/texmf/tex/latex/wargame/
+    cp tikzlibrary*.tex ~/texmf/tex/latex/wargame/
+    cp wargame.sty      ~/texmf/tex/latex/wargame/
+    cp wgexport.cls     ~/texmf/tex/latex/wargame/
+    cp wargame.*.pdf    ~/texmf/tex/latex/wargame/
+    cp wgexport.py      ~/texmf/tex/latex/wargame/
+    cp wgsvg2tikz.py    ~/texmf/tex/latex/wargame/
 
 To generate the documentation, after having done the above, do
 
@@ -135,8 +150,8 @@
 
     mkdir -p ~/texmf/doc/latex/wargame/
     cp wargame.pdf  ~/texmf/doc/latex/wargame/
-    cp symbols.py   ~/texmf/doc/latex/wargame/
-    cp compat.py    ~/texmf/doc/latex/wargame/
+    cp symbols.pdf  ~/texmf/doc/latex/wargame/
+    cp compat.pdf   ~/texmf/doc/latex/wargame/
 
 If you want to generate the tutorial document, do
 
@@ -160,7 +175,7 @@
         
 
 Note, you need `pdfinfo` and `pdftocairo` from Poppler, and Python with
-the module `PIL` for this. On Debian based systems, do
+the module `PIL` for this. On Debian-based systems, do
 
     sudo apt install poppler-utils python3-pil
 
@@ -171,12 +186,23 @@
 ([here](https://ctan.org/tex-archive/macros/latex/contrib/wargame/doc/tutorial/)
 if you browse from CTAN).
 
+Another simple example to get you started is the game
+[Tannenberg4](https://gitlab.com/wargames_tex/advancedguard_tex) also
+available from GitLab.
+
 ## Examples
 
-Below are some print'n'play board wargames made with this package. These
-are not original games but rather revamps of existing games. All credits
-goes to the original authors of these games.
+Below are some print'n'play board wargames made with this package. See
+also [LaTeX Wargames](https://wargames_tex.gitlab.io/wargame_www/) for
+more. These are not original games but rather revamps of existing games.
+All credits goes to the original authors of these games.
 
+-   American Civil War
+    -   [Smithsonian
+        Gettysberg](https://gitlab.com/wargames_tex/sgb_tex) (Avalon
+        Hill)
+-   WWI
+    -   [Tannenberg4](https://gitlab.com/wargames_tex/advancedguard_tex)
 -   WWII
     -   Eastern Front
         -   [Battle for Moscow](https://gitlab.com/wargames_tex/bfm_tex)
@@ -187,10 +213,11 @@
         -   [Smithsonian
             D-Day](https://gitlab.com/wargames_tex/sdday_tex) (Avalon
             Hill)
-        -   [Paul Koenig's Market
-            Garden](https://gitlab.com/wargames_tex/pkmg_tex)
-        -   [Paul Koenig's
-            D-Day](https://gitlab.com/wargames_tex/pkdday_tex)
+        -   [Smithsonian Battle of the
+            Bulge](https://gitlab.com/wargames_tex/sbotb_tex) (Avalon
+            Hill)
+        -   [The Drive on Metz](https://gitlab.com/wargames_tex/dom_tex)
+            (from *The Complete Wargames Handbook*)
     -   Pacific theatre
         -   [First
             Blood](https://gitlab.com/wargames_tex/firstblood_tex)
@@ -205,6 +232,8 @@
         (Avalon Hill classic)
     -   [Port Stanley](https://gitlab.com/wargames_tex/portstanley_tex)
         (from the *Wargamer* magazine)
+-   Abstract
+    -   [Battle](https://gitlab.com/wargames_tex/battle_tex)
 
 ## VASSAL support
 
@@ -287,16 +316,16 @@
 in so far as it is *artistic* (i.e., not a trivial expression that
 anyone knowledgeable within the field can do with rudimentary effort).
 
-So you *can not* copyright your game mechanics, for example, only how
-you described them. You *can not* copyright a title (but you may be able
-to claim trademark on it). You *can* copyright the wording of the rules,
-the graphics that you use, and so on.
+This means you *can not* copyright your game mechanics, for example,
+only how you described them. You *can not* copyright a title (but you
+may be able to claim trademark on it). You *can* copyright the wording
+of the rules, the graphics that you use, and so on.
 
 This also means, that you are essentially free to make your own version
 of previously published game, *as long as*
 
--   you do not copy existing text
--   you do not copy existing graphics
+-   you do not copy existing text,
+-   you do not copy existing graphics, and
 -   you respect any kind of trademark claims
 
 However, it is advisable to contact the copyright holders of the

Modified: trunk/Master/texmf-dist/doc/latex/wargame/compat.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/wargame/symbols.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.sty
===================================================================
--- trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.sty	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/doc/latex/wargame/tutorial/game.sty	2024-11-19 20:38:26 UTC (rev 72903)
@@ -816,7 +816,7 @@
 % \begin{figure}
 %   \centering
 %   \begin{tikzpicture}
-%     \doublechits{\alla}{3}{1.24}
+%     \doublechits{\alla}{3}{0.04}
 %   \end{tikzpicture}
 %   \caption{All faction A counters, front and back}
 %   \label{fig:a-all}
@@ -826,7 +826,7 @@
 %
 % \begin{verbatim}
 %   \begin{tikzpicture}
-%     \doublechits{\alla}{3}{1.24}
+%     \doublechits{\alla}{3}{0.04}
 %   \end{tikzpicture}
 % \end{verbatim}
 %
@@ -874,7 +874,7 @@
 % \begin{figure}
 %   \centering
 %   \begin{tikzpicture}
-%     \doublechits{\allb}{3}{1.24}
+%     \doublechits{\allb}{3}{.04}
 %   \end{tikzpicture}
 %   \caption{All faction B counters, front and back}
 %   \label{fig:b-all}
@@ -884,7 +884,7 @@
 %
 % \begin{verbatim}
 %   \begin{tikzpicture}
-%     \doublechits{\allb}{3}{1.24}
+%     \doublechits{\allb}{3}{.04}
 %   \end{tikzpicture}
 % \end{verbatim}
 %
@@ -920,7 +920,7 @@
 % \begin{figure}
 %   \centering
 %   \begin{tikzpicture}
-%     \doublechits{{game turn chit}}{1}{1.24}
+%     \doublechits{{game turn chit}}{1}{.04}
 %   \end{tikzpicture}
 %   \caption{Modified game turn, front and back}
 %   \label{fig:game-turn}

Modified: trunk/Master/texmf-dist/doc/latex/wargame/wargame.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/latex/wargame/Makefile
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/Makefile	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/Makefile	2024-11-19 20:38:26 UTC (rev 72903)
@@ -2,7 +2,7 @@
 #
 #
 NAME		:= wargame
-VERSION		:= 0.6
+VERSION		:= 0.8
 LATEX_FLAGS	:= -interaction=nonstopmode 	\
 		   -file-line-error		\
 		   --synctex=15			\
@@ -53,6 +53,8 @@
 		   hex/terrain/city.dtx			\
 		   hex/terrain/swamp.dtx		\
 		   hex/terrain/rough.dtx		\
+		   hex/terrain/fields.dtx		\
+		   hex/terrain/speckle.dtx		\
 		   hex/board.dtx			\
 		   hex/coord.dtx			\
 		   hex/split.dtx			\
@@ -81,7 +83,9 @@
 		   natoapp6c/util.dtx			\
 		   tests/map.dtx			\
 		   tests/chits.dtx			
-SCRIPTS		:= utils/wgsvg2tikz.py utils/wgexport.py
+SCRIPTS		:= utils/wgsvg2tikz.py 			\
+		   utils/wgexport.py			\
+		   utils/wgmakenato.py
 
 DESTDIR		:= $(HOME)/texmf/
 instdir		:= tex/latex/$(NAME)
@@ -98,7 +102,9 @@
 		   swamp	\
 		   town		\
 		   village	\
-		   woods
+		   woods	\
+		   fields	\
+		   speckle
 TILES_PDF	:= $(TILES:%=wargame.%.pdf)
 TILES_TEX	:= $(TILES:%=wargame.%.tex)
 TABLES		:= air		\
@@ -196,7 +202,7 @@
 	$(MUTE)rm -f  tikzlibrarywargame.chit.code.tex
 	$(MUTE)rm -f  wgexport.cls mksvg.pdf README.md.version
 	$(MUTE)rm -f  $(TILES_TEX) $(TILES_PDF)
-	$(MUTE)rm -rf public texmf
+	$(MUTE)rm -rf public texmf tex doc
 	$(MUTE)rm -rf $(distsdir)
 	$(MUTE)rm -f  $(distsdir).tar.gz
 	$(MUTE)rm -f  $(distsdir).zip
@@ -303,7 +309,7 @@
 	$(MUTE)(cd tmp && cp -a $(srcdir) ../$(ctandir)/$(NAME)/source)
 	$(MUTE) rm -rf tmp
 
-ctan.tex:ctan.tex.in 
+ctan.tex:utils/ctan.tex.in 
 	@echo "SED	$< -> $@"
 	$(MUTE)$(SED)   -e 's/@NAME@/$(NAME)/g' \
 			-e 's/@VERSION@/$(VERSION)/' \
@@ -335,7 +341,7 @@
 	ls
 	make clean
 
-docker-artifacts: DESTDIR=$(PWD)/public/
+docker-artifacts: DESTDIR=$(PWD)/
 docker-artifacts: install tutorial/game.pdf
 	cp tutorial/game.pdf  $(DESTDIR)$(docdir)/tutorial.pdf
 	cp tutorial/Game.vmod $(DESTDIR)$(docdir)/Game.vmod

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/battle.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/battle.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/battle.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -58,7 +58,7 @@
 \tikzset{
   pics/result marker/.style args={#1,#2}{
     code={
-      \message{^^JResults marker #1 (#2)}
+      \chit at dbg{3}{Results marker #1 (#2)}
       \node[shape=circle,
       font=\sffamily\bfseries\large,
       inner sep=0pt,

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/dice.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/dice.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/dice.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -138,6 +138,12 @@
 % \DiceExample{d6}
 %
 %    \begin{macrocode}
+\newif\ifwg at dsixdot\wg at dsixdotfalse
+\tikzset{
+  d6 dots/.is if=wg at dsixdot,
+  d6 dots/.initial=false,
+  d6 dots/.default=true
+}
 \pgfdeclareshape{d6}{
   \anchor{center}{\pgfpointorigin}    % within the node, (0,0) is the center
   \anchor{text}{
@@ -146,7 +152,48 @@
   \backgroundpath{ % draw border
     \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}}
     % \pgfusepath{draw}  %draw rectangle
-  }}
+  }
+  \behindforegroundpath{ % draw border
+    \ifwg at dsixdot%
+      \edef\numdots{\tikz at node@content}%
+      % Draw over text
+      \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}}%
+      \pgfusepath{fill}  %draw rectangle
+      \ifx\tikz at textcolor\pgfutilempty%
+        \pgfutil at colorlet{.}{pgfstrokecolor}%
+      \else
+        % \pgfsetfillcolor{\tikz at textcolor}%
+        \pgfutil at colorlet{.}{\tikz at textcolor}%
+      \fi
+      \pgfsetfillcolor{.}%
+      \ifodd\numdots
+        %\message{^^JCenter dot}
+        \pgfpathcircle{\pgfpoint{0cm}{0cm}}{.09cm}
+      \fi
+      \ifnum\numdots=2%
+        %\message{^^JSpecial 2}
+        \pgfpathcircle{\pgfpointpolar{-45}{.32cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{135}{.32cm}}{.09cm}
+      \fi%
+      \ifnum\numdots>2%
+        %\message{^^JAt least 2}
+        \pgfpathcircle{\pgfpointpolar{-45}{.35cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{135}{.35cm}}{.09cm}
+      \fi%
+      \ifnum\numdots>3%
+        %\message{^^JAt least 4}
+        \pgfpathcircle{\pgfpointpolar{  45}{.35cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{-135}{.35cm}}{.09cm}
+      \fi%
+      \ifnum\numdots=6%  
+        %\message{^^JLucky 6}
+        \pgfpathcircle{\pgfpoint{-.247cm}{0cm}}{.09cm}
+        \pgfpathcircle{\pgfpoint{ .247cm}{0cm}}{.09cm}
+      \fi
+      \pgfusepath{fill}  %draw rectangle
+    \fi%
+  }
+}
 %    \end{macrocode}
 %    
 % \paragraph{Octahedron}

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/elements.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/elements.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/elements.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -23,6 +23,15 @@
 %   These pictures can be used as the value of \texttt{chit} keys.
 %
 %    \begin{macrocode}
+\def\chit at oset#1#2{%
+  %$\stackrel{{}_{\text{\scriptsize #1}}}{\text{#2}}$%
+  %$\stackrel{\lower2ex\hbox{\text{\scriptsize #1}}}{\text{#2}}$%
+  %${}\overset{\text{\scriptsize #1}}{\text{#2}}{}$%
+  \tikz[]{%
+    \node[inner sep=0pt](chit at dd){#2};%
+    \node[above=-.1ex of chit at dd,inner sep=0pt]{{\scriptsize #1}};%
+  }%  
+}
 \tikzset{%
   chit/1 factor/.pic={
     \chit at dbg{4}{ Chit 1 factor: #1}%
@@ -35,7 +44,7 @@
     code={
       \chit at dbg{4}{ Chit 2 factors w/artillery: `#1' `#2' `#3'}% 
       \node[chit/factor,chit/2 factors]{%
-        {#1}$\overset{\text{\scriptsize #3}}{\text{--}}${#2}};}},
+        {#1}\chit at oset{#3}{--}{#2}};}},
   pics/chit/3 factors/.style args={#1,#2,#3}{%
     code={
       \chit at dbg{4}{ Chit 3 factors: `#1' `#2' `#3'}% 

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/misc.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/misc.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/misc.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -24,7 +24,7 @@
     \pgfkeysalso{%
       chit has drop=true,
       /tikz/blur shadow={shadow blur steps=5,
-        shadow opacity=25,
+        shadow opacity=30,
         shadow xshift=.05cm,
         shadow yshift=-.05cm,
         shadow blur radius=.05cm,

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/oob.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/oob.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/oob.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -79,7 +79,7 @@
   \@ifstar{\wg at star@oob%
   }{\wg at nostar@oob%
   }%
-}
+}%
 %    \end{macrocode}
 % 
 % The inner macro of \cs{oob}.  The arguments are
@@ -91,27 +91,27 @@
 % \end{enumerate}
 % 
 %    \begin{macrocode}
-\def\wg at oob#1#2#3#4{
-  \def\r{0}
+\def\wg at oob#1#2#3#4{%
+  \def\r{0}%
   \pgfmathparse{#3*(#2-1)}%
-  \edef\a{\pgfmathresult}
-  \chit at dbg{2}{OOB: `#1'}
-  \foreach[count=\ti from 0] \t/\y in #1{
-    \xdef\o{\r}
-    \def\c{0}
-    \ifx\t\y\def\y{0}\fi
-    \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}
+  \edef\a{\pgfmathresult}%
+  \chit at dbg{2}{OOB: `#1'}%
+  \foreach[count=\ti from 0] \t/\y in #1{%
+    \xdef\o{\r}%
+    \def\c{0}%
+    \ifx\t\y\def\y{0}\fi%
+    \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}%
     \ifwg at oob@inv%
       \pic[transform shape] at ( .5*#3,\r) {chit/oob turn=\ti};% was dx=0.5
-    \else
+    \else%
       \pic[transform shape] at (-.5*#3,\r) {chit/oob turn=\ti};% was dx=-0.5
     \fi%
     \ifx\t\empty\else%
-      \foreach \u/\m in \t{
+      \foreach \u/\m in \t{%
         %% \chit at dbg{2}{ `\u'=`\m'}
-        \ifx\u\empty\else
-          \ifx\m\@empty\def\m{1}\fi
-          \ifx\u\m\def\m{1}\fi
+        \ifx\u\empty\else%
+          \ifx\m\@empty\def\m{1}\fi%
+          \ifx\u\m\def\m{1}\fi%
           \foreach \n in {1,...,\m}{%
             \chit at dbg{2}{OOB Chit is `\u' `\chit at oob@spacer'}%
             \ifx\u\chit at oob@spacer%
@@ -120,33 +120,33 @@
               \xdef\c{\pgfmathresult}%
             \else%
               \ifx\u\chit at oob@vspacer%
-                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}
-                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}
-                \xdef\ll{\pgfmathresult}
-                \chit at dbg{2}{\string\ll=`\ll'}
-                \chit at oob@rowupdate(\c,\r){\ll}{#4}
+                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}%
+                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}%
+                \xdef\ll{\pgfmathresult}%
+                \chit at dbg{2}{\string\ll=`\ll'}%
+                \chit at oob@rowupdate(\c,\r){\ll}{#4}%
               \else
                 \ifnum\chitdbglvl>2%
                   \node[minimum width=#3cm,minimum height=#3cm,
-                        draw,transform shape] at (\c,\r) {};
+                        draw,transform shape] at (\c,\r) {};%
                 \fi
                 \ifx\u\chit at blank\else%
                   \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);%
                 \fi%
-                \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
-              \fi
-            \fi
-          }
-        \fi
-      }
-    \fi
+                \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}%
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
     \chit at dbg{1}{ End of chits in turn
-      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
+      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
     % IF no units where given, then we force \c to be non-zero so that
     % \chit at oob@turnupdate increments the row 
-    \ifx\t\@empty
-      \def\c{#3}
-      \chit at dbg{2}{ Turn is empty, set c=`\c'}
+    \ifx\t\@empty%
+      \def\c{#3}%
+      \chit at dbg{2}{ Turn is empty, set c=`\c'}%
     \fi
     %\ifnum\y<0% No explicit number of rows given
     %  \def\c{#3}
@@ -160,7 +160,7 @@
     % rows.
     \ifnum\y>0%
       \chit at dbg{2}{ Looping rows from 2 to \y, break when row > \y}%
-      \foreach \rr  in {2,...,\y}{
+      \foreach \rr  in {2,...,\y}{%
         %\ifnum\rr>\y% A little funny, but \y can be negative!
         %  \chit at dbg{2}{ \space Breaking loop \rr\space > \y}%
         %  \breakforeach%
@@ -167,15 +167,15 @@
         %\else%
           \chit at oob@rowupdate(\c,\r){#3}{0}% Extra spacing? 
         %\fi
-      }
-    \fi
+      }%
+    \fi%
     % This will zero \c.  However, if on entry |\c|>0, then we also
     % increment the row 
-    \chit at oob@turnupdate(\c,\r){#3}{#4}
-    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')} 
+    \chit at oob@turnupdate(\c,\r){#3}{#4}%
+    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
   }
-  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}
-  \@ifnextchar;{\@gobble}{}}
+  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}%
+  \@ifnextchar;{\@gobble}{}}%
 %    \end{macrocode}
 %
 % Horizontal flow OOB
@@ -199,49 +199,49 @@
 % \end{enumerate}
 % 
 %    \begin{macrocode}
-\def\wg at hoob#1#2#3#4{
-  \def\r{0}
-  \def\c{0}
+\def\wg at hoob#1#2#3#4{%
+  \def\r{0}%
+  \def\c{0}%
   \pgfmathparse{#3*(#2-1)}%
-  \edef\a{\pgfmathresult}
-  \chit at dbg{2}{OOB: `#1'}
-  \foreach[count=\ti from 0] \t/\y in #1{
-    \xdef\o{\r}
+  \edef\a{\pgfmathresult}%
+  \chit at dbg{2}{OOB: `#1'}%
+  \foreach[count=\ti from 0] \t/\y in #1{%
+    \xdef\o{\r}%
     % \def\c{0}
-    \ifx\t\y\def\y{0}\fi
+    \ifx\t\y\def\y{0}\fi%
     \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}
-    \ifx\t\empty\else
+    \ifx\t\empty\else%
       % Count how many are left for this turn
-      \chit at dbg{2}{At start of turn \t\space\string\c=\c}
+      \chit at dbg{2}{At start of turn \t\space\string\c=\c}%
       \def\l{\c}%
-      \let\ig\empty
-      \foreach \u/\m in \t{
-        \ifx\ig\empty
-          \ifx\u\empty\else
-            \ifx\u\m\def\m{1}\fi
+      \let\ig\empty%
+      \foreach \u/\m in \t{%
+        \ifx\ig\empty%
+          \ifx\u\empty\else%
+            \ifx\u\m\def\m{1}\fi%
             \ifx\u\chit at oob@spacer%
-              \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult}
-              \chit at dbg{2}{Got \m\space hspace (#4) -> \l}
-            \else
+              \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult}%
+              \chit at dbg{2}{Got \m\space hspace (#4) -> \l}%
+            \else%
               \ifx\u\chit at oob@vspace%
-                \xdef\ig{1}
-                \chit at dbg{2}{Got vspace -> \l (\ig)}
-              \else
-                \pgfmathparse{\l+\m*#3}
-                \xdef\l{\pgfmathresult}
-                \chit at dbg{2}{Got \m\space units -> \l}
-              \fi
-            \fi
-          \fi
-        \fi}
+                \xdef\ig{1}%
+                \chit at dbg{2}{Got vspace -> \l (\ig)}%
+              \else%
+                \pgfmathparse{\l+\m*#3}%
+                \xdef\l{\pgfmathresult}%
+                \chit at dbg{2}{Got \m\space units -> \l}%
+              \fi%
+            \fi%
+          \fi%
+        \fi}%
       % Check if there's enough room
       \chit at dbg{2}{To fill the rest of turn needs `\l' compared to
-        `\a' (#3*(#2-1))}
+        `\a' (#3*(#2-1))}%
       \pgfmathparse{ifthenelse(abs(\l)>=#3*(#2-1),0,1}%
       \xdef\l{\pgfmathresult}%
-      \chit at dbg{2}{Break or not `\l'}
-      \ifnum\l=0\chit at oob@turnupdate(\c,\r){#3}{#4}\fi
-    \fi    
+      \chit at dbg{2}{Break or not `\l'}%
+      \ifnum\l=0\chit at oob@turnupdate(\c,\r){#3}{#4}\fi%
+    \fi%
     \ifwg at oob@inv%
       \pic[transform shape] at (\c+.5*#3,\r) {chit/oob turn=\ti};% was dx=0.5
     \else
@@ -249,43 +249,43 @@
     \fi%
     %\chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
     \ifx\t\empty\else%
-      \def\lv{0}
-      \foreach \u/\m in \t{
+      \def\lv{0}%
+      \foreach \u/\m in \t{%
         %% \chit at dbg{2}{ `\u'=`\m'}
-        \ifx\u\empty\else
-          \ifx\m\@empty\def\m{1}\fi
-          \ifx\u\m\def\m{1}\fi
+        \ifx\u\empty\else%
+          \ifx\m\@empty\def\m{1}\fi%
+          \ifx\u\m\def\m{1}\fi%
           \foreach \n in {1,...,\m}{%
             \chit at dbg{2}{OOB Chit is `\u' `\chit at oob@spacer'}%
             \ifx\u\chit at oob@spacer%
-              \chit at dbg{3}{Chit `\u' is spacer `\chit at oob@spacer'}
+              \chit at dbg{3}{Chit `\u' is spacer `\chit at oob@spacer'}%
               \pgfmathparse{\c+#4}%
               \xdef\c{\pgfmathresult}%
             \else%
               \ifx\u\chit at oob@vspacer%
-                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}
-                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}
-                \xdef\ll{\pgfmathresult}
-                \chit at dbg{2}{\string\ll=`\ll'}
-                \chit at oob@rowupdate(\c,\r){\ll}{#4}
-                \xdef\lv{1}
+                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}%
+                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}%
+                \xdef\ll{\pgfmathresult}%
+                \chit at dbg{2}{\string\ll=`\ll'}%
+                \chit at oob@rowupdate(\c,\r){\ll}{#4}%
+                \xdef\lv{1}%
               \else
                 \ifnum\chitdbglvl>2%
                   \node[minimum width=#3cm,minimum height=#3cm,
-                        draw,transform shape] at (\c,\r) {};
-                \fi
+                        draw,transform shape] at (\c,\r) {};%
+                \fi%
                 \ifx\u\chit at blank\else%
                   \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);%
                 \fi%
                 \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
-              \fi
-            \fi
-          }
-        \fi
-      }
-    \fi
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
     \chit at dbg{2}{ End of chits in turn
-      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
+      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
     % --- Not relevant, I think 
     % IF no units where given, then we force \c to be non-zero so that
     % \chit at oob@turnupdate increments the row 
@@ -306,7 +306,7 @@
     % rows.
     \ifnum\y>0%
       \chit at dbg{2}{ Looping rows from 2 to \y, break when row > \y}%
-      \foreach \rr  in {2,...,\y}{
+      \foreach \rr  in {2,...,\y}{%
         %\ifnum\rr>\y% A little funny, but \y can be negative!
         %  \chit at dbg{2}{ \space Breaking loop \rr\space > \y}%
         %  \breakforeach%
@@ -314,7 +314,7 @@
           \chit at oob@rowupdate(\c,\r){#3}{0}% Extra spacing?
         %\fi
       }
-    \fi
+    \fi%
     % --- Not relevant I think   
     % This will zero \c.  However, if on entry |\c|>0, then we also
     % increment the row 
@@ -336,10 +336,10 @@
       \fi
     \fi
     % \xdef\y{0}
-    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')} 
-  }
-  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}
-  \@ifnextchar;{\@gobble}{}} 
+    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
+  }%
+  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}%
+  \@ifnextchar;{\@gobble}{}}% 
 %    \end{macrocode}
 % \iffalse
 % </chit>

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/shape.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/shape.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/shape.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -66,7 +66,8 @@
   bevel/SW/.style         = {/chit/bev=3},
   bevel/SE/.style         = {/chit/bev=4},
   bevel/.default          = north west,
-  clip/.is if=chit at clip%
+  clip/.is if=chit at clip,%
+  nato shape/.store in=\chit at n@to at shape,
 }
 %    \end{macrocode}
 % \end{TikzKey}
@@ -88,8 +89,9 @@
 % be careful. 
 % 
 %    \begin{macrocode}
+\def\chit at sym@sc at le{.4}
 \tikzset{
-  chit/symbol/.style={scale=.4,transform shape},
+  chit/symbol/.style={scale=\chit at sym@sc at le,transform shape},
   chit/parts/.style={shape=rectangle,transform shape},
   chit/factors/.style={chit/parts,anchor=south},
   chit/left/.style={chit/parts,anchor=base,rotate=90},%Anchor was south
@@ -143,7 +145,7 @@
   chit/.code={%
     \chit at dbg{2}{chit arguments are `#1'}%
     \pgfkeys{/tikz/transform shape,/tikz/shape=chit}%
-    \pgfkeys{/chit/.cd,#1}}}
+    \pgfkeys{/chit/.cd,nato shape=natoapp6c,#1}}}
 %    \end{macrocode}
 % \end{TikzKey}
 %
@@ -181,12 +183,16 @@
 % The following macro is called if we had no options.
 % 
 %    \begin{macrocode}
+\def\chit at n@to at shape{natoapp6c}
 \def\@chit at n@to@#1#2#3\@end at chit@n at to{%
   \chit at dbg{1}{Chit NATO App6(c) w/o offset:
-    ^^J  Options:  #3
-    ^^J  ID:       #1
-    ^^J  Position: #2}
-  \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at (#2) {};
+    ^^J  Options:  `#3'
+    ^^J  ID:       `#1'
+    ^^J  Position: `#2'
+    ^^J  Style:    `\chit at n@to at shape'
+  }
+  \chit at dbg{2}{NATO shape: `\chit at n@to at shape'}
+  \node[chit/symbol,\chit at n@to at shape={#3,id=#1}] (#1) at (#2) {};
   \chit at dbg{4}{Chit NATO App6(c) ended}%
 }
 %    \end{macrocode}
@@ -210,7 +216,8 @@
     ^^J  ID:       #1
     ^^J  Position: #2
     ^^J  Offset:   #4}
-  \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};}
+  \chit at dbg{10}{==== NATO shape: `\chit at n@to at shape' ====}
+  \node[chit/symbol,\chit at n@to at shape={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};}
 %    \end{macrocode}
 % \end{Macro}
 % 
@@ -220,20 +227,48 @@
 % because the symbol is translated and scaled.
 % 
 %    \begin{macrocode}
-\def\chit at tr@ns at nchor#1{%
-  \pgf at x=0.4\pgf at x%
-  \pgf at y=0.4\pgf at y\advance\pgf at y#1}
+\def\chit at tr@ns at nchor{%
+  \chit at dbg{10}{Translating anchor `\the\pgf at x',`\the\pgf at y'}
+  \wg at tmpa=\pgf at x%
+  \wg at tmpb=\pgf at y%
+  \symbol%
+  \chit at dbg{10}{Symbol origin `\the\pgf at x',`\the\pgf at y'}
+  \wg at tmpc=\pgf at x%
+  \wg at tmpd=\pgf at y%
+  \pgf at x=\chit at sym@sc at le\wg at tmpa%
+  \pgf at y=\chit at sym@sc at le\wg at tmpb%
+  \chit at dbg{10}{Scaled anchor `\the\pgf at x',`\the\pgf at y'}
+  \advance\pgf at x\wg at tmpc%
+  \advance\pgf at y\wg at tmpd%
+  \chit at dbg{10}{Offset anchor `\the\pgf at x',`\the\pgf at y'}
+}
 %    \end{macrocode}
 %    
 %    \begin{macrocode}
-\def\chit at nchor#1#2#3{%
-  \wg at sub@nchor{#1}{#2}
-  \chit at tr@ns at nchor{#3}}
+\def\chit at nchor#1#2{%
+  \chit at dbg{10}{Get chit sub anchor of `#1' `#2'}%
+  \wg at sub@nchor{#1}{#2}%
+  \chit at tr@ns at nchor%
+  \chit at dbg{10}{Got chit sub anchor of `\the\pgf at x `\the\pgf at y}%
+}
+\def\chit at sym@nchor#1{%
+  \chit at dbg{10}{Get chit symbol `#1'}%
+  \edef\tmpid{\id symbol}%
+  \chit at nchor{\tmpid}{#1}}
 \def\chit at report{}
 \tikzset{
   zone turn/.style={},
   zone mult/.style={}
 }
+\def\chit at bkg@p at th{%
+  \northeast%
+  \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+  \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \pgfclosepath%
+}  
 %    \end{macrocode}
 % \end{Macro}
 % 
@@ -242,16 +277,30 @@
 %    \begin{macrocode}
 \def\chit at bevel@path#1{
   \scope[#1]
-  \wg at tmpc=\wg at tmpa\multiply\wg at tmpc by \chit at bevel@frac
-  \wg at tmpd=\wg at tmpb\multiply\wg at tmpd by \chit at bevel@frac
-  \divide\wg at tmpc100
-  \divide\wg at tmpd100
+  \wg at tmpc=\wg at tmpa%
+  \wg at tmpd=\wg at tmpb%
+  %% Absolute values 
+  \ifdim\wg at tmpa<0pt\multiply\wg at tmpc by -1\fi%
+  \ifdim\wg at tmpb<0pt\multiply\wg at tmpd by -1\fi%
+  % Why the hell do I need this?
+  \chit at dbg{10}{`\the\wg at tmpa' `\the\wg at tmpb'}%
+  %% Smallest dimension 
+  \ifdim\wg at tmpc>\wg at tmpd \wg at tmpc=\wg at tmpd\fi%
+  \ifdim\wg at tmpc<\wg at tmpd \wg at tmpd=\wg at tmpc\fi%
+  %% Restore sign
+  \ifdim\wg at tmpa<0pt\multiply\wg at tmpc by -1\fi%
+  \ifdim\wg at tmpb<0pt\multiply\wg at tmpd by -1\fi%
+  %% Take the fraction
+  \multiply\wg at tmpc by \chit at bevel@frac%
+  \multiply\wg at tmpd by \chit at bevel@frac%
+  \divide\wg at tmpc100%
+  \divide\wg at tmpd100%
   \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move down along edge
-  \wg at tmpb=-\wg at tmpb
+  \wg at tmpb=-\wg at tmpb%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move left along edge
-  \wg at tmpa=-\wg at tmpa
+  \wg at tmpa=-\wg at tmpa%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move in and up 
   \advance\wg at tmpa\wg at tmpc%
@@ -266,10 +315,9 @@
   \advance\wg at tmpb-\wg at tmpd%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   \pgfclosepath%
-  \pgfusepath{fill}
-  \endscope
+  \pgfusepath{fill}%
+  \endscope%
 }
-  
 %    \end{macrocode}
 % 
 % The first thing is we declare some saved anchors.  These are
@@ -281,8 +329,8 @@
 %    \begin{macrocode}
 \pgfdeclareshape{chit}{
   \savedanchor\center{\pgf at x=0cm\pgf at y=0cm}
-  \savedanchor\northeast{\pgf at x=0.6cm\pgf at y=0.6cm}
-  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.2cm}
+  \savedanchor\northeast{\pgf at x=0.6cm\pgf at y=\pgf at x}
+  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.1cm}
   \savedanchor\factors{\pgf at x=0cm\pgf at y=-0.5cm}
   \saveddimen\margin{\pgf at x=0.04cm}
 %    \end{macrocode}
@@ -310,6 +358,7 @@
       \edef\chitframeopt{\chit at frame}}
     \chit at dbg{3}{Chit Frame options: \meaning\chitframeopt}%
   }
+  \savedmacro\thisname{\def\thisname{chit}}
 %    \end{macrocode}
 %
 % We define the regular anchors of the shape.  That is, the centre,
@@ -333,20 +382,20 @@
 % \texttt{full=}\meta{args}.
 % 
 %    \begin{macrocode}
-  \anchor{symbol north east}{\chit at nchor{M\id symbol}{north east}{0.2cm}}
-  \anchor{symbol north west}{\chit at nchor{M\id symbol}{north west}{0.2cm}}
-  \anchor{symbol south east}{\chit at nchor{M\id symbol}{south east}{0.2cm}}
-  \anchor{symbol south west}{\chit at nchor{M\id symbol}{south west}{0.2cm}}
-  \anchor{symbol north}     {\chit at nchor{M\id symbol}{north}{0.2cm}}
-  \anchor{symbol west}      {\chit at nchor{M\id symbol}{west}{0.2cm}}
-  \anchor{symbol south}     {\chit at nchor{M\id symbol}{south}{0.2cm}}
-  \anchor{symbol east}      {\chit at nchor{M\id symbol}{east}{0.2cm}}
-  \anchor{symbol upper}     {\chit at nchor{M\id symbol}{upper}{0.2cm}}
-  \anchor{symbol lower}     {\chit at nchor{M\id symbol}{lower}{0.2cm}}
-  \anchor{symbol left}      {\chit at nchor{M\id symbol}{left}{0.2cm}}
-  \anchor{symbol right}     {\chit at nchor{M\id symbol}{right}{0.2cm}}
-  \anchor{symbol echelon}   {\chit at nchor{M\id symbol}{north}{0.2cm}}
-  \anchor{symbol below}     {\chit at nchor{M\id symbol}{south}{0.1cm}}
+  \anchor{symbol north east}{\chit at sym@nchor{north east}}
+  \anchor{symbol north west}{\chit at sym@nchor{north west}}
+  \anchor{symbol south east}{\chit at sym@nchor{south east}}
+  \anchor{symbol south west}{\chit at sym@nchor{south west}}
+  \anchor{symbol north}     {\chit at sym@nchor{north}}
+  \anchor{symbol west}      {\chit at sym@nchor{west}}
+  \anchor{symbol south}     {\chit at sym@nchor{south}}
+  \anchor{symbol east}      {\chit at sym@nchor{east}}
+  \anchor{symbol upper}     {\chit at sym@nchor{upper}}
+  \anchor{symbol lower}     {\chit at sym@nchor{lower}}
+  \anchor{symbol left}      {\chit at sym@nchor{left}}
+  \anchor{symbol right}     {\chit at sym@nchor{right}}
+  \anchor{symbol echelon}   {\chit at sym@nchor{echelon}}
+  \anchor{symbol below}     {\chit at sym@nchor{below}}
 %    \end{macrocode}
 %
 % Some anchors to sub-elements.  Some of them only exists if we have
@@ -355,8 +404,8 @@
 %    \begin{macrocode}
   \anchor{symbol}    {\symbol}
   \anchor{factors}   {\factors}
-  \anchor{left} {\chit at nchor{M\id symbol}{west}{.2cm}\advance\pgf at x-\margin}
-  \anchor{right}{\chit at nchor{M\id symbol}{east}{.2cm}\advance\pgf at x+\margin}
+  \anchor{left} {\chit at sym@nchor{west}\advance\pgf at x-\margin}
+  \anchor{right}{\chit at sym@nchor{east}\advance\pgf at x+\margin}
   \anchor{upper right} {%
     \northeast \advance\pgf at x-\margin \advance\pgf at y-\margin%
   }
@@ -381,13 +430,14 @@
     %% This is the outline of the chit only.  The rest of the chit is
     %% made on the foreground "path".
     \chit at dbg{1}{Chit drawing background path}
-    \northeast%
-    \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
-    \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \pgfclosepath
+    \chit at bkg@p at th%
+    % \northeast%
+    % \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+    % \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \pgfclosepath
   }
 %    \end{macrocode}
 %
@@ -422,13 +472,14 @@
     %
     \ifchit at clip%
       \chit at dbg{1}{Chit clip path}
-      \northeast%
-      \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
-      \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \pgfclosepath%
+      \chit at bkg@p at th%
+      % \northeast%
+      % \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+      % \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \pgfclosepath%
       \pgfusepath{clip}%
     \fi%
 %    \end{macrocode}
@@ -455,7 +506,7 @@
       \symbol%
       \edef\args{{\symid}{\the\pgf at x,\the\pgf at y}\chit at symbol}%
       \chit at dbg{6}{Arguments to chit NATO symbol: \meaning\args}%
-      \chit at dbg{1}{Chit draw nato image}
+      \chit at dbg{1}{Chit draw nato image `\symid'}
       \expandafter\chit at n@to\args\@end at chit@n at to%
       \chit at dbg{6}{After making NATO symbol in chit}%
 %    \end{macrocode}
@@ -475,21 +526,17 @@
 %    \begin{macrocode}
       % Put in left of symbol
       \@ifundefined{chit at left}{}{%
-        \chit at dbg{1}{Chit draw left: `\meaning\chit at left'}
+        \chit at dbg{2}{Chit draw left: `\meaning\chit at left'}
+        \wg at nchor{\thisname}{left}
         \begin{scope}[]
-          \pgfpointanchor{\symid}{west}%
-          \wg at tmpa=\pgf at x\advance\wg at tmpa-\margin%
-          \wg at tmpb=\pgf at y%
-          \wg at pic@all{\chit at left}{}{\the\wg at tmpa,\the\wg at tmpb}{chit/left}%
+          \wg at pic@all{\chit at left}{}{\pgf at x,\pgf at y}{chit/left}%
         \end{scope}}%
       % Put in right of symbol
       \@ifundefined{chit at right}{}{%
-        \chit at dbg{1}{Chit draw left: `\meaning\chit at right'}
+        \chit at dbg{2}{Chit draw right: `\meaning\chit at right'}
+        \wg at nchor{\thisname}{right}
         \begin{scope}[]
-          \pgfpointanchor{\symid}{east}%
-          \wg at tmpa=\pgf at x\advance\wg at tmpa+\margin%
-          \wg at tmpb=\pgf at y%
-          \wg at pic@all{\chit at right}{}{\the\wg at tmpa,\the\wg at tmpb}{chit/right}%
+          \wg at pic@all{\chit at right}{}{\pgf at x,\pgf at y}{chit/right}%
         \end{scope}}%
 %    \end{macrocode}
 %
@@ -515,31 +562,31 @@
 %    \begin{macrocode}
       % Put in upper left corner
       \@ifundefined{chit at upper@left}{}{%
-        \chit at dbg{1}{Chit draw upper left: `\meaning\chit at upper@left'}
+        \chit at dbg{1}{Chit draw upper left: `\meaning\chit at upper@left'}%
+        \wg at nchor{\thisname}{upper left}
         \begin{scope}[]
-          \wg at pic@all{\chit at upper@left}{}{-\the\wg at tmpa,\the\wg at tmpb}{%
-            chit/upper left}%
+          \wg at pic@all{\chit at upper@left}{}{\pgf at x,\pgf at y}{chit/upper left}%
         \end{scope}}
       % Put in upper right corner
       \@ifundefined{chit at upper@right}{}{%
         \chit at dbg{1}{Chit draw upper right: `\meaning\chit at upper@right'}
+        \wg at nchor{\thisname}{upper right}%
         \begin{scope}[]
-          \wg at pic@all{\chit at upper@right}{}{\the\wg at tmpa,\the\wg at tmpb}{%
-            chit/upper right}%
+          \wg at pic@all{\chit at upper@right}{}{\pgf at x,\pgf at y}{chit/upper right}%
         \end{scope}}
       % Put in lower left corner
       \@ifundefined{chit at lower@left}{}{%
         \chit at dbg{1}{Chit draw lower left: `\meaning\chit at lower@left'}
+        \wg at nchor{\thisname}{lower left}%
         \begin{scope}[]
-          \wg at pic@all{\chit at lower@left}{}{-\the\wg at tmpa,-\the\wg at tmpb}{%
-            chit/lower left}%
+          \wg at pic@all{\chit at lower@left}{}{\pgf at x,\pgf at y}{chit/lower left}%
         \end{scope}}
       % Put in lower right corner
       \@ifundefined{chit at lower@right}{}{%
         \chit at dbg{1}{Chit draw lower right: `\meaning\chit at lower@right'}
+        \wg at nchor{\thisname}{lower right}%
         \begin{scope}[]
-          \wg at pic@all{\chit at lower@right}{}{\the\wg at tmpa,-\the\wg at tmpb}{%
-            chit/lower right}%
+          \wg at pic@all{\chit at lower@right}{}{\pgf at x,\pgf at y}{chit/lower right}%
         \end{scope}}
 %    \end{macrocode}
 %
@@ -683,7 +730,93 @@
 }
 %    \end{macrocode}
 % \end{Macro}
+% 
 % \iffalse
+% --------------------------------------------------------------------
+% \fi
+% \subsubsection{The Kriegspiel option for chits}
+%
+% This will select a Kriegspiel-like chit layout. That is, if we pass
+% \texttt{kriegspiel} as a keyword to the \texttt{chit} style, then we
+% will make a Kriegspiel-like chit (oblong, other symbols).
+%
+%    \begin{macrocode}
+\tikzset{
+  /chit/kriegspiel/.code={
+    \pgfkeys{%
+      /tikz/shape=kriegspiel,%
+      /chit/nato shape=kriegspiel symbol}
+  }
+}
+%    \end{macrocode}
+%
+% Shape of a Kriegspiel like chit.  This mainly inherits from the
+% \texttt{chit} shape but overrides a number of anchors.
+%
+%    \begin{macrocode}
+\pgfdeclareshape{kriegspiel}{
+  \inheritsavedanchors[from=chit]
+  \savedanchor\northeast{\pgf at x=0.8cm\pgf at y=0.4cm}
+  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.15cm}
+  \savedanchor\factors{\pgf at x=.2cm\pgf at y=-.4cm}
+  \savedmacro\thisname{\def\thisname{kriegspiel}}
+  \saveddimen\margin{\pgf at x=0.03cm}
+  \inheritanchor[from=chit]{center}
+  \inheritanchor[from=chit]{north east}
+  \inheritanchor[from=chit]{north west}
+  \inheritanchor[from=chit]{south west}
+  \inheritanchor[from=chit]{south east}
+  \inheritanchor[from=chit]{north}
+  \inheritanchor[from=chit]{south}
+  \inheritanchor[from=chit]{east}
+  \inheritanchor[from=chit]{west}
+  \inheritanchor[from=chit]{symbol north east}
+  \inheritanchor[from=chit]{symbol north west}
+  \inheritanchor[from=chit]{symbol south east}
+  \inheritanchor[from=chit]{symbol south west}
+  \inheritanchor[from=chit]{symbol north}
+  \inheritanchor[from=chit]{symbol west}
+  \inheritanchor[from=chit]{symbol south}
+  \inheritanchor[from=chit]{symbol east}
+  \inheritanchor[from=chit]{symbol upper}
+  \inheritanchor[from=chit]{symbol lower}
+  \inheritanchor[from=chit]{symbol left}
+  \inheritanchor[from=chit]{symbol right}
+  \inheritanchor[from=chit]{symbol echelon}
+  \inheritanchor[from=chit]{symbol below}
+  \inheritanchor[from=chit]{symbol}
+  \inheritanchor[from=chit]{factors}
+  \inheritanchor[from=chit]{left}
+  \inheritanchor[from=chit]{right}
+  \inheritanchor[from=chit]{upper right}
+  \inheritanchor[from=chit]{upper left}
+  \inheritanchor[from=chit]{lower right} 
+  \inheritanchor[from=chit]{lower left}
+  % \anchor{upper right} {%
+  %   \northeast%
+  %   \advance\pgf at x-\margin%
+  %   \advance\pgf at y-\margin%
+  %   \pgf at y=-\pgf at y%
+  %   \advance\pgf at y5pt%
+  % }
+  % \anchor{upper left}{
+  %   \northeast%
+  %   \advance\pgf at x-\margin%
+  %   \advance\pgf at y-\margin%
+  %   \pgf at x=-\pgf at x%
+  %   \pgf at y=-\pgf at y%
+  %   \advance\pgf at y5pt%
+  % }
+  %%
+  \inheritbackgroundpath[from=chit]
+  \inheritbehindforegroundpath[from=chit]
+}
+%    \end{macrocode}
+% 
+%    \begin{macrocode}
+%    \end{macrocode}
+% 
+% \iffalse
 % </chit>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/chit/table.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/chit/table.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/chit/table.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -6,7 +6,12 @@
 %
 %    \begin{macrocode}
 \tikzset{
-  chit/cell background/.style={fill=black},
+  chit/cell background/.style={draw=black},
+  %% Define to not dp anyting to disable wings 
+  chit/cell wings/.style={
+    dashed,
+    dash pattern=on 0pt off 3pt on 3pt off 0pt,
+    draw=black},
   %chit/cell background flipped/.style={fill=black},
   blank chit/.style={/chit/frame={draw=none,fill=none}},
   chit/grid lines/.style={dashed},
@@ -37,6 +42,13 @@
 \newif\ifchits at reset\chits at resettrue
 %    \end{macrocode}
 % \end{Macro}
+%
+% \begin{enumerate}
+% \item column
+% \item row
+% \item width
+% \item number of columns
+% \end{enumerate}
 % 
 % \begin{Macro}{\chits,\@chits,\chit at sng@cellupdate}
 %    \begin{macrocode}
@@ -49,6 +61,26 @@
 }
 %    \end{macrocode}
 %
+%    \begin{macrocode}
+\def\chit at cell@frame#1#2{%
+  \coordinate(cell at frame@north east) at ($(#1.north east)+( #2/2, #2/2)$);
+  \coordinate(cell at frame@north west) at ($(#1.north west)+(-#2/2, #2/2)$);
+  \coordinate(cell at frame@south east) at ($(#1.south east)+( #2/2,-#2/2)$);
+  \coordinate(cell at frame@south west) at ($(#1.south west)+(-#2/2,-#2/2)$);
+  \draw[chit/cell background,fill=none,scale line widths,line width=#2cm]
+  (cell at frame@south west) rectangle (cell at frame@north east);
+  \draw[scale line widths,line width=#2cm,chit/cell wings=10pt,fill=none,scale line widths,line width=#2cm]
+  (cell at frame@north east)--++(     0,10*#2)
+  (cell at frame@north east)--++( 10*#2,0)
+  (cell at frame@north west)--++(     0,10*#2)
+  (cell at frame@north west)--++(-10*#2,0)
+  (cell at frame@south east)--++(     0,-10*#2)
+  (cell at frame@south east)--++( 10*#2,0)
+  (cell at frame@south west)--++(     0,-10*#2)
+  (cell at frame@south west)--++(-10*#2,0)
+  ;
+}
+%    \end{macrocode}
 % The stared version (\cs{chits*}) of this macro continues the
 % previously set chit table.
 % 
@@ -57,11 +89,22 @@
   \@ifstar{\chits at resetfalse\@chits}{\chits at resettrue\@chits}}
 %    \end{macrocode}
 % 
+% \begin{enumerate}
+% \item Chits.  List (or list of list) of chit styles.
+% \item Number of columns
+% \item Size of cells 
+% \end{enumerate}
+% 
 %    \begin{macrocode}
+\newcount\wg at col
+\newcount\wg at row
 \def\@chits#1#2#3{
   \ifchits at reset
-    \def\r{0}%
-    \def\c{0}%
+    %\def\r{0}%
+    %\def\c{0}%
+    \coordinate(wg at next) at (0,0);
+    \wg at col=1
+    \wg at row=1
   \fi
   \chit at dbg{1}{Chits to make: #1}%
   \foreach[count=\ti from 0] \t/\x in #1{%
@@ -77,9 +120,41 @@
             \ifx\u\chit at blank%
               \chit at dbg{3}{Ignoring blank chit:\u}%
             \else%
-              \chit at cellbg(\c,\r){#3}%
-              \chit[\u=\ti](\c,\r)%
-              \chit at sng@cellupdate(\c,\r){#2}{#3}%
+              \ifx\u\chit at oob@vspacer%
+                \chit at dbg{3}{Ignoring vertical spacer:\u}%
+              \else
+                \ifx\u\chit at oob@spacer%
+                  \chit at dbg{3}{Ignoring horizontal spacer:\u}%
+                \else
+                  % \chit at cellbg(\c,\r){#3}%
+                  %\chit[\u=\ti](\c,\r)%
+                  \chit[\u=\ti](wg at next)(wg at chit)%
+                  \chit at cell@frame{wg at chit}{#3}
+                  \global\advance\wg at col1
+                  \ifnum\wg at col>#2%
+                    \path
+                    let
+                    \p1=(wg at chit.north),
+                    \p2=(wg at chit.south),
+                    \p3=(wg at chit),
+                    \n1={\y3-\y1+\y2-#3cm} in 
+                    coordinate(wg at next) at (0,\n1);
+                    \global\wg at col=1
+                    \global\advance\wg at row1
+                    \chit at dbg{10}{Row row, next column `\the\wg at col'}
+                  \else
+                    \path
+                    let
+                    \p1=(wg at chit.east),
+                    \p2=(wg at chit.west),
+                    \p3=(wg at chit),
+                    \n1={\x3+\x1-\x2+#3cm} in 
+                    coordinate(wg at next) at (\n1,\y3);
+                    \chit at dbg{10}{Next column `\the\wg at col'}
+                  \fi
+                  %\chit at sng@cellupdate(\c,\r){#2}{#3}%
+                \fi%
+              \fi%
             \fi%
           }%
         \fi%
@@ -86,6 +161,7 @@
       }%
     \fi%
   }%
+  \chit at dbg{10}{Table is `\the\wg at col'x`\the\wg at row'}
   \@ifnextchar;{\@gobble}{}}
 %    \end{macrocode}
 % \end{Macro}
@@ -158,15 +234,29 @@
   \@ifstar{\chits at resetfalse\@doublechits}{\chits at resettrue\@doublechits}}
 %    \end{macrocode}
 %
+% \begin{enumerate}
+% \item List (or list of list) of counters
+% \item Max number columns (for front)
+% \item Extra spacing
+% \end{enumerate}
+% 
 %    \begin{macrocode}
+\newdimen\chit at w
+\newdimen\chit at h
 \def\@doublechits#1#2#3{%
   \chit at dbg{1}{Setting double-sided chits: #1}
-  \ifchits at reset
-    \pgfmathparse{-(#2-.5)*#3}
-    \xdef\c{\pgfmathresult}
-    \def\r{0}
-  \fi
-  
+  \ifchits at reset%
+    %\pgfmathparse{-(#2-.5)*#3}
+    %\xdef\c{\pgfmathresult}
+    %\def\r{0}
+    \coordinate(wg at next) at (0,0);%
+    \coordinate(wg at fnxt) at (0,0);%
+    \wg at col=1%
+    \wg at row=1%
+    \chit at w=0pt%
+    \chit at h=0pt%
+  \fi%
+  %
   \foreach[count=\ti from 0] \t/\x in #1{
     \ifx\t\empty\else%
       \foreach \u/\m in \t{
@@ -178,21 +268,105 @@
             \ifx\u\chit at blank
               \chit at dbg{3}{Ignoring blank chit:\u}
             \else
-              \chit at cellbg(\c,\r){#3}
-              \chit[\u=\ti](\c,\r)
-              \chit at dbl@flip(\c,\r){#3}
-              \chit at celldblbg(\mc,\r){#3}
-              \chit[\u\space flipped=\ti,zone turn=\t,zone mult=\n](\mc,\r)
-              \chit at dbl@cellupdate(\c,\r){#2}{#3}
-            \fi
-          }
-        \fi
-      }
-    \fi
-  }
-  \draw[dashed](0,-3*#3/4)--(0,\r-#3/4);%
-  \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};%
-  \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};%
+              \ifx\u\chit at oob@vspacer%
+                \chit at dbg{3}{Ignoring vertical spacer:\u}%
+              \else
+                \ifx\u\chit at oob@spacer%
+                  \chit at dbg{3}{Ignoring horizontal spacer:\u}%
+                \else
+                  \chit at dbg{10}{Drawing `\u' at wg at next}
+                  \chit[\u=\ti](wg at next)(wg at chit)%
+                  \chit at cell@frame{wg at chit}{#3}
+                  \ifdim\chit at w=0pt
+                    \pgfextractx\chit at w{\pgfpointanchor{wg at chit}{east}}%
+                    \pgfextractx\wg at tmpa{\pgfpointanchor{wg at chit}{west}}%
+                    \advance\chit at w-\wg at tmpa%
+                    \global\advance\chit at w#3cm%
+                    \pgfextracty\chit at h{\pgfpointanchor{wg at chit}{north}}%
+                    \pgfextracty\wg at tmpa{\pgfpointanchor{wg at chit}{south}}%
+                    \advance\chit at h-\wg at tmpa%
+                    \global\advance\chit at h#3cm%
+                  \fi
+                  \ifnum\wg at col=1
+                    \pgfextractx\wg at tmpa{\pgfpointanchor{wg at chit}{center}}%
+                    \advance\wg at tmpa+\chit at w%
+                    \coordinate(wg at fnxt) at ($(wg at chit)+(\the\chit at w,0pt)$);
+                    %\coordinate(wg at fnxt
+                    %\chit at dbg{10}{Calculating wg at fnxt since `\the\wg at col'==1}
+                    %\path
+                    %let
+                    %\p1=(wg at chit.east),
+                    %\p2=(wg at chit.west),
+                    %\p3=(wg at chit),
+                    %\n1={\x3+\x1-\x2+#3cm} in 
+                    %coordinate(wg at fnxt) at (\n1,\y3);
+                  \fi                  
+                  %\chit at dbg{10}{Drawing `\u\space flipped' at wg at fnxt}
+                  \chit[\u\space flipped=\ti,
+                  zone turn=\t,
+                  zone mult=\n](wg at fnxt)(wg at flip)%
+                  \chit at cell@frame{wg at flip}{#3}
+                  \chit at dbg{10}{Next column: `\the\wg at col'}
+                  \ifnum\wg at col=1
+                    \ifnum\wg at row=1
+                      \coordinate(wg at table@top) at (wg at chit.north east);
+                    \fi
+                    \coordinate(wg at table@bot) at (wg at chit.south east);
+                  \fi
+                  \global\advance\wg at col1
+                  \ifnum\wg at col>#2%
+                    % \path
+                    %    let
+                    %    \p1=(wg at chit.north),
+                    %    \p2=(wg at chit.south),
+                    %    \p3=(wg at chit),
+                    %    \n1={\y3-\y1+\y2-#3cm} in %1.21991
+                    %    coordinate(wg at next) at (0,\n1);
+                    \coordinate(wg at next) at (0,-\wg at row\chit at h);
+                    \global\wg at col=1
+                    \global\advance\wg at row1
+                    \chit at dbg{10}{Row row, next column `\the\wg at col'}
+                  \else
+                    % \path
+                    % let
+                    % \p1=(wg at chit.east),
+                    % \p2=(wg at chit.west),
+                    % \p3=(wg at chit),
+                    % \p4=(wg at flip.east),
+                    % \p5=(wg at flip.west),
+                    % \p6=(wg at flip),
+                    % \n1={\x3-\x1+\x2-#3cm},
+                    % \n2={\x6+\x4-\x5+#3cm} in 
+                    % coordinate(wg at next) at (\n1,\y3)
+                    % coordinate(wg at fnxt) at (\n2,\y6);
+                    \coordinate(wg at next) at ($(wg at chit)+(-\the\chit at w,0)$);
+                    \coordinate(wg at fnxt) at ($(wg at flip)+(+\the\chit at w,0)$);
+                    \chit at dbg{10}{Next column `\the\wg at col'}
+                  \fi
+                \fi%
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
+  }%
+  \draw[dashed,scale line widths](wg at table@top)--++(0,.4);
+  \draw[dashed,scale line widths](wg at table@bot)--++(0,-.4);
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@top)+(0,.2)$) -- ++(.5,0)
+  node[transform shape,anchor=west]{Back};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@top)+(0,.2)$) -- ++(-.5,0)
+  node[transform shape,anchor=east]{Front};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@bot)+(0,-.2)$) -- ++(.5,0)
+  node[transform shape,anchor=west]{Back};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@bot)+(0,-.2)$) -- ++(-.5,0)
+  node[transform shape,anchor=east]{Front};
+  % \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};%
+  % \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};%
   % \foreach \cc in {0,...,#2}{
   %   \draw[dashed] (\cc*#3,-3*#3/4)--(\cc*#3,\r-#3/4);
   %   \draw[dashed] (-\cc*#3,-3*#3/4)--(-\cc*#3,\r-#3/4);}
@@ -214,15 +388,18 @@
 %
 %    \begin{macrocode}
 \def\doublechitgrid#1#2#3{%
+  \message{^^JWARNING - the grid table might be messed up!}
+  %\iffalse
   \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}%
   \pgfmathparse{#2*#3-#3/2}\edef\rmax{\pgfmathresult}%
   \foreach \cc in {0,...,#1}{
-    \draw[chit/grid lines] (\cc*#3,-3*#3/4)--(\cc*#3,\rmax+#3/4);
-    \draw[chit/grid lines] (-\cc*#3,-3*#3/4)--(-\cc*#3,\rmax+#3/4);}
+    \draw[chit/grid lines] (\cc*#3+\rmin,3*#3/4)--(\cc*#3+\rmin,-\rmax-#3/4);
+    \draw[chit/grid lines] (-\cc*#3+\rmin,3*#3/4)--(-\cc*#3+\rmin,-\rmax-#3/4);}
   %\chit at dbg{0}{Drawing horizontal lines from `-\rmin, `\rmin', ..., `\rmax'}
-  \foreach \rr in {-\rmin,\rmin,...,\rmax}{
-    %\chit at dbg{0}{Horizontal line at `\rr'}
-    \draw[chit/grid lines] (-#1*#3-#3/4,\rr)--(#1*#3+#3/4,\rr);}  
+  \foreach \rr in {\rmin,-\rmin,...,-\rmax}{
+    \chit at dbg{0}{Horizontal line at `\rr'}
+    \draw[chit/grid lines] (-#1*#3-#3/4+\rmin,\rr)--(#1*#3+#3/4+\rmin,\rr);}
+  %\fi
 }
 %    \end{macrocode}
 % \end{Macro}

Modified: trunk/Master/texmf-dist/source/latex/wargame/hex/board.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/board.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/board.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -10,7 +10,7 @@
 %   Define the bounding box around the board
 %   
 %   \begin{Syntax}
-%     \cs{boardframe}\oarg{margin}\parg{lower=left}\parg{upper-right}\marg{margin}
+%     \cs{boardframe}\oarg{margin}\parg{lower=left}\parg{upper-right}
 %   \end{Syntax}
 %   
 %   where \meta{lower-left} and \meta{upper-right} specifies the lower
@@ -39,157 +39,157 @@
   \@ifnextchar[{\bo at rdfr@me@}{\bo at rdfr@me@[]}%]
 }
 \def\bo at rdfr@me at u(#1)#2#3#4#5{
-  \hex at coords@conv{#1}
+  \hex at coords@conv{#1}%
   % \hex at dbg{0}{#1 -> `\hex at x',`\hex at y'}
-  \pgfmathparse{min(#2,\hex at x)}\xdef#2{\pgfmathresult}
-  \pgfmathparse{min(#3,\hex at y)}\xdef#3{\pgfmathresult}
-  \pgfmathparse{max(#4,\hex at x)}\xdef#4{\pgfmathresult}
-  \pgfmathparse{max(#5,\hex at y)}\xdef#5{\pgfmathresult}
-  \hex at dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'}
+  \pgfmathparse{min(#2,\hex at x)}\xdef#2{\pgfmathresult}%
+  \pgfmathparse{min(#3,\hex at y)}\xdef#3{\pgfmathresult}%
+  \pgfmathparse{max(#4,\hex at x)}\xdef#4{\pgfmathresult}%
+  \pgfmathparse{max(#5,\hex at y)}\xdef#5{\pgfmathresult}%
+  \hex at dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'}%
 }
-\def\bo at rdfr@me@[#1]#2#3#4#5{
+\def\bo at rdfr@me@[#1]#2#3#4#5{%
   % Define rtmp and a ctmp to by directions 
-  \pgfmathparse{int(\hex at coords@row at fac)}\edef\rtmp{\pgfmathresult}
-  \pgfmathparse{int(\hex at coords@col at fac)}\edef\ctmp{\pgfmathresult}
+  \pgfmathparse{int(\hex at coords@row at fac)}\edef\rtmp{\pgfmathresult}%
+  \pgfmathparse{int(\hex at coords@col at fac)}\edef\ctmp{\pgfmathresult}%
   % Define vertices for path 
-  \def\ctfv{SW}
-  \def\ctsv{SE}
-  \def\cbfv{NE}
-  \def\cbsv{NW}
-  \def\rrfv{E}
-  \def\rrsv{NE}
-  \def\rlfv{W}
-  \def\rlsv{SW}
+  \def\ctfv{SW}%
+  \def\ctsv{SE}%
+  \def\cbfv{NE}%
+  \def\cbsv{NW}%
+  \def\rrfv{E}%
+  \def\rrsv{NE}%
+  \def\rlfv{W}%
+  \def\rlsv{SW}%
   % Swap around some definitions based on the row direction
-  \ifnum\rtmp<0
-    \let\max at short\hex at bot@short at col
-    \let\min at short\hex at top@short at col
-    \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp
-    \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp
-    \def\rrsv{SE}
-    \def\rlsv{NW}
-  \else
-    \let\max at short\hex at top@short at col
-    \let\min at short\hex at bot@short at col
-  \fi
+  \ifnum\rtmp<0%
+    \let\max at short\hex at bot@short at col%
+    \let\min at short\hex at top@short at col%
+    \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp%
+    \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp%
+    \def\rrsv{SE}%
+    \def\rlsv{NW}%
+  \else%
+    \let\max at short\hex at top@short at col%
+    \let\min at short\hex at bot@short at col%
+  \fi%
   % Swap around some definitions based on the column direction
-  \ifnum\ctmp<0
-    \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp
-    \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp
-    \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp
-    \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp
-  \fi
+  \ifnum\ctmp<0%
+    \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp%
+    \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp%
+    \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp%
+    \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp%
+  \fi%
   % Define tmp = 0 if no shorts, 1 if top short, 2 if both  
   \pgfmathparse{ifthenelse(\hex at got@top at short,
-    ifthenelse(\hex at got@bot at short,2,1),0)}\edef\tmp{\pgfmathresult}
+    ifthenelse(\hex at got@bot at short,2,1),0)}\edef\tmp{\pgfmathresult}%
   % If top-short, set factors 
-  \ifnum\tmp=1
-    \def\mnf{-1}
-    \def\mxf{-1}
-    \def\mnn{}
-    \def\mxn{}
+  \ifnum\tmp=1%
+    \def\mnf{-1}%
+    \def\mxf{-1}%
+    \def\mnn{}%
+    \def\mxn{}%
   % If both short, set factors 
-  \else\ifnum\tmp=2
-    \def\mnf{\rtmp}
-    \def\mxf{(-\rtmp)}
+  \else\ifnum\tmp=2%
+    \def\mnf{\rtmp}%
+    \def\mxf{(-\rtmp)}%
     % If inverse rows, set factors 
-    \ifnum\rtmp<0
-      \def\mnn{}
-      \def\mxn{not}
-    \else
-      \def\mnn{not}
-      \def\mxn{}
-    \fi
+    \ifnum\rtmp<0%
+      \def\mnn{}%
+      \def\mxn{not}%
+    \else%
+      \def\mnn{not}%
+      \def\mxn{}%
+    \fi%
   % If none is short
-  \else
-    \def\mnf{1}
-    \def\mxf{1}
-    \def\mnn{not}
-    \def\mxn{not}
-  \fi\fi
+  \else%
+    \def\mnf{1}%
+    \def\mxf{1}%
+    \def\mnn{not}%
+    \def\mxn{not}%
+  \fi\fi%
   % Define row at mn to give least row of column  
   \def\row at mn##1{%   
     \pgfmathparse{int(#3+\mnf*
       \hex at coords@row at fac*\min at short(##1)*
-      \mnn(\min at short(\hex at coords@col at off)))} 
-    \edef\lr{\pgfmathresult}}
+      \mnn(\min at short(\hex at coords@col at off)))}%
+    \edef\lr{\pgfmathresult}}%
   % Define row at mx to give largest row of column
   \def\row at mx##1{%
     \pgfmathparse{int(#5+\mxf* 
       \hex at coords@row at fac*\max at short(##1)*
-      \mxn(\max at short(\hex at coords@col at off)))}
-    \edef\ur{\pgfmathresult}}
+      \mxn(\max at short(\hex at coords@col at off)))}%
+    \edef\ur{\pgfmathresult}}%
   % 
   % 
   % Below defines a path around the perimeter of the hexes.
   %
-  \def\@llx{10000}
-  \def\@lly{10000}
-  \def\@urx{-10000}
-  \def\@ury{-10000}
+  \def\@llx{10000}%
+  \def\@lly{10000}%
+  \def\@urx{-10000}%
+  \def\@ury{-10000}%
   % Start with an empty path 
   \def\p{}
   % Loop across least row (can be top if \rtmp<0)
   \foreach \c in {#2,...,#4}{%
-    \row at mn{\c}
-    \row at mx{\c}
+    \row at mn{\c}%
+    \row at mx{\c}%
     % \message{^^JColumn: `\c' -> `\lr',`\ur' (#3,#5)}
   }
   \foreach \c in {#2,...,#4}{%
-    \row at mn{\c}
+    \row at mn{\c}%
     \xdef\p{\p
       (hex cs:c=\c,r=\lr,v=\ctfv)--
-      (hex cs:c=\c,r=\lr,v=\ctsv)--}
-    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury
-  }
+      (hex cs:c=\c,r=\lr,v=\ctsv)--}%
+    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury%
+  }%
   % Go up (down if \rtmp<0) right side
-  \row at mn{#4}
-  \row at mx{#4}
+  \row at mn{#4}%
+  \row at mx{#4}%
   \foreach \r in {\lr,...,\ur}{%
     \xdef\p{\p
       (hex cs:c=#4,r=\r,v=\rrfv)--
-      (hex cs:c=#4,r=\r,v=\rrsv)--}
-    \bo at rdfr@me at u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury
-  }
+      (hex cs:c=#4,r=\r,v=\rrsv)--}%
+    \bo at rdfr@me at u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury%
+  }%
   % Go across largest row (can be bottom if \rtmp<0)
   \foreach \c in {#4,...,#2}{%
-    \row at mx{\c}
+    \row at mx{\c}%
     % \message{^^JColumn: `\c', max:`\ur'}
     \xdef\p{\p
       (hex cs:c=\c,r=\ur,v=\cbfv)--
-      (hex cs:c=\c,r=\ur,v=\cbsv)--}
-    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury
+      (hex cs:c=\c,r=\ur,v=\cbsv)--}%
+    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury%
   }
   % Go up (down if \rtmp<0) left side.
-  \row at mn{#2}
-  \row at mx{#2}
+  \row at mn{#2}%
+  \row at mx{#2}%
   \foreach \r in {\ur,...,\lr}{%
     \xdef\p{\p
        (hex cs:c=#2,r=\r,v=\rlfv)--
-       (hex cs:c=#2,r=\r,v=\rlsv)--}
-    \bo at rdfr@me at u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury
-  }
+       (hex cs:c=#2,r=\r,v=\rlsv)--}%
+    \bo at rdfr@me at u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury%
+  }%
   % End path with cycle  
-  \edef\p{\p cycle}
+  \edef\p{\p cycle}%
   % Define global path 
-  \global\let\hex at board@path\p
-  \hex at dbg{3}{Hex board path: `\meaning\hex at board@path'}
+  \global\let\hex at board@path\p%
+  \hex at dbg{3}{Hex board path: `\meaning\hex at board@path'}%
   % If an optional argument was given, then use that to actually make
   % hexes. 
-  \ifx|#1|\else
+  \ifx|#1|\else%
     \foreach[count=\nc] \c in {#2,...,#4}{%
-      \row at mn{\c}
-      \row at mx{\c}
+      \row at mn{\c}%
+      \row at mx{\c}%
       \foreach \r in {\lr,...,\ur}{%
-        \hex[#1={\c,\r}](c=\c,r=\r)
-      }
-    }
-  \fi  
-}
+        \hex[#1={\c,\r}](c=\c,r=\r)%
+      }%
+    }%
+  \fi% 
+}%
 %    \end{macrocode}
 %
 %
@@ -198,7 +198,7 @@
 % 
 %    \begin{macrocode}
 \tikzset{%
-  /hex/board/no op/.style args={#1,#2}{}}
+  /hex/board/no op/.style args={#1,#2}{}}%
 %    \end{macrocode}
 %
 % This macro will make the actual hexes using the specified, optional,
@@ -207,15 +207,15 @@
 %    \begin{macrocode}
 \def\boardhexes{%
   \@ifnextchar[{\bo at rdhexes}{\bo at rdhexes[board/no op]}%]
-}
+}%
 \def\bo at rdhexes[#1](#2)(#3){%
-  \hex at coords@conv{#2}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
-  \hex at coords@conv{#3}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
-  \bo at rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}}
+  \hex at coords@conv{#2}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
+  \hex at coords@conv{#3}%
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
+  \bo at rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}}%
 %    \end{macrocode}
 %
 % Creates a board frame using \cs{bo at rdfr@me}. 
@@ -236,40 +236,41 @@
       }}}}
       
 \def\bo at rdframe[#1](#2)(#3){%
-  \hex at coords@conv{#2}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
+  \hex at coords@conv{#2}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
   %
   \hex at coords@conv{#3}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
   %
-  \def\margin{#1}
+  \def\margin{#1}%
   %
   % This will store the bounding box in tmp node `board frame'
   \bo at rdfr@me{\llc}{\llr}{\urc}{\urr}%
-  \begin{scope}[board frame bb]
-    \expandafter\path\hex at board@path;
-  \end{scope}
-  \hex at dbg{1}{Board frame LL: -> `\llx',`\lly'}
-  \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult}
-  \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult}
+  \begin{scope}[board frame bb]%
+    \expandafter\path\hex at board@path;%
+  \end{scope}%
+  \hex at dbg{1}{Board frame LL: -> `\llx',`\lly'}%
+  \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult}%
+  \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult}%
   %
-  \hex at dbg{1}{Board frame UR: -> `\urx',`\ury'}
-  \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult}
-  \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult}
+  \hex at dbg{1}{Board frame UR: -> `\urx',`\ury'}%
+  \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult}%
+  \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult}%
   %
-  \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult}
-  \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult}
+  \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult}%
+  \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult}%
   %% Print to the log
-  \hex at dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)}
+  \hex at dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)}%
   %% Possibly draw 
-  \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury);
+  \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury);%
   %% Store macros 
   \xdef\boardXmin{\llx}%
   \xdef\boardYmin{\lly}%
   \xdef\boardXmax{\urx}%
   \xdef\boardYmax{\ury}%
+  \@ifnextchar;{\@gobble}{}%
 }  
 %    \end{macrocode}
 % 
@@ -283,17 +284,16 @@
 %   \cs{boardclip}\marg{nx}\marg{ny}\marg{preaction}  
 % \end{Syntax}
 %    \begin{macrocode}
-\def\boardpath(#1)(#2){
+\def\boardpath(#1)(#2){%
   \hex at coords@reset%
-  \tikzset{/hex/coords/.cd, #1}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
+  \tikzset{/hex/coords/.cd, #1}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
   %%
   \hex at coords@reset%
-  \tikzset{/hex/coords/.cd, #2}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
-  
+  \tikzset{/hex/coords/.cd, #2}%
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
   % This will store the bounding box in tmp node `board frame'
   \bo at rdfr@me{\llc}{\llr}{\urc}{\urr}%
   %% Use the path to extract the bounding box 
@@ -300,15 +300,16 @@
   %\begin{scope}[local bounding box=board frame]
   %  \expandafter\path\hex at board@path;
   %\end{scope}
-  \global\let\hexboardpath\hex at board@path
+  \global\let\hexboardpath\hex at board@path%
+  \@ifnextchar;{\@gobble}{}%
 }
 
 %    \end{macrocode}
 %    \begin{macrocode}
 \def\boardclip(#1)(#2)#3{%
-  \boardpath(#1)(#2)
+  \boardpath(#1)(#2)%
   \draw \ifx|#3|\else[preaction={#3}]\fi%
-  [clip] \hexboardpath;
+  [clip] \hexboardpath;%
 }
   
 %    \end{macrocode}

Modified: trunk/Master/texmf-dist/source/latex/wargame/hex/coord.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/coord.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/coord.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -302,7 +302,7 @@
   \xdef\hex at eff@row{\pgfmathresult}%
   \hex at dbg{2}{Effective row: \hex at coords@row at fac * (\hex at row +
     \hex at coords@row at off) -> \hex at eff@row}%
-  \pgfmathparse{(2*\hex at eff@row-mod(round((\hex at col+\hex at coords@col at off)),2))*\hex at yy}%
+  %\pgfmathparse{(2*\hex at eff@row-mod(round((\hex at col+\hex at coords@col at off)),2))*\hex at yy}%
   \pgfmathparse{(2*\hex at eff@row-mod(abs(round(\hex at col+\hex at coords@col at off)),2))*\hex at yy}%  
   \xdef\hex at y{\pgfmathresult}%
 %    \end{macrocode}

Modified: trunk/Master/texmf-dist/source/latex/wargame/hex/labels.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/labels.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/labels.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -48,7 +48,9 @@
 % \spec{/hex/label} family to make it easy to parse out only these
 % keys.  This uses some macros defined below.  Note, this uses the
 % macros \cs{hex at col} and \cs{hex at row} defined by the hex coordinate
-% system.
+% system.  \emph{Important:} If you do not want to set a label but
+% they are otherwise automatically added, then set \texttt{label=none}
+% rather than \texttt{label=} (empty), which will not work.
 %
 %    \begin{macrocode}
 \tikzset{%

Added: trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/fields.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/fields.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/fields.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -0,0 +1,60 @@
+% \iffalse
+% --------------------------------------------------------------------
+%<*hex>
+% \fi
+% Next, we define some styles for styling the terrain pictures.  Users
+% can change these styles (e.g., by appending to them) to change say
+% the colour of the terrain graphics.
+%
+%
+% \begin{TikzKey}{hex/terrain/fields}
+%   The style for field hexes.  The pattern is rectangles of varying
+%   opacity to overlay on a fill colour, so as to create variations in
+%   how the terrain comes out. 
+% 
+%    \begin{macrocode}
+\tikzset{
+  hex/terrain/fields/.style={%
+    draw=none%
+  }%
+}
+%    \end{macrocode}
+% \end{TikzKey}
+%
+% \begin{TikzKey}{hex/terrain/fields}
+%   Now for the actual pattern. 
+%
+%   \begin{center}
+%     \includegraphics{wargame.fields}
+%   \end{center}
+%
+%    \begin{macrocode}
+\ifhex at terrain@pic
+\tikzset{
+  hex/terrain/fields/.pic={
+    \path[draw=none,fill=none](1,0)
+    --(60:1)
+    --(120:1)
+    --(180:1)
+    --(240:1)
+    --(300:1)
+    --cycle;
+    \fill[black,opacity=.05] ( .1,   .3)rectangle++(.5,.4);
+    \fill[white,opacity=.15] (-.65, -.5)rectangle++(.3,.5);
+    \fill[white,opacity=.25] (-.60,  .2)rectangle++(.6,.5);
+    \fill[black,opacity=.02] (-.30, -.1)rectangle++(.4,.2);
+    \fill[black,opacity=.02] (-.50, -.8)rectangle++(.4,.25);
+    \fill[black,opacity=.05] (-.30, -.5)rectangle++(.4,.35); 
+    \fill[white,opacity=.15] ( .15, -.6)rectangle++(.4,.8); 
+    \fill[black,opacity=.02] (-.05, -.8)rectangle++(.5,.15);
+    \fill[black,opacity=.02] (-.8,  -.3)rectangle++(.1,.6);
+    \fill[black,opacity=.02] ( .6,  -.35)rectangle++(.2,.6);
+  }
+}
+\fi
+%    \end{macrocode}
+% \end{TikzKey}
+% \iffalse
+%</hex>
+% --------------------------------------------------------------------
+% \fi


Property changes on: trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/fields.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/speckle.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/speckle.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/speckle.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -0,0 +1,76 @@
+% \iffalse
+% --------------------------------------------------------------------
+%<*hex>
+% \fi
+% \begin{TikzKey}{hex/terrain/speckle}
+%   The style for speckled hexes.  The pattern is circles of varying
+%   opacity to overlay on a fill colour, so as to create variations in
+%   how the terrain comes out. 
+% 
+%    \begin{macrocode}
+\tikzset{
+  hex/terrain/speckle/.style={%
+  }%
+}
+%    \end{macrocode}
+% \end{TikzKey}
+%
+% \begin{TikzKey}{hex/terrain/fields}
+%   Now for the actual pattern. 
+%
+%   \begin{center}
+%     \includegraphics{wargame.fields}
+%   \end{center}
+%
+%    \begin{macrocode}
+\ifhex at terrain@pic
+\pgfmathdeclarerandomlist{black white speckles}{{black}{white}}
+\pgfmathdeclarerandomlist{gray white speckles}{{gray}{white}}
+\tikzset{
+  hex/terrain/speckle/minimum radius/.initial=3,
+  hex/terrain/speckle/maximum radius/.initial=10,
+  hex/terrain/speckle/minimum opacity/.initial=3,
+  hex/terrain/speckle/maximum opacity/.initial=7,
+  %%
+  pics/hex/terrain/speckle/.style args={#1,#2}{
+    code={
+      \tikzset{
+        hex/terrain/speckle/minimum radius/.get=\minR,
+        hex/terrain/speckle/maximum radius/.get=\maxR,
+        hex/terrain/speckle/minimum opacity/.get=\minO,
+        hex/terrain/speckle/maximum opacity/.get=\maxO,
+      }
+      \foreach \i in {1,...,#1}{
+        
+        \pgfmathrandominteger{\R}{\minR}{\maxR}
+        \pgfmathrandominteger{\n}{1}{3}% Sub trapezoid
+        \pgfmathrandominteger{\x}{0}{100}% X coordinate
+        \pgfmathrandominteger{\y}{0}{100}% Y coordinate 
+        \pgfmathrandominteger{\o}{\minO}{\maxO}% Opacity 
+        \pgfmathrandomitem{\c}{#2}
+        \pgfmathparse{\x/100}\edef\x{\pgfmathresult}
+        \pgfmathparse{\y/100}\edef\y{\pgfmathresult}
+        \pgfmathparse{\R/100}\edef\R{\pgfmathresult}
+        \pgfmathparse{\o/100}\edef\o{\pgfmathresult}
+        \pgfmathparse{1-\R}\edef\O{\pgfmathresult}
+        \ifnum\n=1
+          \fill[\c,opacity=\o]($\y*(120:\O)+(\x-\R,0)$) circle(\R);
+        \else
+          \ifnum\n=2
+            \fill[\c,opacity=\o]($\y*(240:\O)+(\x-\R,0)$) circle(\R);
+          \else
+            \fill[\c,opacity=\o]($\x*(120:\O)+\y*(240:\O)$) circle(\R);
+          \fi
+        \fi
+      }
+    }
+  },
+  pics/hex/terrain/speckle/.default={200,gray white speckles}
+}
+\fi
+%    \end{macrocode}
+% \end{TikzKey}
+% \iffalse
+%</hex>
+% --------------------------------------------------------------------
+% \fi


Property changes on: trunk/Master/texmf-dist/source/latex/wargame/hex/terrain/speckle.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/source/latex/wargame/hex/terrain.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/terrain.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/terrain.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -46,15 +46,19 @@
   code/.store in=\hex at t@code,%
   clip/.store in=\hex at t@clip,%
   random rotation/.is if=@hex at t@rot,
+  rotate/.store in=\hex at t@s at angle,
   pic/.default=,
   image/.default=,
   code/.default=,
   clip/.default=,
+  rotate/.default=,
 }
 \iffalse
 \tikzset{
   /hex/terrain/.cd,%
   beach/.style={pic=hex/terrain/beach},
+  fields/.style={pic=hex/terrain/fields},
+  speckle/.style={pic=hex/terrain/speckle},
   light woods/.style={pic=hex/terrain/light woods},
   woods/.style={pic=hex/terrain/woods},
   swamp/.style={pic=hex/terrain/swamp},
@@ -68,6 +72,8 @@
 \tikzset{
   /hex/terrain/.cd,%
   beach/.style={image=wargame.beach},
+  fields/.style={image=wargame.fields},
+  speckle/.style={image=wargame.speckle},
   light woods/.style={image=wargame.light_woods},
   woods/.style={image=wargame.woods},
   swamp/.style={image=wargame.swamp},
@@ -146,6 +152,12 @@
     \if at hex@t at rot%
       \pgfmathrandominteger{\hex at t@angle}{0}{5}
       \pgfmathparse{int(60*\hex at t@angle)}\edef\hex at t@angle{\pgfmathresult}%
+    \else%
+      \@ifundefined{hex at t@s at angle}{}{
+        \ifx\hex at t@s at angle\@empty%
+        \else%
+          \edef\hex at t@angle{\hex at t@s at angle}%
+        \fi}%
     \fi%
     \hex at dbg{5}{Will rotate terrain by `\hex at t@angle'}%
 %    \end{macrocode}
@@ -259,7 +271,7 @@
     pics/hex/#2sextant/S/.style=hex/#2sextant/south,
     pics/hex/#2sextant/SE/.style=hex/#2sextant/south east,
     pics/hex/#2sextant/C/.style=hex/#2sextant/center,
-  }
+  }%
 }
 %    \end{macrocode}
 % \end{Macro} 

Modified: trunk/Master/texmf-dist/source/latex/wargame/hex/tile.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/hex/tile.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/hex/tile.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -18,6 +18,8 @@
 %<town>town
 %<village>village
 %<woods>woods
+%<fields>fields
+%<speckle>speckle
 };%
 \end{tikzpicture}
 \end{document}

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/compat/seasurface.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/compat/seasurface.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/compat/seasurface.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -33,7 +33,7 @@
   fishing vessel/{main={fishing vessel}},
   frigate/{main={text=FF}},
   harbour tug/{main={text=YT}},
-  hazardous material transport ship/{main={ship,[yshift=.-.07]small text=HZ}},
+  hazardous material transport ship/{main={ship,[yshift=-.07]small text=HZ}},
   heavy lift/{main={ship,text=H}},
   hospital ship/{main={text=AH}},
   hovercraft/{main={ship,text=J}},

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/friendly.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/friendly.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/friendly.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -200,8 +200,8 @@
 \pgfdeclareshape{natoapp6c friendly equipment}{%
   \inheritsavedanchors[from=natoapp6c base]
   \savedanchor\northeast{%
-    \pgf at x=\n at to@pp at r%
-    \pgf at y=\n at to@pp at r}
+    \pgf at x=1.2\n at to@pp at r%
+    \pgf at y=1.2\n at to@pp at r}
   \anchor{north east}{\northeast}
   \anchor{north west}{\northeast\pgf at x=-\pgf at x}
   \anchor{south east}{\northeast\pgf at y=-\pgf at y}
@@ -446,6 +446,65 @@
 % \end{NatoAppFrame}
 % 
 % \iffalse
+% ....................................................................
+% \fi
+% \begin{NatoAppFrame}{natoapp6c friendly dismounted}
+%
+%   Macro for friendly dismounted command
+%
+%    \begin{macrocode}
+\def\n at to@friendly at l@nd{%
+  \northeast \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+  \pgfpathmoveto{\pgfqpoint{ \wg at tmpa}{ \wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{ \wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{-\wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{ \wg at tmpa}{-\wg at tmpb}}%
+  \pgfclosepath}
+%    \end{macrocode}
+%
+%   The friendly dismounted command. The most used command frame.
+%   
+%    \begin{macrocode}
+\def\n at to@pp at friendl@dismounted{%
+  \northeast\wg at tmpa=\pgf at x\wg at tmpb\pgf at y%
+  \pgfpathmoveto{\pgfpoint{-\wg at tmpa}{-.5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{-\wg at tmpa}{ .5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{0cm}      {   \wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{ \wg at tmpa}{ .5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{ \wg at tmpa}{-.5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{0cm}      {  -\wg at tmpb}}%
+  \pgfclosepath%
+}%
+\pgfdeclareshape{natoapp6c friendly dismounted}{%
+  \inheritsavedanchors[from=natoapp6c base]
+  \savedanchor\northeast{%
+    \pgf at x=1.1\n at to@pp at r%
+    \pgf at y=1.1\n at to@pp at r}
+  \anchor{north east}{\northeast}
+  \anchor{north west}{\northeast\pgf at x=-\pgf at x}
+  \anchor{south east}{\northeast\pgf at y=-\pgf at y}
+  \anchor{south west}{\northeast\pgf at x=-\pgf at x\pgf at y=-\pgf at y}
+  \anchor{north}{\northeast\pgf at x=0cm}
+  \anchor{south}{\northeast\pgf at x=0cm\pgf at y=-\pgf at y}
+  \anchor{east}{\northeast\pgf at y=0cm}
+  \anchor{west}{\northeast\pgf at x=-\pgf at x\pgf at y=0cm}
+  \inheritanchor[from=natoapp6c base]{upper}
+  \inheritanchor[from=natoapp6c base]{lower}
+  \inheritanchor[from=natoapp6c base]{left}
+  \inheritanchor[from=natoapp6c base]{right}
+  \inheritanchor[from=natoapp6c base]{center}
+  \backgroundpath{%
+    \n at to@pp at friendl@dismounted%
+  }
+  \behindforegroundpath{%
+    \n at to@pp at friendl@dismounted%
+    \pgfusepath{stroke}%
+  }
+}
+%    \end{macrocode}
+% \end{NatoAppFrame}
+% 
+% \iffalse
 %</natoapp6c>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/hostile.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/hostile.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/hostile.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -448,6 +448,39 @@
 % \end{NatoAppFrame}
 % 
 % \iffalse
+% ....................................................................
+% \fi
+% \begin{NatoAppFrame}{natoapp6c hostile dismounted}
+%
+%   The hostile dismounted command.  Same as land command.
+%   
+%    \begin{macrocode}
+\pgfdeclareshape{natoapp6c hostile dismounted}{%
+  \inheritsavedanchors[from=natoapp6c hostile land]
+  \inheritanchor[from=natoapp6c hostile land]{inner north east}
+  \inheritanchor[from=natoapp6c hostile land]{inner north west}
+  \inheritanchor[from=natoapp6c hostile land]{inner south west}
+  \inheritanchor[from=natoapp6c hostile land]{inner south east}
+  \inheritanchor[from=natoapp6c hostile land]{north east}
+  \inheritanchor[from=natoapp6c hostile land]{north west}
+  \inheritanchor[from=natoapp6c hostile land]{south east}
+  \inheritanchor[from=natoapp6c hostile land]{south west}
+  \inheritanchor[from=natoapp6c hostile land]{north}
+  \inheritanchor[from=natoapp6c hostile land]{west}
+  \inheritanchor[from=natoapp6c hostile land]{east}
+  \inheritanchor[from=natoapp6c hostile land]{south}
+  \inheritanchor[from=natoapp6c hostile land]{upper}
+  \inheritanchor[from=natoapp6c hostile land]{lower}
+  \inheritanchor[from=natoapp6c hostile land]{left}
+  \inheritanchor[from=natoapp6c hostile land]{right}
+  \inheritanchor[from=natoapp6c hostile land]{center}
+  \inheritbackgroundpath[from=natoapp6c hostile land]
+  \inheritbehindforegroundpath[from=natoapp6c hostile land]
+}
+%    \end{macrocode}
+% \end{NatoAppFrame}
+% 
+% \iffalse
 %</natoapp6c>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/neutral.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/neutral.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/neutral.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -400,6 +400,40 @@
 % \end{NatoAppFrame}
 % 
 % \iffalse
+% ....................................................................
+% \fi
+% \begin{NatoAppFrame}{natoapp6c neutral dismounted}
+%
+%   The neutral dismounted command.  Same as land command
+%   
+%    \begin{macrocode}
+\pgfdeclareshape{natoapp6c neutral dismounted}{%
+  \inheritsavedanchors[from=natoapp6c neutral land]
+  \inheritanchor[from=natoapp6c neutral land]{center}
+  \inheritanchor[from=natoapp6c neutral land]{inner north east}
+  \inheritanchor[from=natoapp6c neutral land]{inner north west}
+  \inheritanchor[from=natoapp6c neutral land]{inner south west}
+  \inheritanchor[from=natoapp6c neutral land]{inner south east}
+  \inheritanchor[from=natoapp6c neutral land]{north east}
+  \inheritanchor[from=natoapp6c neutral land]{north west}
+  \inheritanchor[from=natoapp6c neutral land]{south east}
+  \inheritanchor[from=natoapp6c neutral land]{south west}
+  \inheritanchor[from=natoapp6c neutral land]{north}
+  \inheritanchor[from=natoapp6c neutral land]{west}
+  \inheritanchor[from=natoapp6c neutral land]{east}
+  \inheritanchor[from=natoapp6c neutral land]{south}
+  \inheritanchor[from=natoapp6c neutral land]{upper}
+  \inheritanchor[from=natoapp6c neutral land]{lower}
+  \inheritanchor[from=natoapp6c neutral land]{left}
+  \inheritanchor[from=natoapp6c neutral land]{right}
+  \inheritanchor[from=natoapp6c neutral land]{center}
+  \inheritbackgroundpath[from=natoapp6c neutral land]
+  \inheritbehindbackgroundpath[from=natoapp6c neutral land]
+}
+%    \end{macrocode}
+% \end{NatoAppFrame}
+% 
+% \iffalse
 %</natoapp6c>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/unknown.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/unknown.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/frames/unknown.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -477,7 +477,41 @@
 }
 %    \end{macrocode}
 % \end{NatoAppFrame}
+%
 % \iffalse
+% ....................................................................
+% \fi
+% \begin{NatoAppFrame}{natoapp6c unknown dismounted}
+%
+%   The unknown dismounted command.  Same as land command.
+%   
+%    \begin{macrocode}
+\pgfdeclareshape{natoapp6c unknown dismounted}{%
+  \inheritsavedanchors[from=natoapp6c unknown land]
+  \inheritanchor[from=natoapp6c unknown land]{inner north east}
+  \inheritanchor[from=natoapp6c unknown land]{inner north west}
+  \inheritanchor[from=natoapp6c unknown land]{inner south west}
+  \inheritanchor[from=natoapp6c unknown land]{inner south east}
+  \inheritanchor[from=natoapp6c unknown land]{north east}
+  \inheritanchor[from=natoapp6c unknown land]{north west}
+  \inheritanchor[from=natoapp6c unknown land]{south east}
+  \inheritanchor[from=natoapp6c unknown land]{south west}
+  \inheritanchor[from=natoapp6c unknown land]{north}
+  \inheritanchor[from=natoapp6c unknown land]{west}
+  \inheritanchor[from=natoapp6c unknown land]{east}
+  \inheritanchor[from=natoapp6c unknown land]{south}
+  \inheritanchor[from=natoapp6c unknown land]{upper}
+  \inheritanchor[from=natoapp6c unknown land]{lower}
+  \inheritanchor[from=natoapp6c unknown land]{left}
+  \inheritanchor[from=natoapp6c unknown land]{right}
+  \inheritanchor[from=natoapp6c unknown land]{center}
+  \inheritbackgroundpath[from=natoapp6c unknown land]
+  \inheritbehindforegroundpath[from=natoapp6c unknown land]
+}
+%    \end{macrocode}
+% \end{NatoAppFrame}
+% 
+% \iffalse
 %</natoapp6c>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/shape.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/shape.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/shape.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -35,6 +35,7 @@
   fac/.store in=\natoapp at fac,
   cmd/.store in=\natoapp at cmd,
   ech/.store in=\natoapp at ech,
+  path/.store in=\n at to@ppp at th
 }  
 %    \end{macrocode}
 % \end{NatoAppKey}
@@ -81,6 +82,7 @@
   command/space/.style={cmd=space},
   command/sub surface/.style={cmd=sub surface},
   command/sea mine/.style={cmd=sub surface},
+  command/dismounted/.style={cmd=dismounted},
   command/none/.style={cmd=none},
 }
 %    \end{macrocode}
@@ -199,7 +201,7 @@
 \tikzset{%
   natoapp6c/.code={%
     \pgfkeys{/tikz/transform shape,/tikz/shape=natoapp6c}
-    \pgfkeys{/natoapp6c/.cd,#1}}}
+    \pgfkeys{/natoapp6c/.cd,path=natoapp6c/s/,#1}}}
 %    \end{macrocode}
 % \end{TikzKey}
 %
@@ -238,6 +240,7 @@
 %   (identifier, frame type, and frame options).
 %   
 %    \begin{macrocode}
+% \def\wg at n@to at p@th{}
 \pgfdeclareshape{natoapp6c}{%
   \savedanchor\center{\pgf at x=0cm\pgf at y=0cm}
   \savedmacro\id{%
@@ -263,6 +266,7 @@
       \edef\frameopt{\natoapp at frame}}
     \n at to@pp at dbg{3}{NATO App6(c) Frame options: \meaning\frameopt}%
   }
+  \savedmacro\thisname{\def\thisname{natoapp6c}}
 %    \end{macrocode}
 %
 % Then we define a number of regular anchors 
@@ -293,16 +297,23 @@
 %
 %    \begin{macrocode}
   \anchor{echelon}   {%
-    \n at to@pp at dbg{3}{NATO App6(c) get echelon anchor}%
-    \wg at sub@nchor{M\id}{north}%
-    \wg at tmpa=\n at to@pp at e@y cm%
+    \wg at sub@nchor{M}{north}%
+    \wg at tmpa\pgflinewidth% by 1.5%
+    %\wg at getscale%
+    %\pgfmathparse{ifthenelse(\wg at scale>0.00001,\wg at tmpa/\wg at scale,\wg at tmpa)}
+    %\edef\wg at a{\pgfmathresult}\pgfmathsetlength{\wg at tmpa}{\wg at a pt}
+    %\n at to@pp at dbg{0}{Scale is `\wg at scale' -> `\the\wg at tmpa'}%
+    %\divide\wg at tmpa by \wg at scale%
+    \advance\wg at tmpa by\n at to@pp at e@y cm%
     \advance\pgf at y\wg at tmpa%
-  }%
+  }
   \anchor{below}     {%
     \n at to@pp at dbg{3}{NATO App6(c) get below anchor}%
-    \wg at sub@nchor{M\id}{south}
+    \wg at sub@nchor{M}{south}%
     \wg at tmpa=\n at to@pp at e@yy cm%
-    \advance\pgf at y-\wg at tmpa}
+    \advance\wg at tmpa-\pgflinewidth% by 1.5%
+    \advance\pgf at y-\wg at tmpa%
+  }
 %    \end{macrocode}
 %
 % All right, so time to make the actual frame.  Note that we do this
@@ -311,6 +322,8 @@
 %
 %    \begin{macrocode}
   \behindbackgroundpath{%
+    \wg at ignore@sub at nchortrue%
+    \n at to@pp at dbg{3}{NATO App6(c) Before print}
     \n at to@pp at dbg{3}{NATO App6(c) background path: \meaning\id
       ^^J  ID:      \meaning\natoapp at id
       ^^J  Faction: \meaning\natoapp at fac
@@ -348,7 +361,7 @@
       %% Clip to shape in scope
       %% \message{^^JClipping to NATO App6(c) shape}
       \n at to@pp at iscliptrue%
-      \n at to@pp at dbg{2}{NATO App6(c) frame node M (clip)}
+      \n at to@pp at dbg{1}{NATO App6(c) frame node M (clip)}
       \pgfnode{natoapp6c \frameshape}{center}{}{M}{\pgfusepath{clip}}
       \n at to@pp at isclipfalse%
 %    \end{macrocode}
@@ -387,9 +400,10 @@
 %    \begin{macrocode}
       % Render mains 
       \@ifundefined{natoapp at main}{}{
-        \n at to@pp at dbg{2}{NATO App6(c) mains: \meaning\natoapp at main}
+        \n at to@pp at dbg{1}{NATO App6(c) mains: \meaning\natoapp at main}
         \begin{scope}[natoapp6c/main]
-          \wg at pic@all{\natoapp at main}{natoapp6c/s/}{M.center}{natoapp6c/main}%
+          \n at to@pp at dbg{1}{NATO symbol path `\n at to@ppp at th'}
+          \wg at pic@all{\natoapp at main}{\n at to@ppp at th}{M.center}{natoapp6c/main}%
         \end{scope}}%
       % Modififiers flagged
 %    \end{macrocode}
@@ -437,6 +451,7 @@
       \endpgfinterruptboundingbox
     \end{scope}%
     \fi%
+    \wg at ignore@sub at nchorfalse%
   }
 %    \end{macrocode}
 %
@@ -452,12 +467,14 @@
 % 
 %    \begin{macrocode}
   \behindforegroundpath{%
-    \n at to@pp at dbg{2}{NATO App6(c) foreground path:
+    \wg at ignore@sub at nchortrue%
+    \n at to@pp at dbg{2}{NATO App6(c) foreground path: `\meaning\id'
       ^^J  Echelon: \meaning\natoapp at ech
       ^^J  Symbol:  \meaning\frameshape
       ^^J  Below:   \meaning\natoapp at below
       ^^J  Frame:   \meaning\frameopt}
     %
+    %
 %    \end{macrocode}
 %
 % We check if we have a frame.  If not, stop.
@@ -472,9 +489,8 @@
 % expand the \spec{frame} options in a scope. 
 %
 %    \begin{macrocode}
-                                
-    \edef\tmp at opt{[\frameopt]}
-    \expandafter\scope\tmp at opt
+      \edef\tmp at opt{[\frameopt]}
+      \expandafter\scope\tmp at opt
 %    \end{macrocode}
 %
 % First thing in this scope is to draw the actual frame.  Again, this
@@ -482,8 +498,10 @@
 % node as \spec{M}\meta{id} so we way refer to it later on. 
 %
 %    \begin{macrocode}
-    \n at to@pp at dbg{2}{NATO App6(c) inner node `M\id' ===}
-    \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}}
+      \edef\mid{M\id{}}
+      \n at to@pp at dbg{1}{NATO App6(c) inner node `M\id' ===}
+      \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}}
+      \wg at ignore@sub at nchorfalse%
 %    \end{macrocode}
 %
 % If the user gave an echelon, then put that in. Note that echelons
@@ -491,34 +509,40 @@
 % 
 %    \begin{macrocode}
     % Put in the echelon
-    \@ifundefined{natoapp at ech}{}{%
-      \ifx\natoapp at ech\pgfutil at empty\else%
-      \def\args{echelon=\natoapp at ech}
-      \expandafter\wg at pic\args\@endwg at pic%
-      {natoapp6c/s/}{$(M.north)+(0,1.2*\n at to@pp at e@y)$}{natoapp6c/echelon}
-      \fi%
-    }
+      \n at to@pp at dbg{2}{Echelon}
+      \@ifundefined{natoapp at ech}{}{%
+        \ifx\natoapp at ech\pgfutil at empty\else%
+          \def\args{echelon=\natoapp at ech}
+          \n at to@pp at dbg{3}{Make the echelon}%
+          \wg at nchor{\thisname}{echelon}%
+          \expandafter\wg at pic\args\@endwg at pic%
+          {natoapp6c/s/}{\pgf at x,\pgf at y}{natoapp6c/echelon}
+        \fi%
+      }
 %    \end{macrocode}
 %
 % If the user want something under the frame, put that in. 
 % 
 %    \begin{macrocode}
-    % Put in stuff below main 
-    \@ifundefined{natoapp at below}{}{%
-      \n at to@pp at belowtrue
-      \begin{scope}
-        \wg at pic@all{\natoapp at below}{natoapp6c/s/}{%
-          $(M.south)+(0,-\n at to@pp at e@yy)$}{%
-          natoapp6c/below}%
-      \end{scope}%
-      \n at to@pp at belowfalse}
+      % Put in stuff below main 
+      \n at to@pp at dbg{2}{Below}
+      \@ifundefined{natoapp at below}{}{%
+        \n at to@pp at belowtrue
+        \begin{scope}
+          \n at to@pp at dbg{3}{Make below}%
+          \wg at nchor{\thisname}{below}%
+          \wg at pic@all{\natoapp at below}{natoapp6c/s/}{\pgf at x,\pgf at y}{%
+            natoapp6c/below}%
+        \end{scope}%
+        \n at to@pp at belowfalse}
 %    \end{macrocode}
 %
 % If the \spec{decoy} flag was set, we draw that.
 % 
 %    \begin{macrocode}
-    \ifnatoapp at decoy%
-      \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]%
+      \n at to@pp at dbg{2}{Decoy}
+      \ifnatoapp at decoy%
+        \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]%
         \n at to@pp at dbg{1}{Drawing decoy modifier}%
         \wg at sub@nchor{M\id}{north east}
         \wg at tmpa=\pgf at x%
@@ -530,10 +554,11 @@
         \pgfpathlineto{\pgfqpoint{0cm}{\wg at tmpc}}%
         \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{\wg at tmpb}}%
         \pgfusepath{stroke}%
-      \endscope%                           
-    \fi%                         
-    \endscope%
+        \endscope%                           
+      \fi%                         
+      \endscope%
     \fi%
+    \wg at ignore@sub at nchorfalse%
   }
 }
 %    \end{macrocode}
@@ -591,19 +616,166 @@
 %
 %    \begin{macrocode}
 \def\n at to@pp#1#2(#3){%
-  %\let\name\pgfutil at empty%
-  %\ifx|#3|\else\edef\name{(#3)}\fi%
-  %\n at to@pp at dbg{3}{Arguments: #1}%
-  %\edef\args{[natoapp6c={#1},transform shape] \name at (#2) {}}
-  %\expandafter\node\args;%
   \node[draw,transform shape,natoapp6c={#1}] (#3) at (#2) {};%
   \@ifnextchar;{\@gobble}{}}
 %    \end{macrocode}
 % \end{Macro}
 % 
+% 
 % \iffalse
 % --------------------------------------------------------------------
 % \fi
+% \subsubsection{Kriegspiel-like symbols}
+%
+% Style that selects Kriegspiel-like symbols by setting the symbols
+% picture path.
+% 
+%    \begin{macrocode}
+\tikzset{
+  kriegspiel symbol/.code={
+    \pgfkeys{%
+      /tikz/transform shape,%
+      /tikz/shape=kriegspiel symbol}
+    \n at to@pp at dbg{10}{Args for symbol `#1'}
+    \pgfkeys{/natoapp6c/.cd,%
+      path=kriegspiel/s/,%
+      #1}
+  }
+}
+%    \end{macrocode}
+%
+% Shape of Kriegspiel-like symbols (always the same).  This mainly
+% inherits from the NATO shape.  This hard-codes the frame shape to be
+% \texttt{kriegspiel} (shape \texttt{natoapp6c kriegspiel}).  
+%
+%    \begin{macrocode}
+\pgfdeclareshape{kriegspiel symbol}{
+  \inheritsavedanchors[from=natoapp6c]
+  \savedmacro\frameshape{%
+    \def\frameshape{kriegspiel}}
+  \inheritanchor[from=natoapp6c]{center}
+  \inheritanchor[from=natoapp6c]{north east}
+  \inheritanchor[from=natoapp6c]{north west}
+  \inheritanchor[from=natoapp6c]{south east}
+  \inheritanchor[from=natoapp6c]{south west}
+  \inheritanchor[from=natoapp6c]{north}
+  \inheritanchor[from=natoapp6c]{west}
+  \inheritanchor[from=natoapp6c]{south}
+  \inheritanchor[from=natoapp6c]{east}
+  \inheritanchor[from=natoapp6c]{upper}
+  \inheritanchor[from=natoapp6c]{lower}
+  \inheritanchor[from=natoapp6c]{left}
+  \inheritanchor[from=natoapp6c]{right}
+  \anchor{echelon}   {%
+    \wg at sub@nchor{M\id}{north west}%
+    \wg at tmpa=\n at to@pp at e@y cm%
+    \divide\wg at tmpa by 2%
+    \advance\pgf at y\wg at tmpa%
+  }%
+  \inheritbackgroundpath[from=natoapp6c]
+  \inheritbehindforegroundpath[from=natoapp6c]
+  \inheritbehindbackgroundpath[from=natoapp6c]
+  \savedmacro\thisname{\def\thisname{kriegspiel symbol}}
+}    
+%    \end{macrocode}
+%
+% The shape of a Kriegspiel-like frame.  Always the same.  This never
+% draws the background path.  
+%
+%    \begin{macrocode}
+\pgfdeclareshape{natoapp6c kriegspiel}{
+  \inheritsavedanchors[from=rectangle]
+  \inheritbackgroundpath[from=rectangle]
+  \savedanchor\northeast{\pgf at x=1.4cm\pgf at y=.3125cm}
+  \savedanchor\southwest{\pgf at x=-1.4cm\pgf at y=-.3125cm}
+  \inheritanchor[from=rectangle]{center}
+  \inheritanchor[from=rectangle]{north east}
+  \inheritanchor[from=rectangle]{north west}
+  \inheritanchor[from=rectangle]{south east}
+  \inheritanchor[from=rectangle]{south west}
+  \inheritanchor[from=rectangle]{north}
+  \inheritanchor[from=rectangle]{west}
+  \inheritanchor[from=rectangle]{south}
+  \inheritanchor[from=rectangle]{east}
+  \backgroundpath{}
+}
+%    \end{macrocode}
+%
+% Now some symbols.  We do not define a whole lot, since we do not
+% need that for Kriegspiel-like counters.
+%
+%    \begin{macrocode}
+\tikzset{
+  ks debug frame/.style={
+    draw=none,
+    %draw=magenta,
+    %dashed,
+  },
+  kriegspiel/s/infantry/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[pic actions,fill=none](a)--(b);
+    \path[pic actions,fill=none] let
+    \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2);
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in (-.05,\y2)rectangle++(.1,.2);
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in
+    (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle;
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  }
+}
+
+%    \end{macrocode}
+% 
+% \iffalse
+% --------------------------------------------------------------------
+% \fi
 % \subsubsection{Macros for markings}
 % 
 % \begin{Macro}{\natoappmark}

Modified: trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/symbols.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/symbols.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/natoapp6c/symbols.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -2228,6 +2228,11 @@
   natoapp6c/s/reconnaissance/.pic={%
     \path[draw] (M.north east)--(M.south west);},
 }
+\tikzset{
+  natoapp6c/s/reconnaissance artillery/.pic={
+    \path[draw] (M.north east)--(M.south west);,
+    \path[pic actions,fill=pgfstrokecolor] circle(0.2);},
+}
 %    \end{macrocode}
 % \end{NatoAppSymbol}
 %
@@ -3159,7 +3164,92 @@
 }
 %    \end{macrocode}
 % \end{NatoAppSymbol}
+%
+% \subsubsection{Some Kriegspiel-like  symbols}
+% 
 % \iffalse
+% ....................................................................
+% \fi
+%
+% Now some symbols.  We do not define a whole lot, since we do not
+% need that for Kriegspiel-like counters.
+%
+%    \begin{macrocode}
+\tikzset{
+  pics/kriegspiel/s/.unknown/.code={
+    \message{^^JTry regular `natoapp6c/s/\pgfkeyscurrentname'}
+    % \pic[pic actions,draw=black]{natoapp6c/s/\pgfkeyscurrentname};
+    \pgfkeysalso{/tikz/pics/natoapp6c/s/\pgfkeyscurrentname/.try}
+  },
+  ks debug frame/.style={
+    draw=none,
+    %draw=magenta,
+    %dashed,
+  },
+  kriegspiel/s/infantry/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[pic actions,fill=none](a)--(b);
+    \path[pic actions,fill=none] let
+    \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2);
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in (-.05,\y2)rectangle++(.1,.2);
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in
+    (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle;
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    %\path[pic actions](a)rectangle(b);
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    %\path[pic actions](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  }
+}
+
+%    \end{macrocode}
+% \iffalse
 % </natoapp6c>
 % --------------------------------------------------------------------
 % \fi

Modified: trunk/Master/texmf-dist/source/latex/wargame/util/export.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/util/export.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/util/export.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -134,7 +134,7 @@
   \mk at w{ \space "number": \thepage #1}%
   \let\oldmk at i\mk at i%
   \ifx#1,\relax\edef\mk at i{\mk at i\space\space}\fi}
-\def\end at info{%
+\def\endinfo{%
   \let\mk at i\oldmk at i%
   \mk at w{ \space \@rbchar,}}
 %    \end{macrocode}
@@ -175,30 +175,37 @@
       \foreach \u/\m in \t{% 
         \ifx\u\empty\else% Ignore empty cells 
           \ifx\u\chit at blank\else%
-            \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
-            \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined 
-            \ifx\u\m\def\m{1}\fi% If the same as unit
-            \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
-            %% We only make one copy of the chit, since we can duplicate
-            %% it in VASSAL
-            \info*{\u}{counter}{\x}
-            \nopagecolor%
-            \gdef\wg at drop@margin{0pt}%
-            \begin{tikzpicture}[chit has drop=false,#2]
-              \chit[\u=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}
-            \end at info%
-            %% \foreach \n in {1,...,\m}{% Make a number of copies 
-            %%   \ifx\u\chit at blank%
-            %%     \chit at dbg{3}{Ignoring blank chit:\u}%
-            %%   \else%
-            %%     \info{\u}{counter}{#2}
-            %%     \begin{tikzpicture}
-            %%       \chit[\u=\ti](\c,\r)%
-            %%     \end{tikzpicture}
-            %%   \fi%
-            %% }%
+            \ifx\u\chit at oob@vspacer\else%
+              \ifx\u\chit at oob@spacer\else%
+                \chit at dbg{2}{Next chit `\u' with possible multiplicity
+                  `\m'}%
+                \chit at dbg{1}{Next chit `\u' not `\chit at blank',
+                  `\chit at oob@vspacer', `\chit at oob@spacer'}
+                \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined 
+                \ifx\u\m\def\m{1}\fi% If the same as unit
+                \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
+                %% We only make one copy of the chit, since we can duplicate
+                %% it in VASSAL
+                \begin{info}*{\u}{counter}{\x}
+                  \nopagecolor%
+                  \gdef\wg at drop@margin{0pt}%
+                  \begin{tikzpicture}[chit has drop=false,#2]
+                    \chit[\u=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}
+                \end{info}%
+                %% \foreach \n in {1,...,\m}{% Make a number of copies 
+                %% \ifx\u\chit at blank%
+                %%   \chit at dbg{3}{Ignoring blank chit:\u}%
+                %% \else%
+                %%   \info{\u}{counter}{#2}
+                %%   \begin{tikzpicture}
+                %%     \chit[\u=\ti](\c,\r)%
+                %%   \end{tikzpicture}
+                %% \fi%
+                %% }%
+              \fi%
+            \fi%
           \fi%
         \fi%
       }%
@@ -238,39 +245,43 @@
       \foreach \u/\m in \t{% 
         \ifx\u\empty\else% Ignore empty cells 
           \ifx\u\chit at blank\else%
-            \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
-            \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined 
-            \ifx\u\m\def\m{1}\fi% If the same as unit
-            \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
-            %% Flipped chit
-            \edef\s{\u\space flipped}%
-            %% We only make one copy of the chit, since we can duplicate
-            %% it in VASSAL
-            \info*{\u}{counter}{\x}%
-            \nopagecolor%
-            \gdef\wg at drop@margin{0pt}%
-            \begin{tikzpicture}[chit has drop=false,#2]%
-              \chit[\u=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}%
-            \end at info%
-            \info*{\s}{counter}{\x}%
-            \nopagecolor%
-            \begin{tikzpicture}[chit has drop=false,#2]%
-              \chit[\s=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}%
-            \end at info%
-            %% \foreach \n in {1,...,\m}{% Make a number of copies 
-            %%   \ifx\u\chit at blank%
-            %%     \chit at dbg{3}{Ignoring blank chit:\u}%
-            %%   \else%
-            %%     \info{\u}{counter}{#2}
-            %%     \begin{tikzpicture}
-            %%       \chit[\u=\ti](\c,\r)%
-            %%     \end{tikzpicture}
-            %%   \fi%
-            %% }%
+            \ifx\u\chit at oob@vspacer\else%
+              \ifx\u\chit at oob@spacer\else%
+                \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
+                \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined 
+                \ifx\u\m\def\m{1}\fi% If the same as unit
+                \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
+                %% Flipped chit
+                \edef\s{\u\space flipped}%
+                %% We only make one copy of the chit, since we can duplicate
+                %% it in VASSAL
+                \begin{info}*{\u}{counter}{\x}%
+                  \nopagecolor%
+                  \gdef\wg at drop@margin{0pt}%
+                  \begin{tikzpicture}[chit has drop=false,#2]%
+                    \chit[\u=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}%
+                \end{info}%
+                \begin{info}*{\s}{counter}{\x}%
+                  \nopagecolor%
+                  \begin{tikzpicture}[chit has drop=false,#2]%
+                    \chit[\s=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}%
+                \end{info}%
+                %% \foreach \n in {1,...,\m}{% Make a number of copies 
+                %% \ifx\u\chit at blank%
+                %%   \chit at dbg{3}{Ignoring blank chit:\u}%
+                %% \else%
+                %%   \info{\u}{counter}{#2}
+                %%   \begin{tikzpicture}
+                %%     \chit[\u=\ti](\c,\r)%
+                %%   \end{tikzpicture}
+                %% \fi%
+                %% }%
+              \fi%
+            \fi%
           \fi%
         \fi%
       }%
@@ -608,7 +619,7 @@
   \mk at w{ \mk at i "name": "\bd at n" }%
   \let\mk at i\oomk at i%
   \mk at w{ \mk at i\@rbchar}%
-  \end at info%
+  \endinfo%
 }
 %    \end{macrocode}
 %
@@ -617,7 +628,7 @@
 % 
 %    \begin{macrocode}
 \def\wg at gennumberm@rkers#1#2#3#4{
-  \message{^^JNumbered markers: Type=`#1' Max=`#2' Category=`#3'}
+  \chit at dbg{2}{Numbered markers: Type=`#1' Max=`#2' Category=`#3'}
   \def\markers{}
   \def\keys{}
   \foreach \i in {1,...,#2}{%
@@ -645,7 +656,7 @@
 }%
 \def\@@battlemarkers[#1][#2]#3{%
   \wg at gennumberm@rkers{battle marker}{#3}{#2}{#1}%
-  \message{^^JMake a hidden unit and add to Markers category}
+  \chit at dbg{1}{Make a hidden unit and add to Markers category}
   {%
     \nopagecolor%
     \chitimages[Markers]{{wg hidden unit}}%
@@ -671,7 +682,7 @@
   \foreach \o/\f/\n [count=\i] in {#2}{%
     \ifx\n\f\def\n{\o}\fi%
     \ifx\o\f\def\f{white}\fi%
-    \message{^^JColour no \i marker `#1 \n' w/fill `\f' text `\o'}%
+    \chit at dbg{3}{Colour no `\i' marker `#1 \n' w/fill `\f' text `\o'}%
     \protected at xdef\keys{/tikz/#1 \n/.style={/tikz/#1={\o,\f}},\keys}
     \xdef\markers{\markers,#1 \n}
   }%
@@ -752,22 +763,22 @@
   \info{unit-icon}{icon}{}%
   \tikz[auto icon,scale=.7,auto icon more/.try]{%
     \chit[fill=#2,
-      symbol={[
-        scale line widths,
-        line width=1pt,
-        faction=friend,
-        command=land,
-        main=infantry,
-        scale=1.3](0,-.15)}]}%
-    %
-    \info{layer-icon}{icon}{}%
-    \begin{tikzpicture}[scale=.25]
-      \foreach \i in {-1,0,1}{
-        \scoped[shift={(0,\i*.15)}]{
-          \draw[black,fill=white] (-.5,0)
-          --(0,.3)--(.5,0)--(0,-.3)--cycle;
-        }
+    symbol={[
+      scale line widths,
+      line width=1pt,
+      faction=friend,
+      command=land,
+      main=infantry,
+      scale=1.3](0,-.15)}]}%
+  % 
+  \info{layer-icon}{icon}{}%
+  \begin{tikzpicture}[scale=.25]
+    \foreach \i in {-1,0,1}{
+      \scoped[shift={(0,\i*.15)}]{
+        \draw[black,fill=white] (-.5,0)
+        --(0,.3)--(.5,0)--(0,-.3)--cycle;
       }
+    }
     \end{tikzpicture}%
     %
     \info{los-icon}{icon}{}
@@ -815,8 +826,9 @@
       %\node[shape=#4,transform shape,draw=none,fill=black,opacity=.5]
       %at (.05,-.03){};
       \node[shape=#4,#2,transform shape,
-      chit drop
-      ]{\p};\wg at add@drop at margin{}}}}
+      chit drop,
+      node contents={\p}
+      ]{};\wg at add@drop at margin{}}}}
 %    \end{macrocode}
 % 
 % \subsubsection{Hooks into chits, etc.}

Modified: trunk/Master/texmf-dist/source/latex/wargame/util/misc.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/util/misc.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/util/misc.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -101,7 +101,16 @@
 %    \end{macrocode}
 % \end{Macro}
 %
-%
+% \begin{Macro}{\wg at nchor}
+%    \begin{macrocode}
+\def\wg at nchor#1#2{% shape name, anchor
+  \wg at dbg{10}{Get `#2' in `#1'}%
+  \expandafter\csname pgf at anchor@#1@#2\endcsname%
+  \wg at dbg{10}{Got `\pgf at x',`\pgf at y'}
+}
+%    \end{macrocode}
+% \end{Macro}
+% 
 % \begin{Macro}{\wg at sub@nchor}
 %   Get anchor from sub node.  We cannot use \cs{pgfpointanchor} since
 %   that returns the anchor coordinates in the global coordinate
@@ -108,9 +117,13 @@
 %   system.
 %
 %    \begin{macrocode}
+\newif\ifwg at ignore@sub at nchor\wg at ignore@sub at nchorfalse%
 \def\wg at sub@nchor#1#2{%
-  \wg at dbg{3}{^^JGet `#2' in `#1'}%
+  \wg at dbg{10}{^^JGet `#2' in `#1'}%
   \@ifundefined{pgf at sh@ns@#1}{%
+    \ifwg at ignore@sub at nchor%
+      \wg at dbg{0}{WARNING: Shape `#1' not defined, anchor `#2' unknown}
+    \fi
     \pgf at x=0cm\pgf at y=0cm}{%
     \pgf at process{%
       \csname pgf at sh@ma@#1\endcsname% MW

Modified: trunk/Master/texmf-dist/source/latex/wargame/utils/wgexport.py
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/utils/wgexport.py	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/utils/wgexport.py	2024-11-19 20:38:26 UTC (rev 72903)
@@ -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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PredefinedSetup
@@ -779,6 +782,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Option
@@ -1266,6 +1271,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Prototype
@@ -1279,6 +1285,7 @@
         ----------
         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 @@
                  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 @@
                      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 @@
 
     def getSymbolicDice(self):
         return self.getParent(SymbolicDice)
+
+    def getFaces(self):
+        return self.getAllElements(DieFace,single=False)
         
 registerElement(SpecialDie)
                      
@@ -1622,6 +1668,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : LayerControl
@@ -1977,10 +2024,11 @@
                  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 @@
                  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 @@
                       emptyHexReportForma   = emptyHexReportForma,
                       enableHTML            = enableHTML,
                       extraTextPadding      = extraTextPadding,
-                      fgColor               = fgColor,
+                      bgColor               = bg,
+                      fgColor               = fg,
                       fontSize              = fontSize,
                       graphicsZoom          = graphicsZoom,
                       hotkey                = hotkey,
@@ -2039,7 +2092,8 @@
                       version               = version,
                       verticalOffset        = verticalOffset,
                       verticalTopText       = verticalTopText,
-                      zoomlevel             = zoomlevel)
+                      zoomlevel             = zoomlevel,
+                      stopAfterShowing      = stopAfterShowing)
 
 registerElement(CounterDetailViewer)
 
@@ -2198,7 +2252,26 @@
                  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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Pieces
@@ -2235,6 +2309,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Piece
@@ -2264,7 +2339,7 @@
             Dictionary or list of `Piece` children
 
         '''
-        return self.getElementsWithKey(PieceSlot,'entryName',asdict)
+        return self.getElementsByKey(PieceSlot,'entryName',asdict)
 
 registerElement(AtStart)
 
@@ -2292,6 +2367,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Property
@@ -2347,6 +2423,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Level
@@ -2374,6 +2451,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Counter
@@ -2387,6 +2465,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : List
@@ -2495,6 +2574,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Hotkey
@@ -2626,6 +2706,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AboutScreen
@@ -2639,6 +2720,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HelpFile
@@ -2652,6 +2734,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BrowserHelpFile
@@ -2665,6 +2748,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BrowserPDFFile
@@ -2678,6 +2762,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Tutorial
@@ -2909,6 +2994,7 @@
                buttonText='Retire',
                buttonToolTip='Switch sides, become observer, or release faction'):
         '''Add a player roster to the module
+        
         Parameters
         ----------
         doc : Element
@@ -2931,6 +3017,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Side
@@ -3150,6 +3237,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AboutScreen
@@ -3180,6 +3268,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Tabs
@@ -3192,6 +3281,7 @@
         Parameters
         ----------
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Combo
@@ -3205,6 +3295,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Panel
@@ -3218,6 +3309,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : List
@@ -3231,6 +3323,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MapWidget
@@ -3244,6 +3337,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chart
@@ -3257,6 +3351,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceSlot
@@ -3270,6 +3365,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Piece
@@ -3288,6 +3384,7 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : WidgetMap
@@ -3483,6 +3587,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Numbering
@@ -3720,8 +3826,8 @@
                                         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Numbering
@@ -3892,6 +3999,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Region
@@ -4017,6 +4125,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Highlighter
@@ -4031,6 +4140,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Zone
@@ -4057,6 +4168,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ZoneHighlight
@@ -4093,6 +4206,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HexGrid
@@ -4223,6 +4338,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SquareGrid
@@ -4236,6 +4352,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : RegionGrid
@@ -4249,6 +4366,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Property
@@ -4263,6 +4381,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
     def getProperties(self):
         '''Get all `Property` element from this
 
-        Parameters
-        ----------
         Returns
         -------
         children : dict
@@ -4339,6 +4459,7 @@
             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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Setup
@@ -4411,6 +4533,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Board
@@ -4437,6 +4561,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ZonedGrid
@@ -4526,6 +4652,7 @@
         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 @@
         ----------
         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 @@
                  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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Picker
@@ -4646,6 +4834,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -4964,6 +5173,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -4978,6 +5188,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BoardPicker
@@ -4991,6 +5202,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StackMetrics
@@ -5004,6 +5216,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ImageSaver
@@ -5017,6 +5230,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : TextSaver
@@ -5030,6 +5244,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ForwardToChatter
@@ -5043,6 +5258,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MenuDisplayer
@@ -5056,6 +5272,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MapCenterer
@@ -5069,6 +5286,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StackExpander
@@ -5082,6 +5300,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceMover
@@ -5095,6 +5314,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SelectionHighlighters
@@ -5108,6 +5328,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyBufferer
@@ -5121,6 +5342,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HighlightLastMoved
@@ -5134,6 +5356,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : CounterDetailViewer
@@ -5147,6 +5370,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalMap
@@ -5160,6 +5384,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Zoomer
@@ -5173,6 +5398,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HidePiecesButton
@@ -5186,6 +5412,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MassKey
@@ -5199,6 +5426,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StartupMassKey
@@ -5212,6 +5440,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Flare
@@ -5225,6 +5454,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AtStart
@@ -5240,6 +5470,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceLayers
@@ -5253,6 +5484,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -5266,6 +5498,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -5377,6 +5610,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chart
@@ -5388,12 +5622,16 @@
 
         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 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 @@
             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 @@
         '''
         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 @@
         self._state  = list(kwargs.values())
         self._snames = list(kwargs.keys())
 
-    
     def __getitem__(self,key):
         '''Look up item in either type or state'''
         try:
@@ -5574,7 +5816,7 @@
         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')
@@ -5581,32 +5823,71 @@
                 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 @@
         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 @@
             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
+
+        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
+        
+        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 @@
                 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.
+
+    def encodeAdd(self,*traits,iden='null',verbose=False):
+        '''Encodes type and states'''
+        if len(traits) < 1: return ''
         
-        Parameters
-        ----------
-        traits : tuple of Trait objects
-            The traits to set on this object.
-        iden : str
-            Identifier
-
-        '''
         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 @@
         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 @@
                      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 @@
 # 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 @@
             # 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 @@
                  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,18 +7265,25 @@
                  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
@@ -6840,7 +7290,7 @@
                      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 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BasicCommandEncoder
@@ -7290,6 +7747,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalTranslatableMessages
@@ -7303,6 +7761,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PlayerRoster
@@ -7316,6 +7775,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PlayerRoster
@@ -7329,6 +7789,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Language
@@ -7342,6 +7803,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chatter
@@ -7355,6 +7817,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyNamer
@@ -7368,6 +7831,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Notes
@@ -7381,6 +7845,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Language
@@ -7394,6 +7859,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chatter
@@ -7407,6 +7873,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyNamer
@@ -7420,6 +7887,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalProperties
@@ -7433,6 +7901,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalOptions
@@ -7446,6 +7915,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : TurnTrack
@@ -7459,6 +7929,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Documentation
@@ -7472,6 +7943,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Prototypes
@@ -7485,6 +7957,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceWindow
@@ -7498,6 +7971,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ChartWindow
@@ -7511,6 +7985,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Inventory
@@ -7524,6 +7999,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Map
@@ -7537,6 +8013,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : DiceButton
@@ -7550,6 +8027,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PredefinedSetup
@@ -7563,6 +8041,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GameMassKey
@@ -7576,6 +8055,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StartupMassKey
@@ -7589,6 +8069,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -7602,6 +8083,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SymbolicDice
@@ -7619,6 +8101,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7948,6 +8453,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7965,6 +8471,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7982,6 +8489,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -8014,6 +8522,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Game
@@ -8096,6 +8606,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Version
@@ -8109,6 +8620,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : VASSALVersion
@@ -8122,6 +8634,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Name
@@ -8135,6 +8648,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Description
@@ -8148,6 +8662,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : DateSaved
@@ -8162,6 +8677,7 @@
         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 @@
             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 @@
             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 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Data
@@ -8928,6 +9448,18 @@
     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 @@
             File name in module
         content : str
             File content
+        
         Returns
         -------
         element : File
@@ -8963,6 +9496,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ExternalFile
@@ -8974,6 +9508,14 @@
     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 @@
         ----------
         filenames : tuple
             The files to get 
+        
         Returns
         -------
         files : dict
@@ -9009,7 +9552,7 @@
 
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : VSav
@@ -9043,6 +9587,187 @@
 # 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,6 +9862,80 @@
 # 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',
@@ -9143,6 +9942,7 @@
                  infoname      = 'export.json',
                  title         = 'Draft',
                  version       = 'draft',
+                 imageFormat   = 'png',
                  description   = '',     
                  rules         = None,
                  tutorial      = None,
@@ -9197,76 +9997,80 @@
         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 @@
             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 @@
         ----------
         args : list
             List of process command line elements
+        
         Returns
         -------
         pipe : subprocess.Pipe
@@ -9320,16 +10127,18 @@
     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 @@
         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 @@
         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 @@
             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 @@
         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 @@
         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,12 +10465,22 @@
         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)
+                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
     
-        with Image.open(BytesIO(buffer)) as img:
-            w, h  = img.width, img.height
-    
         return w,h
     
     # ----------------------------------------------------------------
@@ -9591,11 +10530,13 @@
             
                 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 @@
                     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 @@
                 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 @@
                         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 @@
                     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 @@
                            [['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 @@
                 wrap        = True,
                 description = 'Zero battle counter',
             ),
+            # Set global property combat # from this 
             GlobalPropertyTrait(
                 ['',self._setBattle,GlobalPropertyTrait.DIRECT,
                  f'{{{self._battleCounter}}}'],
@@ -10411,6 +11367,8 @@
         #   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 @@
                                  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 @@
                            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 @@
     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 @@
                   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 @@
         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 @@
                               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 @@
         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 @@
         
         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 @@
             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 @@
             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 @@
                                            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,7 +12292,8 @@
                 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',
@@ -11273,7 +12300,7 @@
                                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 @@
                                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 @@
                 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 @@
                                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 @@
             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 @@
         '''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 @@
             
                 # 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,6 +13285,37 @@
         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
 #
@@ -12233,113 +13322,180 @@
 # ====================================================================
 # 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)
+    if args.mode == 'patch':
+        patchIt(args)
+    else:
+        exportIt(args)
         
-    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
-        
 #
 # EOF
 #

Added: trunk/Master/texmf-dist/source/latex/wargame/utils/wgmakenato.py
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/utils/wgmakenato.py	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/wargame/utils/wgmakenato.py	2024-11-19 20:38:26 UTC (rev 72903)
@@ -0,0 +1,756 @@
+#!/usr/bin/env python
+
+# ====================================================================
+def ensure_list(what):
+    if not what:
+        return None
+
+    if isinstance(what,str):
+        return [what]
+
+    return what
+
+# --------------------------------------------------------------------
+def clean(base, suffixes):
+    from pathlib import Path
+
+    if not isinstance(base,Path):
+        base = Path(base)
+        
+    for suffix in suffixes:
+        tgt = base.with_suffix(suffix)
+        tgt.unlink(missing_ok=True)
+
+# ====================================================================
+possible_commands = ['air',
+                     'land',
+                     'equipment',
+                     'installation',
+                     'sea surface',
+                     'sub surface',
+                     'space',
+                     'activity',
+                     'dismounted']
+possible_factions = ['friendly',
+                     'hostile',
+                     'neutral',
+                     'unknown']
+echelon_mapping = {'':       'team',
+                   '*':      'squad',
+                   '**':     'section',
+                   '***':    'platoon',
+                   '|':      'company',
+                   '||':     'battalion',
+                   '|||':    'regiment',
+                   'x':      'brigade',
+                   'xx':     'division',
+                   'xxx':    'corps',
+                   'xxxx':   'army',
+                   'xxxxx':  'army group',
+                   'xxxxxx': 'theater'}
+possible_echelons = list(echelon_mapping.values())+list(echelon_mapping.keys())
+        
+# ====================================================================
+def make_picture(command,
+                 faction,
+                 echelon,
+                 main,
+                 top=None,
+                 bottom=None,
+                 left=None,
+                 right=None,
+                 below=None,
+                 decoy=False,
+                 line_width='1pt',
+                 scale=.45,
+                 more=None,
+                 **kwargs):
+    '''Create latex code for the NATO App6 symbol - a single tikzpicture
+
+    Parameters
+    ----------
+    command : str
+        Command (base symbol)
+    faction : str
+        Faction (base symbol)
+    echelon : str or None
+        Echelon (top of base symbol
+    main : str or list of str
+        Main symbols
+    top : str or list of str
+        Top modifiers
+    bottom : str or list of str
+        Bottom modifiers
+    left : str or list of str
+        Left modifiers
+    right : str or list of str
+        Right modifiers
+    below : str or list of str
+        Below base frame
+    decoy : bool
+        True if it should be a decoy
+    line_width : str
+        Line width, including unit
+    scale : float
+        Scaling of image
+
+    Return
+    ------
+    out : str
+        LaTeX code to write to file 
+    '''
+    ind = '               '
+    def add_line(out,key,what,ind=ind):
+        '''Adds a line with a key'''
+        lwhat = ensure_list(what)
+        if not lwhat:
+            return out
+
+        swhat = ",".join(lwhat)
+
+        return out+f'{ind}    {key}={{{swhat}}},%'+'\n'
+
+        
+    out = fr'''\begin{{tikzpicture}}%
+                 \natoapp[%
+                   scale={scale},%
+                   scale line widths,%
+                   line width={line_width},%
+                   command={command},%
+                   faction={faction},%'''+'\n'
+
+    out = add_line(out,'echelon',echelon)
+    out = add_line(out,'main',   main)
+    out = add_line(out,'top',    top)
+    out = add_line(out,'bottom', bottom)
+    out = add_line(out,'left',   left)
+    out = add_line(out,'right',  right)
+    out = add_line(out,'below',  below)
+    if more:
+        out += f'{ind}    {more}%'+'\n'
+        
+    out += fr'''    ];%
+                \end{{tikzpicture}}%
+             '''
+    lout = out.split('\n')
+    lout = [l[len(ind):] if l.startswith(ind) else l for l in lout]
+    return '\n'.join(lout)
+
+# --------------------------------------------------------------------
+def make_latex(db):
+    '''Create latex code for the all NATO App6 symbols -
+    a complete document
+
+    Parameters
+    ----------
+    db : list of dict
+        Configurations of each symbol
+
+    Return
+    ------
+    out : str
+        LaTeX code to write to file 
+    '''
+
+    ind = '              '
+    out = r'''\documentclass[tikz,11pt]{standalone}
+              \usepackage{wargame}
+              \begin{document}%'''+'\n'
+
+    for entry in db:
+        out += make_picture(**entry)
+
+    out += r'\end{document}'
+
+    lout = out.split('\n')
+    lout = [l[len(ind):] if l.startswith(ind) else l for l in lout]
+    return '\n'.join(lout)
+
+    
+# --------------------------------------------------------------------
+def make_name(command,
+              faction,
+              echelon,
+              main,
+              top=None,
+              bottom=None,
+              left=None,
+              right=None,
+              below=None,
+              decoy=False,
+              output=None,
+              **kwargs):
+    '''Create the file name
+    
+    Parameters
+    ----------
+    command : str
+        Command (base symbol)
+    faction : str
+        Faction (base symbol)
+    echelon : str or None
+        Echelon (top of base symbol
+    main : str or list of str
+        Main symbols
+    top : str or list of str
+        Top modifiers
+    bottom : str or list of str
+        Bottom modifiers
+    left : str or list of str
+        Left modifiers
+    right : str or list of str
+        Right modifiers
+    decoy : bool
+        True if it should be a decoy
+
+    Return
+    ------
+    out : str
+        Output file name
+    '''
+    if output:
+        return output.format(command=command,
+                             faction=faction,
+                             echelon=echelon)\
+                     .replace(' ','-')\
+                     .replace('=','-')
+        
+    def clean_sub(what):
+        from re import sub
+
+        res = what
+        while True:
+            old = res
+            res = sub(r'\[[^]]+\]','',res)
+            res = sub(r'\{[^}]+\}','',res)
+            if res == old:
+                break
+
+        return res
+            
+    def add_component(out, what,sep='-'):
+         
+        lwhat = ensure_list(what)
+        
+        if not lwhat:
+            return out
+
+        return out+'_'+sep.join([clean_sub(l) for l in lwhat])
+
+    def add_front(out,what,sep='_'):
+        if not what:
+            return out
+
+        if len(out) > 0: out += sep
+        return out + what
+    
+    out = ''
+    out = add_front(out,faction)
+    out = add_front(out,command.replace(' ','-'))
+    out = add_front(out,echelon.replace(' ','-'))
+    out = add_component(out,main)
+    out = add_component(out,top)
+    out = add_component(out,bottom)
+    out = add_component(out,left)
+    out = add_component(out,right)
+    out = add_component(out,below)
+
+    return out.replace(' ','-').replace('=','-')
+
+
+# ====================================================================
+default_latex_flags = ['-interaction=nonstopmode',
+		       '-file-line-error',
+		       '-synctex','15',
+		       '-shell-escape']
+default_latex_command = 'pdflatex'
+
+# --------------------------------------------------------------------
+def create_process(args,verbose=False):
+    '''Create a subprocess with arguments
+
+    Returns
+    -------
+    proc : Popen
+        Process with stdout and stderr as pipes (fifos)
+    '''
+    from os import environ
+    from subprocess import Popen, PIPE
+
+    if verbose:
+        print(f'Will run: "{" ".join(args)}"')
+        
+    return Popen(args,env=environ.copy(),stdout=PIPE,stderr=PIPE)
+
+# --------------------------------------------------------------------
+default_timeout = None
+default_keep    = False
+# --------------------------------------------------------------------
+def run_latex(file,
+              content,
+              latex_cmd    = default_latex_command,
+              latex_flags  = default_latex_flags,
+              timeout      = default_timeout,
+              keep         = default_keep,
+              verbose      = False):
+    '''Run latex on generated content
+
+    Parameters
+    ----------
+    filename : str
+        The file to write
+    content : str
+        Content to write to source file
+    latex_cmd : str
+        LaTeX command to run e.g., 'pdflatex'
+    latex_flags : list of str
+        Flags to pass to the LaTeX command line
+    timeout : int
+        Process timeout
+    keep : bool
+        If true, keep intermediate files
+
+    Returns
+    -------
+    None
+    '''
+    from pathlib import Path
+
+    fpath  = Path(file.name).with_suffix('')
+    csuf   = ['.aux','.log','.out','.synctex','.synctex.gz','.tex']
+        
+    file.write(content)
+    file.close()
+        
+    lflags = ensure_list(latex_flags)
+
+    args = [latex_cmd] + (lflags if lflags else + []) + \
+        [fpath.with_suffix('.tex').name]
+
+    proc = create_process(args,verbose=verbose)
+
+    try:
+        out, err = proc.communicate(timeout=timeout)
+
+        sout = out.decode()
+        if 'Error' in sout or \
+           'Emergency stop' in sout or \
+           not Path(fpath).with_suffix('.pdf').is_file():
+            raise RuntimeError(sout)
+        
+    except Exception as e:
+        proc.kill()
+        proc.communicate()
+        if not keep:
+            clean(base=fpath,suffixes=csuf+['.pdf'])
+
+        raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+
+                           str(e) + '\n--- Start LaTeX code -------\n'+
+                           content+ '\n--- End LaTeX code ---------')
+
+    if not keep:
+        clean(base=fpath,suffixes=csuf)
+
+# --------------------------------------------------------------------
+possible_formats   = ['png','svg','jpg','tiff']
+default_format     = possible_formats[0]
+default_resolution = 150
+
+# --------------------------------------------------------------------
+def run_cairo(filename,
+              npages,
+              format     = default_format,
+              resolution = default_resolution,
+              timeout    = default_timeout,
+              keep       = default_keep,
+              verbose    = False):
+    '''Run 'pdftocairo' on generated PDF
+
+    Parameters
+    ----------
+    filename : str
+        The file to write
+    format : str
+        Image format to write
+    resolution : int
+        Pixels Per Inch (PPI)
+    timeout : int
+        Process timeout
+    keep : bool
+        If true, keep intermediate files
+
+    Returns
+    -------
+    None
+    '''
+    from pathlib import Path
+
+    fpath = Path(filename)
+    args  = ['pdftocairo']
+    if format != 'svg':
+        args.extend(['-transp'])
+
+    args.extend(['-r',str(resolution),f'-{format}'])
+
+    inpdf = fpath.with_suffix('.pdf').name
+    
+    def runit(args,pdf=inpdf,outimg=None):
+        args.append(inpdf)
+        if outimg: args.append(outimg)
+        
+        proc = create_process(args,verbose)
+
+        try:
+            out, err = proc.communicate(timeout=timeout)
+              
+        except Exception as e:
+            proc.kill()
+            proc.communicate()
+            if not keep:
+                clean(base=fpath,suffixes=[f'.{format}','.pdf'])
+
+            raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+e)
+
+    if format != 'svg':
+        runit(args)
+    else:
+        from math import ceil, log10
+
+        base    = fpath.stem
+        wn      = ceil(log10(npages))
+        for no in range(1,npages+1):
+            no_args = args.copy()
+            no_args.extend(['-f',str(no),'-l',str(no)])
+
+            runit(no_args,outimg=base+f'-{no:0{wn}d}.{format}')
+        
+    if not keep:
+        clean(base=fpath,suffixes=['.pdf'])
+
+# --------------------------------------------------------------------
+def make_images(db,
+                base_name,
+                format   = default_format,
+                verbose = False):
+    from math import ceil, log10
+    from pathlib import Path
+
+    results = []
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        output = entry['output']+'.'+format
+        inpath = Path(f'{base_name}-{no+1:0{wn}d}.{format}')
+
+        inpath.rename(output)
+        results.append(output)
+        if verbose:
+            print(f'{no+1:0{wn}d}/{ndb}: {output}')
+
+    return results
+    
+# --------------------------------------------------------------------
+default_xml = False
+
+# --------------------------------------------------------------------
+def write_xmls(db,
+               format  = default_format,
+               verbose = False):
+    '''Build an XML snippet of a prototype'''
+
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        if verbose:
+            print(f'{no+1:0{wn}d}/{ndb} ...{entry["output"]}.xml')
+
+        write_xml(**entry,format=format)
+
+# --------------------------------------------------------------------
+def write_xml(command = None,
+              faction = None,
+              echelon = None,
+              main    = None,
+              top     = None,
+              bottom  = None,
+              left    = None,
+              right   = None,
+              below   = None,
+              decoy   = False,
+              output  = None,
+              format  = default_format,
+              **kwargs):
+    '''Build an XML snippet of a prototype'''
+    from xml.dom.minidom import Document
+    from wgexport import LayerTrait, Prototype, MarkTrait, \
+        BasicTrait, BuildFile
+
+    doc = BuildFile()
+    hasEchelon = echelon and len(echelon) > 0
+    
+    yoff   = -8 if main else -10 if echelon else -8
+    traits = [
+        LayerTrait(
+            images   = [f'{output}.{format}'],
+            newNames = [''],
+            activateName = '',
+            activateMask = '',
+            activateChar = '',
+            increaseName = '',
+            increaseMask = '',
+            increaseChar = '',
+            decreaseName = '',
+            decreaseMask = '',
+            decreaseChar = '',
+            resetName    = '',
+            resetKey     = '',
+            resetLevel   = 1,
+            under        = False,
+            underXoff    = 0,
+            underYoff    = yoff,
+            loop         = False,
+            name         = ('NATOAPP6_command_faction'
+                            +('_main' if main else '')
+                            +('_echelon' if hasEchelon else '')),
+            description  = 'NATO App6 symbology',
+            randomKey    = '',
+            randomName   = '',
+            follow       = False,
+            expression   = '',
+            first        = 1,
+            version      = 1,
+            always       = True,
+            activateKey  = '',
+            increaseKey  = '',
+            decreaseKey  = '',
+            scale        = 1.)]
+
+    if command:
+        traits.append(MarkTrait(name  = 'NATOAPP6_command',
+                                value = command))
+    if main:
+        traits.append(MarkTrait(name  = 'NATOAPP6_type',
+                                value = make_name(command = '',
+                                                  faction = '',
+                                                  echelon = '',
+                                                  main    = main,
+                                                  top     = top,
+                                                  bottom  = bottom,
+                                                  left    = left,
+                                                  right   = right,
+                                                  below   = below,
+                                                  decoy   = decoy)))
+    if echelon:
+        traits.append(MarkTrait(name = 'NATOAPP6_echelon',
+                                value = echelon))
+
+    traits.append(BasicTrait())
+
+
+    proto = Prototype(doc,
+                      name        = output,
+                      traits      = traits,
+                      description = 'NATO App6 symbol')
+
+    doc._node.insertBefore(
+        doc._root.createComment('Created via LaTeX wargame package, '
+                                'licensed under the CreativeCommons '
+                                'Share-Alike, '
+                                '© 2024 Christian Holm Christensen'),
+        proto._node)
+    xml_out = doc.encode()
+
+    with open(output+'.xml','wb') as out:
+        out.write(xml_out)
+
+
+# ====================================================================
+def create_images(db,
+                  latex_command = default_latex_command,
+                  latex_flags   = default_latex_flags,
+                  timeout       = default_timeout,
+                  resolution    = default_resolution,
+                  format        = default_format,
+                  keep          = default_keep,
+                  verbose       = False,
+                  xml           = default_xml):
+    from  tempfile import NamedTemporaryFile
+    from pathlib import Path
+    
+    result = []
+
+    
+    with NamedTemporaryFile(mode='w',suffix='.tex',dir='.',
+                            delete=not keep,
+                            delete_on_close=False) as texfile:
+        if verbose:
+            print(f'Temporary file: {texfile.name}')
+            
+        base_name  = Path(texfile.name).stem
+        latex_code = make_latex(db)
+        try:
+            run_latex(texfile,
+                      latex_code,
+                      latex_cmd   = latex_command,
+                      latex_flags = latex_flags,
+                      timeout     = timeout,
+                      keep        = keep,
+                      verbose     = verbose)
+                  
+            run_cairo(base_name,
+                      npages     = len(db),
+                      format     = format,
+                      resolution = resolution,
+                      timeout    = timeout,
+                      keep       = keep,
+                      verbose    = verbose)
+
+            result = make_images(db,
+                                 base_name,
+                                 format   = format,
+                                 verbose  = verbose)
+
+            if xml:
+                write_xmls(db)
+            
+        except Exception as e:
+            import traceback
+            print(traceback.format_exc())        
+            print(e)
+            return None
+
+    return result
+        
+        
+# ====================================================================
+#
+# Main program
+#
+if __name__ == '__main__':
+    from argparse import ArgumentParser, FileType
+    from pprint import pprint
+    
+    ap = ArgumentParser(description='Create an image of NATO App6 symbol')
+    ap.add_argument('-c','--command',type=str,choices=possible_commands,
+                    help='Select command',default=possible_commands[1])
+    ap.add_argument('-f','--faction',type=str,choices=possible_factions,
+                    help='Select faction',default=possible_factions[0])
+    ap.add_argument('-e','--echelon',type=str,choices=possible_echelons,
+                    help='Select echelon')
+    ap.add_argument('-m','--main',type=str,nargs='*',
+                    help='Select main symbol(s)')
+    ap.add_argument('-t','--top',type=str,nargs='*',
+                    help='Select top modifier(s)')
+    ap.add_argument('-b','--bottom',type=str,nargs='*',
+                    help='Select bottom modifier(s)')
+    ap.add_argument('-l','--left',type=str,nargs='*',
+                    help='Select left modifier(s)')
+    ap.add_argument('-r','--right',type=str,nargs='*',
+                    help='Select left modifier(s)')
+    ap.add_argument('-B','--below',type=str,nargs='*',
+                    help='Select below (mobility) modifier(s)')
+    ap.add_argument('-d','--decoy',action='store_true',
+                    help='Mark as decoy')
+    ap.add_argument('-w','--line-width',default='1pt',type=str,
+                    help='Line width, including unit')
+    ap.add_argument('-s','--scale',default=0.45,type=float,
+                    help='Scaling of symbol (width in centimetre)')
+    ap.add_argument('-I','--format',default=default_format,
+                    choices=possible_formats,type=str,
+                    help='Output image format')
+    ap.add_argument('-L','--latex-command',default=default_latex_command,
+                    help='Set the LaTeX command to use',type=str)
+    ap.add_argument('-F','--latex-flags',default=default_latex_flags,
+                    help='Set the LaTeX command to use',type=str,nargs='*')
+    ap.add_argument('-T','--timeout',default=default_timeout,type=int,
+                    help='Timeout of sub-processes')
+    ap.add_argument('-R','--resolution',default=default_resolution,type=int,
+                    help='Resolution out output image (ppi)')
+    ap.add_argument('-k','--keep',action='store_true',
+                    help='Leave intermediate files')
+    ap.add_argument('-o','--output',type=str,
+                    help='Set output file name, default is autogenerated')
+    ap.add_argument('-j','--json',type=str,nargs='*',
+                    help='Read symbols to create from JSON file')
+    ap.add_argument('-v','--verbose',action='store_true')
+    ap.add_argument('-X','--xml',action='store_true',
+                    help='Also output XML snippets')
+
+    args      = ap.parse_args()
+    vargs     = vars(args)
+    oargs     = {}
+    to_remove = ['latex_command',
+                 'latex_flags',
+                 'resolution',
+                 'format',
+                 'timeout',
+                 'keep',
+                 'json',
+                 'verbose',
+                 'xml']
+    for key in to_remove:
+        if key not in vargs:
+            continue
+
+        oargs[key] = vargs[key]
+        del vargs[key]
+        
+    commons = ['infantry',
+               'armoured',
+               'armoured+infantry',
+               'reconnaissance',
+               'armoured+reconnaissance',
+               '[fill=pgfstrokecolor]artillery',
+               'armoured+[fill=pgfstrokecolor]artillery',
+               'text=SF']
+    
+
+    db     = None
+    main   = vargs['main']
+    injson = oargs.pop('json',None)
+    
+    #pprint(vargs)
+    #pprint(oargs)
+    
+    if injson:
+        from json import load
+
+        db = []
+        for j in injson:
+            print(f'=== JSON input: {j}')
+            with open(j,'r') as jin:
+                db.extend(load(jin))
+                
+    elif main and len(main) == 1 and main[0] == 'common':
+        db = [{'main': w.split('+')} for w in commons]
+    else:
+        db = [vargs]
+
+    if not db:
+        raise RuntimeError('Noting to do')
+
+    from math import log10, ceil
+    
+    def ensure(d, key, default):
+        if key not in d and default:
+            d[key] = default[key]
+
+    results = []
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        print(f'{no:{wn}d} of {ndb} ...',end='')
+        if not isinstance(entry,dict):
+            print(' skipped')
+            continue
+
+        for key in vargs.keys():
+            ensure(entry,key,vargs)
+        entry['output'] = make_name(**entry)
+        print(entry['output'])
+
+
+    results = create_images(db,**oargs)
+#
+#
+#


Property changes on: trunk/Master/texmf-dist/source/latex/wargame/utils/wgmakenato.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Modified: trunk/Master/texmf-dist/source/latex/wargame/wargame.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/wargame.dtx	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/wargame.dtx	2024-11-19 20:38:26 UTC (rev 72903)
@@ -473,6 +473,12 @@
 % \item[\spec{code=}\meta{tikz-code}] Any valid \TikZ{} code
 % \item[\spec{clip=}\meta{path(s)}] \TikZ{} path specification to clip
 %   the terrain within the hex.
+% \item[\spec{random rotation}] Applies a random rotation to the
+%   terrain image or picture.  This will create an effect where the
+%   hexes look less alike. 
+% \item[\spec{rotate}=\meta{angle}] Applies a rotation by \meta{angle}
+%   to the terrain image or picture.  Note that the \meta{angle}
+%   should most likely be multiples of $60^{\circ}$. 
 % \end{description}
 %   
 % \begin{table}[htbp]
@@ -493,25 +499,31 @@
 %     & \texttt{}
 %     & \tikz{\hex[terrain={image=wargame.beach}](r=0,c=0) }
 %     & Beach
-%     & \texttt{\{image=wargame.beach\}}\\
+%     & \texttt{\{beach\}}\\
 %     \tikz{\hex[terrain={image=wargame.light_woods}](r=0,c=0) }
 %     & Light woods
-%     & \texttt{\{image=wargame.light\_woods\}}
+%     & \texttt{\{light\_woods\}}
 %     & \tikz{\hex[terrain={image=wargame.woods}](r=0,c=0)}
 %     & Woods
-%     & \texttt{\{image=wargame.woods\}}\\
+%     & \texttt{\{woods\}}\\
 %     \tikz{\hex[terrain={image=wargame.rough}](r=0,c=0) }
 %     & Rough
-%     & \texttt{\{image=wargame.rough\}}
+%     & \texttt{\{rough\}}
 %     & \tikz{\hex[terrain={image=wargame.swamp}](r=0,c=0) }
 %     & Swamp
-%     & \texttt{\{image=wargame.swamp\}}\\
+%     & \texttt{\{swamp\}}\\
 %     \tikz{\hex[terrain={image=wargame.mountains}](r=0,c=0) }
 %     & Mountains
-%     & \texttt{\{image=wargame.mountains\}}
+%     & \texttt{\{mountains\}}
 %     & \tikz{\hex[fill=blue!40!white](r=0,c=0) }
 %     & Sea
-%     & \texttt{\{image=wargame.sea\}}\\
+%     & \texttt{\{sea\}}\\
+%     \tikz{\hex[terrain={image=wargame.fields},fill=yellow](r=0,c=0) }
+%     & Fields
+%     & \texttt{\{fields\},fill=yellow}
+%     &\tikz{\hex[terrain={image=wargame.speckle},fill=hostile!25!white](r=0,c=0) }
+%     & Fields
+%     & \texttt{\{speckle\},fill=hostile}\\
 %     \hline
 %   \end{tabular}
 %   \caption{Terrains specified via tile images}
@@ -1539,6 +1551,148 @@
 % \cs{chit}.  Note that we \emph{must} give the full path to the
 % \spec{chit} keys when defining a style like this. 
 %
+% \subsection{Kriegspiel chits}
+%
+% By passing the option \texttt{kriegspiel} to the \cs{chit} command
+% or in a \texttt{chit} node, the shape of the chits will be changed
+% from square to rectangular.
+%
+% \paragraph{Important} Not all NATO symbols have been adapted to
+% facilitate that shape.
+%
+% Here are some examples
+%
+% \begin{tikzpicture}[
+%   chit/factor/.append style={font=\sffamily\bfseries\small},
+%    /chit/.cd,
+%    hex/.style={lower right={#1}},
+%    turn/.style={lower left={#1}}]
+%    \chit[symbol={
+%        main=infantry,
+%        echelon=corps,
+%        faction=friendly,
+%        command=land},
+%      factors={chit/2 factors={4,3}},
+%      parent={chit/small identifier={II}},
+%      unique={chit/small identifier={4}},
+%      turn={chit/small identifier={2}},
+%      hex={chit/small identifier={0120}},
+%      kriegspiel,
+%      color=black,
+%      fill=hostile](0,0);
+%   \chit[symbol={
+%        main={[fill=pgfstrokecolor]artillery},%
+%        echelon=corps,
+%        faction=friendly,
+%        command=land},
+%      factors={chit/2 factors artillery={4,3,2}},
+%      parent={chit/small identifier={II}},
+%      unique={chit/small identifier={4}},
+%      turn={chit/small identifier={2}},
+%      hex={chit/small identifier={0120}},
+%      kriegspiel,
+%      color=black,
+%      fill=friendly,
+%      kriegspiel
+%    ](2.5,0);
+%    \chit[
+%       frame={black},
+%       symbol={
+%         main=reconnaissance,
+%         echelon=corps,
+%         command=land,
+%         faction=friendly,
+%         color=black,
+%         fill=hostile,
+%       },
+%       factors={chit/2 factors={4,3}},
+%       parent={chit/small identifier={II}},
+%       unique={chit/small identifier={4}},
+%       turn={chit/small identifier={2}},
+%       hex={chit/small identifier={0120}},
+%       color=white,
+%       fill=unknown!50!black,
+%       kriegspiel](5,0);
+%   \chit[
+%       frame={black},
+%       symbol={
+%         main={reconnaissance artillery},
+%         echelon=corps,
+%         command=land,
+%         faction=friendly,
+%         fill=friendly,
+%       },
+%       factors={chit/2 factors artillery={4,3,2}},
+%       parent={chit/small identifier={II}},
+%       unique={chit/small identifier={4}},
+%       turn={chit/small identifier={2}},
+%       color=white,
+%       fill=neutral!50!black,
+%       kriegspiel](7.5,0);
+% \end{tikzpicture}
+%
+%
+% These are produced by
+%
+% \begin{verbatim}
+% \begin{tikzpicture}[
+%   chit/factor/.append style={font=\sffamily\bfseries\small},
+%    /chit/.cd,
+%    hex/.style={lower right={#1}},
+%    turn/.style={lower left={#1}}]
+%    \chit[symbol={main=infantry,echelon=corps,faction=friendly,command=land},
+%      factors={chit/2 factors={4,3}},
+%      parent={chit/small identifier={II}},
+%      unique={chit/small identifier={4}},
+%      turn={chit/small identifier={2}},
+%      hex={chit/small identifier={0120}},
+%      kriegspiel,
+%      color=black,
+%      fill=hostile](0,0);
+%   \chit[symbol={
+%        main={[fill=pgfstrokecolor]artillery},%
+%        echelon=corps,faction=friendly,command=land},
+%      factors={chit/2 factors artillery={4,3,2}},
+%      parent={chit/small identifier={II}},
+%      unique={chit/small identifier={4}},
+%      turn={chit/small identifier={2}},
+%      hex={chit/small identifier={0120}},
+%      kriegspiel,
+%      color=black,
+%      fill=friendly,
+%      kriegspiel
+%    ](2.5,0);
+%    \chit[
+%       frame={black},
+%       symbol={
+%         main=reconnaissance, echelon=corps,command=land,faction=friendly,
+%         color=black,
+%         fill=hostile,
+%       },
+%       factors={chit/2 factors={4,3}},
+%       parent={chit/small identifier={II}},
+%       unique={chit/small identifier={4}},
+%       turn={chit/small identifier={2}},
+%       hex={chit/small identifier={0120}},
+%       color=white,
+%       fill=unknown!50!black,
+%       kriegspiel](5,0);
+%   \chit[
+%       frame={black},
+%       symbol={
+%         main={reconnaissance artillery},
+%         echelon=corps,command=land,faction=friendly,fill=friendly,
+%       },
+%       factors={chit/2 factors artillery={4,3,2}},
+%       parent={chit/small identifier={II}},
+%       unique={chit/small identifier={4}},
+%       turn={chit/small identifier={2}},
+%       color=white,
+%       fill=neutral!50!black,
+%       kriegspiel](7.5,0);
+% \end{tikzpicture}
+% \end{verbatim}
+% 
 % \iffalse
 % --------------------------------------------------------------------
 %
@@ -1650,7 +1804,7 @@
 %   
 % \makeatletter
 % \def\tabledata{}
-% \foreach \c in {air,land,equipment,installation,sea surface,sub surface,space,activity}{%
+% \foreach \c in {air,land,equipment,installation,sea surface,sub surface,space,activity,dismounted}{%
 %   \protected at xappto\tabledata{\spec{\c}}
 %   \foreach \f in {friendly,hostile,neutral,unknown}{%
 %     \protected at xappto\tabledata{

Modified: trunk/Master/texmf-dist/source/latex/wargame/wargame.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/wargame/wargame.ins	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/source/latex/wargame/wargame.ins	2024-11-19 20:38:26 UTC (rev 72903)
@@ -99,6 +99,8 @@
     \from{hex/shape.dtx}              {hex}
     \from{hex/terrain.dtx}            {hex}
     \from{hex/terrain/beach.dtx}      {hex}
+    \from{hex/terrain/fields.dtx}     {hex}
+    \from{hex/terrain/speckle.dtx}    {hex}
     \from{hex/terrain/light_woods.dtx}{hex}
     \from{hex/terrain/woods.dtx}      {hex}
     \from{hex/terrain/swamp.dtx}      {hex}
@@ -153,6 +155,8 @@
   \file{wargame.town.tex}	{\from{hex/tile.dtx}{tile,town}}
   \file{wargame.village.tex}	{\from{hex/tile.dtx}{tile,village}}
   \file{wargame.woods.tex}	{\from{hex/tile.dtx}{tile,woods}}
+  \file{wargame.fields.tex}	{\from{hex/tile.dtx}{tile,fields}}
+  \file{wargame.speckle.tex}	{\from{hex/tile.dtx}{tile,speckle}}
   %
   \file{testmap.tex}    {\from{tests/map.dtx}{testmap}}
   \file{testchits.tex}  {\from{tests/chits.dtx}{testchits}}

Modified: trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.chit.code.tex	2024-11-19 20:38:26 UTC (rev 72903)
@@ -78,10 +78,12 @@
   bevel/SW/.style         = {/chit/bev=3},
   bevel/SE/.style         = {/chit/bev=4},
   bevel/.default          = north west,
-  clip/.is if=chit at clip%
+  clip/.is if=chit at clip,%
+  nato shape/.store in=\chit at n@to at shape,
 }
+\def\chit at sym@sc at le{.4}
 \tikzset{
-  chit/symbol/.style={scale=.4,transform shape},
+  chit/symbol/.style={scale=\chit at sym@sc at le,transform shape},
   chit/parts/.style={shape=rectangle,transform shape},
   chit/factors/.style={chit/parts,anchor=south},
   chit/left/.style={chit/parts,anchor=base,rotate=90},%Anchor was south
@@ -118,7 +120,7 @@
   chit/.code={%
     \chit at dbg{2}{chit arguments are `#1'}%
     \pgfkeys{/tikz/transform shape,/tikz/shape=chit}%
-    \pgfkeys{/chit/.cd,#1}}}
+    \pgfkeys{/chit/.cd,nato shape=natoapp6c,#1}}}
 \newcounter{chit at id}\setcounter{chit at id}{0}
 \def\chit at n@to#1#2{%
   %% Without a following start square bracket '[' by-pass to final
@@ -129,12 +131,16 @@
     %\message{^^JNo start square bracket}%
     \@chit at n@to@{#1}{#2}}%]]
 }
+\def\chit at n@to at shape{natoapp6c}
 \def\@chit at n@to@#1#2#3\@end at chit@n at to{%
   \chit at dbg{1}{Chit NATO App6(c) w/o offset:
-    ^^J  Options:  #3
-    ^^J  ID:       #1
-    ^^J  Position: #2}
-  \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at (#2) {};
+    ^^J  Options:  `#3'
+    ^^J  ID:       `#1'
+    ^^J  Position: `#2'
+    ^^J  Style:    `\chit at n@to at shape'
+  }
+  \chit at dbg{2}{NATO shape: `\chit at n@to at shape'}
+  \node[chit/symbol,\chit at n@to at shape={#3,id=#1}] (#1) at (#2) {};
   \chit at dbg{4}{Chit NATO App6(c) ended}%
 }
 \def\@chit at n@to#1#2[#3]{%
@@ -147,30 +153,73 @@
     ^^J  ID:       #1
     ^^J  Position: #2
     ^^J  Offset:   #4}
-  \node[chit/symbol,natoapp6c={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};}
-\def\chit at tr@ns at nchor#1{%
-  \pgf at x=0.4\pgf at x%
-  \pgf at y=0.4\pgf at y\advance\pgf at y#1}
-\def\chit at nchor#1#2#3{%
-  \wg at sub@nchor{#1}{#2}
-  \chit at tr@ns at nchor{#3}}
+  \chit at dbg{10}{==== NATO shape: `\chit at n@to at shape' ====}
+  \node[chit/symbol,\chit at n@to at shape={#3,id=#1}] (#1) at ($(#2)+(#4)$) {};}
+\def\chit at tr@ns at nchor{%
+  \chit at dbg{10}{Translating anchor `\the\pgf at x',`\the\pgf at y'}
+  \wg at tmpa=\pgf at x%
+  \wg at tmpb=\pgf at y%
+  \symbol%
+  \chit at dbg{10}{Symbol origin `\the\pgf at x',`\the\pgf at y'}
+  \wg at tmpc=\pgf at x%
+  \wg at tmpd=\pgf at y%
+  \pgf at x=\chit at sym@sc at le\wg at tmpa%
+  \pgf at y=\chit at sym@sc at le\wg at tmpb%
+  \chit at dbg{10}{Scaled anchor `\the\pgf at x',`\the\pgf at y'}
+  \advance\pgf at x\wg at tmpc%
+  \advance\pgf at y\wg at tmpd%
+  \chit at dbg{10}{Offset anchor `\the\pgf at x',`\the\pgf at y'}
+}
+\def\chit at nchor#1#2{%
+  \chit at dbg{10}{Get chit sub anchor of `#1' `#2'}%
+  \wg at sub@nchor{#1}{#2}%
+  \chit at tr@ns at nchor%
+  \chit at dbg{10}{Got chit sub anchor of `\the\pgf at x `\the\pgf at y}%
+}
+\def\chit at sym@nchor#1{%
+  \chit at dbg{10}{Get chit symbol `#1'}%
+  \edef\tmpid{\id symbol}%
+  \chit at nchor{\tmpid}{#1}}
 \def\chit at report{}
 \tikzset{
   zone turn/.style={},
   zone mult/.style={}
 }
+\def\chit at bkg@p at th{%
+  \northeast%
+  \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+  \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+  \pgfclosepath%
+}
 \def\chit at bevel@path#1{
   \scope[#1]
-  \wg at tmpc=\wg at tmpa\multiply\wg at tmpc by \chit at bevel@frac
-  \wg at tmpd=\wg at tmpb\multiply\wg at tmpd by \chit at bevel@frac
-  \divide\wg at tmpc100
-  \divide\wg at tmpd100
+  \wg at tmpc=\wg at tmpa%
+  \wg at tmpd=\wg at tmpb%
+  %% Absolute values
+  \ifdim\wg at tmpa<0pt\multiply\wg at tmpc by -1\fi%
+  \ifdim\wg at tmpb<0pt\multiply\wg at tmpd by -1\fi%
+  % Why the hell do I need this?
+  \chit at dbg{10}{`\the\wg at tmpa' `\the\wg at tmpb'}%
+  %% Smallest dimension
+  \ifdim\wg at tmpc>\wg at tmpd \wg at tmpc=\wg at tmpd\fi%
+  \ifdim\wg at tmpc<\wg at tmpd \wg at tmpd=\wg at tmpc\fi%
+  %% Restore sign
+  \ifdim\wg at tmpa<0pt\multiply\wg at tmpc by -1\fi%
+  \ifdim\wg at tmpb<0pt\multiply\wg at tmpd by -1\fi%
+  %% Take the fraction
+  \multiply\wg at tmpc by \chit at bevel@frac%
+  \multiply\wg at tmpd by \chit at bevel@frac%
+  \divide\wg at tmpc100%
+  \divide\wg at tmpd100%
   \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move down along edge
-  \wg at tmpb=-\wg at tmpb
+  \wg at tmpb=-\wg at tmpb%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move left along edge
-  \wg at tmpa=-\wg at tmpa
+  \wg at tmpa=-\wg at tmpa%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   % Move in and up
   \advance\wg at tmpa\wg at tmpc%
@@ -185,14 +234,13 @@
   \advance\wg at tmpb-\wg at tmpd%
   \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
   \pgfclosepath%
-  \pgfusepath{fill}
-  \endscope
+  \pgfusepath{fill}%
+  \endscope%
 }
-
 \pgfdeclareshape{chit}{
   \savedanchor\center{\pgf at x=0cm\pgf at y=0cm}
-  \savedanchor\northeast{\pgf at x=0.6cm\pgf at y=0.6cm}
-  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.2cm}
+  \savedanchor\northeast{\pgf at x=0.6cm\pgf at y=\pgf at x}
+  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.1cm}
   \savedanchor\factors{\pgf at x=0cm\pgf at y=-0.5cm}
   \saveddimen\margin{\pgf at x=0.04cm}
   \savedmacro\id{%
@@ -212,6 +260,7 @@
       \edef\chitframeopt{\chit at frame}}
     \chit at dbg{3}{Chit Frame options: \meaning\chitframeopt}%
   }
+  \savedmacro\thisname{\def\thisname{chit}}
   \anchor{center}{\center}
   \anchor{north east}{\northeast}
   \anchor{north west}{\northeast\pgf at x=-\pgf at x}
@@ -221,24 +270,24 @@
   \anchor{south}     {\northeast\pgf at x=0cm\pgf at y=-\pgf at y}
   \anchor{east}      {\northeast\pgf at y=0cm}
   \anchor{west}      {\northeast\pgf at x=-\pgf at x\pgf at y=0cm}
-  \anchor{symbol north east}{\chit at nchor{M\id symbol}{north east}{0.2cm}}
-  \anchor{symbol north west}{\chit at nchor{M\id symbol}{north west}{0.2cm}}
-  \anchor{symbol south east}{\chit at nchor{M\id symbol}{south east}{0.2cm}}
-  \anchor{symbol south west}{\chit at nchor{M\id symbol}{south west}{0.2cm}}
-  \anchor{symbol north}     {\chit at nchor{M\id symbol}{north}{0.2cm}}
-  \anchor{symbol west}      {\chit at nchor{M\id symbol}{west}{0.2cm}}
-  \anchor{symbol south}     {\chit at nchor{M\id symbol}{south}{0.2cm}}
-  \anchor{symbol east}      {\chit at nchor{M\id symbol}{east}{0.2cm}}
-  \anchor{symbol upper}     {\chit at nchor{M\id symbol}{upper}{0.2cm}}
-  \anchor{symbol lower}     {\chit at nchor{M\id symbol}{lower}{0.2cm}}
-  \anchor{symbol left}      {\chit at nchor{M\id symbol}{left}{0.2cm}}
-  \anchor{symbol right}     {\chit at nchor{M\id symbol}{right}{0.2cm}}
-  \anchor{symbol echelon}   {\chit at nchor{M\id symbol}{north}{0.2cm}}
-  \anchor{symbol below}     {\chit at nchor{M\id symbol}{south}{0.1cm}}
+  \anchor{symbol north east}{\chit at sym@nchor{north east}}
+  \anchor{symbol north west}{\chit at sym@nchor{north west}}
+  \anchor{symbol south east}{\chit at sym@nchor{south east}}
+  \anchor{symbol south west}{\chit at sym@nchor{south west}}
+  \anchor{symbol north}     {\chit at sym@nchor{north}}
+  \anchor{symbol west}      {\chit at sym@nchor{west}}
+  \anchor{symbol south}     {\chit at sym@nchor{south}}
+  \anchor{symbol east}      {\chit at sym@nchor{east}}
+  \anchor{symbol upper}     {\chit at sym@nchor{upper}}
+  \anchor{symbol lower}     {\chit at sym@nchor{lower}}
+  \anchor{symbol left}      {\chit at sym@nchor{left}}
+  \anchor{symbol right}     {\chit at sym@nchor{right}}
+  \anchor{symbol echelon}   {\chit at sym@nchor{echelon}}
+  \anchor{symbol below}     {\chit at sym@nchor{below}}
   \anchor{symbol}    {\symbol}
   \anchor{factors}   {\factors}
-  \anchor{left} {\chit at nchor{M\id symbol}{west}{.2cm}\advance\pgf at x-\margin}
-  \anchor{right}{\chit at nchor{M\id symbol}{east}{.2cm}\advance\pgf at x+\margin}
+  \anchor{left} {\chit at sym@nchor{west}\advance\pgf at x-\margin}
+  \anchor{right}{\chit at sym@nchor{east}\advance\pgf at x+\margin}
   \anchor{upper right} {%
     \northeast \advance\pgf at x-\margin \advance\pgf at y-\margin%
   }
@@ -256,13 +305,14 @@
     %% This is the outline of the chit only.  The rest of the chit is
     %% made on the foreground "path".
     \chit at dbg{1}{Chit drawing background path}
-    \northeast%
-    \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
-    \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-    \pgfclosepath
+    \chit at bkg@p at th%
+    % \northeast%
+    % \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+    % \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+    % \pgfclosepath
   }
   \behindforegroundpath{%
     \chit at dbg{1}{Chit drawing foreground path}
@@ -288,13 +338,14 @@
     %
     \ifchit at clip%
       \chit at dbg{1}{Chit clip path}
-      \northeast%
-      \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
-      \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
-      \pgfclosepath%
+      \chit at bkg@p at th%
+      % \northeast%
+      % \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+      % \pgfpathmoveto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpb=-\wg at tmpb \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \wg at tmpa=-\wg at tmpa \pgfpathlineto{\pgfqpoint{\wg at tmpa}{\wg at tmpb}}%
+      % \pgfclosepath%
       \pgfusepath{clip}%
     \fi%
     \@ifundefined{chit at symbol}{%
@@ -309,26 +360,22 @@
       \symbol%
       \edef\args{{\symid}{\the\pgf at x,\the\pgf at y}\chit at symbol}%
       \chit at dbg{6}{Arguments to chit NATO symbol: \meaning\args}%
-      \chit at dbg{1}{Chit draw nato image}
+      \chit at dbg{1}{Chit draw nato image `\symid'}
       \expandafter\chit at n@to\args\@end at chit@n at to%
       \chit at dbg{6}{After making NATO symbol in chit}%
       % Put in left of symbol
       \@ifundefined{chit at left}{}{%
-        \chit at dbg{1}{Chit draw left: `\meaning\chit at left'}
+        \chit at dbg{2}{Chit draw left: `\meaning\chit at left'}
+        \wg at nchor{\thisname}{left}
         \begin{scope}[]
-          \pgfpointanchor{\symid}{west}%
-          \wg at tmpa=\pgf at x\advance\wg at tmpa-\margin%
-          \wg at tmpb=\pgf at y%
-          \wg at pic@all{\chit at left}{}{\the\wg at tmpa,\the\wg at tmpb}{chit/left}%
+          \wg at pic@all{\chit at left}{}{\pgf at x,\pgf at y}{chit/left}%
         \end{scope}}%
       % Put in right of symbol
       \@ifundefined{chit at right}{}{%
-        \chit at dbg{1}{Chit draw left: `\meaning\chit at right'}
+        \chit at dbg{2}{Chit draw right: `\meaning\chit at right'}
+        \wg at nchor{\thisname}{right}
         \begin{scope}[]
-          \pgfpointanchor{\symid}{east}%
-          \wg at tmpa=\pgf at x\advance\wg at tmpa+\margin%
-          \wg at tmpb=\pgf at y%
-          \wg at pic@all{\chit at right}{}{\the\wg at tmpa,\the\wg at tmpb}{chit/right}%
+          \wg at pic@all{\chit at right}{}{\pgf at x,\pgf at y}{chit/right}%
         \end{scope}}%
       % Get coordinates
       \northeast%
@@ -338,31 +385,31 @@
       \advance\wg at tmpb-\margin%
       % Put in upper left corner
       \@ifundefined{chit at upper@left}{}{%
-        \chit at dbg{1}{Chit draw upper left: `\meaning\chit at upper@left'}
+        \chit at dbg{1}{Chit draw upper left: `\meaning\chit at upper@left'}%
+        \wg at nchor{\thisname}{upper left}
         \begin{scope}[]
-          \wg at pic@all{\chit at upper@left}{}{-\the\wg at tmpa,\the\wg at tmpb}{%
-            chit/upper left}%
+          \wg at pic@all{\chit at upper@left}{}{\pgf at x,\pgf at y}{chit/upper left}%
         \end{scope}}
       % Put in upper right corner
       \@ifundefined{chit at upper@right}{}{%
         \chit at dbg{1}{Chit draw upper right: `\meaning\chit at upper@right'}
+        \wg at nchor{\thisname}{upper right}%
         \begin{scope}[]
-          \wg at pic@all{\chit at upper@right}{}{\the\wg at tmpa,\the\wg at tmpb}{%
-            chit/upper right}%
+          \wg at pic@all{\chit at upper@right}{}{\pgf at x,\pgf at y}{chit/upper right}%
         \end{scope}}
       % Put in lower left corner
       \@ifundefined{chit at lower@left}{}{%
         \chit at dbg{1}{Chit draw lower left: `\meaning\chit at lower@left'}
+        \wg at nchor{\thisname}{lower left}%
         \begin{scope}[]
-          \wg at pic@all{\chit at lower@left}{}{-\the\wg at tmpa,-\the\wg at tmpb}{%
-            chit/lower left}%
+          \wg at pic@all{\chit at lower@left}{}{\pgf at x,\pgf at y}{chit/lower left}%
         \end{scope}}
       % Put in lower right corner
       \@ifundefined{chit at lower@right}{}{%
         \chit at dbg{1}{Chit draw lower right: `\meaning\chit at lower@right'}
+        \wg at nchor{\thisname}{lower right}%
         \begin{scope}[]
-          \wg at pic@all{\chit at lower@right}{}{\the\wg at tmpa,-\the\wg at tmpb}{%
-            chit/lower right}%
+          \wg at pic@all{\chit at lower@right}{}{\pgf at x,\pgf at y}{chit/lower right}%
         \end{scope}}
       % Put in factors
       \@ifundefined{chit at factors}{}{%
@@ -451,10 +498,83 @@
   \fi%
   \@ifnextchar;{\@gobble}{}%
 }
+\tikzset{
+  /chit/kriegspiel/.code={
+    \pgfkeys{%
+      /tikz/shape=kriegspiel,%
+      /chit/nato shape=kriegspiel symbol}
+  }
+}
+\pgfdeclareshape{kriegspiel}{
+  \inheritsavedanchors[from=chit]
+  \savedanchor\northeast{\pgf at x=0.8cm\pgf at y=0.4cm}
+  \savedanchor\symbol{\pgf at x=0cm\pgf at y=0.15cm}
+  \savedanchor\factors{\pgf at x=.2cm\pgf at y=-.4cm}
+  \savedmacro\thisname{\def\thisname{kriegspiel}}
+  \saveddimen\margin{\pgf at x=0.03cm}
+  \inheritanchor[from=chit]{center}
+  \inheritanchor[from=chit]{north east}
+  \inheritanchor[from=chit]{north west}
+  \inheritanchor[from=chit]{south west}
+  \inheritanchor[from=chit]{south east}
+  \inheritanchor[from=chit]{north}
+  \inheritanchor[from=chit]{south}
+  \inheritanchor[from=chit]{east}
+  \inheritanchor[from=chit]{west}
+  \inheritanchor[from=chit]{symbol north east}
+  \inheritanchor[from=chit]{symbol north west}
+  \inheritanchor[from=chit]{symbol south east}
+  \inheritanchor[from=chit]{symbol south west}
+  \inheritanchor[from=chit]{symbol north}
+  \inheritanchor[from=chit]{symbol west}
+  \inheritanchor[from=chit]{symbol south}
+  \inheritanchor[from=chit]{symbol east}
+  \inheritanchor[from=chit]{symbol upper}
+  \inheritanchor[from=chit]{symbol lower}
+  \inheritanchor[from=chit]{symbol left}
+  \inheritanchor[from=chit]{symbol right}
+  \inheritanchor[from=chit]{symbol echelon}
+  \inheritanchor[from=chit]{symbol below}
+  \inheritanchor[from=chit]{symbol}
+  \inheritanchor[from=chit]{factors}
+  \inheritanchor[from=chit]{left}
+  \inheritanchor[from=chit]{right}
+  \inheritanchor[from=chit]{upper right}
+  \inheritanchor[from=chit]{upper left}
+  \inheritanchor[from=chit]{lower right}
+  \inheritanchor[from=chit]{lower left}
+  % \anchor{upper right} {%
+  %   \northeast%
+  %   \advance\pgf at x-\margin%
+  %   \advance\pgf at y-\margin%
+  %   \pgf at y=-\pgf at y%
+  %   \advance\pgf at y5pt%
+  % }
+  % \anchor{upper left}{
+  %   \northeast%
+  %   \advance\pgf at x-\margin%
+  %   \advance\pgf at y-\margin%
+  %   \pgf at x=-\pgf at x%
+  %   \pgf at y=-\pgf at y%
+  %   \advance\pgf at y5pt%
+  % }
+  %%
+  \inheritbackgroundpath[from=chit]
+  \inheritbehindforegroundpath[from=chit]
+}
 \DeclareRobustCommand\chit at sep[2][/]{%
   \foreach[count=\is] \s in {#2}{%
     \ifnum\is>1\relax#1\fi%
     \s}}
+\def\chit at oset#1#2{%
+  %$\stackrel{{}_{\text{\scriptsize #1}}}{\text{#2}}$%
+  %$\stackrel{\lower2ex\hbox{\text{\scriptsize #1}}}{\text{#2}}$%
+  %${}\overset{\text{\scriptsize #1}}{\text{#2}}{}$%
+  \tikz[]{%
+    \node[inner sep=0pt](chit at dd){#2};%
+    \node[above=-.1ex of chit at dd,inner sep=0pt]{{\scriptsize #1}};%
+  }%
+}
 \tikzset{%
   chit/1 factor/.pic={
     \chit at dbg{4}{ Chit 1 factor: #1}%
@@ -467,7 +587,7 @@
     code={
       \chit at dbg{4}{ Chit 2 factors w/artillery: `#1' `#2' `#3'}%
       \node[chit/factor,chit/2 factors]{%
-        {#1}$\overset{\text{\scriptsize #3}}{\text{--}}${#2}};}},
+        {#1}\chit at oset{#3}{--}{#2}};}},
   pics/chit/3 factors/.style args={#1,#2,#3}{%
     code={
       \chit at dbg{4}{ Chit 3 factors: `#1' `#2' `#3'}%
@@ -626,28 +746,28 @@
   \@ifstar{\wg at star@oob%
   }{\wg at nostar@oob%
   }%
-}
-\def\wg at oob#1#2#3#4{
-  \def\r{0}
+}%
+\def\wg at oob#1#2#3#4{%
+  \def\r{0}%
   \pgfmathparse{#3*(#2-1)}%
-  \edef\a{\pgfmathresult}
-  \chit at dbg{2}{OOB: `#1'}
-  \foreach[count=\ti from 0] \t/\y in #1{
-    \xdef\o{\r}
-    \def\c{0}
-    \ifx\t\y\def\y{0}\fi
-    \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}
+  \edef\a{\pgfmathresult}%
+  \chit at dbg{2}{OOB: `#1'}%
+  \foreach[count=\ti from 0] \t/\y in #1{%
+    \xdef\o{\r}%
+    \def\c{0}%
+    \ifx\t\y\def\y{0}\fi%
+    \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}%
     \ifwg at oob@inv%
       \pic[transform shape] at ( .5*#3,\r) {chit/oob turn=\ti};% was dx=0.5
-    \else
+    \else%
       \pic[transform shape] at (-.5*#3,\r) {chit/oob turn=\ti};% was dx=-0.5
     \fi%
     \ifx\t\empty\else%
-      \foreach \u/\m in \t{
+      \foreach \u/\m in \t{%
         %% \chit at dbg{2}{ `\u'=`\m'}
-        \ifx\u\empty\else
-          \ifx\m\@empty\def\m{1}\fi
-          \ifx\u\m\def\m{1}\fi
+        \ifx\u\empty\else%
+          \ifx\m\@empty\def\m{1}\fi%
+          \ifx\u\m\def\m{1}\fi%
           \foreach \n in {1,...,\m}{%
             \chit at dbg{2}{OOB Chit is `\u' `\chit at oob@spacer'}%
             \ifx\u\chit at oob@spacer%
@@ -656,33 +776,33 @@
               \xdef\c{\pgfmathresult}%
             \else%
               \ifx\u\chit at oob@vspacer%
-                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}
-                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}
-                \xdef\ll{\pgfmathresult}
-                \chit at dbg{2}{\string\ll=`\ll'}
-                \chit at oob@rowupdate(\c,\r){\ll}{#4}
+                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}%
+                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}%
+                \xdef\ll{\pgfmathresult}%
+                \chit at dbg{2}{\string\ll=`\ll'}%
+                \chit at oob@rowupdate(\c,\r){\ll}{#4}%
               \else
                 \ifnum\chitdbglvl>2%
                   \node[minimum width=#3cm,minimum height=#3cm,
-                        draw,transform shape] at (\c,\r) {};
+                        draw,transform shape] at (\c,\r) {};%
                 \fi
                 \ifx\u\chit at blank\else%
                   \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);%
                 \fi%
-                \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
-              \fi
-            \fi
-          }
-        \fi
-      }
-    \fi
+                \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}%
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
     \chit at dbg{1}{ End of chits in turn
-      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
+      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
     % IF no units where given, then we force \c to be non-zero so that
     % \chit at oob@turnupdate increments the row
-    \ifx\t\@empty
-      \def\c{#3}
-      \chit at dbg{2}{ Turn is empty, set c=`\c'}
+    \ifx\t\@empty%
+      \def\c{#3}%
+      \chit at dbg{2}{ Turn is empty, set c=`\c'}%
     \fi
     %\ifnum\y<0% No explicit number of rows given
     %  \def\c{#3}
@@ -696,7 +816,7 @@
     % rows.
     \ifnum\y>0%
       \chit at dbg{2}{ Looping rows from 2 to \y, break when row > \y}%
-      \foreach \rr  in {2,...,\y}{
+      \foreach \rr  in {2,...,\y}{%
         %\ifnum\rr>\y% A little funny, but \y can be negative!
         %  \chit at dbg{2}{ \space Breaking loop \rr\space > \y}%
         %  \breakforeach%
@@ -703,15 +823,15 @@
         %\else%
           \chit at oob@rowupdate(\c,\r){#3}{0}% Extra spacing?
         %\fi
-      }
-    \fi
+      }%
+    \fi%
     % This will zero \c.  However, if on entry |\c|>0, then we also
     % increment the row
-    \chit at oob@turnupdate(\c,\r){#3}{#4}
-    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
+    \chit at oob@turnupdate(\c,\r){#3}{#4}%
+    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
   }
-  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}
-  \@ifnextchar;{\@gobble}{}}
+  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}%
+  \@ifnextchar;{\@gobble}{}}%
 \def\wg at star@hoob{\wg at oob@invtrue\wg at hoob}
 \def\wg at nostar@hoob{\wg at oob@invfalse\wg at hoob}
 \def\hoob{%
@@ -719,49 +839,49 @@
   }{\wg at nostar@hoob%
   }%
 }
-\def\wg at hoob#1#2#3#4{
-  \def\r{0}
-  \def\c{0}
+\def\wg at hoob#1#2#3#4{%
+  \def\r{0}%
+  \def\c{0}%
   \pgfmathparse{#3*(#2-1)}%
-  \edef\a{\pgfmathresult}
-  \chit at dbg{2}{OOB: `#1'}
-  \foreach[count=\ti from 0] \t/\y in #1{
-    \xdef\o{\r}
+  \edef\a{\pgfmathresult}%
+  \chit at dbg{2}{OOB: `#1'}%
+  \foreach[count=\ti from 0] \t/\y in #1{%
+    \xdef\o{\r}%
     % \def\c{0}
-    \ifx\t\y\def\y{0}\fi
+    \ifx\t\y\def\y{0}\fi%
     \chit at dbg{2}{Turn \ti\space(\r,\t,y=\y):'}
-    \ifx\t\empty\else
+    \ifx\t\empty\else%
       % Count how many are left for this turn
-      \chit at dbg{2}{At start of turn \t\space\string\c=\c}
+      \chit at dbg{2}{At start of turn \t\space\string\c=\c}%
       \def\l{\c}%
-      \let\ig\empty
-      \foreach \u/\m in \t{
-        \ifx\ig\empty
-          \ifx\u\empty\else
-            \ifx\u\m\def\m{1}\fi
+      \let\ig\empty%
+      \foreach \u/\m in \t{%
+        \ifx\ig\empty%
+          \ifx\u\empty\else%
+            \ifx\u\m\def\m{1}\fi%
             \ifx\u\chit at oob@spacer%
-              \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult}
-              \chit at dbg{2}{Got \m\space hspace (#4) -> \l}
-            \else
+              \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult}%
+              \chit at dbg{2}{Got \m\space hspace (#4) -> \l}%
+            \else%
               \ifx\u\chit at oob@vspace%
-                \xdef\ig{1}
-                \chit at dbg{2}{Got vspace -> \l (\ig)}
-              \else
-                \pgfmathparse{\l+\m*#3}
-                \xdef\l{\pgfmathresult}
-                \chit at dbg{2}{Got \m\space units -> \l}
-              \fi
-            \fi
-          \fi
-        \fi}
+                \xdef\ig{1}%
+                \chit at dbg{2}{Got vspace -> \l (\ig)}%
+              \else%
+                \pgfmathparse{\l+\m*#3}%
+                \xdef\l{\pgfmathresult}%
+                \chit at dbg{2}{Got \m\space units -> \l}%
+              \fi%
+            \fi%
+          \fi%
+        \fi}%
       % Check if there's enough room
       \chit at dbg{2}{To fill the rest of turn needs `\l' compared to
-        `\a' (#3*(#2-1))}
+        `\a' (#3*(#2-1))}%
       \pgfmathparse{ifthenelse(abs(\l)>=#3*(#2-1),0,1}%
       \xdef\l{\pgfmathresult}%
-      \chit at dbg{2}{Break or not `\l'}
-      \ifnum\l=0\chit at oob@turnupdate(\c,\r){#3}{#4}\fi
-    \fi
+      \chit at dbg{2}{Break or not `\l'}%
+      \ifnum\l=0\chit at oob@turnupdate(\c,\r){#3}{#4}\fi%
+    \fi%
     \ifwg at oob@inv%
       \pic[transform shape] at (\c+.5*#3,\r) {chit/oob turn=\ti};% was dx=0.5
     \else
@@ -769,43 +889,43 @@
     \fi%
     %\chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
     \ifx\t\empty\else%
-      \def\lv{0}
-      \foreach \u/\m in \t{
+      \def\lv{0}%
+      \foreach \u/\m in \t{%
         %% \chit at dbg{2}{ `\u'=`\m'}
-        \ifx\u\empty\else
-          \ifx\m\@empty\def\m{1}\fi
-          \ifx\u\m\def\m{1}\fi
+        \ifx\u\empty\else%
+          \ifx\m\@empty\def\m{1}\fi%
+          \ifx\u\m\def\m{1}\fi%
           \foreach \n in {1,...,\m}{%
             \chit at dbg{2}{OOB Chit is `\u' `\chit at oob@spacer'}%
             \ifx\u\chit at oob@spacer%
-              \chit at dbg{3}{Chit `\u' is spacer `\chit at oob@spacer'}
+              \chit at dbg{3}{Chit `\u' is spacer `\chit at oob@spacer'}%
               \pgfmathparse{\c+#4}%
               \xdef\c{\pgfmathresult}%
             \else%
               \ifx\u\chit at oob@vspacer%
-                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}
-                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}
-                \xdef\ll{\pgfmathresult}
-                \chit at dbg{2}{\string\ll=`\ll'}
-                \chit at oob@rowupdate(\c,\r){\ll}{#4}
-                \xdef\lv{1}
+                \chit at dbg{3}{Chit `\u' is vspacer `\chit at oob@vspacer'}%
+                \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)}%
+                \xdef\ll{\pgfmathresult}%
+                \chit at dbg{2}{\string\ll=`\ll'}%
+                \chit at oob@rowupdate(\c,\r){\ll}{#4}%
+                \xdef\lv{1}%
               \else
                 \ifnum\chitdbglvl>2%
                   \node[minimum width=#3cm,minimum height=#3cm,
-                        draw,transform shape] at (\c,\r) {};
-                \fi
+                        draw,transform shape] at (\c,\r) {};%
+                \fi%
                 \ifx\u\chit at blank\else%
                   \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);%
                 \fi%
                 \chit at oob@cellupdate(\c,\r){#2}{#3}{\y}
-              \fi
-            \fi
-          }
-        \fi
-      }
-    \fi
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
     \chit at dbg{2}{ End of chits in turn
-      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
+      \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
     % --- Not relevant, I think
     % IF no units where given, then we force \c to be non-zero so that
     % \chit at oob@turnupdate increments the row
@@ -826,7 +946,7 @@
     % rows.
     \ifnum\y>0%
       \chit at dbg{2}{ Looping rows from 2 to \y, break when row > \y}%
-      \foreach \rr  in {2,...,\y}{
+      \foreach \rr  in {2,...,\y}{%
         %\ifnum\rr>\y% A little funny, but \y can be negative!
         %  \chit at dbg{2}{ \space Breaking loop \rr\space > \y}%
         %  \breakforeach%
@@ -834,7 +954,7 @@
           \chit at oob@rowupdate(\c,\r){#3}{0}% Extra spacing?
         %\fi
       }
-    \fi
+    \fi%
     % --- Not relevant I think
     % This will zero \c.  However, if on entry |\c|>0, then we also
     % increment the row
@@ -856,12 +976,17 @@
       \fi
     \fi
     % \xdef\y{0}
-    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}
-  }
-  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}
-  \@ifnextchar;{\@gobble}{}}
+    \chit at dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')}%
+  }%
+  \chit at dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')}%
+  \@ifnextchar;{\@gobble}{}}%
 \tikzset{
-  chit/cell background/.style={fill=black},
+  chit/cell background/.style={draw=black},
+  %% Define to not dp anyting to disable wings
+  chit/cell wings/.style={
+    dashed,
+    dash pattern=on 0pt off 3pt on 3pt off 0pt,
+    draw=black},
   %chit/cell background flipped/.style={fill=black},
   blank chit/.style={/chit/frame={draw=none,fill=none}},
   chit/grid lines/.style={dashed},
@@ -882,12 +1007,35 @@
   \pgfmathparse{ifthenelse(#1>=#4*(#3-1),0,#1+#4)}%
   \xdef#1{\pgfmathresult}%
 }
+\def\chit at cell@frame#1#2{%
+  \coordinate(cell at frame@north east) at ($(#1.north east)+( #2/2, #2/2)$);
+  \coordinate(cell at frame@north west) at ($(#1.north west)+(-#2/2, #2/2)$);
+  \coordinate(cell at frame@south east) at ($(#1.south east)+( #2/2,-#2/2)$);
+  \coordinate(cell at frame@south west) at ($(#1.south west)+(-#2/2,-#2/2)$);
+  \draw[chit/cell background,fill=none,scale line widths,line width=#2cm]
+  (cell at frame@south west) rectangle (cell at frame@north east);
+  \draw[scale line widths,line width=#2cm,chit/cell wings=10pt,fill=none,scale line widths,line width=#2cm]
+  (cell at frame@north east)--++(     0,10*#2)
+  (cell at frame@north east)--++( 10*#2,0)
+  (cell at frame@north west)--++(     0,10*#2)
+  (cell at frame@north west)--++(-10*#2,0)
+  (cell at frame@south east)--++(     0,-10*#2)
+  (cell at frame@south east)--++( 10*#2,0)
+  (cell at frame@south west)--++(     0,-10*#2)
+  (cell at frame@south west)--++(-10*#2,0)
+  ;
+}
 \def\chits{%
   \@ifstar{\chits at resetfalse\@chits}{\chits at resettrue\@chits}}
+\newcount\wg at col
+\newcount\wg at row
 \def\@chits#1#2#3{
   \ifchits at reset
-    \def\r{0}%
-    \def\c{0}%
+    %\def\r{0}%
+    %\def\c{0}%
+    \coordinate(wg at next) at (0,0);
+    \wg at col=1
+    \wg at row=1
   \fi
   \chit at dbg{1}{Chits to make: #1}%
   \foreach[count=\ti from 0] \t/\x in #1{%
@@ -903,9 +1051,41 @@
             \ifx\u\chit at blank%
               \chit at dbg{3}{Ignoring blank chit:\u}%
             \else%
-              \chit at cellbg(\c,\r){#3}%
-              \chit[\u=\ti](\c,\r)%
-              \chit at sng@cellupdate(\c,\r){#2}{#3}%
+              \ifx\u\chit at oob@vspacer%
+                \chit at dbg{3}{Ignoring vertical spacer:\u}%
+              \else
+                \ifx\u\chit at oob@spacer%
+                  \chit at dbg{3}{Ignoring horizontal spacer:\u}%
+                \else
+                  % \chit at cellbg(\c,\r){#3}%
+                  %\chit[\u=\ti](\c,\r)%
+                  \chit[\u=\ti](wg at next)(wg at chit)%
+                  \chit at cell@frame{wg at chit}{#3}
+                  \global\advance\wg at col1
+                  \ifnum\wg at col>#2%
+                    \path
+                    let
+                    \p1=(wg at chit.north),
+                    \p2=(wg at chit.south),
+                    \p3=(wg at chit),
+                    \n1={\y3-\y1+\y2-#3cm} in
+                    coordinate(wg at next) at (0,\n1);
+                    \global\wg at col=1
+                    \global\advance\wg at row1
+                    \chit at dbg{10}{Row row, next column `\the\wg at col'}
+                  \else
+                    \path
+                    let
+                    \p1=(wg at chit.east),
+                    \p2=(wg at chit.west),
+                    \p3=(wg at chit),
+                    \n1={\x3+\x1-\x2+#3cm} in
+                    coordinate(wg at next) at (\n1,\y3);
+                    \chit at dbg{10}{Next column `\the\wg at col'}
+                  \fi
+                  %\chit at sng@cellupdate(\c,\r){#2}{#3}%
+                \fi%
+              \fi%
             \fi%
           }%
         \fi%
@@ -912,6 +1092,7 @@
       }%
     \fi%
   }%
+  \chit at dbg{10}{Table is `\the\wg at col'x`\the\wg at row'}
   \@ifnextchar;{\@gobble}{}}
 \def\chitgrid#1#2#3{%
   \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}%
@@ -936,14 +1117,22 @@
 }
 \def\doublechits{%
   \@ifstar{\chits at resetfalse\@doublechits}{\chits at resettrue\@doublechits}}
+\newdimen\chit at w
+\newdimen\chit at h
 \def\@doublechits#1#2#3{%
   \chit at dbg{1}{Setting double-sided chits: #1}
-  \ifchits at reset
-    \pgfmathparse{-(#2-.5)*#3}
-    \xdef\c{\pgfmathresult}
-    \def\r{0}
-  \fi
-
+  \ifchits at reset%
+    %\pgfmathparse{-(#2-.5)*#3}
+    %\xdef\c{\pgfmathresult}
+    %\def\r{0}
+    \coordinate(wg at next) at (0,0);%
+    \coordinate(wg at fnxt) at (0,0);%
+    \wg at col=1%
+    \wg at row=1%
+    \chit at w=0pt%
+    \chit at h=0pt%
+  \fi%
+  %
   \foreach[count=\ti from 0] \t/\x in #1{
     \ifx\t\empty\else%
       \foreach \u/\m in \t{
@@ -955,21 +1144,105 @@
             \ifx\u\chit at blank
               \chit at dbg{3}{Ignoring blank chit:\u}
             \else
-              \chit at cellbg(\c,\r){#3}
-              \chit[\u=\ti](\c,\r)
-              \chit at dbl@flip(\c,\r){#3}
-              \chit at celldblbg(\mc,\r){#3}
-              \chit[\u\space flipped=\ti,zone turn=\t,zone mult=\n](\mc,\r)
-              \chit at dbl@cellupdate(\c,\r){#2}{#3}
-            \fi
-          }
-        \fi
-      }
-    \fi
-  }
-  \draw[dashed](0,-3*#3/4)--(0,\r-#3/4);%
-  \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};%
-  \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};%
+              \ifx\u\chit at oob@vspacer%
+                \chit at dbg{3}{Ignoring vertical spacer:\u}%
+              \else
+                \ifx\u\chit at oob@spacer%
+                  \chit at dbg{3}{Ignoring horizontal spacer:\u}%
+                \else
+                  \chit at dbg{10}{Drawing `\u' at wg at next}
+                  \chit[\u=\ti](wg at next)(wg at chit)%
+                  \chit at cell@frame{wg at chit}{#3}
+                  \ifdim\chit at w=0pt
+                    \pgfextractx\chit at w{\pgfpointanchor{wg at chit}{east}}%
+                    \pgfextractx\wg at tmpa{\pgfpointanchor{wg at chit}{west}}%
+                    \advance\chit at w-\wg at tmpa%
+                    \global\advance\chit at w#3cm%
+                    \pgfextracty\chit at h{\pgfpointanchor{wg at chit}{north}}%
+                    \pgfextracty\wg at tmpa{\pgfpointanchor{wg at chit}{south}}%
+                    \advance\chit at h-\wg at tmpa%
+                    \global\advance\chit at h#3cm%
+                  \fi
+                  \ifnum\wg at col=1
+                    \pgfextractx\wg at tmpa{\pgfpointanchor{wg at chit}{center}}%
+                    \advance\wg at tmpa+\chit at w%
+                    \coordinate(wg at fnxt) at ($(wg at chit)+(\the\chit at w,0pt)$);
+                    %\coordinate(wg at fnxt
+                    %\chit at dbg{10}{Calculating wg at fnxt since `\the\wg at col'==1}
+                    %\path
+                    %let
+                    %\p1=(wg at chit.east),
+                    %\p2=(wg at chit.west),
+                    %\p3=(wg at chit),
+                    %\n1={\x3+\x1-\x2+#3cm} in
+                    %coordinate(wg at fnxt) at (\n1,\y3);
+                  \fi
+                  %\chit at dbg{10}{Drawing `\u\space flipped' at wg at fnxt}
+                  \chit[\u\space flipped=\ti,
+                  zone turn=\t,
+                  zone mult=\n](wg at fnxt)(wg at flip)%
+                  \chit at cell@frame{wg at flip}{#3}
+                  \chit at dbg{10}{Next column: `\the\wg at col'}
+                  \ifnum\wg at col=1
+                    \ifnum\wg at row=1
+                      \coordinate(wg at table@top) at (wg at chit.north east);
+                    \fi
+                    \coordinate(wg at table@bot) at (wg at chit.south east);
+                  \fi
+                  \global\advance\wg at col1
+                  \ifnum\wg at col>#2%
+                    % \path
+                    %    let
+                    %    \p1=(wg at chit.north),
+                    %    \p2=(wg at chit.south),
+                    %    \p3=(wg at chit),
+                    %    \n1={\y3-\y1+\y2-#3cm} in %1.21991
+                    %    coordinate(wg at next) at (0,\n1);
+                    \coordinate(wg at next) at (0,-\wg at row\chit at h);
+                    \global\wg at col=1
+                    \global\advance\wg at row1
+                    \chit at dbg{10}{Row row, next column `\the\wg at col'}
+                  \else
+                    % \path
+                    % let
+                    % \p1=(wg at chit.east),
+                    % \p2=(wg at chit.west),
+                    % \p3=(wg at chit),
+                    % \p4=(wg at flip.east),
+                    % \p5=(wg at flip.west),
+                    % \p6=(wg at flip),
+                    % \n1={\x3-\x1+\x2-#3cm},
+                    % \n2={\x6+\x4-\x5+#3cm} in
+                    % coordinate(wg at next) at (\n1,\y3)
+                    % coordinate(wg at fnxt) at (\n2,\y6);
+                    \coordinate(wg at next) at ($(wg at chit)+(-\the\chit at w,0)$);
+                    \coordinate(wg at fnxt) at ($(wg at flip)+(+\the\chit at w,0)$);
+                    \chit at dbg{10}{Next column `\the\wg at col'}
+                  \fi
+                \fi%
+              \fi%
+            \fi%
+          }%
+        \fi%
+      }%
+    \fi%
+  }%
+  \draw[dashed,scale line widths](wg at table@top)--++(0,.4);
+  \draw[dashed,scale line widths](wg at table@bot)--++(0,-.4);
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@top)+(0,.2)$) -- ++(.5,0)
+  node[transform shape,anchor=west]{Back};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@top)+(0,.2)$) -- ++(-.5,0)
+  node[transform shape,anchor=east]{Front};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@bot)+(0,-.2)$) -- ++(.5,0)
+  node[transform shape,anchor=west]{Back};
+  \draw[dashed,scale line widths,{Stealth[]}-]
+  ($(wg at table@bot)+(0,-.2)$) -- ++(-.5,0)
+  node[transform shape,anchor=east]{Front};
+  % \draw[dashed,<-] (#3/5,-2*#3/3)--(#3/2,-2*#3/3) node[transform shape,anchor=west]{Back};%
+  % \draw[dashed,<-] (-#3/5,-2*#3/3)--(-#3/2,-2*#3/3) node[transform shape,anchor=east]{Front};%
   % \foreach \cc in {0,...,#2}{
   %   \draw[dashed] (\cc*#3,-3*#3/4)--(\cc*#3,\r-#3/4);
   %   \draw[dashed] (-\cc*#3,-3*#3/4)--(-\cc*#3,\r-#3/4);}
@@ -980,15 +1253,18 @@
   %   \draw[dashed] (-#2*#3-#3/4,\rr)--(#2*#3+#3/4,\rr);}
   \@ifnextchar;{\@gobble}{}}
 \def\doublechitgrid#1#2#3{%
+  \message{^^JWARNING - the grid table might be messed up!}
+  %\iffalse
   \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}%
   \pgfmathparse{#2*#3-#3/2}\edef\rmax{\pgfmathresult}%
   \foreach \cc in {0,...,#1}{
-    \draw[chit/grid lines] (\cc*#3,-3*#3/4)--(\cc*#3,\rmax+#3/4);
-    \draw[chit/grid lines] (-\cc*#3,-3*#3/4)--(-\cc*#3,\rmax+#3/4);}
+    \draw[chit/grid lines] (\cc*#3+\rmin,3*#3/4)--(\cc*#3+\rmin,-\rmax-#3/4);
+    \draw[chit/grid lines] (-\cc*#3+\rmin,3*#3/4)--(-\cc*#3+\rmin,-\rmax-#3/4);}
   %\chit at dbg{0}{Drawing horizontal lines from `-\rmin, `\rmin', ..., `\rmax'}
-  \foreach \rr in {-\rmin,\rmin,...,\rmax}{
-    %\chit at dbg{0}{Horizontal line at `\rr'}
-    \draw[chit/grid lines] (-#1*#3-#3/4,\rr)--(#1*#3+#3/4,\rr);}
+  \foreach \rr in {\rmin,-\rmin,...,-\rmax}{
+    \chit at dbg{0}{Horizontal line at `\rr'}
+    \draw[chit/grid lines] (-#1*#3-#3/4+\rmin,\rr)--(#1*#3+#3/4+\rmin,\rr);}
+  %\fi
 }
 \tikzset{%
   battle marker/.pic={
@@ -1023,7 +1299,7 @@
 \tikzset{
   pics/result marker/.style args={#1,#2}{
     code={
-      \message{^^JResults marker #1 (#2)}
+      \chit at dbg{3}{Results marker #1 (#2)}
       \node[shape=circle,
       font=\sffamily\bfseries\large,
       inner sep=0pt,
@@ -1097,6 +1373,12 @@
     % \pgfusepath{draw}  %draw border
     % \pgfusepath{draw}  %draw rectangle
   }}
+\newif\ifwg at dsixdot\wg at dsixdotfalse
+\tikzset{
+  d6 dots/.is if=wg at dsixdot,
+  d6 dots/.initial=false,
+  d6 dots/.default=true
+}
 \pgfdeclareshape{d6}{
   \anchor{center}{\pgfpointorigin}    % within the node, (0,0) is the center
   \anchor{text}{
@@ -1105,7 +1387,48 @@
   \backgroundpath{ % draw border
     \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}}
     % \pgfusepath{draw}  %draw rectangle
-  }}
+  }
+  \behindforegroundpath{ % draw border
+    \ifwg at dsixdot%
+      \edef\numdots{\tikz at node@content}%
+      % Draw over text
+      \pgfpathrectanglecorners{\pgfpoint{.4cm}{.4cm}}{\pgfpoint{-.4cm}{-.4cm}}%
+      \pgfusepath{fill}  %draw rectangle
+      \ifx\tikz at textcolor\pgfutilempty%
+        \pgfutil at colorlet{.}{pgfstrokecolor}%
+      \else
+        % \pgfsetfillcolor{\tikz at textcolor}%
+        \pgfutil at colorlet{.}{\tikz at textcolor}%
+      \fi
+      \pgfsetfillcolor{.}%
+      \ifodd\numdots
+        %\message{^^JCenter dot}
+        \pgfpathcircle{\pgfpoint{0cm}{0cm}}{.09cm}
+      \fi
+      \ifnum\numdots=2%
+        %\message{^^JSpecial 2}
+        \pgfpathcircle{\pgfpointpolar{-45}{.32cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{135}{.32cm}}{.09cm}
+      \fi%
+      \ifnum\numdots>2%
+        %\message{^^JAt least 2}
+        \pgfpathcircle{\pgfpointpolar{-45}{.35cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{135}{.35cm}}{.09cm}
+      \fi%
+      \ifnum\numdots>3%
+        %\message{^^JAt least 4}
+        \pgfpathcircle{\pgfpointpolar{  45}{.35cm}}{.09cm}
+        \pgfpathcircle{\pgfpointpolar{-135}{.35cm}}{.09cm}
+      \fi%
+      \ifnum\numdots=6%
+        %\message{^^JLucky 6}
+        \pgfpathcircle{\pgfpoint{-.247cm}{0cm}}{.09cm}
+        \pgfpathcircle{\pgfpoint{ .247cm}{0cm}}{.09cm}
+      \fi
+      \pgfusepath{fill}  %draw rectangle
+    \fi%
+  }
+}
 \pgfdeclareshape{d8}{
   \anchor{center}{\pgfpointorigin}    % within the node, (0,0) is the center
   \anchor{text}{
@@ -1226,7 +1549,7 @@
     \pgfkeysalso{%
       chit has drop=true,
       /tikz/blur shadow={shadow blur steps=5,
-        shadow opacity=25,
+        shadow opacity=30,
         shadow xshift=.05cm,
         shadow yshift=-.05cm,
         shadow blur radius=.05cm,

Modified: trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.hex.code.tex	2024-11-19 20:38:26 UTC (rev 72903)
@@ -9,6 +9,8 @@
 %% hex/shape.dtx  (with options: `hex')
 %% hex/terrain.dtx  (with options: `hex')
 %% hex/terrain/beach.dtx  (with options: `hex')
+%% hex/terrain/fields.dtx  (with options: `hex')
+%% hex/terrain/speckle.dtx  (with options: `hex')
 %% hex/terrain/light_woods.dtx  (with options: `hex')
 %% hex/terrain/woods.dtx  (with options: `hex')
 %% hex/terrain/swamp.dtx  (with options: `hex')
@@ -223,7 +225,7 @@
   \xdef\hex at eff@row{\pgfmathresult}%
   \hex at dbg{2}{Effective row: \hex at coords@row at fac * (\hex at row +
     \hex at coords@row at off) -> \hex at eff@row}%
-  \pgfmathparse{(2*\hex at eff@row-mod(round((\hex at col+\hex at coords@col at off)),2))*\hex at yy}%
+  %\pgfmathparse{(2*\hex at eff@row-mod(round((\hex at col+\hex at coords@col at off)),2))*\hex at yy}%
   \pgfmathparse{(2*\hex at eff@row-mod(abs(round(\hex at col+\hex at coords@col at off)),2))*\hex at yy}%
   \xdef\hex at y{\pgfmathresult}%
   \ifx\hex at vtx\@empty\else%
@@ -533,15 +535,19 @@
   code/.store in=\hex at t@code,%
   clip/.store in=\hex at t@clip,%
   random rotation/.is if=@hex at t@rot,
+  rotate/.store in=\hex at t@s at angle,
   pic/.default=,
   image/.default=,
   code/.default=,
   clip/.default=,
+  rotate/.default=,
 }
 \iffalse
 \tikzset{
   /hex/terrain/.cd,%
   beach/.style={pic=hex/terrain/beach},
+  fields/.style={pic=hex/terrain/fields},
+  speckle/.style={pic=hex/terrain/speckle},
   light woods/.style={pic=hex/terrain/light woods},
   woods/.style={pic=hex/terrain/woods},
   swamp/.style={pic=hex/terrain/swamp},
@@ -555,6 +561,8 @@
 \tikzset{
   /hex/terrain/.cd,%
   beach/.style={image=wargame.beach},
+  fields/.style={image=wargame.fields},
+  speckle/.style={image=wargame.speckle},
   light woods/.style={image=wargame.light_woods},
   woods/.style={image=wargame.woods},
   swamp/.style={image=wargame.swamp},
@@ -597,6 +605,12 @@
     \if at hex@t at rot%
       \pgfmathrandominteger{\hex at t@angle}{0}{5}
       \pgfmathparse{int(60*\hex at t@angle)}\edef\hex at t@angle{\pgfmathresult}%
+    \else%
+      \@ifundefined{hex at t@s at angle}{}{
+        \ifx\hex at t@s at angle\@empty%
+        \else%
+          \edef\hex at t@angle{\hex at t@s at angle}%
+        \fi}%
     \fi%
     \hex at dbg{5}{Will rotate terrain by `\hex at t@angle'}%
     \scope[rotate=\hex at t@angle]%
@@ -671,7 +685,7 @@
     pics/hex/#2sextant/S/.style=hex/#2sextant/south,
     pics/hex/#2sextant/SE/.style=hex/#2sextant/south east,
     pics/hex/#2sextant/C/.style=hex/#2sextant/center,
-  }
+  }%
 }
 \hex at make@sextants{.7}{}
 \hex at make@sextants{.3}{large }
@@ -1347,6 +1361,83 @@
 }
 \fi
 \tikzset{
+  hex/terrain/fields/.style={%
+    draw=none%
+  }%
+}
+\ifhex at terrain@pic
+\tikzset{
+  hex/terrain/fields/.pic={
+    \path[draw=none,fill=none](1,0)
+    --(60:1)
+    --(120:1)
+    --(180:1)
+    --(240:1)
+    --(300:1)
+    --cycle;
+    \fill[black,opacity=.05] ( .1,   .3)rectangle++(.5,.4);
+    \fill[white,opacity=.15] (-.65, -.5)rectangle++(.3,.5);
+    \fill[white,opacity=.25] (-.60,  .2)rectangle++(.6,.5);
+    \fill[black,opacity=.02] (-.30, -.1)rectangle++(.4,.2);
+    \fill[black,opacity=.02] (-.50, -.8)rectangle++(.4,.25);
+    \fill[black,opacity=.05] (-.30, -.5)rectangle++(.4,.35);
+    \fill[white,opacity=.15] ( .15, -.6)rectangle++(.4,.8);
+    \fill[black,opacity=.02] (-.05, -.8)rectangle++(.5,.15);
+    \fill[black,opacity=.02] (-.8,  -.3)rectangle++(.1,.6);
+    \fill[black,opacity=.02] ( .6,  -.35)rectangle++(.2,.6);
+  }
+}
+\fi
+\tikzset{
+  hex/terrain/speckle/.style={%
+  }%
+}
+\ifhex at terrain@pic
+\pgfmathdeclarerandomlist{black white speckles}{{black}{white}}
+\pgfmathdeclarerandomlist{gray white speckles}{{gray}{white}}
+\tikzset{
+  hex/terrain/speckle/minimum radius/.initial=3,
+  hex/terrain/speckle/maximum radius/.initial=10,
+  hex/terrain/speckle/minimum opacity/.initial=3,
+  hex/terrain/speckle/maximum opacity/.initial=7,
+  %%
+  pics/hex/terrain/speckle/.style args={#1,#2}{
+    code={
+      \tikzset{
+        hex/terrain/speckle/minimum radius/.get=\minR,
+        hex/terrain/speckle/maximum radius/.get=\maxR,
+        hex/terrain/speckle/minimum opacity/.get=\minO,
+        hex/terrain/speckle/maximum opacity/.get=\maxO,
+      }
+      \foreach \i in {1,...,#1}{
+
+        \pgfmathrandominteger{\R}{\minR}{\maxR}
+        \pgfmathrandominteger{\n}{1}{3}% Sub trapezoid
+        \pgfmathrandominteger{\x}{0}{100}% X coordinate
+        \pgfmathrandominteger{\y}{0}{100}% Y coordinate
+        \pgfmathrandominteger{\o}{\minO}{\maxO}% Opacity
+        \pgfmathrandomitem{\c}{#2}
+        \pgfmathparse{\x/100}\edef\x{\pgfmathresult}
+        \pgfmathparse{\y/100}\edef\y{\pgfmathresult}
+        \pgfmathparse{\R/100}\edef\R{\pgfmathresult}
+        \pgfmathparse{\o/100}\edef\o{\pgfmathresult}
+        \pgfmathparse{1-\R}\edef\O{\pgfmathresult}
+        \ifnum\n=1
+          \fill[\c,opacity=\o]($\y*(120:\O)+(\x-\R,0)$) circle(\R);
+        \else
+          \ifnum\n=2
+            \fill[\c,opacity=\o]($\y*(240:\O)+(\x-\R,0)$) circle(\R);
+          \else
+            \fill[\c,opacity=\o]($\x*(120:\O)+\y*(240:\O)$) circle(\R);
+          \fi
+        \fi
+      }
+    }
+  },
+  pics/hex/terrain/speckle/.default={200,gray white speckles}
+}
+\fi
+\tikzset{
   hex/terrain/light woods/.style={
     draw=none,
     fill={rgb,100:red,69;green,98;blue,69}
@@ -12832,170 +12923,170 @@
   \@ifnextchar[{\bo at rdfr@me@}{\bo at rdfr@me@[]}%]
 }
 \def\bo at rdfr@me at u(#1)#2#3#4#5{
-  \hex at coords@conv{#1}
+  \hex at coords@conv{#1}%
   % \hex at dbg{0}{#1 -> `\hex at x',`\hex at y'}
-  \pgfmathparse{min(#2,\hex at x)}\xdef#2{\pgfmathresult}
-  \pgfmathparse{min(#3,\hex at y)}\xdef#3{\pgfmathresult}
-  \pgfmathparse{max(#4,\hex at x)}\xdef#4{\pgfmathresult}
-  \pgfmathparse{max(#5,\hex at y)}\xdef#5{\pgfmathresult}
-  \hex at dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'}
+  \pgfmathparse{min(#2,\hex at x)}\xdef#2{\pgfmathresult}%
+  \pgfmathparse{min(#3,\hex at y)}\xdef#3{\pgfmathresult}%
+  \pgfmathparse{max(#4,\hex at x)}\xdef#4{\pgfmathresult}%
+  \pgfmathparse{max(#5,\hex at y)}\xdef#5{\pgfmathresult}%
+  \hex at dbg{2}{#1 -> ll=`#2',`#3', ur=`#4',`#5'}%
 }
-\def\bo at rdfr@me@[#1]#2#3#4#5{
+\def\bo at rdfr@me@[#1]#2#3#4#5{%
   % Define rtmp and a ctmp to by directions
-  \pgfmathparse{int(\hex at coords@row at fac)}\edef\rtmp{\pgfmathresult}
-  \pgfmathparse{int(\hex at coords@col at fac)}\edef\ctmp{\pgfmathresult}
+  \pgfmathparse{int(\hex at coords@row at fac)}\edef\rtmp{\pgfmathresult}%
+  \pgfmathparse{int(\hex at coords@col at fac)}\edef\ctmp{\pgfmathresult}%
   % Define vertices for path
-  \def\ctfv{SW}
-  \def\ctsv{SE}
-  \def\cbfv{NE}
-  \def\cbsv{NW}
-  \def\rrfv{E}
-  \def\rrsv{NE}
-  \def\rlfv{W}
-  \def\rlsv{SW}
+  \def\ctfv{SW}%
+  \def\ctsv{SE}%
+  \def\cbfv{NE}%
+  \def\cbsv{NW}%
+  \def\rrfv{E}%
+  \def\rrsv{NE}%
+  \def\rlfv{W}%
+  \def\rlsv{SW}%
   % Swap around some definitions based on the row direction
-  \ifnum\rtmp<0
-    \let\max at short\hex at bot@short at col
-    \let\min at short\hex at top@short at col
-    \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp
-    \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp
-    \def\rrsv{SE}
-    \def\rlsv{NW}
-  \else
-    \let\max at short\hex at top@short at col
-    \let\min at short\hex at bot@short at col
-  \fi
+  \ifnum\rtmp<0%
+    \let\max at short\hex at bot@short at col%
+    \let\min at short\hex at top@short at col%
+    \let\swp\ctfv\let\ctfv\cbsv\let\cbsv\swp%
+    \let\swp\ctsv\let\ctsv\cbfv\let\cbfv\swp%
+    \def\rrsv{SE}%
+    \def\rlsv{NW}%
+  \else%
+    \let\max at short\hex at top@short at col%
+    \let\min at short\hex at bot@short at col%
+  \fi%
   % Swap around some definitions based on the column direction
-  \ifnum\ctmp<0
-    \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp
-    \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp
-    \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp
-    \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp
-  \fi
+  \ifnum\ctmp<0%
+    \let\swp\ctfv\let\ctfv\ctsv\let\ctsv\swp%
+    \let\swp\cbfv\let\cbfv\cbsv\let\cbsv\swp%
+    \let\swp\rrfv\let\rrfv\rlsv\let\rlsv\swp%
+    \let\swp\rrsv\let\rrsv\rlfv\let\rlfv\swp%
+  \fi%
   % Define tmp = 0 if no shorts, 1 if top short, 2 if both
   \pgfmathparse{ifthenelse(\hex at got@top at short,
-    ifthenelse(\hex at got@bot at short,2,1),0)}\edef\tmp{\pgfmathresult}
+    ifthenelse(\hex at got@bot at short,2,1),0)}\edef\tmp{\pgfmathresult}%
   % If top-short, set factors
-  \ifnum\tmp=1
-    \def\mnf{-1}
-    \def\mxf{-1}
-    \def\mnn{}
-    \def\mxn{}
+  \ifnum\tmp=1%
+    \def\mnf{-1}%
+    \def\mxf{-1}%
+    \def\mnn{}%
+    \def\mxn{}%
   % If both short, set factors
-  \else\ifnum\tmp=2
-    \def\mnf{\rtmp}
-    \def\mxf{(-\rtmp)}
+  \else\ifnum\tmp=2%
+    \def\mnf{\rtmp}%
+    \def\mxf{(-\rtmp)}%
     % If inverse rows, set factors
-    \ifnum\rtmp<0
-      \def\mnn{}
-      \def\mxn{not}
-    \else
-      \def\mnn{not}
-      \def\mxn{}
-    \fi
+    \ifnum\rtmp<0%
+      \def\mnn{}%
+      \def\mxn{not}%
+    \else%
+      \def\mnn{not}%
+      \def\mxn{}%
+    \fi%
   % If none is short
-  \else
-    \def\mnf{1}
-    \def\mxf{1}
-    \def\mnn{not}
-    \def\mxn{not}
-  \fi\fi
+  \else%
+    \def\mnf{1}%
+    \def\mxf{1}%
+    \def\mnn{not}%
+    \def\mxn{not}%
+  \fi\fi%
   % Define row at mn to give least row of column
   \def\row at mn##1{%
     \pgfmathparse{int(#3+\mnf*
       \hex at coords@row at fac*\min at short(##1)*
-      \mnn(\min at short(\hex at coords@col at off)))}
-    \edef\lr{\pgfmathresult}}
+      \mnn(\min at short(\hex at coords@col at off)))}%
+    \edef\lr{\pgfmathresult}}%
   % Define row at mx to give largest row of column
   \def\row at mx##1{%
     \pgfmathparse{int(#5+\mxf*
       \hex at coords@row at fac*\max at short(##1)*
-      \mxn(\max at short(\hex at coords@col at off)))}
-    \edef\ur{\pgfmathresult}}
+      \mxn(\max at short(\hex at coords@col at off)))}%
+    \edef\ur{\pgfmathresult}}%
   %
   %
   % Below defines a path around the perimeter of the hexes.
   %
-  \def\@llx{10000}
-  \def\@lly{10000}
-  \def\@urx{-10000}
-  \def\@ury{-10000}
+  \def\@llx{10000}%
+  \def\@lly{10000}%
+  \def\@urx{-10000}%
+  \def\@ury{-10000}%
   % Start with an empty path
   \def\p{}
   % Loop across least row (can be top if \rtmp<0)
   \foreach \c in {#2,...,#4}{%
-    \row at mn{\c}
-    \row at mx{\c}
+    \row at mn{\c}%
+    \row at mx{\c}%
     % \message{^^JColumn: `\c' -> `\lr',`\ur' (#3,#5)}
   }
   \foreach \c in {#2,...,#4}{%
-    \row at mn{\c}
+    \row at mn{\c}%
     \xdef\p{\p
       (hex cs:c=\c,r=\lr,v=\ctfv)--
-      (hex cs:c=\c,r=\lr,v=\ctsv)--}
-    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury
-  }
+      (hex cs:c=\c,r=\lr,v=\ctsv)--}%
+    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=\c,r=\lr,v=\ctsv)\@llx\@lly\@urx\@ury%
+  }%
   % Go up (down if \rtmp<0) right side
-  \row at mn{#4}
-  \row at mx{#4}
+  \row at mn{#4}%
+  \row at mx{#4}%
   \foreach \r in {\lr,...,\ur}{%
     \xdef\p{\p
       (hex cs:c=#4,r=\r,v=\rrfv)--
-      (hex cs:c=#4,r=\r,v=\rrsv)--}
-    \bo at rdfr@me at u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury
-  }
+      (hex cs:c=#4,r=\r,v=\rrsv)--}%
+    \bo at rdfr@me at u(c=#4,r=\r,v=\rrfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=#4,r=\r,v=\rrsv)\@llx\@lly\@urx\@ury%
+  }%
   % Go across largest row (can be bottom if \rtmp<0)
   \foreach \c in {#4,...,#2}{%
-    \row at mx{\c}
+    \row at mx{\c}%
     % \message{^^JColumn: `\c', max:`\ur'}
     \xdef\p{\p
       (hex cs:c=\c,r=\ur,v=\cbfv)--
-      (hex cs:c=\c,r=\ur,v=\cbsv)--}
-    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury
+      (hex cs:c=\c,r=\ur,v=\cbsv)--}%
+    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=\c,r=\ur,v=\cbsv)\@llx\@lly\@urx\@ury%
   }
   % Go up (down if \rtmp<0) left side.
-  \row at mn{#2}
-  \row at mx{#2}
+  \row at mn{#2}%
+  \row at mx{#2}%
   \foreach \r in {\ur,...,\lr}{%
     \xdef\p{\p
        (hex cs:c=#2,r=\r,v=\rlfv)--
-       (hex cs:c=#2,r=\r,v=\rlsv)--}
-    \bo at rdfr@me at u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury
-    \bo at rdfr@me at u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury
-  }
+       (hex cs:c=#2,r=\r,v=\rlsv)--}%
+    \bo at rdfr@me at u(c=#2,r=\r,v=\rlfv)\@llx\@lly\@urx\@ury%
+    \bo at rdfr@me at u(c=#2,r=\r,v=\rlsv)\@llx\@lly\@urx\@ury%
+  }%
   % End path with cycle
-  \edef\p{\p cycle}
+  \edef\p{\p cycle}%
   % Define global path
-  \global\let\hex at board@path\p
-  \hex at dbg{3}{Hex board path: `\meaning\hex at board@path'}
+  \global\let\hex at board@path\p%
+  \hex at dbg{3}{Hex board path: `\meaning\hex at board@path'}%
   % If an optional argument was given, then use that to actually make
   % hexes.
-  \ifx|#1|\else
+  \ifx|#1|\else%
     \foreach[count=\nc] \c in {#2,...,#4}{%
-      \row at mn{\c}
-      \row at mx{\c}
+      \row at mn{\c}%
+      \row at mx{\c}%
       \foreach \r in {\lr,...,\ur}{%
-        \hex[#1={\c,\r}](c=\c,r=\r)
-      }
-    }
-  \fi
-}
+        \hex[#1={\c,\r}](c=\c,r=\r)%
+      }%
+    }%
+  \fi%
+}%
 \tikzset{%
-  /hex/board/no op/.style args={#1,#2}{}}
+  /hex/board/no op/.style args={#1,#2}{}}%
 \def\boardhexes{%
   \@ifnextchar[{\bo at rdhexes}{\bo at rdhexes[board/no op]}%]
-}
+}%
 \def\bo at rdhexes[#1](#2)(#3){%
-  \hex at coords@conv{#2}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
-  \hex at coords@conv{#3}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
-  \bo at rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}}
+  \hex at coords@conv{#2}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
+  \hex at coords@conv{#3}%
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
+  \bo at rdfr@me[#1]{\llc}{\llr}{\urc}{\urr}}%
 \tikzset{board frame bb/.code={
     \pgfkeys{
       %/tikz/local bounding box=tmp board frame,
@@ -13011,52 +13102,52 @@
       }}}}
 
 \def\bo at rdframe[#1](#2)(#3){%
-  \hex at coords@conv{#2}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
+  \hex at coords@conv{#2}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
   %
   \hex at coords@conv{#3}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
   %
-  \def\margin{#1}
+  \def\margin{#1}%
   %
   % This will store the bounding box in tmp node `board frame'
   \bo at rdfr@me{\llc}{\llr}{\urc}{\urr}%
-  \begin{scope}[board frame bb]
-    \expandafter\path\hex at board@path;
-  \end{scope}
-  \hex at dbg{1}{Board frame LL: -> `\llx',`\lly'}
-  \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult}
-  \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult}
+  \begin{scope}[board frame bb]%
+    \expandafter\path\hex at board@path;%
+  \end{scope}%
+  \hex at dbg{1}{Board frame LL: -> `\llx',`\lly'}%
+  \pgfmathparse{\llx+ifthenelse(\llx<0,-1,1)*\margin}\edef\llx{\pgfmathresult}%
+  \pgfmathparse{\lly+ifthenelse(\lly<0,-1,1)*\margin}\edef\lly{\pgfmathresult}%
   %
-  \hex at dbg{1}{Board frame UR: -> `\urx',`\ury'}
-  \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult}
-  \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult}
+  \hex at dbg{1}{Board frame UR: -> `\urx',`\ury'}%
+  \pgfmathparse{\urx+ifthenelse(\urx<0,-1,1)*\margin}\edef\urx{\pgfmathresult}%
+  \pgfmathparse{\ury+ifthenelse(\ury<0,-1,1)*\margin}\edef\ury{\pgfmathresult}%
   %
-  \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult}
-  \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult}
+  \pgfmathparse{\urx-\llx}\edef\w{\pgfmathresult}%
+  \pgfmathparse{\ury-\lly}\edef\h{\pgfmathresult}%
   %% Print to the log
-  \hex at dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)}
+  \hex at dbg{0}{Board Frame: (\llx,\lly)x(\urx,\ury) (\w x\h) (\llc,\llr)x(\urc,\urr)}%
   %% Possibly draw
-  \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury);
+  \draw[hex/board frame/.try](\llx,\lly) rectangle(\urx,\ury);%
   %% Store macros
   \xdef\boardXmin{\llx}%
   \xdef\boardYmin{\lly}%
   \xdef\boardXmax{\urx}%
   \xdef\boardYmax{\ury}%
+  \@ifnextchar;{\@gobble}{}%
 }
-\def\boardpath(#1)(#2){
+\def\boardpath(#1)(#2){%
   \hex at coords@reset%
-  \tikzset{/hex/coords/.cd, #1}
-  \edef\llc{\hex at col}
-  \edef\llr{\hex at row}
+  \tikzset{/hex/coords/.cd, #1}%
+  \edef\llc{\hex at col}%
+  \edef\llr{\hex at row}%
   %%
   \hex at coords@reset%
-  \tikzset{/hex/coords/.cd, #2}
-  \edef\urc{\hex at col}
-  \edef\urr{\hex at row}
-
+  \tikzset{/hex/coords/.cd, #2}%
+  \edef\urc{\hex at col}%
+  \edef\urr{\hex at row}%
   % This will store the bounding box in tmp node `board frame'
   \bo at rdfr@me{\llc}{\llr}{\urc}{\urr}%
   %% Use the path to extract the bounding box
@@ -13063,13 +13154,14 @@
   %\begin{scope}[local bounding box=board frame]
   %  \expandafter\path\hex at board@path;
   %\end{scope}
-  \global\let\hexboardpath\hex at board@path
+  \global\let\hexboardpath\hex at board@path%
+  \@ifnextchar;{\@gobble}{}%
 }
 
 \def\boardclip(#1)(#2)#3{%
-  \boardpath(#1)(#2)
+  \boardpath(#1)(#2)%
   \draw \ifx|#3|\else[preaction={#3}]\fi%
-  [clip] \hexboardpath;
+  [clip] \hexboardpath;%
 }
 
 \def\debuggrid{%

Modified: trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.natoapp6c.code.tex	2024-11-19 20:38:26 UTC (rev 72903)
@@ -297,8 +297,8 @@
 \pgfdeclareshape{natoapp6c friendly equipment}{%
   \inheritsavedanchors[from=natoapp6c base]
   \savedanchor\northeast{%
-    \pgf at x=\n at to@pp at r%
-    \pgf at y=\n at to@pp at r}
+    \pgf at x=1.2\n at to@pp at r%
+    \pgf at y=1.2\n at to@pp at r}
   \anchor{north east}{\northeast}
   \anchor{north west}{\northeast\pgf at x=-\pgf at x}
   \anchor{south east}{\northeast\pgf at y=-\pgf at y}
@@ -484,6 +484,49 @@
   \backgroundpath{}
   \behindforegroundpath{}
 }
+\def\n at to@friendly at l@nd{%
+  \northeast \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
+  \pgfpathmoveto{\pgfqpoint{ \wg at tmpa}{ \wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{ \wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{-\wg at tmpb}}%
+  \pgfpathlineto{\pgfqpoint{ \wg at tmpa}{-\wg at tmpb}}%
+  \pgfclosepath}
+\def\n at to@pp at friendl@dismounted{%
+  \northeast\wg at tmpa=\pgf at x\wg at tmpb\pgf at y%
+  \pgfpathmoveto{\pgfpoint{-\wg at tmpa}{-.5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{-\wg at tmpa}{ .5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{0cm}      {   \wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{ \wg at tmpa}{ .5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{ \wg at tmpa}{-.5\wg at tmpb}}%
+  \pgfpathlineto{\pgfpoint{0cm}      {  -\wg at tmpb}}%
+  \pgfclosepath%
+}%
+\pgfdeclareshape{natoapp6c friendly dismounted}{%
+  \inheritsavedanchors[from=natoapp6c base]
+  \savedanchor\northeast{%
+    \pgf at x=1.1\n at to@pp at r%
+    \pgf at y=1.1\n at to@pp at r}
+  \anchor{north east}{\northeast}
+  \anchor{north west}{\northeast\pgf at x=-\pgf at x}
+  \anchor{south east}{\northeast\pgf at y=-\pgf at y}
+  \anchor{south west}{\northeast\pgf at x=-\pgf at x\pgf at y=-\pgf at y}
+  \anchor{north}{\northeast\pgf at x=0cm}
+  \anchor{south}{\northeast\pgf at x=0cm\pgf at y=-\pgf at y}
+  \anchor{east}{\northeast\pgf at y=0cm}
+  \anchor{west}{\northeast\pgf at x=-\pgf at x\pgf at y=0cm}
+  \inheritanchor[from=natoapp6c base]{upper}
+  \inheritanchor[from=natoapp6c base]{lower}
+  \inheritanchor[from=natoapp6c base]{left}
+  \inheritanchor[from=natoapp6c base]{right}
+  \inheritanchor[from=natoapp6c base]{center}
+  \backgroundpath{%
+    \n at to@pp at friendl@dismounted%
+  }
+  \behindforegroundpath{%
+    \n at to@pp at friendl@dismounted%
+    \pgfusepath{stroke}%
+  }
+}
 \def\n at to@hostile@@ir{%
   \southeast \wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
   \cntrl \wg at tmpc=\pgf at y%
@@ -813,6 +856,28 @@
     \pgfusepath{stroke}%
   }
 }
+\pgfdeclareshape{natoapp6c hostile dismounted}{%
+  \inheritsavedanchors[from=natoapp6c hostile land]
+  \inheritanchor[from=natoapp6c hostile land]{inner north east}
+  \inheritanchor[from=natoapp6c hostile land]{inner north west}
+  \inheritanchor[from=natoapp6c hostile land]{inner south west}
+  \inheritanchor[from=natoapp6c hostile land]{inner south east}
+  \inheritanchor[from=natoapp6c hostile land]{north east}
+  \inheritanchor[from=natoapp6c hostile land]{north west}
+  \inheritanchor[from=natoapp6c hostile land]{south east}
+  \inheritanchor[from=natoapp6c hostile land]{south west}
+  \inheritanchor[from=natoapp6c hostile land]{north}
+  \inheritanchor[from=natoapp6c hostile land]{west}
+  \inheritanchor[from=natoapp6c hostile land]{east}
+  \inheritanchor[from=natoapp6c hostile land]{south}
+  \inheritanchor[from=natoapp6c hostile land]{upper}
+  \inheritanchor[from=natoapp6c hostile land]{lower}
+  \inheritanchor[from=natoapp6c hostile land]{left}
+  \inheritanchor[from=natoapp6c hostile land]{right}
+  \inheritanchor[from=natoapp6c hostile land]{center}
+  \inheritbackgroundpath[from=natoapp6c hostile land]
+  \inheritbehindforegroundpath[from=natoapp6c hostile land]
+}
 \def\n at to@pp at neutr@l at init{%
   \northeast\wg at tmpa=\pgf at x\wg at tmpb=\pgf at y%
   \def\n at to@pp at neutr@l at left  {\pgflineto{\pgfqpoint{-\wg at tmpa}{-\wg at tmpb}}}%
@@ -1111,6 +1176,29 @@
     \pgfusepath{stroke}%
   }
 }
+\pgfdeclareshape{natoapp6c neutral dismounted}{%
+  \inheritsavedanchors[from=natoapp6c neutral land]
+  \inheritanchor[from=natoapp6c neutral land]{center}
+  \inheritanchor[from=natoapp6c neutral land]{inner north east}
+  \inheritanchor[from=natoapp6c neutral land]{inner north west}
+  \inheritanchor[from=natoapp6c neutral land]{inner south west}
+  \inheritanchor[from=natoapp6c neutral land]{inner south east}
+  \inheritanchor[from=natoapp6c neutral land]{north east}
+  \inheritanchor[from=natoapp6c neutral land]{north west}
+  \inheritanchor[from=natoapp6c neutral land]{south east}
+  \inheritanchor[from=natoapp6c neutral land]{south west}
+  \inheritanchor[from=natoapp6c neutral land]{north}
+  \inheritanchor[from=natoapp6c neutral land]{west}
+  \inheritanchor[from=natoapp6c neutral land]{east}
+  \inheritanchor[from=natoapp6c neutral land]{south}
+  \inheritanchor[from=natoapp6c neutral land]{upper}
+  \inheritanchor[from=natoapp6c neutral land]{lower}
+  \inheritanchor[from=natoapp6c neutral land]{left}
+  \inheritanchor[from=natoapp6c neutral land]{right}
+  \inheritanchor[from=natoapp6c neutral land]{center}
+  \inheritbackgroundpath[from=natoapp6c neutral land]
+  \inheritbehindbackgroundpath[from=natoapp6c neutral land]
+}
 \def\n at to@pp at unknown@init{%
   \def\n at to@pp at unknown@top{%
     \innernortheast \wg at tmpa=\pgf at x%
@@ -1485,6 +1573,28 @@
     \n at to@pp at unknown@right  %
     \pgfusepath{stroke}}
 }
+\pgfdeclareshape{natoapp6c unknown dismounted}{%
+  \inheritsavedanchors[from=natoapp6c unknown land]
+  \inheritanchor[from=natoapp6c unknown land]{inner north east}
+  \inheritanchor[from=natoapp6c unknown land]{inner north west}
+  \inheritanchor[from=natoapp6c unknown land]{inner south west}
+  \inheritanchor[from=natoapp6c unknown land]{inner south east}
+  \inheritanchor[from=natoapp6c unknown land]{north east}
+  \inheritanchor[from=natoapp6c unknown land]{north west}
+  \inheritanchor[from=natoapp6c unknown land]{south east}
+  \inheritanchor[from=natoapp6c unknown land]{south west}
+  \inheritanchor[from=natoapp6c unknown land]{north}
+  \inheritanchor[from=natoapp6c unknown land]{west}
+  \inheritanchor[from=natoapp6c unknown land]{east}
+  \inheritanchor[from=natoapp6c unknown land]{south}
+  \inheritanchor[from=natoapp6c unknown land]{upper}
+  \inheritanchor[from=natoapp6c unknown land]{lower}
+  \inheritanchor[from=natoapp6c unknown land]{left}
+  \inheritanchor[from=natoapp6c unknown land]{right}
+  \inheritanchor[from=natoapp6c unknown land]{center}
+  \inheritbackgroundpath[from=natoapp6c unknown land]
+  \inheritbehindforegroundpath[from=natoapp6c unknown land]
+}
 \def\n at to@pp at e@y{.12}
 \def\n at to@pp at e@yy{.24}
 \def\n at to@pp at e@d#1{($(#1*\n at to@pp at e@y,0)$) circle(0.09)}
@@ -1574,6 +1684,7 @@
   fac/.store in=\natoapp at fac,
   cmd/.store in=\natoapp at cmd,
   ech/.store in=\natoapp at ech,
+  path/.store in=\n at to@ppp at th
 }
 \tikzset{
   /natoapp6c/.cd,
@@ -1602,6 +1713,7 @@
   command/space/.style={cmd=space},
   command/sub surface/.style={cmd=sub surface},
   command/sea mine/.style={cmd=sub surface},
+  command/dismounted/.style={cmd=dismounted},
   command/none/.style={cmd=none},
 }
 \tikzset{
@@ -1656,7 +1768,7 @@
 \tikzset{%
   natoapp6c/.code={%
     \pgfkeys{/tikz/transform shape,/tikz/shape=natoapp6c}
-    \pgfkeys{/natoapp6c/.cd,#1}}}
+    \pgfkeys{/natoapp6c/.cd,path=natoapp6c/s/,#1}}}
 \newcounter{natoappid}\setcounter{natoappid}{0}
 \newif\ifn at to@pp at below\n at to@pp at belowfalse%
 \newif\ifn at to@pp at mod\n at to@pp at modfalse%
@@ -1685,6 +1797,7 @@
       \edef\frameopt{\natoapp at frame}}
     \n at to@pp at dbg{3}{NATO App6(c) Frame options: \meaning\frameopt}%
   }
+  \savedmacro\thisname{\def\thisname{natoapp6c}}
   \anchor{center}    {\center}
   \anchor{north east}{\wg at sub@nchor{M\id}{north east}}
   \anchor{north west}{\wg at sub@nchor{M\id}{north west}}
@@ -1699,17 +1812,26 @@
   \anchor{left}      {\wg at sub@nchor{M\id}{left}}
   \anchor{right}     {\wg at sub@nchor{M\id}{right}}
   \anchor{echelon}   {%
-    \n at to@pp at dbg{3}{NATO App6(c) get echelon anchor}%
-    \wg at sub@nchor{M\id}{north}%
-    \wg at tmpa=\n at to@pp at e@y cm%
+    \wg at sub@nchor{M}{north}%
+    \wg at tmpa\pgflinewidth% by 1.5%
+    %\wg at getscale%
+    %\pgfmathparse{ifthenelse(\wg at scale>0.00001,\wg at tmpa/\wg at scale,\wg at tmpa)}
+    %\edef\wg at a{\pgfmathresult}\pgfmathsetlength{\wg at tmpa}{\wg at a pt}
+    %\n at to@pp at dbg{0}{Scale is `\wg at scale' -> `\the\wg at tmpa'}%
+    %\divide\wg at tmpa by \wg at scale%
+    \advance\wg at tmpa by\n at to@pp at e@y cm%
     \advance\pgf at y\wg at tmpa%
-  }%
+  }
   \anchor{below}     {%
     \n at to@pp at dbg{3}{NATO App6(c) get below anchor}%
-    \wg at sub@nchor{M\id}{south}
+    \wg at sub@nchor{M}{south}%
     \wg at tmpa=\n at to@pp at e@yy cm%
-    \advance\pgf at y-\wg at tmpa}
+    \advance\wg at tmpa-\pgflinewidth% by 1.5%
+    \advance\pgf at y-\wg at tmpa%
+  }
   \behindbackgroundpath{%
+    \wg at ignore@sub at nchortrue%
+    \n at to@pp at dbg{3}{NATO App6(c) Before print}
     \n at to@pp at dbg{3}{NATO App6(c) background path: \meaning\id
       ^^J  ID:      \meaning\natoapp at id
       ^^J  Faction: \meaning\natoapp at fac
@@ -1734,7 +1856,7 @@
       %% Clip to shape in scope
       %% \message{^^JClipping to NATO App6(c) shape}
       \n at to@pp at iscliptrue%
-      \n at to@pp at dbg{2}{NATO App6(c) frame node M (clip)}
+      \n at to@pp at dbg{1}{NATO App6(c) frame node M (clip)}
       \pgfnode{natoapp6c \frameshape}{center}{}{M}{\pgfusepath{clip}}
       \n at to@pp at isclipfalse%
       %% Start new scope including frame key options
@@ -1752,9 +1874,10 @@
       \endscope%
       % Render mains
       \@ifundefined{natoapp at main}{}{
-        \n at to@pp at dbg{2}{NATO App6(c) mains: \meaning\natoapp at main}
+        \n at to@pp at dbg{1}{NATO App6(c) mains: \meaning\natoapp at main}
         \begin{scope}[natoapp6c/main]
-          \wg at pic@all{\natoapp at main}{natoapp6c/s/}{M.center}{natoapp6c/main}%
+          \n at to@pp at dbg{1}{NATO symbol path `\n at to@ppp at th'}
+          \wg at pic@all{\natoapp at main}{\n at to@ppp at th}{M.center}{natoapp6c/main}%
         \end{scope}}%
       % Modififiers flagged
       \n at to@pp at modtrue
@@ -1788,41 +1911,51 @@
       \endpgfinterruptboundingbox
     \end{scope}%
     \fi%
+    \wg at ignore@sub at nchorfalse%
   }
   \behindforegroundpath{%
-    \n at to@pp at dbg{2}{NATO App6(c) foreground path:
+    \wg at ignore@sub at nchortrue%
+    \n at to@pp at dbg{2}{NATO App6(c) foreground path: `\meaning\id'
       ^^J  Echelon: \meaning\natoapp at ech
       ^^J  Symbol:  \meaning\frameshape
       ^^J  Below:   \meaning\natoapp at below
       ^^J  Frame:   \meaning\frameopt}
     %
+    %
     \ifx\frameshape\pgfutil at empty%
       \n at to@pp at dbg{2}{NATO App6(c) has no frame shape!}%
     \else%
-
-    \edef\tmp at opt{[\frameopt]}
-    \expandafter\scope\tmp at opt
-    \n at to@pp at dbg{2}{NATO App6(c) inner node `M\id' ===}
-    \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}}
+      \edef\tmp at opt{[\frameopt]}
+      \expandafter\scope\tmp at opt
+      \edef\mid{M\id{}}
+      \n at to@pp at dbg{1}{NATO App6(c) inner node `M\id' ===}
+      \pgfnode{natoapp6c \frameshape}{center}{}{M\id}{\pgfusepath{stroke}}
+      \wg at ignore@sub at nchorfalse%
     % Put in the echelon
-    \@ifundefined{natoapp at ech}{}{%
-      \ifx\natoapp at ech\pgfutil at empty\else%
-      \def\args{echelon=\natoapp at ech}
-      \expandafter\wg at pic\args\@endwg at pic%
-      {natoapp6c/s/}{$(M.north)+(0,1.2*\n at to@pp at e@y)$}{natoapp6c/echelon}
-      \fi%
-    }
-    % Put in stuff below main
-    \@ifundefined{natoapp at below}{}{%
-      \n at to@pp at belowtrue
-      \begin{scope}
-        \wg at pic@all{\natoapp at below}{natoapp6c/s/}{%
-          $(M.south)+(0,-\n at to@pp at e@yy)$}{%
-          natoapp6c/below}%
-      \end{scope}%
-      \n at to@pp at belowfalse}
-    \ifnatoapp at decoy%
-      \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]%
+      \n at to@pp at dbg{2}{Echelon}
+      \@ifundefined{natoapp at ech}{}{%
+        \ifx\natoapp at ech\pgfutil at empty\else%
+          \def\args{echelon=\natoapp at ech}
+          \n at to@pp at dbg{3}{Make the echelon}%
+          \wg at nchor{\thisname}{echelon}%
+          \expandafter\wg at pic\args\@endwg at pic%
+          {natoapp6c/s/}{\pgf at x,\pgf at y}{natoapp6c/echelon}
+        \fi%
+      }
+      % Put in stuff below main
+      \n at to@pp at dbg{2}{Below}
+      \@ifundefined{natoapp at below}{}{%
+        \n at to@pp at belowtrue
+        \begin{scope}
+          \n at to@pp at dbg{3}{Make below}%
+          \wg at nchor{\thisname}{below}%
+          \wg at pic@all{\natoapp at below}{natoapp6c/s/}{\pgf at x,\pgf at y}{%
+            natoapp6c/below}%
+        \end{scope}%
+        \n at to@pp at belowfalse}
+      \n at to@pp at dbg{2}{Decoy}
+      \ifnatoapp at decoy%
+        \scope[dash pattern=on 3\pgflinewidth off 2\pgflinewidth]%
         \n at to@pp at dbg{1}{Drawing decoy modifier}%
         \wg at sub@nchor{M\id}{north east}
         \wg at tmpa=\pgf at x%
@@ -1834,10 +1967,11 @@
         \pgfpathlineto{\pgfqpoint{0cm}{\wg at tmpc}}%
         \pgfpathlineto{\pgfqpoint{-\wg at tmpa}{\wg at tmpb}}%
         \pgfusepath{stroke}%
+        \endscope%
+      \fi%
       \endscope%
     \fi%
-    \endscope%
-    \fi%
+    \wg at ignore@sub at nchorfalse%
   }
 }
 \newcommand\natoapp[1][]{%
@@ -1850,13 +1984,129 @@
   \@ifnextchar({\n at to@pp{#1}{#2}}{\n at to@pp{#1}{#2}()}%)
 }
 \def\n at to@pp#1#2(#3){%
-  %\let\name\pgfutil at empty%
-  %\ifx|#3|\else\edef\name{(#3)}\fi%
-  %\n at to@pp at dbg{3}{Arguments: #1}%
-  %\edef\args{[natoapp6c={#1},transform shape] \name at (#2) {}}
-  %\expandafter\node\args;%
   \node[draw,transform shape,natoapp6c={#1}] (#3) at (#2) {};%
   \@ifnextchar;{\@gobble}{}}
+\tikzset{
+  kriegspiel symbol/.code={
+    \pgfkeys{%
+      /tikz/transform shape,%
+      /tikz/shape=kriegspiel symbol}
+    \n at to@pp at dbg{10}{Args for symbol `#1'}
+    \pgfkeys{/natoapp6c/.cd,%
+      path=kriegspiel/s/,%
+      #1}
+  }
+}
+\pgfdeclareshape{kriegspiel symbol}{
+  \inheritsavedanchors[from=natoapp6c]
+  \savedmacro\frameshape{%
+    \def\frameshape{kriegspiel}}
+  \inheritanchor[from=natoapp6c]{center}
+  \inheritanchor[from=natoapp6c]{north east}
+  \inheritanchor[from=natoapp6c]{north west}
+  \inheritanchor[from=natoapp6c]{south east}
+  \inheritanchor[from=natoapp6c]{south west}
+  \inheritanchor[from=natoapp6c]{north}
+  \inheritanchor[from=natoapp6c]{west}
+  \inheritanchor[from=natoapp6c]{south}
+  \inheritanchor[from=natoapp6c]{east}
+  \inheritanchor[from=natoapp6c]{upper}
+  \inheritanchor[from=natoapp6c]{lower}
+  \inheritanchor[from=natoapp6c]{left}
+  \inheritanchor[from=natoapp6c]{right}
+  \anchor{echelon}   {%
+    \wg at sub@nchor{M\id}{north west}%
+    \wg at tmpa=\n at to@pp at e@y cm%
+    \divide\wg at tmpa by 2%
+    \advance\pgf at y\wg at tmpa%
+  }%
+  \inheritbackgroundpath[from=natoapp6c]
+  \inheritbehindforegroundpath[from=natoapp6c]
+  \inheritbehindbackgroundpath[from=natoapp6c]
+  \savedmacro\thisname{\def\thisname{kriegspiel symbol}}
+}
+\pgfdeclareshape{natoapp6c kriegspiel}{
+  \inheritsavedanchors[from=rectangle]
+  \inheritbackgroundpath[from=rectangle]
+  \savedanchor\northeast{\pgf at x=1.4cm\pgf at y=.3125cm}
+  \savedanchor\southwest{\pgf at x=-1.4cm\pgf at y=-.3125cm}
+  \inheritanchor[from=rectangle]{center}
+  \inheritanchor[from=rectangle]{north east}
+  \inheritanchor[from=rectangle]{north west}
+  \inheritanchor[from=rectangle]{south east}
+  \inheritanchor[from=rectangle]{south west}
+  \inheritanchor[from=rectangle]{north}
+  \inheritanchor[from=rectangle]{west}
+  \inheritanchor[from=rectangle]{south}
+  \inheritanchor[from=rectangle]{east}
+  \backgroundpath{}
+}
+\tikzset{
+  ks debug frame/.style={
+    draw=none,
+    %draw=magenta,
+    %dashed,
+  },
+  kriegspiel/s/infantry/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[pic actions,fill=none](a)--(b);
+    \path[pic actions,fill=none] let
+    \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2);
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in (-.05,\y2)rectangle++(.1,.2);
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in
+    (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle;
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  }
+}
+
 \providecommand\natoappmark[2][]{%
   \tikz[transform shape,
   scale=.25,
@@ -3110,6 +3360,11 @@
   natoapp6c/s/reconnaissance/.pic={%
     \path[draw] (M.north east)--(M.south west);},
 }
+\tikzset{
+  natoapp6c/s/reconnaissance artillery/.pic={
+    \path[draw] (M.north east)--(M.south west);,
+    \path[pic actions,fill=pgfstrokecolor] circle(0.2);},
+}
 \tikzset{%
   natoapp6c/s/recovery unmanned systems/.pic={%
     \path[draw] (-.5,.15) to [out=-80,in=180] (0,-.15) to
@@ -3650,6 +3905,79 @@
     (0,.15) circle(.07 and .1);
   },
 }
+\tikzset{
+  pics/kriegspiel/s/.unknown/.code={
+    \message{^^JTry regular `natoapp6c/s/\pgfkeyscurrentname'}
+    % \pic[pic actions,draw=black]{natoapp6c/s/\pgfkeyscurrentname};
+    \pgfkeysalso{/tikz/pics/natoapp6c/s/\pgfkeyscurrentname/.try}
+  },
+  ks debug frame/.style={
+    draw=none,
+    %draw=magenta,
+    %dashed,
+  },
+  kriegspiel/s/infantry/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[pic actions,fill=none](a)--(b);
+    \path[pic actions,fill=none] let
+    \p1=(a),\p2=(b) in (\x2,\y1)--(\x1,\y2);
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in (-.05,\y2)rectangle++(.1,.2);
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.2)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \path[fill=pgfstrokecolor] let
+    \p2=(b) in
+    (-.05,\y2)--++(0,.2)--++(-10:.4)--++(-170:.3)--(.05,\y2)--cycle;
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    %\path[pic actions](a)rectangle(b);
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  },
+  kriegspiel/s/reconnaissance artillery/.pic={
+    \coordinate(a) at (M.south west);
+    \coordinate(b) at ($(M.north east)+(0,-.5)$);
+    \@ifundefinedcolor{pgffillcolor}{}{
+      \path[fill=pgffillcolor](a)rectangle(b);}
+    \path[pic actions,fill=none](a)rectangle(b);
+    %\path[pic actions](a)rectangle(b);
+    \path[fill=pgfstrokecolor] (a)--(b)--(a|-b)--cycle;
+    \foreach \s in {-.8,0,.8}{
+      \path[fill=pgfstrokecolor] let
+      \p2=(b),%
+      \n1={\y2+\pgflinewidth} in
+      (\s-.04,\y2)rectangle++( .08,.5)
+      ($(\s-.18,\n1)+(0,.05)$)rectangle++( .08,.2)
+      ($(\s+.18,\n1)+(0,.05)$)rectangle++(-.08,.2);
+    }
+    \draw[ks debug frame](M.south west)rectangle(M.north east);
+  }
+}
+
 \def\n at to@pp at s@ll{
   weapon=base,
   weapon=top,

Modified: trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/tikzlibrarywargame.util.code.tex	2024-11-19 20:38:26 UTC (rev 72903)
@@ -92,9 +92,18 @@
   \toks@\expandafter\expandafter\expandafter{\expandafter#1#2}%
   \xdef#1{\the\toks@}%
   \endgroup}
+\def\wg at nchor#1#2{% shape name, anchor
+  \wg at dbg{10}{Get `#2' in `#1'}%
+  \expandafter\csname pgf at anchor@#1@#2\endcsname%
+  \wg at dbg{10}{Got `\pgf at x',`\pgf at y'}
+}
+\newif\ifwg at ignore@sub at nchor\wg at ignore@sub at nchorfalse%
 \def\wg at sub@nchor#1#2{%
-  \wg at dbg{3}{^^JGet `#2' in `#1'}%
+  \wg at dbg{10}{^^JGet `#2' in `#1'}%
   \@ifundefined{pgf at sh@ns@#1}{%
+    \ifwg at ignore@sub at nchor%
+      \wg at dbg{0}{WARNING: Shape `#1' not defined, anchor `#2' unknown}
+    \fi
     \pgf at x=0cm\pgf at y=0cm}{%
     \pgf at process{%
       \csname pgf at sh@ma@#1\endcsname% MW

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.beach.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.city.pdf
===================================================================
(Binary files differ)

Added: trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf	2024-11-19 20:38:26 UTC (rev 72903)

Property changes on: trunk/Master/texmf-dist/tex/latex/wargame/wargame.fields.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.light_woods.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.mountains.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.rough.pdf
===================================================================
(Binary files differ)

Added: trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf	2024-11-19 20:38:26 UTC (rev 72903)

Property changes on: trunk/Master/texmf-dist/tex/latex/wargame/wargame.speckle.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.swamp.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.town.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.village.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wargame.woods.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wgexport.cls
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/wgexport.cls	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/wgexport.cls	2024-11-19 20:38:26 UTC (rev 72903)
@@ -73,7 +73,7 @@
   \mk at w{ \space "number": \thepage #1}%
   \let\oldmk at i\mk at i%
   \ifx#1,\relax\edef\mk at i{\mk at i\space\space}\fi}
-\def\end at info{%
+\def\endinfo{%
   \let\mk at i\oldmk at i%
   \mk at w{ \space \@rbchar,}}
 \def\wg at add@drop at margin{%
@@ -104,30 +104,37 @@
       \foreach \u/\m in \t{%
         \ifx\u\empty\else% Ignore empty cells
           \ifx\u\chit at blank\else%
-            \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
-            \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined
-            \ifx\u\m\def\m{1}\fi% If the same as unit
-            \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
-            %% We only make one copy of the chit, since we can duplicate
-            %% it in VASSAL
-            \info*{\u}{counter}{\x}
-            \nopagecolor%
-            \gdef\wg at drop@margin{0pt}%
-            \begin{tikzpicture}[chit has drop=false,#2]
-              \chit[\u=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}
-            \end at info%
-            %% \foreach \n in {1,...,\m}{% Make a number of copies
-            %%   \ifx\u\chit at blank%
-            %%     \chit at dbg{3}{Ignoring blank chit:\u}%
-            %%   \else%
-            %%     \info{\u}{counter}{#2}
-            %%     \begin{tikzpicture}
-            %%       \chit[\u=\ti](\c,\r)%
-            %%     \end{tikzpicture}
-            %%   \fi%
-            %% }%
+            \ifx\u\chit at oob@vspacer\else%
+              \ifx\u\chit at oob@spacer\else%
+                \chit at dbg{2}{Next chit `\u' with possible multiplicity
+                  `\m'}%
+                \chit at dbg{1}{Next chit `\u' not `\chit at blank',
+                  `\chit at oob@vspacer', `\chit at oob@spacer'}
+                \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined
+                \ifx\u\m\def\m{1}\fi% If the same as unit
+                \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
+                %% We only make one copy of the chit, since we can duplicate
+                %% it in VASSAL
+                \begin{info}*{\u}{counter}{\x}
+                  \nopagecolor%
+                  \gdef\wg at drop@margin{0pt}%
+                  \begin{tikzpicture}[chit has drop=false,#2]
+                    \chit[\u=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}
+                \end{info}%
+                %% \foreach \n in {1,...,\m}{% Make a number of copies
+                %% \ifx\u\chit at blank%
+                %%   \chit at dbg{3}{Ignoring blank chit:\u}%
+                %% \else%
+                %%   \info{\u}{counter}{#2}
+                %%   \begin{tikzpicture}
+                %%     \chit[\u=\ti](\c,\r)%
+                %%   \end{tikzpicture}
+                %% \fi%
+                %% }%
+              \fi%
+            \fi%
           \fi%
         \fi%
       }%
@@ -155,39 +162,43 @@
       \foreach \u/\m in \t{%
         \ifx\u\empty\else% Ignore empty cells
           \ifx\u\chit at blank\else%
-            \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
-            \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined
-            \ifx\u\m\def\m{1}\fi% If the same as unit
-            \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
-            %% Flipped chit
-            \edef\s{\u\space flipped}%
-            %% We only make one copy of the chit, since we can duplicate
-            %% it in VASSAL
-            \info*{\u}{counter}{\x}%
-            \nopagecolor%
-            \gdef\wg at drop@margin{0pt}%
-            \begin{tikzpicture}[chit has drop=false,#2]%
-              \chit[\u=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}%
-            \end at info%
-            \info*{\s}{counter}{\x}%
-            \nopagecolor%
-            \begin{tikzpicture}[chit has drop=false,#2]%
-              \chit[\s=\ti]%
-              \wg at add@drop at margin%
-            \end{tikzpicture}%
-            \end at info%
-            %% \foreach \n in {1,...,\m}{% Make a number of copies
-            %%   \ifx\u\chit at blank%
-            %%     \chit at dbg{3}{Ignoring blank chit:\u}%
-            %%   \else%
-            %%     \info{\u}{counter}{#2}
-            %%     \begin{tikzpicture}
-            %%       \chit[\u=\ti](\c,\r)%
-            %%     \end{tikzpicture}
-            %%   \fi%
-            %% }%
+            \ifx\u\chit at oob@vspacer\else%
+              \ifx\u\chit at oob@spacer\else%
+                \chit at dbg{2}{Next chit `\u' with possible multiplicity `\m'}%
+                \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined
+                \ifx\u\m\def\m{1}\fi% If the same as unit
+                \chit at dbg{2}{Next chit `\u' multiplicity `\m'}%
+                %% Flipped chit
+                \edef\s{\u\space flipped}%
+                %% We only make one copy of the chit, since we can duplicate
+                %% it in VASSAL
+                \begin{info}*{\u}{counter}{\x}%
+                  \nopagecolor%
+                  \gdef\wg at drop@margin{0pt}%
+                  \begin{tikzpicture}[chit has drop=false,#2]%
+                    \chit[\u=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}%
+                \end{info}%
+                \begin{info}*{\s}{counter}{\x}%
+                  \nopagecolor%
+                  \begin{tikzpicture}[chit has drop=false,#2]%
+                    \chit[\s=\ti]%
+                    \wg at add@drop at margin%
+                  \end{tikzpicture}%
+                \end{info}%
+                %% \foreach \n in {1,...,\m}{% Make a number of copies
+                %% \ifx\u\chit at blank%
+                %%   \chit at dbg{3}{Ignoring blank chit:\u}%
+                %% \else%
+                %%   \info{\u}{counter}{#2}
+                %%   \begin{tikzpicture}
+                %%     \chit[\u=\ti](\c,\r)%
+                %%   \end{tikzpicture}
+                %% \fi%
+                %% }%
+              \fi%
+            \fi%
           \fi%
         \fi%
       }%
@@ -371,10 +382,10 @@
   \mk at w{ \mk at i "name": "\bd at n" }%
   \let\mk at i\oomk at i%
   \mk at w{ \mk at i\@rbchar}%
-  \end at info%
+  \endinfo%
 }
 \def\wg at gennumberm@rkers#1#2#3#4{
-  \message{^^JNumbered markers: Type=`#1' Max=`#2' Category=`#3'}
+  \chit at dbg{2}{Numbered markers: Type=`#1' Max=`#2' Category=`#3'}
   \def\markers{}
   \def\keys{}
   \foreach \i in {1,...,#2}{%
@@ -397,7 +408,7 @@
 }%
 \def\@@battlemarkers[#1][#2]#3{%
   \wg at gennumberm@rkers{battle marker}{#3}{#2}{#1}%
-  \message{^^JMake a hidden unit and add to Markers category}
+  \chit at dbg{1}{Make a hidden unit and add to Markers category}
   {%
     \nopagecolor%
     \chitimages[Markers]{{wg hidden unit}}%
@@ -417,7 +428,7 @@
   \foreach \o/\f/\n [count=\i] in {#2}{%
     \ifx\n\f\def\n{\o}\fi%
     \ifx\o\f\def\f{white}\fi%
-    \message{^^JColour no \i marker `#1 \n' w/fill `\f' text `\o'}%
+    \chit at dbg{3}{Colour no `\i' marker `#1 \n' w/fill `\f' text `\o'}%
     \protected at xdef\keys{/tikz/#1 \n/.style={/tikz/#1={\o,\f}},\keys}
     \xdef\markers{\markers,#1 \n}
   }%
@@ -477,22 +488,22 @@
   \info{unit-icon}{icon}{}%
   \tikz[auto icon,scale=.7,auto icon more/.try]{%
     \chit[fill=#2,
-      symbol={[
-        scale line widths,
-        line width=1pt,
-        faction=friend,
-        command=land,
-        main=infantry,
-        scale=1.3](0,-.15)}]}%
-    %
-    \info{layer-icon}{icon}{}%
-    \begin{tikzpicture}[scale=.25]
-      \foreach \i in {-1,0,1}{
-        \scoped[shift={(0,\i*.15)}]{
-          \draw[black,fill=white] (-.5,0)
-          --(0,.3)--(.5,0)--(0,-.3)--cycle;
-        }
+    symbol={[
+      scale line widths,
+      line width=1pt,
+      faction=friend,
+      command=land,
+      main=infantry,
+      scale=1.3](0,-.15)}]}%
+  %
+  \info{layer-icon}{icon}{}%
+  \begin{tikzpicture}[scale=.25]
+    \foreach \i in {-1,0,1}{
+      \scoped[shift={(0,\i*.15)}]{
+        \draw[black,fill=white] (-.5,0)
+        --(0,.3)--(.5,0)--(0,-.3)--cycle;
       }
+    }
     \end{tikzpicture}%
     %
     \info{los-icon}{icon}{}
@@ -521,8 +532,9 @@
       %\node[shape=#4,transform shape,draw=none,fill=black,opacity=.5]
       %at (.05,-.03){};
       \node[shape=#4,#2,transform shape,
-      chit drop
-      ]{\p};\wg at add@drop at margin{}}}}
+      chit drop,
+      node contents={\p}
+      ]{};\wg at add@drop at margin{}}}}
 \tikzset{
   zone turn/.store in=\zone at turn,
   zone mult/.store in=\zone at mult

Modified: trunk/Master/texmf-dist/tex/latex/wargame/wgexport.py
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/wgexport.py	2024-11-19 20:36:30 UTC (rev 72902)
+++ trunk/Master/texmf-dist/tex/latex/wargame/wgexport.py	2024-11-19 20:38:26 UTC (rev 72903)
@@ -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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PredefinedSetup
@@ -779,6 +782,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Option
@@ -1266,6 +1271,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Prototype
@@ -1279,6 +1285,7 @@
         ----------
         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 @@
                  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 @@
                      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 @@
 
     def getSymbolicDice(self):
         return self.getParent(SymbolicDice)
+
+    def getFaces(self):
+        return self.getAllElements(DieFace,single=False)
         
 registerElement(SpecialDie)
                      
@@ -1622,6 +1668,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : LayerControl
@@ -1977,10 +2024,11 @@
                  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 @@
                  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 @@
                       emptyHexReportForma   = emptyHexReportForma,
                       enableHTML            = enableHTML,
                       extraTextPadding      = extraTextPadding,
-                      fgColor               = fgColor,
+                      bgColor               = bg,
+                      fgColor               = fg,
                       fontSize              = fontSize,
                       graphicsZoom          = graphicsZoom,
                       hotkey                = hotkey,
@@ -2039,7 +2092,8 @@
                       version               = version,
                       verticalOffset        = verticalOffset,
                       verticalTopText       = verticalTopText,
-                      zoomlevel             = zoomlevel)
+                      zoomlevel             = zoomlevel,
+                      stopAfterShowing      = stopAfterShowing)
 
 registerElement(CounterDetailViewer)
 
@@ -2198,7 +2252,26 @@
                  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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Pieces
@@ -2235,6 +2309,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Piece
@@ -2264,7 +2339,7 @@
             Dictionary or list of `Piece` children
 
         '''
-        return self.getElementsWithKey(PieceSlot,'entryName',asdict)
+        return self.getElementsByKey(PieceSlot,'entryName',asdict)
 
 registerElement(AtStart)
 
@@ -2292,6 +2367,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Property
@@ -2347,6 +2423,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Level
@@ -2374,6 +2451,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Counter
@@ -2387,6 +2465,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : List
@@ -2495,6 +2574,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Hotkey
@@ -2626,6 +2706,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AboutScreen
@@ -2639,6 +2720,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HelpFile
@@ -2652,6 +2734,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BrowserHelpFile
@@ -2665,6 +2748,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BrowserPDFFile
@@ -2678,6 +2762,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Tutorial
@@ -2909,6 +2994,7 @@
                buttonText='Retire',
                buttonToolTip='Switch sides, become observer, or release faction'):
         '''Add a player roster to the module
+        
         Parameters
         ----------
         doc : Element
@@ -2931,6 +3017,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Side
@@ -3150,6 +3237,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AboutScreen
@@ -3180,6 +3268,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Tabs
@@ -3192,6 +3281,7 @@
         Parameters
         ----------
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Combo
@@ -3205,6 +3295,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Panel
@@ -3218,6 +3309,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : List
@@ -3231,6 +3323,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MapWidget
@@ -3244,6 +3337,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chart
@@ -3257,6 +3351,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceSlot
@@ -3270,6 +3365,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Piece
@@ -3288,6 +3384,7 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : WidgetMap
@@ -3483,6 +3587,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Numbering
@@ -3720,8 +3826,8 @@
                                         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Numbering
@@ -3892,6 +3999,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Region
@@ -4017,6 +4125,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Highlighter
@@ -4031,6 +4140,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Zone
@@ -4057,6 +4168,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ZoneHighlight
@@ -4093,6 +4206,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HexGrid
@@ -4223,6 +4338,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SquareGrid
@@ -4236,6 +4352,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : RegionGrid
@@ -4249,6 +4366,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Property
@@ -4263,6 +4381,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
     def getProperties(self):
         '''Get all `Property` element from this
 
-        Parameters
-        ----------
         Returns
         -------
         children : dict
@@ -4339,6 +4459,7 @@
             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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Setup
@@ -4411,6 +4533,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Board
@@ -4437,6 +4561,7 @@
         ----------
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ZonedGrid
@@ -4526,6 +4652,7 @@
         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 @@
         ----------
         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 @@
                  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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Picker
@@ -4646,6 +4834,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -4964,6 +5173,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -4978,6 +5188,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BoardPicker
@@ -4991,6 +5202,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StackMetrics
@@ -5004,6 +5216,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ImageSaver
@@ -5017,6 +5230,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : TextSaver
@@ -5030,6 +5244,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ForwardToChatter
@@ -5043,6 +5258,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MenuDisplayer
@@ -5056,6 +5272,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MapCenterer
@@ -5069,6 +5286,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StackExpander
@@ -5082,6 +5300,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceMover
@@ -5095,6 +5314,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SelectionHighlighters
@@ -5108,6 +5328,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyBufferer
@@ -5121,6 +5342,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HighlightLastMoved
@@ -5134,6 +5356,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : CounterDetailViewer
@@ -5147,6 +5370,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalMap
@@ -5160,6 +5384,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Zoomer
@@ -5173,6 +5398,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : HidePiecesButton
@@ -5186,6 +5412,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : MassKey
@@ -5199,6 +5426,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StartupMassKey
@@ -5212,6 +5440,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Flare
@@ -5225,6 +5454,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : AtStart
@@ -5240,6 +5470,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceLayers
@@ -5253,6 +5484,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -5266,6 +5498,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -5377,6 +5610,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chart
@@ -5388,12 +5622,16 @@
 
         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 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 @@
             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 @@
         '''
         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 @@
         self._state  = list(kwargs.values())
         self._snames = list(kwargs.keys())
 
-    
     def __getitem__(self,key):
         '''Look up item in either type or state'''
         try:
@@ -5574,7 +5816,7 @@
         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')
@@ -5581,32 +5823,71 @@
                 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 @@
         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 @@
             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
+
+        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
+        
+        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 @@
                 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.
+
+    def encodeAdd(self,*traits,iden='null',verbose=False):
+        '''Encodes type and states'''
+        if len(traits) < 1: return ''
         
-        Parameters
-        ----------
-        traits : tuple of Trait objects
-            The traits to set on this object.
-        iden : str
-            Identifier
-
-        '''
         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 @@
         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 @@
                      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 @@
 # 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 @@
             # 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 @@
                  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,18 +7265,25 @@
                  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
@@ -6840,7 +7290,7 @@
                      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 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : BasicCommandEncoder
@@ -7290,6 +7747,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalTranslatableMessages
@@ -7303,6 +7761,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PlayerRoster
@@ -7316,6 +7775,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PlayerRoster
@@ -7329,6 +7789,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Language
@@ -7342,6 +7803,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chatter
@@ -7355,6 +7817,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyNamer
@@ -7368,6 +7831,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Notes
@@ -7381,6 +7845,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Language
@@ -7394,6 +7859,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Chatter
@@ -7407,6 +7873,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : KeyNamer
@@ -7420,6 +7887,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalProperties
@@ -7433,6 +7901,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GlobalOptions
@@ -7446,6 +7915,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : TurnTrack
@@ -7459,6 +7929,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Documentation
@@ -7472,6 +7943,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Prototypes
@@ -7485,6 +7957,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PieceWindow
@@ -7498,6 +7971,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ChartWindow
@@ -7511,6 +7985,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Inventory
@@ -7524,6 +7999,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Map
@@ -7537,6 +8013,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : DiceButton
@@ -7550,6 +8027,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : PredefinedSetup
@@ -7563,6 +8041,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : GameMassKey
@@ -7576,6 +8055,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : StartupMassKey
@@ -7589,6 +8069,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Menu
@@ -7602,6 +8083,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : SymbolicDice
@@ -7619,6 +8101,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
         ----------
         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 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7948,6 +8453,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7965,6 +8471,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -7982,6 +8489,7 @@
             elements.  If `False`, return a list of all Board`
             children.
 
+        
         Returns
         -------
         children : dict or list
@@ -8014,6 +8522,7 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Game
@@ -8096,6 +8606,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Version
@@ -8109,6 +8620,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : VASSALVersion
@@ -8122,6 +8634,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Name
@@ -8135,6 +8648,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Description
@@ -8148,6 +8662,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : DateSaved
@@ -8162,6 +8677,7 @@
         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 @@
             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 @@
             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 @@
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : Data
@@ -8928,6 +9448,18 @@
     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 @@
             File name in module
         content : str
             File content
+        
         Returns
         -------
         element : File
@@ -8963,6 +9496,7 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : ExternalFile
@@ -8974,6 +9508,14 @@
     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 @@
         ----------
         filenames : tuple
             The files to get 
+        
         Returns
         -------
         files : dict
@@ -9009,7 +9552,7 @@
 
         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 @@
         ----------
         kwargs : dict
             Dictionary of attribute key-value pairs
+        
         Returns
         -------
         element : VSav
@@ -9043,6 +9587,187 @@
 # 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,6 +9862,80 @@
 # 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',
@@ -9143,6 +9942,7 @@
                  infoname      = 'export.json',
                  title         = 'Draft',
                  version       = 'draft',
+                 imageFormat   = 'png',
                  description   = '',     
                  rules         = None,
                  tutorial      = None,
@@ -9197,76 +9997,80 @@
         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 @@
             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 @@
         ----------
         args : list
             List of process command line elements
+        
         Returns
         -------
         pipe : subprocess.Pipe
@@ -9320,16 +10127,18 @@
     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 @@
         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 @@
         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 @@
             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 @@
         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 @@
         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,12 +10465,22 @@
         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)
+                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
     
-        with Image.open(BytesIO(buffer)) as img:
-            w, h  = img.width, img.height
-    
         return w,h
     
     # ----------------------------------------------------------------
@@ -9591,11 +10530,13 @@
             
                 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 @@
                     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 @@
                 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 @@
                         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 @@
                     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 @@
                            [['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 @@
                 wrap        = True,
                 description = 'Zero battle counter',
             ),
+            # Set global property combat # from this 
             GlobalPropertyTrait(
                 ['',self._setBattle,GlobalPropertyTrait.DIRECT,
                  f'{{{self._battleCounter}}}'],
@@ -10411,6 +11367,8 @@
         #   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 @@
                                  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 @@
                            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 @@
     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 @@
                   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 @@
         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 @@
                               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 @@
         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 @@
         
         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 @@
             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 @@
             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 @@
                                            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,7 +12292,8 @@
                 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',
@@ -11273,7 +12300,7 @@
                                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 @@
                                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 @@
                 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 @@
                                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 @@
             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 @@
         '''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 @@
             
                 # 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,6 +13285,37 @@
         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
 #
@@ -12233,113 +13322,180 @@
 # ====================================================================
 # 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)
+    if args.mode == 'patch':
+        patchIt(args)
+    else:
+        exportIt(args)
         
-    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
-        
 #
 # EOF
 #

Added: trunk/Master/texmf-dist/tex/latex/wargame/wgmakenato.py
===================================================================
--- trunk/Master/texmf-dist/tex/latex/wargame/wgmakenato.py	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/wargame/wgmakenato.py	2024-11-19 20:38:26 UTC (rev 72903)
@@ -0,0 +1,756 @@
+#!/usr/bin/env python
+
+# ====================================================================
+def ensure_list(what):
+    if not what:
+        return None
+
+    if isinstance(what,str):
+        return [what]
+
+    return what
+
+# --------------------------------------------------------------------
+def clean(base, suffixes):
+    from pathlib import Path
+
+    if not isinstance(base,Path):
+        base = Path(base)
+        
+    for suffix in suffixes:
+        tgt = base.with_suffix(suffix)
+        tgt.unlink(missing_ok=True)
+
+# ====================================================================
+possible_commands = ['air',
+                     'land',
+                     'equipment',
+                     'installation',
+                     'sea surface',
+                     'sub surface',
+                     'space',
+                     'activity',
+                     'dismounted']
+possible_factions = ['friendly',
+                     'hostile',
+                     'neutral',
+                     'unknown']
+echelon_mapping = {'':       'team',
+                   '*':      'squad',
+                   '**':     'section',
+                   '***':    'platoon',
+                   '|':      'company',
+                   '||':     'battalion',
+                   '|||':    'regiment',
+                   'x':      'brigade',
+                   'xx':     'division',
+                   'xxx':    'corps',
+                   'xxxx':   'army',
+                   'xxxxx':  'army group',
+                   'xxxxxx': 'theater'}
+possible_echelons = list(echelon_mapping.values())+list(echelon_mapping.keys())
+        
+# ====================================================================
+def make_picture(command,
+                 faction,
+                 echelon,
+                 main,
+                 top=None,
+                 bottom=None,
+                 left=None,
+                 right=None,
+                 below=None,
+                 decoy=False,
+                 line_width='1pt',
+                 scale=.45,
+                 more=None,
+                 **kwargs):
+    '''Create latex code for the NATO App6 symbol - a single tikzpicture
+
+    Parameters
+    ----------
+    command : str
+        Command (base symbol)
+    faction : str
+        Faction (base symbol)
+    echelon : str or None
+        Echelon (top of base symbol
+    main : str or list of str
+        Main symbols
+    top : str or list of str
+        Top modifiers
+    bottom : str or list of str
+        Bottom modifiers
+    left : str or list of str
+        Left modifiers
+    right : str or list of str
+        Right modifiers
+    below : str or list of str
+        Below base frame
+    decoy : bool
+        True if it should be a decoy
+    line_width : str
+        Line width, including unit
+    scale : float
+        Scaling of image
+
+    Return
+    ------
+    out : str
+        LaTeX code to write to file 
+    '''
+    ind = '               '
+    def add_line(out,key,what,ind=ind):
+        '''Adds a line with a key'''
+        lwhat = ensure_list(what)
+        if not lwhat:
+            return out
+
+        swhat = ",".join(lwhat)
+
+        return out+f'{ind}    {key}={{{swhat}}},%'+'\n'
+
+        
+    out = fr'''\begin{{tikzpicture}}%
+                 \natoapp[%
+                   scale={scale},%
+                   scale line widths,%
+                   line width={line_width},%
+                   command={command},%
+                   faction={faction},%'''+'\n'
+
+    out = add_line(out,'echelon',echelon)
+    out = add_line(out,'main',   main)
+    out = add_line(out,'top',    top)
+    out = add_line(out,'bottom', bottom)
+    out = add_line(out,'left',   left)
+    out = add_line(out,'right',  right)
+    out = add_line(out,'below',  below)
+    if more:
+        out += f'{ind}    {more}%'+'\n'
+        
+    out += fr'''    ];%
+                \end{{tikzpicture}}%
+             '''
+    lout = out.split('\n')
+    lout = [l[len(ind):] if l.startswith(ind) else l for l in lout]
+    return '\n'.join(lout)
+
+# --------------------------------------------------------------------
+def make_latex(db):
+    '''Create latex code for the all NATO App6 symbols -
+    a complete document
+
+    Parameters
+    ----------
+    db : list of dict
+        Configurations of each symbol
+
+    Return
+    ------
+    out : str
+        LaTeX code to write to file 
+    '''
+
+    ind = '              '
+    out = r'''\documentclass[tikz,11pt]{standalone}
+              \usepackage{wargame}
+              \begin{document}%'''+'\n'
+
+    for entry in db:
+        out += make_picture(**entry)
+
+    out += r'\end{document}'
+
+    lout = out.split('\n')
+    lout = [l[len(ind):] if l.startswith(ind) else l for l in lout]
+    return '\n'.join(lout)
+
+    
+# --------------------------------------------------------------------
+def make_name(command,
+              faction,
+              echelon,
+              main,
+              top=None,
+              bottom=None,
+              left=None,
+              right=None,
+              below=None,
+              decoy=False,
+              output=None,
+              **kwargs):
+    '''Create the file name
+    
+    Parameters
+    ----------
+    command : str
+        Command (base symbol)
+    faction : str
+        Faction (base symbol)
+    echelon : str or None
+        Echelon (top of base symbol
+    main : str or list of str
+        Main symbols
+    top : str or list of str
+        Top modifiers
+    bottom : str or list of str
+        Bottom modifiers
+    left : str or list of str
+        Left modifiers
+    right : str or list of str
+        Right modifiers
+    decoy : bool
+        True if it should be a decoy
+
+    Return
+    ------
+    out : str
+        Output file name
+    '''
+    if output:
+        return output.format(command=command,
+                             faction=faction,
+                             echelon=echelon)\
+                     .replace(' ','-')\
+                     .replace('=','-')
+        
+    def clean_sub(what):
+        from re import sub
+
+        res = what
+        while True:
+            old = res
+            res = sub(r'\[[^]]+\]','',res)
+            res = sub(r'\{[^}]+\}','',res)
+            if res == old:
+                break
+
+        return res
+            
+    def add_component(out, what,sep='-'):
+         
+        lwhat = ensure_list(what)
+        
+        if not lwhat:
+            return out
+
+        return out+'_'+sep.join([clean_sub(l) for l in lwhat])
+
+    def add_front(out,what,sep='_'):
+        if not what:
+            return out
+
+        if len(out) > 0: out += sep
+        return out + what
+    
+    out = ''
+    out = add_front(out,faction)
+    out = add_front(out,command.replace(' ','-'))
+    out = add_front(out,echelon.replace(' ','-'))
+    out = add_component(out,main)
+    out = add_component(out,top)
+    out = add_component(out,bottom)
+    out = add_component(out,left)
+    out = add_component(out,right)
+    out = add_component(out,below)
+
+    return out.replace(' ','-').replace('=','-')
+
+
+# ====================================================================
+default_latex_flags = ['-interaction=nonstopmode',
+		       '-file-line-error',
+		       '-synctex','15',
+		       '-shell-escape']
+default_latex_command = 'pdflatex'
+
+# --------------------------------------------------------------------
+def create_process(args,verbose=False):
+    '''Create a subprocess with arguments
+
+    Returns
+    -------
+    proc : Popen
+        Process with stdout and stderr as pipes (fifos)
+    '''
+    from os import environ
+    from subprocess import Popen, PIPE
+
+    if verbose:
+        print(f'Will run: "{" ".join(args)}"')
+        
+    return Popen(args,env=environ.copy(),stdout=PIPE,stderr=PIPE)
+
+# --------------------------------------------------------------------
+default_timeout = None
+default_keep    = False
+# --------------------------------------------------------------------
+def run_latex(file,
+              content,
+              latex_cmd    = default_latex_command,
+              latex_flags  = default_latex_flags,
+              timeout      = default_timeout,
+              keep         = default_keep,
+              verbose      = False):
+    '''Run latex on generated content
+
+    Parameters
+    ----------
+    filename : str
+        The file to write
+    content : str
+        Content to write to source file
+    latex_cmd : str
+        LaTeX command to run e.g., 'pdflatex'
+    latex_flags : list of str
+        Flags to pass to the LaTeX command line
+    timeout : int
+        Process timeout
+    keep : bool
+        If true, keep intermediate files
+
+    Returns
+    -------
+    None
+    '''
+    from pathlib import Path
+
+    fpath  = Path(file.name).with_suffix('')
+    csuf   = ['.aux','.log','.out','.synctex','.synctex.gz','.tex']
+        
+    file.write(content)
+    file.close()
+        
+    lflags = ensure_list(latex_flags)
+
+    args = [latex_cmd] + (lflags if lflags else + []) + \
+        [fpath.with_suffix('.tex').name]
+
+    proc = create_process(args,verbose=verbose)
+
+    try:
+        out, err = proc.communicate(timeout=timeout)
+
+        sout = out.decode()
+        if 'Error' in sout or \
+           'Emergency stop' in sout or \
+           not Path(fpath).with_suffix('.pdf').is_file():
+            raise RuntimeError(sout)
+        
+    except Exception as e:
+        proc.kill()
+        proc.communicate()
+        if not keep:
+            clean(base=fpath,suffixes=csuf+['.pdf'])
+
+        raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+
+                           str(e) + '\n--- Start LaTeX code -------\n'+
+                           content+ '\n--- End LaTeX code ---------')
+
+    if not keep:
+        clean(base=fpath,suffixes=csuf)
+
+# --------------------------------------------------------------------
+possible_formats   = ['png','svg','jpg','tiff']
+default_format     = possible_formats[0]
+default_resolution = 150
+
+# --------------------------------------------------------------------
+def run_cairo(filename,
+              npages,
+              format     = default_format,
+              resolution = default_resolution,
+              timeout    = default_timeout,
+              keep       = default_keep,
+              verbose    = False):
+    '''Run 'pdftocairo' on generated PDF
+
+    Parameters
+    ----------
+    filename : str
+        The file to write
+    format : str
+        Image format to write
+    resolution : int
+        Pixels Per Inch (PPI)
+    timeout : int
+        Process timeout
+    keep : bool
+        If true, keep intermediate files
+
+    Returns
+    -------
+    None
+    '''
+    from pathlib import Path
+
+    fpath = Path(filename)
+    args  = ['pdftocairo']
+    if format != 'svg':
+        args.extend(['-transp'])
+
+    args.extend(['-r',str(resolution),f'-{format}'])
+
+    inpdf = fpath.with_suffix('.pdf').name
+    
+    def runit(args,pdf=inpdf,outimg=None):
+        args.append(inpdf)
+        if outimg: args.append(outimg)
+        
+        proc = create_process(args,verbose)
+
+        try:
+            out, err = proc.communicate(timeout=timeout)
+              
+        except Exception as e:
+            proc.kill()
+            proc.communicate()
+            if not keep:
+                clean(base=fpath,suffixes=[f'.{format}','.pdf'])
+
+            raise RuntimeError(f'Failed on "{" ".join(args)}" with:\n'+e)
+
+    if format != 'svg':
+        runit(args)
+    else:
+        from math import ceil, log10
+
+        base    = fpath.stem
+        wn      = ceil(log10(npages))
+        for no in range(1,npages+1):
+            no_args = args.copy()
+            no_args.extend(['-f',str(no),'-l',str(no)])
+
+            runit(no_args,outimg=base+f'-{no:0{wn}d}.{format}')
+        
+    if not keep:
+        clean(base=fpath,suffixes=['.pdf'])
+
+# --------------------------------------------------------------------
+def make_images(db,
+                base_name,
+                format   = default_format,
+                verbose = False):
+    from math import ceil, log10
+    from pathlib import Path
+
+    results = []
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        output = entry['output']+'.'+format
+        inpath = Path(f'{base_name}-{no+1:0{wn}d}.{format}')
+
+        inpath.rename(output)
+        results.append(output)
+        if verbose:
+            print(f'{no+1:0{wn}d}/{ndb}: {output}')
+
+    return results
+    
+# --------------------------------------------------------------------
+default_xml = False
+
+# --------------------------------------------------------------------
+def write_xmls(db,
+               format  = default_format,
+               verbose = False):
+    '''Build an XML snippet of a prototype'''
+
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        if verbose:
+            print(f'{no+1:0{wn}d}/{ndb} ...{entry["output"]}.xml')
+
+        write_xml(**entry,format=format)
+
+# --------------------------------------------------------------------
+def write_xml(command = None,
+              faction = None,
+              echelon = None,
+              main    = None,
+              top     = None,
+              bottom  = None,
+              left    = None,
+              right   = None,
+              below   = None,
+              decoy   = False,
+              output  = None,
+              format  = default_format,
+              **kwargs):
+    '''Build an XML snippet of a prototype'''
+    from xml.dom.minidom import Document
+    from wgexport import LayerTrait, Prototype, MarkTrait, \
+        BasicTrait, BuildFile
+
+    doc = BuildFile()
+    hasEchelon = echelon and len(echelon) > 0
+    
+    yoff   = -8 if main else -10 if echelon else -8
+    traits = [
+        LayerTrait(
+            images   = [f'{output}.{format}'],
+            newNames = [''],
+            activateName = '',
+            activateMask = '',
+            activateChar = '',
+            increaseName = '',
+            increaseMask = '',
+            increaseChar = '',
+            decreaseName = '',
+            decreaseMask = '',
+            decreaseChar = '',
+            resetName    = '',
+            resetKey     = '',
+            resetLevel   = 1,
+            under        = False,
+            underXoff    = 0,
+            underYoff    = yoff,
+            loop         = False,
+            name         = ('NATOAPP6_command_faction'
+                            +('_main' if main else '')
+                            +('_echelon' if hasEchelon else '')),
+            description  = 'NATO App6 symbology',
+            randomKey    = '',
+            randomName   = '',
+            follow       = False,
+            expression   = '',
+            first        = 1,
+            version      = 1,
+            always       = True,
+            activateKey  = '',
+            increaseKey  = '',
+            decreaseKey  = '',
+            scale        = 1.)]
+
+    if command:
+        traits.append(MarkTrait(name  = 'NATOAPP6_command',
+                                value = command))
+    if main:
+        traits.append(MarkTrait(name  = 'NATOAPP6_type',
+                                value = make_name(command = '',
+                                                  faction = '',
+                                                  echelon = '',
+                                                  main    = main,
+                                                  top     = top,
+                                                  bottom  = bottom,
+                                                  left    = left,
+                                                  right   = right,
+                                                  below   = below,
+                                                  decoy   = decoy)))
+    if echelon:
+        traits.append(MarkTrait(name = 'NATOAPP6_echelon',
+                                value = echelon))
+
+    traits.append(BasicTrait())
+
+
+    proto = Prototype(doc,
+                      name        = output,
+                      traits      = traits,
+                      description = 'NATO App6 symbol')
+
+    doc._node.insertBefore(
+        doc._root.createComment('Created via LaTeX wargame package, '
+                                'licensed under the CreativeCommons '
+                                'Share-Alike, '
+                                '© 2024 Christian Holm Christensen'),
+        proto._node)
+    xml_out = doc.encode()
+
+    with open(output+'.xml','wb') as out:
+        out.write(xml_out)
+
+
+# ====================================================================
+def create_images(db,
+                  latex_command = default_latex_command,
+                  latex_flags   = default_latex_flags,
+                  timeout       = default_timeout,
+                  resolution    = default_resolution,
+                  format        = default_format,
+                  keep          = default_keep,
+                  verbose       = False,
+                  xml           = default_xml):
+    from  tempfile import NamedTemporaryFile
+    from pathlib import Path
+    
+    result = []
+
+    
+    with NamedTemporaryFile(mode='w',suffix='.tex',dir='.',
+                            delete=not keep,
+                            delete_on_close=False) as texfile:
+        if verbose:
+            print(f'Temporary file: {texfile.name}')
+            
+        base_name  = Path(texfile.name).stem
+        latex_code = make_latex(db)
+        try:
+            run_latex(texfile,
+                      latex_code,
+                      latex_cmd   = latex_command,
+                      latex_flags = latex_flags,
+                      timeout     = timeout,
+                      keep        = keep,
+                      verbose     = verbose)
+                  
+            run_cairo(base_name,
+                      npages     = len(db),
+                      format     = format,
+                      resolution = resolution,
+                      timeout    = timeout,
+                      keep       = keep,
+                      verbose    = verbose)
+
+            result = make_images(db,
+                                 base_name,
+                                 format   = format,
+                                 verbose  = verbose)
+
+            if xml:
+                write_xmls(db)
+            
+        except Exception as e:
+            import traceback
+            print(traceback.format_exc())        
+            print(e)
+            return None
+
+    return result
+        
+        
+# ====================================================================
+#
+# Main program
+#
+if __name__ == '__main__':
+    from argparse import ArgumentParser, FileType
+    from pprint import pprint
+    
+    ap = ArgumentParser(description='Create an image of NATO App6 symbol')
+    ap.add_argument('-c','--command',type=str,choices=possible_commands,
+                    help='Select command',default=possible_commands[1])
+    ap.add_argument('-f','--faction',type=str,choices=possible_factions,
+                    help='Select faction',default=possible_factions[0])
+    ap.add_argument('-e','--echelon',type=str,choices=possible_echelons,
+                    help='Select echelon')
+    ap.add_argument('-m','--main',type=str,nargs='*',
+                    help='Select main symbol(s)')
+    ap.add_argument('-t','--top',type=str,nargs='*',
+                    help='Select top modifier(s)')
+    ap.add_argument('-b','--bottom',type=str,nargs='*',
+                    help='Select bottom modifier(s)')
+    ap.add_argument('-l','--left',type=str,nargs='*',
+                    help='Select left modifier(s)')
+    ap.add_argument('-r','--right',type=str,nargs='*',
+                    help='Select left modifier(s)')
+    ap.add_argument('-B','--below',type=str,nargs='*',
+                    help='Select below (mobility) modifier(s)')
+    ap.add_argument('-d','--decoy',action='store_true',
+                    help='Mark as decoy')
+    ap.add_argument('-w','--line-width',default='1pt',type=str,
+                    help='Line width, including unit')
+    ap.add_argument('-s','--scale',default=0.45,type=float,
+                    help='Scaling of symbol (width in centimetre)')
+    ap.add_argument('-I','--format',default=default_format,
+                    choices=possible_formats,type=str,
+                    help='Output image format')
+    ap.add_argument('-L','--latex-command',default=default_latex_command,
+                    help='Set the LaTeX command to use',type=str)
+    ap.add_argument('-F','--latex-flags',default=default_latex_flags,
+                    help='Set the LaTeX command to use',type=str,nargs='*')
+    ap.add_argument('-T','--timeout',default=default_timeout,type=int,
+                    help='Timeout of sub-processes')
+    ap.add_argument('-R','--resolution',default=default_resolution,type=int,
+                    help='Resolution out output image (ppi)')
+    ap.add_argument('-k','--keep',action='store_true',
+                    help='Leave intermediate files')
+    ap.add_argument('-o','--output',type=str,
+                    help='Set output file name, default is autogenerated')
+    ap.add_argument('-j','--json',type=str,nargs='*',
+                    help='Read symbols to create from JSON file')
+    ap.add_argument('-v','--verbose',action='store_true')
+    ap.add_argument('-X','--xml',action='store_true',
+                    help='Also output XML snippets')
+
+    args      = ap.parse_args()
+    vargs     = vars(args)
+    oargs     = {}
+    to_remove = ['latex_command',
+                 'latex_flags',
+                 'resolution',
+                 'format',
+                 'timeout',
+                 'keep',
+                 'json',
+                 'verbose',
+                 'xml']
+    for key in to_remove:
+        if key not in vargs:
+            continue
+
+        oargs[key] = vargs[key]
+        del vargs[key]
+        
+    commons = ['infantry',
+               'armoured',
+               'armoured+infantry',
+               'reconnaissance',
+               'armoured+reconnaissance',
+               '[fill=pgfstrokecolor]artillery',
+               'armoured+[fill=pgfstrokecolor]artillery',
+               'text=SF']
+    
+
+    db     = None
+    main   = vargs['main']
+    injson = oargs.pop('json',None)
+    
+    #pprint(vargs)
+    #pprint(oargs)
+    
+    if injson:
+        from json import load
+
+        db = []
+        for j in injson:
+            print(f'=== JSON input: {j}')
+            with open(j,'r') as jin:
+                db.extend(load(jin))
+                
+    elif main and len(main) == 1 and main[0] == 'common':
+        db = [{'main': w.split('+')} for w in commons]
+    else:
+        db = [vargs]
+
+    if not db:
+        raise RuntimeError('Noting to do')
+
+    from math import log10, ceil
+    
+    def ensure(d, key, default):
+        if key not in d and default:
+            d[key] = default[key]
+
+    results = []
+    ndb     = len(db)
+    wn      = ceil(log10(ndb))
+    for no,entry in enumerate(db):
+        print(f'{no:{wn}d} of {ndb} ...',end='')
+        if not isinstance(entry,dict):
+            print(' skipped')
+            continue
+
+        for key in vargs.keys():
+            ensure(entry,key,vargs)
+        entry['output'] = make_name(**entry)
+        print(entry['output'])
+
+
+    results = create_images(db,**oargs)
+#
+#
+#


Property changes on: trunk/Master/texmf-dist/tex/latex/wargame/wgmakenato.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property


More information about the tex-live-commits mailing list.