Merge pull request #9453 from Ghommie/Ghommie-cit226

Mitigates the ghost roles/mid-round antag lock out for suicide/cryo. And related fixes.
This commit is contained in:
kevinz000
2019-11-14 15:47:18 -07:00
committed by GitHub
20 changed files with 124 additions and 52 deletions

View File

@@ -446,7 +446,7 @@
var/list/candidates = list()
for(var/mob/dead/observer/G in GLOB.player_list)
if(G.can_reenter_round)
if(G.reenter_round_timeout < world.realtime)
candidates += G
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)

View File

@@ -139,6 +139,14 @@
min_val = 0
max_val = 1
/datum/config_entry/number/suicide_reenter_round_timer
config_entry_value = 30
min_val = 0
/datum/config_entry/number/roundstart_suicide_time_limit
config_entry_value = 30
min_val = 0
/datum/config_entry/number/shuttle_refuel_delay
config_entry_value = 12000
min_val = 0

View File

@@ -69,6 +69,10 @@ SUBSYSTEM_DEF(pai)
candidate.comments = copytext(sanitize(candidate.comments),1,MAX_MESSAGE_LEN)
if("submit")
if(isobserver(usr))
var/mob/dead/observer/O = usr
if(!O.can_reenter_round())
return FALSE
if(candidate)
candidate.ready = 1
for(var/obj/item/paicard/p in pai_card_list)
@@ -148,6 +152,8 @@ SUBSYSTEM_DEF(pai)
continue
if(!(ROLE_PAI in G.client.prefs.be_special))
continue
if(!G.can_reenter_round()) // this should use notify_ghosts() instead one day.
return FALSE
to_chat(G, "<span class='ghostalert'>[user] is requesting a pAI personality! Use the pAI button to submit yourself as one.</span>")
addtimer(CALLBACK(src, .proc/spam_again), spam_delay)
var/list/available = list()

View File

@@ -408,7 +408,7 @@
// Ghost and delete the mob.
if(!mob_occupant.get_ghost(1))
mob_occupant.ghostize(0) // Players who cryo out may not re-enter the round
mob_occupant.ghostize(FALSE, penalize = TRUE)
QDEL_NULL(occupant)
open_machine()

View File

@@ -201,7 +201,7 @@
S.directive = directive
if(player_spiders)
S.playable_spider = TRUE
notify_ghosts("Spider [S.name] can be controlled", null, enter_link="<a href=?src=[REF(S)];activate=1>(Click to play)</a>", source=S, action=NOTIFY_ATTACK, ignore_key = POLL_IGNORE_SPIDER)
notify_ghosts("Spider [S.name] can be controlled", null, enter_link="<a href=?src=[REF(S)];activate=1>(Click to play)</a>", source=S, action=NOTIFY_ATTACK, ignore_key = POLL_IGNORE_SPIDER, ignore_dnr_observers = TRUE)
qdel(src)

View File

@@ -66,7 +66,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER)
notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER, ignore_dnr_observers = TRUE)
/datum/outfit/ashwalker
name ="Ashwalker"
@@ -133,7 +133,7 @@
. = ..()
var/area/A = get_area(src)
if(!mapload && A)
notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM)
notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM, ignore_dnr_observers = TRUE)
if(has_owner && creator)
flavour_text = "<span class='big bold'>You are a Golem.</span><b> You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost.</b>"
@@ -372,7 +372,7 @@
flavour_text = "<span class='big bold'>You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil.</span><b> Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell.</b>"
var/area/A = get_area(src)
if(!mapload && A)
notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
objectives = "Be [owner.name]'s friend, and keep [owner.name] alive, so you don't get sent back to hell."
spell = summoning_spell

View File

@@ -15,7 +15,7 @@
. = ..()
var/area/A = get_area(src)
if(A && construct_type)
notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_CONSTRUCT)
notify_ghosts("A [construct_name] chassis has been created in [A.name]!", 'sound/magic/clockwork/fellowship_armory.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_CONSTRUCT, ignore_dnr_observers = TRUE)
GLOB.poi_list += src
LAZYADD(GLOB.mob_spawners[name], src)
@@ -39,7 +39,9 @@
. = ..()
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/item/clockwork/construct_chassis/attack_ghost(mob/user)
/obj/item/clockwork/construct_chassis/attack_ghost(mob/dead/observer/user)
if(!user.can_reenter_round())
return FALSE
if(!SSticker.mode)
to_chat(user, "<span class='danger'>You cannot use that before the game has started.</span>")
return

View File

@@ -848,10 +848,10 @@ structure_check() searches for nearby cultist structures required for the invoca
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src, ignore_dnr_observers = TRUE)
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in T)
if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !QDELETED(src) && !QDELETED(O))
if(!QDELETED(O) && O.client && !jobban_isbanned(O, ROLE_CULTIST) && !QDELETED(src) && O.can_reenter_round())
ghosts_on_rune += O
if(!ghosts_on_rune.len)
to_chat(user, "<span class='cultitalic'>There are no spirits near [src]!</span>")

