mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 11:36:24 +01:00
Psykers (#71566)
## 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>
This commit is contained in:
@@ -45,7 +45,8 @@
|
||||
else if(ismob(parent))
|
||||
RegisterSignal(parent, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
|
||||
RegisterSignal(parent, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)
|
||||
to_chat(parent, span_warning("Magic seems to flee from you. You are immune to spells but are unable to cast magic."))
|
||||
if(!HAS_TRAIT(parent, TRAIT_ANTIMAGIC_NO_SELFBLOCK))
|
||||
to_chat(parent, span_warning("Magic seems to flee from you. You are immune to spells but are unable to cast magic."))
|
||||
else
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/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)
|
||||
@@ -171,13 +171,7 @@
|
||||
*/
|
||||
/datum/component/religious_tool/proc/generate_available_sects(mob/user)
|
||||
var/list/sects_to_pick = list()
|
||||
var/human_highpriest = ishuman(user)
|
||||
var/mob/living/carbon/human/highpriest = user
|
||||
for(var/path in subtypesof(/datum/religion_sect))
|
||||
if(human_highpriest && initial(easy_access_sect.invalidating_qualities))
|
||||
var/datum/species/highpriest_species = highpriest.dna.species
|
||||
if(initial(easy_access_sect.invalidating_qualities) in highpriest_species.inherent_traits)
|
||||
continue
|
||||
var/list/sect = list()
|
||||
var/datum/religion_sect/not_a_real_instance_rs = path
|
||||
sect["name"] = initial(not_a_real_instance_rs.name)
|
||||
|
||||
+7
-2
@@ -565,7 +565,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
|
||||
skin_tone = GLOB.skin_tones[deconstruct_block(get_uni_identity_block(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)]
|
||||
eye_color_left = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_LEFT_BLOCK))
|
||||
eye_color_right = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_RIGHT_BLOCK))
|
||||
facial_hairstyle = GLOB.facial_hairstyles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIRSTYLE_BLOCK), GLOB.facial_hairstyles_list.len)]
|
||||
if(HAS_TRAIT(src, TRAIT_SHAVED))
|
||||
hairstyle = "Shaved"
|
||||
else
|
||||
facial_hairstyle = GLOB.facial_hairstyles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIRSTYLE_BLOCK), GLOB.facial_hairstyles_list.len)]
|
||||
if(HAS_TRAIT(src, TRAIT_BALD))
|
||||
hairstyle = "Bald"
|
||||
else
|
||||
@@ -836,7 +839,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
|
||||
ForceContractDisease(new/datum/disease/gastrolosis())
|
||||
to_chat(src, span_notice("Oh, I actually feel quite alright!"))
|
||||
else
|
||||
switch(rand(0,5))
|
||||
switch(rand(0,6))
|
||||
if(0)
|
||||
investigate_log("has been gibbed by DNA instability.", INVESTIGATE_DEATHS)
|
||||
gib()
|
||||
@@ -866,6 +869,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
|
||||
if(5)
|
||||
to_chat(src, span_phobia("LOOK UP!"))
|
||||
addtimer(CALLBACK(src, PROC_REF(something_horrible_mindmelt)), 30)
|
||||
if(6)
|
||||
psykerize()
|
||||
|
||||
/mob/living/carbon/human/proc/something_horrible_mindmelt()
|
||||
if(!is_blind())
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
|
||||
///Burdened grants some more mutations upon injuring yourself sufficiently
|
||||
/datum/mutation/human/burdened
|
||||
name = "Burdened"
|
||||
desc = "Less of a genome and more of a forceful rewrite of genes. Nothing Nanotrasen supplies allows for a genetic restructure like this... \
|
||||
The user feels compelled to injure themselves in various incapacitating and horrific ways. Oddly enough, this gene seems to be connected \
|
||||
to several other ones, possibly ready to trigger more genetic changes in the future."
|
||||
quality = POSITIVE //so it gets carried over on revives
|
||||
locked = TRUE
|
||||
text_gain_indication = "<span class='notice'>You feel burdened!</span>"
|
||||
text_lose_indication = "<span class='warning'>You no longer feel the need to burden yourself!</span>"
|
||||
/// goes from 0 to 6 (but can be beyond 6, just does nothing) and gives rewards. increased by disabling yourself with debuffs
|
||||
var/burden_level = 0
|
||||
|
||||
/datum/mutation/human/burdened/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
RegisterSignal(owner, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(organ_added_burden))
|
||||
RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(organ_removed_burden))
|
||||
|
||||
RegisterSignal(owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(limbs_added_burden))
|
||||
RegisterSignal(owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(limbs_removed_burden))
|
||||
|
||||
RegisterSignal(owner, COMSIG_CARBON_GAIN_ADDICTION, PROC_REF(addict_added_burden))
|
||||
RegisterSignal(owner, COMSIG_CARBON_LOSE_ADDICTION, PROC_REF(addict_removed_burden))
|
||||
|
||||
RegisterSignal(owner, COMSIG_CARBON_GAIN_MUTATION, PROC_REF(mutation_added_burden))
|
||||
RegisterSignal(owner, COMSIG_CARBON_LOSE_MUTATION, PROC_REF(mutation_removed_burden))
|
||||
|
||||
RegisterSignal(owner, COMSIG_CARBON_GAIN_TRAUMA, PROC_REF(trauma_added_burden))
|
||||
RegisterSignal(owner, COMSIG_CARBON_LOSE_TRAUMA, PROC_REF(trauma_removed_burden))
|
||||
|
||||
/datum/mutation/human/burdened/on_losing(mob/living/carbon/human/owner)
|
||||
. = ..()
|
||||
UnregisterSignal(owner, list(
|
||||
COMSIG_CARBON_GAIN_ORGAN,
|
||||
COMSIG_CARBON_LOSE_ORGAN,
|
||||
COMSIG_CARBON_ATTACH_LIMB,
|
||||
COMSIG_CARBON_REMOVE_LIMB,
|
||||
COMSIG_CARBON_GAIN_ADDICTION,
|
||||
COMSIG_CARBON_LOSE_ADDICTION,
|
||||
COMSIG_CARBON_GAIN_MUTATION,
|
||||
COMSIG_CARBON_LOSE_MUTATION,
|
||||
COMSIG_CARBON_GAIN_TRAUMA,
|
||||
COMSIG_CARBON_LOSE_TRAUMA,
|
||||
))
|
||||
|
||||
/**
|
||||
* Called by hooked signals whenever burden_level var needs to go up or down by 1.
|
||||
* Sends messages on burden level, gives powers and takes them if needed, etc
|
||||
*
|
||||
* Arguments:
|
||||
* * increase: whether to tick burden_level up or down 1
|
||||
*/
|
||||
/datum/mutation/human/burdened/proc/update_burden(increase)
|
||||
//adjust burden
|
||||
burden_level = increase ? burden_level + 1 : burden_level - 1
|
||||
if(burden_level < 0) //basically a clamp with a stack on it, because this shouldn't be happening
|
||||
stack_trace("somehow, burden mutation is removing more burden than it's adding.")
|
||||
burden_level = 0
|
||||
//send a message and handle rewards
|
||||
switch(burden_level)
|
||||
if(0)
|
||||
to_chat(owner, span_warning("You feel no weight on your shoulders. You are not feeling [GLOB.deity]'s suffering."))
|
||||
if(1)
|
||||
if(increase)
|
||||
to_chat(owner, span_notice("You begin to feel the scars on [GLOB.deity]. You must continue to burden yourself."))
|
||||
else
|
||||
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You are barely feeling [GLOB.deity]'s suffering."))
|
||||
if(2)
|
||||
if(increase)
|
||||
to_chat(owner, span_notice("You have done well to understand [GLOB.deity]. You are almost at a breakthrough."))
|
||||
else
|
||||
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You have lost some universal truths."))
|
||||
dna.remove_mutation(/datum/mutation/human/telepathy)
|
||||
dna.remove_mutation(/datum/mutation/human/mute)
|
||||
owner.remove_filter("burden_outline")
|
||||
if(3)
|
||||
if(increase)
|
||||
to_chat(owner, span_notice("Your suffering is only a fraction of [GLOB.deity]'s, and yet the universal truths are coming to you."))
|
||||
dna.add_mutation(/datum/mutation/human/telepathy)
|
||||
dna.add_mutation(/datum/mutation/human/mute)
|
||||
owner.add_filter("burden_outline", 9, list("type" = "outline", "color" = "#6c6eff"))
|
||||
else
|
||||
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You feel like you're about to forget."))
|
||||
if(4)
|
||||
if(increase)
|
||||
to_chat(owner, span_notice("The weight on your shoulders is immense. [GLOB.deity] is shattered across the cosmos."))
|
||||
else
|
||||
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You're growing further from your goal."))
|
||||
if(5)
|
||||
if(increase)
|
||||
to_chat(owner, span_notice("You're on the cusp of another breakthrough. [GLOB.deity] lost everything."))
|
||||
else
|
||||
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You have lost some universal truths."))
|
||||
dna.remove_mutation(/datum/mutation/human/telekinesis)
|
||||
dna.remove_mutation(/datum/mutation/human/mindreader)
|
||||
if(6)
|
||||
to_chat(owner, span_notice("You have finally broken yourself enough to understand [GLOB.deity]. It's all so clear to you."))
|
||||
dna.add_mutation(/datum/mutation/human/telekinesis)
|
||||
dna.add_mutation(/datum/mutation/human/mindreader)
|
||||
|
||||
/// Signal to decrease burden_level (see update_burden proc) if an organ is added
|
||||
/datum/mutation/human/burdened/proc/organ_added_burden(mob/burdened, obj/item/organ/new_organ, special)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(special) //aheals
|
||||
return
|
||||
|
||||
if(istype(new_organ, /obj/item/organ/internal/eyes))
|
||||
var/obj/item/organ/internal/eyes/new_eyes = new_organ
|
||||
if(new_eyes.tint < TINT_BLIND) //unless you added unworking eyes (flashlight eyes), this is removing burden
|
||||
update_burden(FALSE)
|
||||
return
|
||||
update_burden(FALSE)//working organ
|
||||
|
||||
/// Signal to increase burden_level (see update_burden proc) if an organ is removed
|
||||
/datum/mutation/human/burdened/proc/organ_removed_burden(mob/burdened, obj/item/organ/old_organ, special)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(special) //aheals
|
||||
return
|
||||
|
||||
if(istype(old_organ, /obj/item/organ/internal/eyes))
|
||||
var/obj/item/organ/internal/eyes/old_eyes = old_organ
|
||||
if(old_eyes.tint < TINT_BLIND) //unless you were already blinded by them (flashlight eyes), this is adding burden!
|
||||
update_burden(TRUE)
|
||||
|
||||
update_burden(TRUE)//lost organ
|
||||
|
||||
/// Signal to decrease burden_level (see update_burden proc) if a limb is added
|
||||
/datum/mutation/human/burdened/proc/limbs_added_burden(datum/source, obj/item/bodypart/new_limb, special)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(special) //something we don't wanna consider, like instaswapping limbs
|
||||
return
|
||||
update_burden(FALSE)
|
||||
|
||||
/// Signal to increase burden_level (see update_burden proc) if a limb is removed
|
||||
/datum/mutation/human/burdened/proc/limbs_removed_burden(datum/source, obj/item/bodypart/old_limb, special)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(special) //something we don't wanna consider, like instaswapping limbs
|
||||
return
|
||||
update_burden(TRUE)
|
||||
|
||||
/// Signal to increase burden_level (see update_burden proc) if an addiction is added
|
||||
/datum/mutation/human/burdened/proc/addict_added_burden(datum/addiction/new_addiction, datum/mind/addict_mind)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
update_burden(TRUE)
|
||||
|
||||
/// Signal to decrease burden_level (see update_burden proc) if an addiction is removed
|
||||
/datum/mutation/human/burdened/proc/addict_removed_burden(datum/addiction/old_addiction, datum/mind/nonaddict_mind)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
update_burden(FALSE)
|
||||
|
||||
/// Signal to increase burden_level (see update_burden proc) if a mutation is added
|
||||
/datum/mutation/human/burdened/proc/mutation_added_burden(mob/living/carbon/burdened, datum/mutation/human/mutation_type, class)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(class == MUT_OTHER) //getting a mutation can give a mutation as a reward, which in turn triggers this, which then rewards a mutation, which in turn...
|
||||
return
|
||||
if(initial(mutation_type.quality) == NEGATIVE)
|
||||
update_burden(TRUE)
|
||||
|
||||
/// Signal to decrease burden_level (see update_burden proc) if a mutation is removed
|
||||
/datum/mutation/human/burdened/proc/mutation_removed_burden(mob/living/carbon/burdened, datum/mutation/human/mutation_type)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(class == MUT_OTHER) //see above
|
||||
return
|
||||
if(initial(mutation_type.quality) == NEGATIVE)
|
||||
update_burden(FALSE)
|
||||
|
||||
/// Signal to increase burden_level (see update_burden proc) if a trauma is added
|
||||
/datum/mutation/human/burdened/proc/trauma_added_burden(mob/living/carbon/burdened, datum/brain_trauma/trauma_added)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(trauma_added, /datum/brain_trauma/severe))
|
||||
update_burden(TRUE)
|
||||
|
||||
/// Signal to decrease burden_level (see update_burden proc) if a trauma is removed
|
||||
/datum/mutation/human/burdened/proc/trauma_removed_burden(mob/living/carbon/burdened, datum/brain_trauma/trauma_removed)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(trauma_removed, /datum/brain_trauma/severe))
|
||||
update_burden(FALSE)
|
||||
@@ -1,318 +0,0 @@
|
||||
|
||||
///Honorbound prevents you from attacking the unready, the just, or the innocent
|
||||
/datum/mutation/human/honorbound
|
||||
name = "Honorbound"
|
||||
desc = "Less of a genome and more of a forceful rewrite of genes. Nothing Nanotrasen supplies allows for a genetic restructure like this... \
|
||||
The user feels compelled to follow supposed \"rules of combat\" but in reality they physically are unable to. \
|
||||
Their brain is rewired to excuse any curious inabilities that arise from this odd effect."
|
||||
quality = POSITIVE //so it gets carried over on revives
|
||||
power_path = /datum/action/cooldown/spell/pointed/declare_evil
|
||||
locked = TRUE
|
||||
text_gain_indication = "<span class='notice'>You feel honorbound!</span>"
|
||||
text_lose_indication = "<span class='warning'>You feel unshackled from your code of honor!</span>"
|
||||
/// list of guilty people
|
||||
var/list/guilty = list()
|
||||
|
||||
/datum/mutation/human/honorbound/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
//moodlet
|
||||
owner.add_mood_event("honorbound", /datum/mood_event/honorbound)
|
||||
//checking spells cast by honorbound
|
||||
RegisterSignal(owner, COMSIG_MOB_CAST_SPELL, PROC_REF(spell_check))
|
||||
RegisterSignal(owner, COMSIG_MOB_FIRED_GUN, PROC_REF(staff_check))
|
||||
//signals that check for guilt
|
||||
RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(attackby_guilt))
|
||||
RegisterSignal(owner, COMSIG_ATOM_HULK_ATTACK, PROC_REF(hulk_guilt))
|
||||
RegisterSignal(owner, COMSIG_ATOM_ATTACK_HAND, PROC_REF(hand_guilt))
|
||||
RegisterSignal(owner, COMSIG_ATOM_ATTACK_PAW, PROC_REF(paw_guilt))
|
||||
RegisterSignal(owner, COMSIG_ATOM_BULLET_ACT, PROC_REF(bullet_guilt))
|
||||
RegisterSignal(owner, COMSIG_ATOM_HITBY, PROC_REF(thrown_guilt))
|
||||
|
||||
//signal that checks for dishonorable attacks
|
||||
RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(attack_honor))
|
||||
|
||||
/datum/mutation/human/honorbound/on_losing(mob/living/carbon/human/owner)
|
||||
owner.clear_mood_event("honorbound")
|
||||
UnregisterSignal(owner, list(
|
||||
COMSIG_PARENT_ATTACKBY,
|
||||
COMSIG_ATOM_HULK_ATTACK,
|
||||
COMSIG_ATOM_ATTACK_HAND,
|
||||
COMSIG_ATOM_ATTACK_PAW,
|
||||
COMSIG_ATOM_BULLET_ACT,
|
||||
COMSIG_ATOM_HITBY,
|
||||
COMSIG_MOB_CLICKON,
|
||||
COMSIG_MOB_CAST_SPELL,
|
||||
COMSIG_MOB_FIRED_GUN,
|
||||
))
|
||||
. = ..()
|
||||
|
||||
/// Signal to see if the mutation allows us to attack a target
|
||||
/datum/mutation/human/honorbound/proc/attack_honor(mob/living/carbon/human/honorbound, atom/clickingon, list/modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(modifiers[ALT_CLICK] || modifiers[SHIFT_CLICK] || modifiers[CTRL_CLICK] || modifiers[MIDDLE_CLICK])
|
||||
return
|
||||
if(!isliving(clickingon))
|
||||
return
|
||||
|
||||
var/mob/living/clickedmob = clickingon
|
||||
var/obj/item/weapon = honorbound.get_active_held_item()
|
||||
|
||||
if(!honorbound.DirectAccess(clickedmob) && !isgun(weapon))
|
||||
return
|
||||
if(weapon?.item_flags & NOBLUDGEON)
|
||||
return
|
||||
if(!honorbound.combat_mode && (HAS_TRAIT(clickedmob, TRAIT_ALLOWED_HONORBOUND_ATTACK) || ((!weapon || !weapon.force) && !LAZYACCESS(modifiers, RIGHT_CLICK))))
|
||||
return
|
||||
if(!is_honorable(honorbound, clickedmob))
|
||||
return (COMSIG_MOB_CANCEL_CLICKON)
|
||||
|
||||
/**
|
||||
* Called by hooked signals whenever someone attacks the person with this mutation
|
||||
* Checks if the attacker should be considered guilty and adds them to the guilty list if true
|
||||
*
|
||||
* Arguments:
|
||||
* * user: person who attacked the honorbound
|
||||
* * declaration: if this wasn't an attack, but instead the honorbound spending favor on declaring this person guilty
|
||||
*/
|
||||
/datum/mutation/human/honorbound/proc/guilty(mob/living/user, declaration = FALSE)
|
||||
if(user in guilty)
|
||||
return
|
||||
var/datum/mind/guilty_conscience = user.mind
|
||||
if(guilty_conscience) //sec and medical are immune to becoming guilty through attack (we don't check holy because holy shouldn't be able to attack eachother anyways)
|
||||
var/datum/job/job = guilty_conscience.assigned_role
|
||||
if(job.departments_bitflags & (DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SECURITY))
|
||||
return
|
||||
if(declaration)
|
||||
to_chat(owner, span_notice("[user] is now considered guilty by [GLOB.deity] from your declaration."))
|
||||
else
|
||||
to_chat(owner, span_notice("[user] is now considered guilty by [GLOB.deity] for attacking you first."))
|
||||
to_chat(user, span_danger("[GLOB.deity] no longer considers you innocent!"))
|
||||
guilty += user
|
||||
|
||||
/**
|
||||
* Called by attack_honor signal to check whether an attack should be allowed or not
|
||||
*
|
||||
* Arguments:
|
||||
* * honorbound_human: typecasted owner of mutation
|
||||
* * target_creature: person honorbound_human is attacking
|
||||
*/
|
||||
/datum/mutation/human/honorbound/proc/is_honorable(mob/living/carbon/human/honorbound_human, mob/living/target_creature)
|
||||
var/is_guilty = (target_creature in guilty)
|
||||
//THE UNREADY (Applies over ANYTHING else!)
|
||||
if(honorbound_human == target_creature)
|
||||
return TRUE //oh come on now
|
||||
if(target_creature.IsSleeping() || target_creature.IsUnconscious() || HAS_TRAIT(target_creature, TRAIT_RESTRAINED))
|
||||
to_chat(honorbound_human, span_warning("There is no honor in attacking the <b>unready</b>."))
|
||||
return FALSE
|
||||
//THE JUST (Applies over guilt except for med, so you best be careful!)
|
||||
if(ishuman(target_creature))
|
||||
var/mob/living/carbon/human/target_human = target_creature
|
||||
var/datum/job/job = target_human.mind?.assigned_role
|
||||
var/is_holy = target_human.mind?.holy_role
|
||||
if(is_holy || (job?.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY))
|
||||
to_chat(honorbound_human, span_warning("There is nothing righteous in attacking the <b>just</b>."))
|
||||
return FALSE
|
||||
if(job?.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL)
|
||||
to_chat(honorbound_human, span_warning("If you truly think this healer is not <b>innocent</b>, declare them guilty."))
|
||||
return FALSE
|
||||
//THE INNOCENT
|
||||
if(!is_guilty)
|
||||
to_chat(honorbound_human, span_warning("There is nothing righteous in attacking the <b>innocent</b>."))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// SIGNALS THAT ARE FOR BEING ATTACKED FIRST (GUILTY)
|
||||
/datum/mutation/human/honorbound/proc/attackby_guilt(datum/source, obj/item/I, mob/attacker)
|
||||
SIGNAL_HANDLER
|
||||
if(I.force && I.damtype != STAMINA)
|
||||
guilty(attacker)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/hulk_guilt(datum/source, mob/attacker)
|
||||
SIGNAL_HANDLER
|
||||
guilty(attacker)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/hand_guilt(datum/source, mob/living/attacker)
|
||||
SIGNAL_HANDLER
|
||||
if(attacker.combat_mode)
|
||||
guilty(attacker)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/paw_guilt(datum/source, mob/living/attacker)
|
||||
SIGNAL_HANDLER
|
||||
guilty(attacker)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/bullet_guilt(datum/source, obj/projectile/proj)
|
||||
SIGNAL_HANDLER
|
||||
var/mob/living/shot_honorbound = source
|
||||
var/static/list/guilty_projectiles = typecacheof(list(
|
||||
/obj/projectile/beam,
|
||||
/obj/projectile/bullet,
|
||||
/obj/projectile/magic,
|
||||
))
|
||||
if(!is_type_in_typecache(proj, guilty_projectiles))
|
||||
return
|
||||
if((proj.damage_type == STAMINA))
|
||||
return
|
||||
if(!proj.nodamage && proj.damage < shot_honorbound.health && isliving(proj.firer))
|
||||
guilty(proj.firer)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/thrown_guilt(datum/source, atom/movable/thrown_movable, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
|
||||
SIGNAL_HANDLER
|
||||
if(isitem(thrown_movable))
|
||||
var/mob/living/honorbound = source
|
||||
var/obj/item/thrown_item = thrown_movable
|
||||
var/mob/thrown_by = thrown_item.thrownby?.resolve()
|
||||
if(thrown_item.throwforce < honorbound.health && ishuman(thrown_by))
|
||||
guilty(thrown_by)
|
||||
|
||||
//spell checking
|
||||
/datum/mutation/human/honorbound/proc/spell_check(mob/user, datum/action/cooldown/spell/spell_cast)
|
||||
SIGNAL_HANDLER
|
||||
punishment(user, spell_cast.school)
|
||||
|
||||
/datum/mutation/human/honorbound/proc/staff_check(mob/user, obj/item/gun/gun_fired, target, params, zone_override)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(gun_fired, /obj/item/gun/magic))
|
||||
return
|
||||
var/obj/item/gun/magic/offending_staff = gun_fired
|
||||
punishment(user, offending_staff.school)
|
||||
|
||||
/**
|
||||
* Called when a spell is casted or a magic gun is fired, checks the signal and punishes accordingly
|
||||
*
|
||||
* Arguments:
|
||||
* * user: typecasted owner of mutation
|
||||
* * school: school of magic casted from the staff/spell
|
||||
*/
|
||||
/datum/mutation/human/honorbound/proc/punishment(mob/living/carbon/human/user, school)
|
||||
switch(school)
|
||||
if(SCHOOL_HOLY, SCHOOL_MIME, SCHOOL_RESTORATION)
|
||||
return
|
||||
if(SCHOOL_NECROMANCY, SCHOOL_FORBIDDEN, SCHOOL_SANGUINE)
|
||||
to_chat(user, span_userdanger("[GLOB.deity] is enraged by your use of forbidden magic!"))
|
||||
lightningbolt(user)
|
||||
owner.add_mood_event("honorbound", /datum/mood_event/banished)
|
||||
user.dna.remove_mutation(/datum/mutation/human/honorbound)
|
||||
user.mind.holy_role = NONE
|
||||
to_chat(user, span_userdanger("You have been excommunicated! You are no longer holy!"))
|
||||
else
|
||||
to_chat(user, span_userdanger("[GLOB.deity] is angered by your use of [school] magic!"))
|
||||
lightningbolt(user)
|
||||
owner.add_mood_event("honorbound", /datum/mood_event/holy_smite)//permanently lose your moodlet after this
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil
|
||||
name = "Declare Evil"
|
||||
desc = "If someone is so obviously an evil of this world you can spend a huge amount of favor to declare them guilty."
|
||||
button_icon_state = "declaration"
|
||||
ranged_mousepointer = 'icons/effects/mouse_pointers/honorbound.dmi'
|
||||
|
||||
school = SCHOOL_HOLY
|
||||
cooldown_time = 0
|
||||
|
||||
invocation = "This is an error!"
|
||||
invocation_type = INVOCATION_SHOUT
|
||||
spell_requirements = SPELL_REQUIRES_HUMAN
|
||||
|
||||
active_msg = "You prepare to declare a sinner..."
|
||||
deactive_msg = "You decide against a declaration."
|
||||
|
||||
/// The amount of favor required to declare on someone
|
||||
var/required_favor = 150
|
||||
/// A ref to our owner's honorbound mutation
|
||||
var/datum/mutation/human/honorbound/honor_mutation
|
||||
/// The declaration that's shouted in invocation. Set in New()
|
||||
var/declaration = "By the divine light of my deity, you are an evil of this world that must be wrought low!"
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/New()
|
||||
. = ..()
|
||||
declaration = "By the divine light of [GLOB.deity], you are an evil of this world that must be wrought low!"
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/Destroy()
|
||||
// If we had an owner, Destroy() called Remove(), and already handled this
|
||||
if(honor_mutation)
|
||||
UnregisterSignal(honor_mutation, COMSIG_PARENT_QDELETING)
|
||||
honor_mutation = null
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/Grant(mob/grant_to)
|
||||
if(!ishuman(grant_to))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/carbon/human/human_owner = grant_to
|
||||
var/datum/mutation/human/honorbound/honor_mut = human_owner.dna?.check_mutation(/datum/mutation/human/honorbound)
|
||||
if(QDELETED(honor_mut))
|
||||
return FALSE
|
||||
|
||||
RegisterSignal(honor_mut, COMSIG_PARENT_QDELETING, PROC_REF(on_honor_mutation_lost))
|
||||
honor_mutation = honor_mut
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/Remove(mob/living/remove_from)
|
||||
. = ..()
|
||||
UnregisterSignal(honor_mutation, COMSIG_PARENT_QDELETING)
|
||||
honor_mutation = null
|
||||
|
||||
/// If we lose our honor mutation somehow, self-delete (and clear references)
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/proc/on_honor_mutation_lost(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
qdel(src)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/can_cast_spell(feedback = TRUE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
// This shouldn't technically be a possible state, but you never know
|
||||
if(!honor_mutation)
|
||||
return FALSE
|
||||
if(GLOB.religious_sect.favor < required_favor)
|
||||
if(feedback)
|
||||
to_chat(owner, span_warning("You need at least 150 favor to declare someone evil!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/is_valid_target(atom/cast_on)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!isliving(cast_on))
|
||||
to_chat(owner, span_warning("You can only declare living beings evil!"))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/living_cast_on = cast_on
|
||||
if(living_cast_on.stat == DEAD)
|
||||
to_chat(owner, span_warning("Declaration on the dead? Really?"))
|
||||
return FALSE
|
||||
|
||||
// sec and medical are immune to becoming guilty through attack
|
||||
// (we don't check holy, because holy shouldn't be able to attack eachother anyways)
|
||||
if(!living_cast_on.key || !living_cast_on.mind)
|
||||
to_chat(owner, span_warning("There is no evil a vacant mind can do."))
|
||||
return FALSE
|
||||
|
||||
// also handles any kind of issues with self declarations
|
||||
if(living_cast_on.mind.holy_role)
|
||||
to_chat(owner, span_warning("Followers of [GLOB.deity] cannot be evil!"))
|
||||
return FALSE
|
||||
|
||||
// cannot declare security as evil
|
||||
if(living_cast_on.mind.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)
|
||||
to_chat(owner, span_warning("Members of security are uncorruptable! You cannot declare one evil!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/before_cast(mob/living/cast_on)
|
||||
. = ..()
|
||||
if(. & SPELL_CANCEL_CAST)
|
||||
return
|
||||
|
||||
invocation = "[cast_on]! [declaration]"
|
||||
|
||||
/datum/action/cooldown/spell/pointed/declare_evil/cast(mob/living/cast_on)
|
||||
. = ..()
|
||||
GLOB.religious_sect.adjust_favor(-required_favor, owner)
|
||||
honor_mutation.guilty(cast_on, declaration = TRUE)
|
||||
Reference in New Issue
Block a user