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

152
.luadefs/foc_engine.lua Normal file
View File

@@ -0,0 +1,152 @@
---@meta
---@diagnostic disable: missing-fields
-- Empire at War / Forces of Corruption engine API stubs.
-- This file is never executed — the Lua Language Server reads it as definitions only.
-- ==================================================
-- Engine Object Types
-- ==================================================
---A player in the game (C++ PlayerWrapper userdata).
---@class PlayerWrapper
---@field Get_Name fun(): string
---@field Get_ID fun(): integer
---@field Get_Faction fun(): string
---@field Get_Credits fun(): number
---@field Is_Valid fun(): boolean
---@field Is_Human fun(): boolean
---@field Is_Ally fun(other: PlayerWrapper): boolean
---@field Is_Enemy fun(other: PlayerWrapper): boolean
---@field Select_Object fun(other: GameObjectWrapper)
---A game object instance (C++ GameObjectWrapper userdata).
---@class GameObjectWrapper
---@field Get_Type fun(): GameObjectTypeWrapper
---@field Get_Owner fun(): PlayerWrapper
---@field Get_Final_Blow_Player fun(): PlayerWrapper
---@field Get_Game_Scoring_Type fun(): GameObjectTypeWrapper
---@field Is_Valid fun(): boolean
---@field Is_Pool_Safe fun(): boolean
---@field Service_Wrapper fun()
---A game object type definition (C++ GameObjectTypeWrapper userdata).
---@class GameObjectTypeWrapper
---@field Get_Name fun(): string
---@field Is_Valid fun(): boolean
-- ==================================================
-- Script Object
-- ==================================================
---The engine-injected script context object.
---@class ScriptObject
---@field Debug_Should_Issue_Event_Alert fun(): boolean
---Engine-injected script context. Set by the engine before the script runs.
---@type ScriptObject
Script = {}
---Engine-injected object context. Set by the engine before the script runs.
---@type GameObjectWrapper
Object = {}
-- ==================================================
-- GlobalValue / ThreadValue
-- (callable table: GlobalValue("key") to get, GlobalValue.Set("key", val) to set)
-- ==================================================
---@class ValueStore
---@field Set fun(key: string, value: any)
---@operator call(string): any
---Persistent global value store shared across scripts.
---@type ValueStore
GlobalValue = {}
---Per-thread value store.
---@type ValueStore
ThreadValue = {}
-- ==================================================
-- Engine Global Functions
-- ==================================================
---Pumps services for the script.
function Pump_Service() end
---Compares two strings for equality (case-insensitive in FoC).
---@param a string
---@param b string
---@return boolean
function StringCompare(a, b) end
---Finds a player by name (e.g. "local", "empire", "rebel").
---@param name string
---@return PlayerWrapper
function Find_Player(name) end
---Returns the current game time in seconds.
---@return number
function GetCurrentTime() end
---Returns the ID of the current thread, or -1 if not in a thread.
---@return integer
function GetThreadID() end
---Returns the next pending event function, or nil if the queue is empty.
---@return (fun(...): any) | nil
function GetEvent() end
---Exits the current script.
function _ScriptExit() end
---Pops up a message box (internal engine call).
---@param message string
function _MessagePopup(message) end
---Sends a message to the script log (internal engine call).
---@param message string
function _ScriptMessage(message) end
---Sends a message to the debug output (internal engine call — note: typo in original source).
---@param message string
function _OuputDebug(message) end
---Dumps the current Lua call stack as a string.
---@return string
function DumpCallStack() end
---Sets the visibility of the named component.
---@param component string
---@param isVisible boolean
function GUI_Component_Visibility(component, isVisible) end
---Sets the text for the named component.
---@param component string
---@param text string
function GUI_Component_Text(component, text) end
---Sets the text color for the named component.
---@param component string
---@param r number
---@param g number
---@param b number
---@param a number
function GUI_Text_Color(component, r, g, b, a) end
---Sets the icon for the named component.
---@param component string
---@param icon string
---@param r number
---@param g number
---@param b number
---@param a number
function GUI_Button_Icon(component, icon, r, g, b, a) end
---Finds all objects matching the criteria.
---@param a any
---@param b any
---@param c any
---@param d any
---@return GameObjectWrapper[]
function Find_All_Objects_Of_Type(a, b, c, d) end

