In Verdun: A Generation Lost there are French and German artillery counters. They are used in the Bombardment Segment and the Defensive Fire Segment.
As in most games, an artillery counter can fire only at one target and only once in a segment. More than one artillery counter may fire at the same target. The total fire power is then added up. The counter needs to be in range of its target. Different artillery counters have different ranges. The counters have a reduced strength side. Different power values (combat factors) are used for
Bombardment and Counter-Battery fire.
When there are 35+ artillery counters on the map, you can imagine that keeping track of who fire where, ranges and total firepower on each target can be demanding. Actually, this is one of the reasons why Verdun: A Generation Lost demands a special interest in the WW1 Western Front to play.
To keep track of the book-keeping and speed up play, I devised a special table, the Artillery Fire Table, for each side. This is the table I will now implement on my game-engine.
Interested can first check out the VASSAL module I made and see how the Artillery Fire Table was implemented on that engine.
The main point of this implementation is to show how a complex module feature can be made. In the VASSAL module this was the most difficult feature to do I remember. Let us then look at its equivalent on this engine. Note that I will not make an implementation with all the counters. GitHub is not designed to host a large number of image files. Also, I will only make the German Artillery Fire Table. The French Artillery Fire Table is just a copy.
It is important not to make “hacks” in the implementation. A “hack” in this context means that the implementation of this specific feature (the Artillery Fire Table) depends on code in C++ to work. A module developer can never change the C++ part. A “hack” would mean that the design is flawed. I have managed to avoid “hacks” to a large part, and what “hacks” are left can easily be removed. I will return to this later.
In order to make clear what implements the table, I have made a new script file called dev.luau. This file, together with module.luau, implements the table. Note: for the time being new script files are “hard-coded”. Later, a module developer will be able to create as many new luau script files as needed.
dev.luau:
-- create fire table window and add button to toolbar with callback
create_window("German artillery table", "artillery-window", "GermanArtilleryB")
local callback = "toggle_window()"
add_toolbar_button("Icons/GermanArtillery", "German artillery table", callback)
create_window creates the class Window. The parameters are window title, window tag and resource name for the window background. The tag is used to access the instance of this Window class. The tag “artillery-window” can access the created C++ class Window.
The C++ class Window is defined in window.cpp.
Note: I renamed the old window.cpp to repository.cpp. The new window.cpp implements the new Window class.
Note: Right now class Window can only exist in one instance. This will fixed later.
Note: create_window is a C API call. I have (mostly) used the convention that these calls have an underline. Right now there are 41 such calls in luau.cpp. I can imagine they will go up in their hundreds. As mention before, a C API call is the way that Luau can access the C++ engine and Qt. It is the only way. Luau can not access the “outside” in any other way.
The created window needs to be shown. This is done by creating a toggle button on the toolbar (add_toolbar_button). The parameters are the resource name for the image to be used on the button, a string for the button tool tip and a string (callback) containing the hook (or handler) for the button, what is executed when you click the button. The callback is naturally a luau script, a so-called chunk. A chunk is just a piece of code (like dev.luau itself) that is run though. It may
contain functions but they have to be called.
The chunk is toggle_window(), a C API function that does the actual toggle.
Note: The toolbar is of now “hard-coded” in C++, mostly in main.cpp. This will of course have to be changed. It is by script that the module developer adds buttons and features to the toolbar. Also note that the function show() in main.cpp is “hard-coded” to show a specific instance of class Window. This is what I mean when I say that there are still “hacks”.These “hacks” can be avoided. For instance: if toggle_window uses the tag defined in create_window, toggle_window(“artillery-window”), then the class instance can be retrieved by Window::instances(tag).
Next up is setting the custom grid in the window. You define a Lua table called grid, define an adder, and then add each grid point.
-- define and set custom grid coordinates
local grid = {[1] = {}}
function grid:add(x, y)
table.insert(grid[1], {x = x, y = y})
end
-- storage locations
grid:add(104, 136)
grid:add(192, 136)
grid:add(280, 136)
grid:add(368, 136)
grid:add(456, 136)
grid:add(544, 136)
grid:add(632, 136)
grid:add(104, 234)
grid:add(192, 234)
grid:add(280, 234)
grid:add(368, 234)
grid:add(456, 234)
grid:add(544, 234)
grid:add(632, 234)
-- fire calculation locations
grid:add(128, 376)
grid:add(128, 471)
set_grid(grid, 1)
Note: These grid points are the center of where a counter is supposed to be placed. The points have meaning in relation to the background image, defined in create_window as GermanArtilleryB. There are storage location for artillery pieces and target locations or fire calculation locations. See this image (I use imgbox for uploaded images, there are ads):

