Some new design thoughts

Hi all. Been awhile.

I have recently had some more thought about the design of a boardgame engine. The topic intrigues me. Some may remember my efforts with Lua, Qt and binding.

https://forum.vassalengine.org/t/lua/10461

The conclusion was that the binding simply did not work. Also, the size of the binding libraries was an order of magnitude greater than the Qt libraries themselves, not good.

On my GitHub wiki I have sketched another way of doing the binding while at the same time ensuring safety. Check it out. Being able to call the GUI from Lua like this is very cool. No binding. I use GTK because it is a standard part of Linux but any other graphic library may do, including Qt.

I will continue to analyze designs and do small feasibility tests. There are a number of issues that Vassal must deal with which I will not deal with. But I hope my design ideas will have some value. I will continue on an irregular basis and post on this thread when the wiki is updated. Comments are welcome.

By the way, how is development of Vassal 4.0 going ?

So quiet here? I hope this thread is not considered spam even if it is not directly related to Vassal.

Next up is the module, see page 2 on my GitHub wiki. Not surprisingly, the module is not too different from a Vassal module. But unlike a Vassal module it contains a directory of Luau scripts. Here I stumbled into my first big problem in design.

There is a good reason why Java classes are closely associated with their files. If all Java classes were in a single file the code would become unwieldy. In Java it is easy to reference classes in other files.

file 1: public class ClassName {}
file 2: ClassName variable;

In Lua we have the require(“fileName”). The problem is that when you load and compile Luau script files into the VM the file name of each individual file becomes meaningless. The problem was solved by using the chunk name in the load-step to define a Luau global that in a unique way can reference the script (chunk) in a file.

What does the Luau script in the design actually do? Two things. It defines a start state for the whole game, how windows, maps, counter, cards, etc. look. Second, it defines what happens when various events are triggered, like right clicking over a counter. That is why the script must be able draw a specific menu with entries. But the script never calls the GUI directly, only calls exposed C functions with parameters which in turn calls the actual GUI. For complex calls C is able to directly access datastructures (tables) in Lua for information.

In principle the module designer must be able to draw whatever on the map, like circles and dotted lines. It depends on what GUI featues are exposed.


In my tests I use GTK simply because it is available by default on Linux systems. I just have to install the header files (libgtk-3-dev).


Next up is Drag-and-Drop. This feature is fundamental and must work.

Then

Grid
Counters and counter attributes
Counter stacks
Game state and undo functionality
… and the list goes on

The fun thing about such a design project is that you never know where it leads you. I may well stumble into problems that can’t be solved or problems I do not wish to use more time on. We will see.

1 Like

My GitHub wiki has been updated with a page on Drag and Drop.

The module designer can set the transparency (alpha) of a dragged counter. All else is located in the C++ part of the engine and need not be modified by the module developer.

The drag and drop is done with GTK3. I have not worked much with GTK. My impression is not good. The knowledge base is small. Examples of code are rare. New versions of GTK require applications to be rewritten. That must be the reason why a Linux distribution have libs for both GTK2, GTK3 and GTK4.

In GTK4 you can not set z-order. Yes, you read correctly. Maybe it’s possible to reorder the children of a container? Who knows. Why have they made such a basic feature so inaccessible?

It will likely soon be necessary to use Qt instead of GTK. But making a small suitable Qt
lib is a project in itself. I had hoped to make some simple pilot test with GTK.

It is my impression that GTK is not well designed. That each new version is so different
shows this. A good design never needs change, only additions. Linux/UNIX is an example of a design that has never needed change.

Next up is Grid. I have the basic idea of the design. Grid is the first real test for how a module is developed. I hope to show what I mean by “the script is the module”. The main idea is that you should not need a separate tool to develop a module. The script of a module tells you how it works. To test and see your modifications you just need to reload the script.

1 Like

My GitHub wiki has been updated with a section on grids.

