LuaScripting
LuaScripting
Overview
Lua scripts is the primary method to implement scripting in Panzer Corps 2. By using Lua scripts, the
game can check certain conditions and react to them by performing certain actions – both cosmetic
(show message, center on a certain hex) and affecting the state of the game.
To add scripting to a Panzer Corps 2 scenario, two main steps need to be done:
Create the script file itself. It must be located in the same folder as scenario file itself, and have
exactly the same name, but a different .lua extension. Inside this file, there must be one or
several Lua functions which the game will call.
Configure triggers inside Scenario Editor. This is done using Edit->Triggers editor mode. For each
trigger, two Lua functions are specified: condition and effect. Both functions must exist in the
script file. Condition is used to check if trigger should fire or not. Effect does all the required job
after the trigger fired.
The following table describes available options in the Game Object Model. Legend:
- means property
+ means method which can be called
GameWorld
- (bool) shroud
- (bool) fog
- (vector<Hex>) hexes
- (Weather) weather
- (GroundState) ground_state
- (enum Difficulty) difficulty
- (bool) allow_upgrades
- (bool) always_retreat
- (enum UnitsAvailability) unit_availability
- (int) seed
- (int) randomness
- (int) round
- (int) max_rounds
- (int) turn
- (vector<enum Relation>) relations
- (vector<Player>) players
- (int) humidity
- (vector<Weather>) weather_forecast
- (bool) cold_winter
- (vector<int>) playing
+ Hex GetHex(Point)
+ Unit GetUnit(int)
+ Player GetPlayer(int)
+ bool CanDisembarkThisTurn(Unit)
+ bool CanEmbarkAny(Unit)
+ vector<Unit> GetUnitTargets(Unit)
+ vector<Hex> GetMoveZone(Unit)
+ vector<Hex> GetDisembarkZone(Unit)
Hex
- (Point) index
- (int) special
- (int) owner
- (Faction) faction
- (int) vision
- (string) name
+ Unit GetUnit(int)
+ vector<Unit> GetUnits(void)
Player
- (enum Player::Control) control
- (FPlayerColor) custom_color --read-only
- (int) side_id
- (vector<Faction>) factions
- (vector<Faction>) core_factions
- (int) prestige
- (int) fuel
- (int) ammo
- (int) elites
- (bool) infinite_fuel
- (bool) infinite_ammo
- (bool) infinite_elites
- (bool) deployment_phase
- (int) prestige_income
- (int) fuel_income
- (int) ammo_income
- (int) elites_income
- (int) core_slots
- (int) aux_slots
- (int) max_core_slots
- (int) max_aux_slots
- (int) selected_unit
- (string) custom_purchase_list
- (vector<int>) weather_forecast_chances
- (vector<WeatherForecast>) weather_forecast --read-only
- (bool) iseeyou
+ void AddTransport(UnitType,int)
+ void Victory(int)
+ vector<Unit> GetDeploymentList(void)
Unit
- (int) owner_id
- (Faction) faction
- (int) name_id
- (bool) auxiliary
- (Point) position
- (vector<UnitEffect>) effects
- (UnitType) type
- (UnitStats) stats
- (int) strength
- (int) max_strength
- (int) overstr
- (int) suppression
- (int) attacks
- (int) moves
- (float) move_points
- (int) ammo
- (int) fuel
- (int) entrenchment
- (int) entrenchment_pts
- (int) accuracy
- (int) experience
- (int) spotted
- (UnitType) native_type
- (UnitType) transport
- (int) clone_id
- (int) carrier
- (Point) airfield
- (enum Unit::Status) status
- (int) aggressiveness
- (vector<AIOrder>) orders
+ bool HasPlace(void)
UnitType
- (string) name
- (enum UnitClass) kind
- (enum TargetType) target_type
- (enum MovementType) movement
- (UnitStats) stats
- (string) model
- (vector<Faction>) factions
- (Faction) faction
- (string) transport
- (int) cost
- (int) slots
- (string) switch_to
+ bool IsTransport(void)
+ bool IsAircraft(void)
+ bool IsLandUnit(void)
+ bool IsNaval(void)
+ bool HasTrait(enum UnitTrait)
UnitStats
- (Attack) attack --read-only
- (Defense) defense --read-only
- (int) strength
- (int) initiative
- (int) spotting
- (int) range
- (int) speed
- (int) ammo
- (int) fuel
Faction
Weather
- (enum EWeather) key
- (int) humidity
- (int) ground_initiative_cap
- (int) air_initiative_cap
- (int) ground_accuracy
- (int) air_accuracy
- (int) ground_spotting
- (int) air_spotting
- (bool) air_operations
- (int) supply
GroundState
- (enum EGroundState) key
StrikeAction
- (UnitsAction::UnitSpecific) unitA
- (UnitsAction::UnitSpecific) unitB
- (CombatLog) log
FightAction
- (UnitsAction::UnitSpecific) unitA
- (UnitsAction::UnitSpecific) unitB
- (CombatLog) log
UnitsAction
- (UnitsAction::UnitSpecific) unitA
- (UnitsAction::UnitSpecific) unitB
StrikeLog
- (int) nshots
- (int) accuracy
- (int) kills
- (int) suppress
- (int) ammo
- (StrikeLog::UnitSpecific) att
- (StrikeLog::UnitSpecific) def
- (StrikeLog::KillChances) chances
- (vector<StrikeLog::ShotRoll>) shots
StrikeLog::UnitSpecific
- (int) rating
StrikeLog::KillChances
- (int) deflect
- (int) kill
StrikeLog::ShotRoll
- (int) hit
- (int) kill
CombatLog
- (CombatLog::UnitSpecific) att
- (CombatLog::UnitSpecific) def
- (bool) ambush
- (vector<CombatLog::SupportFire>) supports
- (int) range
- (CombatLog::CombatEvent) rugged
- (CombatLog::CombatEvent) surprise
- (CombatLog::FirststrikeInfo) firststrike
CombatLog::UnitSpecific
- (int) id
- (bool) dead
- (bool) closefight
- (StrikeLog) strike
CombatLog::SupportFire
- (StrikeLog) strike
CombatLog::CombatEvent
- (bool) trigger
- (int) chance
- (int) roll
CombatLog::FirststrikeInfo
- (CombatLog::FirststrikeInfo::UnitSpecific) att
- (CombatLog::FirststrikeInfo::UnitSpecific) def
- (int) roll
- (enum FirstStrike) result
CombatLog::FirststrikeInfo::UnitSpecific
- (int) initiative
- (int) chance
atLog::FirststrikeInfo::UnitSpecific) def
- (int) roll
- (enum FirstStrike) result
CombatLog::FirststrikeInfo::UnitSpecific
- (int) initiative
- (int) chance
Examples
Rain without mud
The simplest function possible (but still very useful) is a condition which is always true. A trigger with
such condition will fire every time the condition is checked.
function AlwaysTrigger()
return true
end
Now, let’s create a simple effect function. Imagine that we want to create a scenario where it is always
raining, but ground never turns to muddy. One way to achieve this is to reset humidity to zero every
turn, so we could write this simple function:
function ResetMoisture()
world.humidity = 0
LogMessage("Humidity reset!")
end
The most important catch in this example is how we access the game world. There is just one global
object representing the world, and it is called, surprise, world. This object has type GameWorld, and
you can access all of its properties and methods described in Game Object Model section above. One
such property is humidity, which represents the current amount of water on the ground.
LogMessage is a useful function which displays message on the screen. It can be used for debugging
purposes. Note that some elements of the HUD (like unit list) can obscure the messages.
Now, we can combine all this in Scenario Editor the following way:
Zero count means that trigger will never expire and fire unlimited number of times. Condition is
AlwaysTrigger, so trigger will fire unconditionally every time End Turn happens (because EndTurnAction
is checked as a precondition for running this trigger). Finally, we have specified ResetMoisture as
trigger’s effect.
Turn conditions
Here are some examples how to check turn conditions in Panzer Corps 2 (an equivalent of Time
Condition in Panzer Corps).
function IsDeploymentPhase()
return world.round == 0
end
function IsFirstTurn()
return world.round == 1
end
function CheckTurnRange()
return world.round >= 2 and world.round <= 5
end
Terminological note: in game code current turn is designated with round variable, while turn variable
holds the index of the current player.
Random conditions
Panzer Corps had a special “Random Condition” which was true with a certain probability. In Panzer
Corps 2 same effect can be achieved by using built-in math.random() function. For example:
function FiftyFifty()
return math.random() < 0.5
end
math.random() without any parameters generates a random real number between 0 and 1, so on
average it will be less than 0.5 in 50% of checks. This is an equivalent of Panzer Corps Random Condition
with 50% probability.
Using variables
In Panzer Corps triggers could exchange information by creating and removing “tags”. There was a
special action to modify a tag, and a special condition to check it. In Panzer Corps 2 we can simply use
Lua’s global variables, which provide much greater flexility.
First, you create a variable in the beginning of the script and assign it initial value:
some_event = false
function SignalEvent()
some_event = true
end
And some other condition function can check it. So this condition will be true only after this variable has
been modified elsewhere.
function CheckEvent()
return some_event
end
Of course, this is a trivial example. Variables can be used to store all kinds of counters of various events,
accumulate lists of hexes and units etc.
Checking parameters
In Panzer Corps, the options to check game state were very limited. In Panzer Corps 2 we have access to
much more parameters, and can use them in more complex expressions too. We have already looked at
checking current turn, all other parameters are checked the same way. Here are some examples.
function IsRaining()
return world.weather.key == Rain
end
Valid values to compare weather with are: Clear, Cloudy, Rain, Snow.
function IsMuddy()
return world.ground_state.key == Muddy
end
Valid values to compare ground state with are: Dry, Muddy, Snowy, Frozen.
Check difficulty:
function CheckDifficulty()
return world.difficulty == Colonel
end
Valid values to compare difficulty with are: Difficulty, Sergeant, Lieutenant, Colonel, General,
FieldMarshal.
Of course, all these parameters can be checked not only as standalone conditions, but also as part of
some larger function. For example, we can have a function which runs once at the beginning of a
scenario. This function can do lots of various things, and among them check difficulty and adjust the
scenario based on that. This way we can make custom changes based on player’s difficulty.
function CheckAxisPrestige()
return world:GetPlayer(0).prestige >= 100
end
Note that we are using GetPlayer method of the world object in order to get the player by its id,
and in Lua methods are called by using “:”, not “.” which is used to access properties.