[MIRROR] Hallucination Refactor (#10879)

Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-05-16 00:22:42 -07:00
committed by GitHub
parent b6c2f57c69
commit 2f4ae557ee
12 changed files with 689 additions and 612 deletions

View File

@@ -0,0 +1,8 @@
#define HALLUCINATION_THRESHOLD 20
#define XENOCHIFERAL_THRESHOLD 10
#define HUD_HALLUCINATION_NONE 0
#define HUD_HALLUCINATION_CRIT 1
#define HUD_HALLUCINATION_TOXIN 2
#define HUD_HALLUCINATION_OXY 3
#define HUD_HALLUCINATION_ONFIRE 4

View File

@@ -43,9 +43,11 @@ SUBSYSTEM_DEF(motiontracker)
if(Rt && At && count)
while(count-- > 0)
// Place at root turf offset from signal responder's turf using px offsets. So it will show up over visblocking.
var/image/motion_echo/E = new /image/motion_echo('icons/effects/effects.dmi', Rt, "motion_echo", OBFUSCATION_LAYER, SOUTH)
var/image/client_only/motion_echo/E = new /image/client_only/motion_echo('icons/effects/effects.dmi', Rt, "motion_echo", OBFUSCATION_LAYER, SOUTH)
E.place_from_root(At)
for(var/datum/weakref/C in clients)
for(var/datum/weakref/CW in clients)
var/client/C = CW?.resolve()
if(C)
E.append_client(C)
currentrun.Remove(key)
expended_echos[key] = data

View File

@@ -237,196 +237,10 @@
if(!owner.stat)
owner.emote("twitch")
/datum/component/xenochimera/proc/handle_feral()
if(owner.handling_hal) return
owner.handling_hal = 1
if(owner.client && feral >= 10) // largely a copy of handle_hallucinations() without the fake attackers. Unlike hallucinations, only fires once - if they're still feral they'll get hit again anyway.
spawn(rand(200,500)/(feral/10))
if(!feral) return //just to avoid fuckery in the event that they un-feral in the time it takes for the spawn to proc
var/halpick = rand(1,100)
switch(halpick)
if(0 to 15) //15% chance
//Screwy HUD
//to_chat(src, "Screwy HUD")
owner.hal_screwyhud = pick(1,2,3,3,4,4)
spawn(rand(100,250))
owner.hal_screwyhud = 0
if(16 to 25) //10% chance
//Strange items
//to_chat(src, "Traitor Items")
if(!owner.halitem)
owner.halitem = new
var/list/slots_free = list(ui_lhand,ui_rhand)
if(owner.l_hand) slots_free -= ui_lhand
if(owner.r_hand) slots_free -= ui_rhand
if(!owner.belt) slots_free += ui_belt
if(!owner.l_store) slots_free += ui_storage1
if(!owner.r_store) slots_free += ui_storage2
if(slots_free.len)
owner.halitem.screen_loc = pick(slots_free)
owner.halitem.layer = 50
switch(rand(1,6))
if(1) //revolver
owner.halitem.icon = 'icons/obj/gun.dmi'
owner.halitem.icon_state = "revolver"
owner.halitem.name = "Revolver"
if(2) //c4
owner.halitem.icon = 'icons/obj/assemblies.dmi'
owner.halitem.icon_state = "plastic-explosive0"
owner.halitem.name = "Mysterious Package"
if(prob(25))
owner.halitem.icon_state = "c4small_1"
if(3) //sword
owner.halitem.icon = 'icons/obj/weapons.dmi'
owner.halitem.icon_state = "sword1"
owner.halitem.name = "Sword"
if(4) //stun baton
owner.halitem.icon = 'icons/obj/weapons.dmi'
owner.halitem.icon_state = "stunbaton"
owner.halitem.name = "Stun Baton"
if(5) //emag
owner.halitem.icon = 'icons/obj/card.dmi'
owner.halitem.icon_state = "emag"
owner.halitem.name = "Cryptographic Sequencer"
if(6) //flashbang
owner.halitem.icon = 'icons/obj/grenade.dmi'
owner.halitem.icon_state = "flashbang1"
owner.halitem.name = "Flashbang"
if(owner.client) owner.client.screen += owner.halitem
spawn(rand(100,250))
if(owner.client)
owner.client.screen -= owner.halitem
owner.halitem = null
if(26 to 35) //10% chance
//Flashes of danger
//to_chat(src, "Danger Flash")
if(!owner.halimage)
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(owner,world.view))
possible_points += F
if(possible_points.len)
var/turf/simulated/floor/target = pick(possible_points)
switch(rand(1,3))
if(1)
//to_chat(src, "Space")
owner.halimage = image('icons/turf/space.dmi',target,"[rand(1,25)]",TURF_LAYER)
if(2)
//to_chat(src, "Fire")
owner.halimage = image('icons/effects/fire.dmi',target,"1",TURF_LAYER)
if(3)
//to_chat(src, "C4")
owner.halimage = image('icons/obj/assemblies.dmi',target,"plastic-explosive2",OBJ_LAYER+0.01)
if(owner.client) owner.client.images += owner.halimage
spawn(rand(10,50)) //Only seen for a brief moment.
if(owner.client) owner.client.images -= owner.halimage
owner.halimage = null
if(36 to 55) //20% chance
//Strange audio
//to_chat(src, "Strange Audio")
switch(rand(1,12))
if(1) owner << 'sound/machines/door/old_airlock.ogg'
if(2)
if(prob(50))owner << 'sound/effects/Explosion1.ogg'
else owner << 'sound/effects/Explosion2.ogg'
if(3) owner << 'sound/effects/explosionfar.ogg'
if(4) owner << 'sound/effects/Glassbr1.ogg'
if(5) owner << 'sound/effects/Glassbr2.ogg'
if(6) owner << 'sound/effects/Glassbr3.ogg'
if(7) owner << 'sound/machines/twobeep.ogg'
if(8) owner << 'sound/machines/door/windowdoor.ogg'
if(9)
//To make it more realistic, I added two gunshots (enough to kill)
owner << 'sound/weapons/Gunshot1.ogg'
spawn(rand(10,30))
owner << 'sound/weapons/Gunshot2.ogg'
if(10) owner << 'sound/weapons/smash.ogg'
if(11)
//Same as above, but with tasers.
owner << 'sound/weapons/Taser.ogg'
spawn(rand(10,30))
owner << 'sound/weapons/Taser.ogg'
//Rare audio
if(12)
//These sounds are (mostly) taken from Hidden: Source
var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/Heart Beat.ogg', 'sound/effects/screech.ogg',\
'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
owner << pick(creepyasssounds)
if(56 to 60) //5% chance
//Flashes of danger
//to_chat(src, "Danger Flash")
if(!owner.halbody)
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(owner,world.view))
possible_points += F
if(possible_points.len)
var/turf/simulated/floor/target = pick(possible_points)
switch(rand(1,4))
if(1)
owner.halbody = image('icons/mob/human.dmi',target,"husk_l",TURF_LAYER)
if(2,3)
owner.halbody = image('icons/mob/human.dmi',target,"husk_s",TURF_LAYER)
if(4)
owner.halbody = image('icons/mob/alien.dmi',target,"alienother",TURF_LAYER)
// if(5)
// halbody = image('xcomalien.dmi',target,"chryssalid",TURF_LAYER)
if(owner.client) owner.client.images += owner.halbody
spawn(rand(50,80)) //Only seen for a brief moment.
if(owner.client) owner.client.images -= owner.halbody
owner.halbody = null
if(61 to 85) //25% chance
//food
if(!owner.halbody)
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(owner,world.view))
possible_points += F
if(possible_points.len)
var/turf/simulated/floor/target = pick(possible_points)
switch(rand(1,10))
if(1)
owner.halbody = image('icons/mob/animal.dmi',target,"cow",TURF_LAYER)
if(2)
owner.halbody = image('icons/mob/animal.dmi',target,"chicken",TURF_LAYER)
if(3)
owner.halbody = image('icons/obj/food.dmi',target,"bigbiteburger",TURF_LAYER)
if(4)
owner.halbody = image('icons/obj/food.dmi',target,"meatbreadslice",TURF_LAYER)
if(5)
owner.halbody = image('icons/obj/food.dmi',target,"sausage",TURF_LAYER)
if(6)
owner.halbody = image('icons/obj/food.dmi',target,"bearmeat",TURF_LAYER)
if(7)
owner.halbody = image('icons/obj/food.dmi',target,"fishfillet",TURF_LAYER)
if(8)
owner.halbody = image('icons/obj/food.dmi',target,"meat",TURF_LAYER)
if(9)
owner.halbody = image('icons/obj/food.dmi',target,"meatstake",TURF_LAYER)
if(10)
owner.halbody = image('icons/obj/food.dmi',target,"monkeysdelight",TURF_LAYER)
if(owner.client) owner.client.images += owner.halbody
spawn(rand(50,80)) //Only seen for a brief moment.
if(owner.client) owner.client.images -= owner.halbody
owner.halbody = null
if(86 to 100) //15% chance
//hear voices. Could make the voice pick from nearby creatures, but nearby creatures make feral hallucinations rare so don't bother.
var/list/hiddenspeakers = list("Someone distant", "A voice nearby","A familiar voice", "An echoing voice", "A cautious voice", "A scared voice", "Someone around the corner", "Someone", "Something", "Something scary", "An urgent voice", "An angry voice")
var/list/speakerverbs = list("calls out", "yells", "screams", "exclaims", "shrieks", "shouts", "hisses", "snarls")
var/list/spookyphrases = list("It's over here!","Stop it!", "Hunt it down!", "Get it!", "Quick, over here!", "Anyone there?", "Who's there?", "Catch that thing!", "Stop it! Kill it!", "Anyone there?", "Where is it?", "Find it!", "There it is!")
to_chat(owner, span_game(span_say(span_name(pick(hiddenspeakers)) + " [pick(speakerverbs)], \"[pick(spookyphrases)]\"")))
owner.handling_hal = 0
if(QDELETED(owner) || owner.get_hallucination_component() || !owner.client || feral < XENOCHIFERAL_THRESHOLD)
return
owner.LoadComponent(/datum/component/hallucinations/xenochimera)
/obj/screen/xenochimera
icon = 'icons/mob/chimerahud.dmi'

