team
Each team has a unique team number.
teamSize
The number of players on a given team.
teamMem
I've made up the name for this one because I don't know what the stock missions call it. Each player on a team (human or ai) has a different number starting at 0. That number is teamMem. Say for example team 1 has 15 players and team 2 has 12 players, then team 1 will have team members 0 - 14 and team 2 will have team members 0 - 11. (The numbers overlap.) Note: The largest teamMem on a team is always 1 less than teamSize.
charIdx
This is a unique number given to every player (human or ai). This also starts at 0. Eg players on team 1 could be numbered 0 - 14, then players on team 2 could be numbered 15 - 26, and players on team 3 could be numbered 27 - 30 and so on. Notice the numbers don't overlap for separate teams, which is what makes charIdx different from teamMem.
classNum
This tells you the order that the character's class was added to the character's team. (The order of the AddUnitClass calls). The numbers start at 0, and the numbers do overlap for separate teams. Here's an example:
Code: Select all
AddUnitClass(REP, "rep_inf_ep3_rifleman", 0, 4)
AddUnitClass(REP, "rep_inf_ep3_rocketeer", 0, 4)
AddUnitClass(REP, "rep_inf_ep3_sniper", 0, 4)
AddUnitClass(CIS, "cis_inf_rifleman", 0, 4)
classStr
The name of the character's class as a string. eg "rep_hero_kiyadi". You could also call it className. I'm undecided on which I prefer.
charUnit
This is an object handled by the game engine. When a player is alive, they have a charUnit. That charUnit must have some information about what class a specific character is and where they are etc. If you print charUnit, it will print userdata: then a hexadecimal number. The type userdata lets you know that charUnit is an object written in a different programming language to lua (I think C). Then the hexadecimal number is the memory address for a specific instance of that object. You can't do much with this but you can pass it into functions.
entityMatrix
This is an object handled by the game engine. This object is a bit like a set of co-ordinates that the game engine can read but you can't. Like before, you can't directly do much with this but you can pass it into functions.
vehObj
Another object handled by the game engine. This time it's a vehicle. Whenever I write a variable called object or a variable which ends with Obj, assume it's the same as charUnit.
(last updated: 14/03/23)
These are the functions I use a lot. I will add more when I think of more. All of these functions work in ScriptPostLoad().
Code: Select all
GetTeamSize(team) -- returns teamSize
GetTeamMember(team, teamMem) -- returns charIdx (yes it looks backwards with this naming scheme)
GetCharacterTeam(charIdx) -- returns team
GetCharacterClass(charIdx) -- returns classNum
GetCharacterUnit(charIdx) -- returns charUnit if the player is alive, returns nil if the player is dead
IsCharacterHuman(charIdx) -- returns bool
GetWorldPosition(entityMatrix) -- returns x, y and z as numbers
CreateMatrix(rot1, rot2, rot3, rot4, x, y, z, anotherMatrix) -- returns a new entityMatrix
GetPathPoint(string) -- returns a new entityMatrix
GetEntityMatrix(charUnit) -- returns an entityMatrix in the same position as charUnit
SetClassProperty(classStr, ...) -- only affects characters before they spawn, other arguments are ODF parameters (as strings or numbers)
SetProperty(charUnit, ...) -- only affects spawned characters, other arguments are ODF parameters (as strings or numbers)
GetObjectHealth(charUnit) -- returns curHealth, maxHealth, addHealth as numbers
GetObjectShield(charUnit) -- returns curShield, maxShield, addShield as numbers
SelectCharacterClass(charIdx, classNum)
SpawnCharacter(charIdx, entityMatrix)
KillObject(charUnit) -- it will take other game objects too, but this example is for killing characters. It does trigger OnCharacterDeath
AllowAISpawn(team, bool) -- affects SpawnCharacter as well as the automatic AI spawns
GetCharacterVehicle(charIdx) -- returns vehObj
ExitVehicle(charIdx)
SelectCharacterTeam(charIdx, newTeam) -- changes a player's team before they spawn, crashes if the charIdx was alive
BatchChangeTeams(oldTeam, newTeam, num) -- moves a number of players from oldTeam to newTeam. Crashes if any of oldTeam were alive
SetObjectTeam(charUnit, newTeam) -- affects spawned ai players. Weirdly, their charIdx is technically still on the old team. Don't know what happens to humans
(last updated 14/03/23)
Note: Verified filters means I've successfully used these filters before, not that they're the only filters which exist.
Code: Select all
OnCharacterDeath(function(deadIdx, killerIdx) end) -- verified filters: Team (the team of deadIdx)
OnCharacterSpawn(function(charIdx) end) -- verified filters: Class, Team
OnFlagPickup(function(flag, charIdx) end) -- verified filters: Class (the class of the character that picked up the flag)
OnTicketCountChange(function(team, count) end) -- verified filters: Team
OnTimerElapse(function(timerName) end, "timerName")
GetObjectHealth is an example of this. There are two ways get all the variables from GetObjectHealth. One way is to call GetObjectHealth in a table eg.
Code: Select all
local healthTable = {}
healthTable = {GetObjectHealth(charUnit)}
The other technique is to put multiple variables in the = operation eg.
Code: Select all
local curHealth
local maxHealth
local addHealth
curHealth, maxHealth, addHealth = GetObjectHealth(charUnit)
I'm putting some examples here to help people see how these functions are used.
Example 1: Find every living unit on team 1 and give them max health
Code: Select all
local teamSize = GetTeamSize(1)
for teamMem = 0, teamSize - 1 do
local charIdx = GetTeamMember(1, teamMem)
local charUnit = GetCharacterUnit(charIdx)
if charUnit then
-- character is alive
local curHealth, maxHealth = GetObjectHealth(charUnit)
if curHealth < maxHealth then
SetProperty(charUnit, "CurHealth", maxHealth)
end
end
end
Code: Select all
-- function to find the character index of a unit which has not spawned
local function FindFreeIdx(team)
local teamSize = GetTeamSize(team)
for teamMem = 0, teamSize - 1 do
local charIdx = GetTeamMember(team, teamMem)
local charUnit = GetCharacterUnit(charIdx)
if not charUnit then
-- this charIdx is not spawned
return charIdx
end
end
return nil
end
local classNum = 0 -- let's assume this is a clone trooper
local spawnPt = CreateMatrix(0, 0, 0, 0, 4, 5, 7, nil)
local charIdx = FindFreeIdx(3)
if charIdx then
SelectCharacterClass(charIdx, classNum)
SpawnCharacter(charIdx, spawnPt)
local charUnit = GetCharacterUnit(charIdx)
if charUnit then
print("Clone trooper spawned successfully")
else
print("Failed to spawn clone trooper")
end
else
print("Failed to find a free charIdx on team 3")
end
If you scroll down enough you'll see that destroying timers is very important. Here's how I make looping timers that destroy themselves after the match:
Code: Select all
-- initialize the OnTimerElapse event
-- I'm now in the habit of always initializing my OnTimerElapse event before using the timer
CreateTimer("timer1")
SetTimerValue("timer1", 2) -- you don't need this line, I'm just being safe
StopTimer("timer1") -- you don't need this line either, I'm just being safe
OnTimerElapse(
function(timer)
if (GetTeamSize(1) or 0) > 0 then -- my logic is that this function will always return a positive number during a game
-- whatever code you want the timer to perform, write it here
-- loop the timer
SetTimerValue(timer, 15)
StartTimer(timer)
else -- this runs after the match
DestroyTimer(timer)
end
end,
"timer1"
)
DestroyTimer("timer1")
-- end of timer initialization
-- run the looping timer
CreateTimer("timer1")
SetTimerValue("timer1", 15)
StartTimer("timer1")
(last updated: 15/03/23)
You can stop events from running again with Release+EventName(eventObj). Do not put the event filter in the release command. It's easiest to demonstrate with an example:
Code: Select all
-- this event will only run on the first clone trooper which spawns, then it will be destroyed
local eventObj = OnCharacterSpawnClass(
function(charIdx)
-- put your code here
-- this eventObj must contain the OnCharacterSpawnClass, see the first line
ReleaseCharacterSpawn(eventObj)
end,
"rep_inf_ep3_rifleman"
)
I haven't used entity matrices much so I will add more when I understand more.
CreateMatrix(rot1, rot2, rot3, rot4, x, y, z, otherMatrix) -- returns entityMatrix
I haven't properly looked at what the 4 rotation values actually do. I know there are other posts on GT explaining them. This is a weird function because you can pass in an entityMatrix at the end (I've called it otherMatrix). Then CreateMatrix will return a new entityMatrix where the position and rotation of that new entityMatrix were all set relative to the position and rotation of otherMatrix. (Imagine it applying the position and rotation values as transformations on otherMatrix, then returning the result). Alternatively, you can pass in nil instead. Then it will create an entityMatrix where the position and rotation values are all relative to the map's origin (by origin, I mean the co-ordinate x = 0, y = 0, z = 0). (I only know otherMatrix = nil works after seeing MileHighGuy do it.)
GetPathPoint(string) -- returns entityMatrix
If you're making a map, then you can place a path point in ZEditor, set it's direction and give it a name, eg "HeroSpawnPt". If you pass that same name into this function, eg GetPathPoint("HeroSpawnPt"), the function will return an entityMatrix with all the same position and rotation values as the path point you placed in ZEditor. (I haven't done any map making myself, but I've seen this in the stock missions.)
GetEntityMatrix(object) -- returns entityMatrix
I only ever use charUnit with this but I think it can take all kinds of stuff. This returns an entityMatrix with the same position and rotation values as the object (ie in the same place).
GetWorldPosition(entityMatrix) -- returns number * 3
This function returns the x, y and z co-ordinates of an entityMatrix relative to the map's origin. If you pass in charUnit, it will do the same for a living character.
SetEntityMatrix(object, entityMatrix)
I haven't put this in the functions list because I'm not 100% sure how it works, and I haven't ever needed it. I think it applies the entityMatrix to the object so the object moves to a new position. Try it with charUnit. I've seen people use it for teleports, idk.
(last updated 20/03/23)
- If you don't destroy all your timers, they can eventually crash an instant action playlist. From looking at the objective scripts, I'm starting to think the problem may actually be that you need to stop all your timers, not destroy them. Because, those objective scripts get away with a lot and they never crash.
- The OnTimerElapse event will persist after a timer has been destroyed. In other words you can create and destroy the same timer multiple times, but you should run the OnTimerElapse code only once. It's more like you're initializing the event than actually running it. If you initialize the event twice, the event will trigger twice. You can however remove the OnTimerElapse code like any other event using ReleaseTimerElapse.
- If a mission lua contains EnableSPScriptedHeroes(), then that function will apply to every mission in an instant action playlist afterwards, (this breaks the heroes in most of the conquest or ctf missions after you play a campaign mission)
- I don't think OnFlagCapture is a real event even though it's in the documentation
- OnCharacterDeath has no Class filter
- AddMapClassMarker can sometimes persist after you restart a mission (In the debug game. I haven't checked this in the base game.)