The code generates a simple hex grid with snap-to functionality on the center dots.

It is possible to display the grid as a circle with a given radius, color and alpha. Such a display makes it possible to align the grid to the map. The display can be turned off with the function gridShow(false). If you run this code, make sure colors and alpha lie between 0.0 and 1.0. There is no input validation.

This is how the module developer will work. He will have the ability to specify the size of the grid (with what I call that the a and b parameters) and a grid offset. Be displaying the grid, he will then be able to see to what degree the grid fits with the map’s grid and make adjustment.

The adjustment is immediately visible by the ability to reload the Luau script while the engine is running!

There is no tool to align the grid, only this reloading of script. You can say that the tool is the module window itself. This is the core of the design.

There are pros and cons with such a design. Pros are flexibility and simplification. Cons may be the fact that the module developer needs to know Lua scripting, that more is done “manually” and that documentation (and good error messages) are vital. The method of making a module must be intuitive or else the design in flawed.


Next now is the counter and its traits. The concept of traits will be similar to that of VASSAL. It is also a good idea that traits exist in stacks, where a trait influences all traits below it on the stack.

There must be a separate counter window that will show the counters. A change to the counters in the script will be easily visible after a reload of the script.

The basic premise is that the definition of counters and their traits are defined in a flat Luau script file (or files). Traits can be thought of as Luau functions. Many common traits can be pre-made in the Luau part of the engine, like the Delete trait.

The interesting thing about scripting traits is that the module developer can make his own traits. Since all traits are Luau scripts, any trait can be modified or expanded as needed. The only limitation is what is offered from the exposed functions of the C++ API.

Traits have functional aspects, but they can also have visual aspects (like the Can Rotate trait). Traits may or may not have an entry in the right-click menu of a counter.

The data structures (tables) in Lua must be used to define counter-templates that can be copied up for each new counter.

The concept of traits in counters is obviously a large and complex subject. I will not try to solve all problems at once but go forward step by step. The first step is to load the image resources and give them an id. The obvious id is the filename but if a file has the same name in two sub-directories then a number must be added. By printing to a log-file what id belongs to what image file, the module developer can use the ids to specify counter images.


Later it will be time to deal with stacks of counters, among other things changing the position of counters in a stack. I’m afraid I will have to start using Qt for this, which means making a Qt library that is small, sufficient and can be redistributed. This will take time.

A lot has been done. I am glad to say that three traits have been implemented. VASSAL players will recognize them. The design is starting to show itself. Read on.

First off a few concepts. Traits only exist in the Luau-part of the engine. The C++ part of the engine only performs necessary GUI tasks to make the effect of the traits shown. This split is important, for it gives the module designer freedom to define what ever trait is necessary as long as it fits in with what the C++ part can offer.

A trait is a Lua table of key/value pairs. Some keys are standard, like “menuname” (if the trait has an entry in the right-click menu) and “menuclick” (what function to call if a particular right-click menu entry is clicked). But basically a key/value pair can be whatever as long as their use is defined in the implementation of the trait. The Image trait will for instance have the key “images” and as value a table of all the possible images the trait can take. The Lua table data structure is very flexible and can contain other Lua tables.

A counter is a Lua table of traits. Counters (unlike traits) need to have a C++ representation. Therefore a counter must also have a function called “create_class” that creates a C++ class for the counter. But a module developer is free to define whatever special functionality a counter may have.

I know these concepts are a bit confusing at start. The best way to see what I mean is to study the code. Link here:

GitHub wiki

In file traits.luau all the traits exist. In VASSAL designers see a popup window with all available traits on the left side and arrows that move traits into the right side and that can also change their order. In this design lies in the script. There is no tool, only a script file (traits.luau) that define all available traits and a class called Traits that is used to assign a group of traits to a counter. Here is traist.luau:

-- a trait is a table
-- traits have common table-fields but extra custom fields may be added



Image = {}

