Initial commit

This commit is contained in:
2026-03-08 14:18:29 -05:00
parent c360c027e1
commit 77490ae8df
18 changed files with 2037 additions and 1 deletions

View File

@@ -0,0 +1,27 @@
require("PGBase")
---@class ColorStruct
---@field R number The red component.
---@field G number The green component.
---@field B number The blue component.
ColorStruct = {}
ColorStruct.__index = ColorStruct
---Creates a new Color object.
---@param r number The red component
---@param g number The green component
---@param b number The blue component
---@return ColorStruct
function ColorStruct:New(r, g, b)
local self = setmetatable({}, ColorStruct)
self.R = Clamp(r, 0, 1)
self.G = Clamp(g, 0, 1)
self.B = Clamp(b, 0, 1)
return self
end
function ColorStruct:Debug()
return string.format("RGB (%d, %d, %d)", self.R, self.G, self.B)
end

View File

@@ -0,0 +1,105 @@
require("FactionStruct")
-- ==================================================
-- Define Faction constants
-- ==================================================
---Defines tactical factions.
---@return FactionStruct[]
function DefineFactions()
---@type FactionStruct[]
local factions = {}
---Rebel Faction
---@type FactionStruct
local RebelFaction = FactionStruct:New("REBEL")
RebelFaction.DisplayName = "Rebellion"
RebelFaction.Color = ColorStruct:New(1, 0, 0)
RebelFaction.Icon = "I_ICON_SPECTATOR_REBEL.TGA"
RebelFaction.LandStartUnitName = "Rebel_Infantry_Squad"
RebelFaction.SpaceStartUnitName = "Rebel_X-Wing_Squadron"
factions[RebelFaction.Name] = RebelFaction
---Empire Faction
---@type FactionStruct
local EmpireFaction = FactionStruct:New("EMPIRE")
EmpireFaction.DisplayName = "Empire"
EmpireFaction.Color = ColorStruct:New(0, 1, 1)
EmpireFaction.Icon = "I_ICON_SPECTATOR_EMPIRE.TGA"
EmpireFaction.LandStartUnitName = "Imperial_Stormtrooper_Squad"
EmpireFaction.SpaceStartUnitName = "TIE_Interceptor_Squadron_Container"
factions[EmpireFaction.Name] = EmpireFaction
---Underworld Faction
---@type FactionStruct
local UnderworldFaction = FactionStruct:New("UNDERWORLD")
UnderworldFaction.DisplayName = "Zann Consortium"
UnderworldFaction.Color = ColorStruct:New(1, 1, 0)
UnderworldFaction.Icon = "I_ICON_SPECTATOR_UNDERWORLD.TGA"
UnderworldFaction.LandStartUnitName = "Underworld_Merc_Squad"
UnderworldFaction.SpaceStartUnitName = "StarViper_Team"
factions[UnderworldFaction.Name] = UnderworldFaction
---Hutts Faction
local HuttFaction = FactionStruct:New("HUTTS")
HuttFaction.DisplayName = "Hutt Cartel"
HuttFaction.Color = ColorStruct:New(1, 0, 1)
HuttFaction.Icon = "I_ICON_SPECTATOR_HUTTS.TGA"
HuttFaction.LandStartUnitName = "Hutt_Soldier_Squad"
HuttFaction.SpaceStartUnitName = "V_Wing_Squadron_Container"
factions[HuttFaction.Name] = HuttFaction
---Pirates Faction
local PirateFaction = FactionStruct:New("PIRATES")
PirateFaction.DisplayName = "Pirates"
PirateFaction.Color = ColorStruct:New(0, 1, 0)
PirateFaction.Icon = "I_ICON_SPECTATOR_PIRATES.TGA"
PirateFaction.LandStartUnitName = "Pirate_Soldier_Squad"
PirateFaction.SpaceStartUnitName = "Pirate_Fighter_Squadron"
factions[PirateFaction.Name] = PirateFaction
return factions
end
---Constant Global of all Tactical Factions
---@type FactionStruct[]
Factions = DefineFactions()
-- ==================================================
-- Define UI Constants
-- ==================================================
---GUI Component to display game time.
UI_GameTime = "s_select_41"
---GUI Components to display Team 1 credits.
UI_IconT1 = "s_health_42"
UI_CreditsT1 = "s_health_41"
---GUI Component to display Team 2 credits.
UI_IconT2 = "s_shield_42"
UI_CreditsT2 = "s_shield_41"
---GUI Component to enable/disable population.
UI_PlanetaryPop = "text_planetary_pop"
---GUI Component to enable/disable tactical credits.
UI_CreditsTactical = "text_credits_tactical"
---Gets the specator marker for the game mode.
---@param gameMode string Game mode.
---@return string|nil
function GetSpectatorMarkerName(gameMode)
if StringCompare(gameMode, "Space") then
return "Spectator_Reveal_Marker"
elseif StringCompare(gameMode, "Land") then
return "Spectator_Reveal_Marker_Land"
end
return nil
end

