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