diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index f9c07321206..40b021f606f 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -3,37 +3,49 @@
desc = "Looks unstable. Best to test it carefully."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "portal"
- density = 1
- unacidable = 1//Can't destroy energy portals.
+ density = TRUE
+ unacidable = TRUE //Can't destroy energy portals.
+ var/does_teleport = TRUE // Some portals might just be visual
+ var/has_lifespan = TRUE // Whether we want to directly control the lifespan or not
var/failchance = 5
- var/obj/target = null
- var/creator = null
- var/precision = 1
- anchored = 1.0
+ var/obj/target
+ var/creator
+ var/precision = TRUE
+ anchored = TRUE
/obj/effect/portal/CollidedWith(mob/M as mob|obj)
set waitfor = FALSE
- src.teleport(M)
+
+ if(does_teleport)
+ src.teleport(M)
/obj/effect/portal/Crossed(AM as mob|obj)
set waitfor = FALSE
- src.teleport(AM)
+
+ if(does_teleport)
+ src.teleport(AM)
/obj/effect/portal/attack_hand(mob/user as mob)
set waitfor = FALSE
- src.teleport(user)
+
+ if(does_teleport)
+ src.teleport(user)
/obj/effect/portal/New(loc, turf/target, creator=null, lifespan=300, precise = 1)
..()
- src.target = target
- src.creator = creator
+ if(target)
+ src.target = target
+ if(creator)
+ src.creator = creator
- if(lifespan > 0)
+ if(has_lifespan && lifespan > 0)
QDEL_IN(src, lifespan)
precision = precise
/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj)
+ if(!does_teleport) // just to be safe
+ return
if(istype(M, /obj/effect)) //sparks don't teleport
return
if (icon_state == "portal1")
@@ -47,3 +59,78 @@
do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0)
else
do_teleport(M, target, precision)
+
+/obj/effect/portal/spawner
+ name = "portal"
+ desc = "Looks like a one-way portal, don't come too close."
+ description_info = "This portal is a spawner portal. You cannot enter it to teleport, but it will periodically spawn things."
+ does_teleport = FALSE
+ has_lifespan = FALSE
+ layer = OBJ_LAYER - 0.01
+ var/list/spawn_things = list() // The list things to spawn
+ var/num_of_spawns // How many times we want to spawn them before qdel
+ var/next_spawn
+
+/obj/effect/portal/spawner/Initialize()
+ . = ..()
+ START_PROCESSING(SSprocessing, src)
+ next_spawn = world.time + 5 SECONDS
+
+/obj/effect/portal/spawner/Destroy()
+ STOP_PROCESSING(SSprocessing, src)
+ return ..()
+
+/obj/effect/portal/spawner/process()
+ if(next_spawn < world.time)
+ visible_message(SPAN_WARNING("\The [src] spits something out!"))
+ for(var/thing in spawn_things)
+ var/spawn_path = text2path(thing)
+ var/atom/spawned_thing = new spawn_path(get_turf(src))
+
+ if(istype(spawned_thing, /obj/item/stack))
+ var/obj/item/stack/stack = spawned_thing
+ stack.amount = spawn_things[thing]
+ stack.throw_at(get_random_turf_in_range(get_turf(src), 1), 2)
+ else
+ for(var/i = 1; i < spawn_things[thing]; i++)
+ var/atom/movable/A = new spawn_path(get_turf(src))
+ A.throw_at(get_random_turf_in_range(get_turf(src), 1), 2)
+
+ next_spawn = world.time + 30 SECONDS
+ num_of_spawns--
+ if(num_of_spawns <= 0)
+ visible_message(SPAN_WARNING("\The [src] collapses in on itself!"))
+ qdel(src)
+
+/obj/effect/portal/spawner/metal
+ num_of_spawns = 3
+ spawn_things = list(
+ "/obj/item/stack/material/steel" = 10,
+ "/obj/item/stack/material/plasteel" = 5
+ )
+
+/obj/effect/portal/spawner/rare_metal
+ num_of_spawns = 4
+ spawn_things = list(
+ "/obj/item/stack/material/gold" = 2,
+ "/obj/item/stack/material/silver" = 2,
+ "/obj/item/stack/material/uranium" = 2
+ )
+
+/obj/effect/portal/spawner/phoron
+ num_of_spawns = 3
+ spawn_things = list(
+ "/obj/item/stack/material/phoron" = 10
+ )
+
+/obj/effect/portal/spawner/monkey_cube
+ num_of_spawns = 1
+ spawn_things = list(
+ "/obj/item/reagent_containers/food/snacks/monkeycube" = 4
+ )
+
+/obj/effect/portal/spawner/cow // debug but funny so im keeping it
+ num_of_spawns = 1
+ spawn_things = list(
+ "/mob/living/simple_animal/cow" = 1
+ )
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/power_cells.dm b/code/game/objects/items/weapons/power_cells.dm
index 7a723f0c5c6..861c9347ef6 100644
--- a/code/game/objects/items/weapons/power_cells.dm
+++ b/code/game/objects/items/weapons/power_cells.dm
@@ -127,11 +127,24 @@
name = "charged slime core"
desc = "A yellow slime core infused with phoron, it crackles with power."
origin_tech = list(TECH_POWER = 2, TECH_BIO = 4)
- icon = 'icons/mob/npc/slimes.dmi' //'icons/obj/harvest.dmi'
- icon_state = "yellow slime extract" //"potato_battery"
+ icon = 'icons/mob/npc/slimes.dmi'
+ icon_state = "yellow slime extract"
maxcharge = 10000
matter = null
+ var/next_recharge
+/obj/item/cell/slime/Initialize()
+ . = ..()
+ START_PROCESSING(SSprocessing, src)
+
+/obj/item/cell/slime/Destroy()
+ STOP_PROCESSING(SSprocessing, src)
+ return ..()
+
+/obj/item/cell/slime/process()
+ if(next_recharge < world.time)
+ charge += maxcharge / 10
+ next_recharge = world.time + 1 MINUTE
/obj/item/cell/device/emergency_light
name = "miniature power cell"
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 5da631df164..7b9cf20965d 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -1243,7 +1243,7 @@
var/obj/item/slime_extract/T = holder.my_atom
if(T.uses > 0)
return ..()
- return 0
+ return FALSE
/datum/chemical_reaction/slime/on_reaction(var/datum/reagents/holder)
var/obj/item/slime_extract/T = holder.my_atom
@@ -1263,9 +1263,8 @@
required = /obj/item/slime_extract/grey
/datum/chemical_reaction/slime/spawn/on_reaction(var/datum/reagents/holder)
- holder.my_atom.visible_message("Infused with phoron, the core begins to quiver and grow, and soon a new baby slime emerges from it!")
- var/mob/living/carbon/slime/S = new /mob/living/carbon/slime
- S.forceMove(get_turf(holder.my_atom))
+ holder.my_atom.visible_message(SPAN_WARNING("Infused with phoron, the core begins to quiver and grow, and soon a new baby slime emerges from it!"))
+ new /mob/living/carbon/slime(get_turf(holder.my_atom))
..()
/datum/chemical_reaction/slime/monkey
@@ -1277,9 +1276,7 @@
required = /obj/item/slime_extract/grey
/datum/chemical_reaction/slime/monkey/on_reaction(var/datum/reagents/holder)
- for(var/i = 1, i <= 3, i++)
- var /obj/item/reagent_containers/food/snacks/monkeycube/M = new /obj/item/reagent_containers/food/snacks/monkeycube
- M.forceMove(get_turf(holder.my_atom))
+ new /obj/effect/portal/spawner/monkey_cube(get_turf(holder.my_atom))
..()
//Green
@@ -1301,12 +1298,7 @@
required = /obj/item/slime_extract/metal
/datum/chemical_reaction/slime/metal/on_reaction(var/datum/reagents/holder)
- var/obj/item/stack/material/steel/M = new /obj/item/stack/material/steel
- M.amount = 15
- M.forceMove(get_turf(holder.my_atom))
- var/obj/item/stack/material/plasteel/P = new /obj/item/stack/material/plasteel
- P.amount = 5
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/effect/portal/spawner/metal(get_turf(holder.my_atom))
..()
//Gold - added back in
@@ -1375,6 +1367,18 @@
step(C, pick(NORTH,SOUTH,EAST,WEST))
..()
+/datum/chemical_reaction/slime/rare_metal
+ name = "Slime Rare Metal"
+ id = "rm_metal"
+ result = null
+ required_reagents = list("phoron" = 1)
+ result_amount = 1
+ required = /obj/item/slime_extract/gold
+
+/datum/chemical_reaction/slime/rare_metal/on_reaction(var/datum/reagents/holder)
+ new /obj/effect/portal/spawner/rare_metal(get_turf(holder.my_atom))
+ ..()
+
//Silver
/datum/chemical_reaction/slime/bork
name = "Slime Bork"
@@ -1456,11 +1460,13 @@
/datum/chemical_reaction/slime/freeze/on_reaction(var/datum/reagents/holder)
..()
- sleep(50)
+ addtimer(CALLBACK(src, .proc/do_reaction, holder), 50)
+
+/datum/chemical_reaction/slime/freeze/proc/do_reaction(var/datum/reagents/holder)
playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1)
- for(var/mob/living/M in range (get_turf(holder.my_atom), 7))
+ for(var/mob/living/M in range(7, get_turf(holder.my_atom)))
M.bodytemperature -= 140
- to_chat(M, "You feel a chill!")
+ to_chat(M, SPAN_WARNING("You feel a cold chill!"))
//Orange
/datum/chemical_reaction/slime/casp
@@ -1482,12 +1488,13 @@
/datum/chemical_reaction/slime/fire/on_reaction(var/datum/reagents/holder)
..()
- sleep(50)
- var/turf/location = get_turf(holder.my_atom.loc)
- for(var/turf/simulated/floor/target_tile in range(0, location))
+ addtimer(CALLBACK(src, .proc/do_reaction, holder), 50)
+
+/datum/chemical_reaction/slime/fire/proc/do_reaction(var/datum/reagents/holder)
+ var/turf/location = get_turf(holder.my_atom)
+ for(var/turf/simulated/floor/target_tile in range(1, location))
target_tile.assume_gas("phoron", 25, 1400)
- spawn (0)
- target_tile.hotspot_expose(700, 400)
+ target_tile.hotspot_expose(700, 400)
//Yellow
/datum/chemical_reaction/slime/overload
@@ -1497,10 +1504,11 @@
required_reagents = list("blood" = 1)
result_amount = 1
required = /obj/item/slime_extract/yellow
+ mix_message = "The slime extract begins to vibrate violently!"
/datum/chemical_reaction/slime/overload/on_reaction(var/datum/reagents/holder, var/created_volume)
..()
- empulse(get_turf(holder.my_atom), 3, 7)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/empulse, get_turf(holder.my_atom), 3, 7), 50)
/datum/chemical_reaction/slime/cell
name = "Slime Powercell"
@@ -1509,10 +1517,10 @@
required_reagents = list("phoron" = 1)
result_amount = 1
required = /obj/item/slime_extract/yellow
+ mix_message = "A small sparking part of the extract core falls onto the floor."
/datum/chemical_reaction/slime/cell/on_reaction(var/datum/reagents/holder, var/created_volume)
- var/obj/item/cell/slime/P = new /obj/item/cell/slime
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/item/cell/slime(get_turf(holder.my_atom))
/datum/chemical_reaction/slime/glow
name = "Slime Glow"
@@ -1525,8 +1533,7 @@
/datum/chemical_reaction/slime/glow/on_reaction(var/datum/reagents/holder, var/created_volume)
..()
- var/obj/item/device/flashlight/slime/F = new /obj/item/device/flashlight/slime
- F.forceMove(get_turf(holder.my_atom))
+ new /obj/item/device/flashlight/slime(get_turf(holder.my_atom))
//Purple
/datum/chemical_reaction/slime/psteroid
@@ -1539,8 +1546,7 @@
/datum/chemical_reaction/slime/psteroid/on_reaction(var/datum/reagents/holder, var/created_volume)
..()
- var/obj/item/slimesteroid/P = new /obj/item/slimesteroid
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/item/slimesteroid(get_turf(holder.my_atom))
/datum/chemical_reaction/slime/jam
name = "Slime Jam"
@@ -1561,9 +1567,7 @@
/datum/chemical_reaction/slime/plasma/on_reaction(var/datum/reagents/holder)
..()
- var/obj/item/stack/material/phoron/P = new /obj/item/stack/material/phoron
- P.amount = 10
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/effect/portal/spawner/phoron(get_turf(holder.my_atom))
//Red
/datum/chemical_reaction/slime/glycerol
@@ -1585,8 +1589,8 @@
/datum/chemical_reaction/slime/bloodlust/on_reaction(var/datum/reagents/holder)
..()
for(var/mob/living/carbon/slime/slime in viewers(get_turf(holder.my_atom), null))
- slime.rabid = 1
- slime.visible_message("The [slime] is driven into a frenzy!")
+ slime.rabid = TRUE
+ slime.visible_message(SPAN_WARNING("\icon[slime] \The [slime] is driven into a frenzy!"))
//Pink
/datum/chemical_reaction/slime/ppotion
@@ -1599,8 +1603,7 @@
/datum/chemical_reaction/slime/ppotion/on_reaction(var/datum/reagents/holder)
..()
- var/obj/item/slimepotion/P = new /obj/item/slimepotion
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/item/slimepotion(get_turf(holder.my_atom))
//Black
/datum/chemical_reaction/slime/mutate2
@@ -1623,8 +1626,7 @@
/datum/chemical_reaction/slime/explosion/on_reaction(var/datum/reagents/holder)
..()
- sleep(50)
- explosion(get_turf(holder.my_atom), 1, 3, 6)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, get_turf(holder.my_atom), 1, 3, 6), 50)
//Light Pink
/datum/chemical_reaction/slime/potion2
@@ -1637,8 +1639,7 @@
/datum/chemical_reaction/slime/potion2/on_reaction(var/datum/reagents/holder)
..()
- var/obj/item/slimepotion2/P = new /obj/item/slimepotion2
- P.forceMove(get_turf(holder.my_atom))
+ new /obj/item/slimepotion2(get_turf(holder.my_atom))
//Adamantine
/datum/chemical_reaction/slime/golem
@@ -1648,11 +1649,11 @@
required_reagents = list("phoron" = 1)
result_amount = 1
required = /obj/item/slime_extract/adamantine
+ mix_message = "A soft fizzle is heard within the slime extract, and mystic runes suddenly appear on the floor beneath it!"
/datum/chemical_reaction/slime/golem/on_reaction(var/datum/reagents/holder)
..()
- var/obj/effect/golemrune/Z = new /obj/effect/golemrune
- Z.forceMove(get_turf(holder.my_atom))
+ new /obj/effect/golemrune(get_turf(holder.my_atom))
/datum/chemical_reaction/soap_key
name = "Soap Key"
diff --git a/html/changelogs/geeves-xenobio_core_improvements.yml b/html/changelogs/geeves-xenobio_core_improvements.yml
new file mode 100644
index 00000000000..87898602ec5
--- /dev/null
+++ b/html/changelogs/geeves-xenobio_core_improvements.yml
@@ -0,0 +1,12 @@
+author: Geeves
+
+delete-after: True
+
+changes:
+ - tweak: "Tweaked how xenobio spawns in steel, plasteel and phoron, when using slime extracts. They will make more of each now."
+ - rscadd: "The gray extract now spawns 4 monkey cubes instead of 3."
+ - rscadd: "Injecting phoron into a gold extract now spawns small amounts of gold, silver, and uranium."
+ - rscadd: "Slime core batteries now regain 10% of their charge every minute."
+ - tweak: "The slime extract EMP now has a five second timer, instead of being instant."
+ - tweak: "The oil extract flame now affects a slightly larger area, one tile to a one tile radius."
+ - bugfix: "The frost extract now properly chills every in a seven tile radius."
\ No newline at end of file