diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm
index 294baee09b..31a078d9be 100644
--- a/code/modules/food/food/snacks.dm
+++ b/code/modules/food/food/snacks.dm
@@ -3451,6 +3451,7 @@
center_of_mass = list("x"=21, "y"=12)
nutriment_amt = 4
nutriment_desc = list("cheese" = 2,"taco shell" = 2)
+
/obj/item/weapon/reagent_containers/food/snacks/taco/Initialize()
. = ..()
reagents.add_reagent("protein", 3)
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm
new file mode 100644
index 0000000000..38644641e0
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm
@@ -0,0 +1,429 @@
+/mob/living/simple_mob/animal/space/space_worm
+ name = "space worm segment"
+ desc = "A part of a space worm."
+ icon = 'icons/mob/worm.dmi'
+ icon_state = "spaceworm"
+ icon_living = "spaceworm"
+ icon_dead = "spacewormdead"
+
+ tt_desc = "U Tyranochaetus imperator"
+
+ anchored = TRUE // Theoretically, you shouldn't be able to move this without moving the head.
+
+ maxHealth = 200
+ health = 200
+ movement_cooldown = 0
+
+ faction = "worm"
+
+ status_flags = 0
+ universal_speak = 0
+ universal_understand = 1
+ animate_movement = SYNC_STEPS
+
+ response_help = "touches"
+ response_disarm = "flails at"
+ response_harm = "punches the"
+
+ harm_intent_damage = 2
+
+ attacktext = list("slammed")
+
+ ai_holder_type = /datum/ai_holder/simple_mob/inert
+
+ mob_class = MOB_CLASS_ABERRATION // It's a monster.
+
+ meat_amount = 2
+ meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/worm
+
+ var/mob/living/simple_mob/animal/space/space_worm/previous //next/previous segments, correspondingly
+ var/mob/living/simple_mob/animal/space/space_worm/next //head is the nextest segment
+
+ var/severed = FALSE // Is this a severed segment?
+
+ var/severed_head_type = /mob/living/simple_mob/animal/space/space_worm/head/severed // What type of head do we spawn when detaching?
+ var/segment_type = /mob/living/simple_mob/animal/space/space_worm // What type of segment do our heads make?
+
+ var/stomachProcessProbability = 50
+ var/digestionProbability = 20
+ var/flatPlasmaValue = 5 //flat Phoron amount given for non-items
+
+ var/atom/currentlyEating // Worm's current Maw target.
+
+ var/z_transitioning = FALSE // Are we currently moving between Z-levels, or doing something that might mean we can't rely on distance checking for segments?
+ var/sever_chunks = FALSE // Do we fall apart when dying?
+
+ var/time_maw_opened = 0
+ var/maw_cooldown = 30 SECONDS
+ var/open_maw = FALSE // Are we trying to eat things?
+
+/mob/living/simple_mob/animal/space/space_worm/head
+ name = "space worm"
+ icon_state = "spacewormhead"
+ icon_living = "spacewormhead"
+
+ anchored = FALSE // You can pull the head to pull the body.
+
+ maxHealth = 300
+ health = 300
+
+ hovering = TRUE
+
+ melee_damage_lower = 10
+ melee_damage_upper = 25
+ attack_sharp = TRUE
+ attack_edge = TRUE
+ attack_armor_pen = 30
+ attacktext = list("bitten", "gored", "gouged", "chomped", "slammed")
+
+ animate_movement = SLIDE_STEPS
+
+ var/segment_count = 6
+
+/mob/living/simple_mob/animal/space/space_worm/head/severed
+ segment_count = 0
+ severed = TRUE
+
+/mob/living/simple_mob/animal/space/space_worm/head/short
+ segment_count = 3
+
+/mob/living/simple_mob/animal/space/space_worm/head/long
+ segment_count = 10
+
+/mob/living/simple_mob/animal/space/space_worm/head/handle_special()
+ ..()
+ update_body_faction()
+
+/mob/living/simple_mob/animal/space/space_worm/head/update_icon()
+ ..()
+ if(!open_maw && !stat)
+ icon_state = "[icon_living][previous ? 1 : 0]_hunt"
+ else
+ icon_state = "[icon_living][previous ? 1 : 0]"
+
+ if(previous)
+ set_dir(get_dir(previous,src))
+
+ if(stat)
+ icon_state = "[icon_state]_dead"
+
+/mob/living/simple_mob/animal/space/space_worm/head/Initialize()
+ ..()
+
+ var/mob/living/simple_mob/animal/space/space_worm/current = src
+
+ if(segment_count && !severed)
+ for(var/i = 1 to segment_count)
+ var/mob/living/simple_mob/animal/space/space_worm/newSegment = new segment_type(loc)
+ current.Attach(newSegment)
+ current = newSegment
+ current.faction = faction
+
+/mob/living/simple_mob/animal/space/space_worm/head/verb/toggle_devour()
+ set name = "Toggle Feeding"
+ set desc = "Extends your teeth for 30 seconds so that you can chew through mobs and structures alike."
+ set category = "Abilities"
+
+ if(world.time < time_maw_opened + maw_cooldown)
+ if(open_maw)
+ to_chat(src, "You retract your teeth.")
+ time_maw_opened -= maw_cooldown / 2 // Recovers half cooldown if you end it early manually.
+ else
+ to_chat(src, "You are too tired to do this..")
+ set_maw(FALSE)
+ else
+ set_maw(!open_maw)
+
+/mob/living/simple_mob/animal/space/space_worm/proc/set_maw(var/state = FALSE)
+ open_maw = state
+ if(open_maw)
+ time_maw_opened = world.time
+ movement_cooldown = initial(movement_cooldown) + 1.5
+ else
+ movement_cooldown = initial(movement_cooldown)
+ update_icon()
+
+/mob/living/simple_mob/animal/space/space_worm/death()
+ ..()
+
+ DumpStomach()
+
+ if(previous)
+ previous.death()
+
+/mob/living/simple_mob/animal/space/space_worm/handle_special() // Processed in life. Nicer to have it modular incase something in Life change(d)(s)
+ ..()
+
+ if(world.time > time_maw_opened + maw_cooldown) // Auto-stop eating.
+ if(open_maw)
+ to_chat(src, "Your jaws cannot remain open..")
+ set_maw(FALSE)
+
+ if(next && !(next in view(src,1)) && !z_transitioning)
+ Detach(1)
+
+ if(stat == DEAD && sever_chunks) // Dead chunks fall off and die immediately if we sever_chunks
+ if(previous)
+ previous.Detach(1)
+ if(next)
+ Detach(1)
+
+ if(prob(stomachProcessProbability))
+ ProcessStomach()
+
+ update_icon()
+
+ return
+
+/mob/living/simple_mob/animal/space/space_worm/CanPass(atom/movable/mover, turf/target)
+ if(istype(mover, /mob/living/simple_mob/animal/space/space_worm/head))
+ var/mob/living/simple_mob/animal/space/space_worm/head/H = mover
+ if(H.previous == src)
+ return FALSE
+
+ if(istype(mover, /mob/living/simple_mob/animal/space/space_worm)) // Worms don't run over worms. That's weird. And also really annoying.
+ return TRUE
+ else if(src.stat == DEAD && !istype(mover, /obj/item/projectile)) // Projectiles need to do their normal checks.
+ return TRUE
+ return ..()
+
+/mob/living/simple_mob/animal/space/space_worm/Destroy() // If a chunk is destroyed, kill the back half.
+ DumpStomach()
+ if(previous)
+ previous.Detach(1)
+ if(next)
+ next.previous = null
+ next = null
+ ..()
+
+/mob/living/simple_mob/animal/space/space_worm/Move()
+ var/attachementNextPosition = loc
+ . = ..()
+ if(.)
+ if(previous)
+ if(previous.z != z)
+ previous.z_transitioning = TRUE
+ else
+ previous.z_transitioning = FALSE
+ previous.forceMove(attachementNextPosition) // None of this 'ripped in half by an airlock' business.
+ update_icon()
+
+/mob/living/simple_mob/animal/space/space_worm/forceMove()
+ var/attachementNextPosition = loc
+ . = ..()
+ if(.)
+ if(previous)
+ if(previous.z != z)
+ previous.z_transitioning = TRUE
+ else
+ previous.z_transitioning = FALSE
+ previous.forceMove(attachementNextPosition) // None of this 'ripped in half by an airlock' business. x 2
+ update_icon()
+
+/mob/living/simple_mob/animal/space/space_worm/head/Bump(atom/obstacle)
+ if(open_maw && !stat && obstacle != previous)
+ spawn(1)
+ if(currentlyEating != obstacle)
+ currentlyEating = obstacle
+
+ set_AI_busy(TRUE)
+ if(AttemptToEat(obstacle))
+ currentlyEating = null
+ set_AI_busy(FALSE)
+ else
+ currentlyEating = null
+ . = ..(obstacle)
+
+/mob/living/simple_mob/animal/space/space_worm/update_icon()
+ if(previous) //midsection
+ icon_state = "spaceworm[get_dir(src,previous) | get_dir(src,next)]"
+ if(stat)
+ icon_state = "[icon_state]_dead"
+
+ else //tail
+ icon_state = "spacewormtail"
+ if(stat)
+ icon_state = "[icon_state]_dead"
+ set_dir(get_dir(src,next))
+
+ if(next)
+ color = next.color
+
+ return
+
+/mob/living/simple_mob/animal/space/space_worm/proc/AttemptToEat(var/atom/target)
+ if(istype(target,/turf/simulated/wall))
+ var/turf/simulated/wall/W = target
+ if((!W.reinf_material && do_after(src, 5 SECONDS)) || do_after(src, 10 SECONDS)) // 10 seconds for an R-wall, 5 seconds for a normal one.
+ if(target)
+ W.dismantle_wall()
+ return 1
+ else if(istype(target,/atom/movable))
+ if(istype(target,/mob) || do_after(src, 5)) // 5 ticks to eat stuff like tables.
+ var/atom/movable/objectOrMob = target
+ if(istype(objectOrMob, /obj/machinery/door)) // Doors and airlocks take time based on their durability and our damageo.
+ var/obj/machinery/door/D = objectOrMob
+ var/total_hits = max(2, round(D.maxhealth / (2 * melee_damage_upper)))
+
+ for(var/I = 1 to total_hits)
+
+ if(!D)
+ objectOrMob = null
+ break
+
+ if(do_after(src, 5))
+ D.visible_message("Something crashes against \the [D]!")
+ D.take_damage(2 * melee_damage_upper)
+ else
+ objectOrMob = null
+ break
+
+ if(D && (D.stat &= BROKEN|NOPOWER))
+ D.open(TRUE)
+ break
+
+ if(istype(objectOrMob, /obj/effect/energy_field))
+ var/obj/effect/energy_field/EF = objectOrMob
+ objectOrMob = null // No eating shields.
+ if(EF.opacity)
+ EF.visible_message("Something begins forcing itself through \the [EF]!")
+ else
+ EF.visible_message("\The [src] begins forcing itself through \the [EF]!")
+ if(do_after(src, EF.strength * 5))
+ EF.adjust_strength(rand(-8, -10))
+ EF.visible_message("\The [src] crashes through \the [EF]!")
+ else
+ EF.visible_message("\The [EF] reverberates as it returns to normal.")
+
+ if(objectOrMob)
+ objectOrMob.update_nearby_tiles(need_rebuild=1)
+ objectOrMob.forceMove(src)
+ return 1
+
+ return 0
+
+/mob/living/simple_mob/animal/space/space_worm/proc/Attach(var/mob/living/simple_mob/animal/space/space_worm/attachement)
+ if(!attachement)
+ return
+
+ previous = attachement
+ attachement.next = src
+
+ return
+
+/mob/living/simple_mob/animal/space/space_worm/proc/Detach(die = 0)
+ var/mob/living/simple_mob/animal/space/space_worm/head/newHead = new severed_head_type(loc,0)
+ var/mob/living/simple_mob/animal/space/space_worm/newHeadPrevious = previous
+
+ previous = null //so that no extra heads are spawned
+
+ newHead.Attach(newHeadPrevious)
+
+ if(die)
+ newHead.death()
+
+ qdel(src)
+
+/mob/living/simple_mob/animal/space/space_worm/proc/ProcessStomach()
+ for(var/atom/movable/stomachContent in contents)
+ if(stomach_special(stomachContent))
+ continue
+ if(prob(digestionProbability))
+ if(stomach_special_digest(stomachContent))
+ continue
+ if(istype(stomachContent,/obj/item/stack)) //converts to plasma, keeping the stack value
+ if(!istype(stomachContent,/obj/item/stack/material/phoron))
+ var/obj/item/stack/oldStack = stomachContent
+ new /obj/item/stack/material/phoron(src, oldStack.get_amount())
+ qdel(oldStack)
+ continue
+ else if(istype(stomachContent,/obj/item)) //converts to plasma, keeping the w_class
+ var/obj/item/oldItem = stomachContent
+ new /obj/item/stack/material/phoron(src, oldItem.w_class)
+ qdel(oldItem)
+ continue
+ else
+ new /obj/item/stack/material/phoron(src, flatPlasmaValue) //just flat amount
+ if(!isliving(stomachContent))
+ qdel(stomachContent)
+ else
+ var/mob/living/L = stomachContent
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ var/damage_cycles = rand(3, 5)
+ for(var/I = 0, I < damage_cycles, I++)
+ C.apply_damage(damage = rand(10,20), damagetype = BIOACID, def_zone = pick(BP_ALL))
+ else
+ L.apply_damage(damage = rand(10,60), damagetype = BIOACID)
+ continue
+
+ DumpStomach()
+
+ return
+
+/mob/living/simple_mob/animal/space/space_worm/proc/DumpStomach()
+ if(previous && previous.stat != DEAD)
+ for(var/atom/movable/stomachContent in contents) //transfer it along the digestive tract
+ stomachContent.forceMove(previous)
+ else
+ for(var/atom/movable/stomachContent in contents) // Or dump it out.
+ stomachContent.forceMove(get_turf(src))
+ return
+
+/mob/living/simple_mob/animal/space/space_worm/proc/stomach_special(var/atom/A) // Futureproof. Anything that interacts with contents without relying on digestion probability. Return TRUE if it should skip digest.
+ return FALSE
+
+/mob/living/simple_mob/animal/space/space_worm/proc/stomach_special_digest(var/atom/A) // Futureproof. Any special checks that interact with digested atoms. I.E., ore processing. Return TRUE if it should skip future digest checks.
+ return FALSE
+
+/mob/living/simple_mob/animal/space/space_worm/proc/update_body_faction()
+ if(next) // Keep us on the same page, here.
+ faction = next.faction
+ if(previous)
+ previous.update_body_faction()
+ return 1
+ return 0
+
+// Worm meat.
+
+/obj/item/weapon/reagent_containers/food/snacks/meat/worm
+ name = "meat"
+ desc = "A chunk of pulsating meat."
+ icon_state = "wormmeat"
+ health = 180
+ filling_color = "#551A8B"
+ center_of_mass = list("x"=16, "y"=14)
+
+/obj/item/weapon/reagent_containers/food/snacks/meat/worm/Initialize()
+ . = ..()
+ reagents.add_reagent("protein", 6)
+ reagents.add_reagent("phoron", 3)
+ reagents.add_reagent("myelamine", 3)
+ src.bitesize = 3
+
+/obj/item/weapon/reagent_containers/food/snacks/meat/worm/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W,/obj/item/weapon/material/knife))
+ var/to_spawn = pickweight(
+ /obj/random/junk = 30,
+ /obj/random/trash = 30,
+ /obj/random/maintenance/clean = 15,
+ /obj/random/tool = 15,
+ /obj/random/medical = 3,
+ /obj/random/bomb_supply = 7,
+ /obj/random/contraband = 3,
+ /obj/random/unidentified_medicine/old_medicine = 7,
+ /obj/item/weapon/ore/strangerock = 3,
+ /obj/item/weapon/ore/phoron = 7,
+ /obj/random/handgun = 1,
+ /obj/random/toolbox = 4,
+ /obj/random/drinkbottle = 5
+ )
+
+ new to_spawn(get_turf(src))
+
+ if(prob(20))
+ user.visible_message("Something oozes out of \the [src] as it is cut.")
+
+ to_chat(user, "You cut the tissue holding the chunks together.")
+
+ ..()
diff --git a/html/changelogs/Mechoid - SpaceWorm.yml b/html/changelogs/Mechoid - SpaceWorm.yml
new file mode 100644
index 0000000000..a92623314b
--- /dev/null
+++ b/html/changelogs/Mechoid - SpaceWorm.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Mechoid
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Re-adds the Space Worm with New and Improved [Doesn't Break The Universe] logic."
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index f3836ae2c1..2f22e0ddaa 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/icons/mob/worm.dmi b/icons/mob/worm.dmi
new file mode 100644
index 0000000000..0d5e61978b
Binary files /dev/null and b/icons/mob/worm.dmi differ
diff --git a/icons/obj/food.dmi b/icons/obj/food.dmi
index d282fb3e6a..a22005ef29 100644
Binary files a/icons/obj/food.dmi and b/icons/obj/food.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 4c4221a50c..ab2cf8e93b 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -2075,6 +2075,7 @@
#include "code\modules\mob\living\simple_mob\subtypes\animal\space\carp.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\space\goose.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\space\space.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\animal\space\worm.dm"
#include "code\modules\mob\living\simple_mob\subtypes\blob\blob.dm"
#include "code\modules\mob\living\simple_mob\subtypes\blob\spore.dm"
#include "code\modules\mob\living\simple_mob\subtypes\humanoid\clown.dm"