Compare commits

..

14 Commits

23 changed files with 3823 additions and 619 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
-- ------------------------------------------------------------------
-- ------------------------------------------------------------------
-- _____ _ _ _ _
-- | ___| (_) | | | | | |
-- | |__ _ __ ___ _ __ _ _ __ ___ __ _| |_ | | | | __ _ _ __
-- | __| '_ ` _ \| '_ \| | '__/ _ \ / _` | __| | |/\| |/ _` | '__|
-- | |__| | | | | | |_) | | | | __/ | (_| | |_ \ /\ / (_| | |
-- \____/_| |_| |_| .__/|_|_| \___| \__,_|\__| \/ \/ \__,_|_|
-- | |
-- |_|
-- _____ _ ______
-- | ___| | | | _ \
-- | |__ _ __ ___ _ __ ___ _ _| | _ _ __ _| | | |___ ___
-- | __| '_ ` _ \| '_ ` _ \| | | | | | | | |/ _` | | | / _ \ / __|
-- | |__| | | | | | | | | | | |_| | |___| |_| | (_| | |/ / (_) | (__
-- \____/_| |_| |_|_| |_| |_|\__, \_____/\__,_|\__,_|___/ \___/ \___|
-- __/ |
-- |___/
-- ------------------------------------------------------------------
-- ------------------------------------------------------------------
---@diagnostic disable
---Opens a message box that contains the handled event and its parameters as multi-line formatted string.
---**WARNING:** Appears to be non-functional.
---@param event any TODO: This is most likely be the same Event type as used in gamescoring.lua
---@param params table
function DebugEventAlert(event, params) end
---@vararg string
---Opens a message box that contains the formatted input string provided as a vararg.
---The vararg should be a string with _n_ formatting placeholders, and _n_ elements that will be handled via `string.format(unpack(arg))`.
---**WARNING:** Appears to be non-functional!
function MessageBox(...) end
---@vararg string
---Prints a debug message to the `AILog.txt` that contains the formatted input string provided as a vararg if the command `aiff script` has been used in the ingame console.
---The vararg should be a string with _n_ formatting placeholders, and _n_ elements that will be handled via `string.format(unpack(arg))`.
function ScriptMessage(...) end
---@vararg string
---Prints a debug message to the `AILog.txt` that contains the formatted input string provided as a vararg if the command `aiff script` has been used in the ingame console.
---The vararg should be a string with _n_ formatting placeholders, and _n_ elements that will be handled via `string.format(unpack(arg))`.
function DebugMessage(...) end
---@vararg string
---Prints a debug message that contains the formatted input string provided as a vararg directly to the `LogFile.txt`. Usually used as Error Log.
---The vararg should be a string with _n_ formatting placeholders, and _n_ elements that will be handled via `string.format(unpack(arg))`.
---@see string#format
---@see unpack
function OutputDebug(...) end
---@vararg string
---Prints an error log to `LogFile.txt` and `AILog.txt` including the contains the formatted input string provided as a vararg and a stack trace.
---The vararg should be a string with _n_ formatting placeholders, and _n_ elements that will be handled via `string.format(unpack(arg))`.
---@see string#format
---@see unpack
function ScriptError(...) end
---@param unit_table GameObjectWrapper[]
---Unpacks a table and prints its content to the `AILog.txt` if the command `aiff script` has been used in the ingame console.
function DebugPrintTable(unit_table) end

View File

@@ -0,0 +1,999 @@
-- ==================================================================
-- ==================================================================
-- _____ _ _ _ _
-- | ___| (_) | | | | | |
-- | |__ _ __ ___ _ __ _ _ __ ___ __ _| |_ | | | | __ _ _ __
-- | __| '_ ` _ \| '_ \| | '__/ _ \ / _` | __| | |/\| |/ _` | '__|
-- | |__| | | | | | |_) | | | | __/ | (_| | |_ \ /\ / (_| | |
-- \____/_| |_| |_| .__/|_|_| \___| \__,_|\__| \/ \/ \__,_|_|
-- | |
-- |_|
-- _____ _ ______
-- | ___| | | | _ \
-- | |__ _ __ ___ _ __ ___ _ _| | _ _ __ _| | | |___ ___
-- | __| '_ ` _ \| '_ ` _ \| | | | | | | | |/ _` | | | / _ \ / __|
-- | |__| | | | | | | | | | | |_| | |___| |_| | (_| | |/ / (_) | (__
-- \____/_| |_| |_|_| |_| |_|\__, \_____/\__,_|\__,_|___/ \___/ \___|
-- __/ |
-- |___/
-- ==================================================================
-- ==================================================================
---@diagnostic disable
---@class TaskForce
local TaskForce = {}
---@public
---@param targetingPrioritySetName string The name of the Targeting Priority Set as defined in the XML.
---@param categoryMask string The Category Mask of the units contained within the TaskForce to attach the Targeting Priority Set to.
---Attaches a given Targeting Priority Set to all units in the TaskForce that match the given Category Mask.
function TaskForce.Set_Targeting_Priorities(targetingPrioritySetName,
categoryMask)
end
---@public
---@return GameObject[] The units contained within the Task Force.
---Returns all units that are part of the task force. WARNING: The units may not be valid.
function TaskForce.Get_Unit_Table() end
---@public
---@return number The amount of units in the TaskForce's unit table.
---Returns the number of units in a task force.
function TaskForce.Get_Force_Count() end
---@public
---@param target GameObject The object to get the distance to.
---@return number The distance to the given game object.
function TaskForce.Get_Distance(target) end
---@public
---@overload fun()
---@overload fun(stage:PlanetObject)
---@param stage PlanetObject The planet to produce the taskforce on.
---@param unknownArgument boolean TODO: Find out what this does.
---Produces a task force.
function TaskForce.Produce_Force(stage, unknownArgument) end
---@public
---@param target GameObject The target to attack.
---Orders the task force to attack a given target.
function TaskForce.Attack_Target(target) end
---@public
---@param target GameObject The target to attack.
---@param unknownArgument number TODO: Find out what this does.
---@param shouldPathThroughObstacle boolean Should the task force ignore space obstacles.
---Orders the task force to attack a given target.
function TaskForce.Attack_Target(target, unknownArgument,
shouldPathThroughObstacle)
end
---@public
---@param target GameObject The target to guard.
---Orders the task force to guard a given target.
function TaskForce.Guard_Target(target) end
---@public
---@param target GameObject The target to reinforce (usually a position).
-----Orders the task force to reinforce (at) a given target.
function TaskForce.Reinforce(target) end
---@public
---@param target GameObject The target to move to (usually a position).
-----Orders the task force to move to a given target.
function TaskForce.Move_To(target) end
---@public
---@param gameObject GameObject The object to release.
---Releases a given unit from the task force.
function TaskForce.Release_Unit(gameObject) end
---@public
---@param abilityName string The ability to (de-)activate.
---@param shouldActivate boolean Activates the ability if true, deactivates if false.
---Activates or deactivates a given ability.
function TaskForce.Activate_Ability(abilityName, shouldActivate) end
---@public
---@return (PlanetObject|Position) The task force's staging area.
---Returns the task force's staging area.
function TaskForce.Get_Stage() end
---@public
---Lands the task force's units
function TaskForce.Land_Units() end
---@public
---Launches the task force's units into orbit.
function TaskForce.Launch_Units() end
---@public
---@return boolean Returns true if the invasion succeeded, false if the invasion failed.
---Invades a planet.
function TaskForce.Invade() end
---@public
---@param isRemovable boolean
---Adjusts whether the Goal the task force is associated with can be removed before it's been achieved.
function TaskForce.Set_As_Goal_System_Removable(isRemovable) end
---@class GameObject
---A wrapper around a single instance of a generic GameObjectType
local GameObject = {}
---@public
---@return GameObjectType
---Gets the type of the current game object wrapper.
function GameObject.Get_Type() end
---@public
---@param propertyFlag string A string representation of 1..n properties.
---@return boolean
---Tests if a GameObject has 1..n flags of the bitfield defined in GameObjectPropertiesType.xml set. Valid inputs are single items `"Turret"` or many `"Turret | InBase"`.
function GameObject.Has_Property(propertyFlag) end
---@public
---@return PlayerObject The PlayerObject owning the GameObject
---Returns the owner of the game object.
function GameObject.Get_Owner() end
---@public
---@return Position
---Returns the current position of the object in the world.
function GameObject.Get_Position() end
---@public
---@param categoryMask string The category mask to test for. Can be a single mask, or a combination: "CAPITAL" or "CAPITAL | SPACE_HERO"
---@return boolean
---Tests whether a given game object has a given category mask assigned to it.
function GameObject.Is_Category(categoryMask) end
---@public
---@return (GameObject|nil)
---Returns a game object if the unit has an attack target, nil else.
function GameObject.Get_Attack_Target() end
---@public
---@param abilityName string The ability to (de-)activate.
---@param shouldActivate boolean Activates the ability if true, deactivates if false.
---Activates or deactivates a given ability.
function GameObject.Activate_Ability(abilityName, shouldActivate) end
---@public
---@return boolean
---Tests whether a unit has active orders.
function GameObject.Has_Active_Orders() end
---@public
---@return number
---Returns a number between 0 and 1 representing the current percentage of hull points left.
function GameObject.Get_Hull() end
---@public
---@return number
---Returns a number between 0 and 1 representing the current percentage of shield points left.
function GameObject.Get_Shield() end
---@public
---@param target GameObject|Position
---@return number
function GameObject.Get_Distance(target) end
---@public
---@overload fun(target:Position|GameObject|PlanetObject):CommandBlock
---@param unit_list GameObject[]
---@param target Position|GameObject|PlanetObject
---@return CommandBlock
---Orders the GameObject to move to the given target. Returns a CommandBlock is marked as finished when the GameObject has arrived at target
function GameObject.Move_To(unit_list, target) end
---@public
---Despawns the GameObject
function GameObject.Despawn() end
---@public
---@param new_owner PlayerObject The PlayerObject the ownership will be transferred to
---Transfers ownership of a GameObject over to another Player
function GameObject.Change_Owner(new_owner) end
---@public
---@param behavior_id int The ID of the behavior
---@param enable boolean Enables the behavior if set to true. Disables it if it's false.
---Enables or disables a behavior defined in the XML entry of the GameObject
function GameObject.Enable_Behavior(behavior_id, enable) end
---@public
---@return GameObject Returns the parent object of the given GameObject. In galactic conquest this will return either a planet, a company or a container. In tactical mode it returns the squadron for single fighters.
function GameObject.Get_Parent_Object() end
---@public
---@return PlanetObject
---Returns the Planet the GameObject is located on. Can be nil if the GameObject is in Hyperspace
function GameObject.Get_Planet_Location() end
---@public
---@param hidden boolean Determines whether the GameObject is visible or invisible
---Makes the GameObject visible or invisible
function GameObject.Hide(hidden) end
---@public
---@param animation_name string The name of the animation
---@param should_repeat boolean Determines whether the animation gets repeated
---@param animation_index number The index of the animation, e.g. `3` to play `deploy_03.ala`
---Plays an animation on a GameObject. The animation must be present in the 3D model
function GameObject.Play_Animation(animation_name, should_repeat,
animation_index)
end
---@public
---@param abilityName string
---@return boolean
function GameObject.Is_Ability_Autofire(abilityName) end
---@public
---@param sfxEventName string
function GameObject.Play_SFX_Event(sfxEventName) end
---@public
---@return boolean
function GameObject.Is_Transport() end
---@public
---@param planet PlanetObject
---@return boolean
function GameObject.Can_Land_On_Planet(planet) end
---@public
---@return GameObjectType
function GameObject.Get_Game_Scoring_Type() end
---@public
---@param abilityName string
---@return boolean
function GameObject.Is_Ability_Ready(abilityName) end
---@public
---@param position Position|GameObject
function GameObject.Divert(position) end
---@public
---@return boolean
function GameObject.Is_On_Diversion() end
---@public
---@return GameObject[]
function GameObject.Get_Garrisoned_Units() end
---@public
---@param target GameObject
---@return boolean
function GameObject.Should_Switch_Weapons(target) end
---@public
---@param allow_spawn boolean
function GameObject.Set_Garrison_Spawn(allow_spawn) end
---@public
---@param disable boolean
function GameObject.Disable_Capture(disable) end
---@public
---@overload fun(damageAmount:number)
---@param damageAmount number
---@param hardpoint string
function GameObject.Take_Damage(damageAmount, hardpoint) end
---@public
---@overload fun(callbackFunction:function, range:number)
---@param callbackFunction fun(obj:GameObject, trigger:GameObject)
---@param range number
---@param player PlayerObject
function GameObject.Event_Object_In_Range(callbackFunction, range, player) end
---@public
---@param callbackFunction function
function GameObject.Cancel_Event_Object_In_Range(callbackFunction) end
---@public
--- The returned command block will never finish, even if the move is done and there is no target to be attacked.
---@overload fun(target:GameObject):CommandBlock
---@param unit_list GameObject[]
---@param target GameObject|Position
---@return CommandBlock
function GameObject.Attack_Move(unit_list, target) end
---@public
---@overload fun(target:GameObject):CommandBlock
---@param unit_list GameObject[]
---@param target GameObject
---@return CommandBlock
function GameObject.Attack_Target(unit_list, target) end
---@public
---@overload fun(target:GameObject):CommandBlock
---@param unit_list GameObject[]
---@param target GameObject|Position
---@return CommandBlock
function GameObject.Guard_Target(unit_list, target) end
---@public
--- Returns true if the unit can currently move (is false e.g. during hyperspace in)
---@return boolean
function GameObject.Can_Move() end
---@public
--- Stops the unit.
function GameObject.Stop() end
---@public
--- Movability on or off
---@param suspend boolean
function GameObject.Suspend_Locomotor(suspend) end
---@public
--- Activates targeted ability
---@param ability_name string
---@param turn_on GameObject|Position|boolean A target to turn on targeted abilities or a boolean.
function GameObject.Activate_Ability(ability_name, turn_on) end
---@public
--- Finishes ability cooldowns
function GameObject.Reset_Ability_Counter() end
---@public
---@param ability_name string
---@param autofire boolean
function GameObject.Set_Single_Ability_Autofire(ability_name, autofire) end
---@public
---@param autofire boolean
function GameObject.Set_All_Abilities_Autofire(autofire) end
---@public
---@param ability_name string
---@return boolean
function GameObject.Is_Ability_Active(ability_name) end
---@public
---@param ability_name string
---@return boolean
function GameObject.Has_Ability(ability_name) end
---@public
---@param ability_name string
---@param optional number
function GameObject.Force_Ability_Recharge(ability_name, optional) end
---@public
---@param ability_name string
function GameObject.Cancel_Ability(ability_name) end
---@public
---@return boolean
function GameObject.Are_Engines_Online() end
---@public
---@param speed number
function GameObject.Override_Max_Speed(speed) end
---@public
--- Makes the unit hyperspace into battle with a delay by the given number of frames.
---@param delay number The number of frames before the hyperspace animation is started
function GameObject.Cinematic_Hyperspace_In(delay) end
---@public
--- Makes unit leave into hyperspace. Will be removed from galactic level as well if the optional parameter is not set to false.
---@param remove_from_campaign boolean Default is true
function GameObject.Hyperspace_Away(remove_from_campaign) end
---@public
function GameObject.Cancel_Hyperspace() end
---@public
--- Allow or prevent AI usage. In tactical battles this crashes the game if the unit has no active AI! When the faction's AI changes, the unit is AI usable again.
---@param prevent boolean
function GameObject.Prevent_AI_Usage(prevent) end
---@public
--- Shall object be displayed in Cinematic?
---@param keep_visible boolean
function GameObject.In_End_Cinematic(keep_visible) end
---@public
---@param target Position|GameObject
function GameObject.Teleport(target) end
---@public
--- Teleport object make it face the same way as the target
---@param target GameObject
function GameObject.Teleport_And_Face(target) end
---@public
---@param target Position|GameObject
function GameObject.Face_Immediate(target) end
---@public
---@param target Position|GameObject
function GameObject.Turn_To_Face(target) end
---@public
--- Prevent automatically firing at targets in range
---@param prevent boolean
function GameObject.Prevent_Opportunity_Fire(prevent) end
---@public
--- Completely prevent a unit from firing
---@param prevent boolean
function GameObject.Prevent_All_Fire(prevent) end
---@public
--- Stops the unit from taking any damage
---@param invulnerable boolean
function GameObject.Make_Invulnerable(invulnerable) end
---@public
---@param check boolean
function GameObject.Set_Check_Contested_Space(check) end
---@public
function GameObject.Lock_Current_Orders() end
---@public
function GameObject.Unlock_Current_Orders() end
---@public
---@return boolean
function GameObject.Is_In_Asteroid_Field() end
---@public
---@return boolean
function GameObject.Is_In_Ion_Storm() end
---@public
---@return boolean
function GameObject.Is_In_Nebula() end
---@public
---@param enable boolean
function GameObject.Enable_Dynamic_LOD(enable) end
---@public
--- Only for fleets with land units in orbit over an enemy planet (is used in FoC campaign but doesn't seem to work)
function GameObject.Invade() end
---@public
---@param unknown boolean
function GameObject.Set_In_Limbo(unknown) end
---@public
--- Returns a list of the projectile types the unit uses
---@return GameObjectType[]
function GameObject.Get_All_Projectile_Types() end
---@public
---@return boolean
function GameObject.Is_Selectable() end
---@public
--- Sets a unit to be selectable or not selectable by the player
---@param selectable boolean
function GameObject.Set_Selectable(selectable) end
---@public
--- Returns the projectile type the unit is currently using
---@return GameObjectType
function GameObject.Get_Current_Projectile_Type() end
---@public
---@param target GameObject
---@return boolean
function GameObject.Is_Good_Against(target) end
---@public
---@return boolean
function GameObject.Is_In_Garrison() end
---@public
--- Returns true if the object contains any garrisoned units
---@return boolean
function GameObject.Has_Garrison() end
---@public
--- Eject all garrisoned units
function GameObject.Eject_Garrison() end
---@public
--- Make a garrisoned unit leave its garrison
function GameObject.Leave_Garrison() end
---@public
---@return boolean
function GameObject.Can_Garrison_Fire() end
---@public
--- Can the unit be garrisoned in the given object?
---@param target GameObject
---@return boolean
function GameObject.Can_Garrison(target) end
---@public
--- Garrison the unit in the given object
---@param target GameObject
function GameObject.Garrison(target) end
---@public
function GameObject.Play_Cinematic_Engine_Flyby() end
---@public
---@overload fun(event_name:string)
---@param event_name string
---@param optional number
function GameObject.Stop_SFX_Event(event_name, optional) end
---@public
---@overload fun(type:string|GameObjectType)
---@param type string|GameObjectType
---@param optional string
---@return GameObject
function GameObject.Attach_Particle_Effect(type, optional) end
---@public
---@return boolean
function GameObject.Has_Attack_Target() end
---@public
---@param emitter string
---@param show boolean
function GameObject.Show_Emitter(emitter, show) end
---@public
--- Put a small arrow highlight on top of the object
---@overload fun(highlight:boolean)
---@param highlight boolean
---@param optional number Seems to change the distance of the arrow to the object
function GameObject.Highlight_Small(highlight, optional) end
---@public
--- Put an arrow highlight on top of the object
---@overload fun(highlight:boolean)
---@param highlight boolean
---@param optional number Seems to change the distance of the arrow to the object
function GameObject.Highlight(highlight, optional) end
---@public
---@param target AITarget
---@return CommandBlock
function GameObject.Explore_Area(target) end
---@public
--- Check if a space battle should be initiated, e.g. after spawning a fleet at an enemy planet (only works on fleets and planets)
function GameObject.Force_Test_Space_Conflict() end
---@public
--- Keep a unit from dying (it can still be damaged)
---@param cannot_be_killed boolean
function GameObject.Set_Cannot_Be_Killed(cannot_be_killed) end
---@public
--- Returns the hint set for this object in the map editor or nil.
---@return string|nil
function GameObject.Get_Hint() end
---@public
--- Fire death star in tactical mode
function GameObject.Fire_Tactical_Superweapon() end
---@public
--- Test if death star is ready to fire
---@return boolean
function GameObject.Is_Tactical_Superweapon_Ready() end
---@public
---@param bone_name string
---@return Position
function GameObject.Get_Bone_Position(bone_name) end
---@public
---@param lock boolean
function GameObject.Lock_Build_Pad_Contents(lock) end
---@public
--- Only used on fleets
---@param number number
function GameObject.Destroy_Contained_Objects(number) end
---@public
--- Only used on fleets
---@param object_type GameObjectType
---@return boolean
function GameObject.Contains_Object_Type(object_type) end
---@public
--- Only used on fleets
---@return number
function GameObject.Get_Contained_Object_Count() end
---@public
--- (tactical only)
---@param game_object GameObject
---@return number
function GameObject.Get_AI_Power_Vs_Unit(game_object) end
---@public
--- Use in tactical. This kills the corresponding GC object
function GameObject.Mark_Parent_Mode_Object_For_Death() end
---@public
---@param number number
function GameObject.Set_Importance(number) end
---@public
function GameObject.Service_Wrapper() end
---@public
--- Possibly only for build pad contents
function GameObject.Sell() end
---@public
--- Use on build pads or MDUs
---@return GameObject
function GameObject.Get_Build_Pad_Contents() end
---@public
--- Only for fleets
---@return GameObject[]
function GameObject.Get_Contained_Heroes() end
---@public
--- Only for fleets
---@return boolean
function GameObject.Contains_Hero() end
---@public
--- Use on spawned special weapon or on space station with special weapon
---@param target GameObject
---@param user PlayerObject
function GameObject.Fire_Special_Weapon(target, user) end
---@public
---@return number
function GameObject.Get_Rate_Of_Damage_Taken() end
---@public
--- Expected time til death based on damage taken.
---@return number
function GameObject.Get_Time_Till_Dead() end
---@public
---@param number number
function GameObject.Set_Targeting_Stickiness_Time_Threshold(number) end
---@public
---@param targeting_priority_set string
function GameObject.Set_Targeting_Priorities(targeting_priority_set) end
---@public
--- Obeject needs UNIT_AI behavior
---@param prefer boolean
function GameObject.Set_Prefer_Ground_Over_Space(prefer) end
---@public
--- Normalized with respect to unit's total energy
---@return number Float between 0 and 1
function GameObject.Get_Energy() end
---@public
--- Normalized with respect to unit's total health
---@return number Float between 0 and 1
function GameObject.Get_Health() end
---@public
function GameObject.Release() end
---@public
--- Use on buildpads and MDUs. Takes a UC type
---@param type string|GameObjectType
---@param optional boolean
function GameObject.Build(type, optional) end
---@class Position
---Represents a position in the world.
local Position = {}
---@class GameObjectType
---A generic GameObjectType that represents a C++ GameObjectTypeClass
local GameObjectType = {}
---@public
---@return string
---Retrieves the XML name of a GameObjectType
function GameObjectType.Get_Name() end
---@public
---@return boolean
function GameObjectType.Is_Hero() end
---@public
---@return number
function GameObjectType.Get_Build_Cost() end
---@public
--- Returns the AI_Combat_Power
---@return number
function GameObjectType.Get_Combat_Rating() end
---@public
--- Returns required tech level
---@return number
function GameObjectType.Get_Tech_Level() end
---@public
--- Returns level of a starbase type
---@return number
function GameObjectType.Get_Base_Level() end
---@public
---@param player PlayerObject
---@return boolean
function GameObjectType.Is_Affiliated_With(player) end
---@public
---@param player PlayerObject
---@return boolean
function GameObjectType.Is_Build_Locked(player) end
---@public
---@param player PlayerObject
---@return boolean
function GameObjectType.Is_Obsolete(player) end
---@public
---@return number
function GameObjectType.Get_Tactical_Build_Cost() end
---@public
---@return number
function GameObjectType.Get_Score_Cost_Credits() end
---@public
---@return number
function GameObjectType.Get_Max_Range() end
---@public
---@return number
function GameObjectType.Get_Min_Range() end
---@public
---@return number
function GameObjectType.Get_Bribe_Cost() end
---@public
--- This only makes sense for projectile types though it can be used for others as well.
---@return boolean
function GameObjectType.Is_Affected_By_Missile_Shield() end
---@public
--- This only makes sense for projectile types though it can be used for others as well.
---@return boolean
function GameObjectType.Is_Affected_By_Laser_Defense() end
---@class GameObjectWrapper
---A generic wrapper around a GameObject
local GameObjectWrapper = {}
---@public
---Returns `true` if the contained game object is valid.
---@return boolean
function GameObjectWrapper.Is_Valid() end
---@class PlayerObject
---A wrapper around a player (AI or human) taking control of a faction.
local PlayerObject = {}
---@public
---@return boolean
---Tests whether a PlayerObject is human.
function PlayerObject.Is_Human() end
---@public
---@param playerObject PlayerObject
---@return boolean
---Tests whether a player object is an enemy of another player object.
function PlayerObject.Is_Enemy(playerObject) end
---@public
---@param amount number
function PlayerObject.Give_Money(amount) end
---@public
---@return number
function PlayerObject.Get_ID() end
---@public
---@param playerObject PlayerObject
---@return boolean
function PlayerObject.Is_Ally(playerObject) end
---@public
--- Enables Advisor Hints
---@param mode string Is "space" or "ground"
---@param enable boolean
function PlayerObject.Enable_Advisor_Hints(mode, enable) end
---@public
--- Returns an enemy player (may only ever returns Rebel or Empire, in any case there is not much point using it outside the base EaW though it might be related to the Primary Enemy tag)
---@return PlayerObject
function PlayerObject.Get_Enemy() end
---@public
--- Forces player to select object
---@param object GameObject
function PlayerObject.Select_Object(object) end
---@public
--- Activates the AI for the player
function PlayerObject.Enable_As_Actor() end
---@public
--- Enables retreat event. Returns false if player is not able to retreat.
---@return boolean
function PlayerObject.Retreat() end
---@public
--- returns the displayed faction name
---@return string
function PlayerObject.Get_Name() end
---@public
--- Returns xml faction name
---@return string
function PlayerObject.Get_Faction_Name() end
---@public
---@return number
function PlayerObject.Get_Tech_Level() end
---@public
function PlayerObject.Give_Random_Sliceable_Tech() end
---@public
--- Gets reset with any game mode changes, in particular at the end of every tactical battle!
---@param player PlayerObject
function PlayerObject.Make_Ally(player) end
---@public
--- Gets reset with any game mode changes, in particular at the end of every tactical battle!
---@param player PlayerObject
function PlayerObject.Make_Enemy(player) end
---@public
--- Returns the player's space station in space tactical
---@return GameObject
function PlayerObject.Get_Space_Station() end
---@public
--- Team ID in skirmish
---@return number
function PlayerObject.Get_Team() end
---@public
--- Clan ID in skirmish
---@return number
function PlayerObject.Get_Clan_ID() end
---@public
--- Removes the orbital bombardment. Countdown for usage will continue and not be reset if the bombardment is re-enabled.
---@param remove boolean
function PlayerObject.Remove_Orbital_Bombardment(remove) end
---@public
--- Disable the bombardment. The countdown for usage will continue.
---@param disable boolean
function PlayerObject.Disable_Orbital_Bombardment(disable) end
---@public
---@param set boolean
function PlayerObject.Set_Sabotage_Tutorial(set) end
---@public
---@param set boolean
function PlayerObject.Set_Black_Market_Tutorial(set) end
---@public
---@return string Returns "Easy", "Normal" or "Hard"
function PlayerObject.Get_Difficulty() end
---@public
--- The parameter logic is reversed here. The bombing run is disabled if false is passed.
---@param enable boolean
function PlayerObject.Disable_Bombing_Run(enable) end
---@public
---@param object_type GameObjectType
function PlayerObject.Unlock_Tech(object_type) end
---@public
function PlayerObject.Get_GameSpy_Stats_Player_ID() end
---@public
---@return number
function PlayerObject.Get_Credits() end
---@public
--- For AI player (with galactic AI) only. Releases credits for spending in tactical mode.
---@param amount number
function PlayerObject.Release_Credits_For_Tactical(amount) end
---@public
---@param level number
function PlayerObject.Set_Tech_Level(level) end
---@class PlanetObject : GameObject
local PlanetObject = {}
---@public
---@return boolean
function PlanetObject.Get_Is_Planet_AI_Usable() end
---@public
---@return boolean
function PlanetObject.Is_Corrupted() end
---@public
--- Check if a space battle should be initiated, e.g. after spawning a fleet at an enemy planet (only works on fleets and planets)
function PlanetObject.Force_Test_Space_Conflict() end
---@public
---@return boolean
function PlanetObject.Is_Planet_Destroyed() end
---@public
--- Returns the indigenous unit type of that planet affiliated with the given player
---@return GameObjectType
function PlanetObject.Get_Affiliated_Indigenous_Type(player) end
---@public
---@return GameObjectType
function PlanetObject.Get_Next_Starbase_Type() end
---@public
---@return PlayerObject
function PlanetObject.Get_Final_Blow_Player() end
---@public
---@return number
function PlanetObject.Get_Starbase_Level() end
---@class GameScoringType
local GameScoringType = {}
---@public
---@return number
function GameScoringType.Get_Score_Cost_Credits() end
---@public
---@return number
function GameScoringType.Get_Combat_Rating() end
---@public
---@return number
function GameScoringType.Get_Build_Cost() end
---@class CommandBlock
local CommandBlock = {}
---@public
---@return table
---Returns the result of the CommandBlock
function CommandBlock.Result() end
---@public
---@return boolean
---Returns a boolean indicating whether the CommandBlock has finished its command
function CommandBlock.IsFinished() end
---@class AITarget
local AITarget = {}
---@public
--- Returns corresponding game object if there is one (e.g. a targeted unit or planet).
---@return GameObject
function AITarget.Get_Game_Object() end
---@public
---@param position Position
---@return number
function AITarget.Get_Distance(position) end
-- return TaskForce, GameObject, Position, GameObjectType, GameObjectWrapper, PlayerObject, PlanetObject, GameScoringType, CommandBlock

View File

@@ -1,152 +0,0 @@
---@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

View File

@@ -0,0 +1,53 @@
---@diagnostic disable
-- ==================================================
-- I/O
-- ==================================================
---@public
---Appends the text to file.
---@param file string The file name.
---@param text string The text to append.
function WriteToFile(file, text) end
-- ==================================================
-- Finders
-- ==================================================
---Finds all Players in the game.
---@return PlayerObject[]
function Get_All_Players() end
-- ==================================================
-- UI
-- ==================================================
---@public
---Adds the specified text to the tutorial text box.
---A new text key will be appended as a new line.
---An existing text key will be overwritten.
---@param key string The key to use.
---@param text string The text to add.
---@param duration number The duration to keep text on screen, defaults to -1 (infinite).
---@param r number The red color component.
---@param g number The green color component.
---@param b number The blue color component.
---@param a number The alpha channel component.
function Add_Tutorial_Text(key, text, duration, r, g, b, a) end
---@public
---Removes the specified text key from the tutorial text box.
---@param key string The key to remove.
function Remove_Tutorial_Text(key) end
-- ==================================================
-- GameObjectType extensions
-- ==================================================
---@class GameObjectType
---A generic GameObjectType that represents a C++ GameObjectTypeClass
local GameObjectType = {}
---@public
---Returns the display name for the object type.
---@return string
function GameObjectType.Get_Display_Name() end

View File

@@ -39,20 +39,21 @@
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
---@diagnostic disable
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
@@ -64,7 +65,6 @@ 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
@@ -72,9 +72,9 @@ function BlockOnCommand(block, max_duration, alternate_break_func)
end
break_block = false
ThreadValue.Set("BlockStart", GetCurrentTime())
repeat
if break_block == true then
break_block = false
@@ -82,9 +82,9 @@ function BlockOnCommand(block, max_duration, alternate_break_func)
end
PumpEvents()
if ((max_duration ~= nil) and (max_duration ~= -1)
and (GetCurrentTime() - ThreadValue("BlockStart") > max_duration)) then
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
@@ -93,8 +93,7 @@ function BlockOnCommand(block, max_duration, alternate_break_func)
--MessageBox("%s-- had a break func and it returned true", tostring(Script))
return nil
end
until (block.IsFinished() == true)
until (block.IsFinished() == true)
PumpEvents()
@@ -116,7 +115,6 @@ function TestCommand(block)
end
function PumpEvents()
if Object and type(Object) == "userdata" then
Object.Service_Wrapper()
end
@@ -130,26 +128,26 @@ function PumpEvents()
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
else
CurrentEvent()
end
if Script.Debug_Should_Issue_Event_Alert() and DebugEventAlert then
DebugEventAlert(CurrentEvent, EventParams)
end
CurrentEvent = GetEvent()
end
ThreadValue.Set("InPumpEvents", false)
@@ -182,25 +180,24 @@ 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)
-- Machine independent modulus function
function Simple_Mod(a, b)
--return a-b*math.floor(a/b)
return a-b*Dirty_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))
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)
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
@@ -219,41 +216,38 @@ function GetChanceAllowed(difficulty)
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)
-- 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
}
_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
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
for j, important_entry in pairs(very_important_tables) do
if important_entry == g_entry then
keep_table = true
end
@@ -261,27 +255,22 @@ function Flush_G()
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
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

@@ -39,16 +39,17 @@
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
---@diagnostic disable
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

View File

@@ -4,6 +4,7 @@ require("PGBase")
---@field R number The red component.
---@field G number The green component.
---@field B number The blue component.
---@field A number The alpha component.
ColorStruct = {}
ColorStruct.__index = ColorStruct
@@ -11,17 +12,19 @@ ColorStruct.__index = ColorStruct
---@param r number The red component
---@param g number The green component
---@param b number The blue component
---@param a number The alpha component
---@return ColorStruct
function ColorStruct:New(r, g, b)
function ColorStruct:New(r, g, b, a)
local self = setmetatable({}, ColorStruct)
self.R = Clamp(r, 0, 1)
self.G = Clamp(g, 0, 1)
self.B = Clamp(b, 0, 1)
self.A = Clamp(a, 0, 1)
return self
end
function ColorStruct:Debug()
return string.format("RGB (%d, %d, %d)", self.R, self.G, self.B)
return string.format("RGB (%d, %d, %d, %d)", self.R, self.G, self.B, self.A)
end

View File

@@ -1,4 +1,28 @@
require("FactionStruct")
require("GameManager/FactionStruct")
require("GameManager/GUIManager")
require("GameManager/TutorialTextManager")
-- ==================================================
-- Define Manager constants
-- ==================================================
---The singleton GUI Manager.
---@type GUIManager
GUIManager = GUIManager:New()
---The singleton Tutorial Text Manager.
---@type TutorialTextManager
TextManager = TutorialTextManager:New()
---The local player.
---@type PlayerObject|nil
LocalPlayer = nil
---Frag Index of Kill Stat Tables
FragIndex = 1
---Death Index of Kill Stat Tables
DeathIndex = 2
-- ==================================================
-- Define Faction constants
@@ -15,7 +39,7 @@ function DefineFactions()
local RebelFaction = FactionStruct:New("REBEL")
RebelFaction.DisplayName = "Rebellion"
RebelFaction.Color = ColorStruct:New(1, 0, 0)
RebelFaction.Color = ColorStruct:New(1, 0, 0, 1)
RebelFaction.Icon = "I_ICON_SPECTATOR_REBEL.TGA"
RebelFaction.LandStartUnitName = "Rebel_Infantry_Squad"
RebelFaction.SpaceStartUnitName = "Rebel_X-Wing_Squadron"
@@ -26,7 +50,7 @@ function DefineFactions()
local EmpireFaction = FactionStruct:New("EMPIRE")
EmpireFaction.DisplayName = "Empire"
EmpireFaction.Color = ColorStruct:New(0, 1, 1)
EmpireFaction.Color = ColorStruct:New(0, 1, 1, 1)
EmpireFaction.Icon = "I_ICON_SPECTATOR_EMPIRE.TGA"
EmpireFaction.LandStartUnitName = "Imperial_Stormtrooper_Squad"
EmpireFaction.SpaceStartUnitName = "TIE_Interceptor_Squadron_Container"
@@ -37,7 +61,7 @@ function DefineFactions()
local UnderworldFaction = FactionStruct:New("UNDERWORLD")
UnderworldFaction.DisplayName = "Zann Consortium"
UnderworldFaction.Color = ColorStruct:New(1, 1, 0)
UnderworldFaction.Color = ColorStruct:New(1, 1, 0, 1)
UnderworldFaction.Icon = "I_ICON_SPECTATOR_UNDERWORLD.TGA"
UnderworldFaction.LandStartUnitName = "Underworld_Merc_Squad"
UnderworldFaction.SpaceStartUnitName = "StarViper_Team"
@@ -47,7 +71,7 @@ function DefineFactions()
local HuttFaction = FactionStruct:New("HUTTS")
HuttFaction.DisplayName = "Hutt Cartel"
HuttFaction.Color = ColorStruct:New(1, 0, 1)
HuttFaction.Color = ColorStruct:New(1, 0, 1, 1)
HuttFaction.Icon = "I_ICON_SPECTATOR_HUTTS.TGA"
HuttFaction.LandStartUnitName = "Hutt_Soldier_Squad"
HuttFaction.SpaceStartUnitName = "V_Wing_Squadron_Container"
@@ -57,7 +81,7 @@ function DefineFactions()
local PirateFaction = FactionStruct:New("PIRATES")
PirateFaction.DisplayName = "Pirates"
PirateFaction.Color = ColorStruct:New(0, 1, 0)
PirateFaction.Color = ColorStruct:New(0, 1, 0, 1)
PirateFaction.Icon = "I_ICON_SPECTATOR_PIRATES.TGA"
PirateFaction.LandStartUnitName = "Pirate_Soldier_Squad"
PirateFaction.SpaceStartUnitName = "Pirate_Fighter_Squadron"
@@ -70,6 +94,41 @@ 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
-- ==================================================
---Space marker used to identify the spectator player.
Spectator_Marker_Space = "Spectator_Reveal_Marker"
---Land marker used to identify the spectator player.
Spectator_Marker_Land = "Spectator_Reveal_Marker_Land"
-- ==================================================
-- Define UI Constants
-- ==================================================
@@ -77,12 +136,16 @@ Factions = DefineFactions()
---GUI Component to display game time.
UI_GameTime = "s_select_41"
---GUI Components to display Team 1 credits.
---GUI Component to display Team 1 faction icon.
UI_IconT1 = "s_health_42"
--- GUI Component to display Team 1 credits text.
UI_CreditsT1 = "s_health_41"
---GUI Component to display Team 2 credits.
---GUI Component to display Team 2 faction icon.
UI_IconT2 = "s_shield_42"
---GUI Component to display Team 2 credits text.
UI_CreditsT2 = "s_shield_41"
---GUI Component to enable/disable population.
@@ -90,16 +153,3 @@ 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

@@ -1,4 +1,4 @@
require("ColorStruct")
require("GameManager/ColorStruct")
---@class FactionStruct
---@field Name string Faction name.
@@ -18,7 +18,7 @@ function FactionStruct:New(name)
self.Name = name
self.DisplayName = name
self.Color = ColorStruct:New(1, 1, 1)
self.Color = ColorStruct:New(1, 1, 1, 1)
self.Icon = ""
self.LandStartUnitName = ""
self.SpaceStartUnitName = ""

View File

@@ -1,10 +1,7 @@
require("PGBase")
require("GameManager.Constants")
require("GameManager/Utilities")
---@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
@@ -13,22 +10,15 @@ GUIManager.__index = 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
---@param isSpectator boolean Whether to initialize in spectator mode.
function GUIManager:InitSkirmish(isSpectator)
self:InitTactical()
GUI_Component_Visibility(UI_GameTime, true)
if self.SkirmishGame.IsLocalSpectator then
if isSpectator then
GUI_Component_Visibility(UI_CreditsT1, true)
GUI_Component_Visibility(UI_IconT1, true)
@@ -41,18 +31,12 @@ function GUIManager:InitSkirmish(game)
end
---Initializes the GUI Manager for Tactical mode.
---@param game TacticalGameClass The Tactical Game state.
function GUIManager:InitTactical(game)
self.TacticalGame = game
function GUIManager:InitTactical()
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)
@@ -75,120 +59,43 @@ function GUIManager:Reset()
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)
---@param gameTime number The current game time.
function GUIManager:DisplayGameTime(gameTime)
local timeText = FormatTime(gameTime)
local text = string.format("Time: %s", timeText)
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
---Displays credits for the specified team
---@param team TeamStruct The team.
---@param credits number The team credits.
---@param income number The team income.
function GUIManager:DisplayTeamCredits(team, credits, income)
local uiText, uiIcon = "", ""
if team.Number == 1 then
uiText = UI_CreditsT1
uiIcon = UI_IconT1
elseif team.Number == 2 then
uiText = UI_CreditsT2
uiIcon = UI_IconT2
else
return
end
if not skirmishGame.IsLocalSpectator then
return
local positiveText = "+"
if income < 0 then
positiveText = ""
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 = "+"
local text = string.format("Team %d: $ %d (%s%d)", team.Number, credits, positiveText, income)
local icon = team.Faction.Icon
local color = team.Faction.Color
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
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

