Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post new scenarios and mods here to share with other gamers.

Moderator: Campaign Series Matrix Edition Development Group

Post Reply
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)


Follow the main action at:

https://www.matrixgames.com/forums/view ... 7#p4996747


Additional commentary (and discussion) of a more general or technical nature will be posted here.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

CSEE Overview


Quoting the CSVN game Manual:
The Campaign Series Event Engine, hereinafter referred to as the CSEE, is a standard, built-in extension to the game engine giving you the ability to script special messaging and all manner of in-game events...

One way to implement an "event engine" is by means of an "event editor" -- typically a GUI (Graphical User Interface), a set of dialogs with radio buttons, check boxes, input fields, and so on. This approach is rigid and constricting, however. What if you want to do something out of the ordinary that the event editor designers never anticipated? No can do; you are stuck with the current event editor limitations unless and until the game designers add the new feature in the next update, if and whenever that might happen.

No, a better way we think is to program events by means of a scripting language. This approach is far more flexible.

We could design our own customized scripting language. Much easier, however, to adapt a general purpose scripting language to our game's special environment and needs.

There is a variety of embedded scripting languages available. One of the better known is Lua. From the official Lua site (http://www.lua.org):

"Lua is a powerful, efficient, lightweight, embeddable scripting language. It supports procedural programming, object-oriented programming, functional programming, data-driven programming, and data description...

"Lua" (pronounced LOO-ah) means "Moon" in Portuguese. As such, it is neither an acronym nor an abbreviation, but a noun. More specifically, "Lua" is a name, the name of the Earth's moon and the name of the language. Like most names, it should be written in lower case with an initial capital, that is, "Lua". Please do not write it as "LUA", which is both ugly and confusing..."

Certainly, in the gaming world, Lua is a popular choice for an event scripting language, used in quite a number of high-profile and popular games (e.g., Command: Modern Air Naval Operations; Civilization V & VI; Hearts of Iron III; Napoleon: Total War; World of Warcraft; and many others).

Although quite capable, and very powerful "under the hood," you can do many things with simple Lua scripts. A Lua "script" is a computer program like any other, but is generally short and lean, typically just a few dozen or hundreds of lines long, and (in CS usage) mainly just a series of if-then-else statements (although other, fancier control structures are available).

Lua syntax is not "fussy". There are few punctuation characters to trip over and spacing is free-form. Although you can mangle Lua code like you can with any other computing language, spotting and fixing errors is usually not difficult. In the CSEE Lua implementation, besides the language-intrinsic error messages (which come in the form of pop-up dialogs), we additionally have several other features to assist in you in debugging your mistakes.

The simplicity of Lua -- at least in our implementation -- this is important. If we don't keep it simple, if scenario designers and modders find it too difficult to work with, it won't get used. We don't want the game fora to be flooded with Lua questions and cries for help. If that happens, we have done something wrong. No, better to KISS it.

Even though we combine the CS Lua Event engine with the game engine into one integrated whole, vnengine.exe (in Vietnam, for example), on game launch, it is useful to think of the two engines as two separate programs running side by side. The game engine has its own environment, with its own data, procedures, and processing thread; while the Lua EE is in like manner its own separate environment. The ability of the two engines to communicate and inter-operate with each other is limited. There is data sharing; one engine can call procedures in the other; but the interactions are confined, and typically not instantaneous. In some cases, for some events and data, the game engine will only contact the Lua EE once at the beginning of each phase, or at the beginning of a turn. Usually, the game engine remains in control. It is the more active agent, while the Lua EE tends to be more passive.

When you launch the game engine (for example, vnengine.exe), the CSEE starts up with it. At startup, the CSEE loads and processes the file init.lua, located in the top-level game folder right alongside the other game EXEs.

init.lua is, as you might guess, a collection of initializations -- setting up data structures, assigning data values, also defining some of the more basic, low-level functions, either referenced directly or called by other, higher-level functions.

Next, the CSEE loads and processes the file user.lua, if it exists. user.lua is also in the top-level game folder. user.lua serves a similar purpose as init.lua, but with these differences:

  • Unlike init.lua, which is absolutely necessary, user.lua is optional.
  • init.lua has mandatory content (in order for the CSEE to work as designed), while user.lua can be rudimentary, devoid of all content even.
  • init.lua is off-limits. DO NOT MAKE ANY CHANGES TO INIT.LUA! If you break init.lua, then the entire CSEE collapses; it won't work. So leave it alone!
  • user.lua is intended for your own personal customizations to the CSEE. Unlike init.lua (do with user.lua as you wish.

After init.lua and (optionally) user.lua processing, and after you have made your scenario selection, the CSEE looks to see if a scenario-specific .lua file exists, and if so, loads and processes it also. Like scenario-specific .ai files, scenario-specific .lua files are entirely optional. The game will load and play fine without them -- but without benefit of the CSEE. The <scenario>.lua files are located in the scenarios folder. For example, in the Scenarios folder, you might see:

VN_550429_Saigon.ai
VN_550429_Saigon.aix
VN_550429_Saigon.bmp
VN_550429_Saigon.lua
VN_550429_Saigon.map
VN_550429_Saigon.org
VN_550429_Saigon.scn

where the .lua file is entirely optional (and so too the .ai file).

In the scenario-specific .lua files, you will see a set of standard "trigger" function definitions, where every trigger function is on_*() something or other.

Here are the standard on_*() trigger functions:

on_air_attack ()
on_arty_attack ()
on_build_barrier ()
on_build_light_bridge ()
on_build_vehicle_bridge ()
on_clear_hex ()
on_clear_lz ()
on_damage ()
on_entrench_hex ()
on_hex_assault ()
on_hex_attack ()
on_ied_attack ()
on_improve_hex ()
on_lay_mine_field ()
on_mine_attack ()
on_next_phase ()
on_next_turn ()
on_objective_capture ()
on_resume ()
on_set_ied ()
on_shutdown ()
on_startup ()
on_unit_arty_fire ()
on_unit_attack ()
on_unit_clockwise ()
on_unit_counterclockwise ()
on_unit_fire ()
on_unit_kill ()
on_unit_merge ()
on_unit_move ()
on_unit_reduce ()
on_unit_reinforce ()
on_unit_release ()
on_unit_remove ()
on_unit_to_top ()
on_unit_to_bottom ()

In the current VN_550429_Saigon.lua file, for example, here is the default function definition for the trigger function on_next_turn():



function on_next_turn (turn) -- DO NOT REMOVE

end



By default, all of the trigger functions are like that -- empty templates. They don't seem to do anything, right? Right! It is the job of the scenario designer/modder to fill in the blanks.

Look again at the list of trigger functions above. When any one of those things happen in the game engine, it triggers a call to the CSEE. The CSEE then processes the event, and may or may not pass information back to the game engine.

For example, if an objective hex is newly captured in game, the game engine calls the on_objective_capture() function in the CSEE, passing the function arguments:

on_objective_capture (hc, value, values, side)

Depending on the on_objective_capture() definition, that function might then do any of the following:

  • display an in-game message dialog
  • award extra Event Points (EPs) to the occupying side (or subtract points from the other side's EP total)
  • raise or lower one side's or the other's general morale level
  • trigger fixed units to release early somewhere else on the map

and so on -- whatever the on_objective_capture() function definition says.

Likewise, if any of the other triggering events occur in game, the game engine will call the appropriate on_*() trigger function in the CSEE.

on_next_phase()
on_next_turn()
on_startup()

will always be called, but whether or not the other trigger functions are called, that is more or less likely, but it all depends.

As you might guess, on_startup() is called, once and only once, on scenario startup. Whenever you save and later reload the scenario, the on_resume() function is called instead.

on_next_phase() is called every time the phase passes from one side to the next. And on_next_turn() is called at the start of every new turn, obviously.

Like on_startup(), on_shutdown() is called just once, at scenario's end, just before the final Victory Dialog displays. on_shutdown() is rather like the on_next_turn() function (but at scenario's end, there is no next turn!).

The trigger functions – also nearly 600 other functions in the CSEE – are documented in the LUA_FUNCTIONS_REFERENCE.txt. Look for it in the game Manual folder.

NOTE: Since the above was written, there has been a change of user.lua emphasis. More than just a sand box for player/modder experimentation, user.lua is where we now define a number of "uber functions" -- core CSEE functions (in init.lua) packaged into higher level functions to do more sophisticated, complicated things.

The current list of uber functions:

adjust_adaptive_ai ()
air_mission_fire ()
air_mission_gunship_sortie ()
air_mission_spot ()
air_transport_carriers ()
air_transport_evacuate ()
air_transport_passengers ()
air_transport_reinforce ()
attack_nearest ()
attack_nearest_to_hex ()
attack_nearest_way_point ()
attack_org ()
attack_scatter ()
attack_supply ()
column_move ()
company_kill_check ()
company_reduce_check ()
csee_check ()
defend_scatter ()
fire_direct_area ()
fire_direct_nearest ()
fire_direct_nearest_to_hex ()
fire_indirect_area ()
fire_indirect_nearest ()
fire_indirect_nearest_to_hex ()
ground_transport_carriers ()
ground_transport_passengers ()
losses_dump ()
luaval_dump ()
move_air_transport ()
move_fire ()
move_fire_way_point ()
move_ground_transport ()
move_wary ()
move_wary_way_point ()
move_water_transport ()
unload_scatter ()
victory_dump ()
water_transport_carriers ()
water_transport_passengers ()

As we script more and more scenarios and gain ever more experience, the list of uber functions continues to grow.

Over time, some of the uber functions have proven to be so essential and so robust that we have elevated them to "core" status, moving them from user.lua to init.lua.

Be very careful if/when you edit any of the standard uber functions in user.lua. If you break user.lua, it is not quite so dire as breaking init.lua. (Don't do that! Don't edit init.lua!) You may do you own thing with user.lua. Just proceed with caution.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Lua Comments


In Lua, anywhere you see '--', from that point until the end of the line, that is a "comment". A comment might be anything, typically explanation of the code, or suggestions, or code commented out for debugging purposes, or ... really, could be anything.

In VN_550921_Rung_Sat.lua, the lines separating function definitions:

------------------------------------------------------------------------------------------------------------------------

Those too are comments! Because they begin with the comment marker, '--', right? And as such, they are ignored by the Lua interpreter. They are there to organize the code logicially and visually.

In Lua, a multi-line comment begins with

--[[

and ends with

]]

although we prefer

--]]

