mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-13 11:12:14 +00:00
* Fix polymorph behavior for pAIs and mimics (#60502) This fixes #58258. Basically anytime a pAI holoform gets shot with a staff of changing or polymorph item it would change their mob type. This allowed ghost roles infinite respawns since the pAI card is not destroyed when it gets polymorphed. An unlimited army can be created with no downsides since you can keep bringing people back to life with the pAI requests. I also went and fixed another strange behavior with polymorph, where if you used a staff of animate on an object, you could turn around and then polymorph the object into a mob. The animate magic is supposed to wear off the object after a certain while so I made the mimic immune to polymorph effects. * Fix polymorph behavior for pAIs and mimics Co-authored-by: Tim <timothymtorres@gmail.com>
544 lines
16 KiB
Plaintext
544 lines
16 KiB
Plaintext
/obj/projectile/magic
|
|
name = "bolt"
|
|
icon_state = "energy"
|
|
damage = 0
|
|
damage_type = OXY
|
|
nodamage = TRUE
|
|
armour_penetration = 100
|
|
flag = MAGIC
|
|
|
|
/obj/projectile/magic/death
|
|
name = "bolt of death"
|
|
icon_state = "pulse1_bl"
|
|
|
|
/obj/projectile/magic/death/on_hit(mob/living/target)
|
|
. = ..()
|
|
if(!istype(target))
|
|
return
|
|
|
|
if(target.anti_magic_check())
|
|
target.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
|
|
if(target.mob_biotypes & MOB_UNDEAD) //negative energy heals the undead
|
|
if(target.revive(full_heal = TRUE, admin_revive = TRUE))
|
|
target.grab_ghost(force = TRUE) // even suicides
|
|
to_chat(target, span_notice("You rise with a start, you're undead!!!"))
|
|
else if(target.stat != DEAD)
|
|
to_chat(target, span_notice("You feel great!"))
|
|
return
|
|
|
|
target.death()
|
|
|
|
/obj/projectile/magic/resurrection
|
|
name = "bolt of resurrection"
|
|
icon_state = "ion"
|
|
damage = 0
|
|
damage_type = OXY
|
|
nodamage = TRUE
|
|
|
|
/obj/projectile/magic/resurrection/on_hit(mob/living/carbon/target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
if(target.anti_magic_check())
|
|
target.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
if(target.mob_biotypes & MOB_UNDEAD) //positive energy harms the undead
|
|
target.death(0)
|
|
else
|
|
if(target.revive(full_heal = TRUE, admin_revive = TRUE))
|
|
target.grab_ghost(force = TRUE) // even suicides
|
|
to_chat(target, span_notice("You rise with a start, you're alive!!!"))
|
|
else if(target.stat != DEAD)
|
|
to_chat(target, span_notice("You feel great!"))
|
|
|
|
/obj/projectile/magic/teleport
|
|
name = "bolt of teleportation"
|
|
icon_state = "bluespace"
|
|
damage = 0
|
|
damage_type = OXY
|
|
nodamage = TRUE
|
|
var/inner_tele_radius = 0
|
|
var/outer_tele_radius = 6
|
|
|
|
/obj/projectile/magic/teleport/on_hit(mob/target)
|
|
. = ..()
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] fizzles on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
var/teleammount = 0
|
|
var/teleloc = target
|
|
if(!isturf(target))
|
|
teleloc = target.loc
|
|
for(var/atom/movable/stuff in teleloc)
|
|
if(!stuff.anchored && stuff.loc && !isobserver(stuff))
|
|
if(do_teleport(stuff, stuff, 10, channel = TELEPORT_CHANNEL_MAGIC))
|
|
teleammount++
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(max(round(4 - teleammount),0), stuff.loc) //Smoke drops off if a lot of stuff is moved for the sake of sanity
|
|
smoke.start()
|
|
|
|
/obj/projectile/magic/safety
|
|
name = "bolt of safety"
|
|
icon_state = "bluespace"
|
|
damage = 0
|
|
damage_type = OXY
|
|
nodamage = TRUE
|
|
|
|
/obj/projectile/magic/safety/on_hit(atom/target)
|
|
. = ..()
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] fizzles on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
if(isturf(target))
|
|
return BULLET_ACT_HIT
|
|
|
|
var/turf/origin_turf = get_turf(target)
|
|
var/turf/destination_turf = find_safe_turf()
|
|
|
|
if(do_teleport(target, destination_turf, channel=TELEPORT_CHANNEL_MAGIC))
|
|
for(var/t in list(origin_turf, destination_turf))
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(0, t)
|
|
smoke.start()
|
|
|
|
/obj/projectile/magic/door
|
|
name = "bolt of door creation"
|
|
icon_state = "energy"
|
|
damage = 0
|
|
damage_type = OXY
|
|
nodamage = TRUE
|
|
var/list/door_types = list(/obj/structure/mineral_door/wood, /obj/structure/mineral_door/iron, /obj/structure/mineral_door/silver, /obj/structure/mineral_door/gold, /obj/structure/mineral_door/uranium, /obj/structure/mineral_door/sandstone, /obj/structure/mineral_door/transparent/plasma, /obj/structure/mineral_door/transparent/diamond)
|
|
|
|
/obj/projectile/magic/door/on_hit(atom/target)
|
|
. = ..()
|
|
if(istype(target, /obj/machinery/door))
|
|
OpenDoor(target)
|
|
else
|
|
var/turf/T = get_turf(target)
|
|
if(isclosedturf(T) && !isindestructiblewall(T))
|
|
CreateDoor(T)
|
|
|
|
/obj/projectile/magic/door/proc/CreateDoor(turf/T)
|
|
var/door_type = pick(door_types)
|
|
var/obj/structure/mineral_door/D = new door_type(T)
|
|
T.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
|
D.Open()
|
|
|
|
/obj/projectile/magic/door/proc/OpenDoor(obj/machinery/door/D)
|
|
if(istype(D, /obj/machinery/door/airlock))
|
|
var/obj/machinery/door/airlock/A = D
|
|
A.locked = FALSE
|
|
D.open()
|
|
|
|
/obj/projectile/magic/change
|
|
name = "bolt of change"
|
|
icon_state = "ice_1"
|
|
damage = 0
|
|
damage_type = BURN
|
|
nodamage = TRUE
|
|
|
|
/obj/projectile/magic/change/on_hit(atom/change)
|
|
. = ..()
|
|
if(isliving(change))
|
|
var/mob/living/M = change
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] fizzles on contact with [M]!"))
|
|
qdel(src)
|
|
return BULLET_ACT_BLOCK
|
|
M.wabbajack()
|
|
qdel(src)
|
|
|
|
/obj/projectile/magic/animate
|
|
name = "bolt of animation"
|
|
icon_state = "red_1"
|
|
damage = 0
|
|
damage_type = BURN
|
|
nodamage = TRUE
|
|
|
|
/obj/projectile/magic/animate/on_hit(atom/target, blocked = FALSE)
|
|
target.animate_atom_living(firer)
|
|
..()
|
|
|
|
/atom/proc/animate_atom_living(mob/living/owner = null)
|
|
if((isitem(src) || isstructure(src)) && !is_type_in_list(src, GLOB.protected_objects))
|
|
if(istype(src, /obj/structure/statue/petrified))
|
|
var/obj/structure/statue/petrified/P = src
|
|
if(P.petrified_mob)
|
|
var/mob/living/L = P.petrified_mob
|
|
var/mob/living/simple_animal/hostile/statue/S = new(P.loc, owner)
|
|
S.name = "statue of [L.name]"
|
|
if(owner)
|
|
S.faction = list("[REF(owner)]")
|
|
S.icon = P.icon
|
|
S.icon_state = P.icon_state
|
|
S.copy_overlays(P, TRUE)
|
|
S.color = P.color
|
|
S.atom_colours = P.atom_colours.Copy()
|
|
if(L.mind)
|
|
L.mind.transfer_to(S)
|
|
if(owner)
|
|
to_chat(S, span_userdanger("You are an animate statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! Do not harm [owner], your creator."))
|
|
P.forceMove(S)
|
|
return
|
|
else
|
|
var/obj/O = src
|
|
if(istype(O, /obj/item/gun))
|
|
new /mob/living/simple_animal/hostile/mimic/copy/ranged(drop_location(), src, owner)
|
|
else
|
|
new /mob/living/simple_animal/hostile/mimic/copy(drop_location(), src, owner)
|
|
|
|
else if(istype(src, /mob/living/simple_animal/hostile/mimic/copy))
|
|
// Change our allegiance!
|
|
var/mob/living/simple_animal/hostile/mimic/copy/C = src
|
|
if(owner)
|
|
C.ChangeOwner(owner)
|
|
|
|
/obj/projectile/magic/spellblade
|
|
name = "blade energy"
|
|
icon_state = "lavastaff"
|
|
damage = 15
|
|
damage_type = BURN
|
|
flag = MAGIC
|
|
dismemberment = 50
|
|
nodamage = FALSE
|
|
|
|
/obj/projectile/magic/spellblade/on_hit(target)
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
qdel(src)
|
|
return BULLET_ACT_BLOCK
|
|
. = ..()
|
|
|
|
/obj/projectile/magic/arcane_barrage
|
|
name = "arcane bolt"
|
|
icon_state = "arcane_barrage"
|
|
damage = 20
|
|
damage_type = BURN
|
|
nodamage = FALSE
|
|
armour_penetration = 0
|
|
flag = MAGIC
|
|
hitsound = 'sound/weapons/barragespellhit.ogg'
|
|
|
|
/obj/projectile/magic/arcane_barrage/on_hit(target)
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
qdel(src)
|
|
return BULLET_ACT_BLOCK
|
|
. = ..()
|
|
|
|
|
|
/obj/projectile/magic/locker
|
|
name = "locker bolt"
|
|
icon_state = "locker"
|
|
nodamage = TRUE
|
|
flag = MAGIC
|
|
var/weld = TRUE
|
|
var/created = FALSE //prevents creation of more then one locker if it has multiple hits
|
|
var/locker_suck = TRUE
|
|
var/obj/structure/closet/decay/locker_temp_instance
|
|
|
|
/obj/projectile/magic/locker/Initialize()
|
|
. = ..()
|
|
locker_temp_instance = new(src)
|
|
|
|
/obj/projectile/magic/locker/prehit_pierce(atom/A)
|
|
. = ..()
|
|
if(isliving(A) && locker_suck)
|
|
var/mob/living/M = A
|
|
if(M.anti_magic_check()) // no this doesn't check if ..() returned to phase through do I care no it's magic ain't gotta explain shit
|
|
M.visible_message(span_warning("[src] vanishes on contact with [A]!"))
|
|
return PROJECTILE_DELETE_WITHOUT_HITTING
|
|
if(!locker_temp_instance.insertion_allowed(M))
|
|
return
|
|
M.forceMove(src)
|
|
return PROJECTILE_PIERCE_PHASE
|
|
|
|
/obj/projectile/magic/locker/on_hit(target)
|
|
if(created)
|
|
return ..()
|
|
if(LAZYLEN(contents))
|
|
for(var/atom/movable/AM in contents)
|
|
locker_temp_instance.insert(AM)
|
|
locker_temp_instance.welded = weld
|
|
locker_temp_instance.update_appearance()
|
|
created = TRUE
|
|
return ..()
|
|
|
|
/obj/projectile/magic/locker/Destroy()
|
|
locker_suck = FALSE
|
|
for(var/atom/movable/AM in contents)
|
|
AM.forceMove(get_turf(src))
|
|
. = ..()
|
|
|
|
/obj/structure/closet/decay
|
|
breakout_time = 600
|
|
icon_welded = null
|
|
icon_state = "cursed"
|
|
var/weakened_icon = "decursed"
|
|
var/auto_destroy = TRUE
|
|
|
|
/obj/structure/closet/decay/Initialize()
|
|
. = ..()
|
|
if(auto_destroy)
|
|
addtimer(CALLBACK(src, .proc/bust_open), 5 MINUTES)
|
|
|
|
/obj/structure/closet/decay/after_weld(weld_state)
|
|
if(weld_state)
|
|
unmagify()
|
|
|
|
///Fade away into nothing
|
|
/obj/structure/closet/decay/proc/decay()
|
|
animate(src, alpha = 0, time = 30)
|
|
addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, src), 30)
|
|
|
|
/obj/structure/closet/decay/open(mob/living/user, force = FALSE)
|
|
. = ..()
|
|
if(.)
|
|
unmagify()
|
|
|
|
///Give it the lesser magic icon and tell it to delete itself
|
|
/obj/structure/closet/decay/proc/unmagify()
|
|
icon_state = weakened_icon
|
|
update_appearance()
|
|
|
|
addtimer(CALLBACK(src, .proc/decay), 15 SECONDS)
|
|
|
|
/obj/projectile/magic/flying
|
|
name = "bolt of flying"
|
|
icon_state = "flight"
|
|
|
|
/obj/projectile/magic/flying/on_hit(target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
var/mob/living/L = target
|
|
if(L.anti_magic_check())
|
|
L.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
var/atom/throw_target = get_edge_target_turf(L, angle2dir(Angle))
|
|
L.throw_at(throw_target, 200, 4)
|
|
|
|
/obj/projectile/magic/bounty
|
|
name = "bolt of bounty"
|
|
icon_state = "bounty"
|
|
|
|
/obj/projectile/magic/bounty/on_hit(target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
var/mob/living/L = target
|
|
if(L.anti_magic_check() || !firer)
|
|
L.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
L.apply_status_effect(STATUS_EFFECT_BOUNTY, firer)
|
|
|
|
/obj/projectile/magic/antimagic
|
|
name = "bolt of antimagic"
|
|
icon_state = "antimagic"
|
|
|
|
/obj/projectile/magic/antimagic/on_hit(target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
var/mob/living/L = target
|
|
if(L.anti_magic_check())
|
|
L.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
L.apply_status_effect(STATUS_EFFECT_ANTIMAGIC)
|
|
|
|
/obj/projectile/magic/fetch
|
|
name = "bolt of fetching"
|
|
icon_state = "fetch"
|
|
|
|
/obj/projectile/magic/fetch/on_hit(target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
var/mob/living/L = target
|
|
if(L.anti_magic_check() || !firer)
|
|
L.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
var/atom/throw_target = get_edge_target_turf(L, get_dir(L, firer))
|
|
L.throw_at(throw_target, 200, 4)
|
|
|
|
/obj/projectile/magic/sapping
|
|
name = "bolt of sapping"
|
|
icon_state = "sapping"
|
|
|
|
/obj/projectile/magic/sapping/on_hit(target)
|
|
. = ..()
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, src, /datum/mood_event/sapped)
|
|
|
|
/obj/projectile/magic/necropotence
|
|
name = "bolt of necropotence"
|
|
icon_state = "necropotence"
|
|
|
|
/obj/projectile/magic/necropotence/on_hit(target)
|
|
. = ..()
|
|
if(isliving(target))
|
|
var/mob/living/L = target
|
|
if(L.anti_magic_check() || !L.mind)
|
|
L.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
to_chat(L, span_danger("Your body feels drained and there is a burning pain in your chest."))
|
|
L.maxHealth -= 20
|
|
L.health = min(L.health, L.maxHealth)
|
|
if(L.maxHealth <= 0)
|
|
to_chat(L, span_userdanger("Your weakened soul is completely consumed by the [src]!"))
|
|
return
|
|
for(var/obj/effect/proc_holder/spell/spell in L.mind.spell_list)
|
|
spell.charge_counter = spell.charge_max
|
|
spell.recharging = FALSE
|
|
spell.update_appearance()
|
|
|
|
/obj/projectile/magic/wipe
|
|
name = "bolt of possession"
|
|
icon_state = "wipe"
|
|
|
|
/obj/projectile/magic/wipe/on_hit(target)
|
|
. = ..()
|
|
if(iscarbon(target))
|
|
var/mob/living/carbon/M = target
|
|
if(M.anti_magic_check())
|
|
M.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
for(var/x in M.get_traumas())//checks to see if the victim is already going through possession
|
|
if(istype(x, /datum/brain_trauma/special/imaginary_friend/trapped_owner))
|
|
M.visible_message(span_warning("[src] vanishes on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
to_chat(M, span_warning("Your mind has been opened to possession!"))
|
|
possession_test(M)
|
|
return BULLET_ACT_HIT
|
|
|
|
/obj/projectile/magic/wipe/proc/possession_test(mob/living/carbon/M)
|
|
var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = M.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner)
|
|
var/poll_message = "Do you want to play as [M.real_name]?"
|
|
if(M.mind)
|
|
poll_message = "[poll_message] Job:[M.mind.assigned_role.title]."
|
|
if(M.mind && M.mind.special_role)
|
|
poll_message = "[poll_message] Status:[M.mind.special_role]."
|
|
else if(M.mind)
|
|
var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
|
|
if(A)
|
|
poll_message = "[poll_message] Status:[A.name]."
|
|
var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, FALSE, 100, M)
|
|
if(M.stat == DEAD)//boo.
|
|
return
|
|
if(LAZYLEN(candidates))
|
|
var/mob/dead/observer/C = pick(candidates)
|
|
to_chat(M, span_boldnotice("You have been noticed by a ghost and it has possessed you!"))
|
|
var/oldkey = M.key
|
|
M.ghostize(0)
|
|
M.key = C.key
|
|
trauma.friend.key = oldkey
|
|
trauma.friend.reset_perspective(null)
|
|
trauma.friend.Show()
|
|
trauma.friend_initialized = TRUE
|
|
else
|
|
to_chat(M, span_notice("Your mind has managed to go unnoticed in the spirit world."))
|
|
qdel(trauma)
|
|
|
|
/obj/projectile/magic/aoe
|
|
name = "Area Bolt"
|
|
desc = "What the fuck does this do?!"
|
|
damage = 0
|
|
var/proxdet = TRUE
|
|
|
|
/obj/projectile/magic/aoe/Range()
|
|
if(proxdet)
|
|
for(var/mob/living/L in range(1, get_turf(src)))
|
|
if(L.stat != DEAD && L != firer && !L.anti_magic_check())
|
|
return Bump(L)
|
|
..()
|
|
|
|
|
|
/obj/projectile/magic/aoe/lightning
|
|
name = "lightning bolt"
|
|
icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~
|
|
damage = 15
|
|
damage_type = BURN
|
|
nodamage = FALSE
|
|
speed = 0.3
|
|
flag = MAGIC
|
|
|
|
var/zap_power = 20000
|
|
var/zap_range = 15
|
|
var/zap_flags = ZAP_MOB_DAMAGE | ZAP_MOB_STUN | ZAP_OBJ_DAMAGE
|
|
var/chain
|
|
var/mob/living/caster
|
|
|
|
/obj/projectile/magic/aoe/lightning/fire(setAngle)
|
|
if(caster)
|
|
chain = caster.Beam(src, icon_state = "lightning[rand(1, 12)]")
|
|
..()
|
|
|
|
/obj/projectile/magic/aoe/lightning/on_hit(target)
|
|
. = ..()
|
|
if(ismob(target))
|
|
var/mob/M = target
|
|
if(M.anti_magic_check())
|
|
visible_message(span_warning("[src] fizzles on contact with [target]!"))
|
|
qdel(src)
|
|
return BULLET_ACT_BLOCK
|
|
tesla_zap(src, zap_range, zap_power, zap_flags)
|
|
qdel(src)
|
|
|
|
/obj/projectile/magic/aoe/lightning/no_zap
|
|
zap_power = 10000
|
|
zap_range = 4
|
|
zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE
|
|
|
|
/obj/projectile/magic/aoe/lightning/Destroy()
|
|
qdel(chain)
|
|
. = ..()
|
|
|
|
/obj/projectile/magic/aoe/fireball
|
|
name = "bolt of fireball"
|
|
icon_state = "fireball"
|
|
damage = 10
|
|
damage_type = BRUTE
|
|
nodamage = FALSE
|
|
|
|
//explosion values
|
|
var/exp_heavy = 0
|
|
var/exp_light = 2
|
|
var/exp_flash = 3
|
|
var/exp_fire = 2
|
|
|
|
/obj/projectile/magic/aoe/fireball/on_hit(target)
|
|
. = ..()
|
|
if(ismob(target))
|
|
var/mob/living/M = target
|
|
if(M.anti_magic_check())
|
|
visible_message(span_warning("[src] vanishes into smoke on contact with [target]!"))
|
|
return BULLET_ACT_BLOCK
|
|
M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately
|
|
var/turf/T = get_turf(target)
|
|
explosion(T, devastation_range = -1, heavy_impact_range = exp_heavy, light_impact_range = exp_light, flame_range = exp_fire, flash_range = exp_flash, adminlog = FALSE)
|
|
|
|
|
|
//still magic related, but a different path
|
|
|
|
/obj/projectile/temp/chill
|
|
name = "bolt of chills"
|
|
icon_state = "ice_2"
|
|
damage = 0
|
|
damage_type = BURN
|
|
nodamage = FALSE
|
|
armour_penetration = 100
|
|
temperature = -200 // Cools you down greatly per hit
|
|
flag = MAGIC
|
|
|
|
/obj/projectile/magic/nothing
|
|
name = "bolt of nothing"
|