View File

@@ -1,11 +1,14 @@
require("PGBase")
require("GameManager.PlayerClass")
require("GameManager.UnitClass")
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.
---@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
---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]
-- ==================================================
-- Unit event handlers
-- ==================================================
if player then
player:AddUnit(objectType)
---Adds a built unit to the Unit table.
---@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
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

View File

@@ -1,6 +1,6 @@
require("PGBase")
require("GameManager.ResourceClass")
require("GameManager.UnitClass")
require("GameManager/ResourceClass")
require("GameManager/UnitClass")
---@class PlayerClass
---@field Id integer Player ID.
@@ -45,10 +45,10 @@ function PlayerClass:Service()
-- 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
if TestValid(unit.GameObject) then
assignedUnits[unit.GameObject] = true
else
unit:SetDead()
unit:SetDead(-1)
end
end
@@ -80,12 +80,15 @@ 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)
table.insert(self.Units, unit)
ScriptMessage("Unit Added: %s", unit:Debug())
return unit
end
---Gets all Reinforcement Units for this Player.

View File

@@ -20,7 +20,6 @@ end
---@param credits number
function ResourceClass:SetCredits(credits)
self.LastCredits = self.Credits
self.Credits = credits
if credits < 0 then
self.Credits = 0

View File

@@ -1,99 +1,26 @@
require("PGBase")
require("GameManager.Constants")
require("GameManager.TacticalGameClass")
require("GameManager.SpectatorStruct")
require("GameManager.TeamStruct")
require("GameManager/TacticalGameClass")
require("GameManager/SpectatorStruct")
require("GameManager/TeamStruct")
---@class SkirmishGameClass
---@field TacticalGame TacticalGameClass The underlying Tactical Game context.
---@class SkirmishGameClass : TacticalGameClass
---@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
setmetatable(SkirmishGameClass, { __index = TacticalGameClass })
---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)
local self = setmetatable(TacticalGameClass:New(gameMode), 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()
---@cast self SkirmishGameClass
self:_FindSpectators()
self.IsLocalSpectator = self:_GetLocalSpectator()
self:_BuildTeams()
ScriptMessage("Skirmish Game initialized!")
return self
@@ -101,31 +28,144 @@ end
---Services the Skirmish Game.
function SkirmishGameClass:Service()
ScriptMessage("Servicing Skirmish Game...")
-- Service TacticalGame
self.TacticalGame:Service()
TacticalGameClass.Service(self)
ScriptMessage("Servicing Skirmish Game...")
for _, team in pairs(self.Teams) do
local credits = self:CalculateTeamTotalCredits(team.Id)
local income = self:CalculateTeamTotalIncome(team.Id)
GUIManager:DisplayTeamCredits(team, credits, income)
end
end
-- ==================================================
-- Unit event handlers
-- ==================================================
---Displays unit built combat text.
---@param unit UnitClass The built unit.
function SkirmishGameClass:CombatTextUnitBuilt(unit)
if not self.IsLocalSpectator then
-- No Text feed for combatants
return
end
local owner = self.Players[unit.OwnerId]
if not owner then
return
end
local team = self.Teams[owner.TeamId]
if not team then
return
end
local timeText = FormatTime(unit.BuildTime)
local text = string.format("%s [T%d %s]: BUILT %s ($ %d)", timeText, team.Number, team.Faction.DisplayName,
unit.DisplayName, 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)
TextManager:SetCurrentView("combat_feed")
end
---Displays killed unit combat text.
---@param unit UnitClass The killed unit.
function SkirmishGameClass:CombatTextUnitKilled(unit)
-- Right now, we don't really care about seeing killed units in the combat feed
-- If we want it in the future, uncomment the lines below.
return
-- if not self.IsLocalSpectator then
-- -- No Text feed for combatants
-- return
-- end
-- local owner = self.Players[unit.OwnerId]
-- if not owner then
-- return
-- end
-- local killer = self.Players[unit.KillerId]
-- local timeText = FormatTime(unit.DeathTime)
-- if not killer then
-- local team = self.Teams[owner.TeamId]
-- if not team then
-- return
-- end
-- local text = string.format("%s\t<T%d> [%s]: MISTAKES WERE MADE %s", timeText, team.Number, owner.Name,
-- unit.DisplayName)
-- local color = ColorStruct:New(0.8, 0.8, 0.8, 1)
-- self.CombatFeed:Append(text, -1, color)
-- else
-- local team = self.Teams[killer.TeamId]
-- if not team then
-- return
-- end
-- local text = string.format("%s\t<T%d> [%s]: KILLED %s (%s)", timeText, team.Number, killer.Name, unit
-- .DisplayName,
-- 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
-- ==================================================
-- Player functions
-- ==================================================
---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
return self.Spectators[id]
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
return self.Teams[id]
end
---Gets the Team of the Player ID.
---@param id integer Player ID.
---@return TeamStruct|nil
function SkirmishGameClass:GetTeamForPlayer(id)
local player = self.Players[id]
if not player then
return nil
end
return self.Teams[player.TeamId]
end
---Gets the Players for the Team ID.
@@ -135,7 +175,7 @@ function SkirmishGameClass:GetPlayersOnTeam(id)
---@type PlayerClass[]
local players = {}
for playerId, player in pairs(self.TacticalGame.Players) do
for playerId, player in pairs(self.Players) do
if player.TeamId == id then
players[playerId] = player
end
@@ -144,6 +184,10 @@ function SkirmishGameClass:GetPlayersOnTeam(id)
return players
end
-- ==================================================
-- Team functions
-- ==================================================
---Calculates the Team ID's current total credits.
---@param id integer Team ID.
---@return integer
@@ -171,3 +215,193 @@ function SkirmishGameClass:CalculateTeamTotalIncome(id)
return totalIncome
end
--- ==================================================
--- Stat tables
--- ==================================================
---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.
function SkirmishGameClass:UpdateTacticalKillStatsTable(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()
if self.IsLocalSpectator then
local killerTeam = self:GetTeamForPlayer(killerId)
if killerTeam and killerTeam.Number == 1 then
-- For the spectator, let's show Team 1's kills on the left side
killerId = LocalPlayer.Get_ID()
end
end
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 ownerId = unit.OwnerId
if self.IsLocalSpectator then
local ownerTeam = self:GetTeamForPlayer(ownerId)
if ownerTeam and ownerTeam.Number == 1 then
-- For the spectator, let's show Team 1's lost units on the left side
ownerId = LocalPlayer.Get_ID()
end
else
-- Non-spectator: show all teammate deaths as local player's losses
local ownerTeam = self:GetTeamForPlayer(ownerId)
local localTeam = self:GetTeamForPlayer(LocalPlayer.Get_ID())
if ownerTeam and localTeam and ownerTeam.Id == localTeam.Id then
ownerId = LocalPlayer.Get_ID()
end
end
local ownerEntry = deathEntry[ownerId]
if not ownerEntry then
ownerEntry = {}
deathEntry[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
-- ==================================================
-- Private functions
-- ==================================================
---@private
---Finds all spectators in the game.
function SkirmishGameClass:_FindSpectators()
---@type SpectatorStruct[]
local spectators = {}
---@type string
local spectatorMarker = nil
if StringCompare(self.GameMode, "Space") then
spectatorMarker = Spectator_Marker_Space
elseif StringCompare(self.GameMode, "Land") then
spectatorMarker = Spectator_Marker_Land
end
if spectatorMarker 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
self.Spectators = spectators
end
---@private
---Gets whether the local player is a spectator.
---@returns boolean
function SkirmishGameClass:_GetLocalSpectator()
if not LocalPlayer then
return false
end
return self:GetSpectator(LocalPlayer.Get_ID()) ~= nil
end
---@private
---Builds all teams in the game.
function SkirmishGameClass:_BuildTeams()
---@type TeamStruct[]
local teams = {}
-- Build combatant player teams
for playerId, player in pairs(self.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
self.Teams = teams
end

View File

@@ -1,13 +1,15 @@
require("PGBase")
require("GameManager.PlayerClass")
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 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
---@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
@@ -18,42 +20,10 @@ 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()
self.Players = self:_FindPlayers(gameMode)
self.CombatFeed = TutorialTextFeed:New("combat_feed", 6)
ScriptMessage("Tactical Game initialized!")
return self
@@ -67,8 +37,16 @@ function TacticalGameClass:Service()
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()
@@ -80,41 +58,328 @@ 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)
for playerId, player in pairs(self.Players) do
if playerId == id then
return player
end
end
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)
for playerId, player in pairs(self.Players) do
if playerId == id then
player:Quit()
end
local player = self.Players[id]
if player then
player:Quit()
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]
-- ==================================================
-- Unit event handlers
-- ==================================================
if player then
player:AddUnit(objectType)
---Adds a built unit to the Unit table.
---@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
---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 table The FoC GameObjectWrapper object that was killed.
function TacticalGameClass:UnitKilled(gameObject)
-- Find a way to set the unit as killed
---@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]
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:UpdateTacticalKillStatsTable(killedUnit, killer)
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
--- ==================================================
--- 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.
function TacticalGameClass:UpdateTacticalKillStatsTable(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
-- ==================================================
-- Private functions
-- ==================================================
---@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 = {}
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

View File

@@ -1,4 +1,4 @@
require("GameManager.Constants")
require("GameManager/Constants")
---@class TeamStruct
---@field Id integer Team ID.

View File

@@ -0,0 +1,74 @@
require("GameManager/TutorialTextView")
---A fixed-capacity feed view. Appending a new line when at capacity evicts the
---oldest (top-most) entry first. Rendering is driven by the IsActive flag set
---by TutorialTextManager; no manager reference is needed.
---
---@class TutorialTextFeed : TutorialTextView
---@field MaxLines integer Maximum number of lines to display at once.
---@field _KeyOrder string[] Insertion-ordered list of active entry keys (oldest first).
---@field _NextId integer Counter used to generate unique entry keys.
TutorialTextFeed = setmetatable({}, { __index = TutorialTextView })
TutorialTextFeed.__index = TutorialTextFeed
---Creates a new Tutorial Text Feed.
---@param name string The view name.
---@param maxLines integer Maximum number of lines to display at once.
---@return TutorialTextFeed
function TutorialTextFeed:New(name, maxLines)
local self = setmetatable(TutorialTextView:New(name), TutorialTextFeed)
---@cast self TutorialTextFeed
self.MaxLines = maxLines
self._KeyOrder = {}
self._NextId = 0
return self
end
---Appends a new line to the bottom of the feed.
---If the feed is at capacity, the oldest line is removed first.
---Only renders to the screen when this view is active.
---@param text string The display text.
---@param duration number|nil Duration in seconds, defaults to -1 (infinite).
---@param color ColorStruct|nil The text color, defaults to white.
function TutorialTextFeed:Append(text, duration, color)
---@diagnostic disable-next-line
if table.getn(self._KeyOrder) >= self.MaxLines then
local oldestKey = table.remove(self._KeyOrder, 1)
self.Entries[oldestKey] = nil
if self.IsActive then
Remove_Tutorial_Text(oldestKey)
end
end
self._NextId = self._NextId + 1
local key = self.Name .. "_" .. self._NextId
self:SetEntry(key, text, duration, color)
table.insert(self._KeyOrder, key)
if self.IsActive then
local entry = self.Entries[key]
Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration,
entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A)
end
end
---Removes all entries from the feed.
---Only clears the screen when this view is active.
function TutorialTextFeed:Clear()
if self.IsActive then
for _, key in ipairs(self._KeyOrder) do
Remove_Tutorial_Text(key)
end
end
self.Entries = {}
self._KeyOrder = {}
end
function TutorialTextFeed:Debug()
return string.format("TutorialTextFeed '%s' (%d/%d lines)",
---@diagnostic disable-next-line
self.Name, table.getn(self._KeyOrder), self.MaxLines)
end

View File

@@ -0,0 +1,169 @@
require("GameManager/TutorialTextView")
---Manages multiple named tutorial text views, rendering only the active view.
---
---Views hold their own state independently. Only the current view is rendered
---to the screen. Switching views clears the screen and renders the new view's
---current state.
---
---@class TutorialTextManager
---@field Views table<string, TutorialTextView> All registered views by name.
---@field CurrentViewName string|nil The name of the currently active view.
TutorialTextManager = {}
TutorialTextManager.__index = TutorialTextManager
---Creates a new Tutorial Text Manager.
---@return TutorialTextManager
function TutorialTextManager:New()
local self = setmetatable({}, TutorialTextManager)
self.Views = {}
self.CurrentViewName = nil
return self
end
---Registers a new view. If a view with the given name already exists it is returned as-is.
---@param name string The view name.
---@return TutorialTextView
function TutorialTextManager:AddView(name)
if not self.Views[name] then
self.Views[name] = TutorialTextView:New(name)
end
return self.Views[name]
end
---Registers an existing view instance (e.g. a TutorialTextFeed).
---If a view with the same name already exists it is returned as-is.
---@param view TutorialTextView The view to register.
---@return TutorialTextView
function TutorialTextManager:RegisterView(view)
if not self.Views[view.Name] then
self.Views[view.Name] = view
end
return self.Views[view.Name]
end
---Returns a registered view by name, or nil if it does not exist.
---@param name string The view name.
---@return TutorialTextView|nil
function TutorialTextManager:GetView(name)
return self.Views[name]
end
---Switches the active view. Clears the screen then renders the new view's current state.
---@param name string The view name to activate.
function TutorialTextManager:SetCurrentView(name)
if self.CurrentViewName == name then
return
end
local oldView = self.Views[self.CurrentViewName]
if oldView then
oldView.IsActive = false
end
self:_ClearScreen()
self.CurrentViewName = name
local newView = self.Views[name]
if newView then
newView.IsActive = true
self:_RenderView(newView)
end
end
---Sets or updates a text entry in a view.
---If the view is currently active the change is immediately rendered to the screen.
---@param viewName string The view to update.
---@param key string The unique key for this entry.
---@param text string The display text.
---@param duration number|nil Duration in seconds, defaults to -1 (infinite).
---@param color ColorStruct|nil The text color, defaults to white.
function TutorialTextManager:UpdateEntry(viewName, key, text, duration, color)
local view = self.Views[viewName]
if not view then
return
end
view:SetEntry(key, text, duration, color)
if self.CurrentViewName == viewName then
local entry = view.Entries[key]
Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration,
entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A)
end
end
---Removes a text entry from a view.
---If the view is currently active the entry is immediately removed from the screen.
---@param viewName string The view to update.
---@param key string The key to remove.
function TutorialTextManager:RemoveEntry(viewName, key)
local view = self.Views[viewName]
if not view then
return
end
view:RemoveEntry(key)
if self.CurrentViewName == viewName then
Remove_Tutorial_Text(key)
end
end
---Clears all entries from a view.
---If the view is currently active all entries are immediately removed from the screen.
---@param viewName string The view to clear.
function TutorialTextManager:ClearView(viewName)
local view = self.Views[viewName]
if not view then
return
end
if self.CurrentViewName == viewName then
self:_ClearScreen()
end
view:Clear()
end
---Removes all views and clears the screen.
function TutorialTextManager:Reset()
self:_ClearScreen()
self.Views = {}
self.CurrentViewName = nil
end
---Renders all entries of a view to the screen.
---@param view TutorialTextView
function TutorialTextManager:_RenderView(view)
for _, entry in pairs(view.Entries) do
Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration,
entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A)
end
end
---Removes all entries of the current view from the screen.
function TutorialTextManager:_ClearScreen()
if not self.CurrentViewName then
return
end
local view = self.Views[self.CurrentViewName]
if not view then
return
end
for key in pairs(view.Entries) do
Remove_Tutorial_Text(key)
end
end
function TutorialTextManager:Debug()
local count = 0
for _ in pairs(self.Views) do count = count + 1 end
return string.format("TutorialTextManager (views: %d, current: '%s')",
count, self.CurrentViewName or "none")
end

