diff --git a/code/_helpers/unsorted_vr.dm b/code/_helpers/unsorted_vr.dm
index e9b5f86f0b..d7650deb8f 100644
--- a/code/_helpers/unsorted_vr.dm
+++ b/code/_helpers/unsorted_vr.dm
@@ -7,4 +7,35 @@
return
var/atom/A
for(A=O, A && !isturf(A.loc), A=A.loc); // semicolon is for the empty statement
- return A
\ No newline at end of file
+ return A
+
+/proc/get_safe_ventcrawl_target(var/obj/machinery/atmospherics/unary/vent_pump/start_vent)
+ if(!start_vent.network || !start_vent.network.normal_members.len)
+ return
+ var/list/vent_list = list()
+ for(var/obj/machinery/atmospherics/unary/vent_pump/vent in start_vent.network.normal_members)
+ if(vent == start_vent)
+ continue
+ if(vent.welded)
+ continue
+ if(istype(get_area(vent), /area/crew_quarters/sleep)) //No going to dorms
+ continue
+ vent_list += vent
+ if(!vent_list.len)
+ return
+ return pick(vent_list)
+
+/proc/split_into_3(var/total)
+ if(!total || !isnum(total))
+ return
+
+ var/part1 = rand(0,total)
+ var/part2 = rand(0,total)
+ var/part3 = total-(part1+part2)
+
+ if(part3<0)
+ part1 = total-part1
+ part2 = total-part2
+ part3 = -part3
+
+ return list(part1, part2, part3)
\ No newline at end of file
diff --git a/code/game/objects/effects/spider_vr.dm b/code/game/objects/effects/spider_vr.dm
deleted file mode 100644
index d40f08aa9f..0000000000
--- a/code/game/objects/effects/spider_vr.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-/obj/effect/spider/spiderling/grub
- name = "grub larva"
- desc = "It never stays still for long."
- //M.icon = 'grub_vr.dmi'
- icon_state = "spiderling"
-
-obj/effect/spider/spiderling/grub/process()
- if(amount_grown >= 100)
- var/spawn_type = /mob/living/simple_animal/retaliate/solargrub
- new spawn_type(src.loc, src)
- qdel(src)
- else
- ..()
\ No newline at end of file
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 94c0a7999f..e9d25346f2 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -171,6 +171,13 @@
entry_vent = null
else if(entry_vent)
if(get_dist(src, entry_vent) <= 1)
+ //VOREStation Edit Start
+ var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = get_safe_ventcrawl_target(entry_vent)
+ if(!exit_vent)
+ return
+ if(1) //To maintain indentation level
+ //VOREStation Edit End
+ /* //VOREStation Removal Start - prevent spiders in dorms
if(entry_vent.network && entry_vent.network.normal_members.len)
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.network.normal_members)
@@ -179,6 +186,7 @@
entry_vent = null
return
var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents)
+ */ //VOREStation Removal End
/*if(prob(50))
src.visible_message("[src] scrambles into the ventillation ducts!")*/
diff --git a/code/modules/events/grubinfestation_vr.dm b/code/modules/events/grubinfestation_vr.dm
index a7898e11e6..6ef35901b0 100644
--- a/code/modules/events/grubinfestation_vr.dm
+++ b/code/modules/events/grubinfestation_vr.dm
@@ -14,13 +14,15 @@
/datum/event/grub_infestation/start()
var/list/vents = list()
- for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in world)
+ for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines)
+ if(istype(get_area(temp_vent), /area/crew_quarters/sleep))
+ continue
if(!temp_vent.welded && temp_vent.network && temp_vent.loc.z in using_map.station_levels)
if(temp_vent.network.normal_members.len > 50)
vents += temp_vent
while((spawncount >= 1) && vents.len)
var/obj/vent = pick(vents)
- new /obj/effect/spider/spiderling/grub(vent.loc)
+ new /mob/living/simple_animal/solargrub_larva(get_turf(vent))
vents -= vent
spawncount--
diff --git a/code/modules/mob/living/simple_animal/vore/solargrub.dm b/code/modules/mob/living/simple_animal/vore/solargrub.dm
index 69cd1d3abc..0d23c781f2 100644
--- a/code/modules/mob/living/simple_animal/vore/solargrub.dm
+++ b/code/modules/mob/living/simple_animal/vore/solargrub.dm
@@ -80,6 +80,10 @@ List of things solar grubs should be able to do:
if(attached)
if(prob(2))
src.visible_message("\The [src] begins to sink power from the net.")
+ if(prob(5))
+ var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
+ sparks.set_up(5, 0, get_turf(src))
+ sparks.start()
anchored = 1
PN = attached.powernet
PN.draw_power(150000)
@@ -114,4 +118,11 @@ List of things solar grubs should be able to do:
/mob/living/simple_animal/retaliate/solargrub/death()
src.anchored = 0
+ set_light(0)
..()
+
+/mob/living/simple_animal/retaliate/solargrub/handle_light()
+ . = ..()
+ if(. == 0 && !is_dead())
+ set_light(2.5, 1, COLOR_YELLOW)
+ return 1
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/vore/solargrub_larva.dm b/code/modules/mob/living/simple_animal/vore/solargrub_larva.dm
new file mode 100644
index 0000000000..b412be2a4b
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/vore/solargrub_larva.dm
@@ -0,0 +1,253 @@
+var/global/list/grub_machine_overlays = list()
+
+/mob/living/simple_animal/solargrub_larva
+ name = "solargrub larva"
+ desc = "A tiny wormy thing that can grow to massive sizes under the right conditions."
+ icon = 'icons/mob/vore.dmi'
+ icon_state = "grublarva"
+ icon_living = "grublarva"
+ icon_dead = "grublarva-dead"
+
+ health = 5
+ maxHealth = 5
+
+ faction = "grubs"
+
+ response_help = "pats"
+ response_disarm = "nudges"
+ response_harm = "stomps on"
+
+ mob_size = MOB_MINISCULE
+ pass_flags = PASSTABLE
+ can_pull_size = ITEMSIZE_TINY
+ can_pull_mobs = MOB_PULL_NONE
+ density = 0
+
+ stop_when_pulled = 0
+
+ var/static/list/ignored_machine_types = list(
+ /obj/machinery/atmospherics/unary/vent_pump,
+ /obj/machinery/atmospherics/unary/vent_scrubber,
+ /obj/machinery/door/firedoor
+ )
+
+ var/obj/machinery/atmospherics/unary/vent_pump/target_vent
+
+ var/datum/effect/effect/system/spark_spread/sparks
+ var/image/machine_effect
+
+ var/obj/machinery/abstract_grub_machine/powermachine
+ var/power_drained = 0
+ var/forced_out = 0
+
+/mob/living/simple_animal/solargrub_larva/New()
+ ..()
+ powermachine = new(src)
+ sparks = new(src)
+ sparks.set_up()
+ sparks.attach(src)
+ verbs += /mob/living/proc/ventcrawl
+
+/mob/living/simple_animal/solargrub_larva/death()
+ powermachine.draining = 0
+ set_light(0)
+ return ..()
+
+/mob/living/simple_animal/solargrub_larva/Destroy()
+ qdel_null(powermachine)
+ qdel_null(sparks)
+ qdel_null(machine_effect)
+ target_vent = null
+ return ..()
+
+/mob/living/simple_animal/solargrub_larva/Life()
+ . = ..()
+
+ if(machine_effect && !istype(loc, /obj/machinery))
+ qdel_null(machine_effect)
+
+ if(!. || ai_inactive)
+ return
+
+ if(power_drained >= 7 MEGAWATTS && prob(5))
+ expand_grub()
+ return
+
+ if(istype(loc, /obj/machinery))
+ if(machine_effect && air_master.current_cycle%30)
+ for(var/mob/M in player_list)
+ M << machine_effect
+ if(prob(10))
+ sparks.start()
+ return
+
+ if(stance == STANCE_IDLE)
+ if(forced_out)
+ forced_out = Clamp(0, forced_out--, forced_out)
+ return
+
+ if(target_vent)
+ if(Adjacent(target_vent))
+ spawn()
+ do_ventcrawl(target_vent)
+ target_vent = null
+ else
+ target_vent = null
+ stop_automated_movement = 0
+ walk(src, 0)
+ return
+
+ if(prob(20))
+ var/list/possible_machines = list()
+ for(var/obj/machinery/M in orange(1,src))
+ if(!Adjacent(M))
+ continue
+ if(istype(M, /obj/machinery/power/apc) || istype(M, /obj/machinery/power/smes)) //APCs and SMES units don't actually use power, but it's too thematic to ignore them
+ possible_machines += M
+ continue
+ if(is_type_in_list(M, ignored_machine_types))
+ continue
+ if(!M.idle_power_usage && !M.active_power_usage) //If it can't use power at all, ignore it
+ continue
+ possible_machines += M
+ if(possible_machines.len)
+ enter_machine(pick(possible_machines))
+ return
+
+ if(prob(10))
+ var/list/vents = list()
+ for(var/obj/machinery/atmospherics/unary/vent_pump/vent in view(7,src))
+ if(vent.welded)
+ continue
+ vents += vent
+ if(vents.len)
+ var/picked = pick(vents)
+ target_vent = picked
+ WanderTowards(get_turf(picked))
+ return
+
+/mob/living/simple_animal/solargrub_larva/proc/enter_machine(var/obj/machinery/M)
+ if(!istype(M))
+ return
+ forceMove(M)
+ powermachine.draining = 2
+ visible_message("\The [src] finds an opening and crawls inside \the [M].")
+ if(!(M.type in grub_machine_overlays))
+ generate_machine_effect(M)
+ machine_effect = image(grub_machine_overlays[M.type], M) //Can't do this the reasonable way with an overlay,
+ for(var/mob/L in player_list) //because nearly every machine updates its icon by removing all overlays first
+ L << machine_effect
+
+/mob/living/simple_animal/solargrub_larva/proc/generate_machine_effect(var/obj/machinery/M)
+ var/icon/I = new /icon(M.icon, M.icon_state)
+ I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD)
+ I.Blend(new /icon('icons/effects/alert.dmi', "_red"),ICON_MULTIPLY)
+ grub_machine_overlays[M.type] = I
+
+/mob/living/simple_animal/solargrub_larva/proc/eject_from_machine(var/obj/machinery/M)
+ if(!M)
+ if(istype(loc, /obj/machinery))
+ M = loc
+ else
+ return
+ forceMove(get_turf(M))
+ sparks.start()
+ if(machine_effect)
+ qdel_null(machine_effect)
+ forced_out += rand(5,15)
+ powermachine.draining = 1
+
+/mob/living/simple_animal/solargrub_larva/proc/do_ventcrawl(var/obj/machinery/atmospherics/unary/vent_pump/vent)
+ if(!vent)
+ return
+ var/obj/machinery/atmospherics/unary/vent_pump/end_vent = get_safe_ventcrawl_target(vent)
+ if(!end_vent)
+ return
+ forceMove(vent)
+ playsound(vent, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
+ vent.visible_message("\The [src] wiggles into \the [vent]!")
+ var/redirect_attempts = 3
+ while(redirect_attempts)
+ var/travel_time = round(get_dist(get_turf(src), get_turf(end_vent)) / 2)
+ sleep(travel_time)
+ if(end_vent.welded)
+ end_vent = get_safe_ventcrawl_target(vent)
+ if(!end_vent)
+ forceMove(get_turf(vent))
+ return
+ redirect_attempts--
+ continue
+ break
+ playsound(end_vent, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
+ forceMove(get_turf(end_vent))
+
+/mob/living/simple_animal/solargrub_larva/proc/expand_grub()
+ eject_from_machine()
+ visible_message("\The [src] suddenly balloons in size!")
+ new /mob/living/simple_animal/retaliate/solargrub(get_turf(src))
+// var/mob/living/simple_animal/retaliate/solargrub/grub = new(get_turf(src))
+// grub.power_drained = power_drained //TODO
+ qdel(src)
+
+/mob/living/simple_animal/solargrub_larva/handle_light()
+ . = ..()
+ if(. == 0 && !is_dead())
+ set_light(1.5, 1, COLOR_YELLOW)
+ return 1
+
+
+/obj/machinery/abstract_grub_machine
+ var/total_active_power_usage = 45 KILOWATTS
+ var/list/active_power_usages = list(15 KILOWATTS, 15 KILOWATTS, 15 KILOWATTS)
+ var/total_idle_power_usage = 3 KILOWATTS
+ var/list/idle_power_usages = list(1 KILOWATTS, 1 KILOWATTS, 1 KILOWATTS)
+ var/draining = 1
+ var/mob/living/simple_animal/solargrub_larva/grub
+
+/obj/machinery/abstract_grub_machine/New()
+ ..()
+ shuffle_power_usages()
+ grub = loc
+ if(!istype(grub))
+ grub = null
+ qdel(src)
+
+/obj/machinery/abstract_grub_machine/Destroy()
+ grub = null
+ return ..()
+
+/obj/machinery/abstract_grub_machine/process()
+ if(!draining)
+ return
+ var/area/A = get_area(src)
+ if(!A)
+ return
+ var/list/power_list
+ switch(draining)
+ if(1)
+ power_list = idle_power_usages
+ if(2)
+ power_list = active_power_usages
+ for(var/i = 1 to power_list.len)
+ if(A.powered(i))
+ use_power(power_list[i], i)
+ grub.power_drained += power_list[i]
+ if(prob(5))
+ shuffle_power_usages()
+
+/obj/machinery/abstract_grub_machine/proc/shuffle_power_usages()
+ total_active_power_usage = rand(30 KILOWATTS, 60 KILOWATTS)
+ total_idle_power_usage = rand(1 KILOWATTS, 5 KILOWATTS)
+ active_power_usages = split_into_3(total_active_power_usage)
+ idle_power_usages = split_into_3(total_idle_power_usage)
+
+
+/obj/item/device/multitool/afterattack(obj/O, mob/user, proximity)
+ if(proximity)
+ if(istype(O, /obj/machinery))
+ var/mob/living/simple_animal/solargrub_larva/grub = locate() in O
+ if(grub)
+ grub.eject_from_machine(O)
+ to_chat(user, "You disturb a grub nesting in \the [O]!")
+ return
+ return ..()
\ No newline at end of file
diff --git a/icons/mob/vore.dmi b/icons/mob/vore.dmi
index e34a68f2e8..0a6958c1b7 100644
Binary files a/icons/mob/vore.dmi and b/icons/mob/vore.dmi differ
diff --git a/vorestation.dme b/vorestation.dme
index 8ecbda2e26..d38356c3e6 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -811,7 +811,6 @@
#include "code\game\objects\effects\misc.dm"
#include "code\game\objects\effects\overlays.dm"
#include "code\game\objects\effects\portals.dm"
-#include "code\game\objects\effects\spider_vr.dm"
#include "code\game\objects\effects\spiders.dm"
#include "code\game\objects\effects\step_triggers.dm"
#include "code\game\objects\effects\zone_divider.dm"
@@ -2027,6 +2026,7 @@
#include "code\modules\mob\living\simple_animal\vore\panther.dm"
#include "code\modules\mob\living\simple_animal\vore\snake.dm"
#include "code\modules\mob\living\simple_animal\vore\solargrub.dm"
+#include "code\modules\mob\living\simple_animal\vore\solargrub_larva.dm"
#include "code\modules\mob\living\simple_animal\vore\wah.dm"
#include "code\modules\mob\living\simple_animal\vore\wolf.dm"
#include "code\modules\mob\living\simple_animal\vore\zz_vore_overrides.dm"