Squad system questions
Posted: Sun Mar 29, 2020 4:52 pm
by MileHighGuy
Hello,
I am working on a system to let you spawn in on a "squad leader" unit. Here is the code:
It chooses every 5th unit on the team to be a "squad leader". After the squad leaders are chosen, when the player spawns he is moved to the squad leader so it looks like he spawned on them.
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:
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]