diff --git a/code/modules/mob/dead/observer/free_vr.dm b/code/modules/mob/dead/observer/free_vr.dm
new file mode 100644
index 0000000000..6b38abfff5
--- /dev/null
+++ b/code/modules/mob/dead/observer/free_vr.dm
@@ -0,0 +1,68 @@
+var/global/list/prevent_respawns = list()
+
+/hook/death/proc/quit_notify(mob/dead)
+ if(ishuman(dead))
+ to_chat(dead,"You're dead! If you don't intend to continue playing this round as this character, please use the Quit This Round verb in the OOC tab to free your job slot.")
+
+ return TRUE
+
+/mob/observer/dead/verb/cleanup()
+ set name = "Quit This Round"
+ set category = "OOC"
+ set desc = "Free your job slot, remove yourself from the manifest, and prevent respawning as this character for this round."
+
+ var/confirm = alert("This will free up your job slot, remove you from the manifest, and prevent you from respawning with this character for the round. You can rejoin as another \
+ character if you like. Do this now?","Quit This Round","Quit Round","Cancel")
+ if(confirm != "Quit Round")
+ return
+
+ //Why are you clicking this button?
+ if(!mind || !mind.assigned_role)
+ to_chat(src,"Either you haven't played this round, or you already used this verb.")
+ return
+
+ //Add them to the nope list
+ prevent_respawns += mind.name
+
+ //Update any existing objectives involving this mob.
+ for(var/datum/objective/O in all_objectives)
+ if(O.target == src.mind)
+ if(O.owner && O.owner.current)
+ to_chat(O.owner.current,"You get the feeling your target is no longer within your reach...")
+ qdel(O)
+
+ //Resleeving cleanup
+ if(src.mind.name in transcore.backed_up)
+ var/datum/transhuman/mind_record/MR = transcore.backed_up[src.mind.name]
+ transcore.stop_backup(MR)
+ if(src.mind.name in transcore.body_scans) //This uses mind names to avoid people cryo'ing a printed body to delete body scans.
+ var/datum/transhuman/body_record/BR = transcore.body_scans[src.mind.name]
+ transcore.remove_body(BR)
+
+ //Job slot cleanup
+ var/job = src.mind.assigned_role
+ job_master.FreeRole(job)
+
+ //Their objectives cleanup
+ if(src.mind.objectives.len)
+ qdel(src.mind.objectives)
+ src.mind.special_role = null
+
+ //Cut the PDA manifest (ugh)
+ if(PDA_Manifest.len)
+ PDA_Manifest.Cut()
+ for(var/datum/data/record/R in data_core.medical)
+ if((R.fields["name"] == src.real_name))
+ qdel(R)
+ for(var/datum/data/record/T in data_core.security)
+ if((T.fields["name"] == src.real_name))
+ qdel(T)
+ for(var/datum/data/record/G in data_core.general)
+ if((G.fields["name"] == src.real_name))
+ qdel(G)
+
+ //This removes them from being 'active' list on join screen
+ src.mind.assigned_role = null
+
+ //Feedback
+ to_chat(src,"Your job has been free'd up, and you can rejoin as another character or quit. Thanks for using this verb, it helps the server!")
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index e19a308c47..2c7af27235 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -255,6 +255,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients
usr << "Another consciousness is in your body... it is resisting you."
return
+ //VOREStation Add
+ if(prevent_respawns.Find(mind.name))
+ to_chat(usr,"You already quit this round as this character, sorry!")
+ return
+ //VOREStation Add End
if(mind.current.ajourn && mind.current.stat != DEAD) //check if the corpse is astral-journeying (it's client ghosted using a cultist rune).
var/found_rune
for(var/obj/effect/rune/R in mind.current.loc) //whilst corpse is alive, we can only reenter the body if it's on the rune
diff --git a/code/modules/mob/new_player/new_player_vr.dm b/code/modules/mob/new_player/new_player_vr.dm
index 199563de4a..f32834589a 100644
--- a/code/modules/mob/new_player/new_player_vr.dm
+++ b/code/modules/mob/new_player/new_player_vr.dm
@@ -3,7 +3,12 @@
//No OOC notes
if (config.allow_Metadata && client && client.prefs && (isnull(client.prefs.metadata) || length(client.prefs.metadata) < 15))
- src << "Please set informative OOC notes related to ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup."
+ to_chat(src,"Please set informative OOC notes related to ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.")
+ pass = FALSE
+
+ //Are they on the VERBOTEN LIST?
+ if (prevent_respawns.Find(client.prefs.real_name))
+ to_chat(src,"You've already quit the round as this character. You can't go back now that you've free'd your job slot. Play another character, or wait for the next round.")
pass = FALSE
//Custom species checks
@@ -12,7 +17,7 @@
//Didn't name it
if(!client.prefs.custom_species)
pass = FALSE
- src << "You have to name your custom species. Do this on the VORE tab in character setup."
+ to_chat(src,"You have to name your custom species. Do this on the VORE tab in character setup.")
//Check traits/costs
var/list/megalist = client.prefs.pos_traits + client.prefs.neu_traits + client.prefs.neg_traits
@@ -25,7 +30,7 @@
//A trait was removed from the game
if(isnull(cost))
pass = FALSE
- src << "Your custom species is not playable. One or more traits appear to have been removed from the game or renamed. Enter character setup to correct this."
+ to_chat(src,"Your custom species is not playable. One or more traits appear to have been removed from the game or renamed. Enter character setup to correct this.")
break
else
points_left -= traits_costs[T]
@@ -33,7 +38,7 @@
//Went into negatives
if(points_left < 0 || traits_left < 0)
pass = FALSE
- src << "Your custom species is not playable. Reconfigure your traits on the VORE tab."
+ to_chat(src,"Your custom species is not playable. Reconfigure your traits on the VORE tab.")
//Final popup notice
if (!pass)
diff --git a/vorestation.dme b/vorestation.dme
index 31eeacc2fb..9dddf9e064 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -1653,6 +1653,7 @@
#include "code\modules\mob\typing_indicator.dm"
#include "code\modules\mob\update_icons.dm"
#include "code\modules\mob\dead\death.dm"
+#include "code\modules\mob\dead\observer\free_vr.dm"
#include "code\modules\mob\dead\observer\login.dm"
#include "code\modules\mob\dead\observer\logout.dm"
#include "code\modules\mob\dead\observer\observer.dm"