12
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"Lua.workspace.library": [
"${workspaceFolder}/.luadefs"
],
"Lua.workspace.checkThirdParty": false,
"Lua.runtime.version": "Lua 5.1",
"Lua.diagnostics.globals": [
"Script",
"GlobalValue",
"ThreadValue"
]
}

View File

@@ -0,0 +1,287 @@
-- $Id: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGBase.lua#2 $
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- (C) Petroglyph Games, Inc.
--
--
-- ***** ** * *
-- * ** * * *
-- * * * * *
-- * * * * * * * *
-- * * *** ****** * ** **** *** * * * ***** * ***
-- * ** * * * ** * ** ** * * * * ** ** ** *
-- *** ***** * * * * * * * * ** * * * *
-- * * * * * * * * * * * * * * *
-- * * * * * * * * * * ** * * * *
-- * ** * * ** * ** * * ** * * * *
-- ** **** ** * **** ***** * ** *** * *
-- * * *
-- * * *
-- * * *
-- * * * *
-- **** * *
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
-- C O N F I D E N T I A L S O U R C E C O D E -- D O N O T D I S T R I B U T E
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- $File: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGBase.lua $
--
-- Original Author: Brian Hayes
--
-- $Author: James_Yarrow $
--
-- $Change: 45244 $
--
-- $DateTime: 2006/05/26 10:01:00 $
--
-- $Revision: #2 $
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
require("PGDebug")
YieldCount = 0;
function ScriptExit()
_ScriptExit() -- set a flag in 'C' to terminate the whole script on next yield
if GetThreadID() >= 0 then
coroutine.yield(false) -- return false to exit this thread
end
end
function Sleep(time)
--DebugMessage("Sleeping... SleepTime: %.3f, CurTime: %.3f\n", time, GetCurrentTime())
ThreadValue.Set("StartTime", GetCurrentTime())
while GetCurrentTime() - ThreadValue("StartTime") < time do
PumpEvents()
end
--DebugMessage("Done with Sleep. Continuing, CurTime: %.3f\n", GetCurrentTime())
end
-- Service the block until optional max duration has expired or alternate break function returns true
-- Pass -1 max_duration to use optional alternate break function with no time limit
function BlockOnCommand(block, max_duration, alternate_break_func)
PumpEvents()
if not block then
return nil
end
break_block = false
ThreadValue.Set("BlockStart", GetCurrentTime())
repeat
if break_block == true then
break_block = false
return nil
end
PumpEvents()
if ((max_duration ~= nil) and (max_duration ~= -1)
and (GetCurrentTime() - ThreadValue("BlockStart") > max_duration)) then
--MessageBox("%s -- Had a time limit and it expired", tostring(Script))
return nil
end
if ((alternate_break_func ~= nil) and alternate_break_func()) then
--MessageBox("%s-- had a break func and it returned true", tostring(Script))
return nil
end
until (block.IsFinished() == true)
PumpEvents()
return block.Result()
end
function BreakBlock()
break_block = true
end
function TestCommand(block)
if not block then
return nil
end
PumpEvents()
return block.IsFinished()
end
function PumpEvents()
if Object and type(Object) == "userdata" then
Object.Service_Wrapper()
end
if Pump_Service and type(Pump_Service) == "function" then
Pump_Service()
end
if ThreadValue("InPumpEvents") then
ScriptError("%s -- Already in pump event!!", tostring(Script))
end
ThreadValue.Set("InPumpEvents", true)
--DebugMessage("%s -- Entering yield. Count: %d, Time: %.3f\n", tostring(Script), YieldCount, GetCurrentTime())
YieldCount = YieldCount + 1
coroutine.yield(true) -- yield here and return to 'C'
--DebugMessage("%s -- Return from yield. Count: %d, Time: %.3f\n", tostring(Script), YieldCount, GetCurrentTime())
CurrentEvent = GetEvent()
while CurrentEvent do
ScriptMessage("%s -- Pumping Event: %s.", tostring(Script), tostring(CurrentEvent))
EventParams = GetEvent.Params()
if EventParams then
CurrentEvent(unpack(EventParams))
else
CurrentEvent()
end
if Script.Debug_Should_Issue_Event_Alert() and DebugEventAlert then
DebugEventAlert(CurrentEvent, EventParams)
end
CurrentEvent = GetEvent()
end
ThreadValue.Set("InPumpEvents", false)
end
function TestValid(wrapper)
if wrapper == nil then
return false
end
if wrapper.Is_Valid == nil then
return false
end
return wrapper.Is_Valid()
end
function Clamp(value, min, max)
if value < min then
return min
elseif value > max then
return max
else
return value
end
end
-- Nasty hack of a floor function to be replaced if a math library floor funciton is exposed
function Dirty_Floor(val)
return string.format("%d", val) -- works on implicit string to int conversion
end
-- Machine independent modulus function
function Simple_Mod(a,b)
--return a-b*math.floor(a/b)
return a-b*Dirty_Floor(a/b)
end
-- Returns if something happened, given a % chance
function Chance(seed, percent)
roll = Simple_Mod((seed + 1), 100)
is_allowed = roll < percent
DebugMessage("%s -- seed:%d percent:%d roll:%d is_allowed:%s", tostring(Script), seed, percent, roll, tostring(is_allowed))
return is_allowed
end
function GetCurrentMinute()
--return math.floor(GetCurrentTime()/60)
return Dirty_Floor(GetCurrentTime()/60)
end
-- Every X seconds, the AI will have a new opportunity to see if it's allowed to use an ability
function GetAbilityChanceSeed()
return GetCurrentTime()
end
function GetChanceAllowed(difficulty)
--Possibly change these back, but randomness makes things hard to test
chance = 100
if difficulty == "Easy" then
chance = 100
elseif difficulty == "Hard" then
chance = 100
end
return chance
end
function PlayerSpecificName(player_object, var_name)
-- ret_value = tostring(player_object.Get_ID()) .. "_" .. var_name
-- DebugMessage("%s -- creating player specific string %s.", tostring(Script), ret_value)
-- return ret_value
return (tostring("PLAYER" .. player_object.Get_ID()) .. "_" .. var_name)
end
function Flush_G()
entries_for_deletion = {}
--Define the set of tables that we had better keep around
very_important_tables = {
_LOADED,
coroutine,
string,
LuaWrapperMetaTable,
_G,
security,
table,
entries_for_deletion
}
--Silly thing is nil (we think) if we try to add it earlier
table.insert(very_important_tables, very_important_tables)
--Iterate all globals
for i,g_entry in pairs(_G) do
if type(g_entry) == "table" then
--Tables are inherently unsafe: who knows what might be in there?
--If they're not in the list of things we must keep then they go.
for j,important_entry in pairs(very_important_tables) do
if important_entry == g_entry then
keep_table = true
end
end
if not keep_table then
table.insert(entries_for_deletion, i)
end
keep_table = nil
elseif type(g_entry) == "userdata" then
--Some User Data (e.g. our code functions) should be kept, but some is very, very dangerous.
--Query the object to see whether it's safe to persist.
if not g_entry.Is_Pool_Safe() then
table.insert(entries_for_deletion, i)
end
end
end
for i,bad_entry in pairs(entries_for_deletion) do
_G[bad_entry] = nil
end
entries_for_deletion = nil
very_important_tables = nil
end

