Skip to content

Lua Scripting

Alexander Grund edited this page May 16, 2024 · 58 revisions

Content:

Please refer the following documentation as it will be updated! Documentation
Information
Events
Functions
Player
World
GD_*
BLD_*
JOB_*
RES_*
SPEC_*
Example
Videos

Information:

LUA scripts are loaded from pathtomap.lua for a map at pathtomap.swd. The game will look for a file ending in '.lua' instead of '.swd'. Please be aware that case matters for some operating systems. The file will be sent to all clients, overwriting files in the user's own rttr MAPS directory.

The new LUA interface is object oriented. This means all RTTR entities are objects and hence functions must be invoked with rttr:MyFunc() instead of rttr.MyFunc.

LUA scripts (can) contain 2 parts: One that is used during game creating (e.g. set resources, nations, fixed settings...) and one that runs during the game and reacts on events.


There is 1 function every script must have:
getRequiredLuaVersion()
You need to implement this and return the major version your script works with. If it does not match the current version an error will be shown and the script will not be used. See rttr:GetVersion().

Back

Events:

Settings:

onSettingsInit(isSinglePlayer, isSavegame)
Called when the game settings screen is entered. Can be used to set up some dependent variables.
ONLY function called for all players. All other events are called only for host

onSettingsReady()
Called when the settings screen is initialized and player manipulations are possible

onPlayerJoined(playerIdx)
Called whenever a player joined.

onPlayerLeft(playerIdx)
Called when a player has left

onPlayerReady(playerIdx)
Called when a player pressed "ready". For the host player this means the game is starting (or host tries to)

getAllowedChanges()
Should return a map with string keys and bool values. If the bool is false, the given option cannot be changed. If a key does not exist, a default value is used:

  • general(false): Settings like lock teams, team view, ...
  • addonsAll(false): Cannot change any addon
  • addonsSome(false): Can only change addons in list returned by getAllowedAddons
  • swapping(false): Swap places
  • playerState(false): Change player slots (add/change AI, ...)
  • ownNation, ownColor, ownTeam (all true): Change values player
  • aiNation, aiColor, aiTeam (all true): Change values of AI
function getAllowedChanges()    
    return { ["general"] = true}    
end   

getAllowedAddons()
Only meaningfull if addonsAll=false and addonsSome=true: Return a list with addonIds that the host can change

Game:

onStart(isFirstStart)
called at the start of the game, after the headquarters are placed. isFirstStart is true, if the game was just created and false for savegames

onSave(serializer)
Called when the game is being saved. LUA data can be saved to the passed Serializer object. Needs to return true on success or false on error.

onLoad(serializer)
Called when the game is loaded (before onStart). LUA data can be loaded from the Serializer object in the same order they are saved (FIFO). Needs to return true on success or false on error.

onOccupied(player_id, x, y)
called every time a point on the map gets occupied by a player.

onExplored(player_id, x, y, owner)
called every time a point on the map becomes visible for a player. The owner parameter contains the owner's player id, nil means that there is no owner.

onGameFrame(gameframe_number)
Gets called every game frame.

onResourceFound(player_id, x, y, type, quantity)
Given resource (RES_IRON, RES_GOLD, RES_COAL, RES_GRANITE or RES_WATER) was found at x,y.

onCancelPactRequest(pactType, fromPlayer, toPlayer)
Only called for AI. Called when fromPlayer requests to cancel a pact. Return false to decline.

onSuggestPact(PactType, suggestedByPlayerId, targetPlayerId, duration)
Only called for AI. Player suggests a pact. Return true to accept or false to decline

onPactCanceled(PactType, canceledByPlayerId, targetPlayerId)

onPactCreated(PactType, suggestedByPlayerId, targetPlayerId, duration)

Back

Base functions (callable in settings and game):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/libs/libGamedata/lua/LuaInterfaceBase.cpp

rttr:GetVersion()
Get the current major and minor version as a pair. Changes to the major version reflect breaking changes that are expected to cause errors in existing scripts. Minor version changes only add new features. The current version is 1.0.

rttr:Log(message)
Log to console.

rttr:IsHost()
True/false

rttr:GetNumPlayers()
unsigned number

rttr:GetLocalPlayerIdx()
unsigned number (0-based!)

rttr:MsgBox(title, message, isError = false)
Show a message box to the current user. isError is used to show a red or green icon. Scrollbar will be shown for long text.
Note: You can have multiple message boxes opened. First one will be on top till closed

rttr:MsgBoxEx(title, message, iconFile, iconIdx[, iconX, iconY])
Show a message box to the current user. iconIdx is the index into iconFile (name w/o extension) of the icon to use. The file must be loaded at this point! (Safe use "io"). The window is automatically adjusted to fit the image. The text is moved, if possible, otherwise it is drawn over the image. Place the image at the border if you don't want this.
Note: You can have multiple message boxes opened. First one will be on top till closed
Note: You can not do any action until the window is closed
Note: You should use an offset as 0:0 leads to a graphic that is drawn in the upper left over the border (16,24 recommended)

