From dcd2d0e418fbd85c3e990a02f61ab05d2993e1e1 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 23 Jul 2023 04:47:17 +0200 Subject: [PATCH] [MIRROR] Adds a wizard Right and Wrong that lets the caster give one spell (or relic) to everyone on the station [MDB IGNORE] (#22637) * Adds a wizard Right and Wrong that lets the caster give one spell (or relic) to everyone on the station (#76974) ## About The Pull Request This PR adds a new wizard ritual (the kind that require 100 threat on dynamic) This ritual allows the wizard to select one spellbook entry (item or spell), to which everyone on the station will be given or taught said spell or item. If the spell requires a robe, the spell becomes robeless, and if the item requires wizard to use, it makes it usable. Mostly. - Want an epic sword fight? Give everyone a high-frequency blade - One mindswap not enough shenanigans for you? Give out mindswap - Fourth of July? Fireball would be pretty hilarious... The wizard ritual costs 3 points plus the cost of whatever entry you are giving out. So giving everyone fireball is 5 points. It can only be cast once by a wizard, because I didn't want to go through the effort to allow multiple in existence ## Why It's Good For The Game Someone gave me the idea and I thought it sounded pretty funny as an alternative to Summon Magic Maybe I make this a Grand Finale ritual instead / in tandem? That's also an idea. ## Changelog :cl: Melbert add: Wizards have a new Right and Wrong: Mass Teaching, allowing them to grant everyone on the station one spell or relic of their choice! /:cl: * Adds a wizard Right and Wrong that lets the caster give one spell (or relic) to everyone on the station --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> --- code/__DEFINES/traits.dm | 2 + .../items/granters/magic/_spell_granter.dm | 2 +- code/game/objects/items/weaponry.dm | 2 +- .../antagonists/_common/antag_helpers.dm | 7 +- .../antagonists/survivalist/survivalist.dm | 8 ++ .../antagonists/wizard/equipment/soulstone.dm | 4 +- .../equipment/spellbook_entries/_entry.dm | 43 +++--- .../equipment/spellbook_entries/assistance.dm | 2 +- .../equipment/spellbook_entries/challenges.dm | 3 + .../equipment/spellbook_entries/summons.dm | 73 +++++++++- code/modules/antagonists/wizard/wizard.dm | 7 + code/modules/projectiles/guns/magic/staff.dm | 5 +- .../spells/spell_types/right_and_wrong.dm | 134 +++++++++++++----- 13 files changed, 221 insertions(+), 71 deletions(-) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index d7d134e00ac..a0360bb93f7 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -303,6 +303,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock" /// This mob recently blocked magic with some form of antimagic #define TRAIT_RECENTLY_BLOCKED_MAGIC "recently_blocked_magic" +/// The user can do things like use magic staffs without penalty +#define TRAIT_MAGICALLY_GIFTED "magically_gifted" #define TRAIT_DEPRESSION "depression" #define TRAIT_BLOOD_DEFICIENCY "blood_deficiency" #define TRAIT_JOLLY "jolly" diff --git a/code/game/objects/items/granters/magic/_spell_granter.dm b/code/game/objects/items/granters/magic/_spell_granter.dm index e7d8f4f8b0f..2c9ea98c979 100644 --- a/code/game/objects/items/granters/magic/_spell_granter.dm +++ b/code/game/objects/items/granters/magic/_spell_granter.dm @@ -33,7 +33,7 @@ if(!granted_action) CRASH("Someone attempted to learn [type], which did not have an spell set.") if(locate(granted_action) in user.actions) - if(IS_WIZARD(user)) + if(HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) to_chat(user, span_warning("You're already far more versed in the spell [action_name] \ than this flimsy how-to book can provide!")) else diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 2c55feebdec..960289fb750 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -1107,7 +1107,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 bare_wound_bonus = 25 /obj/item/highfrequencyblade/wizard/attack_self(mob/user, modifiers) - if(!IS_WIZARD(user)) + if(!HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) balloon_alert(user, "you're too weak!") return return ..() diff --git a/code/modules/antagonists/_common/antag_helpers.dm b/code/modules/antagonists/_common/antag_helpers.dm index a0cf5a63d70..6ccaa80e55f 100644 --- a/code/modules/antagonists/_common/antag_helpers.dm +++ b/code/modules/antagonists/_common/antag_helpers.dm @@ -1,12 +1,13 @@ -//Returns MINDS of the assigned antags of given type/subtypes -/proc/get_antag_minds(antag_type,specific = FALSE) +/// Returns MINDS of the assigned antags of given type/subtypes +/// Supplying no antag type grants all minds with antag datums +/proc/get_antag_minds(antag_type, specific = FALSE) RETURN_TYPE(/list/datum/mind) . = list() for(var/datum/antagonist/A in GLOB.antagonists) if(!A.owner) continue if(!antag_type || !specific && istype(A,antag_type) || specific && A.type == antag_type) - . += A.owner + . |= A.owner /// From a list of players (minds, mobs or clients), finds the one with the highest playtime (either from a specific role or overall living) and returns it. /proc/get_most_experienced(list/players, specific_role) diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index e4642efcbab..7cb9df6ed25 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -42,3 +42,11 @@ magic.owner = owner objectives += magic ..() + +/datum/antagonist/survivalist/magic/on_gain() + . = ..() + ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) + +/datum/antagonist/survivalist/magic/on_removal() + REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) + return..() diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index b375ac10a30..9162a276847 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -271,7 +271,7 @@ /obj/structure/constructshell/examine(mob/user) . = ..() - if(IS_CULTIST(user) || IS_WIZARD(user) || user.stat == DEAD) + if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD) . += {"A construct shell, used to house bound souls from a soulstone.\n Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n An Artificer, which can produce more shells and soulstones, as well as fortifications.\n @@ -281,7 +281,7 @@ /obj/structure/constructshell/attackby(obj/item/O, mob/user, params) if(istype(O, /obj/item/soulstone)) var/obj/item/soulstone/SS = O - if(!IS_CULTIST(user) && !IS_WIZARD(user) && !SS.theme == THEME_HOLY) + if(!IS_CULTIST(user) && !HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) && !SS.theme == THEME_HOLY) to_chat(user, span_danger("An overwhelming feeling of dread comes over you as you attempt to place [SS] into the shell. It would be wise to be rid of this quickly.")) if(isliving(user)) var/mob/living/living_user = user diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm index 0ba1345cb53..0586d7ea6da 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm @@ -79,9 +79,9 @@ * * user - the mob who's bought the spell * * book - what book they've bought the spell from * - * Return TRUE if the purchase was successful, FALSE otherwise + * Return truthy if the purchase was successful, FALSE otherwise */ -/datum/spellbook_entry/proc/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) +/datum/spellbook_entry/proc/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) var/datum/action/cooldown/spell/existing = locate(spell_type) in user.actions if(existing) var/before_name = existing.name @@ -94,20 +94,23 @@ //we'll need to update the cooldowns for the spellbook set_spell_info() - log_spellbook("[key_name(user)] improved their knowledge of [initial(existing.name)] to level [existing.spell_level] for [cost] points") - SSblackbox.record_feedback("nested tally", "wizard_spell_improved", 1, list("[name]", "[existing.spell_level]")) - log_purchase(user.key) - return TRUE + + if(log_buy) + log_spellbook("[key_name(user)] improved their knowledge of [initial(existing.name)] to level [existing.spell_level] for [cost] points") + SSblackbox.record_feedback("nested tally", "wizard_spell_improved", 1, list("[name]", "[existing.spell_level]")) + log_purchase(user.key) + return existing //No same spell found - just learn it var/datum/action/cooldown/spell/new_spell = new spell_type(user.mind || user) new_spell.Grant(user) to_chat(user, span_notice("You have learned [new_spell.name].")) - log_spellbook("[key_name(user)] learned [new_spell] for [cost] points") - SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) - log_purchase(user.key) - return TRUE + if(log_buy) + log_spellbook("[key_name(user)] learned [new_spell] for [cost] points") + SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) + log_purchase(user.key) + return new_spell /datum/spellbook_entry/proc/log_purchase(key) if(!islist(GLOB.wizard_spellbook_purchases_by_key[key])) @@ -194,12 +197,13 @@ /// Typepath of what item we create when purchased var/obj/item/item_path -/datum/spellbook_entry/item/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) - var/atom/spawned_path = new item_path(get_turf(user)) - log_spellbook("[key_name(user)] bought [src] for [cost] points") - SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) +/datum/spellbook_entry/item/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) + var/atom/spawned_path = new item_path(user.loc) + if(log_buy) + log_spellbook("[key_name(user)] bought [src] for [cost] points") + SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) + log_purchase(user.key) try_equip_item(user, spawned_path) - log_purchase(user.key) return spawned_path /// Attempts to give the item to the buyer on purchase. @@ -214,10 +218,11 @@ refundable = FALSE buy_word = "Cast" -/datum/spellbook_entry/summon/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) - log_spellbook("[key_name(user)] cast [src] for [cost] points") - SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) - log_purchase(user.key) +/datum/spellbook_entry/summon/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) + if(log_buy) + log_spellbook("[key_name(user)] cast [src] for [cost] points") + SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name) + log_purchase(user.key) book.update_static_data(user) // updates "times" var return TRUE diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm index 130b9e66264..7704e11a1cb 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm @@ -44,7 +44,7 @@ var/was_equipped = user.equip_to_slot_if_possible(to_equip, ITEM_SLOT_BELT, disable_warning = TRUE) to_chat(user, span_notice("\A [to_equip.name] has been summoned [was_equipped ? "on your waist" : "at your feet"].")) -/datum/spellbook_entry/item/soulstones/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) +/datum/spellbook_entry/item/soulstones/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) . =..() if(!.) return diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/challenges.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/challenges.dm index d200b897588..cf86c657514 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/challenges.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/challenges.dm @@ -8,3 +8,6 @@ /datum/spellbook_entry/challenge/antiwizard name = "Friendly Wizard Scum" desc = "A \"Friendly\" Wizard will protect the station, and try to kill you. They get a spellbook much like you, but will use it for \"GOOD\"." + +/datum/spellbook_entry/challenge/can_be_purchased() + return FALSE diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm index 37bdef69a64..304c98215ae 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm @@ -9,7 +9,7 @@ and some will use their incredibly minor abilities to frustrate you." cost = 0 -/datum/spellbook_entry/summon/ghosts/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) +/datum/spellbook_entry/summon/ghosts/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) summon_ghosts(user) playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, TRUE) return ..() @@ -27,7 +27,7 @@ // Also must be config enabled return !CONFIG_GET(flag/no_summon_guns) -/datum/spellbook_entry/summon/guns/buy_spell(mob/living/carbon/human/user,obj/item/spellbook/book) +/datum/spellbook_entry/summon/guns/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) summon_guns(user, 10) playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) return ..() @@ -45,7 +45,7 @@ // Also must be config enabled return !CONFIG_GET(flag/no_summon_magic) -/datum/spellbook_entry/summon/magic/buy_spell(mob/living/carbon/human/user,obj/item/spellbook/book) +/datum/spellbook_entry/summon/magic/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) summon_magic(user, 10) playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) return ..() @@ -66,7 +66,7 @@ // Also, must be config enabled return !CONFIG_GET(flag/no_summon_events) -/datum/spellbook_entry/summon/events/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) +/datum/spellbook_entry/summon/events/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) summon_events(user) playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) return ..() @@ -76,12 +76,73 @@ desc = "Curses the station, warping the minds of everyone inside, causing lasting traumas. Warning: this spell can affect you if not cast from a safe distance." cost = 4 -/datum/spellbook_entry/summon/curse_of_madness/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) +/datum/spellbook_entry/summon/curse_of_madness/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) var/message = tgui_input_text(user, "Whisper a secret truth to drive your victims to madness", "Whispers of Madness") - if(!message) + if(!message || QDELETED(user) || QDELETED(book) || !can_buy(user, book)) return FALSE curse_of_madness(user, message) playsound(user, 'sound/magic/mandswap.ogg', 50, TRUE) return ..() +/// A wizard ritual that allows the wizard to teach a specific spellbook enty to everyone on the station. +/// This includes item entries (which will be given to everyone) but disincludes other rituals like itself +/datum/spellbook_entry/summon/specific_spell + name = "Mass Wizard Teaching" + desc = "Teach a specific spell (or give a specific item) to everyone on the station. \ + The cost of this is increased by the cost of the spell you choose. And don't worry - you, too, will learn the spell!" + cost = 3 // cheapest is 4 cost, most expensive is 7 cost + limit = 1 + +/datum/spellbook_entry/summon/specific_spell/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE) + var/list/spell_options = list() + for(var/datum/spellbook_entry/entry as anything in book.entries) + if(istype(entry, /datum/spellbook_entry/summon)) + continue + if(!entry.can_be_purchased()) + continue + + spell_options[entry.name] = entry + + sortTim(spell_options, GLOBAL_PROC_REF(cmp_text_asc)) + var/chosen_spell_name = tgui_input_list(user, "Choose a spell (or item) to grant to everyone...", "Wizardly Teaching", spell_options) + if(isnull(chosen_spell_name) || QDELETED(user) || QDELETED(book)) + return FALSE + if(GLOB.mass_teaching) + tgui_alert(user, "Someone's already cast [name]!", "Wizardly Teaching", list("Shame")) + return FALSE + + var/datum/spellbook_entry/chosen_entry = spell_options[chosen_spell_name] + if(cost + chosen_entry.cost > book.uses) + tgui_alert(user, "You can't afford to grant everyone [chosen_spell_name]! ([cost] points needed)", "Wizardly Teaching", list("Shame")) + return FALSE + + cost += chosen_entry.cost + if(!can_buy(user, book)) + cost = initial(cost) + return FALSE + + GLOB.mass_teaching = new(chosen_entry.type) + GLOB.mass_teaching.equip_all_affected() + + var/item_entry = istype(chosen_entry, /datum/spellbook_entry/item) + to_chat(user, span_hypnophrase("You have [item_entry ? "granted everyone the power" : "taught everyone the ways"] of [chosen_spell_name]!")) + message_admins("[ADMIN_LOOKUPFLW(user)] gave everyone the [item_entry ? "item" : "spell"] \"[chosen_spell_name]\"!") + user.log_message("has gave everyone the [item_entry ? "item" : "spell"] \"[chosen_spell_name]\"!", LOG_GAME) + + name = "[name]: [chosen_spell_name]" + return ..() + +/datum/spellbook_entry/summon/specific_spell/can_buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(GLOB.mass_teaching) + return FALSE + return ..() + +/datum/spellbook_entry/summon/specific_spell/can_be_purchased() + var/datum/game_mode/dynamic/mode = SSticker.mode + if(mode.threat_level < MINIMUM_THREAT_FOR_RITUALS) + return FALSE + if(GLOB.mass_teaching) + return FALSE + return ..() + #undef MINIMUM_THREAT_FOR_RITUALS diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index a080f37ce3d..398f43b3fba 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -55,6 +55,11 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) /datum/antagonist/wizard_minion/on_gain() create_objectives() + . = ..() + ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) + +/datum/antagonist/wizard_minion/on_removal() + REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) return ..() /datum/antagonist/wizard_minion/proc/create_objectives() @@ -81,6 +86,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) . = ..() if(allow_rename) rename_wizard() + ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) /datum/antagonist/wizard/Destroy() QDEL_NULL(ritual) @@ -174,6 +180,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) qdel(spell) owner.current.actions -= spell + REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) return ..() /datum/antagonist/wizard/proc/equip_wizard() diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index dadd865f585..b0c98406b1d 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -11,10 +11,7 @@ var/allow_intruder_use = FALSE /obj/item/gun/magic/staff/proc/is_wizard_or_friend(mob/user) - if(!user?.mind?.has_antag_datum(/datum/antagonist/wizard) \ - && !user.mind.has_antag_datum(/datum/antagonist/survivalist/magic) \ - && !user.mind.has_antag_datum(/datum/antagonist/wizard_minion) \ - && !allow_intruder_use) + if(!HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) && !allow_intruder_use) return FALSE return TRUE diff --git a/code/modules/spells/spell_types/right_and_wrong.dm b/code/modules/spells/spell_types/right_and_wrong.dm index fb6a132abfa..94ad9f96ffb 100644 --- a/code/modules/spells/spell_types/right_and_wrong.dm +++ b/code/modules/spells/spell_types/right_and_wrong.dm @@ -1,8 +1,12 @@ //In this file: Summon Magic/Summon Guns/Summon Events //and corresponding datum controller for them -GLOBAL_DATUM(summon_guns, /datum/summon_things_controller) -GLOBAL_DATUM(summon_magic, /datum/summon_things_controller) +/// A global singleton datum used to store a "summon things controller" for Summon Guns, to grant random guns to stationgoers and latejoiners +GLOBAL_DATUM(summon_guns, /datum/summon_things_controller/item) +/// A global singleton datum used to store a "summon things controller" for Summon Magic, to grant random magical items to stationgoers and latejoiners +GLOBAL_DATUM(summon_magic, /datum/summon_things_controller/item) +/// A global singleton datum used to store a "summon things controller" for Mass Teaching, to grant a specific spellbook entry to stationgoers and latejoiners +GLOBAL_DATUM(mass_teaching, /datum/summon_things_controller/spellbook_entry) // 1 in 50 chance of getting something really special. #define SPECIALIST_MAGIC_PROB 2 @@ -136,7 +140,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( CRASH("give_magic() was called without a summon magic global datum!") if(to_equip.stat == DEAD || !to_equip.client || !to_equip.mind) return - if(IS_WIZARD(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/guns)) + if(IS_WIZARD(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/magic)) return if(!length(to_equip.mind.antag_datums) && prob(GLOB.summon_magic.survivor_probability)) @@ -154,7 +158,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( if(magic_type in GLOB.summoned_special_magic) to_chat(to_equip, span_notice("You feel incredibly lucky.")) -/* +/** * Triggers Summon Ghosts from [user]. */ /proc/summon_ghosts(mob/user) @@ -174,7 +178,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( if(user) to_chat(user, span_warning("You... try to summon ghosts, but nothing seems to happen. Shame.")) -/* +/** * Triggers Summon Magic from [user]. * Can optionally be passed [survivor_probability], to set the chance of creating survivalists. * If Summon Magic has already been triggered, gives out magic to everyone again. @@ -191,10 +195,10 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( if(GLOB.summon_magic) GLOB.summon_magic.survivor_probability = survivor_probability else - GLOB.summon_magic = new /datum/summon_things_controller(/proc/give_magic, survivor_probability) - GLOB.summon_magic.give_out_gear() + GLOB.summon_magic = new /datum/summon_things_controller(survivor_probability, GLOBAL_PROC_REF(give_magic)) + GLOB.summon_magic.equip_all_affected() -/* +/** * Triggers Summon Guns from [user]. * Can optionally be passed [survivor_probability], to set the chance of creating survivalists. * If Summon Guns has already been triggered, gives out guns to everyone again. @@ -211,10 +215,10 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( if(GLOB.summon_guns) GLOB.summon_guns.survivor_probability = survivor_probability else - GLOB.summon_guns = new /datum/summon_things_controller(/proc/give_guns, survivor_probability) - GLOB.summon_guns.give_out_gear() + GLOB.summon_guns = new /datum/summon_things_controller(survivor_probability, GLOBAL_PROC_REF(give_guns)) + GLOB.summon_guns.equip_all_affected() -/* +/** * Triggers Summon Events from [user]. * If Summon Events has already been triggered, speeds up the event timer. */ @@ -258,12 +262,56 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( /datum/summon_things_controller /// Prob. chance someone who is given things will be made a survivalist antagonist. var/survivor_probability = 0 + +/datum/summon_things_controller/New() + RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(on_latejoin)) + +/datum/summon_things_controller/Destroy(force, ...) + . = ..() + UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED) + +/// Determins if the mob is valid to be given whatever we're handing out. +/datum/summon_things_controller/proc/can_give_to(mob/who) + return ishuman(who) + +/// Returns a list of minds of all mobs affected by what we're giving out. +/datum/summon_things_controller/proc/get_affected_minds() + RETURN_TYPE(/list/datum/mind) + var/list/affected = list() + for(var/datum/mind/maybe_affected as anything in get_crewmember_minds() | get_antag_minds()) + if(!can_give_to(maybe_affected.current)) + continue + var/turf/affected_turf = get_turf(maybe_affected.current) + if(!is_station_level(affected_turf?.z) && !is_mining_level(affected_turf?.z)) + continue + affected += maybe_affected + return affected + +/// Signal proc from [COMSIG_GLOB_CREWMEMBER_JOINED]. +/// Calls give_proc_path on latejoiners a number of times (based on num_to_give_to_latejoiners) +/datum/summon_things_controller/proc/on_latejoin(datum/source, mob/living/new_crewmember, rank) + SIGNAL_HANDLER + + if(!can_give_to(new_crewmember)) + return + + equip_latejoiner(new_crewmember) + +/// Called manually to give out our things to all minds returned by [proc/get_affected_minds()] +/datum/summon_things_controller/proc/equip_all_affected() + CRASH("[type] did not implement equip_all_affected()!") + +/// Called via signal to equip latejoin crewmembers +/datum/summon_things_controller/proc/equip_latejoiner(mob/living/carbon/human/new_crewmember) + CRASH("[type] did not implement equip_latejoiner()!") + +/datum/summon_things_controller/item /// The proc path we call on someone to equip them with stuff. Cannot function without it. var/give_proc_path /// The number of equipment we give to latejoiners, to make sure they catch up if it was casted multiple times. var/num_to_give_to_latejoiners = 0 -/datum/summon_things_controller/New(give_proc_path, survivor_probability = 0) +/datum/summon_things_controller/item/New(survivor_probability = 0, give_proc_path) . = ..() if(isnull(give_proc_path)) CRASH("[type] was created without a give_proc_path (the proc that gives people stuff)!") @@ -271,28 +319,46 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( src.survivor_probability = survivor_probability src.give_proc_path = give_proc_path - RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(gear_up_new_crew)) - -/datum/summon_things_controller/Destroy(force, ...) - . = ..() - UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED) - -/// Calls our give_proc_path on all humans in the player list. -/datum/summon_things_controller/proc/give_out_gear() - num_to_give_to_latejoiners++ - for(var/mob/living/carbon/human/to_equip in GLOB.player_list) - var/turf/turf_check = get_turf(to_equip) - if(turf_check && is_away_level(turf_check.z)) - continue - INVOKE_ASYNC(GLOBAL_PROC, give_proc_path, to_equip) - -/// Signal proc from [COMSIG_GLOB_CREWMEMBER_JOINED]. -/// Calls give_proc_path on latejoiners a number of times (based on num_to_give_to_latejoiners) -/datum/summon_things_controller/proc/gear_up_new_crew(datum/source, mob/living/new_crewmember, rank) - SIGNAL_HANDLER - - if(!ishuman(new_crewmember)) - return +/datum/summon_things_controller/item/equip_all_affected() + num_to_give_to_latejoiners += 1 + for(var/datum/mind/crewmember_mind as anything in get_affected_minds()) + INVOKE_ASYNC(GLOBAL_PROC, give_proc_path, crewmember_mind.current) +/datum/summon_things_controller/item/equip_latejoiner(mob/living/carbon/human/new_crewmember) for(var/i in 1 to num_to_give_to_latejoiners) INVOKE_ASYNC(GLOBAL_PROC, give_proc_path, new_crewmember) + +/datum/summon_things_controller/spellbook_entry + /// Spellbook entry instance to hand out + var/datum/spellbook_entry/used_entry + +/datum/summon_things_controller/spellbook_entry/can_give_to(mob/who) + return istype(used_entry, /datum/spellbook_entry/item) ? ishuman(who) : isliving(who) + +/datum/summon_things_controller/spellbook_entry/get_affected_minds() + // The wizards get in on this too, wherever they may be + return ..() | get_antag_minds(/datum/antagonist/wizard) + +/datum/summon_things_controller/spellbook_entry/New(entry_type) + . = ..() + if(!ispath(entry_type, /datum/spellbook_entry)) + CRASH("[type] was created with an invalid entry type (must be a spellbook entry typepath)!") + + used_entry = new entry_type() + +/datum/summon_things_controller/spellbook_entry/equip_all_affected() + for(var/datum/mind/crewmember_mind as anything in get_affected_minds()) + INVOKE_ASYNC(src, PROC_REF(grant_entry), crewmember_mind.current) + +/datum/summon_things_controller/spellbook_entry/equip_latejoiner(mob/living/carbon/human/new_crewmember) + grant_entry(new_crewmember) + +/datum/summon_things_controller/spellbook_entry/proc/grant_entry(mob/to_who) + var/gained = used_entry.buy_spell(to_who, log_buy = FALSE) + // Make spells castable without robes + if(istype(gained, /datum/action/cooldown/spell)) + var/datum/action/cooldown/spell/given_out = gained + given_out.spell_requirements &= ~SPELL_REQUIRES_WIZARD_GARB + + // Makes staffs and related items usable without penalty + ADD_TRAIT(to_who.mind, TRAIT_MAGICALLY_GIFTED, INNATE_TRAIT)