require("PGBase") require("GameManager/Constants") require("GameManager/PlayerClass") require("GameManager/TutorialTextFeed") ---@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 CombatFeed TutorialTextFeed Text feed for unit builds and kills. 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) self.GameMode = gameMode self.StartTime = GetCurrentTime() self.Players = self:_FindPlayers(gameMode) self.CombatFeed = TutorialTextFeed:New("combat_feed", 6) 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 local gameTime = GetCurrentTime() - self.StartTime GUIManager:DisplayGameTime(gameTime) end -- ================================================== -- State functions -- ================================================== ---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 -- ================================================== -- Player functions -- ================================================== ---Gets the Player by ID. ---@param id integer Player ID ---@return PlayerClass|nil function TacticalGameClass:GetPlayer(id) return self.Players[id] end ---Records the Player ID as having quit at the current time. ---@param id integer Player ID. function TacticalGameClass:PlayerQuit(id) local player = self.Players[id] if player then player:Quit() end end -- ================================================== -- Unit event handlers -- ================================================== ---Adds a built unit to the Unit table. ---@param objectType GameObjectType The FoC GameObjectTypeWrapper object type that was built. ---@param player PlayerObject The FoC PlayerWrapper object that owns the new Unit. function TacticalGameClass:UnitBuilt(objectType, player) local playerId = player.Get_ID() local playerEntry = self.Players[playerId] if playerEntry then local unit = playerEntry:AddUnit(objectType) self:CombatTextUnitBuilt(unit) end end ---Displays unit built combat text. ---@param unit UnitClass The built unit. function TacticalGameClass:CombatTextUnitBuilt(unit) local owner = self.Players[unit.OwnerId] if not owner then return end if not StringCompare(LocalPlayer.Get_Faction_Name(), owner.Faction) then -- Do not display combat feed for players on opposite factions return end local timeText = FormatTime(unit.BuildTime) local text = string.format("%s\t[%s]: BUILT %s ($%d)", timeText, owner.Name, unit.Name, unit.BuildCost) local faction = Factions[owner.Faction] ---@type ColorStruct local color if faction then color = faction.Color else color = ColorStruct:New(1, 1, 1, 1) end self.CombatFeed:Append(text, -1, color) end ---Sets a unit as killed. ---@param gameObject GameObject The FoC GameObjectWrapper object that was killed. ---@param killer PlayerObject The FoC PlayerWrapper object that killed the Unit. function TacticalGameClass:UnitKilled(gameObject, killer) local ownerId = gameObject.Get_Owner().Get_ID() local owner = self.Players[ownerId] if not owner then return end ---@type UnitClass|nil local killedUnit = nil for _, unit in pairs(owner.Units) do if unit.GameObject == gameObject then killedUnit = unit break end end if killedUnit then killedUnit:SetDead(killer.Get_ID()) self:CombatTextUnitKilled(killedUnit) end end ---Displays killed unit combat text. ---@param unit UnitClass The killed unit. function TacticalGameClass:CombatTextUnitKilled(unit) local owner = self.Players[unit.OwnerId] if not owner then return end if not StringCompare(LocalPlayer.Get_Faction_Name(), owner.Faction) then -- Do not display combat feed for players on opposite factions return end local killer = self.Players[unit.KillerId] local timeText = FormatTime(unit.DeathTime) if not killer then local text = string.format("%s\t[%s]: MISTAKES WERE MADE %s", timeText, owner.Name, unit.Name) local color = ColorStruct:New(0.8, 0.8, 0.8, 1) self.CombatFeed:Append(text, -1, color) else local text = string.format("%s\t[%s]: KILLED %s (%s)", timeText, killer.Name, unit.Name, owner.Name) local faction = Factions[killer.Faction] ---@type ColorStruct local color = nil if faction then color = faction.Color else color = ColorStruct:New(1, 1, 1, 1) end self.CombatFeed:Append(text, -1, color) end end -- ================================================== -- Private functions -- ================================================== ---@private ---@param gameMode string The current game mode. ---@return PlayerClass[] function TacticalGameClass:_FindPlayers(gameMode) ---@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