View File

@@ -0,0 +1,31 @@
require("ColorStruct")
---@class FactionStruct
---@field Name string Faction name.
---@field DisplayName string Display name.
---@field Color ColorStruct Color.
---@field Icon string Icon name.
---@field LandStartUnitName string Land starting unit name.
---@field SpaceStartUnitName string Space starting unit name.
FactionStruct = {}
FactionStruct.__index = FactionStruct
---Creates a new Faction struct.
---@param name string The internal faction name.
---@return FactionStruct
function FactionStruct:New(name)
local self = setmetatable({}, FactionStruct)
self.Name = name
self.DisplayName = name
self.Color = ColorStruct:New(1, 1, 1)
self.Icon = ""
self.LandStartUnitName = ""
self.SpaceStartUnitName = ""
return self
end
function FactionStruct:Debug()
return string.format("Faction %s (%s)", self.DisplayName, self.Name)
end

View File

@@ -0,0 +1,194 @@
require("PGBase")
require("GameManager.Constants")
---@class GUIManager
---@field TacticalGame TacticalGameClass|nil The local Tactical Game state.
---@field SkirmishGame SkirmishGameClass|nil The local Skirmish Game state.
---@field ShowTeamId integer The Team ID units to display.
GUIManager = {}
GUIManager.__index = GUIManager
---Creates a new GUI Manager.
---@return GUIManager
function GUIManager:New()
local self = setmetatable({}, GUIManager)
self.TacticalGame = nil
self.SkirmishGame = nil
self.ShowTeamId = 0
return self
end
---Initializes the GUI Manager for Skirmish mode.
---@param game SkirmishGameClass The Skirmish Game state.
function GUIManager:InitSkirmish(game)
self.SkirmishGame = game
self.TacticalGame = game.TacticalGame
GUI_Component_Visibility(UI_GameTime, true)
if self.SkirmishGame.IsLocalSpectator then
GUI_Component_Visibility(UI_CreditsT1, true)
GUI_Component_Visibility(UI_IconT1, true)
GUI_Component_Visibility(UI_CreditsT2, true)
GUI_Component_Visibility(UI_IconT2, true)
GUI_Component_Visibility(UI_PlanetaryPop, false)
GUI_Component_Visibility(UI_CreditsTactical, false)
end
end
---Initializes the GUI Manager for Tactical mode.
---@param game TacticalGameClass The Tactical Game state.
function GUIManager:InitTactical(game)
self.TacticalGame = game
GUI_Component_Visibility(UI_GameTime, true)
end
---Resets the GUI Manager.
function GUIManager:Reset()
self.SkirmishGame = nil
self.TacticalGame = nil
GUI_Component_Text(UI_GameTime, "")
GUI_Text_Color(UI_GameTime, 1, 1, 1, 1)
GUI_Component_Visibility(UI_GameTime, false)
GUI_Component_Text(UI_CreditsT1, "")
GUI_Text_Color(UI_CreditsT1, 1, 1, 1, 1)
GUI_Component_Visibility(UI_CreditsT1, false)
GUI_Button_Icon(UI_IconT1, "", 1, 1, 1, 1)
GUI_Component_Visibility(UI_IconT1, false)
GUI_Component_Text(UI_CreditsT2, "")
GUI_Text_Color(UI_CreditsT2, 1, 1, 1, 1)
GUI_Component_Visibility(UI_CreditsT2, false)
GUI_Button_Icon(UI_IconT2, "", 1, 1, 1, 1)
GUI_Component_Visibility(UI_IconT2, false)
GUI_Component_Visibility(UI_PlanetaryPop, true)
GUI_Component_Visibility(UI_CreditsTactical, true)
end
---Services the GUI Manager.
function GUIManager:Service()
if not self.TacticalGame then
-- Nothing to service
return
end
ScriptMessage("Servicing GUI Updates...")
ServiceGameTime(self.TacticalGame)
ServiceTeamCredits(self.SkirmishGame)
--ServiceUnitDisplay(self.SkirmishGame, self.ShowTeamId)
end
---Services Game Time display.
---@param tacticalGame TacticalGameClass The current Tactical Game state
function ServiceGameTime(tacticalGame)
if not tacticalGame then
return
end
local gameTime = Dirty_Floor(GetCurrentTime()) - tacticalGame.StartTime
local minutes = Dirty_Floor(gameTime / 60)
local minutesText = tostring(minutes)
if tonumber(minutes) < 10 then
minutesText = "0" .. minutesText
end
local seconds = gameTime - (minutes * 60)
local secondsText = tostring(seconds)
if tonumber(seconds) < 10 then
secondsText = "0" .. secondsText
end
local text = string.format("Time: %s:%s", minutesText, secondsText)
GUI_Component_Text(UI_GameTime, text)
end
---Services Team Credits display.
---@param skirmishGame SkirmishGameClass The current Skirmish Game state.
function ServiceTeamCredits(skirmishGame)
if not skirmishGame then
return
end
if not skirmishGame.IsLocalSpectator then
return
end
---Displays credits for the specified team
---@param team TeamStruct The team to service.
---@param uiText string The name of the UI Component to display Team credits in.
---@param uiIcon string The name of the UI Component to display the Faction icon in.
local function DisplayTeamCredits(team, uiText, uiIcon)
local teamCredits = skirmishGame:CalculateTeamTotalCredits(team.Id)
local teamIncome = skirmishGame:CalculateTeamTotalIncome(team.Id)
local positiveText = "+"
if teamIncome < 0 then
positiveText = ""
end
local text = string.format("Team %d: $%d (%s%d)", team.Number, teamCredits, positiveText, teamIncome)
local icon = team.Faction.Icon
local color = team.Faction.Color
GUI_Button_Icon(uiIcon, icon, color.R, color.G, color.B, 1)
GUI_Component_Text(uiText, text)
GUI_Text_Color(uiText, color.R, color.G, color.B, 1)
end
for _, team in pairs(skirmishGame.Teams) do
if team.Number == 1 then
DisplayTeamCredits(team, UI_CreditsT1, UI_IconT1)
elseif team.Number == 2 then
DisplayTeamCredits(team, UI_CreditsT2, UI_IconT2)
end
end
end
---Services Team Units display.
---@param skirmishGame SkirmishGameClass The current Skirmish Game state.
---@param teamId integer The Team ID's units to display.
function ServiceUnitDisplay(skirmishGame, teamId)
if not skirmishGame then
return
end
if not skirmishGame.IsLocalSpectator then
return
end
local function DisplayTeamUnit(unit, uiComponent)
end
local localPlayer = Find_Player("local")
local players = skirmishGame:GetPlayersOnTeam(teamId)
---@type table<string, integer> Units Table by name with alive count.
local units = {}
for _, player in pairs(players) do
local playerUnits = player:GetAliveUnits()
for _, playerUnit in pairs(playerUnits) do
localPlayer.Select_Object(playerUnit.GameObjectWrapper)
if not units[playerUnit.Name] then
units[playerUnit.Name] = 1
else
units[playerUnit.Name] = units[playerUnit.Name] + 1
end
end
end
end

