-- ========== GENERATED BY CombatAction Editor DO NOT EDIT MANUALLY! ========== PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = 2000, ActionPoints = 2000, ActionType = "Ranged Attack", AimType = "line", Comment = "-> AttackDual FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(208869050611, --[[CombatAction DualShot Description]] "The Dual Shot attack produces a Basic Attack from each gun."), DisplayName = T(712971913349, --[[CombatAction DualShot DisplayName]] "Dual Shot"), DisplayNameShort = T(811622228569, --[[CombatAction DualShot DisplayNameShort]] "Dual"), FiringModeMember = "AttackDual", GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if not weapon1 or not weapon2 then return -1 end local aim = args and args.aim or 0 -- pass self.ActionPointDelta to both to allow the RainHeavy check to work correctly return Max(unit:GetAttackAPCost(self, weapon1, false, aim, self.ActionPointDelta), unit:GetAttackAPCost(self, weapon2, false, aim, self.ActionPointDelta)) end, GetActionDamage = function (self, unit, target, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if not weapon1 or not weapon2 then return 0 end local aim = args and args.aim if not aim then local dlg = GetInGameInterfaceModeDlg() if IsKindOf(dlg, "IModeCombatAttackBase") and dlg.crosshair then aim = dlg.crosshair.aim end end local weapon1, weapon2 = self:GetAttackWeapons(unit, args) assert(weapon1 and weapon2) local w1Attack = weapon1:GetBaseAttack(unit) w1Attack = w1Attack and CombatActions[w1Attack] local w2Attack = weapon2:GetBaseAttack(unit) w2Attack = w2Attack and CombatActions[w2Attack] -- Demote attacks to single shot if attacks dont match. -- This prevents discrepencies in cth between attacks and such. if w1Attack ~= w2Attack then w1Attack = CombatActions.SingleShot w2Attack = CombatActions.SingleShot end args = args or {} args.weapon = weapon1 local dmg1, base1 = w1Attack:GetActionDamage(unit, target, args) args.weapon = weapon2 args.lof = false local dmg2, base2 = w2Attack:GetActionDamage(unit, target, args) args.weapon = false args.lof = false local damage = dmg1 + dmg2 local baseCombo = base1 + base2 return damage, base1, 0, { min = Min(base1, base2), max = damage, wep1_damage = dmg1, wep2_damage = dmg2, wep1_base = base1, wep2_base = base2 } end, GetActionDescription = function (self, units) local unit = units[1] local damage = self:GetActionDamage(unit) return T{self.Description, damage = damage} end, GetActionResults = function (self, unit, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) args = args or {} args.used_action_id = self.id args.weapon = weapon1 local attack1, args1 = w1Attack:GetActionResults(unit, args) args.weapon = weapon2 args.lof = false -- GetActionResults mutates our table lol local attack2, args2 = w2Attack:GetActionResults(unit, args) args.weapon = false args.lof = false return MergeAttacks({ attack1, attack2 }, { args1, args2 }) end, GetAttackWeapons = function (self, unit, args) return unit:GetActiveWeapons("Firearm", "strict") end, GetUIState = function (self, units, args) local unit = units[1] if not unit then return "hidden" end local weapon1, weapon2 = self:GetAttackWeapons(unit, args) local enable, reason = unit:CanUseWeapon(weapon1) if not enable then return "disabled", reason end enable, reason = unit:CanUseWeapon(weapon2) if not enable then return "disabled", reason end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/dual_shot", IconFiringMode = "UI/Hud/fm_dual_shot", IdDefault = "DualShotdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = -1, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "DualShot", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPoints = 3000, ActionType = "Ranged Attack", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(863161981262, --[[CombatAction MGBurstFire Description]] "Make a burst attack. Lower accuracy against distant enemies, and further reduced accuracy if fired without being Set."), DisplayName = T(706547341778, --[[CombatAction MGBurstFire DisplayName]] "Long Burst"), Execute = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) args.num_shots = weapon:GetAutofireShots(self) args.multishot = true local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return -1 end -- GetAttackAPCost returns aim cost as second value, while GetAPCost's second return value has a different semantic (display cost) local cost = unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0) return cost end, GetActionDamage = function (self, unit, target, args) local weapon = args and args.weapon or self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit and unit:GetBaseDamage(weapon) or weapon.Damage local penalty = self:ResolveValue("dmg_penalty") local num_shots = weapon:GetAutofireShots(self) base = MulDivRound(base, Max(0, 100 + penalty), 100) local damage = num_shots*base return damage, base, damage - base end, GetActionDescription = function (self, units) local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end local unit = units[1] local coneDescription = T{""} local interrupts_info = "" local overwatch = g_Overwatch[unit] if overwatch and overwatch.permanent then coneDescription = T(480046777812, " within the set cone") interrupts_info = T{757307734445, "Remaining interrupt attacks: ", interrupts = unit:GetNumMGInterruptAttacks()} end return T{description, coneDescription = coneDescription, interrupts_info = interrupts_info} end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionIcon = function (self, units) -- replace icon when using Revolver local unit = units and units[1] if unit then local weapon = self:GetAttackWeapons(unit) if IsKindOf(weapon, "Revolver") then return CombatActions.Fanning.Icon end end return self.Icon end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.weapon = args.weapon or self:GetAttackWeapons(unit, args) args.num_shots = args.num_shots or args.weapon and args.weapon:GetAutofireShots(self) args.multishot = true args.damage_bonus = self:ResolveValue("dmg_penalty") --args.cth_loss_per_shot = self:ResolveValue("cth_loss_per_shot") local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) return CombatActionGetOneAttackableEnemy(self, units and units[1], nil, CombatActionTargetFilters.MGBurstFire, units) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetTargets = function (self, units) return CombatActionGetAttackableEnemies(self, units and units[1], nil, CombatActionTargetFilters.MGBurstFire, units) end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/burst_fire", IdDefault = "MGBurstFiredefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectHeavyAttack", KeybindingSortId = "2372", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 8, 'Tag', "", }), PlaceObj('PresetParamPercent', { 'Name', "cth_loss_per_shot", 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "dmg_penalty", 'Value', -50, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "min_shots", 'Value', 1, 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "WeaponAttacks", id = "MGBurstFire", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", Comment = "-> AttackDual FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(520163235403, --[[CombatAction RightHandShot Description]] "Cheap attack with your right-hand weapon that conserves ammo."), DisplayName = T(282789699649, --[[CombatAction RightHandShot DisplayName]] "Right Hand Shot"), DisplayNameShort = T(145254451163, --[[CombatAction RightHandShot DisplayNameShort]] "Right Hand"), FiringModeMember = "AttackDual", GetAPCost = function (self, unit, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) if not w1Attack then return -1 end args = args and table.copy(args) or {} args.weapon = weapon1 return w1Attack:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) args = args or {} args.weapon = weapon1 local dmg, base, bonus, params = w1Attack:GetActionDamage(unit, target, args) args.weapon = false return dmg, base, bonus, params end, GetActionDescription = function (self, units) local w1Attack, w2Attack = GetDualShotAttacks(units[1]) return w1Attack:GetActionDescription(units) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) args = args or {} args.weapon = weapon1 local result, att_args = w1Attack:GetActionResults(unit, args) args.weapon = false return result, att_args end, GetAttackWeapons = function (self, unit, args) local weapon1, weapon2 = unit:GetActiveWeapons("Firearm", "strict") return weapon1 end, GetUIState = function (self, units, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(units[1]) if not w1Attack then return "hidden" end local enable, reason = units[1]:CanUseWeapon(weapon1) if not enable then return "disabled", reason end args = args or {} args.weapon = weapon1 local uiState = w1Attack:GetUIState(units, args) args.weapon = false return uiState end, Icon = "UI/Icons/Hud/attack", IconFiringMode = "UI/Hud/fm_right_hand_shot", IdDefault = "RightHandShotdefault", IsTargetableAttack = true, MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "RightHandShot", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", Comment = "-> Attack FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(298670513550, --[[CombatAction SingleShot Description]] "Cheap attack that conserves ammo."), DisplayName = T(355970076448, --[[CombatAction SingleShot DisplayName]] "Single Shot"), DisplayNameShort = T(392229752968, --[[CombatAction SingleShot DisplayNameShort]] "Single"), FiringModeMember = "Attack", GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if unit:OutOfAmmo(weapon2) or unit:IsWeaponJammed(weapon2) then weapon2 = nil end if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0, self.ActionPointDelta) or -1 end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) local unit = units[1] local _, _, _, params = self:GetActionDamage(unit) local descr = T{self.Description, damage = GetDamageRangeText(params.min, params.max), crit = params.critChance} descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon, _, list = unit:GetActiveWeapons("Firearm") return weapon end, GetUIState = function (self, units, args) local unit = units[1] local attackWep = self:GetAttackWeapons(unit, args) if not attackWep then return "hidden" end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/attack", IconFiringMode = "UI/Hud/fm_single_shot", IdDefault = "SingleShotdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "SingleShot", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", Comment = "mortar attack", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(169779903706, --[[CombatAction Bombard Description]] "Spends all AP\nSetup a zone bombarded at the start of your next turn. The amount of attacks depends on spent AP.\n\n attacks."), DisplayName = T(293721021077, --[[CombatAction Bombard DisplayName]] "Bombard"), GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) return Max(unit:GetUIActionPoints(), unit:GetAttackAPCost(self, weapon, false, 0)) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if weapon and IsKindOf(weapon.ammo, "Ordnance") then return weapon.ammo.BaseDamage end return 0 end, GetActionDescription = function (self, units) local unit = units[1] local attacks = unit and (unit:GetUIActionPoints() / (self:ResolveValue("ap_per_shot")*const.Scale.AP)) or 1 return T{self.Description, attacks = attacks} end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) local target = ResolveGrenadeTargetPos(args.target) local ammos = weapon and unit:GetAvailableAmmos(weapon, nil, "unique") if not weapon or not target or not ammos then return {} end local args = table.copy(args) local cost_ap = args.spent_ap or self:GetAPCost(unit) args.weapon = weapon args.target = target args.ordnance = args.ordnance or ammos[1] args.can_use_covers = false args.bombard_shots = cost_ap / (self:ResolveValue("ap_per_shot")*const.Scale.AP) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("HeavyWeapon") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/heavy_weapon_attack", IdDefault = "Bombarddefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectHeavyAttack", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "ap_per_shot", 'Value', 3, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("PrepareBombard", self.id, ap, ...) end, SortKey = 1, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "WeaponAttacks", id = "Bombard", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "cone", Comment = "-> ShotgunAttack FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(734174652599, --[[CombatAction Buckshot Description]] "All enemy targets in cover are .\nInflicts additional status effects based on the ammo. All targets affected by the area of attack receive additional damage."), DisplayName = T(673459341122, --[[CombatAction Buckshot DisplayName]] "Shotgun Shot"), DisplayNameShort = T(686623207963, --[[CombatAction Buckshot DisplayNameShort]] "Single"), FiringModeMember = "AttackShotgun", GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) local base = weapon and unit:GetBaseDamage(weapon) or 0 local aoeDamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100) return base, base, 0, { aoe_damage = aoeDamage } end, GetActionDescription = function (self, units) local unit = units[1] local damage, _, _, params = self:GetActionDamage(unit) local descr = T{self.Description, damage = damage, aoedamage = params.aoe_damage} descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local args = table.copy(args) if not args.target_spot_group then args.num_shots = 0 end args.aoe_action_id = self.id args.fx_action = "WeaponBuckshot" args.aoe_fx_action = "WeaponBuckshot" args.single_fx = true args.aoe_damage_type = "percent" args.aoe_damage_value = const.Weapons.ShotgunCollateralDamage args.buckshot_scatter_fx = 10 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return 2 end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/shotgun_shot", IconFiringMode = "UI/Hud/fm_buckshot", IdDefault = "Buckshotdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) local dlg = GetInGameInterfaceModeDlg() if IsKindOf(dlg, "IModeCombatAreaAim") and dlg.crosshair then CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "attack") else CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end end, basicAttack = true, group = "WeaponAttacks", id = "Buckshot", }) PlaceObj('CombatAction', { ActionPointDelta = 1000, ActionType = "Ranged Attack", AimType = "cone", Comment = "-> ShotgunAttack FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(112396421802, --[[CombatAction BuckshotBurst Description]] "All enemy targets in cover are .\nShoots bullets at the target. Lower accuracy vs distant enemies. All targets affected by the area of attack receive additional damage."), DisplayName = T(750178209605, --[[CombatAction BuckshotBurst DisplayName]] "Buckshot Burst"), DisplayNameShort = T(946188984206, --[[CombatAction BuckshotBurst DisplayNameShort]] "Burst"), FiringModeMember = "AttackShotgun", GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit and unit:GetBaseDamage(weapon) or weapon.Damage local aoeDamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100) local num_shots = weapon:GetAutofireShots(self) local damage = num_shots*base return damage, base, damage - base, { aoe_damage = aoeDamage } end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local aoedamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100) local num_shots = IsKindOf(weapon, "Firearm") and weapon:GetAutofireShots(self) or 1 local descr = T{self.Description, num_shots = num_shots, damage = base, aoedamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100)} descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local args = table.copy(args) local weapon = self:GetAttackWeapons(unit) args.num_shots = weapon and weapon:GetAutofireShots(self) or 1 args.aoe_action_id = self.id args.fx_action = "WeaponBuckshot" args.buckshot_scatter_fx = 10 args.aoe_damage_type = "percent" args.aoe_damage_value = const.Weapons.ShotgunCollateralDamage local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) -- make sure the fx action plays local shots = results.attacks and results.attacks[1].shots or empty_table if #shots == 0 then attack_args.aoe_fx_action = "WeaponBuckshot" end return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return 2 end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/shotgun_shot", IconFiringMode = "UI/Hud/fm_buckshot_burst", IdDefault = "BuckshotBurstdefault", IsTargetableAttack = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 3, 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) local dlg = GetInGameInterfaceModeDlg() if IsKindOf(dlg, "IModeCombatAreaAim") and dlg.crosshair then CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "attack") else CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end end, basicAttack = true, group = "WeaponAttacks", id = "BuckshotBurst", }) PlaceObj('CombatAction', { ActionPointDelta = 1000, ActionPoints = 1000, ActionType = "Ranged Attack", AimType = "cone", Comment = "-> ShotgunAttack FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(212508932528, --[[CombatAction DoubleBarrel Description]] "All enemy targets in cover are .\nInflicts additional status effects based on the ammo. All targets affected by the area of attack receive additional damage."), DisplayName = T(745680006620, --[[CombatAction DoubleBarrel DisplayName]] "Double Barrel"), DisplayNameShort = T(425861398044, --[[CombatAction DoubleBarrel DisplayNameShort]] "Double"), FiringModeMember = "AttackShotgun", GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end local cost = unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0) return cost and cost + self.ActionPointDelta or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) local base = weapon and unit:GetBaseDamage(weapon) or 0 return MulDivRound(base, const.Weapons.ShotgunCollateralDamage * const.Weapons.DoubleBarrelDamageBonus, 10000) end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) base = MulDivRound(base, 100+const.Weapons.DoubleBarrelDamageBonus, 100) return T{self.Description, damage = base, aoedamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100)} end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.weapon = self:GetAttackWeapons(unit, args) if not args.target_spot_group then args.num_shots = 0 end args.aoe_action_id = self.id args.fx_action = "WeaponBuckshot" args.aoe_fx_action = "WeaponBuckshot" args.single_fx = true args.aoe_damage_type = "percent" args.aoe_damage_value = const.Weapons.ShotgunCollateralDamage args.consumed_ammo = 2 args.damage_bonus = const.Weapons.DoubleBarrelDamageBonus args.buckshot_scatter_fx = 10 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return 2 end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not weapon or not weapon.ammo or weapon.ammo.Amount < 2 then return "disabled", AttackDisableReasons.InsufficientAmmo end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/double_barrel", IconFiringMode = "UI/Hud/fm_double_barrel", IdDefault = "DoubleBarreldefault", IsTargetableAttack = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "bullets", 'Value', 2, 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 1, UIBegin = function (self, units, args) local dlg = GetInGameInterfaceModeDlg() if IsKindOf(dlg, "IModeCombatAreaAim") and dlg.crosshair then CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "attack") else CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end end, basicAttack = true, group = "WeaponAttacks", id = "DoubleBarrel", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", Comment = "HeavyWeaponAttack", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(950970877551, --[[CombatAction GrenadeLauncherFire Description]] "Blasts enemies in the area of effect. Inflicts additional status effects based on grenade type."), DisplayName = T(636102722781, --[[CombatAction GrenadeLauncherFire DisplayName]] "Launch Grenade"), GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) return unit:GetAttackAPCost(self, weapon, false, 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if weapon and IsKindOf(weapon.ammo, "Ordnance") then return weapon.ammo.BaseDamage end return 0 end, GetActionDescription = function (self, units) local unit = units[1] local weapon = unit and self:GetAttackWeapons(unit) if weapon then return T{self.Description, damage = weapon:GetBaseDamage()} end local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end return description end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) local target = ResolveGrenadeTargetPos(args.target) local ammos = weapon and unit:GetAvailableAmmos(weapon, nil, "unique") if not weapon or not target or not ammos then return {} end local args = table.copy(args) args.weapon = weapon args.target = target args.ordnance = args.ordnance or ammos[1] local attack_args = unit:PrepareAttackArgs(self.id, args) local results = weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("HeavyWeapon") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not weapon then return "hidden" end if not weapon.ammo or weapon.ammo.Amount < 1 then return "disabled", AttackDisableReasons.OutOfAmmo end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/heavy_weapon_attack", IdDefault = "GrenadeLauncherFiredefault", KeybindingFromAction = "actionRedirectHeavyAttack", MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("HeavyWeaponAttack", self.id, ap, ...) end, SortKey = 1, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "WeaponAttacks", id = "GrenadeLauncherFire", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", Comment = "-> AttackDual FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(533333504738, --[[CombatAction LeftHandShot Description]] "Cheap attack with your left-hand weapon that conserves ammo."), DisplayName = T(458191457929, --[[CombatAction LeftHandShot DisplayName]] "Left Hand Shot"), DisplayNameShort = T(158585226110, --[[CombatAction LeftHandShot DisplayNameShort]] "Left Hand"), FiringModeMember = "AttackDual", GetAPCost = function (self, unit, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) if not w2Attack then return -1 end args = args and table.copy(args) or {} args.weapon = weapon2 return w2Attack:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) args = args or {} args.weapon = weapon2 local dmg, base, bonus, params = w2Attack:GetActionDamage(unit, target, args) args.weapon = false return dmg, base, bonus, params end, GetActionDescription = function (self, units) local w1Attack, w2Attack = GetDualShotAttacks(units[1]) return w2Attack:GetActionDescription(units) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(unit) args = args or {} args.weapon = weapon2 local result, att_args = w2Attack:GetActionResults(unit, args) args.weapon = false return result, att_args end, GetAttackWeapons = function (self, unit, args) local weapon1, weapon2 = unit:GetActiveWeapons("Firearm", "strict") return weapon2 end, GetUIState = function (self, units, args) local w1Attack, w2Attack, weapon1, weapon2 = GetDualShotAttacks(units[1]) if not w1Attack then return "hidden" end local enable, reason = units[1]:CanUseWeapon(weapon2) if not enable then return "disabled", reason end args = args or {} args.weapon = weapon2 local uiState = w2Attack:GetUIState(units, args) args.weapon = false return uiState end, Icon = "UI/Icons/Hud/attack", IconFiringMode = "UI/Hud/fm_left_hand_shot", IdDefault = "LeftHandShotdefault", IsTargetableAttack = true, MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "LeftHandShot", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", Comment = "HeavyWeaponAttack", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(393692003865, --[[CombatAction RocketLauncherFire Description]] "Blasts enemies in the area of effect. Inflicts additional status effects based on the rocket type."), DisplayName = T(259883670447, --[[CombatAction RocketLauncherFire DisplayName]] "Launch Rocket"), GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) return unit:GetAttackAPCost(self, weapon, false, 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if weapon and IsKindOf(weapon.ammo, "Ordnance") then return weapon.ammo.BaseDamage end return 0 end, GetActionDescription = function (self, units) local unit = units[1] local weapon = unit and self:GetAttackWeapons(unit) if weapon then return T{self.Description, damage = weapon:GetBaseDamage()} end local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end return description end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) local target = ResolveGrenadeTargetPos(args.target) local ammos = weapon and unit:GetAvailableAmmos(weapon, nil, "unique") if not weapon or not target or not ammos then return {} end local args = table.copy(args) args.weapon = weapon args.target = args.target or target args.ordnance = args.ordnance or ammos[1] args.target_height_range = guim local attack_args = unit:PrepareAttackArgs(self.id, args) local results = weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("HeavyWeapon") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not weapon then return "hidden" end if not weapon.ammo or weapon.ammo.Amount < 1 then return "disabled", AttackDisableReasons.OutOfAmmo end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/heavy_weapon_attack", IdDefault = "RocketLauncherFiredefault", KeybindingFromAction = "actionRedirectHeavyAttack", MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("HeavyWeaponAttack", self.id, ap, ...) end, SortKey = 1, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "WeaponAttacks", id = "RocketLauncherFire", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPoints = 3000, ActionType = "Ranged Attack", Comment = "-> Attack FiringMode", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(838141287304, --[[CombatAction BurstFire Description]] "Shoots bullets at the target. Lower accuracy against distant enemies."), DisplayName = T(885711257338, --[[CombatAction BurstFire DisplayName]] "Burst Fire"), DisplayNameShort = T(421007639739, --[[CombatAction BurstFire DisplayNameShort]] "Burst"), Execute = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) args.num_shots = weapon:GetAutofireShots(self) args.multishot = true local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, FiringModeMember = "Attack", GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and (unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0) + self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit and unit:GetBaseDamage(weapon) or weapon.Damage local penalty = self:ResolveValue("dmg_penalty") local num_shots = weapon:GetAutofireShots(self) base = MulDivRound(base, Max(0, 100 + penalty), 100) local damage = num_shots*base return damage, base, damage - base end, GetActionDescription = function (self, units) local description = self.Description local unit = units and units[1] if not unit then return self:GetActionDisplayName() end local weapon = self:GetAttackWeapons(unit) -- replace description when using Revolver if IsKindOf(weapon, "Revolver") then description = CombatActions.Fanning.Description end local damage, base, bonus = self:GetActionDamage(unit) local num_shots = weapon:GetAutofireShots(self) local descr = T{description, num = num_shots, damage = base} return CombatActionsAppendFreeAimDescription(self, unit, descr) end, GetActionDisplayName = function (self, units) local name = self.DisplayName -- replace name when using Revolver local unit = units and units[1] if unit then local weapon = self:GetAttackWeapons(unit) if IsKindOf(weapon, "Revolver") then name = CombatActions.Fanning.DisplayName end end if (name or "") == "" then name = Untranslated(self.id) end return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionIcon = function (self, units) -- replace icon when using Revolver local unit = units and units[1] if unit then local weapon = self:GetAttackWeapons(unit) if IsKindOf(weapon, "Revolver") then return CombatActions.Fanning.Icon end end return self.Icon end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.weapon = self:GetAttackWeapons(unit, args) args.num_shots = args.num_shots or args.weapon and args.weapon:GetAutofireShots(self) args.multishot = true args.damage_bonus = self:ResolveValue("dmg_penalty") --args.cth_loss_per_shot = self:ResolveValue("cth_loss_per_shot") local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon, _, list = unit:GetActiveWeapons("Firearm") return weapon end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) local num_shots = weapon:GetAutofireShots(self) if not weapon.ammo or weapon.ammo.Amount < num_shots then return "disabled", AttackDisableReasons.InsufficientAmmo end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/burst_fire", IconFiringMode = "UI/Hud/fm_burst_fire", IdDefault = "BurstFiredefault", IsTargetableAttack = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 3, 'Tag', "", }), PlaceObj('PresetParamPercent', { 'Name', "cth_loss_per_shot", 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "dmg_penalty", 'Value', -50, 'Tag', "%", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 2, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "BurstFire", }) PlaceObj('CombatAction', { ActionType = "Melee Attack", AimType = "melee", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(892145930253, --[[CombatAction MeleeAttack Description]] "If the enemy is in cover, they will become ."), DisplayName = T(318246892071, --[[CombatAction MeleeAttack DisplayName]] "Melee strike"), EvalTarget = function (self, units, target, args) local unit = units[1] if (not args or not args.goto_pos) and (not IsValid(target) or not unit:GetClosestMeleeRangePos(target)) then return 0 end return unit:CalcChanceToHit(target, self, args) end, GetAPCost = function (self, unit, args) return GetMeleeAttackAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, damage, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionDescription(units) end local damage, base, bonus = self:GetActionDamage(unit) local descr if LastLoadedOrLoadingIMode == "IModeCombatMelee" then descr = T{652639293521, "Select a unit to attack.", damage = damage} else descr = T{self.Description, damage = damage, basedamage = base, bonusdamage = bonus} end descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionDisplayName(units) end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionIcon = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionIcon(units) end return self.Icon end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 args.weapon = self:GetAttackWeapons(unit, args) if IsKindOf(args.weapon, "GutHookKnife") then args.applied_status = { "Bleeding" } end local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] return CombatActionGetOneAttackableEnemy(self, unit, nil, CombatActionTargetFilters.MeleeAttack, unit) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("MeleeWeapon") end, GetDefaultTarget = function (self, unit) local units = {unit} local targets = self:GetTargets(units) local weapon = self:GetAttackWeapons(unit) local nearest, min_dist for _, target in ipairs(targets) do if unit:IsOnEnemySide(target) and HasVisibilityTo(unit.team, target) then local pos = unit:GetClosestMeleeRangePos(target) local dist if not g_Combat or unit:CanAttack(target, weapon, self, 0, pos) then dist = pos and unit:GetDist(pos) end if dist and (not min_dist or dist < min_dist) then nearest, min_dist = target, dist end end end return nearest, min_dist end, GetTargets = function (self, units) local unit = units[1] return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.MeleeAttack, unit) end, GetUIState = function (self, units, args) local unit = units[1] args = args or {} args.ap_cost_breakdown = args.ap_cost_breakdown or {} local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost, self.id, args) then return "disabled", AttackDisableReasons.NoAP end return "enabled" end, Icon = "UI/Icons/Hud/melee", IdDefault = "MeleeAttackdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MoveStep = true, MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("MeleeAttack", self.id, ap, ...) end, SortKey = 2, StealthAttack = true, UIBegin = function (self, units, args) if not args or not args.free_aim then local action = GetMeleeAttackAction(self, units[1]) if action ~= self and action:GetUIState(units) == "enabled" then return action:UIBegin(units, args) end end CombatActionAttackStart(self, units, args, "IModeCombatMelee", "attack") end, UseFreeMove = true, basicAttack = true, group = "WeaponAttacks", id = "MeleeAttack", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Ranged Attack", AimType = "mobile", CostBasedOnWeapon = true, Description = T(978968390011, --[[CombatAction MobileShot Description]] "Once per turn.\nMove to a new location and then shoot the closest enemy."), DisplayName = T(755564702219, --[[CombatAction MobileShot DisplayName]] "Mobile Shot"), GetActionDamage = function (self, unit, target, args) local rangedAttack = unit:GetDefaultAttackAction("ranged") return rangedAttack:GetActionDamage(unit, target, args) end, GetActionDescription = function (self, units) local description = self.Description local unit = units and units[1] if not unit then return self:GetActionDisplayName() end local damage, base, bonus = self:GetActionDamage(unit) return T{description, damage = damage, basedamage = base, bonusdamage = bonus} end, GetActionResults = function (self, unit, args) return GetMobileShotResults(self, unit, args) end, GetAnyTarget = function (self, units) return self:GetTargets(units)[1] end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon = unit:GetActiveWeapons("Firearm") return weapon -- make sure to return only 1 weapon, the attack doesn't use 2 end, GetTargets = function (self, units) local unit = units[1] if unit then return GetEnemies(unit) end return {} end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/mobile_shot", IdDefault = "MobileShotdefault", IsAimableAttack = false, KeybindingSortId = "2371", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "mobile_move_ap", 'Value', 8, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "cooldown", 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("RunAndGun", self.id, ap, ...) end, SortKey = 2, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatMovingAttack") end, group = "WeaponAttacks", id = "MobileShot", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = 2000, ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "mobile", CostBasedOnWeapon = true, Description = T(986970516880, --[[CombatAction RunAndGun Description]] "Once per turn.\nMove to a new position. Fire up to bursts during movement toward the closest enemies. Each shot suffers - Accuracy penalty."), DisplayName = T(624088050886, --[[CombatAction RunAndGun DisplayName]] "Run and Gun"), GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local damage = unit:GetBaseDamage(weapon) local num_shots = self:ResolveValue("mobile_num_shots") return damage, damage / num_shots, 0 end, GetActionDescription = function (self, units) local description = self.Description local unit = units and units[1] if not unit then return self:GetActionDisplayName() end local damage, base, bonus = self:GetActionDamage(unit) local num_shots = self:ResolveValue("mobile_num_shots") local penalty = Presets.ChanceToHitModifier.Default.RunAndGun:ResolveValue("Penalty") description = T{description, damage = damage, basedamage = base, bonusdamage = bonus, num_shots = num_shots, penalty = penalty} if unit.stance ~= "Standing" then description = description .. T(801805830695, "You will end up in Standing stance.") end return description end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit) args.attack_id = "BurstFire" args.num_shots = weapon and weapon:GetAutofireShots("BurstFire") or CombatActions.BurstFire:ResolveValue("num_shots") args.multishot = true return GetMobileShotResults(self, unit, args) end, GetAnyTarget = function (self, units) return self:GetTargets(units)[1] end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon = unit:GetActiveWeapons("Firearm") return weapon -- make sure to return only 1 weapon, the attack doesn't use 2 end, GetTargets = function (self, units) local unit = units[1] if unit then return GetEnemies(unit) end return {} end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/run_and_gun", IdDefault = "RunAndGundefault", IsAimableAttack = false, KeybindingSortId = "2372", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "mobile_move_ap", 'Value', 8, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "cooldown", 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "mobile_num_shots", 'Value', 3, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 9, 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("RunAndGun", self.id, ap, ...) end, SortKey = 2, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatMovingAttack") end, group = "WeaponAttacks", id = "RunAndGun", }) PlaceObj('CombatAction', { ActionType = "Melee Attack", AimType = "melee", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(307739610044, --[[CombatAction UnarmedAttack Description]] "If the enemy is in cover, they will become . Enemies have a chance to be knocked ."), DisplayName = T(878149704237, --[[CombatAction UnarmedAttack DisplayName]] "Punch"), EvalTarget = function (self, units, target, args) return CombatActions.MeleeAttack.EvalTarget(self, units, target, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end if not args.goto_pos then args.goto_pos = unit:GetClosestMeleeRangePos(args.target) end args.stance = "Standing" CombatActionExecuteWithMove(self, unit, args) end, GetAPCost = function (self, unit, args) return GetMeleeAttackAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, damage, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionDescription(units) end local damage, base, bonus = self:GetActionDamage(unit) local descr if LastLoadedOrLoadingIMode == "IModeCombatMelee" then descr = T{315652693177, "Select a unit to punch.", damage = damage} else descr = T{self.Description, damage = damage, basedamage = base, bonusdamage = bonus} end descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionDisplayName(units) end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionIcon = function (self, units) local unit = units[1] local action = GetMeleeAttackAction(self, unit) if action ~= self and action:GetUIState(units) == "enabled" then return action:GetActionIcon(units) end return self.Icon end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) return CombatActions.MeleeAttack.GetAnyTarget(self, units) end, GetAttackWeapons = function (self, unit, args) return unit:GetActiveWeapons("UnarmedWeapon") end, GetDefaultTarget = function (self, unit) return CombatActions.MeleeAttack.GetDefaultTarget(self, unit) end, GetTargets = function (self, units) return CombatActions.MeleeAttack.GetTargets(self, units) end, GetUIState = function (self, units, args) local unit = units[1] args = args or {} args.ap_cost_breakdown = args.ap_cost_breakdown or {} local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost, self.id, args) then return "disabled", AttackDisableReasons.NoAP end return "enabled" end, Icon = "UI/Icons/Hud/melee", IdDefault = "UnarmedAttackdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("MeleeAttack", self.id, ap, ...) end, SortKey = 2, StealthAttack = true, UIBegin = function (self, units, args) return CombatActions.MeleeAttack.UIBegin(self, units, args) end, UseFreeMove = true, basicAttack = true, group = "WeaponAttacks", id = "UnarmedAttack", }) PlaceObj('CombatAction', { ActionPoints = 10000, ActionType = "Ranged Attack", AimType = "line", Comment = "-> Attack FiringMode", ConfigurableKeybind = false, Description = T(393518976346, --[[CombatAction AutoFire Description]] "Spends all AP.\nShoots a hail of bullets and inflict even on miss when the enemy is in weapon range. Lower accuracy against distant enemies."), DisplayName = T(847115637646, --[[CombatAction AutoFire DisplayName]] "Auto Fire"), DisplayNameShort = T(810267106493, --[[CombatAction AutoFire DisplayNameShort]] "Auto"), Execute = function (self, units, args) local unit = units[1] args.multishot = true local weapon = self:GetAttackWeapons(unit, args) args.num_shots = weapon:GetAutofireShots(self) local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, FiringModeMember = "Attack", GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon or not weapon:CanAutofire() then return -1 end return unit:GetMaxActionPoints() end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local penalty = self:ResolveValue("dmg_penalty") local num_shots = weapon:GetAutofireShots(self) base = MulDivRound(base, Max(0, 100 + penalty), 100) local damage = num_shots*base return damage, base, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) local damage, base, bonus = self:GetActionDamage(unit) local params = weapon:GetAreaAttackParams("AutoFire") local descr = T{self.Description, damage = base, bullets = weapon:GetAutofireShots(self)} return CombatActionsAppendFreeAimDescription(self, unit, descr) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.applied_status = "Suppressed" args.multishot = true args.weapon = self:GetAttackWeapons(unit, args) args.num_shots = args.num_shots or args.weapon and args.weapon:GetAutofireShots(self) --args.single_fx = true --args.fx_action = "WeaponAutoFire" args.damage_bonus = self:ResolveValue("dmg_penalty") --args.cth_loss_per_shot = self:ResolveValue("cth_loss_per_shot") local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) local target = attack_args.target if results.miss and not attack_args.stuck and IsKindOf(target, "Unit") and IsValidTarget(target) then local target_dist = unit:GetDist(target) local weapon = attack_args.weapon if target_dist <= weapon.WeaponRange * const.SlabSizeX then local flight_dist = 0 for _, shot in ipairs(results.shots) do for _, hit in ipairs(shot.hits) do flight_dist = Max(flight_dist, shot.attack_pos:Dist(hit.pos)) end end flight_dist = flight_dist / const.SlabSizeX target_dist = target_dist / const.SlabSizeX if flight_dist >= target_dist - 1 then results.extra_packets = results.extra_packets or {} table.insert(results.extra_packets, {target = target, effects = "Suppressed"}) end end end return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon, _, list = unit:GetActiveWeapons("Firearm") return weapon end, GetUIState = function (self, units, args) local state, err = CombatActionGenericAttackGetUIState(self, units, args) if state ~= "enabled" then return state, err end local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) local autoFire_ammo = weapon:GetAutofireShots(self) if not weapon.ammo or weapon.ammo.Amount < autoFire_ammo then return "disabled", AttackDisableReasons.InsufficientAmmo end return "enabled" end, Icon = "UI/Icons/Hud/full_auto", IconFiringMode = "UI/Hud/fm_autoshot", IdDefault = "AutoFiredefault", IsAimableAttack = false, IsTargetableAttack = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "cth_loss_per_shot", 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "dmg_penalty", 'Value', -80, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 15, 'Tag', "", }), }, RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 3, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, basicAttack = true, group = "WeaponAttacks", id = "AutoFire", }) PlaceObj('CombatAction', { ActionPointDelta = 2000, ActionPoints = 6000, ActionType = "Ranged Attack", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(362787141601, --[[CombatAction CancelShot Description]] "Does not provoke attacks.\nRemoves and on hit."), DisplayName = T(984858495047, --[[CombatAction CancelShot DisplayName]] "Distracting Shot"), EvalTarget = function (self, units, target, args) --return units[1]:CalcChanceToHit(target, self, args, "chance_only") if not units or not units[1] then return 0 end local unitList = g_unitOrder[units[1]] if not unitList then return 0 end local orderIdx = unitList[target] local value = orderIdx and -orderIdx or 0 return value end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) local damage = unit and unit:GetBaseDamage(weapon) return T{self.Description, damage = damage} end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.applied_status = "CancelShot" local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local w1 = unit:GetActiveWeapons("Firearm", "HeavyWeapon") return w1 end, GetDefaultTarget = function (self, unit) local units = {unit} local targets = self:GetTargets(units) local distance_to_best = 10000*guim local best_eval, best_target, best_prep = 0, false, false for _, target in ipairs(targets or empty_table) do local eval = self:EvalTarget(units, target) local prep = target:HasPreparedAttack() local dist = IsKindOf(target, "Unit") and unit:GetDist(target) or 0 if not best_target or (not best_prep and prep) then best_target, best_eval, best_prep, distance_to_best = target, eval, prep, dist elseif best_prep == prep then if eval > best_eval or (eval == best_eval and dist < distance_to_best) then best_target, best_eval, best_prep, distance_to_best = target, eval, prep, dist end end end return best_target, best_eval end, GetUIState = function (self, units, args) local unit = units[1] if not HasPerk(unit, "CancelShotPerk") then return "hidden" end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/distracting_shot", IdDefault = "CancelShotdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectCancelShot", MultiSelectBehavior = "first", RequireState = "any", RequireTargets = true, RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 3, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "WeaponAttacks", id = "CancelShot", }) PlaceObj('CombatAction', { ActionPointDelta = 2000, ActionType = "Ranged Attack", AimType = "cone", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(551145852969, --[[CombatAction CancelShotCone Description]] "Does not provoke attacks.\nRemoves and on hit."), DisplayName = T(415284687953, --[[CombatAction CancelShotCone DisplayName]] "Distracting Shot"), GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) local base = weapon and unit:GetBaseDamage(weapon) or 0 return MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100) end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local aoedamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100) local descr = T{self.Description, damage = base, aoedamage = MulDivRound(base, const.Weapons.ShotgunCollateralDamage, 100)} descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local args = table.copy(args) if not args.target_spot_group then args.num_shots = 0 end args.fx_action = "WeaponBuckshot" args.aoe_action_id = self.id args.aoe_damage_type = "percent" args.aoe_damage_value = const.Weapons.ShotgunCollateralDamage args.buckshot_scatter_fx = 10 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) -- make sure the fx action plays local shots = results.attacks and results.attacks[1].shots or empty_table if #shots == 0 then attack_args.aoe_fx_action = "WeaponBuckshot" end return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return 2 end, GetUIState = function (self, units, args) local unit = units[1] if not HasPerk(unit, "CancelShotPerk") then return "hidden" end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/distracting_shot", IdDefault = "CancelShotConedefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectCancelShot", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, SortKey = 3, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "WeaponAttacks", id = "CancelShotCone", }) PlaceObj('CombatAction', { ActionPointDelta = 3000, ActionPoints = 7000, ActionType = "Melee Attack", AimType = "melee-charge", CostBasedOnWeapon = true, Description = T(839516433881, --[[CombatAction Charge Description]] "Minimum Range: \nAll targets in cover are .\nMove in a straight line and attack the selected target for damage."), DisplayName = T(997748923990, --[[CombatAction Charge DisplayName]] "Charge"), EvalTarget = function (self, units, target, args) local unit = units[1] if (not args or not args.goto_pos) and (not IsValid(target) or not unit:GetClosestMeleeRangePos(target)) then return 0 end return unit:CalcChanceToHit(target, self, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end if not args.goto_pos then args.goto_pos = unit:GetClosestMeleeRangePos(args.target) end args.available_move_ap = self:ResolveValue("move_ap") * const.Scale.AP CombatActionExecuteWithMove(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, damage, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local damage, base, bonus = self:GetActionDamage(unit) if LastLoadedOrLoadingIMode == "IModeCombatMelee" then return T{873047291313, "Select a unit to charge.", damage = damage} end return T{self.Description, self, damage = damage, basedamage = base, bonusdamage = bonus} end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 -- distance-based bonus damage local max_dist = self:ResolveValue("max_damage_dist") * const.SlabSizeX local max_bonus = self:ResolveValue("max_damage_bonus") local dist = Min(unit:GetDist(args.target), max_dist) args.damage_bonus = MulDivRound(max_bonus, dist, max_dist) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetOneAttackableEnemy(self, unit, nil, CombatActionTargetFilters.Charge, unit, move_ap, self.id) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("MeleeWeapon") end, GetDefaultTarget = function (self, unit) local best_eval, best_target local units = {unit} local targets = self:GetTargets(units) for _, target in ipairs(targets or empty_table) do local cost = self:GetAPCost(unit, { target = target }) if unit:UIHasAP(cost) then local eval = self:EvalTarget(units, target) if not best_eval or eval > best_eval then best_target, best_eval = target, eval end end end return best_target, best_eval end, GetTargets = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.Charge, unit, move_ap, self.id) end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "hidden" end if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end if unit.stance ~= "Standing" then return "disabled", AttackDisableReasons.OnlyStanding end local ap = self:GetAPCost(unit, args) if not unit:UIHasAP(ap) then return "disabled", GetUnitNoApReason(unit) end if not self:GetAnyTarget(units) then return "disabled", AttackDisableReasons.NoTarget end return "enabled" end, Icon = "UI/Icons/Hud/melee", IdDefault = "Chargedefault", IsAimableAttack = false, KeybindingSortId = "2380", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "move_ap", 'Value', 10, 'Tag', "", }), PlaceObj('PresetParamPercent', { 'Name', "max_damage_bonus", 'Value', 50, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "max_damage_dist", 'Value', 6, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "minDistance", 'Value', 2, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "minAngle", 'Value', 150, 'Tag', "", }), }, RequireState = "any", RequireTargets = true, RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ChargeAttack", self.id, ap, ...) end, SortKey = 3, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatCharge") end, group = "WeaponAttacks", id = "Charge", }) PlaceObj('CombatAction', { ActionPoints = 3000, ActionType = "Ranged Attack", AimType = "parabola aoe", ConfigurableKeybind = false, Description = T(261122276696, --[[CombatAction FireFlare Description]] "Shoot a Flare in the sky that illuminates the selected area. "), DisplayName = T(961636210046, --[[CombatAction FireFlare DisplayName]] "Signal Flare"), GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) return unit:GetAttackAPCost(self, weapon, false, 0) or -1 end, GetActionDescription = function (self, units) local unit = units[1] local weapon = unit and self:GetAttackWeapons(unit) if weapon then return T{self.Description, damage = weapon:GetBaseDamage()} end local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end return description end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) local target = ResolveGrenadeTargetPos(args.target) local ammos = weapon and unit:GetAvailableAmmos(weapon, nil, "unique") if not weapon or not target or not ammos then return {} end local args = table.copy(args) args.weapon = weapon args.target = target args.ordnance = args.ordnance or ammos[1] local attack_args = unit:PrepareAttackArgs(self.id, args) local results = weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("FlareGun") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/fire_flare", IdDefault = "FireFlaredefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("FireFlare", self.id, ap, ...) end, SortKey = 3, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "WeaponAttacks", id = "FireFlare", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Melee Attack", AimType = "melee", Comment = "Used by crocodile animal units granted by the CrocodileJaws weapon", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(107890223879, --[[CombatAction CrocodileBite DisplayName]] "Bite"), Execute = function (self, units, args) CombatActions.MeleeAttack.Execute(self, units, args) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, Icon = "UI/Icons/Hud/melee", IdDefault = "CrocodileBitedefault", IsAimableAttack = false, MultiSelectBehavior = "hidden", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("MeleeAttack", self.id, ap, ...) end, SortKey = 4, UIBegin = function (self, units, args) CombatActions.MeleeAttack.UIBegin(self, units, args) end, group = "WeaponAttacks", id = "CrocodileBite", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Melee Attack", AimType = "melee", Comment = "Used by hyena animal units granted by the HyenaJaws weapon", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(107890223879, --[[CombatAction HyenaBite DisplayName]] "Bite"), Execute = function (self, units, args) CombatActions.MeleeAttack.Execute(self, units, args) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, Icon = "UI/Icons/Hud/melee", IdDefault = "HyenaBitedefault", IsAimableAttack = false, MultiSelectBehavior = "hidden", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("MeleeAttack", self.id, ap, ...) end, SortKey = 4, UIBegin = function (self, units, args) CombatActions.MeleeAttack.UIBegin(self, units, args) end, group = "WeaponAttacks", id = "HyenaBite", }) PlaceObj('CombatAction', { ActionPoints = 5000, ActionType = "Melee Attack", AimType = "melee-charge", Comment = "Used by hyena animal units", ConfigurableKeybind = false, DisplayName = T(617452697208, --[[CombatAction HyenaCharge DisplayName]] "Charge"), Execute = function (self, units, args) CombatActions.Charge.Execute(self, units, args) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.applied_status = "KnockDown" args.move_ap = self:ResolveValue("move_ap") * const.Scale.AP local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetOneAttackableEnemy(self, unit, nil, CombatActionTargetFilters.HyenaCharge, unit, move_ap, false, self.id) end, GetTargets = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.HyenaCharge, unit, move_ap, false, self.id) end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "hidden" end if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end local ap = self:GetAPCost(unit, args) if not unit:UIHasAP(ap) then return "disabled", GetUnitNoApReason(unit) end if not self:GetAnyTarget(units) then return "disabled", AttackDisableReasons.NoTarget end return "enabled" end, Icon = "UI/Icons/Hud/melee", IdDefault = "HyenaChargedefault", IsAimableAttack = false, MultiSelectBehavior = "hidden", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "move_ap", 'Value', 100, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "minAngle", 'Value', 150, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "minDistance", 'Value', 1, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "maxDistance", 'Value', 2, 'Tag', "", }), }, Run = function (self, unit, ap, ...) unit:SetActionCommand("HyenaCharge", self.id, ap, ...) end, SortKey = 4, UIBegin = function (self, units, args) CombatActions.MeleeAttack.UIBegin(self, units, args) end, group = "WeaponAttacks", id = "HyenaCharge", }) PlaceObj('CombatAction', { ActionPoints = 5000, ActionType = "Melee Attack", AimType = "melee", Description = T(647630380410, --[[CombatAction Brutalize Description]] "Spends all AP\nDeliver a series of attacks against random body parts. Cannot be targeted or aimed. These attacks are slightly less accurate than normal attacks. If the enemy is in cover, they will become .\n\n attacks."), DisplayName = T(506780540915, --[[CombatAction Brutalize DisplayName]] "Brutalize"), EvalTarget = function (self, units, target, args) return CombatActions.MeleeAttack.EvalTarget(self, units, target, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end local ap = self:GetAPCost(unit, args) args.num_attacks = unit:GetNumBrutalizeAttacks(args.goto_pos) args.stance = "Standing" NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return Max(unit:GetUIActionPoints(), GetMeleeAttackAPCost(self, unit, args)) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, base, damage - base end, GetActionDescription = function (self, units) local unit = units[1] if unit then local max = unit:GetNumBrutalizeAttacks() local attacks = 3 if max > 3 then attacks = T{516692960914, "-", min = 3, max = max} end return T{self.Description, attacks = attacks} end return self.Description end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.weapon = self:GetAttackWeapons(unit, args) args.num_shots = 0 if IsKindOf(args.weapon, "GutHookKnife") then args.applied_status = { "Bleeding" } end local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) return CombatActions.MeleeAttack.GetAnyTarget(self, units) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon = unit:GetActiveWeapons("MeleeWeapon") or unit:GetActiveWeapons("UnarmedWeapon") return weapon end, GetDefaultTarget = function (self, unit) return CombatActions.MeleeAttack.GetDefaultTarget(self, unit) end, GetTargets = function (self, units) return CombatActions.MeleeAttack.GetTargets(self, units) end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end local attacker = units[1] local no_target = true for _, u in ipairs(g_Units) do if CombatActionTargetFilters.MeleeAttack(u, attacker) then no_target = false break end end if no_target then return "disabled", AttackDisableReasons.NoTarget end return CombatActions.MeleeAttack.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/brutalize_attack", IdDefault = "Brutalizedefault", IsAimableAttack = false, KeybindingSortId = "2376", MoveStep = true, MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("Brutalize", self.id, ap, ...) end, SortKey = 6, UIBegin = function (self, units, args) return CombatActions.MeleeAttack.UIBegin(self, units, args) end, UseFreeMove = true, group = "WeaponAttacks", id = "Brutalize", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", CostBasedOnWeapon = true, Description = T(662192145975, --[[CombatAction KnifeThrow Description]] "Silent ranged attack with a Knife. You can retrieve the knife afterwards."), DisplayName = T(201038143523, --[[CombatAction KnifeThrow DisplayName]] "Throw Knife"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local weapon1= self:GetAttackWeapons(unit, args) if not weapon1 or not weapon1.CanThrow then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) local unit = units[1] local _, _, _, params = self:GetActionDamage(unit) local descr = T{self.Description, damage = GetDamageRangeText(params.min, params.max), crit = params.critChance} descr = CombatActionsAppendFreeAimDescription(self, unit, descr) return descr end, GetActionResults = function (self, unit, args) args = args or {} if unit.stance == "Prone" then args.stance = "Standing" end local attack_args = unit:PrepareAttackArgs(self.id, args) if not attack_args.weapon then return {}, attack_args end local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) if not weapon then return {} end local range = self:GetMaxAimRange(unit, weapon) * const.SlabSizeX return CombatActionGetOneAttackableEnemy(self, units, nil, CombatActionTargetFilters.KnifeThrow, unit, range) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetThrowableKnife() end, GetMaxAimRange = function (self, unit, weapon) local maxRange = weapon.WeaponRange if HasPerk(unit, "Throwing") then maxRange = maxRange + CharacterEffectDefs.Throwing:ResolveValue("RangeIncrease") end return maxRange end, GetTargets = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) if not weapon then return {} end local range = self:GetMaxAimRange(unit, weapon) * const.SlabSizeX return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.KnifeThrow, unit, range) end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/throw_knife", IdDefault = "KnifeThrowdefault", IsTargetableAttack = true, KeybindingSortId = "2375", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ThrowKnife", self.id, ap, ...) end, SortKey = 7, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "WeaponAttacks", id = "KnifeThrow", }) PlaceObj('CombatAction', { Comment = "stub action for the Fanning attack; only texts/icon are used", Description = T(488417500972, --[[CombatAction Fanning Description]] "Shoots bullets at slightly increased AP cost. Lower accuracy against distant enemies."), DisplayName = T(386523063486, --[[CombatAction Fanning DisplayName]] "Fanning"), Icon = "UI/Icons/Hud/burst_fire", IdDefault = "Fanningdefault", IsTargetableAttack = true, RequireState = "any", ShowIn = false, group = "Default", id = "Fanning", }) PlaceObj('CombatAction', { ActionPoints = 1000, ActivePauseBehavior = "queue", DisplayName = T(796190228832, --[[CombatAction Move DisplayName]] "Move"), GetAPCost = function (self, unit, args) if not g_Combat then return 0 end if unit:HasStatusEffect("StationedMachineGun") or unit:HasStatusEffect("ManningEmplacement") then return -1 end local stance = args and args.stance local pos = args and args.goto_pos if not pos then return -1 end local startStance = args and args.stanceAtStart local endStance = args and args.stanceAtEnd local move_ap = args and args.available_move_ap or unit.ActionPoints local combatPath = GetCombatPath(unit, startStance or stance, move_ap, endStance or stance) local apCost = combatPath:GetAP(pos, endStance or stance) if not apCost or apCost == -1 then return -1 end return apCost end, GetTargets = function (self, units) return units[1]:GetVisibleEnemies() end, GetUIState = function (self, units, args) local unit = units[1] if args then local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost, self.id) then return "disabled" end end if g_Combat then if unit:HasStatusEffect("StationedMachineGun") then return "hidden" elseif unit:HasStatusEffect("ManningEmplacement") then return "hidden" elseif unit:HasPreparedAttack() then return "hidden" end end if unit:GetBandageTarget() or unit:IsBeingBandaged() then return "hidden" end return "enabled" end, Icon = "UI/Icons/Hud/placeholder", IdDefault = "Movedefault", InterruptInExploration = true, IsAimableAttack = false, MultiSelectBehavior = "hidden", QueuedBadgeText = T(954873063444, --[[CombatAction Move QueuedBadgeText]] "MOVE"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... if g_Combat then unit:SetActionCommand("CombatGoto", self.id, ap, args.goto_pos, args.path, args.forced_run, args.stanceAtStart, args.stanceAtEnd, args.willBeTracked, args.visibleMovement) else unit:InterruptCommand("GotoSlab", args.goto_pos, nil, nil, args.move_type, args.follow_target) end end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Default", id = "Move", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(219233654993, --[[CombatAction ReloadMultiSelection Description]] "Reload ammunition for all selected mercs."), DisplayName = T(416808859941, --[[CombatAction ReloadMultiSelection DisplayName]] "Reload Everyone"), GetAPCost = function (self, unit, args) return 0 end, GetTargets = function (self, units) -- Reload with the current ammo, if the unit has more of it, or change ammo if the mag is empty local reloadTargets = {} local errorReason for i, u in ipairs(units) do local w1, w2, weaponList = u:GetActiveWeapons("Firearm") for i, w in ipairs(weaponList) do if w.ammo and w.ammo.Amount == w.MagazineSize then errorReason = errorReason or T(790275997183, "Full clip") goto continue end local ammoForWeapon = u:GetAvailableAmmos(w, nil, "unique") if #ammoForWeapon == 0 then errorReason = T(215123596652, "No ammo") goto continue end local currentAmmo = w.ammo and table.find_value(ammoForWeapon, "class", w.ammo.class) if not currentAmmo and w.ammo and w.ammo.Amount > 0 then errorReason = T(215123596652, "No ammo") -- No ammo of current type and magazine isn't empty goto continue end reloadTargets[#reloadTargets + 1] = { u = {u}, w = w, ammo = currentAmmo or ammoForWeapon[1] } ::continue:: end end return reloadTargets, errorReason end, GetUIState = function (self, units, args) if #units < 2 then return "hidden" end if not self:GetAnyTarget(units) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/reload", IdDefault = "ReloadMultiSelectiondefault", IsAimableAttack = false, QueuedBadgeText = T(533763685831, --[[CombatAction ReloadMultiSelection QueuedBadgeText]] "RELOAD"), RequireState = "exploration", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ReloadAction", self.id, ap, ...) end, SortKey = 8, UIBegin = function (self, units, args) local reloadProc = self:GetTargets(units) for i, r in ipairs(reloadProc) do self:Execute(r.u, { weapon = r.w.class, target = r.ammo.class }) end end, group = "Default", id = "ReloadMultiSelection", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", ConfigurableKeybind = false, Description = T(134458994872, --[[CombatAction StopBandaging Description]] "Cancel the bandaging action."), DisplayName = T(856153698041, --[[CombatAction StopBandaging DisplayName]] "Stop Bandaging"), GetActionDescription = function (self, units) local unit = units[1] if unit:IsBeingBandaged() then return T(540349977342, "Cancel the bandaging action. The unit will be able to move but not regain any more HP.") end if not g_Combat then return T(151546259528, "Cancel the bandaging action. The bandaged unit will not regain any more HP.") end return self.Description end, GetUIState = function (self, units, args) local unit = units[1] if unit:GetBandageTarget() or unit:IsBeingBandaged() then return "enabled" end return "hidden" end, Icon = "UI/Icons/Hud/stop_bandaging_downed", IdDefault = "StopBandagingdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectBandage", MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) if unit:GetBandageTarget() then unit:SetActionCommand("EndCombatBandage") else for _, other in ipairs(g_Units) do if unit ~= other and other:GetBandageTarget() == unit then other:SetActionCommand("EndCombatBandage") end end end end, SortKey = 9, group = "Default", id = "StopBandaging", }) PlaceObj('CombatAction', { ActionPoints = 5000, ActivePauseBehavior = "queue", Description = T(582755603672, --[[CombatAction Unjam Description]] "Fix a jammed weapon. The weapon loses Condition depending on the skill."), DisplayName = T(348534461674, --[[CombatAction Unjam DisplayName]] "Unjam"), GetAPCost = function (self, unit, args) if HasPerk(unit, "MrFixit") then return CharacterEffectDefs.MrFixit:ResolveValue("mrfixit_ap") * const.Scale.AP end if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end local weapon = false if args and args.pos then weapon = unit:GetItemAtPackedPos(args.pos) elseif args and args.weapon then weapon = unit:GetWeaponByDefIdOrDefault("Firearm", args and args.weapon, args and args.pos) end if weapon then -- from Inventory local jammed = false if IsKindOf(weapon, "Firearm") and weapon.jammed and not weapon:IsCondition("Broken") then jammed = true end if not jammed then return "hidden" end else local weapon1, weapon2 = unit:GetActiveWeapons() local weaponJammed1, weaponJammed2 = false, false if IsKindOf(weapon1, "Firearm") and weapon1.jammed and not weapon1:IsCondition("Broken") then weaponJammed1 = true end if IsKindOf(weapon2, "Firearm") and weapon2.jammed and not weapon2:IsCondition("Broken") then weaponJammed2 = true end if not weaponJammed1 and not weaponJammed2 then return "hidden" end end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end return "enabled" end, Icon = "UI/Icons/Hud/repair_weapon", IdDefault = "Unjamdefault", IsAimableAttack = false, KeybindingSortId = "2505", MultiSelectBehavior = "first", QueuedBadgeText = T(224178341316, --[[CombatAction Unjam QueuedBadgeText]] "UNJAM"), RequireState = "any", Run = function (self, unit, ap, args) unit:SetActionCommand("UnjamWeapon", self.id, ap, args) end, SortKey = 10, group = "Default", id = "Unjam", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", ActivePauseBehavior = "queue", AimType = "cone", ConfigurableKeybind = false, Description = T(780359204834, --[[CombatAction Overwatch Description]] "Spends all AP\nAny targets who move or shoot in the overwatch area will provoke attacks.\nAccuracy is influenced by ."), DisplayName = T(396308001195, --[[CombatAction Overwatch DisplayName]] "Overwatch"), Execute = function (self, units, args) local unit = units[1] local attacks, aim = unit:GetOverwatchAttacksAndAim(self, args) args.num_attacks = attacks args.aim = aim local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) if args and args.action_cost_only then return self.ActionPoints end local weapon = self:GetAttackWeapons(unit, args) if not weapon or (weapon.PreparedAttackType ~= "Overwatch" and weapon.PreparedAttackType ~= "Both")then return -1 end local attack = unit:GetDefaultAttackAction("ranged", "ungrouped") local atk_cost = attack:GetAPCost(unit, args) + self.ActionPoints return Max(unit:GetUIActionPoints(), atk_cost), atk_cost end, GetActionDescription = function (self, units) local unit = units[1] local apply, value = Presets.ChanceToHitModifier.Default.OpportunityAttack:CalcValue(unit, nil, nil, nil, nil, nil, nil, nil, true) local total, cost = self:GetAPCost(unit) local attacks = 1 if unit and (cost or -1) >= 0 then attacks = unit:GetOverwatchAttacksAndAim() end local descr = g_Combat and self.Description or CombatActions.ExplorationOverwatch.Description local description = descr if g_Overwatch[unit] and (g_Overwatch[unit].num_attacks or 0) > 0 then -- add remaining attacks text attacks = g_Overwatch[unit].num_attacks description = descr .. T{133054169959, "Remaining attacks: ", attacks = attacks} elseif unit:UIHasAP(cost, self.id) then -- add max attacks text description = descr .. T{452784485986, "Max attacks: ", attacks = attacks} end local action = unit and unit:GetDefaultAttackAction() if action and (action.id == "Buckshot" or action.id == "DoubleBarrel") then description = description .. "" .. T(769183913636, "Collateral damage might hit units outside the overwatch cone.") end return description end, GetActionDisplayName = function (self, units) local name = g_Combat and self.DisplayName or CombatActions.ExplorationOverwatch.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetActionIcon = function (self, units) if not g_Combat then return CombatActions.ExplorationOverwatch.Icon end return self.Icon end, GetActionResults = function (self, unit, args) local target = args.target local weapon = self:GetAttackWeapons(unit, args) if not weapon then return {} end local sub_action = unit:GetDefaultAttackAction("ranged", "ungrouped") local attack_args = unit:PrepareAttackArgs(self.id, args) local targets = unit:GetVisibleEnemies() local results = {} if sub_action.AimType == "cone" then -- check for collateral damage around exact targets local sub_attack_args = unit:PrepareAttackArgs(sub_action.id, args) local aoe = GetAreaAttackResults(sub_attack_args) for i, aoeHit in ipairs(aoe) do if IsKindOf(aoeHit.obj, "Unit") then table.insert_unique(targets, aoeHit.obj) results[#results + 1] = aoeHit end end end local modifiers = GetAreaAttackHitModifiers(self.id, attack_args, targets) local sub_action_lof_params = { can_use_covers = false, can_stuck_on_unit = false, } local lof_data = GetLoFData(unit, targets, sub_action_lof_params) for i, target in ipairs(targets) do local mod = modifiers[i] if mod > 0 then sub_action_lof_params.target = target local sub_action_results = sub_action:GetActionResults(unit, sub_action_lof_params) if sub_action.AimType == "cone" then -- cause area action results are in different format... sub_action_results = sub_action_results.area_hits end local target_hit for i, hitData in ipairs(sub_action_results) do if hitData.obj == target then results[#results + 1] = hitData target_hit = true end end --if not target_hit then local target_lof_data = lof_data[i] if target_lof_data.clear_attacks == 0 then results.no_lof_targets = results.no_lof_targets or {} table.insert(results.no_lof_targets, target) end end end -- We dont want to show damage prediction, just hit prediction. for i, result in ipairs(results) do result.damage = 0 result.display_only = true result.ignore_armor = true end return results, attack_args end, GetAimParams = function (self, unit, weapon) local params = weapon:GetAreaAttackParams(self.id, unit) params.min_range = self:GetMinAimRange(unit, weapon) params.max_range = self:GetMaxAimRange(unit, weapon) assert(params.max_range >= params.min_range) return params end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) local range = weapon:GetOverwatchConeParam("MaxRange") local sight = unit:GetSightRadius() / const.SlabSizeX return Min(range, sight) end, GetMinAimRange = function (self, unit, weapon) local range = weapon:GetOverwatchConeParam("MinRange") local sight = unit:GetSightRadius() / const.SlabSizeX return Min(range, sight) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end local attack = unit:GetDefaultAttackAction() local state, reason = attack:GetUIState(units, args) return state, reason end, Icon = "UI/Icons/Hud/overwatch", IdDefault = "Overwatchdefault", KeybindingFromAction = "actionRedirectOverwatch", MultiSelectBehavior = "first", QueuedBadgeText = T(507392307526, --[[CombatAction Overwatch QueuedBadgeText]] "OVERWATCH"), RequireState = "any", Run = function (self, unit, ap, ...) local vr = IsMerc(unit) and "Overwatch" or "AIOverwatch" PlayVoiceResponse(unit, vr) unit:SetActionCommand("OverwatchAction", self.id, ap, ...) end, SortKey = 20, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "cancel") end, group = "Default", id = "Overwatch", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPoints = 3000, ActionType = "Ranged Attack", ActivePauseBehavior = "queue", AimType = "line", Description = T(769278662659, --[[CombatAction PinDown Description]] "Spends all AP\nThe target is . At the start of next turn, shoot the target for damage if the target is still in the line of sight.\n\nPin Down requires a clear line and sight to the target."), DisplayName = T(964546937629, --[[CombatAction PinDown DisplayName]] "Pin Down"), GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon or (weapon.PreparedAttackType ~= "Pin Down" and weapon.PreparedAttackType ~= "Both") then return -1 end local ap = self.ActionPoints if HasPerk(unit, "HawksEye") then ap = CharacterEffectDefs.HawksEye:ResolveValue("pindownCostOverwrite") * const.Scale.AP end return Max(ap, unit:GetUIActionPoints()), ap end, GetActionDescription = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) if not weapon then return self.Description end local damage = unit:GetBaseDamage(weapon) return T{self.Description, damage = damage} end, GetActionResults = function (self, unit, args) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) return CombatActionGetOneAttackableEnemy(self, unit, nil, CombatActionTargetFilters.Pindown, unit, weapon) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon1, weapon2 = unit:GetActiveWeapons("Firearm") if weapon1 and weapon2 then return false end if not weapon1 or (weapon1.PreparedAttackType ~= "Pin Down" and weapon1.PreparedAttackType ~= "Both") then return false end return weapon1 end, GetTargets = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.Pindown, unit, weapon) end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not weapon or (weapon.PreparedAttackType ~= "Pin Down" and weapon.PreparedAttackType ~= "Both") then return "hidden" end if #(self:GetTargets(units) or empty_table) == 0 then return "disabled", AttackDisableReasons.NoTarget end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/steady_does_it", IdDefault = "PinDowndefault", IsTargetableAttack = true, KeybindingSortId = "2353", MultiSelectBehavior = "first", QueuedBadgeText = T(707708840555, --[[CombatAction PinDown QueuedBadgeText]] "PIN DOWN"), RequireTargets = true, RequireWeapon = true, Run = function (self, unit, ap, ...) local vr = IsMerc(unit) and "PinDown" or "AIPinDown" local targetIsCiv = unit.aim_attack_args and unit.aim_attack_args.target and IsKindOf(unit.aim_attack_args.target, "Unit") and unit.aim_attack_args.target:IsCivilian() if not targetIsCiv then PlayVoiceResponse(unit, vr) end unit:SetActionCommand("PinDown", self.id, ap, ...) end, SortKey = 20, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "Default", id = "PinDown", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", Comment = "exploration mode only", ConfigurableKeybind = false, Description = T(309141031133, --[[CombatAction CancelOverwatch Description]] "Cancel and move normally"), DisplayName = T(642998606373, --[[CombatAction CancelOverwatch DisplayName]] "Cancel Overwatch"), DisplayNameShort = T(951774623470, --[[CombatAction CancelOverwatch DisplayNameShort]] "Cancel Overwatch"), GetUIState = function (self, units, args) local has_overwatch for _, unit in ipairs(Selection or units) do -- action should be visible if anyone selected has an overwatch, not just the first has_overwatch = has_overwatch or not not g_Overwatch[unit] or (IsActivePaused() and unit.queued_action_id == "Overwatch") end return has_overwatch and "enabled" or "hidden" end, Icon = "UI/Icons/Hud/cancel_overwatch", IdDefault = "CancelOverwatchdefault", IsAimableAttack = false, RequireState = "exploration", Run = function (self, unit, ap, ...) unit:InterruptPreparedAttack() end, SortKey = 21, group = "Default", id = "CancelOverwatch", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", ConfigurableKeybind = false, Description = "", DisplayName = T(118687433511, --[[CombatAction Despawn DisplayName]] "Despawn"), GetAPCost = function (self, unit, args) return 0 end, Icon = "UI/Icons/Hud/placeholder", IdDefault = "Despawndefault", IsAimableAttack = false, MultiSelectBehavior = "hidden", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("Despawn", self.id, ap) end, ShowIn = false, SortKey = 100, group = "Default", id = "Despawn", }) PlaceObj('CombatAction', { ActionPoints = 2000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(696253281343, --[[CombatAction Hide Description]] "Toggle . This action will force the character to be in the Crouched stance if they are in a Standing stance."), DisplayName = T(490121255879, --[[CombatAction Hide DisplayName]] "Sneak Mode"), DisplayNameShort = T(219222528962, --[[CombatAction Hide DisplayNameShort]] "Sneak Mode"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) PlayFX("activityButtonPress_Sneak", "start") end, GetAPCost = function (self, unit, args) if not unit:CanStealth(unit:GetStanceToStealth()) then return -1 end return self.ActionPoints end, GetUIState = function (self, units, args) units = table.ifilter(units, function(idx, unit) return not unit:HasStatusEffect("Hidden") end) local unit = units[1] if not unit then return "hidden" end if not unit:CanStealth(unit:GetStanceToStealth()) then if unit.enemy_visual_contact then return "disabled", AttackDisableReasons.InEnemySight elseif unit:HasStatusEffect("Revealed") then return "disabled", AttackDisableReasons.Revealed end return "disabled", AttackDisableReasons.CannotSneak end local cost = self:GetAPCost(unit, args) if cost < 0 or not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/stealth_off", IdDefault = "Hidedefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(288854857786, --[[CombatAction Hide QueuedBadgeText]] "HIDE"), RequireState = "any", Run = function (self, unit, ap, ...) if not g_Combat and unit.goto_target and not IsActivePaused() then local stance = unit:GetStanceToStealth(unit.goto_stance) if unit:CanStealth(stance) then unit.goto_stance = stance unit.goto_hide = true end return end unit:SetActionCommand("Hide") end, ShowIn = "Special", SimultaneousPlay = true, SortKey = 100, group = "Default", id = "Hide", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", ConfigurableKeybind = false, Description = T(393528800033, --[[CombatAction Reveal Description]] "Exit and reveal yourself to all nearby enemies."), DisplayName = T(812951099239, --[[CombatAction Reveal DisplayName]] "Stop Sneaking"), DisplayNameShort = T(565305410833, --[[CombatAction Reveal DisplayNameShort]] "Stop Sneaking"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) PlayFX("activityButtonPress_UndoSneak", "start") end, GetAPCost = function (self, unit, args) if not unit:CanStealth(unit:GetStanceToStealth()) then return -1 end return self.ActionPoints end, GetUIState = function (self, units, args) for _, unit in ipairs(units) do if not unit:HasStatusEffect("Hidden") then return "hidden" end end return "enabled" end, Icon = "UI/Icons/Hud/stealth_on", IdDefault = "Revealdefault", IsAimableAttack = false, RequireState = "any", Run = function (self, unit, ap, ...) if not g_Combat and unit.goto_target then unit:Unhide() return end unit:SetActionCommand("Unhide") end, ShowIn = "Special", SimultaneousPlay = true, SortKey = 100, group = "Default", id = "Reveal", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActionShortcut = "J", ActivePauseBehavior = "queue", Description = T(492006416359, --[[CombatAction TakeCover Description]] "Spends all AP\nSets your stance to Crouched. Gain superior protection against attacks from the other side of cover. You will lose the benefit if you become ."), DisplayName = T(365281729213, --[[CombatAction TakeCover DisplayName]] "Take Cover"), DisplayNameShort = T(934207804139, --[[CombatAction TakeCover DisplayNameShort]] "Take Cover"), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDescription = function (self, units) local unit = units[1] if unit then local cost = self:GetAPCost(unit) local num = Max(0, unit:GetUIActionPoints() - cost) num = Min(num, Protected:ResolveValue("max_ap_carried")*const.Scale.AP) if num >= const.Scale.AP then return self.Description .. T{128220108077, "This action will transfer to the next turn", num_ap = num} end end return self.Description end, GetUIState = function (self, units, args) if not units or #units < 1 then return "hidden" end local active = true for _, unit in ipairs(units) do if not unit:HasStatusEffect("Protected") then if unit:CanTakeCover() then if not unit:HasAP(self:GetAPCost(unit)) then return "disabled", AttackDisableReasons.NoAP end return "enabled" end active = false end end if active then return "disabled", AttackDisableReasons.AlreadyActive end return "disabled", AttackDisableReasons.NotInCover end, Icon = "UI/Icons/Hud/take_cover", IdDefault = "TakeCoverdefault", IsAimableAttack = false, KeybindingSortId = "2325", QueuedBadgeText = T(659947635353, --[[CombatAction TakeCover QueuedBadgeText]] "TAKE COVER"), RequireState = "any", Run = function (self, unit, ap, ...) PlayVoiceResponse(unit, "TakeCover") unit:SetActionCommand("TakeCover") GetCoverShieldBonusEffect(unit) end, SimultaneousPlay = true, SortKey = 101, group = "Default", id = "TakeCover", }) PlaceObj('CombatAction', { Comment = "dummy action used to facilitate Reposition queueing when needed", ConfigurableKeybind = false, DisplayName = T(890398580666, --[[CombatAction Reposition DisplayName]] "Reposition"), IdDefault = "Repositiondefault", Run = function (self, unit, ap, ...) unit:SetActionCommand("Reposition", ...) end, SimultaneousPlay = true, group = "Hidden", id = "Reposition", }) PlaceObj('CombatAction', { Comment = "dummy action used for opening attack in reposition", ConfigurableKeybind = false, DisplayName = T(165860894460, --[[CombatAction RepositionOpeningAttack DisplayName]] "Opening Attack"), IdDefault = "RepositionOpeningAttackdefault", Run = function (self, unit, ap, ...) unit:SetActionCommand("RepositionOpeningAttack", ...) end, group = "Hidden", id = "RepositionOpeningAttack", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", ConfigurableKeybind = false, Description = T(733401287883, --[[CombatAction CancelMark Description]] "Cancel the prepared Takedown action and allows preparing Takedown for a new target.The prepared takedown will be executed if you approach the enemy undetected."), DisplayName = T(800852469502, --[[CombatAction CancelMark DisplayName]] "Cancel Takedown"), DisplayNameShort = T(117355578745, --[[CombatAction CancelMark DisplayNameShort]] "Cancel Takedown"), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetMaxAimRange = function (self, unit, weapon) return 150 end, GetUIState = function (self, units, args) local mark = false for _, unit in ipairs(units) do mark = mark or not not unit.marked_target_attack_args end if not mark then return "hidden" end return "enabled" end, Icon = "UI/Icons/Hud/cancel_mark", IdDefault = "CancelMarkdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectTakedown", RequireState = "exploration", Run = function (self, unit, ap, ...) unit:SetActionCommand("CancelMark") end, SimultaneousPlay = true, SortKey = 3, group = "Hidden", id = "CancelMark", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", ActivePauseBehavior = "instant", AimType = "line", ConfigurableKeybind = false, Description = T(688968867904, --[[CombatAction MarkTarget Description]] "Mark an enemy to perform a melee attack when the target is in range. The mark will be removed if combat starts. Often used while ."), DisableAimAnim = true, DisplayName = T(100199336646, --[[CombatAction MarkTarget DisplayName]] "Prepare Takedown"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local weapon = self:GetAttackWeapons(unit, args) args = args or {} args.action_cost_only = true -- only used in exploration, never try to add movement cost if IsKindOf(weapon, "UnarmedWeapon") then return CombatActions.UnarmedAttack:GetAPCost(unit, args) end return CombatActions.MeleeAttack:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end return CombatActionsAppendFreeAimDescription(self, units[1], description, "ignore") end, GetActionResults = function (self, unit, args) local args = table.copy(args) if args.target then args.step_pos = unit:GetClosestMeleeRangePos(args.target) end args.weapon = args.weapon or self:GetAttackWeapons(unit) return CombatActions.MeleeAttack:GetActionResults(unit, args) end, GetAnyTarget = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) if not weapon then return {} end return CombatActionGetOneAttackableEnemy(self, unit) end, GetAttackWeapons = function (self, unit, args) return unit:GetActiveWeapons("MeleeWeapon") or unit:GetActiveWeapons("UnarmedWeapon") end, GetMaxAimRange = function (self, unit, weapon) return 150 end, GetTargets = function (self, units) local unit = units[1] local weapon = self:GetAttackWeapons(unit) if not weapon then return {} end return CombatActionGetAttackableEnemies(self, unit) end, GetUIState = function (self, units, args) local unit = units[1] if not unit or not self:GetAttackWeapons(unit, args) then return "hidden" end if unit.marked_target_attack_args and IsValid(unit.marked_target_attack_args.target) and not unit.marked_target_attack_args.target:IsDead() then return "hidden" end --if not unit:HasStatusEffect("Hidden") then -- return "disabled", AttackDisableReasons.NotSneaking --end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/mark_enemy", IdDefault = "MarkTargetdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectTakedown", MultiSelectBehavior = "first", RequireState = "exploration", RequireTargets = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("MarkTarget", self.id, ap, ...) end, SortKey = 3, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "Hidden", id = "MarkTarget", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Description = T(646737101780, --[[CombatAction Reload Description]] "Reload or change ammo."), DisplayName = T(642187794904, --[[CombatAction Reload DisplayName]] "Reload"), GetAPCost = function (self, unit, args) if unit:HasStatusEffect("ManningEmplacement") then return -1 end local weapon if args and args.item_id then local alt_set = (unit.current_weapon == "Handheld A") and "Handheld B" or "Handheld A" weapon = unit:FindWeaponInSlotById(unit.current_weapon, args.item_id) or unit:FindWeaponInSlotById(alt_set, args.item_id) or unit:FindWeaponInSlotById("Inventory", args.item_id) end if not weapon then if args and args.pos then weapon = unit:GetItemAtPackedPos(args.pos) else weapon = unit:GetWeaponByDefIdOrDefault("Firearm", args and args.weapon) end end return (weapon and not weapon.jammed) and weapon.ReloadAP or -1 end, GetActionDisplayName = function (self, units) local unit = units[1] if not IsKindOf(unit, "Unit") then return end local w1, w2, weaponList = unit:GetActiveWeapons("Firearm") local canChange = false for i, w in ipairs(weaponList) do local ammoForWeapon = unit:GetAvailableAmmos(w, nil, "unique") local noAmmo = #ammoForWeapon == 0 local onlyAmmoIsCurrent = w.ammo and #ammoForWeapon == 1 and ammoForWeapon[1].class == w.ammo.class local fullMag = not w.ammo or w.ammo.Amount == w.MagazineSize canChange = canChange or (onlyAmmoIsCurrent and fullMag) end if canChange then return T(817996274899, "Change Ammo") else return self.DisplayName end end, GetTargets = function (self, units) local unit = units[1] local weapon = unit:GetActiveWeapons() return unit:GetAvailableAmmos(weapon) end, GetUIState = function (self, units, args) if not g_Combat and #units ~= 1 then return "hidden" end local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end local availableWeaponsToReload = 0 local errorReason local weapon if args and args.pos then weapon = unit:GetItemAtPackedPos(args.pos) elseif args and args.weapon then weapon = unit:GetWeaponByDefIdOrDefault("Firearm", args and args.weapon, args and args.pos) end if weapon then local weaponReloadOptions = GetReloadOptionsForWeapon(weapon, unit) if #weaponReloadOptions > 0 then availableWeaponsToReload = availableWeaponsToReload + 1 end else local w1, w2, weaponList = unit:GetActiveWeapons() if not weaponList then return "enabled" end for i, w in ipairs(weaponList) do local canReload, err = IsWeaponAvailableForReload(w, unit:GetAvailableAmmos(w, nil, "unique")) errorReason = err if canReload then availableWeaponsToReload = availableWeaponsToReload + 1 end end end if availableWeaponsToReload == 0 then return "disabled", errorReason end return "enabled" end, Icon = "UI/Icons/Hud/reload", IdDefault = "Reloaddefault", IsAimableAttack = false, QueuedBadgeText = T(871510388525, --[[CombatAction Reload QueuedBadgeText]] "RELOAD"), RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ReloadAction", self.id, ap, ...) end, ShowIn = false, SortKey = 8, UIBegin = function (self, units, args) local unit = units[1] local mode_dlg = GetInGameInterfaceModeDlg() if IsKindOf(mode_dlg, "IModeCommonUnitControl") then -- Process weapons local w1, w2, weaponList = unit:GetActiveWeapons("Firearm") local processedList = {} for i, w in ipairs(weaponList) do local text = T{535301054415, "", weaponName = w.DisplayName} local ammoForWeapon = unit:GetAvailableAmmos(w, nil, "unique") local noAmmo = #ammoForWeapon == 0 if w.ammo == 0 then text = text .. T(642941753004, " (Empty)") end local onlyAmmoIsCurrent = w.ammo and #ammoForWeapon == 1 and ammoForWeapon[1].class == w.ammo.class local fullMag = not w.ammo or w.ammo.Amount == w.MagazineSize local processedAmmo = {} for _, a in ipairs(ammoForWeapon) do local isCurrent = w.ammo and a.class == w.ammo.class local ammoEntry = { DisplayName = isCurrent and T{541680584484, "Current: ", a} or a.DisplayName, ammo = a, disabled = w.ammo and isCurrent and fullMag, icon = a.Icon, uiCtx = a, rolloverTemplate = "RolloverInventory" } if isCurrent then table.insert(processedAmmo, 1, ammoEntry) else table.insert(processedAmmo, ammoEntry) end end processedList[#processedList + 1] = { DisplayName = text, weaponIdx = i, ammo = processedAmmo, disabled = noAmmo or (onlyAmmoIsCurrent and fullMag), icon = w.Icon, uiCtx = w, rolloverTemplate = "RolloverInventory" } end -- First you choose which weapon to reload, then you choose the ammo to load into it. local weaponChoiceCallback = function(u, weaponWrapped) local ammoChoiceCallback = function(u, ammoWrapped) self:Execute({u}, { weapon = weaponWrapped.weaponIdx, target = ammoWrapped.ammo.class }) end mode_dlg:ShowCombatActionTargetChoice(self, {u}, weaponWrapped.ammo, ammoChoiceCallback, "suppress_toggle") end mode_dlg:ShowCombatActionTargetChoice(self, units, processedList, weaponChoiceCallback) else self:Execute(units) end end, group = "Hidden", id = "Reload", }) PlaceObj('CombatAction', { ActionPoints = 1000, ActionShortcut = "U", ActivePauseBehavior = "queue", Description = T(784247279587, --[[CombatAction ChangeWeapon Description]] "Swap to the alternative weapon set: "), DisplayName = T(692166142490, --[[CombatAction ChangeWeapon DisplayName]] "Change Weapon"), GetAPCost = function (self, unit, args) if unit:CanActivatePerk("Scoundrel") then return 0 end local otherSet = "Handheld A" if unit and unit.current_weapon == "Handheld A" then otherSet = "Handheld B" end if unit:FindItemInSlot(otherSet, function(weapon) return weapon:IsWeapon() and weapon:HasComponent("FreeWeaponSwap") end) then return 0 end return self.ActionPoints end, GetActionDescription = function (self, units) local otherSetItems = GetUnitWeapons(units[1], "otherSet") local itemsConcat = {} for i, item in ipairs(otherSetItems) do itemsConcat[#itemsConcat + 1] = item.DisplayName end itemsConcat = table.concat(itemsConcat, ", ") return T{self.Description, items = itemsConcat} end, GetUIState = function (self, units, args) local unit = units[1] if not IsKindOf(unit, "Unit") then return "hidden" end if unit:GetBandageTarget() then return "disabled", AttackDisableReasons.BandagingDowned end if unit:HasStatusEffect("ManningEmplacement") or unit:HasStatusEffect("StationedMachineGun") then return "hidden", AttackDisableReasons.UsingMachineGun end if not unit:UIHasAP(self:GetAPCost(unit, args)) then return "disabled", AttackDisableReasons.NoAP end if g_Combat and HasCombatActionInProgress(unit) then return "hidden" end return "enabled" -- always available to allow switching to unarmed attacks end, Icon = "UI/Icons/Hud/change_weapon_set", IdDefault = "ChangeWeapondefault", IsAimableAttack = false, KeybindingSortId = "2295", MultiSelectBehavior = "first", QueuedBadgeText = T(518198236082, --[[CombatAction ChangeWeapon QueuedBadgeText]] "CHANGE WEAPON"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("SwapActiveWeapon", self.id, ap) end, ShowIn = "Special", SortKey = 11, group = "Hidden", id = "ChangeWeapon", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = "stub action for Overwatch in exploration - changed appearance (text/name/icon/etc)", ConfigurableKeybind = false, Description = T(365505233508, --[[CombatAction ExplorationOverwatch Description]] "Can be used out of combat to set up an ambush. Enemies do not provoke attacks until combat starts.\nMercs in Overwatch start combat without AP."), DisplayName = T(398975828612, --[[CombatAction ExplorationOverwatch DisplayName]] "Overwatch"), Icon = "UI/Icons/Hud/overwatch", IdDefault = "ExplorationOverwatchdefault", KeybindingFromAction = "actionRedirectOverwatch", QueuedBadgeText = T(302085333903, --[[CombatAction ExplorationOverwatch QueuedBadgeText]] "OVERWATCH"), SortKey = 20, group = "Hidden", id = "ExplorationOverwatch", }) PlaceObj('CombatAction', { ActivePauseBehavior = "instant", ConfigurableKeybind = false, Description = T(809105796347, --[[CombatAction Inventory Description]] "Access inventory. Some inventory actions cost Action Points during combat."), DisplayName = T(846446595710, --[[CombatAction Inventory DisplayName]] "Inventory"), EvalTarget = function (self, unit, target) return 0 end, Execute = function (self, units, args) local unit = units[1] if unit then OpenInventory(unit) end end, GetAttackWeapons = function (self, unit, args) return false end, GetDefaultTarget = function (self, unit) return false end, GetTargets = function (self, unit) return false end, GetUIState = function (self, units, args) local unit = units and units[1] if not unit or not unit:CanBeControlled() then return "disabled" end if unit:IsDowned() then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/inventory", IdDefault = "Inventorydefault", IsAimableAttack = false, KeybindingFromAction = "idInventory", MultiSelectBehavior = "first", RequireState = "any", ShowIn = "Special", SortKey = 20, group = "Hidden", id = "Inventory", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(342083935157, --[[CombatAction ItemSkills Description]] "Use one of your equipped quick slot items."), DisplayName = T(205114072648, --[[CombatAction ItemSkills DisplayName]] "Items"), GetActionIcon = function (self, units) return self.Icon end, GetDefaultTarget = function (self, unit) return false end, IdDefault = "ItemSkillsdefault", IsAimableAttack = false, QueuedBadgeText = T(820113998923, --[[CombatAction ItemSkills QueuedBadgeText]] "USE ITEM"), RequireState = "any", SortKey = 98, UIBegin = function (self, units, args) local mode_dlg = GetInGameInterfaceModeDlg() if not IsKindOf(mode_dlg, "IModeCommonUnitControl") then return end local unit = units[1] local availableSkills = { always_show = true } for i, s in ipairs(itemCombatSkillsList) do if unit.ui_actions[s] then local action = CombatActions[s] local entry = {} entry.rolloverTemplate = "CombatActionRollover" entry.rolloverTitle = action:GetActionDisplayName(units) entry.text = action:GetActionDescription(units) entry.icon = action:GetActionIcon(units) entry.disabled = action:GetUIState(units) ~= "enabled" entry.uiCtx = SubContext(units, { action = action }) entry.iconColumns = 3 availableSkills[#availableSkills + 1] = entry end end mode_dlg:ShowCombatActionTargetChoice(self, units, availableSkills, function(u, item) item.uiCtx.action:UIBegin({unit}, args) end) end, group = "Hidden", id = "ItemSkills", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = "(Unused)", ConfigurableKeybind = false, Description = T(269603568429, --[[CombatAction ChangeStance Description]] "Change the unit's stance. Stances affect the movement range and the accuracy of attacks against this unit."), DisplayName = T(536235772160, --[[CombatAction ChangeStance DisplayName]] "Change Stance"), GetActionIcon = function (self, units) local unit = units[1] local stance = unit.stance local currentStanceAction = CombatActions["Stance" .. stance] local image = currentStanceAction and currentStanceAction:GetActionIcon(units) or "UI/Icons/Hud/placeholder.dds" return image end, GetDefaultTarget = function (self, unit) return false end, IdDefault = "ChangeStancedefault", IsAimableAttack = false, QueuedBadgeText = T(827612796111, --[[CombatAction ChangeStance QueuedBadgeText]] "CHANGE STANCE"), RequireState = "any", SortKey = 99, UIBegin = function (self, units, args) local mode_dlg = GetInGameInterfaceModeDlg() if not IsKindOf(mode_dlg, "IModeCommonUnitControl") then return end local stanceChoice = function(u, stance) MultiTargetExecute(self.MultiSelectBehavior, units, function (unit) stance.uiCtx.action:Execute({unit}) end, args and args.target) end local stances = { { uiCtx = CombatActions.StanceStanding }, { uiCtx = CombatActions.StanceCrouch }, { uiCtx = CombatActions.StanceProne } } for i, s in ipairs(stances) do local action = s.uiCtx s.rolloverTemplate = "CombatActionRollover" s.rolloverTitle = action:GetActionDisplayName(units) s.text = action:GetActionDescription(units) s.disabled = #units == 1 and action:GetUIState(units) ~= "enabled" s.icon = action:GetActionIcon(units) s.uiCtx = SubContext(units, { action = action }) s.iconColumns = 3 end mode_dlg:ShowCombatActionTargetChoice(self, units, stances, stanceChoice) end, group = "Hidden", id = "ChangeStance", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = "Execute default attack on the interactable target. Implemented in UIInteractWith", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = "", DisplayName = T(678480902214, --[[CombatAction Interact_Attack DisplayName]] "Attack"), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local action = unit:GetDefaultAttackAction() return action:GetAPCost(unit, args) end return self.ActionPoints end, GetTargets = function (self, units) return CombatActionGetAttackableEnemies(self, units and units[1]) end, Icon = "UI/Hud/iw_attack", IdDefault = "Interact_Attackdefault", MultiSelectBehavior = "first", QueuedBadgeText = T(574905632012, --[[CombatAction Interact_Attack QueuedBadgeText]] "INTERACT"), RequireState = "any", ShowIn = "Special", group = "Interactions", id = "Interact_Attack", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(581591931905, --[[CombatAction Interact_Banter DisplayName]] "Talk with "), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return GetReachableObjects(units, "Unit") end, GetUIState = function (self, units, args) local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end local target = args.target if target:IsNPC() and target:GetAllBanters("findFirst", { target_units = units }) then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_speak", IdDefault = "Interact_Banterdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(755005650995, --[[CombatAction Interact_Banter QueuedBadgeText]] "INTERACT"), RequireState = "exploration", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, UseFreeMove = true, group = "Interactions", id = "Interact_Banter", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(192251955775, --[[CombatAction Interact_CustomInteractable Description]] ""), DisplayName = T(783948937482, --[[CombatAction Interact_CustomInteractable DisplayName]] ""), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("CustomInteractable") end, GetUIState = function (self, units, args) local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end return args and args.target:GetUIState(units, args) or "disabled" end, Icon = "UI/Hud/iw_examine", IdDefault = "Interact_CustomInteractabledefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(695484031538, --[[CombatAction Interact_CustomInteractable QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, UseFreeMove = true, group = "Interactions", id = "Interact_CustomInteractable", }) PlaceObj('CombatAction', { ActionPoints = 3000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(805362980184, --[[CombatAction Interact_Disarm Description]] "Attempt to disarm the trap."), DisplayName = T(628643595814, --[[CombatAction Interact_Disarm DisplayName]] "Disarm "), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetActionDisplayName = function (self, units) local actionText = false for i, u in ipairs(units) do if IsKindOf(u, "BoobyTrappable") then actionText = u:GetDisarmActionName() break end end actionText = actionText or self.DisplayName return T{actionText, units} end, GetTargets = function (self, units) return units[1]:GetReachableObjects("Trap") end, Icon = "UI/Hud/iw_disarm", IdDefault = "Interact_Disarmdefault", InteractionLoadingBar = true, InterruptInExploration = true, IsAimableAttack = false, LocalChoiceAction = true, MultiSelectBehavior = "first", QueuedBadgeText = T(845650418409, --[[CombatAction Interact_Disarm QueuedBadgeText]] "DISARM"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Interactions", id = "Interact_Disarm", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = "Interaction for exiting a sector.", ConfigurableKeybind = false, Description = "", DisplayName = T(131395799549, --[[CombatAction Interact_Exit DisplayName]] "Exit sector"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetTargets = function (self, units) return CombatActionGetAttackableEnemies(self, units) end, Icon = "UI/Hud/iw_travel", IdDefault = "Interact_Exitdefault", InterruptInExploration = true, RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = "Special", UseFreeMove = true, group = "Interactions", id = "Interact_Exit", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(775371181070, --[[CombatAction Interact_LootContainer DisplayName]] "Loot"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("ItemContainer") end, Icon = "UI/Hud/iw_loot", IdDefault = "Interact_LootContainerdefault", InterruptInExploration = true, IsAimableAttack = false, LocalChoiceAction = true, QueuedBadgeText = T(869928919299, --[[CombatAction Interact_LootContainer QueuedBadgeText]] "LOOT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Interactions", id = "Interact_LootContainer", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(844963734438, --[[CombatAction Interact_LootUnit DisplayName]] "Loot "), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("Unit") end, GetUIState = function (self, units, args) if not IsKindOf(args.target, "Unit") or not args.target:IsDead() then return "hidden" end return CombatAction.GetUIState(self, units, args) end, Icon = "UI/Hud/iw_loot", IdDefault = "Interact_LootUnitdefault", InterruptInExploration = true, IsAimableAttack = false, LocalChoiceAction = true, QueuedBadgeText = T(465761957076, --[[CombatAction Interact_LootUnit QueuedBadgeText]] "LOOT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Interactions", id = "Interact_LootUnit", }) PlaceObj('CombatAction', { ActionPoints = 2000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(158391711834, --[[CombatAction Interact_ManEmplacement Description]] "Man Emplacement"), DisplayName = T(666572046174, --[[CombatAction Interact_ManEmplacement DisplayName]] "Operate"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("MachineGunEmplacement") end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not IsVisible(args.target) then return "hidden" end if not unit:UIHasAP(cost, self.id, args) then return "disabled", AttackDisableReasons.NoAP end return "enabled" end, Icon = "UI/Hud/iw_operate", IdDefault = "Interact_ManEmplacementdefault", InterruptInExploration = true, IsAimableAttack = false, LocalChoiceAction = true, MultiSelectBehavior = "nearest", QueuedBadgeText = T(693162081758, --[[CombatAction Interact_ManEmplacement QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Interactions", id = "Interact_ManEmplacement", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(530290341300, --[[CombatAction Interact_NotNow Description]] "Cannot talk with NPCs in combat"), DisplayName = T(629253239925, --[[CombatAction Interact_NotNow DisplayName]] "Talk with "), Execute = function (self, units, args) local unit = units if #unit > 0 then unit = units[1] end local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return self.ActionPoints end, GetTargets = function (self, units) return GetReachableObjects(units, "Unit") end, Icon = "UI/Hud/iw_speak", IdDefault = "Interact_NotNowdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(305159927540, --[[CombatAction Interact_NotNow QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) PlayVoiceResponse(unit, "NotNow") end, ShowIn = false, SimultaneousPlay = true, UseFreeMove = true, group = "Interactions", id = "Interact_NotNow", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(362512741067, --[[CombatAction Interact_Talk DisplayName]] "Talk with "), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("Unit") end, GetUIState = function (self, units, args) local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end local target = args.target if target:IsNPC() and FindEnabledConversation(target) then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_speak", IdDefault = "Interact_Talkdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(865614171711, --[[CombatAction Interact_Talk QueuedBadgeText]] "INTERACT"), RequireState = "exploration", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, UseFreeMove = true, group = "Interactions", id = "Interact_Talk", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(110677158472, --[[CombatAction Interact_UnitCustomInteraction DisplayName]] "Interact with "), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetActionDisplayName = function (self, units) local unit = units[1] local target = units[2] if target and target.spawner and target.spawner.InteractionName then return target.spawner.InteractionName end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetTargets = function (self, units) return GetReachableObjects(units, "Unit") end, GetUIState = function (self, units, args) local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end local target = args.target if target:IsNPC() then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_speak", IdDefault = "Interact_UnitCustomInteractiondefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(380543262427, --[[CombatAction Interact_UnitCustomInteraction QueuedBadgeText]] "INTERACT"), RequireState = "exploration", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, UseFreeMove = true, group = "Interactions", id = "Interact_UnitCustomInteraction", }) PlaceObj('CombatAction', { ActionPoints = 6000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(980718540270, --[[CombatAction Cut Description]] "Cut a wire fence with a Wire Cutter. You need to have a Wire Cutter in the character's Inventory."), DisplayName = T(653704184125, --[[CombatAction Cut DisplayName]] "Cut"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetAnyTarget = function (self, units) return #units > 0 and ChooseClosestFence(units[1]) end, GetDefaultTarget = function (self, unit) local target = ChooseClosestFence(unit) return target, 100 end, GetTargets = function (self, units) if #units == 0 then return {} end return {ChooseClosestFence(units[1])} end, GetUIState = function (self, units, args) local unit for i, u in ipairs(units) do if GetUnitWirecutter(u) then unit = u break end end if not unit then return "hidden", AttackDisableReasons.NoCutters end return CombatAction.GetUIState(self, units, args) end, Icon = "UI/Hud/iw_wire_cut", IdDefault = "Cutdefault", InteractionLoadingBar = true, InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(301556646698, --[[CombatAction Cut QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("Lockpick", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, SortKey = 7, UIBegin = function (self, units, args) CombatActionInteractablesChoice(self, units, args) end, group = "Interactions", id = "Cut", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(585037139087, --[[CombatAction Interact_DoorClose Description]] "Close the door. This may break the line of sight to and from enemies."), DisplayName = T(124371611844, --[[CombatAction Interact_DoorClose DisplayName]] "Close"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) local totalCost, interactCost = CombatActionInteractionGetCost(self, unit, args) local ap_mod = ((args and args.target and args.target.width or 0) >= 3) and 2 or 1 local nonInteractCost = totalCost - interactCost return (interactCost * ap_mod) + nonInteractCost end, GetTargets = function (self, units) return units[1]:GetReachableObjects("SlabWallDoor") end, GetUIState = function (self, units, args) local door = args and args.target if door then if door:IsInteracting() then return "disabled" end if door.room and door.room.outside_border then return "hidden" end end local base_state, reason = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state, reason end local u = units[1] if not DoorOnSameLevel(u, door) then return "hidden" end if not door:GetInteractionPos(u) then return "disabled" end if door and door.pass_through_state == "open" then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_close_door", IdDefault = "Interact_DoorClosedefault", InterruptInExploration = true, IsAimableAttack = false, MultiSelectBehavior = "nearest", QueuedBadgeText = T(690754380204, --[[CombatAction Interact_DoorClose QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target, "from ui") end, ShowIn = false, SortKey = 11, UseFreeMove = true, group = "Interactions", id = "Interact_DoorClose", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(677617724134, --[[CombatAction Interact_DoorOpen Description]] "Attempt to open the door. Some doors may be locked."), DisplayName = T(445739435529, --[[CombatAction Interact_DoorOpen DisplayName]] "Open"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) local totalCost, interactCost = CombatActionInteractionGetCost(self, unit, args) if totalCost < 0 then return totalCost end local ap_mod = ((args and args.target and args.target.width or 0) >= 3) and 2 or 1 local nonInteractCost = totalCost - interactCost return (interactCost * ap_mod) + nonInteractCost end, GetTargets = function (self, units) return units[1]:GetReachableObjects("SlabWallDoor") end, GetUIState = function (self, units, args) local door = args and args.target if door then if door:IsInteracting() then return "disabled" end if door.room and door.room.outside_border then return "hidden" end end local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end local u = units[1] if not DoorOnSameLevel(u, door) then return "hidden" end if not door:GetInteractionPos(u) then return "disabled" end if door and door.pass_through_state ~= "open" then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_open_door", IdDefault = "Interact_DoorOpendefault", InterruptInExploration = true, IsAimableAttack = false, MultiSelectBehavior = "nearest", QueuedBadgeText = T(306356128321, --[[CombatAction Interact_DoorOpen QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target, "from ui") end, ShowIn = false, SortKey = 13, UseFreeMove = true, group = "Interactions", id = "Interact_DoorOpen", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(146720455225, --[[CombatAction LockImpossible Description]] "Can't be opened by skill or force, unless the door is destroyed."), DisplayName = T(307771095785, --[[CombatAction LockImpossible DisplayName]] "Impossible to Open"), DontShowWith = true, Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, Icon = "UI/Hud/iw_broken_lock", IdDefault = "LockImpossibledefault", IsAimableAttack = false, QueuedBadgeText = T(836721737447, --[[CombatAction LockImpossible QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", GetOpenAction(args.target) or self.id, ap, args.goto_pos, args.goto_ap, args.target, "from ui") end, ShowIn = false, SortKey = 14, UIBegin = function (self, units, args) MultiTargetExecute(self.MultiSelectBehavior, units, function (unit) self:Execute({unit}, args) end, args and args.target) end, group = "Interactions", id = "LockImpossible", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(644731668039, --[[CombatAction Lockpick Description]] "Attempt to pick the lock (). Critical fail will jam the lock."), DisplayName = T(277286761846, --[[CombatAction Lockpick DisplayName]] "Pick Lock"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetAnyTarget = function (self, units) if #units == 0 then return end local interactables = GetReachableObjects(units, "Lockpickable") for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then return int end end end, GetDefaultTarget = function (self, unit) local interactables = unit:GetReachableObjects("Lockpickable") for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then return int end end end, GetTargets = function (self, units) if #units == 0 then return {} end local interactables = GetReachableObjects(units, "Lockpickable") local result = {} for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then result[#result + 1] = int end end return result end, GetUIState = function (self, units, args) local target = args and args.target or self:GetAnyTarget(units) if not target or not IsKindOf(target, "Lockpickable") or not target:CanUseAction(self.id) then return "hidden" end local unit for i, u in ipairs(units) do if GetUnitLockpick(u) then unit = u break end end if not unit then return "hidden", AttackDisableReasons.NoLockpick end return "enabled" end, Icon = "UI/Hud/iw_lockpick", IdDefault = "Lockpickdefault", InteractionLoadingBar = true, InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(863593591904, --[[CombatAction Lockpick QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("Lockpick", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, SortKey = 15, UIBegin = function (self, units, args) CombatActionInteractablesChoice(self, units, args) end, group = "Interactions", id = "Lockpick", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(991703344697, --[[CombatAction NoToolsLocked Description]] "You need a Locksmith's Kit to unlock or a Crowbar to break the lock."), DisplayName = T(895045610563, --[[CombatAction NoToolsLocked DisplayName]] "Locked"), DontShowWith = true, Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, Icon = "UI/Hud/iw_lockpick", IdDefault = "NoToolsLockeddefault", IsAimableAttack = false, QueuedBadgeText = T(693029676179, --[[CombatAction NoToolsLocked QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", GetOpenAction(args.target) or self.id, ap, args.goto_pos, args.goto_ap, args.target, "from ui") end, ShowIn = false, SortKey = 15, UIBegin = function (self, units, args) MultiTargetExecute(self.MultiSelectBehavior, units, function (unit) self:Execute({unit}, args) end, args and args.target) end, group = "Interactions", id = "NoToolsLocked", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(148191264365, --[[CombatAction Break Description]] "Attempt to break the lock (). Items in a breached container may be damaged."), DisplayName = T(373874832012, --[[CombatAction Break DisplayName]] "Break Lock"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetAnyTarget = function (self, units) if #units == 0 then return end local interactables = GetReachableObjects(units, "Lockpickable") for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then return int end end end, GetDefaultTarget = function (self, unit) local interactables = unit:GetReachableObjects("Lockpickable") for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then return int end end end, GetTargets = function (self, units) if #units == 0 then return {} end local interactables = GetReachableObjects(units, "Lockpickable") local result = {} for i, int in ipairs(interactables) do if int:CanUseAction(self.id) then result[#result + 1] = int end end return result end, GetUIState = function (self, units, args) local target = args and args.target or self:GetAnyTarget(units) if not target or not IsKindOf(target, "Lockpickable") or not target:CanUseAction(self.id) then return "hidden" end local unit for i, u in ipairs(units) do if GetUnitCrowbar(u) then unit = u break end end if not unit then return "hidden", AttackDisableReasons.NoCrowbar end return "enabled" end, Icon = "UI/Hud/iw_break_lock", IdDefault = "Breakdefault", InteractionLoadingBar = true, InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(334542893280, --[[CombatAction Break QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("Lockpick", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, SortKey = 16, UIBegin = function (self, units, args) CombatActionInteractablesChoice(self, units, args) end, group = "Interactions", id = "Break", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(641342518672, --[[CombatAction NoToolsBlocked Description]] "Lock jammed! You need a Crowbar to break the lock."), DisplayName = T(699556305475, --[[CombatAction NoToolsBlocked DisplayName]] "Blocked"), DontShowWith = true, Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, Icon = "UI/Hud/iw_break_lock", IdDefault = "NoToolsBlockeddefault", IsAimableAttack = false, QueuedBadgeText = T(807947680083, --[[CombatAction NoToolsBlocked QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", GetOpenAction(args.target) or self.id, ap, args.goto_pos, args.goto_ap, args.target, "from ui") end, ShowIn = false, SortKey = 16, UIBegin = function (self, units, args) MultiTargetExecute(self.MultiSelectBehavior, units, function (unit) self:Execute({unit}, args) end, args and args.target) end, group = "Interactions", id = "NoToolsBlocked", }) PlaceObj('CombatAction', { ActionPoints = 1000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(131999829664, --[[CombatAction Interact_WindowBreak Description]] "Break the window."), DisplayName = T(174179805252, --[[CombatAction Interact_WindowBreak DisplayName]] "Break"), Execute = function (self, units, args) CombatActionExecuteWithMove(self, units, args) end, GetAPCost = function (self, unit, args) return CombatActionInteractionGetCost(self, unit, args) end, GetTargets = function (self, units) return units[1]:GetReachableObjects("SlabWallWindow") end, GetUIState = function (self, units, args) local base_state = CombatAction.GetUIState(self, units, args) if base_state ~= "enabled" then return base_state end if not args.target:ShouldShowUnitInteraction() then return "hidden" end if args.target.pass_through_state ~= "broken" then return "enabled" end return "hidden" end, Icon = "UI/Hud/iw_break_lock", IdDefault = "Interact_WindowBreakdefault", InterruptInExploration = true, IsAimableAttack = false, MultiSelectBehavior = "first", QueuedBadgeText = T(815107486828, --[[CombatAction Interact_WindowBreak QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... unit:SetActionCommand("InteractWith", self.id, ap, args.goto_pos, args.goto_ap, args.target) end, ShowIn = false, SortKey = 17, UseFreeMove = true, group = "Interactions", id = "Interact_WindowBreak", }) PlaceObj('CombatAction', { ActionPoints = 2000, ActivePauseBehavior = "queue", Comment = "leave emplacement", ConfigurableKeybind = false, Description = T(353439501830, --[[CombatAction MGLeave Description]] "Leave the Emplacement"), DisplayName = T(108712918403, --[[CombatAction MGLeave DisplayName]] "Leave Emplacement"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) if not unit:HasStatusEffect("ManningEmplacement") then return -1 end return self.ActionPoints end, Icon = "UI/Icons/Hud/dash", IdDefault = "MGLeavedefault", QueuedBadgeText = T(877939170118, --[[CombatAction MGLeave QueuedBadgeText]] "LEAVE"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("LeaveEmplacement") end, UIBegin = function (self, units, args) self:Execute(units, args) end, group = "MachineGun", id = "MGLeave", }) PlaceObj('CombatAction', { ActionPoints = 1000, ActivePauseBehavior = "queue", Description = T(197752638092, --[[CombatAction MGPack Description]] "Cancel machine gun setup and move freely."), DisplayName = T(717760265144, --[[CombatAction MGPack DisplayName]] "Pack Up Machine Gun"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) if unit:HasStatusEffect("ManningEmplacement") then return -1 end self.ActionPoints = unit:GetAttackAPCost(self.action, self.weapon, self.ActionPoints) return self.ActionPoints end, Icon = "UI/Icons/Hud/dash", IdDefault = "MGPackdefault", KeybindingSortId = "2375", QueuedBadgeText = T(841179028797, --[[CombatAction MGPack QueuedBadgeText]] "PACK UP"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("MGPack", self.id, ap, ...) end, UIBegin = function (self, units, args) self:Execute(units, args) end, group = "MachineGun", id = "MGPack", param_bindings = {}, }) PlaceObj('CombatAction', { ActionPoints = 5000, ActionType = "Ranged Attack", ActivePauseBehavior = "queue", AimType = "cone", ConfigurableKeybind = false, Description = T(404393085473, --[[CombatAction MGRotate Description]] "Rotate the machine gun's firing cone."), DisplayName = T(515040916802, --[[CombatAction MGRotate DisplayName]] "Rotate Machine Gun"), Execute = function (self, units, args) return CombatActions.Overwatch.Execute(self, units, args) end, GetAPCost = function (self, unit, args) if unit:HasStatusEffect("ManningEmplacement") then return -1 end local cost = CombatActions.MGSetup:GetAPCost(unit, args) / 2 cost = Max(1, cost / const.Scale.AP) * const.Scale.AP return cost end, GetActionDescription = function (self, units) local unit = units[1] local bonus = 0 local cost = self:GetAPCost(unit) if unit and cost >= 0 then local weapon = self:GetAttackWeapons(unit) local aim = Min((unit:GetUIActionPoints() - cost) / const.Scale.AP, weapon.MaxAimActions) local apply, value = Presets.ChanceToHitModifier.Default.Aim:CalcValue(unit, nil, nil, nil, nil, nil, nil, aim) bonus = value end local attacks = 1 if unit and (cost or -1) >= 0 then attacks = unit:GetNumMGInterruptAttacks(true) end local description = T{self.Description, bonus = bonus} if unit:UIHasAP(cost, self.id) then description = description .. T(813594976169, "Max number of interrupt attacks is based on remaining AP.") end return description end, GetActionResults = function (self, unit, args) return CombatActions.Overwatch.GetActionResults(self, unit, args) end, GetAimParams = function (self, unit, weapon) return CombatActions.Overwatch.GetAimParams(self, unit, weapon) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return CombatActions.Overwatch.GetMaxAimRange(self, unit, weapon) end, GetMinAimRange = function (self, unit, weapon) return CombatActions.Overwatch.GetMinAimRange(self, unit, weapon) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end local in_water = terrain.IsWater(unit) if in_water then return "disabled", AttackDisableReasons.Water end return "enabled" end, Icon = "UI/Icons/Hud/bullet_hell", IdDefault = "MGRotatedefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectOverwatch", KeybindingSortId = "2371", MultiSelectBehavior = "hidden", QueuedBadgeText = T(361425500129, --[[CombatAction MGRotate QueuedBadgeText]] "ROTATE"), Run = function (self, unit, ap, ...) --PlayVoiceResponse(unit, "Overwatch") unit:SetActionCommand("MGTarget", self.id, ap, ...) end, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "cancel") end, group = "MachineGun", id = "MGRotate", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", ActivePauseBehavior = "queue", AimType = "cone", ConfigurableKeybind = false, Description = T(868121469992, --[[CombatAction MGSetup Description]] "Focus on a cone-shaped area, immobilizing yourself and going prone. You can only shoot enemies inside that cone. Accuracy is increased and enemies will provoke interrupt attacks with actions inside the cone (even if your AP are spent)."), DisplayName = T(898083486639, --[[CombatAction MGSetup DisplayName]] "Set Machine Gun"), Execute = function (self, units, args) return CombatActions.Overwatch.Execute(self, units, args) end, GetAPCost = function (self, unit, args) local base = self:ResolveValue("max_cost") * const.Scale.AP local min = self:ResolveValue("min_cost") * const.Scale.AP local min_str = self:ResolveValue("min_str") local cost = base - MulDivRound(Max(0, unit.Strength - min_str), base - min, 100 - min_str) cost = Max(min, (cost / const.Scale.AP) * const.Scale.AP) return unit:GetAttackAPCost(self, self:GetAttackWeapons(unit), cost) end, GetActionDescription = function (self, units) local unit = units[1] local bonus = 0 local cost = self:GetAPCost(unit) if unit and cost >= 0 then local weapon = self:GetAttackWeapons(unit) if not weapon then return self.Description end local aim = Min((unit:GetUIActionPoints() - cost) / const.Scale.AP, weapon.MaxAimActions) local apply, value = Presets.ChanceToHitModifier.Default.Aim:CalcValue(unit, nil, nil, nil, nil, nil, nil, aim) bonus = value end local attacks = 1 if unit and (cost or -1) >= 0 then attacks = unit:GetNumMGInterruptAttacks(true) end local description = T{self.Description, bonus = bonus} if unit:UIHasAP(cost, self.id) then description = description .. T(813594976169, "Max number of interrupt attacks is based on remaining AP.") end return description end, GetActionResults = function (self, unit, args) return CombatActions.Overwatch.GetActionResults(self, unit, args) end, GetAimParams = function (self, unit, weapon) return CombatActions.Overwatch.GetAimParams(self, unit, weapon) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return CombatActions.Overwatch.GetMaxAimRange(self, unit, weapon) end, GetMinAimRange = function (self, unit, weapon) return CombatActions.Overwatch.GetMinAimRange(self, unit, weapon) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end local weapon = self:GetAttackWeapons(unit, args) local ok, reason = unit:CanUseWeapon(weapon) if not ok then return "disabled", reason end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end local in_water = terrain.IsWater(unit) if in_water then return "disabled", AttackDisableReasons.Water end local attack = unit:GetDefaultAttackAction() local state, reason = attack:GetUIState(units, args) if state ~= "enabled" and (reason == AttackDisableReasons.NoWeapon or reason == AttackDisableReasons.OutOfAmmo or reason == AttackDisableReasons.WeaponJammed or reason == AttackDisableReasons.InsufficientAmmo) then return state, reason end return "enabled" end, Icon = "UI/Icons/Hud/SetMachineGun ", IdDefault = "MGSetupdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectOverwatch", KeybindingSortId = "2370", MultiSelectBehavior = "hidden", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "min_cost", 'Value', 4, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "max_cost", 'Value', 10, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "min_str", 'Value', 50, 'Tag', "", }), }, QueuedBadgeText = T(515583344616, --[[CombatAction MGSetup QueuedBadgeText]] "SET"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("MGSetup", self.id, ap, ...) end, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "cancel") end, group = "MachineGun", id = "MGSetup", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(579959239660, --[[CombatAction Attack Description]] "Make an attack using your equipped weapon. Some weapons have alternative attack modes."), DisplayName = T(449333187481, --[[CombatAction Attack DisplayName]] "Attack"), Execute = function (self, units, args) local unit = units[1] local defAction = unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Execute(units, args) end, GetAPCost = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) if not defAction then assert(false) -- This unit's action caching did something weird? return end return CombatActions[defAction]:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionDamage(unit, target, args) end, GetActionDescription = function (self, units) local unit = units[1] return CombatActionsAppendFreeAimDescription(self, unit, self.Description) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionResults(unit, args) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end local weapon, _, list = unit:GetActiveWeapons("Firearm") return weapon end, GetUIState = function (self, units, args) return CombatActionFiringMetaGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/attack", IdDefault = "Attackdefault", InterruptInExploration = true, IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Run(unit, ap, ...) end, ShowIn = "Special", SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack", "attack") end, basicAttack = true, group = "FiringModeMetaAction", id = "Attack", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = -1000, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(278014409867, --[[CombatAction AttackDual Description]] "Make an attack using your equipped weapon. Some weapons have alternative attack modes."), DisplayName = T(701092552040, --[[CombatAction AttackDual DisplayName]] "Dual Shot"), DisplayNameShort = T(582378660435, --[[CombatAction AttackDual DisplayNameShort]] "Dual Shot"), Execute = function (self, units, args) local unit = units[1] local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Execute(units, args) end, GetAPCost = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionDamage(unit, target, args) end, GetActionDescription = function (self, units) local unit = units[1] return CombatActionsAppendFreeAimDescription(self, unit, self.Description) end, GetActionDisplayName = function (self, units) local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end local unit = units[1] return CombatActionsAppendFreeAimActionName(self, unit, name) end, GetActionResults = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionResults(unit, args) end, GetAttackWeapons = function (self, unit, args) return unit:GetActiveWeapons("Firearm") end, GetUIState = function (self, units, args) return CombatActionFiringMetaGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/attack", IdDefault = "AttackDualdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Run(unit, ap, ...) end, ShowIn = "Special", SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack", "attack") end, basicAttack = true, group = "FiringModeMetaAction", id = "AttackDual", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "cone", ConfigurableKeybind = false, CostBasedOnWeapon = true, Description = T(406630009702, --[[CombatAction AttackShotgun Description]] "Make an attack using your equipped weapon. Targets in the attack cone take guaranteed damage. Some weapons have alternative firing modes."), DisplayName = T(591280140960, --[[CombatAction AttackShotgun DisplayName]] "Shotgun Attack"), Execute = function (self, units, args) local unit = units[1] local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Execute(units, args) end, GetAPCost = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetAPCost(unit, args) end, GetActionDamage = function (self, unit, target, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionDamage(unit, target, args) end, GetActionDescription = function (self, units) local unit = units[1] local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionDescription(units) end, GetActionDisplayName = function (self, units) local unit = units[1] local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionDisplayName(units) end, GetActionResults = function (self, unit, args) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetActionResults(unit, args) end, GetAimParams = function (self, unit, weapon) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetAimParams(unit, weapon) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:GetMinAimRange(unit, weapon) end, GetUIState = function (self, units, args) return CombatActionFiringMetaGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/attack", IdDefault = "AttackShotgundefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectBasicAttack", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) local defAction = unit.ui_actions and unit.ui_actions[self.IdDefault] or unit:ResolveDefaultFiringModeAction(self, true) return CombatActions[defAction]:Run(unit, ap, ...) end, ShowIn = "Special", SortKey = 1, StealthAttack = true, UIBegin = function (self, units, args) local dlg = GetInGameInterfaceModeDlg() if IsKindOf(dlg, "IModeCombatAreaAim") and dlg.crosshair then CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "attack") else CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end end, basicAttack = true, group = "FiringModeMetaAction", id = "AttackShotgun", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(760140507992, --[[CombatAction StanceStanding Description]] "Normal movement."), DisplayName = T(892987595505, --[[CombatAction StanceStanding DisplayName]] "Stance: Standing"), DisplayNameShort = T(878700600627, --[[CombatAction StanceStanding DisplayNameShort]] "Standing"), Execute = function (self, units, args) if self:GetUIState(units, args) ~= "enabled" then return end local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return unit:GetStanceToStanceAP("Standing", args and args.stance_override) end, GetActionIcon = function (self, units) local unit = units[1] local cover = GetHighestCoverUI(unit) if cover then return cover == const.CoverHigh and "UI/Hud/stances_cover_full" or "UI/Hud/stances_up" end return self.Icon end, GetUIState = function (self, units, args) if #units > 1 and not g_Combat then return "enabled" end local unit = units[1] local canSwitch, reason = unit:CanSwitchStance("Standing", args) if reason == "hidden" then return reason end return canSwitch and "enabled" or "disabled", reason end, Icon = "UI/Hud/stances_up", IdDefault = "StanceStandingdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(700088630352, --[[CombatAction StanceStanding QueuedBadgeText]] "CHANGE STANCE"), RequireState = "any", Run = function (self, unit, ap, ...) if not g_Combat and unit.goto_target and not IsActivePaused() then unit.goto_stance = "Standing" return end unit:SetActionCommand("ChangeStance", self.id, ap, "Standing", ...) end, ShowIn = "Stances", SortKey = 1, UseFreeMove = true, group = "Stances", id = "StanceStanding", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(923499847364, --[[CombatAction StanceCrouch Description]] "Decreased movement range.\nHarder to hit with ranged attacks and resistant to explosive damage."), DisplayName = T(849813879402, --[[CombatAction StanceCrouch DisplayName]] "Stance: Crouched"), DisplayNameShort = T(904286738966, --[[CombatAction StanceCrouch DisplayNameShort]] "Crouched"), Execute = function (self, units, args) if self:GetUIState(units, args) ~= "enabled" then return end local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return unit:GetStanceToStanceAP("Crouch", args and args.stance_override) end, GetActionIcon = function (self, units) local unit = units[1] local cover = GetHighestCoverUI(unit) if cover then return (cover == const.CoverHigh or cover == const.CoverLow) and "UI/Hud/stances_cover" or "UI/Hud/stances_middle" end return self.Icon end, GetTargets = function (self, units) return units[1]:GetVisibleEnemies() end, GetUIState = function (self, units, args) if #units > 1 and not g_Combat then return "enabled" end local unit = units[1] local canSwitch, reason = unit:CanSwitchStance("Crouch", args) if reason == "hidden" then return reason end return canSwitch and "enabled" or "disabled", reason end, Icon = "UI/Hud/stances_middle", IdDefault = "StanceCrouchdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(114028599146, --[[CombatAction StanceCrouch QueuedBadgeText]] "CHANGE STANCE"), RequireState = "any", Run = function (self, unit, ap, ...) if not g_Combat and unit.goto_target and not IsActivePaused() then unit.goto_stance = "Crouch" return end unit:SetActionCommand("ChangeStance", self.id, ap, "Crouch", ...) end, ShowIn = "Stances", SortKey = 2, UseFreeMove = true, group = "Stances", id = "StanceCrouch", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", ConfigurableKeybind = false, Description = T(772277920996, --[[CombatAction StanceProne Description]] "Greatly decreased movement range. Can't climb or jump over obstacles.\nMuch harder to hit with ranged attacks and very resistant to explosive damage."), DisplayName = T(775355544630, --[[CombatAction StanceProne DisplayName]] "Stance: Prone"), DisplayNameShort = T(587848793431, --[[CombatAction StanceProne DisplayNameShort]] "Prone"), Execute = function (self, units, args) if self:GetUIState(units, args) ~= "enabled" then return end local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local ap = unit:GetStanceToStanceAP("Prone", args and args.stance_override) return ap end, GetUIState = function (self, units, args) local allowed, text for i, unit in ipairs(units) do local canSwitch, reason = unit:CanSwitchStance("Prone", args) if canSwitch or reason == "hidden" then allowed = true break else allowed = "disabled" text = reason end end if allowed == "disabled" then return allowed, text end if #units > 1 and not g_Combat then return "enabled" end local unit = units[1] local canSwitch, reason = unit:CanSwitchStance("Prone", args) if reason == "hidden" then return reason end return canSwitch and "enabled" or "disabled", reason end, Icon = "UI/Hud/stances_down", IdDefault = "StancePronedefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(438930896929, --[[CombatAction StanceProne QueuedBadgeText]] "CHANGE STANCE"), RequireState = "any", Run = function (self, unit, ap, ...) if not g_Combat and unit.goto_target and not IsActivePaused() then unit.goto_stance = "Prone" return end unit:SetActionCommand("ChangeStance", self.id, ap, "Prone", ...) end, ShowIn = "Stances", SortKey = 3, UseFreeMove = true, group = "Stances", id = "StanceProne", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = 'Placeholder action to allow controllable beasts who are in stance ""', ConfigurableKeybind = false, DisplayName = T(417850002799, --[[CombatAction Stance DisplayName]] "StanceStanding"), IdDefault = "Stancedefault", InterruptInExploration = true, QueuedBadgeText = T(562909457289, --[[CombatAction Stance QueuedBadgeText]] "CHANGE STANCE"), ShowIn = false, SortKey = 99, UseFreeMove = true, group = "Stances", id = "Stance", }) PlaceObj('CombatAction', { ActionShortcut = "1", AimType = "line", ConfigurableKeybind = false, DisplayName = T(464727480495, --[[CombatAction Action1 DisplayName]] "Action 1"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action1default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(1) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 1, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action1", }) PlaceObj('CombatAction', { ActionShortcut = "2", ConfigurableKeybind = false, DisplayName = T(950327111815, --[[CombatAction Action2 DisplayName]] "Action 2"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action2default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(2) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 2, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action2", }) PlaceObj('CombatAction', { ActionShortcut = "3", ConfigurableKeybind = false, DisplayName = T(354149051865, --[[CombatAction Action3 DisplayName]] "Action 3"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action3default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(3) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 3, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action3", }) PlaceObj('CombatAction', { ActionShortcut = "4", ConfigurableKeybind = false, DisplayName = T(581763369778, --[[CombatAction Action4 DisplayName]] "Action 4"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action4default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(4) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 4, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action4", }) PlaceObj('CombatAction', { ActionShortcut = "5", ConfigurableKeybind = false, DisplayName = T(640626466336, --[[CombatAction Action5 DisplayName]] "Action 5"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action5default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(5) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 5, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action5", }) PlaceObj('CombatAction', { ActionShortcut = "6", AimType = "melee", ConfigurableKeybind = false, DisplayName = T(131093562331, --[[CombatAction Action6 DisplayName]] "Action 6"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action6default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(6) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 6, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action6", }) PlaceObj('CombatAction', { ActionShortcut = "7", ConfigurableKeybind = false, DisplayName = T(612538760827, --[[CombatAction Action7 DisplayName]] "Action 7"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action7default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(7) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 7, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action7", }) PlaceObj('CombatAction', { ActionShortcut = "8", ConfigurableKeybind = false, DisplayName = T(346351186243, --[[CombatAction Action8 DisplayName]] "Action 8"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action8default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(8) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 8, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action8", }) PlaceObj('CombatAction', { ActionShortcut = "9", ConfigurableKeybind = false, DisplayName = T(929240688056, --[[CombatAction Action9 DisplayName]] "Action 9"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action9default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(9) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 9, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action9", }) PlaceObj('CombatAction', { ActionShortcut = "0", ConfigurableKeybind = false, DisplayName = T(884893693139, --[[CombatAction Action10 DisplayName]] "Action 10"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action10default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(10) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 10, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action10", }) PlaceObj('CombatAction', { ActionShortcut = "-", ConfigurableKeybind = false, DisplayName = T(862495147832, --[[CombatAction Action11 DisplayName]] "Action 11"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action11default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(11) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 11, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action11", }) PlaceObj('CombatAction', { ActionShortcut = "=", ConfigurableKeybind = false, DisplayName = T(550345032199, --[[CombatAction Action12 DisplayName]] "Action 12"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action12default", IsAimableAttack = false, RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(12) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 12, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action12", }) PlaceObj('CombatAction', { Comment = "Signature Ability goes here.", ConfigurableKeybind = false, DisplayName = T(432523773389, --[[CombatAction Action13 DisplayName]] "Signature Ability"), EvalTarget = function (self, units, target, args) local action = self:ResolveAction(units) if action then return action:EvalTarget(units, target, args) end end, Execute = function (self, units, args) local action = self:ResolveAction(units) if action then return action:Execute(units, args) end end, GetAPCost = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAPCost(unit, args) end return -1 end, GetActionDescription = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDescription(units) end return "" end, GetActionDisplayName = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionDisplayName(units) end return "" end, GetActionIcon = function (self, units) local action = self:ResolveAction(units) if action then return action:GetActionIcon(units) end return "" end, GetAnyTarget = function (self, units) local action = self:ResolveAction(units) if action then return action:GetAnyTarget(units) end end, GetAttackWeapons = function (self, unit, args) local action = self:ResolveAction(unit) if action then return action:GetAttackWeapons(unit, args) end end, GetDefaultTarget = function (self, unit) local action = self:ResolveAction(unit) if action then return action:GetDefaultTarget(unit) end end, GetMaxAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMaxAimRange(unit, weapon) end return false end, GetMinAimRange = function (self, unit, weapon) local action = self:ResolveAction(unit) if action then return action:GetMinAimRange(unit, weapon) end return false end, GetTargets = function (self, units) local action = self:ResolveAction(units) if action then return action:GetTargets(units) end end, GetUIState = function (self, units, args) local action = self:ResolveAction(units) if action then return action:GetUIState(units, args) end return "hidden" end, IdDefault = "Action13default", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", ResolveAction = function (self, context) local obj = IsValid(context) and context or (context or empty_table)[1] return obj and IsKindOf(obj, "Unit") and obj:ResolveUIAction(13) end, Run = function (self, unit, ap, ...) local action = self:ResolveAction(unit) if action then return action:Run(unit, ap, ...) end end, SortKey = 13, UIBegin = function (self, units, args) local action = self:ResolveAction(units) if action then return action:UIBegin(units, args) end end, group = "UIActions", id = "Action13", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, Description = T(812548786196, --[[CombatAction RemoteDetonation Description]] "Detonate explosives with remote detonation in the area."), DisableAimAnim = true, DisplayName = T(189657731700, --[[CombatAction RemoteDetonation DisplayName]] "Remote Detonation"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) return 0 end, GetActionDescription = function (self, units) local description = self.Description if (description or "") == "" then description = self:GetActionDisplayName() end return description end, GetActionResults = function (self, unit, args) local grenade = self:GetAttackWeapons(unit, args) local target = ResolveGrenadeTargetPos(args.target) if not grenade or not target then return {} end args.target = target local attack_args = unit:PrepareAttackArgs(self.id, args) local results = grenade:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) return GetUnitEquippedDetonator(unit) end, GetMaxAimRange = function (self, unit, weapon) return weapon.ThrowRange end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/remote_detonation", IdDefault = "RemoteDetonationdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectThrowGrenade", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("RemoteDetonate", self.id, ap, ...) end, SortKey = 5, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "Consumables", id = "RemoteDetonation", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(576285892355, --[[CombatAction ThrowGrenadeA DisplayName]] "Grenade"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) return grenade and unit:GetAttackAPCost(self, grenade, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) return CombatActionGrenadeDescription(self, units) end, GetActionDisplayName = function (self, units) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then return T{355482653923, "Throw ", name = weapon.DisplayName} end end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) local target = ResolveGrenadeTargetPos(args.target, unit:GetPos(), grenade) args.target = target args.stance = "Standing" local attack_args = unit:PrepareAttackArgs(self.id, args) if not grenade or not target then return {}, attack_args end local results = grenade:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) local weapon = unit:GetItemInSlot("Handheld A", "Grenade", 1, 1) return weapon end, GetMaxAimRange = function (self, unit, weapon) local maxRange = weapon:GetMaxAimRange(unit) if HasPerk(unit, "Throwing") then maxRange = maxRange + CharacterEffectDefs.Throwing:ResolveValue("RangeIncrease") end return maxRange end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/throw_grenade", IdDefault = "ThrowGrenadeAdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectThrowGrenade", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ThrowGrenade", self.id, ap, ...) end, SortKey = 5, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "Consumables", id = "ThrowGrenadeA", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(126909368045, --[[CombatAction ThrowGrenadeB DisplayName]] "Grenade"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) return grenade and unit:GetAttackAPCost(self, grenade, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) return CombatActionGrenadeDescription(self, units) end, GetActionDisplayName = function (self, units) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then return T{355482653923, "Throw ", name = weapon.DisplayName} end end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.ThrowGrenadeA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) local weapon = unit:GetItemInSlot("Handheld A", "Grenade", 2, 1) return weapon end, GetMaxAimRange = function (self, unit, weapon) local maxRange = weapon:GetMaxAimRange(unit) if HasPerk(unit, "Throwing") then maxRange = maxRange + CharacterEffectDefs.Throwing:ResolveValue("RangeIncrease") end return maxRange end, GetUIState = function (self, units, args) return CombatActions.ThrowGrenadeA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/throw_grenade", IdDefault = "ThrowGrenadeBdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectThrowGrenade", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ThrowGrenade", self.id, ap, ...) end, SortKey = 6, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "Consumables", id = "ThrowGrenadeB", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(638369035024, --[[CombatAction ThrowGrenadeC DisplayName]] "Grenade"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) return grenade and unit:GetAttackAPCost(self, grenade, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) return CombatActionGrenadeDescription(self, units) end, GetActionDisplayName = function (self, units) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then return T{355482653923, "Throw ", name = weapon.DisplayName} end end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.ThrowGrenadeA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) local weapon = unit:GetItemInSlot("Handheld B", "Grenade", 1, 1) return weapon end, GetMaxAimRange = function (self, unit, weapon) local maxRange = weapon:GetMaxAimRange(unit) if HasPerk(unit, "Throwing") then maxRange = maxRange + CharacterEffectDefs.Throwing:ResolveValue("RangeIncrease") end return maxRange end, GetUIState = function (self, units, args) return CombatActions.ThrowGrenadeA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/throw_grenade", IdDefault = "ThrowGrenadeCdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectThrowGrenade", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ThrowGrenade", self.id, ap, ...) end, SortKey = 6, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "Consumables", id = "ThrowGrenadeC", }) PlaceObj('CombatAction', { ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(264282791362, --[[CombatAction ThrowGrenadeD DisplayName]] "Grenade"), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) return grenade and unit:GetAttackAPCost(self, grenade, false, args and args.aim or 0) or -1 end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) return CombatActionGrenadeDescription(self, units) end, GetActionDisplayName = function (self, units) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then return T{355482653923, "Throw ", name = weapon.DisplayName} end end local name = self.DisplayName if (name or "") == "" then name = Untranslated(self.id) end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.ThrowGrenadeA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) local weapon = unit:GetItemInSlot("Handheld B", "Grenade", 2, 1) return weapon end, GetMaxAimRange = function (self, unit, weapon) local maxRange = weapon:GetMaxAimRange(unit) if HasPerk(unit, "Throwing") then maxRange = maxRange + CharacterEffectDefs.Throwing:ResolveValue("RangeIncrease") end return maxRange end, GetUIState = function (self, units, args) return CombatActions.ThrowGrenadeA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/throw_grenade", IdDefault = "ThrowGrenadeDdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectThrowGrenade", MultiSelectBehavior = "first", RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("ThrowGrenade", self.id, ap, ...) end, SortKey = 6, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "Consumables", id = "ThrowGrenadeD", }) PlaceObj('CombatAction', { ActionPoints = 2000, ActivePauseBehavior = "queue", AimType = "melee", ConfigurableKeybind = false, Description = T(105765420738, --[[CombatAction Bandage Description]] "Spend some time healing a wounded ally. You will not be able to perform other actions while bandaging. Your ally will not be able to move but can still attack.\n\nStabilize allies or heal % of their max HP (based on the skill). Sets your stance to Crouched."), DisplayName = T(615556944457, --[[CombatAction Bandage DisplayName]] "Bandage"), EvalTarget = function (self, units, target, args) local unit = units[1] if not target or unit:IsOnEnemySide(target) then return -1 end if not target:HasStatusEffect("Bleeding") and (target.HitPoints >= target.MaxHitPoints) then return -1 end return (200 - target.HitPoints) or 0 end, GetAPCost = function (self, unit, args) local medicine = self:GetAttackWeapons(unit, args) if not medicine then return -1 end -- can be valid in AI PrecalcAction return self.ActionPoints end, GetActionDescription = function (self, units) local unit = units[1] local medkit = self:GetAttackWeapons(unit) local hp = unit:CalcHealAmount(medkit) or 0 local percent = MulDivRound(100, hp, unit.MaxHitPoints) if LastLoadedOrLoadingIMode == "IModeCombatMelee" then return T{930612158384, "Select the unit you would like to bandage, healing them for % of their max HP.", hp = percent} end return T{self.Description, hp = percent} end, GetAnyTarget = function (self, units) return GetBandageTargets(units[1], "any", "ignore") end, GetAttackWeapons = function (self, unit, args) return GetUnitEquippedMedicine(unit) end, GetDefaultTarget = function (self, unit) local units = {unit} if self:EvalTarget(units, unit) > 0 then return unit end return CombatAction.GetDefaultTarget(self, unit) end, GetTargets = function (self, units) return GetBandageTargets(units[1], "all", "ignore") end, GetUIState = function (self, units, args) local unit = units[1] if g_Combat and not unit:HasAP(self.ActionPoints) then return "disabled", GetUnitNoApReason(unit) end if not GetBandageTargets(unit, "any", "reachable") then return "disabled", AttackDisableReasons.NoBandageTarget end return "enabled" end, Icon = "UI/Icons/Hud/stop_bleeding", IdDefault = "Bandagedefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectBandage", MoveStep = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "selfheal", 'Value', 70, 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "base_heal", 'Value', 20, 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "medical_max_heal", 'Value', 30, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "ReviveConditionLoss", 'Value', 10, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "MaxConditionHPRestore", 'Value', 120, 'Tag', "", }), }, QueuedBadgeText = T(989605585095, --[[CombatAction Bandage QueuedBadgeText]] "BANDAGE"), RequireState = "any", RequireWeapon = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("Bandage", self.id, ap, ...) end, SortKey = 10, UIBegin = function (self, units, args) if self:GetAnyTarget(units) then if units then table.sort(units, function (a,b) return a.Medical > b.Medical and self:GetAttackWeapons(a) end) end CombatActionAttackStart(self, units, args, "IModeCombatMelee") end end, group = "Consumables", id = "Bandage", }) PlaceObj('CombatAction', { ActivePauseBehavior = "queue", Comment = "used by AI", Description = T(682794130547, --[[CombatAction Interact Description]] "Interact with nearby objects."), DisplayName = T(381772065068, --[[CombatAction Interact DisplayName]] "Interact"), EvalTarget = function (self, units, target) local closest_unit = ChooseClosestObject(units, target) local combat_action = target:GetInteractionCombatAction() if not combat_action then return end return combat_action:EvalTarget(closest_unit, target) end, Execute = function (self, units, args) local target = args and args.target if not target then return end local closest_unit = ChooseClosestObject(units, target) local combat_action = target:GetInteractionCombatAction(closest_unit) if not combat_action then return end if HasCombatActionInProgress(closest_unit) and not closest_unit:IsInterruptable() then return end return combat_action:Execute(closest_unit, args) end, GetAPCost = function (self, unit, args) if args and args.override_ap_cost then return args.override_ap_cost end local target = args and args.target if not target then local targets = self:GetTargets{ unit } if #targets ~= 1 then return self.ActionPoints end target = targets[1] end local combat_action = target:GetInteractionCombatAction(unit) if not combat_action then return -1 end local args = args or {} args.target = target return combat_action:GetAPCost(unit, args) end, GetActionDescription = function (self, units) local target = self:GetAnyTarget(units) if not target then return self.Description end local closest_unit = ChooseClosestObject(units, target) local combat_action = target:GetInteractionCombatAction(closest_unit) if not combat_action then return self.Description end return T{combat_action:GetActionDescription(closest_unit), target = target, unit = closest_unit} end, GetActionDisplayName = function (self, units) local target = self:GetAnyTarget(units) if not target then return self.DisplayName end local closest_unit = ChooseClosestObject(units, target) local combat_action = target:GetInteractionCombatAction(closest_unit) if not combat_action then return self.Description end return T{combat_action:GetActionDisplayName(closest_unit), target = target, unit = closest_unit} .. T{697667667158, " ()", closest_unit} end, GetActionIcon = function (self, units) local target = self:GetAnyTarget(units) if not target then return self.Icon end local combat_action = target:GetInteractionCombatAction() if not combat_action then return self.Icon end local closest_unit = ChooseClosestObject(units, target) return combat_action:GetActionIcon(closest_unit) end, GetAnyTarget = function (self, units) return self:GetTargets(units)[1] end, GetDefaultTarget = function (self, unit) local best_eval, best_target local targets = self:GetTargets({unit}) if #targets == 0 then return false, false end return targets[1], 100 end, GetTargets = function (self, units) if #units == 0 then return {} end local interactables = false for i = 1, #units do interactables = units[i]:GetReachableObjects("Interactable") if #interactables > 0 then break end end local result = { } for i=1,#interactables do local interactable = interactables[i] local closest_unit = ChooseClosestObject(units, interactable, function(o) local combat_action = interactable:GetInteractionCombatAction(o) assert(not combat_action or combat_action.group == "Interactions", "Interaction CombatActions must be in the 'Interactions' group") if not combat_action or combat_action.ShowIn ~= false then return false end return o:CanInteractWith(interactable, combat_action.id) end) if closest_unit then table.insert(result, interactable) end end return result end, GetUIState = function (self, units, args) local unit = units[1] local target = args and args.target if target then local combat_action = target:GetInteractionCombatAction(unit) if combat_action then return combat_action:GetUIState(units, args) end return "hidden" end if not self:GetAnyTarget(units) then return "hidden" end return "enabled" end, Icon = "UI/Icons/Hud/interact", IdDefault = "Interactdefault", InterruptInExploration = true, IsAimableAttack = false, QueuedBadgeText = T(560853521122, --[[CombatAction Interact QueuedBadgeText]] "INTERACT"), RequireState = "any", Run = function (self, unit, ap, ...) local args = ... local target = args and args.target if not target or HasCombatActionInProgress(unit) then return end local combat_action = target:GetInteractionCombatAction(unit) if not combat_action then return end return combat_action:Run(unit, ap, args) end, ShowIn = false, SortKey = 12, UIBegin = function (self, units, args) CombatActionInteractablesChoice(self, units, args) end, group = "ToDelete", id = "Interact", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(494742880689, --[[CombatAction BuildingConfidence DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_building_confidence", IdDefault = "BuildingConfidencedefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "BuildingConfidence", }) PlaceObj('CombatAction', { ActionPoints = 8000, ActionType = "Ranged Attack", AimType = "cone", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(962244361104, --[[CombatAction BulletHell DisplayName]] ""), Execute = function (self, units, args) local unit = units[1] local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local damage = base return damage, base, damage - base end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.weapon = args.weapon or self:GetAttackWeapons(unit, args) args.num_shots = Clamp(args.weapon.ammo.Amount, self:ResolveValue("min_ammo"), self:ResolveValue("max_ammo")) args.applied_status = { "Suppressed", "SuppressionChangeStance" } args.aoe_fx_action = "WeaponAutoFire" args.aoe_action_id = self.id args.aoe_damage_bonus = MulDivRound(self:ResolveValue("max_ammo_aoe_damage_bonus"), args.num_shots, self:ResolveValue("max_ammo")) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetUIState = function (self, units, args) local unit = units[1] local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "disabled", AttackDisableReasons.RangedWeapon end local canUse, err = unit:CanUseWeapon(weapon1) if not canUse then return "disabled", err end if not table.find(weapon1.AvailableAttacks, "AutoFire") and not table.find(weapon1.AvailableAttacks, "MGBurstFire") then return "disabled", AttackDisableReasons.WrongWeapon end if not weapon1.ammo or weapon1.ammo.Amount < self:ResolveValue("min_ammo") then return "disabled", AttackDisableReasons.OutOfAmmo end local cost = self:GetAPCost(unit, args) if cost < 0 then return "disabled" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_bullet_hell", IdDefault = "BulletHelldefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "min_ammo", 'Value', 15, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "max_ammo", 'Value', 30, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "max_ammo_aoe_damage_bonus", 'Value', 200, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("BulletHell", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "SignatureAbilities", id = "BulletHell", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(496185040855, --[[CombatAction BunsPerk DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_buns_perk", IdDefault = "BunsPerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "BunsPerk", }) PlaceObj('CombatAction', { ActionPointDelta = 1000, ActionPoints = 1000, ActionType = "Ranged Attack", AimType = "cone", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(613567478477, --[[CombatAction DanceForMe DisplayName]] ""), Execute = function (self, units, args) local unit = units[1] local attacks, aim = unit:GetOverwatchAttacksAndAim(self, args) args.num_attacks = attacks args.aim = aim local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return CombatActions.Overwatch.GetAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local damage = base return damage, base, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local total, cost = self:GetAPCost(unit) local attacks = 1 if unit and (cost or -1) >= 0 then attacks = unit:GetOverwatchAttacksAndAim() end local description = GetSignatureActionDescription(self) description = description .. T{452784485986, "Max attacks: ", attacks = attacks} return description end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) return CombatActions.Overwatch.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetOverwatchConeParam("MaxRange") end, GetMinAimRange = function (self, unit, weapon) return weapon:GetOverwatchConeParam("MinRange") end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end local unit = units[1] local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "disabled", AttackDisableReasons.RangedWeapon end if not weapon1.ammo or weapon1.ammo.Amount < 1 then return "disabled", AttackDisableReasons.OutOfAmmo end local cost = self:GetAPCost(unit, args) if not unit:UIHasAP(cost) then return "disabled" end local attack = unit:GetDefaultAttackAction() local state, reason = attack:GetUIState(units, args) return state, reason end, Icon = "UI/Icons/Hud/perk_dance_for_me", IdDefault = "DanceForMedefault", KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("DanceForMe", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "cancel") end, group = "SignatureAbilities", id = "DanceForMe", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(439812642795, --[[CombatAction DangerClose DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_danger_close", IdDefault = "DangerClosedefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "DangerClose", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(224326165572, --[[CombatAction DedicatedCamper DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_dedicated_camper", IdDefault = "DedicatedCamperdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "DedicatedCamper", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(195694738947, --[[CombatAction DesignerExplosives DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_designer_explosives", IdDefault = "DesignerExplosivesdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "DesignerExplosives", }) PlaceObj('CombatAction', { ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(355737085755, --[[CombatAction DoubleTossA DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) local action = {id = "DoubleToss", Description = self.Description} local description = GetSignatureActionDescription(action) local grenadeDescription = CombatActionGrenadeDescription(self, units) description = description .. T(866574791377, "") .. grenadeDescription return description end, GetActionDisplayName = function (self, units) local action = {id = "DoubleToss", DisplayName = self.DisplayName} local name = GetSignatureActionDisplayName(action) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then name = name .. T{279081600107, " ", name = weapon.DisplayName} end end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) local grenade = self:GetAttackWeapons(unit) local target = ResolveGrenadeTargetPos(args.target) if not target or not grenade then return {} end -- get target_offset from DoubleTossA always (the function can be called for other actions) local target_offset = CombatActions.DoubleTossA:ResolveValue("target_offset") local offset = MulDivRound(grenade.AreaOfEffect * const.SlabSizeX, target_offset, 100) if grenade.coneShaped then offset = offset / DivRound(360, grenade.coneAngle) end local offset_dir = RotateRadius(offset, CalcOrientation(args.step_pos or unit, target) + 90*60) local args_arr = {} args_arr[1] = table.copy(args) args_arr[2] = table.copy(args) args_arr[1].target = target + offset_dir args_arr[2].target = target - offset_dir if args.explosion_pos then args_arr[1].explosion_pos = args.explosion_pos[1] args_arr[2].explosion_pos = args.explosion_pos[2] end local attacks = {} for i = 1, 2 do local results, attack_args = CombatActions.ThrowGrenadeA.GetActionResults(self, unit, args_arr[i]) attacks[i] = results attacks[i].attack_args = attack_args end local results = MergeAttacks(attacks) return results, attacks[1].attack_args end, GetAttackWeapons = function (self, unit, args) return unit:GetItemInSlot("Handheld A", "Grenade", 1, 1) end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetMaxAimRange(unit) end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit) if not weapon1 or weapon1.Amount < 2 then return "disabled", AttackDisableReasons.OutOfAmmo end local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_double_toss", IdDefault = "DoubleTossAdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "target_offset", 'Value', 75, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("DoubleToss", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "SignatureAbilities", id = "DoubleTossA", }) PlaceObj('CombatAction', { ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(355737085755, --[[CombatAction DoubleTossB DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) local action = {id = "DoubleToss", Description = self.Description} local description = GetSignatureActionDescription(action) local grenadeDescription = CombatActionGrenadeDescription(self, units) description = description .. T(866574791377, "") .. grenadeDescription return description end, GetActionDisplayName = function (self, units) local action = {id = "DoubleToss", DisplayName = self.DisplayName} local name = GetSignatureActionDisplayName(action) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then name = name .. T{279081600107, " ", name = weapon.DisplayName} end end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.DoubleTossA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) return unit:GetItemInSlot("Handheld A", "Grenade", 2, 1) end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetMaxAimRange(unit) end, GetUIState = function (self, units, args) return CombatActions.DoubleTossA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_double_toss", IdDefault = "DoubleTossBdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "target_offset", 'Value', 75, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("DoubleToss", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "SignatureAbilities", id = "DoubleTossB", }) PlaceObj('CombatAction', { ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(355737085755, --[[CombatAction DoubleTossC DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) local action = {id = "DoubleToss", Description = self.Description} local description = GetSignatureActionDescription(action) local grenadeDescription = CombatActionGrenadeDescription(self, units) description = description .. T(866574791377, "") .. grenadeDescription return description end, GetActionDisplayName = function (self, units) local action = {id = "DoubleToss", DisplayName = self.DisplayName} local name = GetSignatureActionDisplayName(action) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then name = name .. T{279081600107, " ", name = weapon.DisplayName} end end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.DoubleTossA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) return unit:GetItemInSlot("Handheld B", "Grenade", 1, 1) end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetMaxAimRange(unit) end, GetUIState = function (self, units, args) return CombatActions.DoubleTossA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_double_toss", IdDefault = "DoubleTossCdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "target_offset", 'Value', 75, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("DoubleToss", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "SignatureAbilities", id = "DoubleTossC", }) PlaceObj('CombatAction', { ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "parabola aoe", AlwaysHits = true, ConfigurableKeybind = false, DisplayName = T(355737085755, --[[CombatAction DoubleTossD DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) local base = unit:GetBaseDamage(weapon) local bonus = GetGrenadeDamageBonus(unit) return MulDivRound(base, Max(0, 100 + bonus), 100) end, GetActionDescription = function (self, units) local action = {id = "DoubleToss", Description = self.Description} local description = GetSignatureActionDescription(action) local grenadeDescription = CombatActionGrenadeDescription(self, units) description = description .. T(866574791377, "") .. grenadeDescription return description end, GetActionDisplayName = function (self, units) local action = {id = "DoubleToss", DisplayName = self.DisplayName} local name = GetSignatureActionDisplayName(action) local unit = units[1] if unit then local weapon = self:GetAttackWeapons(unit) if weapon then name = name .. T{279081600107, " ", name = weapon.DisplayName} end end return name end, GetActionIcon = function (self, units) return GetThrowItemIcon(self, units and units[1]) end, GetActionResults = function (self, unit, args) return CombatActions.DoubleTossA.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) return unit:GetItemInSlot("Handheld B", "Grenade", 2, 1) end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetMaxAimRange(unit) end, GetUIState = function (self, units, args) return CombatActions.DoubleTossA.GetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_double_toss", IdDefault = "DoubleTossDdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamPercent', { 'Name', "target_offset", 'Value', 75, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("DoubleToss", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim") end, group = "SignatureAbilities", id = "DoubleTossD", }) PlaceObj('CombatAction', { ActionPoints = 4000, ActionType = "Melee Attack", AimType = "melee", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(484685867041, --[[CombatAction ExplodingPalm DisplayName]] ""), EvalTarget = function (self, units, target, args) return CombatActions.MeleeAttack.EvalTarget(self, units, target, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end if not args.goto_pos then args.goto_pos = unit:GetClosestMeleeRangePos(args.target) end args.stance = "Standing" CombatActionExecuteWithMove(self, unit, args) end, GetAPCost = function (self, unit, args) return CombatActions.MeleeAttack.GetAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, base, damage - base end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 args.applied_status = {"Unconscious"} local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) return CombatActions.MeleeAttack.GetAnyTarget(self, units) end, GetAttackWeapons = function (self, unit, args) return not unit:GetActiveWeapons() and unit:GetActiveWeapons("UnarmedWeapon") end, GetDefaultTarget = function (self, unit) return CombatActions.MeleeAttack.GetDefaultTarget(self, unit) end, GetTargets = function (self, units) return CombatActions.MeleeAttack.GetTargets(self, units) end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end local unit = units[1] local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end if not self:GetAttackWeapons(unit) then return "disabled", AttackDisableReasons.RequiresUnarmed end args = args or {} args.ap_cost_breakdown = args.ap_cost_breakdown or {} local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost, self.id, args) then return "disabled", GetUnitNoApReason(unit) end return "enabled" end, Icon = "UI/Icons/Hud/perk_exploding_palm", IdDefault = "ExplodingPalmdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectSignatureAbility", MoveStep = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("ExplodingPalm", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) return CombatActions.MeleeAttack.UIBegin(self, units, args) end, UseFreeMove = true, group = "SignatureAbilities", id = "ExplodingPalm", }) PlaceObj('CombatAction', { ActionPointDelta = 1000, ActionPoints = 1000, ActionType = "Ranged Attack", ActivePauseBehavior = "queue", AimType = "cone", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(412689699778, --[[CombatAction EyesOnTheBack DisplayName]] "Eyes On The Back"), Execute = function (self, units, args) local unit = units[1] local attacks, aim = unit:GetOverwatchAttacksAndAim(self, args) args.num_attacks = attacks args.aim = aim local ap = self:GetAPCost(unit, args) NetStartCombatAction(self.id, unit, ap, args) end, GetAPCost = function (self, unit, args) return CombatActions.Overwatch.GetAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local damage = base return damage, base, damage - base end, GetActionDescription = function (self, units) local unit = units[1] local total, cost = self:GetAPCost(unit) local attacks = 1 if unit and (cost or -1) >= 0 then attacks = unit:GetOverwatchAttacksAndAim() end local description = GetSignatureActionDescription(self) description = description .. T{452784485986, "Max attacks: ", attacks = attacks} return description end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) return CombatActions.Overwatch.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon:GetOverwatchConeParam("MaxRange") end, GetMinAimRange = function (self, unit, weapon) return weapon:GetOverwatchConeParam("MinRange") end, GetUIState = function (self, units, args) local unit = units[1] local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end local cost = self:GetAPCost(unit, args) if not unit:UIHasAP(cost) then return "disabled" end local attack = unit:GetDefaultAttackAction() local weapon = attack:GetAttackWeapons(unit) if not IsKindOf(weapon, "Firearm") then return "disabled", AttackDisableReasons.RangedWeapon elseif IsKindOf(weapon, "MachineGun") then return "disabled", AttackDisableReasons.WrongWeapon end local state, reason = attack:GetUIState(units, args) return state, reason end, Icon = "UI/Icons/Hud/perk_eyes_on_the_back", IdDefault = "EyesOnTheBackdefault", KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, QueuedBadgeText = T(633993012942, --[[CombatAction EyesOnTheBack QueuedBadgeText]] "OVERWATCH"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("EyesOnTheBack", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAreaAim", "cancel") end, group = "SignatureAbilities", id = "EyesOnTheBack", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(719706520870, --[[CombatAction FleetingShadow DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_fleeting_shadow", IdDefault = "FleetingShadowdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "FleetingShadow", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(321475789120, --[[CombatAction FoxPerk DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_fox_perk", IdDefault = "FoxPerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "FoxPerk", }) PlaceObj('CombatAction', { ActionPointDelta = 3000, ActionPoints = 7000, ActionType = "Melee Attack", AimType = "melee-charge", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(499085123933, --[[CombatAction GloryHog DisplayName]] ""), EvalTarget = function (self, units, target, args) local unit = units[1] if (not args or not args.goto_pos) and (not IsValid(target) or not unit:GetClosestMeleeRangePos(target)) then return 0 end return unit:CalcChanceToHit(target, self, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end if not args.goto_pos then args.goto_pos = unit:GetClosestMeleeRangePos(args.target) end args.available_move_ap = self:ResolveValue("move_ap") * const.Scale.AP CombatActionExecuteWithMove(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, base, damage - base end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 -- distance-based bonus damage local max_dist = self:ResolveValue("max_damage_dist") * const.SlabSizeX local max_bonus = self:ResolveValue("max_damage_bonus") local dist = Min(unit:GetDist(args.target), max_dist) args.damage_bonus = MulDivRound(max_bonus, dist, max_dist) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetOneAttackableEnemy(self, unit, nil, CombatActionTargetFilters.Charge, unit, move_ap, self.id) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("MeleeWeapon") end, GetDefaultTarget = function (self, unit) local best_eval, best_target local units = {unit} local targets = self:GetTargets(units) for _, target in ipairs(targets or empty_table) do local cost = self:GetAPCost(unit, { target = target }) if unit:UIHasAP(cost) then local eval = self:EvalTarget(units, target) if not best_eval or eval > best_eval then best_target, best_eval = target, eval end end end return best_target, best_eval end, GetTargets = function (self, units) local unit = units[1] local move_ap = self:ResolveValue("move_ap") * const.Scale.AP return CombatActionGetAttackableEnemies(self, unit, nil, CombatActionTargetFilters.Charge, unit, move_ap, self.id) end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "disabled", AttackDisableReasons.MacheteWeapon end local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end if unit.stance ~= "Standing" then return "disabled", AttackDisableReasons.OnlyStanding end local ap = self:GetAPCost(unit, args) if not unit:UIHasAP(ap) then return "disabled", GetUnitNoApReason(unit) end if not self:GetAnyTarget(units) then return "disabled", AttackDisableReasons.NoTarget end return "enabled" end, Icon = "UI/Icons/Hud/perk_glory_hog", IdDefault = "GloryHogdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "move_ap", 'Value', 10, 'Tag', "", }), PlaceObj('PresetParamPercent', { 'Name', "max_damage_bonus", 'Value', 50, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "max_damage_dist", 'Value', 6, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "tempHp", 'Value', 15, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "minAngle", 'Value', 120, 'Tag', "", }), }, RequireState = "any", RequireTargets = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("GloryHogCharge", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatCharge") end, group = "SignatureAbilities", id = "GloryHog", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(845495135915, --[[CombatAction GrizzlyPerk DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDamage = function (self, unit, target, args) return CombatActions.MGBurstFire:GetActionDamage(unit, target, args) end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) return CombatActions.MGBurstFire.GetActionResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("MachineGun") end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "disabled", AttackDisableReasons.RequiresMachineGun end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_grizzly_perk", IdDefault = "GrizzlyPerkdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "rechargeTime", 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "num_shots", 'Value', 8, 'Tag', "", }), PlaceObj('PresetParamPercent', { 'Name', "cth_loss_per_shot", 'Tag', "%", }), PlaceObj('PresetParamPercent', { 'Name', "dmg_penalty", 'Value', -50, 'Tag', "%", }), PlaceObj('PresetParamNumber', { 'Name', "min_shots", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("GrizzlyPerk", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "SignatureAbilities", id = "GrizzlyPerk", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(310989078300, --[[CombatAction GruntyPerk DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_grunty_perk", IdDefault = "GruntyPerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "GruntyPerk", }) PlaceObj('CombatAction', { ActionType = "Toggle", ActivePauseBehavior = "instant", Comment = "toggle", ConfigurableKeybind = false, DisplayName = T(744263226529, --[[CombatAction HaveABlast DisplayName]] ""), GetActionDescription = function (self, units) local unit = units[1] local enabled = self:IsToggledOn(unit) local description = GetSignatureActionDescription(self) description = description .. T{237402056493, "", status = enabled and T(685311187808, "Active") or T(183198352286, --[[Perk or skill that can be disabled]] "Inactive")} return description end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_have_a_blast", IdDefault = "HaveABlastdefault", IsToggledOn = function (self, unit) return unit and unit:GetEffectValue("HaveABlast") or false end, KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetEffectValue("HaveABlast", not unit:GetEffectValue("HaveABlast")) ObjModified("combat_bar") return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "HaveABlast", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(710075976136, --[[CombatAction HawksEye DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_hawks_eye", IdDefault = "HawksEyedefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "HawksEye", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPointDelta = 3000, ActionPoints = 6000, ActionType = "Ranged Attack", AimType = "mobile", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(401408176064, --[[CombatAction HundredKnives DisplayName]] ""), GetAPCost = function (self, unit, args) return unit:GetAttackAPCost(self, self:GetAttackWeapons(unit, args), unit.ActionPoint) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local damage = unit:GetBaseDamage(weapon) return damage, damage, 0 end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) args.attack_id = "KnifeThrow" return GetMobileShotResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) return CombatActions.KnifeThrow:GetAttackWeapons(unit, args) end, GetMaxAimRange = function (self, unit, weapon) return CombatActions.KnifeThrow:GetMaxAimRange(unit, weapon) end, GetTargets = function (self, units) local unit = units[1] if unit then return GetEnemies(unit) end return {} end, GetUIState = function (self, units, args) local unit = units[1] local weapon1 = self:GetAttackWeapons(unit, args) if not weapon1 then return "disabled", AttackDisableReasons.KnifeWeapon end if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_hundred_knives", IdDefault = "HundredKnivesdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "mobile_move_ap", 'Value', 8, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "mobile_num_shots", 'Value', 2, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("HundredKnives", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatMovingAttack") end, group = "SignatureAbilities", id = "HundredKnives", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(916379475854, --[[CombatAction IcePerk DisplayName]] ""), GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if unit:OutOfAmmo(weapon2) or unit:IsWeaponJammed(weapon2) then weapon2 = nil end if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0, self.ActionPointDelta) or -1 end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) args.anim_speed_mod = self:ResolveValue("anim_speed_mod") local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not IsKindOf(weapon, "Firearm") then return "disabled", AttackDisableReasons.RangedWeapon elseif IsKindOf(weapon, "Shotgun") then return "disabled", AttackDisableReasons.WrongWeapon end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_ice_perk", IdDefault = "IcePerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "anim_speed_mod", 'Value', 100000, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) PlayVoiceResponse(unit,"PersonalPerkSubtitled") unit:SetActionCommand("IceAttack", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "SignatureAbilities", id = "IcePerk", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(671287745703, --[[CombatAction InnerInfo DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_inners_info", IdDefault = "InnerInfodefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "InnerInfo", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(985157282912, --[[CombatAction JackOfAllTrades DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_jack_of_all_trades", IdDefault = "JackOfAllTradesdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "JackOfAllTrades", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(978989937559, --[[CombatAction KalynaPerk DisplayName]] ""), GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if unit:OutOfAmmo(weapon2) or unit:IsWeaponJammed(weapon2) then weapon2 = nil end if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0, self.ActionPointDelta) or -1 end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not IsKindOf(weapon, "Firearm") then return "disabled", AttackDisableReasons.RangedWeapon elseif IsKindOf(weapon, "Shotgun") then return "disabled", AttackDisableReasons.WrongWeapon end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_kalyna_perk", IdDefault = "KalynaPerkdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("KalynaShot", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "SignatureAbilities", id = "KalynaPerk", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(623714906755, --[[CombatAction KillingWind DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_killing_wind", IdDefault = "KillingWinddefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "KillingWind", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(620581238795, --[[CombatAction LightStep DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_light_step", IdDefault = "LightStepdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "LightStep", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(958256324680, --[[CombatAction MakeThemBleed DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_make_them_bleed", IdDefault = "MakeThemBleeddefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "MakeThemBleed", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(247936211509, --[[CombatAction NailsPerk DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_nails_perk", IdDefault = "NailsPerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "NailsPerk", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(154407600177, --[[CombatAction NaturalHealing DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_natural_healing", IdDefault = "NaturalHealingdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "NaturalHealing", }) PlaceObj('CombatAction', { ActionPoints = 2000, ActivePauseBehavior = "queue", ConfigurableKeybind = false, DisplayName = T(800932587379, --[[CombatAction Nazdarovya DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end if not unit:UIHasAP(cost) then return "disabled" end local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end return "enabled" end, Icon = "UI/Icons/Hud/perk_nazdarovya", IdDefault = "Nazdarovyadefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "tempHp", 'Value', 25, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, QueuedBadgeText = T(975564787824, --[[CombatAction Nazdarovya QueuedBadgeText]] "DRINK"), RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("Nazdarovya", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "Nazdarovya", }) PlaceObj('CombatAction', { ActionPoints = 10000, ActionType = "Ranged Attack", AimType = "allies-attack", ConfigurableKeybind = false, DisplayName = T(851787911979, --[[CombatAction OnMyTarget DisplayName]] ""), GetAPCost = function (self, unit, args) if self.CostBasedOnWeapon then local weapon = self:GetAttackWeapons(unit, args) return weapon and unit:GetAttackAPCost(self, weapon, nil, args and args.aim or 0, self.ActionPointDelta) or -1 end return self.ActionPoints end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local args = table.copy(args) if not args.target_spot_group then args.num_shots = 0 end args.fx_action = "WeaponBuckshot" args.aoe_action_id = self.id args.aoe_damage_type = "percent" args.aoe_damage_value = const.Weapons.ShotgunCollateralDamage local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) -- make sure the fx action plays local shots = results.attacks and results.attacks[1].shots or empty_table if #shots == 0 then attack_args.aoe_fx_action = "WeaponBuckshot" end return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetMaxAimRange = function (self, unit, weapon) return weapon.WeaponRange end, GetMinAimRange = function (self, unit, weapon) return 2 end, GetTargets = function (self, units) local unit = units[1] return unit and unit:GetVisibleEnemies() or {} --return CombatActionGetAttackableEnemies(self, unit) end, GetUIState = function (self, units, args) return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_on_my_target", IdDefault = "OnMyTargetdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), }, RequireState = "any", RequireTargets = true, Run = function (self, unit, ap, ...) unit:SetActionCommand("OnMyTarget", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args) end, group = "SignatureAbilities", id = "OnMyTarget", }) PlaceObj('CombatAction', { ActionCamera = true, ActionPoints = 5000, ActionType = "Ranged Attack", AimType = "mobile", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(586171031351, --[[CombatAction RecklessAssault DisplayName]] ""), GetAPCost = function (self, unit, args) local add = (unit.stance ~= "Standing") and CombatActions.StanceStanding:GetAPCost(unit, args) or 0 return self.ActionPoints + add end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local damage = unit:GetBaseDamage(weapon) local num_shots = self:ResolveValue("mobile_num_shots") return damage, damage / num_shots, 0 end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local weapon = self:GetAttackWeapons(unit) args.attack_id = "BurstFire" args.num_shots = weapon and weapon:GetAutofireShots("BurstFire") or CombatActions.BurstFire:ResolveValue("num_shots") args.multishot = true return GetMobileShotResults(self, unit, args) end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("SubmachineGun") end, GetTargets = function (self, units) local unit = units[1] if unit then return table.ifilter(GetEnemies(unit), function(i, enemy) return IsValidTarget(enemy) end) end return {} end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_reckless_assault", IdDefault = "RecklessAssaultdefault", IsAimableAttack = false, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "mobile_move_ap", 'Value', 12, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "mobile_num_shots", 'Value', 5, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("RecklessAssault", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatMovingAttack") end, group = "SignatureAbilities", id = "RecklessAssault", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(208299655551, --[[CombatAction SecondStoryMan DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_second_man_story", IdDefault = "SecondStoryMandefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "SecondStoryMan", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(935618242248, --[[CombatAction ShoulderToShoulder DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_shoulder_to_shoulder", IdDefault = "ShoulderToShoulderdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "ShoulderToShoulder", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(943208194004, --[[CombatAction SidneyPerk DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_sidney_perk", IdDefault = "SidneyPerkdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "SidneyPerk", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(558757719170, --[[CombatAction Spotter DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_spotter", IdDefault = "Spotterdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "Spotter", }) PlaceObj('CombatAction', { ActionPoints = 3000, ActionType = "Melee Attack", AimType = "melee", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(208002846501, --[[CombatAction SteroidPunch DisplayName]] ""), EvalTarget = function (self, units, target, args) return CombatActions.MeleeAttack.EvalTarget(self, units, target, args) end, Execute = function (self, units, args) local unit = units[1] if CombatActionIsBusy(self, unit) then return end if not args.goto_pos then args.goto_pos = unit:GetClosestMeleeRangePos(args.target) end args.stance = "Standing" CombatActionExecuteWithMove(self, unit, args) end, GetAPCost = function (self, unit, args) return GetMeleeAttackAPCost(self, unit, args) end, GetActionDamage = function (self, unit, target, args) local weapon = self:GetAttackWeapons(unit, args) if not weapon then return 0 end local base = unit:GetBaseDamage(weapon) local mod = 100 + MulDivRound(unit.Strength, weapon.DamageMultiplier, 100) local damage = MulDivRound(base, mod, 100) return damage, base, damage - base end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local args = table.copy(args) args.num_shots = 0 local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAnyTarget = function (self, units) return CombatActions.MeleeAttack.GetAnyTarget(self, units) end, GetAttackWeapons = function (self, unit, args) return not unit:GetActiveWeapons() and unit:GetActiveWeapons("UnarmedWeapon") end, GetDefaultTarget = function (self, unit) return CombatActions.MeleeAttack.GetDefaultTarget(self, unit) end, GetTargets = function (self, units) return CombatActions.MeleeAttack.GetTargets(self, units) end, GetUIState = function (self, units, args) if not g_Combat then return "disabled", AttackDisableReasons.CombatOnly end local unit = units[1] local recharge = unit:GetSignatureRecharge(self.id) if recharge then if recharge.on_kill then return "disabled", AttackDisableReasons.SignatureRechargeOnKill end return "disabled", AttackDisableReasons.SignatureRecharge end if not self:GetAttackWeapons(unit) then return "disabled", AttackDisableReasons.RequiresUnarmed end args = args or {} args.ap_cost_breakdown = args.ap_cost_breakdown or {} local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled", GetUnitNoApReason(unit) end return "enabled" end, Icon = "UI/Icons/Hud/perk_steroid_punch", IdDefault = "SteroidPunchdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectSignatureAbility", MoveStep = true, MultiSelectBehavior = "first", Parameters = { PlaceObj('PresetParamNumber', { 'Name', "recharge_on_kill", 'Value', 1, 'Tag', "", }), PlaceObj('PresetParamNumber', { 'Name', "pushSlabs", 'Value', 1, 'Tag', "", }), }, RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("SteroidPunch", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) return CombatActions.MeleeAttack.UIBegin(self, units, args) end, UseFreeMove = true, group = "SignatureAbilities", id = "SteroidPunch", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(378289237011, --[[CombatAction TagTeam DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_tag_team", IdDefault = "TagTeamdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "TagTeam", }) PlaceObj('CombatAction', { ActionCamera = true, ActionType = "Ranged Attack", AimType = "line", ConfigurableKeybind = false, CostBasedOnWeapon = true, DisplayName = T(644784782885, --[[CombatAction TheGrim DisplayName]] ""), GetAPCost = function (self, unit, args) local weapon1, weapon2 = self:GetAttackWeapons(unit, args) if unit:OutOfAmmo(weapon2) or unit:IsWeaponJammed(weapon2) then weapon2 = nil end if weapon1 and weapon2 then return -1 end if not weapon1 then return -1 end return unit:GetAttackAPCost(self, weapon1, false, args and args.aim or 0, self.ActionPointDelta) or -1 end, GetActionDamage = function (self, unit, target, args) return CombatActionsAttackGenericDamageCalculation(self, unit, args) end, GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetActionResults = function (self, unit, args) local attack_args = unit:PrepareAttackArgs(self.id, args) local results = attack_args.weapon:GetAttackResults(self, attack_args) return results, attack_args end, GetAttackWeapons = function (self, unit, args) if args and args.weapon then return args.weapon end return unit:GetActiveWeapons("Firearm") end, GetUIState = function (self, units, args) local unit = units[1] local weapon = self:GetAttackWeapons(unit, args) if not IsKindOf(weapon, "Firearm") then return "disabled", AttackDisableReasons.RangedWeapon elseif IsKindOf(weapon, "Shotgun") then return "disabled", AttackDisableReasons.WrongWeapon end return CombatActionGenericAttackGetUIState(self, units, args) end, Icon = "UI/Icons/Hud/perk_the_grim", IdDefault = "TheGrimdefault", IsTargetableAttack = true, KeybindingFromAction = "actionRedirectSignatureAbility", MultiSelectBehavior = "first", RequireState = "any", Run = function (self, unit, ap, ...) unit:SetActionCommand("FirearmAttack", self.id, ap, ...) end, ShowIn = "SignatureAbilities", SortKey = 100, StealthAttack = true, UIBegin = function (self, units, args) CombatActionAttackStart(self, units, args, "IModeCombatAttack") end, group = "SignatureAbilities", id = "TheGrim", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(708228622280, --[[CombatAction VengefulTemperament DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_vengeful_temperament", IdDefault = "VengefulTemperamentdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "VengefulTemperament", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(824128303298, --[[CombatAction WeGotThis DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_we_got_this", IdDefault = "WeGotThisdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "WeGotThis", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(598595177377, --[[CombatAction WeaponPersonalization DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_weapon_personalization", IdDefault = "WeaponPersonalizationdefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "WeaponPersonalization", }) PlaceObj('CombatAction', { ActionType = "Passive", ActivePauseBehavior = "instant", Comment = "passive", ConfigurableKeybind = false, DisplayName = T(193034765297, --[[CombatAction YouSeeIgor DisplayName]] ""), GetActionDescription = function (self, units) return GetSignatureActionDescription(self) end, GetActionDisplayName = function (self, units) return GetSignatureActionDisplayName(self) end, GetUIState = function (self, units, args) local unit = units[1] local cost = self:GetAPCost(unit, args) if cost < 0 then return "hidden" end if not unit:UIHasAP(cost) then return "disabled" end return "enabled" end, Icon = "UI/Icons/Hud/perk_you_see_igor", IdDefault = "YouSeeIgordefault", KeybindingFromAction = "actionRedirectSignatureAbility", RequireState = "any", Run = function (self, unit, ap, ...) return false end, ShowIn = "SignatureAbilities", SortKey = 100, group = "SignatureAbilities", id = "YouSeeIgor", })