Nanotrasen basic mobs. (#78917)

## About The Pull Request

First and foremost, converts all Nanotrasen simplemobs into basic mobs.

To avoid messy and redundant code, or god forbid, making Nanotrasen mobs
a subtype of Syndicate ones, I've made Syndicate, Russian, and
Nanotrasen mobs all share a unified "Trooper" parent. This should have
no effect on their behaviors, but makes things much easier to extend
further in the future.

While most of this PR is pretty cut-and-dry, I've done a couple notable
things. For one, all types of ranged trooper will now avoid friendly
fire, instead of shooting their friends in the back. Even the Russians
have trigger discipline.

I've also created a new AI subtree that allows mobs to call for
reinforcements. I've hopefully made this easy to extend, but the
existing version works as follows:

- A mob with this subtree that gains a target that is also a mob will
call out to all mobs within 15 tiles.
- If they share a faction, mobs receiving the call will have the target
added to their retaliate list, and have a new key set targeting the
calling mob.
- If they have the correct subtree in their AI controller, called-to
mobs will then run over to help out.

Sadly, this behavior is currently used only by a few completely unused
Nanotrasen mobs, so in practice it will not yet be seen.

Finally, I've fixed a minor issue where melee Russian mobs punch people
to death despite holding a knife. They now use the proper effects for
stabbing instead of punching.
## Why It's Good For The Game

Removes 8 more simple animals from the list.

As said above, making all "trooper" type mobs share a common parent cuts
down on code reuse, ensures consistency of behavior, and makes it much
easier to add new troopers not affiliated with these groups. I expect
that I'll make pirates share this same parent next.

The new "reinforcements" behavior, though extremely powerful, opens up
exciting new opportunities in the future. There aren't many existing
behaviors that allow basic mobs to work _together_ in interesting ways,
and I think adding some enemy teamwork could be fun.
## Changelog
🆑
refactor: Hostile Nanotrasen mobs now use the basic mob framework. This
should make them a little smarter and more dangerous. Please report any
bugs.
fix: Russian mobs will now actually use those knives they're holding.
/🆑
This commit is contained in:
lizardqueenlexi
2023-10-24 23:20:27 +00:00
committed by GitHub
parent 93627b2c7b
commit b65f729901
32 changed files with 1649 additions and 1597 deletions
@@ -0,0 +1,97 @@
/// Nanotrasen Private Security forces
/mob/living/basic/trooper/nanotrasen
name = "\improper Nanotrasen Private Security Officer"
desc = "An officer of Nanotrasen's private security force. Seems rather unpleased to meet you."
speed = 0
melee_damage_lower = 10
melee_damage_upper = 15
faction = list(ROLE_DEATHSQUAD)
loot = list(/obj/effect/mob_spawn/corpse/human/nanotrasensoldier)
mob_spawner = /obj/effect/mob_spawn/corpse/human/nanotrasensoldier
/// A variant that calls for reinforcements on spotting a target
/mob/living/basic/trooper/nanotrasen/screaming
ai_controller = /datum/ai_controller/basic_controller/trooper/calls_reinforcements
/mob/living/basic/trooper/nanotrasen/ranged
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged
r_hand = /obj/item/gun/ballistic/automatic/pistol/m1911
/// Type of bullet we use
var/casingtype = /obj/item/ammo_casing/c45
/// Sound to play when firing weapon
var/projectilesound = 'sound/weapons/gun/pistol/shot_alt.ogg'
/// number of burst shots
var/burst_shots
/// Time between taking shots
var/ranged_cooldown = 1 SECONDS
/mob/living/basic/trooper/nanotrasen/ranged/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/ranged_attacks,\
casing_type = casingtype,\
projectile_sound = projectilesound,\
cooldown_time = ranged_cooldown,\
burst_shots = burst_shots,\
)
/mob/living/basic/trooper/nanotrasen/ranged/smg
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst
casingtype = /obj/item/ammo_casing/c46x30mm
projectilesound = 'sound/weapons/gun/smg/shot.ogg'
r_hand = /obj/item/gun/ballistic/automatic/wt550
burst_shots = 3
ranged_cooldown = 3 SECONDS
/mob/living/basic/trooper/nanotrasen/ranged/assault
name = "Nanotrasen Assault Officer"
desc = "Nanotrasen Assault Officer. Contact CentCom if you saw him on your station. Prepare to die, if you've been found near Syndicate property."
casingtype = /obj/item/ammo_casing/a223/weak
burst_shots = 4
ranged_cooldown = 3 SECONDS
projectilesound = 'sound/weapons/gun/smg/shot.ogg'
r_hand = /obj/item/gun/ballistic/automatic/ar
loot = list(/obj/effect/mob_spawn/corpse/human/nanotrasenassaultsoldier)
mob_spawner = /obj/effect/mob_spawn/corpse/human/nanotrasenassaultsoldier
/mob/living/basic/trooper/nanotrasen/ranged/elite
name = "Nanotrasen Elite Assault Officer"
desc = "Pray for your life, syndicate. Run while you can."
maxHealth = 150
health = 150
habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_cold_damage = 0
casingtype = /obj/item/ammo_casing/energy/laser
burst_shots = 3
projectilesound = 'sound/weapons/laser.ogg'
ranged_cooldown = 5 SECONDS
faction = list(ROLE_DEATHSQUAD)
loot = list(/obj/effect/gibspawner/human)
mob_spawner = /obj/effect/mob_spawn/corpse/human/nanotrasenelitesoldier
r_hand = /obj/item/gun/energy/pulse/carbine/lethal
/// A more peaceful variant that will only attack when attacked, or when another Nanotrasen officer calls for help.
/mob/living/basic/trooper/nanotrasen/peaceful
desc = "An officer of Nanotrasen's private security force."
ai_controller = /datum/ai_controller/basic_controller/trooper/peaceful
/mob/living/basic/trooper/nanotrasen/peaceful/Initialize(mapload)
. = ..()
var/datum/callback/retaliate_callback = CALLBACK(src, PROC_REF(ai_retaliate_behaviour))
AddComponent(/datum/component/ai_retaliate_advanced, retaliate_callback)
/mob/living/basic/trooper/nanotrasen/ranged/smg/peaceful
desc = "An officer of Nanotrasen's private security force."
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful
/mob/living/basic/trooper/nanotrasen/ranged/smg/peaceful/Initialize(mapload)
. = ..()
var/datum/callback/retaliate_callback = CALLBACK(src, PROC_REF(ai_retaliate_behaviour))
AddComponent(/datum/component/ai_retaliate_advanced, retaliate_callback)
/mob/living/basic/trooper/nanotrasen/proc/ai_retaliate_behaviour(mob/living/attacker)
if (!istype(attacker))
return
for (var/mob/living/basic/trooper/nanotrasen/potential_trooper in oview(src, 7))
potential_trooper.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
@@ -0,0 +1,39 @@
/// Russian trooper subtype
/mob/living/basic/trooper/russian
name = "Russian Mobster"
desc = "For the Motherland!"
speed = 0
melee_damage_lower = 15
melee_damage_upper = 15
unsuitable_cold_damage = 1
unsuitable_heat_damage = 1
faction = list(FACTION_RUSSIAN)
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
attack_sound = 'sound/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
mob_spawner = /obj/effect/mob_spawn/corpse/human/russian
r_hand = /obj/item/knife/kitchen
loot = list(
/obj/effect/mob_spawn/corpse/human/russian,
/obj/item/knife/kitchen,
)
/mob/living/basic/trooper/russian/ranged
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged
mob_spawner = /obj/effect/mob_spawn/corpse/human/russian/ranged
r_hand = /obj/item/gun/ballistic/automatic/pistol
loot = list(
/obj/effect/mob_spawn/corpse/human/russian/ranged,
/obj/item/gun/ballistic/revolver/nagant,
)
var/casingtype = /obj/item/ammo_casing/n762
var/projectilesound = 'sound/weapons/gun/revolver/shot.ogg'
/mob/living/basic/trooper/russian/ranged/Initialize(mapload)
. = ..()
AddComponent(/datum/component/ranged_attacks, casing_type = casingtype, projectile_sound = projectilesound, cooldown_time = 1 SECONDS)
/mob/living/basic/trooper/russian/ranged/lootless
loot = list()
@@ -0,0 +1,248 @@
/// Syndicate troopers
/mob/living/basic/trooper/syndicate
name = "Syndicate Operative"
desc = "Death to Nanotrasen."
speed = 1.1
melee_damage_lower = 10
melee_damage_upper = 10
faction = list(ROLE_SYNDICATE)
loot = list(/obj/effect/mob_spawn/corpse/human/syndicatesoldier)
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatesoldier
/mob/living/basic/trooper/syndicate/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
loot = list(/obj/effect/gibspawner/human)
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
set_light(4)
/mob/living/basic/trooper/syndicate/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
/mob/living/basic/trooper/syndicate/melee //dude with a knife and no shields
melee_damage_lower = 15
melee_damage_upper = 15
loot = list(/obj/effect/gibspawner/human)
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
attack_sound = 'sound/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
r_hand = /obj/item/knife/combat/survival
var/projectile_deflect_chance = 0
/mob/living/basic/trooper/syndicate/melee/bullet_act(obj/projectile/projectile)
if(prob(projectile_deflect_chance))
visible_message(span_danger("[src] blocks [projectile] with its shield!"))
return BULLET_ACT_BLOCK
return ..()
/mob/living/basic/trooper/syndicate/melee/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/melee/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
set_light(4)
/mob/living/basic/trooper/syndicate/melee/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
/mob/living/basic/trooper/syndicate/melee/sword
melee_damage_lower = 30
melee_damage_upper = 30
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
attack_sound = 'sound/weapons/blade1.ogg'
armour_penetration = 35
projectile_deflect_chance = 50
light_range = 2
light_power = 2.5
light_color = COLOR_SOFT_RED
r_hand = /obj/item/melee/energy/sword/saber/red
l_hand = /obj/item/shield/energy
/mob/living/basic/trooper/syndicate/melee/sword/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
projectile_deflect_chance = 50
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/melee/sword/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
/mob/living/basic/trooper/syndicate/melee/sword/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
projectile_deflect_chance = 50
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
///////////////Guns////////////
/mob/living/basic/trooper/syndicate/ranged
loot = list(/obj/effect/gibspawner/human)
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged
r_hand = /obj/item/gun/ballistic/automatic/pistol
/// Type of bullet we use
var/casingtype = /obj/item/ammo_casing/c9mm
/// Sound to play when firing weapon
var/projectilesound = 'sound/weapons/gun/pistol/shot.ogg'
/// number of burst shots
var/burst_shots
/// Time between taking shots
var/ranged_cooldown = 1 SECONDS
/mob/living/basic/trooper/syndicate/ranged/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/ranged_attacks,\
casing_type = casingtype,\
projectile_sound = projectilesound,\
cooldown_time = ranged_cooldown,\
burst_shots = burst_shots,\
)
/mob/living/basic/trooper/syndicate/ranged/infiltrator //shuttle loan event
projectilesound = 'sound/weapons/gun/smg/shot_suppressed.ogg'
loot = list(/obj/effect/mob_spawn/corpse/human/syndicatesoldier)
/mob/living/basic/trooper/syndicate/ranged/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/ranged/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
set_light(4)
/mob/living/basic/trooper/syndicate/ranged/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
/mob/living/basic/trooper/syndicate/ranged/smg
casingtype = /obj/item/ammo_casing/c45
projectilesound = 'sound/weapons/gun/smg/shot.ogg'
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst
burst_shots = 3
ranged_cooldown = 3 SECONDS
r_hand = /obj/item/gun/ballistic/automatic/c20r
/mob/living/basic/trooper/syndicate/ranged/smg/pilot //caravan ambush ruin
name = "Syndicate Salvage Pilot"
loot = list(/obj/effect/mob_spawn/corpse/human/syndicatepilot)
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatepilot
/mob/living/basic/trooper/syndicate/ranged/smg/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/ranged/smg/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
set_light(4)
/mob/living/basic/trooper/syndicate/ranged/smg/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
/mob/living/basic/trooper/syndicate/ranged/shotgun
casingtype = /obj/item/ammo_casing/shotgun/buckshot //buckshot (up to 72.5 brute) fired in a two-round burst
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/shotgunner
ranged_cooldown = 3 SECONDS
burst_shots = 2
r_hand = /obj/item/gun/ballistic/shotgun/bulldog
/mob/living/basic/trooper/syndicate/ranged/shotgun/space
name = "Syndicate Commando"
maxHealth = 170
health = 170
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
speed = 1
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatecommando
/mob/living/basic/trooper/syndicate/ranged/shotgun/space/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
set_light(4)
/mob/living/basic/trooper/syndicate/ranged/shotgun/space/stormtrooper
name = "Syndicate Stormtrooper"
maxHealth = 250
health = 250
mob_spawner = /obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
///////////////Misc////////////
/mob/living/basic/viscerator
name = "viscerator"
desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations."
icon_state = "viscerator_attack"
icon_living = "viscerator_attack"
pass_flags = PASSTABLE | PASSMOB
combat_mode = TRUE
mob_biotypes = MOB_ROBOTIC
basic_mob_flags = DEL_ON_DEATH
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
maximum_survivable_temperature = 700
unsuitable_cold_damage = 0
health = 25
maxHealth = 25
melee_damage_lower = 15
melee_damage_upper = 15
wound_bonus = -10
bare_wound_bonus = 20
sharpness = SHARP_EDGED
obj_damage = 0
attack_verb_continuous = "cuts"
attack_verb_simple = "cut"
attack_sound = 'sound/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
faction = list(ROLE_SYNDICATE)
mob_size = MOB_SIZE_TINY
limb_destroyer = 1
speak_emote = list("states")
bubble_icon = "syndibot"
gold_core_spawnable = HOSTILE_SPAWN
death_message = "is smashed into pieces!"
ai_controller = /datum/ai_controller/basic_controller/trooper/viscerator
/mob/living/basic/viscerator/Initialize(mapload)
. = ..()
AddElement(/datum/element/simple_flying)
AddComponent(/datum/component/swarming)
@@ -0,0 +1,36 @@
/mob/living/basic/trooper
icon = 'icons/mob/simple/simple_human.dmi'
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
sentience_type = SENTIENCE_HUMANOID
maxHealth = 100
health = 100
basic_mob_flags = DEL_ON_DEATH
speed = 1.1
melee_damage_lower = 10
melee_damage_upper = 10
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
attack_sound = 'sound/weapons/punch1.ogg'
melee_attack_cooldown = 1.2 SECONDS
combat_mode = TRUE
unsuitable_atmos_damage = 7.5
unsuitable_cold_damage = 7.5
unsuitable_heat_damage = 7.5
ai_controller = /datum/ai_controller/basic_controller/trooper
/// Loot this mob drops on death.
var/loot = list(/obj/effect/mob_spawn/corpse/human)
/// Path of the mob spawner we base the mob's visuals off of.
var/mob_spawner = /obj/effect/mob_spawn/corpse/human
/// Path of the right hand held item we give to the mob's visuals.
var/r_hand
/// Path of the left hand held item we give to the mob's visuals.
var/l_hand
/mob/living/basic/trooper/Initialize(mapload)
. = ..()
apply_dynamic_human_appearance(src, mob_spawn_path = mob_spawner, r_hand = r_hand, l_hand = l_hand)
if(LAZYLEN(loot))
loot = string_list(loot)
AddElement(/datum/element/death_drops, loot)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SHOE)
@@ -0,0 +1,99 @@
/datum/ai_controller/basic_controller/trooper
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic,
BB_TARGET_MINIMUM_STAT = HARD_CRIT,
BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!"
)
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/attack_obstacle_in_path/trooper,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_planning_subtree/basic_melee_attack_subtree/trooper
melee_attack_behavior = /datum/ai_behavior/basic_melee_attack
/datum/ai_planning_subtree/attack_obstacle_in_path/trooper
attack_behaviour = /datum/ai_behavior/attack_obstructions/trooper
/datum/ai_behavior/attack_obstructions/trooper
action_cooldown = 1.2 SECONDS
/datum/ai_controller/basic_controller/trooper/calls_reinforcements
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/call_reinforcements,
/datum/ai_planning_subtree/attack_obstacle_in_path/trooper,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_controller/basic_controller/trooper/peaceful
planning_subtrees = list(
/datum/ai_planning_subtree/target_retaliate,
/datum/ai_planning_subtree/call_reinforcements,
/datum/ai_planning_subtree/attack_obstacle_in_path/trooper,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_controller/basic_controller/trooper/ranged
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper
/datum/ai_behavior/basic_ranged_attack/trooper
action_cooldown = 1 SECONDS
required_distance = 5
avoid_friendly_fire = TRUE
/datum/ai_controller/basic_controller/trooper/ranged/burst
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper_burst
/datum/ai_behavior/basic_ranged_attack/trooper_burst
action_cooldown = 3 SECONDS
avoid_friendly_fire = TRUE
/datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful
planning_subtrees = list(
/datum/ai_planning_subtree/target_retaliate,
/datum/ai_planning_subtree/call_reinforcements,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_controller/basic_controller/trooper/ranged/shotgunner
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_shotgun,
/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_shotgun
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper_shotgun
/datum/ai_behavior/basic_ranged_attack/trooper_shotgun
action_cooldown = 3 SECONDS
required_distance = 1
avoid_friendly_fire = TRUE
/datum/ai_controller/basic_controller/trooper/viscerator
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic,
)