View File

@@ -0,0 +1,73 @@
require("PGBase")
require("GameManager.PlayerClass")
require("GameManager.UnitClass")
---@class GalacticGameClass
---@field StartTime integer The start time.
---@field Players PlayerClass[] The combatant players in this game.
---@field Units UnitClass[] The units in this game.
GalacticGameClass = {}
GalacticGameClass.__index = GalacticGameClass
---Creates a new Galactic Game context.
---@return GalacticGameClass
function GalacticGameClass:New()
ScriptMessage("Initializing Galactic game...")
local self = setmetatable({}, GalacticGameClass)
---Finds all combatant players in the game.
---@return PlayerClass[]
local function FindPlayers()
return {}
end
self.StartTime = GetCurrentTime()
self.Players = FindPlayers()
self.Units = {}
ScriptMessage("Galactic Game initialized!")
return self
end
---Services the Galactic Game.
function GalacticGameClass:Service()
ScriptMessage("Servicing Galactic game...")
-- Service Players
for playerId, player in pairs(self.Players) do
player:Service()
end
end
---Gets the Player by ID.
---@param id integer Player ID
---@return PlayerClass|nil
function GalacticGameClass:GetPlayer(id)
for playerId, player in pairs(self.Players) do
if playerId == id then
return player
end
end
end
---Records the Player ID as having quit at the current time.
---@param id integer Player ID.
function GalacticGameClass:PlayerQuit(id)
for playerId, player in pairs(self.Players) do
if playerId == id then
player:Quit()
end
end
end
---Adds a built unit to the Unit table.
---@param objectType table The FoC GameObjectTypeWrapper object type that was built.
---@param player table The FoC PlayerWrapper object that owns the new Unit.
function GalacticGameClass:UnitBuilt(objectType, player)
local playerId = player.Get_ID()
local player = self.Players[playerId]
if player then
player:AddUnit(objectType)
end
end