View File

@@ -0,0 +1,60 @@
require("GameManager/ColorStruct")
---Represents a single text entry in a tutorial text view.
---@class TutorialTextEntry
---@field Key string The unique key for this entry.
---@field Text string The display text.
---@field Duration number Duration in seconds, or -1 for infinite.
---@field Color ColorStruct The text color.
---Represents a named set of tutorial text entries.
---@class TutorialTextView
---@field Name string The view name.
---@field Entries table<string, TutorialTextEntry> Entries keyed by their text key.
---@field IsActive boolean Whether this view is currently being displayed.
TutorialTextView = {}
TutorialTextView.__index = TutorialTextView
---Creates a new Tutorial Text View.
---@param name string The view name.
---@return TutorialTextView
function TutorialTextView:New(name)
local self = setmetatable({}, TutorialTextView)
self.Name = name
self.Entries = {}
self.IsActive = false
return self
end
---Sets or updates a text entry in this view.
---@param key string The unique key for this entry.
---@param text string The display text.
---@param duration number|nil Duration in seconds, defaults to -1 (infinite).
---@param color ColorStruct|nil The text color, defaults to white.
function TutorialTextView:SetEntry(key, text, duration, color)
self.Entries[key] = {
Key = key,
Text = text,
Duration = duration or -1,
Color = color or ColorStruct:New(1, 1, 1, 1),
}
end
---Removes a text entry from this view.
---@param key string The key to remove.
function TutorialTextView:RemoveEntry(key)
self.Entries[key] = nil
end
---Removes all entries from this view.
function TutorialTextView:Clear()
self.Entries = {}
end
function TutorialTextView:Debug()
local count = 0
for _ in pairs(self.Entries) do count = count + 1 end
return string.format("TutorialTextView '%s' (%d entries)", self.Name, count)
end

