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:
Qwertytoforty
2025-01-02 08:36:07 +00:00
committed by GitHub
co-authored by DGamerL Burzah
parent 6057ff5c31
commit 7bbebbb23f
62 changed files with 580 additions and 28 deletions
+166
View File
@@ -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)
+13
View File
@@ -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
+2 -1
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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>"
+1
View File
@@ -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."
+7
View File
@@ -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
+6
View File
@@ -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
+2
View File
@@ -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"
+2 -1
View File
@@ -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
+4
View File
@@ -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.
+1
View File
@@ -4,6 +4,7 @@
base_cooldown = 10
clothes_req = FALSE
antimagic_flags = NONE
message = "<span class='notice'>You toggle your night vision!</span>"
+2
View File
@@ -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()
+6
View File
@@ -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)
+10
View File
@@ -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
+4 -1
View File
@@ -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>")