function Image:new (images)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    --- fields
    o.images = images
    o.menuname = "Flip"
    o.menuclick = "actionFlip"
    return o
end


--- since Rotate and Delete has the same function everywhere only one instance may exist


Rotate = {	
    degrees = 60,
    menuname = "Rotate",
    menuclick = "actionRotate"
}				



Delete = {
    menuname = "Delete",
    menuclick = "actionDelete"
} 



Traits = {}

function Traits:new (images)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    --- fields
    o.Rotate = Rotate	
    o.Image = Image:new(images)
    o.Delete = Delete
    return o
end


return Traits

Since this is a script, the module designer is free to make new Traits classes with a custom selection of traits. The order of traits is the order in the script but the use of order has not yet been implemented. Here is counter.luau:

-- a counter is a table of traits
-- the order of traits is as they appear in the table with the lowest trait first

Counter = {}


function Counter:new (images)
    local o = Traits:new(images)
    setmetatable(o, Counter)
    Counter.__index = Counter
    o.imageIndex = 1
    table.insert(Counter, o)
    create_class(images[1])
    return o
end


function Counter:delete (id)	
    Counter[id] = nil	
end


function Counter:nextImage (id)
    Counter[id].imageIndex = Counter[id].imageIndex + 1
    local length = #(Counter[id].Image.images)
    if Counter[id].imageIndex > length then
	    Counter[id].imageIndex = 1
    end
    return Counter[id].Image.images[Counter[id].imageIndex]
end



return Counter

Counters are created in module.luau:

-- create two counters


Counter["12R-51R"] = Counter:new({"12R-51R", "12R-51R_f"})
Counter["BRIGADE-3"] = Counter:new({"BRIGADE-3", "BRIGADE-3_f"})

Counters are key/value pairs where the key is the name (id or filename without extension) of the counter and the value is an instance of the counter class (a table). The constructor takes a list of images the counter can be (here two images, the front and flip side of a counter).

On the GitHub wiki page I have illustrated the sequence of calls between the Luau part and C++ part of the engine to implement and execute traits. I hope this shows how flexible the design is.


The window with counters (the counter repository) is a separate window. The chat window and server window may also be separate windows. It may be useful to maximize the map window and pin the chat window/counter window on top of it or just maximize when needed.

There should be an option to have a multi-windowed application or a single-window application with sub-windows.


Regarding what’s coming next, I am unsure if I want to use time right now on dragging copies of counters to the map window, stacking them and dealing with selection/reorder of stacks.

The basic idea is to be able to open a stack in a window and do reorder/selection there.

Only one stack can be opened at a time. This prevents clutter on the board. Stacks with no offsets between counters can have a small number in the top right corner telling how many counters are in the stack.

Stacking is more an implementation issue than a design issue. I will have to find another GUI library for that. If anyone thinks it’s possible to do this with GTK, feel free to comment. My knowledge of GTK is limited.

What is more interesting from a design perspective is to implement game state and UNDO/REDO functionality. Fortunately the design encourages a split between the logical representation of the game and its visual representation. The logical representation should at any time be able to generate the visual representation. Undo is then a matter of running through the state-stack until you reach the next to last move. Redo is simple, for the next state on the stack will not be deleted until a totally new move is done.

The split between a Luau part and a C++ part, where the C++ part solely deals with the visual representation, means that if game state lies in the Luau part then it’s totally independent of any visual representation. The state-stack can be changed without changing what you see on the board. When you run up the state-stack to the next to last move, the state is ready to be shown on the map.

Undo/Redo has been implemented with a stage stack and a stage pointer.

Check out the new GitHub wiki page.

First let me say more about what was done first.

Drag and drop was already implemented but I had to make a new trait which VASSAL users will recognize as the Marked When Moved trait. I call it MarkMoved. This trait is basically a mask with an offset relative to the counter. The MarkMoved trait inherits a basic Mask trait as shown below in traits.luau

