Squad system questions

In this forum you will find and post information regarding the modding of Star Wars Battlefront 2. DO NOT POST MOD IDEAS/REQUESTS.

Moderator: Moderators

Post Reply
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Squad system questions

Post by MileHighGuy »

Hello,

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]
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:
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]
Last edited by MileHighGuy on Wed Nov 18, 2020 12:31 am, edited 3 times in total.
User avatar
AnthonyBF2
Sith
Sith
Posts: 1255
Joined: Wed Aug 21, 2013 3:55 pm
Projects :: PS2+PSP Overhaul

Re: Squad system questions

Post by AnthonyBF2 »

I can't give a solution but I can tell you that ifs_pc_spawnselect coincides with stuff inside the Battlefront 2 exe, so that's probably why you can't add new buttons.
User avatar
Anakin
Master of the Force
Master of the Force
Posts: 4817
Joined: Sat Sep 19, 2009 11:37 am
Projects :: RC Side Mod - Remastered - SWBF3 Legacy
Location: Mos Espa (germany)

Re: Squad system questions

Post by Anakin »

Diet Dr. Pepper you can add new buttons. just the function that is triggered by the spawn button is somewhere hidden
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

In my post I meant that I WAS able to add the button but whats not clear yet is how to make it do something. I would like it to spawn the unit and also call another function after that
User avatar
Nedarb7
Lieutenant General
Lieutenant General
Posts: 676
Joined: Sat Sep 22, 2012 3:41 pm

Re: Squad system questions

Post by Nedarb7 »

Could you simply add another OnCharacterSpawn which calls the function after they spawn if a certain variable is true, and have it so that if the player presses the button on the spawn screen you change the variable to true?
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

Yes, but that brings us back to the question of how to add a function to a button on the spawn screen (to set the boolean variable). If you look in ifs_pc_spawnselect.lua you will see that it doesn't have an Input_Accept function like most IF screens. So if I wanted to add a duplicate spawn button (which also called my custom function), how would I do it? How is the spawn function being called? How does the .exe know which button is the spawn button?

this is a comment from ifs_pc_spawnselect.lua

Code: Select all

-- Actual contents are created in ifs_charselect_fnBuildScreen
	-- Note: for now, the exe is handling all the inputs/events, so this
	-- screen has no Enter/Exit/Update/Input handlers. It does have an
	-- Input_Back handler to override the base class's default functionality
	-- (go to previous screen)
So there wont be anything in an Input_Accept. I am not convinced though that this means what I'm trying to do is not possible.

I added this to ifs_pc_spawnselect.lua

Code: Select all

Input_Accept = function(this,bFromAI)
		print("pressed spawn button")
	end,
interestingly, only when my new fake button is pressed does this code actually print. When I press the real spawn button then it does not print the statement. So If I can somehow pass the button click of the real spawn button into this function then I will have done what I wanted to.


EDIT: More info

I tried renaming ifs_pc_SpawnSelect.Info.Ok (the spawn button) to ifs_pc_SpawnSelect.Info.NoWay. That made the game silently crash when you press spawn.
User avatar
Nedarb7
Lieutenant General
Lieutenant General
Posts: 676
Joined: Sat Sep 22, 2012 3:41 pm

Re: Squad system questions

Post by Nedarb7 »

I’ve messed around with the screen before as well, and I was barely able to change much on the screen, and changing things usually doesn’t go over well. That is why I suggested what I did. The added input accept function is totally functional as you saw, hence why I suggested a switch function rather than a new spawn button. I don’t know if it is possible to add to the spawn button, perhaps there is a way, but it appears to be hard coded
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

Yes its looking like much of it is hard coded which is unfortunate. It would be super inelegant but I suppose I could add a radio button "spawn on unit" next to the spawn button, with the Boolean as you suggested. I am going to keep looking for a bit but that is at least one way to do it. I wonder if I could add a dynamic number of mutually exclusive radio buttons to choose which unit to spawn on?
User avatar
Nedarb7
Lieutenant General
Lieutenant General
Posts: 676
Joined: Sat Sep 22, 2012 3:41 pm

Re: Squad system questions

Post by Nedarb7 »

Okay, if you do find a way to call the spawn function that’d be great. It’s something I’ve always wanted to work around.

As for your question that definitely sounds like something you could do
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

Well I found these function while looking through the callback functions

Code: Select all