View File

@@ -0,0 +1,123 @@
require("PGBase")
require("GameManager.ResourceClass")
require("GameManager.UnitClass")
---@class PlayerClass
---@field Id integer Player ID.
---@field TeamId integer Team ID.
---@field Name string Player name.
---@field Faction string Faction name.
---@field IsHuman boolean Whether this player is a human.
---@field QuitTime integer The game time this player quit, or -1 if they are still in the game.
---@field Resource ResourceClass The player's resources.
---@field PlayerWrapper table The FoC PlayerWrapper object.
---@field Units UnitClass[] The Units belonging to this player.
PlayerClass = {}
PlayerClass.__index = PlayerClass
---Constructs a new Player object.
---@param player table FoC PlayerWrapper object
---@return PlayerClass
function PlayerClass:New(player)
local self = setmetatable({}, PlayerClass)
self.Id = player.Get_ID()
self.TeamId = player.Get_Team()
self.Name = player.Get_Name()
self.Faction = player.Get_Faction_Name()
self.IsHuman = player.Is_Human()
self.QuitTime = -1
self.Resource = ResourceClass:New()
self.PlayerWrapper = player
self.Units = {}
return self
end
---Updates credits and income.
function PlayerClass:Service()
if not self:HasQuit() then
-- Update player resources
self.Resource:SetCredits(self.PlayerWrapper.Get_Credits())
-- Service Alive Units
local assignedUnits = {}
-- Pass 1: Collect assigned game object wrappers from alive units
for _, unit in pairs(self:GetAliveUnits()) do
if TestValid(unit.GameObjectWrapper) then
assignedUnits[unit.GameObjectWrapper] = true
else
unit:SetDead()
end
end
-- Pass 2: Assign unclaimed wrappers to reinforcement units
for k, unit in pairs(self:GetReinforcementUnits()) do
local matchingUnits = Find_All_Objects_Of_Type(unit.Name, self.PlayerWrapper)
for _, object in pairs(matchingUnits) do
if not assignedUnits[object] then
unit:SetAlive(object)
assignedUnits[object] = true
break
end
end
end
end
end
---Sets that the player has quit.
function PlayerClass:Quit()
self.QuitTime = GetCurrentTime()
end
---Whether this player has quit the game.
---@return boolean
function PlayerClass:HasQuit()
return self.QuitTime >= 0
end
---Adds a Unit type to the Player's reinforcement list.
---@param objectType table FoC GameObjectTypeWrapper object that was built.
function PlayerClass:AddUnit(objectType)
local unit = UnitClass:New(objectType, self.Id)
table.insert(self.Units, unit)
ScriptMessage("Unit Added: %s", unit:Debug())
end
---Gets all Reinforcement Units for this Player.
---@return UnitClass[]
function PlayerClass:GetReinforcementUnits()
---@type UnitClass[]
local units = {}
for k, unit in pairs(self.Units) do
if unit:IsReinforcement() then
table.insert(units, unit)
end
end
return units
end
---Gets all Alive Units for this Player.
---@return UnitClass[]
function PlayerClass:GetAliveUnits()
---@type UnitClass[]
local units = {}
for k, unit in pairs(self.Units) do
if unit:IsAlive() then
table.insert(units, unit)
end
end
return units
end
function PlayerClass:Debug()
return string.format("Player %s (%d) on Faction %s, Team %d", self.Name, self.Id, self.Faction, self.TeamId)
end

