diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b5cac72b7803..b60d02c79d2c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -26,3 +26,8 @@ _build_dependencies.sh @AffectedArc07 dreamchecker.exe @AffectedArc07 rust_g.dll @AffectedArc07 librust_g.so @AffectedArc07 + +### S34NW + +# TGUI stuff +/tgui/bin @S34NW diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f22c1134b10f..ae777746daf3 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -157,7 +157,7 @@ if(thing == TRUE) return "bleh" var/other_thing = pick(TRUE, FALSE) if(other_thing == FALSE) - return "meh" + return "meh" // Good var/thing = pick(TRUE, FALSE) @@ -165,7 +165,7 @@ if(thing) return "bleh" var/other_thing = pick(TRUE, FALSE) if(!other_thing) - return "meh" + return "meh" ``` ### Use `pick(x, y, z)`, not `pick(list(x, y, z))` @@ -452,6 +452,32 @@ Look for code examples on how to properly use it. addtimer(CALLBACK(target, PROC_REF(dothing), arg1, arg2, arg3), 5 SECONDS) ``` +### Signals + +Signals are a slightly more advanced topic, but are often useful for attaching external behavior to objects that should be triggered when a specific event occurs. + +When defining procs that should be called by signals, you must include `SIGNAL_HANDLER` after the proc header. This ensures that no sleeping code can be called from within a signal handler, as that can cause problems with the signal system. + +Since callbacks can be connected to many signals with `RegisterSignal`, it can be difficult to pin down the source that a callback is invoked from. Any new `SIGNAL_HANDLER` should be followed by a comment listing the signals that the proc is expected to be invoked for. If there are multiple signals to be handled, separate them with a `+`. + +```dm +/atom/movable/proc/when_moved(atom/movable/A) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + do_something() + +/datum/component/foo/proc/on_enter(datum/source, atom/enterer) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + COMSIG_ATOM_INITIALIZED_ON + do_something_else() +``` + +If your proc does have something that needs to sleep (such as a `do_after()`), do not simply omit the `SIGNAL_HANDLER`. Instead, call the sleeping code with `INVOKE_ASYNC` from within the signal handling function. + +```dm +/atom/movable/proc/when_moved(atom/movable/A) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + INVOKE_ASYNC(src, PROC_REF(thing_that_sleeps), arg1) +``` + ### Operators #### Spacing of operators diff --git a/SQL/updates/53-54.sql b/SQL/updates/53-54.sql index 303de0b32be3..c2fb1988a299 100644 --- a/SQL/updates/53-54.sql +++ b/SQL/updates/53-54.sql @@ -2,4 +2,4 @@ #Add a choice for what type of brain borgs want to have ALTER TABLE `characters` - ADD COLUMN `cyborg_brain_type` VARCHAR(11) NOT NULL DEFAULT 'MMI' AFTER `height`; + ADD COLUMN `cyborg_brain_type` ENUM('MMI', 'Robobrain', 'Positronic') NOT NULL DEFAULT 'MMI' AFTER `height`; diff --git a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm index a00c71b1e94d..89383c9e77cd 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm @@ -126,6 +126,11 @@ /obj/structure/bed, /turf/simulated/floor/mineral/titanium/purple, /area/ruin/space/powered) +"Q" = ( +/obj/structure/table/wood, +/obj/item/blank_tarot_card, +/turf/simulated/floor/mineral/titanium/purple, +/area/ruin/space/powered) (1,1,1) = {" a @@ -277,7 +282,7 @@ b j r w -n +Q j b a diff --git a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm index cd0277ea77cc..ce511619893d 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm @@ -551,7 +551,6 @@ info = "Nothing of interest to report."; name = "november report" }, -/obj/item/pen, /obj/item/tape, /obj/item/radio/intercom{ freerange = 1; @@ -559,7 +558,7 @@ name = "intercom" }, /obj/item/paper_bin, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel/dark, /area/ruin/space/syndicate_listening_station) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm index 52cb532a602d..b38b995bef2f 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm @@ -523,7 +523,7 @@ /obj/item/paper_bin{ pixel_x = -6 }, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/mineral/plastitanium, /area/ruin/space/mech_transport) "MA" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm index 7fcf54e36ba7..349e4181c28b 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm @@ -5200,6 +5200,7 @@ }, /obj/item/coin/antagtoken/syndicate, /obj/structure/table, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/plating/asteroid/ancient, /area/ruin/space/moonbase19) "rk" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm index 83a335cc9b13..643d69228c37 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndicatedruglab.dmm @@ -98,11 +98,11 @@ pixel_x = 4 }, /obj/item/paper/syndicate_druglab, -/obj/item/pen, /obj/item/flashlight/lamp/green/off{ pixel_y = 12; pixel_x = -6 }, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/carpet/black, /area/ruin/space/syndicate_druglab) "qa" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm index 9c2fcf54cdc0..51db92f2ef44 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm @@ -2985,7 +2985,7 @@ dir = 8 }, /obj/item/hand_labeler, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "darkgreen" @@ -3423,7 +3423,7 @@ "th" = ( /obj/structure/table, /obj/item/paper_bin, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /obj/structure/extinguisher_cabinet{ name = "north bump"; pixel_y = 30 @@ -3760,7 +3760,7 @@ /area/ruin/unpowered/syndicate_space_base/service) "vd" = ( /obj/structure/table, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /obj/item/paper_bin, /obj/item/stamp/syndicate, /obj/machinery/light_switch{ @@ -8462,7 +8462,7 @@ dir = 8 }, /obj/item/hand_labeler, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "darkgreen" diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm index eebd64133f93..c8c725c60a69 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm @@ -498,6 +498,7 @@ /obj/machinery/light/small{ dir = 4 }, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/engine, /area/ruin/space/unpowered) "tX" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm index fc3e95325ab7..5164e88c3292 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm @@ -665,7 +665,7 @@ "bV" = ( /obj/structure/table, /obj/item/folder/syndicate/yellow, -/obj/item/pen, +/obj/item/pen/multi/syndicate, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm index d1f0175e1f43..8434558c253c 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm @@ -167,6 +167,7 @@ /area/ruin/space/unpowered) "aF" = ( /obj/structure/table/wood, +/obj/item/blank_tarot_card, /turf/simulated/floor/wood{ icon_state = "wood-broken6" }, diff --git a/code/__DEFINES/dcs/dcs_helpers.dm b/code/__DEFINES/dcs/dcs_helpers.dm index ba2b9a704a32..c5c7e3c42dd9 100644 --- a/code/__DEFINES/dcs/dcs_helpers.dm +++ b/code/__DEFINES/dcs/dcs_helpers.dm @@ -10,10 +10,6 @@ /// Every proc you pass to RegisterSignal must have this. #define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE) -/// Signifies that this proc is used to handle signals, but also sleeps. -/// Do not use this for new work. -#define SIGNAL_HANDLER_DOES_SLEEP - /// A wrapper for _AddElement that allows us to pretend we're using normal named arguments #define AddElement(arguments...) _AddElement(list(##arguments)) /// A wrapper for _RemoveElement that allows us to pretend we're using normal named arguments diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index f10dc5d3334a..1d4d33ac2644 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -1061,3 +1061,6 @@ /// Used by admin-tooling to remove radiation #define COMSIG_ADMIN_DECONTAMINATE "admin_decontaminate" + +/// Sent when bodies transfer between shades/shards and constructs +#define COMSIG_SHADE_TO_CONSTRUCT_TRANSFER "shade_to_construct_transfer" diff --git a/code/__DEFINES/preferences_defines.dm b/code/__DEFINES/preferences_defines.dm index 61d0ef1652df..162df1bb9737 100644 --- a/code/__DEFINES/preferences_defines.dm +++ b/code/__DEFINES/preferences_defines.dm @@ -17,7 +17,7 @@ #define PREFTOGGLE_CHAT_DEAD (1<<1) #define PREFTOGGLE_CHAT_GHOSTEARS (1<<2) #define PREFTOGGLE_CHAT_GHOSTSIGHT (1<<3) -#define PREFTOGGLE_CHAT_PRAYER (1<<4) +#define PREFTOGGLE_CHAT_PRAYER (1<<4) // Defunct #define PREFTOGGLE_CHAT_RADIO (1<<5) // #define PREFTOGGLE_AZERTY (1<<6) // obsolete #define PREFTOGGLE_CHAT_DEBUGLOGS (1<<7) @@ -81,6 +81,21 @@ #error toggles_2 bitflag over 16777215. Please make an issue report and postpone the feature you are working on. #endif +// This is a list index. Required to start at 1 instead of 0 so it's properly placed in the list +#define PREFTOGGLE_CATEGORY_GENERAL 1 +#define PREFTOGGLE_CATEGORY_LIVING 2 +#define PREFTOGGLE_CATEGORY_GHOST 3 +#define PREFTOGGLE_CATEGORY_ADMIN 4 + +// Preftoggle type defines +/// Special toggles, stuff that just overrides set_toggles entirely +#define PREFTOGGLE_SPECIAL 0 +/// Interacts with the sound bitflag +#define PREFTOGGLE_SOUND 1 +/// Interacts with the toggles bitflag +#define PREFTOGGLE_TOGGLE1 2 +/// Interacts with the toggles2 bitflag +#define PREFTOGGLE_TOGGLE2 3 // Admin attack logs filter system, see /proc/add_attack_logs and /proc/msg_admin_attack @@ -133,6 +148,7 @@ #define TAB_ANTAG 2 #define TAB_GEAR 3 #define TAB_KEYS 4 +#define TAB_TOGGLES 5 // Colourblind modes #define COLOURBLIND_MODE_NONE "None" diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 4f185e0f70a4..d34fb4868293 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -18,6 +18,8 @@ #define STATUS_EFFECT_SHADOW_MEND /datum/status_effect/shadow_mend //Quick, powerful heal that deals damage afterwards. Heals 15 brute/burn every second for 3 seconds. #define STATUS_EFFECT_VOID_PRICE /datum/status_effect/void_price //The price of healing yourself with void energy. Deals 3 brute damage every 3 seconds for 30 seconds. +#define STATUS_EFFECT_SHADOW_MEND_DEVIL /datum/status_effect/shadow_mend/devil //Tarot version, hurts others over self + #define STATUS_EFFECT_HIPPOCRATIC_OATH /datum/status_effect/hippocraticOath //Gives you an aura of healing as well as regrowing the Rod of Asclepius if lost #define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core @@ -37,6 +39,8 @@ #define STATUS_EFFECT_BLOODDRUNK /datum/status_effect/blooddrunk //Stun immunity and greatly reduced damage taken +#define STATUS_EFFECT_BLOODDRUNK_CHARIOT /datum/status_effect/blooddrunk/chariot //adds pacifism + #define STATUS_EFFECT_DASH /datum/status_effect/dash // Grants the ability to dash, expiring after a few secodns /// Rapid burn/brute/oxy/blood healing from the cling ability @@ -62,6 +66,12 @@ #define STATUS_EFFECT_BEARSERKER_RAGE /datum/status_effect/bearserker_rage +#define STATUS_EFFECT_XRAY /datum/status_effect/xray // Xray vision for 2 minutes + +#define STATUS_EFFECT_BADASS /datum/status_effect/badass // Badass trait for 2 minutes. + +#define STATUS_EFFECT_REVERSED_SUN /datum/status_effect/reversed_sun // Weaker eternal darkness, nightvision, but nearsight + ///////////// // DEBUFFS // ///////////// @@ -114,6 +124,8 @@ #define STATUS_EFFECT_PEPPERSPRAYED /datum/status_effect/pepper_spray +#define STATUS_EFFECT_REVERSED_HIGH_PRIESTESS /datum/status_effect/reversed_high_priestess //Bubblegum will chase the person hit by the effect, grabbing people at random. This can and WILL include the caster. + //#define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse //#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured //#define CURSE_SPAWNING 2 //spawns creatures that attack the target only diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 97c9fe44971c..174ecddda53c 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -139,6 +139,11 @@ if(initial(D.name)) GLOB.keybindings += new path() + for(var/path in subtypesof(/datum/preference_toggle)) + var/datum/preference_toggle/pref_toggle = path + if(initial(pref_toggle.name)) + GLOB.preference_toggles += new path() + for(var/path in subtypesof(/datum/objective)) var/datum/objective/O = path if(isnull(initial(O.name))) diff --git a/code/_globalvars/lists/preference_toggle_lists.dm b/code/_globalvars/lists/preference_toggle_lists.dm new file mode 100644 index 000000000000..bc7081595017 --- /dev/null +++ b/code/_globalvars/lists/preference_toggle_lists.dm @@ -0,0 +1,8 @@ +GLOBAL_LIST_EMPTY(preference_toggles) + +GLOBAL_LIST_INIT(preference_toggle_groups, list( + "General Preferences" = PREFTOGGLE_CATEGORY_GENERAL, + "In-Round Preferences" = PREFTOGGLE_CATEGORY_LIVING, + "Ghost Preferences" = PREFTOGGLE_CATEGORY_GHOST, + "Admin Preferences" = PREFTOGGLE_CATEGORY_ADMIN, +)) diff --git a/code/_onclick/hud/human_hud.dm b/code/_onclick/hud/human_hud.dm index 4a036cf6e85c..43ab2c14b69b 100644 --- a/code/_onclick/hud/human_hud.dm +++ b/code/_onclick/hud/human_hud.dm @@ -483,16 +483,3 @@ H.r_hand.screen_loc = null if(H.l_hand) H.l_hand.screen_loc = null - - -/mob/living/carbon/human/verb/toggle_hotkey_verbs() - set category = "OOC" - set name = "Toggle Hotkey Buttons" - set desc = "This disables or enables the user interface buttons which can be used with hotkeys." - - if(hud_used.hotkey_ui_hidden) - client.screen += hud_used.hotkeybuttons - hud_used.hotkey_ui_hidden = FALSE - else - client.screen -= hud_used.hotkeybuttons - hud_used.hotkey_ui_hidden = TRUE diff --git a/code/controllers/configuration/sections/general_configuration.dm b/code/controllers/configuration/sections/general_configuration.dm index 35477712f338..40b1729b7603 100644 --- a/code/controllers/configuration/sections/general_configuration.dm +++ b/code/controllers/configuration/sections/general_configuration.dm @@ -18,7 +18,7 @@ var/allow_antag_hud = TRUE /// Forbid players from rejoining if they use AntagHUD? var/restrict_antag_hud_rejoin = TRUE - /// Enable respanws by default? + /// Enable respawns by default? var/respawn_enabled = FALSE /// Enable CID randomiser buster? var/enabled_cid_randomiser_buster = FALSE @@ -93,8 +93,8 @@ CONFIG_LOAD_BOOL(guest_ban, data["guest_ban"]) CONFIG_LOAD_BOOL(allow_antag_hud, data["allow_antag_hud"]) CONFIG_LOAD_BOOL(restrict_antag_hud_rejoin, data["restrict_antag_hud_rejoin"]) - CONFIG_LOAD_BOOL(respawn_enabled, data["respawn_enabled"]) CONFIG_LOAD_BOOL(enabled_cid_randomiser_buster, data["enable_cid_randomiser_buster"]) + CONFIG_LOAD_BOOL(respawn_enabled, data["respawn_enabled"]) CONFIG_LOAD_BOOL(forbid_singulo_possession, data["prevent_admin_singlo_possession"]) CONFIG_LOAD_BOOL(popup_admin_pm, data["popup_admin_pm"]) CONFIG_LOAD_BOOL(allow_holidays, data["allow_holidays"]) diff --git a/code/controllers/subsystem/non_firing/SSchangelog.dm b/code/controllers/subsystem/non_firing/SSchangelog.dm index acfe7484f9de..53830ebce022 100644 --- a/code/controllers/subsystem/non_firing/SSchangelog.dm +++ b/code/controllers/subsystem/non_firing/SSchangelog.dm @@ -97,7 +97,7 @@ SUBSYSTEM_DEF(changelog) /client/verb/changes() set name = "Changelog" set desc = "View the changelog." - set category = "OOC" + set category = null // Just invoke the actual CL thing SSchangelog.OpenChangelog(src) diff --git a/code/datums/action.dm b/code/datums/action.dm index 511865a1c61a..eb6a1a19005f 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -699,7 +699,7 @@ // Make a holder for the charge text var/image/count_down_holder = image('icons/effects/effects.dmi', icon_state = "nothing") count_down_holder.plane = FLOAT_PLANE + 1.1 - var/text = S.cooldown_handler.statpanel_info() + var/text = S.cooldown_handler.cooldown_info() count_down_holder.maptext = "
[text]
" button.add_overlay(count_down_holder) diff --git a/code/datums/components/cult_held_body.dm b/code/datums/components/cult_held_body.dm new file mode 100644 index 000000000000..24deac16f3b4 --- /dev/null +++ b/code/datums/components/cult_held_body.dm @@ -0,0 +1,76 @@ +/** + * A component for tracking and manipulating bodies held inside constructs/shades + * Will drop the body/brain when the parent dies or is deleted. + */ +/datum/component/construct_held_body + dupe_mode = COMPONENT_DUPE_ALLOWED + /// A reference to either a mob, or a brain organ that is held inside our parent. Will drop when parent dies/is deleted + var/atom/movable/held_body + +/datum/component/construct_held_body/Initialize(atom/movable/new_body) + . = ..() + add_body(new_body) + +/datum/component/construct_held_body/Destroy(force, silent) + if(held_body) + stack_trace("/datum/component/construct_held_body had a held body still despite being destroyed. Body is [held_body] ([held_body.type])") + held_body = null + return ..() + +/datum/component/construct_held_body/PostTransfer() + held_body.forceMove(parent) // forcemove them to the new parent + +/datum/component/construct_held_body/RegisterWithParent() + RegisterSignal(parent, COMSIG_MOB_DEATH, PROC_REF(drop_body)) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(drop_body)) + RegisterSignal(parent, COMSIG_SHADE_TO_CONSTRUCT_TRANSFER, PROC_REF(transfer_held_body)) + +/datum/component/construct_held_body/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_MOB_DEATH) + UnregisterSignal(parent, COMSIG_PARENT_QDELETING) + UnregisterSignal(parent, COMSIG_SHADE_TO_CONSTRUCT_TRANSFER) + +/datum/component/construct_held_body/proc/add_body(atom/movable/new_body) + held_body = new_body + RegisterSignal(new_body, COMSIG_PARENT_QDELETING, PROC_REF(_null_held_body)) + new_body.forceMove(parent) + +/datum/component/construct_held_body/proc/_null_held_body() + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + UnregisterSignal(held_body, COMSIG_PARENT_QDELETING) + held_body = null + +/datum/component/construct_held_body/proc/transfer_held_body(mob/living/current_parent, mob/living/new_body_holder) + SIGNAL_HANDLER // COMSIG_SHADE_TO_CONSTRUCT_TRANSFER + new_body_holder.TakeComponent(src) + +/datum/component/construct_held_body/proc/drop_body() + SIGNAL_HANDLER // COMSIG_MOB_DEATH + COMSIG_PARENT_QDELETING + INVOKE_ASYNC(src, PROC_REF(_drop_body)) + +/datum/component/construct_held_body/proc/_drop_body() // call me lazy ig + if(!held_body) // Null check for empty bodies + return + var/mob/living/parent_mob = parent + held_body.forceMove(get_turf(parent)) + SSticker.mode?.cult_team?.add_cult_immunity(held_body) + + var/mob/living/held_mob = held_body + if(ismob(held_body)) // Check if the held_body is a mob + held_mob.key = parent_mob.key + held_mob.cancel_camera() + else if(istype(held_body, /obj/item/organ/internal/brain)) // Check if the held_body is a brain + var/obj/item/organ/internal/brain/brain = held_body + if(brain.brainmob) // Check if the brain has a brainmob + brain.brainmob.key = parent_mob.key // Set the key to the brainmob + held_mob = brain.brainmob + + if(!istype(held_mob) || QDELETED(held_mob)) + CRASH("/datum/component/construct_held_body/proc/_drop_body attempted to drop a body despite having no body, or a qdeleted body") + + parent_mob.mind.transfer_to(held_mob) // Transfer the mind to the original mob + // goodbye construct antag datums! + held_mob.mind.remove_antag_datum(/datum/antagonist/cultist, silent_removal = TRUE) + held_mob.mind.remove_antag_datum(/datum/antagonist/wizard/construct, silent_removal = TRUE) + held_body = null + qdel(src) // our job here is done diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 9abedd63a0d4..01e94bffe157 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -537,7 +537,10 @@ /client/proc/view_var_Topic(href, href_list, hsrc) //This should all be moved over to datum/admins/Topic() or something ~Carn - if(!check_rights(R_ADMIN|R_MOD, FALSE) && !((href_list["datumrefresh"] || href_list["Vars"] || href_list["VarsList"]) && check_rights(R_VIEWRUNTIMES, FALSE))) + if(!check_rights(R_ADMIN|R_MOD, FALSE) \ + && !((href_list["datumrefresh"] || href_list["Vars"] || href_list["VarsList"]) && check_rights(R_VIEWRUNTIMES, FALSE)) \ + && !((href_list["proc_call"]) && check_rights(R_PROCCALL, FALSE)) \ + ) return // clients with R_VIEWRUNTIMES can still refresh the window/view references/view lists. they cannot edit anything else however. if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings diff --git a/code/datums/spell.dm b/code/datums/spell.dm index cc6a40a91ad5..5d5aaba3c1a2 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -65,8 +65,6 @@ GLOBAL_LIST_INIT(spells, typesof(/datum/spell)) /datum/spell var/name = "Spell" // Only rename this if the spell you're making is not abstract var/desc = "A wizard spell" - var/panel = "Spells"//What panel the proc holder needs to go on. - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? ///recharge time in deciseconds var/base_cooldown = 10 SECONDS diff --git a/code/datums/spell_cooldown/spell_charges.dm b/code/datums/spell_cooldown/spell_charges.dm index 577da557060e..47803609ab25 100644 --- a/code/datums/spell_cooldown/spell_charges.dm +++ b/code/datums/spell_cooldown/spell_charges.dm @@ -49,7 +49,7 @@ ..() charge_time = world.time -/datum/spell_cooldown/charges/statpanel_info() +/datum/spell_cooldown/charges/cooldown_info() var/charge_string = charge_duration != 0 ? round(min(1, (charge_duration - (charge_time - world.time)) / charge_duration), 0.01) * 100 : 100 // need this for possible 0 charge duration var/recharge_string = recharge_duration != 0 ? round(min(1, (recharge_duration - (recharge_time - world.time)) / recharge_duration), 0.01) * 100 : 100 return "[charge_string != 100 ? "[charge_string]%\n" : ""][recharge_string != 100 ? "[recharge_string]%\n" : ""][current_charges]/[max_charges]" diff --git a/code/datums/spell_cooldown/spell_cooldown.dm b/code/datums/spell_cooldown/spell_cooldown.dm index 14936e893ac3..9ac6dbf4520f 100644 --- a/code/datums/spell_cooldown/spell_cooldown.dm +++ b/code/datums/spell_cooldown/spell_cooldown.dm @@ -64,5 +64,5 @@ /datum/spell_cooldown/proc/revert_cast() recharge_time = world.time -/datum/spell_cooldown/proc/statpanel_info() +/datum/spell_cooldown/proc/cooldown_info() return "[round(get_availability_percentage(), 0.01) * 100]%" diff --git a/code/datums/spells/bloodcrawl.dm b/code/datums/spells/bloodcrawl.dm index b54ca987de37..e5c99fac868a 100644 --- a/code/datums/spells/bloodcrawl.dm +++ b/code/datums/spells/bloodcrawl.dm @@ -8,7 +8,6 @@ overlay = null action_icon_state = "bloodcrawl" action_background_icon_state = "bg_demon" - panel = "Demon" var/allowed_type = /obj/effect/decal/cleanable var/phased = FALSE diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index 9fe9431dc8d4..5f483b4edf83 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -2,7 +2,6 @@ name = "Invisible Wall" desc = "The mime's performance transmutates into physical reality." school = "mime" - panel = "Mime" summon_type = list(/obj/structure/forcefield/mime) invocation_type = "emote" invocation_emote_self = "You form a wall in front of yourself." @@ -32,7 +31,6 @@ name = "Speech" desc = "Make or break a vow of silence." school = "mime" - panel = "Mime" clothes_req = FALSE base_cooldown = 5 MINUTES human_req = TRUE @@ -66,7 +64,6 @@ name = "Invisible Greater Wall" desc = "Form an invisible three tile wide blockade." school = "mime" - panel = "Mime" wall_type = /obj/effect/forcefield/mime/advanced invocation_type = "emote" invocation_emote_self = "You form a blockade in front of yourself." @@ -91,7 +88,6 @@ name = "Finger Gun" desc = "Shoot lethal, silencing bullets out of your fingers! 3 bullets available per cast. Use your fingers to holster them manually." school = "mime" - panel = "Mime" clothes_req = FALSE base_cooldown = 30 SECONDS human_req = TRUE diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 646b40e4d26f..38f94e4ce892 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -62,6 +62,11 @@ id = "shadow_mend" duration = 3 SECONDS alert_type = /atom/movable/screen/alert/status_effect/shadow_mend + /// If this is true, the status effect will try to apply the debuff to others, rather than the user + var/devil = FALSE + +/datum/status_effect/shadow_mend/devil + devil = TRUE /atom/movable/screen/alert/status_effect/shadow_mend name = "Shadow Mend" @@ -78,10 +83,25 @@ owner.adjustFireLoss(-15) /datum/status_effect/shadow_mend/on_remove() - owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...") - playsound(owner, 'sound/magic/teleport_diss.ogg', 50, 1) - owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE) + if(!devil) + owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...") + playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE) + owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE) + return + var/found_someone = FALSE + + for(var/mob/living/L in oview(9, owner)) + found_someone = TRUE + playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE) + L.Beam(owner, "grabber_beam", time = 1 SECONDS, maxdistance = 9) + L.apply_status_effect(STATUS_EFFECT_VOID_PRICE) + if(found_someone) + owner.visible_message("The violet light around [owner] glows black... and shoots off to those around [owner.p_them()]!", "The tendrils around you cinch tightly... but then unwravel and fly at others!") + else + owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...") + playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE) + owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE) /datum/status_effect/void_price id = "void_price" @@ -110,6 +130,13 @@ tick_interval = 0 alert_type = /atom/movable/screen/alert/status_effect/blooddrunk var/blooddrunk_damage_mod_remove = 4 // Damage is multiplied by this at the end of the status effect. Modify this one, it changes the _add + /// If this is the chariot subtype, which grants pacifism while the effect is active. + var/chariot = FALSE + +/datum/status_effect/blooddrunk/chariot + duration = 10 SECONDS + chariot = TRUE + blooddrunk_damage_mod_remove = 6 /atom/movable/screen/alert/status_effect/blooddrunk name = "Blood-Drunk" @@ -120,6 +147,8 @@ . = ..() if(.) ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "blooddrunk") + if(chariot) + ADD_TRAIT(owner, TRAIT_PACIFISM, "blooddrunk") if(ishuman(owner)) var/mob/living/carbon/human/H = owner var/blooddrunk_damage_mod_add = 1 / blooddrunk_damage_mod_remove // Damage is multiplied by this at the start of the status effect. Don't modify this one directly. @@ -144,6 +173,7 @@ H.physiology.stamina_mod *= blooddrunk_damage_mod_remove add_attack_logs(owner, owner, "lost blood-drunk stun immunity", ATKLOG_ALL) REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "blooddrunk") + REMOVE_TRAIT(owner, TRAIT_PACIFISM, "blooddrunk") if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"]) owner.remove_stun_absorption("blooddrunk") @@ -183,7 +213,7 @@ H.physiology.stamina_mod *= 0.5 H.physiology.stun_mod *= 0.5 var/datum/antagonist/vampire/V = owner.mind.has_antag_datum(/datum/antagonist/vampire) - if(V.get_ability(/datum/vampire_passive/blood_swell_upgrade)) + if(V?.get_ability(/datum/vampire_passive/blood_swell_upgrade)) bonus_damage_applied = TRUE H.physiology.melee_bonus += 10 H.dna.species.punchstunthreshold += 8 //higher chance to stun but not 100% @@ -776,3 +806,70 @@ var/mob/living/carbon/human/H = owner H.physiology.stamina_mod /= 0.75 add_attack_logs(owner, owner, "lost bearserker rage resistances", ATKLOG_ALL) + +/datum/status_effect/xray + id = "xray" + alert_type = null + status_type = STATUS_EFFECT_REFRESH + duration = 2 MINUTES + tick_interval = 0 + +/datum/status_effect/xray/on_apply() + . = ..() + ADD_TRAIT(owner, TRAIT_XRAY_VISION, "XRAY_BUFF") + ADD_TRAIT(owner, TRAIT_NIGHT_VISION, "XRAY_BUFF") + owner.update_sight() + +/datum/status_effect/xray/on_remove() + . = ..() + REMOVE_TRAIT(owner, TRAIT_XRAY_VISION, "XRAY_BUFF") + REMOVE_TRAIT(owner, TRAIT_NIGHT_VISION, "XRAY_BUFF") + owner.update_sight() + +/datum/status_effect/badass + id = "badass" + alert_type = null + status_type = STATUS_EFFECT_REFRESH + duration = 2 MINUTES + tick_interval = 0 + +/datum/status_effect/badass/on_apply() + . = ..() + ADD_TRAIT(owner, TRAIT_BADASS, "BADDASS_BUFF") + +/datum/status_effect/badass/on_remove() + . = ..() + REMOVE_TRAIT(owner, TRAIT_BADASS, "BADDASS_BUFF") + +/datum/status_effect/reversed_sun + id = "reversed_sun" + alert_type = null + status_type = STATUS_EFFECT_REFRESH + duration = 1 MINUTES + tick_interval = 0.2 SECONDS + +/datum/status_effect/reversed_sun/on_apply() + . = ..() + owner.become_nearsighted("REVERSED_SUN") + ADD_TRAIT(owner, TRAIT_NIGHT_VISION, "REVERSED_SUN") + owner.update_sight() + owner.set_light(7, -5, "#ddd6cf") + +/datum/status_effect/reversed_sun/on_remove() + . = ..() + owner.remove_light() + owner.cure_nearsighted("REVERSED_SUN") + REMOVE_TRAIT(owner, TRAIT_NIGHT_VISION, "REVERSED_SUN") + owner.update_sight() + +/datum/status_effect/reversed_sun/tick() + for(var/atom/movable/AM in oview(8, owner)) + if(isliving(AM)) + var/mob/living/L = AM + if(L.affects_vampire(owner)) + L.adjust_bodytemperature(-1.5 * TEMPERATURE_DAMAGE_COEFFICIENT) + continue + if(istype(AM, /obj/item/projectile)) + var/obj/item/projectile/P = AM + if(P.flag == ENERGY || P.flag == LASER) + P.damage *= 0.85 diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 9edf7eb42ad7..e65da3535cd2 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -1322,3 +1322,29 @@ desc = "Real winners quit before they reach the ultimate prize." #undef DEFAULT_MAX_CURSE_COUNT + +/datum/status_effect/reversed_high_priestess + id = "reversed_high_priestess" + duration = 1 MINUTES + status_type = STATUS_EFFECT_REFRESH + tick_interval = 6 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/bubblegum_curse + +/datum/status_effect/reversed_high_priestess/tick() + . = ..() + new /obj/effect/bubblegum_warning(get_turf(owner)) + +/obj/effect/bubblegum_warning + name = "bloody rift" + desc = "You feel like even being *near* this is a bad idea" + icon = 'icons/obj/biomass.dmi' + icon_state = "rift" + color = "red" + +/obj/effect/bubblegum_warning/Initialize() + . = ..() + addtimer(CALLBACK(src, PROC_REF(slap_someone)), 2.5 SECONDS) //A chance to run away + +/obj/effect/bubblegum_warning/proc/slap_someone() + new /obj/effect/abstract/bubblegum_rend_helper(get_turf(src), null, 10) + qdel(src) diff --git a/code/datums/status_effects/magic_disguise.dm b/code/datums/status_effects/magic_disguise.dm index 97a3640de2b3..4a0ed8dceea5 100644 --- a/code/datums/status_effects/magic_disguise.dm +++ b/code/datums/status_effects/magic_disguise.dm @@ -4,6 +4,7 @@ tick_interval = -1 alert_type = /atom/movable/screen/alert/status_effect/magic_disguise status_type = STATUS_EFFECT_REPLACE + var/mob/living/disguise_mob var/datum/icon_snapshot/disguise /atom/movable/screen/alert/status_effect/magic_disguise @@ -12,27 +13,26 @@ icon = 'icons/mob/actions/actions.dmi' icon_state = "chameleon_outfit" -/datum/status_effect/magic_disguise/on_creation(mob/living/new_owner, mob/living/disguise_mob) +/datum/status_effect/magic_disguise/on_creation(mob/living/new_owner, mob/living/_disguise_mob) . = ..() - if(!ishuman(new_owner)) + disguise_mob = _disguise_mob + +/datum/status_effect/magic_disguise/on_apply() + . = ..() + if(!ishuman(owner)) return FALSE if(!disguise_mob) disguise_mob = select_disguise() - if(disguise_mob && ishuman(disguise_mob)) + if(ishuman(disguise_mob)) create_disguise(disguise_mob) if(disguise) - apply_disguise(new_owner) - return TRUE + apply_disguise(owner) else to_chat(owner, "Your spell fails to find a disguise!") return FALSE -/datum/status_effect/magic_disguise/on_apply() - . = ..() - if(!ishuman(owner)) - return FALSE - RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(remove_disguise)) + return TRUE /datum/status_effect/magic_disguise/on_remove() owner.regenerate_icons() @@ -74,6 +74,7 @@ H.overlays = disguise.overlays H.update_inv_r_hand() H.update_inv_l_hand() + SEND_SIGNAL(H, COMSIG_CARBON_REGENERATE_ICONS) to_chat(H, "You disguise yourself as [disguise.name].") /datum/status_effect/magic_disguise/proc/remove_disguise() diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm index db79ccb1a6c5..ea331744b392 100644 --- a/code/datums/uplink_items/uplink_traitor.dm +++ b/code/datums/uplink_items/uplink_traitor.dm @@ -305,6 +305,17 @@ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) job = list("Head of Personnel", "Quartermaster", "Cargo Technician", "Librarian", "Coroner", "Psychiatrist", "Virologist") +// Tarot card generator, librarian and Chaplain. + +/datum/uplink_item/jobspecific/tarot_generator + name = "Enchanted tarot card deck" + desc = "A magic tarot card deck \"borrowed\" from a Wizard federation storage unit. \ + Capable of producing magic tarot cards of the 22 major arcana, and their reversed versions. Each card has a different effect. \ + Throw the card at someone to use it on them, or use it in hand to apply it to yourself. Unlimited uses, 25 second cooldown, can have up to 3 cards in the world." + reference = "tarot" + item = /obj/item/tarot_generator + cost = 55 //This can do a lot of stuff, but is quite random. As such, higher price. + job = list("Chaplain", "Librarian") //--------------------------// // Species Restricted Gear // diff --git a/code/game/dna/mutations/disabilities.dm b/code/game/dna/mutations/disabilities.dm index c0140fcb7cac..610a513ab930 100644 --- a/code/game/dna/mutations/disabilities.dm +++ b/code/game/dna/mutations/disabilities.dm @@ -490,7 +490,6 @@ /datum/spell/immolate name = "Incendiary Mitochondria" desc = "The subject becomes able to convert excess cellular energy into thermal energy." - panel = "Abilities" base_cooldown = 600 diff --git a/code/game/dna/mutations/mutation_powers.dm b/code/game/dna/mutations/mutation_powers.dm index 463b32757867..0c974c59d64b 100644 --- a/code/game/dna/mutations/mutation_powers.dm +++ b/code/game/dna/mutations/mutation_powers.dm @@ -277,7 +277,6 @@ /datum/spell/cryokinesis name = "Cryokinesis" desc = "Drops the bodytemperature of another person." - panel = "Abilities" base_cooldown = 1200 @@ -348,7 +347,6 @@ /datum/spell/eat name = "Eat" desc = "Eat just about anything!" - panel = "Abilities" base_cooldown = 300 @@ -471,8 +469,6 @@ /datum/spell/leap name = "Jump" desc = "Leap great distances!" - panel = "Abilities" - base_cooldown = 60 clothes_req = FALSE @@ -564,7 +560,6 @@ /datum/spell/polymorph name = "Polymorph" desc = "Mimic the appearance of others!" - panel = "Abilities" base_cooldown = 1800 clothes_req = FALSE @@ -726,7 +721,6 @@ /datum/spell/morph name = "Morph" desc = "Mimic the appearance of your choice!" - panel = "Abilities" base_cooldown = 1800 clothes_req = FALSE diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 481d2dbfc02c..e298a454a269 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -318,7 +318,7 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list( selection_title = "Archives" creation_message = "You invoke the dark magic of the tomes creating a %ITEM%!" choosable_items = list("Shuttle Curse" = /obj/item/shuttle_curse, "Zealot's Blindfold" = /obj/item/clothing/glasses/hud/health/night/cultblind, - "Veil Shifter" = /obj/item/cult_shift, "Reality sunderer" = /obj/item/portal_amulet) //Add void torch to veil shifter spawn + "Veil Shifter" = /obj/item/cult_shift, "Reality sunderer" = /obj/item/portal_amulet, "Blank Tarot Card" = /obj/item/blank_tarot_card) /obj/structure/cult/functional/archives/Initialize(mapload) . = ..() diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index f6181fe104b2..81e111ea154a 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -71,6 +71,7 @@ M.overlays = disguise.overlays M.update_inv_r_hand() M.update_inv_l_hand() + SEND_SIGNAL(M, COMSIG_CARBON_REGENERATE_ICONS) /obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth() if(!stealth_active) diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm index 5dc9a30ce30f..7edd0bf893ee 100644 --- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm +++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm @@ -187,8 +187,6 @@ action_background_icon_state = "shadow_demon_bg" action_icon_state = "shadow_grapple" - panel = "Demon" - sound = null invocation_type = "none" invocation = null diff --git a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm b/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm index 3812df84a3ab..d2595c8634fc 100644 --- a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm +++ b/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm @@ -111,7 +111,6 @@ overlay = null action_icon_state = "bloodcrawl" action_background_icon_state = "bg_cult" - panel = "Demon" /datum/spell/sense_victims/create_new_targeting() return new /datum/spell_targeting/alive_mob_list diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm index 8bb7f2a626c9..b7df938982af 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm @@ -9,7 +9,6 @@ #define PD_UPGRADE_MAX_CHARGE "Capacity" /datum/spell/pulse_demon - panel = "Pulse Demon" school = "pulse demon" clothes_req = FALSE action_background_icon_state = "bg_pulsedemon" diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm index 0913180f2ef1..d1962001d880 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm @@ -110,7 +110,6 @@ //Toggle night vision: lets the revenant toggle its night vision /datum/spell/night_vision/revenant base_cooldown = 0 - panel = "Revenant Abilities" message = "You toggle your night vision." action_icon_state = "r_nightvision" action_background_icon_state = "bg_revenant" @@ -119,7 +118,6 @@ /datum/spell/revenant_transmit name = "Transmit" desc = "Telepathically transmits a message to the target." - panel = "Revenant Abilities" base_cooldown = 0 clothes_req = FALSE action_icon_state = "r_transmit" @@ -145,7 +143,6 @@ name = "Spell" clothes_req = FALSE action_background_icon_state = "bg_revenant" - panel = "Revenant Abilities (Locked)" /// How long it reveals the revenant in deciseconds var/reveal = 8 SECONDS /// How long it stuns the revenant in deciseconds @@ -192,7 +189,6 @@ return FALSE name = "[initial(name)] ([cast_amount]E)" to_chat(user, "You have unlocked [initial(name)]!") - panel = "Revenant Abilities" locked = FALSE cooldown_handler.revert_cast() return FALSE diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm new file mode 100644 index 000000000000..e3efcfa81286 --- /dev/null +++ b/code/game/gamemodes/wizard/magic_tarot.dm @@ -0,0 +1,885 @@ +/obj/item/tarot_generator + name = "Enchanted tarot card deck" + desc = "This tarot card box has quite the array of runes and artwork on it." + icon = 'icons/obj/playing_cards.dmi' + icon_state = "tarot_box" + w_class = WEIGHT_CLASS_SMALL + /// What is the maximum number of cards the tarot generator can have in the world at a time? + var/maximum_cards = 3 + /// List of cards we have created, to check against maximum, and so we can purge them from the pack. + var/list/our_card_list = list() + ///How long the cooldown is each time we draw a card before we can draw another? + var/our_card_cooldown_time = 25 SECONDS + COOLDOWN_DECLARE(card_cooldown) + +/obj/item/tarot_generator/wizard + maximum_cards = 5 + our_card_cooldown_time = 12 SECONDS // A minute for a full hand of 5 cards + +/obj/item/tarot_generator/attack_self(mob/user) + if(!COOLDOWN_FINISHED(src, card_cooldown)) + to_chat(user, "[src]'s magic is still recovering from the last card, wait [round(COOLDOWN_TIMELEFT(src, card_cooldown) / 10)] more second\s!") + return + if(length(our_card_list) >= maximum_cards) + to_chat(user, "[src]'s magic can only support up to [maximum_cards] in the world at once, use or destroy some!") + return + var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src), src) + our_card_list += MTC + user.put_in_hands(MTC) + to_chat(user, "You draw [MTC.name]... [MTC.card_desc]") //No period on purpose. + COOLDOWN_START(src, card_cooldown, our_card_cooldown_time) + +/obj/item/tarot_generator/examine(mob/user) + . = ..() + . += "Alt-Shift-Click to destroy all cards it has produced." + . += "It has [length(our_card_list)] card\s in the world right now." + if(!COOLDOWN_FINISHED(src, card_cooldown)) + . += "You may draw another card again in [round(COOLDOWN_TIMELEFT(src, card_cooldown) / 10)] second\s." + +/obj/item/tarot_generator/AltShiftClick(mob/user) + for(var/obj/item/magic_tarot_card/MTC in our_card_list) + MTC.dust() + to_chat(user, "You dispell the cards [src] had created.") + +// Booster packs filled with 3, 5, or 7 playing cards! Used by the wizard space ruin, or rarely in lavaland tendril chests. +/obj/item/tarot_card_pack + name = "\improper Enchanted Arcana Pack" + desc = "A pack of 3 Enchanted tarot cards. Collect them all!" + icon = 'icons/obj/playing_cards.dmi' + icon_state = "pack" + ///How many cards in a pack. 3 in base, 5 in jumbo, 7 in mega + var/cards = 3 + +/obj/item/tarot_card_pack/attack_self(mob/user) + user.visible_message("[user] tears open [src].", \ + "You tear open [src]!") + playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE) + for(var/i in 1 to cards) + new /obj/item/magic_tarot_card(get_turf(src)) + qdel(src) + +/obj/item/tarot_card_pack/jumbo + name = "\improper Jumbo Arcana Pack" + desc = "A Jumbo card pack from your friend Jimbo!" + icon_state = "jumbopack" + cards = 5 + +/obj/item/tarot_card_pack/mega + name = "\improper MEGA Arcana Pack" + desc = "Sadly, you won't find a Joker for an angel room, or a Soul card in here either." + icon_state = "megapack" + cards = 7 + +// Blank tarot cards. Made by the cult, however also good for space ruins potentially, where one feels a card pack would be too much? +/obj/item/blank_tarot_card + name = "blank tarot card" + desc = "A blank tarot card." + icon = 'icons/obj/playing_cards.dmi' + icon_state = "tarot_blank" + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 10 + throwforce = 0 + force = 0 + resistance_flags = FLAMMABLE + /// If a person can choose what the card produces. No cost if they can choose. + var/let_people_choose = FALSE + +/obj/item/blank_tarot_card/examine(mob/user) + . = ..() + if(!let_people_choose) + . += "With a bit of Ink, a work of art could be created. Will you provide your Ink?" + else + . += "We have the Ink... Could you provide your Vision instead?" + +/obj/item/blank_tarot_card/attack_self(mob/user) + if(!ishuman(user)) + return + if(!let_people_choose) + var/mob/living/carbon/human/H = user + if(H.dna && (NO_BLOOD in H.dna.species.species_traits)) + to_chat(user, "No blood to provide?... Then no Ink for the art...") + return + if(H.blood_volume <= 100) //Shouldn't happen, they should be dead, but failsafe. Not bleeding as then they could recover the blood with blood rites + return + H.blood_volume -= 100 + H.drop_item() + var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src)) + user.put_in_hands(MTC) + to_chat(user, "Your blood flows into [src]... And your Ink makes a work of art! [MTC.name]... [MTC.card_desc]") //No period on purpose. + qdel(src) + return + var/tarot_type + var/tarot_name + var/list/card_by_name = list() + for(var/T in subtypesof(/datum/tarot) - /datum/tarot/reversed) + var/datum/tarot/temp = T + card_by_name[temp.name] = T + + tarot_name = tgui_input_list(user, "Choose the Work of Art to create.", "Art Creation", card_by_name) + tarot_type = card_by_name[tarot_name] + if(tarot_type) + user.drop_item() + var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src), null, tarot_type) + user.put_in_hands(MTC) + to_chat(user, "You put your Vision into [src], and your Vision makes a work of Art! [MTC.name]... [MTC.card_desc]") //No period on purpose. + qdel(src) + +/obj/item/blank_tarot_card/choose //For admins mainly, to spawn a specific tarot card. Not recommended for ruins. + let_people_choose = TRUE + +/obj/item/magic_tarot_card + name = "XXII - The Unknown" + desc = "A beautiful tarot card. However, it feels like... more?" + icon = 'icons/obj/playing_cards.dmi' + icon_state = "tarot_the_unknown" + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 10 + throwforce = 0 + force = 0 + resistance_flags = FLAMMABLE + /// The deck that created us. Notifies it we have been deleted on use. + var/obj/item/tarot_generator/creator_deck + /// Our magic tarot card datum that lets the tarot card do stuff on use, or hitting someone + var/datum/tarot/our_tarot + /// Our fancy description given to use by the tarot datum. + var/card_desc = "Untold answers... wait what? This is a bug, report this as an issue on github!" + ///Is the card face down? Shows the card back, hides the examine / name. + var/face_down = FALSE + +/obj/item/magic_tarot_card/Initialize(mapload, obj/item/tarot_generator/source, datum/tarot/chosen_tarot) + . = ..() + if(source) + creator_deck = source + if(chosen_tarot) + our_tarot = new chosen_tarot + if(!istype(our_tarot)) + var/tarotpath = pick(subtypesof(/datum/tarot) - /datum/tarot/reversed) + our_tarot = new tarotpath + name = our_tarot.name + card_desc = our_tarot.desc + icon_state = "tarot_[our_tarot.card_icon]" + +/obj/item/magic_tarot_card/Destroy() + if(creator_deck) + creator_deck.our_card_list -= src + return ..() + +/obj/item/magic_tarot_card/examine(mob/user) + . = ..() + if(!face_down) + . += "[card_desc]" + . += "Alt-Shift-Click to flip the card over." + +/obj/item/magic_tarot_card/attack_self(mob/user) + if(our_tarot) + INVOKE_ASYNC(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), user) + poof() + +/obj/item/magic_tarot_card/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable) + if(face_down) + flip() + . = ..() + +/obj/item/magic_tarot_card/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(isliving(hit_atom) && our_tarot) + INVOKE_ASYNC(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), hit_atom) + poof() + +/obj/item/magic_tarot_card/AltShiftClick(mob/user) + flip() + +/obj/item/magic_tarot_card/proc/flip() + if(!face_down) + icon_state = "cardback[our_tarot.reversed ? "?" : ""]" + name = "Enchanted tarot card" + face_down = TRUE + else + name = our_tarot.name + icon_state = "tarot_[our_tarot.card_icon]" + face_down = FALSE + +/obj/item/magic_tarot_card/proc/poof() + new /obj/effect/temp_visual/revenant(get_turf(src)) + qdel(src) + +/obj/item/magic_tarot_card/proc/dust() + visible_message("[src] disintegrates into dust!") + new /obj/effect/temp_visual/revenant(get_turf(src)) + qdel(src) + +/datum/tarot + /// Name used for the card + var/name = "XXII - The Unknown." + /// Desc used for the card description of the card + var/desc = "Untold answers... wait what? This is a bug, report this as an issue on github!" + /// What icon is used for the card? + var/card_icon = "the_unknown" + /// Are we reversed? Used for the card back. + var/reversed = FALSE + +/datum/tarot/proc/activate(mob/living/target) + stack_trace("A bugged tarot card was spawned and used. Please make an issue report! Type was [src.type]") + +/datum/tarot/reversed + name = "XXII - The Unknown?" + desc = "Untold answers... wait what? This is a bug, report this as an issue on github! This one was a reversed arcana!" + card_icon = "the_unknown?" + reversed = TRUE + +/datum/tarot/the_fool + name = "0 - The Fool" + desc = "Where journey begins." + card_icon = "the_fool" + +/datum/tarot/the_fool/activate(mob/living/target) + target.forceMove(pick(GLOB.latejoin)) + to_chat(target, "You are abruptly pulled through space!") + +/datum/tarot/the_magician + name = "I - The Magician" + desc = "May you never miss your goal." + card_icon = "the_magician" + +/datum/tarot/the_magician/activate(mob/living/target) + target.apply_status_effect(STATUS_EFFECT_BADASS) + to_chat(target, "You feel badass.") + +/datum/tarot/the_high_priestess + name = "II - The High Priestess" + desc = "Mother is watching you." + card_icon = "the_high_priestess" + +/datum/tarot/the_high_priestess/activate(mob/living/target) + new /obj/effect/abstract/bubblegum_rend_helper(get_turf(target), target, 20) + +/obj/effect/abstract/bubblegum_rend_helper + name = "bubblegum_rend_helper" + +/obj/effect/abstract/bubblegum_rend_helper/Initialize(mapload, mob/living/owner, damage) + . = ..() + INVOKE_ASYNC(src, PROC_REF(rend), owner, damage) + +/obj/effect/abstract/bubblegum_rend_helper/proc/rend(mob/living/owner, damage) + if(!owner) + for(var/mob/living/L in shuffle(view(9, src))) + owner = L + break + owner.Immobilize(3 SECONDS) + for(var/i in 1 to 3) + var/turf/first_turf = get_turf(owner) + new /obj/effect/decal/cleanable/blood/bubblegum(first_turf) + if(prob(50)) + new /obj/effect/temp_visual/bubblegum_hands/rightsmack(first_turf) + else + new /obj/effect/temp_visual/bubblegum_hands/leftsmack(first_turf) + sleep(6) + var/turf/second_turf = get_turf(owner) + to_chat(owner, "Something huge rends you!") + playsound(second_turf, 'sound/misc/demon_attack1.ogg', 100, TRUE, -1) + owner.adjustBruteLoss(damage) + qdel(src) + +/datum/tarot/the_empress + name = "III - The Empress" + desc = "May your rage bring power." + card_icon = "the_empress" + +/datum/tarot/the_empress/activate(mob/living/target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + H.reagents.add_reagent("mephedrone", 4.5) + H.reagents.add_reagent("mitocholide", 12) + +/datum/tarot/the_emperor + name = "IV - The Emperor" + desc = "Challenge me!" + card_icon = "the_emperor" + +/datum/tarot/the_emperor/activate(mob/living/target) + var/list/L = list() + for(var/turf/T in get_area_turfs(/area/station/command/bridge)) + if(is_blocked_turf(T)) + continue + L.Add(T) + + if(!length(L)) + to_chat(target, "Huh. No bridge? Well, that sucks.") + return + + target.forceMove(pick(L)) + to_chat(target, "You are abruptly pulled through space!") + +/datum/tarot/the_hierophant + name = "V - The Hierophant" + desc = "Two prayers for the lost." + card_icon = "the_hierophant" + +/datum/tarot/the_hierophant/activate(mob/living/target) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + if(!H.wear_suit) + return + H.wear_suit.setup_hierophant_shielding() + H.update_appearance(UPDATE_ICON) + +/datum/tarot/the_lovers + name = "VI - The Lovers" + desc = "May you prosper and be in good health." + card_icon = "the_lovers" + +/datum/tarot/the_lovers/activate(mob/living/target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + H.adjustBruteLoss(-40, robotic = TRUE) + H.adjustFireLoss(-40, robotic = TRUE) + H.blood_volume = min(H.blood_volume + 100, BLOOD_VOLUME_NORMAL) + else + target.adjustBruteLoss(-40) + target.adjustFireLoss(-40) + target.adjustOxyLoss(-40) + target.adjustToxLoss(-40) + +/datum/tarot/the_chariot + name = "VII - The Chariot" + desc = "May nothing stand before you." + card_icon = "the_chariot" + +/datum/tarot/the_chariot/activate(mob/living/target) + target.apply_status_effect(STATUS_EFFECT_BLOOD_RUSH) + target.apply_status_effect(STATUS_EFFECT_BLOODDRUNK_CHARIOT) + +/datum/tarot/justice + name = "VIII - Justice" + desc = "May your future become balanced." + card_icon = "justice" + +/datum/tarot/justice/activate(mob/living/target) + var/turf/target_turf = get_turf(target) + new /obj/item/storage/firstaid/regular(target_turf) + new /obj/item/grenade/chem_grenade/waterpotassium(target_turf) + new /obj/item/card/emag/magic_key(target_turf) + new /obj/item/stack/spacecash/c100(target_turf) + +/datum/tarot/the_hermit + name = "IX - The Hermit" + desc = "May you see what life has to offer." + card_icon = "the_hermit" + +/datum/tarot/the_hermit/activate(mob/living/target) + var/list/viable_vendors = list() + for(var/obj/machinery/economy/vending/candidate in GLOB.machines) + if(!is_station_level(candidate.z)) + continue + viable_vendors += candidate + + if(!length(viable_vendors)) + to_chat(target, "No vending machines? Well, with luck cargo will have something to offer. If you go there yourself.") + return + + target.forceMove(get_turf(pick(viable_vendors))) + to_chat(target, "You are abruptly pulled through space!") + +/datum/tarot/wheel_of_fortune + name = "X - Wheel of Fortune" + desc = "Spin the wheel of destiny." + card_icon = "wheel_of_fortune" + +/datum/tarot/wheel_of_fortune/activate(mob/living/target) + var/list/static/bad_vendors = list( + /obj/machinery/economy/vending/liberationstation, + /obj/machinery/economy/vending/toyliberationstation, + /obj/machinery/economy/vending/wallmed + ) + var/turf/target_turf = get_turf(target) + var/vendorpath = pick(subtypesof(/obj/machinery/economy/vending) - bad_vendors) + new vendorpath(target_turf) + +/datum/tarot/strength + name = "XI - Strength" + desc = "May your power bring rage." + card_icon = "strength" + +/datum/tarot/strength/activate(mob/living/target) + target.apply_status_effect(STATUS_EFFECT_VAMPIRE_GLADIATOR) + target.apply_status_effect(STATUS_EFFECT_BLOOD_SWELL) + +/datum/tarot/the_hanged_man + name = "XII - The Hanged Man" + desc = "May you find enlightenment." + card_icon = "the_hanged_man" + +/datum/tarot/the_hanged_man/activate(mob/living/target) + if(target.flying) + return + target.flying = TRUE + addtimer(VARSET_CALLBACK(target, flying, FALSE), 60 SECONDS) + +/datum/tarot/death + name = "XIII - Death" + desc = "Lay waste to all that oppose you." + card_icon = "death" + +/datum/tarot/death/activate(mob/living/target) + for(var/mob/living/L in oview(9, target)) + L.adjustBruteLoss(20) + L.adjustFireLoss(20) + +/datum/tarot/temperance + name = "XIV - Temperance" + desc = "May you be pure in heart." + card_icon = "temperance" + +/datum/tarot/temperance/activate(mob/living/target) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + var/obj/item/organ/internal/body_egg/egg = H.get_int_organ(/obj/item/organ/internal/body_egg) + if(egg) + egg.remove(H) + H.vomit() + egg.forceMove(get_turf(H)) + H.reagents.add_reagent("mutadone", 1) + for(var/obj/item/organ/internal/I in H.internal_organs) + I.heal_internal_damage(60) + H.apply_status_effect(STATUS_EFFECT_PANACEA) + for(var/thing in H.viruses) + var/datum/disease/D = thing + if(D.severity == NONTHREAT) + continue + D.cure() + +/datum/tarot/the_devil + name = "XV - The Devil" + desc = "Revel in the power of darkness." + card_icon = "the_devil" + +/datum/tarot/the_devil/activate(mob/living/target) + target.apply_status_effect(STATUS_EFFECT_SHADOW_MEND_DEVIL) + +/datum/tarot/the_tower + name = "XVI - The Tower" + desc = "Destruction brings creation." + card_icon = "the_tower" + +/datum/tarot/the_tower/activate(mob/living/target) + var/obj/item/grenade/clusterbuster/ied/bakoom = new(get_turf(target)) + bakoom.prime() + +/// I'm sorry matt, this is very funny. +/datum/tarot/the_stars + name = "XVII - The Stars" + desc = "May you find what you desire." + card_icon = "the_stars" + +/datum/tarot/the_stars/activate(mob/living/target) + var/list/L = list() + for(var/turf/T in get_area_turfs(/area/station/security/evidence)) + if(is_blocked_turf(T)) + continue + L.Add(T) + + if(!length(L)) + to_chat(target, "Huh. No evidence? Well, that means they can't charge you with a crime, right?") + return + + target.forceMove(pick(L)) + to_chat(target, "You are abruptly pulled through space!") + for(var/obj/structure/closet/C in shuffle(view(9, target))) + if(istype(C, /obj/structure/closet/secure_closet)) + var/obj/structure/closet/secure_closet/SC = C + SC.locked = FALSE + C.open() + break //Only open one locker + +/datum/tarot/the_moon + name = "XVIII - The Moon" + desc = "May you find all you have lost." + card_icon = "the_moon" + +/datum/tarot/the_moon/activate(mob/living/target) + var/list/funny_ruin_list = list() + var/turf/target_turf = get_turf(target) + for(var/I in GLOB.ruin_landmarks) + var/obj/effect/landmark/ruin/ruin_landmark = I + if(ruin_landmark.z == target_turf.z) + funny_ruin_list += ruin_landmark + + if(length(funny_ruin_list)) + var/turf/T = get_turf(pick(funny_ruin_list)) + target.forceMove(T) + to_chat(target, "You are abruptly pulled through space!") + T.ChangeTurf(/turf/simulated/floor/plating) //we give them plating so they are not trapped in a wall, and a pickaxe to avoid being trapped in a wall + new /obj/item/pickaxe/emergency(T) + target.update_parallax_contents() + return + //We did not find a ruin on the same level. Well. I hope you have a space suit, but we'll go space ruins as they are mostly sorta kinda safer. + for(var/I in GLOB.ruin_landmarks) + var/obj/effect/landmark/ruin/ruin_landmark = I + if(!is_mining_level(ruin_landmark.z)) + funny_ruin_list += ruin_landmark + + if(!length(funny_ruin_list)) + to_chat(target, "Huh. No space ruins? Well, this card is RUINED!") + + var/turf/T = get_turf(pick(funny_ruin_list)) + target.forceMove(T) + to_chat(target, "You are abruptly pulled through space!") + T.ChangeTurf(/turf/simulated/floor/plating) //we give them plating so they are not trapped in a wall, and a pickaxe to avoid being trapped in a wall + new /obj/item/pickaxe/emergency(T) + target.update_parallax_contents() + return + +/datum/tarot/the_sun + name = "XIX - The Sun" + desc = "May the light heal and enlighten you." + card_icon = "the_sun" + +/datum/tarot/the_sun/activate(mob/living/target) + target.revive() + +/datum/tarot/judgement + name = "XX - Judgement" + desc = "Judge lest ye be judged." + card_icon = "judgement" + +/datum/tarot/judgement/activate(mob/living/target) + notify_ghosts("[target] has used a judgment card. Judge them. Or not, up to you.", enter_link = "(Click to judge)", source = target, action = NOTIFY_FOLLOW) + +/datum/tarot/the_world + name = "XXI - The World" + desc = "Open your eyes and see." + card_icon = "the_world" + +/datum/tarot/the_world/activate(mob/living/target) + var/datum/effect_system/smoke_spread/bad/smoke = new() + smoke.set_up(10, FALSE, target) + smoke.start() + target.apply_status_effect(STATUS_EFFECT_XRAY) + +//////////////////////////////// +////////REVERSED ARCANA///////// +//////////////////////////////// + +/datum/tarot/reversed/the_fool + name = "0 - The Fool?" + desc = "Let go and move on." + card_icon = "the_fool?" + +/datum/tarot/reversed/the_fool/activate(mob/living/target) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + for(var/obj/item/I in H) + if(istype(/obj/item/bio_chip, I)) + continue + H.unEquip(I) + +/datum/tarot/reversed/the_magician + name = "I - The Magician?" + desc = "May no harm come to you." + card_icon = "the_magician?" + +/datum/tarot/reversed/the_magician/activate(mob/living/target) + var/list/thrown_atoms = list() + var/sparkle_path = /obj/effect/temp_visual/gravpush + for(var/turf/T in range(5, target)) //Done this way so things don't get thrown all around hilariously. + for(var/atom/movable/AM in T) + thrown_atoms += AM + + for(var/atom/movable/AM as anything in thrown_atoms) + if(AM == target || AM.anchored || (ismob(AM) && !isliving(AM))) + continue + + var/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(AM, target))) + var/dist_from_user = get_dist(target, AM) + if(dist_from_user == 0) + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(6 SECONDS) + M.adjustBruteLoss(10) + to_chat(M, "You're slammed into the floor by [name]!") + add_attack_logs(target, M, "[M] was thrown by [target]'s [name]", ATKLOG_ALMOSTALL) + else + new sparkle_path(get_turf(AM), get_dir(target, AM)) + if(isliving(AM)) + var/mob/living/M = AM + to_chat(M, "You're thrown back by [name]!") + add_attack_logs(target, M, "[M] was thrown by [target]'s [name]", ATKLOG_ALMOSTALL) + INVOKE_ASYNC(AM, TYPE_PROC_REF(/atom/movable, throw_at), throw_target, ((clamp((3 - (clamp(dist_from_user - 2, 0, dist_from_user))), 3, 3))), 1) //So stuff gets tossed around at the same time. + +/datum/tarot/reversed/the_high_priestess + name = "II - The High Priestess?" + desc = "Run." + card_icon = "the_high_priestess?" + +/datum/tarot/reversed/the_high_priestess/activate(mob/living/target) + target.visible_message("WHO DARES TO TRY TO USE MY POWER IN A CARD?") + target.apply_status_effect(STATUS_EFFECT_REVERSED_HIGH_PRIESTESS) + +/datum/tarot/reversed/the_empress + name = "III - The Empress?" + desc = "May your love bring protection." + card_icon = "the_empress?" + +/datum/tarot/reversed/the_empress/activate(mob/living/target) + for(var/mob/living/L in oview(9, target)) + L.apply_status_effect(STATUS_EFFECT_PACIFIED) + +/datum/tarot/reversed/the_emperor + name = "IV - The Emperor?" + desc = "May you find a worthy opponent." + card_icon = "the_emperor?" + +/datum/tarot/reversed/the_emperor/activate(mob/living/target) + var/list/L = list() + var/list/heads = SSticker.mode.get_all_heads() + for(var/datum/mind/head in heads) + if(ishuman(head.current)) + L.Add(head.current) + + if(!length(L)) + to_chat(target, "Huh. No command members? I hope you didn't kill them all already...") + return + + target.forceMove(get_turf(pick(L))) + to_chat(target, "You are abruptly pulled through space!") + +/datum/tarot/reversed/the_hierophant + name = "V - The Hierophant?" + desc = "Two prayers for the forgotten." + card_icon = "the_hierophant?" + +/datum/tarot/reversed/the_hierophant/activate(mob/living/target) + var/active_chasers = 0 + for(var/mob/living/M in shuffle(orange(7, target))) + if(M.stat == DEAD) //Let us not have dead mobs be used to make a disco inferno. + continue + if(active_chasers >= 2) + return + var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(target), target, M, 1, FALSE) + C.moving = 2 + C.standard_moving_before_recalc = 2 + C.moving_dir = text2dir(pick("NORTH", "SOUTH", "EAST", "WEST")) + active_chasers++ + +/datum/tarot/reversed/the_lovers + name = "VI - The Lovers?" + desc = "May your heart shatter to pieces." + card_icon = "the_lovers?" + +/datum/tarot/reversed/the_lovers/activate(mob/living/target) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + H.apply_damage(20, BRUTE, BODY_ZONE_CHEST) + H.bleed(120) + var/obj/item/organ/external/chest = H.get_organ(BODY_ZONE_CHEST) + chest.fracture() + var/datum/organ/heart/datum_heart = H.get_int_organ_datum(ORGAN_DATUM_HEART) + var/obj/item/organ/internal/our_heart = datum_heart.linked_organ + our_heart.receive_damage(20, TRUE) + +/datum/tarot/reversed/the_chariot + name = "VII - The Chariot?" + desc = "May nothing walk past you." + card_icon = "the_chariot?" + +/datum/tarot/reversed/the_chariot/activate(mob/living/target) + target.Stun(4 SECONDS) + new /obj/structure/closet/statue/indestructible(get_turf(target), target) + +/datum/tarot/reversed/justice + name = "VIII - Justice?" + desc = "May your sins come back to torment you." + card_icon = "justice?" + +/datum/tarot/reversed/justice/activate(mob/living/target) + var/list/static/ignored_supply_pack_types = list( + /datum/supply_packs/abstract, + /datum/supply_packs/abstract/shuttle + ) + var/chosen = pick(SSeconomy.supply_packs - ignored_supply_pack_types) + var/datum/supply_packs/the_pack = new chosen() + var/spawn_location = get_turf(target) + var/obj/structure/closet/crate/crate = the_pack.create_package(spawn_location) + crate.name = "magic [crate.name]" + qdel(the_pack) + +/datum/tarot/reversed/the_hermit + name = "IX - The Hermit?" + desc = "May you see the value of all things in life." + card_icon = "the_hermit?" + +/datum/tarot/reversed/the_hermit/activate(mob/living/target) //Someone can improve this in the future (hopefully comment will not be here in 10 years.) + for(var/obj/item/I in view(7, target)) + if(istype(I, /obj/item/gun)) + new /obj/item/stack/spacecash/c200(get_turf(I)) + qdel(I) + continue + if(istype(I, /obj/item/grenade)) + new /obj/item/stack/spacecash/c50(get_turf(I)) + qdel(I) + if(istype(I, /obj/item/clothing/suit/armor)) + new /obj/item/stack/spacecash/c100(get_turf(I)) + qdel(I) + if(istype(I, /obj/item/melee/baton)) + new /obj/item/stack/spacecash/c100(get_turf(I)) + qdel(I) + +/datum/tarot/reversed/wheel_of_fortune + name = "X - Wheel of Fortune?" + desc = "Throw the dice of fate." + card_icon = "wheel_of_fortune?" + +/datum/tarot/reversed/wheel_of_fortune/activate(mob/living/target) + var/obj/item/dice/d20/fate/one_use/gonna_roll_a_one = new /obj/item/dice/d20/fate/one_use(get_turf(target)) + gonna_roll_a_one.diceroll(target) + +/datum/tarot/reversed/strength + name = "XI - Strength?" + desc = "May you break their resolve." + card_icon = "strength?" + +/datum/tarot/reversed/strength/activate(mob/living/target) + for(var/mob/living/M in oview(9, target)) + M.Hallucinate(2 MINUTES) + new /obj/effect/hallucination/delusion(get_turf(M), M) + M.adjustBrainLoss(30) + +/datum/tarot/reversed/the_hanged_man + name = "XII - The Hanged Man?" + desc = "May your greed know no bounds." + card_icon = "the_hanged_man?" + +/datum/tarot/reversed/the_hanged_man/activate(mob/living/target) + var/obj/structure/cursed_slot_machine/pull_the_lever_kronk = new /obj/structure/cursed_slot_machine(get_turf(target)) + if(ishuman(target)) + var/mob/living/carbon/human/WRONG_LEVER = target + pull_the_lever_kronk.attack_hand(WRONG_LEVER) + +/datum/tarot/reversed/death + name = "XIII - Death?" + desc = "May life spring forth from the fallen." + card_icon = "death?" + +/datum/tarot/reversed/death/activate(mob/living/target) + new /obj/structure/constructshell(get_turf(target)) + new /obj/item/soulstone/anybody(get_turf(target)) + +/datum/tarot/reversed/temperance + name = "XIV - Temperance?" + desc = "May your hunger be satiated." + card_icon = "temperance?" + +/datum/tarot/reversed/temperance/activate(mob/living/target) + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + for(var/i in 1 to 5) + var/datum/reagents/R = new /datum/reagents(10) + R.add_reagent(get_unrestricted_random_reagent_id(), 10) + R.reaction(H, REAGENT_INGEST) + R.trans_to(H, 10) + target.visible_message("[target] consumes 5 pills rapidly!") + +/datum/tarot/reversed/the_devil + name = "XV - The Devil?" + desc = "Bask in the light of your mercy." + card_icon = "the_devil?" + +/datum/tarot/reversed/the_devil/activate(mob/living/target) + var/obj/item/grenade/clusterbuster/i_hate_nians = new(get_turf(target)) + i_hate_nians.prime() + +/datum/tarot/reversed/the_tower + name = "XVI - The Tower?" + desc = "Creation brings destruction." + card_icon = "the_tower?" + +/datum/tarot/reversed/the_tower/activate(mob/living/target) + for(var/turf/T in RANGE_TURFS(9, target)) + if(locate(/mob/living) in T) + continue + if(istype(T, /turf/simulated/wall/indestructible)) + continue + if(prob(66)) + continue + T.ChangeTurf(/turf/simulated/mineral/random/labormineral) + +/datum/tarot/reversed/the_stars + name = "XVII - The Stars?" + desc = "May your loss bring fortune." + card_icon = "the_stars?" + +/datum/tarot/reversed/the_stars/activate(mob/living/target) //Heavy clone damage hit, but gain 2 cards. Not teathered to the card producer. Could lead to card stacking, but would require the sun to fix easily + if(!ishuman(target)) + return + var/mob/living/carbon/human/H = target + H.adjustCloneLoss(50) + for(var/obj/item/organ/external/E in shuffle(H.bodyparts)) + switch(rand(1,3)) + if(1) + E.fracture() + if(2) + E.cause_internal_bleeding() + if(3) + E.cause_burn_wound() + break // I forgot the break the first time. Very funny. + + H.drop_l_hand() + H.drop_r_hand() + var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src)) + var/obj/item/magic_tarot_card/MPC = new /obj/item/magic_tarot_card(get_turf(src)) + H.put_in_hands(MTC) + H.put_in_hands(MPC) + +/datum/tarot/reversed/the_moon + name = "XVIII - The Moon?" + desc = "May you remember lost memories." + card_icon = "the_moon?" + +/datum/tarot/reversed/the_moon/activate(mob/living/target) + for(var/mob/living/L in view(5, target)) //Shorter range as this kinda can give away antagonists, though that is also funny. + target.mind.show_memory(L, 0) //Safe code? Bank accounts? PDA codes? It's yours my friend, as long as you have enough tarots + +/datum/tarot/reversed/the_sun + name = "XIX - The Sun?" + desc = "May the darkness swallow all around you." + card_icon = "the_sun?" + +/datum/tarot/reversed/the_sun/activate(mob/living/target) + target.apply_status_effect(STATUS_EFFECT_REVERSED_SUN) + +/datum/tarot/reversed/judgement + name = "XX - Judgement?" + desc = "May you redeem those found wanting" //Who wants more, but ghosts for something interesting + card_icon = "judgement?" + +/datum/tarot/reversed/judgement/activate(mob/living/target) + var/datum/event_container/EC = SSevents.event_containers[EVENT_LEVEL_MODERATE] + var/decrease = 5 MINUTES + EC.next_event_time -= decrease + log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by 5 minutes by use of a [src].") + +/datum/tarot/reversed/the_world + name = "XXI - The World?" + desc = "Step into the abyss." + card_icon = "the_world?" + +/datum/tarot/reversed/the_world/activate(mob/living/target) + var/list/L = list() + for(var/turf/T in get_area_turfs(/area/mine/outpost)) //Lavaland is the abyss, but also too hot to send people too. Mining base should be fair! + if(is_blocked_turf(T)) + continue + L.Add(T) + + if(!length(L)) + to_chat(target, "Hmm. No base? A miner issue.") + return + + target.forceMove(pick(L)) + to_chat(target, "You are abruptly pulled through space!") diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm index 48e603a0994f..0b2c52f36609 100644 --- a/code/game/gamemodes/wizard/rightandwrong.dm +++ b/code/game/gamemodes/wizard/rightandwrong.dm @@ -79,7 +79,9 @@ GLOBAL_LIST_INIT(summoned_magic, list( /obj/item/scrying, /obj/item/clothing/suit/space/hardsuit/wizard, /obj/item/immortality_talisman, - /obj/item/melee/ghost_sword)) + /obj/item/melee/ghost_sword, + /obj/item/tarot_card_pack, + /obj/item/tarot_card_pack/jumbo)) GLOBAL_LIST_INIT(summoned_special_magic, list( /obj/item/gun/magic/staff/animate, @@ -87,7 +89,8 @@ GLOBAL_LIST_INIT(summoned_special_magic, list( /obj/item/contract, /obj/item/gun/magic/staff/chaos, /obj/item/necromantic_stone, - /obj/item/blood_contract)) + /obj/item/blood_contract, + /obj/item/tarot_generator)) //everything above except for single use spellbooks, because they are counted separately (and are for basic bitches anyways) GLOBAL_LIST_INIT(summoned_magic_objectives, list( @@ -100,7 +103,8 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( /obj/item/necromantic_stone, /obj/item/scrying, /obj/item/spellbook, - /obj/item/storage/belt/wands/full)) + /obj/item/storage/belt/wands/full, + /obj/item/tarot_generator)) // If true, it's the probability of triggering "survivor" antag. GLOBAL_VAR_INIT(summon_guns_triggered, FALSE) diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 2190b08c3e15..fdf530ea1198 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -11,8 +11,8 @@ slot_flags = SLOT_FLAG_BELT origin_tech = "bluespace=4;materials=5" - /// The body/brain of the player inside this construct, transferred over from the soulstone. - var/atom/movable/held_body + /// Should we show rays? Triggered by a held body + var/animate_rays = FALSE /// Does this soulstone ask the victim whether they want to be turned into a shade var/optional = FALSE /// Can this soul stone be used by anyone, or only cultists/wizards? @@ -26,13 +26,6 @@ var/opt_in = FALSE var/purified = FALSE -/obj/item/soulstone/proc/add_held_body(atom/movable/body) - held_body = body - RegisterSignal(body, COMSIG_PARENT_QDELETING, PROC_REF(remove_held_body)) - -/obj/item/soulstone/proc/remove_held_body() - SIGNAL_HANDLER - held_body = null /obj/item/soulstone/proc/can_use(mob/living/user) if(IS_CULTIST(user) && purified && !iswizard(user)) @@ -78,13 +71,12 @@ /obj/item/soulstone/Destroy() //Stops the shade from being qdel'd immediately and their ghost being sent back to the arrival shuttle. for(var/mob/living/simple_animal/shade/A in src) A.death() - remove_held_body() STOP_PROCESSING(SSobj, src) return ..() /obj/item/soulstone/process() . = ..() - if(held_body) + if(animate_rays) var/new_filter = isnull(get_filter("ray")) if(!purified) ray_filter_helper(1, 40,"#c926ae", 6, 20) @@ -242,20 +234,19 @@ to_chat(user, "The shard feels too tough to shatter, you are not holy enough to free its captive!") return - if(!do_after_once(user, 10 SECONDS, FALSE, src) || !held_body) + if(!do_after_once(user, 10 SECONDS, FALSE, src)) return - user.visible_message("[user] shatters the soulstone apart! Releasing [held_body] from their prison!", "You shatter the soulstone holding [held_body], binding them free!", "You hear something shatter with a ghastly crack.") - if(ismob(held_body)) - var/mob/M = held_body - M.key = S.key - else if(istype(held_body, /obj/item/organ/internal/brain)) - var/obj/item/organ/internal/brain/B = held_body - B.brainmob.key = S.key - S.cancel_camera() - held_body.forceMove(get_turf(src)) - SSticker.mode?.cult_team?.add_cult_immunity(held_body) - remove_held_body() + if(!S) + return + + var/datum/component/construct_held_body/body_holder = S.GetComponent(/datum/component/construct_held_body) + var/atom/movable/dropped_body = body_holder.held_body + body_holder.drop_body() + if(!dropped_body) + return + + user.visible_message("[user] shatters the soulstone apart! Releasing [dropped_body] from their prison!", "You shatter the soulstone holding [dropped_body], binding them free!", "You hear something shatter with a ghastly crack.") new /obj/effect/temp_visual/cult/sparks(get_turf(src)) playsound(src, 'sound/effects/pylon_shatter.ogg', 40, TRUE) qdel(src) @@ -276,6 +267,7 @@ was_used() remove_filter("ray") STOP_PROCESSING(SSobj, src) + animate_rays = FALSE ///////////////////////////Transferring to constructs///////////////////////////////////////////////////// /obj/structure/constructshell @@ -320,9 +312,8 @@ to_chat(user, "Capture failed! The soul has already fled its mortal frame. You attempt to bring it back...") T.Paralyse(40 SECONDS) if(!get_cult_ghost(T, user, TRUE)) - add_held_body(T) - T.forceMove(src) //If we can't get a ghost, shard the body anyways. - START_PROCESSING(SSobj, src) + // no luck, dont shard them. + to_chat(user, "No soul responds to the soul stone.") if("VICTIM") var/mob/living/carbon/human/T = target @@ -361,6 +352,7 @@ name = "soulstone : [T.name]" to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") to_chat(user, "Capture successful! [T.name]'s has been recaptured and stored within the soul stone.") + animate_rays = TRUE START_PROCESSING(SSobj, src) if("CONSTRUCT") @@ -413,9 +405,8 @@ to_chat(src, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.") else to_chat(src, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - SS.held_body.forceMove(src) - add_held_body(SS.held_body) - SS.remove_held_body() + + SEND_SIGNAL(shade, COMSIG_SHADE_TO_CONSTRUCT_TRANSFER, src) cancel_camera() qdel(shell) qdel(shade) @@ -465,13 +456,13 @@ if(!isrobot(M)) for(var/obj/item/I in M) M.unEquip(I) + + var/target_body = M if(isbrain(M)) - var/obj/item/organ/internal/brain/brain_obj = M.loc - add_held_body(brain_obj) - brain_obj.forceMove(src) - else - add_held_body(M) - M.forceMove(src) + target_body = M.loc // get the brain organ instead of the brain mob + + S.AddComponent(/datum/component/construct_held_body, target_body) + animate_rays = TRUE /obj/item/soulstone/proc/get_shade_type() if(purified) diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 743079d34e55..d175fcbf6ad7 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -483,6 +483,15 @@ cost = 1 category = "Artefacts" +/datum/spellbook_entry/item/tarot_generator + name = "Enchanted tarot card deck" + desc = "An magic tarot card deck, enchanted with special Ink. \ + Capable of producing magic tarot cards of the 22 major arcana, both normal and reversed. Each card has a different effect. \ + Throw the card at someone to use it on them, or use it in hand to apply it to yourself. Unlimited uses, 12 second cooldown, can have up to 5 cards in the world." + item_path = /obj/item/tarot_generator/wizard + cost = 2 + category = "Artefacts" + //Weapons and Armors /datum/spellbook_entry/item/battlemage name = "Battlemage Armor" diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index f4d62e08a8f7..c8481db135a1 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -32,18 +32,6 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list( ROLE_ABDUCTOR = 20, )) -// Client Verbs - -/client/verb/cmd_check_own_playtime() - set category = "Special Verbs" - set name = "Check my playtime" - - if(!GLOB.configuration.jobs.enable_exp_tracking) - to_chat(src, "Playtime tracking is not enabled.") - return - - to_chat(src, "Your [EXP_TYPE_CREW] playtime is [get_exp_type(EXP_TYPE_CREW)].") - // Admin Verbs /client/proc/cmd_mentor_check_player_exp() //Allows admins to determine who the newer players are. diff --git a/code/game/objects/effects/temporary_visuals/cult_visuals.dm b/code/game/objects/effects/temporary_visuals/cult_visuals.dm index a1b2d4320a97..3f6d09dbaf6f 100644 --- a/code/game/objects/effects/temporary_visuals/cult_visuals.dm +++ b/code/game/objects/effects/temporary_visuals/cult_visuals.dm @@ -9,6 +9,13 @@ name = "blood sparks" icon_state = "bloodsparkles" +/obj/effect/temp_visual/cult/sparks/hierophant + icon = 'icons/effects/effects.dmi' + randomdir = TRUE + duration = 12 + name = "purple sparks" + icon_state = "hierophant_blast" + /obj/effect/temp_visual/dir_setting/cult/phase name = "phase glow" duration = 12 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 49c45f27fe8c..18b6d11151a8 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -514,7 +514,7 @@ GLOBAL_LIST_INIT(cult_recipes, list ( new /datum/stack_recipe/cult("runed girder (used to make cult walls)", /obj/structure/girder/cult, 1, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), new /datum/stack_recipe/cult("pylon (heals nearby cultists)", /obj/structure/cult/functional/pylon, 4, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), new /datum/stack_recipe/cult("forge (crafts shielded robes, flagellant's robes, and mirror shields)", /obj/structure/cult/functional/forge, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), - new /datum/stack_recipe/cult("archives (crafts zealot's blindfolds, shuttle curse orbs, veil shifters, and reality sunderers)", /obj/structure/cult/functional/archives, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), + new /datum/stack_recipe/cult("archives (crafts zealot's blindfolds, shuttle curse orbs, veil shifters, reality sunderers, and blank tarot cards)", /obj/structure/cult/functional/archives, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), new /datum/stack_recipe/cult("altar (crafts eldritch whetstones, construct shells, and flasks of unholy water)", /obj/structure/cult/functional/altar, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE), )) diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 7a825263df9d..cc9c370b9735 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -47,6 +47,21 @@ return A.emag_act(user) +/obj/item/card/emag/magic_key + name = "magic key" + desc = "It's a magic key, that will open one door!" + icon_state = "magic_key" + origin_tech = "magnets=2" + +/obj/item/card/emag/magic_key/afterattack(atom/target, mob/user, proximity) + if(!istype(target, /obj/machinery/door)) + return + var/obj/machinery/door/D = target + D.locked = FALSE + update_icon() + . = ..() + qdel(src) + /obj/item/card/cmag desc = "It's a card coated in a slurry of electromagnetic bananium." name = "jestographic sequencer" diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm index f08704aa25d3..a03f07be8b0a 100644 --- a/code/game/objects/items/weapons/grenades/clusterbuster.dm +++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm @@ -227,6 +227,11 @@ desc = "For when you need to knock out EVERYONE." payload = /obj/item/grenade/gas/knockout +/obj/item/grenade/clusterbuster/ied + name = "\improper IED Cluster Grenade" + desc = "For when you need to do something between everything and nothing." + payload = /obj/item/grenade/iedcasing + ////////////Clusterbuster of Clusterbusters//////////// //As a note: be extrodinarily careful about make the payload clusterbusters as it can quickly destroy the MC/Server diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 453c0e0c8cdb..dbc5d3f5ced8 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -720,7 +720,8 @@ /obj/item/warp_cube/red = 1, /obj/item/reagent_containers/drinks/everfull = 2, /obj/item/clothing/suit/space/hardsuit/wizard = 2, - /obj/item/immortality_talisman = 1 ) //spells recharge when invincible + /obj/item/immortality_talisman = 1, //spells recharge when invincible + /obj/item/tarot_generator/wizard = 2) var/obj/item/pickeda = pick(list_a) value += list_a[pickeda] new pickeda(src) diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 5dffb626cf73..0be43090cc96 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -117,3 +117,17 @@ user.dust() dump_contents() visible_message("[src] shatters!") + +/obj/structure/closet/statue/indestructible + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + timer = 240 SECONDS_TO_LIFE_CYCLES + +/obj/structure/closet/statue/indestructible/ex_act(severity) + return //No delimbing them + +/obj/structure/closet/statue/indestructible/shatter(mob/user) + return //No. Failsafe. + +/obj/structure/closet/statue/indestructible/singularity_act() + return //I mean maybe but no. + diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 53b526bad412..a16ff51d0d44 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -112,61 +112,6 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") if(GLOB.configuration.general.auto_disable_ooc && GLOB.ooc_enabled != on) toggle_ooc() -/client/proc/set_ooc(newColor as color) - set name = "Set Player OOC Colour" - set desc = "Modifies the default player OOC color." - set category = "Server" - - if(!check_rights(R_SERVER)) return - - GLOB.normal_ooc_colour = newColor - message_admins("[key_name_admin(usr)] has set the default player OOC color to [newColor]") - log_admin("[key_name(usr)] has set the default player OOC color to [newColor]") - - - SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Player OOC") - -/client/proc/reset_ooc() - set name = "Reset Player OOC Color" - set desc = "Returns the default player OOC color to default." - set category = "Server" - - if(!check_rights(R_SERVER)) return - - GLOB.normal_ooc_colour = DEFAULT_PLAYER_OOC_COLOUR - message_admins("[key_name_admin(usr)] has reset the default player OOC color") - log_admin("[key_name(usr)] has reset the default player OOC color") - - SSblackbox.record_feedback("tally", "admin_verb", 1, "Reset Player OOC") - -/client/proc/colorooc() - set name = "Set Your OOC Color" - set desc = "Allows you to pick a custom OOC color." - set category = "Preferences" - - if(!check_rights(R_ADMIN)) return - - var/new_ooccolor = input(src, "Please select your OOC color.", "OOC color", prefs.ooccolor) as color|null - if(new_ooccolor) - prefs.ooccolor = new_ooccolor - prefs.save_preferences(src) - to_chat(usr, "Your OOC color has been set to [new_ooccolor].") - - SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Own OOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/resetcolorooc() - set name = "Reset Your OOC Color" - set desc = "Returns your OOC color to default." - set category = "Preferences" - - if(!check_rights(R_ADMIN)) return - - prefs.ooccolor = initial(prefs.ooccolor) - prefs.save_preferences(src) - to_chat(usr, "Your OOC color has been reset.") - - SSblackbox.record_feedback("tally", "admin_verb", 1, "Reset Own OOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/verb/looc(msg = "" as text) set name = "LOOC" set desc = "Local OOC, seen only by those in view." diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 49d8b4f0817a..0b9abc3abf31 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -2,8 +2,6 @@ GLOBAL_LIST_INIT(admin_verbs_default, list( /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ /client/proc/hide_verbs, /*hides all our adminverbs*/ - /client/proc/toggleadminhelpsound, - /client/proc/togglementorhelpsound, /client/proc/cmd_mentor_check_new_players, /client/proc/cmd_mentor_check_player_exp /* shows players by playtime */ )) @@ -13,8 +11,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ /datum/admins/proc/announce, /*priority announce something to all clients.*/ - /client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/ - /client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/ /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ /client/proc/toggle_view_range, /*changes how far we can see*/ /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ @@ -35,8 +31,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */ /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ - /client/proc/toggleprayers, /*toggles prayers on/off*/ - /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ /datum/admins/proc/togglelooc, /*toggles looc on/off for everyone*/ @@ -49,9 +43,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/cmd_mentor_say, /datum/admins/proc/show_player_notes, /client/proc/free_slot, /*frees slot for chosen job*/ - /client/proc/toggleattacklogs, - /client/proc/toggleadminlogs, - /client/proc/toggledebuglogs, /client/proc/update_mob_sprite, /client/proc/man_up, /client/proc/global_man_up, @@ -140,8 +131,6 @@ GLOBAL_LIST_INIT(admin_verbs_server, list( /client/proc/view_asays, /client/proc/toggle_antagHUD_use, /client/proc/toggle_antagHUD_restrictions, - /client/proc/set_ooc, - /client/proc/reset_ooc, /client/proc/set_next_map, /client/proc/manage_queue, /client/proc/add_queue_server_bypass @@ -155,7 +144,6 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/cmd_debug_del_sing, /client/proc/restart_controller, /client/proc/enable_debug_verbs, - /client/proc/toggledebuglogs, /client/proc/cmd_display_del_log, /client/proc/cmd_display_del_log_simple, /client/proc/check_bomb_impacts, @@ -181,8 +169,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/debug_timers, /client/proc/force_verb_bypass, /client/proc/show_gc_queues, - /client/proc/debug_global_variables, - /client/proc/toggle_mctabs + /client/proc/debug_global_variables )) GLOBAL_LIST_INIT(admin_verbs_possess, list( /proc/possess, @@ -215,7 +202,6 @@ GLOBAL_LIST_INIT(admin_verbs_mentor, list( /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ /client/proc/openMentorTicketUI, - /client/proc/toggleMentorTicketLogs, /client/proc/cmd_mentor_say /* mentor say*/ // cmd_mentor_say is added/removed by the toggle_mentor_chat verb )) @@ -226,9 +212,7 @@ GLOBAL_LIST_INIT(admin_verbs_proccall, list( )) GLOBAL_LIST_INIT(admin_verbs_ticket, list( /client/proc/openAdminTicketUI, - /client/proc/toggleticketlogs, /client/proc/openMentorTicketUI, - /client/proc/toggleMentorTicketLogs, /client/proc/resolveAllAdminTickets, /client/proc/resolveAllMentorTickets )) @@ -246,15 +230,13 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( /client/proc/view_runtimes, /client/proc/cmd_display_del_log, /client/proc/cmd_display_del_log_simple, - /client/proc/toggledebuglogs, /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ /client/proc/ss_breakdown, /client/proc/show_gc_queues, /client/proc/debug_global_variables, /client/proc/visualise_active_turfs, /client/proc/debug_timers, - /client/proc/timer_log, - /client/proc/toggle_mctabs + /client/proc/timer_log )) /client/proc/add_admin_verbs() @@ -880,91 +862,6 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( log_admin("[key_name(usr)] has freed a job slot for [job].") message_admins("[key_name_admin(usr)] has freed a job slot for [job].") -/client/proc/toggleattacklogs() - set name = "Attack Log Messages" - set category = "Preferences.Toggle" - - if(!check_rights(R_ADMIN)) - return - - if(prefs.atklog == ATKLOG_ALL) - prefs.atklog = ATKLOG_ALMOSTALL - to_chat(usr, "Your attack logs preference is now: show ALMOST ALL attack logs (notable exceptions: NPCs attacking other NPCs, vampire bites, equipping/stripping, people pushing each other over)") - else if(prefs.atklog == ATKLOG_ALMOSTALL) - prefs.atklog = ATKLOG_MOST - to_chat(usr, "Your attack logs preference is now: show MOST attack logs (like ALMOST ALL, except that it also hides player v. NPC combat, and certain areas like lavaland syndie base and thunderdome)") - else if(prefs.atklog == ATKLOG_MOST) - prefs.atklog = ATKLOG_FEW - to_chat(usr, "Your attack logs preference is now: show FEW attack logs (only the most important stuff: attacks on SSDs, use of explosives, messing with the engine, gibbing, AI wiping, forcefeeding, acid sprays, and organ extraction)") - else if(prefs.atklog == ATKLOG_FEW) - prefs.atklog = ATKLOG_NONE - to_chat(usr, "Your attack logs preference is now: show NO attack logs") - else if(prefs.atklog == ATKLOG_NONE) - prefs.atklog = ATKLOG_ALL - to_chat(usr, "Your attack logs preference is now: show ALL attack logs") - else - prefs.atklog = ATKLOG_ALL - to_chat(usr, "Your attack logs preference is now: show ALL attack logs (your preference was set to an invalid value, it has been reset)") - - prefs.save_preferences(src) - - -/client/proc/toggleadminlogs() - set name = "Admin Log Messages" - set category = "Preferences.Toggle" - - if(!check_rights(R_ADMIN)) - return - - prefs.toggles ^= PREFTOGGLE_CHAT_NO_ADMINLOGS - prefs.save_preferences(src) - if(prefs.toggles & PREFTOGGLE_CHAT_NO_ADMINLOGS) - to_chat(usr, "You now won't get admin log messages.") - else - to_chat(usr, "You now will get admin log messages.") - -/client/proc/toggleMentorTicketLogs() - set name = "Mentor Ticket Messages" - set category = "Preferences.Toggle" - - if(!check_rights(R_MENTOR|R_ADMIN)) - return - - prefs.toggles ^= PREFTOGGLE_CHAT_NO_MENTORTICKETLOGS - prefs.save_preferences(src) - if(prefs.toggles & PREFTOGGLE_CHAT_NO_MENTORTICKETLOGS) - to_chat(usr, "You now won't get mentor ticket messages.") - else - to_chat(usr, "You now will get mentor ticket messages.") - -/client/proc/toggleticketlogs() - set name = "Admin Ticket Messgaes" - set category = "Preferences.Toggle" - - if(!check_rights(R_ADMIN)) - return - - prefs.toggles ^= PREFTOGGLE_CHAT_NO_TICKETLOGS - prefs.save_preferences(src) - if(prefs.toggles & PREFTOGGLE_CHAT_NO_TICKETLOGS) - to_chat(usr, "You now won't get admin ticket messages.") - else - to_chat(usr, "You now will get admin ticket messages.") - -/client/proc/toggledebuglogs() - set name = "Debug Log Messages" - set category = "Preferences.Toggle" - - if(!check_rights(R_VIEWRUNTIMES | R_DEBUG)) - return - - prefs.toggles ^= PREFTOGGLE_CHAT_DEBUGLOGS - prefs.save_preferences(src) - if(prefs.toggles & PREFTOGGLE_CHAT_DEBUGLOGS) - to_chat(usr, "You now will get debug log messages") - else - to_chat(usr, "You now won't get debug log messages") - /client/proc/man_up(mob/T as mob in GLOB.player_list) set name = "\[Admin\] Man Up" set desc = "Tells mob to man up and deal with it." diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index 1284f9e0fafe..e68cb9832731 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -28,12 +28,14 @@ set category = "OOC" set name = "Custom Event Info" + var/list/custom_event_information = list() if(!GLOB.custom_event_msg || GLOB.custom_event_msg == "") - to_chat(src, "There currently is no known custom event taking place.") - to_chat(src, "Keep in mind: it is possible that an admin has not properly set this.") + custom_event_information += "There currently is no known custom event taking place." + custom_event_information += "Keep in mind: it is possible that an admin has not properly set this." + to_chat(src, chat_box_regular(custom_event_information.Join("
"))) return - to_chat(src, "

