Fixes the issues with ghost role spawners being able to spawn userless bodies (#73636)

## About The Pull Request
Turns out there was no verification to ensure that you weren't already
in the process of trying to join a ghost role, when clicking on a ghost
role spawner.

That resulted in people being able to double-click on a spawner and
basically, trying to spawn twice... Or thrice... Or even more than that
if they're fast.

Of course, that's not a good thing, so I made it so you can only try to
spawn one time at a time, which includes only getting one prompt at a
time. That includes ALL spawners, because I know that bug will otherwise
pop up at some point from someone thinking they're clever.

I added a CRASH() for the handling of ckey-less ghosts, in case someone
tries to do another subtype that somehow manages to bypass all of the
checks I already added. That way, they'll know if they fuck up.

I also made a second uses check considering there's stoplags in the
is_banned() check.

Fixes https://github.com/tgstation/tgstation/issues/73619.

## Why It's Good For The Game
Having mindless bodies is just not very cash money and it can break
spawners to the point of making them go to negative amounts of uses,
which isn't very cash money.

## Changelog

🆑 GoldenAlpharex
fix: You should only be able to spawn as one ghost role at a time. Close
the prompt if you want to spawn as another one. Begone soulless
randomgen humans!
/🆑
This commit is contained in:
GoldenAlpharex
2023-03-12 22:08:53 -04:00
committed by GitHub
parent e977d4c5f1
commit 1c9990eba4
+33 -2
View File
@@ -115,6 +115,9 @@
var/uses = 1
/// Does the spawner delete itself when it runs out of uses?
var/deletes_on_zero_uses_left = TRUE
/// A list of the ckeys that currently are trying to access this spawner, so that they can't try to spawn more than once (in case there's sleeps).
/// Static because you only really want to be able to spawn in one spawner at a time, obviously.
var/static/list/ckeys_trying_to_spawn
////descriptions
@@ -152,27 +155,45 @@
if(!SSticker.HasRoundStarted() || !loc)
return
// We don't open the prompt more than once at a time.
if(LAZYFIND(ckeys_trying_to_spawn, user.ckey))
return
var/user_ckey = user.ckey // Just in case shenanigans happen, we always want to remove it from the list.
LAZYADD(ckeys_trying_to_spawn, user_ckey)
if(prompt_ghost)
var/prompt = "Become [prompt_name]?"
if(user.can_reenter_corpse && user.mind)
prompt += " (Warning, You can no longer be revived!)"
var/ghost_role = tgui_alert(usr, prompt, buttons = list("Yes", "No"), timeout = 10 SECONDS)
if(ghost_role != "Yes" || !loc || QDELETED(user))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !(flags_1 & ADMIN_SPAWNED_1))
to_chat(user, span_warning("An admin has temporarily disabled non-admin ghost roles!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(uses <= 0 && !infinite_use) //just in case
to_chat(user, span_warning("This spawner is out of charges!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(is_banned_from(user.key, role_ban))
to_chat(user, span_warning("You are banned from this role!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(!allow_spawn(user, silent = FALSE))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(QDELETED(src) || QDELETED(user))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(uses <= 0) // Just in case something took longer than it should've and we got here after the uses went below zero.
to_chat(user, span_warning("This spawner is out of charges!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
create_from_ghost(user)
@@ -192,14 +213,24 @@
user.mind = null // dissassociate mind, don't let it follow us to the next life
var/created = create(user)
var/user_ckey = user.ckey // Just in case shenanigans happen, we always want to remove it from the list.
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey) // We do this AFTER the create() so that we're basically sure that the user won't be in their ghost body anymore, so they can't click on the spawner again.
if(!created)
uses += 1 // Refund use because we didn't actually spawn anything
if(isnull(created)) // If we explicitly return FALSE instead of just not returning a mob, we don't want to spam the admins
CRASH("An instance of [type] didn't return anything when creating a mob, this might be broken!")
uses += 1 // Refund use because we didn't actually spawn anything
check_uses() // Now we check if the spawner should delete itself or not
/obj/effect/mob_spawn/ghost_role/create(mob/mob_possessor, newname)
if(!mob_possessor.key) // This is in the scenario that the server is somehow lagging, or someone fucked up their code, and we try to spawn the same person in twice. We'll simply not spawn anything and CRASH(), so that we report what happened.
CRASH("Attempted to create an instance of [type] with a mob that had no ckey attached to it, which isn't supported by ghost role spawners!")
return ..()
/obj/effect/mob_spawn/ghost_role/special(mob/living/spawned_mob, mob/mob_possessor)
. = ..()
if(mob_possessor)