View File

@@ -33,7 +33,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE)
notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
. = ..()

View File

@@ -44,9 +44,8 @@
return
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round)
to_chat(user, "<span class='warning'>You are unable to reenter the round.</span>")
return
if(!O.can_reenter_round())
return FALSE
var/ghost_role = alert(latejoinercalling ? "Latejoin as [mob_name]? (This is a ghost role, and as such, it's very likely to be off-station.)" : "Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No")
if(ghost_role == "No" || !loc)
return

View File

@@ -49,7 +49,7 @@
if(!(damagetype & (BRUTELOSS | FIRELOSS | TOXLOSS | OXYLOSS) ))
adjustOxyLoss(max(200 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
death(FALSE)
death(FALSE, penalize = TRUE)
return

View File

@@ -75,7 +75,7 @@
spawner.create(M.ckey)
candidates -= M
else
notify_ghosts("Space pirates are waking up!", source = spawner, action=NOTIFY_ATTACK, flashwindow = FALSE)
notify_ghosts("Space pirates are waking up!", source = spawner, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") //CITADEL EDIT also metabreak here too

View File

@@ -3,6 +3,8 @@ GLOBAL_LIST_EMPTY(ghost_images_simple) //this is a list of all ghost images as t
GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
#define CANT_REENTER_ROUND -1
/mob/dead/observer
name = "ghost"
desc = "It's a g-g-g-g-ghooooost!" //jinkies!
@@ -18,7 +20,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
invisibility = INVISIBILITY_OBSERVER
hud_type = /datum/hud/ghost
var/can_reenter_corpse
var/can_reenter_round = TRUE
var/reenter_round_timeout = 0 // used to prevent people from coming back through ghost roles/midround antags as they suicide/cryo for a duration set by CONFIG_GET(number/suicide_reenter_round_timer) and CONFIG_GET(number/roundstart_suicide_time_limit)
var/datum/hud/living/carbon/hud = null // hud
var/bootime = 0
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
@@ -260,14 +262,22 @@ Transfer_mind is there to check if mob is being deleted/not going to have a body
Works together with spawning an observer, noted above.
*/
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE)
if(!key || cmptext(copytext(key,1,2),"@") || (!special && SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special) & COMPONENT_BLOCK_GHOSTING))
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE)
if(!key || cmptext(copytext(key,1,2),"@") || (!special && SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse) & COMPONENT_BLOCK_GHOSTING))
return //mob has no key, is an aghost or some component hijacked.
stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
if(penalize) //penalizing them from making a ghost role / midround antag comeback right away.
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed.
penalty += roundstart_quit_limit - world.time
if(penalty)
ghost.reenter_round_timeout = world.realtime + penalty
if(ghost.reenter_round_timeout - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
ghost.reenter_round_timeout = CANT_REENTER_ROUND
transfer_ckey(ghost, FALSE)
return ghost
@@ -283,26 +293,27 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, (stat == DEAD) ? TRUE : FALSE, FALSE) & COMPONENT_BLOCK_GHOSTING)
return
// CITADEL EDIT
if(istype(loc, /obj/machinery/cryopod))
var/obj/machinery/cryopod/C = loc
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost" || QDELETED(C) || QDELETED(src) || loc != C)
return
C.despawn_occupant()
return
// END EDIT
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
if(world.time < roundstart_quit_limit)
penalty += roundstart_quit_limit - world.time
if(penalty + world.realtime - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
penalty = CANT_REENTER_ROUND
if(stat != DEAD)
succumb()
if(stat == DEAD)
ghostize(1)
else
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst alive you won't be able to re-enter this round [penalty ? "or play ghost roles [penalty != CANT_REENTER_ROUND ? "until the round is over" : "for the next [DisplayTimeText(penalty)]"]" : ""]! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return //didn't want to ghost after-all
ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
suicide_log(TRUE)
if(istype(loc, /obj/machinery/cryopod))
var/obj/machinery/cryopod/C = loc
C.despawn_occupant()
else
ghostize(0, penalize = TRUE) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
suicide_log(TRUE)
/mob/camera/verb/ghost()
set category = "OOC"
@@ -312,10 +323,24 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, FALSE, FALSE) & COMPONENT_BLOCK_GHOSTING)
return
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
if(world.time < roundstart_quit_limit)
penalty += roundstart_quit_limit - world.time
if(penalty + world.realtime - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
penalty = CANT_REENTER_ROUND
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst alive you won't be able to re-enter this round [penalty ? "or play ghost roles [penalty != CANT_REENTER_ROUND ? "until the round is over" : "for the next [DisplayTimeText(penalty)]"]" : ""]! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return
ghostize(0)
ghostize(0, penalize = TRUE)
/mob/dead/observer/proc/can_reenter_round(silent = FALSE)
if(reenter_round_timeout != CANT_REENTER_ROUND && reenter_round_timeout <= world.realtime)
return TRUE
if(!silent)
to_chat(src, "<span class='warning'>You are unable to reenter the round[reenter_round_timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(reenter_round_timeout - world.realtime)]" : ""].</span>")
return FALSE
/mob/dead/observer/Move(NewLoc, direct)
if(updatedir)
@@ -603,6 +628,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "Possess!"
set desc= "Take over the body of a mindless creature!"
if(reenter_round_timeout > world.realtime)
to_chat(src, "<span class='warning'>You are unable to re-enter the round yet. Your ghost role blacklist will expire in [DisplayTimeText(reenter_round_timeout - world.realtime)].</span>")
return FALSE
var/list/possessible = list()
for(var/mob/living/L in GLOB.alive_mob_list)
if(istype(L,/mob/living/carbon/human/dummy) || !get_turf(L)) //Haha no.
@@ -623,10 +652,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, "<span class='warning'>This isn't really a creature, now is it!</span>")
return 0
if(!can_reenter_round)
to_chat(src, "<span class='warning'>You are unable to re-enter the round.</span>")
return FALSE
if(can_reenter_corpse && mind && mind.current)
if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go forward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No")
return 0
@@ -871,3 +896,5 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
spawners_menu = new(src)
spawners_menu.ui_interact(src)
#undef CANT_REENTER_ROUND

