diff --git a/code/_onclick/hud/swarmer.dm b/code/_onclick/hud/swarmer.dm
index 253f58cac1a..6bfcd2f654a 100644
--- a/code/_onclick/hud/swarmer.dm
+++ b/code/_onclick/hud/swarmer.dm
@@ -1,37 +1,35 @@
-
-
/obj/screen/swarmer
icon = 'icons/mob/swarmer.dmi'
/obj/screen/swarmer/fabricate_trap
icon_state = "ui_trap"
- name = "Create trap (Costs 5 Resources)"
- desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)"
+ name = "Create trap (Costs 4 Resources)"
+ desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 4 resources)"
/obj/screen/swarmer/fabricate_trap/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.CreateTrap()
+ S.create_trap()
/obj/screen/swarmer/barricade
icon_state = "ui_barricade"
- name = "Create barricade (Costs 5 Resources)"
- desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)"
+ name = "Create barricade (Costs 4 Resources)"
+ desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 4 resources)"
/obj/screen/swarmer/barricade/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.CreateBarricade()
+ S.create_barricade()
/obj/screen/swarmer/replicate
icon_state = "ui_replicate"
- name = "Replicate (Costs 50 Resources)"
- desc = "Creates another of our kind."
+ name = "Replicate (Costs 20 Resources)"
+ desc = "Creates a drone."
/obj/screen/swarmer/replicate/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.CreateSwarmer()
+ S.create_swarmer()
/obj/screen/swarmer/repair_self
icon_state = "ui_self_repair"
@@ -41,7 +39,7 @@
/obj/screen/swarmer/repair_self/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.RepairSelf()
+ S.repair_self()
/obj/screen/swarmer/toggle_light
icon_state = "ui_light"
@@ -51,7 +49,7 @@
/obj/screen/swarmer/toggle_light/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.ToggleLight()
+ S.toggle_light()
/obj/screen/swarmer/contact_swarmers
icon_state = "ui_contact_swarmers"
@@ -61,7 +59,7 @@
/obj/screen/swarmer/contact_swarmers/Click()
if(isswarmer(usr))
var/mob/living/simple_animal/hostile/swarmer/S = usr
- S.ContactSwarmers()
+ S.contact_swarmers()
/datum/hud/swarmer/New(mob/owner)
..()
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index f6953ef5917..9ecec42a562 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -94,8 +94,8 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/suit/space/hardsuit/deathsquad, /obj/item/clothing/head/helmet/space/hardsuit/deathsquad,
/obj/machinery/door/airlock/centcom)),
- "robots" = typecacheof(list(/obj/machinery/computer/upload, /obj/item/ai_module/, /obj/machinery/recharge_station,
- /obj/item/aicard, /obj/item/deactivated_swarmer, /obj/effect/mob_spawn/swarmer)),
+ "robots" = typecacheof(list(/obj/machinery/computer/upload, /obj/item/ai_module, /obj/machinery/recharge_station,
+ /obj/item/aicard, /obj/structure/swarmer_beacon)),
"doctors" = typecacheof(list(/obj/item/clothing/under/rank/medical,
/obj/item/reagent_containers/syringe, /obj/item/reagent_containers/pill/, /obj/item/reagent_containers/hypospray,
diff --git a/code/game/machinery/droneDispenser.dm b/code/game/machinery/droneDispenser.dm
index 52172faf9d0..af9cdea2be8 100644
--- a/code/game/machinery/droneDispenser.dm
+++ b/code/game/machinery/droneDispenser.dm
@@ -117,28 +117,6 @@
recharge_sound = null
recharge_message = null
-/obj/machinery/drone_dispenser/swarmer
- name = "swarmer fabricator"
- desc = "An alien machine of unknown origin. It whirs and hums with green-blue light, the air above it shimmering."
- icon = 'icons/obj/machines/gateway.dmi'
- icon_state = "toffcenter"
- icon_off = "toffcenter"
- icon_on = "toffcenter"
- icon_recharging = "toffcenter"
- icon_creating = "offcenter"
- metal_cost = 0
- glass_cost = 0
- cooldownTime = 300 //30 seconds
- maximum_idle = 0 // Swarmers have no restraint
- dispense_type = /obj/effect/mob_spawn/swarmer
- begin_create_message = "hums softly as an interface appears above it, scrolling by at unreadable speed."
- end_create_message = "materializes a strange shell, which drops to the ground."
- recharging_text = "Its lights are slowly increasing in brightness."
- work_sound = 'sound/effects/empulse.ogg'
- create_sound = 'sound/effects/phasein.ogg'
- break_sound = 'sound/effects/empulse.ogg'
- break_message = "slowly falls dark, lights stuttering."
-
/obj/machinery/drone_dispenser/examine(mob/user)
. = ..()
if((mode == DRONE_RECHARGING) && !machine_stat && recharging_text)
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index 9aae6a2e7b1..57de089d449 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -1,675 +1,38 @@
-////Deactivated swarmer shell////
-/obj/item/deactivated_swarmer
- name = "deactivated swarmer"
- desc = "A shell of swarmer that was completely powered down. It can no longer activate itself."
- icon = 'icons/mob/swarmer.dmi'
- icon_state = "swarmer_unactivated"
- custom_materials = list(/datum/material/iron=10000, /datum/material/glass=4000)
+/datum/team/swarmer
+ name = "Swarmers"
-/obj/effect/mob_spawn/swarmer
- name = "unactivated swarmer"
- desc = "A currently unactivated swarmer. Swarmers can self activate at any time, so it would be wise to immediately dispose of this."
- icon = 'icons/mob/swarmer.dmi'
- icon_state = "swarmer_unactivated"
- density = FALSE
- anchored = FALSE
+//Simply lists them.
+/datum/team/swarmer/roundend_report()
+ var/list/parts = list()
+ parts += ""
+ parts += printplayerlist(members)
+ return "
[parts.Join("
")]
"
- mob_type = /mob/living/simple_animal/hostile/swarmer
- mob_name = "a swarmer"
- death = FALSE
- roundstart = FALSE
- short_desc = "You are a swarmer, a weapon of a long dead civilization."
- flavour_text = {"
- 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.
- 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.
- Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage.
- Objectives:
- 1. Consume resources and replicate until there are no more resources left.
- 2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable.
- 3. Biological resources will be harvested at a later date; do not harm them.
- "}
-
-/obj/effect/mob_spawn/swarmer/Initialize()
- . = ..()
- var/area/A = get_area(src)
- if(A)
- notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE)
-
-/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
- . = ..()
- if(.)
- return
- to_chat(user, "Picking up the swarmer may cause it to activate. You should be careful about this.")
-
-/obj/effect/mob_spawn/swarmer/attackby(obj/item/W, mob/user, params)
- if(W.tool_behaviour == TOOL_SCREWDRIVER && user.a_intent != INTENT_HARM)
- user.visible_message("[usr.name] deactivates [src].",
- "After some fiddling, you find a way to disable [src]'s power source.",
- "You hear clicking.")
- new /obj/item/deactivated_swarmer(get_turf(src))
- qdel(src)
- else
- ..()
-
-////The Mob itself////
-
-/mob/living/simple_animal/hostile/swarmer
+/datum/antagonist/swarmer
name = "Swarmer"
- unique_name = 1
- icon = 'icons/mob/swarmer.dmi'
- desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
- speak_emote = list("tones")
- initial_language_holder = /datum/language_holder/swarmer
- bubble_icon = "swarmer"
- mob_biotypes = MOB_ROBOTIC
- health = 40
- maxHealth = 40
- status_flags = CANPUSH
- icon_state = "swarmer"
- icon_living = "swarmer"
- icon_dead = "swarmer_unactivated"
- icon_gib = null
- wander = 0
- harm_intent_damage = 5
- minbodytemp = 0
- maxbodytemp = 500
- 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)
- unsuitable_atmos_damage = 0
- melee_damage_lower = 15
- melee_damage_upper = 15
- melee_damage_type = STAMINA
- damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
- hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD)
- obj_damage = 0
- environment_smash = ENVIRONMENT_SMASH_NONE
- attack_verb_continuous = "shocks"
- attack_verb_simple = "shock"
- attack_sound = 'sound/effects/empulse.ogg'
- friendly_verb_continuous = "pinches"
- friendly_verb_simple = "pinch"
- speed = 0
- faction = list("swarmer")
- AIStatus = AI_OFF
- pass_flags = PASSTABLE
- mob_size = MOB_SIZE_TINY
- ventcrawler = VENTCRAWLER_ALWAYS
- ranged = 1
- projectiletype = /obj/projectile/beam/disabler
- ranged_cooldown_time = 20
- projectilesound = 'sound/weapons/taser2.ogg'
- loot = list(/obj/effect/decal/cleanable/robot_debris, /obj/item/stack/ore/bluespace_crystal)
- del_on_death = 1
- deathmessage = "explodes with a sharp pop!"
- light_color = LIGHT_COLOR_CYAN
- hud_type = /datum/hud/swarmer
- speech_span = SPAN_ROBOT
- var/resources = 0 //Resource points, generated by consuming metal/glass
- var/max_resources = 100
+ job_rank = ROLE_ALIEN
+ show_in_antagpanel = FALSE
+ prevent_roundtype_conversion = FALSE
+ var/datum/team/swarmer/swarmer_team
-/mob/living/simple_animal/hostile/swarmer/Initialize()
- . = ..()
- verbs -= /mob/living/verb/pulled
- for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
- diag_hud.add_to_hud(src)
+/datum/antagonist/swarmer/create_team(datum/team/swarmer/new_team)
+ if(!new_team)
+ for(var/datum/antagonist/swarmer/swarmerantag in GLOB.antagonists)
+ if(!swarmerantag.owner || !swarmerantag.swarmer_team)
+ continue
+ swarmer_team = swarmerantag.swarmer_team
+ return
+ swarmer_team = new
+ else
+ if(!istype(new_team))
+ CRASH("Wrong swarmer team type provided to create_team")
+ swarmer_team = new_team
-/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
- var/image/holder = hud_list[DIAG_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
- holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
+/datum/antagonist/swarmer/get_team()
+ return swarmer_team
-/mob/living/simple_animal/hostile/swarmer/med_hud_set_status()
- var/image/holder = hud_list[DIAG_STAT_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
- holder.icon_state = "hudstat"
-
-/mob/living/simple_animal/hostile/swarmer/Stat()
+//SWARMER
+/mob/living/simple_animal/hostile/swarmer/mind_initialize()
..()
- if(statpanel("Status"))
- stat("Resources:",resources)
-
-/mob/living/simple_animal/hostile/swarmer/emp_act()
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if(health > 1)
- adjustHealth(health-1)
- else
- death()
-
-/mob/living/simple_animal/hostile/swarmer/CanAllowThrough(atom/movable/O)
- . = ..()
- if(istype(O, /obj/projectile/beam/disabler))//Allows for swarmers to fight as a group without wasting their shots hitting each other
- return TRUE
- if(isswarmer(O))
- return TRUE
-
-////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
-/mob/living/simple_animal/hostile/swarmer/AttackingTarget()
- if(!isliving(target))
- return target.swarmer_act(src)
- else
- return ..()
-
-/mob/living/simple_animal/hostile/swarmer/CtrlClickOn(atom/A)
- face_atom(A)
- if(!isturf(loc))
- return
- if(next_move > world.time)
- return
- if(!A.Adjacent(src))
- return
- A.swarmer_act(src)
-
-/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE //return TRUE/FALSE whether or not an AI swarmer should try this swarmer_act() again, NOT whether it succeeded.
-
-/obj/effect/mob_spawn/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.Integrate(src)
- return FALSE //would logically be TRUE, but we don't want AI swarmers eating player spawn chances.
-
-/obj/effect/mob_spawn/swarmer/IntegrateAmount()
- return 50
-
-/turf/closed/indestructible/swarmer_act()
- return FALSE
-
-/obj/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- if(resistance_flags & INDESTRUCTIBLE)
- return FALSE
- for(var/mob/living/L in contents)
- if(!issilicon(L) && !isbrain(L))
- to_chat(S, "An organism has been detected inside this object. Aborting.")
- return FALSE
- return ..()
-
-/obj/item/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- return S.Integrate(src)
-
-/atom/movable/proc/IntegrateAmount()
- return 0
-
-/obj/item/IntegrateAmount() //returns the amount of resources gained when eating this item
- if(custom_materials)
- if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)] || custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
- return 1
- return ..()
-
-/obj/item/gun/swarmer_act()//Stops you from eating the entire armory
- return FALSE
-
-/turf/open/swarmer_act()//ex_act() on turf calls it on its contents, this is to prevent attacking mobs by DisIntegrate()'ing the floor
- return FALSE
-
-/obj/structure/lattice/catwalk/swarmer_catwalk/swarmer_act()
- return FALSE
-
-/obj/structure/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- if(S.AIStatus == AI_ON)
- return FALSE
- else
- return ..()
-
-/obj/effect/swarmer_act()
- return FALSE
-
-/obj/effect/decal/cleanable/robot_debris/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- qdel(src)
- return TRUE
-
-/obj/structure/flora/swarmer_act()
- return FALSE
-
-/turf/open/lava/swarmer_act()
- if(!is_safe())
- new /obj/structure/lattice/catwalk/swarmer_catwalk(src)
- return FALSE
-
-/obj/machinery/atmospherics/swarmer_act()
- return FALSE
-
-/obj/structure/disposalpipe/swarmer_act()
- return FALSE
-
-/obj/machinery/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DismantleMachine(src)
- return TRUE
-
-/obj/machinery/light/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- var/isonshuttle = istype(get_area(src), /area/shuttle)
- for(var/turf/T in range(1, src))
- var/area/A = get_area(T)
- if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
- to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.")
- S.target = null
- return FALSE
- else if(istype(A, /area/engine/supermatter))
- to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
- S.target = null
- return FALSE
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- if(!QDELETED(S)) //If it got blown up no need to turn it off.
- toggle_cam(S, 0)
- return TRUE
-
-/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/gravity_generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/vending/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//It's more visually interesting than dismantling the machine
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/turretid/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisIntegrate(src)
- return TRUE
-
-/obj/machinery/chem_dispenser/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "The volatile chemicals in this machine would destroy us. Aborting.")
- return FALSE
-
-/obj/machinery/nuclearbomb/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This device's destruction would result in the extermination of everything in the area. Aborting.")
- return FALSE
-
-/obj/effect/rune/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Searching... sensor malfunction! Target lost. Aborting.")
- return FALSE
-
-/obj/structure/reagent_dispensers/fueltank/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Destroying this object would cause a chain reaction. Aborting.")
- return FALSE
-
-/obj/structure/cable/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Disrupting the power grid would bring no benefit to us. Aborting.")
- return FALSE
-
-/obj/machinery/portable_atmospherics/canister/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "An inhospitable area may be created as a result of destroying this object. Aborting.")
- return FALSE
-
-/obj/machinery/telecomms/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting.")
- return FALSE
-
-/obj/machinery/deepfryer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This kitchen appliance should be preserved, it will make delicious unhealthy snacks for our masters in the future. Aborting.")
- return FALSE
-
-/obj/machinery/power/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Disrupting the power grid would bring no benefit to us. Aborting.")
- return FALSE
-
-/obj/machinery/gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This bluespace source will be important to us later. Aborting.")
- return FALSE
-
-/turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- var/isonshuttle = istype(loc, /area/shuttle)
- for(var/turf/T in range(1, src))
- var/area/A = get_area(T)
- if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
- to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.")
- S.target = null
- return TRUE
- else if(istype(A, /area/engine/supermatter))
- to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
- S.target = null
- return TRUE
- return ..()
-
-/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- var/isonshuttle = istype(get_area(src), /area/shuttle)
- for(var/turf/T in range(1, src))
- var/area/A = get_area(T)
- if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle)))
- to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.")
- S.target = null
- return TRUE
- else if(istype(A, /area/engine/supermatter))
- to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
- S.target = null
- return TRUE
- return ..()
-
-/obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//Wiring would be too effective as a resource
- to_chat(S, "This object does not contain enough materials to work with.")
- return FALSE
-
-/obj/machinery/porta_turret/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Attempting to dismantle this machine would result in an immediate counterattack. Aborting.")
- return FALSE
-
-/obj/machinery/porta_turret_cover/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Attempting to dismantle this machine would result in an immediate counterattack. Aborting.")
- return FALSE
-
-/mob/living/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- S.DisperseTarget(src)
- return TRUE
-
-/mob/living/simple_animal/slime/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This biological resource is somehow resisting our bluespace transceiver. Aborting.")
- return FALSE
-
-/obj/machinery/drone_dispenser/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This object is receiving unactivated swarmer shells to help us. Aborting.")
- return FALSE
-
-/obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- var/turf/here = get_turf(src)
- for(var/A in here.contents)
- var/obj/structure/cable/C = A
- if(istype(C))
- to_chat(S, "Disrupting the power grid would bring no benefit to us. Aborting.")
- return FALSE
- return ..()
-
-/obj/item/deactivated_swarmer/IntegrateAmount()
- return 50
-
-/obj/machinery/hydroponics/soil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This object does not contain enough materials to work with.")
- return FALSE
-
-/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Destroying this object would cause a catastrophic chain reaction. Aborting.")
- return FALSE
-
-/obj/machinery/field/containment/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This object does not contain solid matter. Aborting.")
- return FALSE
-
-/obj/machinery/power/shieldwallgen/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "Destroying this object would have an unpredictable effect on structure integrity. Aborting.")
- return FALSE
-
-/obj/machinery/shieldwall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- to_chat(S, "This object does not contain solid matter. Aborting.")
- return FALSE
-
-////END CTRL CLICK FOR SWARMERS////
-
-/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(atom/fabrication_object,fabrication_cost = 0)
- if(!isturf(loc))
- to_chat(src, "This is not a suitable location for fabrication. We need more space.")
- if(resources >= fabrication_cost)
- resources -= fabrication_cost
- else
- to_chat(src, "You do not have the necessary resources to fabricate this object.")
- return
- return new fabrication_object(loc)
-
-/mob/living/simple_animal/hostile/swarmer/proc/Integrate(atom/movable/target)
- var/resource_gain = target.IntegrateAmount()
- if(resources + resource_gain > max_resources)
- to_chat(src, "We cannot hold more materials!")
- return TRUE
- if(resource_gain)
- resources += resource_gain
- do_attack_animation(target)
- changeNext_move(CLICK_CD_MELEE)
- var/obj/effect/temp_visual/swarmer/integrate/I = new /obj/effect/temp_visual/swarmer/integrate(get_turf(target))
- I.pixel_x = target.pixel_x
- I.pixel_y = target.pixel_y
- I.pixel_z = target.pixel_z
- if(istype(target, /obj/item/stack))
- var/obj/item/stack/S = target
- S.use(1)
- if(S.amount)
- return TRUE
- qdel(target)
- return TRUE
- else
- to_chat(src, "[target] is incompatible with our internal matter recycler.")
- return FALSE
-
-
-/mob/living/simple_animal/hostile/swarmer/proc/DisIntegrate(atom/movable/target)
- new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
- do_attack_animation(target)
- changeNext_move(CLICK_CD_MELEE)
- SSexplosions.lowobj += target
-
-/mob/living/simple_animal/hostile/swarmer/proc/DisperseTarget(mob/living/target)
- if(target == src)
- return
-
- if(!is_station_level(z) && !is_mining_level(z))
- to_chat(src, "Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area.")
- return
-
- to_chat(src, "Attempting to remove this being from our presence.")
-
- if(!do_mob(src, target, 30))
- return
-
- var/turf/open/floor/F
- F = find_safe_turf(zlevels = z, extended_safety_checks = TRUE)
-
- if(!F)
- return
- // If we're getting rid of a human, slap some energy cuffs on
- // them to keep them away from us a little longer
-
- var/mob/living/carbon/human/H = target
- if(ishuman(target) && (!H.handcuffed))
- H.handcuffed = new /obj/item/restraints/handcuffs/energy/used(H)
- H.update_handcuffed()
- log_combat(src, H, "handcuffed")
-
- var/datum/effect_system/spark_spread/S = new
- S.set_up(4,0,get_turf(target))
- S.start()
- playsound(src,'sound/effects/sparks4.ogg',50,TRUE)
- do_teleport(target, F, 0, channel = TELEPORT_CHANNEL_BLUESPACE)
-
-/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
- if(!(flags & SHOCK_TESLA))
- return FALSE
- return ..()
-
-/mob/living/simple_animal/hostile/swarmer/proc/DismantleMachine(obj/machinery/target)
- do_attack_animation(target)
- to_chat(src, "We begin to dismantle this machine. We will need to be uninterrupted.")
- var/obj/effect/temp_visual/swarmer/dismantle/D = new /obj/effect/temp_visual/swarmer/dismantle(get_turf(target))
- D.pixel_x = target.pixel_x
- D.pixel_y = target.pixel_y
- D.pixel_z = target.pixel_z
- if(do_mob(src, target, 100))
- to_chat(src, "Dismantling complete.")
- var/atom/Tsec = target.drop_location()
- new /obj/item/stack/sheet/metal(Tsec, 5)
- for(var/obj/item/I in target.component_parts)
- I.forceMove(Tsec)
- var/obj/effect/temp_visual/swarmer/disintegration/N = new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
- N.pixel_x = target.pixel_x
- N.pixel_y = target.pixel_y
- N.pixel_z = target.pixel_z
- target.dropContents()
- if(istype(target, /obj/machinery/computer))
- var/obj/machinery/computer/C = target
- if(C.circuit)
- C.circuit.forceMove(Tsec)
- qdel(target)
-
-
-/obj/effect/temp_visual/swarmer //temporary swarmer visual feedback objects
- icon = 'icons/mob/swarmer.dmi'
- layer = BELOW_MOB_LAYER
-
-/obj/effect/temp_visual/swarmer/disintegration
- icon_state = "disintegrate"
- duration = 10
-
-/obj/effect/temp_visual/swarmer/disintegration/Initialize()
- . = ..()
- playsound(loc, "sparks", 100, TRUE)
-
-/obj/effect/temp_visual/swarmer/dismantle
- icon_state = "dismantle"
- duration = 25
-
-/obj/effect/temp_visual/swarmer/integrate
- icon_state = "integrate"
- duration = 5
-
-/obj/structure/swarmer //Default swarmer effect object visual feedback
- name = "swarmer ui"
- desc = null
- gender = NEUTER
- icon = 'icons/mob/swarmer.dmi'
- icon_state = "ui_light"
- layer = MOB_LAYER
- resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
- light_color = LIGHT_COLOR_CYAN
- max_integrity = 30
- anchored = TRUE
- 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()
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- 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
- density = FALSE
-
-/obj/structure/swarmer/trap/Crossed(atom/movable/AM)
- if(isliving(AM))
- var/mob/living/L = AM
- if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
- playsound(loc,'sound/effects/snap.ogg',50, TRUE, -1)
- L.electrocute_act(10, src, 1, flags = SHOCK_NOGLOVES|SHOCK_ILLUSION)
- if(iscyborg(L))
- L.Paralyze(100)
- qdel(src)
- ..()
-
-/mob/living/simple_animal/hostile/swarmer/proc/CreateTrap()
- set name = "Create trap"
- set category = "Swarmer"
- set desc = "Creates a simple trap that will non-lethally electrocute anything that steps on it. Costs 5 resources."
- if(locate(/obj/structure/swarmer/trap) in loc)
- to_chat(src, "There is already a trap here. Aborting.")
- return
- Fabricate(/obj/structure/swarmer/trap, 5)
-
-
-/mob/living/simple_animal/hostile/swarmer/proc/CreateBarricade()
- set name = "Create barricade"
- set category = "Swarmer"
- set desc = "Creates a barricade that will stop anything but swarmers and disabler beams from passing through."
- if(locate(/obj/structure/swarmer/blockade) in loc)
- to_chat(src, "There is already a blockade here. Aborting.")
- return
- if(resources < 5)
- to_chat(src, "We do not have the resources for this!")
- return
- if(do_mob(src, src, 10))
- Fabricate(/obj/structure/swarmer/blockade, 5)
-
-
-/obj/structure/swarmer/blockade
- name = "swarmer blockade"
- desc = "A quickly assembled energy blockade. 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
- density = TRUE
-
-/obj/structure/swarmer/blockade/CanAllowThrough(atom/movable/O)
- . = ..()
- if(isswarmer(O))
- return TRUE
- if(istype(O, /obj/projectile/beam/disabler))
- return TRUE
-
-/mob/living/simple_animal/hostile/swarmer/proc/CreateSwarmer()
- set name = "Replicate"
- set category = "Swarmer"
- set desc = "Creates a shell for a new swarmer. Swarmers will self activate."
- to_chat(src, "We are attempting to replicate ourselves. We will need to stand still until the process is complete.")
- if(resources < 50)
- to_chat(src, "We do not have the resources for this!")
- return
- if(!isturf(loc))
- to_chat(src, "This is not a suitable location for replicating ourselves. We need more room.")
- return
- if(do_mob(src, src, 100))
- var/createtype = SwarmerTypeToCreate()
- if(createtype && Fabricate(createtype, 50))
- playsound(loc,'sound/items/poster_being_created.ogg',50, TRUE, -1)
-
-
-/mob/living/simple_animal/hostile/swarmer/proc/SwarmerTypeToCreate()
- return /obj/effect/mob_spawn/swarmer
-
-
-/mob/living/simple_animal/hostile/swarmer/proc/RepairSelf()
- set name = "Self Repair"
- set category = "Swarmer"
- set desc = "Attempts to repair damage to our body. You will have to remain motionless until repairs are complete."
- if(!isturf(loc))
- return
- to_chat(src, "Attempting to repair damage to our body, stand by...")
- if(do_mob(src, src, 100))
- adjustHealth(-100)
- to_chat(src, "We successfully repaired ourselves.")
-
-/mob/living/simple_animal/hostile/swarmer/proc/ToggleLight()
- if(!light_range)
- set_light(3)
- else
- set_light(0)
-
-/mob/living/simple_animal/hostile/swarmer/proc/swarmer_chat(msg)
- var/rendered = "Swarm communication - [src] [say_quote(msg)]"
- for(var/i in GLOB.mob_list)
- var/mob/M = i
- if(isswarmer(M))
- to_chat(M, rendered)
- if(isobserver(M))
- var/link = FOLLOW_LINK(M, src)
- to_chat(M, "[link] [rendered]")
-
-/mob/living/simple_animal/hostile/swarmer/proc/ContactSwarmers()
- var/message = stripped_input(src, "Announce to other swarmers", "Swarmer contact")
- // TODO get swarmers their own colour rather than just boldtext
- if(message)
- swarmer_chat(message)
+ if(!mind.has_antag_datum(/datum/antagonist/swarmer))
+ mind.add_antag_datum(/datum/antagonist/swarmer)
diff --git a/code/modules/antagonists/swarmer/swarmer_event.dm b/code/modules/antagonists/swarmer/swarmer_event.dm
deleted file mode 100644
index e086485a49c..00000000000
--- a/code/modules/antagonists/swarmer/swarmer_event.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-/datum/round_event_control/spawn_swarmer
- name = "Spawn Swarmer Shell"
- typepath = /datum/round_event/spawn_swarmer
- weight = 7
- max_occurrences = 1 //Only once okay fam
- earliest_start = 30 MINUTES
- min_players = 15
-
-
-/datum/round_event/spawn_swarmer
-
-/datum/round_event/spawn_swarmer/start()
- if(find_swarmer())
- return 0
- if(!GLOB.the_gateway)
- return 0
- new /obj/effect/mob_spawn/swarmer(get_turf(GLOB.the_gateway))
- if(prob(25)) //25% chance to announce it to the crew
- var/swarmer_report = "[command_name()] High-Priority Update"
- swarmer_report += "
Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come through."
- print_command_report(swarmer_report, announce=TRUE)
-
-/datum/round_event/spawn_swarmer/proc/find_swarmer()
- for(var/i in GLOB.mob_living_list)
- var/mob/living/L = i
- if(istype(L, /mob/living/simple_animal/hostile/swarmer) && L.client) //If there is a swarmer with an active client, we've found our swarmer
- return 1
- return 0
diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm
index 0df08954398..115dfdf3ff6 100644
--- a/code/modules/cargo/exports/parts.dm
+++ b/code/modules/cargo/exports/parts.dm
@@ -15,11 +15,6 @@
unit_name = "solar panel control board"
export_types = list(/obj/item/circuitboard/computer/solar_control)
-/datum/export/swarmer
- cost = 2000
- unit_name = "deactivated alien deconstruction drone"
- export_types = list(/obj/item/deactivated_swarmer)
-
//Computer Tablets and Parts
/datum/export/modular_part
cost = 15
diff --git a/code/modules/events/swarmer.dm b/code/modules/events/swarmer.dm
new file mode 100644
index 00000000000..ffb79a43e72
--- /dev/null
+++ b/code/modules/events/swarmer.dm
@@ -0,0 +1,27 @@
+/datum/round_event_control/spawn_swarmer
+ name = "Spawn Swarmer Beacon"
+ typepath = /datum/round_event/spawn_swarmer
+ weight = 8
+ max_occurrences = 1 //Only once okay fam
+ earliest_start = 30 MINUTES
+ min_players = 15
+
+/datum/round_event/spawn_swarmer/announce(fake)
+ priority_announce("Our long-range sensors have detected that your station's defenses have been breached by some sort of alien device. We suggest searching for and destroying it as soon as possible.", "[command_name()] High-Priority Update")
+
+/datum/round_event/spawn_swarmer
+ announceWhen = 70
+
+/datum/round_event/spawn_swarmer/start()
+ var/list/spawn_locs = list()
+ for(var/x in GLOB.xeno_spawn)
+ var/turf/spawn_turf = x
+ var/light_amount = spawn_turf.get_lumcount()
+ if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD)
+ spawn_locs += spawn_turf
+ if(!spawn_locs.len)
+ message_admins("No valid spawn locations found in GLOB.xeno_spawn, aborting swarmer spawning...")
+ return MAP_ERROR
+ var/obj/structure/swarmer_beacon/new_beacon = new /obj/structure/swarmer_beacon(pick(spawn_locs))
+ log_game("A Swarmer Beacon was spawned via an event.")
+ notify_ghosts("\A Swarmer Beacon has spawned!", source = new_beacon, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Swarmer Beacon Spawned")
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index de280a579d1..c0c9e3018d8 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -102,7 +102,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
/mob/living/simple_animal/hostile/swarmer/ai/Initialize()
. = ..()
- ToggleLight() //so you can see them eating you out of house and home/shooting you/stunlocking you for eternity
+ toggle_light() //so you can see them eating you out of house and home/shooting you/stunlocking you for eternity
LAZYINITLIST(GLOB.AISwarmersByType[type])
GLOB.AISwarmers += src
GLOB.AISwarmersByType[type] += src
@@ -114,7 +114,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
return ..()
-/mob/living/simple_animal/hostile/swarmer/ai/SwarmerTypeToCreate()
+/mob/living/simple_animal/hostile/swarmer/ai/swarmer_type_to_create()
return GetUncappedAISwarmerType()
@@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
if(!stop_automated_movement)
if(health < maxHealth*0.25)
StartAction(100)
- RepairSelf()
+ repair_self()
return
@@ -211,16 +211,16 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
if(!stop_automated_movement)
if(GLOB.AISwarmers.len < GetTotalAISwarmerCap() && resources >= 50)
StartAction(100) //so they'll actually sit still and use the verbs
- CreateSwarmer()
+ create_swarmer()
return
if(resources > 5)
if(prob(5)) //lower odds, as to prioritise reproduction
StartAction(10) //not a typo
- CreateBarricade()
+ create_barricade()
return
if(prob(5))
- CreateTrap()
+ create_trap()
return
@@ -267,7 +267,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
if(isliving(target))
if(prob(35))
StartAction(30)
- DisperseTarget(target)
+ prepare_target(target)
else
var/mob/living/L = target
L.attack_animal(src)
diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm
new file mode 100644
index 00000000000..67dc4aa9ac2
--- /dev/null
+++ b/code/modules/swarmers/swarmer.dm
@@ -0,0 +1,457 @@
+/**
+ * # Swarmer
+ *
+ * Tiny machines made by an ancient civilization, they seek only to consume materials and replicate.
+ *
+ * Tiny robots which, while not lethal, seek to destroy station components in order to recycle them into more swarmers.
+ * Sentient player swarmers spawn from a beacon spawned in maintenance and they can spawn melee swarmers to protect them.
+ * Swarmers have the following abilities:
+ * - Can melee targets to deal stamina damage. Stuns cyborgs.
+ * - Can teleport friend and foe alike away using ctrl + click. Applies binds to carbons, preventing them from immediate retaliation
+ * - Can shoot lasers which deal stamina damage to carbons and direct damage to simple mobs
+ * - Can self repair for free, completely healing themselves
+ * - Can construct traps which stun targets, and walls which block non-swarmer entites and projectiles
+ * - Can create swarmer drones, which lack the above abilities sans melee stunning targets. A swarmer can order its drones around by middle-clicking a tile.
+ */
+
+/mob/living/simple_animal/hostile/swarmer
+ name = "swarmer"
+ icon = 'icons/mob/swarmer.dmi'
+ desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
+ speak_emote = list("tones")
+ initial_language_holder = /datum/language_holder/swarmer
+ bubble_icon = "swarmer"
+ mob_biotypes = MOB_ROBOTIC
+ health = 40
+ maxHealth = 40
+ status_flags = CANPUSH
+ icon_state = "swarmer"
+ icon_living = "swarmer"
+ icon_dead = "swarmer_unactivated"
+ icon_gib = null
+ wander = 0
+ harm_intent_damage = 5
+ minbodytemp = 0
+ maxbodytemp = 500
+ 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)
+ unsuitable_atmos_damage = 0
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ melee_damage_type = STAMINA
+ damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
+ hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD)
+ obj_damage = 0
+ environment_smash = ENVIRONMENT_SMASH_NONE
+ attack_verb_continuous = "shocks"
+ attack_verb_simple = "shock"
+ attack_sound = 'sound/effects/empulse.ogg'
+ friendly_verb_continuous = "pinches"
+ friendly_verb_simple = "pinch"
+ speed = 0
+ faction = list("swarmer")
+ AIStatus = AI_OFF
+ pass_flags = PASSTABLE
+ mob_size = MOB_SIZE_TINY
+ ventcrawler = VENTCRAWLER_ALWAYS
+ ranged = 1
+ projectiletype = /obj/projectile/beam/disabler/swarmer
+ ranged_cooldown_time = 20
+ projectilesound = 'sound/weapons/taser2.ogg'
+ loot = list(/obj/effect/decal/cleanable/robot_debris, /obj/item/stack/ore/bluespace_crystal)
+ del_on_death = 1
+ deathmessage = "explodes with a sharp pop!"
+ light_color = LIGHT_COLOR_CYAN
+ hud_type = /datum/hud/swarmer
+ speech_span = SPAN_ROBOT
+ ///Resource points, generated by consuming metal/glass
+ var/resources = 0
+ ///Maximum amount of resources a swarmer can store
+ var/max_resources = 100
+ ///List used for player swarmers to keep track of their drones
+ var/list/mob/living/simple_animal/hostile/swarmer/melee/dronelist
+
+/mob/living/simple_animal/hostile/swarmer/Initialize()
+ . = ..()
+ verbs -= /mob/living/verb/pulled
+ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
+ diag_hud.add_to_hud(src)
+
+/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
+ var/image/holder = hud_list[DIAG_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
+
+/mob/living/simple_animal/hostile/swarmer/med_hud_set_status()
+ var/image/holder = hud_list[DIAG_STAT_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon_state = "hudstat"
+
+/mob/living/simple_animal/hostile/swarmer/Stat()
+ ..()
+ if(statpanel("Status"))
+ stat("Resources:",resources)
+
+/mob/living/simple_animal/hostile/swarmer/emp_act()
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ if(health > 1)
+ adjustHealth(health-1)
+ else
+ death()
+
+/mob/living/simple_animal/hostile/swarmer/CanAllowThrough(atom/movable/O)
+ . = ..()
+ if(istype(O, /obj/projectile/beam/disabler))//Allows for swarmers to fight as a group without wasting their shots hitting each other
+ return TRUE
+ if(isswarmer(O))
+ return TRUE
+
+////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
+/mob/living/simple_animal/hostile/swarmer/AttackingTarget()
+ if(!isliving(target))
+ return target.swarmer_act(src)
+ if(iscyborg(target))
+ var/mob/living/silicon/borg = target
+ borg.adjustBruteLoss(melee_damage_lower)
+ return ..()
+
+/mob/living/simple_animal/hostile/swarmer/MiddleClickOn(atom/A)
+ . = ..()
+ if(!LAZYLEN(dronelist))
+ return
+ var/turf/clicked_turf = get_turf(A)
+ if(!clicked_turf)
+ return
+ for(var/d in dronelist)
+ var/mob/living/simple_animal/hostile/drone = d
+ drone.LoseTarget()
+ drone.Goto(clicked_turf, drone.move_to_delay)
+
+/mob/living/simple_animal/hostile/swarmer/CtrlClickOn(atom/A)
+ face_atom(A)
+ if(!isturf(loc))
+ return
+ if(next_move > world.time)
+ return
+ if(!A.Adjacent(src))
+ return
+ prepare_target(src)
+
+////END CTRL CLICK FOR SWARMERS////
+
+/**
+ * Called when a swarmer creates a structure or drone
+ *
+ * Proc called whenever a swarmer creates a structure or drone
+ * Arguments:
+ * * fabrication_object - The atom to create
+ * * fabrication_cost - How many resources it costs for a swarmer to create the object
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(atom/fabrication_object,fabrication_cost = 0)
+ if(!isturf(loc))
+ to_chat(src, "This is not a suitable location for fabrication. We need more space.")
+ return
+ if(resources < fabrication_cost)
+ to_chat(src, "You do not have the necessary resources to fabricate this object.")
+ return
+ resources -= fabrication_cost
+ return new fabrication_object(drop_location())
+
+/**
+ * Called when a swarmer attempts to consume an object
+ *
+ * Proc which determines interaction between a swarmer and whatever it is attempting to consume
+ * Arguments:
+ * * target - The material or object the swarmer is attempting to consume
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/Integrate(obj/target)
+ var/resource_gain = target.integrate_amount()
+ if(resources + resource_gain > max_resources)
+ to_chat(src, "We cannot hold more materials!")
+ return TRUE
+ if(!resource_gain)
+ to_chat(src, "[target] is incompatible with our internal matter recycler.")
+ return FALSE
+ resources += resource_gain
+ do_attack_animation(target)
+ changeNext_move(CLICK_CD_RAPID)
+ var/obj/effect/temp_visual/swarmer/integrate/I = new /obj/effect/temp_visual/swarmer/integrate(get_turf(target))
+ I.pixel_x = target.pixel_x
+ I.pixel_y = target.pixel_y
+ I.pixel_z = target.pixel_z
+ if(istype(target, /obj/item/stack))
+ var/obj/item/stack/S = target
+ S.use(1)
+ if(S.amount)
+ return TRUE
+ qdel(target)
+ return TRUE
+
+/**
+ * Called when a swarmer attempts to destroy a structure
+ *
+ * Proc which determines interaction between a swarmer and a structure it is destroying
+ * Arguments:
+ * * target - The material or object the swarmer is attempting to destroy
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/dis_integrate(atom/movable/target)
+ new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
+ do_attack_animation(target)
+ changeNext_move(CLICK_CD_MELEE)
+ SSexplosions.lowobj += target
+
+/**
+ * Called when a swarmer attempts to teleport a living entity away
+ *
+ * Proc which finds a safe location to teleport a living entity to when a swarmer teleports it away. Also energy handcuffs carbons.
+ * Arguments:
+ * * target - The entity the swarmer is trying to teleport away
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/prepare_target(mob/living/target)
+ if(target == src)
+ return
+
+ if(!is_station_level(z) && !is_mining_level(z))
+ to_chat(src, "Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area.")
+ return
+
+ to_chat(src, "Attempting to remove this being from our presence.")
+
+ if(!do_mob(src, target, 30))
+ return
+
+ teleport_target(target)
+
+/mob/living/simple_animal/hostile/swarmer/proc/teleport_target(mob/living/target)
+ var/turf/open/floor/safe_turf = find_safe_turf(zlevels = z, extended_safety_checks = TRUE)
+
+ if(!safe_turf )
+ return
+ // If we're getting rid of a human, slap some energy cuffs on
+ // them to keep them away from us a little longer
+
+ if(ishuman(target))
+ var/mob/living/carbon/human/victim = target
+ if(!victim.handcuffed)
+ victim.handcuffed = new /obj/item/restraints/handcuffs/energy/used(victim)
+ victim.update_handcuffed()
+ log_combat(src, victim, "handcuffed")
+
+ var/datum/effect_system/spark_spread/sparks = new
+ sparks.set_up(4,0,get_turf(target))
+ sparks.start()
+ playsound(src, 'sound/effects/sparks4.ogg', 50, TRUE)
+ do_teleport(target, safe_turf , 0, channel = TELEPORT_CHANNEL_BLUESPACE)
+
+/mob/living/simple_animal/hostile/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ if(!(flags & SHOCK_TESLA))
+ return FALSE
+ return ..()
+
+/**
+ * Called when a swarmer attempts to disassemble a machine
+ *
+ * Proc called when a swarmer attempts to disassemble a machine. Destroys the machine, and gives the swarmer metal.
+ * Arguments:
+ * * target - The machine the swarmer is attempting to disassemble
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/dismantle_machine(obj/machinery/target)
+ do_attack_animation(target)
+ to_chat(src, "We begin to dismantle this machine. We will need to be uninterrupted.")
+ var/obj/effect/temp_visual/swarmer/dismantle/dismantle_effect = new /obj/effect/temp_visual/swarmer/dismantle(get_turf(target))
+ dismantle_effect.pixel_x = target.pixel_x
+ dismantle_effect.pixel_y = target.pixel_y
+ dismantle_effect.pixel_z = target.pixel_z
+ if(do_mob(src, target, 100))
+ to_chat(src, "Dismantling complete.")
+ var/atom/target_loc = target.drop_location()
+ new /obj/item/stack/sheet/metal(target_loc, 5)
+ for(var/p in target.component_parts)
+ var/obj/item/part = p
+ part.forceMove(target_loc)
+ var/obj/effect/temp_visual/swarmer/disintegration/disintegration_effect = new /obj/effect/temp_visual/swarmer/disintegration(get_turf(target))
+ disintegration_effect.pixel_x = target.pixel_x
+ disintegration_effect.pixel_y = target.pixel_y
+ disintegration_effect.pixel_z = target.pixel_z
+ target.dropContents()
+ if(istype(target, /obj/machinery/computer))
+ var/obj/machinery/computer/computer_target = target
+ if(computer_target.circuit)
+ computer_target.circuit.forceMove(target_loc)
+ qdel(target)
+
+/**
+ * Called when a swarmer attempts to create a trap
+ *
+ * Proc used to allow a swarmer to create a trap. Checks if a trap is on the tile, then if the swarmer can afford, and then places the trap.
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/create_trap()
+ set name = "Create trap"
+ set category = "Swarmer"
+ set desc = "Creates a simple trap that will non-lethally electrocute anything that steps on it. Costs 4 resources."
+ if(locate(/obj/structure/swarmer/trap) in loc)
+ to_chat(src, "There is already a trap here. Aborting.")
+ return
+ if(resources < 4)
+ to_chat(src, "We do not have the resources for this!")
+ return
+ Fabricate(/obj/structure/swarmer/trap, 4)
+
+/**
+ * Called when a swarmer attempts to create a barricade
+ *
+ * Proc used to allow a swarmer to create a barricade. Checks if a barricade is on the tile, then if the swarmer can afford it, and then will attempt to create a barricade after a second delay.
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/create_barricade()
+ set name = "Create barricade"
+ set category = "Swarmer"
+ set desc = "Creates a barricade that will stop anything but swarmers and disabler beams from passing through. Costs 4 resources."
+ if(locate(/obj/structure/swarmer/blockade) in loc)
+ to_chat(src, "There is already a blockade here. Aborting.")
+ return
+ if(resources < 4)
+ to_chat(src, "We do not have the resources for this!")
+ return
+ if(!do_mob(src, src, 1 SECONDS))
+ return
+ Fabricate(/obj/structure/swarmer/blockade, 4)
+
+/**
+ * Called when a swarmer attempts to create a drone
+ *
+ * Proc used to allow a swarmer to create a drone. Checks if the swarmer can afford the drone, then creates it after 5 seconds, and also registers it to the creating swarmer so it can command it
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/create_swarmer()
+ set name = "Replicate"
+ set category = "Swarmer"
+ set desc = "Creates a duplicate of ourselves, capable of protecting us while we complete our objectives."
+ to_chat(src, "We are attempting to replicate ourselves. We will need to stand still until the process is complete.")
+ if(resources < 20)
+ to_chat(src, "We do not have the resources for this!")
+ return
+ if(!isturf(loc))
+ to_chat(src, "This is not a suitable location for replicating ourselves. We need more room.")
+ return
+ if(!do_mob(src, src, 5 SECONDS))
+ return
+ var/createtype = swarmer_type_to_create()
+ if(!createtype)
+ return
+ var/mob/newswarmer = Fabricate(createtype, 20)
+ add_drone(newswarmer)
+ playsound(loc,'sound/items/poster_being_created.ogg', 20, TRUE, -1)
+
+/**
+ * Used to determine what type of swarmer a swarmer should create
+ *
+ * Returns the type of the swarmer to be created
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/swarmer_type_to_create()
+ return /mob/living/simple_animal/hostile/swarmer/melee
+
+/**
+ * Called when a swarmer attempts to repair itself
+ *
+ * Proc used to allow a swarmer self-repair. If the swarmer does not move after a period of time, then it will heal fully
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/repair_self()
+ if(!isturf(loc))
+ return
+ to_chat(src, "Attempting to repair damage to our body, stand by...")
+ if(!do_mob(src, src, 10 SECONDS))
+ return
+ adjustHealth(-maxHealth)
+ to_chat(src, "We successfully repaired ourselves.")
+
+/**
+ * Called when a swarmer toggles its light
+ *
+ * Proc used to allow a swarmer to toggle its light on and off. If a swarmer has any drones, change their light settings to match their master's.
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/toggle_light()
+ if(!light_range)
+ set_light(3)
+ if(!mind)
+ return
+ for(var/d in dronelist)
+ var/mob/living/simple_animal/hostile/swarmer/melee/drone = d
+ drone.set_light(3)
+ else
+ set_light(0)
+ if(!mind)
+ return
+ for(var/d in dronelist)
+ var/mob/living/simple_animal/hostile/swarmer/melee/drone = d
+ drone.set_light(0)
+
+/**
+ * Proc which is used for swarmer comms
+ *
+ * Proc called which sends a message to all other swarmers.
+ * Arugments:
+ * * msg - The message the swarmer is sending, gotten from ContactSwarmers()
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/swarmer_chat(msg)
+ var/rendered = "Swarm communication - [src] [say_quote(msg)]"
+ for(var/i in GLOB.mob_list)
+ var/mob/listener = i
+ if(isswarmer(listener))
+ to_chat(listener, rendered)
+ else if(isobserver(listener))
+ var/link = FOLLOW_LINK(listener, src)
+ to_chat(listener, "[link] [rendered]")
+
+/**
+ * Proc which is used for inputting a swarmer message
+ *
+ * Proc which is used for a swarmer to input a message on a pop-up box, then attempt to send that message to the other swarmers
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/contact_swarmers()
+ var/message = stripped_input(src, "Announce to other swarmers", "Swarmer contact")
+ // TODO get swarmers their own colour rather than just boldtext
+ if(message)
+ swarmer_chat(message)
+
+
+///Adds a drone to the swarmer list and keeps track of it in case it's deleted and requires cleanup.
+/mob/living/simple_animal/hostile/swarmer/proc/add_drone(mob/newswarmer)
+ LAZYADD(dronelist, newswarmer)
+ RegisterSignal(newswarmer, COMSIG_PARENT_QDELETING, .proc/remove_drone, newswarmer)
+
+
+/**
+ * Removes a drone from the swarmer's list.
+ *
+ * Removes the drone from our list.
+ * Called specifically when a drone is about to be destroyed, so we don't have any null references.
+ * Arguments:
+ * * mob/drone - The drone to be removed from the list.
+ */
+/mob/living/simple_animal/hostile/swarmer/proc/remove_drone(mob/drone, force)
+ UnregisterSignal(drone, COMSIG_PARENT_QDELETING)
+ dronelist -= drone
+
+/**
+ * # Swarmer Drone
+ *
+ * Melee subtype of swarmers, always AI-controlled under normal circumstances. Cannot fire projectiles, but does double stamina damage on melee
+ */
+/mob/living/simple_animal/hostile/swarmer/melee
+ icon_state = "swarmer_melee"
+ icon_living = "swarmer_melee"
+ ranged = FALSE
+ AIStatus = AI_ON
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+
+/obj/projectile/beam/disabler/swarmer/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(!istype(target, /mob/living/simple_animal) || !istype(firer, /mob/living/simple_animal/hostile/swarmer))
+ return
+ var/mob/living/simple_animal/hostile/swarmer/swarmer = firer
+ swarmer.teleport_target(target)
diff --git a/code/modules/swarmers/swarmer_act.dm b/code/modules/swarmers/swarmer_act.dm
new file mode 100644
index 00000000000..f6429fad864
--- /dev/null
+++ b/code/modules/swarmers/swarmer_act.dm
@@ -0,0 +1,231 @@
+/**
+ * Determines what happens to an atom when a swarmer interacts with it
+ *
+ * Determines behavior upon being interacted on by a swarmer.
+ * Arguments:
+ * * S - A reference to the swarmer doing the interaction
+ */
+/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE //return TRUE/FALSE whether or not an AI swarmer should try this swarmer_act() again, NOT whether it succeeded.
+
+/obj/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ if(resistance_flags & INDESTRUCTIBLE)
+ return FALSE
+ for(var/mob/living/living_content in contents)
+ if(issilicon(living_content) || isbrain(living_content))
+ continue
+ to_chat(actor, "An organism has been detected inside this object. Aborting.")
+ return FALSE
+ return ..()
+
+/obj/item/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ return actor.Integrate(src)
+
+/**
+ * Return used to determine how many resources a swarmer gains when consuming an object
+ */
+/obj/proc/integrate_amount()
+ return 0
+
+/obj/item/integrate_amount() //returns the amount of resources gained when eating this item
+ if(custom_materials && (custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)] || custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]))
+ return 1
+ return ..()
+
+/obj/item/gun/swarmer_act()//Stops you from eating the entire armory
+ return FALSE
+
+/turf/open/swarmer_act()//ex_act() on turf calls it on its contents, this is to prevent attacking mobs by DisIntegrate()'ing the floor
+ return FALSE
+
+/obj/structure/lattice/catwalk/swarmer_catwalk/swarmer_act()
+ return FALSE
+
+/obj/structure/swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ if(actor.AIStatus == AI_ON)
+ return FALSE
+ return ..()
+
+/obj/effect/swarmer_act()
+ return FALSE
+
+/obj/effect/decal/cleanable/robot_debris/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ qdel(src)
+ return TRUE
+
+/obj/structure/swarmer_beacon/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This machine is required for further reproduction of swarmers. Aborting.")
+ return FALSE
+
+/obj/structure/flora/swarmer_act()
+ return FALSE
+
+/turf/open/lava/swarmer_act()
+ if(!is_safe())
+ new /obj/structure/lattice/catwalk/swarmer_catwalk(src)
+ return FALSE
+
+/obj/machinery/atmospherics/swarmer_act()
+ return FALSE
+
+/obj/structure/disposalpipe/swarmer_act()
+ return FALSE
+
+/obj/machinery/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dismantle_machine(src)
+ return TRUE
+
+/obj/machinery/light/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ var/isonshuttle = istype(get_area(src), /area/shuttle)
+ for(var/turf/turf_in_range in range(1, src))
+ var/area/turf_area = get_area(turf_in_range)
+ if(isspaceturf(turf_in_range) || (!isonshuttle && (istype(turf_area, /area/shuttle) || istype(turf_area, /area/space))) || (isonshuttle && !istype(turf_area, /area/shuttle)))
+ to_chat(actor, "Destroying this object has the potential to cause a hull breach. Aborting.")
+ actor.target = null
+ return FALSE
+ else if(istype(turf_area, /area/engine/supermatter))
+ to_chat(actor, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
+ actor.target = null
+ return FALSE
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ if(!QDELETED(actor)) //If it got blown up no need to turn it off.
+ toggle_cam(actor, FALSE)
+ return TRUE
+
+/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/gravity_generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/vending/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)//It's more visually interesting than dismantling the machine
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/turretid/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ actor.dis_integrate(src)
+ return TRUE
+
+/obj/machinery/chem_dispenser/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "The volatile chemicals in this machine would destroy us. Aborting.")
+ return FALSE
+
+/obj/machinery/nuclearbomb/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This device's destruction would result in the extermination of everything in the area. Aborting.")
+ return FALSE
+
+/obj/effect/rune/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Searching... sensor malfunction! Target lost. Aborting.")
+ return FALSE
+
+/obj/structure/reagent_dispensers/fueltank/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Destroying this object could cause a chain reaction. Aborting.")
+ return FALSE
+
+/obj/structure/cable/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Disrupting the power grid would bring no benefit to us. Aborting.")
+ return FALSE
+
+/obj/machinery/portable_atmospherics/canister/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "An inhospitable area may be created as a result of destroying this object. Aborting.")
+ return FALSE
+
+/obj/machinery/telecomms/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting.")
+ return FALSE
+
+/obj/machinery/deepfryer/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This kitchen appliance should be preserved, it will make delicious unhealthy snacks for our masters in the future. Aborting.")
+ return FALSE
+
+/obj/machinery/power/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Disrupting the power grid would bring no benefit to us. Aborting.")
+ return FALSE
+
+/obj/machinery/gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This bluespace source will be important to us later. Aborting.")
+ return FALSE
+
+/turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ var/isonshuttle = istype(loc, /area/shuttle)
+ for(var/turf/turf_in_range in range(1, src))
+ var/area/turf_area = get_area(turf_in_range)
+ if(isspaceturf(turf_area) || (!isonshuttle && (istype(turf_area, /area/shuttle) || istype(turf_area, /area/space))) || (isonshuttle && !istype(turf_area, /area/shuttle)))
+ to_chat(actor, "Destroying this object has the potential to cause a hull breach. Aborting.")
+ actor.target = null
+ return TRUE
+ else if(istype(turf_area, /area/engine/supermatter))
+ to_chat(actor, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
+ actor.target = null
+ return TRUE
+ return ..()
+
+/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ var/is_on_shuttle = istype(get_area(src), /area/shuttle)
+ for(var/turf/adj_turf in range(1, src))
+ var/area/adj_area = get_area(adj_turf)
+ if(isspaceturf(adj_turf) || (!is_on_shuttle && (istype(adj_area, /area/shuttle) || istype(adj_area, /area/space))) || (is_on_shuttle && !istype(adj_area, /area/shuttle)))
+ to_chat(actor, "Destroying this object has the potential to cause a hull breach. Aborting.")
+ actor.target = null
+ return TRUE
+ if(istype(adj_area, /area/engine/supermatter))
+ to_chat(actor, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.")
+ actor.target = null
+ return TRUE
+ return ..()
+
+/obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)//Wiring would be too effective as a resource
+ to_chat(actor, "This object does not contain enough materials to work with.")
+ return FALSE
+
+/obj/machinery/porta_turret/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Attempting to dismantle this machine would result in an immediate counterattack. Aborting.")
+ return FALSE
+
+/obj/machinery/porta_turret_cover/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Attempting to dismantle this machine would result in an immediate counterattack. Aborting.")
+ return FALSE
+
+/obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ var/turf/here = get_turf(src)
+ for(var/a in here.contents)
+ if(istype(a, /obj/structure/cable))
+ to_chat(actor, "Disrupting the power grid would bring no benefit to us. Aborting.")
+ return FALSE
+ return ..()
+
+/obj/machinery/hydroponics/soil/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This object does not contain enough materials to work with.")
+ return FALSE
+
+/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Destroying this object would cause a catastrophic chain reaction. Aborting.")
+ return FALSE
+
+/obj/machinery/field/containment/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This object does not contain solid matter. Aborting.")
+ return FALSE
+
+/obj/machinery/power/shieldwallgen/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "Destroying this object would have an unpredictable effect on structure integrity. Aborting.")
+ return FALSE
+
+/obj/machinery/shieldwall/swarmer_act(mob/living/simple_animal/hostile/swarmer/actor)
+ to_chat(actor, "This object does not contain solid matter. Aborting.")
+ return FALSE
diff --git a/code/modules/swarmers/swarmer_objs.dm b/code/modules/swarmers/swarmer_objs.dm
new file mode 100644
index 00000000000..7bbc7dedd25
--- /dev/null
+++ b/code/modules/swarmers/swarmer_objs.dm
@@ -0,0 +1,153 @@
+/obj/structure/swarmer //Default swarmer effect object visual feedback
+ name = "swarmer ui"
+ desc = null
+ gender = NEUTER
+ icon = 'icons/mob/swarmer.dmi'
+ icon_state = "ui_light"
+ layer = MOB_LAYER
+ resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ light_color = LIGHT_COLOR_CYAN
+ max_integrity = 30
+ anchored = TRUE
+ ///How strong the light effect for the structure is
+ var/glow_range = 1
+
+/obj/structure/swarmer/Initialize(mapload)
+ . = ..()
+ set_light(glow_range)
+
+/obj/structure/swarmer/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = NONE)
+ 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()
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ qdel(src)
+
+/**
+ * # Swarmer Beacon
+ *
+ * Beacon which creates sentient player swarmers.
+ *
+ * The beacon which creates sentient player swarmers during the swarmer event. Spawns in maint on xeno locations, and can create a player swarmer once every 30 seconds.
+ * The beacon cannot be damaged by swarmers, and must be destroyed to prevent the spawning of further player-controlled swarmers.
+ * Holds a swarmer within itself during the 30 seconds before releasing it and allowing for another swarmer to be spawned in.
+ */
+
+/obj/structure/swarmer_beacon
+ name = "swarmer beacon"
+ desc = "A machine that prints swarmers."
+ icon = 'icons/mob/swarmer.dmi'
+ icon_state = "swarmer_console"
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
+ max_integrity = 400
+ layer = MASSIVE_OBJ_LAYER
+ light_color = LIGHT_COLOR_CYAN
+ light_range = 10
+ anchored = TRUE
+ density = FALSE
+ ///Whether or not a swarmer is currently being created by this beacon
+ var/processing_swarmer = FALSE
+
+/obj/structure/swarmer_beacon/attack_ghost(mob/user)
+ . = ..()
+ if(processing_swarmer)
+ to_chat(user, "A swarmer is currently being created. Try again soon.")
+ return
+ que_swarmer(user)
+
+/**
+ * Interaction when a ghost interacts with a swarmer beacon
+ *
+ * Called when a ghost interacts with a swarmer beacon, allowing them to become a swarmer
+ * Arguments:
+ * * user - A reference to the ghost interacting with the beacon
+ */
+/obj/structure/swarmer_beacon/proc/que_swarmer(mob/user)
+ var/swarm_ask = alert("Become a swarmer?", "Do you wish to consume the station?", "Yes", "No")
+ if(swarm_ask == "No" || QDELETED(src) || QDELETED(user) || processing_swarmer)
+ return FALSE
+ var/mob/living/simple_animal/hostile/swarmer/newswarmer = new /mob/living/simple_animal/hostile/swarmer(src)
+ newswarmer.key = user.key
+ addtimer(CALLBACK(src, .proc/release_swarmer, newswarmer), 30 SECONDS)
+ to_chat(newswarmer, "SWARMER CONSTURCTION INITIALIZED. TIME TO COMPLETION: 30 SECONDS")
+ processing_swarmer = TRUE
+ return TRUE
+
+/**
+ * Releases a swarmer from the beacon and tells it what to do
+ *
+ * Occcurs 30 seconds after a ghost becomes a swarmer. The beacon releases it, tells it what to do, and opens itself up to spawn in a new swarmer.
+ * Arguments:
+ * * swarmer - The swarmer being released and told what to do
+ */
+/obj/structure/swarmer_beacon/proc/release_swarmer(mob/swarmer)
+ to_chat(swarmer, "SWARMER CONSTURCTION COMPLETED. OBJECTIVES:\n\
+ 1. CONSUME RESOURCES AND REPLICATE UNTIL THERE ARE NO MORE RESOURCES LEFT\n\
+ 2. ENSURE PROTECTION OF THE BEACON SO THIS LOCATION CAN BE INVADED AT A LATER DATE; DO NOT PERFORM ACTIONS THAT WOULD RENDER THIS LOCATION DANGEROUS OR INHOSPITABLE\n\
+ 3. BIOLOGICAL RESOURCES WILL BE HARVESTED AT A LATER DATE: DO NOT HARM THEM\n\
+ OPERATOR NOTES:\n\
+ - CONSUME RESOURCES TO CONSTRUCT TRAPS, BARRIERS, AND FOLLOWER DRONES\n\
+ - FOLLOWER DRONES CAN BE ORDERED TO MOVE VIA MIDDLE CLICKING ON A TILE. WHILE DRONES CANNOT ASSIST IN RESOURCE HARVESTING, THEY CAN PROTECT YOU FROM THREATS\n\
+ - LCTRL + ATTACKING AN ORGANIC WILL ALOW YOU TO REMOVE SAID ORGANIC FROM THE AREA\n\
+ - YOU AND YOUR DRONES HAVE A STUN EFFECT ON MELEE. YOU ARE ALSO ARMED WITH A DISABLER PROJECTILE, USE THESE TO PREVENT ORGANICS FROM HALTING YOUR PROGRESS\n\
+ GLORY TO !*# $*#^")
+ swarmer.forceMove(get_turf(src))
+ processing_swarmer = FALSE
+
+/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
+ density = FALSE
+
+/obj/structure/swarmer/trap/Crossed(atom/movable/AM)
+ if(isliving(AM))
+ var/mob/living/living_crosser = AM
+ if(!istype(living_crosser, /mob/living/simple_animal/hostile/swarmer))
+ playsound(loc,'sound/effects/snap.ogg',50, TRUE, -1)
+ living_crosser.electrocute_act(0, src, 1, flags = SHOCK_NOGLOVES|SHOCK_ILLUSION)
+ if(iscyborg(living_crosser))
+ living_crosser.Paralyze(100)
+ qdel(src)
+ return ..()
+
+/obj/structure/swarmer/blockade
+ name = "swarmer blockade"
+ desc = "A quickly assembled energy blockade. 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/blockade/CanAllowThrough(atom/movable/O)
+ . = ..()
+ if(isswarmer(O))
+ return TRUE
+ if(istype(O, /obj/projectile/beam/disabler))
+ return TRUE
+
+/obj/effect/temp_visual/swarmer //temporary swarmer visual feedback objects
+ icon = 'icons/mob/swarmer.dmi'
+ layer = BELOW_MOB_LAYER
+
+/obj/effect/temp_visual/swarmer/disintegration
+ icon_state = "disintegrate"
+ duration = 1 SECONDS
+
+/obj/effect/temp_visual/swarmer/disintegration/Initialize()
+ . = ..()
+ playsound(loc, "sparks", 100, TRUE)
+
+/obj/effect/temp_visual/swarmer/dismantle
+ icon_state = "dismantle"
+ duration = 2.5 SECONDS
+
+/obj/effect/temp_visual/swarmer/integrate
+ icon_state = "integrate"
+ duration = 0.5 SECONDS
diff --git a/tgstation.dme b/tgstation.dme
index 678ef083b27..169c50b7226 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1529,7 +1529,6 @@
#include "code\modules\antagonists\space_dragon\space_dragon.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
#include "code\modules\antagonists\swarmer\swarmer.dm"
-#include "code\modules\antagonists\swarmer\swarmer_event.dm"
#include "code\modules\antagonists\traitor\datum_traitor.dm"
#include "code\modules\antagonists\traitor\syndicate_contract.dm"
#include "code\modules\antagonists\traitor\equipment\contractor.dm"
@@ -1861,6 +1860,7 @@
#include "code\modules\events\spider_infestation.dm"
#include "code\modules\events\spontaneous_appendicitis.dm"
#include "code\modules\events\stray_cargo.dm"
+#include "code\modules\events\swarmer.dm"
#include "code\modules\events\vent_clog.dm"
#include "code\modules\events\wisdomcow.dm"
#include "code\modules\events\wormholes.dm"
@@ -3065,6 +3065,9 @@
#include "code\modules\surgery\organs\tails.dm"
#include "code\modules\surgery\organs\tongue.dm"
#include "code\modules\surgery\organs\vocal_cords.dm"
+#include "code\modules\swarmers\swarmer.dm"
+#include "code\modules\swarmers\swarmer_act.dm"
+#include "code\modules\swarmers\swarmer_objs.dm"
#include "code\modules\tgs\includes.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\states.dm"