diff --git a/aurorastation.dme b/aurorastation.dme
index a4b5cddfe62..efa7ecfef51 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -1587,6 +1587,7 @@
#include "code\modules\ghostroles\spawner\human\responseteams\response_team.dm"
#include "code\modules\ghostroles\spawner\human\responseteams\syndicate.dm"
#include "code\modules\ghostroles\spawner\human\responseteams\tcfl.dm"
+#include "code\modules\ghostroles\spawner\simplemob\borer.dm"
#include "code\modules\ghostroles\spawner\simplemob\maintdrone.dm"
#include "code\modules\ghostroles\spawner\simplemob\rat.dm"
#include "code\modules\ghostroles\spawner\simplemob\spider.dm"
diff --git a/code/controllers/subsystems/ghostroles.dm b/code/controllers/subsystems/ghostroles.dm
index f4077093f43..b6274eabe09 100644
--- a/code/controllers/subsystems/ghostroles.dm
+++ b/code/controllers/subsystems/ghostroles.dm
@@ -2,6 +2,7 @@
/datum/controller/subsystem/ghostroles
name = "Ghost Roles"
+ init_order = SS_INIT_JOBS
flags = SS_NO_FIRE
var/list/list/spawnpoints = list() //List of the available spawnpoints by spawnpoint type
@@ -10,6 +11,10 @@
var/list/spawners = list() //List of the available spawner datums
+ // For special spawners that have mobile or object spawnpoints
+ var/list/spawn_types = list("Golems", "Borers")
+ var/list/spawn_atom = list()
+
/datum/controller/subsystem/ghostroles/Recover()
src.spawnpoints = SSghostroles.spawnpoints
src.spawners = SSghostroles.spawners
@@ -36,6 +41,9 @@
CHECK_TICK
update_spawnpoint_status_by_identifier(identifier)
+ for(var/spawn_type in spawn_types)
+ spawn_atom[spawn_type] = list()
+
//Adds a spawnpoint to the spawnpoint list
/datum/controller/subsystem/ghostroles/proc/add_spawnpoints(var/obj/effect/ghostspawpoint/P)
if(!P.identifier) //If the spawnpoint has no identifier -> Abort
@@ -177,3 +185,25 @@
for(var/i in S.spawnpoints)
update_spawnpoint_status_by_identifier(i)
return
+
+/datum/controller/subsystem/ghostroles/proc/add_spawn_atom(var/ghost_role_name, var/atom/spawn_atom)
+ if(ghost_role_name && spawn_atom)
+ var/datum/ghostspawner/G = spawners[ghost_role_name]
+ if(G)
+ G.spawn_atoms += spawn_atom
+ if(length(G.spawn_atoms) == 1)
+ G.enable()
+
+/datum/controller/subsystem/ghostroles/proc/remove_spawn_atom(var/ghost_role_name, var/atom/spawn_atom)
+ if(ghost_role_name && spawn_atom)
+ var/datum/ghostspawner/G = spawners[ghost_role_name]
+ if(G)
+ G.spawn_atoms -= spawn_atom
+ if(!length(G.spawn_atoms))
+ G.disable()
+
+/datum/controller/subsystem/ghostroles/proc/get_spawn_atoms(var/ghost_role_name)
+ var/datum/ghostspawner/G = spawners[ghost_role_name]
+ if(G)
+ return G.spawn_atoms
+ return list()
diff --git a/code/game/antagonist/alien/borer.dm b/code/game/antagonist/alien/borer.dm
index 97fb9fc08e1..6487bdb8f39 100644
--- a/code/game/antagonist/alien/borer.dm
+++ b/code/game/antagonist/alien/borer.dm
@@ -4,7 +4,7 @@ var/datum/antagonist/xenos/borer/borers
id = MODE_BORER
role_text = "Cortical Borer"
role_text_plural = "Cortical Borers"
- mob_path = /mob/living/simple_animal/borer
+ mob_path = /mob/living/simple_animal/borer/roundstart
bantype = "Borer"
welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with ,x."
antag_indicator = "brainworm"
@@ -65,4 +65,4 @@ var/datum/antagonist/xenos/borer/borers
var/obj/item/organ/external/head = borer.host.get_organ(BP_HEAD)
head.implants += borer
else
- ..()
+ ..()
\ No newline at end of file
diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm
index 272e94f203e..e5e651e99c4 100644
--- a/code/game/jobs/whitelist.dm
+++ b/code/game/jobs/whitelist.dm
@@ -122,6 +122,9 @@ var/list/whitelist = list()
if (!istype(C) || C.holder)
return 0
+ if(!dbcon.IsConnected())
+ return TRUE
+
var/age_to_beat = 0
// Assume it's an antag role.
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 0b04983db58..8c79e007bb0 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -68,18 +68,19 @@ BREATH ANALYZER
else
. = "moderate"
-/proc/health_scan_mob(var/mob/M, var/mob/living/user, var/show_limb_damage = TRUE)
- if (((user.is_clumsy()) || (DUMB in user.mutations)) && prob(50))
- user.visible_message("\The [user] runs the scanner over the floor.", "You run the scanner over the floor.", "You hear metal repeatedly clunking against the floor.")
- to_chat(user, "Scan results for the floor:")
- to_chat(user, "Overall Status: Healthy")
- return
+/proc/health_scan_mob(var/mob/M, var/mob/living/user, var/show_limb_damage = TRUE, var/just_scan = FALSE)
+ if(!just_scan)
+ if (((user.is_clumsy()) || (DUMB in user.mutations)) && prob(50))
+ user.visible_message("\The [user] runs the scanner over the floor.", "You run the scanner over the floor.", "You hear metal repeatedly clunking against the floor.")
+ to_chat(user, "Scan results for the floor:")
+ to_chat(user, "Overall Status: Healthy")
+ return
- if(!usr.IsAdvancedToolUser())
- to_chat(usr, "You don't have the dexterity to do this!")
- return
+ if(!usr.IsAdvancedToolUser())
+ to_chat(usr, "You don't have the dexterity to do this!")
+ return
- user.visible_message("[user] runs the scanner over [M].","You run the scanner over [M].")
+ user.visible_message("[user] runs the scanner over [M].","You run the scanner over [M].")
if(!istype(M, /mob/living/carbon/human))
to_chat(user, "This scanner is designed for humanoid patients only.")
diff --git a/code/modules/ghostroles/spawner/base.dm b/code/modules/ghostroles/spawner/base.dm
index be8ab0c54b4..27849a07342 100644
--- a/code/modules/ghostroles/spawner/base.dm
+++ b/code/modules/ghostroles/spawner/base.dm
@@ -8,6 +8,7 @@
//Vars regarding the spawnpoints and conditions of the spawner
var/list/spawnpoints = null //List of the applicable spawnpoints (by name)
+ var/list/spawn_atoms = list() // List of atoms you can spawn at
var/landmark_name = null //Alternatively you can specify a landmark name
var/max_count = 0 //How often can this spawner be used
var/count = 0 //How ofen has this spawner been used
@@ -94,6 +95,11 @@
//This proc selects the spawnpoint to use.
/datum/ghostspawner/proc/select_spawnpoint(var/use=TRUE)
+ if(length(spawn_atoms))
+ var/chosen_spawn_atom = pick(spawn_atoms)
+ var/turf/T = get_turf(chosen_spawn_atom)
+ if(T)
+ return T
if(!isnull(spawnpoints))
for(var/spawnpoint in spawnpoints) //Loop through the applicable spawnpoints
var/turf/T = SSghostroles.get_spawnpoint(spawnpoint, use) //Gets the first matching spawnpoint or null if none are available
diff --git a/code/modules/ghostroles/spawner/human/golem.dm b/code/modules/ghostroles/spawner/human/golem.dm
index 6a03bd8c370..e1f484a2320 100644
--- a/code/modules/ghostroles/spawner/human/golem.dm
+++ b/code/modules/ghostroles/spawner/human/golem.dm
@@ -14,18 +14,12 @@
//The proc to actually spawn in the user
/datum/ghostspawner/human/golem/spawn_mob(mob/user)
- var/obj/effect/golemrune/rune
- var/list/global_runes = list()
-
- for(var/obj/effect/golemrune/eligible_rune in golem_runes)
- global_runes += eligible_rune
-
- if(!length(global_runes))
+ if(!length(spawn_atoms))
to_chat(user, span("danger", "There are no available golem runes to spawn at!"))
return FALSE
- rune = pick(global_runes)
+ var/obj/effect/golemrune/rune = pick(spawn_atoms)
- if(user)
+ if(user && rune)
return rune.spawn_golem(user)
return FALSE
\ No newline at end of file
diff --git a/code/modules/ghostroles/spawner/simplemob/borer.dm b/code/modules/ghostroles/spawner/simplemob/borer.dm
new file mode 100644
index 00000000000..14f43abb5d1
--- /dev/null
+++ b/code/modules/ghostroles/spawner/simplemob/borer.dm
@@ -0,0 +1,24 @@
+/datum/ghostspawner/simplemob/borer
+ short_name = "borer"
+ name = "Cortical Borer"
+ desc = "Infest crew, reproduce, repeat."
+ tags = list("Antagonist")
+
+ enabled = FALSE
+
+ spawn_mob = /mob/living/simple_animal/borer
+
+/datum/ghostspawner/simplemob/borer/select_spawnpoint(var/use)
+ return TRUE //We just fake it here, since the spawnpoint is selected if someone is spawned in.
+
+//The proc to actually spawn in the user
+/datum/ghostspawner/simplemob/borer/spawn_mob(mob/user)
+ if(!length(spawn_atoms))
+ to_chat(user, SPAN_DANGER("There are no available borers to spawn at!"))
+ return FALSE
+
+ var/mob/living/simple_animal/borer/spawn_borer = pick(spawn_atoms)
+
+ if(user && spawn_borer)
+ return spawn_borer.spawn_into_borer(user)
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/slime/items.dm b/code/modules/mob/living/carbon/slime/items.dm
index 87f8cf02f35..fea57c6be4f 100644
--- a/code/modules/mob/living/carbon/slime/items.dm
+++ b/code/modules/mob/living/carbon/slime/items.dm
@@ -216,8 +216,6 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle17"
-var/list/global/golem_runes = list()
-
/obj/effect/golemrune
anchored = TRUE
desc = "A strange rune used to create golems. It glows when spirits are nearby."
@@ -233,25 +231,15 @@ var/list/global/golem_runes = list()
. = ..()
START_PROCESSING(SSprocessing, src)
announce_to_ghosts()
- golem_runes += src
- if(length(golem_runes) == 1)
- for(var/role_spawner in SSghostroles.spawners)
- if(role_spawner == "golem")
- var/datum/ghostspawner/human/golem/golem_spawner = SSghostroles.spawners[role_spawner]
- golem_spawner.enable()
+ SSghostroles.add_spawn_atom("golem", src)
/obj/effect/golemrune/random_type/Initialize()
. = ..()
golem_type = pick(golem_types)
/obj/effect/golemrune/Destroy()
- . = ..()
- golem_runes -= src
- if(!length(golem_runes))
- for(var/role_spawner in SSghostroles.spawners)
- if(role_spawner == "golem")
- var/datum/ghostspawner/human/golem/golem_spawner = SSghostroles.spawners[role_spawner]
- golem_spawner.disable()
+ SSghostroles.remove_spawn_atom("golem", src)
+ return ..()
/obj/effect/golemrune/process()
var/mob/abstract/observer/ghost
diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm
index 5b15400ced1..d111ab63a8a 100644
--- a/code/modules/mob/living/simple_animal/borer/borer.dm
+++ b/code/modules/mob/living/simple_animal/borer/borer.dm
@@ -21,7 +21,7 @@
maxHealth = 40
health = 40
pass_flags = PASSTABLE
- universal_understand = 1
+ universal_understand = TRUE
holder_type = /obj/item/holder/borer
mob_size = 1
hunger_enabled = FALSE
@@ -33,10 +33,10 @@
var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth.
var/controlling // Used in human death check.
var/has_reproduced
- var/roundstart
+ var/request_player = TRUE
/mob/living/simple_animal/borer/roundstart
- roundstart = TRUE
+ request_player = FALSE
/mob/living/simple_animal/borer/LateLogin()
..()
@@ -52,15 +52,23 @@
verbs += /mob/living/proc/hide
truename = "[pick("Primary","Secondary","Tertiary","Quaternary")]-[rand(1000,9999)]"
- if(!roundstart)
- request_player()
+ if(request_player && !ckey && !client)
+ SSghostroles.add_spawn_atom("borer", src)
+ var/area/A = get_area(src)
+ if(A)
+ say_dead_direct("A borer has been birthed in [A.name]! Spawn in as it by using the ghost spawner menu in the ghost tab.")
+
+/mob/living/simple_animal/borer/death(gibbed, deathmessage)
+ SSghostroles.remove_spawn_atom("borer", src)
+ return ..(gibbed,deathmessage)
/mob/living/simple_animal/borer/Life()
..()
if(host)
- if(!stat && !host.stat)
+ if(!stat && host.stat != DEAD)
if(chemicals < 250)
chemicals++
+ if(host && !host.stat)
if(controlling && prob(host.getBrainLoss()/20))
host.say("*[pick(list("blink","blink_r","choke","drool","twitch","twitch_s","gasp"))]")
@@ -152,10 +160,10 @@
host = null
return
-//Procs for grabbing players.
-/mob/living/simple_animal/borer/proc/request_player()
- var/datum/ghosttrap/G = get_ghost_trap("cortical borer")
- G.request_player(src, "A cortical borer needs a player.")
+/mob/living/simple_animal/borer/proc/spawn_into_borer(var/mob/user)
+ ckey = user.ckey
+ qdel(user)
+ SSghostroles.remove_spawn_atom("borer", src)
/mob/living/simple_animal/borer/cannot_use_vents()
return
diff --git a/code/modules/mob/living/simple_animal/borer/borer_powers.dm b/code/modules/mob/living/simple_animal/borer/borer_powers.dm
index 4ee1ace42f7..996089d7b68 100644
--- a/code/modules/mob/living/simple_animal/borer/borer_powers.dm
+++ b/code/modules/mob/living/simple_animal/borer/borer_powers.dm
@@ -414,7 +414,7 @@
/mob/living/simple_animal/borer/verb/awaken_psionics()
set category = "Abilities"
set name = "Awaken Host Psionics (150)"
- set desc = "Probe into your host an unlock their psionic potential."
+ set desc = "Probe into your host and unlock their psionic potential. Note, it will give them a seizure as their brain realizes its potential."
if(!host)
to_chat(src, span("notice", "You are not inside a host body."))
@@ -437,7 +437,8 @@
chemicals -= 150
to_chat(src, span("notice", "You probe your tendrils deep within your host's zona bovinae, seeking to unleash their potential."))
to_chat(host, span("danger", "You feel some tendrils probe at the back of your head..."))
- addtimer(CALLBACK(src, .proc/jumpstart_psi), 100)
+ to_chat(host, FONT_LARGE(SPAN_WARNING("You feel something terrible coming on...")))
+ addtimer(CALLBACK(src, .proc/jumpstart_psi), 150)
/mob/living/simple_animal/borer/proc/jumpstart_psi()
to_chat(src, span("notice", "You succeed in interfacing with the host's zona bovinae, this will be a painful process for them."))
@@ -486,3 +487,17 @@
to_chat(src, span("notice", "You successfully manage to upgrade your host's [selected_faculty] faculty."))
to_chat(host, span("good", "A breeze of fresh air washes over your mind, you feel powerful!"))
to_chat(host, span("notice", "You have been psionically enlightened. You are now a [psychic_ranks_to_strings[host.psi.ranks[selected_faculty]]] in the [selected_faculty] faculty."))
+
+/mob/living/simple_animal/borer/verb/host_health_scan()
+ set category = "Abilities"
+ set name = "Inspect Host Health"
+ set desc = "Survey your host for injuries, allowing you to better treat them."
+
+ if(!host)
+ to_chat(src, SPAN_WARNING("You have no host to scan."))
+ return
+ if(stat)
+ to_chat(src, SPAN_WARNING("You cannot do that in your current state."))
+ return
+
+ health_scan_mob(host, src, TRUE, TRUE)
\ No newline at end of file
diff --git a/code/modules/shareddream/dream_entry.dm b/code/modules/shareddream/dream_entry.dm
index 8ef626d173f..be94d60815f 100644
--- a/code/modules/shareddream/dream_entry.dm
+++ b/code/modules/shareddream/dream_entry.dm
@@ -8,7 +8,9 @@ var/list/dream_entries = list()
// If either changes, they should be nocked back to the real world.
if(can_commune() && stat == UNCONSCIOUS && sleeping > 1)
if(!istype(bg) && client) // Don't spawn a brainghost if we're not logged in.
- bg = new(src) // Generate a new brainghost.
+ bg = new /mob/living/brain_ghost(src) // Generate a new brainghost.
+ if(isnull(bg)) // Prevents you from getting kicked if the brain ghost didn't spawn - geeves
+ return
bg.ckey = ckey
bg.client = client
ckey = "@[bg.ckey]"
@@ -27,8 +29,17 @@ var/list/dream_entries = list()
log_and_message_admins("has left the shared dream",bg)
var/mob/living/brain_ghost/old_bg = bg
bg = null
- ckey = old_bg.ckey
+
+ var/return_text = "You are ripped from the Srom as your body awakens."
+ var/mob/return_mob = src
+
+ var/mob/living/simple_animal/borer/B = has_brain_worms()
+ if(B?.host_brain)
+ return_mob = B.host_brain
+ return_text = "You are ripped from the Srom as you return to the captivity of your own mind."
+
+ return_mob.ckey = old_bg.ckey
old_bg.show_message("[bg] fades as their connection is severed.")
animate(old_bg, alpha=0, time = 200)
QDEL_IN(old_bg, 20)
- to_chat(src, "You are ripped from the Srom as your body awakens.")
\ No newline at end of file
+ to_chat(return_mob, SPAN_WARNING("[return_text]"))
\ No newline at end of file
diff --git a/html/changelogs/geeves-borer_stuff.yml b/html/changelogs/geeves-borer_stuff.yml
new file mode 100644
index 00000000000..832273e9bb7
--- /dev/null
+++ b/html/changelogs/geeves-borer_stuff.yml
@@ -0,0 +1,11 @@
+author: Geeves
+
+delete-after: True
+
+changes:
+ - rscadd: "Spawned borers now get added to the Ghostspawner menu, allowing players to inhabit them even if they didn't catch the original request."
+ - rscadd: "Borers have gained the ability to survey their host's health, allowing them to better choose when to administer certain chemicals."
+ - tweak: "Borers now regenerate chemicals even if their host is unconscious."
+ - rscadd: "Awakening psionics now take 15 seconds instead of 10, and the host now gets a message about feeling something bad coming on, allowing them to make a dash to safety and or seclusion."
+ - bugfix: "Skrell returning from Srom no longer eject controlling borers to the lobby by replacing their ckey into the aether."
+ - bugfix: "Skrell no longer spontaneously die when entering Srom on a map that doesn't have a Srom location."
\ No newline at end of file