Files
SoaFE/DATA/SCRIPTS/LIBRARY/PGCOMMANDS.LUA
2026-02-28 14:00:45 -06:00

594 lines
21 KiB
Lua

-- $Id: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGCommands.lua#10 $
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- (C) Petroglyph Games, Inc.
--
--
-- ***** ** * *
-- * ** * * *
-- * * * * *
-- * * * * * * * *
-- * * *** ****** * ** **** *** * * * ***** * ***
-- * ** * * * ** * ** ** * * * * ** ** ** *
-- *** ***** * * * * * * * * ** * * * *
-- * * * * * * * * * * * * * * *
-- * * * * * * * * * * ** * * * *
-- * ** * * ** * ** * * ** * * * *
-- ** **** ** * **** ***** * ** *** * *
-- * * *
-- * * *
-- * * *
-- * * * *
-- **** * *
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
-- C O N F I D E N T I A L S O U R C E C O D E -- D O N O T D I S T R I B U T E
--/////////////////////////////////////////////////////////////////////////////////////////////////
--
-- $File: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGCommands.lua $
--
-- Original Author: Brian Hayes
--
-- $Author: James_Yarrow $
--
-- $Change: 53991 $
--
-- $DateTime: 2006/09/08 11:01:51 $
--
-- $Revision: #10 $
--
--/////////////////////////////////////////////////////////////////////////////////////////////////
require("PGBaseDefinitions")
-- Deprecated...
--
-- function ProduceForce(taskforce)
-- tfIndex = 0
--
-- DebugMessage("Beginning production on %s at %s.", tostring(taskforce),
-- tostring(taskforce.Production_Facility()))
--
-- -- loop through all the unit types and produce each one.
-- UnitType = taskforce.Get_Type_Of_Unit(tfIndex)
-- while UnitType do
--
-- -- Build the object at the production facility.
-- tfObj = WaitProduceObject(UnitType, taskforce.Production_Facility())
-- DebugMessage("Object %s produced.", tostring(tfObj))
--
-- -- Add the object to the task force.
-- taskforce.Add_Force(tfObj)
--
-- -- Remove our reference to the object, since a merge will destroy it.
-- tfObj.Release()
--
-- -- Merge the new fleets together.
-- BlockOnCommand(taskforce.Form_Units())
--
-- -- Look for another unit type.
-- tfIndex = tfIndex + 1
-- UnitType = taskforce.Get_Type_Of_Unit(tfIndex)
-- end
-- end
--
-- function ProduceObject(player, objecttype, where)
-- return _ProduceObject(player, objecttype, where)
-- end
--
-- function WaitProduceObject(objecttype, where)
-- return BlockOnCommand(ProduceObject(PlayerObject, objecttype, where))
-- end
function WaitForever()
DebugMessage("%s -- Waiting forever...", tostring(Script))
while true do
PumpEvents()
end
-- BlockOnCommand(BlockForever())
DebugMessage("%s -- Something interrupted the wait!.", tostring(Script))
end
-- TimerTable[func] = {timeout = timeout, start_time = GetCurrentTime(), param = param }
--function Debug_Timer_Table()
-- OutputDebug("%s -- TimerTable Dump.\n", tostring(Script))
-- for func,tabtab in pairs(TimerTable) do
-- for idx,tab in pairs(tabtab) do
-- OutputDebug("%s, %f, %f, %s\n", tostring(func), tab.timeout, tab.start_time, tostring(tab.param))
-- end
-- end
--end
function Register_Timer(func, timeout, param)
if TimerTable[func] == nil then
TimerTable[func] = {}
end
table.insert(TimerTable[func], {timeout = timeout, start_time = GetCurrentTime(), param = param})
end
function Process_Timers()
for func,tabtab in pairs(TimerTable) do
found_entry = false
for idx,tab in pairs(tabtab) do
found_entry = true
if tab.timeout + tab.start_time < GetCurrentTime() then
tabtab[idx] = nil
func(tab.param)
Process_Timers()
return
end
end
if found_entry == false then
TimerTable[func] = nil
Process_Timers()
return
end
end
end
-- Cancels all occurences of this timer function
function Cancel_Timer(func)
if func ~= nil then
TimerTable[func] = nil
else
MessageBox("%s -- cancelling nonexistant function, got:%s; aborting.", tostring(Script), type(func))
end
end
-- Setup a callback for the death or deletion of a given object.
function Register_Death_Event(obj, func)
if not TestValid(obj) then
MessageBox("%s -- Error, object doesn't exist or has already died.", tostring(Script))
return
end
if DeathTable[obj] ~= nil then
MessageBox("%s -- Error, object already registered for death event", tostring(Script))
return
end
DeathTable[obj] = func
end
function Process_Death_Events()
for obj, func in pairs(DeathTable) do
if not TestValid(obj) then
DeathTable[obj] = nil
func()
Process_Death_Events()
return
end
end
end
-- Setup a callback for a given object falling under attack.
function Register_Attacked_Event(obj, func)
if not TestValid(obj) then
MessageBox("%s -- Error, object doesn't exist or has died.", tostring(Script))
return
end
if AttackedTable[obj] ~= nil then
MessageBox("%s -- Error, object already registered for attacked event", tostring(Script))
return
end
-- Storing the callback and if the object currently has a "deadly enemy"
AttackedTable[obj] = {func, false}
end
-- Executes the callback with (true, object) if first going under attack.
-- Executes the callback with (false) if no longer under attack.
function Process_Attacked_Events()
for obj, table in pairs(AttackedTable) do
if not TestValid(obj) then
AttackedTable[obj] = nil
else
most_deadly_enemy = FindDeadlyEnemy(obj)
if most_deadly_enemy then
-- If we have a deadly enemy and this just became true, run the callback.
if not table[2] then
table[2] = true
table[1](true, most_deadly_enemy, obj)
Process_Attacked_Events()
return
end
-- Update that we don't have a deadly enemy any longer.
--else
elseif table[2] then
--MessageBox("obj:%s now has no deadly enemy", tostring(obj))
table[2] = false
table[1](false, nil, obj)
end
end
end
end
function Cancel_Attacked_Event(obj)
if obj ~= nil then
AttackedTable[obj] = nil
else
MessageBox("received nil object")
end
end
-- Set up proximity triggers on arbitrary objects and have them serviced.
function Register_Prox(obj, func, range, player_filter)
-- prevent this from doing anything in galactic mode
if Get_Game_Mode() == "Galactic" then
DebugMessage("%s -- Warning, proximity register disallowed in galactic mode; aborting.", tostring(Script))
return
end
if not TestValid(obj) then
MessageBox("%s -- Error, prox object doesn't exist; aborting", tostring(Script))
ScriptError("%s -- Error, prox object doesn't exist; aborting", tostring(Script))
return
end
-- Note the player_filter is optional. The user of this function must check
-- for player validity at the source.
if player_filter == nil then
DebugMessage("%s -- Warning, passed a nil player, not filtering prox by player", tostring(Script))
end
obj.Event_Object_In_Range(func, range, player_filter)
ProxTable[obj] = 1
end
function Process_Proximities()
for obj, count in pairs(ProxTable) do
if TestValid(obj) then
obj.Service_Wrapper()
else
ProxTable[obj] = nil
end
end
end
function Pump_Service()
Process_Timers()
Process_Death_Events()
Process_Proximities()
Process_Attacked_Events()
-- Don't test if this is a function, so that we can catch accidental redefinitions when it's used as a function.
if Process_Reinforcements then
Process_Reinforcements()
end
-- This is for behavior that we need evaluated every service without regard to story event state.
if Story_Mode_Service then
Story_Mode_Service()
end
end
-- Try an ability if the AI difficulty will allow a chance
function Try_Ability(thing, ability_name, target)
owner = PlayerObject
if not Is_A_Taskforce(thing) then
owner = thing.Get_Owner()
end
if owner == nil then
MessageBox("%s -- no owner for thing:%s", tostring(Script), tostring(thing))
end
-- At a given difficulty, there is a chance that the ability use will be allowed.
if not Chance(GetAbilityChanceSeed(), GetChanceAllowed(owner.Get_Difficulty())) then
return false
end
return Use_Ability_If_Able(thing, ability_name, target)
end
-- Activates the ability for a unit or a taskforce's units if able.
-- Optionally uses ability on a target.
-- Returns true if the ability was attempted
function Use_Ability_If_Able(thing, ability_name, target)
-- Taskforces aren't able to check for the ability availablity or readiness, but check this for units
if Is_A_Taskforce(thing) or (thing.Has_Ability(ability_name) and thing.Is_Ability_Ready(ability_name) and (not thing.Is_Ability_Active(ability_name))) then
if target == nil then
thing.Activate_Ability(ability_name, true)
elseif TestValid(target) then
thing.Activate_Ability(ability_name, target)
end
return true
end
return false
end
function Is_A_Taskforce(thing)
return thing and thing.Get_Unit_Table
end
-- This will consider diverting the passed object in order to use an area of effect ability centered on the unit.
function ConsiderDivertAndAOE(object, ability_name, area_of_effect, recent_enemy_units, min_threat_to_use_ability)
-- At a given difficulty, there is a chance that the divert for ability use will be allowed.
if (not aoe_pos) and (not Chance(GetCurrentMinute(), GetChanceAllowed(object.Get_Owner().Get_Difficulty()))) then
return
end
-- See if the ability is ready and there are enough enemies around to consider using it.
if object.Is_Ability_Ready(ability_name) then
DebugMessage("%s -- %s is ready and trigger number met", tostring(Script), ability_name)
-- If we haven't found a good use for the ability
if aoe_pos == nil then
-- Find a good place to use the ability and divert or throw the result away.
aoe_pos, aoe_victim_threat = Find_Best_Local_Threat_Center(recent_enemy_units, area_of_effect)
if aoe_pos == nil then
DebugMessage("%s -- couldn't get a valid threat center", tostring(Script))
DebugPrintTable(recent_enemy_units)
return
end
DebugMessage("%s -- Found ability pos with threat %d", tostring(Script), aoe_victim_threat)
if (aoe_victim_threat > min_threat_to_use_ability) then
-- Check distance to prevent the unit from spinning in circles on repeated diversions
if object.Get_Distance(aoe_pos) > 15 then
DebugMessage("%s -- Met minimum threat; diverting.", tostring(Script))
Use_Ability_If_Able(object, "SPRINT")
object.Divert(aoe_pos)
else
DebugMessage("%s -- Met minimum threat; Already very close to ideal target so no divert necessary.", tostring(Script))
end
else
DebugMessage("%s -- Resetting pos and threat.", tostring(Script))
aoe_pos = nil
aoe_victim_threat = nil
end
-- We have found a good use for the ability
else
-- Are we done chasing down the position to use the ability?
if object.Is_On_Diversion() then
DebugMessage("%s -- In process of diverting to chase threat %d (no new orders issued)", tostring(Script), aoe_victim_threat)
else
-- We're done diverting. Perform a sanity check to make sure at least one enemy is now in range.
--if OneOrMoreInRange(object, recent_enemy_units, area_of_effect) then
-- We're done diverting so check to see if we're at least in range of the best location (even if not centered on it)
aoe_pos, aoe_victim_threat = Find_Best_Local_Threat_Center(recent_enemy_units, area_of_effect)
if aoe_pos and (object.Get_Distance(aoe_pos) < area_of_effect) then
-- Use the ability
DebugMessage("%s -- Attempting %s.", tostring(Script), ability_name)
Use_Ability_If_Able(object, ability_name)
else
DebugMessage("%s -- Nothing at diversion locaion; aborting.", tostring(Script))
end
-- Reset everything; this try is done. If the victims moved too much, we'll need to start over.
recent_enemy_units = {}
aoe_pos = nil
aoe_victim_threat = nil
end
end
end
end
function OneOrMoreInRange(origin_unit, target_unit_list, range)
for key, unit in pairs(target_unit_list) do
if origin_unit.Get_Distance(unit) < range then
return true
end
end
return false
end
function PruneFriendlyObjects(obj_table)
non_friendly_obj_table = {}
for i, obj in pairs(obj_table) do
if not (obj.Get_Owner() == PlayerObject) then
table.insert(non_friendly_obj_table, obj)
end
end
return non_friendly_obj_table
end
function Try_Garrison(tf, unit, offensive_only, range)
lib_nearest_garrison = Find_Nearest(unit, "GarrisonCanFire", unit.Get_Owner(), true)
if TestValid(lib_nearest_garrison) and unit.Can_Garrison(lib_nearest_garrison) then
lib_dist_to_garrison = unit.Get_Distance(lib_nearest_garrison)
if lib_dist_to_garrison < range then
unit.Activate_Ability("SPREAD_OUT", false)
unit.Garrison(lib_nearest_garrison)
if TestValid(tf) then
tf.Release_Unit(unit)
unit.Lock_Current_Orders()
end
return true
end
end
if not offensive_only then
lib_nearest_garrison = Find_Nearest(unit, "CanContainGarrison", unit.Get_Owner(), true)
if TestValid(lib_nearest_garrison) and unit.Can_Garrison(lib_nearest_garrison) then
lib_dist_to_garrison = unit.Get_Distance(lib_nearest_garrison)
if lib_dist_to_garrison < range then
unit.Activate_Ability("SPREAD_OUT", false)
unit.Garrison(lib_nearest_garrison)
if TestValid(tf) then
unit.Lock_Current_Orders()
tf.Release_Unit(unit)
end
return true
end
end
end
return false
end
function Try_Deploy_Garrison(object, target, health_threshold)
lib_any_deployed = false
lib_garrison_table = object.Get_Garrisoned_Units()
if table.getn(lib_garrison_table) > 0 then
for i,garrison in pairs(lib_garrison_table) do
if garrison.Get_Hull() > health_threshold then
if (not TestValid(target)) or garrison.Is_Good_Against(target) then
garrison.Leave_Garrison()
lib_any_deployed = true
end
end
end
end
return lib_any_deployed
end
function Get_Special_Healer_Property_Flag(unit)
if not TestValid(unit) then
return nil
end
if not special_healer_table then
special_healer_table = {}
special_healer_table["BOBA_FETT"] = "HealsInfantry"
special_healer_table["EMPEROR_PALPATINE"] = "HealsInfantry"
special_healer_table["HAN_SOLO"] = "HealsInfantry"
special_healer_table["CHEWBACCA"] = "HealsInfantry"
special_healer_table["MARA_JADE"] = "HealsInfantry"
special_healer_table["OBI_WAN_KENOBI"] = "HealsInfantry"
special_healer_table["DARTH_VADER"] = "HealsInfantry"
special_healer_table["KYLE_KATARN"] = "HealsInfantry"
special_healer_table["TACTICAL_R2_3PO_TEAM"] = "HealsVehicles"
special_healer_table["GARGANTUAN_BATTLE_PLATFORM"] = "HealsVehicles"
special_healer_table["LUKE_SKYWALKER_JEDI"] = "HealsInfantry"
special_healer_table["YODA"] = "HealsInfantry"
special_healer_table["BOSSK"] = "HealsInfantry"
special_healer_table["IG-88"] = "HealsVehicles"
special_healer_table["SILRI"] = "HealsInfantry"
special_healer_table["TYBER_ZANN"] = "HealsInfantry"
special_healer_table["URAI_FEN"] = "HealsInfantry"
special_healer_table["PESTELOUS_THE_HUTT"] = "HealsVehicles"
special_healer_table["CUARSON"] = "HealsInfantry"
special_healer_table["XIZOR"] = "HealsInfantry"
special_healer_table["SYKARIIUS"] = "HealsInfantry"
special_healer_table["MPTL_SPOTTER"] = "HealsVehicles"
special_healer_table["SCOUT_TROOPER"] = "HealsVehicles"
special_healer_table["B1_BATTLEDROID"] = "HealsVehicles"
special_healer_table["B2_BATTLEDROID"] = "HealsVehicles"
special_healer_table["B1_REPAIRDROID"] = "HealsVehicles"
special_healer_table["MAGNAGUARD"] = "HealsVehicles"
end
return special_healer_table[unit.Get_Type().Get_Name()]
end
function Set_Land_AI_Targeting_Priorities(tf)
--First set up generic priorities
tf.Set_Targeting_Priorities("Infantry_Attack_Move", "Infantry")
tf.Set_Targeting_Priorities("Infantry_Attack_Move", "LandHero")
tf.Set_Targeting_Priorities("Air_Attack_Move", "Air")
tf.Set_Targeting_Priorities("Heavy_Vehicle_Attack_Move", "Vehicle")
--Now for some more specific stuff
tf.Set_Targeting_Priorities("Rocket_Infantry_Attack_Move", "Plex_Soldier_Team")
tf.Set_Targeting_Priorities("Rocket_Infantry_Attack_Move", "Pirate_Plex_Soldier_Team")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "TIE_Crawler")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "AT_RT_Walker")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "AT_PT_Walker")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "AT_ST_Walker")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "T1C_Tank")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "T2B_Tank")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "Dark_Trooper_PhaseI")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "Hutt_Modified_Skiff")
tf.Set_Targeting_Priorities("Light_Vehicle_Attack_Move", "Hutt_Modified_Swamp_Speeder")
tf.Set_Targeting_Priorities("Artillery_Attack_Move", "MPTL")
tf.Set_Targeting_Priorities("Artillery_Attack_Move", "SPMAT_Walker")
tf.Set_Targeting_Priorities("Artillery_Attack_Move", "MAL_Rocket_Vehicle")
tf.Set_Targeting_Priorities("Artillery_Attack_Move", "HAG")
end
function Try_Weapon_Switch(object, target)
lib_switcher_type = object.Get_Type()
if not lib_t4b_type then
lib_t4b_type = Find_Object_Type("T4B_TANK")
lib_t3b_type = Find_Object_Type("T3B_TANK")
-- lib_marauder_type = Find_Object_Type("Marauder_Missile_Cruiser")
-- lib_broadside_type = Find_Object_Type("Broadside_Class_Cruiser")
-- lib_atst_type = Find_Object_Type("AT_ST_Walker")
lib_bossk_type = Find_Object_Type("BOSSK")
lib_b2battledroid_type = Find_Object_Type("B2_Battledroid")
lib_destroyer_droid_type = Find_Object_Type("DESTROYER_DROID")
lib_mal_type = Find_Object_Type("MAL_ROCKET_VEHICLE")
-- lib_subjugator_type = Find_Object_Type("Malevolence")
lib_vader_type = Find_Object_Type("TIE_Prototype")
lib_vader_team_type = Find_Object_Type("Darth_Vader_TIE_Fighter_Container")
end
if lib_switcher_type == lib_t4b_type then
object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Structure") or target.Is_Category("Infantry"))
elseif lib_switcher_type == lib_t3b_type then
object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Structure") or target.Is_Category("Infantry"))
-- elseif lib_switcher_type == lib_marauder_type then
-- object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Structure") or target.Is_Category("Capital"))
-- elseif lib_switcher_type == lib_broadside_type then
-- object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Structure") or target.Is_Category("Capital"))
-- elseif lib_switcher_type == lib_atst_type then
-- object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Infantry") or target.Is_Category("Vehicle"))
elseif lib_switcher_type == lib_bossk_type then
object.Activate_Ability("SWAP_WEAPONS", target.Is_Category("Infantry") or target.Is_Category("Vehicle"))
elseif lib_switcher_type == lib_b2battledroid_type then
object.Activate_Ability("SWAP_WEAPONS", target.Is_Category("Infantry") or target.Is_Category("Vehicle"))
elseif lib_switcher_type == lib_destroyer_droid_type then
object.Activate_Ability("ROCKET_ATTACK", target.Get_Shield() > 0.0)
elseif lib_switcher_type == lib_mal_type then
object.Activate_Ability("SWAP_WEAPONS", (target.Is_Category("Infantry") or target.Is_Category("Vehicle")) and GameRandom.Get_Float() > 0.8)
-- elseif lib_switcher_type == lib_subjugator_type then
-- object.Activate_Ability("ROCKET_ATTACK", target.Is_Category("Structure") or target.Is_Category("Super") or target.Is_Category("Capital") and target.Get_Shield() > 0.0)
elseif lib_switcher_type == lib_vader_type then
object.Activate_Ability("SWAP_WEAPONS", target.Get_Shield() > 0.0)
elseif lib_switcher_type == lib_vader_team_type then
object.Activate_Ability("SWAP_WEAPONS", target.Get_Shield() > 0.0)
elseif object.Should_Switch_Weapons(target) then
object.Activate_Ability("SWAP_WEAPONS", not object.Is_Ability_Active("SWAP_WEAPONS"))
end
end
function Determine_Magic_Wait_Duration()
lib_magic_wait_time = 2400 - GetCurrentTime()
if lib_magic_wait_time < 60 then
lib_magic_wait_time = 60
end
return lib_magic_wait_time
end