View File

@@ -0,0 +1,86 @@
-- $Id: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGDebug.lua#1 $
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- (C) Petroglyph Games, Inc.
--
--
-- ***** ** * *
-- * ** * * *
-- * * * * *
-- * * * * * * * *
-- * * *** ****** * ** **** *** * * * ***** * ***
-- * ** * * * ** * ** ** * * * * ** ** ** *
-- *** ***** * * * * * * * * ** * * * *
-- * * * * * * * * * * * * * * *
-- * * * * * * * * * * ** * * * *
-- * ** * * ** * ** * * ** * * * *
-- ** **** ** * **** ***** * ** *** * *
-- * * *
-- * * *
-- * * *
-- * * * *
-- **** * *
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
-- C O N F I D E N T I A L S O U R C E C O D E -- D O N O T D I S T R I B U T E
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- $File: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGDebug.lua $
--
-- Original Author: Brian Hayes
--
-- $Author: Andre_Arsenault $
--
-- $Change: 37816 $
--
-- $DateTime: 2006/02/15 15:33:33 $
--
-- $Revision: #1 $
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
function DebugEventAlert(event, params)
message = tostring(Script) .. ": handled event " .. tostring(event)
function AppendParameter(ival, parameter)
message = message .. "\nParameter " .. tostring(ival) .. ": " .. tostring(parameter)
end
table.foreachi(params, AppendParameter)
MessageBox(message)
end
function MessageBox(...)
_MessagePopup(string.format(unpack(arg)))
end
function ScriptMessage(...)
_ScriptMessage(string.format(unpack(arg)))
end
function DebugMessage(...)
_ScriptMessage(string.format(unpack(arg)))
end
function OutputDebug(...)
_OuputDebug(string.format(unpack(arg)))
end
function ScriptError(...)
outstr = string.format(unpack(arg))
_OuputDebug(outstr .. "\n")
_ScriptMessage(outstr)
outstr = DumpCallStack()
_OuputDebug(outstr .. "\n")
_ScriptMessage(outstr)
ScriptExit()
end
function DebugPrintTable(unit_table)
DebugMessage("%s -- unit table contents:", tostring(Script))
for key, obj in pairs(unit_table) do
DebugMessage("%s -- \t\t** unit:%s", tostring(Script), tostring(obj))
end
end

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