Mask = {}
    
function Mask:new (mask, x, y)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    --- fields
    o.mask = mask
    o.offsetx = x
    o.offsety = y
    return o
end



MarkMoved = {}
    
function MarkMoved:new (x, y)
    local o = Mask:new("_Moved", x, y)
    setmetatable(self, {__index = Mask})
    self.__index = self
    o.moved = false 	
    o.menuname = "Moved"
    o.menuclick = "actionMoved"
    o.movetrigger = function(id) Counter:markMoved(id) end	
    return o
end

This is how inheritance works in Lua. MarkMoved creates a Mask with _Moved as the image and offsets x and y. In addition MarkMoved needs its own fields to tell if the mask is active (moved) and a trigger for what shall be done when it receives a move call from the C++ part (function movetrigger). Besides this it needs the usual menuname and menuaction for the counter’s right-click menu. The menuaction flips the counter’s move status.

The _Moved is an index to the image resources.

string file = Counter::resources["_Moved"];

Both system resources (inherent images of the engine) and resources loaded from the module (the images created by the module developer) will be indexed in the same way with the convention that system resources have an underline.

If you look at the definition of a counter in counter.luau

Counter = {}


function Counter:new (images, x, y)
    local o = Traits:new(images)
    setmetatable(o, Counter)
    Counter.__index = Counter
    o.degrees = 0
    o.x = x
    o.y = y
    --table.insert(Counter, o)
    create_class(images[1], x, y)
    State:add(images[1], Counter:copy(o))
    return o
end

you will see fields for x and y position. It’s probably a good idea to move this out to a new trait called Position. Besides x, y coordinates this trait can give the position in a stack and also the position (location) within a hex. A module developer can f.ex. add a custom field for what floor the counter is on.

In module.luau where counters are created you will now see this:

-- create two counters


Counter["12R-51R"] = Counter:new({"12R-51R", "12R-51R_f"}, 130, 100)
Counter:addTrait("12R-51R", "MarkMoved", MarkMoved:new(72, 15))


Counter["BRIGADE-3"] = Counter:new({"BRIGADE-3", "BRIGADE-3_f"}, 250, 100)

I have made a special addTrait function that can add traits individually to counters. “12R-51R” has a MarkMoved trait while “BRIGADE-3” does not have one. Also note the calculated offsets 72 and 15. This presumes all counters are of the same size. It is possible to let these offsets be calculated as a function of the counter size.


In the C++ part a very important change was necessary. A counter has to be generated from a base image to its current image as defined by the state of the counter. The state of a counter in C++ is for the moment defined by these five fields in a state struct.

struct State
{
	int x;
	int y;
	bool moved;				// only true if trait		
	int degrees;			// only not zero if trait
	std::string name;		// name of current (flipped) image	
};

Every instance of a counter-class has a function to generate it visual appearance:

void Counter::setImage()

See the full code on GitHub. I also made some changes to the rotate function but “unexpected results” may occur when moving rotated counters. It will be rewritten with the new GUI library. Note that struct State is updated from the Luau part, not the other way around.

counter.luau

function Counter:generateGUI()
    
    for k, v in pairs(Counter) do
	    
	    if type(k) == "string" and k ~= '__index' and type(v) ~= 'function' then
	    
		    local name = Counter[k].Image.images[ Counter[k].Image.imageIndex ]
		    
		    generateGUI(k, 
					    Counter[k].x, 
					    Counter[k].y,  
					    Counter[k].MarkMoved ~= nil and Counter[k].MarkMoved.moved,
					    Counter[k].degrees,
					    name)
	    end
    end

end

luau.cpp