View File

@@ -674,6 +674,7 @@ About the new airlock wires panel:
return
else if(user.hallucination > 50 && prob(10) && src.operating == 0)
to_chat(user, span_danger("You feel a powerful shock course through your body!"))
user.playsound_local(get_turf(user), get_sfx("sparks"), vol = 75)
user.halloss += 10
user.stunned += 10
return

View File

@@ -0,0 +1,23 @@
/*
* Sending images to clients can cause memory leaks if not handled safely.
* This is a wrapper for handling it safely. Mostly used by self deleting effects.
*/
/image/client_only
var/list/clients = list()
/image/client_only/proc/append_client(var/client/C)
C.images += src
clients.Add(WEAKREF(C))
/image/client_only/Destroy(force)
. = ..()
for(var/datum/weakref/CW in clients)
var/client/C = CW?.resolve()
if(C)
C.images -= src
// Mostly for motion echos, but someone will probably find another use for it... So parent type gets it instead!
/image/client_only/proc/place_from_root(var/turf/At)
var/rand_limit = 12
pixel_x += ((At.x - loc.x) * 32) + rand(-rand_limit,rand_limit)
pixel_y += ((At.y - loc.y) * 32) + rand(-rand_limit,rand_limit)

View File

@@ -1,27 +1,8 @@
/image/motion_echo
/image/client_only/motion_echo
plane = PLANE_FULLSCREEN
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance_flags = (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
var/list/clients = list()
/image/motion_echo/New(icon, loc, icon_state, layer, dir)
/image/client_only/motion_echo/New(icon, loc, icon_state, layer, dir)
. = ..()
QDEL_IN(src, 2 SECONDS)
/image/motion_echo/proc/place_from_root(var/turf/At)
var/rand_limit = 12
pixel_x += ((At.x - loc.x) * 32) + rand(-rand_limit,rand_limit)
pixel_y += ((At.y - loc.y) * 32) + rand(-rand_limit,rand_limit)
/image/motion_echo/proc/append_client(var/datum/weakref/C)
var/client/CW = C?.resolve()
if(CW)
CW.images += src
clients.Add(C)
/image/motion_echo/Destroy(force)
. = ..()
for(var/datum/weakref/C in clients)
var/client/CW = C?.resolve()
if(CW)
CW.images -= src

View File

@@ -8,409 +8,132 @@ Brief flashes of fire/space/bombs/c4/dangerous shit (done)
Items that are rare/traitorous/don't exist appearing in your inventory slots (done)
Strange audio (should be rare) (done)
Gunshots/explosions/opening doors/less rare audio (done)
*/
/mob/living/carbon/var
image/halimage
image/halbody
obj/halitem
hal_screwyhud = 0 //1 - critical, 2 - dead, 3 - oxygen indicator, 4 - toxin indicator
handling_hal = 0
hal_crit = 0
/datum/component/hallucinations
dupe_mode = COMPONENT_DUPE_UNIQUE // First come first serve
VAR_PRIVATE/mob/living/carbon/human/our_human = null
VAR_PRIVATE/datum/weakref/halimage
VAR_PRIVATE/datum/weakref/halbody
VAR_PRIVATE/list/halitem = list() // weakref pair of obj-key, client-value
VAR_PRIVATE/hal_crit = FALSE
VAR_PRIVATE/hal_screwyhud = HUD_HALLUCINATION_NONE
/datum/component/hallucinations/Initialize()
if(!ishuman(parent))
return COMPONENT_INCOMPATIBLE
our_human = parent
make_timer()
/datum/component/hallucinations/Destroy(force)
if(halitem.len)
remove_hallucination_item()
our_human = null
. = ..()
/datum/component/hallucinations/proc/make_timer()
PROTECTED_PROC(TRUE)
addtimer(CALLBACK(src, PROC_REF(trigger)), ((rand(20,50) SECONDS) / (min(our_human.hallucination,100)/25)), TIMER_DELETE_ME)
/datum/component/hallucinations/proc/get_fakecrit()
SHOULD_NOT_OVERRIDE(TRUE)
return hal_crit
/datum/component/hallucinations/proc/get_hud_state()
SHOULD_NOT_OVERRIDE(TRUE)
return hal_screwyhud
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Traditional hallucinations
/////////////////////////////////////////////////////////////////////////////////////////////////////
/mob/living/carbon/proc/handle_hallucinations()
if(handling_hal) return
handling_hal = 1
while(client && hallucination > 20)
sleep(rand(200,500)/(hallucination/25))
if(get_hallucination_component() || !client)
return
LoadComponent(/datum/component/hallucinations)
/mob/living/carbon/proc/get_hallucination_component()
RETURN_TYPE(/datum/component/hallucinations)
return GetComponent(/datum/component/hallucinations)
/datum/component/hallucinations/proc/trigger()
PROTECTED_PROC(TRUE)
if(QDELETED(our_human))
qdel(src)
return
if(!our_human.client)
qdel(src)
return
if(our_human.hallucination < HALLUCINATION_THRESHOLD)
qdel(src)
return
handle_hallucinating()
make_timer()
/datum/component/hallucinations/proc/handle_hallucinating()
PROTECTED_PROC(TRUE)
var/halpick = rand(1,100)
switch(halpick)
if(0 to 15)
//Screwy HUD
//to_chat(src, "Screwy HUD")
hal_screwyhud = pick(1,2,3,3,4,4)
spawn(rand(100,250))
hal_screwyhud = 0
event_hudscrew()
if(16 to 25)
//Strange items
//to_chat(src, "Traitor Items")
if(!halitem)
halitem = new
var/list/slots_free = list(ui_lhand,ui_rhand)
if(l_hand) slots_free -= ui_lhand
if(r_hand) slots_free -= ui_rhand
if(ishuman(src))
var/mob/living/carbon/human/H = src
if(!H.belt) slots_free += ui_belt
if(!H.l_store) slots_free += ui_storage1
if(!H.r_store) slots_free += ui_storage2
if(slots_free.len)
halitem.screen_loc = pick(slots_free)
halitem.hud_layerise()
switch(rand(1,6))
if(1) //revolver
halitem.icon = 'icons/obj/gun.dmi'
halitem.icon_state = "revolver"
halitem.name = "Revolver"
if(2) //c4
halitem.icon = 'icons/obj/assemblies.dmi'
halitem.icon_state = "plastic-explosive0"
halitem.name = "Mysterious Package"
if(prob(25))
halitem.icon_state = "c4small_1"
if(3) //sword
halitem.icon = 'icons/obj/weapons.dmi'
halitem.icon_state = "sword1"
halitem.name = "Sword"
if(4) //stun baton
halitem.icon = 'icons/obj/weapons.dmi'
halitem.icon_state = "stunbaton"
halitem.name = "Stun Baton"
if(5) //emag
halitem.icon = 'icons/obj/card.dmi'
halitem.icon_state = "emag"
halitem.name = "Cryptographic Sequencer"
if(6) //flashbang
halitem.icon = 'icons/obj/grenade.dmi'
halitem.icon_state = "flashbang1"
halitem.name = "Flashbang"
if(client) client.screen += halitem
spawn(rand(100,250))
if(client)
client.screen -= halitem
halitem = null
event_fake_item()
if(26 to 40)
//Flashes of danger
//to_chat(src, "Danger Flash")
if(!halimage)
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(src,world.view))
possible_points += F
if(possible_points.len)
var/turf/simulated/floor/target = pick(possible_points)
switch(rand(1,3))
if(1)
//to_chat(src, "Space")
halimage = image('icons/turf/space.dmi',target,"[rand(1,25)]",TURF_LAYER)
if(2)
//to_chat(src, "Fire")
halimage = image('icons/effects/fire.dmi',target,"1",TURF_LAYER)
if(3)
//to_chat(src, "C4")
halimage = image('icons/obj/assemblies.dmi',target,"plastic-explosive2",OBJ_LAYER+0.01)
if(client) client.images += halimage
spawn(rand(10,50)) //Only seen for a brief moment.
if(client) client.images -= halimage
halimage = null
event_flash_environmental_threats()
if(41 to 65)
//Strange audio
//to_chat(src, "Strange Audio")
switch(rand(1,12))
if(1) src << 'sound/machines/door/old_airlock.ogg'
if(2)
if(prob(50))src << 'sound/effects/Explosion1.ogg'
else src << 'sound/effects/Explosion2.ogg'
if(3) src << 'sound/effects/explosionfar.ogg'
if(4) src << 'sound/effects/Glassbr1.ogg'
if(5) src << 'sound/effects/Glassbr2.ogg'
if(6) src << 'sound/effects/Glassbr3.ogg'
if(7) src << 'sound/machines/twobeep.ogg'
if(8) src << 'sound/machines/door/windowdoor.ogg'
if(9)
//To make it more realistic, I added two gunshots (enough to kill)
src << 'sound/weapons/Gunshot1.ogg'
spawn(rand(10,30))
src << 'sound/weapons/Gunshot2.ogg'
if(10) src << 'sound/weapons/smash.ogg'
if(11)
//Same as above, but with tasers.
src << 'sound/weapons/Taser.ogg'
spawn(rand(10,30))
src << 'sound/weapons/Taser.ogg'
//Rare audio
if(12)
//These sounds are (mostly) taken from Hidden: Source
var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/Heart Beat.ogg', 'sound/effects/screech.ogg',\
'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
src << pick(creepyasssounds)
event_strange_sound()
if(66 to 70)
//Flashes of danger
//to_chat(src, "Danger Flash")
if(!halbody)
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(src,world.view))
possible_points += F
if(possible_points.len)
var/turf/simulated/floor/target = pick(possible_points)
switch(rand(1,4))
if(1)
halbody = image('icons/mob/human.dmi',target,"husk_l",TURF_LAYER)
if(2,3)
halbody = image('icons/mob/human.dmi',target,"husk_s",TURF_LAYER)
if(4)
halbody = image('icons/mob/alien.dmi',target,"alienother",TURF_LAYER)
// if(5)
// halbody = image('xcomalien.dmi',target,"chryssalid",TURF_LAYER)
if(client) client.images += halbody
spawn(rand(50,80)) //Only seen for a brief moment.
if(client) client.images -= halbody
halbody = null
if(prob(20) && our_human.nutrition < 100)
event_hunger() // Hungi, meant for xenochi but this is too funny to pass up
else
event_flash_monsters()
if(71 to 72)
//Fake death
// src.sleeping_willingly = 1
SetSleeping(20)
hal_crit = 1
hal_screwyhud = 1
spawn(rand(50,100))
// src.sleeping_willingly = 0
SetSleeping(0)
hal_crit = 0
hal_screwyhud = 0
handling_hal = 0
/*obj/machinery/proc/mockpanel(list/buttons,start_txt,end_txt,list/mid_txts)
if(!mocktxt)
mocktxt = ""
var/possible_txt = list("Launch Escape Pods","Self-Destruct Sequence","\[Swipe ID\]","De-Monkify",\
"Reticulate Splines","Plasma","Open Valve","Lockdown","Nerf Airflow","Kill Traitor","Nihilism",\
"OBJECTION!","Arrest Stephen Bowman","Engage Anti-Trenna Defenses","Increase Site Manager IQ","Retrieve Arms",\
"Play Charades","Oxygen","Inject BeAcOs","Ninja Lizards","Limit Break","Build Sentry")
if(mid_txts)
while(mid_txts.len)
var/mid_txt = pick(mid_txts)
mocktxt += mid_txt
mid_txts -= mid_txt
while(buttons.len)
var/button = pick(buttons)
var/button_txt = pick(possible_txt)
mocktxt += "<a href='byond://?src=\ref[src];[button]'>[button_txt]</a><br>"
buttons -= button
possible_txt -= button_txt
return start_txt + mocktxt + end_txt + "</TT></BODY></HTML>"
/proc/check_panel(mob/M)
if (ishuman(M) || isAI(M))
if(M.hallucination < 15)
return 1
return 0*/
/* //ChompREMOVE fake_attacker - EXTREME image qdel usage. Look at the attack_loop and updateimage procs and weep.
/obj/effect/fake_attacker
icon = null
icon_state = null
name = ""
desc = ""
density = FALSE
anchored = TRUE
opacity = 0
var/mob/living/carbon/human/my_target = null
var/weapon_name = null
var/obj/item/weap = null
var/image/stand_icon = null
var/image/currentimage = null
var/icon/base = null
var/s_tone
var/mob/living/clone = null
var/image/left
var/image/right
var/image/up
var/collapse
var/image/down
var/health = 100
/obj/effect/fake_attacker/attackby(var/obj/item/P as obj, mob/user as mob)
step_away(src,my_target,2)
for(var/mob/M in oviewers(world.view,my_target))
to_chat(M, span_bolddanger("[my_target] flails around wildly."))
my_target.show_message(span_bolddanger("[src] has been attacked by [my_target] "), 1) //Lazy.
src.health -= P.force
event_sleeping()
if(73 to 75) // If you don't want hallucination beatdowns, comment this out
event_attacker()
if(76 to 80)
event_painmessage()
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Xenochimera hallucinations
// Unlike normal hallucinations this one is triggered from handle_feral randomly.
// So it destroys itself after it triggers, freeing up space for the next run of it.
/////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/hallucinations/xenochimera/make_timer()
var/datum/component/xenochimera/XC = our_human.GetComponent(/datum/component/xenochimera)
var/F = XC ? (XC.feral / 10) : 1
addtimer(CALLBACK(src, PROC_REF(trigger)), ((rand(20,50) SECONDS) / F), TIMER_DELETE_ME)
/datum/component/hallucinations/xenochimera/trigger()
if(QDELETED(our_human))
qdel(src)
return
/obj/effect/fake_attacker/Crossed(var/mob/M, somenumber)
if(M == my_target)
step_away(src,my_target,2)
if(prob(30))
for(var/mob/O in oviewers(world.view , my_target))
to_chat(O, span_bolddanger("[my_target] stumbles around."))
/obj/effect/fake_attacker/Initialize(mapload)
. = ..()
QDEL_IN(src, 30 SECONDS)
step_away(src,my_target,2)
addtimer(CALLBACK(src, PROC_REF(attack_loop)), rand(5, 10), TIMER_DELETE_ME)
/obj/effect/fake_attacker/Destroy()
if(my_target)
my_target.hallucinations -= src
return ..()
/obj/effect/fake_attacker/proc/updateimage()
// qdel(src.currentimage)
if(src.dir == NORTH)
qdel(src.currentimage)
src.currentimage = new /image(up,src)
else if(src.dir == SOUTH)
qdel(src.currentimage)
src.currentimage = new /image(down,src)
else if(src.dir == EAST)
qdel(src.currentimage)
src.currentimage = new /image(right,src)
else if(src.dir == WEST)
qdel(src.currentimage)
src.currentimage = new /image(left,src)
my_target << currentimage
/obj/effect/fake_attacker/proc/attack_loop()
if(src.health < 0)
collapse()
addtimer(CALLBACK(src, PROC_REF(attack_loop)), rand(5, 10), TIMER_DELETE_ME)
if(!our_human.client)
qdel(src)
return
if(get_dist(src,my_target) > 1)
src.set_dir(get_dir(src,my_target))
step_towards(src,my_target)
updateimage()
else
if(prob(15))
if(weapon_name)
my_target << sound(pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg'))
my_target.show_message(span_bolddanger("[my_target] has been attacked with [weapon_name] by [src.name]!"), 1)
my_target.halloss += 8
if(prob(20)) my_target.eye_blurry += 3
if(prob(33))
if(!locate(/obj/effect/overlay) in my_target.loc)
fake_blood(my_target)
else
my_target << sound(pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg'))
my_target.show_message(span_bolddanger("[src.name] has punched [my_target]!"), 1)
my_target.halloss += 4
if(prob(33))
if(!locate(/obj/effect/overlay) in my_target.loc)
fake_blood(my_target)
if(prob(15))
step_away(src,my_target,2)
addtimer(CALLBACK(src, PROC_REF(attack_loop)), rand(5, 10), TIMER_DELETE_ME)
/obj/effect/fake_attacker/proc/collapse()
collapse = 1
updateimage()
/proc/fake_blood(var/mob/target)
var/obj/effect/overlay/O = new/obj/effect/overlay(target.loc)
O.name = "blood"
var/image/I = image('icons/effects/blood.dmi',O,"floor[rand(1,7)]",O.dir,1)
target << I
QDEL_IN(O, 30 SECONDS)
var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_magazine/s357,\
/obj/item/gun/energy/crossbow, /obj/item/melee/energy/sword,\
/obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\
/obj/item/cartridge/syndicate, /obj/item/clothing/under/chameleon,\
/obj/item/clothing/shoes/syndigaloshes, /obj/item/card/id/syndicate,\
/obj/item/clothing/mask/gas/voice, /obj/item/clothing/glasses/thermal,\
/obj/item/chameleon, /obj/item/card/emag,\
/obj/item/storage/toolbox/syndicate, /obj/item/aiModule,\
/obj/item/radio/headset/syndicate, /obj/item/plastique,\
/obj/item/powersink, /obj/item/storage/box/syndie_kit,\
/obj/item/toy/syndicateballoon, /obj/item/gun/energy/captain,\
/obj/item/hand_tele, /obj/item/rcd, /obj/item/tank/jetpack,\
/obj/item/clothing/under/rank/captain, /obj/item/aicard,\
/obj/item/clothing/shoes/magboots, /obj/item/areaeditor/blueprints, /obj/item/disk/nuclear,\
/obj/item/clothing/suit/space/void, /obj/item/tank)
/proc/fake_attack(var/mob/living/target)
var/list/possible_clones = new/list()
var/mob/living/carbon/human/clone = null
var/clone_weapon = null
for(var/mob/living/carbon/human/H in living_mob_list)
if(H.stat || H.lying)
continue
possible_clones += H
// clone = H
// break //changed the code a bit. Less randomised, but less work to do. Should be ok, world.contents aren't stored in any particular order.
if(!possible_clones.len)
return
clone = pick(possible_clones)
if(!clone)
var/datum/component/xenochimera/XC = our_human.GetComponent(/datum/component/xenochimera)
if(!XC || XC.feral < XENOCHIFERAL_THRESHOLD)
qdel(src)
return
handle_hallucinating()
QDEL_IN(src,rand(3,9)SECONDS)
//var/obj/effect/fake_attacker/F = new/obj/effect/fake_attacker(outside_range(target))
var/obj/effect/fake_attacker/F = new/obj/effect/fake_attacker(target.loc)
if(clone.l_hand)
if(!(locate(clone.l_hand) in non_fakeattack_weapons))
clone_weapon = clone.l_hand.name
F.weap = clone.l_hand
else if (clone.r_hand)
if(!(locate(clone.r_hand) in non_fakeattack_weapons))
clone_weapon = clone.r_hand.name
F.weap = clone.r_hand
F.name = clone.name
F.my_target = target
F.weapon_name = clone_weapon
target.hallucinations += F
F.left = image(clone,dir = WEST)
F.right = image(clone,dir = EAST)
F.up = image(clone,dir = NORTH)
F.down = image(clone,dir = SOUTH)
// F.base = new /icon(clone.stand_icon)
// F.currentimage = new /image(clone)
/*
F.left = new /icon(clone.stand_icon,dir=WEST)
for(var/icon/i in clone.overlays)
F.left.Blend(i)
F.up = new /icon(clone.stand_icon,dir=NORTH)
for(var/icon/i in clone.overlays)
F.up.Blend(i)
F.down = new /icon(clone.stand_icon,dir=SOUTH)
for(var/icon/i in clone.overlays)
F.down.Blend(i)
F.right = new /icon(clone.stand_icon,dir=EAST)
for(var/icon/i in clone.overlays)
F.right.Blend(i)
target << F.up
*/
F.updateimage()
*/ //ChompREMOVE End
/datum/component/hallucinations/xenochimera/handle_hallucinating()
var/halpick = rand(1,100)
switch(halpick)
if(0 to 15) //15% chance
//Screwy HUD
event_hudscrew()
if(16 to 25) //10% chance
event_fake_item()
if(26 to 35) //10% chance
event_flash_environmental_threats()
if(36 to 55) //20% chance
event_strange_sound()
if(56 to 60) //5% chance
event_flash_monsters()
if(61 to 85) //25% chance
event_hunger()
if(86 to 100) //15% chance
event_hear_voices()