View File

@@ -36,7 +36,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
/obj/item/mmi/posibrain/proc/ping_ghosts(msg, newlymade)
if(newlymade || GLOB.posibrain_notify_cooldown <= world.time)
notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/misc/server-ready.ogg':null, enter_link = "<a href=?src=[REF(src)];activate=1>(Click to enter)</a>", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN)
notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/misc/server-ready.ogg':null, enter_link = "<a href=?src=[REF(src)];activate=1>(Click to enter)</a>", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN, ignore_dnr_observers = TRUE)
if(!newlymade)
GLOB.posibrain_notify_cooldown = world.time + askDelay
@@ -83,11 +83,14 @@ GLOBAL_VAR(posibrain_notify_cooldown)
//Two ways to activate a positronic brain. A clickable link in the ghost notif, or simply clicking the object itself.
/obj/item/mmi/posibrain/proc/activate(mob/user)
if(QDELETED(brainmob))
return
if(is_occupied() || jobban_isbanned(user,"posibrain") || QDELETED(brainmob) || QDELETED(src) || QDELETED(user))
if(QDELETED(brainmob) || is_occupied() || jobban_isbanned(user,"posibrain") || QDELETED(src) || QDELETED(user))
return
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round())
return FALSE
var/posi_ask = alert("Become a [name]? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No")
if(posi_ask == "No" || QDELETED(src))
return

View File

