Files
Paradise/code/modules/hallucinations/effects/major.dm
2022-12-24 18:38:43 +01:00

411 lines
14 KiB
Plaintext

/**
* # Hallucination - Terror Infestation
*
* Creates spider webs and a terror spider near a random vent around the target.
*/
/obj/effect/hallucination/terror_infestation
duration = 30 SECONDS
/obj/effect/hallucination/terror_infestation/Initialize(mapload, mob/living/carbon/target)
. = ..()
// Find a vent around us
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/vent in range(world.view, target))
vents += vent
if(!length(vents))
return
// Spawn webs around a random vent
var/obj/vent = pick(vents)
for(var/t in RANGE_TURFS(1, vent))
var/turf/T = t
if(!isfloorturf(T))
continue
new /obj/effect/hallucination/tripper/spider_web(T, target)
new /obj/effect/hallucination/chaser/attacker/terror_spider(get_turf(vent), target)
/obj/effect/hallucination/chaser/attacker/terror_spider
hallucination_icon = 'icons/mob/terrorspider.dmi'
hallucination_icon_state = "terror_green"
duration = 30 SECONDS
damage = 25
/obj/effect/hallucination/chaser/attacker/terror_spider/Initialize(mapload, mob/living/carbon/target)
. = ..()
name = "Green Terror spider ([rand(100, 999)])"
/obj/effect/hallucination/chaser/attacker/terror_spider/attack_effects()
do_attack_animation(target, ATTACK_EFFECT_BITE)
target.playsound_local(get_turf(src), 'sound/weapons/bite.ogg', 50, TRUE)
to_chat(target, "<span class='userdanger'>[name] bites you!</span>")
/obj/effect/hallucination/chaser/attacker/terror_spider/on_knockdown()
target.visible_message("<span class='warning'>[target] recoils as if hit by something, before suddenly collapsing!</span>",
"<span class='userdanger'>[name] bites you!</span>")
/**
* # Hallucination - Spider Web
*
* A fake spider web that trips the target if crossed.
*/
/obj/effect/hallucination/tripper/spider_web
name = "spider web"
desc = "It's stringy and sticky."
hallucination_icon = 'icons/effects/effects.dmi'
hallucination_icon_state = "stickyweb1"
hallucination_override = TRUE
hallucination_layer = OBJ_LAYER
trip_chance = 80
/obj/effect/hallucination/tripper/spider_web/Initialize(mapload, mob/living/carbon/target)
if(prob(50))
hallucination_icon_state = "stickyweb2"
. = ..()
/obj/effect/hallucination/tripper/spider_web/on_crossed()
target.visible_message("<span class='warning'>[target] trips over nothing.</span>",
"<span class='userdanger'>You get stuck in [src]!</span>")
/obj/effect/hallucination/tripper/spider_web/attackby(obj/item/I, mob/user, params)
if(user != target)
return
step_towards(target, get_turf(src))
target.Weaken(4 SECONDS)
target.visible_message("<span class='warning'>[target] flails [target.p_their()] [I.name] as if striking something, only to trip!</span>",
"<span class='userdanger'>[src] vanishes as you strike it with [I], causing you to stumble forward!</span>")
qdel(src)
/**
* # Hallucination - Abduction
*
* Sends an abductor agent after the target. On knockdown, spawns an abductor scientist next to the target. Nothing else happens.
*/
/obj/effect/hallucination/abduction
duration = 45 SECONDS
/// The abductor agent hallucination.
var/obj/effect/hallucination/chaser/attacker/abductor/agent = null
/// The abductor scientist image handle.
var/image/scientist = null
/obj/effect/hallucination/abduction/Initialize(mapload, mob/living/carbon/target)
. = ..()
var/list/locs = list()
for(var/turf/T in oview(world.view, target))
if(!is_blocked_turf(T))
locs += T
if(!length(locs))
qdel(src)
return
// Spawn agent
var/turf/T = pick(locs)
agent = new(T, target)
agent.owning_hallucination = src
// Teleport effect
var/image/teleport_end = image('icons/mob/mob.dmi', T, "uncloak", layer = ABOVE_MOB_LAYER)
teleport_end.plane = GAME_PLANE
add_icon(teleport_end)
clear_icon_in(teleport_end, 0.9 SECONDS)
/obj/effect/hallucination/abduction/Destroy()
QDEL_NULL(agent)
QDEL_NULL(scientist)
return ..()
/**
* Called when the fake abductor scientist should spawn.
*/
/obj/effect/hallucination/abduction/proc/spawn_scientist()
// Find a spot for the scientist to spawn
var/list/locs = list()
for(var/turf/T in orange(1, target))
if(!is_blocked_turf(T))
locs += T
locs -= get_turf(agent)
if(!length(locs))
qdel(src)
return
QDEL_IN(src, 10 SECONDS)
var/turf/T = pick(locs)
// Spawn the scientist in
var/image/teleport = image('icons/obj/abductor.dmi', T, "teleport", layer = ABOVE_MOB_LAYER)
teleport.plane = GAME_PLANE
add_icon(teleport)
clear_icon_in(teleport, 4 SECONDS)
addtimer(CALLBACK(src, PROC_REF(do_spawn_scientist), T), 4 SECONDS)
playsound(T, "sparks", 100, TRUE)
/**
* Timer called to actually spawn the scientist.
*
* Arguments:
* * T - Where to spawn the scientist.
*/
/obj/effect/hallucination/abduction/proc/do_spawn_scientist(turf/T)
if(QDELETED(target))
qdel(src)
return
else if(scientist)
return
var/image/teleport_end = image('icons/mob/mob.dmi', T, "uncloak", layer = ABOVE_MOB_LAYER)
teleport_end.plane = GAME_PLANE
add_icon(teleport_end)
clear_icon_in(teleport_end, 0.9 SECONDS)
scientist = image('icons/mob/simple_human.dmi', T, "abductor_scientist", layer = MOB_LAYER)
scientist.plane = GAME_PLANE
scientist.dir = get_dir(T, target)
add_icon(scientist)
/obj/effect/hallucination/chaser/attacker/abductor
hallucination_icon = 'icons/mob/simple_human.dmi'
hallucination_icon_state = "abductor_agent"
duration = 45 SECONDS
damage = 100
/// The hallucination that spawned us.
var/obj/effect/hallucination/abduction/owning_hallucination = null
/obj/effect/hallucination/chaser/attacker/abductor/Initialize(mapload, mob/living/carbon/target)
. = ..()
name = "Unknown"
/obj/effect/hallucination/chaser/attacker/abductor/attack_effects()
do_attack_animation(target)
target.playsound_local(get_turf(src), 'sound/weapons/egloves.ogg', 50, TRUE)
/obj/effect/hallucination/chaser/attacker/abductor/on_knockdown()
target.visible_message("<span class='warning'>[target] recoils as if hit by something, before suddenly collapsing!</span>",
"<span class='userdanger'>[name] has stunned you with the advanced baton!</span>")
if(!QDELETED(owning_hallucination))
owning_hallucination.spawn_scientist()
else
qdel(src)
/**
* # Hallucination - Loose Energy Ball
*
* A progressive hallucination that begins with intermittent explosions, before displaying an energy ball that shocks the target.
*/
/obj/effect/hallucination/loose_energy_ball
duration = 30 SECONDS
/// Length of phase 1 in deciseconds.
var/length_phase_1 = 10 SECONDS
/// Length of phase 2 in deciseconds.
var/length_phase_2 = 10 SECONDS
/// Length of phase 3 in deciseconds.
var/length_phase_3 = 6 SECONDS
/obj/effect/hallucination/loose_energy_ball/Initialize(mapload)
. = ..()
phase_1()
addtimer(CALLBACK(src, PROC_REF(phase_2)), length_phase_1)
addtimer(CALLBACK(src, PROC_REF(phase_3)), length_phase_1 + length_phase_2)
/**
* First phase of the hallucination: intermittent, far-away explosion sounds.
*/
/obj/effect/hallucination/loose_energy_ball/proc/phase_1()
for(var/i in 0 to (length_phase_1 / 20) - 1)
play_sound_in(i * 2 SECONDS, null, 'sound/effects/explosionfar.ogg', min(50 + i * 5, 100))
/**
* Second phase of the hallucination: closer explosions and zap sounds from a random direction.
*/
/obj/effect/hallucination/loose_energy_ball/proc/phase_2()
var/turf/source = get_step_rand(get_turf(target))
for(var/i in 0 to (length_phase_2 / 20) - 1)
if(prob(33))
play_sound_in(i * 2 SECONDS, source, pick('sound/effects/explosion1.ogg', 'sound/effects/explosion2.ogg'), min(5 + i * 3, 100))
play_sound_in(i * 2 SECONDS, source, 'sound/magic/lightningbolt.ogg', min(5 + i * 3, 100))
/**
* Third and final phase of the hallucination: an energy ball that approaches the target before shocking it.
*/
/obj/effect/hallucination/loose_energy_ball/proc/phase_3()
// Create the image
var/image/ball = image('icons/obj/tesla_engine/energy_ball.dmi', src, "energy_ball")
ball.layer = MASSIVE_OBJ_LAYER
ball.plane = GAME_PLANE
add_icon(ball)
var/steps = (length_phase_3 / 20) - 1
for(var/i in 0 to steps)
addtimer(CALLBACK(src, PROC_REF(phase_3_inner), ball, steps - i, i), i * 2 SECONDS)
/**
* Called during phase 3 to approach the energy ball towards the target.
*
* Arguments:
* * ball - The energy ball image.
* * distance - The remaining distance.
* * step - The current step.
*/
/obj/effect/hallucination/loose_energy_ball/proc/phase_3_inner(image/ball, distance, step)
if(QDELETED(ball) || QDELETED(target))
return
var/turf/T = get_turf(target)
var/list/turfs = RANGE_TURFS(distance + 1, T) // expensive?
var/turf/dest = pick(turfs) || T // uh oh
ball.loc = dest
target.playsound_local(dest, 'sound/magic/lightningbolt.ogg', 15 + step * 10)
target.playsound_local(dest, 'sound/magic/lightningshock.ogg', 15 + step * 10)
if(distance == 0)
target.electrocute_act(100, src, flags = SHOCK_ILLUSION)
/**
* # Hallucination - Assault
*
* An imaginary attacker spawns close to the target and attacks them to stamcrit.
*/
/obj/effect/hallucination/assault
duration = 30 SECONDS
/// The attacker hallucination.
var/obj/effect/hallucination/chaser/attacker/assaulter/fake_attacker = null
/obj/effect/hallucination/assault/Initialize(mapload, mob/living/carbon/target)
. = ..()
var/list/locs = list()
for(var/turf/T in oview(world.view / 2, target))
if(!is_blocked_turf(T))
locs += T
if(!length(locs))
qdel(src)
return
// Spawn attacker
var/turf/T = pick(locs)
fake_attacker = new(T, target)
/obj/effect/hallucination/chaser/attacker/assaulter
duration = 30 SECONDS
damage = 40
/// The attack verb to display.
var/attack_verb = "punches"
/// The attack sound to play. Can be a file or text (passed to [/proc/get_sfx]).
var/attack_sound = "punch"
/obj/effect/hallucination/chaser/attacker/assaulter/Initialize(mapload, mob/living/carbon/target)
var/new_name
// 80% chance to use a simple human sprite
if(prob(80))
new_name = "Unknown"
hallucination_icon = 'icons/mob/simple_human.dmi'
hallucination_icon_state = pick("arctic_skeleton", "templar", "skeleton", "russianmelee", "piratemelee", "plasma_miner_tool", "cat_butcher", "syndicate_space_sword", "syndicate_stormtrooper_sword", "zombie", "scary_clown")
// Adjust the attack verb and sound depending on the "mob"
switch(hallucination_icon_state)
if("arctic_skeleton", "templar", "russianmelee", "plasma_miner_tool")
attack_verb = "slashed"
attack_sound = 'sound/weapons/bladeslice.ogg'
if("cat_butcher")
attack_verb = "sawed"
attack_sound = 'sound/weapons/circsawhit.ogg'
if("piratemelee", "syndicate_space_sword", "syndicate_stormtrooper_sword")
attack_verb = "slashed"
attack_sound = 'sound/weapons/blade1.ogg'
// If nothing else we'll stay a monke
. = ..()
name = new_name || name
/obj/effect/hallucination/chaser/attacker/assaulter/attack_effects()
do_attack_animation(target)
target.playsound_local(get_turf(src), istext(attack_sound) ? get_sfx(attack_sound) : attack_sound, 25, TRUE)
to_chat(target, "<span class='userdanger'>[name] has [attack_verb] [target]!</span>")
/obj/effect/hallucination/chaser/attacker/assaulter/on_knockdown()
target.visible_message("<span class='warning'>[target] recoils as if hit by something, before suddenly collapsing!</span>",
"<span class='userdanger'>[name] has [attack_verb] [target]!</span>")
QDEL_IN(src, 3 SECONDS)
/**
* # Hallucination - Xeno Pounce
*
* An imaginary alien hunter pounces towards the target.
*/
/obj/effect/hallucination/xeno_pounce
duration = 15 SECONDS
// Settings
/// Maximum number of times the alien will pounce.
var/num_pounces = 3
/// How often to pounce in deciseconds.
var/pounce_interval = 5 SECONDS
// Variables
/// The xeno hallucination reference.
var/obj/effect/hallucination/xeno_pouncer/xeno = null
/obj/effect/hallucination/xeno_pounce/Initialize(mapload, mob/living/carbon/target)
. = ..()
// Find a vent around us
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/vent in range(world.view / 2, target))
vents += vent
if(!length(vents))
return
var/turf/T = get_turf(pick(vents))
xeno = new(T, target)
xeno.dir = get_dir(T, target)
addtimer(CALLBACK(src, PROC_REF(do_pounce)), pounce_interval)
/obj/effect/hallucination/xeno_pounce/proc/do_pounce()
if(QDELETED(xeno) || QDELETED(target))
return
xeno.leap_to(target)
if(--num_pounces > 0)
addtimer(CALLBACK(src, PROC_REF(do_pounce)), pounce_interval)
/obj/effect/hallucination/xeno_pouncer
hallucination_icon = 'icons/mob/alien.dmi'
hallucination_icon_state = "alienh_pounce"
hallucination_override = TRUE
/obj/effect/hallucination/xeno_pouncer/Initialize(mapload, mob/living/carbon/target)
. = ..()
name = "\proper alien hunter ([rand(100, 999)])"
/obj/effect/hallucination/xeno_pouncer/throw_impact(A)
if(A == target)
forceMove(get_turf(target))
target.Weaken(10 SECONDS)
target.visible_message("<span class='danger'>[target] recoils backwards and falls flat!</span>",
"<span class='userdanger'>[name] pounces on you!</span>")
to_chat(target, "<span class='notice'>[name] begins climbing into the ventilation system...</span>")
QDEL_IN(src, 2 SECONDS)
/**
* Throws the xeno towards the given loc.
*
* Arguments:
* * dest - The loc to leap to.
*/
/obj/effect/hallucination/xeno_pouncer/proc/leap_to(dest)
if(images && images[1])
images[1].icon = 'icons/mob/alienleap.dmi'
images[1].icon_state = "alienh_leap"
dir = get_dir(get_turf(src), dest)
throw_at(dest, 7, 1, spin = FALSE, diagonals_first = TRUE, callback = CALLBACK(src, PROC_REF(reset_icon)))
/**
* Resets the xeno's icon to a resting state.
*/
/obj/effect/hallucination/xeno_pouncer/proc/reset_icon()
if(images && images[1])
images[1].icon = 'icons/mob/alien.dmi'
images[1].icon_state = "alienh_pounce"