-- $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