|
DefineClass.BossfightPierre = { |
|
__parents = { "Encounter" }, |
|
|
|
default_assigned_area = false, |
|
boss = false, |
|
guards_melee = false, |
|
guard_heavy = false, |
|
guard_rpg = false, |
|
|
|
enrage_turn = 4, |
|
fallback_turn = false, |
|
area_tactics = true, |
|
} |
|
|
|
g_SectorEncounters.H4 = "BossfightPierre" |
|
|
|
function BossfightPierre:ShouldStart() |
|
return not GetQuestVar("ErnieSideQuests", "FortressFirstCapture") |
|
end |
|
|
|
function BossfightPierre:Setup() |
|
for _, obj in ipairs(Groups.LegionRocketeer_SlowReloader) do |
|
if IsKindOf(obj, "Unit") then |
|
self.guard_rpg = obj |
|
break |
|
end |
|
end |
|
for _, obj in ipairs(Groups.PierreGuard_Ordnance) do |
|
if IsKindOf(obj, "Unit") then |
|
self.guard_heavy = obj |
|
break |
|
end |
|
end |
|
self.guards_melee = {} |
|
for _, obj in ipairs(Groups.PierreGuard_Stormer) do |
|
if IsKindOf(obj, "Unit") then |
|
table.insert(self.guards_melee, obj) |
|
end |
|
end |
|
|
|
for _, obj in ipairs(Groups.Pierre) do |
|
if IsKindOf(obj, "Unit") then |
|
self.boss = obj |
|
break |
|
end |
|
end |
|
end |
|
|
|
function BossfightPierre:GetDynamicData(data) |
|
data.boss = IsValid(self.boss) and self.boss:GetHandle() or nil |
|
data.guards_melee = {} |
|
for i, unit in ipairs(self.guards_melee) do |
|
local handle = IsValid(unit) and not unit:IsDead() and unit:GetHandle() |
|
if handle then |
|
table.insert(data.guards_melee, handle) |
|
end |
|
end |
|
data.guard_heavy = IsValid(self.guard_heavy) and self.guard_heavy:GetHandle() or nil |
|
data.guard_rpg = IsValid(self.guard_rpg) and self.guard_rpg:GetHandle() or nil |
|
data.enrage_turn = self.enrage_turn |
|
data.area_tactics = self.area_tactics |
|
data.fallback_turn = self.fallback_turn |
|
end |
|
|
|
function BossfightPierre:SetDynamicData(data) |
|
self.boss = data.boss and HandleToObject[data.boss] |
|
self.guards_melee = {} |
|
for _, handle in ipairs(data.guards_melee) do |
|
table.insert(self.guards_melee, HandleToObject[handle]) |
|
end |
|
self.guard_heavy = data.guard_heavy and HandleToObject[data.guard_heavy] |
|
self.guard_rpg = data.guard_rpg and HandleToObject[data.guard_rpg] |
|
|
|
self.enrage_turn = data.enrage_turn |
|
self.area_tactics = data.area_tactics |
|
self.fallback_turn = data.fallback_turn |
|
end |
|
|
|
function BossfightPierre:ShouldHoldPosition(unit) |
|
return table.find(unit.AIKeywords, "Sniper") or table.find(unit.AIKeywords, "Ordnance") |
|
end |
|
|
|
function BossfightPierre:OnTurnStart() |
|
local player_units_in_area, enemy_units_in_area = g_TacticalMap:CountUnitsInAreas() |
|
local plrunits = 0 |
|
plrunits = plrunits + (player_units_in_area.PierreFight_DetectAssault_Yard_1 or 0) |
|
plrunits = plrunits + (player_units_in_area.PierreFight_DetectAssault_Yard_2 or 0) |
|
plrunits = plrunits + (player_units_in_area.PierreFight_DetectFlanking_BackPass or 0) |
|
plrunits = plrunits + (player_units_in_area.PierreFight_DetectFlanking_MainBuilding or 0) |
|
plrunits = plrunits + (player_units_in_area.PierreFight_Yard_Transition or 0) |
|
plrunits = plrunits + (player_units_in_area.PierreFight_InnerPerimeter_MainBuilding or 0) |
|
|
|
local area_tactics = self.area_tactics and (g_Combat.current_turn < self.enrage_turn) |
|
|
|
if area_tactics and plrunits < 2 then |
|
self.boss.script_archetype = "GuardArea" |
|
for _, guard in ipairs(self.guards_melee) do |
|
guard.script_archetype = "GuardArea" |
|
end |
|
|
|
local mg_detect = (player_units_in_area.PierreFight_DetectFlanking_MG or 0) > 1 |
|
if g_Combat.current_turn == 1 or mg_detect then |
|
g_TacticalMap:AssignUnit(self.boss, nil) |
|
g_TacticalMap:AssignUnit(self.boss, "PierreFight_Yard_Hangar", nil, g_TacticalMap.PriorityHigh) |
|
g_TacticalMap:AssignUnit(self.boss, "PierreFight_Yard_Containers", nil, g_TacticalMap.PriorityMedium) |
|
if mg_detect then |
|
g_TacticalMap:AssignUnit(self.boss, "PierreFight_FlankingDefense_Yard", nil, g_TacticalMap.PriorityMedium) |
|
else |
|
g_TacticalMap:AssignUnit(self.boss, "PierreFight_Yard_SideHangar", nil, g_TacticalMap.PriorityMedium) |
|
end |
|
for _, guard in ipairs(self.guards_melee) do |
|
g_TacticalMap:AssignUnit(guard, nil) |
|
g_TacticalMap:AssignUnit(guard, "PierreFight_Yard_Hangar", nil, g_TacticalMap.PriorityHigh) |
|
g_TacticalMap:AssignUnit(guard, "PierreFight_Yard_Containers", nil, g_TacticalMap.PriorityMedium) |
|
if mg_detect then |
|
g_TacticalMap:AssignUnit(guard, "PierreFight_FlankingDefense_Yard", nil, g_TacticalMap.PriorityMedium) |
|
else |
|
g_TacticalMap:AssignUnit(guard, "PierreFight_Yard_SideHangar", nil, g_TacticalMap.PriorityMedium) |
|
end |
|
end |
|
end |
|
|
|
if self.fallback_turn and g_Combat.current_turn > self.fallback_turn then |
|
|
|
|
|
local units = table.union(g_TacticalMap:GetUnitsInArea("PierreFight_Yard_Transition"), g_TacticalMap:GetUnitsInArea("PierreFight_FlankingDefense_Yard")) |
|
for _, unit in ipairs(units) do |
|
if not self:ShouldHoldPosition(unit) then |
|
unit.script_archetype = "GuardArea" |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_InnerPerimeter_MainBuilding", "reset") |
|
end |
|
end |
|
elseif (self.fallback_turn and self.fallback_turn == g_Combat.current_turn) or g_Combat.current_turn >= 3 then |
|
|
|
|
|
|
|
local units = g_TacticalMap:GetUnitsInArea("PierreFight_Yard_SideHangar") |
|
for _, unit in ipairs(units) do |
|
if not self:ShouldHoldPosition(unit) then |
|
unit.script_archetype = "GuardArea" |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_InnerPerimeter_Cannon", "reset") |
|
end |
|
end |
|
|
|
|
|
units = table.union(g_TacticalMap:GetUnitsInArea("PierreFight_Yard_Hangar"), g_TacticalMap:GetUnitsInArea("PierreFight_Yard_Containers")) |
|
for _, unit in ipairs(units) do |
|
if not self:ShouldHoldPosition(unit) then |
|
unit.script_archetype = "GuardArea" |
|
g_TacticalMap:AssignUnit(unit, nil) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_InnerPerimeter_MainBuilding", nil, g_TacticalMap.PriorityHigh) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_Yard_Transition", nil, g_TacticalMap.PriorityMedium) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_FlankingDefense_Yard", nil, g_TacticalMap.PriorityLow) |
|
end |
|
end |
|
end |
|
else |
|
if plrunits >= 2 then |
|
self.area_tactics = false |
|
end |
|
for _, unit in ipairs(self.boss.team.units) do |
|
g_TacticalMap:AssignUnit(unit, nil) |
|
unit.script_archetype = nil |
|
end |
|
|
|
self.guard_heavy.AIKeywords = table.copy(self.guard_heavy.AIKeywords) |
|
if self.guard_rpg then |
|
self.guard_rpg.AIKeywords = table.copy(self.guard_rpg.AIKeywords) |
|
end |
|
|
|
if g_Combat.current_turn >= self.enrage_turn then |
|
self.boss.script_archetype = "Brute" |
|
for _, guard in ipairs(self.guards_melee) do |
|
guard.script_archetype = "Brute" |
|
end |
|
|
|
table.insert_unique(self.guard_heavy.AIKeywords, "Ordnance") |
|
else |
|
|
|
local boss = self.boss |
|
local enemies = GetEnemies(boss) |
|
local can_charge |
|
local ap = CombatActions.GloryHog:ResolveValue("move_ap") * const.Scale.AP |
|
for _, enemy in ipairs(enemies) do |
|
if GetChargeAttackPosition(boss, enemy, ap, "GloryHog") then |
|
can_charge = true |
|
break |
|
end |
|
end |
|
if can_charge then |
|
self.boss.script_archetype = "Brute" |
|
for _, guard in ipairs(self.guards_melee) do |
|
guard.script_archetype = "Brute" |
|
end |
|
else |
|
self.boss.script_archetype = "Soldier" |
|
for _, guard in ipairs(self.guards_melee) do |
|
guard.script_archetype = "Soldier" |
|
end |
|
end |
|
|
|
table.remove_value(self.guard_heavy.AIKeywords, "Ordnance") |
|
end |
|
|
|
|
|
if self.guard_rpg then |
|
if g_Combat.current_turn > 3 then |
|
table.insert_unique(self.guard_rpg.AIKeywords, "Ordnance") |
|
else |
|
table.remove_value(self.guard_rpg.AIKeywords, "Ordnance") |
|
end |
|
end |
|
end |
|
|
|
|
|
for _, unit in ipairs(self.boss.team.units) do |
|
if unit.script_archetype == "GuardArea" and not g_TacticalMap:GetAssignedAreas(unit) then |
|
g_TacticalMap:AssignUnit(unit, nil) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_InnerPerimeter_MainBuilding", nil, g_TacticalMap.PriorityHigh) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_Yard_Transition", nil, g_TacticalMap.PriorityMedium) |
|
g_TacticalMap:AssignUnit(unit, "PierreFight_FlankingDefense_Yard", nil, g_TacticalMap.PriorityLow) |
|
end |
|
end |
|
|
|
|
|
self:EquipProperWeapon(self.boss) |
|
for _, guard in ipairs(self.guards_melee) do |
|
self:EquipProperWeapon(guard) |
|
end |
|
self:EquipProperWeapon(self.guard_rpg) |
|
end |
|
|
|
local function FindWeaponByClass(unit, class, slot) |
|
if not slot then |
|
slot = unit.current_weapon |
|
elseif slot == "alt" then |
|
slot = (unit.current_weapon == "Handheld A") and "Handheld B" or "Handheld A" |
|
end |
|
|
|
local weapons = unit:GetEquippedWeapons(slot) |
|
|
|
for _, weapon in ipairs(weapons) do |
|
if IsKindOf(weapon, class) and (class ~= "Firearm" or not IsKindOf(weapon, "HeavyWeapon")) then |
|
return weapon |
|
end |
|
if IsKindOf(weapon, "Firearm") then |
|
for slot, sub in sorted_pairs(weapon.subweapons) do |
|
if IsKindOf(sub, class) and (class ~= "Firearm" or not IsKindOf(sub, "HeavyWeapon")) then |
|
return sub |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
|
|
function BossfightPierre:EquipProperWeapon(unit) |
|
if not IsValidTarget(unit) then return end |
|
|
|
if table.find(unit.AIKeywords, "Ordnance") then |
|
|
|
if not FindWeaponByClass(unit, "HeavyWeapon") and FindWeaponByClass(unit, "HeavyWeapon", "alt") then |
|
unit:SwapActiveWeapon() |
|
end |
|
elseif unit:GetArchetype().id == "Brute" then |
|
|
|
if not FindWeaponByClass(unit, "MeleeWeapon") then |
|
if FindWeaponByClass(unit, "MeleeWeapon", "alt") then |
|
unit:SwapActiveWeapon() |
|
elseif not FindWeaponByClass(unit, "Shotgun") and FindWeaponByClass(unit, "Shotgun", "alt") then |
|
unit:SwapActiveWeapon() |
|
end |
|
end |
|
else |
|
|
|
if not FindWeaponByClass(unit, "AssaultRifle") then |
|
if FindWeaponByClass(unit, "AssaultRifle", "alt") then |
|
unit:SwapActiveWeapon() |
|
elseif not FindWeaponByClass(unit, "SubmachineGun") then |
|
if FindWeaponByClass(unit, "SubmachineGun", "alt") then |
|
unit:SwapActiveWeapon() |
|
elseif not FindWeaponByClass(unit, "Firearm") and FindWeaponByClass(unit, "Firearm", "alt") then |
|
unit:SwapActiveWeapon() |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
function BossfightPierre:OnEnemyFall(unit) |
|
if g_Combat and IsValid(self.boss) and not self.boss:IsDead() and self.boss:GetDist(unit) < 5 * const.SlabSizeX then |
|
self.enrage_turn = g_Combat.current_turn + 3 |
|
end |
|
end |
|
|
|
function BossfightPierre:OnUnitDied(unit) |
|
if not g_Combat or not self.boss then return end |
|
if unit.team == self.boss.team and not table.find(unit.AIKeywords, "Sniper") then |
|
self.fallback_turn = g_Combat.current_turn |
|
if g_Teams[g_CurrentTeam] == self.boss.team then |
|
self.fallback_turn = self.fallback_turn + 1 |
|
end |
|
elseif unit:IsOnEnemySide(self.boss) then |
|
self:OnEnemyFall(unit) |
|
end |
|
end |
|
|
|
function BossfightPierre:OnUnitDowned(unit) |
|
return self:OnUnitDied(unit) |
|
end |