diff --git a/Data/Scripts/Miscellaneous/GameManager/Constants.lua b/Data/Scripts/Miscellaneous/GameManager/Constants.lua index fe24489..2811ab2 100644 --- a/Data/Scripts/Miscellaneous/GameManager/Constants.lua +++ b/Data/Scripts/Miscellaneous/GameManager/Constants.lua @@ -94,6 +94,31 @@ end ---@type FactionStruct[] Factions = DefineFactions() +---Faction Title Rank table. Rebel at 2, Empire at 3 +---@type table +FactionTitleTable = { + { 145000, "TEXT_REBEL_TITLE19", "TEXT_EMPIRE_TITLE19" }, + { 125000, "TEXT_REBEL_TITLE18", "TEXT_EMPIRE_TITLE18" }, + { 115000, "TEXT_REBEL_TITLE17", "TEXT_EMPIRE_TITLE17" }, + { 100000, "TEXT_REBEL_TITLE16", "TEXT_EMPIRE_TITLE16" }, + { 90000, "TEXT_REBEL_TITLE15", "TEXT_EMPIRE_TITLE15" }, + { 85000, "TEXT_REBEL_TITLE14", "TEXT_EMPIRE_TITLE14" }, + { 80000, "TEXT_REBEL_TITLE13", "TEXT_EMPIRE_TITLE13" }, + { 75000, "TEXT_REBEL_TITLE12", "TEXT_EMPIRE_TITLE12" }, + { 70000, "TEXT_REBEL_TITLE11", "TEXT_EMPIRE_TITLE11" }, + { 60000, "TEXT_REBEL_TITLE10", "TEXT_EMPIRE_TITLE10" }, + { 55000, "TEXT_REBEL_TITLE9", "TEXT_EMPIRE_TITLE9" }, + { 50000, "TEXT_REBEL_TITLE8", "TEXT_EMPIRE_TITLE8" }, + { 45000, "TEXT_REBEL_TITLE7", "TEXT_EMPIRE_TITLE7" }, + { 40000, "TEXT_REBEL_TITLE6", "TEXT_EMPIRE_TITLE6" }, + { 25000, "TEXT_REBEL_TITLE5", "TEXT_EMPIRE_TITLE5" }, + { 20000, "TEXT_REBEL_TITLE4", "TEXT_EMPIRE_TITLE4" }, + { 15000, "TEXT_REBEL_TITLE3", "TEXT_EMPIRE_TITLE3" }, + { 10000, "TEXT_REBEL_TITLE2", "TEXT_EMPIRE_TITLE2" }, + { 5000, "TEXT_REBEL_TITLE1", "TEXT_EMPIRE_TITLE1" }, + { 0, "TEXT_REBEL_TITLE0", "TEXT_EMPIRE_TITLE0" } +} + -- ================================================== -- Define Spectator Constants -- ================================================== diff --git a/Data/Scripts/Miscellaneous/GameManager/GalacticGameClass.lua b/Data/Scripts/Miscellaneous/GameManager/GalacticGameClass.lua index 2457b7e..1ea0494 100644 --- a/Data/Scripts/Miscellaneous/GameManager/GalacticGameClass.lua +++ b/Data/Scripts/Miscellaneous/GameManager/GalacticGameClass.lua @@ -5,7 +5,10 @@ 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. +---@field BuildStatsTable table The engine-owned GalacticBuildStatsTable reference, set from GameScoring.lua. +---@field KillStatsTable table The engine-owned GalacticKillStatsTable reference, set from GameScoring.lua. +---@field NeutralizedStatsTable table The engine-owned GalacticNeutralizedTable reference, set from GameScoring.lua. +---@field GalacticConquestTable table The engine-owned GalacticConquestTable reference, set from GameScoring.lua. GalacticGameClass = {} GalacticGameClass.__index = GalacticGameClass @@ -15,15 +18,8 @@ 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 = {} + self.Players = self:_FindPlayers() ScriptMessage("Galactic Game initialized!") return self @@ -34,11 +30,15 @@ function GalacticGameClass:Service() ScriptMessage("Servicing Galactic game...") -- Service Players - for playerId, player in pairs(self.Players) do + for _, player in pairs(self.Players) do player:Service() end end +-- ================================================== +-- Player event handlers +-- ================================================== + ---Gets the Player by ID. ---@param id integer Player ID ---@return PlayerClass|nil @@ -60,14 +60,263 @@ function GalacticGameClass:PlayerQuit(id) end end +-- ================================================== +-- Unit event handlers +-- ================================================== + ---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) +---@param objectType GameObjectType The game object type that was built. +---@param player PlayerObject The player that owns the new game object. +---@param planet PlanetObject The planet the game object type was built at. +function GalacticGameClass:UnitBuilt(objectType, player, planet) local playerId = player.Get_ID() local playerEntry = self.Players[playerId] if playerEntry then - playerEntry:AddUnit(objectType) + local unit = playerEntry:AddUnit(objectType) + + self:UpdateGalacticBuildStatsTable(unit, planet) end end + +---Sets a unit as killed. +---@param gameObject GameObject The game object that was killed. +---@param killer PlayerObject The player that killed the game object. +function GalacticGameClass: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:UpdateGalacticKillStatsTable(killedUnit, killer) + end +end + +function GalacticGameClass:UnitNeutralized(objectType, killer) + self:UpdateGalacticNeutralizedTable(objectType, killer) +end + +-- ================================================== +-- Planet event handlers +-- ================================================== + +---Sets a planet as changed faction. +---@param planet PlanetObject +---@param newOwner PlayerObject +---@param oldOwner PlayerObject +function GalacticGameClass:PlanetFactionChange(planet, newOwner, oldOwner) + +end + +-- ================================================== +-- Stat tables +-- ================================================== + +---Updates the Galactic Build Stats Table with the build object type. +---@param unit UnitClass The game object type that was built. +---@param planet PlanetObject|nil The planet where the game object was built. +function GalacticGameClass:UpdateGalacticBuildStatsTable(unit, planet) + ---@type GameObjectType|integer + local planetType = 1 + + if planet then + planetType = planet.Get_Type() + end + + local playerEntry = self.BuildStatsTable[unit.OwnerId] + + if not playerEntry then + playerEntry = {} + self.BuildStatsTable[unit.OwnerId] = playerEntry + end + + local planetEntry = playerEntry[planetType] + + if not planetEntry then + planetEntry = {} + playerEntry[planetType] = planetEntry + end + + local typeEntry = planetEntry[unit.Name] + + if not typeEntry then + typeEntry = { + build_count = 1, + combat_power = unit.CombatRating, + build_cost = unit.BuildCost, + score_value = unit.ScoreValue + } + planetEntry[unit.Name] = typeEntry + else + typeEntry.build_count = typeEntry.build_count + 1 + typeEntry.combat_power = typeEntry.combat_power + unit.CombatRating + typeEntry.build_cost = typeEntry.build_cost + unit.BuildCost + typeEntry.score_value = typeEntry.score_value + unit.ScoreValue + end +end + +---Updates the Galactic Kill Stats Table with the killed object. +---@param unit UnitClass The unit that was killed. +---@param killer PlayerObject The player that killed the unit. +function GalacticGameClass:UpdateGalacticKillStatsTable(unit, killer) + if not TestValid(killer) then + return + end + + -- Update Frags + local fragEntry = self.KillStatsTable[FragIndex] + + if not fragEntry then + fragEntry = {} + self.KillStatsTable[FragIndex] = fragEntry + end + + local killerId = killer.Get_ID() + local killerEntry = fragEntry[killerId] + + if not killerEntry then + killerEntry = {} + fragEntry[killerId] = killerEntry + end + + local killerObjEntry = killerEntry[unit.GameObjectType] + + if not killerObjEntry then + killerObjEntry = { + kills = 1, + combat_power = unit.CombatRating, + build_cost = unit.BuildCost, + score_value = unit.ScoreValue + } + killerEntry[unit.GameObjectType] = killerObjEntry + else + killerObjEntry.kills = killerObjEntry.kills + 1 + killerObjEntry.combat_power = killerObjEntry.combat_power + unit.CombatRating + killerObjEntry.build_cost = killerObjEntry.build_cost + unit.BuildCost + killerObjEntry.score_value = killerObjEntry.score_value + unit.ScoreValue + end + + -- Update Deaths + local deathEntry = self.KillStatsTable[DeathIndex] + + if not deathEntry then + deathEntry = {} + self.KillStatsTable[DeathIndex] = deathEntry + end + + local ownerEntry = deathEntry[unit.OwnerId] + + if not ownerEntry then + ownerEntry = {} + deathEntry[unit.OwnerId] = ownerEntry + end + + local ownerObjEntry = ownerEntry[unit.GameObjectType] + + if not ownerObjEntry then + ownerObjEntry = { + kills = 1, + combat_power = unit.CombatRating, + build_cost = unit.BuildCost, + score_value = unit.ScoreValue + } + ownerEntry[unit.GameObjectType] = ownerObjEntry + else + ownerObjEntry.kills = ownerObjEntry.kills + 1 + ownerObjEntry.combat_power = ownerObjEntry.combat_power + unit.CombatRating + ownerObjEntry.build_cost = ownerObjEntry.build_cost + unit.BuildCost + ownerObjEntry.score_value = ownerObjEntry.score_value + unit.ScoreValue + end +end + +---Updates the Galactic Neutralized Table with the neutralized hero. +---@param objectType GameObjectType The hero type that was neutralized. +---@param killer GameObject The hero that killed this hero. +function GalacticGameClass:UpdateGalacticNeutralizedTable(objectType, killer) + local killerId = killer.Get_Owner().Get_ID() + local killerEntry = self.NeutralizedStatsTable[killerId] + + if not killerEntry then + killerEntry = {} + self.NeutralizedStatsTable[killerId] = killerEntry + end + + local neutralizedEntry = killerEntry[objectType] + + if not neutralizedEntry then + neutralizedEntry = { + neutralized = 1 + } + killerEntry[objectType] = neutralizedEntry + else + neutralizedEntry.neutralized = neutralizedEntry.neutralized + 1 + end +end + +-- ================================================== +-- Private functions +-- ================================================== + +---@private +---Finds all players of defined Factions via FoCAPI. +---@return PlayerClass[] +function GalacticGameClass:_FindPlayers() + local isSuccess, result = pcall(Get_All_Players) + + if isSuccess then + local players = {} + + for _, playerWrapper in pairs(result) do + local factionName = playerWrapper.Get_Faction_Name() + + for _, faction in pairs(Factions) do + if faction.Name == factionName then + local player = PlayerClass:New(playerWrapper) + + players[player.Id] = player + end + end + end + + return players + end + + return self:_FindPlayersFallback() +end + +---@private +---Finds all players of defined Factions via planets. +---@return PlayerClass[] +function GalacticGameClass:_FindPlayersFallback() + ---@type PlayerClass[] + local players = {} + + for _, planet in pairs(FindPlanet.Get_All_Planets()) do + local owner = planet.Get_Owner() + local factionName = owner.Get_Faction_Name() + + for _, faction in pairs(Factions) do + if faction.Name == factionName then + local player = PlayerClass:New(owner) + + players[player.Id] = player + end + end + end + + return players +end diff --git a/Data/Scripts/Miscellaneous/GameManager/PlayerClass.lua b/Data/Scripts/Miscellaneous/GameManager/PlayerClass.lua index c65862c..b76df53 100644 --- a/Data/Scripts/Miscellaneous/GameManager/PlayerClass.lua +++ b/Data/Scripts/Miscellaneous/GameManager/PlayerClass.lua @@ -80,7 +80,7 @@ function PlayerClass:HasQuit() end ---Adds a Unit type to the Player's reinforcement list. ----@param objectType table FoC GameObjectTypeWrapper object that was built. +---@param objectType GameObjectType FoC GameObjectTypeWrapper object that was built. ---@return UnitClass function PlayerClass:AddUnit(objectType) local unit = UnitClass:New(objectType, self.Id) diff --git a/Data/Scripts/Miscellaneous/GameManager/TacticalGameClass.lua b/Data/Scripts/Miscellaneous/GameManager/TacticalGameClass.lua index 4ac35f9..401a38e 100644 --- a/Data/Scripts/Miscellaneous/GameManager/TacticalGameClass.lua +++ b/Data/Scripts/Miscellaneous/GameManager/TacticalGameClass.lua @@ -8,6 +8,7 @@ require("GameManager/TutorialTextFeed") ---@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. +---@field BuildStatsTable table The engine-owned TacticalBuildStatsTable reference, set from GameScoring.lua. ---@field KillStatsTable table The engine-owned TacticalKillStatsTable reference, set from GameScoring.lua. TacticalGameClass = {} TacticalGameClass.__index = TacticalGameClass @@ -83,15 +84,17 @@ end -- ================================================== ---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) +---@param objectType GameObjectType The game object type that was built. +---@param player PlayerObject The player that owns the new game object. +---@param planet PlanetObject|nil The planet the game object type was built at. +function TacticalGameClass:UnitBuilt(objectType, player, planet) local playerId = player.Get_ID() local playerEntry = self.Players[playerId] if playerEntry then local unit = playerEntry:AddUnit(objectType) + self:UpdateTacticalBuildStatsTable(unit, planet) self:CombatTextUnitBuilt(unit) end end @@ -126,8 +129,8 @@ function TacticalGameClass:CombatTextUnitBuilt(unit) 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. +---@param gameObject GameObject The game object that was killed. +---@param killer PlayerObject The player that killed the game object. function TacticalGameClass:UnitKilled(gameObject, killer) local ownerId = gameObject.Get_Owner().Get_ID() local owner = self.Players[ownerId] @@ -195,6 +198,49 @@ end --- Stat tables --- ================================================== +---Updates the Tactical Build Stats Table with the build object type. +---@param unit UnitClass The game object type that was built. +---@param planet PlanetObject|nil The planet the game object type was built at. +function TacticalGameClass:UpdateTacticalBuildStatsTable(unit, planet) + ---@type GameObjectType|integer + local planetType = 1 + + if planet then + planetType = planet.Get_Type() + end + + local playerEntry = self.BuildStatsTable[unit.OwnerId] + + if not playerEntry then + playerEntry = {} + self.BuildStatsTable[unit.OwnerId] = playerEntry + end + + local planetEntry = playerEntry[planetType] + + if not planetEntry then + planetEntry = {} + playerEntry[planetType] = planetEntry + end + + local typeEntry = planetEntry[unit.Name] + + if not typeEntry then + typeEntry = { + build_count = 1, + combat_power = unit.CombatRating, + build_cost = unit.BuildCost, + score_value = unit.ScoreValue + } + planetEntry[unit.Name] = typeEntry + else + typeEntry.build_count = typeEntry.build_count + 1 + typeEntry.combat_power = typeEntry.combat_power + unit.CombatRating + typeEntry.build_cost = typeEntry.build_cost + unit.BuildCost + typeEntry.score_value = typeEntry.score_value + unit.ScoreValue + end +end + ---Updates the Tactical Kill Stats Table with the killed object. ---@param unit UnitClass The object that was killed. ---@param killer PlayerObject The player that killed the object. @@ -274,9 +320,40 @@ end -- ================================================== ---@private +---Finds all players of defined Factions via FoCAPI. ---@param gameMode string The current game mode. ---@return PlayerClass[] function TacticalGameClass:_FindPlayers(gameMode) + local isSuccess, result = pcall(Get_All_Players) + + if isSuccess then + local players = {} + + for _, playerWrapper in pairs(result) do + local factionName = playerWrapper.Get_Faction_Name() + + for _, faction in pairs(Factions) do + if faction.Name == factionName then + local player = PlayerClass:New(playerWrapper) + + players[player.Id] = player + end + end + end + + return players + end + + return self:_FindPlayersFallback(gameMode) +end + +---@private +---Finds all players of defined Factions via starting unit. +---Note: this will not work when starting units are disabled. +---For the spectator team, it will only ever find the player in seat 1. +---@param gameMode string The current game mode. +---@return PlayerClass[] +function TacticalGameClass:_FindPlayersFallback(gameMode) ---@type PlayerClass[] local players = {} diff --git a/Data/Scripts/Miscellaneous/GameManager/UnitClass.lua b/Data/Scripts/Miscellaneous/GameManager/UnitClass.lua index 1f475e4..b6aae0f 100644 --- a/Data/Scripts/Miscellaneous/GameManager/UnitClass.lua +++ b/Data/Scripts/Miscellaneous/GameManager/UnitClass.lua @@ -2,9 +2,10 @@ require("PGBase") ---@class UnitClass ---@field Name string Unit name. ----@field DisplayName string The localized display name. +---@field DisplayName string|nil The localized display name. ---@field OwnerId integer Owner Player ID. ----@field BuildCost integer Tactical build cost. +---@field BuildCost integer Galactic build cost. +---@field TacticalBuildCost integer Tactical build cost. ---@field CombatRating integer AI combat power rating. ---@field ScoreValue integer Score cost credits. ---@field BuildTime number Game time that this unit was built. @@ -17,15 +18,16 @@ UnitClass = {} UnitClass.__index = UnitClass ---Constructs a new Unit object. ----@param objectType GameObjectType FoC GameObjectTypeWrapper object +---@param objectType GameObjectType The game object type. ---@param playerId integer The Owner Player ID function UnitClass:New(objectType, playerId) local self = setmetatable({}, UnitClass) self.Name = objectType.Get_Name() - self.DisplayName = objectType.Get_Display_Name() + _, self.DisplayName = pcall(objectType.Get_Display_Name) self.OwnerId = playerId - self.BuildCost = objectType.Get_Tactical_Build_Cost() + self.BuildCost = objectType.Get_Build_Cost() + self.TacticalBuildCost = objectType.Get_Tactical_Build_Cost() self.CombatRating = objectType.Get_Combat_Rating() self.ScoreValue = objectType.Get_Score_Cost_Credits() self.BuildTime = GetCurrentTime() diff --git a/Data/Scripts/Miscellaneous/GameScoring.lua b/Data/Scripts/Miscellaneous/GameScoring.lua index fd19f9e..c98ceae 100755 --- a/Data/Scripts/Miscellaneous/GameScoring.lua +++ b/Data/Scripts/Miscellaneous/GameScoring.lua @@ -38,6 +38,10 @@ GameSpy_Game_Stats = {} ---@deprecated GameSpy_Player_Stats = {} +---Tactical Build Stat Table for post-game. +---[playerid][planetname][object_type][build_count, credits_spent, combat_power] +TacticalBuildStatsTable = {} + ---Tactical Frag/Death Stat Table for post-game. ---[frag|death][playerid][object_type][build_count, credits_spent, combat_power] TacticalKillStatsTable = { @@ -45,6 +49,32 @@ TacticalKillStatsTable = { [DeathIndex] = {} } +---Tactical Team Frag/Death Stat Table for post-game. +---[frag|death][playerid][object_type][build_count, credits_spent, combat_power] +TacticalTeamKillStatsTable = { + [FragIndex] = {}, + [DeathIndex] = {} +} + +---Galactic Build Stat Table for post-game. +---[playerid][planetname][object_type][build_count, credits_spent, combat_power] +GalacticBuildStatsTable = {} + +---Galactic Frag/Death Stat Table for post-game. +---[frag|death][playerid][object_type][build_count, credits_spent, combat_power] +GalacticKillStatsTable = { + [FragIndex] = {}, + [DeathIndex] = {} +} + +---Galactic Neutralized Hero Table for post-game. +---[playerid][object_type][neutralized_count] +GalacticNeutralizedTable = {} + +---Galactic Planet Conquest Table for post-game. +---[playerid][planet_type][sacked_count, lost_count] +GalacticConquestTable = {} + -- ================================================== -- Script State Functions -- ================================================== @@ -126,6 +156,7 @@ end function Reset_Tactical_Stats() DebugMessage("%s -- In Reset_Tactical_Stats", tostring(Script)) + TacticalBuildStatsTable = {} TacticalKillStatsTable = { [FragIndex] = {}, [DeathIndex] = {} @@ -143,6 +174,14 @@ function Reset_Stats() DebugMessage("%s -- In Reset_Stats", tostring(Script)) Reset_Tactical_Stats() + + GalacticBuildStatsTable {} + GalacticKillStatsTable = { + [FragIndex] = {}, + [DeathIndex] = {} + } + GalacticNeutralizedTable = {} + GalacticConquestTable = {} end ---A dirty hack to reset tactical script registry values @@ -257,18 +296,25 @@ end ---@param object GameObject|GameObjectType FoC GameObjectWrapper or GameObjectTypeWrapper of the object that was just created. function Galactic_Production_End_Event(planet, object) local typeName = "" + local type = nil if object.Get_Type == nil then - -- object must be a GameObjectTypeWrapper not a GameObjectWrapper if it doesn't - -- have a Get_Type function. + -- object must be a GameObjectTypeWrapper if it doesn't have a Get_Type function. + ---@cast object GameObjectType + type = object typeName = object.Get_Name() else -- object points to the GameObjectWrapper that was just created. + type = object.Get_Type() typeName = object.Get_Type().Get_Name() end DebugMessage("%s -- Galactic production of %s ended at %s.", tostring(Script), typeName, planet.Get_Type().Get_Name()) + + if GalacticGame then + GalacticGame:UnitBuilt(type, planet.Get_Owner(), planet) + end end ---This event is triggered when a unit is destroyed in galactic mode. @@ -276,6 +322,14 @@ end ---@param killer PlayerObject FoC PlayerWrapper object that killed the object. function Galactic_Unit_Destroyed_Event(object, killer) DebugMessage("%s -- Object %s killed by %s.", tostring(Script), object.Get_Type().Get_Name(), killer.Get_Name()) + + if GalacticGame then + GalacticGame:UnitKilled(object, killer) + end + + if TacticalGame then + TacticalGame:UnitKilled(object, killer) + end end ---This event is triggered when the level of a starbase changes. @@ -434,10 +488,10 @@ function Init_Tactical(gameMode) DebugMessage("%s -- Initializing Tactical %s rules.", tostring(Script), gameMode) Reset_Tactical_Stats() TacticalGame = TacticalGameClass:New(gameMode) - TacticalGame.KillStatsTable = TacticalKillStatsTable + TacticalGame.KillStatsTable = TacticalTeamKillStatsTable GUIManager:InitTactical() - TextManager:RegisterView(TacticalGame.CombatFeed) - TextManager:SetCurrentView("combat_feed") + --TextManager:RegisterView(TacticalGame.CombatFeed) + --TextManager:SetCurrentView("combat_feed") end ---Resets Tactical mode.