diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index bf1a0f70469..d9cd4c15d1b 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -200,12 +200,13 @@ user.blind_eyes(10) /obj/item/book/granter/spell/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell = /obj/effect/proc_holder/spell/pointed/mind_transfer spellname = "mindswap" icon_state ="bookmindswap" desc = "This book's cover is pristine, though its pages look ragged and torn." - var/mob/stored_swap //Used in used book recoils to store an identity for mindswaps remarks = list("If you mindswap from a mouse, they will be helpless when you recover...", "Wait, where am I...?", "This book is giving me a horrible headache...", "This page is blank, but I feel words popping into my head...", "GYNU... GYRO... Ugh...", "The voices in my head need to stop, I'm trying to read here...", "I don't think anyone will be happy when I cast this spell...") + /// Mob used in book recoils to store an identity for mindswaps + var/mob/living/stored_swap /obj/item/book/granter/spell/mindswap/onlearned() spellname = pick("fireball","smoke","blind","forcewall","knock","barnyard","charge") @@ -224,8 +225,8 @@ if(stored_swap == user) to_chat(user,"You stare at the book some more, but there doesn't seem to be anything else to learn...") return - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new - if(swapper.cast(list(stored_swap), user, TRUE, TRUE)) + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new + if(swapper.cast(list(stored_swap), user, TRUE)) to_chat(user,"You're suddenly somewhere else... and someone else?!") to_chat(stored_swap,"Suddenly you're staring at [src] again... where are you, who are you?!") else diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index 4af8dd16466..d163f9c2c5a 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -157,7 +157,7 @@ /datum/spellbook_entry/mindswap name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell_type = /obj/effect/proc_holder/spell/pointed/mind_transfer category = "Mobility" /datum/spellbook_entry/forcewall diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index fdd2d00e2cf..e6f994e64d8 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -210,7 +210,7 @@ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned life-saving survival spells. You are able to cast charge and forcewall.") if(APPRENTICE_ROBELESS) owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null)) to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.") /datum/antagonist/wizard/apprentice/create_objectives() diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 7e374292232..55dc909abf7 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -91,11 +91,11 @@ shuffle_inplace(mobs) - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new /obj/effect/proc_holder/spell/targeted/mind_transfer + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new while(mobs.len > 1) var/mob/living/carbon/human/H = pick(mobs) mobs -= H - swapper.cast(list(H), mobs[mobs.len], 1) + swapper.cast(list(H), mobs[mobs.len], TRUE) mobs -= mobs[mobs.len] for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) diff --git a/code/modules/spells/spell_types/blind.dm b/code/modules/spells/spell_types/blind.dm index f2745e29229..387bcbd80ce 100644 --- a/code/modules/spells/spell_types/blind.dm +++ b/code/modules/spells/spell_types/blind.dm @@ -11,7 +11,6 @@ starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind", "/obj/effect/proc_holder/spell/targeted/genetic/blind") ranged_mousepointer = 'icons/effects/blind_target.dmi' action_icon_state = "blind" - base_icon_state = "blind" active_msg = "You prepare to blind a target..." /obj/effect/proc_holder/spell/targeted/inflict_handler/blind @@ -26,7 +25,8 @@ sound = 'sound/magic/blind.ogg' /obj/effect/proc_holder/spell/pointed/trigger/blind/intercept_check(mob/user, atom/target) - if(!..()) + . = ..() + if(!.) return FALSE if(!isliving(target)) to_chat(user, "You can only blind living beings!") diff --git a/code/modules/spells/spell_types/mind_transfer.dm b/code/modules/spells/spell_types/mind_transfer.dm index 4cebceeca82..c14f5d83b44 100644 --- a/code/modules/spells/spell_types/mind_transfer.dm +++ b/code/modules/spells/spell_types/mind_transfer.dm @@ -1,7 +1,6 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer +/obj/effect/proc_holder/spell/pointed/mind_transfer name = "Mind Transfer" - desc = "This spell allows the user to switch bodies with a target." - + desc = "This spell allows the user to switch bodies with a target next to him." school = "transmutation" charge_max = 600 clothes_req = FALSE @@ -9,90 +8,111 @@ invocation_type = "whisper" range = 1 cooldown_min = 200 //100 deciseconds reduction per rank - var/unconscious_amount_caster = 400 //how much the caster is stunned for after the spell - var/unconscious_amount_victim = 400 //how much the victim is stunned for after the spell - + ranged_mousepointer = 'icons/effects/mindswap_target.dmi' action_icon_state = "mindswap" + active_msg = "You prepare to swap minds with a target..." + /// For how long is the caster stunned for after the spell + var/unconscious_amount_caster = 40 SECONDS + /// For how long is the victim stunned for after the spell + var/unconscious_amount_victim = 40 SECONDS -/* -Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. -Make sure spells that are removed from spell_list are actually removed and deleted when mind transferring. -Also, you never added distance checking after target is selected. I've went ahead and did that. -*/ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride, silent = FALSE) +/obj/effect/proc_holder/spell/pointed/mind_transfer/cast(list/targets, mob/living/user, silent = FALSE) if(!targets.len) if(!silent) to_chat(user, "No mind found!") - return - + return FALSE if(targets.len > 1) if(!silent) to_chat(user, "Too many minds! You're not a hive damnit!") - return + return FALSE - var/mob/living/target = targets[1] + var/mob/living/victim = targets[1] //The target of the spell whos body will be transferred to. + if(!swap_check(user, victim, silent)) + return FALSE + if(istype(victim, /mob/living/simple_animal/hostile/guardian)) + var/mob/living/simple_animal/hostile/guardian/stand = victim + if(stand.summoner) + victim = stand.summoner - var/t_He = target.p_they(TRUE) - var/t_is = target.p_are() + //MIND TRANSFER BEGIN + var/mob/dead/observer/ghost = victim.ghostize() + user.mind.transfer_to(victim) - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. + ghost.mind.transfer_to(user) + if(ghost.key) + user.key = ghost.key //have to transfer the key since the mind was not active + qdel(ghost) + //MIND TRANSFER END + + //Here we knock both mobs out for a time. + user.Unconscious(unconscious_amount_caster) + victim.Unconscious(unconscious_amount_victim) + SEND_SOUND(user, sound('sound/magic/mandswap.ogg')) + SEND_SOUND(victim, sound('sound/magic/mandswap.ogg')) // only the caster and victim hear the sounds, that way no one knows for sure if the swap happened + return TRUE + +/** + * swap_check: Checks if we are allowed and able to swap two minds + * + * Arguments: + * * user - caster of the spell + * * target - target of the spell + * * silent - if set to TRUE, the spell will not produce any warning messages to the user if the spell fails + */ +/obj/effect/proc_holder/spell/pointed/mind_transfer/proc/swap_check(mob/user, atom/target, silent = FALSE) + if(!isliving(target)) if(!silent) - to_chat(user, "[t_He] [t_is] too far away!") - return + to_chat(user, "You can only swap minds with living beings!") + return FALSE - if(ismegafauna(target)) + if(user == target) + if(!silent) + to_chat(user, "You can't swap minds with yourself!") + return FALSE + + var/mob/living/victim = target + var/t_He = victim.p_they(TRUE) + + if(ismegafauna(victim)) if(!silent) to_chat(user, "This creature is too powerful to control!") - return + return FALSE - if(target.stat == DEAD) + if(victim.stat == DEAD) if(!silent) to_chat(user, "You don't particularly want to be dead!") - return + return FALSE - if(!target.key || !target.mind) + if(!victim.key || !victim.mind) if(!silent) - to_chat(user, "[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.") - return + to_chat(user, "[t_He] appear[victim.p_s()] to be catatonic! Not even magic can affect [victim.p_their()] vacant mind.") + return FALSE if(user.suiciding) if(!silent) to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") - return + return FALSE - var/datum/mind/TM = target.mind - if(target.anti_magic_check(TRUE, FALSE) || TM.has_antag_datum(/datum/antagonist/wizard) || TM.has_antag_datum(/datum/antagonist/cult) || TM.has_antag_datum(/datum/antagonist/changeling) || TM.has_antag_datum(/datum/antagonist/rev) || target.key[1] == "@") + var/datum/mind/VM = victim.mind + if(victim.anti_magic_check(TRUE, FALSE) || VM.has_antag_datum(/datum/antagonist/wizard) || VM.has_antag_datum(/datum/antagonist/cult) || VM.has_antag_datum(/datum/antagonist/changeling) || VM.has_antag_datum(/datum/antagonist/rev) || victim.key[1] == "@") if(!silent) - to_chat(user, "[target.p_their(TRUE)] mind is resisting your spell!") - return + to_chat(user, "[victim.p_their(TRUE)] mind is resisting your spell!") + return FALSE - if(istype(target, /mob/living/simple_animal/hostile/guardian)) - var/mob/living/simple_animal/hostile/guardian/stand = target + if(istype(victim, /mob/living/simple_animal/hostile/guardian)) + var/mob/living/simple_animal/hostile/guardian/stand = victim if(stand.summoner) if(stand.summoner == user) if(!silent) to_chat(user, "Swapping minds with your own guardian would just put you back into your own head!") - return - else - target = stand.summoner + return FALSE - var/mob/living/victim = target//The target of the spell whos body will be transferred to. - var/mob/living/caster = user//The wizard/whomever doing the body transferring. - - //MIND TRANSFER BEGIN - var/mob/dead/observer/ghost = victim.ghostize() - caster.mind.transfer_to(victim) - - ghost.mind.transfer_to(caster) - if(ghost.key) - caster.key = ghost.key //have to transfer the key since the mind was not active - qdel(ghost) - - //MIND TRANSFER END - - //Here we knock both mobs out for a time. - caster.Unconscious(unconscious_amount_caster) - victim.Unconscious(unconscious_amount_victim) - SEND_SOUND(caster, sound('sound/magic/mandswap.ogg')) - SEND_SOUND(victim, sound('sound/magic/mandswap.ogg'))// only the caster and victim hear the sounds, that way no one knows for sure if the swap happened + return TRUE + +/obj/effect/proc_holder/spell/pointed/mind_transfer/intercept_check(mob/user, atom/target) + . = ..() + if(!.) + return FALSE + if(!swap_check(user, target)) + return FALSE return TRUE diff --git a/code/modules/spells/spell_types/pointed.dm b/code/modules/spells/spell_types/pointed.dm index 52db062aec1..75060b78491 100644 --- a/code/modules/spells/spell_types/pointed.dm +++ b/code/modules/spells/spell_types/pointed.dm @@ -1,12 +1,11 @@ /obj/effect/proc_holder/spell/pointed name = "pointed spell" ranged_mousepointer = 'icons/effects/throw_target.dmi' + action_icon_state = "projectile" /// Message showing to the spell owner upon deactivating pointed spell. var/deactive_msg = "You dispel the magic..." /// Message showing to the spell owner upon activating pointed spell. var/active_msg = "You prepare to use the spell on a target..." - /// Default icon for the pointed spell, used for active/inactive states switching. - var/base_icon_state = "projectile" /obj/effect/proc_holder/spell/pointed/Click() var/mob/living/user = usr @@ -20,56 +19,68 @@ if(active) msg = "[deactive_msg]" remove_ranged_ability(msg) - on_deactivation(user) else msg = "[active_msg] Left-click to activate spell on a target!" add_ranged_ability(user, msg, TRUE) - on_activation(user) + +/obj/effect/proc_holder/spell/pointed/remove_ranged_ability(msg) + . = ..() + on_deactivation(ranged_ability_user) + +/obj/effect/proc_holder/spell/pointed/add_ranged_ability(mob/living/user, msg, forced) + . = ..() + on_activation(user) /** + * on_activation: What happens upon pointed spell activation. * - * What happens upon pointed spell activation. - * - * user mob The mob interacting owning the spell. - * - **/ + * Arguments: + * * user The mob interacting owning the spell. + */ /obj/effect/proc_holder/spell/pointed/proc/on_activation(mob/user) return - /** +/** + * on_activation: What happens upon pointed spell deactivation. * - * What happens upon pointed spell deactivation. - * - * user mob The mob interacting owning the spell. - * - **/ + * Arguments: + * * user The mob interacting owning the spell. + */ /obj/effect/proc_holder/spell/pointed/proc/on_deactivation(mob/user) return /obj/effect/proc_holder/spell/pointed/update_icon() if(!action) return - action.button_icon_state = "[base_icon_state][active]" + if(active) + action.button_icon_state = "[action_icon_state]1" + else + action.button_icon_state = "[action_icon_state]" action.UpdateButtonIcon() /obj/effect/proc_holder/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target) if(..()) - return FALSE + return TRUE + if(!(target in view_or_range(range, ranged_ability_user, selection_type))) + to_chat(ranged_ability_user, "[target.p_theyre(TRUE)] too far away!") + return TRUE if(!intercept_check(ranged_ability_user, target)) - return FALSE + return TRUE if(!cast_check(user = ranged_ability_user)) - return FALSE + return TRUE perform(list(target), user = ranged_ability_user) remove_ranged_ability() - return TRUE + return TRUE // Do not do any underlying actions after the spell cast - /** +/** + * intercept_check: Specific spell checks for InterceptClickOn() targets. * - * Specific spell checks for InterceptClickOn() targets. - * - * user mob The mob using the ranged spell via intercept. - * target atom The atom being targeted by the spell via intercept. - * - **/ + * Arguments: + * * user The mob using the ranged spell via intercept. + * * target The atom that is being targeted by the spell via intercept. + */ /obj/effect/proc_holder/spell/pointed/proc/intercept_check(mob/user, atom/target) + if(!can_target(target)) + to_chat(user, "Invalid target!") + return FALSE return TRUE diff --git a/icons/effects/mindswap_target.dmi b/icons/effects/mindswap_target.dmi new file mode 100644 index 00000000000..32ccda154db Binary files /dev/null and b/icons/effects/mindswap_target.dmi differ diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi index 3a7722d9fd9..da4416370a8 100644 Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