-- $Id: //depot/Projects/StarWars_Expansion/Run/Data/Scripts/Library/PGTaskForce.lua#6 $ --///////////////////////////////////////////////////////////////////////////////////////////////// -- -- (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/PGTaskForce.lua $ -- -- Original Author: Brian Hayes -- -- $Author: James_Yarrow $ -- -- $Change: 46700 $ -- -- $DateTime: 2006/06/21 14:40:55 $ -- -- $Revision: #6 $ -- --///////////////////////////////////////////////////////////////////////////////////////////////// require("PGAICommands") function FindStageArea(_target, taskforce) return _FindStageArea(PlayerObject, _target, taskforce); end function AssembleForce(taskforce, stage_at_strongest_space) if stage_at_strongest_space then stage = FindTarget.Reachable_Target(PlayerObject, "FriendlySpaceForceStrength", "Friendly | Enemy | Neutral", "Enemy_undefended", 1.0, AITarget) DebugMessage("%s -- Using the enemy_undefended reachable system %s with the strongest space force for staging.", tostring(Script), tostring(stage)) else stage = taskforce.Get_Stage() DebugMessage("%s -- Using default system %s (nearest faction system) for staging.", tostring(Script), tostring(stage)) end if not stage then DebugMessage("%s -- Unable to find a staging area. Abandonning plan.", tostring(Script)) ScriptExit() end DebugMessage("%s -- Selected Stage: %s\n", tostring(Script), tostring(stage)) BlockOnCommand(taskforce.Produce_Force(stage)); end function SynchronizedAssemble(taskforce, stage_at_strongest_space) if stage_at_strongest_space then stage = FindTarget(taskforce, "FriendlySpaceForceStrength", "Friendly | Enemy | Neutral", 1.0) DebugMessage("%s -- Using the system %s with the strongest space force for staging.", tostring(Script), tostring(stage)) else stage = taskforce.Get_Stage() DebugMessage("%s -- Using default system %s (nearest faction system) for staging.", tostring(Script), tostring(stage)) end if not stage then DebugMessage("%s -- Unable to find a staging area. Abandonning plan.", tostring(Script)) ScriptExit() end DebugMessage("%s -- Selected Stage: %s\n", tostring(Script), tostring(stage)) BlockOnCommand(taskforce.Produce_Force(stage, true)); end function WaitMoveForce(taskforce, planet) DebugMessage("Moving Taskforce %s to object %s.", tostring(taskforce), tostring(planet)) BlockOnCommand(taskforce.Move_To(FindPlanet(planet))) DebugMessage("Moving Command finished") end function LandUnits(taskforce) DebugMessage("Landing on %s with taskforce %s.", tostring(Target), tostring(taskforce)) BlockOnCommand(taskforce.Land_Units()) DebugMessage("Land Units Command finished") end function LaunchUnits(taskforce) DebugMessage("Launching units into orbit with taskforce %s.", tostring(taskforce)) block = taskforce.Launch_Units() if not block then return false end BlockOnCommand(block) BlockOnCommand(taskforce.Form_Units()) DebugMessage("Launch Units Command finished") return true end function Invade(taskforce) DebugMessage("Invading %s with taskforce %s.", tostring(Target), tostring(taskforce)) InvasionActive = true invade_status = taskforce.Invade() if invade_status and BlockOnCommand(invade_status) then DebugMessage("Invasion successful. Waiting for planet conversion."); taskforce.Set_As_Goal_System_Removable(false) LandUnits(taskforce) InvasionActive = false return true else DebugMessage("Invasion failed!") InvasionActive = false return false end end function FundBases(player, target) --Prefer starbases since they block enemy movement DebugMessage("%s -- giving desire bonus to build starbase", tostring(Script)) BlockOnCommand(GiveDesireBonus(player, "Build_Initial_Starbase_Only", target, 15, 5)) DebugMessage("%s -- waiting for starbase", tostring(Script)) BlockOnCommand(WaitForStarbase(Target, 1)) DebugMessage("%s -- giving desire bonus to build groundbase", tostring(Script)) BlockOnCommand(GiveDesireBonus(player, "Build_Initial_Groundbase_Only", target, 15, 5)) end -- Generally follow the task force we're escorting and attack it's attackers. -- Conservative style will avoid confrontation with non fighters/bombers function Escort(taskforce, target, conservative_style) if not TestValid(target) then Sleep(1) return end lib_enemy = FindDeadlyEnemy(target) if lib_enemy then if Get_Game_Mode() == "Space" then if not conservative_style or (lib_enemy.Is_Category("Fighter") or lib_enemy.Is_Category("Bomber")) then -- Lure them away from our escorted target if we can. Try_Ability(taskforce,"LURE") -- Weave a bit. Weave(taskforce, lib_enemy) -- engage the attackers for a duration if not TestValid(lib_enemy) then return end DebugMessage("%s -- In Escort: Attacking lib_enemy: %s for Target: %s", tostring(Script), tostring(lib_enemy), tostring(target)) BlockOnCommand(taskforce.Attack_Target(lib_enemy), 15) else -- run away a bit, then hold ground before resuming escort DebugMessage("%s -- In Escort: attempting to divert lib_enemy: %s for Target: %s", tostring(Script), tostring(lib_enemy), tostring(target)) BlockOnCommand(taskforce.Move_To(Project_By_Unit_Range(lib_enemy, taskforce)), 15) if not TestValid(target) then return end BlockOnCommand(taskforce.Guard_Target(target, 5)) end else BlockOnCommand(taskforce.Attack_Target(lib_enemy), 20) end return end if not TestValid(target) then Sleep(1) return end -- Make sure we're assisting the target provided we're not already assisting -- because reissuing a guard will cause the assisting escort to break off attack. -- Note: may receive a non tf "target" param, for example EscortPlan passes a unit if not Tf_Has_Attack_Target(taskforce) then -- Escort forces should have attack priorities that allow them to attack structures by this point DebugMessage("%s on %s -- In Escort: Resuming escort of %s", tostring(Script), tostring(AITarget), tostring(target)) taskforce.Guard_Target(target) end Sleep(1) end -- Does any member of the given task force have an attack target? function Tf_Has_Attack_Target(tf) if TestValid(tf) then for i, unit in tf.Get_Unit_Table() do if unit.Has_Attack_Target() == true then return true end end else DebugMessage("%s -- task force %s isn't valid", tostring(Script), tostring(tf)) end return false end function Weave(tf, enemy) if not TestValid(enemy) then return end BlockOnCommand(tf.Attack_Target(enemy), 10) if not TestValid(enemy) then return end BlockOnCommand(tf.Move_To(Project_By_Unit_Range(enemy, tf)), 5) if not TestValid(enemy) then return end BlockOnCommand(tf.Attack_Target(enemy), 2) if not TestValid(enemy) then return end BlockOnCommand(tf.Move_To(Project_By_Unit_Range(enemy, tf)), 4) end function WaitForAllReinforcements(taskforce, target) block = taskforce.Reinforce(target) if block then while block and not BlockOnCommand(block) do block = taskforce.Reinforce(target) end end end -- Keep a task force from being idle -- expects lib_tf_to_anti_idle and lib_enemy_to_avoid to be defined before use function Anti_Idle_Taskforce() DebugMessage("%s-- AntiIdle attempt", tostring(Script)) if lib_tf_to_anti_idle and TestValid(lib_enemy_to_avoid) then DebugMessage("%s-- AntiIdle lib vars are defined", tostring(Script)) if lib_new_anti_idle == true then DebugMessage("%s-- AntiIdling %s", tostring(Script), tostring(lib_tf_to_anti_idle)) lib_anti_idle_start_time = GetCurrentTime() lib_anti_idle_block = lib_tf_to_anti_idle.Move_To(Project_By_Unit_Range(lib_enemy_to_avoid, lib_tf_to_anti_idle)) lib_new_anti_idle = false elseif lib_anti_idle_block then PumpEvents() -- Reissue a new command if the old one has expired or some time has passed -- This ensures that newly landed reinforcements on the task force will also anti-idle if lib_anti_idle_block.IsFinished() == true or 2 + lib_anti_idle_start_time < GetCurrentTime() then lib_new_anti_idle = true end end else DebugMessage("%s-- needed global vars aren't defined for anti idle", tostring(Script)) end end -- Try to get units on the map in the most reasonable manner. -- Passing an escort_force is optional function QuickReinforce(player, target, tf_to_reinforce, second_try_tf) if not player then DebugMessage("%s-- received nil player", tostring(Script)) return end -- Global plans can pass a nil target and rely on failsafes below to find a target -- if not target then -- DebugMessage("%s-- received nil target", tostring(Script)) -- return -- end if not tf_to_reinforce then DebugMessage("%s-- received nil tf_to_reinforce", tostring(Script)) return end -- Space mode will attempt various reinforce attempts with timeouts, -- then simply fail after the last option is exhausted. if Get_Game_Mode() == "Space" then lib_reinforce_timeout = 3 -- If some units have landed, try land the remainder near the rest of the main tf if tf_to_reinforce.Get_Force_Count() ~= 0 then -- Define some global vars for the anti idle lib_tf_to_anti_idle = tf_to_reinforce lib_enemy_to_avoid = Find_Nearest(tf_to_reinforce, PlayerObject, false) lib_new_anti_idle = true -- Reinforce new units and anti-idle existing units waiting for the reinforce if BlockOnCommand(tf_to_reinforce.Reinforce(tf_to_reinforce), lib_reinforce_timeout, Anti_Idle_Taskforce) then DebugMessage("%s-- reinforced by task force", tostring(Script)) lib_tf_to_anti_idle = nil lib_enemy_to_avoid = nil return end end -- If some of the escort force exists, try to land there if second_try_tf and second_try_tf.Get_Force_Count() ~= 0 then -- Define some global vars for the anti idle lib_tf_to_anti_idle = second_try_tf lib_enemy_to_avoid = Find_Nearest(second_try_tf, PlayerObject, false) lib_new_anti_idle = true -- Reinforce new units and anti-idle existing units waiting for the reinforce if BlockOnCommand(tf_to_reinforce.Reinforce(second_try_tf), lib_reinforce_timeout, Anti_Idle_Taskforce) then DebugMessage("%s-- reinforced by secondary task force", tostring(Script)) lib_tf_to_anti_idle = nil lib_enemy_to_avoid = nil return end end -- Try to land near the default starting point, or some base building, or finally the target itself. lib_start_loc = FindTarget(tf_to_reinforce, "Is_Friendly_Start", "Tactical_Location", 1.0) if lib_start_loc then DebugMessage("%s-- Trying to reinforce by start point", tostring(Script)) BlockOnCommand(tf_to_reinforce.Reinforce(lib_start_loc), lib_reinforce_timeout) else lib_base_list = Find_All_Objects_Of_Type(player, "Structure") if lib_base_list[1] then DebugMessage("%s-- Trying to reinforce by friendly structure", tostring(Script)) BlockOnCommand(tf_to_reinforce.Reinforce(lib_base_list[1]), lib_reinforce_timeout) else lib_base_list = Find_All_Objects_Of_Type(player, "Capital") if lib_base_list[1] then DebugMessage("%s-- Trying to reinforce by friendly capital", tostring(Script)) BlockOnCommand(tf_to_reinforce.Reinforce(lib_base_list[1]), lib_reinforce_timeout) else DebugMessage("%s-- Trying to reinforce by plan target", tostring(Script)) BlockOnCommand(tf_to_reinforce.Reinforce(target), lib_reinforce_timeout) end end end -- If we were unable to bring in any reinforcements within a reasonable amount of time -- then just abandon the plan; it's not worth holding up other units for too long if tf_to_reinforce.Get_Force_Count() == 0 then ScriptExit() end -- Land mode: this will just return if the units are all available; -- otherwise we'll reinforce for a long time and then the plan will fail because it may no longer be relevant. else if tf_to_reinforce.Are_All_Units_On_Free_Store() then DebugMessage("%s-- All units available, proceeding with plan", tostring(Script)) -- form up any spread out infantry tf_to_reinforce.Activate_Ability("SPREAD_OUT", false) else DebugMessage("%s-- Reinforcements required--waiting for them.", tostring(Script)) -- Generally, we want things reinforced near the target, if possible -- But this will try all of the reinforcement points, if needed. -- This typically takes a long time. if AITarget then WaitForAllReinforcements(tf_to_reinforce, target) else -- find something to reinforce near, for global plans with a nil target friendly_loc = FindTarget(tf_to_reinforce, "Space_Area_Is_Hidden", "Tactical_Location", 1.0) WaitForAllReinforcements(tf_to_reinforce, friendly_loc) end --WaitForAllReinforcements(tf_to_reinforce, tf_to_reinforce) DebugMessage("%s-- Reinforcements arrived. Abandonning plan for reproposal if still relevant", tostring(Script)) -- If the plan is still relevant, it will be proposed again. ScriptExit() end end end function SetClassPriorities(tf, priority_set_type) priority_set_name = "Fighter" .. "_" .. priority_set_type tf.Set_Targeting_Priorities(priority_set_name, "Fighter") priority_set_name = "Bomber" .. "_" .. priority_set_type tf.Set_Targeting_Priorities(priority_set_name, "Bomber") priority_set_name = "Corvette" .. "_" .. priority_set_type tf.Set_Targeting_Priorities(priority_set_name, "Corvette") priority_set_name = "Frigate" .. "_" .. priority_set_type tf.Set_Targeting_Priorities(priority_set_name, "Frigate") priority_set_name = "Capital" .. "_" .. priority_set_type tf.Set_Targeting_Priorities(priority_set_name, "Capital") end -- Move task force to asteroids or nebulae if within range and the asteroids aren't near an enemy -- Returns true if the task force is already obscured or this function obscured it function Obscure(tf, range, use_nebulae, path_through_fields) tf_unit_list = tf.Get_Unit_Table() if not UnitListIsObscured(tf_unit_list) then asteroids = Find_Nearest_Space_Field(tf, "Asteroid") if asteroids and tf.Get_Distance(asteroids) < range then enemy_nearest_loc = Find_Nearest(asteroids, PlayerObject, false) if not TestValid(enemy_nearest_loc) or enemy_nearest_loc.Get_Distance(asteroids) < 750 then --MessageBox("%s -- moving to asteroids:%s", tostring (Script), path_through_fields) BlockOnCommand(tf.Move_To(asteroids, 10, path_through_fields)) return true end elseif use_nebulae then nebula = Find_Nearest_Space_Field(tf, "Nebula") if nebula and tf.Get_Distance(nebula) < range then enemy_nearest_loc = Find_Nearest(nebula, PlayerObject, false) if not TestValid(enemy_nearest_loc) or enemy_nearest_loc.Get_Distance(nebula) < 750 then --MessageBox("%s -- moving to nebulae:%s", tostring (Script), path_through_fields) BlockOnCommand(tf.Move_To(nebula, 10, path_through_fields)) return true end end end return false -- We're not obscured, and there's nothing in range to obscure us. end return true end -- Fix the unit or hide if appropriate. -- Return true if the unit was fixed. function ConsiderHeal(unit) unit_hull = unit.Get_Hull() if (unit_hull < 0.8) then bacta = Find_Nearest(unit, "HealsInfantry", PlayerObject, true) if TestValid(bacta) then if (unit_hull < 0.4) or (unit.Get_Distance(bacta) < 700) then unit.Activate_Ability("STEALTH", true) unit.Activate_Ability("FORCE_CLOAK", true) BlockOnCommand(unit.Move_To(bacta)) while unit.Get_Hull() < 0.9 do DebugMessage("%s-- Waiting to heal", tostring(Script)) BlockOnCommand(unit.Guard_Target(bacta), 2) end return true end end end return false end -- Fix the unit or hide if appropriate. -- Return true if the unit was fixed. function ConsiderRepair(unit) unit_hull = unit.Get_Hull() if (unit_hull < 0.8) then repair_station = Find_Nearest(unit, "HealsVehicles", PlayerObject, true) if TestValid(repair_station) then if (unit_hull < 0.4) or (unit.Get_Distance(repair_station) < 700) then BlockOnCommand(unit.Move_To(repair_station)) while unit.Get_Hull() < 0.9 do BlockOnCommand(unit.Guard_Target(repair_station), 2) end return true end end end return false end -- Note, this is safe for event response (because it doesn't block) function Release_To_Hide(tf, unit) flee_loc = Find_Nearest(unit, "HealsInfantry", PlayerObject, true) if not flee_loc then flee_loc = FindTarget(tf, "Space_Area_Is_Hidden", "Tactical_Location", 1.0, 5000.0) end if flee_loc then unit.Move_To(flee_loc) unit.Lock_Current_Orders() tf.Release_Unit(unit) return true end end -- This is for some give and take pacing. The AI is allowed to attack without difficulty-based pause -- 1 to N times, then it must wait for the player to win 1 to N times before attacking without pause. function GalacticAttackAllowed(difficulty, ai_territories_just_gained) num_ai_attacks_left = GetGlobalValueOrZero(PlayerObject, "num_ai_attacks_left") if num_ai_attacks_left > 0 then -- If the AI has allowed attacks left, permit the attack and decrement GlobalValue.Set(PlayerSpecificName(PlayerObject, "num_ai_attacks_left"), num_ai_attacks_left - 1) --MessageBox("%s -- ai has %d attacks left; not pausing before next attack", tostring(Script), num_ai_attacks_left) return true else -- AI has to wait for player wins -- Difficulty will dictate aggressiveness and amount of contrived breathing room the AI gives for free min_player_wins_unresponded = 4 max_player_wins_unresponded = 8 max_ai_attacks_allowed = 2 if difficulty == "Normal" then min_player_wins_unresponded = 3 max_player_wins_unresponded = 6 max_ai_attacks_allowed = 2 elseif difficulty == "Hard" then min_player_wins_unresponded = 1 max_player_wins_unresponded = 2 max_ai_attacks_allowed = 4 end -- Track the player's progress num_land_enemy_controlled = EvaluatePerception("Num_Enemy_Land_Territories", PlayerObject) num_space_enemy_controlled = EvaluatePerception("Num_Enemy_Space_Territories", PlayerObject) --MessageBox("%s -- num_land_enemy_controlled:%d num_space_enemy_controlled:%d", tostring(Script), num_land_enemy_controlled, num_space_enemy_controlled) player_gains_before_attacking = GetGlobalValueOrZero(PlayerObject, "player_gains_before_attacking") if player_gains_before_attacking == 0 then -- Establish how far the player can go unresponded player_gains_before_attacking = GameRandom(min_player_wins_unresponded, max_player_wins_unresponded) GlobalValue.Set(PlayerSpecificName(PlayerObject, "player_gains_before_attacking"), player_gains_before_attacking) --MessageBox("%s -- Determined %d player gains allowed and recording current player territories.", tostring(Script), player_gains_before_attacking) -- Record the state of the galaxy GlobalValue.Set(PlayerSpecificName(PlayerObject, "recent_land_enemy_controlled"), num_land_enemy_controlled) GlobalValue.Set(PlayerSpecificName(PlayerObject, "recent_space_enemy_controlled"), num_space_enemy_controlled) end -- Check the player's progress (before our current attack) player_territories_gained = num_land_enemy_controlled + num_space_enemy_controlled - GlobalValue.Get(PlayerSpecificName(PlayerObject, "recent_land_enemy_controlled")) - GlobalValue.Get(PlayerSpecificName(PlayerObject, "recent_space_enemy_controlled")) -- + ai_territories_just_gained if player_territories_gained >= player_gains_before_attacking then -- Player has won all that he's allowed without AI response, time to start attacking again num_ai_attacks_left = GameRandom(0, max_player_wins_unresponded - 1) GlobalValue.Set(PlayerSpecificName(PlayerObject, "num_ai_attacks_left"), num_ai_attacks_left) -- Also reset the losses allowed, so that it will be randomized next time GlobalValue.Set(PlayerSpecificName(PlayerObject, "player_gains_before_attacking"), 0) --MessageBox("%s -- player has enough wins unresponded (%d); not pausing before next attack. \n AI gets %d attacks without pause", tostring(Script), player_territories_gained, num_ai_attacks_left) return true else --MessageBox("%s -- %d player territories gained, %d allowed before responding so pausing before next attack" -- , tostring(Script), player_territories_gained, player_gains_before_attacking) if player_territories_gained < 0 then -- Record the state of the galaxy --MessageBox("%s -- player lost ground; resetting recent territories records", tostring(Script)) GlobalValue.Set(PlayerSpecificName(PlayerObject, "recent_land_enemy_controlled"), num_land_enemy_controlled) GlobalValue.Set(PlayerSpecificName(PlayerObject, "recent_space_enemy_controlled"), num_space_enemy_controlled) end end end return false end function DifficultyBasedMinPause(difficulty) sleep_duration = 300 if difficulty == "Normal" then sleep_duration = 200 elseif difficulty == "Hard" then sleep_duration = 45 end return sleep_duration end function GetGlobalValueOrZero(player, value_name) ret_value = GlobalValue.Get(PlayerSpecificName(player, value_name)) if ret_value == nil then return 0.0 end return ret_value end