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 += "The [name] were:" + 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"