mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-31 12:41:46 +00:00
* cryogenic anomaly * sprite * reactive gelidic armor * america number 1 raaah * sprite (thanks atp) * qwerty suggestion * floor freezing * Update code/game/objects/effects/anomalies.dm Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> * steel review commit 2 * whitespace apparently? idk * whitespace fix (real) * vetus * Update code/game/objects/effects/anomalies.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/game/objects/effects/anomalies.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/game/objects/effects/anomalies.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/modules/clothing/suits/armor_suits.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/modules/clothing/suits/armor_suits.dm Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> * early return thing --------- Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
476 lines
14 KiB
Plaintext
476 lines
14 KiB
Plaintext
//Anomalies, used for events. Note that these DO NOT work by themselves; their procs are called by the event datum.
|
|
|
|
/// Chance of taking a step per second
|
|
#define ANOMALY_MOVECHANCE 70
|
|
#define BLUESPACE_MASS_TELEPORT_RANGE 16
|
|
|
|
/obj/effect/anomaly
|
|
name = "anomaly"
|
|
desc = "A mysterious anomaly, seen commonly only in the region of space that the station orbits..."
|
|
icon_state = "bhole3"
|
|
density = FALSE
|
|
light_range = 3
|
|
var/movechance = ANOMALY_MOVECHANCE
|
|
var/obj/item/assembly/signaler/anomaly/aSignal = /obj/item/assembly/signaler/anomaly
|
|
var/area/impact_area
|
|
/// Time in deciseconds before the anomaly triggers
|
|
var/lifespan = 990
|
|
var/death_time
|
|
|
|
var/countdown_colour
|
|
var/obj/effect/countdown/anomaly/countdown
|
|
|
|
/// Do we drop a core when we're neutralized?
|
|
var/drops_core = TRUE
|
|
|
|
/obj/effect/anomaly/Initialize(mapload, new_lifespan, _drops_core = TRUE)
|
|
. = ..()
|
|
GLOB.poi_list |= src
|
|
START_PROCESSING(SSobj, src)
|
|
impact_area = get_area(src)
|
|
|
|
if(!impact_area)
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
drops_core = _drops_core
|
|
|
|
aSignal = new aSignal(src)
|
|
aSignal.code = rand(1, 100)
|
|
aSignal.anomaly_type = type
|
|
|
|
var/frequency = rand(PUBLIC_LOW_FREQ, PUBLIC_HIGH_FREQ)
|
|
if(ISMULTIPLE(frequency, 2))//signaller frequencies are always uneven!
|
|
frequency++
|
|
aSignal.frequency = frequency
|
|
|
|
if(new_lifespan)
|
|
lifespan = new_lifespan
|
|
death_time = world.time + lifespan
|
|
countdown = new(src)
|
|
if(countdown_colour)
|
|
countdown.color = countdown_colour
|
|
countdown.start()
|
|
|
|
/obj/effect/anomaly/Destroy()
|
|
GLOB.poi_list.Remove(src)
|
|
STOP_PROCESSING(SSobj, src)
|
|
QDEL_NULL(countdown)
|
|
QDEL_NULL(aSignal)
|
|
return ..()
|
|
|
|
/obj/effect/anomaly/process()
|
|
anomalyEffect()
|
|
if(death_time < world.time)
|
|
if(loc)
|
|
detonate()
|
|
qdel(src)
|
|
|
|
/obj/effect/anomaly/proc/anomalyEffect()
|
|
if(prob(movechance))
|
|
step(src, pick(GLOB.alldirs))
|
|
|
|
/obj/effect/anomaly/proc/detonate()
|
|
return
|
|
|
|
/obj/effect/anomaly/ex_act(severity)
|
|
if(severity == EXPLODE_DEVASTATE)
|
|
qdel(src)
|
|
|
|
/obj/effect/anomaly/proc/anomalyNeutralize()
|
|
new /obj/effect/particle_effect/smoke/bad(loc)
|
|
|
|
if(drops_core)
|
|
aSignal.forceMove(drop_location())
|
|
aSignal = null
|
|
// else, anomaly core gets deleted by qdel(src).
|
|
|
|
qdel(src)
|
|
|
|
|
|
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
|
|
if(istype(I, /obj/item/analyzer))
|
|
to_chat(user, "<span class='notice'>Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].</span>")
|
|
|
|
///////////////////////
|
|
|
|
/obj/effect/anomaly/grav
|
|
name = "gravitational anomaly"
|
|
icon_state = "shield2"
|
|
density = FALSE
|
|
appearance_flags = PIXEL_SCALE|LONG_GLIDE
|
|
var/boing = FALSE
|
|
var/knockdown = FALSE
|
|
aSignal = /obj/item/assembly/signaler/anomaly/grav
|
|
var/obj/effect/warp_effect/supermatter/warp
|
|
|
|
/obj/effect/anomaly/grav/Initialize(mapload, new_lifespan, _drops_core = TRUE, event_spawned = TRUE)
|
|
. = ..()
|
|
warp = new(src)
|
|
vis_contents += warp
|
|
if(!event_spawned) //So an anomaly in the hallway is assured to have some risk to it, but not make sm / vetus too much pain
|
|
return
|
|
for(var/I in 1 to 3)
|
|
if(prob(75))
|
|
new /obj/item/stack/rods(loc)
|
|
if(prob(75))
|
|
new /obj/item/shard(loc)
|
|
|
|
/obj/effect/anomaly/grav/Destroy()
|
|
vis_contents -= warp
|
|
QDEL_NULL(warp) // don't want to leave it hanging
|
|
return ..()
|
|
|
|
/obj/effect/anomaly/grav/anomalyEffect()
|
|
..()
|
|
boing = TRUE
|
|
for(var/obj/O in orange(4, src))
|
|
if(!O.anchored)
|
|
step_towards(O,src)
|
|
for(var/mob/living/M in range(0, src))
|
|
gravShock(M)
|
|
for(var/mob/living/M in orange(4, src))
|
|
if(!M.mob_negates_gravity())
|
|
step_towards(M,src)
|
|
for(var/obj/O in range(0, src))
|
|
if(!O.anchored && O.loc != src && O.move_resist < MOVE_FORCE_OVERPOWERING) // so it cannot throw the anomaly core or super big things
|
|
for(var/mob/living/target in view(4, src))
|
|
if(target && !target.stat && (get_dist(target, src) > 1 || prob(50))) //We don't want to always throw at the person that is in the anomaly, fuck up people around it.
|
|
O.throw_at(target, 5, 10, dodgeable = FALSE)
|
|
break
|
|
//anomaly quickly contracts then slowly expands it's ring
|
|
animate(warp, time = 6, transform = matrix().Scale(0.5,0.5))
|
|
animate(time = 14, transform = matrix())
|
|
|
|
/obj/effect/anomaly/grav/Crossed(atom/movable/AM)
|
|
. = ..()
|
|
gravShock(AM)
|
|
|
|
/obj/effect/anomaly/grav/Bump(atom/A)
|
|
gravShock(A)
|
|
|
|
/obj/effect/anomaly/grav/Bumped(atom/movable/AM)
|
|
gravShock(AM)
|
|
|
|
/obj/effect/anomaly/grav/proc/gravShock(mob/living/A)
|
|
if(boing && isliving(A) && !A.stat)
|
|
if(!knockdown)
|
|
A.Weaken(4 SECONDS)
|
|
else
|
|
A.KnockDown(4 SECONDS) //You know, maybe hard stuns in a megafauna fight are a bad idea.
|
|
var/atom/target = get_edge_target_turf(A, get_dir(src, get_step_away(A, src)))
|
|
A.throw_at(target, 5, 1)
|
|
boing = FALSE
|
|
|
|
/obj/effect/anomaly/grav/detonate()
|
|
if(!drops_core)
|
|
return
|
|
var/turf/T = get_turf(src)
|
|
if(T && GLOB.gravity_generators["[T.z]"])
|
|
var/obj/machinery/gravity_generator/main/G = pick(GLOB.gravity_generators["[T.z]"])
|
|
G.set_broken() //Requires engineering to fix the gravity generator, as it gets overloaded by the anomaly.
|
|
|
|
/////////////////////
|
|
|
|
/obj/effect/anomaly/flux
|
|
name = "flux wave anomaly"
|
|
icon_state = "electricity2"
|
|
density = TRUE
|
|
aSignal = /obj/item/assembly/signaler/anomaly/flux
|
|
var/canshock = FALSE
|
|
var/shockdamage = 20
|
|
var/explosive = TRUE
|
|
var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE
|
|
var/zap_range = 5
|
|
var/power = 5000
|
|
var/knockdown = FALSE
|
|
|
|
/obj/effect/anomaly/flux/Initialize(mapload, new_lifespan, drops_core = TRUE, _explosive = TRUE)
|
|
. = ..()
|
|
explosive = _explosive
|
|
if(explosive)
|
|
zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN
|
|
power = 15000
|
|
|
|
/obj/effect/anomaly/flux/anomalyEffect()
|
|
..()
|
|
canshock = TRUE
|
|
for(var/mob/living/M in get_turf(src))
|
|
mobShock(M)
|
|
if(explosive) //Let us not fuck up the sm that much
|
|
tesla_zap(src, zap_range, power, zap_flags)
|
|
|
|
|
|
/obj/effect/anomaly/flux/Crossed(atom/movable/AM)
|
|
. = ..()
|
|
mobShock(AM)
|
|
|
|
/obj/effect/anomaly/flux/Bump(atom/A)
|
|
mobShock(A)
|
|
|
|
/obj/effect/anomaly/flux/Bumped(atom/movable/AM)
|
|
mobShock(AM)
|
|
|
|
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
|
|
if(canshock && istype(M))
|
|
canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second.
|
|
M.electrocute_act(shockdamage, name, flags = SHOCK_NOGLOVES)
|
|
if(!knockdown)
|
|
M.Weaken(explosive ? 6 SECONDS : 3 SECONDS) //Back to being deadly if you touch it, rather than just being able to crawl out of it. Non explosive ones less deadly, since you can't loot them
|
|
else
|
|
M.KnockDown(3 SECONDS)
|
|
|
|
/obj/effect/anomaly/flux/detonate()
|
|
if(explosive)
|
|
explosion(src, 1, 4, 16, 18) //Low devastation, but hits a lot of stuff.
|
|
else
|
|
new /obj/effect/particle_effect/sparks(loc)
|
|
|
|
/////////////////////
|
|
|
|
/obj/effect/anomaly/bluespace
|
|
name = "bluespace anomaly"
|
|
icon = 'icons/obj/projectiles.dmi'
|
|
icon_state = "bluespace"
|
|
density = TRUE
|
|
var/mass_teleporting = TRUE
|
|
aSignal = /obj/item/assembly/signaler/anomaly/bluespace
|
|
|
|
/obj/effect/anomaly/bluespace/Initialize(mapload, new_lifespan, drops_core = TRUE, _mass_teleporting = TRUE)
|
|
. = ..()
|
|
mass_teleporting = _mass_teleporting
|
|
|
|
/obj/effect/anomaly/bluespace/anomalyEffect()
|
|
..()
|
|
for(var/mob/living/M in range(4, src))
|
|
do_teleport(M, locate(M.x, M.y, M.z), 4)
|
|
for(var/obj/O in range (4, src))
|
|
if(!O.anchored && O.invisibility == 0 && prob(50))
|
|
do_teleport(O, locate(O.x, O.y, O.z), 6)
|
|
|
|
/obj/effect/anomaly/bluespace/Bumped(atom/movable/AM)
|
|
if(isliving(AM))
|
|
do_teleport(AM, locate(AM.x, AM.y, AM.z), 8)
|
|
|
|
/obj/effect/anomaly/bluespace/detonate()
|
|
if(!mass_teleporting)
|
|
return
|
|
var/turf/T = pick(get_area_turfs(impact_area))
|
|
if(T)
|
|
// Calculate new position (searches through beacons in world)
|
|
var/obj/item/radio/beacon/chosen
|
|
var/list/possible = list()
|
|
for(var/obj/item/radio/beacon/W in GLOB.beacons)
|
|
if(!is_station_level(W.z))
|
|
continue
|
|
possible += W
|
|
|
|
if(length(possible))
|
|
chosen = pick(possible)
|
|
|
|
if(chosen)
|
|
// Calculate previous position for transition
|
|
var/turf/turf_from = T // the turf of origin we're travelling FROM
|
|
var/turf/turf_to = get_turf(chosen) // the turf of origin we're travelling TO
|
|
|
|
playsound(turf_to, 'sound/effects/phasein.ogg', 100, TRUE)
|
|
GLOB.minor_announcement.Announce("Massive bluespace translocation detected.", "Anomaly Alert")
|
|
|
|
var/list/flashers = list()
|
|
for(var/mob/living/carbon/C in viewers(turf_to, null))
|
|
if(C.flash_eyes())
|
|
flashers += C
|
|
|
|
var/y_distance = turf_to.y - turf_from.y
|
|
var/x_distance = turf_to.x - turf_from.x
|
|
for(var/atom/movable/A in urange(BLUESPACE_MASS_TELEPORT_RANGE, turf_from)) // iterate thru list of mobs in the area
|
|
if(istype(A, /obj/item/radio/beacon))
|
|
continue // don't teleport beacons because that's just insanely stupid
|
|
if(A.anchored || A.move_resist == INFINITY)
|
|
continue
|
|
|
|
var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, turf_to.z) // calculate the new place
|
|
if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
|
|
A.forceMove(newloc)
|
|
|
|
if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
|
|
var/mob/M = A
|
|
if(M.client)
|
|
INVOKE_ASYNC(src, PROC_REF(blue_effect), M)
|
|
|
|
/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M)
|
|
var/obj/blueeffect = new /obj(src)
|
|
blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
|
|
blueeffect.icon = 'icons/effects/effects.dmi'
|
|
blueeffect.icon_state = "shieldsparkles"
|
|
blueeffect.layer = FLASH_LAYER
|
|
blueeffect.plane = FULLSCREEN_PLANE
|
|
blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
M.client.screen += blueeffect
|
|
sleep(20)
|
|
M.client.screen -= blueeffect
|
|
qdel(blueeffect)
|
|
|
|
|
|
/////////////////////
|
|
|
|
/obj/effect/anomaly/pyro
|
|
name = "pyroclastic anomaly"
|
|
icon_state = "mustard"
|
|
var/ticks = 0
|
|
var/produces_slime = TRUE
|
|
aSignal = /obj/item/assembly/signaler/anomaly/pyro
|
|
|
|
/obj/effect/anomaly/pyro/Initialize(mapload, new_lifespan, drops_core = TRUE, _produces_slime = TRUE)
|
|
. = ..()
|
|
produces_slime = _produces_slime
|
|
|
|
/obj/effect/anomaly/pyro/anomalyEffect()
|
|
..()
|
|
ticks++
|
|
for(var/mob/living/M in hearers(4, src))
|
|
if(prob(50))
|
|
M.adjust_fire_stacks(4)
|
|
M.IgniteMob()
|
|
|
|
if(ticks < 4)
|
|
return
|
|
else
|
|
ticks = 0
|
|
var/turf/simulated/T = get_turf(src)
|
|
if(istype(T))
|
|
T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS | LINDA_SPAWN_OXYGEN, 20)
|
|
|
|
/obj/effect/anomaly/pyro/detonate()
|
|
if(produces_slime)
|
|
INVOKE_ASYNC(src, PROC_REF(makepyroslime))
|
|
|
|
/obj/effect/anomaly/pyro/proc/makepyroslime()
|
|
var/turf/simulated/T = get_turf(src)
|
|
if(istype(T))
|
|
T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS | LINDA_SPAWN_OXYGEN, 500) //Make it hot and burny for the new slime
|
|
var/new_colour = pick("red", "orange")
|
|
var/mob/living/simple_animal/slime/S = new(T, new_colour)
|
|
S.rabid = TRUE
|
|
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
|
|
S.Evolve()
|
|
var/datum/action/innate/slime/reproduce/A = new
|
|
A.Grant(S)
|
|
|
|
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENT, FALSE, 100, source = S, role_cleanname = "pyroclastic anomaly slime")
|
|
if(length(candidates) && !QDELETED(S))
|
|
var/mob/dead/observer/chosen = pick(candidates)
|
|
S.key = chosen.key
|
|
S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME
|
|
log_game("[key_name(S.key)] was made into a slime by pyroclastic anomaly at [AREACOORD(T)].")
|
|
|
|
/////////////////////
|
|
|
|
/obj/effect/anomaly/cryo
|
|
name = "cryogenic anomaly"
|
|
desc = "Hope you brought a jacket!"
|
|
icon_state = "cryoanomaly"
|
|
aSignal = /obj/item/assembly/signaler/anomaly/cryo
|
|
|
|
/obj/effect/anomaly/cryo/anomalyEffect()
|
|
..()
|
|
|
|
var/list/turf_targets = list()
|
|
for(var/turf/T in oview(get_turf(src), 7))
|
|
turf_targets += T
|
|
|
|
for(var/I in 1 to rand(1, 3))
|
|
var/turf/target = pick(turf_targets)
|
|
shootAt(target)
|
|
|
|
if(prob(50))
|
|
for(var/turf/simulated/floor/nearby_floor in oview(get_turf(src), (drops_core ? 2 : 1)))
|
|
nearby_floor.MakeSlippery(TURF_WET_PERMAFROST)
|
|
|
|
var/turf/simulated/T = get_turf(src)
|
|
if(istype(T))
|
|
T.atmos_spawn_air(LINDA_SPAWN_COLD | LINDA_SPAWN_N2O | LINDA_SPAWN_CO2, 20)
|
|
|
|
if(prob(10))
|
|
var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src))
|
|
for(var/i in 1 to 5)
|
|
step_towards(A, pick(turf_targets))
|
|
sleep(2)
|
|
A.Smoke()
|
|
|
|
/obj/effect/anomaly/cryo/proc/shootAt(atom/movable/target)
|
|
var/turf/T = get_turf(src)
|
|
var/turf/U = get_turf(target)
|
|
if(!T || !U)
|
|
return
|
|
var/obj/item/projectile/temp/basilisk/O = new /obj/item/projectile/temp/basilisk(T)
|
|
playsound(get_turf(src), 'sound/weapons/taser2.ogg', 75, TRUE)
|
|
O.current = T
|
|
O.yo = U.y - T.y
|
|
O.xo = U.x - T.x
|
|
O.fire()
|
|
|
|
/obj/effect/anomaly/cryo/detonate()
|
|
var/turf/simulated/T = get_turf(src)
|
|
if(istype(T) && drops_core)
|
|
T.atmos_spawn_air(LINDA_SPAWN_COLD | LINDA_SPAWN_CO2, 1000)
|
|
|
|
/////////////////////
|
|
|
|
/obj/effect/anomaly/bhole
|
|
name = "vortex anomaly"
|
|
icon_state = "bhole3"
|
|
desc = "That's a nice station you have there. It'd be a shame if something happened to it."
|
|
aSignal = /obj/item/assembly/signaler/anomaly/vortex
|
|
|
|
/obj/effect/anomaly/bhole/anomalyEffect()
|
|
..()
|
|
if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen
|
|
qdel(src)
|
|
return
|
|
|
|
grav(rand(0, 3), rand(2, 3), 100, 30)
|
|
|
|
//Throwing stuff around!
|
|
for(var/obj/O in range(3, src))
|
|
if(O == src || O.loc == src)
|
|
return //DON'T DELETE YOURSELF OR YOUR CORE GOD DAMN
|
|
if(!O.anchored)
|
|
var/mob/living/target = locate() in view(5, src)
|
|
if(target && !target.stat)
|
|
O.throw_at(target, 7, 5, dodgeable = FALSE)
|
|
else
|
|
O.ex_act(EXPLODE_HEAVY)
|
|
|
|
/obj/effect/anomaly/bhole/proc/grav(r, ex_act_force, pull_chance, turf_removal_chance)
|
|
for(var/t = -r, t < r, t++)
|
|
affect_coord(x + t, y - r, ex_act_force, pull_chance, turf_removal_chance)
|
|
affect_coord(x - t, y + r, ex_act_force, pull_chance, turf_removal_chance)
|
|
affect_coord(x + r, y + t, ex_act_force, pull_chance, turf_removal_chance)
|
|
affect_coord(x - r, y - t, ex_act_force, pull_chance, turf_removal_chance)
|
|
|
|
/obj/effect/anomaly/bhole/proc/affect_coord(x, y, ex_act_force, pull_chance, turf_removal_chance)
|
|
//Get turf at coordinate
|
|
var/turf/T = locate(x, y, z)
|
|
if(isnull(T))
|
|
return
|
|
|
|
//Pulling and/or ex_act-ing movable atoms in that turf
|
|
if(prob(pull_chance))
|
|
for(var/obj/O in T.contents)
|
|
if(O.anchored)
|
|
O.ex_act(ex_act_force)
|
|
else
|
|
step_towards(O, src)
|
|
for(var/mob/living/M in T.contents)
|
|
step_towards(M, src)
|
|
if(drops_core)
|
|
M.Weaken(3.5 SECONDS) //You ran into a black hole, you ride the pain train.
|
|
M.KnockDown(7 SECONDS)
|
|
|
|
//Damaging the turf
|
|
if(T && prob(turf_removal_chance))
|
|
T.ex_act(ex_act_force)
|
|
|
|
#undef ANOMALY_MOVECHANCE
|
|
#undef BLUESPACE_MASS_TELEPORT_RANGE
|