View File

@@ -0,0 +1,218 @@
/obj/effect/fake_attacker // I did want to use /image/client_only but this needs to be somewhat more complex
name = ""
desc = ""
density = FALSE
anchored = TRUE
opacity = 0
VAR_PRIVATE/list/clients = list()
VAR_PRIVATE/list/image/dir_images = list()
/obj/effect/fake_attacker/process()
. = ..()
// Passive cleanup
for(var/datum/weakref/C in clients)
var/client/CW = C?.resolve()
if(isnull(CW))
clients.Remove(C)
if(!clients.len)
qdel(src)
/obj/effect/fake_attacker/set_dir(newdir)
if(!(newdir in GLOB.cardinal))
newdir &= ~(EAST|WEST) // Don't allow diagonals, prefer north/south
if(!newdir)
newdir = SOUTH
var/updatesprite = (dir != newdir)
. = ..()
if(updatesprite)
for(var/datum/weakref/C in clients)
var/client/CW = C?.resolve()
clear_images_from_client(CW)
assign_image_to_client(CW)
/obj/effect/fake_attacker/Moved(atom/old_loc, direction, forced, movetime)
. = ..()
var/turf_move = isturf(loc) && isturf(old_loc)
for(var/img in dir_images)
var/image/G = dir_images[img]
G.loc = loc
if(turf_move)
var/MT = 0.7 SECONDS
G.pixel_x = (old_loc.x - loc.x) * WORLD_ICON_SIZE
G.pixel_y = (old_loc.y - loc.y) * WORLD_ICON_SIZE
animate(G, pixel_x = 0, time = MT, flags = ANIMATION_PARALLEL)
animate(G, pixel_y = 0, time = MT, flags = ANIMATION_PARALLEL)
/obj/effect/fake_attacker/Destroy(force)
. = ..()
clear_every_clients_images()
qdel_all_images()
/obj/effect/fake_attacker/proc/create_images_from(var/atom/clone)
SHOULD_NOT_OVERRIDE(TRUE)
dir_images["[NORTH]"] = image(clone,dir = NORTH)
dir_images["[SOUTH]"] = image(clone,dir = SOUTH)
dir_images["[EAST]"] = image(clone,dir = EAST)
dir_images["[WEST]"] = image(clone,dir = WEST)
for(var/img in dir_images)
var/image/G = dir_images[img]
G.layer = clone.layer
G.plane = clone.plane
G.appearance_flags = clone.appearance_flags
G.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
G.loc = loc
/obj/effect/fake_attacker/proc/append_client(var/client/C)
SHOULD_NOT_OVERRIDE(TRUE)
clients.Add(WEAKREF(C))
assign_image_to_client(C)
/obj/effect/fake_attacker/proc/assign_image_to_client(var/client/C)
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
if(!C)
return
if(!dir_images.len)
return
var/image/I = dir_images["[dir]"]
if(I)
C.images += I
/obj/effect/fake_attacker/proc/clear_every_clients_images()
for(var/datum/weakref/C in clients)
clear_images_from_client(C?.resolve())
/obj/effect/fake_attacker/proc/clear_images_from_client(var/client/C)
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
if(!C)
return
if(!dir_images.len)
return
for(var/img in dir_images)
C.images -= dir_images[img]
/obj/effect/fake_attacker/proc/qdel_all_images()
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
for(var/img in dir_images)
qdel_null(dir_images[img])
dir_images.Cut()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Hallucination attackers with AI behaviors
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/mob/proc/create_hallucination_attacker(var/turf/T = null,var/mob/living/carbon/human/clone = null, var/forced_type = null)
SHOULD_NOT_OVERRIDE(TRUE)
if(!client)
return null
if(!clone)
// Get a randomized clone from the living mob's list, must be standing
var/list/possible_clones = new/list()
for(var/mob/living/carbon/human/H in living_mob_list)
if(H.stat || H.lying)
continue
possible_clones += H
if(!possible_clones.len)
return null
clone = pick(possible_clones)
if(!clone)
return null
if(!T)
// Get the target's turf, then make some random steps to get away from them, respecting walls
T = get_turf(clone)
var/turf/CT = T
for(var/i = 0 to 8)
var/Cdir = pick(GLOB.cardinal)
if(prob(30))
Cdir = clone.dir // Results in hallucinations somewhat being in front of you
var/turf/NT = get_step(CT,Cdir)
if(!NT.density)
CT = NT
T = CT
if(!forced_type)
// Picking from all available options
var/list/get_types = subtypesof(/obj/effect/fake_attacker/human)
forced_type = pick(get_types)
// Finally! After a thousand years I'm finally free to conquer EARTH!
return new forced_type(T,src,clone)
/obj/effect/fake_attacker/human
VAR_PROTECTED/datum/weakref/target = null
var/requires_hallucinating = TRUE // Mob will qdel if the target is not hallucinating if this is true
/obj/effect/fake_attacker/human/Initialize(mapload,var/mob/targeting_mob,var/atom/clone_appearance_from)
. = ..()
START_PROCESSING(SSobj, src)
set_target(targeting_mob)
create_images_from(clone_appearance_from)
append_client(targeting_mob.client)
name = clone_appearance_from.name
// Usually we want to face our target for maximum spooky effect
set_dir(get_dir(src,targeting_mob))
/obj/effect/fake_attacker/human/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/effect/fake_attacker/human/process()
// check if valid
var/mob/living/M = target?.resolve()
if(!M)
qdel(src)
return null
if(requires_hallucinating)
if(!ishuman(M))
qdel(src)
return null
var/mob/living/carbon/human/H = M
if(!H.hallucination)
qdel(src)
return null
return M
/obj/effect/fake_attacker/human/proc/set_target(var/mob/M)
target = WEAKREF(M)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Attacker: Performs hostile shoves and attacks
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/effect/fake_attacker/human/attacker/process()
var/mob/living/M = ..()
if(get_dist(src,M) > 1)
step_towards(src,M)
else if(prob(15))
M << sound(pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg'))
M.show_message(span_bolddanger("\The [src] has punched \the [M]!"), 1)
M.halloss += 4
if(prob(15))
step_away(src,M)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Fleeing: Runs away when you get close
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/effect/fake_attacker/human/fleeing
VAR_PRIVATE/flee = FALSE
/obj/effect/fake_attacker/human/fleeing/process()
var/mob/living/M = ..()
set_dir(get_dir(src,M))
if(get_dist(src,M) < 6 || flee)
flee = TRUE
step_away(src,M)
if(get_dist(src,M) > 10 || get_dist(src,M) < 2 || (flee && prob(10)))
target = null
qdel(src)

View File

@@ -0,0 +1,302 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Hallucination events
/////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/hallucinations/proc/event_hudscrew()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Screwy HUD
//to_chat(our_human, "Screwy HUD")
hal_screwyhud = pick(list(
HUD_HALLUCINATION_CRIT,
HUD_HALLUCINATION_TOXIN,
HUD_HALLUCINATION_OXY,
HUD_HALLUCINATION_ONFIRE
))
VARSET_IN(src, hal_screwyhud, HUD_HALLUCINATION_NONE, rand(15,35) SECONDS)
/datum/component/hallucinations/proc/event_painmessage()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Fake pain messages
//to_chat(our_human, "fake messages")
var/list/pain_message = list("The inside of your head hurts...",
"Your stomach hurts.",
"You feel sick.",
"Your upper body hurts.",
"Your head hurts.",
"Your arm hurts.",
"Your leg hurts.",
"Your lower body hurts.",
"Your foot hurts.",
"Your hand hurts.",
"The pain is excruciating!",
"Your whole body is going numb!",
"You feel like you could die any moment now!",
"Please, just end the pain!",
"You feel a sudden pain across your body.",
"You cringe as a violent pain takes over your body.",
"It feels like your body is eating itself inside out.",
"IT HURTS.",
"Your body feels as if it's trying to rip itself open..."
)
if(prob(20) && our_human.jitteriness < 50)
our_human.make_jittery(120)
to_chat(our_human, span_danger(pick(pain_message)))
/datum/component/hallucinations/proc/event_fake_item()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Strange items
//to_chat(our_human, "Traitor Items")
if(halitem.len)
return
var/list/slots_free = list(ui_lhand,ui_rhand)
if(our_human.l_hand)
slots_free -= ui_lhand
if(our_human.r_hand)
slots_free -= ui_rhand
if(!our_human.belt)
slots_free += ui_belt
if(!our_human.l_store)
slots_free += ui_storage1
if(!our_human.r_store)
slots_free += ui_storage2
if(!slots_free.len)
return
var/obj/CI = new()
CI.screen_loc = pick(slots_free)
CI.hud_layerise()
switch(rand(1,6))
if(1) //revolver
CI.icon = 'icons/obj/gun.dmi'
CI.icon_state = "revolver"
CI.name = "Revolver"
if(2) //c4
CI.icon = 'icons/obj/assemblies.dmi'
CI.icon_state = "plastic-explosive0"
CI.name = "Mysterious Package"
if(prob(25))
CI.icon_state = "c4small_1"
if(3) //sword
CI.icon = 'icons/obj/weapons.dmi'
CI.icon_state = "longsword"
CI.name = "Sword"
if(4) //stun baton
CI.icon = 'icons/obj/weapons.dmi'
CI.icon_state = "stunbaton"
CI.name = "Stun Baton"
if(5) //emag
CI.icon = 'icons/obj/card.dmi'
CI.icon_state = "emag"
CI.name = "Cryptographic Sequencer"
if(6) //flashbang
CI.icon = 'icons/obj/grenade.dmi'
CI.icon_state = "flashbang1"
CI.name = "Flashbang"
halitem[WEAKREF(CI)] = WEAKREF(our_human.client)
our_human.client.screen += CI
addtimer(CALLBACK(src, PROC_REF(remove_hallucination_item)), rand(10,25) SECONDS, TIMER_DELETE_ME)
/datum/component/hallucinations/proc/remove_hallucination_item()
// I can't manage this with /image/client_only due to screenloc, so key-value weakref pair it is! Called on both timer and destroying this component.
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
var/datum/weakref/first = halitem[1]
var/obj/itm = first?.resolve()
var/datum/weakref/CW = halitem[first]
var/client/C = CW?.resolve()
C.screen -= itm
qdel(itm)
halitem.Cut()
/datum/component/hallucinations/proc/event_strange_sound()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Strange audio
var/send_sound
switch(rand(1,12))
if(1)
send_sound = 'sound/machines/door/old_airlock.ogg'
if(2)
if(prob(50))
send_sound = 'sound/effects/Explosion1.ogg'
else
send_sound = 'sound/effects/Explosion2.ogg'
if(3)
send_sound = 'sound/effects/explosionfar.ogg'
if(4)
send_sound = 'sound/effects/Glassbr1.ogg'
if(5)
send_sound = 'sound/effects/Glassbr2.ogg'
if(6)
send_sound = 'sound/effects/Glassbr3.ogg'
if(7)
send_sound = 'sound/machines/twobeep.ogg'
if(8)
send_sound = 'sound/machines/door/windowdoor.ogg'
if(9)
//To make it more realistic, I added two gunshots (enough to kill)
send_sound = 'sound/weapons/Gunshot1.ogg'
addtimer(CALLBACK(src, PROC_REF(secondary_sound), 'sound/weapons/Gunshot2.ogg'), rand(1,3) SECONDS, TIMER_DELETE_ME)
if(10)
send_sound = 'sound/weapons/smash.ogg'
if(11)
//Same as above, but with tasers.
send_sound = 'sound/weapons/Taser.ogg'
addtimer(CALLBACK(src, PROC_REF(secondary_sound), 'sound/weapons/Taser.ogg'), rand(1,3) SECONDS, TIMER_DELETE_ME)
//Rare audio
if(12)
//These sounds are (mostly) taken from Hidden: Source
var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/Heart Beat.ogg', 'sound/effects/screech.ogg',\
'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
send_sound = pick(creepyasssounds)
our_human.playsound_local(get_turf(our_human), send_sound, vol = 75, channel = CHANNEL_AMBIENCE_FORCED)
/datum/component/hallucinations/proc/secondary_sound(var/sound_path)
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
our_human.playsound_local(get_turf(our_human), sound_path, vol = 75, channel = CHANNEL_AMBIENCE_FORCED)
/datum/component/hallucinations/proc/event_flash_environmental_threats()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Flashes of danger
//to_chat(our_human, "Danger Flash")
if(halimage?.resolve())
return
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(our_human,world.view))
possible_points += F
if(!possible_points.len)
return
var/turf/simulated/floor/target = pick(possible_points)
var/image/client_only/CI
switch(rand(1,3))
if(1)
//to_chat(our_human, "Space")
CI = new('icons/turf/space.dmi',target,"[rand(1,25)]",TURF_LAYER)
if(2)
//to_chat(our_human, "Fire")
CI = new('icons/effects/fire.dmi',target,"1",TURF_LAYER)
if(3)
//to_chat(our_human, "C4")
CI = new('icons/obj/assemblies.dmi',target,"plastic-explosive2",OBJ_LAYER+0.01)
halimage = WEAKREF(CI)
CI.append_client(our_human.client)
QDEL_IN(CI, rand(1,5) SECONDS) //Only seen for a brief moment.
/datum/component/hallucinations/proc/event_flash_monsters()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Flashes of danger
//to_chat(our_human, "Danger Flash")
if(halbody?.resolve())
return
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(our_human,world.view))
possible_points += F
if(!possible_points.len)
return
var/turf/simulated/floor/target = pick(possible_points)
var/image/client_only/CI
switch(rand(1,4))
if(1)
CI = new('icons/mob/human.dmi',target,"husk_l",TURF_LAYER)
if(2,3)
CI = new('icons/mob/human.dmi',target,"husk_s",TURF_LAYER)
if(4)
CI = new('icons/mob/alien.dmi',target,"alienother",TURF_LAYER)
// if(5)
// CI = new('xcomalien.dmi',target,"chryssalid",TURF_LAYER)
halbody = WEAKREF(CI)
CI.append_client(our_human.client)
QDEL_IN(CI, rand(5,8) SECONDS) //Only seen for a brief moment.
/datum/component/hallucinations/proc/event_sleeping()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//Fake death
//to_chat(our_human, "fake death")
our_human.SetSleeping(20)
hal_crit = TRUE
hal_screwyhud = HUD_HALLUCINATION_CRIT
addtimer(CALLBACK(src, PROC_REF(reset_hallucination_sleeping)), rand(5,10) SECONDS, TIMER_DELETE_ME)
/datum/component/hallucinations/proc/reset_hallucination_sleeping()
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
our_human.SetSleeping(0)
hal_crit = FALSE
hal_screwyhud = HUD_HALLUCINATION_NONE
/datum/component/hallucinations/proc/event_attacker()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
var/attacker = our_human.create_hallucination_attacker(forced_type = /obj/effect/fake_attacker/human/attacker) // Currently just uses the attacker type for now, remove argument to use any other subtype
if(attacker)
QDEL_IN(attacker, rand(25,30) SECONDS)
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Xenochimera feral events
/////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/component/hallucinations/proc/event_hunger()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//food
if(halbody?.resolve())
return
var/list/possible_points = list()
for(var/turf/simulated/floor/F in view(our_human,world.view))
possible_points += F
if(!possible_points.len)
return
var/turf/simulated/floor/target = pick(possible_points)
var/image/client_only/CI
switch(rand(1,10))
if(1)
CI = new('icons/mob/animal.dmi',target,"cow",TURF_LAYER)
if(2)
CI = new('icons/mob/animal.dmi',target,"chicken",TURF_LAYER)
if(3)
CI = new('icons/obj/food.dmi',target,"bigbiteburger",TURF_LAYER)
if(4)
CI = new('icons/obj/food.dmi',target,"meatbreadslice",TURF_LAYER)
if(5)
CI = new('icons/obj/food.dmi',target,"sausage",TURF_LAYER)
if(6)
CI = new('icons/obj/food.dmi',target,"bearmeat",TURF_LAYER)
if(7)
CI = new('icons/obj/food.dmi',target,"fishfillet",TURF_LAYER)
if(8)
CI = new('icons/obj/food.dmi',target,"meat",TURF_LAYER)
if(9)
CI = new('icons/obj/food.dmi',target,"meatstake",TURF_LAYER)
if(10)
CI = new('icons/obj/food.dmi',target,"monkeysdelight",TURF_LAYER)
halbody = WEAKREF(CI)
CI.append_client(our_human.client)
QDEL_IN(CI, rand(5,8) SECONDS) //Only seen for a brief moment.
/datum/component/hallucinations/proc/event_hear_voices()
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
//hear voices. Could make the voice pick from nearby creatures, but nearby creatures make feral hallucinations rare so don't bother.
var/list/hiddenspeakers = list("Someone distant", "A voice nearby","A familiar voice", "An echoing voice", "A cautious voice", "A scared voice", "Someone around the corner", "Someone", "Something", "Something scary", "An urgent voice", "An angry voice")
var/list/speakerverbs = list("calls out", "yells", "screams", "exclaims", "shrieks", "shouts", "hisses", "snarls")
var/list/spookyphrases = list("It's over here!","Stop it!", "Hunt it down!", "Get it!", "Quick, over here!", "Anyone there?", "Who's there?", "Catch that thing!", "Stop it! Kill it!", "Anyone there?", "Where is it?", "Find it!", "There it is!")
to_chat(our_human, span_game(span_say(span_name(pick(hiddenspeakers)) + " [pick(speakerverbs)], \"[pick(spookyphrases)]\"")))