Note: The grid points have to be manually entered. The module developer who has created the background image will have an understanding of where these points are. They can be measured and written down. There is no tool to do this job.
Next we define the coordinates of the locations where firepower is to be calculated. These are the fire locations.
--- define fire location, key = target
local FireLocations = { {x = 128, y = 376, gridx = 1, gridy = 1},
{x = 128, y = 471, gridx = 1, gridy = 1}}
function setTarget(k, x, y)
FireLocations[k].gridx = x
FireLocations[k].gridy = y
end
Note: In my VASSAL module there were 10 such locations and 10 target counters. Here I only define 2 locations for 2 targets.
-- create boxes for target coordinate and calculated firepower
create_label('coor1', 180, 355, 78, 38, 'border:1px solid black; background:white; font:bold 30px; qproperty-alignment: AlignCenter;')
create_label('valu1', 268, 355, 58, 38, 'border:1px solid black; background:white; font:bold 30px; qproperty-alignment: AlignCenter; color:red;')
set_label_text('coor1', '---')
set_label_text('valu1', '0')
create_label('coor2', 180, 450, 78, 38, 'border:1px solid black; background:white; font:bold 30px; qproperty-alignment: AlignCenter;')
create_label('valu2', 268, 450, 58, 38, 'border:1px solid black; background:white; font:bold 30px; qproperty-alignment: AlignCenter; color:red;')
set_label_text('coor2', '---')
set_label_text('valu2', '0');
The boxes holding the coordinates for the target counters and the boxes holding the calculated firepower for each target is defined. These boxes are QLabels. The first parameter is the tag (the indetifier) of the box. The four next parameters is x, y, width and height of the box. The last parameter a stylesheet for the QLabel. The stylesheet is a very convenient way of specifying the whole look of the box in a single parameter.
Note: Qt Css is unfortunately not the same as Html Css. Among other things text-align:center does not work. Instead you use qproperty-alignment:AlignCenter 
Setters for the text is set_label_text. There is also a getter called get_label_text.
-- create check box for setting counter battery fire
callback = "recalculate()"
create_checkbox('cb', 100, 20, 200, 28, 'Counter Battery Fire', callback,
'QCheckBox:checked {color: black;} QCheckBox {font:bold 16px Arial,sans-serif; color:white;}');
In Verdun: A Generation Lost artillery pieces also have a counter-battery role with a fire factor generally less than the Bombardment factor. You can toggle the calculation of Bombardment/Counter-Battery with this checkbox.
Both create_label and create_checkbox are general in the sense that they can create QLabels and QCheckboxes anywhere, also on the main map. They just need an extra parameter (tag) to decide in which window they are placed. I will implement many more such widgets that can be used by the module developer. Among other things create_label can be used (with a transparent background) to draw circles and squares on the map. A background can be build dynamically with a set of create_labels.
Now comes the definition of the “counters” in the Artillery Fire Table which I call tokens because they must not be confused with the counters on the map.
-- create Luau tokens
local Token = {}
function Token:new (fromid, toid)
local o = Counter:duplicate(Repository[fromid])
o = Counter:merge(o, Artillery[fromid])
o.FullStrength = (o.Image.imageIndex == 1)
o.counter = toid
o.gridx = 0
o.gridy = 0
o.Overlays = Overlays:new ()
o.Overlays:add(Label:new('', "arial", 18, "black", 0, 0, function(o) return o.text ~= '' end))
return o
end
function Token:flip (id)
Token[id].Image.imageIndex = Token[id].Image.imageIndex + 1
local length = #(Token[id].Image.images)
if Token[id].Image.imageIndex > length then
Token[id].Image.imageIndex = 1
end
return Token[id].Image.images[Token[id].Image.imageIndex]
end
function Token:pos (id, x ,y)
Token[id].gridx = x
Token[id].gridy = y
end
This definition follows the usual defintion of a trait, the Token trait. It has a ‘new’ function and utility functions for flip and pos. Tokens have no right-click menu. They are automatically created when you drop an artillery counter on the map. They are automatically deleted when you delete an artillery counter on the map.
The definition of a token is up to the module developer depending on what is needed. Tokens have a lot in common with counters. They need the same basic image(s) from the Repository, so we duplicate Repository[fromid]. But then they need special attributes to hold values for firepower (see the Artillery trait below). We need to save its id on the map (toid). The grid coordinates on the map must be known.
Note: In Verdun: A Generation Lost there is a square grid. The coordinates are a letter (A-V) + a number (1-34).
The Artillery trait is defined. It needs a field for the various firepower values and must hold the range of the artillery piece. The x, y values are the pixel coordinates (not the grid coordinates) of token in the Artillery Fire Table window. Each piece has a start location.
Artillery = {}
function Artillery:new (x, y, bfull, cfull, breduced, creduced, range)
local o = {}
-- placement in artillery window
o.x = x
o.y = y
o.Bombardment = bfull
o.CounterBattery = cfull
o.Bomb2nd = breduced
o.Counter2nd = creduced
o.Range = range
return o
end
Artillery["German/Artillery/5-77-2"] = Artillery:new(104, 136, 2, 2, 2, 2, 6)
Artillery["German/Artillery/5-150H"] = Artillery:new(192, 136, 3, 4, 2, 2, 8)
Artillery["German/Artillery/5-420H-6"] = Artillery:new(280, 136, 6, 2, 3, 1, 14)
Note: If more than one instance of an artillery piece is on the map (like “German/Artillery/5-77-2”) then they are put on top of eachother.
local function char(num)
return string.char(string.byte("A") + num - 1)
end
function printCoordinates(x, y)
x = math.floor((x + 20) / 113)
y = math.floor((y + 20) / 113)
return tostring(char(y + 1)) .. tostring(x + 1)
end
function xyCoordinates(x, y)
x = math.floor((x + 20) / 113)
y = math.floor((y + 20) / 113)
return (x + 1), (y + 1)
end
These functions calculate the coordinates. The coordinates come in two variations: what is show above the token (like A3) and what is need when firepower is calculated (the numerical x,y values for determining if tokens are within range: A3 = 1, 3).
Here is the function that does the calculation itself.
function recalculate()
local counterBattery = get_checkbox_checked('artillery-window', 'cb')
for k, v in ipairs(FireLocations) do
local text = get_label_text('coor' .. k)
-- if target counter on map
if text ~= '---' then
-- find tokens in target slot and add firepower
local power = 0
for key, token in pairs(Token) do
if type(key) == "string" and key ~= '__index' and type(token) ~= 'function' then
if v.x == token.x and v.y == token.y then
if math.abs(v.gridx - token.gridx) <= token.Range and
math.abs(v.gridy - token.gridy) <= token.Range then
if token.FullStrength then
if counterBattery then
power = power + token.CounterBattery
else
power = power + token.Bombardment
end
else
if counterBattery then
power = power + token.Counter2nd
else
power = power + token.Bomb2nd
end
end
end
end
end
end
set_label_text('valu' .. k, tostring(power))
end
end
end
get_checkbox_checked finds out if the Counter-Battery box is checked or not. The parameters are the tag for the window and a tag for the box. In this way we avoid “hacks”, hard-coded values in the C++.
A typical Artillery Fire Table window and the corresponding main map looks like this.