ScriptCB_SetFunctionIdForButtonId
ScriptCB_GetFunctionIdForButtonId
I tried to use it like this in ifs_pc_SpawnSelect

Code: Select all

Input_Accept = function(this,bFromAI)
		print("pressed spawn button")
		spawnFunc = ScriptCB_GetFunctionIdForButtonId(this.Info.Ok)
		print(" spawn function is " .. tostring(spawnFunc))
		spawnFunc()
	end,
But spawnFunc was nil.

Do you have any ideas how to use them? They are used in ifs_opt_controller_vehunit.lua


EDIT: looks like that callback function is referring to the actual game buttons, not interface buttons
Last edited by MileHighGuy on Thu Apr 02, 2020 11:21 pm, edited 1 time in total.
User avatar
Nedarb7
Lieutenant General
Lieutenant General
Posts: 676
Joined: Sat Sep 22, 2012 3:41 pm

Re: Squad system questions

Post by Nedarb7 »

It looks like that has to do with controller settings, check out ifs_opt_controller_vehunit
User avatar
Teancum
Jedi Admin
Jedi Admin
Posts: 11080
Joined: Wed Sep 07, 2005 11:42 pm
Projects :: No Mod project currently.
Games I'm Playing :: Destiny
xbox live or psn: No gamertag set
Location: Indiana

Re: Squad system questions

Post by Teancum »

I'm not sure whether the buttons are raytraced, and if so whether the button click keeps going, but you could try adding a button in front of or behind the spawn button to see if you can capture the click of that button.
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

Interesting idea.

So in single player the human player's character index is always 0 (from GetCharacterUnit) . Is there any way to get your index if you join a server in multiplayer?

If I make it so you set a true/false variable to spawn on your squad then I also have to find some way to make sure that its actually you who set it.

EDIT: looks like you can use the version 1.3 function uf_processPlayers() to get a table of current players and their indices. And then you can check the current profile name with ScriptCB_GetCurrentProfileName (which must be wrapped with ScriptCB_ununicode() to get a readable string)

Currently I cant call uf_processPlayers because I am using a custom ingame.lvl to add my button. Once I figure that out I think I will have all the pieces I need!
That being said if you know why I cant call that function please let me know!

EDIT: Alright.. I think I got past that part. Now its just up to me to code the implementation we discussed

EDIT: it looks like I dont need to use uf_processPLayers, I can just use the ifs_mp_lobby_listbox_contents table that gets instantiated in multiplayer
Last edited by MileHighGuy on Tue Apr 07, 2020 2:20 pm, edited 1 time in total.
User avatar
Anakin
Master of the Force
Master of the Force
Posts: 4817
Joined: Sat Sep 19, 2009 11:37 am
Projects :: RC Side Mod - Remastered - SWBF3 Legacy
Location: Mos Espa (germany)

Re: Squad system questions

Post by Anakin »

So you can call a function that does the same as the spawn button press?

What i know so far, you can call all C functions from lua and you can call all lua functions from C. But you need to know the position in the memory. For convenient use, you set function names from c for the functions you wanna use in lua.
But this is experimental when i set up a example project with a newer version from lua. Worked very well. I was able to load additional c++ functions from a dll within lua and call lua scripts from c++. Problem, SWBF2 lua is old and not even from the old lua it has all functions available otherwise i were able to write my own c functions. But it gave me an idea of how SWBF2 works.

So C++ requires a lua data structure that contains that special button. And if it does not exists, it crashs (when you renamed it)
Teancum's idea should work fine, since the button press is very basic. The mouse cursor is tracked and for each movement the position is checked if it is over any element and sets some variables to the screen's table such as CurButton. So if you have an input accepted you just check if the mouse was on the button's position.

What works fine, too is, you can move the stock spawn button. So if you know the function that is called (from C++) when you press spawn, we can simply add a new button and move the old one out of screen.
MileHighGuy
Jedi
Jedi
Posts: 1194
Joined: Fri Dec 19, 2008 7:58 pm

Re: Squad system questions

Post by MileHighGuy »

I meant I was able to create a button to toggle whether I spawn on a unit or on a command post. I haven't been able to make another spawn button. The original spawn button doesn't seem to accept a tag to set "this.CurButton", but I could make a button that fit exactly over it that does.

EDIT: This code is what came of it

viewtopic.php?f=64&t=34466
Post Reply