@@ -21,7 +21,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("A drone shell has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_DRONE)
notify_ghosts("A drone shell has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_DRONE, ignore_dnr_observers = TRUE)
GLOB.poi_list |= src
if(isnull(possible_seasonal_hats))
build_seasonal_hats()
@@ -40,7 +40,7 @@
. = ..()
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/item/drone_shell/attack_ghost(mob/user)
/obj/item/drone_shell/attack_ghost(mob/dead/observer/user)
if(jobban_isbanned(user,"drone") || QDELETED(src) || QDELETED(user))
return
if(CONFIG_GET(flag/use_age_restriction_for_jobs))
@@ -49,6 +49,8 @@
if(user.client.player_age < DRONE_MINIMUM_AGE)
to_chat(user, "<span class='danger'>You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days.</span>")
return
if(!user.can_reenter_round())
return FALSE
if(!SSticker.mode)
to_chat(user, "Can't become a drone before the game has started.")
return

View File

@@ -33,7 +33,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("A banana spider has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
notify_ghosts("A banana spider has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
/mob/living/simple_animal/banana_spider/attack_ghost(mob/user)
if(key) //please stop using src. without a good reason.
@@ -41,12 +41,19 @@
if(CONFIG_GET(flag/use_age_restriction_for_jobs))
if(!isnum(user.client.player_age))
return
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round())
return
if(!SSticker.mode)
to_chat(user, "Can't become a banana spider before the game has started.")
return
var/be_spider = alert("Become a banana spider? (Warning, You can no longer be cloned!)",,"Yes","No")
if(be_spider == "No" || QDELETED(src) || !isobserver(user))
return
if(key)
to_chat(user, "<span class='notice'>Someone else already took this banana spider.</span>")
return
sentience_act()
user.transfer_ckey(src, FALSE)
density = TRUE

View File

@@ -83,15 +83,19 @@
/mob/living/simple_animal/hostile/poison/giant_spider/proc/humanize_spider(mob/user)
if(key || !playable_spider || stat)//Someone is in it, it's dead, or the fun police are shutting it down
return 0
return FALSE
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round())
return FALSE
var/spider_ask = alert("Become a spider?", "Are you australian?", "Yes", "No")
if(spider_ask == "No" || !src || QDELETED(src))
return 1
return TRUE
if(key)
to_chat(user, "<span class='notice'>Someone else already took this spider.</span>")
return 1
return TRUE
user.transfer_ckey(src, FALSE)
return 1
return TRUE
//nursemaids - these create webs and eggs
/mob/living/simple_animal/hostile/poison/giant_spider/nurse

View File

@@ -575,13 +575,15 @@ Difficulty: Very Hard
if(..() && !ready_to_deploy)
GLOB.poi_list |= src
ready_to_deploy = TRUE
notify_ghosts("An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.", enter_link = "<a href=?src=[REF(src)];ghostjoin=1>(Click to enter)</a>", ghost_sound = 'sound/effects/ghost2.ogg', source = src, action = NOTIFY_ATTACK)
notify_ghosts("An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.", enter_link = "<a href=?src=[REF(src)];ghostjoin=1>(Click to enter)</a>", ghost_sound = 'sound/effects/ghost2.ogg', source = src, action = NOTIFY_ATTACK, ignore_dnr_observers = TRUE)
/obj/machinery/anomalous_crystal/helpers/attack_ghost(mob/dead/observer/user)
. = ..()
if(.)
return
if(ready_to_deploy)
if(!user.can_reenter_round())
return FALSE
var/be_helper = alert("Become a Lightgeist? (Warning, You can no longer be cloned!)",,"Yes","No")
if(be_helper == "Yes" && !QDELETED(src) && isobserver(user))
var/mob/living/simple_animal/hostile/lightgeist/W = new /mob/living/simple_animal/hostile/lightgeist(get_turf(loc))

View File

@@ -352,12 +352,12 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
/mob/proc/reagent_check(datum/reagent/R) // utilized in the species code
return 1
/proc/notify_ghosts(var/message, var/ghost_sound = null, var/enter_link = null, var/atom/source = null, var/mutable_appearance/alert_overlay = null, var/action = NOTIFY_JUMP, flashwindow = TRUE, ignore_mapload = TRUE, ignore_key) //Easy notification of ghosts.
/proc/notify_ghosts(message, ghost_sound, enter_link, atom/source, mutable_appearance/alert_overlay, action = NOTIFY_JUMP, flashwindow = TRUE, ignore_mapload = TRUE, ignore_key, ignore_dnr_observers = FALSE) //Easy notification of ghosts.
if(ignore_mapload && SSatoms.initialized != INITIALIZATION_INNEW_REGULAR) //don't notify for objects created during a map load
return
for(var/mob/dead/observer/O in GLOB.player_list)
if(O.client)
if (ignore_key && O.ckey in GLOB.poll_ignore[ignore_key])
if ((ignore_key && (O.ckey in GLOB.poll_ignore[ignore_key])) || (ignore_dnr_observers && !O.can_reenter_round(TRUE)))
continue
to_chat(O, "<span class='ghostalert'>[message][(enter_link) ? " [enter_link]" : ""]</span>")
if(ghost_sound)

View File

@@ -623,6 +623,18 @@ MIDROUND_ANTAG_TIME_CHECK 60
## A ratio of living to total crew members, the lower this is, the more people will have to die in order for midround antag to be skipped
MIDROUND_ANTAG_LIFE_CHECK 0.7
## A time, in real-time deciseconds, applied upon suicide, cryosleep or ghosting whilst alive
## during which the player shouldn't be able to come back through
## midround playable roles or mob spawners.
## Set to 0 to completely disable it.
SUICIDE_REENTER_ROUND_TIMER 18000
## A time, in real-time deciseconds, below which the player receives
## a timed penalty, for purposes similar to the aforementioned one (can also stack)
## and equal to this config difference with world.time.
## Both configs are indipendent from each other, disabling one won't affect the other.
ROUNDSTART_SUICIDE_TIME_LIMIT 18000
##Limit Spell Choices##
## Uncomment to disallow wizards from using certain spells that may be too chaotic/fun for your playerbase