Note that target 1 lies beyond the range of the artillery piece in A2, so the firepower is 0. When more than one artillery piece points at the same target the tokens will lie on top of eachother. This will make it difficult to see individual coordinates. I never dealt with this problem in the VASSAL module. Here you can always add a hoover window to inspect the stack, but I drop this now.
Note: class Window still needs work to make in general. I will add more functionality to this class in the prototype for Paths of Glory. Among other things, this class can hold a hand of cards. The class can likely be a base for a mat.
Class Window has its own drag/drop, its own setImage for rendering of tokens. It has the first grid functionality (Window::Frame::snaptoGrid) and functions that implement Label and Checkbox. Actually these functions could be moved out of class window and into a general class GUI or something.
I made an additional trait. In traits.luau
OnlyOneInstance = {
}
Counters that can only exist as one instance has this trait. The trait is used in all target counters (repository.luau):
Repository:addTrait("German/Targets/GermanTarget1", "OnlyOneInstance", OnlyOneInstance)
Repository:addTrait("German/Targets/GermanTarget2", "OnlyOneInstance", OnlyOneInstance)
The implementation of the trait can be made purely in script (module.luau):
if counter[id]['OnlyOneInstance'] ~= nil then
-- check if counter already exists
for k, v in pairs(Counter) do
if type(k) == "string" and k ~= '__index' and type(v) ~= 'function' then
if v.Image.images[1] == id and Counter[k].OnlyOneInstance ~= nil then
return false
end
end
end
end
Now is time to talk about the new hooks. Recall that hooks are called in the C++ at important moments. There are now 7 hooks (module.luau).
-
beforeDrag - called just before a counter is picked up in drag/drop. If false the counter can’t be moved. This is where the code for OnlyOneInstance is.
-
afterDrag - called just before a counter is dropped. If false the counter can’t be dropped. Here is where code for DoesNotStack is.
-
dropped - called after the counter has dropped. It is then time to create a token in the Artillery Fire Table.
-
moved - called after a counter has moved on the map. Dropped will also call moved. This is when recalculation of the Artillery Fire Table happens.
-
deleted - called just before a counter is deleted to do additional clean-up. This is time to delete tokens and reset coordinate and firepower boxes.
-
flipped - called after a counter has flipped. Time to recalculate.
-
undohook - called after any undo. All boxes in the Artillery Fire Table are reset.
Note: There is no undo functionality in class Window. Undoing movements in this window is meaningless. But since undo changes the main map, undo will also have an effect on the Artillery Fire Table. The way undo works is to move all counters their initial positions and then go forward. Since tokens in the Artillery Fire Table do not “go forward” they remain in their start positions. This means that the whole allocation of targets and tokens must be redone. This is better then trying to “undo” the Artillery Fire Table when no undo exists.
A few comments about how it was to develop dev.laua and class Window.
I was surprised at how much code already existed or could be reused with little effort. I am happy to say that this new functionality followed the general design.
It is never easy to develop in C++. Maybe it’s time to go over to a C++ IDE with a debugger … lol
The Luau code has become a bit “sprawling”, spread over many files. It has to be like this, but note one important thing: all variables and functions are global unless they have the keyword ‘local’. Try to use ‘local’ as much as possible. The pollution of namespace is a problem in scripted languages.
Maybe there should be a better separation between script that is part of the engine and script that is part of the module. I experienced how easy it is to introduce bugs in the undo/redo system. Another point: in the hook beforeDrag lies the implementation of the trait OnlyOneInstance which actually has nothing to do with the the Artillery Fire Table. How do you separate general code from temporal code?
It is the power of script that it is flexible, but changing the script wrongly can introduce bugs or crash the engine. These issues need more attention. Obviously good documentation is important.
The module developer must not have to do a lot of Luau programming to get his module to work. That would be a design flaw. dev.laua is an example of a complex module feature. Most modules don’t need such features. Later I will try to implement a complete game of a “simple” module to see how much coding is actually needed.
New code is on GitHub.
CPLUS_INCLUDE_PATH=/home/me/Qt/6.8.1/gcc_64/include;export CPLUS_INCLUDE_PATH
LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/home/me/Qt/6.8.1/gcc_64/lib;export LD_LIBRARY_PATH
g++ -Wall -o main main.cpp luau.cpp counter.cpp window.cpp repository.cpp frame.cpp overlay.cpp io.cpp scale.cpp toolbar.cpp settings.cpp -I/home/me/luau/VM/include -I/home/me/luau/Compiler/include -I/home/me/Qt/6.8.1/gcc_64/include/QtWidgets -Wl,--copy-dt-needed-entries -L/home/me/Qt/6.8.1/gcc_64/lib -lQt6Core -lQt6Widgets -L/home/me/luau -lLuau.VM -lLuau.Compiler -lLuau.Ast -lisocline
Now comes a prototype of Paths of Glory. I am sure a totally new game on the engine will give me some “surprises”. The main purpose of the prototype will be to introduce cards. A hand of cards will use a more developed version of class Window. Regarding cards, they have a lot in common with counters. To not duplicate code, what is likely needed is a base class called Piece with derived classes Counter and Card. I must look into this.
After cards come grid. A custom grid has already been made in class Window. Hex grids and square grids must by made. I already did this in GTK.
But before anything else one very important thing has to be done. I have to check if the engine runs on Windows. The C++ code must be compiled with a Windows compiler. I must install the Windows version of the Luau VM
(if I can find it, lol, found it: Luau download | SourceForge.net)
and I must download Qt for Windows. I will test it on Windows 7 and 11.
If the code runs on Windows I presume it will run on Mac and Android too. I have never worked with iOS/macOS. I have no idea how to test software on these platforms without actually owning the hardware …