View File

@@ -722,6 +722,11 @@
spawn(0) emote(pick("giggle", "laugh"))
breath.adjust_gas(GAS_N2O, -breath.gas[GAS_N2O]/6, update = 0) //update after
if(get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_OXY)
throw_alert("oxy", /obj/screen/alert/not_enough_atmos)
else if(get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_TOXIN)
throw_alert("tox_in_air", /obj/screen/alert/tox_in_air)
// Were we able to breathe?
if (failed_inhale || failed_exhale)
failed_last_breath = 1
@@ -1249,20 +1254,15 @@
Paralyse(3)
if(hallucination)
if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) )
if(!handling_hal)
spawn handle_hallucinations() //The not boring kind!
if(hallucination >= HALLUCINATION_THRESHOLD && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) )
handle_hallucinations()
/* Stop spinning the view, it breaks too much.
if(client && prob(5))
client.dir = pick(2,4,8)
spawn(rand(20,50))
client.dir = 1
*/
hallucination = max(0, hallucination - 2)
else
for(var/atom/a in hallucinations)
qdel(a)
//Brain damage from Oxyloss
if(should_have_organ(O_BRAIN))
@@ -1326,7 +1326,7 @@
//Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar.
if(client || sleeping > 3)
handle_sleeping()
if( prob(2) && health && !hal_crit && client )
if( prob(2) && health && !get_hallucination_component()?.get_fakecrit() && client )
spawn(0)
emote("snore")
//CONSCIOUS
@@ -1554,10 +1554,12 @@
health_images += E.get_damage_hud_image(limb_trauma_val)
// Apply a fire overlay if we're burning.
if(on_fire)
if(on_fire || get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_ONFIRE)
health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]")
// Show a general pain/crit indicator if needed.
if(get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_CRIT)
trauma_val = 2
if(trauma_val)
if(!(species.flags & NO_PAIN))
if(trauma_val > 0.7)