View File

@@ -0,0 +1,36 @@
---@class ResourceClass
---@field Credits number The current credits.
---@field LastCredits number The credits on the last frame.
---@field Income integer The current income.
ResourceClass = {}
ResourceClass.__index = ResourceClass
---Constructs a new Resource object.
function ResourceClass:New()
local self = setmetatable({}, ResourceClass)
self.Credits = 0
self.LastCredits = 0
self.Income = 0
return self
end
---Sets the current credits and updates income.
---@param credits number
function ResourceClass:SetCredits(credits)
self.LastCredits = self.Credits
self.Credits = credits
if credits < 0 then
self.Credits = 0
else
self.Credits = credits
end
self.Income = self.Credits - self.LastCredits
end
function ResourceClass:Debug()
return string.format("Credits: %d, Income: %d", self.Credits, self.Income)
end

View File

@@ -0,0 +1,173 @@
require("PGBase")
require("GameManager.Constants")
require("GameManager.TacticalGameClass")
require("GameManager.SpectatorStruct")
require("GameManager.TeamStruct")
---@class SkirmishGameClass
---@field TacticalGame TacticalGameClass The underlying Tactical Game context.
---@field Spectators SpectatorStruct[] The spectator players in this game.
---@field IsLocalSpectator boolean Whether the local player is a spectator.
---@field Teams TeamStruct[] The teams in this game.
SkirmishGameClass = {}
SkirmishGameClass.__index = SkirmishGameClass
---Creates a new Skirmish Game context.
---@param gameMode string The game mode.
---@return SkirmishGameClass
function SkirmishGameClass:New(gameMode)
ScriptMessage("Initializing Skirmish game...")
local self = setmetatable({}, SkirmishGameClass)
---Finds all spectators in the game.
---@return SpectatorStruct[]
local function FindSpectators()
---@type SpectatorStruct[]
local spectators = {}
local spectatorMarker = GetSpectatorMarkerName(gameMode)
if spectatorMarker ~= nil then
for k, marker in pairs(Find_All_Objects_Of_Type(spectatorMarker)) do
local playerWrapper = marker.Get_Owner()
local playerId = playerWrapper.Get_ID()
if not spectators[playerId] then
local spectator = SpectatorStruct:New(playerWrapper)
spectators[playerId] = spectator
ScriptMessage("Found Spectator: %s", spectator:Debug())
end
end
end
return spectators
end
self.TacticalGame = TacticalGameClass:New(gameMode)
self.Spectators = FindSpectators()
---Gets whether the local player is a spectator.
---@returns boolean
local function GetLocalSpectator()
local localPlayer = Find_Player("local")
return self:GetSpectator(localPlayer.Get_ID()) ~= nil
end
self.IsLocalSpectator = GetLocalSpectator()
---Builds all teams in the game.
---@return TeamStruct[]
local function BuildTeams()
---@type TeamStruct[]
local teams = {}
-- Build combatant player teams
for playerId, player in pairs(self.TacticalGame.Players) do
local teamId = player.TeamId
if teamId >= 0 then
if teams[teamId] == nil then
teams[teamId] = TeamStruct:New(teamId, player.Faction, false)
end
end
end
-- Build spectator player teams
for playerId, spectator in pairs(self.Spectators) do
local teamId = spectator.TeamId
if teamId >= 0 then
local team = teams[teamId]
if team == nil then
team = TeamStruct:New(teamId, spectator.Faction, true)
teams[teamId] = team
ScriptMessage("Found Team: %s", team:Debug())
else
team.IsSpectator = true
end
end
end
return teams
end
self.Teams = BuildTeams()
ScriptMessage("Skirmish Game initialized!")
return self
end
---Services the Skirmish Game.
function SkirmishGameClass:Service()
ScriptMessage("Servicing Skirmish Game...")
-- Service TacticalGame
self.TacticalGame:Service()
end
---Gets the Spectator by Player ID.
---@param id integer Player ID
---@return SpectatorStruct|nil
function SkirmishGameClass:GetSpectator(id)
for playerId, spectator in pairs(self.Spectators) do
if playerId == id then
return spectator
end
end
end
---Gets the Team by ID.
---@param id integer Team ID
---@return TeamStruct|nil
function SkirmishGameClass:GetTeam(id)
for teamId, team in pairs(self.Teams) do
if teamId == id then
return team
end
end
end
---Gets the Players for the Team ID.
---@param id integer Team ID
---@return PlayerClass[]
function SkirmishGameClass:GetPlayersOnTeam(id)
---@type PlayerClass[]
local players = {}
for playerId, player in pairs(self.TacticalGame.Players) do
if player.TeamId == id then
players[playerId] = player
end
end
return players
end
---Calculates the Team ID's current total credits.
---@param id integer Team ID.
---@return integer
function SkirmishGameClass:CalculateTeamTotalCredits(id)
local players = self:GetPlayersOnTeam(id)
local totalCredits = 0
for playerId, player in pairs(players) do
totalCredits = totalCredits + player.Resource.Credits
end
return totalCredits
end
---Calculates the Team ID's current total income.
---@param id integer Team ID.
---@return integer
function SkirmishGameClass:CalculateTeamTotalIncome(id)
local players = self:GetPlayersOnTeam(id)
local totalIncome = 0
for playerId, player in pairs(players) do
totalIncome = totalIncome + player.Resource.Income
end
return totalIncome
end