An example of a multi-line comment, in VN_550921_Rung_Sat.lua's on_next_phase() function:


Code: Select all

function on_next_phase (turn, side) -- DO NOT REMOVE

    ...

--[[ commonly used, uncomment and adapt as necessary:
    -- every phase, adjust downward the NVA assault_attack_aggressiveness_effect bonus per their current loss rate
    adjust_adaptive_ai (sideof(NORTH_VIETNAM_NATION), NORTH_VIETNAM_NATION, "assault_attack_aggressiveness_effect", - math.floor(total_loss_rate(sideof(NORTH_VIETNAM_NATION))/2))
--]]

    ...

end


In the preceding example, everything between the '--[[' ... '--]]' is "commented out", is effectively inert. If we were to remove the comment wrappers, the code would look like


Code: Select all

function on_next_phase (turn, side) -- DO NOT REMOVE

    ...

    -- every phase, adjust downward the NVA assault_attack_aggressiveness_effect bonus per their current loss rate
    adjust_adaptive_ai (sideof(NORTH_VIETNAM_NATION), NORTH_VIETNAM_NATION, "assault_attack_aggressiveness_effect", - math.floor(total_loss_rate(sideof(NORTH_VIETNAM_NATION))/2))

    ...

end


Done that way, only the '-- every phase...' is a comment. The 'adjust_adaptive_ai(...)' is now active Lua code, to be processed just like any other.

Note: There is no requirement that '--' or '--[[' or '--]]' be placed at the beginning of the line. They can be placed anywhere.

In scripting the CSEE, it is vitally important you understand and master Lua comments. There are so many ways to make use of them!

For more about Lua comments, see: https://www.lua.org/pil/1.3.html (second half). Or just do a Web search: lua comment
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Anatomy of the CSEE Lua File


The purpose of this post is to described in broad outline the layout of a CSEE scenario Lua file.

Using the CSlint csmklua.pl tool, let's begin by (re)creating the VN_550921_Rung_Sat Lua file:



rober@Rob10rto ~/cslint
$ ./csmklua.pl -a -p -g vn -f VN_550921_Rung_Sat.scn -o VN_550921_Rung_Sat_TEST.lua



So as not to clobber the existing file, note where I have designated this a _TEST version.