View File

@@ -0,0 +1,443 @@
require("PGBase")
require("GameManager.Constants")
require("GameManager.GUIManager")
require("GameManager.GalacticGameClass")
require("GameManager.TacticalGameClass")
require("GameManager.SkirmishGameClass")
-- ==================================================
-- Script Locals
-- ==================================================
---Debug Counter. When this equals ServiceDebugRate, the script will print debug stats.
local DebugCounter = 0
---Debugging service rate.
local ServiceDebugRate = 10
---The Local Player FoC PlayerWrapper object.
local LocalPlayer = nil
---The GUI Manager.
---@type GUIManager
local GUIManager = GUIManager:New()
---The Galactic Game manager.
---@type GalacticGameClass|nil
local GalacticGame = nil
---The Tactical Game manager.
---@type TacticalGameClass|nil
local TacticalGame = nil
---The Skirmish Game manager.
---@type SkirmishGameClass|nil
local SkirmishGame = nil
-- ==================================================
-- Script Globals
-- ==================================================
ScriptPoolCount = 0
ServiceRate = 1
ScriptShouldCRC = false
---GameSpy Game Stats table
---@deprecated
GameSpy_Game_Stats = {}
---GameSpy Player Stats table
---@deprecated
GameSpy_Player_Stats = {}
-- ==================================================
-- Script State Functions
-- ==================================================
---Sets up the base variables for this script.
function Base_Definitions()
DebugMessage("%s -- In Base_Definitions.", tostring(Script))
DebugCounter = 0
LocalPlayer = Find_Player("local")
end
---Main script function. Does event pumps and servicing.
function main()
DebugMessage("%s -- In main.", tostring(Script))
if GameService then
while 1 do
GameService()
PumpEvents()
end
end
ScriptExit()
end
---Script servicing function.
function GameService()
DebugCounter = DebugCounter + 1
if DebugCounter == ServiceDebugRate then
ServiceDebug()
DebugCounter = 0
end
if GalacticGame then
GalacticGame:Service()
if TacticalGame then
TacticalGame:Service()
end
end
if SkirmishGame then
SkirmishGame:Service()
end
if GUIManager then
GUIManager:Service()
end
end
---Script debug servicing.
function ServiceDebug()
end
-- ==================================================
-- Reset State
-- ==================================================
---Reset the Tactical mode game stats.
function Reset_Tactical_Stats()
DebugMessage("%s -- In Reset_Tactical_Stats", tostring(Script))
ResetTacticalRegistry()
end
---Reset all the stats and player lists.
function Reset_Stats()
DebugMessage("%s -- In Reset_Stats", tostring(Script))
Reset_Tactical_Stats()
end
---A dirty hack to reset tactical script registry values
function ResetTacticalRegistry()
DebugMessage("Resetting Allow_AI_Controlled_Fog_Reveal to 1 (allowed)")
GlobalValue.Set("Allow_AI_Controlled_Fog_Reveal", 1)
end
--- ==================================================
--- Event Handlers
--- ==================================================
---This event is triggered on a game mode start.
---@param mode string Name of the new mode (i.e. Galactic, Land, Space)
---@param map string Name of the map.
function Game_Mode_Starting_Event(mode, map)
DebugMessage("%s -- Game Mode %s (%s) now starting.", tostring(Script), mode, map)
if StringCompare(mode, "Galactic") then
-- Galactic Campaign
Init_Galactic()
elseif GalacticGame then
-- Galactic mode transition to Tactical
Init_Tactical(mode)
else
-- Skirmish tactical
Init_Skirmish(mode)
end
end
---This event is triggered on a game mode end.
---@param oldMode string Name of the old mode (i.e. Galactic, Land, Space)
function Game_Mode_Ending_Event(oldMode)
DebugMessage("%s -- Game Mode %s now ending.", tostring(Script), oldMode)
if StringCompare(oldMode, "Galactic") then
-- Galactic mode ending
Reset_Galactic()
elseif GalacticGame then
-- Tactical mode transition to Galactic
Reset_Tactical()
else
-- Skirmish mode ending
Reset_Skirmish()
end
end
---This event is triggered when a player quits the game.
---@param player table FoC PlayerWrapper object that just quit.
function Player_Quit_Event(player)
DebugMessage("%s -- Player %s (%d) has quit.", tostring(Script), player.Get_Name(), player.Get_ID())
if TacticalGame then
TacticalGame:PlayerQuit(player.Get_ID())
end
end
---This event is triggered when production has finished in a tactical mode
---@param objectType table FoC GameObjectTypeWrapper object that was just built.
---@param player table FoC PlayerWrapper object that built the object.
---@param location table|nil FoC GameObjectWrapper of the planet.
function Tactical_Production_End_Event(objectType, player, location)
if location then
DebugMessage("%s -- Tactical production of unit %s by %s ended at %s.", tostring(Script), objectType.Get_Name(),
player.Get_Name(),
location.Get_Type().Get_Name())
else
DebugMessage("%s -- Tactical production of unit %s by %s ended.", tostring(Script), objectType.Get_Name(),
player.Get_Name())
end
if TacticalGame then
TacticalGame:UnitBuilt(objectType, player)
end
end
---This event is triggered when a unit is destroyed in tactical mode.
---@param object table FoC GameObjectWrapper object that was just killed.
---@param killer table FoC PlayerWrapper object that killed the object.
function Tactical_Unit_Destroyed_Event(object, killer)
DebugMessage("%s -- Tactical unit %s destroyed by %s.", tostring(Script), object.Get_Type().Get_Name(),
killer.Get_Name())
if TacticalGame then
TacticalGame:UnitKilled(object)
end
end
---This event is triggered when production has begun on an item at a given planet.
---@param planet table FoC GameObjectWrapper object of the planet.
---@param objectType table FoC GameObjectTypeWrapper object that was just queued.
function Galactic_Production_Begin_Event(planet, objectType)
DebugMessage("%s -- Galactic production of %s started at %s.", tostring(Script), objectType.Get_Name(),
planet.Get_Type().Get_Name())
end
---This event is triggered when production has been prematurely canceled
---on an item at a given planet.
---@param planet table FoC GameObjectWrapper object of the planet.
---@param objectType table FoC GameObjectTypeWrapper object that was just canceled..
function Galactic_Production_Canceled_Event(planet, objectType)
DebugMessage("%s -- Galactic production of %s canceled at %s.", tostring(Script), objectType.Get_Name(),
planet.Get_Type().Get_Name())
end
---This event is triggered when production has finished on an item at a given planet.
---@param planet table FoC GameObjectWrapper or of the planet.
---@param object table FoC GameObjectWrapper or GameObjectTypeWrapper of the object that was just created.
function Galactic_Production_End_Event(planet, object)
local typeName = ""
if object.Get_Type == nil then
-- object must be a GameObjectTypeWrapper not a GameObjectWrapper if it doesn't
-- have a Get_Type function.
typeName = object.Get_Name()
else
-- object points to the GameObjectWrapper that was just created.
typeName = object.Get_Type().Get_Name()
end
DebugMessage("%s -- Galactic production of %s ended at %s.", tostring(Script), typeName,
planet.Get_Type().Get_Name())
end
---This event is triggered when a unit is destroyed in galactic mode.
---@param object table FoC GameObjectWrapper object that was just killed.
---@param killer table 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())
end
---This event is triggered when the level of a starbase changes.
---@param planet table FoC GameObjectWrapper object of the planet.
---@param oldType table FoC GameObjectTypeWrapper object of the old starbase type.
---@param newType table FoC GameObjectTypeWrapper object of the new starbase type.
function Galactic_Starbase_Level_Change(planet, oldType, newType)
DebugMessage("%s -- %s Starbase changed from %s to %s.", tostring(Script), planet.Get_Type().Get_Name(),
tostring(oldType), tostring(newType))
if oldType == nil then
return
end
if newType ~= nil then
return
end
local fake_object_type = oldType
local fake_object_player = planet.Get_Owner()
local fake_object = {}
fake_object.Get_Owner = function()
return fake_object_player
end
fake_object.Get_Type = function()
return fake_object_type
end
fake_object.Get_Game_Scoring_Type = function()
return fake_object_type
end
fake_object.Is_Valid = function()
return true
end
Galactic_Unit_Destroyed_Event(fake_object, planet.Get_Final_Blow_Player())
end
---This event is called when a planet changes faction in galactic mode.
---@param planet table FoC GameObjectWrapper object of the planet.
---@param newOwner table FoC PlayerWrapper of the new owner.
---@param oldOwner table FoC PlayerWrapper of the old owner.
function Galactic_Planet_Faction_Change(planet, newOwner, oldOwner)
local planetName = planet.Get_Type().Get_Name()
DebugMessage("%s -- %s changed control from %s to %s.", tostring(Script), planetName,
oldOwner.Get_Name(), newOwner.Get_Name())
end
---This event is called when a hero is neutralized by another hero in galactic mode.
---@param heroType table FoC GameObjectTypeWrapper object for the neutralized hero type.
---@param killer table FoC GameObjectWrapper object that killed the hero.
function Galactic_Neutralized_Event(heroType, killer)
DebugMessage("%s -- Hero %s killed by %s.", tostring(Script), heroType.Get_Name(), killer.Get_Owner().Get_Name())
end
--- ==================================================
--- Engine Callbacks
--- ==================================================
---Returns the number of frags by player for the given object type.
---@param objectType table FoC GameObjectTypeWrapper object to query.
---@param player table FoC PlayerWrapper object to query.
---@return integer
function Get_Frag_Count_For_Type(objectType, player)
return 0
end
---Returns the number of heroes neutralized by player for the given object type.
---@param objectType table FoC GameObjectTypeWrapper object to query.
---@param player table FoC PlayerWrapper object to query.
---@return integer
function Get_Neutralized_Count_For_Type(objectType, player)
return 0
end
---Returns a game stat for the provided control ID.
---@param player table FoC PlayerWrapper object to query.
---@param controlId string The Control ID.
---@param isTactical boolean Whether the game mode was a tactical mode.
---@return number|string
function Get_Game_Stat_For_Control_ID(player, controlId, isTactical)
DebugMessage("%s -- In Get_Game_Stat_For_Control_ID for %s, Control %s, %s", tostring(Script), player.Get_Name(),
controlId, isTactical and "Tactical" or "Galactic")
if StringCompare(controlId, "IDC_MILITARY_EFFICIENCY_STATIC") then
elseif StringCompare(controlId, "IDC_CONQUEST_EFFICIENCY_STATIC") then
elseif StringCompare(controlId, "IDC_KILL_EFFICIENCY_STATIC") then
elseif StringCompare(controlId, "IDC_YOUR_LOSS_VAL_STATIC") then
elseif StringCompare(controlId, "IDC_ENEMY_LOSS_VAL_STATIC") then
elseif StringCompare(controlId, "IDC_TITLE_STATIC") then
else
DebugMessage("%s -- Unknown Control ID: %s", tostring(Script), controlId)
end
return 0
end
---Returns the current Player ID winner by score.
---@return integer Player ID
function Get_Current_Winner_By_Score()
return 0
end
--- ==================================================
--- Engine Callbacks for GameSpy [deprecated]
--- ==================================================
---Updates the GameSpy game stats table.
---@deprecated
function Update_GameSpy_Game_Stats()
GameSpy_Game_Stats = {}
end
---Updates the GameSpy player stats table
---@param player table FoC PlayerWrapper object.
function Update_GameSpy_Player_Stats(player)
GameSpy_Player_Stats = {}
end
--- ==================================================
--- Galactic Functions
--- ==================================================
---Initializes Galactic mode.
function Init_Galactic()
DebugMessage("%s -- Initializing Galactic rules.", tostring(Script))
Reset_Stats()
GalacticGame = GalacticGameClass:New()
end
---Resets Galactic mode.
function Reset_Galactic()
DebugMessage("%s -- Resetting Galactic rules.", tostring(Script))
Reset_Stats()
GalacticGame = nil
end
--- ==================================================
--- Tactical Functions
--- ==================================================
---Initializes Tactical mode.
---@param gameMode string The tactical game mode.
function Init_Tactical(gameMode)
DebugMessage("%s -- Initializing Tactical %s rules.", tostring(Script), gameMode)
Reset_Tactical_Stats()
TacticalGame = TacticalGameClass:New(gameMode)
GUIManager:InitTactical(TacticalGame)
end
---Resets Tactica mode.
function Reset_Tactical()
DebugMessage("%s -- Resetting Tactical rules.", tostring(Script))
Reset_Tactical_Stats()
TacticalGame = nil
GUIManager:Reset()
end
--- ==================================================
--- Skirmish Functions
--- ==================================================
---Initializes Skirmish mode.
---@param gameMode string The tactical game mode.
function Init_Skirmish(gameMode)
DebugMessage("%s -- Initializing Skirmish Tacitcal %s rules.", tostring(Script), gameMode)
Reset_Stats()
SkirmishGame = SkirmishGameClass:New(gameMode)
TacticalGame = SkirmishGame.TacticalGame
GUIManager:InitSkirmish(SkirmishGame)
end
function Reset_Skirmish()
DebugMessage("%s -- Resetting Skirmish Tactical rules.", tostring(Script))
Reset_Stats()
SkirmishGame = nil
TacticalGame = nil
GUIManager:Reset()
end