View File

@@ -0,0 +1,26 @@
---@class SpectatorStruct
---@field Id integer Player ID.
---@field TeamId integer Team ID.
---@field Name string Player name.
---@field Faction string Faction name.
---@field PlayerWrapper table The FoC PlayerWrapper object.
SpectatorStruct = {}
SpectatorStruct.__index = SpectatorStruct
---Constructs a new Spectator object.
---@param wrapper table FoC PlayerWrapper
function SpectatorStruct:New(wrapper)
local self = setmetatable({}, SpectatorStruct)
self.Id = wrapper.Get_ID()
self.TeamId = wrapper.Get_Team()
self.Name = wrapper.Get_Name()
self.Faction = wrapper.Get_Faction_Name()
self.PlayerWrapper = wrapper
return self
end
function SpectatorStruct:Debug()
return string.format("Spectator %s (%d) on Faction %s, Team %d", self.Name, self.Id, self.Faction, self.TeamId)
end

View File

@@ -0,0 +1,120 @@
require("PGBase")
require("GameManager.PlayerClass")
---@class TacticalGameClass
---@field GameMode string The tactical game mode.
---@field StartTime integer The start time.
---@field Players PlayerClass[] The combatant players in this game.
---@field ReinforcementUnitCount table<string, integer> Key: Unit Type, Value: Count
---@field AliveUnitCount table<string, integer> Key: Unit Type, Value: Count
---@field DeadUnitCount table<string, integer> Key: Unit Type, Value: Count
TacticalGameClass = {}
TacticalGameClass.__index = TacticalGameClass
---Creates a new Tactical Game context.
---@param gameMode string The game mode.
---@return TacticalGameClass
function TacticalGameClass:New(gameMode)
ScriptMessage("Initializing Tactical game...")
local self = setmetatable({}, TacticalGameClass)
---Finds all combatant players in the game.
---@return PlayerClass[]
local function FindPlayers()
---@type PlayerClass[]
local players = {}
for k, faction in pairs(Factions) do
local startingUnit = nil
if StringCompare(gameMode, "Land") then
startingUnit = faction.LandStartUnitName
elseif StringCompare(gameMode, "Space") then
startingUnit = faction.SpaceStartUnitName
end
if startingUnit then
for i, unit in pairs(Find_All_Objects_Of_Type(startingUnit)) do
local playerWrapper = unit.Get_Owner()
local playerId = playerWrapper.Get_ID()
if players[playerId] == nil then
local player = PlayerClass:New(playerWrapper)
players[playerId] = player
ScriptMessage("Found Player: %s", player:Debug())
end
end
end
end
return players
end
self.GameMode = gameMode
self.StartTime = GetCurrentTime()
self.Players = FindPlayers()
ScriptMessage("Tactical Game initialized!")
return self
end
---Services the Tactical Game.
function TacticalGameClass:Service()
ScriptMessage("Servicing Tactical Game...")
-- Service Players
for _, player in pairs(self.Players) do
player:Service()
end
end
---Whether this Tactical game is in space mode.
---@return boolean
function TacticalGameClass:IsSpaceMode()
return StringCompare(self.GameMode, "Space")
end
---Whether this Tactical game is in land mode.
function TacticalGameClass:IsLandMode()
return StringCompare(self.GameMode, "Land")
end
---Gets the Player by ID.
---@param id integer Player ID
---@return PlayerClass|nil
function TacticalGameClass:GetPlayer(id)
for playerId, player in pairs(self.Players) do
if playerId == id then
return player
end
end
end
---Records the Player ID as having quit at the current time.
---@param id integer Player ID.
function TacticalGameClass:PlayerQuit(id)
for playerId, player in pairs(self.Players) do
if playerId == id then
player:Quit()
end
end
end
---Adds a built unit to the Unit table.
---@param objectType table The FoC GameObjectTypeWrapper object type that was built.
---@param player table The FoC PlayerWrapper object that owns the new Unit.
function TacticalGameClass:UnitBuilt(objectType, player)
local playerId = player.Get_ID()
local player = self.Players[playerId]
if player then
player:AddUnit(objectType)
end
end
---Sets a unit as killed.
---@param gameObject table The FoC GameObjectWrapper object that was killed.
function TacticalGameClass:UnitKilled(gameObject)
-- Find a way to set the unit as killed
end

