diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index f65f13f4f9f..4136771eef5 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -35,6 +35,7 @@
#define ROLE_SENTIENT "sentient animal"
#define ROLE_POSIBRAIN "positronic brain"
#define ROLE_GUARDIAN "guardian"
+#define ROLE_MORPH "morph"
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
@@ -67,4 +68,5 @@ var/global/list/special_roles = list(
ROLE_POSIBRAIN,
ROLE_REVENANT,
ROLE_GUARDIAN,
+ ROLE_MORPH,
)
diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/borer.dm
rename to code/game/gamemodes/miniantags/borer/borer.dm
diff --git a/code/modules/events/borers.dm b/code/game/gamemodes/miniantags/borer/borer_event.dm
similarity index 100%
rename from code/modules/events/borers.dm
rename to code/game/gamemodes/miniantags/borer/borer_event.dm
diff --git a/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
rename to code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
diff --git a/code/modules/mob/living/simple_animal/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/bot_swarm/swarmer_event.dm
rename to code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/guardian/guardian.dm
rename to code/game/gamemodes/miniantags/guardian/guardian.dm
diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm
new file mode 100644
index 00000000000..378ef84990a
--- /dev/null
+++ b/code/game/gamemodes/miniantags/morph/morph.dm
@@ -0,0 +1,183 @@
+#define MORPH_COOLDOWN 50
+
+/mob/living/simple_animal/hostile/morph
+ name = "morph"
+ real_name = "morph"
+ desc = "A revolting, pulsating pile of flesh."
+ speak_emote = list("gurgles")
+ emote_hear = list("gurgles")
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "morph"
+ icon_living = "morph"
+ icon_dead = "morph_dead"
+ speed = 2
+ a_intent = "harm"
+ stop_automated_movement = 1
+ status_flags = CANPUSH
+ pass_flags = PASSTABLE
+ ventcrawler = 2
+
+ min_oxy = 0
+ max_oxy = 0
+ min_tox = 0
+ max_tox = 0
+ min_co2 = 0
+ max_co2 = 0
+ min_n2 = 0
+ max_n2 = 0
+
+ minbodytemp = 0
+ maxHealth = 150
+ health = 150
+ environment_smash = 1
+ melee_damage_lower = 20
+ melee_damage_upper = 20
+ see_in_dark = 8
+ see_invisible = SEE_INVISIBLE_MINIMUM
+ idle_vision_range = 1 // Only attack when target is close
+ wander = 0
+ attacktext = "glomps"
+ attack_sound = 'sound/effects/blobattack.ogg'
+ meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/slab
+
+ var/morphed = 0
+ var/atom/movable/form = null
+ var/morph_time = 0
+
+ var/playstyle_string = "You are a morph, an abomination of science created primarily with changeling cells. \
+ You may take the form of anything nearby by shift-clicking it. This process will alert any nearby \
+ observers, and can only be performed once every five seconds. While morphed, you move faster, but do \
+ less damage. In addition, anyone within three tiles will note an uncanny wrongness if examining you. \
+ You can attack any item or dead creature to consume it - creatures will restore 1/3 of your max health. \
+ Finally, you can restore yourself to your original form while morphed by shift-clicking yourself."
+
+/mob/living/simple_animal/hostile/morph/examine(mob/user)
+ if(morphed)
+ if(form)
+ form.examine(user) // Refactor examine to return desc so it's static? Not sure if worth it
+ // If the object you've disguised as has been deleted, your cover's probably blown
+ else
+ ..()
+ if(get_dist(user,src)<=3)
+ user << "It doesn't look quite right..."
+ else
+ ..()
+ return
+
+/mob/living/simple_animal/hostile/morph/proc/allowed(atom/movable/A) // make it into property/proc ? not sure if worth it
+ if(istype(A,/obj/screen))
+ return 0
+ if(istype(A,/obj/singularity))
+ return 0
+ if(istype(A,/mob/living/simple_animal/hostile/morph))
+ return 0
+ return 1
+
+/mob/living/simple_animal/hostile/morph/proc/eat(atom/movable/A)
+ if(A && A.loc != src)
+ visible_message("[src] swallows [A] whole!")
+ A.forceMove(src)
+ return 1
+ return 0
+
+/mob/living/simple_animal/hostile/morph/ShiftClickOn(atom/movable/A)
+ if(morph_time <= world.time && !stat)
+ if(A == src)
+ restore()
+ return
+ if(istype(A) && allowed(A))
+ assume(A)
+ else
+ src << "Your chameleon skin is still repairing itself!"
+ ..()
+
+/mob/living/simple_animal/hostile/morph/proc/assume(atom/movable/target)
+ morphed = 1
+ form = target
+ visible_message("[src] suddenly twists and changes shape, becoming a copy of [target]!", \
+ "You twist your body and assume the form of [target].")
+
+ appearance = target.appearance
+ transform = initial(transform)
+ pixel_y = initial(pixel_y)
+ pixel_x = initial(pixel_x)
+ //Morphed is weaker
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+ speed = 0
+
+ morph_time = world.time + MORPH_COOLDOWN
+ return
+
+/mob/living/simple_animal/hostile/morph/proc/restore()
+ if(!morphed)
+ return
+ morphed = 0
+ form = null
+
+ //anim(loc,src,'icons/mob/mob.dmi',,"morph",,src.dir)
+
+ visible_message("[src] suddenly collapses in on itself, dissolving into a pile of green flesh!", \
+ "You reform to your normal body.")
+ name = initial(name)
+ icon = initial(icon)
+ icon_state = initial(icon_state)
+ overlays.Cut()
+
+ //Baseline stats
+ melee_damage_lower = initial(melee_damage_lower)
+ melee_damage_upper = initial(melee_damage_upper)
+ speed = initial(speed)
+
+ morph_time = world.time + MORPH_COOLDOWN
+
+/mob/living/simple_animal/hostile/morph/handle_state_icons()
+ return
+
+/mob/living/simple_animal/hostile/morph/death(gibbed)
+ if(morphed)
+ visible_message("[src] twists and dissolves into a pile of green flesh!", \
+ "Your skin ruptures! Your flesh breaks apart! No disguise can ward off de--")
+ restore()
+ if(gibbed)
+ for(var/atom/movable/AM in src)
+ AM.forceMove(loc)
+ if(prob(90))
+ step(AM, pick(alldirs))
+ ..(gibbed)
+
+/mob/living/simple_animal/hostile/morph/Aggro() // automated only
+ ..()
+ restore()
+
+/mob/living/simple_animal/hostile/morph/LoseAggro()
+ vision_range = idle_vision_range
+
+/mob/living/simple_animal/hostile/morph/AIShouldSleep(var/list/possible_targets)
+ . = ..()
+ if(.)
+ var/list/things = list()
+ for(var/atom/movable/A in view(src))
+ if(allowed(A))
+ things += A
+ var/atom/movable/T = pick(things)
+ assume(T)
+
+/mob/living/simple_animal/hostile/morph/AttackingTarget()
+ if(isliving(target)) // Eat Corpses to regen health
+ var/mob/living/L = target
+ if(L.stat == DEAD)
+ if(do_after(src, 30, target = L))
+ if(eat(L))
+ heal_overall_damage(50,50)
+ return
+ else if(istype(target,/obj/item)) // Eat items just to be annoying
+ var/obj/item/I = target
+ if(!I.anchored)
+ if(do_after(src,20, target = I))
+ eat(I)
+ return
+ target.attack_animal(src)
+
+/mob/living/simple_animal/hostile/morph/update_action_buttons() //So all eaten objects are not counted every life
+ return
\ No newline at end of file
diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm
new file mode 100644
index 00000000000..4a65ba85d83
--- /dev/null
+++ b/code/game/gamemodes/miniantags/morph/morph_event.dm
@@ -0,0 +1,45 @@
+/datum/event/spawn_morph
+ var/key_of_morph
+
+/datum/event/spawn_morph/proc/get_morph(end_if_fail = 0)
+ key_of_morph = null
+ if(!key_of_morph)
+ var/list/candidates = get_candidates(ROLE_MORPH)
+ if(!candidates.len)
+ if(end_if_fail)
+ return 0
+ return find_morph()
+ var/client/C = pick(candidates)
+ key_of_morph = C.key
+ if(!key_of_morph)
+ if(end_if_fail)
+ return 0
+ return find_morph()
+ var/datum/mind/player_mind = new /datum/mind(key_of_morph)
+ player_mind.active = 1
+ if(!xeno_spawn)
+ return find_morph()
+ var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(xeno_spawn))
+ player_mind.transfer_to(S)
+ player_mind.assigned_role = "Morph"
+ player_mind.special_role = "Morph"
+ ticker.mode.traitors |= player_mind
+ S << S.playstyle_string
+ S << 'sound/magic/Mutate.ogg'
+ message_admins("[key_of_morph] has been made into morph by an event.")
+ log_game("[key_of_morph] was spawned as a morph by an event.")
+ return 1
+
+/datum/event/spawn_morph/start()
+ get_morph()
+
+
+/datum/event/spawn_morph/proc/find_morph()
+ message_admins("Attempted to spawn a morph but there was no players available. Will try again momentarily.")
+ spawn(50)
+ if(get_morph(1))
+ message_admins("Situation has been resolved, [key_of_morph] has been spawned as a morph.")
+ log_game("[key_of_morph] was spawned as a morph by an event.")
+ return 0
+ message_admins("Unfortunately, no candidates were available for becoming a morph. Shutting down.")
+ return kill()
diff --git a/code/modules/mob/living/simple_animal/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/revenant/revenant.dm
rename to code/game/gamemodes/miniantags/revenant/revenant.dm
diff --git a/code/modules/mob/living/simple_animal/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/revenant/revenant_abilities.dm
rename to code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
diff --git a/code/modules/mob/living/simple_animal/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/revenant/revenant_spawn_event.dm
rename to code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
diff --git a/code/modules/mob/living/bloodcrawl.dm b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
similarity index 100%
rename from code/modules/mob/living/bloodcrawl.dm
rename to code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
diff --git a/code/modules/mob/living/simple_animal/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/slaughter/slaughter.dm
rename to code/game/gamemodes/miniantags/slaughter/slaughter.dm
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 03af198e808..fcf786745a6 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, "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, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, is_one_shot = 1)
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, is_one_shot = 1),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/spawn_morph, 0, is_one_shot = 1)
)
/datum/event_container/major
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 59afc38f1cd..399ae068586 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -102,6 +102,9 @@
handle_automated_speech()
. = 1
+ handle_state_icons()
+
+/mob/living/simple_animal/proc/handle_state_icons()
if(resting && icon_resting && stat != DEAD)
icon_state = icon_resting
else if(stat != DEAD)
diff --git a/html/changelogs/crazylemon-morphport.yml b/html/changelogs/crazylemon-morphport.yml
new file mode 100644
index 00000000000..a28b15d58e8
--- /dev/null
+++ b/html/changelogs/crazylemon-morphport.yml
@@ -0,0 +1,4 @@
+author: Crazylemon
+delete-after: True
+changes:
+ - rscadd: "New classes of shapeshifters have been sighted around the NSS Cyberiad..."
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index 47a0c11e9b2..4b839a4bf66 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 580b9217bbf..ef97cd9bf11 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -335,6 +335,18 @@
#include "code\game\gamemodes\malfunction\malfunction.dm"
#include "code\game\gamemodes\meteor\meteor.dm"
#include "code\game\gamemodes\meteor\meteors.dm"
+#include "code\game\gamemodes\miniantags\borer\borer.dm"
+#include "code\game\gamemodes\miniantags\borer\borer_event.dm"
+#include "code\game\gamemodes\miniantags\bot_swarm\swarmer.dm"
+#include "code\game\gamemodes\miniantags\bot_swarm\swarmer_event.dm"
+#include "code\game\gamemodes\miniantags\guardian\guardian.dm"
+#include "code\game\gamemodes\miniantags\morph\morph.dm"
+#include "code\game\gamemodes\miniantags\morph\morph_event.dm"
+#include "code\game\gamemodes\miniantags\revenant\revenant.dm"
+#include "code\game\gamemodes\miniantags\revenant\revenant_abilities.dm"
+#include "code\game\gamemodes\miniantags\revenant\revenant_spawn_event.dm"
+#include "code\game\gamemodes\miniantags\slaughter\bloodcrawl.dm"
+#include "code\game\gamemodes\miniantags\slaughter\slaughter.dm"
#include "code\game\gamemodes\mutiny\auth_key.dm"
#include "code\game\gamemodes\mutiny\directive.dm"
#include "code\game\gamemodes\mutiny\emergency_authentication_device.dm"
@@ -1145,7 +1157,6 @@
#include "code\modules\events\anomaly_pyro.dm"
#include "code\modules\events\anomaly_vortex.dm"
#include "code\modules\events\blob.dm"
-#include "code\modules\events\borers.dm"
#include "code\modules\events\brand_intelligence.dm"
#include "code\modules\events\carp_migration.dm"
#include "code\modules\events\communications_blackout.dm"
@@ -1296,7 +1307,6 @@
#include "code\modules\mob\dead\observer\say.dm"
#include "code\modules\mob\dead\observer\spells.dm"
#include "code\modules\mob\living\autohiss.dm"
-#include "code\modules\mob\living\bloodcrawl.dm"
#include "code\modules\mob\living\damage_procs.dm"
#include "code\modules\mob\living\default_language.dm"
#include "code\modules\mob\living\life.dm"
@@ -1444,7 +1454,6 @@
#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_say.dm"
#include "code\modules\mob\living\simple_animal\bees.dm"
-#include "code\modules\mob\living\simple_animal\borer.dm"
#include "code\modules\mob\living\simple_animal\constructs.dm"
#include "code\modules\mob\living\simple_animal\corpse.dm"
#include "code\modules\mob\living\simple_animal\parrot.dm"
@@ -1454,8 +1463,6 @@
#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"
@@ -1472,7 +1479,6 @@
#include "code\modules\mob\living\simple_animal\friendly\slime.dm"
#include "code\modules\mob\living\simple_animal\friendly\spiderbot.dm"
#include "code\modules\mob\living\simple_animal\friendly\tomato.dm"
-#include "code\modules\mob\living\simple_animal\guardian\guardian.dm"
#include "code\modules\mob\living\simple_animal\hostile\alien.dm"
#include "code\modules\mob\living\simple_animal\hostile\bat.dm"
#include "code\modules\mob\living\simple_animal\hostile\bear.dm"
@@ -1499,10 +1505,6 @@
#include "code\modules\mob\living\simple_animal\hostile\retaliate\pet.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\undead.dm"
-#include "code\modules\mob\living\simple_animal\revenant\revenant.dm"
-#include "code\modules\mob\living\simple_animal\revenant\revenant_abilities.dm"
-#include "code\modules\mob\living\simple_animal\revenant\revenant_spawn_event.dm"
-#include "code\modules\mob\living\simple_animal\slaughter\slaughter.dm"
#include "code\modules\mob\new_player\login.dm"
#include "code\modules\mob\new_player\logout.dm"
#include "code\modules\mob\new_player\new_player.dm"
diff --git a/sound/magic/Mutate.ogg b/sound/magic/Mutate.ogg
new file mode 100644
index 00000000000..d31514f310b
Binary files /dev/null and b/sound/magic/Mutate.ogg differ