Custom Event

") - to_chat(src, "

A custom event is taking place. OOC Info:

") - to_chat(src, "[html_encode(GLOB.custom_event_msg)]") - to_chat(src, "
") + custom_event_information += "

Custom Event

" + custom_event_information += "

A custom event is taking place. OOC Info:

" + custom_event_information += "[html_encode(GLOB.custom_event_msg)]" + to_chat(src, chat_box_regular(custom_event_information.Join("
"))) diff --git a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm index d69d28fe36aa..641894c0eabd 100644 --- a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm @@ -117,7 +117,6 @@ action_icon_state = "demonic_grasp" - panel = "Vampire" school = "vampire" action_background_icon_state = "bg_vampire" sound = null diff --git a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm index f086ac67f57f..220ba634df79 100644 --- a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm @@ -250,7 +250,6 @@ gain_desc = "You have gained the ability to shift into a pool of blood, allowing you to evade pursuers with great mobility." jaunt_duration = 3 SECONDS clothes_req = FALSE - panel = "Vampire" school = "vampire" action_background_icon_state = "bg_vampire" action_icon_state = "blood_pool" diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm index ecf21d68ed4d..f2b54d4c8567 100644 --- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm @@ -13,7 +13,6 @@ return TRUE /datum/spell/vampire - panel = "Vampire" school = "vampire" action_background_icon_state = "bg_vampire" human_req = TRUE diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm index c872499557dc..be522b5ebb31 100644 --- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm +++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm @@ -18,8 +18,12 @@ /obj/item/guardiancreator = 1, // jackpot. /obj/item/spellbook/oneuse/knock = 1, // tresspassing charges incoming /obj/item/gun/magic/wand/resurrection = 1, // medbay's best friend + /obj/item/tarot_generator = 1, // A little bit of everything, all of the time. /obj/item/spellbook/oneuse/charge = 20, // and now for less useful stuff to dilute the good loot chances /obj/item/spellbook/oneuse/summonitem = 20, /obj/item/spellbook/oneuse/forcewall = 10, - /obj/item/book/granter/spell/summon_cheese = 20 // hungry wizard stuff + /obj/item/tarot_card_pack = 10, + /obj/item/tarot_card_pack/jumbo = 6, + /obj/item/tarot_card_pack/mega = 4, + /obj/item/book/granter/spell/summon_cheese = 15 // hungry wizard stuff ) diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm index 04198097d385..9d7b78b6e239 100644 --- a/code/modules/client/preference/link_processing.dm +++ b/code/modules/client/preference/link_processing.dm @@ -1285,6 +1285,10 @@ init_keybindings(keybindings_overrides) save_preferences(user) //Ideally we want to save people's keybinds when they enter them + if("preference_toggles") + if(href_list["toggle"]) + var/datum/preference_toggle/toggle = locateUID(href_list["toggle"]) + toggle.set_toggles(user.client) ShowChoices(user) - return 1 + return TRUE diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index b8a53d743a7a..1cd021814b27 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -167,6 +167,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "Antagonists" dat += "Loadout" dat += "Key Bindings" + dat += "General Preferences" dat += "" dat += "
" @@ -484,6 +485,12 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts else var/is_special = (i in src.be_special) dat += "Be [capitalize(i)]:[(is_special) ? "Yes" : "No"]
" + + dat += "