static int generateGUI(lua_State *L)
{		
    
    const char *name	= lua_tostring(L, -1);	
    int degrees 		= lua_tonumber(L, -2);	
    bool moved 			= lua_toboolean(L, -3);
    int y 				= lua_tonumber(L, -4);
    int x 				= lua_tonumber(L, -5);
    const char *id 		= lua_tostring(L, -6);
    lua_pop(L, 6);
    
    Counter *found = Counter::findObj(id);
    if (found)
    {
        found->state.x = x;
        found->state.y = y;
        found->state.moved = moved;
        found->state.degrees = degrees;
        found->state.name = string(name);
        found->setImage();
    }
    else
        printf("error\n");
    
    
    return 0;
}

A game state is just the value of all fields in all traits in all counters at a given instance.

A move brings the game from one state to the next state.

Moves are stored on the stageStack.

A stage is a number of moves that brings the game state from on meaningful instance to the next meaningful instance. An example of this is moving a counter. This consists of two moves, one updating the x field and the other updating the y field. It would be meaningless to undo the x and y coordinate independently of each other.

Moves on the stageStack are separated with a special ‘end’ move. The ‘end’ move separates stages.

The stagePointer points at the current stage, the stage that is visible on the screen.

An Undo will move the stagePointer back to the previous ‘end’. A Redo will move the stagePointer forward to the next ‘end’ (if there are any).

This is the basic idea behind the implementation of Undo and Redo.

In order to generate the game state at n - 1, it is necessary to update all the fields from
the base state up until stage n - 1. The implementation of this is very simple in Lua.

function State:executeUntil(stage)
    
    for i = 1, stage do
	    
	    local id = State.stageStack[i].id
	    local trait = State.stageStack[i].trait
	    local key = State.stageStack[i].key
	    local value = State.stageStack[i].value	
	    
	    
	    if trait == 'Counter' then
		    Counter[id][key] = value		 
	    end
	    
	    if trait == 'MarkMoved' then
		    Counter[id][trait][key] = value		 
	    end
	    
	    if trait == 'Image' then
		    Counter[id][trait][key] = value 
	    end
	    
	    if trait == 'Delete' then			
		    Counter[id] = nil	
	    end
	    
    end
    
end

Loop through all moves from index one the the desired stage and update the fields in the traits. When this is done it’s just a matter of generating the GUI (the visible representation of the state).

function State:undo()
    
    local i = State:findLastStage ()
    
    State:reset()			-- reset Counter with saved State	
    
    State:executeUntil(i)	-- execute moves up to last stage
    
    
    Counter:generateGUI()
    
end

Next up is finding and making a new library that will replace Gtk3.

Then it’s time to deal with stacking. First drag and drop must be implemented with the new library.

Then it must be possible to drag copies of counters from the counter window to the map window.

A grid must be set up and counters must be able to be stacked with an given offset.

Then it must be possible to open a stack and do two things: select some units (SHIFT key)
and reorder units.

I will follow the usual conventions. Hoovering over the stack will display a popup window with its contents. Double clicking a stack will open a window where selecting and reordering will be possible.

I do not think it’s a good idea to open several stacks at the same time. That clutters the board. But exactly how this is done depends on practical considerations. It is important to avoid clutter but also important to make it as user friendly as possible.


Regarding the new library, I will look closely at what I need. It must be possible to place a GUI element at x, y on the screen. It must be possible to change the z-order of GUI elements if they happen to lie on top of each other.

It is a plus (but maybe not that important) that all functions in the library that can read/write the host file system be removed. Reading/writing files is done only with C++.

I think it is good design practice to make the module as self-contained as possible. By this I mean that it should have all the needed libraries supplied. It should not be necessary for the user to install any extra software.

This means distributing the Luau VM (Virtual Machine) and the GUI library with game engine.

Of course, since many libraries are dynamic libraries, “self-contained” is a relative notion.
But the rule still holds, a user should not have to install any software to run the module.
The user just downloads the module (which is a combination of the implementation of a game, the game engine and the GUI library) and can run it straight away.

The downside of this is that the size of a module is greater than the size of a typical VASSAL module. Let us look at what the size may be.