View File

@@ -2,34 +2,40 @@ require("PGBase")
---@class UnitClass
---@field Name string Unit 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.
---@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.
---@field KillerId integer|nil The Player ID that killed this unit.
---@field GameObject GameObject|nil The FoC GameObjectWrapper object.
---@field GameObjectType GameObjectType The FoC GameObjectTypeWrapper object.
UnitClass = {}
UnitClass.__index = UnitClass
---Constructs a new Unit object.
---@param objectType table 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 = 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()
self.AliveTime = -1
self.DeathTime = -1
self.GameObjectWrapper = nil
self.GameObjectTypeWrapper = objectType
self.Icon = "I_BUTTON_COMMAND_BAR_PIRATE_SYMBOL.TGA"
self.KillerId = nil
self.GameObject = nil
self.GameObjectType = objectType
return self
end
@@ -61,18 +67,20 @@ function UnitClass:SetAlive(object)
end
self.AliveTime = GetCurrentTime()
self.GameObjectWrapper = object
self.GameObject = object
ScriptMessage("Unit Spawned: %s", self:Debug())
end
---Sets the unit as dead.
function UnitClass:SetDead()
---@param killerId integer The Player ID that killed this unit.
function UnitClass:SetDead(killerId)
if self.DeathTime >= 0 then
-- Do not override once set
return
end
self.DeathTime = GetCurrentTime()
self.KillerId = killerId
ScriptMessage("Unit Killed: %s", self:Debug())
end

