diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index 1f879efb01e..b837799f963 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -220,6 +220,9 @@ ///some behaviors that check current_target also set this on deep crit mobs #define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target" +///List of mobs who have damaged us +#define BB_BASIC_MOB_RETALIATE_LIST "BB_basic_mob_shitlist" + ///list of foods this mob likes #define BB_BASIC_FOODS "BB_basic_foods" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm index 04755aca997..0e9b7161a99 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm @@ -40,3 +40,7 @@ #define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" ///from base of atom/attack_paw(): (mob/user) #define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw" +///from base of atom/mech_melee_attack(): (obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) +#define COMSIG_ATOM_ATTACK_MECH "atom_attack_mech" +///from relay_attackers element: (atom/attacker) +#define COMSIG_ATOM_WAS_ATTACKED "atom_was_attacked" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm b/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm new file mode 100644 index 00000000000..ff92435765e --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm @@ -0,0 +1,69 @@ +/// Sets the BB target to a mob which you can see and who has recently attacked you +/datum/ai_planning_subtree/target_retaliate + +/datum/ai_planning_subtree/target_retaliate/SelectBehaviors(datum/ai_controller/controller, delta_time) + . = ..() + controller.queue_behavior(/datum/ai_behavior/target_from_retaliate_list, BB_BASIC_MOB_RETALIATE_LIST, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) + +/** + * Picks a target from a provided list of atoms who have been pissing you off + * You will probably need /datum/element/ai_retaliate to take advantage of this unless you're populating the blackboard yourself + */ +/datum/ai_behavior/target_from_retaliate_list + action_cooldown = 2 SECONDS + /// How far can we see stuff? + var/vision_range = 9 + +/datum/ai_behavior/target_from_retaliate_list/perform(delta_time, datum/ai_controller/controller, shitlist_key, target_key, targetting_datum_key, hiding_location_key) + . = ..() + var/mob/living/living_mob = controller.pawn + var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key] + if(!targetting_datum) + CRASH("No target datum was supplied in the blackboard for [controller.pawn]") + + var/list/enemy_refs = controller.blackboard[shitlist_key] + if (!length(enemy_refs)) + finish_action(controller, succeeded = FALSE) + return + + var/list/enemies_list = list() + for (var/datum/weakref/enemy_ref as anything in enemy_refs) + var/atom/enemy = enemy_ref.resolve() + if (!can_attack_target(living_mob, enemy, targetting_datum)) + controller.blackboard[shitlist_key] -= enemy_ref + continue + enemies_list += enemy + + if (!length(enemies_list)) + finish_action(controller, succeeded = FALSE) + return + + var/datum/weakref/weak_target = controller.blackboard[target_key] + var/atom/target = weak_target?.resolve() + if (target && (locate(target) in enemies_list)) // Don't bother changing + finish_action(controller, succeeded = FALSE) + return + + var/atom/new_target = pick_final_target(controller, enemies_list) + controller.blackboard[target_key] = WEAKREF(new_target) + + var/atom/potential_hiding_location = targetting_datum.find_hidden_mobs(living_mob, new_target) + + if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially. + controller.blackboard[hiding_location_key] = WEAKREF(potential_hiding_location) + + finish_action(controller, succeeded = TRUE) + +/// Returns true if this target is valid for attacking based on current conditions +/datum/ai_behavior/target_from_retaliate_list/proc/can_attack_target(mob/living/living_mob, atom/target, datum/targetting_datum/targetting_datum) + if (!target) + return FALSE + if (target == living_mob) + return FALSE + if (!can_see(living_mob, target, vision_range)) + return FALSE + return targetting_datum.can_attack(living_mob, target) + +/// Returns the desired final target from the filtered list of enemies +/datum/ai_behavior/target_from_retaliate_list/proc/pick_final_target(datum/ai_controller/controller, list/enemies_list) + return pick(enemies_list) diff --git a/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm b/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm index 5b1ffcd0e4e..86fa48b377a 100644 --- a/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm +++ b/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm @@ -25,16 +25,15 @@ if(M.status_flags & GODMODE) return FALSE - if(living_mob.see_invisible < the_target.invisibility)//Target's invisible to us, forget it + if(living_mob.see_invisible < the_target.invisibility) //Target's invisible to us, forget it return FALSE - if(living_mob.z != the_target.z) + if(isturf(the_target.loc) && living_mob.z != the_target.z) // z check will always fail if target is in a mech return FALSE if(isliving(the_target)) //Targeting vs living mobs var/mob/living/L = the_target - var/faction_check = living_mob.faction_check_mob(L, exact_match = check_factions_exactly) - if(faction_check || L.stat) + if(faction_check(living_mob, L) || L.stat) return FALSE return TRUE @@ -56,6 +55,10 @@ return FALSE +/// Returns true if the mob and target share factions +/datum/targetting_datum/basic/proc/faction_check(mob/living/living_mob, mob/living/the_target) + return living_mob.faction_check_mob(the_target, exact_match = check_factions_exactly) + /// Subtype more forgiving for items. /// Careful, this can go wrong and keep a mob hyper-focused on an item it can't lose aggro on /datum/targetting_datum/basic/allow_items @@ -65,3 +68,10 @@ if(isitem(the_target)) // trust fall exercise return TRUE + +/// Subtype which doesn't care about faction +/// Mobs which retaliate but don't otherwise target seek should just attack anything which annoys them +/datum/targetting_datum/basic/ignore_faction + +/datum/targetting_datum/basic/ignore_faction/faction_check(mob/living/living_mob, mob/living/the_target) + return FALSE diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm index 7e1ab026bfc..b2bcb7bdd93 100644 --- a/code/datums/ai/monkey/monkey_controller.dm +++ b/code/datums/ai/monkey/monkey_controller.dm @@ -53,16 +53,10 @@ have ways of interacting with a specific mob and control it. return AI_CONTROLLER_INCOMPATIBLE var/mob/living/living_pawn = new_pawn - RegisterSignal(new_pawn, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) - RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) - RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_PAW, PROC_REF(on_attack_paw)) - RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_ANIMAL, PROC_REF(on_attack_animal)) - RegisterSignal(new_pawn, COMSIG_MOB_ATTACK_ALIEN, PROC_REF(on_attack_alien)) - RegisterSignal(new_pawn, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) - RegisterSignal(new_pawn, COMSIG_ATOM_HITBY, PROC_REF(on_hitby)) + living_pawn.AddElement(/datum/element/relay_attackers) + RegisterSignal(new_pawn, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) RegisterSignal(new_pawn, COMSIG_LIVING_START_PULL, PROC_REF(on_startpulling)) RegisterSignal(new_pawn, COMSIG_LIVING_TRY_SYRINGE, PROC_REF(on_try_syringe)) - RegisterSignal(new_pawn, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) RegisterSignal(new_pawn, COMSIG_CARBON_CUFF_ATTEMPTED, PROC_REF(on_attempt_cuff)) RegisterSignal(new_pawn, COMSIG_MOB_MOVESPEED_UPDATED, PROC_REF(update_movespeed)) @@ -72,18 +66,11 @@ have ways of interacting with a specific mob and control it. /datum/ai_controller/monkey/UnpossessPawn(destroy) UnregisterSignal(pawn, list( - COMSIG_PARENT_ATTACKBY, - COMSIG_ATOM_ATTACK_HAND, - COMSIG_ATOM_ATTACK_PAW, - COMSIG_ATOM_BULLET_ACT, - COMSIG_ATOM_HITBY, + COMSIG_ATOM_WAS_ATTACKED, COMSIG_LIVING_START_PULL, COMSIG_LIVING_TRY_SYRINGE, - COMSIG_ATOM_HULK_ATTACK, COMSIG_CARBON_CUFF_ATTEMPTED, COMSIG_MOB_MOVESPEED_UPDATED, - COMSIG_ATOM_ATTACK_ANIMAL, - COMSIG_MOB_ATTACK_ALIEN, )) return ..() //Run parent at end @@ -159,48 +146,10 @@ have ways of interacting with a specific mob and control it. var/list/enemies = blackboard[BB_MONKEY_ENEMIES] enemies[WEAKREF(L)] += MONKEY_HATRED_AMOUNT -/datum/ai_controller/monkey/proc/on_attackby(datum/source, obj/item/I, mob/user) +/datum/ai_controller/monkey/proc/on_attacked(datum/source, mob/attacker) SIGNAL_HANDLER - if(I.force && I.damtype != STAMINA) - retaliate(user) - -/datum/ai_controller/monkey/proc/on_attack_hand(datum/source, mob/living/user, list/modifiers) - SIGNAL_HANDLER - if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) - retaliate(user) - -/datum/ai_controller/monkey/proc/on_attack_paw(datum/source, mob/living/user, list/modifiers) - SIGNAL_HANDLER - if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) - retaliate(user) - -/datum/ai_controller/monkey/proc/on_attack_animal(datum/source, mob/living/user) - SIGNAL_HANDLER - if(user.melee_damage_upper > 0 && prob(MONKEY_RETALIATE_PROB)) - retaliate(user) - -/datum/ai_controller/monkey/proc/on_attack_alien(datum/source, mob/living/user, list/modifiers) - SIGNAL_HANDLER - if((user.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) && prob(MONKEY_RETALIATE_PROB)) - retaliate(user) - -/datum/ai_controller/monkey/proc/on_bullet_act(datum/source, obj/projectile/Proj) - SIGNAL_HANDLER - var/mob/living/living_pawn = pawn - if(istype(Proj, /obj/projectile/beam) || istype(Proj, /obj/projectile/bullet)) - if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) - if(!Proj.nodamage && Proj.damage < living_pawn.health && isliving(Proj.firer)) - retaliate(Proj.firer) - -/datum/ai_controller/monkey/proc/on_hitby(datum/source, atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) - SIGNAL_HANDLER - if(isitem(AM)) - var/mob/living/living_pawn = pawn - var/obj/item/I = AM - var/mob/thrown_by = I.thrownby?.resolve() - if(I.throwforce && I.throwforce < living_pawn.health && ishuman(thrown_by)) - var/mob/living/carbon/human/H = thrown_by - retaliate(H) + if(prob(MONKEY_RETALIATE_PROB)) + retaliate(attacker) /datum/ai_controller/monkey/proc/on_startpulling(datum/source, atom/movable/puller, state, force) SIGNAL_HANDLER @@ -215,10 +164,6 @@ have ways of interacting with a specific mob and control it. if(prob(MONKEY_SYRINGE_RETALIATION_PROB)) retaliate(user) -/datum/ai_controller/monkey/proc/on_attack_hulk(datum/source, mob/user) - SIGNAL_HANDLER - retaliate(user) - /datum/ai_controller/monkey/proc/on_attempt_cuff(datum/source, mob/user) SIGNAL_HANDLER // chance of monkey retaliation diff --git a/code/datums/ai/robot_customer/robot_customer_controller.dm b/code/datums/ai/robot_customer/robot_customer_controller.dm index 901c0f2f69c..cf53c521b3a 100644 --- a/code/datums/ai/robot_customer/robot_customer_controller.dm +++ b/code/datums/ai/robot_customer/robot_customer_controller.dm @@ -20,13 +20,15 @@ /datum/ai_controller/robot_customer/TryPossessPawn(atom/new_pawn) if(!istype(new_pawn, /mob/living/simple_animal/robot_customer)) return AI_CONTROLLER_INCOMPATIBLE + new_pawn.AddElement(/datum/element/relay_attackers) RegisterSignal(new_pawn, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignal(new_pawn, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) RegisterSignal(new_pawn, COMSIG_LIVING_GET_PULLED, PROC_REF(on_get_pulled)) RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_get_punched)) return ..() //Run parent at end /datum/ai_controller/robot_customer/UnpossessPawn(destroy) - UnregisterSignal(pawn, list(COMSIG_PARENT_ATTACKBY, COMSIG_LIVING_GET_PULLED, COMSIG_ATOM_ATTACK_HAND)) + UnregisterSignal(pawn, list(COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_WAS_ATTACKED, COMSIG_LIVING_GET_PULLED, COMSIG_ATOM_ATTACK_HAND)) return ..() //Run parent at end /datum/ai_controller/robot_customer/proc/on_attackby(datum/source, obj/item/I, mob/living/user) @@ -39,6 +41,9 @@ else INVOKE_ASYNC(src, PROC_REF(warn_greytider), user) +/datum/ai_controller/robot_customer/proc/on_attacked(datum/source, mob/living/attacker) + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(warn_greytider), attacker) /datum/ai_controller/robot_customer/proc/eat_order(obj/item/order_item, datum/venue/attending_venue) if(!blackboard[BB_CUSTOMER_EATING]) diff --git a/code/datums/elements/ai_retaliate.dm b/code/datums/elements/ai_retaliate.dm new file mode 100644 index 00000000000..d4e2144f558 --- /dev/null +++ b/code/datums/elements/ai_retaliate.dm @@ -0,0 +1,29 @@ +/** + * Attached to a mob with an AI controller, passes things which have damaged it to a blackboard. + * The AI controller is responsible for doing anything with that information. + */ +/datum/element/ai_retaliate + +/datum/element/ai_retaliate/Attach(datum/target) + . = ..() + if(!ismob(target)) + return ELEMENT_INCOMPATIBLE + + target.AddElement(/datum/element/relay_attackers) + RegisterSignal(target, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) + +/datum/element/ai_retaliate/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_ATOM_WAS_ATTACKED) + +/// Add an attacking atom to a blackboard list of things which attacked us +/datum/element/ai_retaliate/proc/on_attacked(mob/victim, atom/attacker) + SIGNAL_HANDLER + + if (!victim.ai_controller) + return + var/list/enemy_refs = victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] + if (!enemy_refs) + enemy_refs = list() + enemy_refs |= WEAKREF(attacker) + victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] = enemy_refs diff --git a/code/datums/elements/relay_attackers.dm b/code/datums/elements/relay_attackers.dm new file mode 100644 index 00000000000..27eb25a537d --- /dev/null +++ b/code/datums/elements/relay_attackers.dm @@ -0,0 +1,79 @@ +/** + * This element registers to a shitload of signals which can signify "someone attacked me". + * If anyone does it sends a single "someone attacked me" signal containing details about who done it. + * This prevents other components and elements from having to register to the same list of a million signals, should be more maintainable in one place. + */ +/datum/element/relay_attackers + +/datum/element/relay_attackers/Attach(datum/target) + . = ..() + // Boy this sure is a lot of ways to tell us that someone tried to attack us + RegisterSignal(target, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc)) + RegisterSignal(target, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) + RegisterSignal(target, COMSIG_ATOM_HITBY, PROC_REF(on_hitby)) + RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) + RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech)) + +/datum/element/relay_attackers/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, list( + COMSIG_PARENT_ATTACKBY, + COMSIG_ATOM_ATTACK_HAND, + COMSIG_ATOM_ATTACK_PAW, + COMSIG_ATOM_ATTACK_BASIC_MOB, + COMSIG_ATOM_ATTACK_ANIMAL, + COMSIG_MOB_ATTACK_ALIEN, + COMSIG_ATOM_BULLET_ACT, + COMSIG_ATOM_HITBY, + COMSIG_ATOM_HULK_ATTACK, + COMSIG_ATOM_ATTACK_MECH, + )) + +/datum/element/relay_attackers/proc/on_attackby(atom/target, obj/item/weapon, mob/attacker) + SIGNAL_HANDLER + if(weapon.force) + relay_attacker(target, attacker) + +/datum/element/relay_attackers/proc/on_attack_generic(atom/target, mob/living/attacker, list/modifiers) + SIGNAL_HANDLER + if(attacker.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) + relay_attacker(target, attacker) + +/datum/element/relay_attackers/proc/on_attack_npc(atom/target, mob/living/attacker) + SIGNAL_HANDLER + if(attacker.melee_damage_upper > 0) + relay_attacker(target, attacker) + +/datum/element/relay_attackers/proc/on_bullet_act(atom/target, obj/projectile/hit_projectile) + SIGNAL_HANDLER + if(hit_projectile.nodamage) + return + if(!ismob(hit_projectile.firer)) + return + relay_attacker(target, hit_projectile.firer) + +/datum/element/relay_attackers/proc/on_hitby(atom/target, atom/movable/hit_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) + SIGNAL_HANDLER + if(!isitem(hit_atom)) + return + var/obj/item/hit_item = hit_atom + if(!hit_item.throwforce) + return + var/mob/thrown_by = hit_item.thrownby?.resolve() + if(!ismob(thrown_by)) + return + relay_attacker(target, thrown_by) + +/datum/element/relay_attackers/proc/on_attack_hulk(atom/target, mob/attacker) + SIGNAL_HANDLER + relay_attacker(target, attacker) + +/datum/element/relay_attackers/proc/on_attack_mech(atom/target, obj/vehicle/sealed/mecha/mecha_attacker, mob/living/pilot) + SIGNAL_HANDLER + relay_attacker(target, mecha_attacker) + +/// Send out a signal identifying whoever just attacked us (usually a mob but sometimes a mech or turret) +/datum/element/relay_attackers/proc/relay_attacker(atom/victim, atom/attacker) + SEND_SIGNAL(victim, COMSIG_ATOM_WAS_ATTACKED, attacker) diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm index 42bf1d546e8..8d1c4b323c4 100644 --- a/code/modules/mob/living/basic/farm_animals/pig.dm +++ b/code/modules/mob/living/basic/farm_animals/pig.dm @@ -21,6 +21,8 @@ attack_verb_simple = "kick" attack_sound = 'sound/weapons/punch1.ogg' attack_vis_effect = ATTACK_EFFECT_KICK + melee_damage_lower = 1 + melee_damage_upper = 2 health = 50 maxHealth = 50 gold_core_spawnable = FRIENDLY_SPAWN @@ -29,6 +31,7 @@ /mob/living/basic/pig/Initialize() AddElement(/datum/element/pet_bonus, "oinks!") + AddElement(/datum/element/ai_retaliate) make_tameable() . = ..() @@ -43,7 +46,21 @@ visible_message(span_notice("[src] snorts respectfully.")) /datum/ai_controller/basic_controller/pig + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/ignore_faction() + ) + ai_traits = STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk - + + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate, + /datum/ai_planning_subtree/basic_melee_attack_subtree/pig, + ) + +/datum/ai_planning_subtree/basic_melee_attack_subtree/pig + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/pig + +/datum/ai_behavior/basic_melee_attack/pig + action_cooldown = 2 SECONDS diff --git a/code/modules/vehicles/mecha/mech_melee_attack.dm b/code/modules/vehicles/mecha/mech_melee_attack.dm index 7b1261b6f4a..08669ebdd0c 100644 --- a/code/modules/vehicles/mecha/mech_melee_attack.dm +++ b/code/modules/vehicles/mecha/mech_melee_attack.dm @@ -9,6 +9,7 @@ */ /atom/proc/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user) SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_MECH, mecha_attacker, user) log_combat(user, src, "attacked", mecha_attacker, "(COMBAT MODE: [uppertext(user.combat_mode)] (DAMTYPE: [uppertext(mecha_attacker.damtype)])") return 0 diff --git a/tgstation.dme b/tgstation.dme index 0a6f0bfc715..43da1764229 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -721,6 +721,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\speech_subtree.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\tipped_subtree.dm" #include "code\datums\ai\basic_mobs\targetting_datums\basic_targetting_datum.dm" #include "code\datums\ai\cursed\cursed_behaviors.dm" @@ -1025,6 +1026,7 @@ #include "code\datums\diseases\advance\symptoms\weight.dm" #include "code\datums\diseases\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" +#include "code\datums\elements\ai_retaliate.dm" #include "code\datums\elements\animal_variety.dm" #include "code\datums\elements\art.dm" #include "code\datums\elements\atmos_requirements.dm" @@ -1096,6 +1098,7 @@ #include "code\datums\elements\radiation_protected_clothing.dm" #include "code\datums\elements\radioactive.dm" #include "code\datums\elements\ranged_attacks.dm" +#include "code\datums\elements\relay_attackers.dm" #include "code\datums\elements\ridable.dm" #include "code\datums\elements\rust.dm" #include "code\datums\elements\selfknockback.dm"