diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index ef9610159e9..89acad38c49 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -118,12 +118,6 @@ #define COMSIG_ITEM_PICKUP "item_pickup" ///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) #define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" -///return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user) -#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" - #define COMPONENT_BLOCK_IMBUE (1 << 0) -///called before marking an object for retrieval, checked in /obj/effect/proc_holder/spell/targeted/summonitem/cast() : (mob/user) -#define COMSIG_ITEM_MARK_RETRIEVAL "item_mark_retrieval" - #define COMPONENT_BLOCK_MARK_RETRIEVAL (1<<0) ///from base of obj/item/hit_reaction(): (list/args) #define COMSIG_ITEM_HIT_REACT "item_hit_react" #define COMPONENT_HIT_REACTION_BLOCK (1<<0) @@ -306,6 +300,22 @@ ///called from /obj/effect/proc_holder/spell/perform (src) #define COMSIG_MOB_CAST_SPELL "mob_cast_spell" +/// Sent from /obj/effect/proc_holder/spell/targeted/lichdom/cast(), to the item being imbued: (mob/user) +#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" + /// Returns to block this item from being imbued into a phylactery + #define COMPONENT_BLOCK_IMBUE (1 << 0) +/// Sent from /obj/effect/proc_holder/spell/targeted/summonitem/cast(), to the item being marked : () +#define COMSIG_ITEM_MARK_RETRIEVAL "item_mark_retrieval" + /// Returns to block this item from being marked for instant summons + #define COMPONENT_BLOCK_MARK_RETRIEVAL (1<<0) + +/// Sent from /obj/effect/proc_holder/spell/targeted/charge/cast(), to the item in hand being charged: (obj/effect/proc_holder/spell/targeted/charge/spell, mob/living/caster) +#define COMSIG_ITEM_MAGICALLY_CHARGED "item_magic_charged" + /// Returns if an item was successfuly recharged + #define COMPONENT_ITEM_CHARGED (1 << 0) + /// Returns if the item had a negative side effect occur while recharging + #define COMPONENT_ITEM_BURNT_OUT (1 << 1) + // /obj/item/camera signals ///from /obj/item/camera/captureimage(): (atom/target, mob/user) @@ -398,4 +408,3 @@ /// from base of /obj/item/slimepotion/speed/afterattack(): (obj/target, /obj/src, mob/user) #define COMSIG_SPEED_POTION_APPLIED "speed_potion" #define SPEED_POTION_STOP (1<<0) - diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index 7d96f8e57ef..75d506b7e75 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -122,6 +122,35 @@ var/spell var/spellname = "conjure bugs" + +/obj/item/book/granter/spell/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_MAGICALLY_CHARGED, .proc/on_magic_charge) + +/** + * Signal proc for [COMSIG_ITEM_MAGICALLY_CHARGED] + * + * Refreshes uses on our spell granter, or make it quicker to read if it's already infinite use + */ +/obj/item/book/granter/spell/proc/on_magic_charge(datum/source, obj/effect/proc_holder/spell/targeted/charge/spell, mob/living/caster) + SIGNAL_HANDLER + + if(!oneuse) + to_chat(caster, span_notice("This book is infinite use and can't be recharged, \ + yet the magic has improved it somehow...")) + pages_to_mastery = max(pages_to_mastery - 1, 1) + return COMPONENT_ITEM_CHARGED|COMPONENT_ITEM_BURNT_OUT + + if(prob(80)) + caster.dropItemToGround(src, TRUE) + visible_message(span_warning("[src] catches fire and burns to ash!")) + new /obj/effect/decal/cleanable/ash(drop_location()) + qdel(src) + return COMPONENT_ITEM_BURNT_OUT + + used = FALSE + return COMPONENT_ITEM_CHARGED + /obj/item/book/granter/spell/already_known(mob/user) if(!spell) return TRUE diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index b2ffc895858..4c179ec1246 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -668,6 +668,28 @@ /obj/item/spellbook/Initialize(mapload) . = ..() prepare_spells() + RegisterSignal(src, COMSIG_ITEM_MAGICALLY_CHARGED, .proc/on_magic_charge) + +/** + * Signal proc for [COMSIG_ITEM_MAGICALLY_CHARGED] + * + * Has no effect on charge, but gives a funny message to people who think they're clever. + */ +/obj/item/spellbook/proc/on_magic_charge(datum/source, obj/effect/proc_holder/spell/targeted/charge/spell, mob/living/caster) + SIGNAL_HANDLER + + var/static/list/clever_girl = list( + "NICE TRY BUT NO!", + "CLEVER BUT NOT CLEVER ENOUGH!", + "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", + "CUTE! VERY CUTE!", + "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?", + ) + + to_chat(caster, span_warning("Glowing red letters appear on the front cover...")) + to_chat(caster, span_red(pick(clever_girl))) + + return COMPONENT_ITEM_BURNT_OUT /obj/item/spellbook/attack_self(mob/user) if(!owner) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index dd75d0a0711..1c543fdace7 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -60,6 +60,48 @@ desc += " This one has a rating of [display_energy(maxcharge)][prob(10) ? ", and you should not swallow it" : ""]." //joke works better if it's not on every cell update_appearance() + RegisterSignal(src, COMSIG_ITEM_MAGICALLY_CHARGED, .proc/on_magic_charge) + var/static/list/loc_connections = list( + COMSIG_ITEM_MAGICALLY_CHARGED = .proc/on_magic_charge, + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/** + * Signal proc for [COMSIG_ITEM_MAGICALLY_CHARGED] + * + * If we, or the item we're located in, is subject to the charge spell, gain some charge back + */ +/obj/item/stock_parts/cell/proc/on_magic_charge(datum/source, obj/effect/proc_holder/spell/targeted/charge/spell, mob/living/caster) + SIGNAL_HANDLER + + // This shouldn't be running if we're not being held by a mob, + // or if we're not within an object being held by a mob, but just in case... + if(!ismovable(loc)) + return + + . = COMPONENT_ITEM_CHARGED + + if(prob(80)) + maxcharge -= 200 + + if(maxcharge <= 1) // Div by 0 protection + maxcharge = 1 + . |= COMPONENT_ITEM_BURNT_OUT + + charge = maxcharge + update_appearance() + + // Guns need to process their chamber when we've been charged + if(isgun(loc)) + var/obj/item/gun/gun_loc = loc + gun_loc.process_chamber() + + // The thing we're in might have overlays or icon states for whether the cell is charged + if(!ismob(loc)) + loc.update_appearance() + + return . + /obj/item/stock_parts/cell/create_reagents(max_vol, flags) . = ..() RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change) diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 856fb95ebee..26977d40344 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -16,6 +16,8 @@ var/charges = 0 var/recharge_rate = 8 var/charge_timer = 0 + /// Whether this wand/staff recharges on its own over time. + /// (This is not related to the spell "Charge" whatsoever!) var/can_charge = TRUE var/ammo_type var/no_den_usage @@ -23,6 +25,34 @@ trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead pin = /obj/item/firing_pin/magic +/obj/item/gun/magic/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_MAGICALLY_CHARGED, .proc/on_magic_charge) + +/** + * Signal proc for [COMSIG_ITEM_MAGICALLY_CHARGED] + * + * Adds uses to wands or staffs. + */ +/obj/item/gun/magic/proc/on_magic_charge(datum/source, obj/effect/proc_holder/spell/targeted/charge/spell, mob/living/caster) + SIGNAL_HANDLER + + . = COMPONENT_ITEM_CHARGED + + // Non-self charging staves and wands can potentially expire + if(!can_charge && max_charges && prob(80)) + max_charges-- + + if(max_charges <= 0) + max_charges = 0 + . |= COMPONENT_ITEM_BURNT_OUT + + charges = max_charges + update_appearance(UPDATE_ICON_STATE) + recharge_newshot() + + return . + /obj/item/gun/magic/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) if(no_den_usage) var/area/A = get_area(user) diff --git a/code/modules/spells/spell_types/charge.dm b/code/modules/spells/spell_types/charge.dm index 467af52aefb..754b731f780 100644 --- a/code/modules/spells/spell_types/charge.dm +++ b/code/modules/spells/spell_types/charge.dm @@ -1,6 +1,10 @@ /obj/effect/proc_holder/spell/targeted/charge name = "Charge" - desc = "This spell can be used to recharge a variety of things in your hands, from magical artifacts to electrical components. A creative wizard can even use it to grant magical power to a fellow magic user." + desc = "This spell can be used to recharge a variety of things in your hands, \ + from magical artifacts to electrical components. A creative wizard can even use it \ + to grant magical power to a fellow magic user." + sound = 'sound/magic/charge.ogg' + action_icon_state = "charge" school = SCHOOL_TRANSMUTATION charge_max = 600 @@ -10,92 +14,43 @@ range = -1 cooldown_min = 400 //50 deciseconds reduction per rank include_user = TRUE - action_icon_state = "charge" -/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets,mob/user = usr) - for(var/mob/living/L in targets) - var/list/hand_items = list(L.get_active_held_item(),L.get_inactive_held_item()) - var/charged_item = null - var/burnt_out = FALSE +/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets, mob/user = usr) + // Charge people we're pulling first and foremost + if(isliving(user.pulling)) + var/mob/living/pulled_living = user.pulling + var/pulled_has_spells = FALSE - if(L.pulling && isliving(L.pulling)) - var/mob/living/M = L.pulling - if(LAZYLEN(M.mob_spell_list) || (LAZYLEN(M.mind?.spell_list))) - for(var/obj/effect/proc_holder/spell/S in M.mob_spell_list) - S.charge_counter = S.charge_max - if(M.mind) - for(var/obj/effect/proc_holder/spell/S in M.mind.spell_list) - S.charge_counter = S.charge_max - to_chat(M, span_notice("You feel raw magic flowing through you. It feels good!")) - else - to_chat(M, span_notice("You feel very strange for a moment, but then it passes.")) - burnt_out = TRUE - charged_item = M - break - for(var/obj/item in hand_items) - if(istype(item, /obj/item/spellbook)) - to_chat(L, span_danger("Glowing red letters appear on the front cover...")) - to_chat(L, span_warning("[pick("NICE TRY BUT NO!","CLEVER BUT NOT CLEVER ENOUGH!", "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", "CUTE! VERY CUTE!", "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?")]")) - burnt_out = TRUE - else if(istype(item, /obj/item/book/granter/spell)) - var/obj/item/book/granter/spell/I = item - if(!I.oneuse) - to_chat(L, span_notice("This book is infinite use and can't be recharged, yet the magic has improved the book somehow...")) - burnt_out = TRUE - I.pages_to_mastery-- - break - if(prob(80)) - L.visible_message(span_warning("[I] catches fire!")) - qdel(I) - else - I.used = FALSE - charged_item = I - break - else if(istype(item, /obj/item/gun/magic)) - var/obj/item/gun/magic/I = item - if(prob(80) && !I.can_charge) - I.max_charges-- - if(I.max_charges <= 0) - I.max_charges = 0 - burnt_out = TRUE - I.charges = I.max_charges - if(istype(item, /obj/item/gun/magic/wand) && I.max_charges != 0) - var/obj/item/gun/magic/W = item - W.icon_state = initial(W.icon_state) - I.recharge_newshot() - charged_item = I - break - else if(istype(item, /obj/item/stock_parts/cell)) - var/obj/item/stock_parts/cell/C = item - if(prob(80)) - C.maxcharge -= 200 - if(C.maxcharge <= 1) //Div by 0 protection - C.maxcharge = 1 - burnt_out = TRUE - C.charge = C.maxcharge - charged_item = C - break - else if(item.contents) - var/obj/I = null - for(I in item.contents) - if(istype(I, /obj/item/stock_parts/cell/)) - var/obj/item/stock_parts/cell/C = I - if(prob(80)) - C.maxcharge -= 200 - if(C.maxcharge <= 1) //Div by 0 protection - C.maxcharge = 1 - burnt_out = TRUE - C.charge = C.maxcharge - if(istype(C.loc, /obj/item/gun)) - var/obj/item/gun/G = C.loc - G.process_chamber() - item.update_appearance() - charged_item = item - break - if(!charged_item) - to_chat(L, span_notice("You feel magical power surging through your hands, but the feeling rapidly fades...")) - else if(burnt_out) - to_chat(L, span_warning("[charged_item] doesn't seem to be reacting to the spell!")) - else - playsound(get_turf(L), 'sound/magic/charge.ogg', 50, TRUE) - to_chat(L, span_notice("[charged_item] suddenly feels very warm!")) + for(var/obj/effect/proc_holder/spell/spell in pulled_living.mob_spell_list | pulled_living.mind?.spell_list) + spell.charge_counter = spell.charge_max + spell.recharging = FALSE + spell.update_appearance() + pulled_has_spells = TRUE + + if(pulled_has_spells) + to_chat(pulled_living, span_notice("You feel raw magic flowing through you. It feels good!")) + to_chat(user, span_notice("[pulled_living] suddenly feels very warm!")) + return + + to_chat(pulled_living, span_notice("You feel very strange for a moment, but then it passes.")) + + // Then charge their main hand item, then charge their offhand item + var/obj/item/to_charge = user.get_active_held_item() || user.get_inactive_held_item() + if(!to_charge) + to_chat(user, span_notice("You feel magical power surging through your hands, but the feeling rapidly fades.")) + return + + var/charge_return = SEND_SIGNAL(to_charge, COMSIG_ITEM_MAGICALLY_CHARGED, src, user) + + if(QDELETED(to_charge)) + to_chat(user, span_warning("[src] seems to react adversely with [to_charge]!")) + return + + if(charge_return & COMPONENT_ITEM_BURNT_OUT) + to_chat(user, span_warning("[to_charge] seems to react negatively to [src], becoming uncomfortably warm!")) + + else if(charge_return & COMPONENT_ITEM_CHARGED) + to_chat(user, span_notice("[to_charge] suddenly feels very warm!")) + + else + to_chat(user, span_notice("[to_charge] doesn't seem to be react to [src]."))