myspace / Lua /Tactical /Bossfight_Faucheaux.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.57 kB
-- area reached flags
local reachedPlayer = 1
local reachedBoss = 2
DefineClass.BossfightFaucheaux = {
__parents = { "Encounter" },
default_assigned_area = false,
-- state
run_away = false,
chosen_path = 1,
boss = false,
escape_turn = false,
escaped = false,
enemies_aware = false,
reached_areas = false, -- after Faucheaux has started running
released_areas = false,
}
g_SectorEncounters.K16 = "BossfightFaucheaux"
function BossfightFaucheaux:ShouldStart()
return not GetQuestVar("05_TakeDownFaucheux", "Completed")
end
function BossfightFaucheaux:Init()
self.reached_areas = {}
self.released_areas = {}
end
function BossfightFaucheaux:Setup()
self:InitAssignedArea()
end
function BossfightFaucheaux:GetDynamicData(data)
data.run_away = self.run_away or nil
data.chosen_path = (self.chosen_path ~= 1) and self.chosen_path or nil
data.escape_turn = self.escape_turn or nil
data.escaped = self.escaped or nil
data.reached_areas = table.copy(self.reached_areas)
data.released_areas = table.copy(self.released_areas)
data.boss = IsValid(self.boss) and self.boss:GetHandle() or nil
data.enemies_aware = self.enemies_aware or nil
end
function BossfightFaucheaux:SetDynamicData(data)
self.run_away = data.run_away
self.chosen_path = data.chosen_path
self.escape_turn = data.escape_turn
self.escaped = data.escaped
self.reached_areas = table.copy(data.reached_areas)
self.released_areas = table.copy(data.released_areas)
self.boss = data.boss and HandleToObject[data.boss]
self.enemies_aware = data.enemies_aware
end
local path1areas = {
"Control_Zone_HQ",
"Control_Zone_HQFront",
"Control_Zone_Containers1",
"Control_Zone_Containers2",
"ControlZone_OpenCorridor2",
"Control_Zone_FinalDest",
}
local path2areas = {
"Control_Zone_HQ",
"Control_Zone_MessHall",
"Control_Zone_Tents",
"Control_Zone_Armory",
"Control_Zone_Passage",
"Control_Zone_OfficerRoom",
"ControlZone_OpenCorridor2",
"Control_Zone_FinalDest",
}
local detection_areas = {
["Control_Zone_MessHall"] = true,
["Control_Zone_HQFront"] = true,
["Control_Zone_FaucheauxCabinet"] = true,
["Control_Zone_HQ"] = true,
["Control_Zone_Tents"] = true,
}
function BossfightFaucheaux:TriggerRetreat()
if self.run_away then
return
end
self.run_away = true
local path1alive, path1total = 0, 0
local path2alive, path2total = 0, 0
for _, unit in ipairs(g_Units) do
local x, y, z = unit:GetPosXYZ()
local unit_pos = point_pack(x, y, z)
local area = g_TacticalMap:GetUnitPrimaryArea(unit)
local original_area = self.original_area[unit]
if unit.team.player_enemy and table.find(path1areas, original_area) then
if not unit:IsDead() then
path1alive = path1alive + 1
end
path1total = path1total + 1
end
if unit.team.player_enemy and table.find(path2areas, original_area) then
if not unit:IsDead() then
path2alive = path2alive + 1
end
path2total = path2total + 1
end
end
local chance1 = MulDivRound(path1alive, 100, Max(1, path1total))
local chance2 = MulDivRound(path2alive, 100, Max(1, path2total))
if chance1 + chance2 > 0 then
local roll = InteractionRand(chance1 + chance2, "Bossfight")
self.chosen_path = (roll <= chance1) and 1 or 2
end
self.boss.script_archetype = "Faucheaux_BossRetreating"
end
function BossfightFaucheaux:OnDamageDone(attacker, target, dmg, hit_descr)
if target == self.boss then
self:TriggerRetreat()
end
end
function BossfightFaucheaux:OnAreaReached(area, flag)
if not self.run_away then
if flag == reachedPlayer and self.enemies_aware and detection_areas[area] then
self:TriggerRetreat()
end
return
end
if flag == reachedPlayer then
if area == "Control_Zone_KillingField1" or area == "Control_Zone_KillingField2" or area == "Control_Zone_Containers1" or area == "Control_Zone_Armory" then
self.released_areas.Control_Zone_EastWall = true
end
if area == "Control_Zone_KillingField1" or area == "Control_Zone_KillingField2" or area == "Control_Zone_Containers1" or area == "Control_Zone_MessHall" then
self.released_areas.Control_Zone_BlastEntrance = true
self.released_areas.Control_Zone_WestPicket = true
self.released_areas.Control_Zone_WestTower = true
end
if area == "Control_Zone_OpenCorridor1" or area == "Control_Zone_Containers1" or area == "Control_Zone_Tents" then
self.released_areas.Control_Zone_WallWest = true
self.released_areas.Control_Zone_RadioTower = true
end
elseif flag == reachedBoss then
if area == "Control_Zone_Tents" then
self.released_areas.Control_Zone_EastWall = true
end
if area == "Control_Zone_Containers1" or area == "Control_Zone_Armory" then
self.released_areas.Control_Zone_BlastEntrance = true
self.released_areas.Control_Zone_WestPicket = true
self.released_areas.Control_Zone_WestTower = true
self.released_areas.Control_Zone_WallWest = true
self.released_areas.Control_Zone_RadioTower = true
end
if area == "Control_Zone_FinalDest" then
self.escape_turn = g_Combat.current_turn + 1
end
end
end
function BossfightFaucheaux:UpdateAwareness()
if not self.boss then return end
local aware = self.enemies_aware
if not aware then
for _, unit in ipairs(g_Units) do
if unit.team == self.boss.team then
aware = aware or unit:IsAware()
end
end
end
if aware then
for _, unit in ipairs(g_Units) do
if unit.team == self.boss.team then
unit:RemoveStatusEffect("Unaware")
end
end
self.enemies_aware = true
end
end
function BossfightFaucheaux:UpdateAreaProgress()
assert(g_TacticalMap)
if not self.run_away then
return
end
for _, unit in ipairs(g_Units) do
local areas = g_TacticalMap:GetUnitAreas(unit)
if unit.team.player_team and not unit:IsDead() then
for area_id in tac_area_ids(areas) do
if band(self.reached_areas[area_id] or 0, reachedPlayer) == 0 then
self.reached_areas[area_id] = bor(self.reached_areas[area_id] or 0, reachedPlayer)
self:OnAreaReached(area_id, reachedPlayer)
end
end
elseif unit == self.boss and not unit:IsDead() then
for area_id in tac_area_ids(areas) do
if band(self.reached_areas[area_id] or 0, reachedBoss) == 0 then
self.reached_areas[area_id] = bor(self.reached_areas[area_id] or 0, reachedBoss)
self:OnAreaReached(area_id, reachedBoss)
end
end
end
end
end
function BossfightFaucheaux:AssignToNextArea(path, cur_area)
local area_idx = table.find(path, cur_area)
if area_idx then
-- assign to next area
local next_area_idx = Min(area_idx + 1, #path)
local area = path[next_area_idx]
g_TacticalMap:AssignUnit(self.boss, area, "reset", g_TacticalMap.PriorityHigh)
if area == "ControlZone_OpenCorridor2" then
g_TacticalMap:AssignUnit(self.boss, "ControlZone_OpenCorridor1", nil, g_TacticalMap.PriorityMedium)
end
g_TacticalMap:AssignUnit(self.boss, cur_area, nil, g_TacticalMap.PriorityLow)
else
-- fallback: assign to the nearest area (dist to marker)
local area, min_dist
local markers = MapGetMarkers()
for _, id in ipairs(path) do
local idx = table.find(markers, "ID", id)
if idx and id ~= "Control_Zone_OpenCorridor2" then
local dist = self.boss:GetDist(markers[idx])
if dist < (min_dist or dist + 1) then
area, min_dist = id, dist
end
end
end
if area then
g_TacticalMap:AssignUnit(self.boss, area, "reset")
else
assert(false, string.format("unable to find fallback area to assign Faucheaux following path %d; current area: %s", self.chosen_path, tostring(cur_area)))
end
end
end
function BossfightFaucheaux:UpdateUnitArchetypes()
assert(g_TacticalMap)
for _, unit in ipairs(g_Units) do
if unit.team.player_enemy and not unit:IsDead() then
local def_id = unit.unitdatadef_id or false
local classdef = g_Classes[def_id]
local base_archetype = classdef and classdef.archetype
if self.run_away and unit ~= self.boss then
local orig_area = self.original_area[unit]
unit.archetype = base_archetype
if not self.released_areas[orig_area] then
unit.scrpit_archetype = "GuardArea"
else
unit.scrpit_archetype = nil
end
else
unit.scrpit_archetype = "GuardArea"
end
end
end
-- Faucheaux
if self.run_away then
self.boss.scrpit_archetype = "Faucheaux_BossRetreating"
local cur_area = g_TacticalMap:GetUnitPrimaryArea(self.boss)
if cur_area == "Control_Zone_FinalDest" then
g_TacticalMap:AssignUnit(self.boss, "Control_Zone_FinalDest", "reset")
elseif self.chosen_path == 1 then
self:AssignToNextArea(path1areas, cur_area)
else
self:AssignToNextArea(path2areas, cur_area)
end
else
g_TacticalMap:AssignUnit(self.boss, "Control_Zone_FaucheauxCabinet", "reset")
end
end
function BossfightFaucheaux:OnTurnStart()
if g_Combat and self.escape_turn and g_Combat.current_turn >= self.escape_turn then
-- lose sequence
if not self.escaped then
self.escaped = true
-- in a thread so it can wait for the setpiece here
CreateGameTimeThread(function()
-- wait the boss escape setpiece to start
while not IsSetpiecePlaying() do
WaitMsg("SetpieceStarted", 10)
end
-- wait the boss escape setpiece to end
while IsSetpiecePlaying() do
WaitMsg("SetpieceEnded", 10)
end
-- run the rest of the logic now
self:OnTurnStart()
end)
return
end
end
if self.escaped then return end
for _, obj in ipairs(Groups.Faucheux) do
if IsKindOf(obj, "Unit") then
self.boss = obj
break
end
end
g_Encounter:UpdateAwareness()
g_Encounter:UpdateAreaProgress()
g_Encounter:UpdateUnitArchetypes()
end