Following -- with data detail, all but the most important code, and many comments removed -- is the scenario Lua file "skeleton". Any '...' indicates data, code or comments included in the default Lua file but omitted here to show the highlights only (also for brevity's sake).


Code: Select all

------------------------------------------------------------------------------------------------------------------------

-- VN_550921_Rung_Sat_TEST.lua

-- Author:
-- Scripter:

------------------------------------------------------------------------------------------------------------------------

function on_startup () -- DO NOT REMOVE

    ...

    init_constants ()
    init_variables ()

    set_org_lists()

end

------------------------------------------------------------------------------------------------------------------------

function on_resume () -- DO NOT REMOVE

    init_constants ()

    set_org_lists(current_turn(), current_side())

end

------------------------------------------------------------------------------------------------------------------------

function init_constants ()

    -- initialize names and labels unvarying through the course of the scenario
    -- also define here lists with "holes" (index gaps) (such lists are not saved)
    -- called in on_startup(), and potentially again (and again) in any subsequent on_resume()

    -- Side "a" and "b" values with descriptive names
    SIDE_A = "a" -- _NATION ## (in user.lua)
    SIDE_B = "b" -- _NATION ## (in user.lua

    ...

end

------------------------------------------------------------------------------------------------------------------------

function set_org_lists (turn, side)

    -- called in on_startup(), in every on_next_turn(), and potentially again (and again) in any subsequent on_resume()

    ...

end

------------------------------------------------------------------------------------------------------------------------

function init_variables ()

    -- initialize values possibly varying through the course of the scenario
    -- called once only, in on_startup()

    ...

end

------------------------------------------------------------------------------------------------------------------------

function show_briefing (side) -- DO NOT REMOVE

    if side == SIDE_A then

        ...

    elseif side == SIDE_B then

        ...

    end

end

------------------------------------------------------------------------------------------------------------------------

function on_shutdown () -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_next_turn (turn) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_next_phase (turn, side) -- DO NOT REMOVE

    ...

    set_org_lists(turn, side)

    if turn == 1 then
        show_briefing(side)
    end

    battle_plan(turn, side)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function battle_plan (turn, side)

    ...

    if side == SIDE_A and is_ai(side) then

        battle_plan_a(turn, side)

    elseif side == SIDE_B and is_ai(side) then

        battle_plan_b(turn, side)

    end

end

------------------------------------------------------------------------------------------------------------------------

function battle_plan_a (turn, side)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function battle_plan_b (turn, side)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_objective_capture (hc, value, values, side) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_move (hc_from, hc_to, trackid) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_fire (hc, trackid, pid, name, side, nation, oid, orgname, strength) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_arty_fire (hc, trackid, pid, name, side, nation, oid, orgname, strength) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_attack (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader, attype) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_reduce (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader, loss, combat) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_kill (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_unit_reinforce (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_release (trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_remove (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_merge (hc, trackid0, pid0, name0, side0, nation0, oid0, orgname0, points0, strength0, HQ0, Leader0, trackid1, pid1, name1, side1, nation1, oid1, orgname1, points1, strength1, HQ1, Leader1) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_to_top (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_to_bottom (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_clockwise (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_unit_counterclockwise (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_hex_attack (hc, side, nation, attype) -- DO NOT REMOVE

    ...

end

------------------------------------------------------------------------------------------------------------------------

function on_hex_assault (hc, side) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_air_attack (hc, pid, name, side, nation, points, strength) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_arty_attack (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_mine_attack (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_ied_attack (hc, trackid, pid, name, side, nation, oid, orgname, points, strength, HQ, Leader) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_build_vehicle_bridge (hc, dir, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_build_light_bridge (hc, dir, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_lay_mine_field (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_set_ied (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_build_barrier (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_damage (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_improve_hex (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_entrench_hex (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_clear_lz (hc, trackid, pid, name, side, nation, oid, orgname) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------

function on_clear_hex (hc) -- DO NOT REMOVE



end

------------------------------------------------------------------------------------------------------------------------


To begin, the name of the scenario Lua file, the scenario Author, and the CSEE scripter of that scenario (who may or may not be same as the Author). Because those lines begin with the comment marker, '--', they are just window dressing, are not executable code.

on_startup() is a so-called "trigger function". In the CSEE, all trigger functions begin with the name 'on_'. Something has happened in the game engine: a new scenario has started up. This event triggers a call to the event engine. The CSEE springs to action, in this case doing the things described in the on_startup() function definition.

init_constants() is our first "function call" (invocation). At this point, the code will take a detour to the init_constants() function, return, then detour to the init_variables() function, then return again.

After saving a game, then resuming, this too is a special game event. Whenever a game resumes, the game engine calls the on_resume() function. on_resume() is much like on_startup(), except when resuming the game we don't call init_variables(). We don't want to reset to their initial defaults any variables that have, in the course of earlier game play, um ... varied. (Note: As of CSVN 1.20, only Lua variables are saved, not Lua constants.)

In init_constants(), we set some common variables, SIDE_A & SIDE_B (among possibly other things).

set_org_lists() and init_variables() are called in on_startup() and/or on_resume().

show_briefing() is typically called on Turn 1 only, with different briefings for each side. (In multi-day scenarios, and other special circumstances, it is possible to show different briefings from day to day.)

When the scenario ends, at the final turn, that too is a game event! When that happens, the on_shutdown() function is triggered.

on_next_turn() and on_next_phase() are routine game events triggered with every new game turn and phase.

In on_next_phase(), note where show_briefing() is called for the appropriate side, typically on Turn 1 (only).

With every new phase, on_next_phase() calls the current side's battle_plan function.

battle_plan_a() and battle_plan_b() -- these are were orders are assigned, usually. It is possible to assign unit orders elsewhere, but you need to be careful, because those orders might not be refreshed, when given in other trigger functions that only happen occasionally, even not at all. Since battle_plan_a() and battle_plan_b() are called regularly, they are the best place to give most or all unit orders.

Beginning with on_objective_capture, the remainder of the standard scenario Lua file functions include:

on_objective_capture()
on_unit_move()
on_unit_fire()
on_unit_arty_fire()
on_unit_attack()
on_unit_reduce()
on_unit_kill()
on_unit_reinforce()
on_unit_release()
on_unit_remove()
on_unit_merge()
on_unit_to_top()
on_unit_to_bottom()
on_unit_clockwise()
on_unit_counterclockwise()
on_hex_attack()
on_hex_assault()
on_air_attack()
on_arty_attack()
on_mine_attack()
on_ied_attack()
on_build_vehicle_bridge()
on_build_light_bridge()
on_lay_mine_field()
on_set_ied()
on_build_barrier()
on_damage()
on_improve_hex()
on_entrench_hex()
on_clear_lz()
on_clear_hex()

Unlike the previous trigger functions, this last set of trigger functions are occasional, hit or miss. In some scenarios (on_set_ied(), for instance), they might not be called at all.

Except possibly for comments and commented out suggested code (uncomment in order to activate), It is not atypical for these later trigger functions to be empty. In the current scenario, if any of these triggering events are unlikely to happen, why bother to fill them with code? (Of course, for some triggering events, maybe they are without CSEE consequence. Another reason why some trigger functions might remain empty.)

For most of the standard CSEE trigger functions, you will see the warning comment: '--DO NOT REMOVE'. Please heed that warning! The game engine expects to find these functions. Bad things might happen if the game engine calls a missing trigger function. So don't remove them! (You may reorder them, however, if you really feel a need to. The functions without any coding -- maybe reposition them toward the end.)

NOTE: Can you add your own functions to the scenario Lua file? Why, yes you can! In fact, it is encouraged. More on this later.

NOTE: Trigger functions (those beginning with 'on_') should not call other trigger functions! The game engine calls trigger functions, sequentially, one at a time. Nesting trigger function calls is very bad. Don't do that!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Anatomy of the CSEE user.lua File

Moving on to user.lua, we have (for CSVN):


Code: Select all

------------------------------------------------------------------------------------------------------------------------

--[[

    user.lua -- called each time a scenario is started, or resumed (after a save and scenario restart).

    Intended for global identifiers, "uber functions" (unofficial, subject to change), and other shared, common odds & ends.

--]]

------------------------------------------------------------------------------------------------------------------------

--[[

-- With DEBUG=true, user.lua, init.lua, and all [scenario filename].lua files in Scenarios folder will adapt to this setting;
-- use when debugging your Event files.

-- Hand edit the DEBUG toggle here, maybe, but better to set DEBUG at the game engine command line with the -G switch
-- (sets DEBUG to true; omitting the switch, DEBUG defaults to false); and/or use Ctrl+Alt+G in-game to goggle DEBUG=true
-- DEBUG=false.

DEBUG = false
#DEBUG = true

-- Note also that the command-line switch -E toggles on/off Lua script execution tracing.

-- In addition to the command-line switch -L# (e.g., -L3) to raise/lower the game engine LogLevel (higher is more verbose,
-- and slower).

-- With all logging cranked up to the max -- ... -G -E -L5 ... -- the game will run glacially s-l-o-w, so beware!

--]]

------------------------------------------------------------------------------------------------------------------------

-- Some standard identifiers.

TRUE = 1
FALSE = 0

ON = 1
OFF = 0

...

------------------------------------------------------------------------------------------------------------------------

-- Nation IDs with descriptive names

...

------------------------------------------------------------------------------------------------------------------------

-- Nation (Side) specific commendations

------------------------------------------------------------------------------------------------------------------------

-- csee_check() -- do diagnostic QA checks (usually called in on_next_phase()) (for diagnostic purposes; not intended for ordinary scripter/modder use)

------------------------------------------------------------------------------------------------------------------------

function csee_check (turn, side, memceil)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function luaval_dump (all)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function victory_dump (turn, side)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function losses_dump (turn, side)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function ground_transport_passengers (trackids, loadpt, unloadpt, assemblypt)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function ground_transport_carriers (trackids, loadpt, unloadpt, roundtrip)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_ground_transport (ptrackids, ctrackids, loadpt, unloadpt, assemblypt, roundtrip)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function column_move (trackids, hc, dir, radius, actprob)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function water_transport_passengers (trackids, embarkpt, loadpt, unloadpt, disembarkpt, assemblypt)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function water_transport_carriers (trackids, loadpt, unloadpt, roundtrip)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_water_transport (ptrackids, ctrackids, embarkpt, loadpt, unloadpt, disembarkpt, assemblypt, roundtrip)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_transport_passengers (trackids, loadpt, unloadpt, assemblypt, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_transport_carriers (trackids, loadpt, unloadpt, roundtrip, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_air_transport (ptrackids, ctrackids, loadpt, unloadpt, assemblypt, roundtrip, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_mission_fire (trackids, basept, targetpt, proximity, _range, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_mission_gunship_sortie (trackids, entrypt, targetpt, proximity, _range, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_mission_spot (trackids, basept, targetpt, proximity, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_transport_reinforce (trackids, entrypt, unloadpt, roundtrip, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function air_transport_evacuate (trackids, entrypt, loadpt, roundtrip, airlevel)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_org (trackids, otrackids, dir, radius, actprob, attype, ground_only, unverified)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_nearest (trackids, dir, radius, actprob, attype, ground_only, unverified)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_nearest_way_point (trackids, hcs, dir, radius, actprob, attype, ground_only, unverified)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_direct_area (trackids, hc, dir, radius, actprob, verified_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_direct_nearest (trackids, actprob, ground_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_direct_nearest_to_hex (trackids, hc, actprob, verified_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_nearest_to_hex (trackids, hc, dir, radius, actprob, attype, ground_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_indirect_area (trackids, hc, dir, radius, actprob, verified_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_indirect_nearest (trackids, actprob, ground_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function fire_indirect_nearest_to_hex (trackids, hc, actprob, verified_only)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_fire_way_point (trackids, hcs, dir, radius, actprob)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_fire (trackids, hc, dir, radius, actprob)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_wary_way_point (trackids, hcs, dir, radius, actprob, foe_count)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function move_wary (trackids, hc, dir, radius, actprob, foe_count)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_supply (trackids, extent)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function company_kill_check (trackid, coys, sides, eps, nations, mshifts, reveal, phase)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function company_reduce_check (trackid, coys, sides, eps, nations, mshifts, rate, reveal, phase)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function adjust_adaptive_ai (side, nation, parameter, adjustment)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function unload_scatter (trackids, hc, dir, extent, actprob, off_center)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function attack_scatter (trackids, hc, dir, extent, actprob, off_center, attype)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function defend_scatter (trackids, hc, dir, extent, actprob, off_center, dftype)

    ...

end

------------------------------------------------------------------------------------------------------------------------

function allot_airstrikes(turn, side, allotment, limit, replenishment, halt_text, resume_text, night)

    ...

end

------------------------------------------------------------------------------------------------------------------------


user.lua opens with some good-to-know commentary when debugging the CSEE and Lua code.

Then some useful, but unofficial (because not defined in init.lua), "constant" definitions, including:

DEBUG = false
TRUE = 1
FALSE = 0
ON = 1
OFF = 0

There follow some handy descriptive names as stand-ins for the game's nation ID #s (e.g., NORTH_VIETNAM_NATION = 21).

The commendations are a set of one-liner dialog text that you are free to make use of, or not (or even add your own).

There follow the list of so-called "uber functions": core CSEE functions (in init.lua) packaged into higher level functions to do more sophisticated, complicated things.

None of the uber functions are official. All are subject to change.

Over time, some of the uber functions have proven to be so essential and so robust that we have elevated them to "core" status, moving them from user.lua to init.lua.

Be very careful if/when you edit any of the standard uber functions in user.lua. If you break user.lua, it is not quite so dire as breaking init.lua. (Don't do that! Don't edit init.lua!) You may do you own thing with user.lua. Just proceed with caution.

As with the scenario Lua files, you are free to, and encouraged to, revise the uber functions, even create your own. But be careful not to break the file! (By adding bad, syntax mangling code that confuses the Lua interpreter, even stops further processing from the point of error.)

What about init.lua? We discourage you from making any changes to init.lua. It is the CSEE core. Break it, and the whole CSEE ceases to function. Leave well enough alone!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

LUA_FUNCTIONS_REFERENCE.txt


When scripting the Lua CSEE, something you will want to keep handy, and refer to often: Manual/LUA_FUNCTIONS_REFERENCE.txt.

LUA_FUNCTIONS_REFERENCE.txt is where the CSEE "core" init.lua functions are documented.

(NOTE: The user.lua "uber" functions are at present not formally documented. If they are documented at all, it is internally, in the function definitions, as explanatory comment.)

LUA_FUNCTIONS_REFERENCE.txt is way too long to post here. At last count, 23268 lines! This will just be a brief summary introduction.

The file opens with alphabetical lists of the functions, by category:

TRIGGERS
AI
COUNTERS
GENERAL
HEXES
MAP
MESSAGING
DEBUGGING
LISTS
MISC
OBJECTIVES
REINFORCEMENTS
RELEASES
SCENARIO
SIDES
UNITS
SCRIPTED AI
USER.LUA "uber functions"

Over 600 CSEE functions are listed, again way too long even just to list them here.

There follows, in barely adequate detail, descriptions of every one of the "core" CSEE functions defined in init.lua. (Or in some cases, not defined there; these are meant for internal CSEE/game engine direct use only.)

Here is a fairly typical function summary:



FUNCTION

attack_normal (trackids, hc, dir, radius, actprob)

DESCRIPTION

For the given trackids (counters), attack the specified hex coordinates with normal intensity.

If a possible assault situation presents itself, assault with 50/50 chance, unless actprob specifies otherwise.

INPUTS

trackids -- a list of trackids (which may include just a single member)
hc -- character string, or some function or expression evaluating to a character string; e.g., "60,75"

and optionally also:

dir -- any of UPDIR, UPRIGHTDIR, DOWNRIGHTDIR, DOWNDIR, DOWNLEFTDIR, UPLEFTDIR; if not specified, defaults to NODIR
radius -- integer, 0 or greater; if not specified, defaults to _NONE
actprob -- integer; -1 (UNKNOWN), else 0 to 100; if omitted, defaults to 50

RETURN VALUE

none

EXAMPLES

attack_strong ({784,786}, "34,70")

SEE ALSO

attack_banzai ()
attack_fanatical ()
attack_weak ()
attack_strong ()



As of now, and perhaps forever, the EXAMPLES are rather sparse. As more "real world", production code examples are created in the course of normal CSEE scripting, sooner or later we might add more and richer EXAMPLES. (However, maybe not, because that code not being in an actual, production Lua file, how can we be sure that the code remains good and bug free, how can we QA test it?)

Also, sooner or later, we will author a formal CSEE/SAI How-To and reference. Until then, LUA_FUNCTIONS_REFERENCE.txt, together with in-code commentary and forum posts -- this will have to do.

LUA_FUNCTIONS_REFERENCE.txt: Make good and frequent use of it!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Auto-Testing


At the WSL (or Cygwin) terminal command line, we run an auto-test of the Rung Sat scenario via:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ ./vnengine.exe -W -6 -T VN_550921_Rung_Sat.scn



Here is an incomplete listing of the game engine command-line options (showing only those options pertinent for purposes of auto-testing):

  • 0 -- no3D, InitialZoom 5
  • 1 -- InitialZoom 0 (3D Zoom-In View)
  • 2 -- InitialZoom 1 (3D Normal View)
  • 3 -- InitialZoom 2 (3D Zoom-Out View)
  • 4 -- InitialZoom 3 (3D Extreme Zoom-Out View)
  • 5 -- InitialZoom 4 (2D Zoom-In View)
  • 6 -- InitialZoom 5 (2D Normal View)
  • 7 -- InitialZoom 6 (2D Zoom-Out View)
  • 8 -- InitialZoom 7 (Extreme Zoom-Out View)
  • 9 -- InitialZoom 8 (Strategic View)
  • z -- for -z[0-9], to specify the InitialZoom, where the 'z' is optional
  • Z -- InitialZoom selected at random (0 thru 8)
  • m -- NoMusic (no in-game music or sounds)
  • P -- PassWait set to true (defaults to false)
  • v -- NoVideo (no in-game videos)
  • W -- Windowed Mode
  • X -- NoEncryption
  • L# -- LogLevel #
  • G -- LuaDebug set to true (defaults to false)
  • E -- LuaTrace set to true (defaults to false)
  • U -- LuaUnleash set to true (defaults to false)
  • T -- TestTrialPlay set to true (defaults to false)

Thus, in the above command line, we launch a hands-off auto-test of the VN_550921_Rung_Sat scenario, with the game in windowed mode and the map graphics showing at 2D Zoom-Out View (hot key 6 zoom).

You might prefer to run the auto-test at some other map zoom. Anyway, you can stop the auto-test in mid test and change the zoom manually whenever and to whatever you like.

The '-P' option says to pause game play at the start of every new phase if no 'pass' file is detected. Without a pass file, no passing!

If there is an empty (zero length) pass file, the auto-test will run without stopping at the start of each new phase. You may pass!

You can create an empty pass file via



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "" > pass



Note where we create the pass file in the top-level game folder (where the game EXEs reside).

As stated earlier, if there is no pass file, the game will stop at the beginning of each new phase, whether Side A or Side B.

With the game stopped, you can look around, check the game status, maybe change some game display options, even quit the test -- whatever you like. You can even take over from the AI and move counters around manually! Or have units Dig In, adopt a Weak or Fanatical Defense, order airstrikes, and so on. Whatever you please. (Normally, you would not interfere with the automatic AI play, however.)

After a stop, you can restart the test either by pressing the Next Turn Toolbar button, else via menu option AI > Activate AI (toggling it ON).

You can stop the auto-test at Side A phases only with a pass file:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "a" > pass



Similarly, you can stop the auto-test at Side B phases only with a pass file:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "b" > pass



Another way to stop at both phases is with a pass file:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "ab" > pass



A pass file "ab" is effectively the same as having no pass file. Either will stop the game at the beginning of each new game phase. (Remember: only with an empty, zero length pass file is the auto-test free to pass without hindrance from one phase to the next.)

You can also stop the game at any turn by adding a turn # to the pass file. For example, this will stop the auto-test at the Turn 10 Side A phase:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "10a" > pass



Why would you want to specify the stop turn? When you are scripting or modifying a scenario Lua file, you will second guess yourself, or discover a logical mistake in your script, or encounter a bug -- where Turn 10 (or whatever) is the critical juncture. Or you might stop at Turn 10 (or whatever) for any other reason.

Normally, you will be running many auto-tests, over and over and over ... again. It soon becomes terribly boring watching the game intently to stop the test manually at exactly the turn, or phase, you require. Instead, use the pass file!

You can stop the test manually via toggling OFF the menu option AI > Activate AI. Note: You cannot do this when units are firing (typically at the beginning of the phase), only during the movement portion of the phase.

NOTE: You restart from a stop via toggling ON the menu option AI > Activate AI.

What if you really need to stop the game at the crucial Turn 10 Side A phase? Out of boredom (so many earlier replays!), you have been multi-tasking, focusing your attention doing something else. When you check back ... darn! The auto-test has progressed past Turn 10, Side A Phase. You missed your chance to manually stop the test! Darn! You need to start over again with a new test.

It was like that often in the early days of CSEE/SAI development. To preserve our sanity, we developed the pass file, turn-and-side auto-stop mechanism. Let the system stop the game for you, so you don't have to!

Incidentally, there is another way to stop the game at a certain point: by using the CSEE function aistop():



FUNCTION

aistop (text)

DESCRIPTION

Stops AI processing. To restart, manually toggle AI > Activate AI.

Optionally show a text message in the pop-up dialog announcing the stoppage.

INPUTS

text -- character string, or some function or expression evaluating to a character string

RETURN VALUE

none

EXAMPLES

if turn == 3 and
side = SIDE_A then
aistop("in on_next_phase(), Turn " .. turn .. ", Side " .. side)
end

SEE ALSO

exestop ()



Using aistop() in this way requires you to edit the scenario Lua script manually to stop and start the game, changing the turn and/or side. But usually it is so much easier to use the pass file mechanism for this.

Note that you can change the pass file any time you like. After stopping the game at Turn 10, Side A phase, you could have auto-test stop every Side B phase thereafter by changing the pass file via



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "b" > pass


Or you could order the next stop at Turn 15, Side B phase:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ echo "15b" > pass



When developing scenario Lua scripts, I would often set my pass file to stop every five turns -- "5a", "10a", "15a", "20a", ... No watching every move by move, every play by play, every turn by turn. But only checking in to observe the latest game situation occasionally (every 5 turns, or whatever).

Stop (and restart) the game whenever you wish, as often as you wish. Learn to master the pass file system.

To view the contents of the current pass file, if any, you can try:



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ cat pass
15b



To reimpose the automatic stop at new phase, you can remove the pass file at any time with



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ rm pass



At auto-test start, you can set the game's LogLevel via the '-L#' option. For example, '-L3' says to log at the heightened level 3 (LogLevel 1 is the default). As a scripter/modder, you will almost never want to run an auto-test higher than LogLevel 3. (As developer, I will sometimes run at LogLevel 5 or even higher for truly deep, deep debugging.)

The command-line option '-G', as in



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ ./vnengine.exe -W -6 -G -T VN_550921_Rung_Sat.scn



toggles ON the CSEE DEBUG switch. This will activate CSEE debugging code, for instance



if DEBUG then
log(APPLOG, LOG_DEBUG, "in fire_indirect_area(), turn " .. current_turn() .. ", firing unit " .. trackid .. ", targets " .. list_dump(targets))
end



If DEBUG were set to false (OFF, the default), that log entry would not happen.

The command-line option '-E', as in



rober@Rob10rto /cygdrive/c/Games/Matrix Games/Vietnam/vietnam
$ ./vnengine.exe -W -6 -G -E -T VN_550921_Rung_Sat.scn



toggles ON CSEE Lua tracing. This will activate logging, to the game engine.log, line by line trace statements, for example:



...
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:11343
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:11347
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:11483
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:11499
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) scenarios\VN_550921_Rung_Sat.lua:2208
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) scenarios\VN_550921_Rung_Sat.lua:2062
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:9749
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:9751
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:4320
2022-05-16 14:03:15 vnengine.exe: [DEBUG ID 10] (lua.cpp, line 856, l_log()) init.lua:2081
...



With every new Lua line, that line -- the file and line # -- is "traced" and recorded in engine.log. Not only is that slow, it bloats the engine.log something crazy.

How slow? In our latest auto-test, more than ten minutes elapsed from scenario startup to the map appearing, and units moving.

How crazy?



rober@Rob10rto /cygdrive/r/Temp/Logs
$ ls -lh "$VN"/Logs/engine.log
-rwx------+ 1 rober rober 240M May 16 14:03 '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/Logs/engine.log'

rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep -c "\[DEBUG ID 10\] \(lua.cpp, line 856.*\.lua:[[:digit:]]" "$VN"/Logs/engine.log
2710161



A 240 MB engine.log with over two million seven hundred thousand trace entries!

With so many and so frequent log file writes, is it any wonder the auto-test runs so terribly slow? (In order speed things up and spare our hard drive or SSD drive from such excessive disk writes, we usually redirect Logs/engine.log to a RAM drive. More about this in future.)

Why would you want to do such a thing, run an auto-test in -E tracE mode? Well, there are occasional bugs that are so murky and mysterious that no amount of reviewing the code and conventional debugging techniques will reveal the mystery. Sometimes, you need to follow the Lua code execution line by line, comparing trace statement line #s with line #s in the code. (Why is line number XXX not being reached? Why is code execution stopping short unexpectedly at line XXY? Hmm, maybe there is something wrong at line XXY?)

It is beyond the scope of this here forum post to discuss CSEE Lua debugging techniques. Some other time.

The point about Lua tracing is: When you need it, you need it. Sometimes, there is no other way to finding that elusive bug.

The next option, '-U', you would use if, when reloading a saved auto-test, you want all units to follow the CSEE unleash() order, henceforth ignore any previous CSEE/SAI commands. This option is seldom used, and is rather obscure.

Finally, there is the '-T'. This is how you tell the game engine to play itself!

NOTE: '-T' implies '-P'. In the above auto-test command lines and pass file discussion, note where the '-P' was not explicitly specified. '-T' by itself activates the effect of '-P'.

If you develop or seriously mod any of the scenario Lua files, anticipate spending hours and hours running -- and watching! -- auto-tests. So much time viewing auto-tests! This is the biggest reason why scenario scripting can be so time consuming.

TIP: Before launching CSEE auto-tests, make sure that Map Labels (Display > Labels) and Smooth Scroll (Options > Scrolling > Smooth Scroll) are both toggled OFF (are unchecked in the menu). Your auto-tests will run faster if both of these are OFF.

As you script more, you have more earlier good, tested code to copy-paste into your current project. As you script more, you gain more experience, you are less likely to scratch your head: How can I do this? As you get better at scripting, the less likely you are to write buggy code. A very important tip: Run csluachk.pl often! Especially before any expected long auto-test run.

When you auto-test, apart from any pass file or code or manual stops along the way, the auto-test will in every case end at the second phase of the final turn. This gives you the opportunity to look around, assess the final situation, before the game session terminates. Did everything run to your satisfaction, as you expected? At the end of an auto-test, you have one last chance to check things out.

More will be said about auto-testing in future. But the above basics should be enough to get you started.

Happy auto-testing!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

'do local units = ... end' blocks


When you run the CSlint csmklua.pl utility to create a scenario Lua file, the '-b[locks]' parameter creates, in the battle_plan functions, empty template code blocks like for example this:


Code: Select all

    -- _C2_1ST_5TH_RIFLE_COY_48_US_73 -- C2/1st/5th Rifle Company 48 - US
    do local units = _C2_1ST_5TH_RIFLE_COY_48_US_73

    end


In the Lua language, the code block


Code: Select all

    do

        ...

    end


doesn't really, um, do much of anything (like for instance a 'for ... end' or 'while ... end' block would). What's the point then?

In CSEE standard practice, we have


Code: Select all

    do local units ...

        ...

    end


to create a block of code as a container for the local variable 'units'.

IMPORTANT: As a 'local' variable, 'units' only has meaning in the 'do ... end' block where it is declared. After the 'end', 'units' ceases to have meaning. (Unless/until you create an entirely separate 'units' local variable elsewhere in the script.) If you were to do something like this


Code: Select all

    do local units = _C2_1ST_5TH_RIFLE_COY_48_US_73

        ...

    end

    halt(units)


this would break your script. The Lua interpreter would complain of a "nil value" when referencing that last 'units' mention outside the preceding 'do ... end' scope.

When CSEE scripting the AI (SAI'ing), you will make plenty of "nil value" mistakes. Repeating: Run csluachk.pl early and often!

For more about Lua local variables and blocks, see:

https://www.lua.org/pil/4.2.html

Or do a Web search: lua local variable

Here is the _C2_1ST_5TH_RIFLE_COY_48_US_73 'do local units = ... end' block filled in with actual orders code:


Code: Select all

    -- _C2_1ST_5TH_RIFLE_COY_48_US_73 -- C2/1st/5th Rifle Company 48 - US
    do local units = _C2_1ST_5TH_RIFLE_COY_48_US_73
        if counter_exists(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST) then
            local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)
            if not within(units, objective, DOWNLEFTDIR, 2) then
                move_norush(units, objective, NODIR, 0, 100)
            else
                defend_scatter(units, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
            end
        else
            defend_way_point(units, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)
        end
    end


Without the 'do local units = ... end' wrapper, without the outer code block, the code would like this:


Code: Select all

    -- _C2_1ST_5TH_RIFLE_COY_48_US_73 -- C2/1st/5th Rifle Company 48 - US
    if counter_exists(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST) then
        local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)
        if not within(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2) then
            move_norush(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, NODIR, 0, 100)
        else
            defend_scatter(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
        end
    else
        defend_way_point(_C2_1ST_5TH_RIFLE_COY_48_US_73, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)
    end


Compare the former with the latter. In the second version, note the repeated referencing of '_C2_1ST_5TH_RIFLE_COY_48_US_73'. In the CSEE/SAI, unit org names tend to be rather long and "busy". Hard to read, easy to make typos.

In the first version, by opening the 'do ... end' with setting a local variable (local to the block) 'units' equal to '_C2_1ST_5TH_RIFLE_COY_48_US_73', we make the code easier to read. And we are far less likely to misspell 'units'.

The first version is better, and is preferred, standard practice for CSEE/SAI. (At least when auto-generated via csmklua.pl.)

There is another good reason for following this standard practice. When you have coded and tested and perfected a common orders sequence for one particular company, often you will want to apply that to other companies. You will copy/paste the known good code to other companies in the same file, or perhaps from one scenario file to another.

So with the above code, the latter version, we apply it to another company:


Code: Select all

    -- _C3_1ST_1ST_RIFLE_COY_48_US_16 -- C3/1st/1st Rifle Company 48 - US
    if counter_exists(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST) then
        local objective = counter_hex(_C2_1ST_5TH_RIFLE_COY_48_US_73_POST)
        if not within(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2) then
            move_norush(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, NODIR, 0, 100)
        else
            defend_scatter(_C2_1ST_5TH_RIFLE_COY_48_US_73, objective, DOWNLEFTDIR, 2, 50, true, DEFEND_STRONG)
        end
    else
        defend_way_point(_C2_1ST_5TH_RIFLE_COY_48_US_73, {"71,64", "71,61", "73,61", OBJECTIVES[19]}, UPRIGHTDIR, 1, 100, DEFEND_STRONG)
    end


In that 'if ... end' block of code, we must then remember to search/replace



_C2_1ST_5TH_RIFLE_COY_48_US_73 -> _C3_1ST_1ST_RIFLE_COY_48_US_16



In the example, we would have to do six such substitutions, not just the unit org name but the company _POST also. In practice, it is all too common forgetting to make those search/replace. You think you now have code ordering _C3_1ST_1ST_RIFLE_COY_48_US_16. But in fact, you are repeating the orders for _C2_1ST_5TH_RIFLE_COY_48_US_73. Equally bad, you have left _C3_1ST_1ST_RIFLE_COY_48_US_16 without orders. All because of thoughtless copy/paste, and forgetting to do the needed search/replace.

For some companies, csmklua.pl will create blocks like


Code: Select all

    -- _C4_1ST_5TH_DAI_DOI_HOA_LUC_48_81MM_83 -- C4/1st/5th Dai Doi Hoa Luc 48 - 81mm
    do local units = _C4_1ST_5TH_DAI_DOI_HOA_LUC_48_81MM_83

    end
    -- _MORTAR_84 -- VM 81mm Mortars


What is the point of the separate


Code: Select all

    -- _MORTAR_84 -- VM 81mm Mortars


_C4_1ST_5TH_DAI_DOI_HOA_LUC_48_81MM_83 is a combined arms company including infantry and mortars. Usually, you will want the infantry to man the front lines, to defend or to attack, with the mortars holding back somewhat to the rear. Direct Fire Infantry unit orders are typically different from Indirect Fire unit orders.

You will want to define your 'local units =' carefully because of that:


Code: Select all

    -- _C4_1ST_5TH_DAI_DOI_HOA_LUC_48_81MM_83 -- C4/1st/5th Dai Doi Hoa Luc 48 - 81mm
    do local units = difference(_C4_1ST_5TH_DAI_DOI_HOA_LUC_48_81MM_83, _MORTAR_84)

        ...  [code applying only to the infantry; i.e., referencing 'units']

    end
    -- _MORTAR_84 -- VM 81mm Mortars
    ...  [code applying only to the mortars; no reference to units]


(See the difference() description in Manual//LUA_FUNCTIONS_REFERENCE.txt for more details.)

In this orders 'do ... end' code block, 'units' is not the entire company; rather, is a subset, excluding the mortars, which are handled separately from the infantry units. Code the infantry orders within the 'do local units = difference() ... end' block; code the support units' orders just after that block.

Make good and proper use of 'do local units = ... end' blocks. They will save you lots of grief!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

SAI vs. EAI (vs. AAI)


In the unit movements shown here, here, and here, consider this: Are they scripted?

Well, yes of course they are. But are they micromanaged? Is the SAI, the Scripted AI, Lua code directing the movement of each individual unit hex by hex? No!

What then is controlling those micromovements? It is the EAI, the Engine AI, the C++ hard-coded into the game engine EXE. Indeed, if units have no SAI orders (or did, but are now unleash()'ed), the EAI controls the units' movement, combat etc. entirely.

Whether or not guided by an overarching SAI, from point to point, the EAI directs movement

  • to proceed quickly, by default taking the most direct or fastest path (going the farthest with the least expenditure of APs, Action Points)
  • to prioritize the near vs. the far
  • to take advantage of covering terrain (hexes with higher TEM, Terrain Effect Modifier)
  • to avoid the enemy, where possible
  • to avoid mines, IEDs etc., if known
  • to avoid backtracking and going around in circles
  • other factors

and taking into account

  • unit health (Disrupted? Fatigued? Low Morale? Low or Out of Ammo?)
  • unit strength (also combatant vs. non-combatant, e.g., HQ)
  • the importance of the destination (Objective? with how many OPs (Objective Points)?)
  • enemy proximity (the AAI -- Adaptive AI -- hot_max, hot_trigger, and move_trigger* parameters, and others)
  • other factors

Upon arriving at or near the destination, the EAI will also have units redeploy around the arrival/attack/defend point considering

  • distance from the central point, actual and scatter
  • unit density
  • terrain effects
  • frontal attack vs. flank attack vs. surround (encirclement) attack
  • staying put vs. jumping around (repositioning)
  • other factors

All the while, possibly affected by overriding SAI orders for

  • Weak vs. Normal vs. Strong vs. Fanatical ('actprob' intensity)
  • directionality ('dir')
  • compacted vs. scattered ('extent', 'radius')
  • other

and a host of other SAI programmed guiding factors, not to mention AAI parameters nudging behaviors and actions this way and that.

All of this randomized, albeit not totally so, rather, more or less constrained.

People somehow have the notion that the SAI dictates everything. No. As we explain and discuss and tweak the Rung Sat (and other) Lua file(s), it will become clearer over time that the game's SAI is only half of the AI story, not even that; the other half is the EAI, assisted by the AAI (Adaptive AI).

The SAI paints in broad strokes with a wide brush, works best at company level and higher. The EAI paints the finer details, works best at directing the individual platoons. The AAI adds a touch of color.

When scripting the AI, don't think of micromanaging each and every counter's movement and action hex by hex. It doesn't work that way.

There are so many factors involved, the algorithms are so complex, so much randomness is built into the system, you can never precisely direct or anticipate what the SAI/EAI/AAI will do in combination.

Are there ways to force the AI to do exactly what you want? Yes, things like attack_fanatical() etc., and actprob 100%, and single-hexside DIR, and 0 radius or extent, and other parameters available as inputs to many of the SAI functions. But better to loosen up, to coax the AI and to nudge it and to give it leeway to do its own thing.

Kind of like how quantum mechanics' uncertainty principle describes the physical world, with the game's AI, you never quite know what it will do, it will be full of surprises. Think of the replayability!

Some people will also confidently assert: All AIs cheat! Well, not this one. In the Campaign Series, the AIs together have no more or less info than the human player. The AI and the human suffer equal combat results (unless at scenario's outset, at the opening AI Selection dialog, you elect to give one side or the other an artificial Advantage, i.e., augmenting one side or the other's SPs by some small or large multiple). The laws and rules of movement and every other game activity are the same. The Campaign Series AI does not cheat! (Unless you script the SAI to do so. Don't!)

Bear all of this mind as you play, and script, the game. There is so much going on under the hood. There is so much (constrained) randomness. Good luck, bad luck, and everything in between. Plausible, we hope. Nerve-wracking and exciting we are certain!

Another controversy: What makes for a "good" AI?

Is it one that considers all possible alternatives and selects the best possible rated outcomes? No.

For one thing, none of us plays the Campaign Series with a super computer, much less a quantum computer. There is such a vast array out possible outcomes (hexes X units X parameters X turns X etc., etc.) as to be astronomically, mind bogglingly huge.

For another thing, how do you rate outcome A vs outcome B vs. ...? How can you be sure one or the other is "better"?

For another, every game system has its flaws and limitations. We don't want an AI that will "game the system". Possibly choosing tactics and strategies quite bizarre from a human, historical perspective. (Read up on Google's Go Machine, and how it plays that game in such alien fashion, in ways that no human has ever played. Or consider computerized Sabermetrics and how in many people's judgment that is ruining the game of baseball.) How do you wish to be entertained? Is it a war game, or a comedy show or freak show?

No, what we are looking to achieve is AI plausibility. Does the AI play more or less like human commander(s) would? Does it pass the war gaming equivalent of the Turing Test?

Do we want the game AI to play like Napoleon? But remember: Even Napoleon had his bad days. He didn't win 'em all.

We want a half-way decent or better AI that will win some, lose some. Do clever and even brilliant moves, but will occasionally do something questionable and even unbelievably stupid. (Do commanders ever get shell shocked, dazed and confused? Do they misinterpret maps, are they misled by intentionally bad signage? Do they get lost in the Fog of War? Yes, yes, and yes! Do combatants perform extraordinary feats of bravery, do the "impossible"? Sometimes. Do they sometimes unexpectedly turn tail and run? Of course!)

With the CSEE/SAI, you have at your disposal a comprehensive mechanism to script the "perfect" AI. Ha! Good luck with that! Don't get lost in the "decision tree" forest! Whatever, have fun!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

CSEE function inputs


In Manual/LUA_FUNCTIONS_REFERENCE.txt, defend_normal() is described thusly:

CSVN_AAR_AI_AI_RungSat40.jpg
CSVN_AAR_AI_AI_RungSat40.jpg (106.82 KiB) Viewed 1343 times

There are five inputs (arguments) to that function, one mandatory and the other four optional.

For trackids, it says:

trackids -- a list of trackids (which may include just a single member)

Here is one example (from VN_550921_Rung_Sat.lua):


Code: Select all

    -- _AAMG_491 -- VM Anti-Aircraft Machine Gun
    do local units = _AAMG_491
        defend_normal(units)
    end


We could instead code that as


Code: Select all

        defend_normal(_AAMG_491)


where _AAMG_491 is defined in set_org_lists() as


Code: Select all

   _AAMG_491 = {491} -- [P] [75,57] [212065] VM Anti-Aircraft Machine Gun


_AAMG_491 is merely a variable with an assigned value {491}. _AAMG_491 is a stand-in for the list {491}. One that helpfully indicates the unit type (Anti-Aircraft Machine Gun), not just a meaningless number 491 (as a trackid # meaningful to the Lua CSEE, but otherwise meaningless to a casual reader of the code).

The code above could be interpreted as


Code: Select all

        defend_normal({491})


{491} is a list with a single member. In the Lua CSEE, lists are a collection of items, surrounded by an opening { (left curly brace) and closing } (right curly brace), and separated one from another by , (commas).

NOTE: In a Lua CSEE list, the items need not be of the same type or categorization. Indeed, you could have a list consisting of numbers, text strings, boolean true/false values, even embedded lists (list within list).

Would this work (be equivalent to the above)?


Code: Select all

        defend_normal(491)


Why, yes it would. We share with you here our little secret <shhhh...>: In the CSEE, in most functions (not quite all) where the expected trackids input is a "list of trackids (which may include just a single member)", in the single member case you can specify the input to be either {<trackid#>} or <trackid#>, for example, either '{491}' (a Lua list) or '491' (a simple number). Either is acceptable for a list with just a single member.

Confusing? Perhaps. But the CSEE is loose in this way because it a common mistake, especially for Lua beginners, to confuse one from the other. Although this is formally incorrect


Code: Select all

        defend_normal(491)


we don't want CSEE operation to fail, maybe frequently, because of this innocuous little mistake, confusing a list with a single member with that single member (not a list).

(If you wish, you can investigate, in init.lua, the defend_normal() definition, or see below. You will see how a not-a-list-rather-a-single-number trackids input is converted to a list on the fly. All behind the scenes. You don't need to understand how this works. It just does.)

Would this work?


Code: Select all

        defend_normal(_AAMG_491[1])


Why, yes this would also. Look at the definition of _AAMG_491 above. _AAMG_491 is a list. _AAMG_491[1] references the 1st element of that list. Which is the number 491. (Which defend_normal() converts back to a list at the outset. Confused much? Hahaha!)

For a multi-item list, named 'objectives' say, you would reference the first item via objectives[1], the second item via objectives[2], the third via objectives[3], and so on. Indeed in the VN_550921_Rung_Sat.lua file, we have this (in init_constants():


Code: Select all

    OBJECTIVES = {}
    OBJECTIVES[1] = "27,38" -- 1-40[2/2] 11
    OBJECTIVES[2] = "34,33" -- 1-40[2/2] 11
    OBJECTIVES[3] = "42,55" -- 1-40[2/2] 21
    OBJECTIVES[4] = "43,35" -- 1-40[2/2] 21
    ...
    OBJECTIVES[31] = "93,24" -- 1-40[2/2] 21
    OBJECTIVES[32] = "95,50" -- 1-40[2/2] 21
    OBJECTIVES[33] = "98,26" -- 1-40[2/2] 21
    OBJECTIVES[34] = "101,20" -- 1-40[2/2] 21


Where OBJECTIVES is first set to the empty list {} (a perfectly valid Lua expression), then populated with the indicated items (hcs, hex coordinate strings). Effectively, then OBJECTIVES becomes


Code: Select all

    OBJECTIVES = {"27,38", "34,33", "42,55", "43,35", ... , "93,24", "95,50", "98,26", "101,20"}


For a single element trackids list, you can in fact specify a number, the single element. For a trackids list with more than one element, would this work?


Code: Select all

        defend_normal(645, 646, "28,34", NODIR, 0, 50)


No, because defend_normal() (see above) expects up to five inputs, while the example just given is six inputs. You might intend that 645 and 646 are trackids, but in fact the Lua CSEE would interpret the second input, the 646, as an hc (a hex coordinate pair). Which because not a text string of the form "x,y" would choke the Lua interpreter, would generate an error.

Same reasoning for this broken code


Code: Select all

        defend_normal(645, 646)


No, this


Code: Select all

        defend_normal({645, 646}, "28,34", NODIR, 0, 50)


or this


Code: Select all

        defend_normal({645, 646})


would both work, because the all-important { and } combine the two trackids into a list, thus a single input.

Confusing? Maybe, probably, to a beginner. But patience. You will soon get the hang of it. In any case, we have as much as possible designed the CSEE to be forgiving of your beginner mistakes.

"Hmm", maybe you are thinking. "Doesn't defend_normal() expect to have five inputs? How then is the preceding example valid?"

Lua has a feature where, despite the number of arguments in the formal function definition, the number of inputs (parameters) you pass to a function is arbitrary, might vary. Could be the exact same number of inputs, could be more, could be less, could be zero even. Whether or not that causes trouble, whether it leads to run-time bugs is uncertain. It depends on how the CSEE functions are coded.

For the defend_normal() function, all of these are valid:


Code: Select all

        defend_normal(units, "28,34", NODIR, 0, 50)

        defend_normal(units, "28,34", NODIR, 0)

        defend_normal(units, "28,34", NODIR)

        defend_normal(units, "28,34")

        defend_normal(units)


If an input is not specified, the defend_normal definition supplies a default value. In init.lua, we have


Code: Select all

function defend_normal (trackids, hc, dir, radius, actprob)

    if not trackids then return false end
    local tl
    if type(trackids) == "table" then
        if #trackids == 0 then return false end
        tl = trackids
    elseif type(trackids) == "number" then
        tl = {trackids}
    else
        return false
    end
    hc = hc or HEXUNKNOWN
    if (hc ~= HEXUNKNOWN) and not on_map(hc) then
        log(APPLOG, LOG_WARNING, "in defend_normal(), mistaken (off-map or nonsensical) hc " .. hc .. ", order not given")
        return false
    end
    dir = dir or NODIR
    radius = radius or -1
    actprob = actprob or 50

    ...

end


You need not understand all of that. Just know that if you omit specifying one or more of the formal inputs, the CSEE will assign sensible default values for the missing inputs.

Typically, but not always, the first listed input is mandatory. Sometimes, all inputs are mandatory. Sometimes not. Here for example is a function where the input, 'turn', may or may not be needed:

CSVN_AAR_AI_AI_RungSat41.jpg
CSVN_AAR_AI_AI_RungSat41.jpg (53.96 KiB) Viewed 1277 times

In the first EXAMPLE, is_night() without any input is understood to mean: Is the current turn a night turn? In the second example, it questions whether Turn 32 is night. Both uses are perfectly valid. (So long as you input a number, or not. If you were to pass a text string to the function, or a true/false value, that would cause an error.)

In the CSEE implementation of this, if you omit any inputs, it must be done from right to left, from the end of the inputs backwards toward the beginning of the inputs list.

You also cannot leave any "holes" in the sequence. You should not for example omit the NODIR etc. as in


Code: Select all

        defend_normal(units, "28,34", 0, 50)


because the results would be uncertain, might be garbage, might cause an outright bug (and error pop-up) even. Again, it all depends on how the CSEE defines the function.

Why would you ever omit inputs (and accept the underlying default values)? Mainly for readability. If


Code: Select all

        defend_normal(units)


suffices, don't bother with the more wordy


Code: Select all

        defend_normal(units, HEXUNKNOWN, NODIR, UNKNOWN, 50)


(after units, the subsequent parameters are the defaults; see above) since the latter is cluttered, less readable.

Are the following legitimate?


Code: Select all

        defend_normal(units, "28,34", NODIR, 0, dieroll(100))

        defend_normal(units, "28,34", NODIR, random_pick(0,0,1,2))

        defend_normal(units, hex_adjacent(OBJECTIVES[9], UPDIR))


Yes! Each of those functions-as-input-parameters returns a value. So long as the function return value matches what the defend_normal() inputs list requires, it's okay.

There is much more that could be said about Lua function arguments, and some of their nuances and more advanced features. But since we are trying to KISS the CSEE, that's as far and as complicated as we take it.

In the course of the Rung Sat AAR and in other discussions of the CSEE/SAI, you will be seeing many, many more examples of Lua function calls and their inputs. If we show something out of the ordinary, we will point it out and provide commentary about it.

Enough for now!
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Debugging OrderType


In the course of auto-testing the VN_550921_Rung_Sat scenario

https://www.matrixgames.com/forums/view ... 2&t=383968

I witnessed the following situation:

CSVN_AAR_AI_AI_RungSat73.jpg
CSVN_AAR_AI_AI_RungSat73.jpg (2.03 MiB) Viewed 1238 times

_3RD_PLT_450 (turquoise circle) was observed to be wandering away from the fight for OBJECTIVES[16] (magenta circle) and apparently heading off in the direction of OBJECTIVES[13] (yellow circle). What's that all about?

From auto-test to auto-test, from time to time, you might see strange, unexpected SAI'ed unit moves. Is it a scripting error? A CSEE glitch? Worse, could it be a CSEE/game engine bug? We want to make sure.

In the VN_550921_Rung_Sat.lua file, we see:


Code: Select all

    _2ND_5TH_PARACHUTE_COY_55_A_447 = {448,449,450} -- [C] [1102243] 2nd/5th Parachute Company 55 - A
    _1ST_PLT_448 = {448} -- [P] [T3: 67,13] [112022] Parachute Platoon 55 A
    _2ND_PLT_449 = {449} -- [P] [T3: 67,13] [112022] Parachute Platoon 55 A
    _3RD_PLT_450 = {450} -- [P] [T3: 67,13] [112022] Parachute Platoon 55 A


_3RD_PLT_450 is a member of the 2nd/5th Parachute Company. In that same VN_550921_Rung_Sat.lua file, the company's orders code shows:


Code: Select all

    -- _2ND_5TH_PARACHUTE_COY_55_A_447 -- 2nd/5th Parachute Company 55 - A
    do local units = _2ND_5TH_PARACHUTE_COY_55_A_447
        if _5TH_PARACHUTE_BTN_55_A_441_READY_TURN then
            if obj13_threatened then
                attack_nearest_to_hex(units, OBJECTIVES[13], NODIR, 1, 100, ATTACK_STRONG)
            else
                if not _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER then -- set one time only
                    _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER = 1 -- random_pick({1,2})
                end
                if _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER == 1 then
                    if loss_rate(units) > 65 then
                        if not _2ND_5TH_PARACHUTE_COY_55_A_447_RETIRE_PT then
                            local cch = counters_center_hex(units)
                            local hno = hexes_not_occupied (hexes_habitat(), BX_SIDE)
                            _2ND_5TH_PARACHUTE_COY_55_A_447_RETIRE_PT = random_pick(hexes_nearest (cch, hno))
                        end
                        defend_weak(units, _2ND_5TH_PARACHUTE_COY_55_A_447_RETIRE_PT, NODIR, 1)
                    else
                        if objective_owner(OBJECTIVES[16]) == BX_SIDE then
                            move_way_point(units, {"65,8", "66,4"})
                            if within(difference(join({_3RD_5TH_PARACHUTE_COY_55_A_451, _5TH_PARACHUTE_WEAPONS_COY_55_455}), _MMG_457), OBJECTIVES[16], NODIR, 4) then
                                attack_strong(units, OBJECTIVES[16], {DOWNDIR, DOWNLEFTDIR, UPLEFTDIR, UPDIR, UPRIGHTDIR}, 1)
                            end
                        elseif objective_owner(OBJECTIVES[17]) == BX_SIDE then
                            attack_strong(units, OBJECTIVES[17], NODIR, 2)
                        end
                    end
                elseif _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER == 2 then
                    defend_normal(units, OBJECTIVES[13], NODIR, 1)
                end
            end
        end
    end

In that mass of orders code, which of it applies here, what's going on?

For _3RD_PLT_450, we inspect the engine.log file to see that unit's SAI assigned orders:



rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep "assigning order.*trackid 450" engine.log | tail
2022-07-09 05:59:24 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 21, side 0, unit Parachute Platoon 55 A, hex 66,4, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:00:36 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 22, side 0, unit Parachute Platoon 55 A, hex 69,3, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:02:00 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 23, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:03:14 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 24, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:04:32 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 25, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:06:12 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 26, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:07:41 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 27, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 60, yai 11, hexai 60,11, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:09:28 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 28, side 0, unit Parachute Platoon 55 A, hex 66,3, trackid 450, strackid 447, xai 61, yai 12, hexai 61,12, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:11:18 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 29, side 0, unit Parachute Platoon 55 A, hex 64,4, trackid 450, strackid 447, xai 61, yai 12, hexai 61,12, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:13:13 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 30, side 0, unit Parachute Platoon 55 A, hex 63,6, trackid 450, strackid 447, xai 60, yai 12, hexai 60,12, aidir -1, airadius 1, aiactprob 100, aiorder 13



Pay special attention to the 'aiorder' field (the final field at the ends of those log entries). We can see that, for every turn 21 through 30, unit trackid 450 was assigned the aiorder 13. What does that mean?

In the CSEE, here are the enumerated OrderType's:



-1 UnknownOrder = -1
0 NoOrder
1 Hold
2 Halt
3 DefendWeak
4 DefendNormal
5 DefendStrong
6 DefendFanatical
7 MoveRoam
8 MoveRecon
9 MoveSlow
10 MoveNoRush
11 MoveRush
12 AttackWeak
13 AttackNormal
14 AttackStrong
15 AttackFanatical
16 Load
17 UnLoad
18 Disembark
19 TakeOff
20 Land
21 AscendGround
22 AscendNOE
23 AscendLow
24 AscendHigh
25 DescendLow
26 DescendNOE
27 DescendGround
28 DescendUnderGround
29 AscendMove
30 MoveDescend
31 Exit
32 DigIn
33 Reconnoiter
34 BuildVehicleBridge
35 BuildLightBridge
36 LayMineField
37 SetIED
38 BuildBarrier
39 Damage
40 ClearLZ
41 FireDirect
42 FireIndirect
43 FireSpecial
44 AirStrike
45 _Assault
46 _Reduce
47 _Merge
48 _ToTop
49 _ToBottom
50 _Clockwise
51 _CounterClockwise



When you give an SAI order to a unit, for example


Code: Select all

                                attack_strong(units, OBJECTIVES[16], {DOWNDIR, DOWNLEFTDIR, UPLEFTDIR, UPDIR, UPRIGHTDIR}, 1)


the units involved are assigned an aiorder. In the init.lua file, for attack_strong() we see:


Code: Select all

function attack_strong (trackids, hc, dir, radius, actprob)

    if not trackids then return false end

    hc = hc or HEXUNKNOWN

    if not on_map(hc) then
        log(APPLOG, LOG_WARNING, "in attack_strong(), mistaken (off-map or nonsensical) hc " .. hc .. ", order not given")
        return false
    end

    dir = dir or NODIR

    radius = radius or -1

    actprob = actprob or 100  -- by default, 100 chance of assault

    return set_ai(trackids, hc, dir2hexdir(dir), radius, actprob, ATTACK_STRONG)

end


Note that next to last function statement


Code: Select all

    return set_ai(trackids, hc, dir2hexdir(dir), radius, actprob, ATTACK_STRONG)


That is a call to still another CSEE function, set_ai():


Code: Select all

function set_ai (trackids, hc, hexdir, radius, actprob, order)

    ...

            set_xai (trackid, x(hc))
            set_yai (trackid, y(hc))
            set_aidir (trackid, hexdir)
            set_airadius (trackid, radius)
            set_aiactprob (trackid, actprob)
            set_aiorder (trackid, order)


    ...

    return true

end


The statement


Code: Select all

            set_aiorder (trackid, order)


tells the game engine to assign the unit with 'trackid' the AI 'order'. (Important: Do not call set_aiorder() directly. That is an undocumented deep system function internal to the CSEE, for the CSEE's direct use only.)

Looking again at the engine.log entries above, we see for example



...
2022-07-09 06:06:12 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 26, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 70, yai 2, hexai 70,2, aidir -1, airadius 1, aiactprob 100, aiorder 13
2022-07-09 06:07:41 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 27, side 0, unit Parachute Platoon 55 A, hex 69,2, trackid 450, strackid 447, xai 60, yai 11, hexai 60,11, aidir -1, airadius 1, aiactprob 100, aiorder 13
...



For Turns 26 & 27, in both cases the unit Parachute Platoon 55 A with trackid 450, at current hex location 69,2, is assigned the aiorder 13. From the OrderType table



13 AttackStrong



we infer that unit trackid 450 has been given the attack_strong() order. In the orders code, we see:


Code: Select all

    -- _2ND_5TH_PARACHUTE_COY_55_A_447 -- 2nd/5th Parachute Company 55 - A
    ...
            if obj13_threatened then
                attack_nearest_to_hex(units, OBJECTIVES[13], NODIR, 1, 100, ATTACK_STRONG)
            else
                ...
                                attack_strong(units, OBJECTIVES[16], {DOWNDIR, DOWNLEFTDIR, UPLEFTDIR, UPDIR, UPRIGHTDIR}, 1)
            end
    ...


Look at the engine.log entries just above. We see 'hexai 70,2', which is OBJECTIVES[16]. And 'hexai 60,11', which is a hex near OBJECTIVES[13]. Evidently, OBJECTIVES[13] is (newly) (re)threatened, the _2ND_5TH_PARACHUTE_COY_55_A_447 is ordered to break off the attack on OBJECTIVES[16], and is assigned the new order to strong attack any enemy units nearest to OBJECTIVES[13].

It all checks out. Nothing wrong here, no bug or scripting error, it's all good.

The BX Army is in the process of (surprise) attacking OBJECTIVES[13]. What are those VNA units (green circles) around OBJECTIVES[8] doing?



rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep "assigning order.*trackid 444" engine.log | tail -n 1
2022-07-09 06:13:13 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 30, side 0, unit Parachute Platoon 55 A, hex 52,7, trackid 444, strackid 443, xai 52, yai 7, hexai 52,7, aidir -1, airadius -1, aiactprob 100, aiorder 2

rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep "assigning order.*trackid 446" engine.log | tail -n 1
2022-07-09 06:13:13 vnengine.exe: [DEBUG ID 0] (control.cpp, line 1644, Control::MakeGoal()) in Control::MakeGoal(), setting goal, assigning order, turn 30, side 0, unit Parachute Platoon 55 A, hex 48,7, trackid 446, strackid 443, xai 48, yai 7, hexai 48,7, aidir -1, airadius -1, aiactprob 100, aiorder 2



From the OrderType table



2 Halt



we see where the _1ST_5TH_PARACHUTE_COY_55_A_443 has been ordered to halt(), evidently. That company's orders code:


Code: Select all

function battle_plan_a (turn, side)

    -- standard orders, uncomment as necessary:
    if turn >= 1 then
        -- all Side A units halt initially (or maybe use hold())
        halt(ALLA)
    end

    ...

    -- _1ST_5TH_PARACHUTE_COY_55_A_443 -- 1st/5th Parachute Company 55 - A
    do local units = _1ST_5TH_PARACHUTE_COY_55_A_443
        if _5TH_PARACHUTE_BTN_55_A_441_READY_TURN then
            if not _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER and
               obj13_threatened then
                attack_nearest_to_hex(units, OBJECTIVES[13], NODIR, 1, 100, ATTACK_STRONG)
            else
                if not _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER then -- set one time only
                    _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER = 1
                end
                if loss_rate(units) > 65 then
                    if not _1ST_5TH_PARACHUTE_COY_55_A_443_RETIRE_PT then
                        local cch = counters_center_hex(units)
                        local hno = hexes_not_occupied (hexes_habitat(), BX_SIDE)
                        _1ST_5TH_PARACHUTE_COY_55_A_443_RETIRE_PT = random_pick(hexes_nearest (cch, hno))
                    end
                    defend_weak(units, _1ST_5TH_PARACHUTE_COY_55_A_443_RETIRE_PT, NODIR, 1)
                elseif not attack_nearest_arc(units, UPLEFTDIR, 2, ATTACK_NORMAL, true, true) and
                       (objective_owner(OBJECTIVES[8]) == BX_SIDE) then
                    attack_way_point(units, {"59,12", "50,6", OBJECTIVES[8]}, UPDIR, 2, 50, ATTACK_STRONG)
                end
            end
        end
    end

    ...

end


After strong attacking and successfully taking OBJECTIVES[8], there is no applicable, overriding order assigned to _1ST_5TH_PARACHUTE_COY_55_A_443. It therefore defaults to the general order halt(). Where the halt() AI order type value is 2. The engine.log entries for the _1ST_5TH_PARACHUTE_COY_55_A_443 say 'aiorder 2'. _1ST_PLT_446 is halted at hex 48,7 (OBJECTIVES[8]), its hexai. _1ST_PLT_444 is halted at hex 52,7, its hexai. (See the engine.log entries for trackids 446 & 444 above. See also the screenshot above.) Again, it all verifies, no worry, we're good.

(In the _1ST_5TH_PARACHUTE_COY_55_A_443 orders code just above, yes obj13_threatened is true. But the immediately preceding 'if not _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER and' short circuits the 'if ...' test, since _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER is not nil, 'not <not nil>' is false, the 'obj13_threatened' is not considered, the 'if ...' is bypassed, and the code execution moves on to the 'else ...'. Verstehen?)

(After taking OBJECTIVES[8], _1ST_5TH_PARACHUTE_COY_55_A_443 is apparently resting on its laurels. If OBJECTIVES[13] is attacked, maybe we should additionally send _1ST_5TH_PARACHUTE_COY_55_A_443 to the rescue? Something to consider. We'll see how the overall auto-tests go, also the for-real game plays.)

You should thoroughly understand this extended example. Make a copy of the OrderType table for your notes (or bookmark this forum post). Maybe practice looking up "assigning order" and aiorder entires in your own engine.log file(s). For successful SAI'ing, these are very important debugging techniques (inspecting the engine.log entries and comparing with the Lua code and OrderType values etc.). Be sure to master them.
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
User avatar
berto
Posts: 21461
Joined: Wed Mar 13, 2002 1:15 am
Location: metro Chicago, Illinois, USA
Contact:

Re: Forest of Assassins - Battle of Rung Sat - 9/21/55 - AAR (AI vs. AI)

Post by berto »

Debugging saved Lua variables



In VN_550921_Rung_Sat.lua, for the 2nd/5th Company, we have the orders code (abridged):


Code: Select all

    -- _2ND_5TH_PARACHUTE_COY_55_A_447 -- 2nd/5th Parachute Company 55 - A
    do local units = _2ND_5TH_PARACHUTE_COY_55_A_447
        ...
                if not _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER then -- set one time only
                    _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER = 1 -- random_pick({1,2})
                end
                if _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER == 1 then
                    [join 3rd/5th & Weapons in attacking OBJECTIVES[16] etc.]
                elseif _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER == 2 then
                    [stay behind to help defend OBJECTIVES[13]]
                end
        ...
    end


In that 'random_pick({1,2}', how do we know which order number, 1 or 2, has been assigned to _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER?

We might infer one or the other by following the auto-test action, and making an inference from what we observe.

A better way: Look in the save game .btl file!

When you run an auto-test, by default the OLDER and OLDEST save files are retained in addition to the current one:



rober@Rob10rto /cygdrive/r/Temp/Logs
$ ls -ltr "$VN"/*Rung_Sat*.btl
-rwx------+ 1 rober rober 44515 Jul 9 06:11 '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/VN_550921_Rung_Sat TEST TRIAL OLDEST.btl'
-rwx------+ 1 rober rober 44128 Jul 9 06:12 '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/VN_550921_Rung_Sat TEST TRIAL OLDER.btl'
-rwx------+ 1 rober rober 44554 Jul 9 06:13 '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/VN_550921_Rung_Sat TEST TRIAL.btl'



If we pattern match the current save file, VN_550921_Rung_Sat TEST TRIAL.btl, for the Lua variable save lines -- lines beginning with 'l ' (the letter 'l' followed by a space), we see:



rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep "^l " '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/VN_550921_Rung_Sat TEST TRIAL.btl'
l _C3_1ST_1ST_RIFLE_COY_48_US_16_POST:148
l _C2_1ST_1ST_RIFLE_COY_48_US_11_POST:-1
l SIDE_B_FIRED:true
l _C2_1ST_2ND_RIFLE_COY_48_US_42_POST:488
l _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER:1
l SIDE_B_ARTY_FIRED:true
l _ARVN_OBJECTIVES13_GARRISON:{457,448}
l _3RD_5TH_PARACHUTE_COY_5TH_PARACHUTE_WEAPONS_COY_ORDER:1
l _1ST_5TH_PARACHUTE_COY_55_A_443_ORDER:1
l OBJECTIVES13_RECAPTURED:true
l _2ND_VM_RIFLE_48_106_REINFORCE:1
l _FIRE_SUPPORT_COY_50_82MM_128_ATTACK_TURN:24
l NUMBER_AIRSTRIKES:{11}
l _5TH_PARACHUTE_BTN_55_A_441_READY_TURN:1
l _C2_1ST_5TH_RIFLE_COY_48_US_73_POST:149
l _C3_1ST_2ND_RIFLE_COY_48_US_47_POST:87,57
l _C1_1ST_2ND_RIFLE_COY_48_US_37_POST:-1,-1
l _C1_1ST_1ST_RIFLE_COY_48_US_6_POST:490
l SIDE_A_ARTY_FIRED:true
l OBJECTIVES13_CAPTURED:true
l SIDE_A_FIRED:true



Aha! Evidently _2ND_5TH_PARACHUTE_COY_55_A_447_ORDER has the value 1 (see the highlighted line). We remember the earlier action. Yes, we observed the _2ND_5TH_PARACHUTE_COY_55_A_447 heading off to attack OBJECTIVES[16]. Our inference is correct. It's all good.

There are some other interesting finds. Here are some of the BX Army Side B defensive posts:



...
l _C3_1ST_1ST_RIFLE_COY_48_US_16_POST:148
l _C2_1ST_1ST_RIFLE_COY_48_US_11_POST:-1
l _C2_1ST_2ND_RIFLE_COY_48_US_42_POST:488
...
l _C2_1ST_5TH_RIFLE_COY_48_US_73_POST:149
l _C3_1ST_2ND_RIFLE_COY_48_US_47_POST:87,57
l _C1_1ST_2ND_RIFLE_COY_48_US_37_POST:-1,-1
l _C1_1ST_1ST_RIFLE_COY_48_US_6_POST:490
...



(If not -1 or -1,-1, the other values are either hexes, such as 87,57, or trackids, such as 148, 488 etc.)

We can also see



l _FIRE_SUPPORT_COY_50_82MM_128_ATTACK_TURN:24



That BX Army force lurking to the north of OBJECTIVES[13] will be released to attack that objective on Turn 24, the result from the earlier


Code: Select all

        _FIRE_SUPPORT_COY_50_82MM_128_ATTACK_TURN = _FIRE_SUPPORT_COY_50_82MM_128_ATTACK_TURN or random_pick({21,22,23,24,25})


We could see that and other secrets, but it would be "wrong" to do so in normal game play; that would be cheating! No, we really only should inspect the .btl save file when modding/SAI'ing the .lua file, to solve some puzzling debugging situations, or to answer some questions.

Here's one: Why are those units breaking off the attack, retiring some distance away? In an earlier auto-test, an inspection of the 'VN_550921_Rung_Sat TEST TRIAL.btl' file showed:



rober@Rob10rto /cygdrive/r/Temp/Logs
$ egrep "_RETIRE_PT" '/cygdrive/c/Games/Matrix Games/Vietnam/vietnam/VN_550921_Rung_Sat TEST TRIAL.btl'
l _2ND_5TH_PARACHUTE_COY_55_A_447_RETIRE_PT:67,14
l _3RD_5TH_PARACHUTE_COY_5TH_PARACHUTE_WEAPONS_COY_RETIRE_PT:62,12
l _1ST_5TH_PARACHUTE_COY_55_A_443_RETIRE_PT:67,14



From those Lua variable save lines, we infer (from the VN_550921_Rung_Sat.lua file; see previous post):

  • Each of those companies -- 2nd/5th, 3rd/5th & 1st/5th -- has suffered 65% or greater casualties. Which activates the code where those _RETIRE_PT variables are set, given a hex value. (If casualties were less than 65%, those _RETIRE_PT variables would not be set, or saved.)
  • We can see which _RETIRE_PT each of those companies is ordered to withdraw to.

The .btl files and their Lua variable saved values -- these are gold mines of useful debugging nuggets. Useful for debugging. Alas, in normal game play, useful also for cheating. Don't do that. In normal game play, no peeking! :twisted:
Campaign Series Legion https://cslegion.com/
Campaign Series Lead Coder https://www.matrixgames.com/forums/view ... hp?f=10167
Panzer Campaigns, Panzer Battles Lead Coder https://wargameds.com
Post Reply

Return to “Scenario Design and Modding”