I am working on a system to let you spawn in on a "squad leader" unit. Here is the code:
Hidden/Spoiler:
[code]-- create initial tables
unitsSet = {}
leaderTable = {}
spawnHandlersTable = {}
function createUnitHandlers(teamIndex)
print("setting up handlers for team " .. tostring(teamIndex))
-- add entry for team index
leaderTable[teamIndex] = {}
spawnHandlersTable[teamIndex] = {}
unitsSet[teamIndex] = {}
local totalUnitsOnSide = 20
local leadersAreSet = false
spawnHandlersTable[teamIndex]["spawnHandler"] = OnCharacterSpawnTeam(function(player)
-- add unit to set of units (avoid duplicates)
unitsSet[teamIndex][player] = 1
print("size of units set for team " .. tostring(teamIndex) .. " is " .. tostring(table.getn(unitsSet[teamIndex])))
-- add marker back to respawned squad leader
if leadersAreSet then
for index, unit in leaderTable[teamIndex] do
if player == unit then
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", true)
end
end
end
-- add squad leader for every x units
-- this should be set up at the start of the match
if not leadersAreSet then
if table.getn(unitsSet[teamIndex]) >= totalUnitsOnSide-5 then
local unitsIndex = 1
for unit, _ in unitsSet[teamIndex] do
unitsIndex = unitsIndex + 1
-- every 5th unit is squad leader
if math.mod(unitsIndex, 5) == 0 then
table.insert(leaderTable[teamIndex], unit)
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", true)
end
end
print(" SET SQUAD LEADERS ======================")
leadersAreSet = true
end
end
-- if human move them to random squad leader location
-- TODO make this happen on button press rather than every time
if IsCharacterHuman(player) then
if table.getn(leaderTable[teamIndex]) > 0 then
local randCharacter = leaderTable[teamIndex][math.random(1, table.getn(leaderTable[teamIndex]))]
-- check if squad leader alive
if GetCharacterUnit(randCharacter) then
print("character is human, moving to " .. tostring(randCharacter))
local randMatrix = GetEntityMatrix(GetCharacterUnit(randCharacter))
-- move to 3 units behind the character
local newMatrix = CreateMatrix(0,0,0,0,0,0,-3, randMatrix)
SetEntityMatrix(GetCharacterUnit(player), newMatrix)
else
print(" SQUAD LEADER WAS DEAD, CANNOT SPAWN ON")
end
else
print(" NO SQUAD LEADERS YET ")
end
end
end,
teamIndex)
end,
teamIndex)
end[/code]
unitsSet = {}
leaderTable = {}
spawnHandlersTable = {}
function createUnitHandlers(teamIndex)
print("setting up handlers for team " .. tostring(teamIndex))
-- add entry for team index
leaderTable[teamIndex] = {}
spawnHandlersTable[teamIndex] = {}
unitsSet[teamIndex] = {}
local totalUnitsOnSide = 20
local leadersAreSet = false
spawnHandlersTable[teamIndex]["spawnHandler"] = OnCharacterSpawnTeam(function(player)
-- add unit to set of units (avoid duplicates)
unitsSet[teamIndex][player] = 1
print("size of units set for team " .. tostring(teamIndex) .. " is " .. tostring(table.getn(unitsSet[teamIndex])))
-- add marker back to respawned squad leader
if leadersAreSet then
for index, unit in leaderTable[teamIndex] do
if player == unit then
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", true)
end
end
end
-- add squad leader for every x units
-- this should be set up at the start of the match
if not leadersAreSet then
if table.getn(unitsSet[teamIndex]) >= totalUnitsOnSide-5 then
local unitsIndex = 1
for unit, _ in unitsSet[teamIndex] do
unitsIndex = unitsIndex + 1
-- every 5th unit is squad leader
if math.mod(unitsIndex, 5) == 0 then
table.insert(leaderTable[teamIndex], unit)
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", true)
end
end
print(" SET SQUAD LEADERS ======================")
leadersAreSet = true
end
end
-- if human move them to random squad leader location
-- TODO make this happen on button press rather than every time
if IsCharacterHuman(player) then
if table.getn(leaderTable[teamIndex]) > 0 then
local randCharacter = leaderTable[teamIndex][math.random(1, table.getn(leaderTable[teamIndex]))]
-- check if squad leader alive
if GetCharacterUnit(randCharacter) then
print("character is human, moving to " .. tostring(randCharacter))
local randMatrix = GetEntityMatrix(GetCharacterUnit(randCharacter))
-- move to 3 units behind the character
local newMatrix = CreateMatrix(0,0,0,0,0,0,-3, randMatrix)
SetEntityMatrix(GetCharacterUnit(player), newMatrix)
else
print(" SQUAD LEADER WAS DEAD, CANNOT SPAWN ON")
end
else
print(" NO SQUAD LEADERS YET ")
end
end
end,
teamIndex)
end,
teamIndex)
end[/code]
My question is how can I tie this function to a button press on the spawn screen. I edited ifs_pc_spawnselect.lua to add a new button. So far it does nothing. How can I make it spawn the unit AND then call this function?
Can I add a dynamic number of buttons, one per squad leader?
EDIT: This code is what came of it
viewtopic.php?f=64&t=34466
EDIT
Here is the code from the working first release:
Hidden/Spoiler:
[code]---
--- Created by MileHighGuy.
--- DateTime: 4/4/2020 3:51 PM
---
-- setup object attributes and defaults
SquadSetup = {
-- set of all units on a team
-- contains their character index
unitsSet = {},
-- table containing squad leaders for a team
-- contains their character index
leaderTable = {},
-- table containing handles to OnCharacterSpawnTeam
spawnHandlersTable = {},
-- table containing handles to OnCharacterDeathTeam
deathHandlersTable = {},
--table containing true/false whether to spawn on leader for each team
spawnOnLeaderBoolTable={}
}
-- constructor for SquadSetup object
function SquadSetup:New(o)
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end
-- Add squads for a team
-- teamIndex: For default map scripts, this is usually a number stored in ATT or DEF variables
-- unitsOnSide: number on units on the field at once. Defaults to 20
-- numSquadLeaders: how many squad leaders you want for you team. Must be lower than total number of units per side
function SquadSetup:addTeam(teamIndex, unitsOnSide, numSquadLeaders)
-- Disable all this if in multiplayer for now
if ScriptCB_InMultiplayer() then return end
local totalUnitsOnSide = unitsOnSide or 20
if numSquadLeaders > totalUnitsOnSide then
numSquadLeaders = 1
end
local totalNumSquadLeaders = numSquadLeaders or 1
-- add entry for team to table, if doesnt exist
if not self.unitsSet[teamIndex] then
self.unitsSet[teamIndex] = {}
end
if not self.leaderTable[teamIndex] then
self.leaderTable[teamIndex] = {}
end
if not self.spawnOnLeaderBoolTable[teamIndex] then
self.spawnOnLeaderBoolTable[teamIndex] = false
end
local humanPlayerIsAlive = false
--used for assigning squad leaders
local step = math.floor( totalUnitsOnSide/ totalNumSquadLeaders)
local startIndex = math.floor( math.max( 1, ( totalUnitsOnSide - ((totalNumSquadLeaders - 1) * step ) + 1 ) /2 ) )
local leaderCountTable = { [teamIndex] = startIndex }
-- store the handle to this event listener
self.spawnHandlersTable[teamIndex] = OnCharacterSpawnTeam(function(player)
-- add unit to set of units (avoid duplicates)
self.unitsSet[teamIndex][player] = 1
-- how many unique units have spawned thus far
local unitSetSize = self:countSet(self.unitsSet[teamIndex])
local leaderTableSize = table.getn(self.leaderTable[teamIndex])
local playerIsHuman = IsCharacterHuman(player)
-- add marker back to respawned squad leader
if leaderTableSize > 0 then
for index, unit in self.leaderTable[teamIndex] do
if player == unit then
-- do alive check
if GetCharacterUnit(unit) then
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", false)
end
end
end
end
-- add a squad leader evenly through he spawned units
if leaderTableSize < totalNumSquadLeaders then
if unitSetSize == leaderCountTable[teamIndex] then
if not playerIsHuman then
table.insert(self.leaderTable[teamIndex], player )
print("adding marker to unit " .. tostring(player) .. " which is character " .. tostring(GetCharacterUnit(player)) .. " team " .. tostring(teamIndex))
--check if alive first
if GetCharacterUnit(player) then
MapAddEntityMarker(GetCharacterUnit(player), "hud_objective_icon", 3.0, teamIndex, "GREEN", false)
end
else
print("Cannot make player squad leader")
end
--increment counter
leaderCountTable[teamIndex] = leaderCountTable[teamIndex] + step
end
end
-- if human move them to random squad leader location
if playerIsHuman and not humanPlayerIsAlive and self.spawnOnLeaderBoolTable[teamIndex] == true then
local aliveLeaders = self:GetAliveSquadLeadersTable(teamIndex)
local aliveLeaderCount = table.getn(aliveLeaders)
-- check if there is at least 1 alive leader
if aliveLeaderCount > 0 then
local randCharacter = aliveLeaders[math.random(1, aliveLeaderCount)]
-- redundant alive check to make me feel good
if GetCharacterUnit(randCharacter) then
print("character is human, moving to " .. tostring(randCharacter))
local randMatrix = GetEntityMatrix(GetCharacterUnit(randCharacter))
-- move to 3 units behind the character
local newMatrix = CreateMatrix(0,0,0,0,0,0,-3, randMatrix)
SetEntityMatrix(GetCharacterUnit(player), newMatrix)
else
print(" SQUAD LEADER WAS DEAD, CANNOT SPAWN ON")
end
else
print(" NO SQUAD LEADERS YET ")
end
end
if playerIsHuman then
--set player alive
--used to make sure the player cannot teleport via changing classes!
humanPlayerIsAlive = true
end
end, teamIndex)
self.deathHandlersTable[teamIndex] = OnCharacterDeathTeam(function(player)
if IsCharacterHuman(player) then
humanPlayerIsAlive = false
end
end, teamIndex)
end
-- helper function to count table size
function SquadSetup:countSet( table )
local count = 0;
for k,v in pairs( table ) do count = count + 1 end
return count
end
-- returns a table of alive squad leaders for a team
-- it holds their character index
-- table is allowed to be empty (if no squad leaders)
-- WARNING: the squad leaders may not be chosen at the time you call this function
-- teamIndex: the team number you want to get leaders from
function SquadSetup:GetAliveSquadLeadersTable(teamIndex)
local aliveLeaders = {}
-- update whether squad leaders are alive or dead
for index, unit in self.leaderTable[teamIndex] do
-- GetCharacterUnit will return nil (eval to false) if unit is dead
if GetCharacterUnit(unit) then
table.insert(aliveLeaders, unit)
end
end
return aliveLeaders
end
-- returns a table of squad leaders for a team, may be alive or dead
-- table is allowed to be empty (if no squad leaders)
-- WARNING: the squad leaders may not be chosen at the time you call this function
-- teamIndex: the team number you want to get leaders from
function SquadSetup:GetSquadLeadersTable(teamIndex)
return self.leaderTable[teamIndex]
end
-- toggle whether we should spawn on units or not
function SquadSetup:ToggleSpawnOnLeader(teamIndex)
if self.spawnOnLeaderBoolTable[teamIndex] then
self.spawnOnLeaderBoolTable[teamIndex] = false
else
self.spawnOnLeaderBoolTable[teamIndex] = true
end
end
-- return the boolean that determines spawning on leader
function SquadSetup:GetSpawnOnLeaderBool(teamIndex)
return self.spawnOnLeaderBoolTable[teamIndex]
end
function overrideSpawnScreen()
-- Disable all this if in multiplayer for now
if ScriptCB_InMultiplayer() then return end
---- add our button
if AddIFScreen then
print("DEBUG: function AddIFScreen is defined")
print("DEBUG: overriding AddIFScreen")
originalAddIFScreen = AddIFScreen
AddIFScreen = function (screen, screenName)
if screenName == "ifs_pc_SpawnSelect" then
print("DEBUG: adding our button to spawn select screen")
-- info for squad leader toggle button
local w,h = ScriptCB_GetSafeScreenInfo()
local okcancelw = w * 0.2
local okcancelh = h * 0.03
local okYPos = h - 0.1*h
screen.Info.SpawnOnRandom = NewPCIFButton
{
x = w/2 + okcancelw + okcancelw/4,
y = okYPos,
btnw = okcancelw,
btnh = okcancelh,
font = "gamefont_large",
bg_width = okcancelw,
string = "Spawn on Unit [ ]",
tag = "randomSpawn"
}
-- end button info
screen.Input_Accept = function(this)
if(gShellScreen_fnDefaultInputAccept(this)) then
return
end
-- if the new button is pressed
if this.CurButton == "randomSpawn" then
squadSetup:ToggleSpawnOnLeader(this.activeSide + 1)
end
end
screen.Update = function(this,fDt)
-- Call default base class's update function (make button bounce)
gIFShellScreenTemplate_fnUpdate(this,fDt)
-- if the models are done animating, slow down the rotations
if(this.IconModelFastMode and not this.Info.SideModel0.bAnimActive) then
IFModel_fnSetOmegaY(this.Info.SideModel0,-0.3)
IFModel_fnSetOmegaY(this.Info.SideModel1,-0.3)
this.IconModelFastMode = nil
end
-- add this in Update so it constantly gets called. Value will get refreshed when player switches teams
-- watcher for SpawnOnLeader boolean
local isSpawnActive = squadSetup:GetSpawnOnLeaderBool(this.activeSide + 1)
if isSpawnActive then
RoundIFButtonLabel_fnSetString(screen.Info.SpawnOnRandom , "Spawn on Unit [x]")
else
RoundIFButtonLabel_fnSetString(screen.Info.SpawnOnRandom , "Spawn on Unit [ ]")
end
end
end
print("DEBUG: Performing original AddIFScreen with args " .. tostring(screen) .. " " .. tostring(screenName))
originalAddIFScreen(screen, screenName)
end
else
print("DEBUG: function AddIFScreen not defined yet yet")
end
end[/code]
--- Created by MileHighGuy.
--- DateTime: 4/4/2020 3:51 PM
---
-- setup object attributes and defaults
SquadSetup = {
-- set of all units on a team
-- contains their character index
unitsSet = {},
-- table containing squad leaders for a team
-- contains their character index
leaderTable = {},
-- table containing handles to OnCharacterSpawnTeam
spawnHandlersTable = {},
-- table containing handles to OnCharacterDeathTeam
deathHandlersTable = {},
--table containing true/false whether to spawn on leader for each team
spawnOnLeaderBoolTable={}
}
-- constructor for SquadSetup object
function SquadSetup:New(o)
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end
-- Add squads for a team
-- teamIndex: For default map scripts, this is usually a number stored in ATT or DEF variables
-- unitsOnSide: number on units on the field at once. Defaults to 20
-- numSquadLeaders: how many squad leaders you want for you team. Must be lower than total number of units per side
function SquadSetup:addTeam(teamIndex, unitsOnSide, numSquadLeaders)
-- Disable all this if in multiplayer for now
if ScriptCB_InMultiplayer() then return end
local totalUnitsOnSide = unitsOnSide or 20
if numSquadLeaders > totalUnitsOnSide then
numSquadLeaders = 1
end
local totalNumSquadLeaders = numSquadLeaders or 1
-- add entry for team to table, if doesnt exist
if not self.unitsSet[teamIndex] then
self.unitsSet[teamIndex] = {}
end
if not self.leaderTable[teamIndex] then
self.leaderTable[teamIndex] = {}
end
if not self.spawnOnLeaderBoolTable[teamIndex] then
self.spawnOnLeaderBoolTable[teamIndex] = false
end
local humanPlayerIsAlive = false
--used for assigning squad leaders
local step = math.floor( totalUnitsOnSide/ totalNumSquadLeaders)
local startIndex = math.floor( math.max( 1, ( totalUnitsOnSide - ((totalNumSquadLeaders - 1) * step ) + 1 ) /2 ) )
local leaderCountTable = { [teamIndex] = startIndex }
-- store the handle to this event listener
self.spawnHandlersTable[teamIndex] = OnCharacterSpawnTeam(function(player)
-- add unit to set of units (avoid duplicates)
self.unitsSet[teamIndex][player] = 1
-- how many unique units have spawned thus far
local unitSetSize = self:countSet(self.unitsSet[teamIndex])
local leaderTableSize = table.getn(self.leaderTable[teamIndex])
local playerIsHuman = IsCharacterHuman(player)
-- add marker back to respawned squad leader
if leaderTableSize > 0 then
for index, unit in self.leaderTable[teamIndex] do
if player == unit then
-- do alive check
if GetCharacterUnit(unit) then
MapAddEntityMarker(GetCharacterUnit(unit), "hud_objective_icon", 3.0, teamIndex, "GREEN", false)
end
end
end
end
-- add a squad leader evenly through he spawned units
if leaderTableSize < totalNumSquadLeaders then
if unitSetSize == leaderCountTable[teamIndex] then
if not playerIsHuman then
table.insert(self.leaderTable[teamIndex], player )
print("adding marker to unit " .. tostring(player) .. " which is character " .. tostring(GetCharacterUnit(player)) .. " team " .. tostring(teamIndex))
--check if alive first
if GetCharacterUnit(player) then
MapAddEntityMarker(GetCharacterUnit(player), "hud_objective_icon", 3.0, teamIndex, "GREEN", false)
end
else
print("Cannot make player squad leader")
end
--increment counter
leaderCountTable[teamIndex] = leaderCountTable[teamIndex] + step
end
end
-- if human move them to random squad leader location
if playerIsHuman and not humanPlayerIsAlive and self.spawnOnLeaderBoolTable[teamIndex] == true then
local aliveLeaders = self:GetAliveSquadLeadersTable(teamIndex)
local aliveLeaderCount = table.getn(aliveLeaders)
-- check if there is at least 1 alive leader
if aliveLeaderCount > 0 then
local randCharacter = aliveLeaders[math.random(1, aliveLeaderCount)]
-- redundant alive check to make me feel good
if GetCharacterUnit(randCharacter) then
print("character is human, moving to " .. tostring(randCharacter))
local randMatrix = GetEntityMatrix(GetCharacterUnit(randCharacter))
-- move to 3 units behind the character
local newMatrix = CreateMatrix(0,0,0,0,0,0,-3, randMatrix)
SetEntityMatrix(GetCharacterUnit(player), newMatrix)
else
print(" SQUAD LEADER WAS DEAD, CANNOT SPAWN ON")
end
else
print(" NO SQUAD LEADERS YET ")
end
end
if playerIsHuman then
--set player alive
--used to make sure the player cannot teleport via changing classes!
humanPlayerIsAlive = true
end
end, teamIndex)
self.deathHandlersTable[teamIndex] = OnCharacterDeathTeam(function(player)
if IsCharacterHuman(player) then
humanPlayerIsAlive = false
end
end, teamIndex)
end
-- helper function to count table size
function SquadSetup:countSet( table )
local count = 0;
for k,v in pairs( table ) do count = count + 1 end
return count
end
-- returns a table of alive squad leaders for a team
-- it holds their character index
-- table is allowed to be empty (if no squad leaders)
-- WARNING: the squad leaders may not be chosen at the time you call this function
-- teamIndex: the team number you want to get leaders from
function SquadSetup:GetAliveSquadLeadersTable(teamIndex)
local aliveLeaders = {}
-- update whether squad leaders are alive or dead
for index, unit in self.leaderTable[teamIndex] do
-- GetCharacterUnit will return nil (eval to false) if unit is dead
if GetCharacterUnit(unit) then
table.insert(aliveLeaders, unit)
end
end
return aliveLeaders
end
-- returns a table of squad leaders for a team, may be alive or dead
-- table is allowed to be empty (if no squad leaders)
-- WARNING: the squad leaders may not be chosen at the time you call this function
-- teamIndex: the team number you want to get leaders from
function SquadSetup:GetSquadLeadersTable(teamIndex)
return self.leaderTable[teamIndex]
end
-- toggle whether we should spawn on units or not
function SquadSetup:ToggleSpawnOnLeader(teamIndex)
if self.spawnOnLeaderBoolTable[teamIndex] then
self.spawnOnLeaderBoolTable[teamIndex] = false
else
self.spawnOnLeaderBoolTable[teamIndex] = true
end
end
-- return the boolean that determines spawning on leader
function SquadSetup:GetSpawnOnLeaderBool(teamIndex)
return self.spawnOnLeaderBoolTable[teamIndex]
end
function overrideSpawnScreen()
-- Disable all this if in multiplayer for now
if ScriptCB_InMultiplayer() then return end
---- add our button
if AddIFScreen then
print("DEBUG: function AddIFScreen is defined")
print("DEBUG: overriding AddIFScreen")
originalAddIFScreen = AddIFScreen
AddIFScreen = function (screen, screenName)
if screenName == "ifs_pc_SpawnSelect" then
print("DEBUG: adding our button to spawn select screen")
-- info for squad leader toggle button
local w,h = ScriptCB_GetSafeScreenInfo()
local okcancelw = w * 0.2
local okcancelh = h * 0.03
local okYPos = h - 0.1*h
screen.Info.SpawnOnRandom = NewPCIFButton
{
x = w/2 + okcancelw + okcancelw/4,
y = okYPos,
btnw = okcancelw,
btnh = okcancelh,
font = "gamefont_large",
bg_width = okcancelw,
string = "Spawn on Unit [ ]",
tag = "randomSpawn"
}
-- end button info
screen.Input_Accept = function(this)
if(gShellScreen_fnDefaultInputAccept(this)) then
return
end
-- if the new button is pressed
if this.CurButton == "randomSpawn" then
squadSetup:ToggleSpawnOnLeader(this.activeSide + 1)
end
end
screen.Update = function(this,fDt)
-- Call default base class's update function (make button bounce)
gIFShellScreenTemplate_fnUpdate(this,fDt)
-- if the models are done animating, slow down the rotations
if(this.IconModelFastMode and not this.Info.SideModel0.bAnimActive) then
IFModel_fnSetOmegaY(this.Info.SideModel0,-0.3)
IFModel_fnSetOmegaY(this.Info.SideModel1,-0.3)
this.IconModelFastMode = nil
end
-- add this in Update so it constantly gets called. Value will get refreshed when player switches teams
-- watcher for SpawnOnLeader boolean
local isSpawnActive = squadSetup:GetSpawnOnLeaderBool(this.activeSide + 1)
if isSpawnActive then
RoundIFButtonLabel_fnSetString(screen.Info.SpawnOnRandom , "Spawn on Unit [x]")
else
RoundIFButtonLabel_fnSetString(screen.Info.SpawnOnRandom , "Spawn on Unit [ ]")
end
end
end
print("DEBUG: Performing original AddIFScreen with args " .. tostring(screen) .. " " .. tostring(screenName))
originalAddIFScreen(screen, screenName)
end
else
print("DEBUG: function AddIFScreen not defined yet yet")
end
end[/code]





