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)