rttr:RegisterTranslations(translations)
To register translations for multilanguage scripts this function can be used. You can pass a table containing languages as keys. A Language can be registered as following:

rttr:RegisterTranslations(
{ 
    en_GB = 
    { 
        World   = 'Hello World',
        Message = 'Have a nice day'
    },
    de =
    {
        World   = 'Hallo Welt',
        Message = 'Hab einen schönen Tag'
    }
})

In order to retrieve the Value, you simply use the following call:

_('World')

In this example,

  • If the client language is english, Hello World is returned
  • If the client language is german, Hallo Welt is returned
  • If the client language is czech (or any other than english or german), the key is returned, in this case world

Back

Settings functions (callable in settings only):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaInterfaceSettings.h
Important: All functions can only be used by the host!

rttr:GetPlayer(playerIdx)
Get the player with the given index (0-based) -> ServerPlayer

rttr:SetAddon(addonId, value)
Change setting of an addon. The addonId is the internal AddonID (see const_addons.h) with prefix ADDON_.

rttr:SetAddon(ADDON_FRONTIER_DISTANCE_REACHABLE, true)

For addons which can be enabled / disabled simply use true or false, for addons with different settings, add the number according to your setting.

rttr:ResetAddons()
Set all addons to S2 defaults

rttr:ResetGameSettings()
Set all settings and addons to their defaults. Note: Exploration is currently FoW. Will be classic later.

rttr:SetGameSettings(settings)
Set the settings with a table of keys:

  • speed: GS_VERYSLOW, GS_SLOW, GS_NORMAL, GS_FAST, GS_VERYFAST
  • objective: GO_NONE, GO_CONQUER3_4, GO_TOTALDOMINATION
  • startWares: SWR_VLOW, SWR_LOW, SWR_NORMAL, SWR_ALOT
  • fow: EXP_DISABLED, EXP_CLASSIC, EXP_FOGOFWAR, EXP_FOGOFWARE_EXPLORED
  • lockedTeams, teamView, randomStartPosition: true/false

Back

Gamefunctions (callable in game only):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaInterfaceGame.h
Important: All state-changing functions should be executed by all players at the same time (GF), or there will be asyncs!

rttr:ClearResources()
Remove all workers and wares from all players warehouses.

rttr:GetGF()
Return the current game frame number.

rttr:Chat(player, message)
Send message to player (-1 for all players).

rttr:MissionStatement(player, title, message, imgIdx = IM_SWORDSMAN, pause = true)
Send mission statement to player, pauses the game in single player if pause is set.
Possible images: IM_NONE, IM_SWORDSMAN, IM_READER, IM_RIDER, IM_AVATAR1-IM_AVATAR12 (see iwMissionStatement)
rttr:MissionStatement(0, "Diary", "Forth day\n\n\nThere are " .. rttr:GetNumPlayers() .. " players.")
Note: You can have multiple message boxes opened. First one will be on top till closed Not saved in savegame!

rttr:PostMessage(player, message)
Send post message to player.

rttr:PostMessageWithLocation(player, message, x, y)
Send post message to player (with location x, y).

rttr:GetPlayer(playerIdx)
Get the player with the given index (0-based) -> LuaPlayer

rttr:GetWorld()
Get the world object -> LuaWorld

Back

PlayerBase (functions available in settings and game):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaPlayerBase.h

GetName()

GetNation()
NAT_AFRICANS, NAT_JAPANESE, NAT_ROMANS, NAT_VIKINGS, NAT_BABYLONIANS

GetTeam()
TM_NOTEAM, TM_RANDOMTEAM, TM_RANDOMTEAM2, TM_RANDOMTEAM3, TM_RANDOMTEAM4, TM_TEAM1, TM_TEAM2, TM_TEAM3, TM_TEAM4
See TeamTypes for enums.

GetColor()
Return color index if found or player color. Color indices always have an alpha value.

IsHuman()

IsAI()

IsClosed()

IsFree()

GetAILevel()
-1: Not an AI, 0: Dummy, 1-3 = Easy-Hard

Back

LuaServerPlayer (For settings):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaServerPlayer.h

SetNation(Nation)

SetTeam(Team)

SetColor(color or colorIdx)
Sets the players color by index or directly if its alpha value is not zero. Duplicate color values are possible, so you have to ensure unique colors if you want them!

Close()
Closes a spot kicking any player or AI there.

SetAI(level)

SetName(name)

Back

LuaPlayer (Ingame-player object):

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaPlayer.h
player:EnableBuilding(type, notify)
Enable the building specified for the player. If notify is true, the players gets a notification that the building is now available.
Not saved in savegame!