The size of the Gtk3 library (libgtk-3.so.0.2404.29) my Linux distribution is just 8,5 MB. This is a dynamic library. The actual needed size is larger but I guess not by much. Among other libraries GTK3 needs gdk-pixbuf (libpixman-1.so ?) which is just 694,4 kB and Cairo (libcairo.so.2.11600.0) which is just 1,2 MB. On a Linux system these libraries are already
installed and need not be redistributed.

The size of the Luau executable is 10,5 MB.

When I link I use these static libraries:

libluauvm.a 2,3 MB
libluaucompiler.a 7,7 MB
libisocline.a 1,0 MB
libluauast.a 4,9 MB

The total Luau size is then about 26 MB.

Total size of Luau and libraries is then roughly 26 MB + 10 MB or 36 MB.

The compiled size of the game engine is right now 6,7 MB but will of course increase. Finally there are the image resources. My WW1 VASSAL module has images resources of 6,7 MB. Large VASSAL modules may have image resources of 20 MB. It varies greatly.

A typically self-contained module will then be around 50 MB which is of course
much compared with a typical VASSAL module.

The downside of self-contained modules is that a lot of redundant software is distributed.
But with today’s bandwidth and storage capacity this should not really be a problem. 40
downloaded modules is still just 2 GB, which is nothing on disks with a 500 GB or 1000 GB
capacity.

The upside of such a distributions is that the module never becomes obsolete. As long as
an opponent has the same distribution the module will always work. There is nothing outside the module that will make it stop working.

The C++ part (and GUI library) of the engine can be changed independently of the Luau part as long as the Luau C API remains the same. This is useful for the inevitable correction of bugs and new features. Only if changes imply a change of script must the user download a new version of the module. But the old module will still run with its version of binary, script and library.

A distribution of this game engine is an independently compiled C++ binary with a GUI library together with a VASSAL-like module for any particular game. The module will contain the image resources and the Luau script files for a particular game. The Luau script files are based on a template (the Luau part of the engine). They are then modified/developed to implement a particular game.

Regarding integrity, the script files can be verified before they are compiled with a checksum. The sum can be compared with a server checksum. The C++ part will have a build version for checking.


When stacking is done it’s time to piece it all together. The pilot tests I have made will be merged into a very rough pilot game engine. This in itself will demand more development.

After the game engine is in place, a very interesting phase starts. I will try to implement my WW1 Verdun VASSAL module on the new engine. How will it go? How will I be able to implement those artillery fire tables? It will be a good test on how the engine copes. It will bring into focus what more development is needed.

One very important issue is map scaling. The map and counters must be scaled independently and placed relative to each other.

It was not difficult to chose a new GUI library. But first a few words about wxWidgets.

I managed to download, compile, setup and run their drag-and-drop example in one afternoon. Good. Then I made investigations into the existence of functions for z-order.
To my disappointment there were none. Here it says plainly:

wxWidgets does not support overlapping siblings.

The choice was then simple. It was of course Qt.


On the GitHub wiki 7. GUI page I have described in detail what I did to install and run Qt. It did not go as smoothly as with wxWidgets. Among other things there was no default generation of the so-called xcb-plugin even if this plugin is vital for running Qt on Linux. You have to explicitly give options in the configure-step.

Don’t forget to use a virtual machine when you install Qt. You need to install a number of additional developer (-dev) libraries.

I have reimplemented the code in 6.State with Qt. That is to say, the functionality is
the same but 6.State used Gtk3 while 7.GUI uses Qt. The difference is interesting to study. On the whole I think that the Qt code is better but a few things were in my opinion done better in Gtk. One example is signal connecting:

In Gtk3 :

g_signal_connect (menuitem, “activate”, G_CALLBACK (do_activate), (gpointer)e.entryaction);

In Qt :

QObject::connect( action, &QAction::triggered, this, [=]()->void{ do_activate(action); } );