View File

@@ -1,2 +1,33 @@
# FoC-GameManager
# FoC GameManager
A Lua framework for Star Wars: Empire at War: Forces of Corruption that hijacks `GameScoring.lua` to create game manager states for each game mode type.
## Features
- In-game time display in tactical modes
- Skirmish spectating with team credits display
- *Coming soon* Spectator unit list display of per-team reinforcement pool, active units, and killed units
## Installation
- Copy the `Data/Scripts/Miscellaneous` folder into your mod.
- That's it.
## Important Considerations
If your mod has made changes to `GameScoring.lua`, this framework will not work without some extra care.
I have essentially entirely re-written `GameScoring.lua` as most all of the scoring functions within it are deprecated post-GameSpy era.
If you had other stuff that you were doing in there, you may want to consider adding it to one of these files as serviced functions:
- `GalacticGameClass.lua`
- `TacticalGameClass.lua`
- `SkirmishGameClass.lua`
Alternatively, you can create your own game class using one of these as the template to service your own custom scripts.
Just make sure to add it into `GameScoring.lua` as a game manager when your particular game mode is initialized.
## FAQ
**Q:** `Data/Scripts/Library` is included in the repository. Do I need to include those files as well?
**A:** No. I included these files so that the Lua language server would identify issues in the framework code files. There are no changes to these files and they already exist in the core game, so there is no need to include them.