Lua

From MegaGlest
Jump to navigation Jump to search

Lua is the scripting language used by MegaGlest for all scripted scenarios and tutorials. It allows you to add custom gameplay and interact with the player by showing messages or displaying a custom objective. One example of a scripted scenario is Amazones, where you need to survive multiple waves of attacking enemies. The ingame tutorials are scripted scenarios as well.

There are many possibilities to interact with the gameplay. You can react to unit deaths, have timer events, create units at custom spawn points, highlight units and much more.

Basics[edit]

To enable scripting in a scenario set the external attribute of the scripts node to true in the XML/Scenario. If enabled, a file with the name script.lua needs to be placed inside the scenario folder. This file will contain the Lua code of the scenario.

Scripting in MegaGlest is event based. When an event happens during the game, a function with the corresponding name in the Lua script file will be called. For example, when a unit dies, a function with the name unitDied will be called if present in the script.lua file. This table contains all possible events that are currently supported.

Function name Description
global() Called whenever the scenario will be started or loaded from a savegame.
startup() Called once at the start of the scenario.
onSave() Called when the scenario gets saved. Here you can store variables you when the savegame gets loaded.
onLoad() Called when the savegame of the scenario is started. Here you can also restore saved variables.
unitCreated() Called each time any player creates a new unit. Use lastCreatedUnit() to get the unit ID.
resourceHarvested() Called whenever a resource is harvested. Use resourceAmount(resource, faction) to get the current amount of a resource.
unitCreatedOfType_unitname() Called when a unit of the specified name is created by any player.
unitDied() Called every time a unit from any player dies. Use lastDeadUnit() to get the unit ID of the unit that died.
unitAttacked() Called when any unit is attacked. Use lastAttackedUnit() to get the unit ID.
unitAttacking() Called when any unit is attacking. Use lastAttackingUnit() to get the unit ID.
gameOver() Called when the game is over.
timerTriggerEvent() Called when a second has elapsed for any timer event or when a efficient timer event triggers. Use triggeredTimerEventId() to get the timer ID.
cellTriggerEvent() Called when a cell event triggers. Cell events are triggered when a unit enters a defined region. Use triggeredCellEventUnitId() to get the unit that triggered the event.
unitTriggerEvent() Called when a unit event triggers. Unit events are triggered if a property of a registered unit changes. Use lastUnitTriggerEventUnit() to get the unit that triggered the event.
dayNightTriggerEvent() Called when it gets day or night if enabled by registerDayNightEvent()

Inside of these function any lua code can be used. There are many functions that can be used to interact with the game itself. This table contains some of the most important functions. A list of all functions and more elaborate info can be found here.