DisableBuilding(type)
Disable the building for the player Not saved in savegame!

EnableAllBuildings() Not saved in savegame!

DisableAllBuildings()
Not saved in savegame!

SetRestrictedArea(x1,y1, ...)
Restrict the area a player may have territory in. If called without any x/y-values (e.g. rttr:GetPlayer(1):SetRestrictedArea()) , the restrictions are lifted. Otherwise, the coordinates specify one or multiple polygons the player may have territory in (replacing older restrictions). A polygon inside a polygon will create a hole in it. To specify multiple polygons, start with a 0,0-pair followed by the first polygon, another 0,0-pair, the second polygon etc. and a final 0,0-pair at the end. For multiple polygons, each polygons first point has to be repeated at the end of it.
Notes:

  1. It is unspecified whether a point on the boundary of a polygon is inside or outside of it. However if they are inside a polygon, then they are outside of a contained or containing polygon. This allows creation of mutual exclusive areas like allowing an area only for a player and disallowing this area for another player.
  2. RTTR/S2 uses a triangle-based grid. This may cause surprises when using this function as you cannot easily describe a "real" rectangle. Generally consider the border area as a "maybe" unless you tested it exactly.
  3. The current implementation uses the crossing-count method described here. But it assumes a rectangle grid, so every 2nd row is shifted by half a triangle for the means of the algorithm.

IsInRestrictedArea(x, y)
Return true if (x,y) is in the restricted area. Restricted means, building is allowed. The SetRestrictedArea function defines where you may build buildings and not where you are not allowed to.

ClearResources()
Remove all wares and workers from all warehouses

AddWares(wareMap)
Add the specified goods to the players first warehouse. Keys must be GoodTypes and values unsigned numbers

AddPeople(peopleMap)
Add the specified people to the player's first warehouse. Keys must be Jobs and values unsigned numbers

GetNumBuildings(building_type)
Get number of buildings a player has of a given type.

GetNumBuildingSites(building_type) Get number of building sites a player has of a given type.

GetNumWares(ware_type)
Get number of wares a player has of a given type.

GetNumPeople(job_type)
Get number of people a player has with a given job.

AIConstructionOrder(x,y,buildingtype)
order ai to build buildingtype at the given x,y location (ai will usually retry at a nearby location if the location is bad) Returns true, if the order was submitted. False otherwise.
Ignored, if player is not the host (returns always true)

ModifyHQ(isTent)
Set whether the players HQ is shown as a tent (1) or not (0)

IsDefeated()
Return true if the player is defeated

IsAlly(otherPlayerId)

IsAttackable(otherPlayerId)

SuggestPact(otherPlayerId, PactType, duration)

CancelPact(PactType, otherPlayerId)

GetHQPos()
Return x,y of the players HQ

Surrender(destroyBuildings)
Let the player give up either keeping its buildings or destroying them. Can be called multiple times e.g. to destroy buildings later.

Back

LuaWorld

Reference: https://github.com/Return-To-The-Roots/s25client/blob/master/libs/s25main/lua/LuaWorld.h

AddEnvObject(x, y, id, file = 0xFFFF)
Adds new environment object. Only environment/static objects and empty space can be overwritten. id: id in file file: 0xFFFF map_?_z.lst, 0-5 mis?bobs.lst

AddStaticObject(x, y, id, file = 0xFFFF, size = 0)
Adds new static object. Only environment/static objects and empty space can be overwritten. id: id in file file: 0xFFFF map_?_z.lst, 0-5 mis?bobs.lst size: 0: Block nothing (destructible), 1: Block single spot, 2: Block spots like castle-sized buildings

AddAnimal(x, y, species)
Adds an animal. If the terrain is unsuitable the animal will die soon. Species must be a SPEC_* constant

rttr:GetWorld():AddAnimal(41, 44, SPEC_DUCK)

Back

Serializer

The following methods are available. Remember that this is a FIFO structure, hence what is Pushed first needs to be Popped first.

PushInt(value)
PushBool(value)
PushString(value)
number PopInt()
true/false PopBool()
string PopString()

GD_*:

--> https://github.com/Return-To-The-Roots/s25client/blob/master/src/gameTypes/GoodTypes.h
GD_BEER
GD_TONGS
GD_HAMMER
GD_AXE
GD_SAW
GD_PICKAXE
GD_SHOVEL
GD_CRUCIBLE
GD_RODANDLINE
GD_SCYTHE
GD_WATER
GD_CLEAVER
GD_ROLLINGPIN
GD_BOW
GD_BOAT
GD_SWORD
GD_IRON
GD_FLOUR
GD_FISH
GD_BREAD
GD_SHIEL
GD_WOOD
GD_BOARDS
GD_STONES
GD_GRAIN
GD_COINS
GD_GOLD
GD_IRONORE
GD_COAL
GD_MEAT
GD_HAM