View File

@@ -0,0 +1,28 @@
require("GameManager.Constants")
---@class TeamStruct
---@field Id integer Team ID.
---@field Number integer Team number.
---@field Faction FactionStruct Faction struct for this team.
---@field IsSpectator boolean Whether this team's players are spectators.
TeamStruct = {}
TeamStruct.__index = TeamStruct
---Constructs a new Team object.
---@param id integer Team ID
---@param faction string Faction name
---@param isSpectator boolean Is a Spectator team
function TeamStruct:New(id, faction, isSpectator)
local self = setmetatable({}, TeamStruct)
self.Id = id
self.Number = id + 1
self.Faction = Factions[faction]
self.IsSpectator = isSpectator
return self
end
function TeamStruct:Debug()
return string.format("Team %d: %s (%d)", self.Number, self.Faction.Name, self.Id)
end

View File

@@ -0,0 +1,89 @@
require("PGBase")
---@class UnitClass
---@field Name string Unit name.
---@field OwnerId integer Owner Player ID.
---@field BuildCost integer Tactical build cost.
---@field CombatRating integer AI combat power rating.
---@field BuildTime number Game time that this unit was built.
---@field AliveTime number Game time that this unit became "alive".
---@field DeathTime number Game time that this unit was killed.
---@field GameObjectWrapper table|nil The FoC GameObjectWrapper object.
---@field GameObjectTypeWrapper table The FoC GameObjectTypeWrapper object.
---@field Icon string Name of the icon for this object.
UnitClass = {}
UnitClass.__index = UnitClass
---Constructs a new Unit object.
---@param objectType table FoC GameObjectTypeWrapper object
---@param playerId integer The Owner Player ID
function UnitClass:New(objectType, playerId)
local self = setmetatable({}, UnitClass)
self.Name = objectType.Get_Name()
self.OwnerId = playerId
self.BuildCost = objectType.Get_Tactical_Build_Cost()
self.CombatRating = objectType.Get_Combat_Rating()
self.BuildTime = GetCurrentTime()
self.AliveTime = -1
self.DeathTime = -1
self.GameObjectWrapper = nil
self.GameObjectTypeWrapper = objectType
self.Icon = "I_BUTTON_COMMAND_BAR_PIRATE_SYMBOL.TGA"
return self
end
---Whether this Unit is a reinforcement.
---@return boolean
function UnitClass:IsReinforcement()
return self.AliveTime < 0 and self.DeathTime < 0
end
---Whether this Unit is currently alive.
---@return boolean
function UnitClass:IsAlive()
return self.AliveTime >= 0 and self.DeathTime < 0
end
---Whether this Unit is dead.
---@return boolean
function UnitClass:IsDead()
return self.DeathTime >= 0
end
---Sets the time this unit became "alive".
---@param object table FoC GameObjectWrapper object
function UnitClass:SetAlive(object)
if self.AliveTime >= 0 then
-- Do not override once set
return
end
self.AliveTime = GetCurrentTime()
self.GameObjectWrapper = object
ScriptMessage("Unit Spawned: %s", self:Debug())
end
---Sets the unit as dead.
function UnitClass:SetDead()
if self.DeathTime >= 0 then
-- Do not override once set
return
end
self.DeathTime = GetCurrentTime()
ScriptMessage("Unit Killed: %s", self:Debug())
end
function UnitClass:Debug()
if self:IsReinforcement() then
return string.format("Reinforcement Unit %s, Owned by %d, Built at: %d", self.Name, self.OwnerId, self.BuildTime)
elseif self:IsAlive() then
return string.format("Alive Unit %s, Owned by %d, Alive at: %d", self.Name, self.OwnerId, self.AliveTime)
elseif self:IsDead() then
return string.format("Dead Unit %s, Owned by %d, Death at: %d", self.Name, self.OwnerId, self.DeathTime)
else
return string.format("Unit %s, Owned by %d, Unknown state", self.Name, self.OwnerId)
end
end