--///////////////////////////////////////////////////////////////////////////////////////////////// -- (c) Galyana's Ultimate Empire at War Modifications -- -- ****** *** *** ************ *** *** -- * * * * * * * * -- * * * * * * * -- * * * * * * * -- * * * * * **** * * * -- * * * ****** * * * * -- * **** * * * * * * * *** * -- * * * * * * * * * * -- * * * * * * * * * * -- * * * * * * * ** * * * -- ****** ****** ************ **** ** ******* --///////////////////////////////////////////////////////////////////////////////////////////////// -- 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: GUEaW/Data/Scripts/Library/PGEvents.LUA -- -- Original Editor: Giovanni Galyana -- -- Final Edit By: Giovanni Galyana -- -- Date: 7 March, 2015 -- -- Revisions: 0 -- --///////////////////////////////////////////////////////////////////////////////////////////////// -- -- Default Event Handlers. -- require("PGTaskForce") function GoHeal(tf, unit, healer, release) --If we can lay proximity mines, do so and then rely on a further call to kite the enemy over them --Add a random factor here since proximity mines currently have no cooldown and so we'll never run --because we can always use them if (GameRandom.Get_Float() > 0.5) and Try_Ability(unit, "PROXIMITY_MINES", unit) then return end if Try_Ability(unit, "BUZZ_DROIDS", unit) then return end if release then Try_Ability(unit,"JET_PACK", healer) Try_Ability(unit,"SPRINT") Try_Ability(unit, "FORCE_CLOAK") Try_Ability(unit, "STEALTH") unit.Move_To(healer, 10) unit.Lock_Current_Orders() tf.Release_Unit(unit) else Try_Ability(unit,"JET_PACK", healer) Try_Ability(unit,"SPRINT") Try_Ability(unit, "FORCE_CLOAK") Try_Ability(unit, "STEALTH") unit.Divert(healer) end end function GoKite(tf, unit, kite_pos, release) --If we can lay proximity mines, do so and then rely on a further call to kite the enemy over them --Add a random factor here since proximity mines currently have no cooldown and so we'll never run --because we can always use them if (GameRandom.Get_Float() > 0.5) and Try_Ability(unit, "PROXIMITY_MINES", unit) then return end if Try_Ability(unit, "BUZZ_DROIDS", unit) then return end if release then Try_Ability(unit,"Turbo") -- Enable Turbo mode, if we have it. Try_Ability(unit,"JET_PACK", kite_pos) Try_Ability(unit,"EJECT_VEHICLE_THIEF") Try_Ability(unit,"SPRINT") Try_Ability(unit, "SPOILER_LOCK") Try_Ability(unit, "REPLENISH_WINGMEN") Try_Ability(unit, "FORCE_CLOAK") Try_Ability(unit, "STEALTH") if unit.Get_Hull() > 0.5 then Try_Ability(unit, "STIM_PACK") end unit.Move_To(kite_pos) unit.Lock_Current_Orders() tf.Release_Unit(unit) else if not unit.Is_On_Diversion() then --MessageBox("%s -- Diverting: %s", tostring(Script), tostring(unit)) Try_Ability(unit,"Turbo") -- Enable Turbo mode, if we have it. Try_Ability(unit,"JET_PACK", kite_pos) Try_Ability(unit,"EJECT_VEHICLE_THIEF") Try_Ability(unit,"SPRINT") Try_Ability(unit, "SPOILER_LOCK") Try_Ability(unit, "REPLENISH_WINGMEN") Try_Ability(unit, "FORCE_CLOAK") Try_Ability(unit, "STEALTH") if unit.Get_Hull() > 0.5 then Try_Ability(unit, "STIM_PACK") end unit.Divert(kite_pos) end end end function Try_Good_Ground(tf, unit) nearest_good_ground_indicator = Find_Nearest(unit, "Prop_Good_Ground_Area") if nearest_good_ground_indicator then dist_to_good_ground = unit.Get_Distance(nearest_good_ground_indicator) if (dist_to_good_ground < 300) and (dist_to_good_ground > 20) then unit.Activate_Ability("SPREAD_OUT", false) unit.Move_To(nearest_good_ground_indicator) unit.Lock_Current_Orders() tf.Release_Unit(unit) return true end end return false end -- This should be useful for getting units within the minimum attack range -- or outside the maximum attack range on opponents who behave like artillery function Respond_To_MinRange_Attacks(tf, unit) DebugMessage("%s-- looking at attacker for minrange response", tostring(Script)) min_range_attackers = { "R_Ground_Turbolaser_Tower" ,"E_Ground_Turbolaser_Tower" ,"U_Ground_Turbolaser_Tower" ,"H_Ground_Turbolaser_Tower" ,"H_Ground_Heavy_Beam_Tower" ,"FCU_E_Ground_Turbolaser_Tower" ,"FCU_R_Ground_Turbolaser_Tower" ,"FCU_U_Ground_Turbolaser_Tower" ,"FCU_H_Ground_Heavy_Beam_Tower" ,"MPTL" ,"SPMAT_Walker" ,"AALL6" ,"Hutt_SailBarge" ,"Marauder_Missile_Cruiser" ,"Broadside_Class_Cruiser" } deadly_enemy = FindDeadlyEnemy(unit) if TestValid(deadly_enemy) then deadly_enemy_type = deadly_enemy.Get_Type() if Is_Type_In_List(deadly_enemy_type, min_range_attackers) then DebugMessage("%s -- attacked by min range attacker", tostring(Script)) -- Move any units in the task force which are in range of the attacker -- to a position over the max range or under the min range approach_or_flee_range = ((deadly_enemy_type.Get_Max_Range() - deadly_enemy_type.Get_Min_Range()) * 2 / 3) + deadly_enemy_type.Get_Min_Range() for i, tf_unit in pairs(tf.Get_Unit_Table()) do DebugMessage("%s -- considering run or approach for %s", tostring(Script), tostring(tf_unit)) distance = tf_unit.Get_Distance(deadly_enemy) if distance < deadly_enemy_type.Get_Max_Range() then if distance < approach_or_flee_range then DebugMessage("%s -- trying to run inside min attack range", tostring(Script)) tf_unit.Move_To(deadly_enemy) tf_unit.Lock_Current_Orders() tf.Release_Unit(unit) else DebugMessage("%s -- trying to run outside max attack range", tostring(Script)) GoKite(tf, tf_unit, Project_By_Unit_Range(deadly_enemy, tf_unit)) end end end return true end end return false end function Is_Type_In_List(unit_type, type_name_list) for i, type_name in pairs(type_name_list) do DebugMessage("%s -- type:%s checking match to:%s", tostring(Script), unit_type.Get_Name(), type_name) if unit_type == Find_Object_Type(type_name) then return true end end return false end -- Check if the passed ability is one of the type that the AI wants to turn back on when cancelled function IsAbilityAllowedToRecover(ability) allowed_abilities = { "SPOILER_LOCK" ,"Turbo" } for i, allowed_ability in pairs(allowed_abilities) do if ability == allowed_ability then return true end end return false end function Default_Space_Conflict_Begin() DebugMessage("%s -- In Default_Space_Conflict_Begin.", tostring(Script)) InSpaceConflict = true GlobalValue.Set(PlayerSpecificName(PlayerObject, "CONTACT_OCCURED"), 0.0) end function Default_Space_Conflict_End() DebugMessage("%s -- In Default_Space_Conflict_End.", tostring(Script)) InSpaceConflict = false GlobalValue.Set(PlayerSpecificName(PlayerObject, "CONTACT_OCCURED"), 0.0) end function Default_Unit_Destroyed() DebugMessage("%s -- In Default_Unit_Destroyed.", tostring(Script)) end function Default_Unit_Damaged(tf, unit, attacker, deliberate) DebugMessage("%s -- In Default_Unit_Damaged.", tostring(Script)) if not TestValid(unit) or not TestValid(attacker) or attacker.Is_Category("Structure") then return end lib_issued_movement_response = false -- all units but Interdictors try to maneuver against artillery and turbolasers -- Interdictors should use missile shield instead if unit.Get_Type() ~= Find_Object_Type("Interdictor_Cruiser") then lib_issued_movement_response = Respond_To_MinRange_Attacks(tf, unit) end if Should_Crush(unit, attacker) then unit.Divert(Project_By_Unit_Range(unit, attacker)) lib_issued_movement_response = true end -- If the unit is infantry, try to use any nearby garrisons or "good ground" if unit.Is_Category("Infantry") then if not lib_issued_movement_response then lib_issued_movement_response = Try_Garrison(tf, unit, unit.Get_Hull() > 0.4, 300.0) end if not lib_issued_movement_response then lib_issued_movement_response = Try_Good_Ground(tf, unit) end end if attacker.Is_Category("Infantry") then Try_Deploy_Garrison(unit, attacker, 0.5) end -- Use "Power to Shields" if this unit has it and it's ready lib_ability_activated = false lib_shield_level = unit.Get_Shield() if lib_shield_level < 0.2 then lib_ability_activated = Try_Ability(unit, "INVULNERABILITY") end if (not lib_ability_activated) and (lib_shield_level < 0.8) then lib_ability_activated = Try_Ability(unit, "Defend") end projectile_type = attacker.Get_Current_Projectile_Type() if TestValid(projectile_type) then if (not lib_ability_activated) and projectile_type.Is_Affected_By_Missile_Shield() then lib_ability_activated = Try_Ability(unit, "SENSOR_JAMMING") or Try_Ability(unit, "MISSILE_SHIELD") end if (not lib_ability_activated) and projectile_type.Is_Affected_By_Laser_Defense() then lib_ability_activated = Try_Ability(unit, "LASER_DEFENSE") end end --Everything below deals with movement orders if lib_issued_movement_response then return end -- non-hero fighters will always willingly dogfight other fighters if deliberate and attacker.Is_Category("Fighter") and unit.Is_Category("Fighter") then DebugMessage("%s -- Fighter shot by fighter, releasing unit from tf and attacking.", tostring(Script)) tf.Release_Unit(unit) unit.Activate_Ability("SPOILER_LOCK", false) if not unit.Activate_Ability("BUZZ_DROIDS", attacker) then unit.Attack_Target(attacker) end -- Default handling for a dying unit in both space and land modes. -- Is this unit not fodder AND do we have low health AND (we have a bad face off OR we're rapidly being killed for any reason) else lib_time_till_dead = unit.Get_Time_Till_Dead() lib_attacker_is_good_vs_me = attacker.Is_Good_Against(unit) lib_i_am_good_vs_attacker = unit.Is_Good_Against(attacker) lib_current_health = unit.Get_Hull() lib_is_hero = unit.Get_Type().Is_Hero() lib_is_fodder = unit.Has_Property("Fodder") if not lib_i_am_good_vs_attacker then Try_Ability(unit, "DEPLOY_TROOPERS") end if ((not lib_is_fodder) and lib_time_till_dead < 20) or ((not lib_is_fodder) and lib_current_health < 0.2) or (lib_is_hero and lib_current_health < 0.6) or (lib_current_health < 0.7 and lib_attacker_is_good_vs_me) or (lib_current_health < 0.4 and not lib_i_am_good_vs_attacker) then --Certain factions have no self-preservation lib_faction_name = unit.Get_Owner().Get_Faction_Name() if lib_faction_name == "PIRATES" or lib_faction_name == "HUTTS" then return end -- Turn off any abilities that would hinder self-preservation unit.Activate_Ability("Power_To_Weapons", false) -- Try to find the nearest healing structure appropriate for this unit lib_healer_property_flag = Get_Special_Healer_Property_Flag(unit) if not lib_healer_property_flag then if unit.Is_Category("Infantry") then lib_healer_property_flag = "HealsInfantry" elseif unit.Is_Category("Vehicle") then lib_healer_property_flag = "HealsVehicles" end end if lib_healer_property_flag then healer = Find_Nearest(unit, lib_healer_property_flag, PlayerObject, true) end lib_should_release = lib_attacker_is_good_vs_me or lib_is_hero or (lib_current_health < 0.33) -- Try to heal if we have a healer if healer then GoHeal(tf, unit, healer, lib_should_release) end if not lib_is_fodder then -- Try to find a protected kiting location friendly = Find_Nearest(unit, PlayerObject, true) xfire_pos = Get_Most_Defended_Position(unit, PlayerObject) if xfire_pos then kite_pos = Project_By_Unit_Range(attacker, xfire_pos) elseif TestValid(friendly) then DebugMessage("Failed to find a Most Defended Position, using position of nearby friendly.") kite_pos = Project_By_Unit_Range(attacker, friendly) else kite_pos = Project_By_Unit_Range(attacker, unit) end GoKite(tf, unit, kite_pos, lib_should_release) end end end end function Default_Original_Target_Destroyed() --DebugMessage("%s -- Original target destroyed. Aborting.", tostring(Script)) Attacking = false --ScriptExit() Some plans now have behaviors to occur after this event. end function Default_Current_Target_Destroyed(tf) --MessageBox("%s -- Current target destroyed. Aborting.", tostring(Script)) Attacking = false -- Turn off some unending abilities that might no longer be appropriate tf.Activate_Ability("SPREAD_OUT", false) --ScriptExit() Some plans now have behaviors to occur after this event. end function Default_Original_Target_Owner_Changed(tf, old_player, new_player) if InvasionActive == true then return end DebugMessage("%s -- Original target ownership changed. Aborting.", tostring(Script)) ScriptExit() end function Default_Unit_Move_Finished(tf, unit) Try_Deploy_Garrison(unit, nil, 0.5) end function Should_Crush(unit, target) -- don't crush in easy mode if (PlayerObject.Get_Difficulty() == "Easy") then return false end -- don't try to crush again if already on a diversion if unit.Is_On_Diversion() then return false end -- if the vehicle is good at it, the victim is infantry, and vulnerable to crushing if unit.Has_Property("GoodInfantryCrusher") and target.Is_Category("Infantry") then if target.Is_Ability_Active("SPREAD_OUT") then return true elseif GameRandom.Get_Float() > 0.8 then return true end end -- if the vehicle can crush anything and is close enough if unit.Has_Property("IsSupercrusher") and (unit.Get_Distance(target) < 75) then return true end return false end function Default_Target_In_Range(tf, unit, target) --MessageBox("%s -- unit: Default_Target_In_Range for: %s.", tostring(Script), tostring(unit), tostring(target)) -- We'll assume that once the first unit within a task force is in range, the attack has begun. Attacking = true -- Certain units like to try to crush other units, but we can't crush Rebel units that are spread out if Should_Crush(unit, target) then DebugMessage("%s -- Attempting to crush %s with %s", tostring(Script), tostring(target), tostring(unit)) unit.Divert(Project_By_Unit_Range(unit, target)) end Try_Deploy_Garrison(unit, target, 0.5) -- Turn various abilities that would be useful. -- The ability time-out or the plan ending will turn off the ability. Try_Ability(unit, "Power_To_Weapons") Try_Ability(unit, "SPREAD_OUT") Try_Ability(unit, "BARRAGE", target) Try_Ability(unit, "BUZZ_DROIDS", target) Try_Ability(unit, "FULL_SALVO") Try_Ability(unit, "BLAST") if unit.Get_Hull() > 0.5 then Try_Ability(unit, "STIM_PACK") end Try_Weapon_Switch(unit, target) GlobalValue.Set(PlayerSpecificName(PlayerObject, "CONTACT_OCCURED"), 1.0) end function Default_Hardpoint_Target_In_Range(tf, unit, target) DebugMessage("%s -- A hardpoint of %s can now fire on target %s.", tostring(Script), tostring(unit), tostring(attacker)) end function Default_No_Units_Remaining() DebugMessage("%s -- All units dead or non-buildable. Abandonning plan.", tostring(Script)) ScriptExit() end function Default_Unit_Diversion_Finished(tf, unit) if TestValid(unit) then -- Turn off turbo, if we happened to be using it to assist in a divert (evasive maneuver). unit.Activate_Ability("Turbo", false) unit.Activate_Ability("SPOILER_LOCK", false) end end -- This fires if the countdown was going and it is now refreshed or if you come out of a nebula function Default_Unit_Ability_Ready(tf, unit, ability) --MessageBox("%s ready for %s", ability, tostring(unit)) -- Try to recover use of interrupted abilities. if lib_cancelled_abilities[unit] and lib_cancelled_abilities[unit][ability] then --MessageBox("%s-- attempting to recover use of %s", tostring(Script), ability) unit.Activate_Ability(ability, true) lib_cancelled_abilities[unit][ability] = false end end -- An ability was interrupted before naturally finishing. function Default_Unit_Ability_Cancelled(tf, unit, ability) DebugMessage("%s -- Ability %s of %s has been cancelled!", tostring(Script), ability, tostring(unit)) -- Track certain abilities that get cancelled, so we can recover them later if IsAbilityAllowedToRecover(ability) then -- make a new ability table for this unit, if one doesn't yet exist if not lib_cancelled_abilities[unit] then lib_cancelled_abilities[unit] = {} end lib_cancelled_abilities[unit][ability] = true end end -- An ability finished naturally by its duration running out or by being turned off. function Default_Unit_Ability_Finished(tf, unit) --DebugMessage("%s -- An ability for %s has finished!", tostring(Script), ability, tostring(unit)) end