Come on, guys. What on earth is [=]()->void{ do_activate(action) ??? It is a “lambda expression”. Read more here. Parameter passing in signals is done a lot easier in Gtk3. You just give a gpointer to whatever you like and it will be accessible in the handler.


Qt documentation is excellent. The Qt knowledge base is ok. It’s what you expect from a professional company. The down-side is they are profit-driven. Profit-driven companies are of course needed but maybe not for an application like this. Their software is mainly open-source and distributable but you never know what kind of restrictions they may put.

The size of the dynamic Qt libraries are modest:

libQt6Core.so.6.5.2         6,9 MB
libQt6Gui.so.6.5.2          9,7 MB
libQt6Widgets.so.6.5.2      7,1 MB    

To my amusement I discovered that there already is a Qt5 library on my Linux distribution. So they do take free distribution seriously. Still, it would be a good idea to contact Qt and describe in detail what distribution of their software this application has and get an explicit ok. Maybe we have to state visibly that the engine is powered with Qt?


A great plus with Qt is that all the major platforms are support: Linux, macOS, Windows, Andriod and iOS.

Read more about their supported platforms here.

That means that little or no modification is necessary to generate the engine on any platform. But of course, it is necessary to compile and maintain five different executables. As long as the platform has a C++ compiler this should not be a problem. It may also be possible to cross-compile from a single platform (Linux) to make maintaining five executables easier.

Linux has something called Wine that may be used to run Windows-executables. MacOS (and maybe Android) has something similar.


The flipping, rotation, moving and deleting of counters should now work. There has been no extensive testing. The Undo and Redo functionality works well and has a simple implementation. Note that the menu bar can be customized to look better.

Regarding rendering, I don’t see a difference in quality between Gtk3 and Qt. Compare the excecutables of 6.State and 7.GUI.


Next is dragging of counter-copies from the counter window to the map window and stacking of counters, the ability to open a stack and reorder a stack. Then comes selection of counters and the ability to perform the same task on multiple counters (like moving, rotating, etc).

There are two ways to organize an application: single-windowed or multi-windowed. In a single-windowed application sub-windows are organized in a grid. In a multi-windowed application there are separate windows for every major function like map window, repository window, chat window and server window.

For the time being I go for a multi-windowed application. Most players want to see as much of the map as possible. A single-windowed version can be made later.

Technically it is not more difficult to make a multi-windowed application than a single-windowed one. You can easily drag counters in Qt between windows. No extra code is needed. You just need to pay attention to the source of a drag-operation.


The main window is the map window. The first separate window I make is what I call the repository window. This is where every counter/marker in the game is stored.

I refer to the new section on the GitHub wiki for more information and images.

Later the repository window will have tabs, subtabs and subsubtabs.


I then started dealing with stacks of counters.

First of all: stacking is just a rendering issue. That means that the Luau-part need not know about stacking. It only knows that certain counters have the same x,y coordinate.

The fundamental design idea is this: the data structure needed for stacks is generated every time it is needed from the position of the counters. “Every time it is needed” means every time the map is repainted.

This may sound a bit strange. But it is not, because the time needed to do this generation is small on modern CPUs, especially when considering that the maximum number of counters is in the magnitude of a thousand.

This design idea means that the data structure holding stacks need not be updated in complex way every time a counter or a stack of counters move. It saves code and it lessens the risk for bugs.


I have made two types of stacks: with or without offset. The user will later be able to chose what type he wants. Stacks without offset have a white number on a black background in the lower corner showing stack size.


As usual the code has not been tested much. The quality of the code is not tip-top. In particular I use externs which I don’t like. You can avoid them but for the time being they are useful.

One more issue: a rotated counter needs a mask so only the visible part of the counter triggers the popup menu (like it is in VASSAL).

Next up is showing stack contents when hoovering, opening and closing stacks, stack reordering, selection of counters and the ability to move a selected stack.

I am busy now but I hope to post again in late November.