diff --git a/code/__DEFINES/dcs/atom_signals.dm b/code/__DEFINES/dcs/atom_signals.dm index 294e5bd5aac..42745166c8d 100644 --- a/code/__DEFINES/dcs/atom_signals.dm +++ b/code/__DEFINES/dcs/atom_signals.dm @@ -134,5 +134,9 @@ #define COMPONENT_NO_MOUSEDROP (1<<0) ///from base of atom/MouseDrop_T: (/atom/from, /mob/user) #define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" + +/// Called on the atom being hit, from /datum/component/anti_magic/on_attack() : (obj/item/weapon, mob/user, antimagic_flags) +#define COMSIG_ATOM_HOLY_ATTACK "atom_holyattacked" /// On a ranged attack: base of mob/living/carbon/human/RangedAttack (/mob/living/carbon/human) #define COMSIG_ATOM_RANGED_ATTACKED "atom_range_attacked" + diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm index 3d0406f583e..e9b71bdc58b 100644 --- a/code/__DEFINES/dcs/mob_signals.dm +++ b/code/__DEFINES/dcs/mob_signals.dm @@ -22,11 +22,14 @@ #define COMSIG_MOB_ALTCLICKON "mob_altclickon" #define COMSIG_MOB_CANCEL_CLICKON (1<<0) +///from base of mob/can_cast_magic(): (mob/user, magic_flags, charge_cost) +#define COMSIG_MOB_RESTRICT_MAGIC "mob_cast_magic" +///from base of mob/can_block_magic(): (mob/user, casted_magic_flags, charge_cost) +#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" + #define COMPONENT_MAGIC_BLOCKED (1<<0) + ///from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj #define COMSIG_MOB_ALLOWED "mob_allowed" -///from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources) -#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" - #define COMPONENT_BLOCK_MAGIC (1<<0) ///from base of mob/create_mob_hud(): () #define COMSIG_MOB_HUD_CREATED "mob_hud_created" ///from base of atom/attack_hand(): (mob/user) diff --git a/code/__DEFINES/magic_defines.dm b/code/__DEFINES/magic_defines.dm new file mode 100644 index 00000000000..9d0ee8158cd --- /dev/null +++ b/code/__DEFINES/magic_defines.dm @@ -0,0 +1,16 @@ +// Bitflags for magic resistance types +/// Default magic resistance that blocks normal magic (wizard, spells, magical staff projectiles) +#define MAGIC_RESISTANCE (1<<0) +/// Tinfoil hat magic resistance that blocks mental magic (telepathy / mind links, mind curses, abductors) +#define MAGIC_RESISTANCE_MIND (1<<1) +/// Holy magic resistance that blocks unholy magic (revenant, vampire, voice of god) +#define MAGIC_RESISTANCE_HOLY (1<<2) + +DEFINE_BITFIELD(antimagic_flags, list( + "MAGIC_RESISTANCE" = MAGIC_RESISTANCE, + "MAGIC_RESISTANCE_HOLY" = MAGIC_RESISTANCE_HOLY, + "MAGIC_RESISTANCE_MIND" = MAGIC_RESISTANCE_MIND, +)) + +/// Whether the spell can be cast while the user has antimagic on them that corresponds to the spell's own antimagic flags. +#define SPELL_REQUIRES_NO_ANTIMAGIC (1 << 0) diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index 245fcc291bb..095e02a0fa8 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -244,6 +244,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_EMP_RESIST "emp_resist" //The mob will take less damage from EMPs #define TRAIT_MINDFLAYER_NULLIFIED "flayer_nullified" //The mindflayer will not be able to activate their abilities, or drain swarms from people #define TRAIT_FLYING "flying" +/// This mob is antimagic, and immune to spells / cannot cast spells +#define TRAIT_ANTIMAGIC "anti_magic" +/// This allows a person who has antimagic to cast spells without getting blocked +#define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock" +/// This mob recently blocked magic with some form of antimagic +#define TRAIT_RECENTLY_BLOCKED_MAGIC "recently_blocked_magic" #define TRAIT_UNKNOWN "unknown" // The person with this trait always appears as 'unknown'. #define TRAIT_CRYO_DESPAWNING "cryo_despawning" // dont adminbus this please diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 525e2cddb76..e0be5bf3b90 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -106,6 +106,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CANNOT_PULL" = TRAIT_CANNOT_PULL, "TRAIT_BSG_IMMUNE" = TRAIT_BSG_IMMUNE, "TRAIT_FLYING" = TRAIT_FLYING, + "TRAIT_ANTIMAGIC" = TRAIT_ANTIMAGIC, + "TRAIT_ANTIMAGIC_NO_SELFBLOCK" = TRAIT_ANTIMAGIC_NO_SELFBLOCK, + "TRAIT_RECENTLY_BLOCKED_MAGIC" = TRAIT_RECENTLY_BLOCKED_MAGIC, "TRAIT_UNKNOWN" = TRAIT_UNKNOWN ), diff --git a/code/datums/components/anti_magic.dm b/code/datums/components/anti_magic.dm new file mode 100644 index 00000000000..645e579a709 --- /dev/null +++ b/code/datums/components/anti_magic.dm @@ -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, "[parent] is interfering with your ability to cast magic!") + 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) diff --git a/code/datums/spell.dm b/code/datums/spell.dm index cd595686d61..3604140c819 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -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, "Some form of antimagic is preventing you from casting [src]!") + return FALSE + if(!holy_area_cancast && user.holy_check()) return FALSE diff --git a/code/datums/spells/alien_spells/basetype_alien_spell.dm b/code/datums/spells/alien_spells/basetype_alien_spell.dm index 12d98e8e489..02ac8ddab2a 100644 --- a/code/datums/spells/alien_spells/basetype_alien_spell.dm +++ b/code/datums/spells/alien_spells/basetype_alien_spell.dm @@ -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 diff --git a/code/datums/spells/alien_spells/basetype_alien_touch.dm b/code/datums/spells/alien_spells/basetype_alien_touch.dm index c58ff15bed2..f979b2af6ba 100644 --- a/code/datums/spells/alien_spells/basetype_alien_touch.dm +++ b/code/datums/spells/alien_spells/basetype_alien_touch.dm @@ -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 diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm index b09b68d0639..9dc684f061a 100644 --- a/code/datums/spells/banana_touch.dm +++ b/code/datums/spells/banana_touch.dm @@ -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, "Seriously?! Honk THEM, not me!") 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 diff --git a/code/datums/spells/bloodcrawl.dm b/code/datums/spells/bloodcrawl.dm index 007ff329932..9bc2864a656 100644 --- a/code/datums/spells/bloodcrawl.dm +++ b/code/datums/spells/bloodcrawl.dm @@ -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 diff --git a/code/datums/spells/chaplain_bless.dm b/code/datums/spells/chaplain_bless.dm index 970cebd638c..518f51207c8 100644 --- a/code/datums/spells/chaplain_bless.dm +++ b/code/datums/spells/chaplain_bless.dm @@ -7,6 +7,7 @@ clothes_req = FALSE invocation = "none" invocation_type = "none" + antimagic_flags = NONE selection_activated_message = "You prepare a blessing. Click on a target to start blessing." selection_deactivated_message = "The crew will be blessed another time." diff --git a/code/datums/spells/chef.dm b/code/datums/spells/chef.dm index a28438b397a..2e42e0b1be6 100644 --- a/code/datums/spells/chef.dm +++ b/code/datums/spells/chef.dm @@ -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." diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm index f0614edddd5..32d5cab3dcb 100644 --- a/code/datums/spells/horsemask.dm +++ b/code/datums/spells/horsemask.dm @@ -27,6 +27,13 @@ var/mob/living/carbon/human/target = targets[1] + if(target.can_block_magic(antimagic_flags)) + target.visible_message("[target]'s face bursts into flames, which instantly burst outward, leaving [target.p_them()] unharmed!", + "Your face starts burning up, but the flames are repulsed by your anti-magic protection!", + ) + to_chat(user, "The spell had no effect!") + 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 diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index d4a141c1ccb..c7e79e2e103 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -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( + "[target] absorbs the spell, remaining unharmed!", + "You absorb the spell, remaining unharmed!" + ) + return if(damaging) target.electrocute_act(energy, "Lightning Bolt", flags = SHOCK_NOGLOVES) else diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index 2e139981d64..eda8b982241 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -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" diff --git a/code/datums/spells/mime_malaise.dm b/code/datums/spells/mime_malaise.dm index f5e0011860c..67de6301334 100644 --- a/code/datums/spells/mime_malaise.dm +++ b/code/datums/spells/mime_malaise.dm @@ -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 diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm index 4690dec4972..2e3bad262cf 100644 --- a/code/datums/spells/mind_transfer.dm +++ b/code/datums/spells/mind_transfer.dm @@ -9,6 +9,7 @@ selection_activated_message = "You prepare to transfer your mind. Click on a target to cast the spell." selection_deactivated_message = "You decide that your current form is good enough." 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, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") return + if(target.can_block_magic(antimagic_flags)) + to_chat(user, "Their mind is resisting your spell.") + 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. diff --git a/code/datums/spells/night_vision.dm b/code/datums/spells/night_vision.dm index de085eae199..ae5c94f9761 100644 --- a/code/datums/spells/night_vision.dm +++ b/code/datums/spells/night_vision.dm @@ -4,6 +4,7 @@ base_cooldown = 10 clothes_req = FALSE + antimagic_flags = NONE message = "You toggle your night vision!" diff --git a/code/datums/spells/rathens.dm b/code/datums/spells/rathens.dm index 19510324444..22969027c46 100644 --- a/code/datums/spells/rathens.dm +++ b/code/datums/spells/rathens.dm @@ -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() diff --git a/code/datums/spells/spacetime_dist.dm b/code/datums/spells/spacetime_dist.dm index 2e7a45c95ee..9d7b09c1629 100644 --- a/code/datums/spells/spacetime_dist.dm +++ b/code/datums/spells/spacetime_dist.dm @@ -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) diff --git a/code/datums/spells/wizard_spells.dm b/code/datums/spells/wizard_spells.dm index 5466ee845cf..c62f868f401 100644 --- a/code/datums/spells/wizard_spells.dm +++ b/code/datums/spells/wizard_spells.dm @@ -313,6 +313,10 @@ return var/mob/living/target = targets[1] + if(target.can_block_magic(antimagic_flags)) + to_chat(target, "Your eye itches, but it passes momentarily.") + to_chat(user, "The spell had no effect!") + 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 diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index c4795b8d71a..aa8dfa6c23d 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -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, "You shake off the tendrils that try to wrap around you!") + continue + found_someone = TRUE L.apply_status_effect(STATUS_EFFECT_VOID_PRICE) if(found_someone) owner.visible_message("The violet light around [owner] glows black... and shoots off to those around [owner.p_them()]!", "The tendrils around you cinch tightly... but then unwravel and fly at others!") diff --git a/code/game/dna/mutations/mutation_powers.dm b/code/game/dna/mutations/mutation_powers.dm index f9cb5574c82..078633409f4 100644 --- a/code/game/dna/mutations/mutation_powers.dm +++ b/code/game/dna/mutations/mutation_powers.dm @@ -301,6 +301,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS + antimagic_flags = NONE selection_activated_message = "Your mind grow cold. Click on a target to cast the spell." selection_deactivated_message = "Your mind returns to normal." @@ -372,6 +373,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = NONE action_icon_state = "genetic_eat" @@ -511,6 +513,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = NONE action_icon_state = "genetic_jump" var/leap_distance = 10 @@ -636,6 +639,7 @@ selection_deactivated_message = "Your body calms down again." invocation_type = "none" + antimagic_flags = NONE action_icon_state = "genetic_poly" @@ -684,6 +688,7 @@ human_req = TRUE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = MAGIC_RESISTANCE_MIND action_icon_state = "genetic_empath" @@ -793,6 +798,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = NONE action_icon_state = "genetic_morph" @@ -983,6 +989,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = MAGIC_RESISTANCE_MIND action_icon_state = "genetic_project" @@ -1017,6 +1024,7 @@ base_cooldown = 0 clothes_req = FALSE stat_allowed = CONSCIOUS + antimagic_flags = MAGIC_RESISTANCE_MIND invocation_type = "none" action_icon_state = "genetic_mindscan" var/list/expanded_minds = list() @@ -1105,6 +1113,7 @@ clothes_req = FALSE stat_allowed = CONSCIOUS invocation_type = "none" + antimagic_flags = MAGIC_RESISTANCE_MIND action_icon_state = "genetic_view" diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm index 6f97e380e15..12b80aa8da5 100644 --- a/code/game/gamemodes/cult/blood_magic.dm +++ b/code/game/gamemodes/cult/blood_magic.dm @@ -385,6 +385,7 @@ var/uses = 1 var/health_cost = 0 //The amount of health taken from the user when invoking the spell var/datum/action/innate/cult/blood_spell/source + var/antimagic_flags = MAGIC_RESISTANCE_HOLY /obj/item/melee/blood_magic/Initialize(mapload, spell) . = ..() @@ -418,6 +419,11 @@ uses = 0 qdel(src) return + if(M.can_block_magic(MAGIC_RESISTANCE_HOLY)) + to_chat(user, "[M] absorbs your spell!") + uses = 0 + qdel(src) + return add_attack_logs(user, M, "used a cult spell ([src]) on") M.lastattacker = user.real_name diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index ebe886ff10c..9592e8ecaf4 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -249,9 +249,8 @@ structure_check() searches for nearby cultist structures required for the invoca qdel(src) /mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes - var/obj/item/nullrod/N = locate() in src - if(N) - return N + if(can_block_magic(MAGIC_RESISTANCE_HOLY)) + return TRUE return FALSE //Rite of Enlightenment: Converts a normal crewmember to the cult, or offer them as sacrifice if cant be converted. diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 87b88676d3c..90ba39865b7 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -13,6 +13,7 @@ base_cooldown = 0 var/uses //If we have multiple uses of the same power var/auto_use_uses = TRUE //If we automatically use up uses on each activation + antimagic_flags = NONE /datum/spell/ai_spell/create_new_targeting() return new /datum/spell_targeting/self diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm index df5df3aae74..d44adb00d17 100644 --- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm +++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm @@ -212,6 +212,8 @@ return hit = TRUE // to prevent double hits from the pull . = ..() + if(!.) + return for(var/atom/extinguish_target in range(2, src)) extinguish_target.extinguish_light(TRUE) if(!isliving(target)) diff --git a/code/game/gamemodes/miniantags/guardian/host_actions.dm b/code/game/gamemodes/miniantags/guardian/host_actions.dm index 4a4ff5144ba..e21f2509415 100644 --- a/code/game/gamemodes/miniantags/guardian/host_actions.dm +++ b/code/game/gamemodes/miniantags/guardian/host_actions.dm @@ -106,6 +106,7 @@ name = "Place Teleportation Beacon" desc = "Mark a floor as your beacon point, allowing you to warp targets to it. Your beacon requires an anchor, will not work on space tiles." clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 300 SECONDS action_icon_state = "no_state" action_background_icon = 'icons/mob/guardian.dmi' @@ -129,6 +130,7 @@ name = "Set Surveillance Snare" desc = "Places an invisible Surveillance Snare on the ground, if someone walks over it you'll be alerted. Max of 6 snares active at a time" clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 3 SECONDS action_icon_state = "no_state" action_background_icon = 'icons/mob/guardian.dmi' @@ -160,6 +162,7 @@ name = "Change battlecry" desc = "Changes your battlecry." clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 1 SECONDS action_icon_state = "no_state" action_background_icon = 'icons/mob/guardian.dmi' diff --git a/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm b/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm index b8f2f1623bc..847ee5cce00 100644 --- a/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm +++ b/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm @@ -5,6 +5,7 @@ action_background_icon_state = "bg_morph" action_icon_state = "morph_airlock" clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 10 SECONDS selection_activated_message = "Click on an airlock to try pass it." diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm index d248e460ba2..97c86faf060 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm +++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm @@ -10,6 +10,7 @@ /datum/spell/pulse_demon clothes_req = FALSE + antimagic_flags = NONE action_background_icon_state = "bg_pulsedemon" var/locked = TRUE var/unlock_cost = 1 KJ diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm index 0a167ec7724..ad6da8561d8 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm @@ -134,6 +134,7 @@ clothes_req = FALSE action_icon_state = "r_transmit" action_background_icon_state = "bg_revenant" + antimagic_flags = MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND /datum/spell/revenant_transmit/create_new_targeting() var/datum/spell_targeting/targeted/T = new() @@ -165,6 +166,7 @@ var/unlock_amount = 100 /// How much essence it costs to use var/cast_amount = 50 + antimagic_flags = MAGIC_RESISTANCE_HOLY /datum/spell/aoe/revenant/New() ..() diff --git a/code/game/gamemodes/wizard/godhand.dm b/code/game/gamemodes/wizard/godhand.dm index c9ffe3584e3..f75c5a692b8 100644 --- a/code/game/gamemodes/wizard/godhand.dm +++ b/code/game/gamemodes/wizard/godhand.dm @@ -16,6 +16,8 @@ throw_range = 0 throw_speed = 0 new_attack_chain = TRUE + /// Has it been blocked by antimagic? If so, abort. + var/blocked_by_antimagic = FALSE /obj/item/melee/touch_attack/New(spell) attached_spell = spell @@ -37,6 +39,17 @@ to_chat(user, "You can't reach out!") return FINISH_ATTACK +/obj/item/melee/touch_attack/after_attack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + var/mob/mob_victim = target + if(istype(mob_victim) && mob_victim.can_block_magic(attached_spell.antimagic_flags)) + to_chat(user, "[mob_victim] absorbs your spell!") + blocked_by_antimagic = TRUE + if(attached_spell && attached_spell.cooldown_handler) + attached_spell.cooldown_handler.start_recharge(attached_spell.cooldown_handler.recharge_duration * 0.5) + qdel(src) + return + /obj/item/melee/touch_attack/proc/handle_delete(mob/user) if(catchphrase) user.say(catchphrase) @@ -53,9 +66,10 @@ icon_state = "disintegrate" item_state = "disintegrate" + /obj/item/melee/touch_attack/disintegrate/after_attack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(!proximity_flag || target == user || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //exploding after touching yourself would be bad + if(!proximity_flag || target == user || blocked_by_antimagic || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //exploding after touching yourself would be bad return var/mob/M = target do_sparks(4, 0, M.loc) //no idea what the 0 is @@ -72,7 +86,8 @@ /obj/item/melee/touch_attack/fleshtostone/after_attack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(!proximity_flag || target == user || !isliving(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //getting hard after touching yourself would also be bad + + if(!proximity_flag || target == user || blocked_by_antimagic || !isliving(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //getting hard after touching yourself would also be bad return var/mob/living/L = target L.Stun(4 SECONDS) @@ -90,12 +105,14 @@ /obj/item/melee/touch_attack/plushify/after_attack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(!proximity_flag || target == user || !isliving(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //There are better ways to get a good nights sleep in a bed. + + if(!proximity_flag || target == user || blocked_by_antimagic || !isliving(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //There are better ways to get a good nights sleep in a bed. return var/mob/living/L = target L.plushify() handle_delete(user) + /obj/item/melee/touch_attack/fake_disintegrate name = "toy plastic hand" desc = "This hand of mine glows with an awesome power! Ok, maybe just batteries." @@ -107,7 +124,8 @@ /obj/item/melee/touch_attack/fake_disintegrate/after_attack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(!proximity_flag || target == user || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //not exploding after touching yourself would be bad + + if(!proximity_flag || target == user || blocked_by_antimagic || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //not exploding after touching yourself would be bad return do_sparks(4, 0, target.loc) playsound(target.loc, 'sound/goonstation/effects/gib.ogg', 50, 1) @@ -123,7 +141,8 @@ /obj/item/melee/touch_attack/cluwne/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)) //clowning around after touching yourself would unsurprisingly, be bad + + if(!proximity_flag || target == user || blocked_by_antimagic || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //clowning around after touching yourself would unsurprisingly, be bad return if(iswizard(target)) diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm index 5f7eed073e9..b6d2cbc6140 100644 --- a/code/game/gamemodes/wizard/magic_tarot.dm +++ b/code/game/gamemodes/wizard/magic_tarot.dm @@ -232,6 +232,11 @@ qdel(src) /obj/item/magic_tarot_card/proc/pre_activate(mob/user, atom/movable/thrower) + if(user != thrower) //Ignore antimagic stuff if the user is the thrower (aka self activation) + if(user.can_block_magic(our_tarot.antimagic_flags, 1)) + visible_message("[src] burns up in a flash on contact with [user]!") + qdel(src) + return has_been_activated = TRUE forceMove(user) var/obj/effect/temp_visual/card_preview/tarot/draft = new(user, "tarot_[our_tarot.card_icon]") @@ -288,6 +293,8 @@ var/card_icon = "the_unknown" /// Are we reversed? Used for the card back. var/reversed = FALSE + /// What antimagic flags do we have? + var/antimagic_flags = MAGIC_RESISTANCE /datum/tarot/proc/activate(mob/living/target) stack_trace("A bugged tarot card was spawned and used. Please make an issue report! Type was [src.type]") @@ -688,8 +695,11 @@ var/sparkle_path = /obj/effect/temp_visual/gravpush for(var/turf/T in range(5, target)) //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 thrown_atoms += AM - for(var/atom/movable/AM as anything in thrown_atoms) if(AM == target || AM.anchored || (ismob(AM) && !isliving(AM))) continue @@ -729,6 +739,9 @@ /datum/tarot/reversed/the_empress/activate(mob/living/target) for(var/mob/living/L in oview(9, target)) + if(L.can_block_magic(antimagic_flags)) + to_chat(L, "You feel calm for a second, but it quickly passes.") + continue L.apply_status_effect(STATUS_EFFECT_PACIFIED) /datum/tarot/reversed/the_emperor @@ -763,6 +776,8 @@ for(var/mob/living/M in shuffle(orange(7, target))) if(M.stat == DEAD) //Let us not have dead mobs be used to make a disco inferno. continue + if(M.can_block_magic(antimagic_flags)) //Be spared! + continue if(active_chasers >= 2) return var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(target), target, M, 1, FALSE) @@ -959,6 +974,7 @@ desc = "May you remember lost memories." extended_desc = "will reveal the memories of everyone in range to the user." card_icon = "the_moon?" + antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND /datum/tarot/reversed/the_moon/activate(mob/living/target) for(var/mob/living/L in view(5, target)) //Shorter range as this kinda can give away antagonists, though that is also funny. diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index ab14142c562..22e844b1917 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -499,7 +499,9 @@ //Weapons and Armors /datum/spellbook_entry/item/battlemage name = "Battlemage Armor" - desc = "An ensorceled spaceproof suit of protective yet light armor, protected by a powerful shield. The shield can completely negate 15 attacks before permanently failing." + desc = "An ensorceled spaceproof suit of protective yet light armor, protected by a powerful shield. The shield can completely negate 15 attacks before permanently failing. \ + This armor grants you full protection from magical attacks, and allows you to cast magic despite that. However, this means it will also block wands or staffs of \ + healing from working on you, and should be removed before application." item_path = /obj/item/storage/box/wizard/hardsuit limit = 1 category = "Weapons and Armors" diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm index 7c59c10efe1..b86ae670a39 100644 --- a/code/game/objects/effects/forcefields.dm +++ b/code/game/objects/effects/forcefields.dm @@ -17,6 +17,8 @@ /obj/effect/forcefield/wizard var/mob/wizard + /// Flags for what antimagic can just ignore our forcefields + var/antimagic_flags = MAGIC_RESISTANCE /obj/effect/forcefield/wizard/Initialize(mapload, mob/summoner) . = ..() @@ -25,6 +27,10 @@ /obj/effect/forcefield/wizard/CanPass(atom/movable/mover, border_dir) if(mover == wizard) return TRUE + if(isliving(mover)) + var/mob/living/living_mover = mover + if(living_mover.can_block_magic(antimagic_flags, charge_cost = 0)) + return TRUE return FALSE ///////////Mimewalls/////////// diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index a1179c68167..0b567a426af 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -21,9 +21,12 @@ var/list/fluff_transformations = list() /// Extra 'Holy' burn damage for ERT null rods var/sanctify_force = 0 + /// The antimagic type the nullrod has. + var/antimagic_type = MAGIC_RESISTANCE_HOLY /obj/item/nullrod/Initialize(mapload) . = ..() + AddComponent(/datum/component/anti_magic, antimagic_type) if(!length(variant_names)) for(var/I in typesof(/obj/item/nullrod)) var/obj/item/nullrod/rod = I @@ -37,11 +40,6 @@ /obj/item/nullrod/attack__legacy__attackchain(mob/M, mob/living/carbon/user) ..() - var/datum/antagonist/vampire/V = M.mind?.has_antag_datum(/datum/antagonist/vampire) - if(ishuman(M) && V && HAS_MIND_TRAIT(M, TRAIT_HOLY)) - if(!V.get_ability(/datum/vampire_passive/full)) - to_chat(M, "The nullrod's power interferes with your own!") - V.adjust_nullification(30 + sanctify_force, 15 + sanctify_force) if(!sanctify_force) return if(isliving(M)) @@ -593,6 +591,16 @@ to_chat(user, "Your prayer to [SSticker.Bible_deity_name] was interrupted.") praying = FALSE + +/obj/item/nullrod/nazar + name = "nazar" + icon_state = "nazar" + item_state = null + desc = "A set of glass beads and amulet, which has been forged to provide powerful magic protection to the wielder." + force = 0 + throwforce = 0 + antimagic_type = ALL + /obj/item/nullrod/salt name = "Holy Salt" icon = 'icons/obj/food/containers.dmi' diff --git a/code/modules/antagonists/mind_flayer/flayer_power.dm b/code/modules/antagonists/mind_flayer/flayer_power.dm index b48c9c25ca9..481c3d0a78a 100644 --- a/code/modules/antagonists/mind_flayer/flayer_power.dm +++ b/code/modules/antagonists/mind_flayer/flayer_power.dm @@ -3,6 +3,7 @@ desc = "This spell needs a description!" human_req = TRUE clothes_req = FALSE + antimagic_flags = NONE /// A reference to the owner mindflayer's antag datum. var/datum/antagonist/mindflayer/flayer diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm index 3fe4dcc7660..b6e97c0c671 100644 --- a/code/modules/antagonists/vampire/vamp_datum.dm +++ b/code/modules/antagonists/vampire/vamp_datum.dm @@ -93,6 +93,7 @@ RESTRICT_TYPE(/datum/antagonist/vampire) mob_override.dna?.species.hunger_icon = initial(mob_override.dna.species.hunger_icon) owner.current.alpha = 255 REMOVE_TRAITS_IN(owner.current, "vampire") + UnregisterSignal(owner, COMSIG_ATOM_HOLY_ATTACK) #define BLOOD_GAINED_MODIFIER 0.5 @@ -362,8 +363,21 @@ RESTRICT_TYPE(/datum/antagonist/vampire) mob_override.dna?.species.hunger_icon = 'icons/mob/screen_hunger_vampire.dmi' check_vampire_upgrade(FALSE) + RegisterSignal(mob_override, COMSIG_ATOM_HOLY_ATTACK, PROC_REF(holy_attack_reaction)) - +/datum/antagonist/vampire/proc/holy_attack_reaction(mob/target, obj/item/source, mob/user, antimagic_flags) + SIGNAL_HANDLER // COMSIG_ATOM_HOLY_ATTACK + if(!HAS_MIND_TRAIT(user, TRAIT_HOLY)) // Sec officer with a nullrod, or miner with a talisman, does not get to do this + return + if(!source.force) // Needs force to work. + return + var/bonus_force = 0 + if(istype(source, /obj/item/nullrod)) + var/obj/item/nullrod/N = source + bonus_force = N.sanctify_force + if(!get_ability(/datum/vampire_passive/full)) + to_chat(owner.current, "[source]'s power interferes with your own!") + adjust_nullification(30 + bonus_force, 15 + bonus_force) /datum/antagonist/vampire/custom_blurb() return "On the date [GLOB.current_date_string], at [station_time_timestamp()],\n in the [station_name()], [get_area_name(owner.current, TRUE)]...\nThe hunt begins again..." diff --git a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm index d749bfccee3..c38b8523e3d 100644 --- a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm @@ -161,6 +161,10 @@ /datum/spell/vampire/switch_places/cast(list/targets, mob/user) var/mob/living/target = targets[1] + if(target.can_block_magic(antimagic_flags)) + to_chat(user, "The spell had no effect!") + to_chat(target, "You feel space bending, but it rapidly dissipates.") + return FALSE var/turf/user_turf = get_turf(user) var/turf/target_turf = get_turf(target) if(!(SEND_SIGNAL(target, COMSIG_MOVABLE_TELEPORTING, user_turf) & COMPONENT_BLOCK_TELEPORT)) diff --git a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm index 20071ab2ebf..5207ccf7d97 100644 --- a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm @@ -157,6 +157,8 @@ /obj/item/projectile/magic/demonic_grasp/on_hit(atom/target, blocked, hit_zone) . = ..() + if(!.) + return if(!isliving(target)) return var/mob/living/L = target diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm index ff227c1774b..21e6b97f5fa 100644 --- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm @@ -3,6 +3,9 @@ //Other vampires and thralls aren't affected if(mind?.has_antag_datum(/datum/antagonist/vampire) || mind?.has_antag_datum(/datum/antagonist/mindslave/thrall)) return FALSE + /// Chaplains with their nullrod can block a full power vampire, but a chaplain by themselfs or a crew with a null rod can not. + if(can_block_magic(MAGIC_RESISTANCE_HOLY) && HAS_MIND_TRAIT(src, TRAIT_HOLY)) + return FALSE //Vampires who have reached their full potential can affect nearly everything var/datum/antagonist/vampire/V = user?.mind.has_antag_datum(/datum/antagonist/vampire) if(V?.get_ability(/datum/vampire_passive/full)) @@ -10,12 +13,15 @@ //Holy characters are resistant to vampire powers if(HAS_MIND_TRAIT(src, TRAIT_HOLY)) return FALSE + if(can_block_magic(MAGIC_RESISTANCE_HOLY)) + return FALSE return TRUE /datum/spell/vampire action_background_icon_state = "bg_vampire" human_req = TRUE clothes_req = FALSE + antimagic_flags = MAGIC_RESISTANCE_HOLY /// How much blood this ability costs to use var/required_blood var/deduct_blood_on_cast = TRUE @@ -52,6 +58,7 @@ action_icon_state = "vampire_rejuvinate" base_cooldown = 20 SECONDS stat_allowed = UNCONSCIOUS + antimagic_flags = NONE // So. If you have a null rod on your person, you can't cast vampire spells. I would rather not have officers abuse this by putting a nullrod in their pocket or something to block rejuvinate. /datum/spell/vampire/self/rejuvenate/cast(list/targets, mob/user = usr) var/mob/living/U = user diff --git a/code/modules/antagonists/zombie/zombie_spells.dm b/code/modules/antagonists/zombie/zombie_spells.dm index 3739a953438..39d02fa9982 100644 --- a/code/modules/antagonists/zombie/zombie_spells.dm +++ b/code/modules/antagonists/zombie/zombie_spells.dm @@ -5,6 +5,7 @@ action_background_icon_state = "bg_vampire" human_req = TRUE clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 0 SECONDS var/list/our_claws = list() var/infection_stage = 1 // mostly for adminbus and testing diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 6a4511534f1..7314f898497 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -200,6 +200,17 @@ /obj/item/clothing/suit/space/hardsuit/wizard/setup_shielding() AddComponent(/datum/component/shielded, max_charges = 15, recharge_start_delay = 0 SECONDS) +/obj/item/clothing/suit/space/hardsuit/wizard/equipped(mob/user, slot) + . = ..() + ADD_TRAIT(user, TRAIT_ANTIMAGIC, "[UID(src)]") + ADD_TRAIT(user, TRAIT_ANTIMAGIC_NO_SELFBLOCK, "[UID(src)]") + +/obj/item/clothing/suit/space/hardsuit/wizard/dropped(mob/user) + . = ..() + REMOVE_TRAIT(user, TRAIT_ANTIMAGIC, "[UID(src)]") + REMOVE_TRAIT(user, TRAIT_ANTIMAGIC_NO_SELFBLOCK, "[UID(src)]") + + /obj/item/clothing/suit/space/hardsuit/wizard/arch desc = "For the arch wizard in need of additional protection." min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm index d0329bf4fcb..4cf0d24f8ce 100644 --- a/code/modules/mining/lavaland/loot/tendril_loot.dm +++ b/code/modules/mining/lavaland/loot/tendril_loot.dm @@ -338,6 +338,7 @@ max_charges = 1 flags = NOBLUDGEON force = 18 + antimagic_flags = NONE /obj/item/ammo_casing/magic/hook name = "hook" @@ -384,7 +385,7 @@ //Immortality Talisman /obj/item/immortality_talisman - name = "Immortality Talisman" + name = "\improper Immortality Talisman" desc = "A dread talisman that can render you completely invulnerable." icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "talisman" @@ -393,6 +394,21 @@ actions_types = list(/datum/action/item_action/immortality) var/cooldown = 0 +/obj/item/immortality_talisman/Initialize(mapload) + . = ..() + AddComponent(/datum/component/anti_magic, ALL) + +/obj/item/immortality_talisman/equipped(mob/user, slot) + ..() + if(slot != ITEM_SLOT_IN_BACKPACK) + var/user_UID = user.UID() + ADD_TRAIT(user, TRAIT_ANTIMAGIC_NO_SELFBLOCK, user_UID) + +/obj/item/immortality_talisman/dropped(mob/user, silent) + . = ..() + var/user_UID = user.UID() + REMOVE_TRAIT(user, TRAIT_ANTIMAGIC_NO_SELFBLOCK, user_UID) + /datum/action/item_action/immortality name = "Immortality" diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index e4b1f52529d..89e0de5217f 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -184,6 +184,10 @@ ) hide_tail_by_species = list("Unathi", "Tajaran", "Vox", "Vulpkanin") +/obj/item/clothing/suit/hooded/berserker/Initialize(mapload) + . = ..() + AddComponent(/datum/component/anti_magic, ALL, inventory_flags = ITEM_SLOT_OUTER_SUIT) + /obj/item/clothing/head/hooded/berserker name = "berserker helmet" desc = "Peering into the eyes of the helmet is enough to seal damnation." diff --git a/code/modules/mob/living/carbon/superheroes.dm b/code/modules/mob/living/carbon/superheroes.dm index a7f63bd6e07..cb497112049 100644 --- a/code/modules/mob/living/carbon/superheroes.dm +++ b/code/modules/mob/living/carbon/superheroes.dm @@ -143,6 +143,7 @@ desc = "Allows you to recruit a conscious, non-braindead, non-catatonic human to be part of the Greyshirts, your personal henchmen. This works on Assistants only and you can recruit a maximum of 3!." base_cooldown = 450 clothes_req = FALSE + antimagic_flags = NONE action_icon_state = "spell_greytide" var/recruiting = 0 diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index dd136add437..653a6c7cdb3 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -242,6 +242,7 @@ name = "Unfold/Fold Chassis" desc = "Allows you to fold in/out of your mobile form." clothes_req = FALSE + antimagic_flags = NONE base_cooldown = 20 SECONDS action_icon_state = "repairbot" action_background_icon_state = "bg_tech_blue" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm index 05f9e655b73..cd32ea73e96 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm @@ -63,6 +63,7 @@ gain_desc = "You can now charge at a target on screen, dealing massive damage and destroying structures." base_cooldown = 30 SECONDS clothes_req = FALSE + antimagic_flags = NONE action_icon_state = "terror_prince" /datum/spell/princely_charge/create_new_targeting() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 10f65af02b1..7583e3bf47e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1428,3 +1428,90 @@ GLOBAL_LIST_INIT(holy_areas, typecacheof(list( if(user.incapacitated()) return return relaydrive(user, direction) + + +/** + * Checks to see if the mob can cast normal magic spells. + * + * args: + * * magic_flags (optional) A bitfield with the type of magic being cast (see flags at: /datum/component/anti_magic) +**/ +/mob/proc/can_cast_magic(magic_flags = MAGIC_RESISTANCE) + if(magic_flags == NONE) // magic with the NONE flag can always be cast + return TRUE + + var/restrict_magic_flags = SEND_SIGNAL(src, COMSIG_MOB_RESTRICT_MAGIC, magic_flags) + return restrict_magic_flags == NONE + +/** + * Checks to see if the mob can block magic + * + * args: + * * casted_magic_flags (optional) A bitfield with the types of magic resistance being checked (see flags at: /datum/component/anti_magic) + * * charge_cost (optional) The cost of charge to block a spell that will be subtracted from the protection used +**/ +/mob/proc/can_block_magic(casted_magic_flags = MAGIC_RESISTANCE, charge_cost = 1) + if(casted_magic_flags == NONE) // magic with the NONE flag is immune to blocking + return FALSE + + // A list of all things which are providing anti-magic to us + var/list/antimagic_sources = list() + var/is_magic_blocked = FALSE + + if(SEND_SIGNAL(src, COMSIG_MOB_RECEIVE_MAGIC, casted_magic_flags, charge_cost, antimagic_sources) & COMPONENT_MAGIC_BLOCKED) + is_magic_blocked = TRUE + if(HAS_TRAIT(src, TRAIT_ANTIMAGIC)) + is_magic_blocked = TRUE + if((casted_magic_flags & MAGIC_RESISTANCE_HOLY) && HAS_TRAIT(src, TRAIT_HOLY)) + is_magic_blocked = TRUE + + if(is_magic_blocked && charge_cost > 0 && !HAS_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC)) + on_block_magic_effects(casted_magic_flags, antimagic_sources) + + return is_magic_blocked + +/// Called whenever a magic effect with a charge cost is blocked and we haven't recently blocked magic. +/mob/proc/on_block_magic_effects(magic_flags, list/antimagic_sources) + return + +/mob/living/on_block_magic_effects(magic_flags, list/antimagic_sources) + ADD_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC, MAGIC_TRAIT) + addtimer(CALLBACK(src, PROC_REF(remove_recent_magic_block)), 6 SECONDS) + + var/mutable_appearance/antimagic_effect + var/antimagic_color + var/atom/antimagic_source = length(antimagic_sources) ? pick(antimagic_sources) : src + + if(magic_flags & MAGIC_RESISTANCE) + visible_message( + "[src] pulses red as [ismob(antimagic_source) ? p_they() : antimagic_source] absorbs magic energy!", + "An intense magical aura pulses around [ismob(antimagic_source) ? "you" : antimagic_source] as it dissipates into the air!", + ) + antimagic_effect = mutable_appearance('icons/effects/effects.dmi', "shield-red", ABOVE_MOB_LAYER) + antimagic_color = LIGHT_COLOR_BLOOD_MAGIC + playsound(src, 'sound/magic/magic_block.ogg', 50, TRUE) + + else if(magic_flags & MAGIC_RESISTANCE_HOLY) + visible_message( + "[src] starts to glow as [ismob(antimagic_source) ? p_they() : antimagic_source] emits a halo of light!", + "A feeling of warmth washes over [ismob(antimagic_source) ? "you" : antimagic_source] as rays of light surround your body and protect you!", + ) + antimagic_effect = mutable_appearance('icons/mob/genetics.dmi', "servitude", ABOVE_MOB_LAYER) + antimagic_color = LIGHT_COLOR_HOLY_MAGIC + playsound(src, 'sound/magic/magic_block_holy.ogg', 50, TRUE) + + else if(magic_flags & MAGIC_RESISTANCE_MIND) + visible_message( + "[src] forehead shines as [ismob(antimagic_source) ? p_they() : antimagic_source] repulses magic from their mind!", + "A feeling of cold splashes on [ismob(antimagic_source) ? "you" : antimagic_source] as your forehead reflects magic usering your mind!", + ) + antimagic_effect = mutable_appearance('icons/mob/genetics.dmi', "telekinesishead", ABOVE_MOB_LAYER) + antimagic_color = LIGHT_COLOR_DARK_BLUE + playsound(src, 'sound/magic/magic_block_mind.ogg', 50, TRUE) + + mob_light(_color = antimagic_color, _range = 2, _power = 2, _duration = 5 SECONDS) + add_overlay(antimagic_effect) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, cut_overlay), antimagic_effect), 5 SECONDS) + +/mob/living/proc/remove_recent_magic_block() + REMOVE_TRAIT(src, TRAIT_RECENTLY_BLOCKED_MAGIC, MAGIC_TRAIT) diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index 74176c1a164..af6292a6284 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -366,12 +366,17 @@ var/insignia_type = /obj/item/mod/module/insignia /// Additional module we add, as a treat. var/additional_module + /// Inquisitorial module, as we have reached that point. + var/inquisitorial_module /obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin, new_core) applied_modules.Insert(1, insignia_type) if(additional_module) applied_modules += additional_module default_pins += additional_module + if(inquisitorial_module) + applied_modules += inquisitorial_module + return ..() /obj/item/mod/control/pre_equipped/responsory/commander @@ -410,9 +415,10 @@ insignia_type = /obj/item/mod/module/insignia/chaplain additional_module = /obj/item/mod/module/injector -/// Diffrent look, as well as magic proof on TG. We don't have the magic proof stuff here, but it's perfect for inqusitors. Or if you want to give your ERT a fancy look. +/// Diffrent look, as well as magic proof. It's perfect for inqusitors. Or if you want to give your ERT a fancy look. At this time, the other ones are unused, and frankly I don't like the idea of antimagic gamma. /obj/item/mod/control/pre_equipped/responsory/inquisitory applied_skin = "inquisitory" + inquisitorial_module = /obj/item/mod/module/anti_magic /obj/item/mod/control/pre_equipped/responsory/inquisitory/commander insignia_type = /obj/item/mod/module/insignia/commander diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index f33db48aa27..002de3f3b30 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -527,6 +527,24 @@ /obj/item/mod/module/energy_shield/gamma shield_icon = "shield-old" +///Magic Nullifier - Protects you from magic. +/obj/item/mod/module/anti_magic + name = "MOD magic nullifier module" + desc = "A series of obsidian rods installed into critical points around the suit, \ + vibrated at a certain low frequency to enable them to resonate. \ + This creates a low-range, yet strong, magic nullification field around the user, \ + aided by a full replacement of the suit's normal coolant with holy water. \ + Spells will spall right off this field, though it'll do nothing to help others believe you about all this." + icon_state = "magic_nullifier" + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/anti_magic) + +/obj/item/mod/module/anti_magic/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, "[UID(src)]") + +/obj/item/mod/module/anti_magic/on_suit_deactivation(deleting = FALSE) + REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, "[UID(src)]") + /obj/item/mod/module/anomaly_locked/teslawall name = "MOD arc-shield module" // temp desc = "A module that uses a flux core to project an unstable protective shield." //change diff --git a/code/modules/projectiles/guns/chaos_bolt.dm b/code/modules/projectiles/guns/chaos_bolt.dm index fab8d9955f4..cd197bd8275 100644 --- a/code/modules/projectiles/guns/chaos_bolt.dm +++ b/code/modules/projectiles/guns/chaos_bolt.dm @@ -18,7 +18,8 @@ /obj/item/projectile/magic/chaos/on_hit(atom/target, blocked = 0) . = ..() - + if(!.) + return if(iswallturf(target) || isobj(target)) target.color = pick(GLOB.random_color_list) return diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index c542ffa0041..795e3176086 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -15,6 +15,7 @@ var/can_charge = TRUE var/ammo_type var/no_den_usage + var/antimagic_flags = MAGIC_RESISTANCE origin_tech = null clumsy_check = FALSE trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead @@ -31,6 +32,9 @@ return else no_den_usage = 0 + if(!user.can_cast_magic(antimagic_flags)) + to_chat(user, "[src] whizzles quietly.") + return FALSE ..() /obj/item/gun/magic/can_shoot() diff --git a/code/modules/projectiles/projectile/magic_projectiles.dm b/code/modules/projectiles/projectile/magic_projectiles.dm index 99c40d5929c..7f06491c7e9 100644 --- a/code/modules/projectiles/projectile/magic_projectiles.dm +++ b/code/modules/projectiles/projectile/magic_projectiles.dm @@ -6,6 +6,28 @@ nodamage = 1 armour_penetration_percentage = 100 flag = MAGIC + /// determines what type of antimagic can block the spell projectile + var/antimagic_flags = MAGIC_RESISTANCE + /// determines the drain cost on the antimagic item + var/antimagic_charge_cost = 1 + +/obj/item/projectile/magic/prehit(atom/target) + if(isliving(target)) + var/mob/living/victim = target + if(victim.can_block_magic(antimagic_flags, antimagic_charge_cost)) + visible_message("[src] fizzles on contact with [victim]!") + damage = 0 + nodamage = 1 + return FALSE + return ..() + +/obj/item/projectile/magic/on_hit(atom/target, blocked, hit_zone) + if(isliving(target)) + var/mob/living/victim = target + if(victim.can_block_magic(antimagic_flags, antimagic_charge_cost)) // Yes we have to check this twice welcome to bullet hell code + return FALSE + return ..() + /obj/item/projectile/magic/death name = "bolt of death" @@ -21,7 +43,7 @@ muzzle_flash_range = 2 muzzle_flash_color_override = LIGHT_COLOR_PURPLE impact_light_intensity = 7 - impact_light_range = 2.5 + impact_light_range = 2.5 impact_light_color_override = LIGHT_COLOR_PURPLE /obj/item/projectile/magic/fireball @@ -41,6 +63,8 @@ /obj/item/projectile/magic/death/on_hit(mob/living/carbon/target) . = ..() + if(!.) + return . if(isliving(target)) if(target.mob_biotypes & MOB_UNDEAD) //negative energy heals the undead if(target.revive()) @@ -75,6 +99,8 @@ /obj/item/projectile/magic/fireball/on_hit(target) . = ..() + if(!.) + return . var/turf/T = get_turf(target) explosion(T, exp_devastate, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire) if(ismob(target)) //multiple flavors of pain @@ -95,6 +121,8 @@ /obj/item/projectile/magic/resurrection/on_hit(mob/living/carbon/target) . = ..() + if(!.) + return . if(ismob(target)) if(target.mob_biotypes & MOB_UNDEAD) //positive energy harms the undead target.death(FALSE) @@ -121,6 +149,8 @@ /obj/item/projectile/magic/teleport/on_hit(mob/target) . = ..() + if(!.) + return . var/teleammount = 0 var/teleloc = target if(!isturf(target)) @@ -142,6 +172,8 @@ /obj/item/projectile/magic/door/on_hit(atom/target) . = ..() + if(!.) + return . var/atom/T = target.loc if(isturf(target) && target.density) if(!(istype(target, /turf/simulated/wall/indestructible))) @@ -179,6 +211,8 @@ /obj/item/projectile/magic/change/on_hit(atom/change) . = ..() + if(!.) + return . wabbajack(change) GLOBAL_LIST_INIT(wabbajack_hostile_animals, list( @@ -380,6 +414,9 @@ GLOBAL_LIST_INIT(wabbajack_docile_animals, list( SpinAnimation() /obj/item/projectile/magic/slipping/on_hit(atom/target, blocked = 0) + . = ..() + if(!.) + return . if(ishuman(target)) var/mob/living/carbon/human/H = target H.slip(src, slip_weaken, 0, FALSE, TRUE) //Slips even with noslips/magboots on. NO ESCAPE! @@ -394,7 +431,6 @@ GLOBAL_LIST_INIT(wabbajack_docile_animals, list( to_chat(target, "You get splatted by [src].") L.Weaken(slip_weaken) L.Stun(slip_stun) - . = ..() /obj/item/projectile/magic/arcane_barrage name = "arcane bolt" diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index b9084c82162..a96307d18a7 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -506,6 +506,7 @@ clothes_req = FALSE invocation = "none" invocation_type = "none" + antimagic_flags = NONE selection_activated_message = "You warm up your Binyat deck, there's an idle buzzing at the back of your mind as it awaits a target." selection_deactivated_message = "Your hacking deck makes an almost disappointed sounding buzz at the back of your mind as it powers down." action_icon_state = "hackerman" diff --git a/icons/mob/genetics.dmi b/icons/mob/genetics.dmi new file mode 100644 index 00000000000..cb5a3df029c Binary files /dev/null and b/icons/mob/genetics.dmi differ diff --git a/icons/obj/weapons/magical_weapons.dmi b/icons/obj/weapons/magical_weapons.dmi index f8be67a8c08..154cd879015 100644 Binary files a/icons/obj/weapons/magical_weapons.dmi and b/icons/obj/weapons/magical_weapons.dmi differ diff --git a/paradise.dme b/paradise.dme index 7db88875bf9..a508f9c8cb5 100644 --- a/paradise.dme +++ b/paradise.dme @@ -83,6 +83,7 @@ #include "code\__DEFINES\lighting_defines.dm" #include "code\__DEFINES\logs.dm" #include "code\__DEFINES\machines.dm" +#include "code\__DEFINES\magic_defines.dm" #include "code\__DEFINES\mappers.dm" #include "code\__DEFINES\martial_arts.dm" #include "code\__DEFINES\MC.dm" @@ -428,6 +429,7 @@ #include "code\datums\cache\crew.dm" #include "code\datums\cache\powermonitor.dm" #include "code\datums\components\_component.dm" +#include "code\datums\components\anti_magic.dm" #include "code\datums\components\boomerang.dm" #include "code\datums\components\boss_music.dm" #include "code\datums\components\caltrop.dm" @@ -930,8 +932,8 @@ #include "code\game\machinery\computer\prisoner.dm" #include "code\game\machinery\computer\robot_control.dm" #include "code\game\machinery\computer\security_records.dm" -#include "code\game\machinery\computer\sm_monitor.dm" #include "code\game\machinery\computer\singulo_monitor.dm" +#include "code\game\machinery\computer\sm_monitor.dm" #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\computer\arcade_games\recruiter.dm" #include "code\game\machinery\doors\airlock.dm" diff --git a/sound/magic/magic_block.ogg b/sound/magic/magic_block.ogg new file mode 100644 index 00000000000..e9c4ffdca94 Binary files /dev/null and b/sound/magic/magic_block.ogg differ diff --git a/sound/magic/magic_block_holy.ogg b/sound/magic/magic_block_holy.ogg new file mode 100644 index 00000000000..eb34f01a896 Binary files /dev/null and b/sound/magic/magic_block_holy.ogg differ diff --git a/sound/magic/magic_block_mind.ogg b/sound/magic/magic_block_mind.ogg new file mode 100644 index 00000000000..3b824da3e4c Binary files /dev/null and b/sound/magic/magic_block_mind.ogg differ