mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-09-06 19:07:56 +01:00
[READY] Reworks and Refactors Hallucinations (#15360)
* Hallucination rework start * Chaser/Attacker, Abduction * Moderates * Majors * saber alter * address SteelSlayer * Fix admin logging and runtime * put hallucination logging on ATKLOG_ALL
This commit is contained in:
@@ -743,3 +743,11 @@
|
||||
///SSalarm signals
|
||||
#define COMSIG_TRIGGERED_ALARM "ssalarm_triggered"
|
||||
#define COMSIG_CANCELLED_ALARM "ssalarm_cancelled"
|
||||
|
||||
// /obj/machinery/door signals
|
||||
#define COMSIG_DOOR_OPEN "door_open"
|
||||
#define COMSIG_DOOR_CLOSE "door_close"
|
||||
|
||||
// /obj/machinery/door/airlock signals
|
||||
#define COMSIG_AIRLOCK_OPEN "airlock_open"
|
||||
#define COMSIG_AIRLOCK_CLOSE "airlock_close"
|
||||
|
||||
@@ -489,3 +489,8 @@
|
||||
|
||||
/// Send to the mentor Discord webhook
|
||||
#define DISCORD_WEBHOOK_MENTOR "MENTOR"
|
||||
|
||||
// Hallucination severities
|
||||
#define HALLUCINATE_MINOR 1
|
||||
#define HALLUCINATE_MODERATE 2
|
||||
#define HALLUCINATE_MAJOR 3
|
||||
|
||||
@@ -277,6 +277,11 @@
|
||||
#define HEARING_PROTECTION_MAJOR 2
|
||||
#define HEARING_PROTECTION_TOTAL 3
|
||||
|
||||
// Defines used in /mob/living/carbon/human/update_health_hud to override the health status
|
||||
#define HEALTH_HUD_OVERRIDE_NONE 0
|
||||
#define HEALTH_HUD_OVERRIDE_CRIT 1
|
||||
#define HEALTH_HUD_OVERRIDE_DEAD 2
|
||||
#define HEALTH_HUD_OVERRIDE_HEALTHY 3
|
||||
// Eye protection
|
||||
#define FLASH_PROTECTION_SENSITIVE -1
|
||||
#define FLASH_PROTECTION_NONE 0
|
||||
|
||||
@@ -742,6 +742,24 @@
|
||||
log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton")
|
||||
href_list["datumrefresh"] = href_list["make_skeleton"]
|
||||
|
||||
else if(href_list["hallucinate"])
|
||||
if(!check_rights(R_SERVER | R_EVENT))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/C = locateUID(href_list["hallucinate"])
|
||||
if(!istype(C))
|
||||
to_chat(usr, "<span class='warning'>This can only be used on instances of type /mob/living/carbon</span>")
|
||||
return
|
||||
|
||||
var/haltype = input(usr, "Select the hallucination type:", "Hallucinate") as null|anything in subtypesof(/obj/effect/hallucination)
|
||||
if(!haltype)
|
||||
return
|
||||
|
||||
C.hallucinate(haltype)
|
||||
message_admins("[key_name(usr)] has given [key_name(C)] the [haltype] hallucination")
|
||||
log_admin("[key_name_admin(usr)] has given [key_name_admin(C)] the [haltype] hallucination")
|
||||
href_list["datumrefresh"] = href_list["hallucinate"]
|
||||
|
||||
else if(href_list["offer_control"])
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
|
||||
@@ -30,9 +30,8 @@
|
||||
/mob/living/simple_animal/hostile/guardian/fire/AttackingTarget()
|
||||
. = ..()
|
||||
if(toggle)
|
||||
if(. && ishuman(target) && !summoner)
|
||||
spawn(0)
|
||||
new /obj/effect/hallucination/delusion(target.loc, target, force_kind = "custom", duration = 200, skip_nearby = 0, custom_icon = icon_state, custom_icon_file = icon)
|
||||
if(. && iscarbon(target))
|
||||
new /obj/effect/hallucination/delusion(get_turf(target), target, icon, icon_state)
|
||||
else
|
||||
if(prob(45))
|
||||
if(ismovable(target))
|
||||
|
||||
@@ -1188,6 +1188,7 @@ About the new airlock wires panel:
|
||||
|
||||
if(!density)
|
||||
return TRUE
|
||||
SEND_SIGNAL(src, COMSIG_AIRLOCK_OPEN)
|
||||
operating = TRUE
|
||||
update_icon(AIRLOCK_OPENING, 1)
|
||||
sleep(1)
|
||||
@@ -1228,6 +1229,7 @@ About the new airlock wires panel:
|
||||
if(killthis)
|
||||
killthis.ex_act(EXPLODE_HEAVY)//Smashin windows
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_AIRLOCK_CLOSE)
|
||||
operating = TRUE
|
||||
update_icon(AIRLOCK_CLOSING, 1)
|
||||
layer = CLOSED_DOOR_LAYER
|
||||
|
||||
@@ -266,6 +266,7 @@
|
||||
return TRUE
|
||||
if(operating)
|
||||
return
|
||||
SEND_SIGNAL(src, COMSIG_DOOR_OPEN)
|
||||
operating = TRUE
|
||||
do_animate("opening")
|
||||
set_opacity(0)
|
||||
@@ -295,6 +296,7 @@
|
||||
autoclose_in(60)
|
||||
return
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_DOOR_CLOSE)
|
||||
operating = TRUE
|
||||
|
||||
do_animate("closing")
|
||||
|
||||
@@ -135,8 +135,7 @@
|
||||
var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0)
|
||||
var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0)
|
||||
|
||||
spawn(0)
|
||||
new /obj/effect/hallucination/delusion(victim.loc, victim, force_kind = "demon", duration = duration, skip_nearby = 0)
|
||||
new /obj/effect/hallucination/delusion(get_turf(victim), victim, 'icons/mob/mob.dmi', "daemon")
|
||||
|
||||
var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc)
|
||||
chainsaw.flags |= NODROP | DROPDEL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* # Hallucination - Tripper
|
||||
*
|
||||
* A generic hallucination that causes the target to trip if they cross it.
|
||||
*/
|
||||
/obj/effect/hallucination/tripper
|
||||
anchored = TRUE
|
||||
/// Chance to trip when crossing.
|
||||
var/trip_chance = 100
|
||||
/// Stun to add when crossed.
|
||||
var/stun = 4 SECONDS_TO_LIFE_CYCLES
|
||||
/// Weaken to add when crossed.
|
||||
var/weaken = 4 SECONDS_TO_LIFE_CYCLES
|
||||
|
||||
/obj/effect/hallucination/tripper/CanPass(atom/movable/mover, turf/T)
|
||||
. = TRUE
|
||||
if(isliving(mover) && mover == target)
|
||||
var/mob/living/M = mover
|
||||
if(M.lying || !prob(trip_chance))
|
||||
return
|
||||
M.Stun(stun)
|
||||
M.Weaken(weaken)
|
||||
on_crossed()
|
||||
|
||||
/**
|
||||
* Called when the target crosses this hallucination.
|
||||
*/
|
||||
/obj/effect/hallucination/tripper/proc/on_crossed()
|
||||
return
|
||||
|
||||
/**
|
||||
* # Hallucination - Chaser
|
||||
*
|
||||
* A generic hallucination that chases the target.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser
|
||||
hallucination_icon = 'icons/mob/monkey.dmi'
|
||||
hallucination_icon_state = "monkey1"
|
||||
hallucination_override = TRUE
|
||||
// Settings
|
||||
// Minimum distance required between the target and us to keep chasing them.
|
||||
var/min_distance = 1
|
||||
/// Interval between two thinks in deciseconds. Shouldn't be too low to prevent lag.
|
||||
var/think_interval = 1 SECONDS
|
||||
// Variables
|
||||
/// Think timer handle.
|
||||
var/think_timer = null
|
||||
|
||||
/obj/effect/hallucination/chaser/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
name = "\proper monkey ([rand(100, 999)])"
|
||||
think_timer = addtimer(CALLBACK(src, .proc/think), think_interval, TIMER_LOOP | TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/hallucination/chaser/Destroy()
|
||||
deltimer(think_timer)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Called at regular intervals to determine what to do.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/proc/think()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
else if(QDELETED(target))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(get_dist(src, target) > min_distance)
|
||||
chase()
|
||||
else
|
||||
within_range()
|
||||
|
||||
/**
|
||||
* Called every Think when we are not close enough to the target.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/proc/chase()
|
||||
step_towards(src, target)
|
||||
|
||||
/**
|
||||
* Called every Think when we are close enough to the target.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/proc/within_range()
|
||||
return
|
||||
|
||||
/**
|
||||
* # Hallucination - Attacker
|
||||
*
|
||||
* A generic hallucination based on the Chaser that attacks if close enough.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/attacker
|
||||
/// Chance to attack per Think spent in range.
|
||||
var/attack_chance = 100
|
||||
/// Stamina damage to heal on hit.
|
||||
var/damage = 25
|
||||
/// Whether to attack if the target is knocked down.
|
||||
var/should_attack_weakened = FALSE
|
||||
|
||||
/obj/effect/hallucination/chaser/attacker/within_range()
|
||||
if(!prob(attack_chance))
|
||||
return
|
||||
var/was_weakened = target.IsWeakened()
|
||||
if(was_weakened && !should_attack_weakened)
|
||||
return
|
||||
|
||||
attack(was_weakened)
|
||||
|
||||
/**
|
||||
* Called every Think when we are attacking the target.
|
||||
*
|
||||
* Arguments:
|
||||
* * was_weakened - Whether the target was already knocked down prior to this attack.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/attacker/proc/attack(was_weakened)
|
||||
dir = get_dir(src, target)
|
||||
attack_effects()
|
||||
target.adjustStaminaLoss(damage)
|
||||
if(!was_weakened && target.IsWeakened())
|
||||
on_knockdown()
|
||||
|
||||
/**
|
||||
* Called to handle the visual and audio effects of an attack.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/attacker/proc/attack_effects()
|
||||
do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
target.playsound_local(get_turf(src), get_sfx("punch"), 25, TRUE)
|
||||
to_chat(target, "<span class='userdanger'>[name] has punched [target]!</span>")
|
||||
|
||||
/**
|
||||
* Called when one of our attacks put the target in stamina crit.
|
||||
*/
|
||||
/obj/effect/hallucination/chaser/attacker/proc/on_knockdown()
|
||||
target.visible_message("<span class='warning'>[target] recoils as if hit by something, before suddenly collapsing!</span>",
|
||||
"<span class='userdanger'>[src]'s blow was too much for you, causing you to collapse!</span>")
|
||||
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
* # 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_TO_LIFE_CYCLES)
|
||||
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/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/phase_2), length_phase_1)
|
||||
addtimer(CALLBACK(src, .proc/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/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("eskimo", "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("eskimo", "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/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/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(5)
|
||||
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/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"
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* # Hallucination - Audio
|
||||
*
|
||||
* Plays a random sound.
|
||||
*/
|
||||
/obj/effect/hallucination/audio
|
||||
duration = 0
|
||||
/// Associative list of sounds that may be played. Value corresponds to the volume.
|
||||
var/list/sounds = list(
|
||||
'sound/effects/explosionfar.ogg' = 50,
|
||||
'sound/effects/pray_chaplain.ogg' = 50,
|
||||
'sound/machines/alarm.ogg' = 100,
|
||||
'sound/magic/summon_guns.ogg' = 50,
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/audio/Initialize(mapload, mob/living/carbon/target, atom/source = null)
|
||||
. = ..()
|
||||
var/snd = pick(sounds)
|
||||
target.playsound_local(source, snd, sounds[snd])
|
||||
|
||||
/**
|
||||
* # Hallucination - Audio (Localized)
|
||||
*
|
||||
* Plays a random sound at a random location around the target.
|
||||
*/
|
||||
/obj/effect/hallucination/audio/localized
|
||||
sounds = list(
|
||||
'sound/effects/explosion1.ogg' = 50,
|
||||
'sound/effects/explosion2.ogg' = 50,
|
||||
'sound/effects/glassbr1.ogg' = 50,
|
||||
'sound/effects/glassbr2.ogg' = 50,
|
||||
'sound/effects/glassbr3.ogg' = 50,
|
||||
'sound/machines/airlock_open.ogg' = 50,
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/audio/localized/Initialize(mapload, mob/living/carbon/target)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(world.view, target))
|
||||
turfs += T
|
||||
if(length(turfs))
|
||||
. = ..(mapload, target, pick(turfs))
|
||||
else
|
||||
. = ..(mapload, target)
|
||||
|
||||
/**
|
||||
* # Hallucination - Bolts
|
||||
*
|
||||
* Visually bolts a random number of airlocks around the target.
|
||||
*/
|
||||
/obj/effect/hallucination/bolts
|
||||
duration = 15 SECONDS
|
||||
/// The maximum amount of airlocks to fake bolt.
|
||||
var/bolt_amount = 2
|
||||
/// The duration of fake bolt in deciseconds.
|
||||
var/bolt_duration = 10 SECONDS
|
||||
/// Lazy list of fake bolted airlocks. Key is airlock, value is bolt overlay.
|
||||
var/list/bolted
|
||||
|
||||
/obj/effect/hallucination/bolts/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/airlocks = list()
|
||||
for(var/obj/machinery/door/airlock/A in oview(world.view, target))
|
||||
airlocks += A
|
||||
|
||||
var/num_bolted = 0
|
||||
while(bolt_amount && length(airlocks))
|
||||
var/obj/machinery/door/airlock/A = pick_n_take(airlocks)
|
||||
if(A.locked)
|
||||
continue
|
||||
addtimer(CALLBACK(src, .proc/do_bolt, A), num_bolted++ * rand(5, 7))
|
||||
bolt_amount--
|
||||
|
||||
/**
|
||||
* Called in a timer to fake bolt the given airlock.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - The airlock to fake bolt.
|
||||
*/
|
||||
/obj/effect/hallucination/bolts/proc/do_bolt(obj/machinery/door/airlock/A)
|
||||
if(QDELETED(A) || (A.locked && A.arePowerSystemsOn()) || A.operating || !A.density)
|
||||
return
|
||||
|
||||
var/bolt_overlay = image(get_airlock_overlay("lights_bolts", A.overlays_file), A)
|
||||
add_icon(bolt_overlay)
|
||||
target?.playsound_local(get_turf(A), A.boltDown, 30, FALSE, 3)
|
||||
LAZYSET(bolted, A, bolt_overlay)
|
||||
// Timer and signal to turn it off (only one can happen)
|
||||
RegisterSignal(A, COMSIG_AIRLOCK_OPEN, .proc/do_unbolt)
|
||||
addtimer(CALLBACK(src, .proc/do_unbolt, A, bolt_overlay), bolt_duration)
|
||||
|
||||
/**
|
||||
* Called in a timer to fake unbolt the given airlock.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - The airlock to fake unbolt.
|
||||
* * bolt_overlay - The bolt overlay image currently displayed on A.
|
||||
*/
|
||||
/obj/effect/hallucination/bolts/proc/do_unbolt(obj/machinery/door/airlock/A, image/bolt_overlay)
|
||||
if(QDELETED(A))
|
||||
return
|
||||
// bolt_overlay is null if this proc is called from the signal, so use the lookup table to retrieve it
|
||||
bolt_overlay = bolt_overlay || bolted[A]
|
||||
if(QDELETED(bolt_overlay))
|
||||
return
|
||||
UnregisterSignal(A, COMSIG_AIRLOCK_CLOSE)
|
||||
clear_icon(bolt_overlay)
|
||||
target?.playsound_local(get_turf(A), A.boltUp, 30, FALSE, 3)
|
||||
bolted[A] = null
|
||||
|
||||
/**
|
||||
* # Hallucination - Speech
|
||||
*
|
||||
* Causes the target to hear a fake message from a random mob around them.
|
||||
*/
|
||||
/obj/effect/hallucination/speech
|
||||
duration = 3 SECONDS
|
||||
/// List of messages that may be heard.
|
||||
var/list/messages = list(
|
||||
"I'm watching you...",
|
||||
"I'm going to kill you!",
|
||||
"Get out!",
|
||||
"Kchck-Chkck? Kchchck!",
|
||||
"Did you hear that?",
|
||||
"What did you do?",
|
||||
"Why?",
|
||||
"Give me that!",
|
||||
"Honk!",
|
||||
"Kill me!",
|
||||
"HELP!!",
|
||||
"RUN!!",
|
||||
"EI NATH!!",
|
||||
"O bidai nabora se'sma!",
|
||||
"I have the disk!",
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/speech/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/mobs = list()
|
||||
for(var/mob/living/M in oview(world.view, target))
|
||||
mobs += M
|
||||
if(!length(mobs))
|
||||
return
|
||||
|
||||
var/mob/living/M = pick(mobs)
|
||||
var/message = pick(messages + "[target]!")
|
||||
target.hear_say(message_to_multilingual(message, pick(target.languages)), speaker = M)
|
||||
// Speech bubble
|
||||
var/image/speech_bubble = image('icons/mob/talk.dmi', M, "[target.bubble_icon][say_test(message)]", layer = FLY_LAYER)
|
||||
speech_bubble.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
||||
add_icon(speech_bubble)
|
||||
|
||||
/**
|
||||
* # Hallucination - Fake Danger
|
||||
*
|
||||
* Sends a random danger message to the target's chat.
|
||||
*/
|
||||
/obj/effect/hallucination/fake_danger
|
||||
duration = 0
|
||||
/// List of messages that may be displayed.
|
||||
var/list/messages = list(
|
||||
"The light burns you!",
|
||||
"You experience a stabbing sensation and your ears begin to ring...",
|
||||
"You get the feeling this is a bad idea.",
|
||||
"Your blood boils in your veins!",
|
||||
"You hear a loud buzz in your head, silencing your thoughts!",
|
||||
"You feel an awful sense of being watched...",
|
||||
"You suddenly feel very hot.",
|
||||
"You feel like you could blow up at any moment!",
|
||||
"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?",
|
||||
"You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?",
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/fake_danger/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
to_chat(target, "<span class='userdanger'>[pick(messages)]</span>")
|
||||
|
||||
/**
|
||||
* # Hallucination - Fake Health
|
||||
*
|
||||
* Visually changes the target's health status to something it shouldn't be.
|
||||
*/
|
||||
/obj/effect/hallucination/fake_health
|
||||
duration = list(10 SECONDS, 25 SECONDS)
|
||||
|
||||
/obj/effect/hallucination/fake_health/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
if(target.health > HEALTH_THRESHOLD_CRIT)
|
||||
target.health_hud_override = pick(HEALTH_HUD_OVERRIDE_CRIT, HEALTH_HUD_OVERRIDE_DEAD)
|
||||
else
|
||||
target.health_hud_override = HEALTH_HUD_OVERRIDE_HEALTHY // You think you're fine, but you're not
|
||||
target.update_health_hud()
|
||||
|
||||
/obj/effect/hallucination/fake_health/Destroy()
|
||||
target?.health_hud_override = HEALTH_HUD_OVERRIDE_NONE
|
||||
target?.update_health_hud()
|
||||
return ..()
|
||||
@@ -0,0 +1,478 @@
|
||||
/**
|
||||
* # Hallucination - Bolts (Moderate)
|
||||
*
|
||||
* A variation that affects more airlocks.
|
||||
*/
|
||||
/obj/effect/hallucination/bolts/moderate
|
||||
bolt_amount = 7
|
||||
|
||||
/**
|
||||
* # Hallucination - Fake Alert
|
||||
*
|
||||
* Displays a random alert on the target's HUD.
|
||||
*/
|
||||
/obj/effect/hallucination/fake_alert
|
||||
duration = list(10 SECONDS, 25 SECONDS)
|
||||
/// The possible alerts to be displayed. Key is alert type, value is alert category.
|
||||
var/list/alerts = list(
|
||||
/obj/screen/alert/not_enough_oxy = "not_enough_oxy",
|
||||
/obj/screen/alert/not_enough_tox = "not_enough_tox",
|
||||
/obj/screen/alert/not_enough_co2 = "not_enough_co2",
|
||||
/obj/screen/alert/not_enough_nitro = "not_enough_nitro",
|
||||
/obj/screen/alert/too_much_oxy = "too_much_oxy",
|
||||
/obj/screen/alert/too_much_co2 = "too_much_co2",
|
||||
/obj/screen/alert/too_much_tox = "too_much_tox",
|
||||
/obj/screen/alert/fat = "nutrition",
|
||||
/obj/screen/alert/starving = "nutrition",
|
||||
/obj/screen/alert/hot = "temp",
|
||||
/obj/screen/alert/cold = "temp",
|
||||
/obj/screen/alert/highpressure = "pressure",
|
||||
/obj/screen/alert/lowpressure = "pressure",
|
||||
)
|
||||
/// Alert severities. Only needed for some alerts such as temperature or pressure. Key is alert category, value is severity.
|
||||
var/list/severities = list(
|
||||
"temp" = 3,
|
||||
"pressure" = 2,
|
||||
)
|
||||
/// The alert category that was affected(arc) as part of this hallucination.
|
||||
var/alert_category
|
||||
|
||||
/obj/effect/hallucination/fake_alert/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
var/alert_type = pick(alerts)
|
||||
alert_category = alerts[alert_type]
|
||||
target.throw_alert(alert_category, alert_type, override = TRUE, severity = severities[alert_category])
|
||||
|
||||
/obj/effect/hallucination/fake_alert/Destroy()
|
||||
target?.clear_alert(alert_category, clear_override = TRUE)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* # Hallucination - Fake Item
|
||||
*
|
||||
* Displays a random fake item around the target. If it's on the floor and they try to pick it up, they will trip and fall.
|
||||
*/
|
||||
/obj/effect/hallucination/fake_item
|
||||
hallucination_override = TRUE
|
||||
hallucination_layer = OBJ_LAYER
|
||||
/// Static list of items this hallucination can be.
|
||||
var/static/list/items = list(
|
||||
"\improper .357 revolver" = list('icons/obj/guns/projectile.dmi', "revolver"),
|
||||
"\improper ARG" = list('icons/obj/guns/projectile.dmi', "arg-30"),
|
||||
"\improper C4" = list('icons/obj/grenade.dmi', "plastic-explosive0"),
|
||||
"\improper L6 SAW" = list('icons/obj/guns/projectile.dmi', "l6closed100"),
|
||||
"chainsaw" = list('icons/obj/items.dmi', "chainsaw0"),
|
||||
"combat shotgun" = list('icons/obj/guns/projectile.dmi', "cshotgun"),
|
||||
"double-bladed energy sword" = list('icons/obj/items.dmi', "dualsaberred1"),
|
||||
"energy sword" = list('icons/obj/items.dmi', "swordred"),
|
||||
"fireaxe" = list('icons/obj/items.dmi', "fireaxe1"),
|
||||
"ritual dagger" = list('icons/obj/cult.dmi', "blood_dagger"),
|
||||
"ritual dagger" = list('icons/obj/cult.dmi', "death_dagger"),
|
||||
"ritual dagger" = list('icons/obj/cult.dmi', "hell_dagger"),
|
||||
"sniper rifle" = list('icons/obj/guns/projectile.dmi', "sniper"),
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/fake_item/Initialize(mapload, mob/living/carbon/target)
|
||||
name = pick(items)
|
||||
var/list/icon_data = items[name]
|
||||
hallucination_icon = icon_data[1]
|
||||
hallucination_icon_state = icon_data[2]
|
||||
. = ..()
|
||||
|
||||
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
|
||||
loc = pick(locs)
|
||||
|
||||
/obj/effect/hallucination/fake_item/attack_hand(mob/living/user)
|
||||
if(user != target)
|
||||
return
|
||||
if(hasorgans(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/obj/item/organ/external/temp = H.bodyparts_by_name["r_hand"]
|
||||
if(user.hand)
|
||||
temp = H.bodyparts_by_name["l_hand"]
|
||||
if(!temp)
|
||||
to_chat(user, "<span class='warning'>You try to use your hand, but it's missing!</span>")
|
||||
return
|
||||
if(!temp.is_usable())
|
||||
to_chat(user, "<span class='warning'>You try to move your [temp.name], but cannot!</span>")
|
||||
return
|
||||
|
||||
user.Weaken(4 SECONDS_TO_LIFE_CYCLES)
|
||||
user.visible_message("<span class='warning'>[user] does a grabbing motion towards [get_turf(src)] but [user.p_they()] stumble[user.p_s()] - nothing is there!</span>",
|
||||
"<span class='userdanger'>[src] vanishes as you try grabbing it, causing you to stumble!</span>")
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* # Hallucination - Fake Weapon
|
||||
*
|
||||
* Displays a random fake weapon wielded by a human around the target.
|
||||
*/
|
||||
/obj/effect/hallucination/fake_weapon
|
||||
/// Static list of weapons this hallucination can be. Key is icon state, value is LEFT-HAND icon file.
|
||||
var/static/list/weapons = list(
|
||||
"advtaserstun4" = 'icons/mob/inhands/guns_lefthand.dmi',
|
||||
"arm_blade" = null,
|
||||
"blood_blade" = null,
|
||||
"crossbow" = 'icons/mob/inhands/guns_lefthand.dmi',
|
||||
"death_blade" = null,
|
||||
"disintegrate" = null,
|
||||
"fireaxe0" = null,
|
||||
"hell_blade" = null,
|
||||
"ling_shield" = null,
|
||||
"nucgun" = 'icons/mob/inhands/guns_lefthand.dmi',
|
||||
"prod" = null,
|
||||
"staffofslipping" = null,
|
||||
"staffofstorms" = null,
|
||||
"swordred" = null,
|
||||
"ttv" = null,
|
||||
)
|
||||
/// The default LEFT-HAND icon file for weapons. Static.
|
||||
var/static/default_icon = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
/// Static list of RIGHT-HAND counterpart for any LEFT-HAND icon files used above.
|
||||
var/static/right_hand_icons = list(
|
||||
'icons/mob/inhands/items_lefthand.dmi' = 'icons/mob/inhands/items_righthand.dmi',
|
||||
'icons/mob/inhands/guns_lefthand.dmi' = 'icons/mob/inhands/guns_righthand.dmi',
|
||||
)
|
||||
/// The mob wielding the fake weapon.
|
||||
var/mob/living/carbon/human/wielder = null
|
||||
|
||||
/obj/effect/hallucination/fake_weapon/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
// Find able-bodied mobs first
|
||||
var/list/mobs = list()
|
||||
for(var/mob/living/carbon/human/H in oview(world.view, target))
|
||||
if(H.stat || !((H.has_left_hand() && !H.l_hand) || (H.has_right_hand() && !H.r_hand)))
|
||||
continue
|
||||
mobs += H
|
||||
if(!length(mobs))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
// Pick a hand if it exists of course
|
||||
wielder = pick(mobs)
|
||||
var/right = FALSE
|
||||
if(!(wielder.bodyparts_by_name["l_hand"] && !wielder.l_hand) || ((wielder.bodyparts_by_name["r_hand"] && !wielder.r_hand) && prob(50)))
|
||||
right = TRUE
|
||||
|
||||
// Create the icon
|
||||
hallucination_icon_state = pick(weapons)
|
||||
var/icon = weapons[hallucination_icon_state] || default_icon
|
||||
if(right)
|
||||
icon = right_hand_icons[icon]
|
||||
var/image/I = image(icon, wielder, hallucination_icon_state)
|
||||
I = center_image(I, 32, 32)
|
||||
add_icon(I)
|
||||
|
||||
if(hallucination_icon_state == "swordred")
|
||||
target.playsound_local(get_turf(wielder), 'sound/weapons/saberon.ogg', 35, TRUE)
|
||||
|
||||
/obj/effect/hallucination/fake_weapon/Destroy()
|
||||
if(!QDELETED(wielder) && hallucination_icon_state == "swordred")
|
||||
target.playsound_local(get_turf(wielder), 'sound/weapons/saberoff.ogg', 35, TRUE)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* # Hallucination - Chasms
|
||||
*
|
||||
* Displays fake chasms around the target that if crossed, cause them to trip.
|
||||
*/
|
||||
/obj/effect/hallucination/chasms
|
||||
/// Minimum number of chasms to create.
|
||||
var/min_amount = 3
|
||||
/// Maximum number of chasms to create.
|
||||
var/max_amount = 7
|
||||
|
||||
/obj/effect/hallucination/chasms/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
// Let's check if we can spawn somewhere first
|
||||
var/list/locs = list()
|
||||
for(var/turf/T in oview(world.view, target))
|
||||
if(isfloorturf(T) && !is_blocked_turf(T))
|
||||
locs += T
|
||||
if(!length(locs))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/amount = rand(min_amount, max_amount)
|
||||
while(amount-- && length(locs))
|
||||
new /obj/effect/hallucination/tripper/chasm(pick_n_take(locs), target)
|
||||
|
||||
/**
|
||||
* # Hallucination - Chasm
|
||||
*
|
||||
* A fake chasm that if crossed by the target, causes them to trip.
|
||||
*/
|
||||
/obj/effect/hallucination/tripper/chasm
|
||||
name = "chasm"
|
||||
hallucination_icon = 'icons/turf/floors/Chasms.dmi'
|
||||
hallucination_icon_state = "smooth"
|
||||
hallucination_override = TRUE
|
||||
hallucination_layer = HIGH_TURF_LAYER
|
||||
stun = 8 SECONDS_TO_LIFE_CYCLES
|
||||
weaken = 8 SECONDS_TO_LIFE_CYCLES
|
||||
|
||||
/obj/effect/hallucination/tripper/chasm/on_crossed()
|
||||
target.visible_message("<span class='warning'>[target] trips over nothing and flails on [get_turf(target)] as if they were falling!</span>",
|
||||
"<span class='userdanger'>You stumble and stare into an abyss before you. It stares back, and you fall into the enveloping dark!</span>")
|
||||
|
||||
/**
|
||||
* # Hallucination - Delamination Alarm
|
||||
*
|
||||
* A fake radio message and audio that alerts of an increasing SM unstability.
|
||||
*/
|
||||
/obj/effect/hallucination/delamination_alarm
|
||||
duration = 0
|
||||
|
||||
/obj/effect/hallucination/delamination_alarm/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
target.playsound_local(target, 'sound/machines/engine_alert2.ogg', 25, FALSE, 30, 30)
|
||||
target.hear_radio(message_to_multilingual("Danger! Crystal hyperstructure integrity faltering! Integrity: [rand(30, 50)]%"), vname = "supermatter crystal", part_a = "<span class='[SSradio.frequency_span_class(PUB_FREQ)]'><b>\[[get_frequency_name(PUB_FREQ)]\]</b> <span class='name'>", part_b = "</span> <span class='message'>")
|
||||
|
||||
/**
|
||||
* # Hallucination - Plasma Flood
|
||||
*
|
||||
* A fake plasma flood emanating from a nearby vent.
|
||||
*/
|
||||
/obj/effect/hallucination/plasma_flood
|
||||
duration = 25 SECONDS
|
||||
/// List of turfs that need expanding from.
|
||||
var/list/turf/expand_queue = list()
|
||||
/// Associative list of turfs that have already been processed.
|
||||
var/list/turf/processed = list()
|
||||
/// The delay at which the plasma flood expands in deciseconds. Shouldn't be too low to prevent lag.
|
||||
var/expand_delay = 2.5 SECONDS // Expand 10 times
|
||||
/// Expand timer handle.
|
||||
var/expand_timer = null
|
||||
|
||||
/obj/effect/hallucination/plasma_flood/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/vents = list()
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/vent in oview(world.view, target))
|
||||
if(!is_blocked_turf(vent) && !vent.welded)
|
||||
vents += vent
|
||||
if(!length(vents))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(pick(vents))
|
||||
create_plasma(T)
|
||||
expand_queue += T
|
||||
processed[T] = TRUE
|
||||
expand_timer = addtimer(CALLBACK(src, .proc/expand), expand_delay, TIMER_LOOP | TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/hallucination/plasma_flood/Destroy()
|
||||
deltimer(expand_timer)
|
||||
QDEL_NULL(expand_queue)
|
||||
QDEL_NULL(processed)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Called regularly in a timer to process the plasma flooding.
|
||||
*/
|
||||
/obj/effect/hallucination/plasma_flood/proc/expand()
|
||||
// Brace for potentially expensive proc
|
||||
for(var/t in expand_queue)
|
||||
var/turf/source_turf = t
|
||||
expand_queue -= source_turf
|
||||
// Expand to each dir
|
||||
for(var/dir in GLOB.cardinal)
|
||||
var/turf/target_turf = get_step(source_turf, dir)
|
||||
if(processed[target_turf] || !source_turf.CanAtmosPass(target_turf))
|
||||
continue
|
||||
create_plasma(target_turf)
|
||||
expand_queue += target_turf
|
||||
processed[target_turf] = TRUE
|
||||
|
||||
/**
|
||||
* Creates a fake plasma overlay on the given turf.
|
||||
*
|
||||
* Arguments:
|
||||
* * T - The turf to create a fake plasma overlay on.
|
||||
*/
|
||||
/obj/effect/hallucination/plasma_flood/proc/create_plasma(turf/T)
|
||||
var/image/I = image('icons/effects/tile_effects.dmi', T, "plasma", layer = FLY_LAYER)
|
||||
I.plane = GAME_PLANE
|
||||
add_icon(I)
|
||||
|
||||
/**
|
||||
* # Hallucination - Husks
|
||||
*
|
||||
* A random number of fake husks around the target.
|
||||
*/
|
||||
/obj/effect/hallucination/husks
|
||||
duration = 25 SECONDS
|
||||
/// The base number of husks to create.
|
||||
var/num_base = 3
|
||||
/// The husk number variation, both negative and positive.
|
||||
var/variation = 1
|
||||
|
||||
/obj/effect/hallucination/husks/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/locs = list()
|
||||
for(var/turf/T in oview(world.view, target))
|
||||
if(isfloorturf(T) && !is_blocked_turf(T))
|
||||
locs += T
|
||||
if(!length(locs))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/to_spawn = num_base + rand(-variation, variation)
|
||||
while(to_spawn-- && length(locs))
|
||||
var/image/I = image('icons/mob/human.dmi', pick_n_take(locs), "husk_s", layer = MOB_LAYER, dir = pick(GLOB.cardinal))
|
||||
I.plane = GAME_PLANE
|
||||
I.transform = turn(I.transform, pick(-90, 90))
|
||||
add_icon(I)
|
||||
|
||||
/**
|
||||
* # Hallucination - Stunprodding
|
||||
*
|
||||
* A series of localized audio playback simulating a kidnapping with a stunprod.
|
||||
*/
|
||||
/obj/effect/hallucination/stunprodding
|
||||
duration = 3 SECONDS
|
||||
|
||||
/obj/effect/hallucination/stunprodding/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(world.view, target))
|
||||
turfs += T
|
||||
|
||||
var/turf/T = pick(turfs)
|
||||
target.playsound_local(T, 'sound/weapons/Egloves.ogg', 25, TRUE)
|
||||
target.playsound_local(T, get_sfx("bodyfall"), 25, TRUE)
|
||||
target.playsound_local(T, "sparks", 50, TRUE)
|
||||
|
||||
if(prob(50))
|
||||
var/snd = pick('sound/goonstation/voice/female_scream.ogg', 'sound/goonstation/voice/male_scream.ogg')
|
||||
play_sound_in(rand(13, 20), T, snd, 50, TRUE, rand(9, 11) / 10)
|
||||
|
||||
play_sound_in(rand(17, 20), T, 'sound/weapons/cablecuff.ogg', 15, TRUE)
|
||||
|
||||
/**
|
||||
* # Hallucination - Energy Sword
|
||||
*
|
||||
* A series of localized audio playback simulating an energy sword murder.
|
||||
*/
|
||||
/obj/effect/hallucination/energy_sword
|
||||
duration = 10 SECONDS
|
||||
|
||||
/obj/effect/hallucination/energy_sword/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(world.view, target))
|
||||
turfs += T
|
||||
|
||||
var/turf/T = pick(turfs)
|
||||
loc = T
|
||||
target.playsound_local(T, 'sound/weapons/saberon.ogg', 20, TRUE)
|
||||
|
||||
var/scream_sound = pick('sound/goonstation/voice/female_scream.ogg', 'sound/goonstation/voice/male_scream.ogg')
|
||||
var/scream_pitch = rand(9, 11) / 10
|
||||
var/num_hits = rand(5, 6)
|
||||
var/scream_cd = 0
|
||||
for(var/i in 1 to num_hits)
|
||||
var/time = i * CLICK_CD_MELEE + rand(3, 7)
|
||||
play_sound_in(time, T, 'sound/weapons/blade1.ogg', 15, TRUE)
|
||||
if(i == num_hits)
|
||||
play_sound_in(time, T, pick('sound/goonstation/voice/deathgasp_1.ogg', 'sound/goonstation/voice/deathgasp_2.ogg'), 50, TRUE, scream_pitch)
|
||||
else if(scream_sound && scream_cd-- <= 0 && prob(20))
|
||||
scream_cd = 2
|
||||
play_sound_in(time, T, scream_sound, 50, TRUE, scream_pitch)
|
||||
|
||||
/obj/effect/hallucination/energy_sword/Destroy()
|
||||
target.playsound_local(loc, 'sound/weapons/saberoff.ogg', 20, TRUE)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* # Hallucination - Gunfire
|
||||
*
|
||||
* A series of localized audio playback simulating a gunshot murder.
|
||||
*/
|
||||
/obj/effect/hallucination/gunfire
|
||||
duration = 10 SECONDS
|
||||
|
||||
/obj/effect/hallucination/gunfire/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(world.view, target))
|
||||
turfs += T
|
||||
|
||||
var/turf/T = pick(turfs)
|
||||
loc = T
|
||||
|
||||
var/gun_sound = pick('sound/weapons/gunshots/gunshot_pistol.ogg', 'sound/weapons/gunshots/gunshot_strong.ogg')
|
||||
var/scream_sound = pick('sound/goonstation/voice/female_scream.ogg', 'sound/goonstation/voice/male_scream.ogg')
|
||||
var/scream_pitch = rand(9, 11) / 10
|
||||
var/num_hits = rand(7, 8)
|
||||
var/scream_cd = 0
|
||||
for(var/i in 1 to num_hits)
|
||||
var/time = i * CLICK_CD_RANGE + rand(2, 4)
|
||||
play_sound_in(time, T, gun_sound, 25, TRUE)
|
||||
if(i == num_hits)
|
||||
play_sound_in(time, T, pick('sound/goonstation/voice/deathgasp_1.ogg', 'sound/goonstation/voice/deathgasp_2.ogg'), 50, TRUE, scream_pitch)
|
||||
else if(scream_sound && scream_cd-- <= 0 && prob(20))
|
||||
scream_cd = 2
|
||||
play_sound_in(time, T, scream_sound, 50, TRUE, scream_pitch)
|
||||
|
||||
/**
|
||||
* # Hallucination - Self Delusion
|
||||
*
|
||||
* Changes the target's appearance to something else temporarily.
|
||||
*/
|
||||
/obj/effect/hallucination/self_delusion
|
||||
duration = 15 SECONDS
|
||||
|
||||
/obj/effect/hallucination/self_delusion/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
|
||||
var/image/I = get_image()
|
||||
I.override = TRUE
|
||||
add_icon(I)
|
||||
|
||||
to_chat(target, "<span class='italics'>...wabbajack...wabbajack...</span>")
|
||||
target.playsound_local(get_turf(target), 'sound/magic/staff_change.ogg', 50, TRUE, -1)
|
||||
|
||||
/**
|
||||
* Returns the image to use as override to the target's appearance.
|
||||
*/
|
||||
/obj/effect/hallucination/self_delusion/proc/get_image()
|
||||
return image('icons/mob/animal.dmi', target, pick("bear", "brownbear", "corgi", "cow", "deer", "goat", "goose", "pig", "blank-body"))
|
||||
|
||||
/**
|
||||
* # Hallucination - Delusion
|
||||
*
|
||||
* Changes the appearance of all humans around the target.
|
||||
*/
|
||||
/obj/effect/hallucination/delusion
|
||||
duration = 15 SECONDS
|
||||
|
||||
/obj/effect/hallucination/delusion/Initialize(mapload, mob/living/carbon/target, override_icon, override_icon_state)
|
||||
. = ..()
|
||||
|
||||
for(var/mob/living/carbon/human/H in orange(world.view, target))
|
||||
var/image/I
|
||||
if(override_icon && override_icon_state)
|
||||
I = image(override_icon, H, override_icon_state)
|
||||
else
|
||||
I = get_image(H)
|
||||
I.override = TRUE
|
||||
add_icon(I)
|
||||
|
||||
/**
|
||||
* Returns the image to use as override to the target's appearance.
|
||||
*/
|
||||
/obj/effect/hallucination/delusion/proc/get_image(mob/living/carbon/human/H)
|
||||
return image('icons/mob/animal.dmi', H, pick("bear", "brownbear", "corgi", "cow", "deer", "goat", "goose", "pig", "blank-body"))
|
||||
@@ -0,0 +1,198 @@
|
||||
#define HALLUCINATE_COOLDOWN_MIN 20 SECONDS
|
||||
#define HALLUCINATE_COOLDOWN_MAX 50 SECONDS
|
||||
/// This is multiplied with [/mob/var/hallucination] to determine the final cooldown. A higher hallucination value means shorter cooldown.
|
||||
#define HALLUCINATE_COOLDOWN_FACTOR 0.03
|
||||
/// Percentage defining the chance at which an hallucination may spawn past the cooldown.
|
||||
#define HALLUCINATE_CHANCE 80
|
||||
// Severity weights, should sum up to 100!
|
||||
#define HALLUCINATE_MINOR_WEIGHT 60
|
||||
#define HALLUCINATE_MODERATE_WEIGHT 30
|
||||
#define HALLUCINATE_MAJOR_WEIGHT 10
|
||||
|
||||
GLOBAL_LIST_INIT(hallucinations, list(
|
||||
HALLUCINATE_MINOR = list(
|
||||
/obj/effect/hallucination/bolts = 10,
|
||||
/obj/effect/hallucination/fake_danger = 10,
|
||||
/obj/effect/hallucination/fake_health = 15,
|
||||
/obj/effect/hallucination/speech = 15,
|
||||
/obj/effect/hallucination/audio = 25,
|
||||
/obj/effect/hallucination/audio/localized = 25,
|
||||
),
|
||||
HALLUCINATE_MODERATE = list(
|
||||
/obj/effect/hallucination/delusion = 5,
|
||||
/obj/effect/hallucination/self_delusion = 5,
|
||||
/obj/effect/hallucination/bolts/moderate = 10,
|
||||
/obj/effect/hallucination/chasms = 10,
|
||||
/obj/effect/hallucination/fake_alert = 10,
|
||||
/obj/effect/hallucination/gunfire = 10,
|
||||
/obj/effect/hallucination/plasma_flood = 10,
|
||||
/obj/effect/hallucination/stunprodding = 10,
|
||||
/obj/effect/hallucination/delamination_alarm = 15,
|
||||
/obj/effect/hallucination/fake_item = 15,
|
||||
/obj/effect/hallucination/fake_weapon = 15,
|
||||
/obj/effect/hallucination/husks = 15,
|
||||
),
|
||||
HALLUCINATE_MAJOR = list(
|
||||
/obj/effect/hallucination/abduction = 10,
|
||||
/obj/effect/hallucination/assault = 10,
|
||||
/obj/effect/hallucination/terror_infestation = 10,
|
||||
/obj/effect/hallucination/loose_energy_ball = 10,
|
||||
)
|
||||
))
|
||||
|
||||
/**
|
||||
* Called as part of [/mob/living/proc/handle_status_effects] to handle hallucinations.
|
||||
*/
|
||||
/mob/living/carbon/proc/handle_hallucinations()
|
||||
if(!hallucination || next_hallucination > world.time)
|
||||
return
|
||||
|
||||
next_hallucination = world.time + rand(HALLUCINATE_COOLDOWN_MIN, HALLUCINATE_COOLDOWN_MAX) / (hallucination * HALLUCINATE_COOLDOWN_FACTOR)
|
||||
if(!prob(HALLUCINATE_CHANCE))
|
||||
return
|
||||
|
||||
// Pick a severity
|
||||
var/severity = HALLUCINATE_MINOR
|
||||
switch(rand(100))
|
||||
if(0 to HALLUCINATE_MINOR_WEIGHT)
|
||||
severity = HALLUCINATE_MINOR
|
||||
if((HALLUCINATE_MINOR_WEIGHT + 1) to HALLUCINATE_MODERATE_WEIGHT)
|
||||
severity = HALLUCINATE_MODERATE
|
||||
if((HALLUCINATE_MINOR_WEIGHT + HALLUCINATE_MODERATE_WEIGHT + 1) to 100)
|
||||
severity = HALLUCINATE_MAJOR
|
||||
|
||||
hallucinate(pickweight(GLOB.hallucinations[severity]))
|
||||
|
||||
/**
|
||||
* Spawns an hallucination for the mob.
|
||||
*
|
||||
* Arguments:
|
||||
* * H - The type path of the hallucination to spawn.
|
||||
*/
|
||||
/mob/living/carbon/proc/hallucinate(obj/effect/hallucination/H)
|
||||
ASSERT(ispath(H))
|
||||
if(ckey)
|
||||
add_attack_logs(null, src, "Received hallucination [H]", ATKLOG_ALL)
|
||||
return new H(get_turf(src), src)
|
||||
|
||||
/**
|
||||
* # Hallucination
|
||||
*
|
||||
* Base object for hallucinations. Contains basic behaviour to display an icon only to the target.
|
||||
*/
|
||||
/obj/effect/hallucination
|
||||
density = FALSE
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
/// Duration in deciseconds. Can also be a list with the form [lower bound, upper bound] for a random duration.
|
||||
var/duration = 15 SECONDS
|
||||
/// Hallucination icon.
|
||||
var/hallucination_icon
|
||||
/// Hallucination icon state.
|
||||
var/hallucination_icon_state
|
||||
/// Hallucination override.
|
||||
var/hallucination_override = FALSE
|
||||
/// Hallucination layer.
|
||||
var/hallucination_layer = MOB_LAYER
|
||||
/// The mob that sees this hallucination.
|
||||
var/mob/living/carbon/target = null
|
||||
/// Lazy list of images created as part of the hallucination. Cleared on destruction.
|
||||
var/list/image/images = null
|
||||
|
||||
/obj/effect/hallucination/Initialize(mapload, mob/living/carbon/target)
|
||||
. = ..()
|
||||
src.target = target
|
||||
if(hallucination_icon && hallucination_icon_state)
|
||||
var/image/I = image(hallucination_icon, hallucination_override ? src : get_turf(src), hallucination_icon_state)
|
||||
I.override = hallucination_override
|
||||
I.layer = hallucination_layer
|
||||
add_icon(I)
|
||||
// Lifetime
|
||||
if(islist(duration))
|
||||
duration = rand(duration[1], duration[2])
|
||||
QDEL_IN(src, duration)
|
||||
|
||||
/obj/effect/hallucination/Destroy()
|
||||
clear_icons()
|
||||
return ..()
|
||||
|
||||
/obj/effect/hallucination/examine(mob/user, infix, suffix)
|
||||
if(user != target)
|
||||
return list()
|
||||
|
||||
// Overriding to not include call to [/proc/bicon] as it lags the client due to invalid image.
|
||||
. = list(
|
||||
"That's \a [name].",
|
||||
"<span class='whisper'>Something seems odd about this...</span>"
|
||||
)
|
||||
|
||||
/obj/effect/hallucination/singularity_pull()
|
||||
return
|
||||
|
||||
/obj/effect/hallucination/singularity_act()
|
||||
return
|
||||
|
||||
/**
|
||||
* Adds an image to the hallucination. Cleared on destruction.
|
||||
*
|
||||
* Arguments:
|
||||
* * I - The image to add.
|
||||
*/
|
||||
/obj/effect/hallucination/proc/add_icon(image/I)
|
||||
LAZYADD(images, I)
|
||||
target?.client?.images |= I
|
||||
|
||||
/**
|
||||
* Clears an image from the hallucination.
|
||||
*
|
||||
* Arguments:
|
||||
* * I - The image to clear.
|
||||
*/
|
||||
/obj/effect/hallucination/proc/clear_icon(image/I)
|
||||
LAZYREMOVE(images, I)
|
||||
target?.client?.images -= I
|
||||
qdel(I)
|
||||
|
||||
/**
|
||||
* Clears an image from the hallucination after a delay.
|
||||
*
|
||||
* Arguments:
|
||||
* * I - The image to clear.
|
||||
* * delay - Delay in deciseconds.
|
||||
*/
|
||||
/obj/effect/hallucination/proc/clear_icon_in(image/I, delay)
|
||||
addtimer(CALLBACK(src, .proc/clear_icon, I), delay)
|
||||
|
||||
/**
|
||||
* Clears all images from the hallucination.
|
||||
*/
|
||||
/obj/effect/hallucination/proc/clear_icons()
|
||||
if(!images)
|
||||
return
|
||||
target?.client?.images -= images
|
||||
QDEL_LIST(images)
|
||||
|
||||
/**
|
||||
* Plays a sound to the target only.
|
||||
*
|
||||
* Arguments:
|
||||
* * time - Deciseconds before the sound plays.
|
||||
* * source - The turf to play the sound from. Optional.
|
||||
* * snd - The sound file to play.
|
||||
* * volume - The sound volume.
|
||||
* * vary - Whether to randomize the sound's pitch.
|
||||
* * frequency - The sound's pitch.
|
||||
*/
|
||||
/obj/effect/hallucination/proc/play_sound_in(time, turf/source = null, snd, volume, vary, frequency)
|
||||
ASSERT(time >= 0)
|
||||
if(time == 0) // whatever
|
||||
target?.playsound_local(source, snd, volume, vary, frequency)
|
||||
return
|
||||
addtimer(CALLBACK(target, /mob/.proc/playsound_local, source, snd, volume, vary, frequency), time)
|
||||
|
||||
#undef HALLUCINATE_COOLDOWN_MIN
|
||||
#undef HALLUCINATE_COOLDOWN_MAX
|
||||
#undef HALLUCINATE_COOLDOWN_FACTOR
|
||||
#undef HALLUCINATE_CHANCE
|
||||
#undef HALLUCINATE_MINOR_WEIGHT
|
||||
#undef HALLUCINATE_MODERATE_WEIGHT
|
||||
#undef HALLUCINATE_MAJOR_WEIGHT
|
||||
@@ -68,7 +68,6 @@
|
||||
var/mob/living/carbon/C = loc
|
||||
if(istype(C) && prob(2)) //cursed by bubblegum
|
||||
if(prob(15))
|
||||
new /obj/effect/hallucination/oh_yeah(get_turf(C), C)
|
||||
to_chat(C, "<span class='colossus'><b>[pick("I AM IMMORTAL.","I SHALL TAKE BACK WHAT'S MINE.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","DEATH CANNOT HOLD ME.")]</b></span>")
|
||||
else
|
||||
to_chat(C, "<span class='warning'>[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]</span>")
|
||||
|
||||
@@ -27,4 +27,7 @@
|
||||
var/dreaming = 0 //How many dream images we have left to send
|
||||
var/nightmare = 0
|
||||
|
||||
/// The world.time after which the mob can hallucinate again.
|
||||
var/next_hallucination = 0
|
||||
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
|
||||
@@ -746,12 +746,12 @@
|
||||
if(healths)
|
||||
var/health_amount = get_perceived_trauma()
|
||||
if(..(health_amount)) //not dead
|
||||
switch(hal_screwyhud)
|
||||
if(SCREWYHUD_CRIT)
|
||||
switch(health_hud_override)
|
||||
if(HEALTH_HUD_OVERRIDE_CRIT)
|
||||
healths.icon_state = "health6"
|
||||
if(SCREWYHUD_DEAD)
|
||||
if(HEALTH_HUD_OVERRIDE_DEAD)
|
||||
healths.icon_state = "health7"
|
||||
if(SCREWYHUD_HEALTHY)
|
||||
if(HEALTH_HUD_OVERRIDE_HEALTHY)
|
||||
healths.icon_state = "health0"
|
||||
|
||||
if(healthdoll)
|
||||
|
||||
@@ -315,8 +315,7 @@
|
||||
AdjustJitter(-restingpwr)
|
||||
|
||||
if(hallucination)
|
||||
spawn handle_hallucinations()
|
||||
|
||||
handle_hallucinations()
|
||||
AdjustHallucinate(-2)
|
||||
|
||||
// Keep SSD people asleep
|
||||
@@ -347,7 +346,7 @@
|
||||
if(comfort > 1 && prob(3))//You don't heal if you're just sleeping on the floor without a blanket.
|
||||
adjustBruteLoss(-1 * comfort, FALSE)
|
||||
adjustFireLoss(-1 * comfort)
|
||||
if(prob(10) && health && hal_screwyhud != SCREWYHUD_CRIT)
|
||||
if(prob(10) && health && health_hud_override != HEALTH_HUD_OVERRIDE_CRIT)
|
||||
emote("snore")
|
||||
|
||||
return sleeping
|
||||
|
||||
@@ -598,7 +598,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
|
||||
|
||||
if(next_move >= world.time)
|
||||
return
|
||||
if(!isturf(loc) || istype(A, /obj/effect/temp_visual/point))
|
||||
if(!isturf(loc) || istype(A, /obj/effect/temp_visual/point) || istype(A, /obj/effect/hallucination))
|
||||
return FALSE
|
||||
|
||||
var/tile = get_turf(A)
|
||||
@@ -1327,6 +1327,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
|
||||
.["Toggle Build Mode"] = "?_src_=vars;build_mode=[UID()]"
|
||||
|
||||
.["Make 2spooky"] = "?_src_=vars;make_skeleton=[UID()]"
|
||||
.["Hallucinate"] = "?_src_=vars;hallucinate=[UID()]"
|
||||
|
||||
.["Assume Direct Control"] = "?_src_=vars;direct_control=[UID()]"
|
||||
.["Offer Control to Ghosts"] = "?_src_=vars;offer_control=[UID()]"
|
||||
|
||||
@@ -199,5 +199,7 @@
|
||||
|
||||
var/obj/effect/proc_holder/ranged_ability //Any ranged ability the mob has, as a click override
|
||||
|
||||
/// Overrides the health HUD element state if set.
|
||||
var/health_hud_override = HEALTH_HUD_OVERRIDE_NONE
|
||||
/// The location our runechat message should appear. Should be src by default.
|
||||
var/atom/runechat_msg_location
|
||||
|
||||
@@ -252,7 +252,7 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby"))
|
||||
else if((findtext(message, GLOB.hallucinate_words)))
|
||||
for(var/V in listeners)
|
||||
var/mob/living/L = V
|
||||
new /obj/effect/hallucination/delusion(get_turf(L),L,duration=150 * power_multiplier,skip_nearby=0)
|
||||
new /obj/effect/hallucination/delusion(get_turf(L), L)
|
||||
next_command = world.time + cooldown_meme
|
||||
|
||||
//WAKE UP
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 116 KiB |
+5
-1
@@ -1521,7 +1521,6 @@
|
||||
#include "code\modules\fish\fish_types.dm"
|
||||
#include "code\modules\fish\fishtank.dm"
|
||||
#include "code\modules\flufftext\Dreaming.dm"
|
||||
#include "code\modules\flufftext\Hallucination.dm"
|
||||
#include "code\modules\flufftext\TextFilters.dm"
|
||||
#include "code\modules\food_and_drinks\food.dm"
|
||||
#include "code\modules\food_and_drinks\drinks\drinks.dm"
|
||||
@@ -1574,6 +1573,11 @@
|
||||
#include "code\modules\games\52card.dm"
|
||||
#include "code\modules\games\cards.dm"
|
||||
#include "code\modules\games\tarot.dm"
|
||||
#include "code\modules\hallucinations\hallucinations.dm"
|
||||
#include "code\modules\hallucinations\effects\common.dm"
|
||||
#include "code\modules\hallucinations\effects\major.dm"
|
||||
#include "code\modules\hallucinations\effects\minor.dm"
|
||||
#include "code\modules\hallucinations\effects\moderate.dm"
|
||||
#include "code\modules\holiday\christmas.dm"
|
||||
#include "code\modules\holiday\holiday.dm"
|
||||
#include "code\modules\hydroponics\biogenerator.dm"
|
||||
|
||||
Reference in New Issue
Block a user