-- ========== GENERATED BY AIArchetype Editor DO NOT EDIT MANUALLY! ========== PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 600, }), PlaceObj('AIPolicyWeaponRange', { 'Weight', 300, 'RangeBase', "Absolute", 'RangeMin', 2, 'RangeMax', 2, }), }, }), }, MoveStance = "", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Absolute", 'RangeMin', 2, 'RangeMax', 2, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "", TargetChangePolicy = "restart", group = "Beasts", id = "Beast_Crocodile", }) PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 600, }), }, 'SignatureActions', { PlaceObj('AIActionHyenaCharge', nil), }, }), }, MoveStance = "", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Melee", 'RangeMin', 1000, 'RangeMax', 1000, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "", SignatureActions = { PlaceObj('AIActionCharge', { 'BiasId', "HyenaCharge", 'Weight', 200, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "HyenaCharge", 'Effect', "disable", }), }, 'ForbiddenInState', set( "RainHeavy" ), 'DestPreference', "nearest", }), }, TargetChangePolicy = "restart", group = "Beasts", id = "Beast_Hyena", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if g_Encounter then return g_Encounter:EnumDestsInAssignedArea(unit, context) end end, 'PickEndTurnPolicies', function (self, unit, context, debug_data) if g_Encounter then return g_Encounter:SelectEndTurnPolicies(unit, context) end end, 'PickOptimalLoc', function (self, unit, context, debug_data) if g_Encounter then return g_Encounter:FindOptimalLocationInAssignedArea(unit, context) end end, 'PickEndTurnLoc', function (self, unit, context, debug_data) AITacticCalcPathDistances(unit, context, "disable bias") local policies = self:PickEndTurnPolicies(unit, context) or self.EndTurnPolicies context.ai_destination = AIScoreReachableVoxels(context, policies, self.OptLocWeight, nil, "prefer") return true end, 'SelectSignatureActions', function (self, unit, context, debug_data) if g_Encounter then return g_Encounter:SelectSignatureActions(unit, context) end end, }), }, Comment = "generic implementation, try reaching assigned area, valid positions are in assigned & current areas only", group = "BossFights", id = "Bossfight_GuardArea", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'turn_phase', "Early", 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if not g_Encounter then return end return CorazonEnumDestsInAssignedArea(unit, context) end, 'PickOptimalLoc', function (self, unit, context, debug_data) if g_Encounter:GetUnitArea(unit) == g_Encounter.areaFinalRoom then context.best_dest = GetPackedPosAndStance(unit) return true end local _, positions = CorazonGetAreaMarkerPositions(g_Encounter.areaFinalRoom) positions = positions or empty_table if #positions > 0 then local goto_pos = table.interaction_rand(positions, "Behavior") local x, y, z = point_unpack(goto_pos) context.best_dest = stance_pos_pack(x, y, z, StancesList.Standing) return true end end, 'PickEndTurnLoc', function (self, unit, context, debug_data) CorazonCalcPathDistances(unit, context, "disable bias") end, }), }, group = "BossFights", id = "Corazon_BossRetreating", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if not g_Encounter then return end return CorazonEnumDestsInAssignedArea(unit, context) end, 'PickEndTurnPolicies', function (self, unit, context, debug_data) if IsKindOf(g_Encounter, "BossfightCorazon") then return g_Encounter:SelectEndTurnPolicies(unit, context) end end, 'PickOptimalLoc', function (self, unit, context, debug_data) return CorazonOptimalLocationInAssignedArea(unit, context) end, 'PickEndTurnLoc', function (self, unit, context, debug_data) CorazonCalcPathDistances(unit, context, "disable bias") local policies = self:PickEndTurnPolicies(unit, context) or self.EndTurnPolicies context.ai_destination = AIScoreReachableVoxels(context, policies, self.OptLocWeight, nil, "prefer") return true end, 'SelectSignatureActions', function (self, unit, context, debug_data) if IsKindOf(g_Encounter, "BossfightCorazon") then return g_Encounter:SelectSignatureActions(unit, context) end end, 'Execute', function (self, unit, context, debug_data) -- todo: set context.force_max_aim if smoke -- todo: swap to shotgun if in close range, out of it if not end, }), }, Comment = "basic logic: move to assigned area, engange enemies in/from it until assigned elsewhere", group = "BossFights", id = "Corazon_GuardArea", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if not g_Encounter then return end return CorazonEnumDestsInAssignedArea(unit, context) end, 'PickOptimalLoc', function (self, unit, context, debug_data) return CorazonOptimalLocationInAssignedArea(unit, context) end, 'PickEndTurnLoc', function (self, unit, context, debug_data) CorazonCalcPathDistances(unit, context, "disable bias") end, 'SelectSignatureActions', function (self, unit, context, debug_data) if IsKindOf(g_Encounter, "BossfightCorazon") then return g_Encounter:SelectSignatureActions(unit, context) end end, 'Execute', function (self, unit, context, debug_data) -- todo: set context.force_max_aim if smoke -- todo: swap to shotgun if in close range, out of it if not end, }), }, Comment = "right corridor kite back - similar to GuardArea but maybe more defensive?", group = "BossFights", id = "Corazon_KiteBack", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if not g_Encounter then return end return CorazonEnumDestsInAssignedArea(unit, context) end, 'EvalDamageScore', function (self, unit, context, debug_data) context.max_attacks = 1 AIPrecalcDamageScore(context) end, 'PickOptimalLoc', function (self, unit, context, debug_data) return CorazonOptimalLocationInAssignedArea(unit, context) end, 'PickEndTurnLoc', function (self, unit, context, debug_data) -- we need 2 destinations: attack one and end turn one local hide_dests = {} local attack_dest, attack_score, scoot_ap for _, dest in ipairs(context.destinations) do if not g_AIDestEnemyLOSCache[dest] then hide_dests[#hide_dests+1] = dest else local score = context.dest_target_score or 0 if score > 0 then -- modify score by remaining ap at dest local move_stance_idx = context.dest_combat_path[dest] local cpath = context.combat_paths[move_stance_idx] local ap_at_dest = cpath:GetAP(dest) score = MulDivRound(score, ap_at_dest, Max(1, unit.ActionPoints)) if score > (attack_score or 0) then attack_dest, attack_score, scoot_ap = dest, score, ap_at_dest - context.default_attack_cost end end end end if not attack_dest or #hide_dests < 1 or not scoot_ap then return end -- pick a reachable hiding dest local x, y, z, stance_idx = stance_pos_unpack(attack_dest) local atk_pos = point(x, y, z) local combat_path = CombatPath:new() local weights, total_weight = {}, 0 local hide_dest combat_path:RebuildPaths(unit, scoot_ap, atk_pos, StancesList[stance_idx]) for i, dest in ipairs(hide_dests) do local x, y, z = stance_pos_unpack(dest) local pos = point_pack(x, y, z) local ap = combat_path:GetAP(pos) weights[i] = ap total_weight = total_weight + ap end if total_weight < 1 then return end local roll = unit:Random(total_weight) for i = 1, #hide_dests - 1 do local w = weights[i] if w >= roll then hide_dest = hide_dests[i] break end end context.ai_destination = attack_dest context.scoot_and_shoot_dest = hide_dest or hide_dests[#hide_dests] DoneObject(combat_path) return true end, 'SelectSignatureActions', function (self, unit, context, debug_data) if IsKindOf(g_Encounter, "BossfightCorazon") then return g_Encounter:SelectSignatureActions(unit, context) end end, 'Execute', function (self, unit, context, debug_data) if not context.scoot_and_shoot_dest then return end -- do a single attack, then scoot to the hidey place local dest = GetPackedPosAndStance(unit) AIPrecalcDamageScore(context, {dest}) local target = (context.dest_target or empty_table)[dest] local args = { target = target }--, voiceResponse = voice_response } AIPlayCombatAction(context.default_attack.id, unit, nil, args) -- recalc path to make sure the position is free local x, y, z = stance_pos_unpack(context.scoot_and_shoot_dest) local combat_path = CombatPath:new() combat_path:RebuildPaths(unit) if not combat_path:GetAP(point_pack(x, y, z)) then local hide_dests = {} for _, dest in ipairs(context.destinations) do local x, y, z = stance_pos_unpack(dest) if not g_AIDestEnemyLOSCache[dest] and combat_path:GetAP(point_pack(x, y, z)) then hide_dests[#hide_dests+1] = dest end end if #hide_dests > 0 then local dest = table.interaction_rand(hide_dests, "Behavior") local x, y, z = stance_point_unpack(dest) AIPlayCombatAction("Move", unit, unit.ActionPoints, { goto_pos = point(x, y, z) }) else AIPlayAttacks(unit, context) end else AIPlayCombatAction("Move", unit, unit.ActionPoints, { goto_pos = point(x, y, z) }) end AITakeCover(unit) DoneObject(combat_path) return "done" end, }), }, Comment = "right corridor shoot-and-scoot action", group = "BossFights", id = "Corazon_ShootAndScoot", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'turn_phase', "Early", 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if g_TacticalMap then return g_TacticalMap:EnumDestsInAssignedArea(unit, context) end end, 'PickOptimalLoc', function (self, unit, context, debug_data) if g_TacticalMap then return g_TacticalMap:FindOptimalLocationInAssignedArea(unit, context) end end, 'PickEndTurnLoc', function (self, unit, context, debug_data) AITacticCalcPathDistances(unit, context, "disable bias") local policies = self:PickEndTurnPolicies(unit, context) or self.EndTurnPolicies context.ai_destination = AIScoreReachableVoxels(context, policies, self.OptLocWeight, nil, "avoid") return true end, 'SelectSignatureActions', function (self, unit, context, debug_data) return AITacticSelectSignatureActions(unit, context) end, }), }, group = "BossFights", id = "Faucheaux_BossRetreating", }) PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 50, }), PlaceObj('AIPolicyProximity', { 'MinScore', 12, }), PlaceObj('AIPolicyLosToEnemy', { 'Invert', true, }), }, }), }, MoveStance = "Crouch", OptLocPolicies = { PlaceObj('AIPolicyProximity', { 'MinScore', 12, }), PlaceObj('AIPolicyLosToEnemy', { 'Invert', true, }), PlaceObj('AIPolicyTakeCover', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", group = "Civilian", id = "ActiveCivilian", }) PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('HoldPositionAI', { 'Priority', true, }), PlaceObj('StandardAI', { 'Weight', 0, }), }, MoveStance = "Crouch", OptLocPolicies = { PlaceObj('AIPolicyTakeCover', nil), }, PrefStance = "Crouch", group = "Civilian", id = "AnimTestDummy_Crouched", }) PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('HoldPositionAI', { 'Priority', true, }), PlaceObj('StandardAI', { 'Weight', 0, }), }, MoveStance = "Prone", OptLocPolicies = { PlaceObj('AIPolicyTakeCover', nil), }, PrefStance = "Prone", group = "Civilian", id = "AnimTestDummy_Prone", }) PlaceObj('AIArchetype', { BaseAttackWeight = 0, Behaviors = { PlaceObj('HoldPositionAI', { 'Priority', true, }), PlaceObj('StandardAI', { 'Weight', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyTakeCover', nil), }, group = "Civilian", id = "AnimTestDummy_Standing", }) PlaceObj('AIArchetype', { BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), PlaceObj('AIPolicyProximity', { 'Weight', 1000, 'MinScore', 10, }), }, 'TakeCoverChance', 100, }), }, OptLocPolicies = { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), PlaceObj('AIPolicyProximity', { 'Weight', 1000, 'MinScore', 10, }), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIActionMobileShot', { 'Weight', 200, 'NotificationText', "", 'action_id', "RunAndGun", }), }, TargetScoreRandomization = 40, group = "Lieutenants", id = "CorazonBoss", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Neck", "Torso" ), BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Melee", 'RangeMin', 0, 'RangeMax', 6, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, SignatureActions = { PlaceObj('AIActionThrowGrenade', { 'BiasId', "StunGrenade", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "StunGrenade", 'Effect', "disable", }), }, 'min_score', 100, 'MinDist', 3000, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), PlaceObj('AIActionCharge', { 'BiasId', "PierreCharge", 'Weight', 300, }), }, TargetChangePolicy = "restart", TargetScoreRandomization = 10, group = "Lieutenants", id = "Pierre", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Arms", "Groin", "Legs", "Torso" ), BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'BiasId', "Standard", 'Weight', 150, 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 50, 'visibility_mode', "team", }), PlaceObj('AIPolicyDealDamage', nil), }, }), }, MoveStance = "Crouch", OptLocPolicies = { PlaceObj('AIPolicyTakeCover', nil), PlaceObj('AIPolicyLosToEnemy', { 'Weight', 300, }), PlaceObj('AIPolicyWeaponRange', { 'Weight', 10, 'RangeMin', 10, 'RangeMax', 25, }), PlaceObj('AIPolicyWeaponRange', { 'Weight', 20, 'RangeMin', 26, 'RangeMax', 49, }), PlaceObj('AIPolicyWeaponRange', { 'RangeMin', 50, 'RangeMax', 100, }), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "PierreGuardLauncherFire", 'Weight', 1000, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "PierreGuardLauncherFire", 'Effect', "disable", 'Period', 10, }), PlaceObj('AIBiasModification', { 'BiasId', "PierreGuardLauncherFire", 'Effect', "disable", 'Value', -50, 'Period', 10, 'ApplyTo', "Team", }), }, 'self_score_mod', -1000, 'min_score', 100, 'MinDist', 5000, }), }, TargetScoreRandomization = 10, group = "Lieutenants", id = "PierreGuard", }) PlaceObj('AIArchetype', { BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'BiasId', "Standard", 'Weight', 80, 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 50, 'visibility_mode', "team", }), PlaceObj('AIPolicyDealDamage', { 'Weight', 400, }), }, }), PlaceObj('StandardAI', { 'Fallback', false, 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'SignatureActions', { PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'Weight', 70, 'Priority', true, 'NotificationText', "", 'action_id', "AutoFire", 'AttackTargeting', set( "Torso" ), }), }, }), }, Comment = "basic flank and attack AI", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'Weight', 600, 'RangeBase', "Absolute", 'RangeMin', 4, 'RangeMax', 8, }), PlaceObj('AIPolicyHighGround', { 'Weight', 30, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'Weight', 70, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "Autofire", 'Value', -500, 'Period', 2, }), }, 'NotificationText', "", 'action_id', "AutoFire", 'Aiming', "Remaining AP", 'AttackTargeting', set( "Torso" ), }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "WildThrowGrenade", 'Weight', 300, 'Priority', true, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "WildThrowGrenade", 'Effect', "disable", 'Value', -300, 'Period', 0, }), }, 'team_score', 0, 'self_score_mod', -1000, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), }, TargetingPolicies = { PlaceObj('AITargetingCancelShot', { 'Weight', 80, }), }, group = "Lieutenants", id = "TheMajor", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Torso" ), BaseAttackWeight = 50, BaseMovementWeight = 0, Behaviors = { PlaceObj('HoldPositionAI', { 'BiasId', "HoldPositionBehavior", 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'SignatureActions', { PlaceObj('AIAttackSingleTarget', { 'BiasId', "LegShot", 'Weight', 120, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "LegShot", 'Effect', "disable", }), PlaceObj('AIBiasModification', { 'BiasId', "DefensiveOverwatch", 'Effect', "priority", 'Period', 0, }), }, 'Aiming', "Remaining AP", 'AttackTargeting', set( "Legs" ), }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "ArmShot", 'Weight', 120, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "ArmShot", 'Effect', "disable", }), PlaceObj('AIBiasModification', { 'BiasId', "DefensiveOverwatch", 'Effect', "priority", 'Period', 0, }), }, 'Aiming', "Remaining AP", 'AttackTargeting', set( "Groin" ), }), PlaceObj('AIConeAttack', { 'BiasId', "DefensiveOverwatch", 'Weight', 10, 'team_score', 0, 'min_score', 100, 'action_id', "Overwatch", }), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeMin', 25, 'RangeMax', 100, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", group = "Lieutenants", id = "TurretBoss", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'CheckLOS', false, }), }, }), }, MoveStance = "Crouch", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Absolute", 'RangeMin', 20, 'RangeMax', 50, }), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "MortarShot", 'Priority', true, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "MortarShot", 'Effect', "disable", }), }, 'team_score', 0, 'self_score_mod', 0, 'MinDist', 12000, 'action_id', "Bombard", }), }, TargetScoreRandomization = 10, group = "Simplified", id = "Artillery", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Neck", "Torso" ), BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, }), }, Comment = "Keywords: Explosives", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Melee", 'RangeMin', 0, 'RangeMax', 6, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, SignatureActions = { PlaceObj('AIActionMobileShot', { 'Priority', true, 'NotificationText', "", 'RequiredKeywords', { "RunAndGun", }, 'action_id', "RunAndGun", }), PlaceObj('AIActionMobileShot', { 'Priority', true, 'NotificationText', "", 'RequiredKeywords', { "MobileShot", }, }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "StunGrenade", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "StunGrenade", 'Effect', "disable", }), }, 'RequiredKeywords', { "Explosives", }, 'min_score', 100, 'MinDist', 3000, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "Nova", 'Weight', 300, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "Nova", 'Effect', "disable", }), }, 'RequiredKeywords', { "Nova", }, 'team_score', 0, 'self_score_mod', 100, 'MinDist', 0, 'MaxDist', 3000, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), }, TargetChangePolicy = "restart", TargetScoreRandomization = 10, group = "Simplified", id = "Brute", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Torso" ), Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 1000, }), PlaceObj('AIPolicyWeaponRange', { 'RangeMin', 30, 'RangeMax', 80, }), }, 'SignatureActions', { PlaceObj('AIActionMGSetup', { 'Priority', true, 'team_score', 0, 'min_score', 100, 'cur_zone_mod', 140, }), PlaceObj('AIActionMGBurstFire', { 'AttackTargeting', set( "Torso" ), }), }, 'TakeCoverChance', 0, 'override_attack_id', "BurstFire", 'override_cost_id', "MGSetup", }), }, OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'Weight', 600, 'RangeMin', 40, 'RangeMax', 80, }), PlaceObj('AIPolicyLosToEnemy', { 'Weight', 200, }), }, OptLocSearchRadius = 80, PrefStance = "Prone", TargetScoreRandomization = 10, TargetingPolicies = { PlaceObj('AITargetingEnemyHealth', { 'Health', 50, 'AboveHealth', true, }), PlaceObj('AITargetingEnemyWeapon', nil), PlaceObj('AITargetingEnemyWeapon', { 'EnemyWeapon', "SMG", }), PlaceObj('AITargetingEnemyWeapon', { 'EnemyWeapon', "Shotgun", }), }, group = "Simplified", id = "HeavyGunner", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Arms", "Torso" ), BaseAttackWeight = 80, BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'BiasId', "Standard", 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 20, }), PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), PlaceObj('AIPolicyWeaponRange', { 'RangeMin', 60, 'RangeMax', 100, }), }, 'SignatureActions', { PlaceObj('AIActionMobileShot', { 'Priority', true, 'NotificationText', "", }), }, 'TakeCoverChance', 50, }), PlaceObj('StandardAI', { 'BiasId', "Healer", 'Priority', true, 'Fallback', false, 'Score', function (self, unit, proto_context, debug_data) for _, ally in ipairs(unit.team.units) do if not ally:IsDead() and ally.HitPoints < MulDivRound(ally.MaxHitPoints, 70, 100) then return self.Weight end end return 0 end, 'turn_phase', "Late", 'OptLocWeight', 1, 'EndTurnPolicies', { PlaceObj('AIPolicyHealingRange', { 'Weight', 300, 'CanUseMod', 1000, }), }, 'SignatureActions', { PlaceObj('AIActionBandage', { 'Priority', true, 'RequiredKeywords', { "Heal", }, 'CanUseMod', 1000, }), PlaceObj('AIActionStim', { 'Priority', true, 'RequiredKeywords', { "Stim", }, 'TargetRules', { PlaceObj('AIStimRule', { 'Keyword', "Flank", 'Weight', 100, }), PlaceObj('AIStimRule', { 'Keyword', "Control", 'Weight', 50, }), PlaceObj('AIStimRule', { 'Keyword', "Explosives", 'Weight', 50, }), PlaceObj('AIStimRule', { 'Keyword', "Ordnance", 'Weight', 50, }), PlaceObj('AIStimRule', { 'Keyword', "RunAndGun", 'Weight', 100, }), }, }), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", TargetScoreRandomization = 10, TargetingPolicies = { PlaceObj('AITargetingCancelShot', { 'Weight', 200, }), }, group = "Simplified", id = "Medic", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Arms", "Legs", "Torso" ), BaseAttackWeight = 10, BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'Weight', 10, 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 50, }), PlaceObj('PositioningAI', { 'BiasId', "Flanking", 'Weight', 500, 'Fallback', false, 'RequiredKeywords', { "Flank", }, 'OptLocWeight', 20, 'EndTurnPolicies', { PlaceObj('AIPolicyFlanking', { 'Weight', 1000, 'Required', true, 'ReserveAttackAP', true, }), PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, 'VoiceResponse', "AIFlanking", }), }, Comment = "Keywords: Flank, Explosives", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'Weight', 300, 'RangeBase', "Absolute", 'RangeMin', 2, 'RangeMax', 8, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIActionMobileShot', { 'Priority', true, 'NotificationText', "", 'RequiredKeywords', { "RunAndGun", }, 'action_id', "RunAndGun", }), PlaceObj('AIActionMobileShot', { 'Priority', true, 'NotificationText', "", 'RequiredKeywords', { "MobileShot", }, }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "AssaultGrenadeThrow", 'Weight', 200, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "AssaultGrenadeThrow", 'Effect', "disable", }), PlaceObj('AIBiasModification', { 'BiasId', "AssaultGrenadeThrow", 'Value', -50, 'Period', 0, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Explosives", }, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), }, TargetScoreRandomization = 10, TargetingPolicies = { PlaceObj('AITargetingEnemyHealth', { 'Health', 50, }), PlaceObj('AITargetingEnemyWeapon', { 'EnemyWeapon', "Sniper", }), }, group = "Simplified", id = "Skirmisher", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Torso" ), BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'BiasId', "Standard", 'Weight', 150, 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 50, 'visibility_mode', "team", }), PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 50, }), }, Comment = "Keywords: Soldier, Sniper, Control, Ordnance, Smoke, Explosives", MoveStance = "Crouch", OptLocPolicies = { PlaceObj('AIPolicyTakeCover', { 'Weight', 50, }), PlaceObj('AIPolicyHighGround', { 'RequiredKeywords', { "Sniper", }, 'Weight', 200, }), PlaceObj('AIPolicyLosToEnemy', { 'Weight', 300, }), PlaceObj('AIPolicyWeaponRange', { 'Weight', 50, 'RangeMin', 10, 'RangeMax', 25, }), PlaceObj('AIPolicyWeaponRange', { 'RangeMin', 26, 'RangeMax', 49, }), PlaceObj('AIPolicyWeaponRange', { 'Weight', 150, 'RangeMin', 50, 'RangeMax', 100, }), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'Weight', 150, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "Autofire", 'Effect', "disable", 'Period', 0, 'ApplyTo', "Team", }), }, 'NotificationText', "", 'RequiredKeywords', { "Soldier", }, 'action_id', "AutoFire", 'AttackTargeting', set( "Torso" ), }), PlaceObj('AIActionPinDown', { 'BiasId', "PinDownAttack", 'Weight', 80, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "PinDownAttack", 'Value', -50, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Sniper", }, }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "AssaultGrenadeThrow", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "AssaultGrenadeThrow", 'Effect', "disable", }), PlaceObj('AIBiasModification', { 'BiasId', "AssaultGrenadeThrow", 'Effect', "disable", 'Period', 0, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Explosives", }, 'self_score_mod', -1000, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "SmokeGrenade", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "SmokeGrenade", 'Effect', "disable", }), PlaceObj('AIBiasModification', { 'BiasId', "SmokeGrenade", 'Effect', "disable", 'Period', 0, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Smoke", }, 'enemy_score', 0, 'team_score', 100, 'self_score_mod', 100, 'MinDist', 0, 'AllowedAoeTypes', set( "smoke" ), }), PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "LauncherFire", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "LauncherFire", 'Effect', "disable", 'Period', 0, }), PlaceObj('AIBiasModification', { 'BiasId', "LauncherFire", 'Value', -50, 'Period', 0, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Ordnance", }, 'self_score_mod', -1000, 'MinDist', 5000, 'LimitRange', true, 'MaxTargetRange', 30, }), PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "RocketFire", 'Weight', 200, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "RocketFire", 'Effect', "disable", }), }, 'RequiredKeywords', { "Ordnance", }, 'self_score_mod', -1000, 'MinDist', 5000, 'action_id', "RocketLauncherFire", 'LimitRange', true, 'MaxTargetRange', 30, }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "GroinShot", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "GroinShot", 'Effect', "disable", 'Period', 0, 'ApplyTo', "Team", }), PlaceObj('AIBiasModification', { 'BiasId', "GroinShot", 'Effect', "disable", }), }, 'RequiredKeywords', { "Sniper", }, 'Aiming', "Remaining AP", 'AttackTargeting', set( "Groin" ), }), PlaceObj('AIConeAttack', { 'BiasId', "Overwatch", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "Overwatch", 'Value', -50, 'ApplyTo', "Team", }), PlaceObj('AIBiasModification', { 'BiasId', "Overwatch", 'Effect', "disable", 'Value', -50, 'Period', 2, }), }, 'RequiredKeywords', { "Soldier", }, 'team_score', 0, 'min_score', 300, 'action_id', "Overwatch", }), PlaceObj('AIConeAttack', { 'BiasId', "SpamOverwatch", 'Weight', 200, 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "SpamOverwatch", 'Effect', "disable", 'Value', -50, 'ApplyTo', "Team", }), }, 'RequiredKeywords', { "Control", }, 'team_score', 0, 'min_score', 100, 'action_id', "Overwatch", }), }, TargetScoreRandomization = 10, group = "Simplified", id = "Soldier", }) PlaceObj('AIArchetype', { BaseAttackTargeting = set( "Arms", "Legs", "Torso" ), BaseAttackWeight = 50, BaseMovementWeight = 0, Behaviors = { PlaceObj('HoldPositionAI', { 'BiasId', "HoldPositionBehavior", 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'OnActivationBiases', { PlaceObj('AIBiasModification', { 'BiasId', "Autofire", 'Effect', "disable", }), }, 'NotificationText', "", 'action_id', "AutoFire", 'Aiming', "Remaining AP", 'AttackTargeting', set( "Torso" ), }), }, TargetScoreRandomization = 10, group = "Simplified", id = "Turret", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Required', true, }), }, }), }, OptLocPolicies = { PlaceObj('AIPolicyLosToEnemy', { 'Required', true, }), PlaceObj('AIPolicyWeaponRange', { 'Required', true, 'RangeBase', "Melee", 'RangeMin', 1, 'RangeMax', 5, }), }, OptLocSearchRadius = 10, SignatureActions = { PlaceObj('AIActionBasicAttack', { 'BiasId', "MeleeAttack", 'Priority', true, }), PlaceObj('AIActionMGSetup', { 'BiasId', "MGSetup", 'team_score', 0, 'min_score', 100, 'cur_zone_mod', 140, }), PlaceObj('AIActionMGBurstFire', { 'BiasId', "MGBurst", }), PlaceObj('AIActionBandage', { 'BiasId', "Bandage", 'MaxHp', 90, 'SelfHealMod', 500, }), PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "RPGFire", 'action_id', "RocketLauncherFire", }), PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "LauncherFire", }), PlaceObj('AIActionHeavyWeaponAttack', { 'BiasId', "MortarShot", 'action_id', "Bombard", }), PlaceObj('AIActionMobileShot', { 'BiasId', "RunAndGun", 'NotificationText', "", 'action_id', "RunAndGun", }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "SingleShot", }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "BurstFire", 'action_id', "BurstFire", }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'NotificationText', "", 'action_id', "AutoFire", }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "Buckshot", 'action_id', "Buckshot", }), PlaceObj('AIAttackSingleTarget', { 'BiasId', "DoubleBarrel", 'NotificationText', "", 'action_id', "DoubleBarrel", }), PlaceObj('AIActionPinDown', { 'BiasId', "PinDown", }), PlaceObj('AIConeAttack', { 'BiasId', "Overwatch", 'team_score', 0, 'min_score', 1, 'action_id', "Overwatch", }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "FragGrenade", 'team_score', 0, 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "SmokeGrenade", 'team_score', 0, 'AllowedAoeTypes', set( "smoke" ), }), }, group = "System", id = "AITestArchetype", }) PlaceObj('AIArchetype', { BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'Weight', 50, 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, }), }, Comment = "morale-related (cloned Assault)", OptLocPolicies = { PlaceObj('AIPolicyWeaponRange', { 'Weight', 600, 'RangeBase', "Absolute", 'RangeMin', 4, 'RangeMax', 8, }), PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, PrefStance = "Crouch", SignatureActions = { PlaceObj('AIAttackSingleTarget', { 'BiasId', "Autofire", 'Weight', 200, 'NotificationText', "", 'action_id', "AutoFire", 'AttackTargeting', set( "Torso" ), }), PlaceObj('AIActionThrowGrenade', { 'BiasId', "AssaultGrenadeThrow", 'AllowedAoeTypes', set( "fire", "none", "teargas", "toxicgas" ), }), }, group = "System", id = "Berserk", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('RetreatAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 10, }), PlaceObj('AIPolicyLosToEnemy', { 'Weight', 300, 'Invert', true, }), PlaceObj('AIRetreatPolicy', { 'Weight', 1000, 'Required', false, }), }, 'TakeCoverChance', 0, }), }, Comment = "retreat", OptLocPolicies = { PlaceObj('AIRetreatPolicy', nil), }, OptLocSearchRadius = 80, group = "System", id = "Deserter", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('ApproachInteractableAI', { 'Comment', "approach/man the selected emplacement", 'Score', function (self, unit, proto_context, debug_data) local emplacement = g_Combat and g_Combat:GetEmplacementAssignment(unit) if not emplacement or emplacement.manned_by then return 0 end return self.Weight end, 'OptLocWeight', 1000, 'TakeCoverChance', 0, }), PlaceObj('HoldPositionAI', { 'Comment', "when manning emplacement", 'Score', function (self, unit, proto_context, debug_data) local emplacement = g_Combat and g_Combat:GetEmplacementAssignment(unit) if not emplacement or emplacement.manned_by ~= unit then return 0 end return self.Weight end, 'TakeCoverChance', 0, }), }, OptLocSearchRadius = 80, group = "System", id = "EmplacementGunner", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('CustomAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 100, 'EnumDests', function (self, unit, context, debug_data) if g_TacticalMap then return g_TacticalMap:EnumDestsInAssignedArea(unit, context) end end, 'PickEndTurnPolicies', function (self, unit, context, debug_data) return AITacticSelectEndTurnPolicies(unit, context) end, 'PickOptimalLoc', function (self, unit, context, debug_data) if g_TacticalMap then return g_TacticalMap:FindOptimalLocationInAssignedArea(unit, context) end end, 'PickEndTurnLoc', function (self, unit, context, debug_data) AITacticCalcPathDistances(unit, context, "disable bias") local policies = self:PickEndTurnPolicies(unit, context) or self.EndTurnPolicies local weight = self.OptLocWeight if context.optimal_dest_in_assigned_area and context.assigned_area_unreachable then weight = weight * 10 end context.ai_destination = AIScoreReachableVoxels(context, policies, weight, debug_data and debug_data.reachable_scores, "prefer") return true end, 'SelectSignatureActions', function (self, unit, context, debug_data) return AITacticSelectSignatureActions(unit, context) end, }), }, FallbackAction = "overwatch", group = "System", id = "GuardArea", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('RetreatAI', { 'turn_phase', "Early", 'EndTurnPolicies', { PlaceObj('AIPolicyTakeCover', { 'Weight', 10, }), PlaceObj('AIPolicyLosToEnemy', { 'Weight', 300, 'Invert', true, }), PlaceObj('AIPolicyProximity', { 'Weight', 1000, }), }, 'TakeCoverChance', 50, 'DespawnAllowed', false, }), }, Comment = "morale-related (based on Deserter)", OptLocPolicies = { PlaceObj('AIRetreatPolicy', nil), }, OptLocSearchRadius = 100, group = "System", id = "Panicked", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('StandardAI', { 'Priority', true, 'Comment', "breaking pindown", 'Score', function (self, unit, proto_context, debug_data) local enemies = {} for _, descr in pairs(g_Pindown) do if descr.target == self then enemies[#enemies + 1] = enemy end end return #enemies > 0 and self.Weight or 0 end, 'EndTurnPolicies', { PlaceObj('AIPolicyLosToEnemy', { 'Invert', true, }), PlaceObj('AIPolicyDealDamage', nil), }, 'TakeCoverChance', 0, }), PlaceObj('StandardAI', { 'Comment', "fallback (aggressive)", 'Fallback', false, 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 600, }), PlaceObj('AIPolicyWeaponRange', { 'RangeBase', "Absolute", 'RangeMin', 0, 'RangeMax', 5, }), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyLosToEnemy', nil), }, OptLocSearchRadius = 80, group = "System", id = "PinnedDown", }) PlaceObj('AIArchetype', { BaseMovementWeight = 10, Behaviors = { PlaceObj('StandardAI', { 'TakeCoverChance', 0, }), }, Comment = "used to advance toward last known enemy location", OptLocPolicies = { PlaceObj('AIPolicyLastEnemyPos', nil), }, OptLocSearchRadius = 80, group = "System", id = "Scout_LastLocation", }) PlaceObj('AIArchetype', { Behaviors = { PlaceObj('StandardAI', { 'EndTurnPolicies', { PlaceObj('AIPolicyDealDamage', { 'Weight', 600, }), PlaceObj('AIPolicyTakeCover', { 'visibility_mode', "team", }), }, 'TakeCoverChance', 0, }), }, OptLocPolicies = { PlaceObj('AIPolicyTakeCover', nil), PlaceObj('AIPolicyWeaponRange', { 'Weight', 600, 'RangeMin', 25, 'RangeMax', 50, }), }, OptLocSearchRadius = 80, group = "Tutorial", id = "TutorialMinion", })