diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index a0430b09569..901d4ecbab9 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -199,6 +199,8 @@ datum/hud/New(mob/owner)
else if(mymob.mind && mymob.mind.vampire)
vampire_hud()
human_hud('icons/mob/screen1_Vampire.dmi')
+ else if(isswarmer(mymob))
+ swarmer_hud()
//Triggered when F12 is pressed (Unless someone changed something in the DMF)
/mob/verb/button_pressed_F12(var/full = 0 as null)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 0c53fd9bcca..796eadccad0 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -219,10 +219,8 @@
if(!isanimal(user))
return
var/mob/living/simple_animal/M = user
- if(M.melee_damage_upper <= 0)
- return
- attack_generic(M, M.melee_damage_upper)
-
+ if(M.melee_damage_upper > 0 && (M.melee_damage_type == BRUTE || M.melee_damage_type == BURN))
+ attack_generic(M, M.melee_damage_upper)
/obj/machinery/door/window/attack_slime(mob/living/carbon/slime/user as mob)
if(!user.is_adult)
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 87485873569..16e3fb56d23 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -325,6 +325,11 @@ Class Procs:
/obj/machinery/CouldNotUseTopic(var/mob/user)
usr.unset_machine()
+/obj/machinery/proc/dropContents()//putting for swarmers, occupent code commented out, someone can use later.
+ var/turf/T = get_turf(src)
+ for(var/atom/movable/AM in contents)
+ AM.forceMove(T)
+
////////////////////////////////////////////////////////////////////////////////////////////
/obj/machinery/attack_ai(var/mob/user as mob)
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 7a9dca3fdef..8bd43923ad6 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -401,7 +401,7 @@ var/list/turret_icons
/obj/machinery/porta_turret/attack_animal(mob/living/simple_animal/M)
M.changeNext_move(CLICK_CD_MELEE)
M.do_attack_animation(src)
- if(M.melee_damage_upper == 0)
+ if(M.melee_damage_upper == 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN))
return
if(!(stat & BROKEN))
visible_message("[M] [M.attacktext] [src]!")
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index b15fa393e9b..a596db41b82 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -142,12 +142,14 @@
for (var/atom/movable/A as mob|obj in src)
A.forceMove(loc)
A.ex_act(severity++)
+ new /obj/item/stack/sheet/metal(loc)
qdel(src)
if(3)
if(prob(5))
for(var/atom/movable/A as mob|obj in src)
A.forceMove(loc)
A.ex_act(severity++)
+ new /obj/item/stack/sheet/metal(loc)
qdel(src)
/obj/structure/closet/bullet_act(var/obj/item/projectile/Proj)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index ddd121434d2..19c2dd13ec8 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -184,7 +184,7 @@
qdel(src)
return
if(3.0)
- if (prob(30))
+ if (prob(40))
var/remains = pick(/obj/item/stack/rods,/obj/item/stack/sheet/metal)
new remains(loc)
qdel(src)
diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
index 890d84a1f70..efdadfbb7c2 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
@@ -11,11 +11,13 @@
qdel(src)
return
if(2.0)
- if (prob(50))
+ if (prob(70))
+ new /obj/item/stack/sheet/metal(src.loc)
qdel(src)
return
if(3.0)
- if (prob(5))
+ if (prob(50))
+ new /obj/item/stack/sheet/metal(src.loc)
qdel(src)
return
return
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 928d0c5ec2c..2e38ae54079 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -153,7 +153,8 @@ var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8f
/obj/structure/window/attack_animal(mob/living/user as mob)
if(!isanimal(user)) return
var/mob/living/simple_animal/M = user
- if(M.melee_damage_upper <= 0) return
+ if(M.melee_damage_upper <= 0 || (M.melee_damage_type != BRUTE && M.melee_damage_type != BURN))
+ return
attack_generic(M, M.melee_damage_upper)
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index e7c77f639f3..964da2ca2b9 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -1,3 +1,4 @@
+var/obj/machinery/gateway/centerstation/the_gateway = null
/obj/machinery/gateway
name = "gateway"
desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations."
@@ -10,6 +11,8 @@
/obj/machinery/gateway/New()
+ if(!the_gateway)
+ the_gateway = src
spawn(25)
update_icon()
if(dir == 2)
@@ -37,12 +40,19 @@
var/obj/machinery/gateway/centeraway/awaygate = null
/obj/machinery/gateway/centerstation/New()
+ if(!the_gateway)
+ the_gateway = src
spawn(25)
update_icon()
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway) in world
+/obj/machinery/gateway/centerstation/Destroy()
+ if(the_gateway == src)
+ the_gateway = null
+ return ..()
+
/obj/machinery/gateway/centerstation/update_icon()
if(active)
icon_state = "oncenter"
@@ -243,4 +253,3 @@ obj/machinery/gateway/centerstation/process()
user << "Recalibration successful!: \black This gate's systems have been fine tuned. Travel to this gate will now be on target."
calibrated = 1
return
-
\ No newline at end of file
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 9eb6e4728b6..881ee29d402 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -169,7 +169,8 @@ var/list/event_last_fired = list()
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Bluespace Anomaly", /datum/event/anomaly/anomaly_bluespace, 50, list(ASSIGNMENT_ENGINEER = 25)),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Flux Anomaly", /datum/event/anomaly/anomaly_flux, 50, list(ASSIGNMENT_ENGINEER = 50)),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravitational Anomaly", /datum/event/anomaly/anomaly_grav, 200),
- new /datum/event_meta(EVENT_LEVEL_MODERATE, "Revenant", /datum/event/revenant, 150)
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Revenant", /datum/event/revenant, 150),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, list(ASSIGNMENT_ANY = 100), 1)
)
/datum/event_container/major
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index a5bb5b1388e..03ed2d630b4 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -508,6 +508,17 @@
drone_only = 1
follow = 1
+/datum/language/swarmer
+ name = "Swarmer"
+ desc = "A heavily encoded alien binary pattern."
+ speech_verb = "tones"
+ ask_verb = "tones"
+ exclaim_verb = "tones"
+ colour = "say_quote"
+ key = "z"//Zwarmer...Or Zerg!
+ flags = RESTRICTED || HIVEMIND
+ follow = 1
+
// Language handling.
/mob/proc/add_language(var/language)
diff --git a/code/modules/mob/living/carbon/alien/alien_defenses.dm b/code/modules/mob/living/carbon/alien/alien_defenses.dm
index ed98e221993..7ae01f43cfe 100644
--- a/code/modules/mob/living/carbon/alien/alien_defenses.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defenses.dm
@@ -66,5 +66,17 @@ In all, this is a lot like the monkey code. /N
/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M as mob)
if(..())
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
- adjustBruteLoss(damage)
+ switch(M.melee_damage_type)
+ if(BRUTE)
+ adjustBruteLoss(damage)
+ if(BURN)
+ adjustFireLoss(damage)
+ if(TOX)
+ adjustToxLoss(damage)
+ if(OXY)
+ adjustOxyLoss(damage)
+ if(CLONE)
+ adjustCloneLoss(damage)
+ if(STAMINA)
+ adjustStaminaLoss(damage)
updatehealth()
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 1ea66f59c61..3be3cd9d865 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -107,13 +107,14 @@ mob/living
return
return
-/mob/living/carbon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
+/mob/living/carbon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null,var/override = 0)
+
if(status_flags & GODMODE) //godmode
return 0
if(NO_SHOCK in mutations) //shockproof
return 0
shock_damage *= siemens_coeff
- if (shock_damage<1)
+ if(shock_damage<1 && !override)
return 0
src.apply_damage(shock_damage, BURN, def_zone, used_weapon="Electrocution")
@@ -127,6 +128,8 @@ mob/living
"\red You feel a mild shock course through your body.", \
"\red You hear a light zapping." \
)
+ jitteriness += (rand(2,4))//mostly for the swarmer trap
+ do_jitter_animation(jitteriness)
if (shock_damage > 10)
if (shock_damage < 200)
src.visible_message(
@@ -150,7 +153,10 @@ mob/living
)
playsound(loc, "sound/effects/eleczap.ogg", 50, 1, -1)
explosion(src.loc,-1,0,2,2)
- return shock_damage
+ if(override)
+ return override
+ else
+ return shock_damage
/mob/living/carbon/proc/swap_hand()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 672c91755c6..97ba63077e6 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -413,7 +413,7 @@
var/obj/item/organ/external/affected = src.get_organ(dam_zone)
if(affected)
affected.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data
- apply_damage(damage, BRUTE, affecting, armor, M.name)
+ apply_damage(damage,M.melee_damage_type, affecting, armor, M.name)
updatehealth()
/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L as mob)
@@ -725,7 +725,8 @@
//Removed the horrible safety parameter. It was only being used by ninja code anyways.
//Now checks siemens_coefficient of the affected area by default
-/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null)
+/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null,var/override = 0)
+
if(status_flags & GODMODE) //godmode
return 0
if(NO_SHOCK in mutations) //shockproof
@@ -737,7 +738,7 @@
var/obj/item/organ/external/affected_organ = get_organ(check_zone(def_zone))
var/siemens_coeff = base_siemens_coeff * get_siemens_coefficient_organ(affected_organ)
- return ..(shock_damage, source, siemens_coeff, def_zone)
+ return ..(shock_damage, source, siemens_coeff, def_zone,override)
/mob/living/carbon/human/Topic(href, href_list)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 0b80df799ee..579bcec93d4 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -917,7 +917,19 @@ var/list/robot_verbs_default = list(
visible_message("[M] [M.attacktext] [src]!")
add_logs(M, src, "attacked", admin=0)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
- adjustBruteLoss(damage)
+ switch(M.melee_damage_type)
+ if(BRUTE)
+ adjustBruteLoss(damage)
+ if(BURN)
+ adjustFireLoss(damage)
+ if(TOX)
+ adjustToxLoss(damage)
+ if(OXY)
+ adjustOxyLoss(damage)
+ if(CLONE)
+ adjustCloneLoss(damage)
+ if(STAMINA)
+ adjustStaminaLoss(damage)
updatehealth()
diff --git a/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm b/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
new file mode 100644
index 00000000000..530409bd517
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
@@ -0,0 +1,602 @@
+////Deactivated swarmer shell////
+/obj/item/unactivated_swarmer
+ name = "unactivated swarmer"
+ desc = "A currently unactivated swarmer. Swarmers can self activate at any time, it would be wise to immediately dispose of this."
+ icon = 'icons/mob/swarmer.dmi'
+ icon_state = "swarmer_unactivated"
+
+/obj/item/unactivated_swarmer/New()
+ notify_ghosts("An unactivated swarmer has been created in [get_area(src)]! (Click to enter)")
+ ..()
+
+/obj/item/unactivated_swarmer/Topic(href, href_list)
+ if(..())
+ return 1
+ if(href_list["ghostjoin"])
+ var/mob/dead/observer/ghost = usr
+ if(istype(ghost))
+ attack_ghost(ghost)
+
+/obj/item/unactivated_swarmer/attack_ghost(mob/user as mob)
+ var/be_swarmer = alert("Become a swarmer? (Warning, You can no longer be cloned!)",,"Yes","No")
+ if(be_swarmer == "No")
+ return
+ if(qdeleted(src))
+ user << "Swarmer has been occupied by someone else."
+ return
+ var/mob/living/simple_animal/hostile/swarmer/S = new /mob/living/simple_animal/hostile/swarmer(get_turf(loc))
+ S.key = user.key
+ qdel(src)
+
+////The Mob itself////
+
+/mob/living/simple_animal/hostile/swarmer
+ name = "Swarmer"
+ real_name = "Swarmer"
+ icon = 'icons/mob/swarmer.dmi'
+ desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely."
+ speak_emote = list("tones")
+ health = 40
+ maxHealth = 40
+ status_flags = CANPUSH
+ icon_state = "swarmer"
+ icon_living = "swarmer"
+ icon_dead = "swarmer_unactivated"
+ wander = 0
+ harm_intent_damage = 5
+ minbodytemp = 0
+ maxbodytemp = 500
+ min_oxy = 0
+ max_oxy = INFINITY
+ min_tox = 0
+ max_tox = INFINITY
+ min_co2 = 0
+ max_co2 = INFINITY
+ min_n2 = 0
+ max_n2 = INFINITY
+ unsuitable_atmos_damage = 0
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ melee_damage_type = STAMINA
+ ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
+ environment_smash = 0
+ attacktext = "shocks"
+ attack_sound = 'sound/effects/EMPulse.ogg'
+ friendly = "pinches"
+ speed = 0
+ faction = list("swarmer")
+ projectiletype = /obj/item/projectile/beam/disabler
+ pass_flags = PASSTABLE
+ ventcrawler = 2
+ ranged = 1
+ ranged_cooldown_cap = 2
+ projectilesound = 'sound/weapons/taser2.ogg'
+ AIStatus = AI_OFF
+ var/resources = 0 //Resource points, generated by consuming metal/glass
+
+/mob/living/simple_animal/hostile/swarmer/Login()
+ ..()
+ src << "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."
+ src << "Ctrl + Click provides most of your swarmer specific interactions, such as cannibalizing metal or glass, destroying the environment, or teleporting mobs away from you."
+ src << "Objectives:"
+ src << "1. Consume resources and replicate until there are no more resources left."
+ src << "2. Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable."
+ src << "3. Biological and Sentient resources will be harvested at a later date, do not harm them."
+
+/mob/living/simple_animal/hostile/swarmer/New()
+ ..()
+ add_language("Swarmer", 1)
+ verbs -= /mob/living/verb/pulled
+ updatename()
+
+/mob/living/simple_animal/hostile/swarmer/Stat()
+ ..()
+ if(statpanel("Status"))
+ stat("Resources:",resources)
+
+/mob/living/simple_animal/hostile/swarmer/Die()
+ ..()
+ new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc)
+ ghostize()
+ qdel(src)
+
+/mob/living/simple_animal/hostile/swarmer/emp_act()
+ if(health > 1)
+ health = 1
+ return
+ else
+ Die()
+
+/mob/living/simple_animal/hostile/swarmer/CanPass(atom/movable/O)
+ if(istype(O, /obj/item/projectile/beam/disabler))//Allows for swarmers to fight as a group without wasting their shots hitting each other
+ return 1
+ if(isswarmer(O))
+ return 1
+ ..()
+
+/mob/living/simple_animal/hostile/swarmer/proc/updatename()
+ real_name = "Swarmer [rand(100,999)]-[pick("kappa","sigma","beta","omicron","iota","epsilon","omega","gamma","delta","tau","alpha")]"
+ name = real_name
+
+////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
+/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)
+ return
+
+/atom/proc/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/item/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.Integrate(src)
+
+/obj/item/weapon/gun/swarmer_act()//Stops you from eating the entire armory
+ return
+
+/turf/simulated/floor/swarmer_act()//ex_act() on turf calls it on its contents, this is to prevent attacking mobs by DisIntegrate()'ing the floor
+ return
+
+/obj/machinery/atmospherics/swarmer_act()
+ return
+
+/obj/structure/disposalpipe/swarmer_act()
+ return
+
+/obj/machinery/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DismantleMachine(src)
+
+/obj/machinery/light/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+ deactivate(S, 0)
+
+/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/field/generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/gravity_generator/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/vending/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//It's more visually interesting than dismantling the machine
+ S.DisIntegrate(src)
+
+/obj/machinery/turretid/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisIntegrate(src)
+
+/obj/machinery/chem_dispenser/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "The volatile chemicals in this machine would destroy us. Aborting."
+
+/obj/machinery/nuclearbomb/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This device's destruction would result in the extermination of everything in the area. Aborting."
+
+/obj/effect/rune/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "Searching... sensor malfunction! Target lost. Aborting."
+
+/obj/structure/reagent_dispensers/fueltank/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "Destroying this object would cause a chain reaction. Aborting."
+
+/obj/structure/cable/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "Disrupting the power grid would bring no benefit to us. Aborting."
+
+/obj/machinery/portable_atmospherics/canister/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "An inhospitable area may be created as a result of destroying this object. Aborting."
+
+/obj/machinery/telecomms/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting."
+
+/obj/machinery/message_server/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This communications relay should be preserved, it will be a useful resource to our masters in the future. Aborting."
+
+/obj/machinery/blackbox_recorder/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This machine has recorded large amounts of data on this structure and its inhabitants, it will be a useful resource to our masters in the future. Aborting. "
+
+
+/obj/machinery/power/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "Disrupting the power grid would bring no benefit to us. Aborting."
+
+/obj/machinery/gateway/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This bluespace source will be important to us later. Aborting."
+
+/turf/simulated/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ for(var/turf/T in range(1, src))
+ if(istype(T, /turf/space) || istype(T.loc, /area/space))
+ S << "Destroying this object has the potential to cause a hull breach. Aborting."
+ return
+ ..()
+
+/obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ for(var/turf/T in range(1, src))
+ if(istype(T, /turf/space) || istype(T.loc, /area/space))
+ S << "Destroying this object has the potential to cause a hull breach. Aborting."
+ return
+ ..()
+
+/obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//Wiring would be too effective as a resource
+ S << "This object does not contain enough materials to work with."
+
+/obj/machinery/porta_turret/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "Attempting to dismantle this machine would result in an immediate counterattack. Aborting."
+
+/mob/living/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S.DisperseTarget(src)
+
+/mob/living/carbon/slime/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ S << "This biological resource is somehow resisting our bluespace transceiver. Aborting."
+
+////END CTRL CLICK FOR SWARMERS////
+
+/mob/living/simple_animal/hostile/swarmer/proc/Fabricate(var/atom/fabrication_object,var/fabrication_cost = 0)
+ if(!isturf(loc))
+ src << "This is not a suitable location for fabrication. We need more space."
+ if(resources >= fabrication_cost)
+ resources -= fabrication_cost
+ else
+ src << "You do not have the necessary resources to fabricate this object."
+ return 0
+ new fabrication_object(loc)
+ return 1
+
+/mob/living/simple_animal/hostile/swarmer/proc/Integrate(var/obj/item/target)
+ if(resources >= 100)
+ src << "We cannot hold more materials!"
+ return
+ //Check if any entries are either MAT_METAL or MAT_GLASS
+ if((MAT_METAL in target.materials) || (MAT_GLASS in target.materials))
+ resources++
+ do_attack_animation(target)
+ changeNext_move(CLICK_CD_MELEE)
+ var/obj/effect/swarmer/integrate/I = new /obj/effect/swarmer/integrate(get_turf(target))
+ I.pixel_x = target.pixel_x
+ I.pixel_y = target.pixel_y
+ if(istype(target, /obj/item/stack))
+ var/obj/item/stack/S = target
+ S.use(1)
+ if(S.amount)
+ return
+ qdel(target)
+ else
+ src << "\the [target] is incompatible with our internal matter recycler."
+ return
+
+/mob/living/simple_animal/hostile/swarmer/proc/DisIntegrate(var/atom/movable/target)
+ new /obj/effect/swarmer/disintegration(get_turf(target))
+ do_attack_animation(target)
+ changeNext_move(CLICK_CD_MELEE)
+ target.ex_act(3)
+
+/mob/living/simple_animal/hostile/swarmer/proc/DisperseTarget(var/mob/living/target)
+ if(target != src)
+ src << "Attempting to remove this being from our presence."
+ if(src.z != ZLEVEL_STATION)
+ src << "Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area."
+ return
+ if(do_mob(src, target, 30))
+ var/cycle
+ for(cycle=0,cycle<100,cycle++)
+ var/random_location = locate(rand(37,202),rand(75,192),ZLEVEL_STATION)//Drunk dial a turf in the general ballpark of the station
+ if(istype(random_location, /turf/simulated/floor))
+ var/turf/simulated/floor/F = random_location
+ if(F.air)
+ var/datum/gas_mixture/A = F.air
+ if(A.oxygen >= 16 && !A.toxins && A.carbon_dioxide < 10 && !A.trace_gases.len)//Can most things breathe in this location?
+ if((A.temperature > 270) && (A.temperature < 360))//Not too hot, not too cold
+ var/pressure = A.return_pressure()
+ if((pressure > 20) && (pressure < 550))//Account for crushing pressure or vaccuums
+ if(ishuman(target))//If we're getting rid of a human, slap some zipties on them to keep them away from us a little longer
+ var/obj/item/weapon/restraints/handcuffs/cable/zipties/Z = new /obj/item/weapon/restraints/handcuffs/cable/zipties(src)
+ Z.apply_cuffs(target, src)
+ do_teleport(target, F, 0)
+ playsound(src,'sound/effects/sparks4.ogg',50,1)
+ break
+ return
+
+/mob/living/simple_animal/hostile/swarmer/proc/DismantleMachine(var/obj/machinery/target)
+ do_attack_animation(target)
+ src << "We begin to dismantle this machine. We will need to be uninterrupted."
+ var/obj/effect/swarmer/dismantle/D = new /obj/effect/swarmer/dismantle(get_turf(target))
+ D.pixel_x = target.pixel_x
+ D.pixel_y = target.pixel_y
+ if(do_mob(src, target, 100))
+ src << "Dismantling complete."
+ var/obj/item/stack/sheet/metal/M = new /obj/item/stack/sheet/metal(target.loc)
+ M.amount = 5
+ if(target.component_parts && target.component_parts.len)
+ for(var/obj/item/I in target.component_parts)
+ I.forceMove(M.loc)
+ var/obj/effect/swarmer/disintegration/N = new /obj/effect/swarmer/disintegration(get_turf(target))
+ N.pixel_x = target.pixel_x
+ N.pixel_y = target.pixel_y
+ target.dropContents()
+ if(istype(target, /obj/machinery/computer))
+ var/obj/machinery/computer/C = target
+ if(C.circuit)
+ var/obj/item/weapon/circuitboard/circuit = text2path(C.circuit)
+ new circuit(get_turf(M))
+ qdel(target)
+
+
+/obj/effect/swarmer //Default swarmer effect object visual feedback
+ name = "swarmer ui"
+ desc = null
+ gender = NEUTER
+ icon = 'icons/mob/swarmer.dmi'
+ icon_state = "ui_light"
+ mouse_opacity = 0
+ layer = 4
+ unacidable = 1
+
+/obj/effect/swarmer/disintegration
+ icon_state = "disintegrate"
+
+/obj/effect/swarmer/disintegration/New()
+ playsound(src.loc, "sparks", 100, 1)
+ spawn(10)
+ qdel(src)
+
+/obj/effect/swarmer/dismantle
+ icon_state = "dismantle"
+
+/obj/effect/swarmer/dismantle/New()
+ spawn(25)
+ qdel(src)
+
+/obj/effect/swarmer/integrate
+ icon_state = "integrate"
+
+/obj/effect/swarmer/integrate/New()
+ spawn(5)
+ qdel(src)
+
+/obj/effect/swarmer/destructible //Default destroyable object for swarmer constructions
+ light_range = 1
+ mouse_opacity = 1
+ var/health = 30
+
+/obj/effect/swarmer/destructible/proc/TakeDamage(damage)
+ health -= damage
+ if(health <= 0)
+ qdel(src)
+
+/obj/effect/swarmer/destructible/bullet_act(obj/item/projectile/Proj)
+ if(Proj.damage)
+ if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
+ TakeDamage(Proj.damage)
+ ..()
+
+/obj/effect/swarmer/destructible/attackby(obj/item/weapon/I, mob/living/user, params)
+ if(istype(I, /obj/item/weapon))
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
+ TakeDamage(I.force)
+ return
+
+/obj/effect/swarmer/destructible/ex_act()
+ qdel(src)
+ return
+
+/obj/effect/swarmer/destructible/blob_act()
+ qdel(src)
+ return
+
+/obj/effect/swarmer/destructible/emp_act()
+ qdel(src)
+ return
+
+/obj/effect/swarmer/destructible/attack_animal(mob/living/user)
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ S.do_attack_animation(src)
+ user.changeNext_move(CLICK_CD_MELEE)
+ if(S.melee_damage_type == BRUTE || S.melee_damage_type == BURN)
+ TakeDamage(rand(S.melee_damage_lower, S.melee_damage_upper))
+ return
+
+/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(/obj/effect/swarmer/destructible/trap in loc)
+ src << "There is already a trap here. Aborting."
+ return
+ Fabricate(/obj/effect/swarmer/destructible/trap, 5)
+ return
+
+/obj/effect/swarmer/destructible/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"
+ light_range = 1
+ health = 10
+
+/obj/effect/swarmer/destructible/trap/Crossed(var/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, 1, -1)
+ L.Stun(1) //i am doing this here instead of electrocute act
+ L.electrocute_act(0, src, 1, "l_foot", 1)
+ if(isrobot(L) || L.isSynthetic())
+ L.Weaken(5)
+ qdel(src)
+ ..()
+
+/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(/obj/effect/swarmer/destructible/blockade in loc)
+ src << "There is already a blockade here. Aborting."
+ return
+ if(resources < 5)
+ src << "We do not have the resources for this!"
+ return
+ if(do_mob(src, src, 10))
+ Fabricate(/obj/effect/swarmer/destructible/blockade, 5)
+ return
+
+/obj/effect/swarmer/destructible/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 = 1
+ health = 50
+ density = 1
+ anchored = 1
+
+/obj/effect/swarmer/destructible/blockade/CanPass(atom/movable/O)
+ if(isswarmer(O))
+ return 1
+ if(istype(O, /obj/item/projectile/beam/disabler))
+ return 1
+
+/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."
+ src << "We are attempting to replicate ourselves. We will need to stand still until the process is complete."
+ if(resources < 50)
+ src << "We do not have the resources for this!"
+ return
+ if(!isturf(loc))
+ src << "This is not a suitable location for replicating ourselves. We need more room."
+ return
+ if(do_mob(src, src, 100))
+ if(Fabricate(/obj/item/unactivated_swarmer, 50))
+ playsound(loc,'sound/items/poster_being_created.ogg',50, 1, -1)
+
+/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
+ src << "Attempting to repair damage to our body, stand by..."
+ if(do_mob(src, src, 100))
+ adjustBruteLoss(-100)
+ 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/ContactSwarmers()
+ var/message = input(src, "Announce to other swarmers", "Swarmer contact")
+ if(message)
+ for(var/mob/M in mob_list)
+ if(isswarmer(M) || (M in dead_mob_list))
+ M << "Swarm communication - [src] states: [message]"
+
+
+
+////HUD NONSENSE////
+/obj/screen/swarmer
+ icon = 'icons/mob/swarmer.dmi'
+
+/obj/screen/swarmer/FabricateTrap
+ 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)"
+
+/obj/screen/swarmer/FabricateTrap/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.CreateTrap()
+
+/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)"
+
+/obj/screen/swarmer/Barricade/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.CreateBarricade()
+
+/obj/screen/swarmer/Replicate
+ icon_state = "ui_replicate"
+ name = "Replicate (Costs 50 Resources)"
+ desc = "Creates a another of our kind."
+
+/obj/screen/swarmer/Replicate/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.CreateSwarmer()
+
+/obj/screen/swarmer/RepairSelf
+ icon_state = "ui_self_repair"
+ name = "Repair self"
+ desc = "Repairs damage to our body."
+
+/obj/screen/swarmer/RepairSelf/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.RepairSelf()
+
+/obj/screen/swarmer/ToggleLight
+ icon_state = "ui_light"
+ name = "Toggle light"
+ desc = "Toggles our inbuilt light on or off."
+
+/obj/screen/swarmer/ToggleLight/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.ToggleLight()
+
+/obj/screen/swarmer/ContactSwarmers
+ icon_state = "ui_contact_swarmers"
+ name = "Contact swarmers"
+ desc = "Sends a message to all other swarmers, should they exist."
+
+/obj/screen/swarmer/ContactSwarmers/Click()
+ if(isswarmer(usr))
+ var/mob/living/simple_animal/hostile/swarmer/S = usr
+ S.ContactSwarmers()
+
+/datum/hud/proc/swarmer_hud(ui_style = 'icons/mob/screen1_midnight.dmi')
+ adding = list()
+
+ var/obj/screen/using
+
+ using = new /obj/screen/swarmer/FabricateTrap()
+ using.screen_loc = ui_rhand
+ adding += using
+
+ using = new /obj/screen/swarmer/Barricade()
+ using.screen_loc = ui_lhand
+ adding += using
+
+ using = new /obj/screen/swarmer/Replicate()
+ using.screen_loc = ui_zonesel
+ adding += using
+
+ using = new /obj/screen/swarmer/RepairSelf()
+ using.screen_loc = ui_storage1
+ adding += using
+
+ using = new /obj/screen/swarmer/ToggleLight()
+ using.screen_loc = ui_back
+ adding += using
+
+ using = new /obj/screen/swarmer/ContactSwarmers()
+ using.screen_loc = ui_inventory
+ adding += using
+
+ mymob.client.screen = list()
+ //mymob.client.screen += mymob.client.void
+ mymob.client.screen += adding
+
diff --git a/code/modules/mob/living/simple_animal/bot_swarm/swarmer_event.dm b/code/modules/mob/living/simple_animal/bot_swarm/swarmer_event.dm
new file mode 100644
index 00000000000..72c1c5050f0
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/bot_swarm/swarmer_event.dm
@@ -0,0 +1,37 @@
+/datum/event/spawn_swarmer
+ //name = "Sawrmer Spawn"
+ //one_shot = 1
+ startWhen = 3 //30 minutes
+ announceWhen = 10
+
+
+/datum/event/spawn_swarmer/announce()
+ 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."
+ for (var/obj/machinery/computer/communications/C in machines)
+ if(! (C.stat & (BROKEN|NOPOWER) ) )
+ var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( C.loc )
+ P.name = "'Classified [command_name()] Update'"
+ P.info = swarmer_report
+ P.update_icon()
+ C.messagetitle.Add("Classified [command_name()] Update")
+ C.messagetext.Add(P.info)
+ command_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
+
+
+
+/datum/event/spawn_swarmer/start()
+ if(find_swarmer())
+ return 0
+ if(!the_gateway)
+ return 0
+ new /obj/item/unactivated_swarmer(get_turf(the_gateway))
+
+
+/datum/event/spawn_swarmer/proc/find_swarmer()
+ for(var/mob/living/M in mob_list)
+ if(istype(M, /mob/living/simple_animal/hostile/swarmer) && M.client) //If there is a swarmer with an active client, we've found our swarmer
+ return 1
+ return 0
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 125d4007879..7694f714aca 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -253,22 +253,21 @@
var/target = the_target
visible_message("[src] [ranged_message] at [target]!")
- var/tturf = get_turf(target)
if(rapid)
spawn(1)
- Shoot(tturf, src.loc, src)
+ Shoot(target, src.loc, src)
if(casingtype)
new casingtype(get_turf(src))
spawn(4)
- Shoot(tturf, src.loc, src)
+ Shoot(target, src.loc, src)
if(casingtype)
new casingtype(get_turf(src))
spawn(6)
- Shoot(tturf, src.loc, src)
+ Shoot(target, src.loc, src)
if(casingtype)
new casingtype(get_turf(src))
else
- Shoot(tturf, src.loc, src)
+ Shoot(target, src.loc, src)
if(casingtype)
new casingtype
ranged_cooldown = ranged_cooldown_cap
@@ -278,17 +277,17 @@
if(target == start)
return
- var/obj/item/projectile/A = new projectiletype(user:loc)
+ var/obj/item/projectile/A = new projectiletype(src.loc)
playsound(user, projectilesound, 100, 1)
if(!A) return
- if (!istype(target, /turf))
- qdel(A)
- return
A.current = target
A.firer = src
A.yo = target:y - start:y
A.xo = target:x - start:x
+ if(AIStatus == AI_OFF)//Don't want mindless mobs to have their movement screwed up firing in space
+ newtonian_move(get_dir(target, user))
+ A.original = target
spawn( 0 )
A.process()
return
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index ae8f7da7d4c..887bf93c611 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -57,6 +57,8 @@
//LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly
var/melee_damage_lower = 0
var/melee_damage_upper = 0
+ var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage.
+ var/list/ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 0, CLONE = 0, STAMINA = 1, OXY = 0) //Set 0 to receive that damage type, 1 to ignore
var/attacktext = "attacks"
var/attack_sound = null
var/friendly = "nuzzles" //If the mob does no damage with it's attack
@@ -280,7 +282,7 @@
"\The [M] [M.attacktext] [src]!")
add_logs(M, src, "attacked", admin=0)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
- adjustBruteLoss(damage)
+ attack_threshold_check(damage,M.melee_damage_type)
/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj)
if(!Proj)
@@ -321,7 +323,7 @@
M.do_attack_animation(src)
visible_message("[M] [response_harm] [src]!")
playsound(loc, "punch", 25, 1, -1)
- adjustBruteLoss(harm_intent_damage)
+ attack_threshold_check(harm_intent_damage)
return
@@ -341,7 +343,7 @@
visible_message("[M] has slashed at [src]!", \
"[M] has slashed at [src]!")
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
- adjustBruteLoss(damage)
+ attack_threshold_check(damage)
return
@@ -361,7 +363,7 @@
if(stat != DEAD)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
- adjustBruteLoss(damage)
+ attack_threshold_check(damage)
/mob/living/simple_animal/attack_slime(mob/living/carbon/slime/M as mob)
@@ -383,7 +385,7 @@
else
damage = rand(5, 35)
- adjustBruteLoss(damage)
+ attack_threshold_check(damage)
return
@@ -474,9 +476,20 @@
adjustBruteLoss(30)
/mob/living/simple_animal/adjustBruteLoss(damage)
- health = Clamp(health - damage, 0, maxHealth)
- if(health < 1)
- Die()
+ if(!ignored_damage_types[BRUTE])
+ ..()
+
+/mob/living/simple_animal/adjustFireLoss(damage)
+ if(!ignored_damage_types[BURN])
+ ..(damage)
+
+/mob/living/simple_animal/adjustToxLoss(damage)
+ if(!ignored_damage_types[TOX])
+ ..(damage)
+
+/mob/living/simple_animal/adjustCloneLoss(damage)
+ if(!ignored_damage_types[CLONE])
+ ..(damage)
/mob/living/simple_animal/proc/CanAttack(var/atom/the_target)
if(see_invisible < the_target.invisibility)
@@ -495,6 +508,14 @@
return 0
return 1
+/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE)
+ if(damage <= force_threshold || ignored_damage_types[damagetype])
+ visible_message("[src] looks unharmed from the damage.")
+ else
+ adjustBruteLoss(damage)
+ updatehealth()
+
+
/mob/living/simple_animal/update_fire()
return
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 9a5ce12d904..795027e55b6 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -137,6 +137,11 @@ proc/isembryo(A)
return 1
return 0
+/proc/isswarmer(A)
+ if(istype(A, /mob/living/simple_animal/hostile/swarmer))
+ return 1
+ return 0
+
proc/isobserver(A)
if(istype(A, /mob/dead/observer))
return 1
diff --git a/icons/mob/swarmer.dmi b/icons/mob/swarmer.dmi
new file mode 100644
index 00000000000..5a3595f7baf
Binary files /dev/null and b/icons/mob/swarmer.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 3035a9e060c..e6197d9bc68 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1431,6 +1431,8 @@
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\tribbles.dm"
#include "code\modules\mob\living\simple_animal\vox.dm"
+#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer.dm"
+#include "code\modules\mob\living\simple_animal\bot_swarm\swarmer_event.dm"
#include "code\modules\mob\living\simple_animal\friendly\butterfly.dm"
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"