Total Playtime:

" + if(!GLOB.configuration.jobs.enable_exp_tracking) + dat += "Playtime tracking is not enabled." + else + dat += "Your [EXP_TYPE_CREW] playtime is [user.client.get_exp_type(EXP_TYPE_CREW)]
" dat += "" if(TAB_GEAR) @@ -595,6 +602,42 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "" + if(TAB_TOGGLES) + dat += "
Preference Toggles: " + + dat += "" + + // Lookup lists to make our life easier + var/static/list/pref_toggles_by_category + if(!pref_toggles_by_category) + pref_toggles_by_category = list() + for(var/datum/preference_toggle/toggle as anything in GLOB.preference_toggles) + pref_toggles_by_category["[toggle.preftoggle_category]"] += list(toggle) + + for(var/category in GLOB.preference_toggle_groups) + dat += "" + dat += "" + for(var/datum/preference_toggle/toggle as anything in pref_toggles_by_category["[GLOB.preference_toggle_groups[category]]"]) + dat += "" + dat += "" + dat += "" + if(toggle.preftoggle_category == PREFTOGGLE_CATEGORY_ADMIN) + if(!check_rights(toggle.rights_required, 0, (user))) + dat += "" + dat += "" + continue + switch(toggle.preftoggle_toggle) + if(PREFTOGGLE_SPECIAL) + dat += "" + if(PREFTOGGLE_TOGGLE1) + dat += "" + if(PREFTOGGLE_TOGGLE2) + dat += "" + if(PREFTOGGLE_SOUND) + dat += "" + dat += "" + dat += "" + dat += "
" if(!IsGuestKey(user.key)) diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index ecae3e50c1c1..31cfd405f5c3 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -1,361 +1,528 @@ -//toggles -/client/verb/toggle_ghost_ears() - set name = "GhostEars" - set category = "Preferences.Show/Hide" - set desc = "Toggle Between seeing all mob speech, and only speech of nearby mobs" - prefs.toggles ^= PREFTOGGLE_CHAT_GHOSTEARS - to_chat(src, "As a ghost, you will now [(prefs.toggles & PREFTOGGLE_CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].") - prefs.save_preferences(src) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle GhostEars") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggle_ghost_sight() - set name = "GhostSight" - set category = "Preferences.Show/Hide" - set desc = "Toggle Between seeing all mob emotes, and only emotes of nearby mobs" - prefs.toggles ^= PREFTOGGLE_CHAT_GHOSTSIGHT - to_chat(src, "As a ghost, you will now [(prefs.toggles & PREFTOGGLE_CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"].") - prefs.save_preferences(src) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle GhostSight") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggle_ghost_radio() - set name = "GhostRadio" - set category = "Preferences.Show/Hide" - set desc = "Toggle between hearing all radio chatter, or only from nearby speakers" - prefs.toggles ^= PREFTOGGLE_CHAT_GHOSTRADIO - to_chat(src, "As a ghost, you will now [(prefs.toggles & PREFTOGGLE_CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"].") - prefs.save_preferences(src) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle GhostRadio") - -/client/proc/toggle_hear_radio() - set name = "RadioChatter" - set category = "Preferences.Show/Hide" - set desc = "Toggle seeing radiochatter from radios and speakers" - if(!check_rights(R_ADMIN)) - return - prefs.toggles ^= PREFTOGGLE_CHAT_RADIO - prefs.save_preferences(src) - to_chat(usr, "You will [(prefs.toggles & PREFTOGGLE_CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle RadioChatter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggle_ai_voice_annoucements() - set name = "AI Voice Announcements" - set category = "Preferences.Hear/Silence" - set desc = "Toggle hearing AI annoucements in voice form or in text form" - prefs.sound ^= SOUND_AI_VOICE - prefs.save_preferences(src) - to_chat(usr, "[(prefs.sound & SOUND_AI_VOICE) ? "You will now hear AI announcements." : "AI annoucements will now be converted to text."] ") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle AI Voice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggleadminhelpsound() - set name = "Admin Bwoinks" - set category = "Preferences.Hear/Silence" - set desc = "Toggle hearing a notification when admin PMs are received" - if(!check_rights(R_ADMIN)) - return - prefs.sound ^= SOUND_ADMINHELP - prefs.save_preferences(src) - to_chat(usr, "You will [(prefs.sound & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Admin Bwoinks") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/togglementorhelpsound() - set name = "Mentorhelp Bwoinks" - set category = "Preferences.Hear/Silence" - set desc = "Toggle hearing a notification when mentorhelps are received" - if(!check_rights(R_ADMIN|R_MENTOR)) - return - prefs.sound ^= SOUND_MENTORHELP - prefs.save_preferences(src) - to_chat(usr, "You will [(prefs.sound & SOUND_MENTORHELP) ? "now" : "no longer"] hear a sound when mentorhelps arrive.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Mentor Bwoinks") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/deadchat() // Deadchat toggle is usable by anyone. - set name = "Deadchat" - set category = "Preferences.Show/Hide" - set desc ="Toggles seeing deadchat" - prefs.toggles ^= PREFTOGGLE_CHAT_DEAD - prefs.save_preferences(src) - - if(src.holder) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_CHAT_DEAD) ? "now" : "no longer"] see deadchat.") - else - to_chat(src, "As a ghost, you will [(prefs.toggles & PREFTOGGLE_CHAT_DEAD) ? "now" : "no longer"] see deadchat.") - - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Deadchat") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggleprayers() - set name = "Prayers" - set category = "Preferences.Show/Hide" - set desc = "Toggles seeing prayers" - prefs.toggles ^= PREFTOGGLE_CHAT_PRAYER - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Prayers") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggleprayernotify() - set name = "Prayer Notification Sound" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing when prayers are made" - prefs.sound ^= SOUND_PRAYERNOTIFY - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.sound & SOUND_PRAYERNOTIFY) ? "now" : "no longer"] hear when prayers are made.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Prayer Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/togglescoreboard() - set name = "End Round Scoreboard" - set category = "Preferences.Show/Hide" - set desc = "Toggles displaying end of round scoreboard" - prefs.toggles ^= PREFTOGGLE_DISABLE_SCOREBOARD - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_DISABLE_SCOREBOARD) ? "no longer" : "now"] see the end of round scoreboard.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Scoreboard") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggletitlemusic() - set name = "LobbyMusic" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing the GameLobby music" - prefs.sound ^= SOUND_LOBBY - prefs.save_preferences(src) - if(prefs.sound & SOUND_LOBBY) - to_chat(src, "You will now hear music in the game lobby.") +/client/verb/setup_character() + set name = "Game Preferences" + set category = "Special Verbs" + prefs.current_tab = 1 + prefs.ShowChoices(usr) + +// Preference toggles +/datum/preference_toggle + /// Name of the preference toggle. Don't set this if you don't want it to appear in game + var/name + /// Bitflag this datum will set to + var/preftoggle_bitflag + /// Category of the toggle + var/preftoggle_category + /// What toggles to set this to? + var/preftoggle_toggle + /// Description of what the pref setting does + var/description + /// Message to display when this toggle is enabled + var/enable_message + /// Message to display when this toggle is disabled + var/disable_message + /// Message for the blackbox, legacy verbs so we can't just use the name + var/blackbox_message + /// Rights required to be able to use this pref option + var/rights_required + +/datum/preference_toggle/proc/set_toggles(client/user) + var/datum/preferences/our_prefs = user.prefs + switch(preftoggle_toggle) + if(PREFTOGGLE_SPECIAL) + CRASH("[src] did not have it's set_toggles overriden even though it was a special toggle, please use the special_toggle path!") + if(PREFTOGGLE_TOGGLE1) + our_prefs.toggles ^= preftoggle_bitflag + to_chat(user, "[(our_prefs.toggles & preftoggle_bitflag) ? enable_message : disable_message]") + if(PREFTOGGLE_TOGGLE2) + our_prefs.toggles2 ^= preftoggle_bitflag + to_chat(user, "[(our_prefs.toggles2 & preftoggle_bitflag) ? enable_message : disable_message]") + if(PREFTOGGLE_SOUND) + our_prefs.sound ^= preftoggle_bitflag + to_chat(user, "[(our_prefs.sound & preftoggle_bitflag) ? enable_message : disable_message]") + + SSblackbox.record_feedback("tally", "toggle_verbs", 1, blackbox_message) + our_prefs.save_preferences(user) + +/datum/preference_toggle/toggle_ghost_ears + name = "Toggle Hearing All Speech as a Ghost" + description = "Toggle Between seeing all mob speech, and only speech of nearby mobs" + preftoggle_bitflag = PREFTOGGLE_CHAT_GHOSTEARS + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GHOST + enable_message = "As a ghost, you will now only see speech from nearby mobs." + disable_message = "As a ghost, you will now see all speech in the world." + blackbox_message = "Toggle GhostEars" + +/datum/preference_toggle/toggle_ghost_sight + name = "Toggle Ghost Emote Viewing" + description = "Toggle Between seeing all mob emotes, and only emotes of nearby mobs" + preftoggle_bitflag = PREFTOGGLE_CHAT_GHOSTSIGHT + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GHOST + enable_message = "As a ghost, you will now only see speech from nearby mobs." + disable_message = "As a ghost, you will now see all emotes in the world." + blackbox_message = "Toggle GhostSight" + +/datum/preference_toggle/toggle_ghost_radio + name = "Toggle Ghost Radio" + description = "Toggle between hearing all radio chatter, or only from nearby speakers" + preftoggle_bitflag = PREFTOGGLE_CHAT_GHOSTRADIO + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GHOST + enable_message = "As a ghost, you will now only hear from nearby speakers." + disable_message = "As a ghost, you will now hear all radio chat in the world." + blackbox_message = "Toggle GhostRadio" + +/datum/preference_toggle/toggle_admin_radio + name = "Admin Radio" + description = "Toggle seeing radiochatter from radios and speakers" + preftoggle_bitflag = PREFTOGGLE_CHAT_RADIO + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + enable_message = "You will no longer see radio chatter from radios or speakers." + disable_message = "You will now see radio chatter from radios or speakers." + blackbox_message = "Toggle RadioChatter" + +/datum/preference_toggle/toggle_ai_voice_annoucements + name = "AI Voice Announcements" + description = "Toggle hearing AI annoucements in voice form or in text form" + preftoggle_bitflag = SOUND_AI_VOICE + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear AI announcements." + disable_message = "You will now hear AI announcements." + blackbox_message = "Toggle AI Voice" + +/datum/preference_toggle/toggle_admin_pm_sound + name = "Admin PM sound" + description = "Toggle hearing a notification when admin PMs are received" + preftoggle_bitflag = SOUND_ADMINHELP + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + enable_message = "You will now hear a sound when adminhelp is sent." + disable_message = "You will no longer hear a sound when adminhelp is sent." + blackbox_message = "Toggle Admin Bwoinks" + +/datum/preference_toggle/toggle_mentor_pm_sound + name = "Mentor PM sound" + description = "Toggle hearing a notification when mentor PMs are received" + preftoggle_bitflag = SOUND_MENTORHELP + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_MENTOR + enable_message = "You will now hear a sound when mentorhelp is sent." + disable_message = "You will no longer hear a sound when mentorhelp is sent." + blackbox_message = "Toggle Mentor Bwoinks" + +/datum/preference_toggle/toggle_deadchat_visibility + name = "Toggle Deadchat visibility" + description = "Toggles Dchat's visibility" + preftoggle_bitflag = PREFTOGGLE_CHAT_DEAD + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see deadchat." + disable_message = "You will no longer see deadchat." + blackbox_message = "Toggle Deadchat" + +/datum/preference_toggle/end_of_round_scoreboard + name = "Toggle the End of Round Scoreboard" + description = "Prevents you from seeing the end of round scoreboard" + preftoggle_bitflag = PREFTOGGLE_DISABLE_SCOREBOARD + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see the end of round scoreboard." + disable_message = "You will no longer see see the end of round scoreboard." + blackbox_message = "Toggle Scoreboard" + +/datum/preference_toggle/title_music + name = "Toggle Lobby Music" + description = "Toggles hearing the GameLobby music" + preftoggle_bitflag = SOUND_LOBBY + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear music in the game lobby." + disable_message = "You will no longer hear music in the game lobby." + blackbox_message = "Toggle Lobby Music" + +/datum/preference_toggle/title_music/set_toggles(client/user) + . = ..() + if(user.prefs.sound & SOUND_LOBBY) if(isnewplayer(usr)) - usr.client.playtitlemusic() + user.playtitlemusic() else - to_chat(src, "You will no longer hear music in the game lobby.") usr.stop_sound_channel(CHANNEL_LOBBYMUSIC) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Lobby Music") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/togglemidis() - set name = "Midis" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing sounds uploaded by admins" - prefs.sound ^= SOUND_MIDI - prefs.save_preferences(src) - if(prefs.sound & SOUND_MIDI) - to_chat(src, "You will now hear any sounds uploaded by admins.") - else +/datum/preference_toggle/toggle_admin_midis + name = "Toggle Admin Midis" + description = "Toggles hearing sounds uploaded by admins" + preftoggle_bitflag = SOUND_MIDI + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear any sounds uploaded by admins." + disable_message = "You will no longer hear sounds uploaded by admins; any currently playing midis have been disabled." + blackbox_message = "Toggle MIDIs" + +/datum/preference_toggle/toggle_admin_midis/set_toggles(client/user) + . = ..() + if(user.prefs.sound & ~SOUND_LOBBY) usr.stop_sound_channel(CHANNEL_ADMIN) - to_chat(src, "You will no longer hear sounds uploaded by admins; any currently playing midis have been disabled.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle MIDIs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/listen_ooc() - set name = "OOC (Out of Character)" - set category = "Preferences.Show/Hide" - set desc = "Toggles seeing OutOfCharacter chat" - prefs.toggles ^= PREFTOGGLE_CHAT_OOC - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle OOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/verb/listen_looc() - set name = "LOOC (Local Out of Character)" - set category = "Preferences.Show/Hide" - set desc = "Toggles seeing Local OutOfCharacter chat" - prefs.toggles ^= PREFTOGGLE_CHAT_LOOC - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle LOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/verb/Toggle_Soundscape() //All new ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful - set name = "Ambience" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing ambient sound effects" - prefs.sound ^= SOUND_AMBIENCE - prefs.save_preferences(src) - if(prefs.sound & SOUND_AMBIENCE) - to_chat(src, "You will now hear ambient sounds.") - else - to_chat(src, "You will no longer hear ambient sounds.") +/datum/preference_toggle/toggle_ooc + name = "Toggle OOC chat" + description = "Toggles seeing OutOfCharacter chat" + preftoggle_bitflag = PREFTOGGLE_CHAT_OOC + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see the OOC channel." + disable_message = "You will no longer see the OOC channel." + blackbox_message = "Toggle OOC" + +/datum/preference_toggle/toggle_looc + name = "Toggle LOOC chat" + description = "Toggles seeing Local OutOfCharacter chat" + preftoggle_bitflag = PREFTOGGLE_CHAT_LOOC + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see the LOOC channel." + disable_message = "You will no longer see the LOOC channel." + blackbox_message = "Toggle LOOC" + +/datum/preference_toggle/toggle_ambience + name = "Toggle Ambient sounds" + description = "Toggles hearing ambient sound effects" + preftoggle_bitflag = SOUND_AMBIENCE + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You now hear ambient sounds." + disable_message = "Ambience is now silenced." + blackbox_message = "Toggle Ambience" + +/datum/preference_toggle/toggle_ambience/set_toggles(client/user) + . = ..() + if(user.prefs.sound & ~SOUND_AMBIENCE) usr.stop_sound_channel(CHANNEL_AMBIENCE) - update_ambience_pref() - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Ambience") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/Toggle_Parallax_Dark() //All new ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful - set name = "Parallax in darkness" - set category = "Preferences.Show/Hide" - set desc = "If enabled, drawing parallax if you see in dark instead of black tiles." - prefs.toggles2 ^= PREFTOGGLE_2_PARALLAX_IN_DARKNESS - prefs.save_preferences(src) - if(prefs.toggles2 & PREFTOGGLE_2_PARALLAX_IN_DARKNESS) - to_chat(src, "You will now see parallax in dark with nightvisions.") - else - to_chat(src, "You will no longer see parallax in dark with nightvisions.") + user.update_ambience_pref() + +/datum/preference_toggle/toggle_parallax_in_darkness + name = "Toggle Parallax in darkness" + description = "Toggles seeing space tiles instead of blank tiles" + preftoggle_bitflag = PREFTOGGLE_2_PARALLAX_IN_DARKNESS + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see parallax in dark with nightvision." + disable_message = "You will no longer see parallax in dark with nightvision." + blackbox_message = "Toggle Parallax Darkness" + +/datum/preference_toggle/toggle_parallax_in_darkness/set_toggles(client/user) + . = ..() usr.hud_used?.update_parallax_pref() - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Parallax Darkness") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/Toggle_Buzz() //No more headaches because headphones bump up shipambience.ogg to insanity levels. - set name = "White Noise" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing ambient white noise" - prefs.sound ^= SOUND_BUZZ - prefs.save_preferences(src) - if(prefs.sound & SOUND_BUZZ) - to_chat(src, "You will now hear ambient white noise.") - else - to_chat(src, "You will no longer hear ambient white noise.") - usr.stop_sound_channel(CHANNEL_BUZZ) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Whitenoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/preference_toggle/toggle_white_noise + name = "Toggle White Noise" + description = "Toggles hearing White Noise" + preftoggle_bitflag = SOUND_BUZZ + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear ambient white noise." + disable_message = "You will no longer hear ambient white noise." + blackbox_message = "Toggle Whitenoise" + +/datum/preference_toggle/toggle_white_noise/set_toggles(client/user) + . = ..() + if(user.prefs.sound & ~SOUND_BUZZ) + usr.stop_sound_channel(CHANNEL_BUZZ) -/client/verb/Toggle_Heartbeat() //to toggle off heartbeat sounds, in case they get too annoying - set name = "Heartbeat" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing heart beating sound effects" - prefs.sound ^= SOUND_HEARTBEAT - prefs.save_preferences(src) - if(prefs.sound & SOUND_HEARTBEAT) - to_chat(src, "You will now hear heartbeat sounds.") - else - to_chat(src, "You will no longer hear heartbeat sounds.") +/datum/preference_toggle/toggle_heartbeat_noise + name = "Toggle Heartbeat noise" + description = "Toggles hearing heartbeat sounds" + preftoggle_bitflag = SOUND_HEARTBEAT + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear heartbeat sounds." + disable_message = "You will no longer hear heartbeat sounds." + blackbox_message = "Toggle Hearbeat" + +/datum/preference_toggle/toggle_heartbeat_noise/set_toggles(client/user) + . = ..() + if(user.prefs.sound & ~SOUND_HEARTBEAT) usr.stop_sound_channel(CHANNEL_HEARTBEAT) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Hearbeat") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -// This needs a toggle because you people are awful and spammed terrible music -/client/verb/toggle_instruments() - set name = "Instruments" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing musical instruments like the violin and piano" - prefs.sound ^= SOUND_INSTRUMENTS - prefs.save_preferences(src) - if(prefs.sound & SOUND_INSTRUMENTS) - to_chat(src, "You will now hear people playing musical instruments.") - else - to_chat(src, "You will no longer hear musical instruments.") - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Instruments") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/toggle_input() - set name = "TGUI Input" - set category = "Preferences.Toggle" - set desc = "Switches inputs between the TGUI and the standard one" - prefs.toggles2 ^= PREFTOGGLE_2_DISABLE_TGUI_INPUT - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_DISABLE_TGUI_INPUT) ? "no longer" : "now"] use TGUI Inputs.") - -/client/verb/Toggle_disco() //to toggle off the disco machine locally, in case it gets too annoying - set name = "Dance Machine" - set category = "Preferences.Hear/Silence" - set desc = "Toggles hearing and dancing to the radiant dance machine" - prefs.sound ^= SOUND_DISCO - prefs.save_preferences(src) - if(prefs.sound & SOUND_DISCO) - to_chat(src, "You will now hear and dance to the radiant dance machine.") - else - to_chat(src, "You will no longer hear or dance to the radiant dance machine.") - usr.stop_sound_channel(CHANNEL_JUKEBOX) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Dance Machine") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/verb/setup_character() - set name = "Game Preferences" - set category = "Preferences" - set desc = "Allows you to access the Setup Character screen. Changes to your character won't take effect until next round, but other changes will." - prefs.current_tab = 1 - prefs.ShowChoices(usr) +/datum/preference_toggle/toggle_instruments + name = "Toggle Instruments" + description = "Toggles hearing musical instruments like the violin and piano" + preftoggle_bitflag = SOUND_INSTRUMENTS + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear people playing musical instruments." + disable_message = "You will no longer hear musical instruments." + blackbox_message = "Toggle Instruments" + +/datum/preference_toggle/toggle_disco + name = "Toggle Disco Machine Music" + description = "Toggles hearing musical instruments like the violin and piano" + preftoggle_bitflag = SOUND_DISCO + preftoggle_toggle = PREFTOGGLE_SOUND + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now hear and dance to the radiant dance machine." + disable_message = "You will no longer hear or dance to the radiant dance machine." + blackbox_message = "Toggle Dance Machine" + +/datum/preference_toggle/toggle_disco/set_toggles(client/user) + . = ..() + if(user.prefs.sound & ~SOUND_DISCO) + usr.stop_sound_channel(CHANNEL_JUKEBOX) -/client/verb/toggle_ghost_pda() - set name = "GhostPDA" - set category = "Preferences.Show/Hide" - set desc = "Toggle seeing PDA messages as an observer." - prefs.toggles ^= PREFTOGGLE_CHAT_GHOSTPDA - to_chat(src, "As a ghost, you will now [(prefs.toggles & PREFTOGGLE_CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].") - prefs.save_preferences(src) - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Ghost PDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/preference_toggle/toggle_ghost_pda + name = "Toggle Ghost PDA messages" + description = "Toggle seeing PDA messages as an observer" + preftoggle_bitflag = PREFTOGGLE_CHAT_GHOSTPDA + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_GHOST + enable_message = "As a ghost, you will now see all PDA messages." + disable_message = "As a ghost, you will no longer see PDA messages." + blackbox_message = "Toggle Ghost PDA" /client/verb/silence_current_midi() set name = "Silence Current Midi" - set category = "Preferences" + set category = "Special Verbs" set desc = "Silence the current admin midi playing" usr.stop_sound_channel(CHANNEL_ADMIN) to_chat(src, "The current admin midi has been silenced") - -/client/verb/toggle_runechat() - set name = "Runechat" - set category = "Preferences.Toggle" - set desc = "Toggle runechat messages" - prefs.toggles2 ^= PREFTOGGLE_2_RUNECHAT - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_RUNECHAT) ? "now see" : "no longer see"] floating chat messages.") - -/client/verb/toggle_death_messages() - set name = "Death Notifications" - set category = "Preferences.Toggle" - set desc = "Toggle player death notifications" - prefs.toggles2 ^= PREFTOGGLE_2_DEATHMESSAGE - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_DEATHMESSAGE) ? "now" : "no longer"] see a notification in deadchat when a player dies.") - -/client/verb/toggle_reverb() - set name = "Reverb" - set category = "Preferences.Toggle" - set desc = "Toggle ingame reverb effects" - prefs.toggles2 ^= PREFTOGGLE_2_REVERB_DISABLE - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_REVERB_DISABLE) ? "no longer" : "now"] get reverb on ingame sounds.") - -/client/verb/toggle_forced_white_runechat() - set name = "Runechat Colour Forcing" - set category = "Preferences.Toggle" - set desc = "Toggles forcing your runechat colour to white" - prefs.toggles2 ^= PREFTOGGLE_2_FORCE_WHITE_RUNECHAT - prefs.save_preferences(src) - to_chat(src, "Your runechats will [(prefs.toggles2 & PREFTOGGLE_2_FORCE_WHITE_RUNECHAT) ? "now" : "no longer"] be forced to be white.") - -/client/verb/toggle_item_outlines() - set name = "Item Outlines" - set category = "Preferences.Toggle" - set desc = "Toggles seeing item outlines on hover." - prefs.toggles2 ^= PREFTOGGLE_2_SEE_ITEM_OUTLINES - prefs.save_preferences(src) - to_chat(usr, "You will [(prefs.toggles2 & PREFTOGGLE_2_SEE_ITEM_OUTLINES) ? "now" : "no longer"] see item outlines on hover.") - -/client/verb/toggle_item_tooltips() - set name = "Hover-over Item Tooltips" - set category = "Preferences.Toggle" - set desc = "Toggles textboxes with the item descriptions after hovering on them in your inventory." - prefs.toggles2 ^= PREFTOGGLE_2_HIDE_ITEM_TOOLTIPS - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_HIDE_ITEM_TOOLTIPS) ? "no longer" : "now"] see item tooltips when you hover over items on your HUD.") - -/mob/verb/toggle_anonmode() - set name = "Anonymous Mode" - set category = "Preferences.Toggle" - set desc = "Toggles showing your key in various parts of the game (deadchat, end round, etc)." - client.prefs.toggles2 ^= PREFTOGGLE_2_ANON - to_chat(src, "Your key will [(client.prefs.toggles2 & PREFTOGGLE_2_ANON) ? "no longer" : "now"] be shown in certain events (end round reports, deadchat, etc).") - client.prefs.save_preferences(src) - -/client/verb/toggle_dance() - set name = "Disco Machine Dancing" - set category = "Preferences.Toggle" - set desc = "Toggles automatic dancing from the radiant dance machine" - prefs.toggles2 ^= PREFTOGGLE_2_DANCE_DISCO - prefs.save_preferences(src) - to_chat(usr, "You will [(prefs.toggles2 & PREFTOGGLE_2_DANCE_DISCO) ? "now" : "no longer"] dance to the radiant dance machine.") - -/client/verb/manage_adminsound_mutes() - set name = "Manage Admin Sound Mutes" - set category = "Preferences" - set desc = "Manage admins that you wont hear played audio from" - - if(!length(prefs.admin_sound_ckey_ignore)) +/datum/preference_toggle/toggle_runechat + name = "Toggle Runechat" + description = "Toggle seeing Runechat messages" + preftoggle_bitflag = PREFTOGGLE_2_RUNECHAT + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now see runechat." + disable_message = "You will no longer see runechat." + blackbox_message = "Toggle Runechat" + +/datum/preference_toggle/toggle_runechat + name = "Toggle Ghost Death Notifications" + description = "Toggle a notification when a player dies" + preftoggle_bitflag = PREFTOGGLE_2_DEATHMESSAGE + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GHOST + enable_message = "You will now see a notification in deadchat when a player dies." + disable_message = "You will no longer see a notification in deadchat when a player dies." + blackbox_message = "Toggle Death Notifications" + +/datum/preference_toggle/toggle_reverb + name = "Toggle Reverb" + description = "Toggles Reverb on specific sounds" + preftoggle_bitflag = PREFTOGGLE_2_REVERB_DISABLE + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now get reverb on some in game sounds." + disable_message = "You will no longer get reverb on some in game sounds." + blackbox_message = "Toggle reverb" + +/datum/preference_toggle/toggle_white_runechat + name = "Toggle Runechat Colour Forcing" + description = "Forces your runechat color to white" + preftoggle_bitflag = PREFTOGGLE_2_FORCE_WHITE_RUNECHAT + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "Your runechat messages are forced to be white." + disable_message = "Your runechat messages are no longer forced to be white." + blackbox_message = "Toggle runechat color" + +/datum/preference_toggle/toggle_simple_stat_panel + name = "Toggle item outlines" + description = "Toggles seeing item outlines on hover" + preftoggle_bitflag = PREFTOGGLE_2_SEE_ITEM_OUTLINES + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_LIVING + enable_message = "You no longer see item outlines when hovering over an item with your mouse." + disable_message = "You now see item outlines when hovering over an item with your mouse." + blackbox_message = "Toggle item outlines" + +/datum/preference_toggle/toggle_item_tooltips + name = "Toggle item tooltips" + description = "Toggles textboxes with the item descriptions after hovering on them in your inventory" + preftoggle_bitflag = PREFTOGGLE_2_HIDE_ITEM_TOOLTIPS + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_LIVING + enable_message = "You no longer see item tooltips." + disable_message = "You now see item tooltips." + blackbox_message = "Toggle item tooltips" + +/datum/preference_toggle/toggle_anonmode + name = "Toggle Anonymous Mode" + description = "Toggles showing your key in various parts of the game (deadchat, end round, etc)" + preftoggle_bitflag = PREFTOGGLE_2_ANON + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "Your key will no longer be shown in certain events (end round reports, deadchat, etc)." + disable_message = "Your key will now will be shown in certain events (end round reports, deadchat, etc)." + blackbox_message = "Toggle Anon mode" + +/datum/preference_toggle/toggle_disco_dance + name = "Toggle Disco Machine Dancing" + description = "Toggles automatic dancing from the radiant dance machine" + preftoggle_bitflag = PREFTOGGLE_2_DANCE_DISCO + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_LIVING + enable_message = "You will now dance to the radiant dance machine." + disable_message = "You will no longer dance to the radiant dance machine." + blackbox_message = "Toggle disco machine dancing" + +/datum/preference_toggle/toggle_typing_indicator + name = "Toggle Typing Indicator" + description = "Hides the typing indicator" + preftoggle_bitflag = PREFTOGGLE_SHOW_TYPING + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_LIVING + enable_message = "You will no longer display a typing indicator." + disable_message = "You will now display a typing indicator." + blackbox_message = "Toggle Typing Indicator (Speech)" + +/datum/preference_toggle/toggle_typing_indicator/set_toggles(client/user) + . = ..() + if(user.prefs.toggles & PREFTOGGLE_SHOW_TYPING) + if(istype(usr)) + usr.set_typing_indicator(FALSE) + usr.set_thinking_indicator(FALSE) + +/datum/preference_toggle/toggle_tgui_input_lists + name = "Toggle TGUI Input" + description = "Switches input lists between the TGUI and the standard one" + preftoggle_bitflag = PREFTOGGLE_2_DISABLE_TGUI_INPUT + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + enable_message = "You will now use TGUI Input." + disable_message = "You will no longer use TGUI Input." + blackbox_message = "Toggle TGUI Input" + +/datum/preference_toggle/toggle_admin_logs + name = "Toggle Admin Log Messages" + description = "Disables admin log messages" + preftoggle_bitflag = PREFTOGGLE_CHAT_NO_ADMINLOGS + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + enable_message = "Admin logs disabled." + disable_message = "Admin logs re-enabled." + blackbox_message = "Admin logs toggled" + +/datum/preference_toggle/toggle_mhelp_notification + name = "Toggle Mentor Ticket Messages" + description = "Disables mentor ticket notifications" + preftoggle_bitflag = PREFTOGGLE_CHAT_NO_MENTORTICKETLOGS + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_MENTOR | R_ADMIN + enable_message = "You now won't get mentor ticket messages." + disable_message = "You now will get mentor ticket messages." + blackbox_message = "Mentor ticket notification toggled" + +/datum/preference_toggle/toggle_ahelp_notification + name = "Toggle Admin Ticket Messages" + description = "Disables admin ticket notifications" + preftoggle_bitflag = PREFTOGGLE_CHAT_NO_TICKETLOGS + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + enable_message = "You now won't get admin ticket messages." + disable_message = "You now will get admin ticket messages." + blackbox_message = "Admin ticket notification toggled" + +/datum/preference_toggle/toggle_debug_logs + name = "Toggle Debug Log Messages" + description = "Disables debug notifications (Runtimes, ghost role notifications, weird checks that weren't removed)" + preftoggle_bitflag = PREFTOGGLE_CHAT_DEBUGLOGS + preftoggle_toggle = PREFTOGGLE_TOGGLE1 + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_VIEWRUNTIMES | R_DEBUG + enable_message = "You now won't get debug logs." + disable_message = "You now will get debug logs." + blackbox_message = "Debug logs toggled" + +/datum/preference_toggle/toggle_mctabs + name = "Toggle MC tab" + description = "Toggles MC tab visibility" + preftoggle_bitflag = PREFTOGGLE_2_MC_TAB + preftoggle_toggle = PREFTOGGLE_TOGGLE2 + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_VIEWRUNTIMES | R_DEBUG + enable_message = "You'll now see subsystem information in the verb panel." + disable_message = "You'll no longer see subsystem information in the verb panel." + blackbox_message = "MC tabs toggled" + +/datum/preference_toggle/special_toggle + preftoggle_toggle = PREFTOGGLE_SPECIAL + +/datum/preference_toggle/special_toggle/set_toggles(client/user) + SSblackbox.record_feedback("tally", "toggle_verbs", 1, blackbox_message) + user.prefs.save_preferences(user) + +/datum/preference_toggle/special_toggle/toggle_adminsound_mutes + name = "Manage Admin Sound Mutes" + description = "Manage admins that you wont hear played audio from" + preftoggle_category = PREFTOGGLE_CATEGORY_GENERAL + blackbox_message = "MC tabs toggled" + +/datum/preference_toggle/special_toggle/toggle_adminsound_mutes/set_toggles(client/user) + if(!length(user.prefs.admin_sound_ckey_ignore)) to_chat(usr, "You have no admins with muted sounds.") return - var/choice = input(usr, "Select an admin to unmute sounds from.", "Pick an admin") as null|anything in prefs.admin_sound_ckey_ignore + var/choice = input(usr, "Select an admin to unmute sounds from.", "Pick an admin") as null|anything in user.prefs.admin_sound_ckey_ignore if(!choice) return - prefs.admin_sound_ckey_ignore -= choice + user.prefs.admin_sound_ckey_ignore -= choice to_chat(usr, "You will now hear sounds from [choice] again.") - prefs.save_preferences(src) - -/client/proc/toggle_mctabs() - set name = "MC Tab" - set category = "Preferences.Show/Hide" - set desc = "Shows or hides the MC tab." - prefs.toggles2 ^= PREFTOGGLE_2_MC_TAB - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles2 & PREFTOGGLE_2_MC_TAB) ? "now" : "no longer"] see the MC tab on the top right.") + return ..() + +/datum/preference_toggle/special_toggle/set_ooc_color + name = "Set Your OOC Color" + description = "Pick a custom OOC color" + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + blackbox_message = "Set Own OOC" + +/datum/preference_toggle/special_toggle/set_ooc_color/set_toggles(client/user) + var/new_ooccolor = input(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) as color|null + if(new_ooccolor) + user.prefs.ooccolor = new_ooccolor + to_chat(usr, "Your OOC color has been set to [new_ooccolor].") + else + user.prefs.ooccolor = initial(user.prefs.ooccolor) + to_chat(usr, "Your OOC color has been reset.") + return ..() + +/datum/preference_toggle/special_toggle/set_attack_logs + name = "Change Attack Log settings" + description = "Changes what attack logs you see, ranges from all attacklogs to no attacklogs" + preftoggle_category = PREFTOGGLE_CATEGORY_ADMIN + rights_required = R_ADMIN + blackbox_message = "changed attack log settings" + +/datum/preference_toggle/special_toggle/set_attack_logs/set_toggles(client/user) + var/static/list/attack_log_settings = list("All attack logs" = ATKLOG_ALL, "Almost all attack logs" = ATKLOG_ALMOSTALL, "Most attack logs" = ATKLOG_MOST, "Few attack logs" = ATKLOG_FEW, "No attack logs" = ATKLOG_NONE) + var/input = input(usr, "Please select your Attack Log settings.") as null|anything in attack_log_settings + if(!input) + return + var/attack_log_type = attack_log_settings[input] + switch(attack_log_type) + if(ATKLOG_ALL) + user.prefs.atklog = ATKLOG_ALL + to_chat(usr, "Your attack logs preference is now: show ALL attack logs") + if(ATKLOG_ALMOSTALL) + user.prefs.atklog = ATKLOG_ALMOSTALL + to_chat(usr, "Your attack logs preference is now: show ALMOST ALL attack logs (notable exceptions: NPCs attacking other NPCs, vampire bites, equipping/stripping, people pushing each other over)") + if(ATKLOG_MOST) + user.prefs.atklog = ATKLOG_MOST + to_chat(usr, "Your attack logs preference is now: show MOST attack logs (like ALMOST ALL, except that it also hides player v. NPC combat, and certain areas like lavaland syndie base and thunderdome)") + if(ATKLOG_FEW) + user.prefs.atklog = ATKLOG_FEW + to_chat(usr, "Your attack logs preference is now: show FEW attack logs (only the most important stuff: attacks on SSDs, use of explosives, messing with the engine, gibbing, AI wiping, forcefeeding, acid sprays, and organ extraction)") + if(ATKLOG_NONE) + user.prefs.atklog = ATKLOG_NONE + to_chat(usr, "Your attack logs preference is now: show NO attack logs") + return ..() diff --git a/code/modules/client/preference/preferences_volume_mixer.dm b/code/modules/client/preference/preferences_volume_mixer.dm index c9e84373153b..61f3e134f65b 100644 --- a/code/modules/client/preference/preferences_volume_mixer.dm +++ b/code/modules/client/preference/preferences_volume_mixer.dm @@ -88,7 +88,7 @@ /client/verb/volume_mixer() set name = "Open Volume Mixer" - set category = "Preferences" + set category = null set hidden = TRUE var/datum/ui_module/volume_mixer/VM = new() diff --git a/code/modules/client/view.dm b/code/modules/client/view.dm index 7832ba0419fc..18966f49678a 100644 --- a/code/modules/client/view.dm +++ b/code/modules/client/view.dm @@ -4,9 +4,6 @@ * Also includes */ -/* Defines */ -#define CUSTOM_VIEWRANGES list(1, 2, 3, 4, 5, 6, "RESET") - /client/proc/AddViewMod(id, size) var/datum/viewmod/V = new /datum/viewmod(id, size) ViewMods[V.id] = V @@ -69,22 +66,3 @@ /* Client verbs */ /proc/viewNum_to_text(view) return "[(view * 2) + 1]x[(view * 2) + 1]" - -/client/verb/set_view_range() - set name = "Set View Range" - set category = "Preferences" - - var/view_range = tgui_input_list(src.mob, "Select a view range", "Set View Range", CUSTOM_VIEWRANGES, "RESET") - - if(!view_range) - return - - RemoveViewMod("custom") - if(view_range == "RESET") - to_chat(src, "View range reset.") - return - - to_chat(src, "View range set to [viewNum_to_text(view_range)]") - AddViewMod("custom", view_range) - -#undef CUSTOM_VIEWRANGES diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 1a057782f162..72465c1a2a44 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -611,6 +611,23 @@ /obj/item/clothing/suit/proc/setup_shielding() return +///Hierophant card shielding. Saves me time. +/obj/item/clothing/suit/proc/setup_hierophant_shielding() + var/datum/component/shielded/shield = GetComponent(/datum/component/shielded) + if(!shield) + AddComponent(/datum/component/shielded, recharge_start_delay = 0 SECONDS, shield_icon = "shield-hierophant", run_hit_callback = CALLBACK(src, PROC_REF(hierophant_shield_damaged))) + return + if(shield.shield_icon == "shield-hierophant") //If the hierophant shield has been used, recharge it. Otherwise, it's a shielded component we don't want to touch + shield.current_charges = 3 + +/// A proc for callback when the shield breaks, since I am stupid and want custom effects. +/obj/item/clothing/suit/proc/hierophant_shield_damaged(mob/living/wearer, attack_text, new_current_charges) + wearer.visible_message("[attack_text] is deflected in a burst of dark-purple sparks!") + new /obj/effect/temp_visual/cult/sparks/hierophant(get_turf(wearer)) + playsound(wearer,'sound/magic/blind.ogg', 200, TRUE, -2) + if(new_current_charges == 0) + wearer.visible_message("The runed shield around [wearer] suddenly disappears!") + //Proc that opens and closes jackets. /obj/item/clothing/suit/proc/adjustsuit(mob/user) if(ignore_suitadjust) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 65386ffc6de1..099d24c5e85e 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -69,6 +69,18 @@ if(23) new /obj/item/borg/upgrade/modkit/lifesteal(src) new /obj/item/bedsheet/cult(src) + if(24) + switch(rand(1, 11)) + if(1) + new /obj/item/blank_tarot_card(src) + if(2 to 5) + new /obj/item/tarot_card_pack(src) + if(6 to 8) + new /obj/item/tarot_card_pack/jumbo(src) + if(9, 10) + new /obj/item/tarot_card_pack/mega(src) + if(11) + new /obj/item/tarot_generator(src) // ~1/250? Seems reasonable //KA modkit design discs /obj/item/disk/design_disk/modkit_disk diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 7a51a6bc812d..aa68095ad627 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -46,38 +46,12 @@ set_light(2, 3, l_color = GET_CULT_DATA(construct_glow, LIGHT_COLOR_BLOOD_MAGIC)) -/mob/living/simple_animal/hostile/construct/Destroy() - mind?.remove_antag_datum(/datum/antagonist/cultist, silent_removal = TRUE) - mind?.remove_antag_datum(/datum/antagonist/wizard/construct, silent_removal = TRUE) - remove_held_body() - return ..() - /mob/living/simple_animal/hostile/construct/death(gibbed) - mind?.remove_antag_datum(/datum/antagonist/cultist, silent_removal = TRUE) - mind?.remove_antag_datum(/datum/antagonist/wizard/construct, silent_removal = TRUE) - if(held_body) // Null check for empty bodies - held_body.forceMove(get_turf(src)) - SSticker.mode?.cult_team?.add_cult_immunity(held_body) - if(ismob(held_body)) // Check if the held_body is a mob - held_body.key = key - else if(istype(held_body, /obj/item/organ/internal/brain)) // Check if the held_body is a brain - var/obj/item/organ/internal/brain/brain = held_body - if(brain.brainmob) // Check if the brain has a brainmob - brain.brainmob.key = key // Set the key to the brainmob - brain.brainmob.mind.transfer_to(brain.brainmob) // Transfer the mind to the brainmob - held_body.cancel_camera() + // we also drop our heldbody from the /construct_held_body component, as well as our cult/wiz construct antag datums new /obj/effect/temp_visual/cult/sparks(get_turf(src)) playsound(src, 'sound/effects/pylon_shatter.ogg', 40, TRUE) return ..() -/mob/living/simple_animal/hostile/construct/proc/add_held_body(atom/movable/body) - held_body = body - RegisterSignal(body, COMSIG_PARENT_QDELETING, PROC_REF(remove_held_body)) - -/mob/living/simple_animal/hostile/construct/proc/remove_held_body() - SIGNAL_HANDLER - held_body = null - /mob/living/simple_animal/hostile/construct/examine(mob/user) . = ..() diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index d89aed14643b..2fecb79c254c 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -34,11 +34,6 @@ deathmessage = "lets out a contented sigh as their form unwinds." var/holy = FALSE -/mob/living/simple_animal/shade/Destroy() - mind?.remove_antag_datum(/datum/antagonist/cultist, silent_removal = TRUE) - mind?.remove_antag_datum(/datum/antagonist/wizard/construct, silent_removal = TRUE) - return ..() - /mob/living/simple_animal/shade/attackby(obj/item/O, mob/user) //Marker -Agouri if(istype(O, /obj/item/soulstone)) var/obj/item/soulstone/SS = O diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index f2ff761520e0..ed9505e5c13b 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -152,7 +152,6 @@ real_name = name if(!loc) stack_trace("Simple animal being instantiated in nullspace") - remove_verb(src, /mob/verb/observe) if(can_hide) var/datum/action/innate/hide/hide = new() hide.Grant(src) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index bca60f87eee1..158b59b5ff9b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -769,112 +769,8 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ else return "[copytext_preserve_html(msg, 1, 37)]... More..." -// Nobody in their right mind will have this enabled on the production server, uncomment if you want this for some reason -/* -/mob/verb/abandon_mob() - set name = "Respawn" - set category = "OOC" - - if(!GLOB.configuration.general.respawn_enabled) - to_chat(usr, "Respawning is disabled.") - return - - if(stat != DEAD || !SSticker) - to_chat(usr, "You must be dead to use this!") - return - - log_game("[key_name(usr)] has respawned.") - - to_chat(usr, "Make sure to play a different character, and please roleplay correctly!") - - if(!client) - log_game("[key_name(usr)] respawn failed due to disconnect.") - return - client.screen.Cut() - client.screen += client.void - - if(!client) - log_game("[key_name(usr)] respawn failed due to disconnect.") - return - - var/mob/new_player/M = new /mob/new_player() - if(!client) - log_game("[key_name(usr)] respawn failed due to disconnect.") - qdel(M) - return - - M.key = key - return - -*/ -/mob/verb/observe() - set name = "Observe" - set category = "OOC" - var/is_admin = 0 - - if(client.holder && (client.holder.rights & R_ADMIN)) - is_admin = 1 - else if(stat != DEAD || isnewplayer(src)) - to_chat(usr, "You must be observing to use this!") - return - - if(is_admin && stat == DEAD) - is_admin = 0 - - var/list/names = list() - var/list/namecounts = list() - var/list/creatures = list() - - for(var/obj/O in GLOB.poi_list) - if(!O.loc) - continue - if(istype(O, /obj/item/disk/nuclear)) - var/name = "Nuclear Disk" - if(names.Find(name)) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - creatures[name] = O - - if(istype(O, /obj/singularity)) - var/name = "Singularity" - if(names.Find(name)) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - creatures[name] = O - - - for(var/mob/M in sortAtom(GLOB.mob_list)) - var/name = M.name - if(names.Find(name)) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - - creatures[name] = M - - - client.perspective = EYE_PERSPECTIVE - - var/eye_name = null - - var/ok = "[is_admin ? "Admin Observe" : "Observe"]" - eye_name = tgui_input_list(usr, "Please, select a player!", ok, creatures) - - if(!eye_name) - return - - var/mob/mob_eye = creatures[eye_name] - - if(client && mob_eye) - client.eye = mob_eye +/mob/proc/is_dead() + return stat == DEAD /mob/verb/cancel_camera() set name = "Cancel Camera View" diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index dc361ba9e2dd..02df559452db 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -92,19 +92,3 @@ GLOBAL_LIST_EMPTY(thinking_indicator) set_typing_indicator(FALSE) if(message) me_verb(message) - -/client/verb/typing_indicator() - set name = "Typing Indicator" - set category = "Preferences.Show/Hide" - set desc = "Toggles showing a typing/thought indicator when you have TGUIsay open." - prefs.toggles ^= PREFTOGGLE_SHOW_TYPING - prefs.save_preferences(src) - to_chat(src, "You will [(prefs.toggles & PREFTOGGLE_SHOW_TYPING) ? "no longer" : "now"] display a typing/thought indicator when you have TGUIsay open.") - - // Clear out any existing typing indicator. - if(prefs.toggles & PREFTOGGLE_SHOW_TYPING) - if(istype(mob)) - mob.set_typing_indicator(FALSE) - mob.set_thinking_indicator(FALSE) - - SSblackbox.record_feedback("tally", "toggle_verbs", 1, "Toggle Typing Indicator (Speech)") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index 7e43f0ada910..bc55296544c2 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -245,6 +245,7 @@ /obj/item/ammo_casing/shotgun/ion name = "ion shell" desc = "An advanced 12 gauge shell that fires a spread of ion bolts." + icon_state = "ionshell" projectile_type = /obj/item/projectile/ion/weak pellets = 4 variance = 35 diff --git a/code/modules/reagents/chemistry/reagents_holder.dm b/code/modules/reagents/chemistry/reagents_holder.dm index 851788b90d88..4d68ff36daa4 100644 --- a/code/modules/reagents/chemistry/reagents_holder.dm +++ b/code/modules/reagents/chemistry/reagents_holder.dm @@ -969,6 +969,17 @@ var/picked_reagent = pick(random_reagents) return picked_reagent +/// Returns a random reagent ID, with real non blacklisted balance boosting action! +/proc/get_unrestricted_random_reagent_id() + var/static/list/random_reagents + if(!length(random_reagents)) + random_reagents = list() + for(var/datum/reagent/thing as anything in subtypesof(/datum/reagent)) + var/R = initial(thing.id) + random_reagents += R + var/picked_reagent = pick(random_reagents) + return picked_reagent + /datum/reagents/proc/get_reagent_from_id(id) var/datum/reagent/result = null for(var/A in reagent_list) diff --git a/code/modules/station_goals/bluespace_tap.dm b/code/modules/station_goals/bluespace_tap.dm index cfa51561e127..3f88756eeb36 100644 --- a/code/modules/station_goals/bluespace_tap.dm +++ b/code/modules/station_goals/bluespace_tap.dm @@ -116,7 +116,11 @@ /obj/item/bedsheet/cult = 2, /obj/item/bedsheet/wiz = 2, /obj/item/stack/sheet/mineral/tranquillite/fifty = 3, - /obj/item/clothing/gloves/combat = 5 + /obj/item/clothing/gloves/combat = 5, + /obj/item/blank_tarot_card = 5, + /obj/item/tarot_card_pack = 5, + /obj/item/tarot_card_pack/jumbo = 3, + /obj/item/tarot_card_pack/mega = 2 ) /obj/effect/spawner/lootdrop/bluespace_tap/organic diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index c2e847de6a97..9dac53823a47 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -292,7 +292,7 @@ /obj/item/organ/internal/heart/cybernetic/upgraded/proc/shock_heart(mob/living/carbon/human/source, intensity) - SIGNAL_HANDLER_DOES_SLEEP + SIGNAL_HANDLER // COMSIG_LIVING_MINOR_SHOCK + COMSIG_LIVING_ELECTROCUTE_ACT if(!ishuman(owner)) return @@ -305,10 +305,11 @@ if(emagged && !(status & ORGAN_DEAD)) if(prob(numHigh)) to_chat(owner, "Your [name] spasms violently!") - owner.adjustBruteLoss(numHigh) + // invoke asyncs here because this sleeps + INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob/living/carbon/human, adjustBruteLoss), numHigh) if(prob(numHigh)) to_chat(owner, "Your [name] shocks you painfully!") - owner.adjustFireLoss(numHigh) + INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob/living/carbon/human, adjustFireLoss), numHigh) if(prob(numMid)) to_chat(owner, "Your [name] lurches awkwardly!") owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) @@ -318,14 +319,14 @@ heart_datum.change_beating(FALSE) // Rambunctious Crew - Stop My Fucking Heart if(prob(numLow)) to_chat(owner, "Your [name] shuts down!") - necrotize() + INVOKE_ASYNC(src, PROC_REF(necrotize)) else if(!emagged && !(status & ORGAN_DEAD)) if(prob(numMid)) to_chat(owner, "Your [name] spasms violently!") - owner.adjustBruteLoss(numMid) + INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob/living/carbon/human, adjustBruteLoss), numMid) if(prob(numMid)) to_chat(owner, "Your [name] shocks you painfully!") - owner.adjustFireLoss(numMid) + INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob/living/carbon/human, adjustFireLoss), numMid) if(prob(numLow)) to_chat(owner, "Your [name] lurches awkwardly!") owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index d24193821713..656a8380279d 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index 7596aae9be05..3818dd761e92 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/icons/obj/playing_cards.dmi b/icons/obj/playing_cards.dmi index 866cf0a5fc42..58d9508af72d 100644 Binary files a/icons/obj/playing_cards.dmi and b/icons/obj/playing_cards.dmi differ diff --git a/modular_ss220/text_to_speech/code/tts_component.dm b/modular_ss220/text_to_speech/code/tts_component.dm index 85579d2c9406..6acd969be636 100644 --- a/modular_ss220/text_to_speech/code/tts_component.dm +++ b/modular_ss220/text_to_speech/code/tts_component.dm @@ -89,8 +89,10 @@ return new_tts_seed /datum/component/tts_component/proc/tts_seed_change(atom/being_changed, mob/chooser, override = FALSE, fancy_voice_input_tgui = FALSE, list/new_traits = null) - SIGNAL_HANDLER_DOES_SLEEP - set waitfor = FALSE + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(set_tts_seed), being_changed, chooser, override, fancy_voice_input_tgui, new_traits) + +/datum/component/tts_component/proc/set_tts_seed(atom/being_changed, mob/chooser, override = FALSE, fancy_voice_input_tgui = FALSE, list/new_traits = null) var/datum/tts_seed/new_tts_seed = select_tts_seed(chooser = chooser, override = override, fancy_voice_input_tgui = fancy_voice_input_tgui, new_traits = new_traits) if(!new_tts_seed) return null diff --git a/paradise.dme b/paradise.dme index 89bc1814f21d..63585d4056ae 100644 --- a/paradise.dme +++ b/paradise.dme @@ -200,6 +200,7 @@ #include "code\_globalvars\lists\mob_lists.dm" #include "code\_globalvars\lists\names.dm" #include "code\_globalvars\lists\objects.dm" +#include "code\_globalvars\lists\preference_toggle_lists.dm" #include "code\_globalvars\lists\reagents_lists.dm" #include "code\_globalvars\lists\typecache.dm" #include "code\_onclick\adjacent.dm" @@ -402,6 +403,7 @@ #include "code\datums\components\codeword_hearing.dm" #include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\corpse_description.dm" +#include "code\datums\components\cult_held_body.dm" #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\decal.dm" #include "code\datums\components\defibrillator.dm" @@ -754,6 +756,7 @@ #include "code\game\gamemodes\vampire\vampire_gamemode.dm" #include "code\game\gamemodes\wizard\artefact.dm" #include "code\game\gamemodes\wizard\godhand.dm" +#include "code\game\gamemodes\wizard\magic_tarot.dm" #include "code\game\gamemodes\wizard\raginmages.dm" #include "code\game\gamemodes\wizard\rightandwrong.dm" #include "code\game\gamemodes\wizard\soulstone.dm"

[category]

[toggle.name][toggle.description]Admin Restricted.
Adjust[(toggles & toggle.preftoggle_bitflag) ? "Enabled" : "Disabled"][(toggles2 & toggle.preftoggle_bitflag) ? "Enabled" : "Disabled"][(sound & toggle.preftoggle_bitflag) ? "Enabled" : "Disabled"]