View File

@@ -0,0 +1,20 @@
---Formats the game time in seconds to MM:SS.
---@param gameTime number The game time in seconds.
---@return string
function FormatTime(gameTime)
local minutes = Dirty_Floor(gameTime / 60)
local minutesText = tostring(minutes)
if tonumber(minutes) < 10 then
minutesText = "0" .. minutesText
end
local seconds = Dirty_Floor(gameTime - (minutes * 60))
local secondsText = tostring(seconds)
if tonumber(seconds) < 10 then
secondsText = "0" .. secondsText
end
return string.format("%s:%s", minutesText, secondsText)
end

View File

@@ -1,9 +1,8 @@
require("PGBase")
require("GameManager.Constants")
require("GameManager.GUIManager")
require("GameManager.GalacticGameClass")
require("GameManager.TacticalGameClass")
require("GameManager.SkirmishGameClass")
require("GameManager/Constants")
require("GameManager/GalacticGameClass")
require("GameManager/TacticalGameClass")
require("GameManager/SkirmishGameClass")
-- ==================================================
-- Script Locals
@@ -15,25 +14,14 @@ 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
---@type TacticalGameClass|SkirmishGameClass|nil
local TacticalGame = nil
---The Skirmish Game manager.
---@type SkirmishGameClass|nil
local SkirmishGame = nil
-- ==================================================
-- Script Globals
-- ==================================================
@@ -50,6 +38,43 @@ 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 = {
[FragIndex] = {},
[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
-- ==================================================
@@ -59,10 +84,10 @@ 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.
---@diagnostic disable-next-line
function main()
DebugMessage("%s -- In main.", tostring(Script))
@@ -87,24 +112,40 @@ function GameService()
if GalacticGame then
GalacticGame:Service()
if TacticalGame then
TacticalGame:Service()
end
end
if SkirmishGame then
SkirmishGame:Service()
end
if GUIManager then
GUIManager:Service()
if TacticalGame then
TacticalGame:Service()
end
end
---Script debug servicing.
function ServiceDebug()
DebugMessage("%s -- TacticalKillStatsTable dump:", tostring(Script))
local fragEntry = TacticalKillStatsTable[FragIndex]
if fragEntry then
for playerId, unitTypes in pairs(fragEntry) do
for objType, data in pairs(unitTypes) do
DebugMessage(" FRAG player=%d type=%s kills=%d cp=%d cost=%d score=%d",
playerId, tostring(objType), data.kills, data.combat_power, data.build_cost, data.score_value)
end
end
else
DebugMessage(" FRAG (empty)")
end
local deathEntry = TacticalKillStatsTable[DeathIndex]
if deathEntry then
for playerId, unitTypes in pairs(deathEntry) do
for objType, data in pairs(unitTypes) do
DebugMessage(" DEATH player=%d type=%s kills=%d cp=%d cost=%d score=%d",
playerId, tostring(objType), data.kills, data.combat_power, data.build_cost, data.score_value)
end
end
else
DebugMessage(" DEATH (empty)")
end
end
-- ==================================================
@@ -115,6 +156,16 @@ end
function Reset_Tactical_Stats()
DebugMessage("%s -- In Reset_Tactical_Stats", tostring(Script))
TacticalBuildStatsTable = {}
TacticalKillStatsTable = {
[FragIndex] = {},
[DeathIndex] = {}
}
TacticalTeamKillStatsTable = {
[FragIndex] = {},
[DeathIndex] = {}
}
ResetTacticalRegistry()
end
@@ -123,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
@@ -141,6 +200,8 @@ end
function Game_Mode_Starting_Event(mode, map)
DebugMessage("%s -- Game Mode %s (%s) now starting.", tostring(Script), mode, map)
LocalPlayer = Find_Player("local")
if StringCompare(mode, "Galactic") then
-- Galactic Campaign
Init_Galactic()
@@ -168,10 +229,12 @@ function Game_Mode_Ending_Event(oldMode)
-- Skirmish mode ending
Reset_Skirmish()
end
LocalPlayer = nil
end
---This event is triggered when a player quits the game.
---@param player table FoC PlayerWrapper object that just quit.
---@param player PlayerObject 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())
@@ -181,9 +244,9 @@ function Player_Quit_Event(player)
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.
---@param objectType GameObjectType FoC GameObjectTypeWrapper object that was just built.
---@param player PlayerObject FoC PlayerWrapper object that built the object.
---@param location PlanetObject|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(),
@@ -200,20 +263,20 @@ function Tactical_Production_End_Event(objectType, player, location)
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.
---@param object GameObject FoC GameObjectWrapper object that was just killed.
---@param killer PlayerObject 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)
TacticalGame:UnitKilled(object, killer)
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.
---@param planet PlanetObject FoC GameObjectWrapper object of the planet.
---@param objectType GameObjectType 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())
@@ -221,43 +284,58 @@ 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..
---@param planet PlanetObject FoC GameObjectWrapper object of the planet.
---@param objectType GameObjectType 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.
---@param planet PlanetObject FoC GameObjectWrapper or of the planet.
---@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.
---@param object table FoC GameObjectWrapper object that was just killed.
---@param killer table FoC PlayerWrapper object that killed the object.
---@param object GameObject FoC GameObjectWrapper object that was just killed.
---@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.
---@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.
---@param planet PlanetObject FoC GameObjectWrapper object of the planet.
---@param oldType GameObjectType FoC GameObjectTypeWrapper object of the old starbase type.
---@param newType GameObjectType 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))
@@ -294,9 +372,9 @@ function Galactic_Starbase_Level_Change(planet, oldType, newType)
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.
---@param planet PlanetObject FoC GameObjectWrapper object of the planet.
---@param newOwner PlayerObject FoC PlayerWrapper of the new owner.
---@param oldOwner PlayerObject FoC PlayerWrapper of the old owner.
function Galactic_Planet_Faction_Change(planet, newOwner, oldOwner)
local planetName = planet.Get_Type().Get_Name()
@@ -305,8 +383,8 @@ function Galactic_Planet_Faction_Change(planet, newOwner, oldOwner)
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.
---@param heroType GameObjectType FoC GameObjectTypeWrapper object for the neutralized hero type.
---@param killer GameObject 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
@@ -316,23 +394,23 @@ end
--- ==================================================
---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.
---@param objectType GameObjectType FoC GameObjectTypeWrapper object to query.
---@param player PlayerObject 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.
---@param objectType GameObjectType FoC GameObjectTypeWrapper object to query.
---@param player PlayerObject 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 player PlayerObject 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
@@ -376,7 +454,8 @@ function Update_GameSpy_Game_Stats()
end
---Updates the GameSpy player stats table
---@param player table FoC PlayerWrapper object.
---@param player PlayerObject FoC PlayerWrapper object.
---@deprecated
function Update_GameSpy_Player_Stats(player)
GameSpy_Player_Stats = {}
end
@@ -409,15 +488,19 @@ function Init_Tactical(gameMode)
DebugMessage("%s -- Initializing Tactical %s rules.", tostring(Script), gameMode)
Reset_Tactical_Stats()
TacticalGame = TacticalGameClass:New(gameMode)
GUIManager:InitTactical(TacticalGame)
TacticalGame.KillStatsTable = TacticalTeamKillStatsTable
GUIManager:InitTactical()
--TextManager:RegisterView(TacticalGame.CombatFeed)
--TextManager:SetCurrentView("combat_feed")
end
---Resets Tactica mode.
---Resets Tactical mode.
function Reset_Tactical()
DebugMessage("%s -- Resetting Tactical rules.", tostring(Script))
Reset_Tactical_Stats()
TacticalGame = nil
GUIManager:Reset()
TextManager:Reset()
end
--- ==================================================
@@ -427,17 +510,20 @@ end
---Initializes Skirmish mode.
---@param gameMode string The tactical game mode.
function Init_Skirmish(gameMode)
DebugMessage("%s -- Initializing Skirmish Tacitcal %s rules.", tostring(Script), gameMode)
DebugMessage("%s -- Initializing Skirmish Tactical %s rules.", tostring(Script), gameMode)
Reset_Stats()
SkirmishGame = SkirmishGameClass:New(gameMode)
TacticalGame = SkirmishGame.TacticalGame
GUIManager:InitSkirmish(SkirmishGame)
TacticalGame = SkirmishGameClass:New(gameMode)
TacticalGame.KillStatsTable = TacticalKillStatsTable
GUIManager:InitSkirmish(TacticalGame.IsLocalSpectator)
TextManager:RegisterView(TacticalGame.CombatFeed)
TextManager:SetCurrentView("combat_feed")
end
---Resets Skirmish mode.
function Reset_Skirmish()
DebugMessage("%s -- Resetting Skirmish Tactical rules.", tostring(Script))
Reset_Stats()
SkirmishGame = nil
TacticalGame = nil
GUIManager:Reset()
TextManager:Reset()
end