Function name Description
disableAi(factionIndex) Disables AI control of a faction
setPlayerAsWinner(factionIndex) Sets a faction as having won the game.
endGame() Finishes the game.
setCameraPosition(pos) Move the camera so that the cell at pos is in the centre of the view.
showMessage(text, header) Displays a message to the user in a messagebox. Use the .lng file for translations.
setDisplayText(text) Displays a message to the user at the top of the screen. Use the .lng file for translations.
clearDisplayText() Clears the display message from the top of the screen.
createUnit(type, factionIndex, pos) Create a unit a unit for a faction at (or near) pos.
giveResource(type, factionIndex, amount) Gifts some resources to a faction (don't forget to make sure they can store them!)
givePositionCommand(unitId, command, pos) Give a position based command to a unit. (For example 'move' or 'attack')
giveProductionCommand(unitId, unitType) Give a poduce or morph command to a unit.
giveUpgradeCommand(unitId, upgrade) Give an upgrade command to a unit.
startLocation(factionIndex) Returns a start location, as an array of two elements.
unitPosition(unitId) Returns the location of a unit, as an array of two elements.
unitFaction(unitId) Returns the faction index of a unit.
resourceAmount(resource, factionIndex) Returns the amount of a resource a player has.
lastCreatedUnit() Returns the ID of the last created unit.
lastDeadUnit() Returns the ID of the last unit to die.
unitCount(factionIndex) Returns the number of units a player has.
unitCountOfType(factionIndex, type) Returns the number of units of a specific type a player has.

IDs[edit]

An important concept to scripting in MegaGlest are IDs. IDs are handles that can be used to reference to things such as a factions/players, units, timer events and more. You can store an ID in a variable to access the same thing on a later occasion.

Faction IDs[edit]

All players get an ID ranging from 0 to one less then the max number of players in the game. For example if the scenario has 4 players, valid faction IDs are 0, 1, 2, 3 NOT 1, 2, 3, 4. This is called the faction index, and is used in many functions that affect a faction/player. Closed slots do not count when determining the faction ID.

Unit IDs[edit]

All units in MegaGlest have a unique number assigned to them. This number can be used to reference a unit in the Lua code. There are multiple functions to get the ID of a certain unit. The most used one is the lastCreatedUnit() function, which will return the ID of the last unit that was spawned. The ID will always stay the same for the same unit. A typical usage of this feature is to store the ID of an important unit in a variable and check if it survives using the lastDeadUnit() function in the unitDied event.

Coordinates[edit]

Coordinates can be used for anything that needs a position. These are in the format of {x,y}, and replace common queries such as startLocation() and unitPosition(). Maps are split into tiles. Each tile is 2 x 2 cells. So a 128 x 128 map acutally has 256 x 256 cells. Lua uses the CELLS instead of tiles for positions. Also, like the faction IDs, the positions start at {0,0} instead of {1,1}.

There are multiple commands which query for positions. These can be used to generate a relative position. For example, if we wanted to have a unit appear 20 tiles (40 cells) north of the start location we'd use createUnit('worker', 0, {startLocation(0)[1],startLocation(0)[2]-40}). All location queries will return the x value if a [1] is placed after it, and a y value if a [2] is placed after it.

Translation files[edit]

If you want to have dialog or objectives in the scenario, .lng files can be used for the different messages. Their advantage is that they can be translated into other languages. They should be in the format <scenario_name>_<language>.lng. For example, if the scenario is called storming, then the english lng file will be called storming_english.lng. English should be the priority, while other languages are optional.

The lng files are in the same format as an ini, and lines that start with ; are comments.

Lines in the .lng file are simply <tagName>=text string.

The tagName is the name used to refer to the lng in the lua script, such as showMessage('message-tag', 'message-topic'). This lng file would need two tags, message-tag and message-topic. Note that text strings are unrestricted, and can contain any ASCII characters and punctuation. They can have capital letters as well, and go until the end of the line. Note that if you go too long, the dialogue box may not have room to fit the message. If that's the case, split it down into separate messages.

Example[edit]

The best way to learn is by looking at an example. In this example a player needs to defend a defense_tower in the middle of the map against a CPU. He looses when the tower dies and wins if he successfully defended the tower for 10 minutes. The scenario is called protect_the_tower.

protect_the_tower.xml[edit]

<?xml version="1.0" standalone="yes" ?>
<scenario>
	<difficulty value="2"/>
	<players>
		<player control="human" faction="tech" team="1"/>
		<player control="closed"/>
		<player control="cpu-ultra" faction="romans" team="2"/>
		<player control="closed"/>
	</players>
	<map value="conflict"/>
	<tileset value="autumn"/>
	<tech-tree value="megapack"/>
	<default-resources value="true"/>
	<default-units value="true"/>
    <!-- default victory conditions are turned of because we implement custom conditions in the lua code -->
	<default-victory-conditions value="false"/>
    <!-- (external) scripts need to be turned on to load the lua code -->
    <scripts external="true"/>
</scenario>

script.lua[edit]

-- This is called when the scenario starts
function startup()
    -- create the tower in the middle of the map
    -- the first parameter is the unit name
    -- the second parameter is the faction id - the player has ID 0 here
    -- the third parameter are the coordinates where to the tower
    -- Note: 64,64 is the middle of the map even though conflict is a 64x64 map
	createUnit('defense_tower', 0, {64,64})
    -- save the id of the tower that was just created to the 'importantTower' variable
	importantTower=lastCreatedUnit()
    -- highlight the tower so the player can see what he needs to protect
    -- parameters are unitID, radius, thickness, colour
    highlightUnit(importantTower, 1, 0.2, {1,1,1,0.7})
    -- start the ten minute timer
    -- we also save the timer ID for later
    timerEventID=startEfficientTimerEvent(10*60)
    -- Displays an objective on the top of the screen
    -- The text is taken from tag 'ProtectTheTower' in the .lng file
    setDisplayText('ProtectTheTower')
end

-- This is called whenever a unit dies
function unitDied()
    -- compare the unit ID of the last unit that died with the importantTower ID
	if lastDeadUnit()==importantTower then
        -- the tower died
        -- clear objective
		clearDisplayText()
        -- set the CPU as winner
        -- Note: the faction ID is 1 and not 2 because closed slots do not count
		setPlayerAsWinner(1)
        -- end the game
		endGame()
	end
end

-- Triggered whenever an efficient timer event runs out
-- or every second for normal timer events
function timerTriggerEvent()
    -- check whether this event was triggered by the ten minute timer
    -- (in this case we only have one timer event so this will always be true)
    if triggeredTimerEventId()==timerEventID then
        -- the player survived long enough
        -- clear objective
		clearDisplayText()
        -- set the Player as winner
		setPlayerAsWinner(0)
        -- end the game
		endGame()
    end
end

protect_the_tower_english.lng[edit]

; This message could be translated to different languages
; You can use \n for line breaks in the messages
ProtectTheTower=Protect the highlighted Tower for ten minutes!

List of Lua Commands[edit]

See MG/Lua

Click the link to see all available lua commands in MegaGlest.

External Links[edit]