diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 73fbe6723e6..4af9f4396be 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -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 diff --git a/code/datums/actions/action.dm b/code/datums/actions/action.dm index 843998ff50e..d81c72cc920 100644 --- a/code/datums/actions/action.dm +++ b/code/datums/actions/action.dm @@ -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) diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index fc93123921c..18420016c54 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -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) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 3b2a13c2cb5..062412f70e9 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -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.")) diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm b/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm new file mode 100644 index 00000000000..07958ed94a7 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm @@ -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]") diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm new file mode 100644 index 00000000000..876f2475d55 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm @@ -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 diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm b/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm new file mode 100644 index 00000000000..d1a3c1afaf7 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm @@ -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 diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm b/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm new file mode 100644 index 00000000000..714cd62659b --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm @@ -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\ + [invoker] must die.")) + 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...")) diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm new file mode 100644 index 00000000000..bda79c908c0 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm @@ -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 diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm new file mode 100644 index 00000000000..b92ae4d2f20 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm @@ -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 = "This ritual will be available to begin invoking in [DisplayTimeText(minimum_time - world.time - SSticker.round_start_time)]" + 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! diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm new file mode 100644 index 00000000000..d20ca06752b --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm @@ -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 extremely 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 diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/midas.dm b/code/modules/antagonists/wizard/grand_ritual/finales/midas.dm new file mode 100644 index 00000000000..b2e3329261f --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/finales/midas.dm @@ -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 diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_ritual_finale.dm b/code/modules/antagonists/wizard/grand_ritual/grand_ritual_finale.dm deleted file mode 100644 index 9a59b1f1a79..00000000000 --- a/code/modules/antagonists/wizard/grand_ritual/grand_ritual_finale.dm +++ /dev/null @@ -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 = "This ritual will be available to begin invoking in [DisplayTimeText(minimum_time - world.time - SSticker.round_start_time)]" - 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\ - [invoker] must die.")) - 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 - diff --git a/code/modules/capture_the_flag/ctf_player_component.dm b/code/modules/capture_the_flag/ctf_player_component.dm index 0bf37b979de..d3abc0f2571 100644 --- a/code/modules/capture_the_flag/ctf_player_component.dm +++ b/code/modules/capture_the_flag/ctf_player_component.dm @@ -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) diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm index 02502c1e6fb..a8d51c87728 100644 --- a/code/modules/holodeck/holo_effect.dm +++ b/code/modules/holodeck/holo_effect.dm @@ -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) diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm index 1916a65f7b1..f8d9db13106 100644 --- a/code/modules/mafia/controller.dm +++ b/code/modules/mafia/controller.dm @@ -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)) diff --git a/code/modules/mob/living/basic/blob_minions/blob_spore.dm b/code/modules/mob/living/basic/blob_minions/blob_spore.dm index f13a6a70f84..e8c3acc8b97 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_spore.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_spore.dm @@ -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,\ diff --git a/code/modules/mob/living/basic/blob_minions/blob_zombie.dm b/code/modules/mob/living/basic/blob_minions/blob_zombie.dm index 40b73091abf..c9bf3b7346a 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_zombie.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_zombie.dm @@ -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) diff --git a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm index 256ab1fbd8f..11043e58d11 100644 --- a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm +++ b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm @@ -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) diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm index bc21bd0e506..962d232c5ef 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm @@ -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) diff --git a/code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm b/code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm index 2c0b9ba983a..1b8cfe9de68 100644 --- a/code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm +++ b/code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm @@ -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 diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 2fec84385b2..d07775b42bd 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -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) diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm index f29ee2e731d..bb0b30ddd62 100644 --- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm +++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm @@ -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 diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 29e59dc6c71..d59f065da28 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 2fe8275a014..57b5fe15138 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"