Back

BLD_*:

--> https://github.com/Return-To-The-Roots/s25client/blob/master/src/gameTypes/BuildingType.h
BLD_HEADQUARTERS
BLD_BARRACKS
BLD_GUARDHOUSE
BLD_WATCHTOWER
BLD_FORTRESS
BLD_GRANITEMINE
BLD_COALMINE
BLD_IRONMINE
BLD_GOLDMINE
BLD_LOOKOUTTOWER
BLD_CATAPULT
BLD_WOODCUTTER
BLD_FISHERY
BLD_QUARRY
BLD_FORESTER
BLD_SLAUGHTERHOUSE
BLD_HUNTER
BLD_BREWERY
BLD_ARMORY
BLD_METALWORKS
BLD_IRONSMELTER
BLD_CHARBURNER
BLD_PIGFARM
BLD_STOREHOUSE
BLD_MILL
BLD_BAKERY
BLD_SAWMILL
BLD_MINT
BLD_WELL
BLD_SHIPYARD
BLD_FARM
BLD_DONKEYBREEDER
BLD_HARBORBUILDING

Back

JOB_*:

--> https://github.com/Return-To-The-Roots/s25client/blob/master/src/gameTypes/JobTypes.h
JOB_HELPER
JOB_WOODCUTTER
JOB_FISHER
JOB_FORESTER
JOB_CARPENTER
JOB_STONEMASON
JOB_HUNTER
JOB_FARMER
JOB_MILLER
JOB_BAKER
JOB_BUTCHER
JOB_MINER
JOB_BREWER
JOB_PIGBREEDER
JOB_DONKEYBREEDER
JOB_IRONFOUNDER
JOB_MINTER
JOB_METALWORKER
JOB_ARMORER
JOB_BUILDER
JOB_PLANER
JOB_PRIVATE
JOB_PRIVATEFIRSTCLASS
JOB_SERGEANT
JOB_OFFICER
JOB_GENERAL
JOB_GEOLOGIST
JOB_SHIPWRIGHT
JOB_SCOUT
JOB_PACKDONKEY
JOB_BOATCARRIER
JOB_CHARBURNER
JOB_NOTHING

Back

RES_*:

--> https://github.com/Return-To-The-Roots/s25client/blob/master/src/lua/LuaInterfaceGame.cpp
RES_IRON
RES_GOLD
RES_COAL
RES_GRANITE
RES_WATER

Back

SPEC_*:

--> https://github.com/Return-To-The-Roots/s25client/blob/master/libs/s25main/gameTypes/AnimalTypes.h
SPEC_POLARBEAR
SPEC_RABBITWHITE
SPEC_RABBITGREY
SPEC_FOX
SPEC_STAG
SPEC_DEER
SPEC_DUCK
SPEC_SHEEP

Pacts:

NON_AGGRESSION_PACT
TREATY_OF_ALLIANCE
DURATION_INFINITE

Back

Example:

-- start callback, called after everything is set up  
function onStart(isFirstStart)  
    if(not isFirstStart) then
        return
    end
    -- add 100 generals and 10 officers for player 0  
    rttr:GetPlayer(0):AddPeople({[JOB_GENERAL] = 100, [JOB_OFFICER] = 10})  
    -- disable barracks (building 1) for player 0
    rttr:GetPlayer(0):DisableBuilding(BLD_BARRACKS)
    
    -- restrictions for player 0
    rttr:GetPlayer(0):SetRestrictedArea(0,42, 255,42, 255,255, 0,255)
end  
  
function onGameFrame(no)
    -- every 100 gf...
    if no % 100 == 0 then
        -- for all players..
        for player = 1, rttr:GetNumPlayers() do
            pl = rttr:GetPlayer(player - 1)
            rttr:Chat(0xFFFFFFFF, "[Player "..pl:GetName().." @GF "..rttr:GetGF().."] helpers: "..pl:GetNumPeople(JOB_HELPER)..", wells: "..pl:GetNumBuildings(BLD_WELL)..", water: "..pl:GetNumWares(GD_WATER))
        end
    end
end

-- just output a message for now
function onOccupied(player, x, y)
    rttr:Log("Point occupied: "..player..": "..x..","..y)
end

-- just output a message for now
function onExplored(player, x, y)
    rttr:Log("Point explored: "..player..": "..x..","..y)
end

For a more complete example usage of all functions have a look at https://github.com/Return-To-The-Roots/s25client/blob/master/tests/testData/maps/LuaFunctions.lua

Back

Videos:

1 - Quickstart
2 - Functions
3 - Sending a message
4 - Enable or Disable Buildings
5 - Counting and adding wares or people
6 - Custom objects
Custom animated Objects
Neutral Tribes Tests

Back