View File

@@ -22,7 +22,6 @@
var/max_nutrition = MAX_NUTRITION
var/hallucination = 0 //Directly affects how long a mob will hallucinate for
var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm
var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out.
var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN

View File

@@ -76,6 +76,7 @@
#include "code\__defines\fonts.dm"
#include "code\__defines\footsteps.dm"
#include "code\__defines\gamemode.dm"
#include "code\__defines\hallucination.dm"
#include "code\__defines\holder.dm"
#include "code\__defines\holomap.dm"
#include "code\__defines\hoses.dm"
@@ -1339,6 +1340,7 @@
#include "code\game\objects\effects\blockers_yw.dm"
#include "code\game\objects\effects\bluespace_spawner_ch.dm"
#include "code\game\objects\effects\bump_teleporter.dm"
#include "code\game\objects\effects\client_only_image.dm"
#include "code\game\objects\effects\confetti_vr.dm"
#include "code\game\objects\effects\effect_system.dm"
#include "code\game\objects\effects\effect_system_ch.dm"
@@ -2707,7 +2709,9 @@
#include "code\modules\fishing\fishing_rod.dm"
#include "code\modules\fishing\fishing_rod_vr.dm"
#include "code\modules\fishing\fishing_vr.dm"
#include "code\modules\flufftext\fake_attacker.dm"
#include "code\modules\flufftext\Hallucination.dm"
#include "code\modules\flufftext\hallucination_events.dm"
#include "code\modules\flufftext\look_up.dm"
#include "code\modules\flufftext\TextFilters.dm"
#include "code\modules\food\food.dm"