diff options
Diffstat (limited to 'macros/latex/contrib/wargame')
21 files changed, 1392 insertions, 212 deletions
diff --git a/macros/latex/contrib/wargame/README.md b/macros/latex/contrib/wargame/README.md index 3de32ab4d0..70eb286498 100644 --- a/macros/latex/contrib/wargame/README.md +++ b/macros/latex/contrib/wargame/README.md @@ -1,6 +1,6 @@ # A package to make Hex'n'Counter wargames in LaTeX -## Version 0.5 +## Version 0.6 This package can help make classic [Hex'n'Counter wargames](https://en.wikipedia.org/wiki/Wargame) using LaTeX. The diff --git a/macros/latex/contrib/wargame/doc/compat.pdf b/macros/latex/contrib/wargame/doc/compat.pdf Binary files differindex 2ba344d4ab..8f70ab6b2e 100644 --- a/macros/latex/contrib/wargame/doc/compat.pdf +++ b/macros/latex/contrib/wargame/doc/compat.pdf diff --git a/macros/latex/contrib/wargame/doc/symbols.pdf b/macros/latex/contrib/wargame/doc/symbols.pdf Binary files differindex cd8f0d3ddc..9722803067 100644 --- a/macros/latex/contrib/wargame/doc/symbols.pdf +++ b/macros/latex/contrib/wargame/doc/symbols.pdf diff --git a/macros/latex/contrib/wargame/doc/tutorial/export.tex b/macros/latex/contrib/wargame/doc/tutorial/export.tex index 6d6722d9ac..390b8785ae 100644 --- a/macros/latex/contrib/wargame/doc/tutorial/export.tex +++ b/macros/latex/contrib/wargame/doc/tutorial/export.tex @@ -77,9 +77,9 @@ % counter. % % \begin{macrocode} -\doublechitimages[A]{\alla} -\doublechitimages[B]{\allb} -\doublechitimages[Markers]{{game turn chit}} +\doublechitimages[A][chit drop shadows]{\alla} +\doublechitimages[B][chit drop shadows]{\allb} +\doublechitimages[Markers][chit drop shadows]{{game turn chit}} % \end{macrocode} % % Again, the macros we made for the counters comes in handy. Note @@ -96,7 +96,9 @@ % can.}. % % \begin{macrocode} -\battlemarkers{12} +\tikzset{} +\battlemarkers[marker drop shadows]{12} +\tikzset{every battle marker/.style={}} % \end{macrocode} % % Furthermore, we can add ``odds'' and battle result markers. Odds @@ -108,14 +110,14 @@ % in the markers. % % \begin{macrocode} -\oddsmarkers{% +\oddsmarkers[marker drop shadows]{% 1:3/red!25!white,% 1:2/red!15!white,% 1:1/orange!15!white,% 2:1/white,% 3:1/green!15!white,% 4:1/green!25!white} -\resultmarkers{ +\resultmarkers[marker drop shadows]{ AE/red!50!white, AR/red!25!white, EX/white, diff --git a/macros/latex/contrib/wargame/doc/tutorial/game.pdf b/macros/latex/contrib/wargame/doc/tutorial/game.pdf Binary files differindex f4e8a22e2a..63ea8b0733 100644 --- a/macros/latex/contrib/wargame/doc/tutorial/game.pdf +++ b/macros/latex/contrib/wargame/doc/tutorial/game.pdf diff --git a/macros/latex/contrib/wargame/doc/wargame.pdf b/macros/latex/contrib/wargame/doc/wargame.pdf Binary files differindex 01a58c597b..9b608b4144 100644 --- a/macros/latex/contrib/wargame/doc/wargame.pdf +++ b/macros/latex/contrib/wargame/doc/wargame.pdf diff --git a/macros/latex/contrib/wargame/source/Makefile b/macros/latex/contrib/wargame/source/Makefile index e11c477bf7..8320c42419 100644 --- a/macros/latex/contrib/wargame/source/Makefile +++ b/macros/latex/contrib/wargame/source/Makefile @@ -2,7 +2,7 @@ # # NAME := wargame -VERSION := 0.5 +VERSION := 0.6 LATEX_FLAGS := -interaction=nonstopmode \ -file-line-error \ --synctex=15 \ diff --git a/macros/latex/contrib/wargame/source/chit/battle.dtx b/macros/latex/contrib/wargame/source/chit/battle.dtx index 5057d620a5..381f4e4aba 100644 --- a/macros/latex/contrib/wargame/source/chit/battle.dtx +++ b/macros/latex/contrib/wargame/source/chit/battle.dtx @@ -41,7 +41,7 @@ minimum size=8mm, draw=black, fill=#2, - every odds marker/.try] at (.2,-.2) {#1}; + every odds marker/.try] at (.16,-.16) {#1}; } }, odds marker/.style args={#1,#2}{ diff --git a/macros/latex/contrib/wargame/source/chit/misc.dtx b/macros/latex/contrib/wargame/source/chit/misc.dtx index a5b9a79b24..e5d4b4b708 100644 --- a/macros/latex/contrib/wargame/source/chit/misc.dtx +++ b/macros/latex/contrib/wargame/source/chit/misc.dtx @@ -4,6 +4,58 @@ % \fi % \subsubsection{Some utilities} % +% This style allows us to add a fading drop-shadow to chits. +% +% \begin{macrocode} +\usetikzlibrary{shadows.blur} +\newif\ifwg@chit@drop\wg@chit@dropfalse +\tikzset{ + chit has drop/.is if=wg@chit@drop, + chit has drop/.default=true, + chit has drop/.initial=false, + /tikz/render blur shadow/.add code={% + \chit@dbg{2}{Number of blur steps: \pgfbs@steps}% + \ifnum\pgfbs@steps=0\else + \chit@dbg{2}{Making shadow blur}% + }{\fi}} +\tikzset{% + chit drop/.code={% + %% \message{^^J Args `#1'}% + \pgfkeysalso{% + chit has drop=true, + /tikz/blur shadow={shadow blur steps=5, + shadow opacity=25, + shadow xshift=.05cm, + shadow yshift=-.05cm, + shadow blur radius=.05cm, + #1}}% + \ifnum\pgfbs@steps=0% + \gdef\wg@drop@margin{0pt}% + \else% + \ifwg@chit@drop% + \pgfmathparse{ + \pgfbs@radius+ + veclen( + \pgfkeysvalueof{/tikz/shadow xshift}, + \pgfkeysvalueof{/tikz/shadow yshift})} + \xdef\wg@drop@margin{\pgfmathresult pt}% + \else% + \gdef\wg@drop@margin{0pt}% + \fi + %% \message{^^J Drop margin is `\wg@drop@margin' + %% `\pgfbs@radius' + %% `\pgfkeysvalueof{/tikz/shadow xshift}', + %% `\pgfkeysvalueof{/tikz/shadow yshift}'}% + \fi% + },% + chit drop/.default=,% + no chit drop/.code={% + \pgfkeysalso{ + /tikz/blur shadow={shadow blur steps=0}} + \gdef\wg@drop@margin{0pt}% + } +}% +% \end{macrocode} % % Game turn marker % diff --git a/macros/latex/contrib/wargame/source/chit/modifiers.dtx b/macros/latex/contrib/wargame/source/chit/modifiers.dtx index b42ee5790f..f2f47ee3a8 100644 --- a/macros/latex/contrib/wargame/source/chit/modifiers.dtx +++ b/macros/latex/contrib/wargame/source/chit/modifiers.dtx @@ -14,7 +14,7 @@ code={% \path[fill=white,opacity=#1,pic actions] (-.6,-.6) rectangle(.6,.6);}}, pics/chit/eliminate/.style={ - code={% + code={% \path[fill=red,opacity=#1,pic actions] (-.6,-.6) rectangle(.6,.6);}}, pics/chit/shade/.default=0.5, pics/chit/eliminate/.default=0.25, diff --git a/macros/latex/contrib/wargame/source/chit/oob.dtx b/macros/latex/contrib/wargame/source/chit/oob.dtx index d640dadebf..9e5f0c4f93 100644 --- a/macros/latex/contrib/wargame/source/chit/oob.dtx +++ b/macros/latex/contrib/wargame/source/chit/oob.dtx @@ -37,8 +37,9 @@ \def\chit@oob@rowupdate(#1,#2)#3#4{% \chit@dbg{2}{ Row update c=`#1',r=`#2',s=`#3',e=`#4'} %\pgfmathparse{ifthenelse(#1>0,#2-#3,#2)}% - \pgfmathparse{#2-#3)}% + \pgfmathparse{#2-#3-#4}% \xdef#2{\pgfmathresult}% + \xdef#1{0}%\pgfmathresult}% %\xdef#1{0} \chit@dbg{2}{ \space\space-> update `\string#2'=#2} } @@ -71,6 +72,7 @@ % \begin{macrocode} \newif\ifwg@oob@inv\wg@oob@invfalse \def\chit@oob@spacer{hspace} +\def\chit@oob@vspacer{vspace} \def\wg@star@oob{\wg@oob@invtrue\wg@oob} \def\wg@nostar@oob{\wg@oob@invfalse\wg@oob} \def\oob{% @@ -91,6 +93,8 @@ % \begin{macrocode} \def\wg@oob#1#2#3#4{ \def\r{0} + \pgfmathparse{#3*(#2-1)}% + \edef\a{\pgfmathresult} \chit@dbg{2}{OOB: `#1'} \foreach[count=\ti from 0] \t/\y in #1{ \xdef\o{\r} @@ -109,20 +113,28 @@ \ifx\m\@empty\def\m{1}\fi \ifx\u\m\def\m{1}\fi \foreach \n in {1,...,\m}{% - \chit@dbg{2}{OOB Chit is `\u'}% + \chit@dbg{2}{OOB Chit is `\u' `\chit@oob@spacer'}% \ifx\u\chit@oob@spacer% \chit@dbg{3}{Chit `\u' is spacer `\chit@oob@spacer'} \pgfmathparse{\c+#4}% \xdef\c{\pgfmathresult}% - \else - \ifnum\chitdbglvl>2% - \node[minimum width=#3cm,minimum height=#3cm, - draw,transform shape] at (\c,\r) {}; + \else% + \ifx\u\chit@oob@vspacer% + \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'} + \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)} + \xdef\ll{\pgfmathresult} + \chit@dbg{2}{\string\ll=`\ll'} + \chit@oob@rowupdate(\c,\r){\ll}{#4} + \else + \ifnum\chitdbglvl>2% + \node[minimum width=#3cm,minimum height=#3cm, + draw,transform shape] at (\c,\r) {}; + \fi + \ifx\u\chit@blank\else% + \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);% + \fi% + \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} \fi - \ifx\u\chit@blank\else% - \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);% - \fi% - \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} \fi } \fi @@ -153,7 +165,7 @@ % \chit@dbg{2}{ \space Breaking loop \rr\space > \y}% % \breakforeach% %\else% - \chit@oob@rowupdate(\c,\r){#3}{#4} + \chit@oob@rowupdate(\c,\r){#3}{0}% Extra spacing? %\fi } \fi @@ -166,6 +178,169 @@ \@ifnextchar;{\@gobble}{}} % \end{macrocode} % +% Horizontal flow OOB +% +% \begin{macrocode} +\def\wg@star@hoob{\wg@oob@invtrue\wg@hoob} +\def\wg@nostar@hoob{\wg@oob@invfalse\wg@hoob} +\def\hoob{% + \@ifstar{\wg@star@hoob% + }{\wg@nostar@hoob% + }% +} +% \end{macrocode} +% +% The inner macro of \cs{hoob}. The arguments are +% \begin{enumerate} +% \item The list of lists of chits styles +% \item The maximum number of columns +% \item The width of each cell +% \item Additional row spacing between turns +% \end{enumerate} +% +% \begin{macrocode} +\def\wg@hoob#1#2#3#4{ + \def\r{0} + \def\c{0} + \pgfmathparse{#3*(#2-1)}% + \edef\a{\pgfmathresult} + \chit@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@dbg{2}{Turn \ti\space(\r,\t,y=\y):'} + \ifx\t\empty\else + % Count how many are left for this turn + \chit@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 + \ifx\u\chit@oob@spacer% + \pgfmathparse{\l+\m*#4}\xdef\l{\pgfmathresult} + \chit@dbg{2}{Got \m\space hspace (#4) -> \l} + \else + \ifx\u\chit@oob@vspace% + \xdef\ig{1} + \chit@dbg{2}{Got vspace -> \l (\ig)} + \else + \pgfmathparse{\l+\m*#3} + \xdef\l{\pgfmathresult} + \chit@dbg{2}{Got \m\space units -> \l} + \fi + \fi + \fi + \fi} + % Check if there's enough room + \chit@dbg{2}{To fill the rest of turn needs `\l' compared to + `\a' (#3*(#2-1))} + \pgfmathparse{ifthenelse(abs(\l)>=#3*(#2-1),0,1}% + \xdef\l{\pgfmathresult}% + \chit@dbg{2}{Break or not `\l'} + \ifnum\l=0\chit@oob@turnupdate(\c,\r){#3}{#4}\fi + \fi + \ifwg@oob@inv% + \pic[transform shape] at (\c+.5*#3,\r) {chit/oob turn=\ti};% was dx=0.5 + \else + \pic[transform shape] at (\c+-.5*#3,\r) {chit/oob turn=\ti};% was dx=-0.5 + \fi% + %\chit@oob@cellupdate(\c,\r){#2}{#3}{\y} + \ifx\t\empty\else% + \def\lv{0} + \foreach \u/\m in \t{ + %% \chit@dbg{2}{ `\u'=`\m'} + \ifx\u\empty\else + \ifx\m\@empty\def\m{1}\fi + \ifx\u\m\def\m{1}\fi + \foreach \n in {1,...,\m}{% + \chit@dbg{2}{OOB Chit is `\u' `\chit@oob@spacer'}% + \ifx\u\chit@oob@spacer% + \chit@dbg{3}{Chit `\u' is spacer `\chit@oob@spacer'} + \pgfmathparse{\c+#4}% + \xdef\c{\pgfmathresult}% + \else% + \ifx\u\chit@oob@vspacer% + \chit@dbg{3}{Chit `\u' is vspacer `\chit@oob@vspacer'} + \pgfmathparse{ifthenelse(abs(\c)<0.0001,0,#3)} + \xdef\ll{\pgfmathresult} + \chit@dbg{2}{\string\ll=`\ll'} + \chit@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 + \ifx\u\chit@blank\else% + \chit[\u=\ti,zone oob point={\u}{\c}{\r}](\c,\r);% + \fi% + \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} + \fi + \fi + } + \fi + } + \fi + \chit@dbg{2}{ End of chits in turn + \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@oob@turnupdate increments the row + % \ifx\t\@empty + % \def\c{#3} + % \chit@dbg{2}{ Turn is empty, set c=`\c'} + % \fi + % --- + %\ifnum\y<0% No explicit number of rows given + % \def\c{#3} + % \chit@dbg{2}{ No explicit number of rows given, set c=`\c'} + %\fi + % In case the user gave and explicit number of rows, add the rows + % that are missing. \y is initially set to the number of + % requested rows, and then decremented every time we go down one + % row. So if the number of rows we did so far is N, and the + % requested number of rows is M, then the loop below adds M-N + % rows. + \ifnum\y>0% + \chit@dbg{2}{ Looping rows from 2 to \y, break when row > \y}% + \foreach \rr in {2,...,\y}{ + %\ifnum\rr>\y% A little funny, but \y can be negative! + % \chit@dbg{2}{ \space Breaking loop \rr\space > \y}% + % \breakforeach% + %\else% + \chit@oob@rowupdate(\c,\r){#3}{0}% Extra spacing? + %\fi + } + \fi + % --- Not relevant I think + % This will zero \c. However, if on entry |\c|>0, then we also + % increment the row + % \chit@oob@turnupdate(\c,\r){#3}{#4} + % --- + % Horizontal spacer + + %\pgfmathparse{ifthenelse(abs(\c)>=\a,1,0)}\xdef\l{\pgfmathresult} + \pgfmathparse{\c+1.5*#4}% + \xdef\c{\pgfmathresult}% + \ifnum\lv=1% + \pgfmathparse{\r-#4} + \chit@oob@rowupdate(\c,\r){0}{#4} + \else + \chit@oob@cellupdate(\c,\r){#2}{#3}{\y} + \ifnum\y<0 + \chit@oob@turnupdate(\c,\r){#3}{#4} + \else + \fi + \fi + % \xdef\y{0} + \chit@dbg{2}{End of turn \ti\space(c=`\c',r=`\r',o='\o',y='\y')} + } + \chit@dbg{3}{End of OOB (c=`\c',r=`\r',y=`\y')} + \@ifnextchar;{\@gobble}{}} +% \end{macrocode} % \iffalse % </chit> % -------------------------------------------------------------------- diff --git a/macros/latex/contrib/wargame/source/chit/shape.dtx b/macros/latex/contrib/wargame/source/chit/shape.dtx index 8a3d70fbd0..f68ea25a44 100644 --- a/macros/latex/contrib/wargame/source/chit/shape.dtx +++ b/macros/latex/contrib/wargame/source/chit/shape.dtx @@ -92,7 +92,7 @@ chit/symbol/.style={scale=.4,transform shape}, chit/parts/.style={shape=rectangle,transform shape}, chit/factors/.style={chit/parts,anchor=south}, - chit/left/.style={chit/parts,anchor=south,rotate=90}, + chit/left/.style={chit/parts,anchor=base,rotate=90},%Anchor was south chit/right/.style={chit/parts,anchor=north,rotate=90}, chit/upper left/.style={chit/parts,anchor=north west}, chit/upper right/.style={chit/parts,anchor=north east}, @@ -141,7 +141,8 @@ % \begin{macrocode} \tikzset{% chit/.code={% - \pgfkeys{/tikz/transform shape,/tikz/shape=chit} + \chit@dbg{2}{chit arguments are `#1'}% + \pgfkeys{/tikz/transform shape,/tikz/shape=chit}% \pgfkeys{/chit/.cd,#1}}} % \end{macrocode} % \end{TikzKey} @@ -657,7 +658,11 @@ % % The work horse. This simply makes a \cs{node} with the shape % \texttt{chit}. Note, we allow for a trailing semi-colon -% (\texttt{;}) to have a similar feel to other \TikZ{} macros. +% (\texttt{;}) to have a similar feel to other \TikZ{} macros. +% +% The macro will execute the style \texttt{/tikz/every chit} if +% defined. Note that this will be executed \emph{before} the usual +% \texttt{every chip node} style. % % \begin{macrocode} \def\chit@@@#1#2(#3){% @@ -667,7 +672,7 @@ ^^J Name: `#3'} \let\name\pgfutil@empty% \chit@dbg{1}{=== Before chit node}% - \node[chit={every chit/.try,id=#3,#1}] (tmp) at (#2) {}; + \node[chit={/tikz/every chit/.try,id=#3,#1}] (tmp) at (#2) {}; \chit@dbg{2}{=== After chit node}% \ifx|#3|\relax% \else% diff --git a/macros/latex/contrib/wargame/source/chit/table.dtx b/macros/latex/contrib/wargame/source/chit/table.dtx index 7094e436ed..f99236119e 100644 --- a/macros/latex/contrib/wargame/source/chit/table.dtx +++ b/macros/latex/contrib/wargame/source/chit/table.dtx @@ -7,7 +7,9 @@ % \begin{macrocode} \tikzset{ chit/cell background/.style={fill=black}, + %chit/cell background flipped/.style={fill=black}, blank chit/.style={/chit/frame={draw=none,fill=none}}, + chit/grid lines/.style={dashed}, } % \end{macrocode} % @@ -20,6 +22,10 @@ \def\chit@cellbg(#1,#2)#3{% \draw[chit/cell background](#1-#3/2,#2-#3/2) rectangle++(#3,#3); } +\def\chit@celldblbg(#1,#2)#3{% + \draw[chit/cell background,chit/cell background flipped/.try]% + (#1-#3/2,#2-#3/2) rectangle++(#3,#3); +} % \end{macrocode} % % \begin{Macro}{\ifchits@reset} @@ -83,6 +89,27 @@ \@ifnextchar;{\@gobble}{}} % \end{macrocode} % \end{Macro} +% \begin{Macro}{\chitgrid} +% \begin{enumerate} +% \item columns +% \item rows +% \item cell-size +% \end{enumerate} +% +% \begin{macrocode} +\def\chitgrid#1#2#3{% + \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}% + \pgfmathparse{#2*#3-#3/2}\edef\rmax{\pgfmathresult}% + %\draw[red](-#3/2,\rmin)rectangle(#3*#1-#3/2,-\rmax); + \foreach \cc in {0,...,#1}{ + \draw[chit/grid lines] (\cc*#3-#3/2,3*#3/4)--(\cc*#3-#3/2,-\rmax-#3/4);} + %\chit@dbg{0}{Drawing horizontal lines from `\rmin, `-\rmin', ..., `-\rmax'} + \foreach \rr in {\rmin,-\rmin,...,-\rmax}{ + %\chit@dbg{0}{Horizontal line at `\rr'} + \draw[chit/grid lines] (-3*#3/4,\rr)--(#1*#3-#3/4,\rr);} +} +% \end{macrocode} +% \end{Macro} % % \begin{Macro}{\doublechits, % \@doublechits, @@ -154,7 +181,7 @@ \chit@cellbg(\c,\r){#3} \chit[\u=\ti](\c,\r) \chit@dbl@flip(\c,\r){#3} - \chit@cellbg(\mc,\r){#3} + \chit@celldblbg(\mc,\r){#3} \chit[\u\space flipped=\ti,zone turn=\t,zone mult=\n](\mc,\r) \chit@dbl@cellupdate(\c,\r){#2}{#3} \fi @@ -166,10 +193,39 @@ \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};% + % \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);} + % \pgfmathparse{#3/2}\edef\rmin{\pgfmathresult}% + % \chit@dbg{0}{Drawing horizontal lines from `-\rmin, `\rmin', ..., `\r'} + % \foreach \rr in {-\rmin,\rmin,...,\r}{ + % \chit@dbg{0}{Horizontal line at `\rr'} + % \draw[dashed] (-#2*#3-#3/4,\rr)--(#2*#3+#3/4,\rr);} \@ifnextchar;{\@gobble}{}} % \end{macrocode} % \end{Macro} % +% \begin{Macro}{\doublechitgrid} +% \begin{enumerate} +% \item columns +% \item rows +% \item cell-size +% \end{enumerate} +% +% \begin{macrocode} +\def\doublechitgrid#1#2#3{% + \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);} + %\chit@dbg{0}{Drawing horizontal lines from `-\rmin, `\rmin', ..., `\rmax'} + \foreach \rr in {-\rmin,\rmin,...,\rmax}{ + %\chit@dbg{0}{Horizontal line at `\rr'} + \draw[chit/grid lines] (-#1*#3-#3/4,\rr)--(#1*#3+#3/4,\rr);} +} +% \end{macrocode} +% \end{Macro} % \iffalse % </chit> % -------------------------------------------------------------------- diff --git a/macros/latex/contrib/wargame/source/hex/labels.dtx b/macros/latex/contrib/wargame/source/hex/labels.dtx index 7767dcc661..f622bf6cda 100644 --- a/macros/latex/contrib/wargame/source/hex/labels.dtx +++ b/macros/latex/contrib/wargame/source/hex/labels.dtx @@ -132,7 +132,7 @@ % % \begin{macrocode} \def\hex@do@label{% - \hex@dbg{1}{Hex label: `\meaning\hex@label'}% + \hex@dbg{3}{Hex label: `\meaning\hex@label'}% \edef\hex@l@tmp{[% /hex/label/.cd,% rotate=0,% diff --git a/macros/latex/contrib/wargame/source/hex/ridges.dtx b/macros/latex/contrib/wargame/source/hex/ridges.dtx index 24552e36e2..658f55888b 100644 --- a/macros/latex/contrib/wargame/source/hex/ridges.dtx +++ b/macros/latex/contrib/wargame/source/hex/ridges.dtx @@ -157,11 +157,12 @@ \xdef\hex@r@p{\hex@r@p ($(300:\hex@r@r)+(-120:\hex@r@t/2)$)} \fi \ifhex@r@ne - \xdef\hex@r@p{\hex@r@p --cycle} + %\xdef\hex@r@p{\hex@r@p --cycle} + \xdef\hex@r@p{\hex@r@p -- (0:\hex@r@r)} \else \xdef\hex@r@p{\hex@r@p --(.5:\hex@r@r)} \fi - \hex@dbg{4}{Ridge along se: `\hex@r@p'} + \hex@dbg{4}{Ridge along south east: `\hex@r@p'} \fi \hex@dbg{3}{ Ridges path: \hex@r@p} % \draw[red] \hex@r@p; diff --git a/macros/latex/contrib/wargame/source/hex/terrain.dtx b/macros/latex/contrib/wargame/source/hex/terrain.dtx index 4c486adf77..9d15a555ec 100644 --- a/macros/latex/contrib/wargame/source/hex/terrain.dtx +++ b/macros/latex/contrib/wargame/source/hex/terrain.dtx @@ -37,6 +37,7 @@ % select the common terrains. % % \begin{macrocode} +\newif\if@hex@t@rot\@hex@t@rotfalse% \tikzset{% /hex/terrain/.search also={/tikz},% /hex/terrain/.cd,% @@ -44,6 +45,7 @@ image/.store in=\hex@t@image,% code/.store in=\hex@t@code,% clip/.store in=\hex@t@clip,% + random rotation/.is if=@hex@t@rot, pic/.default=, image/.default=, code/.default=, @@ -137,8 +139,23 @@ \@ifundefined{hex@t@code}{\let\hex@t@code\empty}{} % \end{macrocode} % +% Possible make rotation. We define a scope and rotate within that. +% +% \begin{macrocode} + \def\hex@t@angle{0}% + \if@hex@t@rot% + \pgfmathrandominteger{\hex@t@angle}{0}{5} + \pgfmathparse{int(60*\hex@t@angle)}\edef\hex@t@angle{\pgfmathresult}% + \fi% + \hex@dbg{5}{Will rotate terrain by `\hex@t@angle'}% +% \end{macrocode} +% +% If we have specified code for the terrain, then execute that. +% % \begin{macrocode} - \ifx\hex@t@code\empty\else\hex@t@code\fi% + \scope[rotate=\hex@t@angle]% + \ifx\hex@t@code\empty\else\hex@t@code\fi% + \endscope% End rotate code % \end{macrocode} % First we check if we have not got terrain images, but terrain % pictures. If we have that, we process these in turn. Note, the @@ -154,7 +171,9 @@ \hex@dbg{5}{Terrain pictures}% \pgfpointorigin\wg@tmpa=\pgf@x\wg@tmpb=\pgf@y% \foreach \i in \hex@t@pic{% - \wg@pic@all{\i}{}{\the\wg@tmpa,\the\wg@tmpb}{}}% + \wg@pic@all{\i}{}{\the\wg@tmpa,\the\wg@tmpb}{% + rotate=\hex@t@angle, + transform shape}}% \fi% We have pictures. % \end{macrocode} % @@ -172,6 +191,7 @@ \expandafter\wg@node{% \includegraphics[width=2cm]{\i}}\@endwg@node % {}{\wg@tmpa,\wg@tmpb}{% + rotate=\hex@t@angle,% shape=rectangle,% anchor=center,% transform shape,% diff --git a/macros/latex/contrib/wargame/source/tests/chits.dtx b/macros/latex/contrib/wargame/source/tests/chits.dtx index 7dcf26c50d..966e3b4a9f 100644 --- a/macros/latex/contrib/wargame/source/tests/chits.dtx +++ b/macros/latex/contrib/wargame/source/tests/chits.dtx @@ -6,7 +6,11 @@ % Some chits for testing stuff % % \begin{macrocode} -\def\firstchit{ +\makeatletter +\def\firstchit{% + \@ifnextchar[{\@firstchit}{\@firstchit[]}%] +}% +\def\@firstchit[#1]{ % \chitdbglvl=5 % \natoappdbglvl=5 \chit[symbol={ @@ -17,13 +21,14 @@ left={text=A}, frame={fill=yellow!50!black}, ultra thick, - command=land}, + command=land, + }, left={chit/identifier={1}}, right={chit/identifier={2}}, factors={chit/2 factors={4,8}}, frame={draw=black}, - color=white,text=yellow,fill={blue}]} -\makeatletter + color=white,text=yellow,fill={blue}, + #1]} \def\secondchit{% \@ifnextchar({\second@chit}{\second@chit(0,0)}%) } @@ -34,6 +39,7 @@ command=air](0,-.15)}, left={[{red,font=\noexpand\sffamily}]chit/identifier={Fighter}}, right={chit/identifier={USAF}}, + fill=white, factors={chit/1 factor=3}}] at (#1){};} \def\thirdchit{ \chit[symbol={[echelon=battalion, diff --git a/macros/latex/contrib/wargame/source/tests/test.tex b/macros/latex/contrib/wargame/source/tests/test.tex index 5833d679f2..49062b70f0 100644 --- a/macros/latex/contrib/wargame/source/tests/test.tex +++ b/macros/latex/contrib/wargame/source/tests/test.tex @@ -2,6 +2,7 @@ \usepackage{wargame} \usetikzlibrary{decorations.markings} +\usetikzlibrary{shadows.blur} \title{A test of \textsf{wargame}} \author{} \date{} @@ -28,7 +29,7 @@ \section{Some chits} -\begin{tikzpicture} +\begin{tikzpicture}[every chit node/.style={blur shadow={shadow blur steps=5}}] \fill[red!50] (1,-1) rectangle (3,1); \firstchit(0,0)% %\secondchit(2,0)% @@ -45,18 +46,20 @@ \end{tikzpicture} \section{Chits on a board} -\begin{tikzpicture} +\begin{tikzpicture}[every chit/.style={chit drop}] + \chitdbglvl=5 \begin{scope}[every hex/.style={/hex/label=auto},transform shape] \boardframe[0.5](c=0,r=0)(c=3,r=3) \boardclip(c=0,r=0)(c=3,r=3){fill=white} \testmap{} - \firstchit(hex cs:c=2,r=1) - \secondchit(hex cs:c=3,r=0) + \firstchit[no chit drop](hex cs:c=2,r=2) + %\secondchit(hex cs:c=3,r=0) \thirdchit(hex cs:c=0,r=3) \stackchits(hex cs:c=0,r=1)(.3,.3){ \noexpand\firstchit, - \noexpand\secondchit/\noexpand\chit[{full=chit/eliminate}]} + \noexpand\secondchit%/\noexpand\eliminatechit + } \begin{scope}[m/.style={hex/long move=green}] \path[m] (hex cs:c=3,r=0) -- (hex cs:c=3,r=1)--(hex cs:c=3,r=2); diff --git a/macros/latex/contrib/wargame/source/util/export.dtx b/macros/latex/contrib/wargame/source/util/export.dtx index fde067dfe8..09f74c0513 100644 --- a/macros/latex/contrib/wargame/source/util/export.dtx +++ b/macros/latex/contrib/wargame/source/util/export.dtx @@ -72,6 +72,7 @@ % % Preceed all images (\textsf{tikzpicture}) with this command % +% % First argument is the name of the image. This can be anything. % Note that for counters, if the name ends in \texttt{flipped} then it % is considered the backside of a counter. @@ -140,13 +141,32 @@ % % Make separate images for each counter (single sided). % +% First optional argument is the group to put the chits into. +% Second optional argument is options to give to each Tikz picture +% environment. Third, mandatory, argument is the list of chit +% identifiers to render. +% % \begin{macrocode} -\newcommand\chitimages[2][]{% +\def\wg@add@drop@margin{% + \@ifundefined{wg@drop@margin}{}{ + \dimen0=\wg@drop@margin + % \ifwg@chit@drop + \ifdim\dimen0>0pt% + \path ($(current bounding box.north east)+(45:\wg@drop@margin)$) + -- ($(current bounding box.south west)+(225:\wg@drop@margin)$); + \fi}} +\def\chitimages{% + \@ifnextchar[{\@chitimages}{\chitimages[]}%] +}% +\def\@chitimages[#1]{% + \@ifnextchar[{\@@chitimages[#1]}{\@@chitimages[#1][]}%] +}% +\def\@@chitimages[#1][#2]#3{% \begingroup% \let\chit@report\do@chit@report% \let\natoapp@report\do@natoapp@report% - \chit@dbg{2}{chits to make images of `#2'}% - \foreach[count=\ti from 0] \t/\x in #2{% + \chit@dbg{2}{chits to make images of `#3'}% + \foreach[count=\ti from 0] \t/\x in #3{% \chit@dbg{2}{^^JRow: `\t' (`\x')} \ifx\t\empty\else% Ignore empty rows \chit@dbg{5}{^^JSubcategory: `\x' (default `#1')} @@ -156,14 +176,17 @@ \ifx\u\empty\else% Ignore empty cells \ifx\u\chit@blank\else% \chit@dbg{2}{Next chit `\u' with possible multiplicity `\m'}% - \ifx\m\@empty\def\m{1}\fi% If not multiplicity defined + \ifx\m\@empty\def\m{1}\fi% If no multiplicity defined \ifx\u\m\def\m{1}\fi% If the same as unit \chit@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} - \begin{tikzpicture} + \nopagecolor% + \gdef\wg@drop@margin{0pt}% + \begin{tikzpicture}[chit has drop=false,#2] \chit[\u=\ti]% + \wg@add@drop@margin% \end{tikzpicture} \end@info% %% \foreach \n in {1,...,\m}{% Make a number of copies @@ -189,14 +212,25 @@ % % Make separate images for each counter (double sided). The back-side % counters must be defined by append `\texttt{ flipped}' the front -% face name +% face name. +% +% First optional argument is the group to put the chits into. +% Second optional argument is options to give to each Tikz picture +% environment. Third, mandatory, argument is the list of chit +% identifiers to render. % % \begin{macrocode} -\newcommand\doublechitimages[2][]{% +\def\doublechitimages{% + \@ifnextchar[{\@doublechitimages}{\doublechitimages[]}%] +}% +\def\@doublechitimages[#1]{% + \@ifnextchar[{\@@doublechitimages[#1]}{\@@doublechitimages[#1][]}%] +}% +\def\@@doublechitimages[#1][#2]#3{% \begingroup% \let\chit@report\do@chit@report% \let\natoapp@report\do@natoapp@report% - \foreach[count=\ti from 0] \t/\x in #2{% + \foreach[count=\ti from 0] \t/\x in #3{% \ifx\t\empty\else% Ignore empty rows \chit@dbg{5}{^^JSubcategory: `\x' (default `#1')} % Take sub-category or default @@ -213,13 +247,18 @@ %% We only make one copy of the chit, since we can duplicate %% it in VASSAL \info*{\u}{counter}{\x}% - \begin{tikzpicture}% + \nopagecolor% + \gdef\wg@drop@margin{0pt}% + \begin{tikzpicture}[chit has drop=false,#2]% \chit[\u=\ti]% + \wg@add@drop@margin% \end{tikzpicture}% \end@info% \info*{\s}{counter}{\x}% - \begin{tikzpicture}% + \nopagecolor% + \begin{tikzpicture}[chit has drop=false,#2]% \chit[\s=\ti]% + \wg@add@drop@margin% \end{tikzpicture}% \end@info% %% \foreach \n in {1,...,\m}{% Make a number of copies @@ -414,7 +453,7 @@ \def\hex@col{0}% \def\hex@row{0}% \node[hex,inner sep=0,outer sep=0]{% - \message{^^JHex label: `\meaning\hex@label'}% + %\message{^^JHex label: `\meaning\hex@label'}% \global\let\mk@label\hex@label}}}% % \end{macrocode} % @@ -427,7 +466,7 @@ \edef\mk@i{\mk@i\space} %% Everything is made into centimeters \mk@w{ \mk@i "units": "cm",} - \hex@dbg{0}{Label: `\meaning\mk@label'} + \hex@dbg{3}{Label: `\meaning\mk@label'} \@ifundefined{mk@label}{}{\mk@w{ \mk@i "labels": "\mk@label",}} %% Write out coordinate options as "coords" object \mk@w{ \mk@i"coords": \@lbchar}% @@ -577,7 +616,7 @@ % optional is the group to add the markers to. % % \begin{macrocode} -\def\wg@gennumberm@rkers#1#2#3{ +\def\wg@gennumberm@rkers#1#2#3#4{ \message{^^JNumbered markers: Type=`#1' Max=`#2' Category=`#3'} \def\markers{} \def\keys{} @@ -585,24 +624,37 @@ \xdef\keys{/tikz/#1 \i/.style={/tikz/#1=\i},\keys} \xdef\markers{\markers,#1 \i}} {% - \nopagecolor\pgfkeysalsofrom{\keys}\chitimages[#3]{\markers}}}% + \nopagecolor\pgfkeysalsofrom{\keys}\chitimages[#3][#4]{\markers}}}% \tikzset{ wg hidden unit/.pic={}, wg hidden unit/.style={ chit={ + no chit drop, frame={draw=none,fill=none}, full=wg hidden unit}}} -\DeclareRobustCommand\battlemarkers[2][BattleMarkers]{% - \wg@gennumberm@rkers{battle marker}{#2}{#1}% +% +% First optional argument are extra styles +% Second is category +% Third is number of markers +% +\def\battlemarkers{% + \@ifnextchar[{\@battlemarkers}{\battlemarkers[]}%] +}% +\def\@battlemarkers[#1]{% + \@ifnextchar[{\@@battlemarkers[#1]}{\battlemarkers[#1][BattleMarkers]}%] +}% +\def\@@battlemarkers[#1][#2]#3{% + \wg@gennumberm@rkers{battle marker}{#3}{#2}{#1}% \message{^^JMake a hidden unit and add to Markers category} {% \nopagecolor% \chitimages[Markers]{{wg hidden unit}}% % \info{battle-marker-icon}{icon}{}% - \tikz[scale=.7,transform shape]{\pic{battle marker=0};}% + \tikz[scale=.7,transform shape,auto icon more/.try]{% + \pic{battle marker=0};}% \info{clear-battles-icon}{icon}{} - \tikz[scale=.4,transform shape]{% + \tikz[scale=.4,transform shape,auto icon more/.try]{% \pic{eliminate icon}; \pic[scale=.7,transform shape] at (-.3,0) {battle marker=0};}% }% @@ -613,21 +665,40 @@ % Optional is the group to add the markers to. % % \begin{macrocode} -\def\wg@gencolorm@rkers#1#2#3{% +\def\wg@gencolorm@rkers#1#2#3#4{% \def\markers{} \def\keys{} - \foreach \o/\f in {#2}{% + \foreach \o/\f/\n [count=\i] in {#2}{% + \ifx\n\f\def\n{\o}\fi% \ifx\o\f\def\f{white}\fi% - \message{^^JOdds marker `#1 \o' w/fill `\f'}% - \xdef\keys{/tikz/#1 \o/.style={/tikz/#1={\o,\f}},\keys} - \xdef\markers{\markers,#1 \o}} - {\nopagecolor\pgfkeysalsofrom{\keys}\chitimages[#3]{\markers}}}% -\DeclareRobustCommand\oddsmarkers[2][OddsMarkers]{% - \wg@gencolorm@rkers{odds marker}{#2}{#1}% + \message{^^JColour no \i marker `#1 \n' w/fill `\f' text `\o'}% + \protected@xdef\keys{/tikz/#1 \n/.style={/tikz/#1={\o,\f}},\keys} + \xdef\markers{\markers,#1 \n} + }% + {% + \nopagecolor% + \pgfkeysalsofrom{\keys}% + \chitimages[#3][#4]{\markers}% + }% +}% +% +% First optional argument are extra styles +% Second is category +% Third is marker list +% +\def\oddsmarkers{% + \@ifnextchar[{\@oddsmarkers}{\oddsmarkers[]}%] +}% +\def\@oddsmarkers[#1]{% + \@ifnextchar[{\@@oddsmarkers[#1]}{\oddsmarkers[#1][OddsMarkers]}%] +}% +\def\@@oddsmarkers[#1][#2]#3{% + \wg@gencolorm@rkers{odds marker}{#3}{#2}{#1}% \info{odds-battles-icon}{icon}{} - \tikz[scale=.5,transform shape]{\pic{odds marker={?:?,white}}} + \tikz[scale=.5,transform shape,auto icon more/.try]{% + \pic{odds marker={?:?,white}}} \info{resolve-battles-icon}{icon}{} - \tikz[scale=.3,transform shape]{% + \tikz[scale=.3,transform shape,auto icon more/.try]{% \pic{dice}; \pic[scale=1.2,transform shape] at (-.2,-.2) {battle marker=0};}% } @@ -636,40 +707,51 @@ % Make results markers. Mandatory argument is a list of results and % fill colours. Optional is the group to add the markers to. % +% +% First optional argument are extra styles +% Second is category +% Third is marker list +% % \begin{macrocode} -\DeclareRobustCommand\resultmarkers[2][ResultMarkers]{% - \wg@gencolorm@rkers{result marker}{#2}{#1}} +\def\resultmarkers{% + \@ifnextchar[{\@resultmarkers}{\resultmarkers[]}%] +}% +\def\@resultmarkers[#1]{% + \@ifnextchar[{\@@resultmarkers[#1]}{\resultmarkers[#1][ResultMarkers]}%] +}% +\def\@@resultmarkers[#1][#2]#3{% + \wg@gencolorm@rkers{result marker}{#3}{#2}{#1}}% % \end{macrocode} % % Common icons used by many modules % % \begin{macrocode} -\DeclareRobustCommand\commonicons[2]{% +\DeclareRobustCommand\commonicons[3][]{% \begingroup% \nopagecolor% - \tikzset{icon/.style={scale=.4,transform shape}}% + \tikzset{auto icon/.style={scale=.4,transform shape,#1}}% % \info{pool-icon}{icon}{} - \tikz[icon]{\pic{pool icon};} + \tikz[auto icon,auto icon more/.try]{\pic{pool icon};} % \info{oob-icon}{icon}{}% - \tikz[icon]{\pic{oob icon={#1}{#2}};}% + \tikz[auto icon,auto icon more/.try]{\pic{oob icon={#2}{#3}};}% % \info{flip-icon}{icon}{}% - \tikz[icon]{\pic{flip icon};}% + \tikz[auto icon,auto icon more/.try]{\pic{flip icon};}% % \info{eliminate-icon}{icon}{}% - \tikz[icon]{\pic{eliminate icon};}% + \tikz[auto icon,auto icon more/.try]{\pic{eliminate icon};}% % \info{restore-icon}{icon}{}% - \tikz[icon]{\pic{restore icon};}% + \tikz[auto icon,auto icon more/.try]{\pic{restore icon};}% % \info{dice-icon}{icon}{}% - \tikz[icon,scale=.9]{\pic{dice};}% + \tikz[auto icon,scale=.9,auto icon more/.try]{\pic{dice};}% % \info{unit-icon}{icon}{}% - \tikz[icon,scale=.7]{% - \chit[fill=#1, + \tikz[auto icon,scale=.7,auto icon more/.try]{% + \chit[fill=#2, symbol={[ scale line widths, line width=1pt, @@ -677,7 +759,29 @@ command=land, main=infantry, scale=1.3](0,-.15)}]}% - \endgroup% + % + \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}{} + \begin{tikzpicture}[scale=.25] + \draw[scale line widths,line width=2pt,fill=white](-.5,0) + to[out=70,in=110] (.5,0) + to[out=-110,in=-70] cycle; + \begin{scope}[even odd rule] + \clip circle(.2); + \fill circle(.2) (125:.18) circle(.1); + \end{scope} + \end{tikzpicture}% + % + \endgroup% } % \end{macrocode} % @@ -708,9 +812,11 @@ \foreach \v/\p in {#5}{% \info{#3-\v}{die-roll}{#3} \tikz[#1]{ - \node[shape=#4,transform shape,draw=none,fill=black,opacity=.5] - at (.05,-.03){}; - \node[shape=#4,#2,transform shape]{\p};}}} + %\node[shape=#4,transform shape,draw=none,fill=black,opacity=.5] + %at (.05,-.03){}; + \node[shape=#4,#2,transform shape, + chit drop + ]{\p};\wg@add@drop@margin{}}}} % \end{macrocode} % % \subsubsection{Hooks into chits, etc.} @@ -809,6 +915,28 @@ \mk@w{ \mk@i\space "end": 0} \mk@w{ \mk@i \@rbchar} } +\tikzset{ + chit drop margin/.store in=\wg@drop@margin, + chit drop shadows/.code={ + \pgfkeysalso{% + /tikz/every chit node/.prefix style={chit drop={#1}}, + /tikz/chit has drop=true} + }, + chit drop shadows/.default=, + marker drop shadows/.code={ + \pgfkeysalso{% + /tikz/every battle marker/.prefix style={chit drop={#1}}, + /tikz/every odds marker/.prefix style={chit drop={#1}}, + /tikz/every result marker/.prefix style={chit drop={#1}}, + /tikz/auto icon more/.prefix style={no chit drop}}}, + marker drop shadows/.default={ + chit has drop=false, + shadow xshift=0.04cm, + shadow yshift=-0.04cm, + shadow blur radius=0.04cm} +} + + % \end{macrocode} % % diff --git a/macros/latex/contrib/wargame/source/util/tikz.dtx b/macros/latex/contrib/wargame/source/util/tikz.dtx index fa31a7b58c..2f6dbefc17 100644 --- a/macros/latex/contrib/wargame/source/util/tikz.dtx +++ b/macros/latex/contrib/wargame/source/util/tikz.dtx @@ -86,12 +86,17 @@ % Save pgf rounded corners macro % \let\wg@pgfsetcornersarced\pgfsetcornersarced \def\wg@setcornersarched#1{% - \ifx|#1|\else% - \edef\pgf@corner@arc{{#1}{#1}}% - \pgf@arccornerstrue% - \ifdim#1=0pt% - \pgf@arccornersfalse% - \fi\fi} + \def\arg{#1}% + \let\isarched\relax% Cannot set \ifpgf@arccorners directly inside + % other \if + \ifx\arg\@empty\else% + \edef\pgf@corner@arc{{#1}{#1}}% + \let\isarched\pgf@arccornerstrue% + \ifdim#1=0pt% + \let\isarched\pgf@arccornersfalse% + \fi% + \fi% + \isarched} \newdimen\wg@lw@scaled\wg@lw@scaled=1pt \def\wg@getscale{% \pgfgettransformentries{% diff --git a/macros/latex/contrib/wargame/source/utils/wgexport.py b/macros/latex/contrib/wargame/source/utils/wgexport.py index a09a7e4257..d573b63d36 100755 --- a/macros/latex/contrib/wargame/source/utils/wgexport.py +++ b/macros/latex/contrib/wargame/source/utils/wgexport.py @@ -208,6 +208,8 @@ CTRL_SHIFT = CTRL+SHIFT ALT_SHIFT = ALT+SHIFT NONE = '\ue004' NONE_MOD = 0 + +# -------------------------------------------------------------------- def key(let,mod=CTRL): '''Encode a key sequence @@ -220,6 +222,8 @@ def key(let,mod=CTRL): Modifier mask ''' return f'{ord(let)},{mod}' + +# -------------------------------------------------------------------- # def hexcolor(s): if isinstance(s,str): @@ -241,6 +245,7 @@ def hexcolor(s): return rgb(int(r*256),int(g*256),int(b*256)) +# -------------------------------------------------------------------- # Colour encoding def rgb(r,g,b): '''Encode RGB colour @@ -261,6 +266,7 @@ def rgb(r,g,b): ''' return ','.join([str(r),str(g),str(b)]) +# -------------------------------------------------------------------- def rgba(r,g,b,a): '''Encode RGBA colour @@ -282,6 +288,7 @@ def rgba(r,g,b,a): ''' return ','.join([str(r),str(g),str(b),str(a)]) +# -------------------------------------------------------------------- def dumpTree(node,ind=''): '''Dump the tree of nodes @@ -296,6 +303,12 @@ def dumpTree(node,ind=''): for c in node.childNodes: dumpTree(c,ind+' ') +# -------------------------------------------------------------------- +def registerElement(cls): + + Element.known_tags[cls.TAG] = cls + + # # EOF # @@ -310,6 +323,8 @@ class Element: MAP = MODULE + 'map.' PICKER = MAP + 'boardPicker.' BOARD = PICKER + 'board.' + known_tags = {} + def __init__(self,parent,tag,node=None,**kwargs): '''Create a new element @@ -429,9 +444,14 @@ class Element: return {c[key] : c for c in cand} return cand - def getParent(self,cls,checkTag=True): + def getParent(self,cls=None,checkTag=True): if self._node.parentNode is None: return None + if cls is None: + cls = self.getTagClass(self._node.parentNode.tagName) + checkTag = False + if cls is None: + return None if checkTag and self._node.parentNode.tagName != cls.TAG: return None return cls(self,node=self._node.parentNode) @@ -440,6 +460,10 @@ class Element: '''Searches back until we find the parent with the right class, or none ''' + try: + iter(cls) + except: + cls = [cls] t = {c.TAG: c for c in cls} p = self._node.parentNode while p is not None: @@ -447,6 +471,11 @@ class Element: if c is not None: return c(self,node=p) p = p.parentNode return None + + def getTagClass(self,tag): + '''Get class corresponding to the tag''' + if tag not in self.known_tags: return None; + return self.known_tags[tag] # ---------------------------------------------------------------- # Adders @@ -518,7 +547,83 @@ class DummyElement(Element): ''' super(DummyElement,self).__init__(parent,'Dummy',node=node) +# -------------------------------------------------------------------- +class ToolbarElement(Element): + def __init__(self, + parent, + tag, + node = None, + name = '', # Toolbar element name + tooltip = '', # Tool tip + text = '', # Button text + icon = '', # Button icon, + hotkey = '', # Named key or key stroke + canDisable = False, + propertyGate = '', + disabledIcon = '', + **kwargs): + '''Base class for toolbar elements. + Parameters + ---------- + parent : Element + Parent element if any + tag : str + Element tag + node : XMLNode + Possible node - when reading back + name : str + Name of element (user reminder). If not set, and tooltip is set, + set to tooltip + toolttip : str + Tool tip when hovering. If not set, and name is set, then + use name as tooltip. + text : str + Text of button + icon : str + Image path for button image + hotkey : str + Named key or key-sequence + canDisable : bool + If true, then the element can be disabled + propertyGate : str + Name of a global property. When this property is `true`, + then this element is _disabled_. Note that this _must_ be + the name of a property - it cannot be a BeanShell + expression. + disabledIcon : str + Path to image to use when the element is disabled. + kwargs : dict + Other attributes to set on the element + ''' + if name == '' and tooltip != '': name = tooltip + if name != '' and tooltip == '': tooltip = name + + # Build arguments for super class + args = { + 'node': node, + 'name': name, + 'icon': icon, + 'tooltip': tooltip, + 'hotkey': hotkey, + 'canDisable': canDisable, + 'propertyGate': propertyGate, + 'disabledIcon': disabledIcon } + bt = kwargs.pop('buttonText',None) + # If the element expects buttonText attribute, then do not set + # the text attribute - some elements interpret that as a + # legacy name attribute, + if bt is not None: + args['buttonText'] = bt + else: + args['text'] = text + args.update(kwargs) + + super(ToolbarElement,self).__init__(parent, + tag, + **args) + # print('Attributes\n','\n'.join([f'- {k}="{v}"' for k,v in self._node.attributes.items()])) + # # EOF # @@ -526,26 +631,28 @@ class DummyElement(Element): # From globalkey.py # -------------------------------------------------------------------- -class GlobalKey(Element): +class GlobalKey(ToolbarElement): SELECTED = 'MAP|false|MAP|||||0|0||true|Selected|true|EQUALS' def __init__(self, parent, tag, node = None, name = '', + icon = '', + tooltip = '', buttonHotkey = '', - hotkey = '', buttonText = '', canDisable = False, + propertyGate = '', + disabledIcon = '', + # Local + hotkey = '', deckCount = '-1', filter = '', - propertyGate = '', reportFormat = '', reportSingle = False, singleMap = True, - target = SELECTED, - tooltip = '', - icon = ''): + target = SELECTED): ''' Parameters ---------- @@ -569,28 +676,26 @@ class GlobalKey(Element): Default targets are selected units ''' - nme = (name if len(name) > 0 else - tooltip if len(tooltip) > 0 else '') - tip = (tooltip if len(tooltip) > 0 else - name if len(name) > 0 else '') + super(GlobalKey,self).\ __init__(parent, tag, node = node, - name = nme, + name = name, + icon = icon, + tooltip = tooltip, buttonHotkey = buttonHotkey, # This hot key - hotkey = hotkey, # Target hot key buttonText = buttonText, canDisable = canDisable, + propertyGate = propertyGate, + disabledIcon = disabledIcon, + hotkey = hotkey, # Target hot key deckCount = deckCount, filter = filter, - propertyGate = propertyGate, reportFormat = reportFormat, reportSingle = reportSingle, singleMap = singleMap, - target = target, - tooltip = tip, - icon = icon) + target = target) # # EOF # @@ -600,7 +705,7 @@ class GlobalKey(Element): # -------------------------------------------------------------------- class GameElementService: def getGame(self): - return self.getParent(Game) + return self.getParentOfClass(Game) # -------------------------------------------------------------------- class GameElement(Element,GameElementService): @@ -608,23 +713,34 @@ class GameElement(Element,GameElementService): super(GameElement,self).__init__(game,tag,node=node,**kwargs) # -------------------------------------------------------------------- -class Notes(GameElement): +class Notes(ToolbarElement,GameElementService): TAG = Element.MODULE+'NotesWindow' def __init__(self,elem,node=None, - name = 'Notes', - buttonText = '', - hotkey = key('N',ALT), - icon = '/images/notes.gif', - tooltip = 'Show notes window'): - super(Notes,self).__init__(elem,self.TAG,node=node, - name = name, - buttonText = buttonText, - hotkey = hotkey, - icon = icon, - tooltip = tooltip) + name = 'Notes', # Toolbar element name + tooltip = 'Show notes window', # Tool tip + text = '', # Button text + icon = '/images/notes.gif', # Button icon, + hotkey = key('N',ALT), # Named key or key stroke + canDisable = False, + propertyGate = '', + disabledIcon = '', + description = ''): + super(Notes,self).__init__(elem,self.TAG, + node = node, + name = name, + tooltip = tooltip, + text = text, + icon = icon, + hotkey = hotkey, + canDisable = canDisable, + propertyGate = propertyGate, + disabledIcon = disabledIcon, + description = description) def encode(self): return ['NOTES\t\\','PNOTES'] +registerElement(Notes) + # -------------------------------------------------------------------- class PredefinedSetup(GameElement): TAG = Element.MODULE+'PredefinedSetup' @@ -673,6 +789,7 @@ class PredefinedSetup(GameElement): +registerElement(PredefinedSetup) # -------------------------------------------------------------------- class GlobalTranslatableMessages(GameElement): @@ -690,11 +807,15 @@ class GlobalTranslatableMessages(GameElement): super(GlobalTranslatableMessages,self).\ __init__(elem,self.TAG,node=node) +registerElement(GlobalTranslatableMessages) + # -------------------------------------------------------------------- class Language(GameElement): TAG = 'VASSAL.i18n.Language' def __init__(self,elem,node=None,**kwargs): super(Languate,self).__init__(sele,self.TAG,node=none,**kwargs) + +registerElement(Language) # -------------------------------------------------------------------- class Chatter(GameElement): @@ -712,6 +833,8 @@ class Chatter(GameElement): Attributes ''' super(Chatter,self).__init__(elem,self.TAG,node=node,**kwargs) + +registerElement(Chatter) # -------------------------------------------------------------------- class KeyNamer(GameElement): @@ -730,6 +853,8 @@ class KeyNamer(GameElement): ''' super(KeyNamer,self).__init__(elem,self.TAG,node=node,**kwargs) +registerElement(KeyNamer) + # -------------------------------------------------------------------- # <VASSAL.build.module.GlobalOptions @@ -883,6 +1008,7 @@ class GlobalOptions(GameElement): return retd +registerElement(GlobalOptions) # -------------------------------------------------------------------- class Option(Element): @@ -894,6 +1020,8 @@ class Option(Element): def getGlobalOptions(self): return self.getParent(GlobalOptions) +registerElement(Option) + # -------------------------------------------------------------------- class Preference(Element): PREFS = 'VASSAL.preferences.' @@ -947,6 +1075,9 @@ class IntPreference(Preference): default = str(default), desc = desc, tab = tab) + +registerElement(IntPreference) + # -------------------------------------------------------------------- class FloatPreference(Preference): TAG = Preference.PREFS+'DoublePreference' @@ -964,6 +1095,9 @@ class FloatPreference(Preference): default = str(default), desc = desc, tab = tab) + +registerElement(FloatPreference) + # -------------------------------------------------------------------- class BoolPreference(Preference): TAG = Preference.PREFS+'BooleanPreference' @@ -982,6 +1116,9 @@ class BoolPreference(Preference): else 'false'), desc = desc, tab = tab) + +registerElement(BoolPreference) + # -------------------------------------------------------------------- class StrPreference(Preference): TAG = Preference.PREFS+'StringPreference' @@ -999,6 +1136,9 @@ class StrPreference(Preference): default = default, desc = desc, tab = tab) + +registerElement(StrPreference) + # -------------------------------------------------------------------- class TextPreference(Preference): TAG = Preference.PREFS+'TextPreference' @@ -1017,6 +1157,9 @@ class TextPreference(Preference): .replace('\n',' ')), desc = desc, tab = tab) + +registerElement(TextPreference) + # -------------------------------------------------------------------- class EnumPreference(Preference): TAG = Preference.PREFS+'EnumPreference' @@ -1042,39 +1185,42 @@ class EnumPreference(Preference): tab = tab, list = sl) + +registerElement(EnumPreference) + # -------------------------------------------------------------------- # CurrentMap == "Board" -class Inventory(GameElement): +class Inventory(ToolbarElement,GameElementService): TAG = Element.MODULE+'Inventory' def __init__(self,doc,node=None, - canDisable = False, - centerOnPiece = True, - disabledIcon = '', - drawPieces = True, - foldersOnly = False, - forwardKeystroke = True, - groupBy = '', - hotkey = key('I',ALT), - icon = '/images/inventory.gif', - include = '{}', - launchFunction = 'functionHide', - leafFormat = '$PieceName$', - name = '', - nonLeafFormat = '$PropertyValue$', - pieceZoom = '0.33', - pieceZoom2 = '0.5', - pieceZoom3 = '0.6', - propertyGate = '', - refreshHotkey = key('I',ALT_SHIFT), - showMenu = True, - sides = '', - sortFormat = '$PieceName$', - sortPieces = True, - sorting = 'alpha', - text = '', - tooltip = 'Show inventory of all pieces', - zoomOn = False): + name = '', + icon = '/images/inventory.gif', + text = '', + tooltip = 'Show inventory of all pieces', + hotkey = key('I',ALT), + canDisable = False, + propertyGate = '', + disabledIcon = '', + centerOnPiece = True, + drawPieces = True, + foldersOnly = False, + forwardKeystroke = True, + groupBy = '', + include = '{}', + launchFunction = 'functionHide', + leafFormat = '$PieceName$', + nonLeafFormat = '$PropertyValue$', + pieceZoom = '0.33', + pieceZoom2 = '0.5', + pieceZoom3 = '0.6', + refreshHotkey = key('I',ALT_SHIFT), + showMenu = True, + sides = '', + sortFormat = '$PieceName$', + sortPieces = True, + sorting = 'alpha', + zoomOn = False): super(Inventory,self).__init__(doc,self.TAG,node=node, canDisable = canDisable, centerOnPiece = centerOnPiece, @@ -1104,16 +1250,7 @@ class Inventory(GameElement): tooltip = tooltip, zoomOn = zoomOn) - - - - - - - - - - +registerElement(Inventory) # -------------------------------------------------------------------- class Prototypes(GameElement): @@ -1149,17 +1286,21 @@ class Prototypes(GameElement): ''' return self.getElementsByKey(Prototype,'name',asdict=asdict) - - +registerElement(Prototypes) # -------------------------------------------------------------------- -class DiceButton(GameElement): +class DiceButton(ToolbarElement,GameElementService): TAG=Element.MODULE+'DiceButton' def __init__(self,elem,node=None, - addToTotal = 0, - canDisable = False, - hotkey = key('6',ALT), + name = '1d6', + tooltip = 'Roll a 1d6', + text = '1d6', icon = '/images/die.gif', + hotkey = key('6',ALT), + canDisable = False, + propertyGate = '', + disabledIcon = '', + addToTotal = 0, keepCount = 1, keepDice = False, keepOption = '>', @@ -1169,19 +1310,16 @@ class DiceButton(GameElement): lockSides = False, nDice = 1, nSides = 6, - name = '1d6', plus = 0, prompt = False, - propertyGate = '', - reportFormat = '** $name$ = $result$ *** <$PlayerName$>;', + reportFormat = '$name$ = $result$', reportTotal = False, - sortDice = False, - text = '1d6', - tooltip = 'Roll a 1d6'): + sortDice = False): super(DiceButton,self).\ __init__(elem,self.TAG,node=node, addToTotal = addToTotal, canDisable = canDisable, + disabledIcon = disabledIcon, hotkey = hotkey, icon = icon, keepCount = keepCount, @@ -1203,24 +1341,27 @@ class DiceButton(GameElement): text = text, tooltip = tooltip) +registerElement(DiceButton) + # -------------------------------------------------------------------- class GameMassKey(GlobalKey,GameElementService): TAG = Element.MODULE+'GlobalKeyCommand' def __init__(self,map,node=None, name = '', - buttonHotkey = '', - hotkey = '', buttonText = '', + tooltip = '', + icon = '', canDisable = False, + propertyGate = '', + disabledIcon = '', + buttonHotkey = '', + hotkey = '', deckCount = '-1', filter = '', - propertyGate = '', reportFormat = '', reportSingle = False, singleMap = True, - target = GlobalKey.SELECTED, - tooltip = '', - icon = ''): + target = GlobalKey.SELECTED): '''Default targets are selected units''' super(GameMassKey,self).\ __init__(map, @@ -1241,6 +1382,8 @@ class GameMassKey(GlobalKey,GameElementService): tooltip = tooltip, icon = icon) +registerElement(GameMassKey) + # -------------------------------------------------------------------- class StartupMassKey(GlobalKey,GameElementService): TAG = Element.MODULE+'StartupGlobalKeyCommand' @@ -1287,21 +1430,24 @@ class StartupMassKey(GlobalKey,GameElementService): if node is None: self['whenToApply'] = whenToApply +registerElement(StartupMassKey) + # -------------------------------------------------------------------- class Menu(GameElement): TAG = Element.MODULE+'ToolbarMenu' def __init__(self, game, node = None, + name = '', + tooltip = '', + text = '', # Menu name canDisable = False, - description = '', + propertyGate = '', disabledIcon = '', + description = '', hotkey = '', icon = '', - menuItems = [],# Button texts - propertyGate = '', - text = '',# Menu name - tooltip = ''): + menuItems = []): if len(description) <= 0 and len(tooltip) > 0: description = tooltip if len(tooltip) <= 0 and len(description) > 0: @@ -1310,6 +1456,7 @@ class Menu(GameElement): __init__(game, self.TAG, node = node, + name = name, canDisable = canDisable, description = description, disabledIcon = disabledIcon, @@ -1320,6 +1467,8 @@ class Menu(GameElement): text = text, tooltip = tooltip) +registerElement(Menu) + # -------------------------------------------------------------------- class SymbolicDice(GameElement): @@ -1370,6 +1519,8 @@ class SymbolicDice(GameElement): def getSymbolicDice(self): return self.getParent(SymbolicDice) +registerElement(SymbolicDice) + # -------------------------------------------------------------------- class SpecialDie(GameElement): @@ -1399,8 +1550,7 @@ class SpecialDie(GameElement): def getSymbolicDice(self): return self.getParent(SymbolicDice) - - +registerElement(SpecialDie) # -------------------------------------------------------------------- class DieFace(GameElement): @@ -1421,6 +1571,9 @@ class DieFace(GameElement): def getSpecialDie(self): return self.getParent(SpecialDie) + +registerElement(DieFace) + # # EOF # @@ -1451,6 +1604,223 @@ class MapElement(Element,MapElementService): # -------------------------------------------------------------------- +class PieceLayers(MapElement): + TAG=Element.MAP+'LayeredPieceCollection' + def __init__(self,map,node=None, + property = 'PieceLayer', + description = '', + layerOrder = []): + super(PieceLayers,self).__init__(map,self.TAG,node=node, + property = property, + description = description, + layerOrder = ','.join(layerOrder)) + + def addControl(self,**kwargs): + '''Add `LayerControl` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : LayerControl + The added element + ''' + return self.add(LayerControl,**kwargs) + def getControls(self,asdict=True): + '''Get all `LayerControl` element(s) from this + + Parameters + ---------- + asdict : bool + If `True`, return a dictonary that maps name to + `LayerControl` elements. If `False`, return a list of all + `LayerControl` children. + + Returns + ------- + children : dict or list + Dictionary or list of `LayerControl` children + + ''' + return self.getElementsByKey(LayerControl,'name',asdict) + +registerElement(PieceLayers) + +# -------------------------------------------------------------------- +class LayerControl(MapElement): + TAG=Element.MAP+'LayerControl' + CYCLE_UP='Rotate Layer Order Up' + CYCLE_DOWN='Rotate Layer Order Down' + ENABLE='Make Layer Active' + DISABLE='Make Layer Inactive' + TOGGLE='Switch Layer between Active and Inactive' + RESET='Reset All Layers' + def __init__(self,col,node=None, + name = '', + tooltip = '', + text = '', + hotkey = '', + icon = '', + canDisable = False, + propertyGate = '', #Property name, disable when property false + disabledIcon = '', + command = TOGGLE, + skip = False, + layers = [], + description = ''): + super(LayerControl,self).__init__(col,self.TAG,node=node, + name = name, + tooltip = tooltip, + text = text, + buttonText = text, + hotkey = hotkey, + icon = icon, + canDisable = canDisable, + propertyGate = propertyGate, + disabledIcon = disabledIcon, + command = command, + skip = skip, + layers = ','.join(layers), + description = description) + + def getLayers(self): + '''Get map - either a Map or WidgetMap''' + return self.getParentOfClass([PieceLayers]) + +registerElement(LayerControl) + + +# -------------------------------------------------------------------- +class LineOfSight(MapElement): + TAG=Element.MAP+'LOS_Thread' + ROUND_UP = 'Up' + ROUND_DOWN = 'Down' + ROUND_NEAREST = 'Nearest whole number' + FROM_LOCATION = 'FromLocation' + TO_LOCATION = 'ToLocation' + CHECK_COUNT = 'NumberOfLocationsChecked' + CHECK_LIST = 'AllLocationsChecked' + RANGE = 'Range' + NEVER = 'Never' + ALWAYS = 'Always' + WHEN_PERSISTENT = 'When persistent' + CTRL_CLICK = 'Cltr-Click & Drag' + + def __init__(self,map, + node=None, + threadName = 'LOS', + hotkey = key('L',ALT), + tooltip = 'Trace line of sight', + iconName = '/images/thread.gif', #'los-icon.png', + label = '', + snapLOS = False, + snapStart = True, + snapEnd = True, + report = (f'{{"Range from "+{FROM_LOCATION}' + f'+" to "+{TO_LOCATION}+" is "' + f'+{RANGE}+" (via "+{CHECK_LIST}+")"}}'), + persistent = CTRL_CLICK, + persistentIconName = '/images/thread.gif', + globl = ALWAYS, + losThickness = 3, + threadColor = rgb(255,0,0), + drawRange = True, + # rangeBg = rgb(255,255,255), + # rangeFg = rgb(0,0,0), + rangeScale = 0, + hideCounters = True, + hideOpacity = 50, + round = ROUND_UP, + canDisable = False, + propertyGate = '', + disabledIcon = ''): + '''Make Line of Sight interface + + Parameters + ---------- + threadName : str + Name of interface + hotkey : str + Start LOS key + tooltip : str + Tool tip text + iconName : str + Path to button icon + label : str + Button text + snapLOS : bool + Wether to snap both ends + snapStart : bool + Snap to start + snapEnd: bool + Snap to end + report : str + Report format + persistent : str + When persistent + persistentIconName : str + Icon when persistent(?) + globl : str + Visisble to opponents + losThickness : int + Thickness in pixels + losColor : str + Colour of line + drawRange : bool + Draw the range next to LOST thread + rangeBg : str + Range backgroung colour + rangeFg : str + Range foregrond colour + rangeScale : int + Scale of range - pixels per unit + round : str + How to round range + hideCounters :bool + If true, hide counters while making thread + hideOpacity : int + Opacity of hidden counters (percent) + canDisable : bool + IF true, then can be hidden + propertyGate : str + Name of property. When that property is TRUE, then the + interface is disabled. Must be a property name, not an expression. + disabledIcon : str + Icon to use when disabled + ''' + super(LineOfSight,self).__init__(map,self.TAG, + node = node, + threadName = threadName, + hotkey = hotkey, + tooltip = tooltip, + iconName = iconName, + label = label, + snapLOS = snapLOS, + snapStart = snapStart, + snapEnd = snapEnd, + report = report, + persistent = persistent, + persistentIconName = persistentIconName, + losThickness = losThickness, + threadColor = threadColor, + drawRange = drawRange, + #rangeBg = rangeBg, + #rangeFg = rangeFg, + rangeScale = rangeScale, + hideCounters = hideCounters, + hideOpacity = hideOpacity, + round = round, + canDisable = canDisable, + propertyGate = propertyGate, + disabledIcon = disabledIcon) + self.setAttribute('global',globl) + + +registerElement(LineOfSight) + +# -------------------------------------------------------------------- class StackMetrics(MapElement): TAG=Element.MAP+'StackMetrics' def __init__(self,map,node=None, @@ -1474,6 +1844,8 @@ class StackMetrics(MapElement): unexSepY = unexSepY, up = up) +registerElement(StackMetrics) + # -------------------------------------------------------------------- class ImageSaver(MapElement): TAG=Element.MAP+'ImageSaver' @@ -1491,6 +1863,9 @@ class ImageSaver(MapElement): icon = icon, propertyGate = propertyGate, tooltip = tooltip) + +registerElement(ImageSaver) + # -------------------------------------------------------------------- class TextSaver(MapElement): TAG=Element.MAP+'TextSaver' @@ -1509,6 +1884,7 @@ class TextSaver(MapElement): propertyGate = propertyGate, tooltip = tooltip) +registerElement(TextSaver) # -------------------------------------------------------------------- class ForwardToChatter(MapElement): @@ -1516,30 +1892,40 @@ class ForwardToChatter(MapElement): def __init__(self,map,node=None,**kwargs): super(ForwardToChatter,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(ForwardToChatter) + # -------------------------------------------------------------------- class MenuDisplayer(MapElement): TAG=Element.MAP+'MenuDisplayer' def __init__(self,map,node=None,**kwargs): super(MenuDisplayer,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(MenuDisplayer) + # -------------------------------------------------------------------- class MapCenterer(MapElement): TAG=Element.MAP+'MapCenterer' def __init__(self,map,node=None,**kwargs): super(MapCenterer,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(MapCenterer) + # -------------------------------------------------------------------- class StackExpander(MapElement): TAG=Element.MAP+'StackExpander' def __init__(self,map,node=None,**kwargs): super(StackExpander,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(StackExpander) + # -------------------------------------------------------------------- class PieceMover(MapElement): TAG=Element.MAP+'PieceMover' def __init__(self,map,node=None,**kwargs): super(PieceMover,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(PieceMover) + # -------------------------------------------------------------------- class SelectionHighlighters(MapElement): TAG=Element.MAP+'SelectionHighlighters' @@ -1547,12 +1933,16 @@ class SelectionHighlighters(MapElement): super(SelectionHighlighters,self).\ __init__(map,self.TAG,node=node,**kwargs) +registerElement(SelectionHighlighters) + # -------------------------------------------------------------------- class KeyBufferer(MapElement): TAG=Element.MAP+'KeyBufferer' def __init__(self,map,node=None,**kwargs): super(KeyBufferer,self).__init__(map,self.TAG,node=node,**kwargs) +registerElement(KeyBufferer) + # -------------------------------------------------------------------- class HighlightLastMoved(MapElement): TAG=Element.MAP+'HighlightLastMoved' @@ -1565,6 +1955,8 @@ class HighlightLastMoved(MapElement): enabled = enabled, thickness = thickness) +registerElement(HighlightLastMoved) + # -------------------------------------------------------------------- class CounterDetailViewer(MapElement): TAG=Element.MAP+'CounterDetailViewer' @@ -1587,7 +1979,7 @@ class CounterDetailViewer(MapElement): extraTextPadding = 0, fgColor = rgb(0,0,0), fontSize = 9, - graphicsZoom = 1.0, + graphicsZoom = 1.0,# Zoom on counters hotkey = key('\n',0), layerList = '', minDisplayPieces = 2, @@ -1649,6 +2041,8 @@ class CounterDetailViewer(MapElement): verticalTopText = verticalTopText, zoomlevel = zoomlevel) +registerElement(CounterDetailViewer) + # -------------------------------------------------------------------- class GlobalMap(MapElement): TAG=Element.MAP+'GlobalMap' @@ -1668,6 +2062,8 @@ class GlobalMap(MapElement): scale = scale, tooltip = 'Show/Hide overview window') +registerElement(GlobalMap) + # -------------------------------------------------------------------- class Zoomer(MapElement): TAG = Element.MAP+'Zoomer' @@ -1687,6 +2083,7 @@ class Zoomer(MapElement): zoomOutKey = key('-'), zoomPickKey = key('='), zoomStart = 3): + '''Zoom start is counting from the back (with default zoom levels, and zoom start, the default zoom is 1''' lvls = ','.join([str(z) for z in zoomLevels]) @@ -1707,6 +2104,8 @@ class Zoomer(MapElement): zoomPickKey = zoomPickKey, zoomStart = zoomStart) +registerElement(Zoomer) + # -------------------------------------------------------------------- class HidePiecesButton(MapElement): TAG=Element.MAP+'HidePiecesButton' @@ -1724,6 +2123,8 @@ class HidePiecesButton(MapElement): showingIcon = showingIcon, tooltip = tooltip) +registerElement(HidePiecesButton) + # -------------------------------------------------------------------- class MassKey(GlobalKey,MapElementService): TAG = Element.MAP+'MassKeyCommand' @@ -1760,6 +2161,7 @@ class MassKey(GlobalKey,MapElementService): tooltip = tooltip, icon = icon) +registerElement(MassKey) # -------------------------------------------------------------------- class Flare(MapElement): @@ -1783,10 +2185,13 @@ class Flare(MapElement): flarePulsesPerSec = flarePulsesPerSec, reportFormat = '') +registerElement(Flare) + # -------------------------------------------------------------------- class AtStart(MapElement): TAG = Element.MODULE+'map.SetupStack' - def __init__(self,map,node=None, + def __init__(self,map, + node = None, name = '', location = '', useGridLocation = True, @@ -1861,6 +2266,8 @@ class AtStart(MapElement): ''' return self.getElementsWithKey(PieceSlot,'entryName',asdict) +registerElement(AtStart) + # # EOF # @@ -1894,6 +2301,7 @@ class GlobalProperties(Element): def getProperties(self): return getElementsByKey(GlobalProperty,'name') +registerElement(GlobalProperties) # -------------------------------------------------------------------- class GlobalProperty(Element): @@ -1919,6 +2327,7 @@ class GlobalProperty(Element): def getGlobalProperties(self): return self.getParent(GlobalProperties) +registerElement(GlobalProperty) # # EOF @@ -2099,7 +2508,8 @@ class TurnTrack(TurnLevel): return [] - +registerElement(TurnTrack) + # -------------------------------------------------------------------- class TurnCounter(TurnLevel): TAG = Element.MODULE+"turn.CounterTurnLevel" @@ -2118,6 +2528,8 @@ class TurnCounter(TurnLevel): loopLimit = loopLimit, turnFormat = turnFormat) +registerElement(TurnCounter) + # -------------------------------------------------------------------- class TurnList(TurnLevel): TAG = Element.MODULE+"turn.ListTurnLevel" @@ -2135,6 +2547,8 @@ class TurnList(TurnLevel): configList = configList, turnFormat = turnFormat) +registerElement(TurnList) + # -------------------------------------------------------------------- class TurnGlobalHotkey(Element): TAG = Element.MODULE+'turn.TurnGlobalHotkey' @@ -2172,6 +2586,8 @@ class TurnGlobalHotkey(Element): '''Get the turn track''' return self.getParent(TurnTrack) +registerElement(TurnGlobalHotkey) + # # EOF # @@ -2316,6 +2732,7 @@ class Documentation(GameElement): return txt +registerElement(Documentation) # -------------------------------------------------------------------- class AboutScreen(Element): @@ -2343,6 +2760,8 @@ class AboutScreen(Element): '''Get Parent element''' return self.getParent(Documentation) +registerElement(AboutScreen) + # -------------------------------------------------------------------- class BrowserPDFFile(Element): TAG = Element.MODULE+'documentation.BrowserPDFFile' @@ -2368,6 +2787,8 @@ class BrowserPDFFile(Element): '''Get Parent element''' return self.getParent(Documentation) +registerElement(BrowserPDFFile) + # -------------------------------------------------------------------- class HelpFile(Element): TAG = Element.MODULE+'documentation.HelpFile' @@ -2400,6 +2821,8 @@ class HelpFile(Element): '''Get Parent element''' return self.getParent(Documentation) +registerElement(HelpFile) + # -------------------------------------------------------------------- class BrowserHelpFile(Element): TAG = Element.MODULE+'documentation.BrowserHelpFile' @@ -2427,7 +2850,9 @@ class BrowserHelpFile(Element): def getDocumentation(self): '''Get Parent element''' return self.getParent(Documentation) - + +registerElement(BrowserHelpFile) + # -------------------------------------------------------------------- class Tutorial(Element): TAG = Element.MODULE+'documentation.Tutorial' @@ -2468,6 +2893,8 @@ class Tutorial(Element): '''Get Parent element''' return self.getParent(Documentation) +registerElement(Tutorial) + # # EOF @@ -2517,6 +2944,8 @@ class PlayerRoster(GameElement): '''Encode for save''' return ['PLAYER\ta\ta\t<observer>'] +registerElement(PlayerRoster) + # -------------------------------------------------------------------- class PlayerSide(Element): TAG = 'entry' @@ -2540,6 +2969,8 @@ class PlayerSide(Element): '''Get Parent element''' return self.getParent(PlayerRoster) +registerElement(PlayerSide) + # # EOF @@ -2614,7 +3045,9 @@ class ChessClock(Element): def getControl(self): '''Get Parent element''' return self.getParent(ChessClockControl) - + +registerElement(ChessClock) + # ==================================================================== class ChessClockControl(GameElement): TAG=Element.MODULE+'ChessClockControl' @@ -2727,6 +3160,7 @@ class ChessClockControl(GameElement): '''Return dictionary of clocs''' return self.getElementsByKey(ChessClock,'side',asdict) +registerElement(ChessClockControl) # # EOF @@ -2961,6 +3395,8 @@ class PieceWindow(GameElement,WidgetElement): tooltip = tooltip, icon = icon) +registerElement(PieceWindow) + # -------------------------------------------------------------------- class ComboWidget(Element,WidgetElement): TAG=Element.WIDGET+'BoxWidget' @@ -2972,6 +3408,8 @@ class ComboWidget(Element,WidgetElement): width = width, height = height) +registerElement(ComboWidget) + # -------------------------------------------------------------------- class TabWidget(Element,WidgetElement): TAG=Element.WIDGET+'TabWidget' @@ -2981,6 +3419,8 @@ class TabWidget(Element,WidgetElement): node = node, entryName = entryName) +registerElement(TabWidget) + # -------------------------------------------------------------------- class ListWidget(Element,WidgetElement): TAG=Element.WIDGET+'ListWidget' @@ -2997,6 +3437,8 @@ class ListWidget(Element,WidgetElement): scale = scale, divider = divider) +registerElement(ListWidget) + # -------------------------------------------------------------------- class PanelWidget(Element,WidgetElement): TAG=Element.WIDGET+'PanelWidget' @@ -3011,6 +3453,8 @@ class PanelWidget(Element,WidgetElement): nColumns = nColumns, vert = vert) +registerElement(PanelWidget) + # -------------------------------------------------------------------- class MapWidget(Element): TAG=Element.WIDGET+'MapWidget' @@ -3046,6 +3490,8 @@ class MapWidget(Element): ''' return self.getElementsByKey(WidgetMap,'mapName',asdict=asdict) +registerElement(MapWidget) + # # EOF # @@ -3258,6 +3704,8 @@ class HexGrid(BaseGrid): width = self.getZone().getWidth() return floor(width / self.getDeltaY() + .5) +registerElement(HexGrid) + # -------------------------------------------------------------------- class SquareGrid(BaseGrid): TAG = Element.BOARD+'SquareGrid' @@ -3307,6 +3755,8 @@ class SquareGrid(BaseGrid): width = self.getZone().getWidth() return floor(width / self.getDeltaX() + .5) +registerElement(SquareGrid) + # -------------------------------------------------------------------- class HexNumbering(BaseNumbering): TAG = Element.BOARD+'mapgrid.HexGridNumbering' @@ -3368,6 +3818,8 @@ class HexNumbering(BaseNumbering): return x,y +registerElement(HexNumbering) + # -------------------------------------------------------------------- class SquareNumbering(BaseNumbering): TAG = Element.BOARD+'mapgrid.SquareGridNumbering' @@ -3395,6 +3847,7 @@ class SquareNumbering(BaseNumbering): return x,y +registerElement(SquareNumbering) # -------------------------------------------------------------------- class RegionGrid(Element): @@ -3452,6 +3905,8 @@ class RegionGrid(Element): return None +registerElement(RegionGrid) + # -------------------------------------------------------------------- class Region(Element): TAG = Element.BOARD+'Region' @@ -3527,6 +3982,8 @@ class Region(Element): return b.getMap() return None +registerElement(Region) + # # EOF # @@ -3579,7 +4036,7 @@ class ZonedGrid(Element): children : list List of `Highligter` children (even if `single=True`) ''' - return self.getAllElements(ZonedGridHighliger,single=single) + return self.getAllElements(ZonedGridHighlighter,single=single) def addZone(self,**kwargs): '''Add a `Zone` element to this @@ -3607,6 +4064,8 @@ class ZonedGrid(Element): ''' return self.getElementsByKey(Zone,'name',asdict=asdict) +registerElement(ZonedGrid) + # -------------------------------------------------------------------- class ZonedGridHighlighter(Element): TAG=Element.BOARD+'mapgrid.ZonedGridHighlighter' @@ -3614,6 +4073,96 @@ class ZonedGridHighlighter(Element): super(ZonedGridHighlighter,self).__init__(zoned,self.TAG,node=node) def getZonedGrid(self): return self.getParent(ZonedGrid) + def addZoneHighlight(self,**kwargs): + '''Add a `ZoneHighlight` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : ZoneHighlight + The added element + ''' + return self.add(ZoneHighlight,**kwargs) + def getZoneHighlights(self,asdict=True): + '''Get all ZoneHighlight element(s) from this + + Parameters + ---------- + 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 + Dictionary or list of `Zone` children + ''' + return self.getElementsByKey(ZoneHighlight,'name',asdict=asdict) + +registerElement(ZonedGridHighlighter) + +# -------------------------------------------------------------------- +class ZoneHighlight(Element): + TAG=Element.BOARD+'mapgrid.ZoneHighlight' + FULL='Entire Zone', + BORDER='Zone Border', + PLAIN='Plain', + STRIPED='Striped' + CROSS='Crosshatched', + TILES='Tiled Image' + def __init__(self, + highlighters, + node = None, + name = '', + color = rgb(255,0,0), + coverage = FULL, + width = 1, + style = PLAIN, + image = '', + opacity = 50): + super(ZoneHighlight,self).__init__(highlighters, + self.TAG, + node = node, + name = name, + color = color, + coverage = coverage, + width = width, + style = style, + image = image, + opacity = int(opacity)) + def getZonedGridHighlighter(self): + return self.getParent(ZonedGridHighlighter) + + +registerElement(ZoneHighlight) + + +# -------------------------------------------------------------------- +class ZoneProperty(Element): + TAG = Element.MODULE+'properties.ZoneProperty' + def __init__(self,zone,node=None, + name = '', + initialValue = '', + isNumeric = False, + min = "null", + max = "null", + wrap = False, + description = ""): + super(ZoneProperty,self).__init__(zone,self.TAG, + node = node, + name = name, + initialValue = initialValue, + isNumeric = isNumeric, + min = min, + max = max, + wrap = wrap, + description = description) + + def getZone(self): + return self.getParent(Zone) + +registerElement(ZoneProperty) # -------------------------------------------------------------------- class Zone(Element): @@ -3693,6 +4242,19 @@ class Zone(Element): The added element ''' return self.add(RegionGrid,**kwargs) + def addProperty(self,**kwargs): + '''Add a `Property` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : Property + The added element + ''' + return self.add(ZoneProperty,**kwargs) def getHexGrids(self,single=True): '''Get all or a sole `HexGrid` element(s) from this @@ -3758,6 +4320,17 @@ class Zone(Element): if g is not None: return g return g + def getProperties(self): + '''Get all `Property` element from this + + Parameters + ---------- + Returns + ------- + children : dict + dict of `Property` children + ''' + return getElementsByKey(ZoneProperty,'name') def getPath(self): p = self['path'].split(';') @@ -3785,6 +4358,8 @@ class Zone(Element): def getYOffset(self): return self.getBB()[1] +registerElement(Zone) + # # EOF # @@ -3879,6 +4454,8 @@ class BoardPicker(MapElement): return ret +registerElement(BoardPicker) + # -------------------------------------------------------------------- class Setup(Element): TAG = 'setup' @@ -3901,7 +4478,9 @@ class Setup(Element): self.addText(txt) def getPicker(self): return self.getParent(BoardPicker) - + +registerElement(Setup) + # -------------------------------------------------------------------- class Board(Element): TAG = Element.PICKER+'Board' @@ -3982,6 +4561,7 @@ class Board(Element): return int(self['height']) return 0 +registerElement(Board) # # EOF @@ -4340,6 +4920,57 @@ class BaseMap(Element): picker = self.getPicker() if picker is None: return None return picker[0].getBoards(asdict=asdict) + def getLayers(self,asdict=True): + '''Get all `PieceLayer` element(s) from this + + Parameters + ---------- + asdict : bool + If `True`, return a dictonary that maps property name + `PieceLayers` elements. If `False`, return a list of all + `PieceLayers` children. + + Returns + ------- + children : dict or list + Dictionary or list of `PieceLayers` children + + ''' + return self.getElementsByKey(PieceLayers,'property',asdict) + def getMenus(self,asdict=True): + '''Get all Menu element(s) from this + + Parameters + ---------- + 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 + Dictionary or list of `Board` children + + ''' + return self.getElementsByKey(Menu,'name',asdict) + def getLOSs(self,asdict=True): + '''Get all Menu element(s) from this + + Parameters + ---------- + 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 + Dictionary or list of `Board` children + + ''' + return self.getElementsByKey(LineOfSight,'threadName',asdict) def addBoardPicker(self,**kwargs): '''Add a `BoardPicker` element to this @@ -4600,6 +5231,47 @@ class BaseMap(Element): The added element ''' return self.add(AtStart,**kwargs) + + + def addLayers(self,**kwargs): + '''Add `PieceLayers` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : PieceLayers + The added element + ''' + return self.add(PieceLayers,**kwargs) + def addMenu(self,**kwargs): + '''Add a `Menu` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : Menu + The added element + ''' + return self.add(Menu,**kwargs) + def addLOS(self,**kwargs): + '''Add a `Menu` element to this + + Parameters + ---------- + kwargs : dict + Dictionary of attribute key-value pairs + Returns + ------- + element : Menu + The added element + ''' + return self.add(LineOfSight,**kwargs) # -------------------------------------------------------------------- class Map(BaseMap): @@ -4657,7 +5329,9 @@ class Map(BaseMap): def getGame(self): return self.getParent(Game) - + +registerElement(Map) + # -------------------------------------------------------------------- class WidgetMap(BaseMap): TAG = Element.WIDGET+'WidgetMap' @@ -4669,6 +5343,8 @@ class WidgetMap(BaseMap): def getMapWidget(self): return self.getParent(MapWidget) +registerElement(WidgetMap) + # # EOF @@ -4721,7 +5397,7 @@ class ChartWindow(GameElement,WidgetElement): ''' return self.getElementsById(Chart,'chartName',asdict=asdict) - +registerElement(ChartWindow) # -------------------------------------------------------------------- class Chart(Element): @@ -4735,6 +5411,8 @@ class Chart(Element): description = description, fileName = fileName) +registerElement(Chart) + # # EOF # @@ -4766,7 +5444,7 @@ class Trait: 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: @@ -5040,6 +5718,15 @@ class WithTraits(Element): Identifier ''' + last = traits[-1] + # A little hackish to use the name of the class, but needed + # because of imports into patch scripts. + if not isinstance(last,BasicTrait) and \ + not last.__class__.__name__.endswith('BasicTrait'): + from sys import stderr + print(f'Warning - last trait NOT a BasicTrait, but a {type(last)}', + file=stderr) + types = [] states = [] for trait in traits: @@ -5165,7 +5852,9 @@ class DummyWithTraits(WithTraits): if parent is not None: parent.remove(self) - + +registerElement(DummyWithTraits) + # -------------------------------------------------------------------- class PieceSlot(WithTraits): TAG = Element.WIDGET+'PieceSlot' @@ -5226,6 +5915,7 @@ class PieceSlot(WithTraits): piece.setTraits(*traits) return piece +registerElement(PieceSlot) # -------------------------------------------------------------------- class Prototype(WithTraits): @@ -5255,6 +5945,8 @@ class Prototype(WithTraits): name = name, description = description) +registerElement(Prototype) + # # EOF # @@ -5262,16 +5954,21 @@ class Prototype(WithTraits): # From traits/dynamicproperty.py # -------------------------------------------------------------------- -# Base class for property (piece or global) change traits. Encodes -# constraints and commands. +# +# class ChangePropertyTrait(Trait): DIRECT = 'P' + INCREMENT = 'I' def __init__(self, *commands, numeric = False, min = 0, max = 100, wrap = False): + '''Base class for property (piece or global) change traits. + + Encodes constraints and commands. + ''' # assert name is not None and len(name) > 0, \ # 'No name specified for ChangePropertyTriat' super(ChangePropertyTrait,self).__init__() @@ -5293,8 +5990,11 @@ class ChangePropertyTrait(Trait): # print(cmd) com = cmd[0] + ':' + cmd[1].replace(',',r'\,') + ':' + cmd[2] if cmd[2] == self.DIRECT: - com += f'\,'+cmd[3].replace(':',r'\:') + com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') + elif cmd[2] == self.INCREMENT: + com += f'\,'+cmd[3].replace(',',r'\\,').replace(':',r'\:') cmds.append(com) + # print(cmds) return ','.join(cmds) def decodeCommands(self,commands): @@ -5305,6 +6005,8 @@ class ChangePropertyTrait(Trait): # print('parts',parts) if parts[-1][0] == self.DIRECT: parts = parts[:-1]+Trait.decodeKeys(parts[-1],',') + if parts[-1][0] == self.INCREMENT: + parts = parts[:-1]+Trait.decodeKeys(parts[-1],',') ret.append(parts) # print(commands,parts) return ret @@ -5840,7 +6542,7 @@ class NoStackTrait(Trait): canStack = False, ignoreGrid = False, description = ''): - + '''No stacking trait''' selectionOptions = (select + (self.IGNORE_GRID if ignoreGrid else '') + bandSelect) @@ -7319,6 +8021,7 @@ class Game(Element): ''' return self.getAllElements(AtStart,single) +registerElement(Game) # -------------------------------------------------------------------- class BasicCommandEncoder(GameElement): @@ -7326,6 +8029,8 @@ class BasicCommandEncoder(GameElement): def __init__(self,doc,node=None): super(BasicCommandEncoder,self).__init__(doc,self.TAG,node=node) +registerElement(BasicCommandEncoder) + # # EOF # @@ -8446,7 +9151,8 @@ class LaTeXExporter(Exporter): vassalVersion = '3.6.7', nonato = False, nochit = False, - counterScale = 1): + counterScale = 1, + resolution = 150): '''Exports a PDF and associated JSON files to a VASSAL module. Parameters @@ -8473,6 +9179,8 @@ class LaTeXExporter(Exporter): Make grids visible vassalVersion : str VASSAL version to encode this module for + resolution : int + Resolution for images (default 150) ''' self._vmodname = vmodname self._pdfname = pdfname @@ -8487,6 +9195,7 @@ class LaTeXExporter(Exporter): self._vassalVersion = vassalVersion self._nonato = nonato self._nochit = nochit + self._resolution = resolution self._counterScale = counterScale self._battleMark = 'wgBattleMarker' self._battleMarks = [] @@ -8572,6 +9281,7 @@ class LaTeXExporter(Exporter): v(f'Tutorial log: {self._tutorial}') v(f'Patch scripts: {self._patch}') v(f'Visible grids: {self._visible}') + v(f'Resolution: {self._resolution}') v(f'Scale of counters: {self._counterScale}') def setup(self): @@ -8704,6 +9414,7 @@ class LaTeXExporter(Exporter): args = ['pdftocairo', '-transp', '-singlefile', + '-r', str(self._resolution), '-f', str(page), '-l', str(page), '-png' ] @@ -8772,9 +9483,9 @@ class LaTeXExporter(Exporter): imgsinfo = self.getImagesInfo() if len(imgsinfo) - 1 != docinfo['Pages']: - raise RuntimeError(f'Number of pages in {pdfname} ' - f'(doc["Pages"]) not matched in JSON ' - f'{self._infoname} -> {len(imagesinfo)}') + raise RuntimeError(f'Number of pages in {self._pdfname} ' + f'{docinfo["Pages"]} not matched in JSON ' + f'{self._infoname} -> {len(imgsinfo)}') with VerboseGuard(f'Converting {docinfo["Pages"]} ' f'pages in {self._pdfname}') as v: @@ -9126,10 +9837,16 @@ class LaTeXExporter(Exporter): 'property': 'Phase', 'names': self._sides } }) turns.addHotkey(hotkey = self._clearMoved+'Phase', - name = 'Clear moved markers') + name = 'Clear moved markers', + reportFormat = (f'{{{self._verbose}?(' + f'"`Clear all moved markers, "+' + f'""):""}}')) if len(self._battleMarks) > 0: turns.addHotkey(hotkey = self._clearBattlePhs, - name = 'Clear battle markers') + name = 'Clear battle markers', + reportFormat = (f'{{{self._verbose}?(' + f'"`Clear all battle markers, "+' + f'""):""}}')) self._dice = self._categories\ .get('die-roll',{}) @@ -9153,7 +9870,9 @@ class LaTeXExporter(Exporter): # f'+" <img src=\'{die}-"+result1' # f'+".png\' width=24 height=24>"' f'}}'), - resultWindow = True); + resultWindow = True, + windowX = str(int(67 * self._resolution/150)), + windowY = str(int(65 * self._resolution/150))); sdie = symb.addDie(name = die); for face, fdata in faces.items(): fn = fdata['filename'] @@ -10186,13 +10905,14 @@ class LaTeXExporter(Exporter): # ---------------------------------------------------------------- def factionTraits(self,faction): - traits = [ReportTrait(self._eliminateKey, - self._restoreKey, - self._trailKey), + offX = 36 * self._counterScale * self._resolution/150 + offY = -38 * self._counterScale * self._resolution/150 + traits = [#ReportTrait(self._eliminateKey, + # self._restoreKey, + # self._trailKey), TrailTrait(), RotateTrait(), - MovedTrait(xoff = int( 36 * self._counterScale), - yoff = int(-38 * self._counterScale)), + MovedTrait(xoff = int(offX),yoff = int(offY)), DeleteTrait(), SendtoTrait(mapName = 'DeadMap', boardName = f'{faction} pool', @@ -11308,7 +12028,10 @@ class LaTeXExporter(Exporter): pc = p["coords"] if j == 0: vv(f'',end='') vv(f'[{pn}] ',end='',flush=True,noindent=True) - + + if pn.endswith(' flipped'): + pn = pn[:-len(' flipped')] + x, y = tran(*pc) r = grid.addRegion(name = pn, originx = x, @@ -11499,7 +12222,7 @@ class LaTeXExporter(Exporter): def addDie(self): '''Add a `Die` element to the module ''' - if self._dice is not None: + if self._dice is not None and len(self._dice) > 0: return self._game.addDiceButton(name = '1d6', hotkey = self._diceKey) @@ -11568,6 +12291,9 @@ if __name__ == '__main__': ap.add_argument('-S','--counter-scale', type=float, default=1, help='Scale counters by factor') + ap.add_argument('-R','--resolution', + type=int, default=150, + help='Resolution of images') args = ap.parse_args() @@ -11600,6 +12326,7 @@ if __name__ == '__main__': 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: |