Revert "Adds midround event: Swarmer Infestation (#30673)" (#31078)

This reverts commit d6ed342a6d.
This commit is contained in:
warriorstar-orion
2025-11-22 14:33:08 -05:00
committed by GitHub
parent a6359ea097
commit 76c38f83e8
14 changed files with 0 additions and 951 deletions
@@ -1,365 +0,0 @@
// =====================
// MARK: Swarmer
// =====================
/mob/living/basic/swarmer
name = "swarmer"
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
icon = 'icons/mob/swarmer.dmi'
icon_state = "swarmer"
icon_living = "swarmer"
icon_dead = "swarmer_unactivated"
bubble_icon = "swarmer"
mob_biotypes = MOB_ROBOTIC
health = 50
maxHealth = 50
melee_damage_lower = 6
melee_damage_upper = 10
melee_damage_type = BURN
melee_attack_cooldown_min = 1.5 SECONDS
melee_attack_cooldown_max = 2.5 SECONDS
a_intent = INTENT_HARM
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
mob_size = MOB_SIZE_SMALL
pass_flags = PASSTABLE
ventcrawler = VENTCRAWLER_ALWAYS
light_color = LIGHT_COLOR_CYAN
attack_verb_continuous = "shocks"
attack_verb_simple = "shock"
friendly_verb_continuous = "pinches"
friendly_verb_simple = "pinch"
attack_sound = 'sound/effects/empulse.ogg'
faction = list("swarmer")
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minimum_survivable_temperature = 0
maximum_survivable_temperature = 500
speak_emote = list("tones")
loot = list(/obj/effect/decal/cleanable/blood/gibs/robot)
death_message = "explodes with a sharp pop!"
basic_mob_flags = DEL_ON_DEATH
sentience_type = SENTIENCE_OTHER // No, you cannot sentience or mind-transfer into them
environment_smash = ENVIRONMENT_SMASH_RWALLS // EAT EVERYTHING
step_type = FOOTSTEP_MOB_CLAW
is_ranged = TRUE
projectile_type = /obj/projectile/beam/disabler
projectile_sound = 'sound/weapons/taser2.ogg'
ranged_cooldown = 1 SECONDS
ai_controller = /datum/ai_controller/basic_controller/swarmer
see_in_dark = 6
light_range = 2
light_color = LIGHT_COLOR_CYAN
step_type = FOOTSTEP_MOB_CLAW
loot = list(
/obj/effect/gibspawner/robot,
/obj/item/stack/ore/bluespace_crystal,
)
/// Swarmer actions
var/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/swarmer_trap = BB_SWARMER_TRAP_ACTION,
/datum/action/cooldown/mob_cooldown/swarmer_barrier = BB_SWARMER_BARRIER_ACTION,
/datum/action/cooldown/mob_cooldown/swarmer_replicate = BB_SWARMER_REPLICATE_ACTION,
)
/// Resources the swarmer gains from consuming things
var/resources = 0
/// Maximum possible resources a swarmer can have
var/resource_max = 100
/mob/living/basic/swarmer/Initialize(mapload)
. = ..()
AddComponent(/datum/component/ai_retaliate_advanced, CALLBACK(src, PROC_REF(retaliate_callback)))
add_language("Swarmer", 1)
verbs -= /mob/living/verb/pulled
updatename()
grant_actions_by_list(innate_actions)
set_default_language(GLOB.all_languages["Swarmer"])
/mob/living/basic/swarmer/proc/retaliate_callback(mob/living/attacker)
if(!istype(attacker))
return
if(attacker.ai_controller) // Don't chain retaliates.
var/list/shitlist = attacker.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]
if(src in shitlist)
return
for(var/mob/living/basic/swarmer/lesser/the_swarm in oview(src, 7))
if(the_swarm == attacker) // Do not commit suicide attacking yourself
continue
if(the_swarm.faction_check_mob(attacker, FALSE)) // Don't attack other swarmers
continue
the_swarm.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
/mob/living/basic/swarmer/proc/updatename()
real_name = "Swarmer [rand(100,999)]-[pick("kappa", "sigma", "beta", "omicron", "iota", "epsilon", "omega", "gamma", "delta", "tau", "alpha")]"
name = real_name
/mob/living/basic/swarmer/get_status_tab_items()
var/list/status_tab_data = ..()
status_tab_data[++status_tab_data.len] = list("Resources:", "[resources] / [resource_max]")
return status_tab_data
/mob/living/basic/swarmer/emp_act()
adjustHealth(35)
/mob/living/basic/swarmer/death(gibbed)
do_sparks(3, 1, src)
. = ..()
/mob/living/basic/swarmer/Login() // In case an admin gets a funny idea.
..()
var/list/string_list = list()
string_list += "<b>You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.</b>"
string_list += "<b>Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.</b>"
string_list += "<b>Prime Directives:</b>"
string_list += "1. Consume resources and replicate until there are no more resources left."
string_list += "2. Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable."
string_list += "3. Defend yourself and your fellow machines."
to_chat(src, string_list.Join())
/mob/living/basic/swarmer/melee_attack(atom/target, list/modifiers, ignore_cooldown)
face_atom(target)
if(is_type_in_list(target, GLOB.swarmer_blacklist))
to_chat(src, "<span class='warning'>Our sensors have designated this object important to station functionality or to our mission. Aborting.</span>")
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return FALSE
if(HAS_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING))
to_chat(src, "<span class='warning'>This asset is already being converted into useable resources. Aborting.</span>")
return FALSE
if(iswallturf(target))
disintegrate_wall(target)
return FALSE
if(ismachinery(target))
disintegrate_machine(target)
return FALSE
if(istype(target, /obj/structure))
disintegrate_machine(target)
return FALSE
if(isitem(target))
integrate(target)
return FALSE
if(!isliving(target))
return ..()
var/mob/living/L = target
if(istype(L, /mob/living/basic/swarmer))
to_chat(src, "<span class='warning'>We do not wish to harm one of our own. Aborting.</span>")
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return FALSE
if(L.stat == DEAD)
disintegrate_mob(target)
return FALSE
if(iscarbon(target))
var/mob/living/carbon/C = target
if((!C.IsWeakened()))
L.apply_damage(30, STAMINA)
C.Confused(6 SECONDS)
C.Jitter(6 SECONDS)
return ..()
else if(C.canBeHandcuffed() && !C.handcuffed)
L.apply_damage(30, STAMINA)
var/obj/item/restraints/handcuffs/cable/cyan/cuffs = new /obj/item/restraints/handcuffs/cable/cyan(src)
playsound(loc, cuffs.cuffsound, 15, TRUE, -10)
if(do_mob(src, C, 1 SECONDS))
cuffs.apply_cuffs(target, src)
return FALSE
// Make it go away.
disintegrate_mob(target)
return FALSE
if(issilicon(target))
var/mob/living/silicon/S = target
S.apply_damage(30, STAMINA)
if(S.IsWeakened())
disintegrate_mob(target)
return ..()
/mob/living/basic/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
if(!(flags & SHOCK_TESLA))
return FALSE
return ..()
/mob/living/basic/swarmer/proc/disintegrate_wall(turf/simulated/wall/target)
if(istype(target, /turf/simulated/wall/indestructible))
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return
if(spacecheck(target))
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return
new /obj/effect/temp_visual/swarmer(target.loc)
to_chat(src, "<span class='notice'>Beginning disintegration of [target].")
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
if(!do_after_once(src, 1 SECONDS, target = target, attempt_cancel_message = "You stop disintegrating [target].", interaction_key = "disintegrate"))
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
return
resources = clamp(resources + 8, 0, resource_max)
adjustHealth(-40)
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
target.ex_act(EXPLODE_HEAVY)
/mob/living/basic/swarmer/proc/disintegrate_machine(obj/machinery/target)
if(spacecheck(target))
return
new /obj/effect/temp_visual/swarmer/dismantle(target.loc)
to_chat(src, "<span class='notice'>Beginning disintegration of [target].")
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
if(!do_after_once(src, 2.5 SECONDS, target = target, attempt_cancel_message = "You stop disintegrating [target].", interaction_key = "disintegrate"))
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
return
resources = clamp(resources + 10, 0, resource_max)
adjustHealth(-20)
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
if(istype(target, /obj/structure/reagent_dispensers))
var/obj/structure/reagent_dispensers/dispenser = target
dispenser.boom()
return
target.deconstruct()
/mob/living/basic/swarmer/proc/integrate(obj/item/target)
new /obj/effect/temp_visual/swarmer/integrate(target.loc)
resources = clamp(resources + 10, 0, resource_max)
adjustHealth(-10)
qdel(target)
/mob/living/basic/swarmer/proc/disintegrate_mob(mob/living/target)
new /obj/effect/temp_visual/swarmer/integrate(target.loc)
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
to_chat(src, "<span class='notice'>Beginning integration of [target].")
if(!do_after_once(src, 1 SECONDS, target = target, attempt_cancel_message = "You stop integrating [target].", interaction_key = "disintegrate"))
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
return
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
resources = clamp(resources + 50, 0, resource_max)
adjustHealth(-40)
if(isanimal_or_basicmob(target) || issilicon(target)) // Not crew? Are a silicon? Don't care.
target.gib()
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
disappear_mob(target)
/mob/living/basic/swarmer/proc/disappear_mob(mob/target)
if(!is_station_level(z))
to_chat(src, "<span class='warning'>Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area.</span>")
return
var/turf/simulated/floor/F = find_safe_turf(zlevels = z)
if(!F)
return
do_sparks(4, 0, target)
playsound(src,'sound/effects/sparks4.ogg', 50, TRUE)
do_teleport(target, F, 0)
/mob/living/basic/swarmer/proc/spacecheck(atom/target)
for(var/turf/T in range(1, target))
var/datum/gas_mixture/environment = T.get_readonly_air()
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
var/environment_pressure = environment.return_pressure()
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
var/area/A = get_area(T)
if(isspaceturf(T) || istype(A, /area/shuttle) || istype(A, /area/space) || pressure_dangerlevel)
to_chat(src, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
return TRUE
else if(istype(A, /area/station/engineering/engine/supermatter))
to_chat(src, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
return TRUE
return FALSE
// =====================
// MARK: Lesser Swarmer
// =====================
/mob/living/basic/swarmer/lesser
name = "lesser swarmer"
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely. This one is more fragile."
icon_state = "lesser_swarmer"
health = 35
maxHealth = 35
ai_controller = /datum/ai_controller/basic_controller/swarmer/lesser
loot = list(
/obj/effect/gibspawner/robot,
)
innate_actions = list(
/datum/action/cooldown/mob_cooldown/swarmer_trap = BB_SWARMER_TRAP_ACTION,
/datum/action/cooldown/mob_cooldown/swarmer_barrier = BB_SWARMER_BARRIER_ACTION,
)
/mob/living/basic/swarmer/lesser/Initialize(mapload)
. = ..()
icon_state = pick("lesser_swarmer", "lesser_swarmer_2")
/mob/living/basic/swarmer/lesser/updatename()
real_name = "Lesser Swarmer [rand(100,999)]-[pick("kappa", "sigma", "beta", "omicron", "iota", "epsilon", "omega", "gamma", "delta", "tau", "alpha")]"
name = real_name
// =====================
// MARK: Swarmer Structures
// =====================
/obj/structure/swarmer // Default swarmer effect object visual feedback
name = "swarmer ui"
desc = null
icon = 'icons/mob/swarmer.dmi'
icon_state = "ui_light"
layer = MOB_LAYER
anchored = TRUE
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
light_color = LIGHT_COLOR_CYAN
max_integrity = 30
anchored = TRUE
/// Light range
var/lon_range = 1
/obj/structure/swarmer/Initialize(mapload)
. = ..()
set_light(lon_range)
/obj/structure/swarmer/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
playsound(src, 'sound/weapons/egloves.ogg', 80, TRUE)
if(BURN)
playsound(src, 'sound/items/welder.ogg', 100, TRUE)
/obj/structure/swarmer/emp_act()
do_sparks(3, 1, src)
qdel(src)
/obj/structure/swarmer/trap
name = "swarmer trap"
desc = "A quickly assembled trap that electrifies living beings and overwhelms machine sensors. Will not retain its form if damaged enough."
icon_state = "trap"
max_integrity = 10
/obj/structure/swarmer/trap/Initialize(mapload)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/structure/swarmer/trap/proc/on_atom_entered(datum/source, atom/movable/entered)
SIGNAL_HANDLER // COMSIG_ATOM_ENTERED
if(!isliving(entered))
return
var/mob/living/L = entered
if(istype(L, /mob/living/basic/swarmer))
return
playsound(loc,'sound/effects/snap.ogg', 50, 1, -1)
L.electrocute_act(100, src, 1, flags = SHOCK_NOGLOVES | SHOCK_ILLUSION)
L.Weaken(10 SECONDS)
qdel(src)
/obj/structure/swarmer/barricade
name = "swarmer barricade"
desc = "A quickly assembled energy barricade. Will not retain its form if damaged enough, but disabler beams and swarmers pass right through."
icon_state = "barricade"
light_range = MINIMUM_USEFUL_LIGHT_RANGE
max_integrity = 50
/obj/structure/swarmer/barricade/CanPass(atom/movable/O)
if(istype(O, /mob/living/basic/swarmer))
return TRUE
if(istype(O, /obj/projectile/beam/disabler))
return TRUE
@@ -1,99 +0,0 @@
/datum/action/cooldown/mob_cooldown/swarmer_trap
name = "Construct Shock Trap"
button_icon = 'icons/mob/swarmer.dmi'
button_icon_state = "ui_trap"
desc = "Construct a shock trap, using 25 resources."
click_to_activate = FALSE
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
cooldown_time = 60 SECONDS
shared_cooldown = NONE
/datum/action/cooldown/mob_cooldown/swarmer_trap/Activate(atom/target)
var/mob/living/basic/swarmer/user = target
if(!istype(user))
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
return
var/turf/T = get_turf(user)
for(var/turf/turf_in_view in view(1, T))
if(locate(/obj/structure/swarmer) in turf_in_view)
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
return
if(user.resources < 25)
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
return
user.resources -= 25
user.visible_message("<span class='warning'>[user] manufactures a sparking pylon.</span>")
playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
new /obj/structure/swarmer/trap(T)
StartCooldown()
/datum/action/cooldown/mob_cooldown/swarmer_barrier
name = "Construct Barricade"
button_icon = 'icons/mob/swarmer.dmi'
button_icon_state = "ui_barricade"
desc = "Construct a barricade, using 25 resources."
click_to_activate = FALSE
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
cooldown_time = 75 SECONDS
shared_cooldown = NONE
/datum/action/cooldown/mob_cooldown/swarmer_barrier/Activate(atom/target)
var/mob/living/basic/swarmer/user = target
if(!istype(user))
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
return
if(user.resources < 25)
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
return
var/turf/T = get_turf(user)
for(var/turf/turf_in_view in view(1, T))
if(locate(/obj/structure/swarmer) in turf_in_view)
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
return
if(!do_after_once(user, 2 SECONDS, target = T, attempt_cancel_message = "You stop building a barricade.", interaction_key = "swarmer_barricade_create"))
return
// Check again to make sure they still have the requirements
if(user.resources < 25)
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
return
T = get_turf(user)
for(var/turf/turf_in_view in view(1, T))
if(locate(/obj/structure/swarmer) in turf_in_view)
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
return
user.resources -= 25
user.visible_message("<span class='warning'>[user] manufactures a holographic shield.</span>")
playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
new /obj/structure/swarmer/barricade(T)
StartCooldown()
/datum/action/cooldown/mob_cooldown/swarmer_replicate
name = "Replicate"
button_icon = 'icons/mob/swarmer.dmi'
button_icon_state = "ui_replicate"
desc = "Construct a new swarmer, using 50 resources."
click_to_activate = FALSE
cooldown_time = 20 SECONDS
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
shared_cooldown = NONE
/datum/action/cooldown/mob_cooldown/swarmer_replicate/Activate(atom/target)
var/mob/living/basic/swarmer/user = target
if(!istype(user))
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
return
if(user.resources < 50)
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
return
if(!do_after_once(user, 4 SECONDS, target = user, attempt_cancel_message = "You stop building a new swarmer.", interaction_key = "swarmer_replicate_create"))
return
// Check again after the do-after.
if(user.resources < 50)
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
return
var/turf/T = get_turf(user)
user.resources -= 50
user.visible_message("<span class='warning'>[user] manufactures a new swarmer.</span>")
playsound(get_turf(user), 'sound/items/rped.ogg', 50, TRUE)
new /mob/living/basic/swarmer/lesser(T)
StartCooldown()
@@ -1,284 +0,0 @@
/// Attacks people it can see, disintegrates things based on priority.
/datum/ai_controller/basic_controller/swarmer
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_AGGRO_RANGE = 4,
)
ai_movement = /datum/ai_movement/jps
idle_behavior = /datum/idle_behavior/idle_random_walk
ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/target_retaliate/check_faction,
/datum/ai_planning_subtree/ranged_skirmish/avoid_friendly,
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/swarmer_replicate,
/datum/ai_planning_subtree/swarmer_find_construction_target,
/datum/ai_planning_subtree/swarmer_create_trap,
/datum/ai_planning_subtree/swarmer_create_barricade,
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting,
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer,
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects,
/datum/ai_planning_subtree/find_and_hunt_target/prowl,
)
/// Attacks people it can see, disintegrates things based on priority. More aggressive.
/datum/ai_controller/basic_controller/swarmer/lesser
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_AGGRO_RANGE = 8,
)
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/target_retaliate/check_faction,
/datum/ai_planning_subtree/ranged_skirmish/avoid_friendly,
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/swarmer_find_construction_target,
/datum/ai_planning_subtree/swarmer_create_trap,
/datum/ai_planning_subtree/swarmer_create_barricade,
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting,
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer,
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects,
/datum/ai_planning_subtree/find_and_hunt_target/prowl,
)
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/swarmer
hunt_range = 4
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects
finding_behavior = /datum/ai_behavior/find_hunt_target/swarmer_objects
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/swarmer
hunt_targets = list(/obj/item, /obj/structure, /obj/machinery, /turf/simulated/wall, /mob/living)
hunt_range = 4
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting
target_key = BB_CURRENT_HUNTING_TARGET
/datum/ai_behavior/hunt_target/interact_with_target/swarmer
always_reset_target = TRUE
hunt_cooldown = 0 SECONDS
/datum/ai_behavior/find_hunt_target/swarmer_objects
/datum/ai_behavior/find_hunt_target/swarmer_objects/valid_dinner(mob/living/source, atom/dinner, radius)
if(is_type_in_list(dinner, GLOB.swarmer_blacklist))
return FALSE
if(HAS_TRAIT(dinner, TRAIT_SWARMER_DISINTEGRATING))
return FALSE
if(isitem(dinner) && !isturf(dinner.loc))
return FALSE
if(isliving(dinner))
var/mob/living/M = dinner
if(M.stat == DEAD)
return TRUE
for(var/turf/T in range(1, dinner))
var/datum/gas_mixture/environment = T.get_readonly_air()
var/environment_pressure = environment.return_pressure()
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
var/area/A = get_area(T)
if(isspaceturf(T) || istype(A, /area/shuttle) || istype(A, /area/space) || istype(A, /area/station/engineering/engine/supermatter) || pressure_dangerlevel)
return FALSE
return TRUE
/// Run the barricade construction behaviour if we have an ability to use for it
/datum/ai_planning_subtree/swarmer_replicate
/// Key where the trap action is stored
var/action_key = BB_SWARMER_REPLICATE_ACTION
/datum/ai_planning_subtree/swarmer_replicate/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/basic/swarmer/user = controller.pawn
if(!istype(user))
return
if(user.resources < 50)
return
if(!controller.blackboard_key_exists(action_key))
return
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
if(replicate_action.IsAvailable())
controller.queue_behavior(/datum/ai_behavior/swarmer_replicate, action_key)
return SUBTREE_RETURN_FINISH_PLANNING
/// REPLICATE
/datum/ai_behavior/swarmer_replicate
required_distance = 0
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/swarmer_replicate/setup(datum/ai_controller/controller, action_key)
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
var/mob/living/basic/swarmer/user = controller.pawn
if(!istype(user))
return FALSE
if(!replicate_action || user.resources < 50)
return FALSE
return ..()
/datum/ai_behavior/swarmer_replicate/perform(seconds_per_tick, datum/ai_controller/controller, action_key)
. = ..()
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
var/result = replicate_action.Trigger()
if(result)
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
/// Search for a nearby location to build on
/datum/ai_planning_subtree/swarmer_find_construction_target
/datum/ai_planning_subtree/swarmer_find_construction_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/basic/swarmer/user = controller.pawn
if(!istype(user))
return
if(user.resources < 25)
return
controller.queue_behavior(/datum/ai_behavior/swarmer_find_construction_target)
/// Find a nearby clear turf and store it
/datum/ai_behavior/swarmer_find_construction_target
action_cooldown = 5 SECONDS
/// Where do we store the target data
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
/// How far do we look for valid turfs?
var/scan_range = 5
/datum/ai_behavior/swarmer_find_construction_target/perform(seconds_per_tick, datum/ai_controller/controller)
. = ..()
var/mob/living/swarmer = controller.pawn
var/atom/current_target = controller.blackboard[target_key]
if(current_target && !(locate(/obj/structure/swarmer) in current_target))
// Already got a target
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
controller.clear_blackboard_key(target_key)
var/turf/our_turf = get_turf(swarmer)
if(is_valid_construction_turf(our_turf, swarmer))
controller.set_blackboard_key(target_key, our_turf)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
var/list/turfs_by_range = list()
for(var/i in 1 to scan_range)
turfs_by_range["[i]"] = list()
for(var/turf/turf_in_view in oview(scan_range, our_turf))
if(!is_valid_construction_turf(turf_in_view, swarmer))
continue
turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view
var/list/final_turfs
for(var/list/turf_list as anything in turfs_by_range)
if(length(turfs_by_range[turf_list]))
final_turfs = turfs_by_range[turf_list]
break
if(!length(final_turfs))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
controller.set_blackboard_key(target_key, pick(final_turfs))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/swarmer_find_construction_target/proc/is_valid_construction_turf(turf/target_turf, mob/living/swarmer)
for(var/turf/turf_in_view in view(1, target_turf))
if(locate(/obj/structure/swarmer) in turf_in_view)
return FALSE
if(HAS_TRAIT(target_turf, TRAIT_SWARMER_CONSTRUCTION))
return FALSE
if(isspaceturf(target_turf))
return FALSE
var/datum/gas_mixture/environment = target_turf.get_readonly_air()
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
var/environment_pressure = environment.return_pressure()
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
if(pressure_dangerlevel)
return FALSE
return !target_turf.is_blocked_turf(source_atom = swarmer)
/// Run the trap construction behaviour if we have an ability to use for it
/datum/ai_planning_subtree/swarmer_create_trap
/// Key where the trap action is stored
var/action_key = BB_SWARMER_TRAP_ACTION
/// Key where the target turf is stored
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
/datum/ai_planning_subtree/swarmer_create_trap/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key))
return
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
if(trap_action.IsAvailable())
controller.queue_behavior(/datum/ai_behavior/swarmer_create_trap, action_key, target_key)
return SUBTREE_RETURN_FINISH_PLANNING
/// Move to an open nearby turf and build a trap
/datum/ai_behavior/swarmer_create_trap
action_cooldown = 15 SECONDS // We don't want them doing this too quickly
required_distance = 0
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/swarmer_create_trap/setup(datum/ai_controller/controller, action_key, target_key)
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
var/turf/target_turf = controller.blackboard[target_key]
var/mob/living/basic/swarmer/user = controller.pawn
if(!istype(user))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if(!trap_action || !target_turf || user.resources < 25)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
set_movement_target(controller, target_turf)
return ..()
/datum/ai_behavior/swarmer_create_trap/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
. = ..()
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
var/result = trap_action.Trigger()
if(result)
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
/datum/ai_behavior/swarmer_create_trap/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
controller.clear_blackboard_key(target_key)
return ..()
/// Run the barricade construction behaviour if we have an ability to use for it
/datum/ai_planning_subtree/swarmer_create_barricade
/// Key where the trap action is stored
var/action_key = BB_SWARMER_BARRIER_ACTION
/// Key where the target turf is stored
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
/datum/ai_planning_subtree/swarmer_create_barricade/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key))
return
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
if(barrier_action.IsAvailable())
controller.queue_behavior(/datum/ai_behavior/swarmer_create_trap, action_key, target_key)
return SUBTREE_RETURN_FINISH_PLANNING
/// Move to an open nearby turf and build a barricade
/datum/ai_behavior/swarmer_create_barricade
action_cooldown = 45 SECONDS
required_distance = 0
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/swarmer_create_barricade/setup(datum/ai_controller/controller, action_key, target_key)
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
var/turf/target_turf = controller.blackboard[target_key]
var/mob/living/basic/swarmer/user = controller.pawn
if(!istype(user))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if(!barrier_action || !target_turf || user.resources < 25)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
set_movement_target(controller, target_turf)
return ..()
/datum/ai_behavior/swarmer_create_barricade/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
. = ..()
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
if(barrier_action?.Trigger())
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
/datum/ai_behavior/swarmer_create_barricade/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
controller.clear_blackboard_key(target_key)
return ..()
@@ -1,15 +0,0 @@
/obj/effect/temp_visual/swarmer // temporary swarmer visual feedback objects
icon = 'icons/mob/swarmer.dmi'
icon_state = "disintegrate"
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/swarmer/Initialize(mapload)
. = ..()
playsound(loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
/obj/effect/temp_visual/swarmer/dismantle
icon_state = "dismantle"
duration = 2.5 SECONDS
/obj/effect/temp_visual/swarmer/integrate
icon_state = "integrate"