mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-30 03:22:41 +00:00
## About The Pull Request Finishes #66471 At burden level nine (or through a deadly genetic breakdown), you now turn into a psyker. This splits your skull in half and transforms it into a weird fleshy mass. You become blind, but your skull is perfectly suited for sending out psychic waves. You get potent psy abilities. First one is brainwave echolocation, inspired by Gehennites (but not as laggy). Secondly, you get the ability of Psychic Walls, which act similarly to wizard ones, but last shorter, and cause projectiles to ricochet off them. Thirdly, you get a projectile boost ability, this temporarily lets you fire guns twice as fast and gives them homing to the target you clicked. Lastly, you get the ability of psychic projection. This terrifies the victim, fucking their screen up and causing them to rapidfire any gun they have in their general direction (they'll probably miss you) With most of the abilities being based around guns, a burden level nine chaplain now gets a new rite, Transmogrify. This lets them turn their null rod into a 5-shot 18 damage .77 revolver. The revolver possesses a weaker version of antimagic (protects against mind and unholy spells, but not wizard/cult ones). It is reloaded by a prayer action (can also only be performed by a max burdened person). General Video: https://streamable.com/w3kkrk Psychic Projection Video: https://streamable.com/4ibu7o  ## Why It's Good For The Game Rewards the burdened chaplain with some pretty cool stuff for going through hell like losing half his limbs, cause the current psychics dont cut it as much as probably necessary, adds echolocation which can be used for neat stuff in the future (bat organs for DNA infuser for example). ## Changelog 🆑 Fikou, sprites from Halcyon, some old code from Basilman and Armhulen. refactor: Honorbound and Burdened mutations are brain traumas now. add: Psykers. Become a psyker through the path of the burdened, or a genetic breakdown. add: Echolocation Component. /🆑 Co-authored-by: tralezab <spamqetuo2@gmail.com> Co-authored-by: tralezab <40974010+tralezab@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
184 lines
7.2 KiB
Plaintext
184 lines
7.2 KiB
Plaintext
/datum/component/echolocation
|
|
/// Radius of our view.
|
|
var/echo_range = 4
|
|
/// Time between echolocations.
|
|
var/cooldown_time = 2 SECONDS
|
|
/// Time for the image to start fading out.
|
|
var/image_expiry_time = 1.5 SECONDS
|
|
/// Time for the image to fade in.
|
|
var/fade_in_time = 0.5 SECONDS
|
|
/// Time for the image to fade out and delete itself.
|
|
var/fade_out_time = 0.5 SECONDS
|
|
/// Are images static? If yes, spawns them on the turf and makes them not change location. Otherwise they change location and pixel shift with the original.
|
|
var/images_are_static = TRUE
|
|
/// With mobs that have this echo group in their echolocation receiver trait, we share echo images.
|
|
var/echo_group = null
|
|
/// Ref of the client color we give to the echolocator.
|
|
var/client_color
|
|
/// Associative list of world.time when created to a list of the images.
|
|
var/list/images = list()
|
|
/// Associative list of world.time when created to a list of receivers.
|
|
var/list/receivers = list()
|
|
/// All the saved appearances, keyed by icon-icon_state.
|
|
var/static/list/saved_appearances = list()
|
|
/// Typecache of all the allowed paths to render.
|
|
var/static/list/allowed_paths
|
|
/// Typecache of turfs that are dangerous, to give them a special icon.
|
|
var/static/list/danger_turfs
|
|
/// A matrix that turns everything except #ffffff into pure blackness, used for our images (the outlines are #ffffff).
|
|
var/static/list/black_white_matrix = list(85, 85, 85, 0, 85, 85, 85, 0, 85, 85, 85, 0, 0, 0, 0, 1, -254, -254, -254, 0)
|
|
/// Cooldown for the echolocation.
|
|
COOLDOWN_DECLARE(cooldown_last)
|
|
|
|
/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, echo_group, echo_icon, color_path)
|
|
. = ..()
|
|
var/mob/living/echolocator = parent
|
|
if(!istype(echolocator))
|
|
return COMPONENT_INCOMPATIBLE
|
|
if(!danger_turfs)
|
|
danger_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/chasm, /turf/open/lava))
|
|
if(!allowed_paths)
|
|
allowed_paths = typecacheof(list(/turf/closed, /obj, /mob/living)) + danger_turfs
|
|
if(!isnull(echo_range))
|
|
src.echo_range = echo_range
|
|
if(!isnull(cooldown_time))
|
|
src.cooldown_time = cooldown_time
|
|
if(!isnull(image_expiry_time))
|
|
src.image_expiry_time = image_expiry_time
|
|
if(!isnull(fade_in_time))
|
|
src.fade_in_time = fade_in_time
|
|
if(!isnull(fade_out_time))
|
|
src.fade_out_time = fade_out_time
|
|
if(!isnull(images_are_static))
|
|
src.images_are_static = images_are_static
|
|
if(!isnull(echo_group))
|
|
src.echo_group = echo_group
|
|
if(!isnull(color_path))
|
|
client_color = echolocator.add_client_colour(color_path)
|
|
ADD_TRAIT(echolocator, TRAIT_ECHOLOCATION_RECEIVER, echo_group || REF(src))
|
|
echolocator.become_blind(ECHOLOCATION_TRAIT)
|
|
echolocator.overlay_fullscreen("echo", /atom/movable/screen/fullscreen/echo, echo_icon)
|
|
START_PROCESSING(SSfastprocess, src)
|
|
|
|
/datum/component/echolocation/Destroy(force, silent)
|
|
STOP_PROCESSING(SSfastprocess, src)
|
|
var/mob/living/echolocator = parent
|
|
QDEL_NULL(client_color)
|
|
REMOVE_TRAIT(echolocator, TRAIT_ECHOLOCATION_RECEIVER, echo_group || REF(src))
|
|
echolocator.cure_blind(ECHOLOCATION_TRAIT)
|
|
echolocator.clear_fullscreen("echo")
|
|
for(var/timeframe in images)
|
|
delete_images(timeframe)
|
|
return ..()
|
|
|
|
/datum/component/echolocation/process()
|
|
var/mob/living/echolocator = parent
|
|
if(echolocator.stat == DEAD)
|
|
return
|
|
echolocate()
|
|
|
|
/datum/component/echolocation/proc/echolocate()
|
|
if(!COOLDOWN_FINISHED(src, cooldown_last))
|
|
return
|
|
COOLDOWN_START(src, cooldown_last, cooldown_time)
|
|
var/mob/living/echolocator = parent
|
|
var/list/filtered = list()
|
|
var/list/seen = dview(echo_range, echolocator.loc)
|
|
for(var/atom/seen_atom as anything in seen)
|
|
if(seen_atom.invisibility > echolocator.see_invisible || !seen_atom.alpha)
|
|
continue
|
|
if(allowed_paths[seen_atom.type])
|
|
filtered += seen_atom
|
|
if(!length(filtered))
|
|
return
|
|
var/current_time = "[world.time]"
|
|
images[current_time] = list()
|
|
receivers[current_time] = list()
|
|
for(var/mob/living/viewer in filtered)
|
|
if(HAS_TRAIT_FROM(viewer, TRAIT_ECHOLOCATION_RECEIVER, echo_group))
|
|
receivers[current_time] += viewer
|
|
for(var/atom/filtered_atom as anything in filtered)
|
|
show_image(saved_appearances["[filtered_atom.icon]-[filtered_atom.icon_state]"] || generate_appearance(filtered_atom), filtered_atom, current_time)
|
|
addtimer(CALLBACK(src, .proc/fade_images, current_time), image_expiry_time)
|
|
|
|
/datum/component/echolocation/proc/show_image(image/input_appearance, atom/input, current_time)
|
|
var/image/final_image = image(input_appearance)
|
|
final_image.layer += EFFECTS_LAYER
|
|
final_image.plane = FULLSCREEN_PLANE
|
|
final_image.loc = images_are_static ? get_turf(input) : input
|
|
final_image.dir = input.dir
|
|
final_image.alpha = 0
|
|
if(images_are_static)
|
|
final_image.pixel_x = input.pixel_x
|
|
final_image.pixel_y = input.pixel_y
|
|
images[current_time] += final_image
|
|
for(var/mob/living/echolocate_receiver as anything in receivers[current_time])
|
|
if(echolocate_receiver == input)
|
|
continue
|
|
if(echolocate_receiver.client)
|
|
echolocate_receiver.client.images += final_image
|
|
animate(final_image, alpha = 255, time = fade_in_time)
|
|
|
|
/datum/component/echolocation/proc/generate_appearance(atom/input)
|
|
var/use_outline = TRUE
|
|
var/mutable_appearance/copied_appearance = new /mutable_appearance()
|
|
copied_appearance.appearance = input
|
|
if(istype(input, /obj/machinery/door/airlock)) //i hate you
|
|
copied_appearance.icon = 'icons/obj/doors/airlocks/station/public.dmi'
|
|
copied_appearance.icon_state = "closed"
|
|
else if(danger_turfs[input.type])
|
|
copied_appearance.icon = 'icons/turf/floors.dmi'
|
|
copied_appearance.icon_state = "danger"
|
|
use_outline = FALSE
|
|
copied_appearance.color = black_white_matrix
|
|
if(use_outline)
|
|
copied_appearance.filters += outline_filter(size = 1, color = COLOR_WHITE)
|
|
if(!images_are_static)
|
|
copied_appearance.pixel_x = 0
|
|
copied_appearance.pixel_y = 0
|
|
copied_appearance.transform = matrix()
|
|
if(!iscarbon(input)) //wacky overlay people get generated everytime
|
|
saved_appearances["[input.icon]-[input.icon_state]"] = copied_appearance
|
|
return copied_appearance
|
|
|
|
/datum/component/echolocation/proc/fade_images(from_when)
|
|
for(var/image_echo in images[from_when])
|
|
animate(image_echo, alpha = 0, time = fade_out_time)
|
|
addtimer(CALLBACK(src, .proc/delete_images, from_when), fade_out_time)
|
|
|
|
/datum/component/echolocation/proc/delete_images(from_when)
|
|
for(var/mob/living/echolocate_receiver as anything in receivers[from_when])
|
|
if(!echolocate_receiver.client)
|
|
continue
|
|
for(var/image_echo in images[from_when])
|
|
echolocate_receiver.client.images -= image_echo
|
|
images -= from_when
|
|
receivers -= from_when
|
|
|
|
/atom/movable/screen/fullscreen/echo
|
|
icon_state = "echo"
|
|
layer = ECHO_LAYER
|
|
show_when_dead = TRUE
|
|
|
|
/atom/movable/screen/fullscreen/echo/Initialize(mapload)
|
|
. = ..()
|
|
particles = new /particles/echo()
|
|
|
|
/atom/movable/screen/fullscreen/echo/Destroy()
|
|
QDEL_NULL(particles)
|
|
return ..()
|
|
|
|
/particles/echo
|
|
icon = 'icons/effects/particles/echo.dmi'
|
|
icon_state = list("echo1" = 1, "echo2" = 1, "echo3" = 2)
|
|
width = 480
|
|
height = 480
|
|
count = 1000
|
|
spawning = 0.5
|
|
lifespan = 2 SECONDS
|
|
fade = 1 SECONDS
|
|
gravity = list(0, -0.1)
|
|
position = generator(GEN_BOX, list(-240, -240), list(240, 240), NORMAL_RAND)
|
|
drift = generator(GEN_VECTOR, list(-0.1, 0), list(0.1, 0))
|
|
rotation = generator(GEN_NUM, 0, 360, NORMAL_RAND)
|