Grand Ritual Finale: An end to death (#78497)

## About The Pull Request

Adds a new Wizard Ritual Finale effect which makes everything immortal.
By this I mean, 10 seconds after death a ghostly image of them will
appear somewhere near where the corpse was and then 50 seconds after
that the mob will return to life at that location.
This applies to every mob, everywhere.
This is likely to cause a little bit of disruption to the rest of the
round, so you can only do it after at least 30 minutes have passed.
After that the crew will have to figure out how to deal with their new
gift of immortality. It will involve throwing people into chasms and
lava, probably.


![dreamseeker_7SwMsSrnt1](https://github.com/tgstation/tgstation/assets/7483112/5b794bcc-fcba-46b5-b228-e95aa685942d)
Here's a gif sped up for example purposes.

You can escape from the cycle of death and rebirth via suicide, purely
because it's pointless to try and force people to play the video game if
they don't want to.

Also I split all of these effects into their own files, the only new
code for those is in `immortality.dm`

shout out to Vekter for distracting Oranges while I posted this
wizard-related PR so I didn't get disapprovingly reacted for posting
magic shit (yet)

## Why It's Good For The Game

This might be _too_ much but I want to see what would happen.
It will allow us to simulate whether polite society can survive when
violence has no consequences.

## Changelog

🆑
add: Wizards who complete the grand ritual can now gift everyone with
eternal life
/🆑
This commit is contained in:
Jacquerel
2023-09-30 23:34:02 -04:00
committed by GitHub
parent 43d2ebc29a
commit 15efbf64a5
25 changed files with 778 additions and 485 deletions
+5
View File
@@ -892,6 +892,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// For clarity, this trait should always be associated/tied to a reference to the mob that suicided- not anything else.
#define TRAIT_SUICIDED "committed_suicide"
/// Trait given to a living mob to prevent wizards from making it immortal
#define TRAIT_PERMANENTLY_MORTAL "permanently_mortal"
///Trait given to a mob with a ckey currently in a temporary body, allowing people to know someone will re-enter the round later.
#define TRAIT_MIND_TEMPORARILY_GONE "temporarily_gone"
@@ -993,6 +996,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define MOTOR_LACK_TRAIT "motor-lack"
/// Trait associated with mafia
#define MAFIA_TRAIT "mafia"
/// Trait associated with ctf
#define CTF_TRAIT "ctf"
/// Trait associated with highlander
#define HIGHLANDER_TRAIT "highlander"
/// Trait given from playing pretend with baguettes
+1 -1
View File
@@ -117,7 +117,7 @@
if(!hud.mymob)
continue
HideFrom(hud.mymob)
LAZYREMOVE(remove_from.actions, src) // We aren't always properly inserted into the viewers list, gotta make sure that action's cleared
LAZYREMOVE(remove_from?.actions, src) // We aren't always properly inserted into the viewers list, gotta make sure that action's cleared
viewers = list()
if(owner)
+17 -16
View File
@@ -8,28 +8,29 @@
/// List of refs to falling objects -> how many levels deep we've fallen
var/static/list/falling_atoms = list()
var/static/list/forbidden_types = typecacheof(list(
/obj/singularity,
/obj/energy_ball,
/obj/narsie,
/obj/docking_port,
/obj/structure/lattice,
/obj/structure/stone_tile,
/obj/projectile,
/obj/effect/projectile,
/obj/effect/portal,
/obj/effect/abstract,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/temp_visual,
/obj/effect/light_emitter/tendril,
/obj/effect/collapse,
/obj/effect/particle_effect/ion_trails,
/obj/effect/constructing_effect,
/obj/effect/dummy/phased_mob,
/obj/effect/mapping_helpers,
/obj/effect/wisp,
/obj/effect/ebeam,
/obj/effect/fishing_lure,
/obj/effect/constructing_effect,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/light_emitter/tendril,
/obj/effect/mapping_helpers,
/obj/effect/particle_effect/ion_trails,
/obj/effect/portal,
/obj/effect/projectile,
/obj/effect/spectre_of_resurrection,
/obj/effect/temp_visual,
/obj/effect/wisp,
/obj/energy_ball,
/obj/narsie,
/obj/projectile,
/obj/singularity,
/obj/structure/lattice,
/obj/structure/stone_tile,
))
/datum/component/chasm/Initialize(turf/target, mapload)
+1 -1
View File
@@ -886,7 +886,7 @@ structure_check() searches for nearby cultist structures required for the invoca
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
new_human.apply_status_effect(/datum/status_effect/cultghost) //ghosts can't summon more ghosts
new_human.set_invis_see(SEE_INVISIBLE_OBSERVER)
ADD_TRAIT(new_human, TRAIT_NOBREATH, INNATE_TRAIT)
new_human.add_traits(list(TRAIT_NOBREATH, TRAIT_PERMANENTLY_MORTAL), INNATE_TRAIT) // permanently mortal can be removed once this is a bespoke kind of mob
ghosts++
playsound(src, 'sound/magic/exit_blood.ogg', 50, TRUE)
visible_message(span_warning("A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo":""]man."))
@@ -0,0 +1,17 @@
/// Open all of the doors
/datum/grand_finale/all_access
name = "Connection"
desc = "The ultimate use of your gathered power! Unlock every single door that they have! Nobody will be able to keep you out now, or anyone else for that matter!"
icon = 'icons/mob/actions/actions_spells.dmi'
icon_state = "knock"
/datum/grand_finale/all_access/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] removed all door access requirements")
for(var/obj/machinery/door/target_door as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door))
if(is_station_level(target_door.z))
target_door.unlock()
target_door.req_access = list()
target_door.req_one_access = list()
INVOKE_ASYNC(target_door, TYPE_PROC_REF(/obj/machinery/door/airlock, open))
CHECK_TICK
priority_announce("AULIE OXIN FIERA!!", null, 'sound/magic/knock.ogg', sender_override = "[invoker.real_name]")
@@ -0,0 +1,60 @@
#define DOOM_SINGULARITY "singularity"
#define DOOM_TESLA "tesla"
#define DOOM_METEORS "meteors"
/// Kill yourself and probably a bunch of other people
/datum/grand_finale/armageddon
name = "Annihilation"
desc = "This crew have offended you beyond the realm of pranks. Make the ultimate sacrifice to teach them a lesson your elders can really respect. \
YOU WILL NOT SURVIVE THIS."
icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
icon_state = "legion_head"
minimum_time = 90 MINUTES // This will probably immediately end the round if it gets finished.
ritual_invoke_time = 60 SECONDS // Really give the crew some time to interfere with this one.
dire_warning = TRUE
glow_colour = "#be000048"
/// Things to yell before you die
var/static/list/possible_last_words = list(
"Flames and ruin!",
"Dooooooooom!!",
"HAHAHAHAHAHA!! AHAHAHAHAHAHAHAHAA!!",
"Hee hee hee!! Hoo hoo hoo!! Ha ha haaa!!",
"Ohohohohohoho!!",
"Cower in fear, puny mortals!",
"Tremble before my glory!",
"Pick a god and pray!",
"It's no use!",
"If the gods wanted you to live, they would not have created me!",
"God stays in heaven out of fear of what I have created!",
"Ruination is come!",
"All of creation, bend to my will!",
)
/datum/grand_finale/armageddon/trigger(mob/living/carbon/human/invoker)
priority_announce(pick(possible_last_words), null, 'sound/magic/voidblink.ogg', sender_override = "[invoker.real_name]")
var/turf/current_location = get_turf(invoker)
invoker.gib()
var/static/list/doom_options = list()
if (!length(doom_options))
doom_options = list(DOOM_SINGULARITY, DOOM_TESLA)
if (!SSmapping.config.planetary)
doom_options += DOOM_METEORS
switch(pick(doom_options))
if (DOOM_SINGULARITY)
var/obj/singularity/singulo = new(current_location)
singulo.energy = 300
if (DOOM_TESLA)
var/obj/energy_ball/tesla = new (current_location)
tesla.energy = 200
if (DOOM_METEORS)
var/datum/dynamic_ruleset/roundstart/meteor/meteors = new()
meteors.meteordelay = 0
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", ANNOUNCER_METEORS)
#undef DOOM_SINGULARITY
#undef DOOM_TESLA
#undef DOOM_METEORS
@@ -0,0 +1,113 @@
/// Become the official Captain of the station
/datum/grand_finale/usurp
name = "Usurpation"
desc = "The ultimate use of your gathered power! Rewrite time such that you have been Captain of this station the whole time."
icon = 'icons/obj/card.dmi'
icon_state = "card_gold"
/datum/grand_finale/usurp/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] has replaced the Captain")
var/list/former_captains = list()
var/list/other_crew = list()
SEND_SOUND(world, sound('sound/magic/timeparadox2.ogg'))
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (!crewmate.mind)
continue
crewmate.Unconscious(3 SECONDS) // Everyone falls unconscious but not everyone gets told about a new captain
if (crewmate == invoker || IS_HUMAN_INVADER(crewmate))
continue
to_chat(crewmate, span_notice("The world spins and dissolves. Your past flashes before your eyes, backwards.\n\
Life strolls back into the ocean and shrinks into nothingness, planets explode into storms of solar dust, \
the stars rush back to greet each other at the beginning of things and then... you snap back to the present. \n\
Everything is just as it was and always has been. \n\n\
A stray thought sticks in the forefront of your mind. \n\
[span_hypnophrase("I'm so glad that [invoker.real_name] is our legally appointed Captain!")] \n\
Is... that right?"))
if (is_captain_job(crewmate.mind.assigned_role))
former_captains += crewmate
demote_to_assistant(crewmate)
continue
if (crewmate.stat != DEAD)
other_crew += crewmate
dress_candidate(invoker)
GLOB.manifest.modify(invoker.real_name, JOB_CAPTAIN, JOB_CAPTAIN)
minor_announce("Captain [invoker.real_name] on deck!")
// Enlist some crew to try and restore the natural order
for (var/mob/living/carbon/human/former_captain as anything in former_captains)
create_vendetta(former_captain.mind, invoker.mind)
for (var/mob/living/carbon/human/random_crewmate as anything in other_crew)
if (prob(10))
create_vendetta(random_crewmate.mind, invoker.mind)
/**
* Anyone who thought they were Captain is in for a nasty surprise, and won't be very happy about it
*/
/datum/grand_finale/usurp/proc/demote_to_assistant(mob/living/carbon/human/former_captain)
var/obj/effect/particle_effect/fluid/smoke/exit_poof = new(get_turf(former_captain))
exit_poof.lifetime = 2 SECONDS
former_captain.unequip_everything()
if(isplasmaman(former_captain))
former_captain.equipOutfit(/datum/outfit/plasmaman)
former_captain.internal = former_captain.get_item_for_held_index(2)
else
former_captain.equipOutfit(/datum/outfit/job/assistant)
GLOB.manifest.modify(former_captain.real_name, JOB_ASSISTANT, JOB_ASSISTANT)
var/list/valid_turfs = list()
// Used to be into prison but that felt a bit too mean
for (var/turf/exile_turf as anything in get_area_turfs(/area/station/maintenance, subtypes = TRUE))
if (isspaceturf(exile_turf) || exile_turf.is_blocked_turf())
continue
valid_turfs += exile_turf
do_teleport(former_captain, pick(valid_turfs), no_effects = TRUE)
var/obj/effect/particle_effect/fluid/smoke/enter_poof = new(get_turf(former_captain))
enter_poof.lifetime = 2 SECONDS
/**
* Does some item juggling to try to dress you as both a Wizard and Captain without deleting any items you have bought.
* ID, headset, and uniform are forcibly replaced. Other slots are only filled if unoccupied.
* We could forcibly replace shoes and gloves too but people might miss their insuls or... meown shoes?
*/
/datum/grand_finale/usurp/proc/dress_candidate(mob/living/carbon/human/invoker)
// Won't be needing these
var/obj/id = invoker.get_item_by_slot(ITEM_SLOT_ID)
QDEL_NULL(id)
var/obj/headset = invoker.get_item_by_slot(ITEM_SLOT_EARS)
QDEL_NULL(headset)
// We're about to take off your pants so those are going to fall out
var/obj/item/pocket_L = invoker.get_item_by_slot(ITEM_SLOT_LPOCKET)
var/obj/item/pocket_R = invoker.get_item_by_slot(ITEM_SLOT_RPOCKET)
// In case we try to put a PDA there
var/obj/item/belt = invoker.get_item_by_slot(ITEM_SLOT_BELT)
belt?.moveToNullspace()
var/obj/pants = invoker.get_item_by_slot(ITEM_SLOT_ICLOTHING)
QDEL_NULL(pants)
invoker.equipOutfit(/datum/outfit/job/wizard_captain)
// And put everything back!
equip_to_slot_then_hands(invoker, ITEM_SLOT_BELT, belt)
equip_to_slot_then_hands(invoker, ITEM_SLOT_LPOCKET, pocket_L)
equip_to_slot_then_hands(invoker, ITEM_SLOT_RPOCKET, pocket_R)
/// An outfit which replaces parts of a wizard's clothes with captain's clothes but keeps the robes
/datum/outfit/job/wizard_captain
name = "Captain (Wizard Transformation)"
jobtype = /datum/job/captain
id = /obj/item/card/id/advanced/gold
id_trim = /datum/id_trim/job/captain
uniform = /obj/item/clothing/under/rank/captain/parade
belt = /obj/item/modular_computer/pda/heads/captain
ears = /obj/item/radio/headset/heads/captain/alt
glasses = /obj/item/clothing/glasses/sunglasses
gloves = /obj/item/clothing/gloves/captain
shoes = /obj/item/clothing/shoes/laceup
accessory = /obj/item/clothing/accessory/medal/gold/captain
backpack_contents = list(
/obj/item/melee/baton/telescopic = 1,
/obj/item/station_charter = 1,
)
box = null
@@ -0,0 +1,49 @@
/**
* Gives the wizard a defensive/mood buff and a Wabbajack, a juiced up chaos staff that will surely break something.
* Everyone but the wizard goes crazy, suffers major brain damage, and is given a vendetta against the wizard.
* Already insane people are instead cured of their madness, ignoring any other effects as the station around them loses its marbles.
*/
/datum/grand_finale/cheese
// we don't set name, desc and others, thus we won't appear in the radial choice of a normal finale rune
dire_warning = TRUE
minimum_time = 45 MINUTES //i'd imagine speedrunning this would be crummy, but the wizard's average lifespan is barely reaching this point
/datum/grand_finale/cheese/trigger(mob/living/invoker)
message_admins("[key_name(invoker)] has summoned forth The Wabbajack and cursed the crew with madness!")
priority_announce("Danger: Extremely potent reality altering object has been summoned on station. Immediate evacuation advised. Brace for impact.", "Central Command Higher Dimensional Affairs", 'sound/effects/glassbr1.ogg')
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (isnull(crewmate.mind))
continue
if (crewmate == invoker) //everyone but the wizard is royally fucked, no matter who they are
continue
if (crewmate.has_trauma_type(/datum/brain_trauma/mild/hallucinations)) //for an already insane person, this is retribution
to_chat(crewmate, span_boldwarning("Your surroundings suddenly fill with a cacophony of manic laughter and psychobabble..."))
to_chat(crewmate, span_nicegreen("...but as the moment passes, you realise that whatever eldritch power behind the event happened to affect you \
has resonated within the ruins of your already shattered mind, creating a singularity of mental instability! \
As it collapses unto itself, you feel... at peace, finally."))
if(crewmate.has_quirk(/datum/quirk/insanity))
crewmate.remove_quirk(/datum/quirk/insanity)
else
crewmate.cure_trauma_type(/datum/brain_trauma/mild/hallucinations, TRAUMA_RESILIENCE_ABSOLUTE)
else
//everyone else gets to relish in madness
//yes killing their mood will also trigger mood hallucinations
create_vendetta(crewmate.mind, invoker.mind)
to_chat(crewmate, span_boldwarning("Your surroundings suddenly fill with a cacophony of manic laughter and psychobabble. \n\
You feel your inner psyche shatter into a myriad pieces of jagged glass of colors unknown to the universe, \
infinitely reflecting a blinding, maddening light coming from the innermost sanctums of your destroyed mind. \n\
After a brief pause which felt like a millenia, one phrase rebounds ceaselessly in your head, imbued with the false hope of absolution... \n\
<b>[invoker] must die.</b>"))
var/datum/brain_trauma/mild/hallucinations/added_trauma = new()
added_trauma.resilience = TRAUMA_RESILIENCE_ABSOLUTE
crewmate.adjustOrganLoss(ORGAN_SLOT_BRAIN, BRAIN_DAMAGE_DEATH - 25, BRAIN_DAMAGE_DEATH - 25) //you'd better hope chap didn't pick a hypertool
crewmate.gain_trauma(added_trauma)
crewmate.add_mood_event("wizard_ritual_finale", /datum/mood_event/madness_despair)
//drip our wizard out
invoker.apply_status_effect(/datum/status_effect/blessing_of_insanity)
invoker.add_mood_event("wizard_ritual_finale", /datum/mood_event/madness_elation)
var/obj/item/gun/magic/staff/chaos/true_wabbajack/the_wabbajack = new
invoker.put_in_active_hand(the_wabbajack)
to_chat(invoker, span_mind_control("Your every single instinct and rational thought is screaming at you as [the_wabbajack] appears in your firm grip..."))
@@ -0,0 +1,76 @@
/// Dress the crew as magical clowns
/datum/grand_finale/clown
name = "Jubilation"
desc = "The ultimate use of your gathered power! Rewrite time so that everyone went to clown college! Now they'll prank each other for you!"
icon = 'icons/obj/clothing/masks.dmi'
icon_state = "clown"
glow_colour = "#ffff0048"
/datum/grand_finale/clown/trigger(mob/living/carbon/human/invoker)
for(var/mob/living/carbon/human/victim as anything in GLOB.human_list)
victim.Unconscious(3 SECONDS)
if (!victim.mind || IS_HUMAN_INVADER(victim) || victim == invoker)
continue
if (HAS_TRAIT(victim, TRAIT_CLOWN_ENJOYER))
victim.add_mood_event("clown_world", /datum/mood_event/clown_world)
to_chat(victim, span_notice("The world spins and dissolves. Your past flashes before your eyes, backwards.\n\
Life strolls back into the ocean and shrinks into nothingness, planets explode into storms of solar dust, \
the stars rush back to greet each other at the beginning of things and then... you snap back to the present. \n\
Everything is just as it was and always has been. \n\n\
A stray thought sticks in the forefront of your mind. \n\
[span_hypnophrase("I'm so glad that I work at Clown Research Station [station_name()]!")] \n\
Is... that right?"))
if (is_clown_job(victim.mind.assigned_role))
var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim)
new_spell.Grant(victim)
continue
if (!ismonkey(victim)) // Monkeys cannot yet wear clothes
dress_as_magic_clown(victim)
if (prob(15))
create_vendetta(victim.mind, invoker.mind)
/**
* Clown enjoyers who are effected by this become ecstatic, they have achieved their life's dream.
* This moodlet is equivalent to the one for simply being a traitor.
*/
/datum/mood_event/clown_world
mood_change = 4
/datum/mood_event/clown_world/add_effects(param)
description = "I LOVE working at Clown Research Station [station_name()]!!"
/// Dress the passed mob as a magical clown, self-explanatory
/datum/grand_finale/clown/proc/dress_as_magic_clown(mob/living/carbon/human/victim)
var/obj/effect/particle_effect/fluid/smoke/poof = new(get_turf(victim))
poof.lifetime = 2 SECONDS
var/obj/item/tank/internal = victim.internal
// We're about to take off your pants so those are going to fall out
var/obj/item/pocket_L = victim.get_item_by_slot(ITEM_SLOT_LPOCKET)
var/obj/item/pocket_R = victim.get_item_by_slot(ITEM_SLOT_RPOCKET)
var/obj/item/id = victim.get_item_by_slot(ITEM_SLOT_ID)
var/obj/item/belt = victim.get_item_by_slot(ITEM_SLOT_BELT)
var/obj/pants = victim.get_item_by_slot(ITEM_SLOT_ICLOTHING)
var/obj/mask = victim.get_item_by_slot(ITEM_SLOT_MASK)
QDEL_NULL(pants)
QDEL_NULL(mask)
if(isplasmaman(victim))
victim.equip_to_slot_if_possible(new /obj/item/clothing/under/plasmaman/clown/magic(), ITEM_SLOT_ICLOTHING, disable_warning = TRUE)
victim.equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat/plasmaman(), ITEM_SLOT_MASK, disable_warning = TRUE)
else
victim.equip_to_slot_if_possible(new /obj/item/clothing/under/rank/civilian/clown/magic(), ITEM_SLOT_ICLOTHING, disable_warning = TRUE)
victim.equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat(), ITEM_SLOT_MASK, disable_warning = TRUE)
var/obj/item/clothing/mask/gas/clown_hat/clown_mask = victim.get_item_by_slot(ITEM_SLOT_MASK)
if (clown_mask)
var/list/options = GLOB.clown_mask_options
clown_mask.icon_state = options[pick(clown_mask.clownmask_designs)]
victim.update_worn_mask()
clown_mask.update_item_action_buttons()
equip_to_slot_then_hands(victim, ITEM_SLOT_LPOCKET, pocket_L)
equip_to_slot_then_hands(victim, ITEM_SLOT_RPOCKET, pocket_R)
equip_to_slot_then_hands(victim, ITEM_SLOT_ID, id)
equip_to_slot_then_hands(victim, ITEM_SLOT_BELT, belt)
victim.internal = internal
@@ -0,0 +1,88 @@
/**
* A big final event to run when you complete seven rituals
*/
/datum/grand_finale
/// Friendly name for selection menu
var/name
/// Tooltip description for selection menu
var/desc
/// An icon to display to represent the choice
var/icon/icon
/// Icon state to use to represent the choice
var/icon_state
/// Prevent especially dangerous options from being chosen until we're fine with the round ending
var/minimum_time = 0
/// Override the rune invocation time
var/ritual_invoke_time = 30 SECONDS
/// Provide an extremely loud radio message when this one starts
var/dire_warning = FALSE
/// Overrides the default colour you glow while channeling the rune, optional
var/glow_colour
/**
* Returns an entry for a radial menu for this choice.
* Returns null if entry is abstract or invalid for current circumstances.
*/
/datum/grand_finale/proc/get_radial_choice()
if (!name || !desc || !icon || !icon_state)
return
var/time_remaining_desc = ""
if (minimum_time >= world.time - SSticker.round_start_time)
time_remaining_desc = "<i>This ritual will be available to begin invoking in [DisplayTimeText(minimum_time - world.time - SSticker.round_start_time)]</i>"
var/datum/radial_menu_choice/choice = new()
choice.name = name
choice.image = image(icon = icon, icon_state = icon_state)
choice.info = desc + time_remaining_desc
return choice
/**
* Actually do the thing.
* Arguments
* * invoker - The wizard casting this.
*/
/datum/grand_finale/proc/trigger(mob/living/invoker)
// Do something cool.
/// Tries to equip something into an inventory slot, then hands, then the floor.
/datum/grand_finale/proc/equip_to_slot_then_hands(mob/living/carbon/human/invoker, slot, obj/item/item)
if(!item)
return
if(!invoker.equip_to_slot_if_possible(item, slot, disable_warning = TRUE))
invoker.put_in_hands(item)
/// They are not going to take this lying down.
/datum/grand_finale/proc/create_vendetta(datum/mind/aggrieved_crewmate, datum/mind/wizard)
aggrieved_crewmate.add_antag_datum(/datum/antagonist/wizard_prank_vendetta)
var/datum/antagonist/wizard_prank_vendetta/antag_datum = aggrieved_crewmate.has_antag_datum(/datum/antagonist/wizard_prank_vendetta)
var/datum/objective/assassinate/wizard_murder = new
wizard_murder.owner = aggrieved_crewmate
wizard_murder.target = wizard
wizard_murder.explanation_text = "Kill [wizard.current.name], the one who did this."
antag_datum.objectives += wizard_murder
to_chat(aggrieved_crewmate.current, span_warning("No! This isn't right!"))
aggrieved_crewmate.announce_objectives()
/**
* Antag datum to give to people who want to kill the wizard.
* This doesn't preclude other people choosing to want to kill the wizard, just these people are rewarded for it.
*/
/datum/antagonist/wizard_prank_vendetta
name = "\improper Wizard Prank Victim"
roundend_category = "wizard prank victims"
show_in_antagpanel = FALSE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
silent = TRUE
/// Give everyone magic items, its so simple it feels pointless to give it its own file
/datum/grand_finale/magic
name = "Evolution"
desc = "The ultimate use of your gathered power! Give the crew their own magic, they'll surely realise that right and wrong have no meaning when you hold ultimate power!"
icon = 'icons/obj/scrolls.dmi'
icon_state = "scroll"
/datum/grand_finale/magic/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] summoned magic")
summon_magic(survivor_probability = 20) // Wow, this one was easy!
@@ -0,0 +1,277 @@
/// Amount of time to wait after someone dies to steal their body from their killers
#define IMMORTAL_PRE_ACTIVATION_TIME 10 SECONDS
/// Amount of time it takes a mob to return to the living world
#define IMMORTAL_RESURRECT_TIME 50 SECONDS
/**
* Nobody will ever die ever again
* Or if they do, they will be back
*/
/datum/grand_finale/immortality
name = "Perpetuation"
desc = "The ultimate use of your gathered power! Share with the crew the gift, or curse, of eternal life! \
And why not just the crew? How about their pets too? And any other animals around here! \
What if nobody died ever again!?"
icon = 'icons/obj/mining_zones/artefacts.dmi'
icon_state = "asclepius_active"
glow_colour = COLOR_PALE_GREEN
minimum_time = 30 MINUTES // This is enormously disruptive but doesn't technically in of itself end the round.
/datum/grand_finale/immortality/trigger(mob/living/carbon/human/invoker)
new /obj/effect/temp_visual/immortality_blast(get_turf(invoker))
SEND_SOUND(world, sound('sound/magic/teleport_diss.ogg'))
for (var/mob/living/alive_guy as anything in GLOB.mob_living_list)
new /obj/effect/temp_visual/immortality_pulse(get_turf(alive_guy))
if (!alive_guy.mind)
continue
to_chat(alive_guy, span_notice("You feel <b>extremely</b> healthy."))
RegisterSignal(SSdcs, COMSIG_GLOB_MOB_DEATH, PROC_REF(something_died))
/// Called when something passes into the great beyond, make it not do that
/datum/grand_finale/immortality/proc/something_died(datum/source, mob/living/died, gibbed)
SIGNAL_HANDLER
if (died.stat != DEAD || HAS_TRAIT(died, TRAIT_PERMANENTLY_MORTAL) || died.flags_1 & HOLOGRAM_1)
return
var/body_type = died.type
var/turf/died_turf = get_turf(died)
var/list/nearby_turfs = circle_view_turfs(died_turf, 2)
var/list/nearby_safe_turfs = list()
for (var/turf/check_turf as anything in nearby_turfs)
if (check_turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = died))
nearby_turfs -= check_turf
continue
if (islava(check_turf) || ischasm(check_turf))
continue
nearby_safe_turfs += check_turf
if (length(nearby_safe_turfs)) // If you're in the middle of a 5x5 chasm, tough luck I guess
died_turf = pick(nearby_safe_turfs)
else if (length(nearby_turfs))
died_turf = pick(nearby_turfs)
var/saved_appearance = ishuman(died) ? new /datum/human_appearance_profile(died) : null
var/datum/mind/dead_mind = HAS_TRAIT(died, TRAIT_SUICIDED) ? null : died.mind // There is a way out of the cycle
if (!isnull(dead_mind))
to_chat(died, span_boldnotice("Your spirit surges! You will return to life in [DisplayTimeText(IMMORTAL_PRE_ACTIVATION_TIME + IMMORTAL_RESURRECT_TIME)]."))
animate(died, alpha = died.alpha, time = IMMORTAL_PRE_ACTIVATION_TIME / 2, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = IMMORTAL_PRE_ACTIVATION_TIME / 2, easing = SINE_EASING | EASE_IN)
addtimer(CALLBACK(src, PROC_REF(reverse_death), died, dead_mind, died_turf, body_type, saved_appearance), IMMORTAL_PRE_ACTIVATION_TIME, TIMER_DELETE_ME)
/// Create a ghost ready for revival
/datum/grand_finale/immortality/proc/reverse_death(mob/living/died, datum/mind/dead_mind, turf/died_turf, body_type, datum/human_appearance_profile/human_appearance)
if (died.stat != DEAD)
return
var/ghost_type = ispath(body_type, /mob/living/carbon/human) ? /obj/effect/spectre_of_resurrection/human : /obj/effect/spectre_of_resurrection
var/obj/effect/spectre_of_resurrection/ghost = new ghost_type(died_turf)
var/mob/living/corpse = QDELETED(died) ? new body_type(ghost) : died
if (!isnull(human_appearance))
corpse.real_name = human_appearance.name
corpse.alpha = initial(corpse.alpha)
corpse.add_traits(list(TRAIT_NO_TELEPORT, TRAIT_AI_PAUSED), MAGIC_TRAIT)
corpse.apply_status_effect(/datum/status_effect/grouped/stasis, MAGIC_TRAIT)
ghost.set_up_resurrection(corpse, dead_mind, human_appearance)
/// Store of data we use to recreate someone who was gibbed, like a simplified version of changeling profiles
/datum/human_appearance_profile
/// The name of the profile / the name of whoever this profile source.
var/name = "human"
/// The DNA datum associated with our profile from the profile source
var/datum/dna/dna
/// The age of the profile source.
var/age
/// The body type of the profile source.
var/physique
/// The quirks of the profile source.
var/list/quirks = list()
/// The hair and facial hair gradient styles of the profile source.
var/list/hair_gradient_style = list("None", "None")
/// The hair and facial hair gradient colours of the profile source.
var/list/hair_gradient_colours = list(null, null)
/// The TTS voice of the profile source
var/voice
/// The TTS filter of the profile filter
var/voice_filter = ""
/datum/human_appearance_profile/New(mob/living/carbon/human/target)
copy_from(target)
/// Copy the appearance data of the target
/datum/human_appearance_profile/proc/copy_from(mob/living/carbon/human/target)
target.dna.real_name = target.real_name
dna = new target.dna.type()
target.dna.copy_dna(dna)
name = target.real_name
age = target.age
physique = target.physique
for(var/datum/quirk/target_quirk as anything in target.quirks)
LAZYADD(quirks, new target_quirk.type)
hair_gradient_style = LAZYLISTDUPLICATE(target.grad_style)
hair_gradient_colours = LAZYLISTDUPLICATE(target.grad_color)
voice = target.voice
voice_filter = target.voice_filter
/// Make the targetted human look like this
/datum/human_appearance_profile/proc/apply_to(mob/living/carbon/human/target)
target.real_name = name
target.age = age
target.physique = physique
target.grad_style = LAZYLISTDUPLICATE(hair_gradient_style)
target.grad_color = LAZYLISTDUPLICATE(hair_gradient_colours)
target.voice = voice
target.voice_filter = voice_filter
for(var/datum/quirk/target_quirk as anything in quirks)
target_quirk.add_to_holder(target)
dna.transfer_identity(target, TRUE)
for(var/obj/item/bodypart/limb as anything in target.bodyparts)
limb.update_limb(is_creating = TRUE)
target.updateappearance(mutcolor_update = TRUE)
target.domutcheck()
target.regenerate_icons()
/// A ghostly image of a mob showing where and what is going to respawn
/obj/effect/spectre_of_resurrection
name = "spectre"
desc = "A frightening apparition, slowly growing more solid."
icon_state = "blank_white"
anchored = TRUE
layer = MOB_LAYER
plane = GAME_PLANE
alpha = 0
color = COLOR_PALE_GREEN
light_range = 2
light_color = COLOR_PALE_GREEN
/// Who are we reviving?
var/mob/living/corpse
/// Who if anyone is playing as them?
var/datum/mind/dead_mind
/obj/effect/spectre_of_resurrection/Initialize(mapload)
. = ..()
animate(src, alpha = 150, time = 2 SECONDS)
/// Prepare to revive someone
/obj/effect/spectre_of_resurrection/proc/set_up_resurrection(mob/living/corpse, datum/mind/dead_mind, datum/human_appearance_profile/human_appearance)
if (isnull(corpse))
qdel(src)
return
src.corpse = corpse
src.dead_mind = dead_mind
corpse.forceMove(src)
name = "spectre of [corpse]"
setup_icon(corpse)
DO_FLOATING_ANIM(src)
RegisterSignal(corpse, COMSIG_LIVING_REVIVE, PROC_REF(on_corpse_revived))
RegisterSignal(corpse, COMSIG_QDELETING, PROC_REF(on_corpse_deleted))
RegisterSignal(dead_mind, COMSIG_QDELETING, PROC_REF(on_mind_lost))
addtimer(CALLBACK(src, PROC_REF(revive)), IMMORTAL_RESURRECT_TIME, TIMER_DELETE_ME)
/// Copy appearance from ressurecting mob
/obj/effect/spectre_of_resurrection/proc/setup_icon(mob/living/corpse)
icon = initial(corpse.icon)
icon_state = initial(corpse.icon_state)
/obj/effect/spectre_of_resurrection/Destroy(force)
QDEL_NULL(corpse)
dead_mind = null
return ..()
/obj/effect/spectre_of_resurrection/Exited(atom/movable/gone, direction)
. = ..()
if (gone != corpse)
return // Weird but ok
UnregisterSignal(corpse, list(COMSIG_LIVING_REVIVE, COMSIG_QDELETING))
corpse = null
qdel(src)
/// Bring our body back to life
/obj/effect/spectre_of_resurrection/proc/revive()
if (!isnull(dead_mind))
if (dead_mind.current == corpse)
dead_mind.grab_ghost(force = TRUE)
else
dead_mind.transfer_to(corpse, force_key_move = TRUE)
corpse.revive(HEAL_ALL) // The signal is sent even if they weren't actually dead
/// Remove our stored corpse back to the living world
/obj/effect/spectre_of_resurrection/proc/on_corpse_revived()
SIGNAL_HANDLER
if (isnull(corpse))
return
visible_message(span_boldnotice("[corpse] suddenly shudders to life!"))
corpse.remove_traits(list(TRAIT_NO_TELEPORT, TRAIT_AI_PAUSED), MAGIC_TRAIT)
corpse.remove_status_effect(/datum/status_effect/grouped/stasis, MAGIC_TRAIT)
corpse.forceMove(loc)
/// If the body is destroyed then we can't come back, F
/obj/effect/spectre_of_resurrection/proc/on_corpse_deleted()
SIGNAL_HANDLER
qdel(src)
/// If the mind is deleted somehow we just don't transfer it on revival
/obj/effect/spectre_of_resurrection/proc/on_mind_lost()
SIGNAL_HANDLER
dead_mind = null
/// A ressurection spectre with extra behaviour for humans
/obj/effect/spectre_of_resurrection/human
/// Stored data used to restore someone to a fascimile of what they were before
var/datum/human_appearance_profile/human_appearance
/obj/effect/spectre_of_resurrection/human/set_up_resurrection(mob/living/corpse, datum/mind/dead_mind, datum/human_appearance_profile/human_appearance)
. = ..()
src.human_appearance = human_appearance
// We just use a generic floating human appearance to save unecessary costly icon operations
/obj/effect/spectre_of_resurrection/human/setup_icon(mob/living/corpse)
return
// Apply stored human details
/obj/effect/spectre_of_resurrection/human/on_corpse_revived()
if (isnull(corpse))
return
human_appearance?.apply_to(corpse)
return ..()
/// Visual flair on the wizard when cast
/obj/effect/temp_visual/immortality_blast
name = "immortal wave"
duration = 2.5 SECONDS
icon = 'icons/effects/96x96.dmi'
icon_state = "boh_tear"
color = COLOR_PALE_GREEN
pixel_x = -32
pixel_y = -32
/obj/effect/temp_visual/immortality_blast/Initialize(mapload)
. = ..()
transform *= 0
animate(src, transform = matrix(), time = 1.5 SECONDS, easing = ELASTIC_EASING)
animate(transform = matrix() * 3, time = 1 SECONDS, alpha = 0, easing = SINE_EASING | EASE_OUT)
/// Visual flair on living creatures who have become immortal
/obj/effect/temp_visual/immortality_pulse
name = "immortal pulse"
duration = 1 SECONDS
icon = 'icons/effects/anomalies.dmi'
icon_state = "dimensional_overlay"
color = COLOR_PALE_GREEN
/obj/effect/temp_visual/immortality_pulse/Initialize(mapload)
. = ..()
transform *= 0
animate(src, transform = matrix() * 1.5, alpha = 0, time = 1 SECONDS, easing = SINE_EASING | EASE_OUT)
#undef IMMORTAL_PRE_ACTIVATION_TIME
#undef IMMORTAL_RESURRECT_TIME
@@ -0,0 +1,46 @@
/// Completely transform the station
/datum/grand_finale/midas
name = "Transformation"
desc = "The ultimate use of your gathered power! Turn their precious station into something much MORE precious, materially speaking!"
icon = 'icons/obj/stack_objects.dmi'
icon_state = "sheet-gold_2"
glow_colour = "#dbdd4c48"
var/static/list/permitted_transforms = list( // Non-dangerous only
/datum/dimension_theme/gold,
/datum/dimension_theme/meat,
/datum/dimension_theme/pizza,
/datum/dimension_theme/natural,
)
var/datum/dimension_theme/chosen_theme
// I sure hope this doesn't have performance implications
/datum/grand_finale/midas/trigger(mob/living/carbon/human/invoker)
var/theme_path = pick(permitted_transforms)
chosen_theme = new theme_path()
var/turf/start_turf = get_turf(invoker)
var/greatest_dist = 0
var/list/turfs_to_transform = list()
for (var/turf/transform_turf as anything in GLOB.station_turfs)
if (!chosen_theme.can_convert(transform_turf))
continue
var/dist = get_dist(start_turf, transform_turf)
if (dist > greatest_dist)
greatest_dist = dist
if (!turfs_to_transform["[dist]"])
turfs_to_transform["[dist]"] = list()
turfs_to_transform["[dist]"] += transform_turf
if (chosen_theme.can_convert(start_turf))
chosen_theme.apply_theme(start_turf)
for (var/iterator in 1 to greatest_dist)
if(!turfs_to_transform["[iterator]"])
continue
addtimer(CALLBACK(src, PROC_REF(transform_area), turfs_to_transform["[iterator]"]), (5 SECONDS) * iterator)
/datum/grand_finale/midas/proc/transform_area(list/turfs)
for (var/turf/transform_turf as anything in turfs)
if (!chosen_theme.can_convert(transform_turf))
continue
chosen_theme.apply_theme(transform_turf)
CHECK_TICK
@@ -1,456 +0,0 @@
#define DOOM_SINGULARITY "singularity"
#define DOOM_TESLA "tesla"
#define DOOM_METEORS "meteors"
/**
* A big final event to run when you complete seven rituals
*/
/datum/grand_finale
/// Friendly name for selection menu
var/name
/// Tooltip description for selection menu
var/desc
/// An icon to display to represent the choice
var/icon/icon
/// Icon state to use to represent the choice
var/icon_state
/// Prevent especially dangerous options from being chosen until we're fine with the round ending
var/minimum_time = 0
/// Override the rune invocation time
var/ritual_invoke_time = 30 SECONDS
/// Provide an extremely loud radio message when this one starts
var/dire_warning = FALSE
/// Overrides the default colour you glow while channeling the rune, optional
var/glow_colour
/**
* Returns an entry for a radial menu for this choice.
* Returns null if entry is abstract or invalid for current circumstances.
*/
/datum/grand_finale/proc/get_radial_choice()
if (!name || !desc || !icon || !icon_state)
return
var/time_remaining_desc = ""
if (minimum_time >= world.time - SSticker.round_start_time)
time_remaining_desc = "<i>This ritual will be available to begin invoking in [DisplayTimeText(minimum_time - world.time - SSticker.round_start_time)]</i>"
var/datum/radial_menu_choice/choice = new()
choice.name = name
choice.image = image(icon = icon, icon_state = icon_state)
choice.info = desc + time_remaining_desc
return choice
/**
* Actually do the thing.
* Arguments
* * invoker - The wizard casting this.
*/
/datum/grand_finale/proc/trigger(mob/living/invoker)
// Do something cool.
/// Tries to equip something into an inventory slot, then hands, then the floor.
/datum/grand_finale/proc/equip_to_slot_then_hands(mob/living/carbon/human/invoker, slot, obj/item/item)
if(!item)
return
if(!invoker.equip_to_slot_if_possible(item, slot, disable_warning = TRUE))
invoker.put_in_hands(item)
/// They are not going to take this lying down.
/datum/grand_finale/proc/create_vendetta(datum/mind/aggrieved_crewmate, datum/mind/wizard)
aggrieved_crewmate.add_antag_datum(/datum/antagonist/wizard_prank_vendetta)
var/datum/antagonist/wizard_prank_vendetta/antag_datum = aggrieved_crewmate.has_antag_datum(/datum/antagonist/wizard_prank_vendetta)
var/datum/objective/assassinate/wizard_murder = new
wizard_murder.owner = aggrieved_crewmate
wizard_murder.target = wizard
wizard_murder.explanation_text = "Kill [wizard.current.name], the one who did this."
antag_datum.objectives += wizard_murder
to_chat(aggrieved_crewmate.current, span_warning("No! This isn't right!"))
aggrieved_crewmate.announce_objectives()
/**
* Antag datum to give to people who want to kill the wizard.
* This doesn't preclude other people choosing to want to kill the wizard, just these people are rewarded for it.
*/
/datum/antagonist/wizard_prank_vendetta
name = "\improper Wizard Prank Victim"
roundend_category = "wizard prank victims"
show_in_antagpanel = FALSE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
silent = TRUE
/// Become the official Captain of the station
/datum/grand_finale/usurp
name = "Usurpation"
desc = "The ultimate use of your gathered power! Rewrite time such that you have been Captain of this station the whole time."
icon = 'icons/obj/card.dmi'
icon_state = "card_gold"
/datum/grand_finale/usurp/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] has replaced the Captain")
var/list/former_captains = list()
var/list/other_crew = list()
SEND_SOUND(world, sound('sound/magic/timeparadox2.ogg'))
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (!crewmate.mind)
continue
crewmate.Unconscious(3 SECONDS) // Everyone falls unconscious but not everyone gets told about a new captain
if (crewmate == invoker || IS_HUMAN_INVADER(crewmate))
continue
to_chat(crewmate, span_notice("The world spins and dissolves. Your past flashes before your eyes, backwards.\n\
Life strolls back into the ocean and shrinks into nothingness, planets explode into storms of solar dust, \
the stars rush back to greet each other at the beginning of things and then... you snap back to the present. \n\
Everything is just as it was and always has been. \n\n\
A stray thought sticks in the forefront of your mind. \n\
[span_hypnophrase("I'm so glad that [invoker.real_name] is our legally appointed Captain!")] \n\
Is... that right?"))
if (is_captain_job(crewmate.mind.assigned_role))
former_captains += crewmate
demote_to_assistant(crewmate)
continue
if (crewmate.stat != DEAD)
other_crew += crewmate
dress_candidate(invoker)
GLOB.manifest.modify(invoker.real_name, JOB_CAPTAIN, JOB_CAPTAIN)
minor_announce("Captain [invoker.real_name] on deck!")
// Enlist some crew to try and restore the natural order
for (var/mob/living/carbon/human/former_captain as anything in former_captains)
create_vendetta(former_captain.mind, invoker.mind)
for (var/mob/living/carbon/human/random_crewmate as anything in other_crew)
if (prob(10))
create_vendetta(random_crewmate.mind, invoker.mind)
/**
* Anyone who thought they were Captain is in for a nasty surprise, and won't be very happy about it
*/
/datum/grand_finale/usurp/proc/demote_to_assistant(mob/living/carbon/human/former_captain)
var/obj/effect/particle_effect/fluid/smoke/exit_poof = new(get_turf(former_captain))
exit_poof.lifetime = 2 SECONDS
former_captain.unequip_everything()
if(isplasmaman(former_captain))
former_captain.equipOutfit(/datum/outfit/plasmaman)
former_captain.internal = former_captain.get_item_for_held_index(2)
else
former_captain.equipOutfit(/datum/outfit/job/assistant)
GLOB.manifest.modify(former_captain.real_name, JOB_ASSISTANT, JOB_ASSISTANT)
var/list/valid_turfs = list()
// Used to be into prison but that felt a bit too mean
for (var/turf/exile_turf as anything in get_area_turfs(/area/station/maintenance, subtypes = TRUE))
if (isspaceturf(exile_turf) || exile_turf.is_blocked_turf())
continue
valid_turfs += exile_turf
do_teleport(former_captain, pick(valid_turfs), no_effects = TRUE)
var/obj/effect/particle_effect/fluid/smoke/enter_poof = new(get_turf(former_captain))
enter_poof.lifetime = 2 SECONDS
/**
* Does some item juggling to try to dress you as both a Wizard and Captain without deleting any items you have bought.
* ID, headset, and uniform are forcibly replaced. Other slots are only filled if unoccupied.
* We could forcibly replace shoes and gloves too but people might miss their insuls or... meown shoes?
*/
/datum/grand_finale/usurp/proc/dress_candidate(mob/living/carbon/human/invoker)
// Won't be needing these
var/obj/id = invoker.get_item_by_slot(ITEM_SLOT_ID)
QDEL_NULL(id)
var/obj/headset = invoker.get_item_by_slot(ITEM_SLOT_EARS)
QDEL_NULL(headset)
// We're about to take off your pants so those are going to fall out
var/obj/item/pocket_L = invoker.get_item_by_slot(ITEM_SLOT_LPOCKET)
var/obj/item/pocket_R = invoker.get_item_by_slot(ITEM_SLOT_RPOCKET)
// In case we try to put a PDA there
var/obj/item/belt = invoker.get_item_by_slot(ITEM_SLOT_BELT)
belt?.moveToNullspace()
var/obj/pants = invoker.get_item_by_slot(ITEM_SLOT_ICLOTHING)
QDEL_NULL(pants)
invoker.equipOutfit(/datum/outfit/job/wizard_captain)
// And put everything back!
equip_to_slot_then_hands(invoker, ITEM_SLOT_BELT, belt)
equip_to_slot_then_hands(invoker, ITEM_SLOT_LPOCKET, pocket_L)
equip_to_slot_then_hands(invoker, ITEM_SLOT_RPOCKET, pocket_R)
/// An outfit which replaces parts of a wizard's clothes with captain's clothes but keeps the robes
/datum/outfit/job/wizard_captain
name = "Captain (Wizard Transformation)"
jobtype = /datum/job/captain
id = /obj/item/card/id/advanced/gold
id_trim = /datum/id_trim/job/captain
uniform = /obj/item/clothing/under/rank/captain/parade
belt = /obj/item/modular_computer/pda/heads/captain
ears = /obj/item/radio/headset/heads/captain/alt
glasses = /obj/item/clothing/glasses/sunglasses
gloves = /obj/item/clothing/gloves/captain
shoes = /obj/item/clothing/shoes/laceup
accessory = /obj/item/clothing/accessory/medal/gold/captain
backpack_contents = list(
/obj/item/melee/baton/telescopic = 1,
/obj/item/station_charter = 1,
)
box = null
/// Dress the crew as magical clowns
/datum/grand_finale/clown
name = "Jubilation"
desc = "The ultimate use of your gathered power! Rewrite time so that everyone went to clown college! Now they'll prank each other for you!"
icon = 'icons/obj/clothing/masks.dmi'
icon_state = "clown"
glow_colour = "#ffff0048"
/datum/grand_finale/clown/trigger(mob/living/carbon/human/invoker)
for(var/mob/living/carbon/human/victim as anything in GLOB.human_list)
victim.Unconscious(3 SECONDS)
if (!victim.mind || IS_HUMAN_INVADER(victim) || victim == invoker)
continue
if (HAS_TRAIT(victim, TRAIT_CLOWN_ENJOYER))
victim.add_mood_event("clown_world", /datum/mood_event/clown_world)
to_chat(victim, span_notice("The world spins and dissolves. Your past flashes before your eyes, backwards.\n\
Life strolls back into the ocean and shrinks into nothingness, planets explode into storms of solar dust, \
the stars rush back to greet each other at the beginning of things and then... you snap back to the present. \n\
Everything is just as it was and always has been. \n\n\
A stray thought sticks in the forefront of your mind. \n\
[span_hypnophrase("I'm so glad that I work at Clown Research Station [station_name()]!")] \n\
Is... that right?"))
if (is_clown_job(victim.mind.assigned_role))
var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim)
new_spell.Grant(victim)
continue
if (!ismonkey(victim)) // Monkeys cannot yet wear clothes
dress_as_magic_clown(victim)
if (prob(15))
create_vendetta(victim.mind, invoker.mind)
/**
* Clown enjoyers who are effected by this become ecstatic, they have achieved their life's dream.
* This moodlet is equivalent to the one for simply being a traitor.
*/
/datum/mood_event/clown_world
mood_change = 4
/datum/mood_event/clown_world/add_effects(param)
description = "I LOVE working at Clown Research Station [station_name()]!!"
/// Dress the passed mob as a magical clown, self-explanatory
/datum/grand_finale/clown/proc/dress_as_magic_clown(mob/living/carbon/human/victim)
var/obj/effect/particle_effect/fluid/smoke/poof = new(get_turf(victim))
poof.lifetime = 2 SECONDS
var/obj/item/tank/internal = victim.internal
// We're about to take off your pants so those are going to fall out
var/obj/item/pocket_L = victim.get_item_by_slot(ITEM_SLOT_LPOCKET)
var/obj/item/pocket_R = victim.get_item_by_slot(ITEM_SLOT_RPOCKET)
var/obj/item/id = victim.get_item_by_slot(ITEM_SLOT_ID)
var/obj/item/belt = victim.get_item_by_slot(ITEM_SLOT_BELT)
var/obj/pants = victim.get_item_by_slot(ITEM_SLOT_ICLOTHING)
var/obj/mask = victim.get_item_by_slot(ITEM_SLOT_MASK)
QDEL_NULL(pants)
QDEL_NULL(mask)
if(isplasmaman(victim))
victim.equip_to_slot_if_possible(new /obj/item/clothing/under/plasmaman/clown/magic(), ITEM_SLOT_ICLOTHING, disable_warning = TRUE)
victim.equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat/plasmaman(), ITEM_SLOT_MASK, disable_warning = TRUE)
else
victim.equip_to_slot_if_possible(new /obj/item/clothing/under/rank/civilian/clown/magic(), ITEM_SLOT_ICLOTHING, disable_warning = TRUE)
victim.equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat(), ITEM_SLOT_MASK, disable_warning = TRUE)
var/obj/item/clothing/mask/gas/clown_hat/clown_mask = victim.get_item_by_slot(ITEM_SLOT_MASK)
if (clown_mask)
var/list/options = GLOB.clown_mask_options
clown_mask.icon_state = options[pick(clown_mask.clownmask_designs)]
victim.update_worn_mask()
clown_mask.update_item_action_buttons()
equip_to_slot_then_hands(victim, ITEM_SLOT_LPOCKET, pocket_L)
equip_to_slot_then_hands(victim, ITEM_SLOT_RPOCKET, pocket_R)
equip_to_slot_then_hands(victim, ITEM_SLOT_ID, id)
equip_to_slot_then_hands(victim, ITEM_SLOT_BELT, belt)
victim.internal = internal
/// Give everyone magic items
/datum/grand_finale/magic
name = "Evolution"
desc = "The ultimate use of your gathered power! Give the crew their own magic, they'll surely realise that right and wrong have no meaning when you hold ultimate power!"
icon = 'icons/obj/scrolls.dmi'
icon_state = "scroll"
/datum/grand_finale/magic/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] summoned magic")
summon_magic(survivor_probability = 20) // Wow, this one was easy!
/// Open all of the doors
/datum/grand_finale/all_access
name = "Connection"
desc = "The ultimate use of your gathered power! Unlock every single door that they have! Nobody will be able to keep you out now, or anyone else for that matter!"
icon = 'icons/mob/actions/actions_spells.dmi'
icon_state = "knock"
/datum/grand_finale/all_access/trigger(mob/living/carbon/human/invoker)
message_admins("[key_name(invoker)] removed all door access requirements")
for(var/obj/machinery/door/target_door as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door))
if(is_station_level(target_door.z))
target_door.unlock()
target_door.req_access = list()
target_door.req_one_access = list()
INVOKE_ASYNC(target_door, TYPE_PROC_REF(/obj/machinery/door/airlock, open))
CHECK_TICK
priority_announce("AULIE OXIN FIERA!!", null, 'sound/magic/knock.ogg', sender_override = "[invoker.real_name]")
/// Completely transform the station
/datum/grand_finale/midas
name = "Transformation"
desc = "The ultimate use of your gathered power! Turn their precious station into something much MORE precious, materially speaking!"
icon = 'icons/obj/stack_objects.dmi'
icon_state = "sheet-gold_2"
glow_colour = "#dbdd4c48"
var/static/list/permitted_transforms = list( // Non-dangerous only
/datum/dimension_theme/gold,
/datum/dimension_theme/meat,
/datum/dimension_theme/pizza,
/datum/dimension_theme/natural,
)
var/datum/dimension_theme/chosen_theme
// I sure hope this doesn't have performance implications
/datum/grand_finale/midas/trigger(mob/living/carbon/human/invoker)
var/theme_path = pick(permitted_transforms)
chosen_theme = new theme_path()
var/turf/start_turf = get_turf(invoker)
var/greatest_dist = 0
var/list/turfs_to_transform = list()
for (var/turf/transform_turf as anything in GLOB.station_turfs)
if (!chosen_theme.can_convert(transform_turf))
continue
var/dist = get_dist(start_turf, transform_turf)
if (dist > greatest_dist)
greatest_dist = dist
if (!turfs_to_transform["[dist]"])
turfs_to_transform["[dist]"] = list()
turfs_to_transform["[dist]"] += transform_turf
if (chosen_theme.can_convert(start_turf))
chosen_theme.apply_theme(start_turf)
for (var/iterator in 1 to greatest_dist)
if(!turfs_to_transform["[iterator]"])
continue
addtimer(CALLBACK(src, PROC_REF(transform_area), turfs_to_transform["[iterator]"]), (5 SECONDS) * iterator)
/datum/grand_finale/midas/proc/transform_area(list/turfs)
for (var/turf/transform_turf as anything in turfs)
if (!chosen_theme.can_convert(transform_turf))
continue
chosen_theme.apply_theme(transform_turf)
CHECK_TICK
/// Kill yourself and probably a bunch of other people
/datum/grand_finale/armageddon
name = "Annihilation"
desc = "This crew have offended you beyond the realm of pranks. Make the ultimate sacrifice to teach them a lesson your elders can really respect. \
YOU WILL NOT SURVIVE THIS."
icon = 'icons/hud/screen_alert.dmi'
icon_state = "wounded"
minimum_time = 90 MINUTES // This will probably immediately end the round if it gets finished.
ritual_invoke_time = 60 SECONDS // Really give the crew some time to interfere with this one.
dire_warning = TRUE
glow_colour = "#be000048"
/// Things to yell before you die
var/static/list/possible_last_words = list(
"Flames and ruin!",
"Dooooooooom!!",
"HAHAHAHAHAHA!! AHAHAHAHAHAHAHAHAA!!",
"Hee hee hee!! Hoo hoo hoo!! Ha ha haaa!!",
"Ohohohohohoho!!",
"Cower in fear, puny mortals!",
"Tremble before my glory!",
"Pick a god and pray!",
"It's no use!",
"If the gods wanted you to live, they would not have created me!",
"God stays in heaven out of fear of what I have created!",
"Ruination is come!",
"All of creation, bend to my will!",
)
/datum/grand_finale/armageddon/trigger(mob/living/carbon/human/invoker)
priority_announce(pick(possible_last_words), null, 'sound/magic/voidblink.ogg', sender_override = "[invoker.real_name]")
var/turf/current_location = get_turf(invoker)
invoker.gib()
var/static/list/doom_options = list()
if (!length(doom_options))
doom_options = list(DOOM_SINGULARITY, DOOM_TESLA)
if (!SSmapping.config.planetary)
doom_options += DOOM_METEORS
switch(pick(doom_options))
if (DOOM_SINGULARITY)
var/obj/singularity/singulo = new(current_location)
singulo.energy = 300
if (DOOM_TESLA)
var/obj/energy_ball/tesla = new (current_location)
tesla.energy = 200
if (DOOM_METEORS)
var/datum/dynamic_ruleset/roundstart/meteor/meteors = new()
meteors.meteordelay = 0
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.execute_roundstart_rule(meteors) // Meteors will continue until morale is crushed.
priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", ANNOUNCER_METEORS)
/**
* Gives the wizard a defensive/mood buff and a Wabbajack, a juiced up chaos staff that will surely break something.
* Everyone but the wizard goes crazy, suffers major brain damage, and is given a vendetta against the wizard.
* Already insane people are instead cured of their madness, ignoring any other effects as the station around them loses its marbles.
*/
/datum/grand_finale/cheese
// we don't set name, desc and others, thus we won't appear in the radial choice of a normal finale rune
dire_warning = TRUE
minimum_time = 45 MINUTES //i'd imagine speedrunning this would be crummy, but the wizard's average lifespan is barely reaching this point
/datum/grand_finale/cheese/trigger(mob/living/invoker)
message_admins("[key_name(invoker)] has summoned forth The Wabbajack and cursed the crew with madness!")
priority_announce("Danger: Extremely potent reality altering object has been summoned on station. Immediate evacuation advised. Brace for impact.", "Central Command Higher Dimensional Affairs", 'sound/effects/glassbr1.ogg')
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (isnull(crewmate.mind))
continue
if (crewmate == invoker) //everyone but the wizard is royally fucked, no matter who they are
continue
if (crewmate.has_trauma_type(/datum/brain_trauma/mild/hallucinations)) //for an already insane person, this is retribution
to_chat(crewmate, span_boldwarning("Your surroundings suddenly fill with a cacophony of manic laughter and psychobabble..."))
to_chat(crewmate, span_nicegreen("...but as the moment passes, you realise that whatever eldritch power behind the event happened to affect you \
has resonated within the ruins of your already shattered mind, creating a singularity of mental instability! \
As it collapses unto itself, you feel... at peace, finally."))
if(crewmate.has_quirk(/datum/quirk/insanity))
crewmate.remove_quirk(/datum/quirk/insanity)
else
crewmate.cure_trauma_type(/datum/brain_trauma/mild/hallucinations, TRAUMA_RESILIENCE_ABSOLUTE)
else
//everyone else gets to relish in madness
//yes killing their mood will also trigger mood hallucinations
create_vendetta(crewmate.mind, invoker.mind)
to_chat(crewmate, span_boldwarning("Your surroundings suddenly fill with a cacophony of manic laughter and psychobabble. \n\
You feel your inner psyche shatter into a myriad pieces of jagged glass of colors unknown to the universe, \
infinitely reflecting a blinding, maddening light coming from the innermost sanctums of your destroyed mind. \n\
After a brief pause which felt like a millenia, one phrase rebounds ceaselessly in your head, imbued with the false hope of absolution... \n\
<b>[invoker] must die.</b>"))
var/datum/brain_trauma/mild/hallucinations/added_trauma = new()
added_trauma.resilience = TRAUMA_RESILIENCE_ABSOLUTE
crewmate.adjustOrganLoss(ORGAN_SLOT_BRAIN, BRAIN_DAMAGE_DEATH - 25, BRAIN_DAMAGE_DEATH - 25) //you'd better hope chap didn't pick a hypertool
crewmate.gain_trauma(added_trauma)
crewmate.add_mood_event("wizard_ritual_finale", /datum/mood_event/madness_despair)
//drip our wizard out
invoker.apply_status_effect(/datum/status_effect/blessing_of_insanity)
invoker.add_mood_event("wizard_ritual_finale", /datum/mood_event/madness_elation)
var/obj/item/gun/magic/staff/chaos/true_wabbajack/the_wabbajack = new
invoker.put_in_active_hand(the_wabbajack)
to_chat(invoker, span_mind_control("Your every single instinct and rational thought is screaming at you as [the_wabbajack] appears in your firm grip..."))
#undef DOOM_SINGULARITY
#undef DOOM_TESLA
#undef DOOM_METEORS
@@ -8,7 +8,7 @@
var/can_respawn = TRUE
///Reference to the game this player is participating in.
var/datum/ctf_controller/ctf_game
///Item dropped on death,
///Item dropped on death,
var/death_drop = /obj/effect/powerup/ammo/ctf
///Reference to players ckey, used for sending messages to them relating to CTF.
var/ckey_reference
@@ -22,19 +22,20 @@
var/datum/mind/true_parent = parent
player_mob = true_parent.current
ckey_reference = player_mob.ckey
setup_dusting()
register_mob()
/datum/component/ctf_player/PostTransfer()
if(!istype(parent, /datum/mind))
return COMPONENT_INCOMPATIBLE
var/datum/mind/true_parent = parent
player_mob = true_parent.current
setup_dusting()
register_mob()
///CTF players are dusted upon taking damage that puts them into critical or leaving their body.
/datum/component/ctf_player/proc/setup_dusting()
/// Called when we get a new player mob, register signals and set up the mob.
/datum/component/ctf_player/proc/register_mob()
RegisterSignal(player_mob, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(damage_type_check))
RegisterSignal(player_mob, COMSIG_MOB_GHOSTIZED, PROC_REF(ctf_dust))
ADD_TRAIT(player_mob, TRAIT_PERMANENTLY_MORTAL, CTF_TRAIT)
///Stamina and oxygen damage will not dust a player by themself.
/datum/component/ctf_player/proc/damage_type_check(datum/source, damage, damage_type)
+1
View File
@@ -61,6 +61,7 @@
mobtype = pick(mobtype)
our_mob = new mobtype(loc)
our_mob.flags_1 |= HOLOGRAM_1
ADD_TRAIT(our_mob, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT)
// these vars are not really standardized but all would theoretically create stuff on death
for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & our_mob.vars)
+1 -1
View File
@@ -578,7 +578,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment())
for(var/datum/mafia_role/role as anything in all_roles)
var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
H.add_traits(list(TRAIT_NOFIRE, TRAIT_NOBREATH, TRAIT_CANNOT_CRYSTALIZE), MAFIA_TRAIT)
H.add_traits(list(TRAIT_NOFIRE, TRAIT_NOBREATH, TRAIT_CANNOT_CRYSTALIZE, TRAIT_PERMANENTLY_MORTAL), MAFIA_TRAIT)
H.equipOutfit(outfit_to_distribute)
H.status_flags |= GODMODE
RegisterSignal(H, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(display_votes))
@@ -88,6 +88,11 @@
/// If the blob changes to distributed neurons then you can control the spores
/mob/living/basic/blob_minion/spore/minion/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain)
if (isnull(overmind))
REMOVE_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT)
else
ADD_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT)
if (istype(new_strain, /datum/blobstrain/reagent/distributed_neurons))
AddComponent(\
/datum/component/ghost_direct_control,\
@@ -28,6 +28,7 @@
/mob/living/basic/blob_minion/zombie/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT) // This mob doesn't function visually without a corpse and wouldn't respawn with one
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBSPORE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
/mob/living/basic/blob_minion/zombie/death(gibbed)
@@ -102,7 +102,7 @@
/mob/living/basic/hivelord_brood/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_PERMANENTLY_MORTAL), INNATE_TRAIT)
AddElement(/datum/element/simple_flying)
AddComponent(/datum/component/swarming)
AddComponent(/datum/component/clickbox, icon_state = "hivelord", max_scale = INFINITY)
@@ -34,7 +34,7 @@
/mob/living/basic/legion_brood/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_PERMANENTLY_MORTAL), INNATE_TRAIT)
AddElement(/datum/element/simple_flying)
AddComponent(/datum/component/swarming)
AddComponent(/datum/component/clickbox, icon_state = "sphere", max_scale = 2)
@@ -198,6 +198,7 @@
for(var/i in 1 to worm_length)
current = new type(drop_location(), FALSE)
ADD_TRAIT(current, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT)
current.icon_state = "armsy_mid"
current.icon_living = "armsy_mid"
current.AIStatus = AI_OFF
@@ -113,6 +113,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, list(/obj/structure/table, /obj/structure
/mob/living/simple_animal/hostile/mimic/copy/Initialize(mapload, obj/copy, mob/living/creator, destroy_original = 0, no_googlies = FALSE)
. = ..()
ADD_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT) // They won't remember their original contents upon ressurection and would just be floating eyes
if (no_googlies)
overlay_googly_eyes = FALSE
CopyObject(copy, creator, destroy_original)
@@ -108,7 +108,7 @@
if(!COOLDOWN_FINISHED(src, crystalize_cooldown) || ethereal.stat != DEAD)
return //Should probably not happen, but lets be safe.
if(ismob(location) || isitem(location) || HAS_TRAIT_FROM(src, TRAIT_HUSK, CHANGELING_DRAIN)) //Stops crystallization if they are eaten by a dragon, turned into a legion, consumed by his grace, etc.
if(ismob(location) || isitem(location) || iseffect(location) || HAS_TRAIT_FROM(src, TRAIT_HUSK, CHANGELING_DRAIN)) //Stops crystallization if they are eaten by a dragon, turned into a legion, consumed by his grace, etc.
to_chat(ethereal, span_warning("You were unable to finish your crystallization, for obvious reasons."))
stop_crystalization_process(ethereal, FALSE)
return
Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 KiB

After

Width:  |  Height:  |  Size: 928 KiB

+8 -1
View File
@@ -3047,9 +3047,16 @@
#include "code\modules\antagonists\wizard\equipment\spellbook_entries\summons.dm"
#include "code\modules\antagonists\wizard\grand_ritual\fluff.dm"
#include "code\modules\antagonists\wizard\grand_ritual\grand_ritual.dm"
#include "code\modules\antagonists\wizard\grand_ritual\grand_ritual_finale.dm"
#include "code\modules\antagonists\wizard\grand_ritual\grand_rune.dm"
#include "code\modules\antagonists\wizard\grand_ritual\grand_side_effect.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\all_access.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\armageddon.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\captaincy.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\cheese.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\clown.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\grand_ritual_finale.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\immortality.dm"
#include "code\modules\antagonists\wizard\grand_ritual\finales\midas.dm"
#include "code\modules\antagonists\xeno\xeno.dm"
#include "code\modules\art\paintings.dm"
#include "code\modules\art\statues.dm"