mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-21 11:07:12 +01:00
Ports Anti-magic from TG. (#27560)
* the rest of the fucking owl * OK hands need work but otherwise good * fuck time out fix vampire after * temporary codersprite do not merge this lmao * codersprite up * pause, time out * deploy the antimagic * Update code/__HELPERS/trait_helpers.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Signed-off-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> * Update code/datums/status_effects/buffs.dm Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * Update code/game/gamemodes/wizard/magic_tarot.dm Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> --------- Signed-off-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
co-authored by
DGamerL
Burzah
parent
6057ff5c31
commit
7bbebbb23f
@@ -0,0 +1,166 @@
|
||||
/// This provides different types of magic resistance on an object
|
||||
/datum/component/anti_magic
|
||||
/// A bitflag with the types of magic resistance on the object
|
||||
var/antimagic_flags
|
||||
/// The amount of times the object can protect the user from magic
|
||||
/// Set to INFINITY to have, well, infinite charges.
|
||||
var/charges
|
||||
/// The inventory slot the object must be located at in order to activate
|
||||
var/inventory_flags
|
||||
/// The callback invoked when we have been drained a antimagic charge
|
||||
var/datum/callback/drain_antimagic
|
||||
/// The callback invoked when twe have been depleted of all charges
|
||||
var/datum/callback/expiration
|
||||
/// Whether we should, on equipping, alert the caster that this item can block any of their spells
|
||||
/// This changes between true and false on equip and drop, don't set it outright to something
|
||||
var/alert_caster_on_equip = TRUE
|
||||
|
||||
/**
|
||||
* Adds magic resistances to an object
|
||||
*
|
||||
* Magic resistance will prevent magic from affecting the user if it has the correct resistance
|
||||
* against the type of magic being used
|
||||
*
|
||||
* args:
|
||||
* * antimagic_flags (optional) A bitflag with the types of magic resistance on the object
|
||||
* * charges (optional) The amount of times the object can protect the user from magic
|
||||
* * inventory_flags (optional) The inventory slot the object must be located at in order to activate
|
||||
* * drain_antimagic (optional) The proc that is triggered when an object has been drained a antimagic charge
|
||||
* * expiration (optional) The proc that is triggered when the object is depleted of charges
|
||||
* *
|
||||
* antimagic bitflags: (see code/__DEFINES/magic.dm)
|
||||
* * MAGIC_RESISTANCE - Default magic resistance that blocks normal magic (wizard, spells, staffs)
|
||||
* * MAGIC_RESISTANCE_MIND - Tinfoil hat magic resistance that blocks mental magic (telepathy, abductors, jelly people)
|
||||
* * MAGIC_RESISTANCE_HOLY - Holy magic resistance that blocks unholy magic (revenant, cult, vampire, voice of god)
|
||||
**/
|
||||
/datum/component/anti_magic/Initialize(
|
||||
antimagic_flags = MAGIC_RESISTANCE,
|
||||
charges = INFINITY,
|
||||
inventory_flags = ~ITEM_SLOT_IN_BACKPACK, // items in a backpack won't activate, anywhere else is fine
|
||||
datum/callback/drain_antimagic,
|
||||
datum/callback/expiration,
|
||||
)
|
||||
|
||||
|
||||
var/atom/movable/movable = parent
|
||||
if(!istype(movable))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
var/compatible = FALSE
|
||||
if(isitem(movable))
|
||||
RegisterSignal(movable, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
|
||||
RegisterSignal(movable, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
|
||||
RegisterSignal(movable, COMSIG_ATTACK, PROC_REF(on_attack))
|
||||
compatible = TRUE
|
||||
else if(ismob(movable))
|
||||
register_antimagic_signals(movable)
|
||||
compatible = TRUE
|
||||
|
||||
if(movable.can_buckle)
|
||||
RegisterSignal(movable, COMSIG_MOVABLE_BUCKLE, PROC_REF(on_buckle))
|
||||
RegisterSignal(movable, COMSIG_MOVABLE_UNBUCKLE, PROC_REF(on_unbuckle))
|
||||
compatible = TRUE
|
||||
|
||||
if(!compatible)
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.antimagic_flags = antimagic_flags
|
||||
src.charges = charges
|
||||
src.inventory_flags = inventory_flags
|
||||
src.drain_antimagic = drain_antimagic
|
||||
src.expiration = expiration
|
||||
|
||||
/datum/component/anti_magic/Destroy(force)
|
||||
drain_antimagic = null
|
||||
expiration = null
|
||||
return ..()
|
||||
|
||||
/datum/component/anti_magic/proc/register_antimagic_signals(datum/on_what)
|
||||
RegisterSignal(on_what, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
|
||||
RegisterSignal(on_what, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)
|
||||
|
||||
/datum/component/anti_magic/proc/unregister_antimagic_signals(datum/on_what)
|
||||
UnregisterSignal(on_what, list(COMSIG_MOB_RECEIVE_MAGIC, COMSIG_MOB_RESTRICT_MAGIC))
|
||||
|
||||
/datum/component/anti_magic/proc/on_buckle(atom/movable/source, mob/living/bucklee)
|
||||
SIGNAL_HANDLER // COMSIG_MOVABLE_BUCKLE
|
||||
register_antimagic_signals(bucklee)
|
||||
|
||||
/datum/component/anti_magic/proc/on_unbuckle(atom/movable/source, mob/living/bucklee)
|
||||
SIGNAL_HANDLER // COMSIG_MOVABLE_UNBUCKLE
|
||||
unregister_antimagic_signals(bucklee)
|
||||
|
||||
/datum/component/anti_magic/proc/on_equip(atom/movable/source, mob/equipper, slot)
|
||||
SIGNAL_HANDLER // COMSIG_ITEM_EQUIPPED
|
||||
addtimer(CALLBACK(src, PROC_REF(on_equip_part_2), source, equipper, slot), 0.1 SECONDS) //We wait a moment to see if the item grants antimagic flags
|
||||
|
||||
/datum/component/anti_magic/proc/on_equip_part_2(atom/movable/source, mob/equipper, slot)
|
||||
if(!(inventory_flags & slot)) //Check that the slot is valid for antimagic
|
||||
unregister_antimagic_signals(equipper)
|
||||
return
|
||||
|
||||
register_antimagic_signals(equipper)
|
||||
if(HAS_TRAIT(equipper, TRAIT_ANTIMAGIC_NO_SELFBLOCK)) //If they do not care about antimagic, don't warn them
|
||||
return
|
||||
if(!alert_caster_on_equip)
|
||||
return
|
||||
|
||||
// Check to see if we have any spells that are blocked due to antimagic
|
||||
if(!equipper.mind)
|
||||
return
|
||||
for(var/datum/spell/knownspell in equipper.mind.spell_list)
|
||||
if(!(knownspell.spell_requirements & SPELL_REQUIRES_NO_ANTIMAGIC))
|
||||
continue
|
||||
|
||||
if(!(antimagic_flags & knownspell.antimagic_flags))
|
||||
continue
|
||||
|
||||
to_chat(equipper, "<span class='warning'>[parent] is interfering with your ability to cast magic!</span>")
|
||||
alert_caster_on_equip = FALSE
|
||||
break
|
||||
|
||||
/datum/component/anti_magic/proc/on_drop(atom/movable/source, mob/user)
|
||||
SIGNAL_HANDLER //COMSIG_ITEM_DROPPED
|
||||
|
||||
// Reset alert
|
||||
if(source.loc != user)
|
||||
alert_caster_on_equip = TRUE
|
||||
unregister_antimagic_signals(user)
|
||||
|
||||
/datum/component/anti_magic/proc/block_receiving_magic(mob/living/carbon/source, casted_magic_flags, charge_cost, list/antimagic_sources)
|
||||
SIGNAL_HANDLER // COMSIG_MOB_RECEIVE_MAGIC
|
||||
|
||||
// We do not block this type of magic, good day
|
||||
if(!(casted_magic_flags & antimagic_flags))
|
||||
return NONE
|
||||
|
||||
// We have already blocked this spell
|
||||
if(parent in antimagic_sources)
|
||||
return NONE
|
||||
|
||||
// Block success! Add this parent to the list of antimagic sources
|
||||
antimagic_sources += parent
|
||||
|
||||
if((charges != INFINITY) && charge_cost > 0)
|
||||
drain_antimagic?.Invoke(source, parent)
|
||||
charges -= charge_cost
|
||||
if(charges <= 0)
|
||||
expiration?.Invoke(source, parent)
|
||||
qdel(src) // no more antimagic
|
||||
|
||||
return COMPONENT_MAGIC_BLOCKED
|
||||
|
||||
/// cannot cast magic with the same type of antimagic present
|
||||
/datum/component/anti_magic/proc/restrict_casting_magic(mob/user, magic_flags)
|
||||
SIGNAL_HANDLER // COMSIG_MOB_RESTRICT_MAGIC
|
||||
|
||||
if(magic_flags & antimagic_flags)
|
||||
if(HAS_TRAIT(user, TRAIT_ANTIMAGIC_NO_SELFBLOCK)) // this trait bypasses magic casting restrictions
|
||||
return NONE
|
||||
return COMPONENT_MAGIC_BLOCKED
|
||||
|
||||
return NONE
|
||||
|
||||
/datum/component/anti_magic/proc/on_attack(obj/item/source, mob/living/target, mob/living/user)
|
||||
SIGNAL_HANDLER //COMSIG_ATTACK
|
||||
SEND_SIGNAL(target, COMSIG_ATOM_HOLY_ATTACK, source, user, antimagic_flags)
|
||||
@@ -134,6 +134,13 @@ GLOBAL_LIST_INIT(spells, typesof(/datum/spell))
|
||||
var/static/list/spell_handlers = list()
|
||||
/// handles a given spells cooldowns. tracks the time until its off cooldown,
|
||||
var/datum/spell_cooldown/cooldown_handler
|
||||
/// Flag for certain states that the spell requires the user be in to cast.
|
||||
var/spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
|
||||
/// This determines what type of antimagic is needed to block the spell.
|
||||
/// (MAGIC_RESISTANCE, MAGIC_RESISTANCE_MIND, MAGIC_RESISTANCE_HOLY)
|
||||
/// If SPELL_REQUIRES_NO_ANTIMAGIC is set in Spell requirements,
|
||||
/// The spell cannot be cast if the caster has any of the antimagic flags set.
|
||||
var/antimagic_flags = MAGIC_RESISTANCE
|
||||
|
||||
/* Checks if the user can cast the spell
|
||||
* @param charge_check If the proc should do the cooldown check
|
||||
@@ -467,6 +474,12 @@ GLOBAL_LIST_INIT(spells, typesof(/datum/spell))
|
||||
if(T && is_admin_level(T.z))
|
||||
return FALSE
|
||||
|
||||
// If the spell requires the user has no antimagic equipped, and they're holding antimagic
|
||||
// that corresponds with the spell's antimagic, then they can't actually cast the spell
|
||||
if((spell_requirements & SPELL_REQUIRES_NO_ANTIMAGIC) && !user.can_cast_magic(antimagic_flags))
|
||||
to_chat(user, "<span class='warning'>Some form of antimagic is preventing you from casting [src]!</span>")
|
||||
return FALSE
|
||||
|
||||
if(!holy_area_cancast && user.holy_check())
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ Updates the spell's actions on use as well, so they know when they can or can't
|
||||
create_attack_logs = FALSE
|
||||
/// Every alien spell creates only logs, no attack messages on someone placing weeds, but you DO get attack messages on neurotoxin and corrosive acid
|
||||
create_custom_logs = TRUE
|
||||
antimagic_flags = NONE
|
||||
/// How much plasma it costs to use this
|
||||
var/plasma_cost = 0
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
base_cooldown = 1
|
||||
action_background_icon_state = "bg_alien"
|
||||
clothes_req = FALSE
|
||||
antimagic_flags = NONE
|
||||
create_attack_logs = FALSE
|
||||
/// Every alien spell creates only logs, no attack messages on someone placing weeds, but you DO get attack messages on neurotoxin and corrosive acid
|
||||
create_custom_logs = TRUE
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
|
||||
/obj/item/melee/touch_attack/banana/after_attack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
|
||||
if(is_apprentice_spell && iswizard(target) && target != user)
|
||||
to_chat(user, "<span class='danger'>Seriously?! Honk THEM, not me!</span>")
|
||||
return
|
||||
if(!proximity_flag || target == user || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
|
||||
if(!proximity_flag || target == user || blocked_by_antimagic || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
|
||||
return
|
||||
|
||||
var/datum/effect_system/smoke_spread/s = new
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
overlay = null
|
||||
action_icon_state = "bloodcrawl"
|
||||
action_background_icon_state = "bg_demon"
|
||||
antimagic_flags = NONE
|
||||
var/allowed_type = /obj/effect/decal/cleanable
|
||||
var/phased = FALSE
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
clothes_req = FALSE
|
||||
invocation = "none"
|
||||
invocation_type = "none"
|
||||
antimagic_flags = NONE
|
||||
|
||||
selection_activated_message = "<span class='notice'>You prepare a blessing. Click on a target to start blessing.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>The crew will be blessed another time.</span>"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
clothes_req = FALSE
|
||||
base_cooldown = 5 SECONDS
|
||||
human_req = TRUE
|
||||
antimagic_flags = NONE
|
||||
action_icon_state = "chef"
|
||||
action_background_icon_state = "bg_default"
|
||||
still_recharging_msg = "All this thinking makes your head hurt, wait a bit longer."
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
if(target.can_block_magic(antimagic_flags))
|
||||
target.visible_message("<span class='danger'>[target]'s face bursts into flames, which instantly burst outward, leaving [target.p_them()] unharmed!</span>",
|
||||
"<span class='danger'>Your face starts burning up, but the flames are repulsed by your anti-magic protection!</span>",
|
||||
)
|
||||
to_chat(user, "<span class='warning'>The spell had no effect!</span>")
|
||||
return FALSE
|
||||
|
||||
var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead
|
||||
magichead.flags |= NODROP | DROPDEL //curses!
|
||||
magichead.flags_inv = null //so you can still see their face
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
origin.Beam(target, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
|
||||
|
||||
/datum/spell/charge_up/bounce/lightning/apply_bounce_effect(mob/origin, mob/living/target, energy, mob/user)
|
||||
if(target.can_block_magic(antimagic_flags))
|
||||
target.visible_message(
|
||||
"<span class='warning'>[target] absorbs the spell, remaining unharmed!</span>",
|
||||
"<span class='danger'>You absorb the spell, remaining unharmed!</span>"
|
||||
)
|
||||
return
|
||||
if(damaging)
|
||||
target.electrocute_act(energy, "Lightning Bolt", flags = SHOCK_NOGLOVES)
|
||||
else
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
clothes_req = FALSE
|
||||
cast_sound = null
|
||||
human_req = TRUE
|
||||
antimagic_flags = NONE
|
||||
|
||||
action_icon_state = "mime"
|
||||
action_background_icon_state = "bg_mime"
|
||||
@@ -87,6 +88,7 @@
|
||||
clothes_req = FALSE
|
||||
base_cooldown = 30 SECONDS
|
||||
human_req = TRUE
|
||||
antimagic_flags = NONE
|
||||
|
||||
action_icon_state = "fingergun"
|
||||
action_background_icon_state = "bg_mime"
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
|
||||
/obj/item/melee/touch_attack/mime_malaise/after_attack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!proximity_flag || target == user || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
|
||||
|
||||
if(!proximity_flag || target == user || blocked_by_antimagic || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
|
||||
return
|
||||
|
||||
var/datum/effect_system/smoke_spread/s = new
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
selection_activated_message = "<span class='notice'>You prepare to transfer your mind. Click on a target to cast the spell.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>You decide that your current form is good enough.</span>"
|
||||
cooldown_min = 200 //100 deciseconds reduction per rank
|
||||
antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND
|
||||
var/list/protected_roles = list(SPECIAL_ROLE_WIZARD, SPECIAL_ROLE_CHANGELING, SPECIAL_ROLE_CULTIST) //which roles are immune to the spell
|
||||
var/paralysis_amount_caster = 40 SECONDS //how much the caster is paralysed for after the spell
|
||||
var/paralysis_amount_victim = 40 SECONDS //how much the victim is paralysed for after the spell
|
||||
@@ -47,6 +48,9 @@ Also, you never added distance checking after target is selected. I've went ahea
|
||||
if(issilicon(target))
|
||||
to_chat(user, "<span class='warning'>You feel this enslaved being is just as dead as its cold, hard exoskeleton.</span>")
|
||||
return
|
||||
if(target.can_block_magic(antimagic_flags))
|
||||
to_chat(user, "<span class='danger'>Their mind is resisting your spell.</span>")
|
||||
return
|
||||
|
||||
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.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
base_cooldown = 10
|
||||
clothes_req = FALSE
|
||||
antimagic_flags = NONE
|
||||
|
||||
message = "<span class='notice'>You toggle your night vision!</span>"
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
/datum/spell/rathens/cast(list/targets, mob/user = usr)
|
||||
for(var/mob/living/carbon/human/H in targets)
|
||||
if(H.can_block_magic(antimagic_flags))
|
||||
continue
|
||||
var/datum/effect_system/smoke_spread/s = new
|
||||
s.set_up(5, FALSE, H)
|
||||
s.start()
|
||||
|
||||
@@ -82,6 +82,8 @@
|
||||
desc = "A distortion in spacetime. You can hear faint music..."
|
||||
icon_state = "nothing"
|
||||
/// A flags which save people from being thrown about
|
||||
var/antimagic_flags = MAGIC_RESISTANCE
|
||||
/// A flags which save people from being thrown about
|
||||
var/obj/effect/cross_action/spacetime_dist/linked_dist
|
||||
/// Used to prevent an infinite loop in the space tiime continuum
|
||||
var/cant_teleport = FALSE
|
||||
@@ -101,6 +103,10 @@
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
/obj/effect/cross_action/spacetime_dist/proc/walk_link(atom/movable/AM)
|
||||
if(ismob(AM))
|
||||
var/mob/M = AM
|
||||
if(M.can_block_magic(antimagic_flags, charge_cost = 0))
|
||||
return
|
||||
if(linked_dist && walks_left > 0)
|
||||
flick("purplesparkles", src)
|
||||
linked_dist.get_walker(AM)
|
||||
|
||||
@@ -313,6 +313,10 @@
|
||||
return
|
||||
|
||||
var/mob/living/target = targets[1]
|
||||
if(target.can_block_magic(antimagic_flags))
|
||||
to_chat(target, "<span class='notice'>Your eye itches, but it passes momentarily.</span>")
|
||||
to_chat(user, "<span class='notice'>The spell had no effect!</span>")
|
||||
return FALSE
|
||||
target.EyeBlurry(40 SECONDS)
|
||||
target.EyeBlind(30 SECONDS)
|
||||
|
||||
@@ -414,6 +418,10 @@
|
||||
playMagSound()
|
||||
for(var/turf/T in targets) //Done this way so things don't get thrown all around hilariously.
|
||||
for(var/atom/movable/AM in T)
|
||||
if(ismob(AM))
|
||||
var/mob/victim_mob = AM
|
||||
if(victim_mob.can_block_magic(antimagic_flags))
|
||||
continue
|
||||
thrownatoms += AM
|
||||
|
||||
for(var/am in thrownatoms)
|
||||
@@ -457,6 +465,8 @@
|
||||
|
||||
/datum/spell/sacred_flame/cast(list/targets, mob/user = usr)
|
||||
for(var/mob/living/L in targets)
|
||||
if(L.can_block_magic(antimagic_flags))
|
||||
continue
|
||||
L.adjust_fire_stacks(20)
|
||||
if(isliving(user))
|
||||
var/mob/living/U = user
|
||||
|
||||
@@ -92,9 +92,12 @@
|
||||
var/found_someone = FALSE
|
||||
|
||||
for(var/mob/living/L in oview(9, owner))
|
||||
found_someone = TRUE
|
||||
playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE)
|
||||
L.Beam(owner, "grabber_beam", time = 1 SECONDS, maxdistance = 9)
|
||||
if(L.can_block_magic(MAGIC_RESISTANCE))
|
||||
to_chat(L, "<span class='warning'>You shake off the tendrils that try to wrap around you!</span>")
|
||||
continue
|
||||
found_someone = TRUE
|
||||
L.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
|
||||
if(found_someone)
|
||||
owner.visible_message("<span class='warning'>The violet light around [owner] glows black... and shoots off to those around [owner.p_them()]!</span>", "<span class='warning'>The tendrils around you cinch tightly... but then unwravel and fly at others!</span>")
|
||||
|
||||
Reference in New Issue
Block a user