## 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

![image](https://user-images.githubusercontent.com/23585223/204150279-a6cf8e2f-c678-476e-b72c-6088cd8b684b.png)

## 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:
Fikou
2022-11-29 21:13:28 +01:00
committed by GitHub
parent f0d87f7554
commit 35b5ac0c4e
37 changed files with 985 additions and 377 deletions
@@ -325,6 +325,8 @@
#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit"
///from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target)
#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire"
///from base of /obj/projectile/proc/fire(): (obj/projectile, atom/firer, atom/original_target)
#define COMSIG_PROJECTILE_FIRER_BEFORE_FIRE "projectile_firer_before_fire"
///from the base of /obj/projectile/proc/fire(): ()
#define COMSIG_PROJECTILE_FIRE "projectile_fire"
///sent to targets during the process_hit proc of projectiles
+1
View File
@@ -210,6 +210,7 @@
#define BLIND_LAYER 4
#define CRIT_LAYER 5
#define CURSE_LAYER 6
#define ECHO_LAYER 7
#define FOV_EFFECT_LAYER 100
+1
View File
@@ -120,6 +120,7 @@
#define BODYPART_ID_ROBOTIC "robotic"
#define BODYPART_ID_DIGITIGRADE "digitigrade"
#define BODYPART_ID_LARVA "larva"
#define BODYPART_ID_PSYKER "psyker"
//See: datum/species/var/digitigrade_customization
///The species does not have digitigrade legs in generation.
+2
View File
@@ -22,6 +22,8 @@
#define CALIBER_357 ".357"
/// The caliber used by the detective's revolver.
#define CALIBER_38 ".38"
/// The caliber used by the chaplain's revolver.
#define CALIBER_77 ".77"
/// The caliber used by the C-20r SMG, the tommygun, and the M1911 pistol.
#define CALIBER_45 ".45"
/// The caliber used by sniper rifles and the desert eagle.
+9 -2
View File
@@ -609,6 +609,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_GRABWEAKNESS "grab_weakness"
#define TRAIT_SNOB "snob"
#define TRAIT_BALD "bald"
#define TRAIT_SHAVED "shaved"
#define TRAIT_BADTOUCH "bad_touch"
#define TRAIT_EXTROVERT "extrovert"
#define TRAIT_INTROVERT "introvert"
@@ -677,6 +678,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// this is used to bypass tongue language restrictions but not tongue disabilities
#define TRAIT_TOWER_OF_BABEL "tower_of_babel"
/// This target has recently been shot by a marksman coin and is very briefly immune to being hit by one again to prevent recursion
#define TRAIT_RECENTLY_COINED "recently_coined"
/// Receives echolocation images.
#define TRAIT_ECHOLOCATION_RECEIVER "echolocation_receiver"
//Medical Categories for quirks
#define CAT_QUIRK_ALL 0
#define CAT_QUIRK_NOTES 1
@@ -987,5 +994,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define RIGHT_LEG_TRAIT "right_leg"
#define LEFT_LEG_TRAIT "left_leg"
/// This target has recently been shot by a marksman coin and is very briefly immune to being hit by one again to prevent recursion
#define TRAIT_RECENTLY_COINED "trait_recently_coined"
/// Trait given by echolocation component.
#define ECHOLOCATION_TRAIT "echolocation"
+2 -1
View File
@@ -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
+183
View File
@@ -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)
-6
View File
@@ -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
View File
@@ -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())
+10
View File
@@ -66,3 +66,13 @@
name = "invisible blockade"
desc = "You're gonna be here awhile."
initial_duration = 1 MINUTES
/// Psyker forcefield
/obj/effect/forcefield/psychic
name = "psychic forcefield"
desc = "A wall of psychic energy powerful enough stop the motion of objects. Projectiles ricochet."
icon_state = "psychic"
can_atmos_pass = ATMOS_PASS_YES
flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD
receive_ricochet_chance_mod = INFINITY //we do ricochet a lot!
initial_duration = 10 SECONDS
+4 -1
View File
@@ -166,6 +166,9 @@
if(!get_location_accessible(H, location))
to_chat(user, span_warning("The mask is in the way!"))
return
if(HAS_TRAIT(H, TRAIT_SHAVED))
to_chat(user, span_warning("[H] is just way too shaved. Like, really really shaved."))
return
user.visible_message(span_notice("[user] tries to change [H]'s facial hairstyle using [src]."), span_notice("You try to change [H]'s facial hairstyle using [src]."))
if(new_style && do_after(user, 60, target = H))
user.visible_message(span_notice("[user] successfully changes [H]'s facial hairstyle using [src]."), span_notice("You successfully change [H]'s facial hairstyle using [src]."))
@@ -215,7 +218,7 @@
to_chat(user, span_warning("The headgear is in the way!"))
return
if(HAS_TRAIT(H, TRAIT_BALD))
to_chat(H, span_warning("[H] is just way too bald. Like, really really bald."))
to_chat(user, span_warning("[H] is just way too bald. Like, really really bald."))
return
user.visible_message(span_notice("[user] tries to change [H]'s hairstyle using [src]."), span_notice("You try to change [H]'s hairstyle using [src]."))
if(new_style && do_after(user, 60, target = H))
+2
View File
@@ -34,6 +34,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
return TRUE
if(!user.canUseTopic(src, be_close = TRUE, no_dexterity = FALSE, no_tk = TRUE))
return TRUE //no tele-grooming
if(HAS_TRAIT(hairdresser, TRAIT_SHAVED))
to_chat(hairdresser, span_notice("If only growing back facial hair were that easy for you..."))
hairdresser.facial_hairstyle = new_style
else
hairdresser.facial_hairstyle = "Shaved"
+5
View File
@@ -213,6 +213,11 @@
/datum/client_colour/rave
priority = PRIORITY_LOW
/datum/client_colour/psyker
priority = PRIORITY_ABSOLUTE
override = TRUE
colour = list(0.8,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
#undef PRIORITY_ABSOLUTE
#undef PRIORITY_HIGH
#undef PRIORITY_NORMAL
+7 -6
View File
@@ -42,7 +42,7 @@
/obj/item/organ/internal/brain/Insert(mob/living/carbon/C, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
. = ..()
name = "brain"
name = initial(name)
if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer) //congrats, you're trapped in a body you don't control
if(brainmob && !(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_DEATHCOMA))))
@@ -88,11 +88,12 @@
/obj/item/organ/internal/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE)
// Delete skillchips first as parent proc sets owner to null, and skillchips need to know the brain's owner.
if(!QDELETED(C) && length(skillchips))
to_chat(C, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
if(!special)
to_chat(C, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
for(var/chip in skillchips)
var/obj/item/skillchip/skillchip = chip
// Run the try_ proc with force = TRUE.
skillchip.try_deactivate_skillchip(FALSE, TRUE)
skillchip.try_deactivate_skillchip(silent = special, force = TRUE)
. = ..()
@@ -106,7 +107,7 @@
C.update_body_parts()
/obj/item/organ/internal/brain/proc/transfer_identity(mob/living/L)
name = "[L.name]'s brain"
name = "[L.name]'s [initial(name)]"
if(brainmob || decoy_override)
return
if(!L.mind)
@@ -297,7 +298,7 @@
// If we have some sort of brain type or subtype change and have skillchips, engage the failsafe procedure!
if(owner && length(skillchips) && (replacement_brain.type != type))
activate_skillchip_failsafe(FALSE)
activate_skillchip_failsafe(silent = TRUE)
// Check through all our skillchips, remove them from this brain, add them to the replacement brain.
for(var/chip in skillchips)
@@ -347,7 +348,7 @@
organ_traits = list(TRAIT_CAN_STRIP)
/obj/item/organ/internal/brain/primitive //No like books and stompy metal men
name = "Primative Brain"
name = "primitive brain"
desc = "This juicy piece of meat has a clearly underdeveloped frontal lobe."
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_PRIMITIVE) // No literacy
+2 -2
View File
@@ -94,7 +94,7 @@
plane = FULLSCREEN_PLANE
/// Plays a visual effect representing a sound cue for people with vision obstructed by FOV or blindness
/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0, list/override_list)
/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0, time = 1.5 SECONDS, list/override_list)
var/turf/anchor_point = get_turf(center)
var/image/fov_image/fov_image
var/list/clients_shown
@@ -123,7 +123,7 @@
//when added as an image mutable_appearances act identically. we just make it an MA becuase theyre faster to change appearance
if(clients_shown)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(remove_image_from_clients), fov_image, clients_shown), 30)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(remove_image_from_clients), fov_image, clients_shown), time)
/atom/movable/screen/fov_blocker
icon = 'icons/effects/fov/field_of_view.dmi'
+1 -1
View File
@@ -327,7 +327,7 @@
update_appearance()
/obj/item/gun/ballistic/can_shoot()
return chambered
return chambered?.loaded_projectile
/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
. = ..()
+2
View File
@@ -701,6 +701,8 @@
LAZYINITLIST(impacted)
if(fired_from)
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
if(firer)
SEND_SIGNAL(firer, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, src, fired_from, original)
//If no angle needs to resolve it from xo/yo!
if(shrapnel_type && LAZYLEN(embedding))
AddElement(/datum/element/embed, projectile_payload = shrapnel_type)
@@ -2052,7 +2052,7 @@
/datum/reagent/barbers_aid/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=FALSE)
. = ..()
if(!(methods & (TOUCH|VAPOR)) || !ishuman(exposed_mob) || HAS_TRAIT(exposed_mob, TRAIT_BALD))
if(!(methods & (TOUCH|VAPOR)) || !ishuman(exposed_mob) || HAS_TRAIT(exposed_mob, TRAIT_BALD) || HAS_TRAIT(exposed_mob, TRAIT_SHAVED))
return
var/mob/living/carbon/human/exposed_human = exposed_mob
@@ -2074,7 +2074,7 @@
/datum/reagent/concentrated_barbers_aid/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=FALSE)
. = ..()
if(!(methods & (TOUCH|VAPOR)) || !ishuman(exposed_mob) || HAS_TRAIT(exposed_mob, TRAIT_BALD))
if(!(methods & (TOUCH|VAPOR)) || !ishuman(exposed_mob) || HAS_TRAIT(exposed_mob, TRAIT_BALD) || HAS_TRAIT(exposed_mob, TRAIT_SHAVED))
return
var/mob/living/carbon/human/exposed_human = exposed_mob
@@ -1,20 +1,15 @@
///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
///Burdened grants some mutations upon injuring yourself sufficiently
/datum/brain_trauma/special/burdened
name = "Flagellating Compulsions"
desc = "Patient feels compelled to injure themselves in various incapacitating and horrific ways. There seems to be an odd genetic... trigger, following these compulsions may lead to?"
scan_desc = "damaged frontal lobe"
gain_text = span_notice("You feel burdened!")
lose_text = span_warning("You no longer feel the need to burden yourself!")
random_gain = FALSE
/// goes from 0 to 9 (but can be beyond 9, 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
/datum/brain_trauma/special/burdened/on_gain()
RegisterSignal(owner, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(organ_added_burden))
RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(organ_removed_burden))
@@ -29,9 +24,9 @@
RegisterSignal(owner, COMSIG_CARBON_GAIN_TRAUMA, PROC_REF(trauma_added_burden))
RegisterSignal(owner, COMSIG_CARBON_LOSE_TRAUMA, PROC_REF(trauma_removed_burden))
return ..()
/datum/mutation/human/burdened/on_losing(mob/living/carbon/human/owner)
. = ..()
/datum/brain_trauma/special/burdened/on_lose()
UnregisterSignal(owner, list(
COMSIG_CARBON_GAIN_ORGAN,
COMSIG_CARBON_LOSE_ORGAN,
@@ -44,6 +39,7 @@
COMSIG_CARBON_GAIN_TRAUMA,
COMSIG_CARBON_LOSE_TRAUMA,
))
return ..()
/**
* Called by hooked signals whenever burden_level var needs to go up or down by 1.
@@ -52,11 +48,15 @@
* Arguments:
* * increase: whether to tick burden_level up or down 1
*/
/datum/mutation/human/burdened/proc/update_burden(increase)
/datum/brain_trauma/special/burdened/proc/update_burden(increase)
var/datum/dna/dna = owner?.dna
if(!dna)
qdel(src)
return
//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.")
stack_trace("somehow, burden trauma is removing more burden than it's adding.")
burden_level = 0
//send a message and handle rewards
switch(burden_level)
@@ -73,35 +73,54 @@
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)
dna.remove_mutation(/datum/mutation/human/unintelligible)
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)
dna.add_mutation(/datum/mutation/human/unintelligible)
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."))
to_chat(owner, span_notice("It hurts, each ounce of pain a lesson told. How does [GLOB.deity] bear this weight?"))
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."))
to_chat(owner, span_notice("Your body is a canvas of loss. 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/telekinesis)
dna.remove_mutation(/datum/mutation/human/mindreader)
if(6)
if(increase)
to_chat(owner, span_notice("Your suffering is respectful, your scars immaculate. More universal truths are clear, but you do not fully understand yet."))
dna.add_mutation(/datum/mutation/human/telekinesis)
dna.add_mutation(/datum/mutation/human/mindreader)
else
to_chat(owner, span_warning("The weight on your shoulders feels lighter. You feel like you're about to forget."))
if(7)
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(8)
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."))
if(9)
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)
var/mob/living/carbon/human/knower = owner
if(!istype(knower))
return
INVOKE_ASYNC(knower, TYPE_PROC_REF(/mob/living/carbon/human, psykerize))
/// 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)
/datum/brain_trauma/special/burdened/proc/organ_added_burden(mob/burdened, obj/item/organ/new_organ, special)
SIGNAL_HANDLER
if(special) //aheals
@@ -112,10 +131,13 @@
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
else if(istype(new_organ, /obj/item/organ/internal/appendix))
return
update_burden(increase = 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)
/datum/brain_trauma/special/burdened/proc/organ_removed_burden(mob/burdened, obj/item/organ/old_organ, special)
SIGNAL_HANDLER
if(special) //aheals
@@ -125,65 +147,64 @@
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)
return
else if(istype(old_organ, /obj/item/organ/internal/appendix))
return
update_burden(TRUE)//lost organ
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = 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)
/datum/brain_trauma/special/burdened/proc/addict_added_burden(datum/addiction/new_addiction, datum/mind/addict_mind)
SIGNAL_HANDLER
update_burden(TRUE)
update_burden(increase = 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)
/datum/brain_trauma/special/burdened/proc/addict_removed_burden(datum/addiction/old_addiction, datum/mind/nonaddict_mind)
SIGNAL_HANDLER
update_burden(FALSE)
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = 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)
/datum/brain_trauma/special/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)
update_burden(increase = FALSE)
+376
View File
@@ -0,0 +1,376 @@
/obj/item/organ/internal/brain/psyker
name = "psyker brain"
desc = "This brain is blue, split into two hemispheres, and has immense psychic powers. What kind of monstrosity would use that?"
icon_state = "brain-psyker"
actions_types = list(
/datum/action/cooldown/spell/pointed/psychic_projection,
/datum/action/cooldown/spell/charged/psychic_booster,
/datum/action/cooldown/spell/forcewall/psychic_wall,
)
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP, TRAIT_ANTIMAGIC_NO_SELFBLOCK)
w_class = WEIGHT_CLASS_NORMAL
/obj/item/organ/internal/brain/psyker/Insert(mob/living/carbon/inserted_into, special, drop_if_replaced, no_id_transfer)
. = ..()
inserted_into.AddComponent(/datum/component/echolocation, echo_group = "psyker", echo_icon = "psyker", color_path = /datum/client_colour/psyker)
inserted_into.AddComponent(/datum/component/anti_magic, antimagic_flags = MAGIC_RESISTANCE_MIND)
/obj/item/organ/internal/brain/psyker/Remove(mob/living/carbon/removed_from, special, no_id_transfer)
. = ..()
qdel(removed_from.GetComponent(/datum/component/echolocation))
qdel(removed_from.GetComponent(/datum/component/anti_magic))
/obj/item/organ/internal/brain/psyker/on_life(delta_time, times_fired)
. = ..()
var/obj/item/bodypart/head/psyker/psyker_head = owner.get_bodypart(zone)
if(istype(psyker_head))
return
if(!DT_PROB(2, delta_time))
return
to_chat(owner, span_userdanger("Your head hurts... It can't fit your brain!"))
owner.adjust_disgust(33 * delta_time)
applyOrganDamage(5 * delta_time, 199)
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5 * delta_time)
/obj/item/bodypart/head/psyker
limb_id = BODYPART_ID_PSYKER
is_dimorphic = FALSE
should_draw_greyscale = FALSE
bodypart_traits = list(TRAIT_DISFIGURED, TRAIT_BALD, TRAIT_SHAVED, TRAIT_BLIND)
/obj/item/bodypart/head/psyker/try_attach_limb(mob/living/carbon/new_head_owner, special, abort)
. = ..()
if(!. || !new_head_owner.dna?.species)
return
new_head_owner.dna.species.species_traits |= NOEYESPRITES //MAKE VISUALS TIED TO BODYPARTS ARGHH
new_head_owner.update_body()
/// Makes us go through a transform sequency, to turn into a psyker.
/mob/living/carbon/human/proc/psykerize()
if(stat == DEAD || !get_bodypart(BODY_ZONE_HEAD) || istype(get_bodypart(BODY_ZONE_HEAD), /obj/item/bodypart/head/psyker))
return
to_chat(src, span_userdanger("You feel unwell..."))
sleep(5 SECONDS)
if(stat == DEAD || !get_bodypart(BODY_ZONE_HEAD))
return
to_chat(src, span_userdanger("You feel your skin ripping off!"))
emote("scream")
apply_damage(30, BRUTE, BODY_ZONE_HEAD)
sleep(5 SECONDS)
var/obj/item/bodypart/head/old_head = get_bodypart(BODY_ZONE_HEAD)
var/obj/item/organ/internal/brain/old_brain = getorganslot(ORGAN_SLOT_BRAIN)
var/obj/item/organ/internal/old_eyes = getorganslot(ORGAN_SLOT_EYES)
if(stat == DEAD || !old_head || !old_brain)
return
to_chat(src, span_userdanger("Your head splits open! Your brain mutates!"))
new /obj/effect/gibspawner/generic(drop_location(), src)
emote("scream")
var/obj/item/bodypart/head/psyker/psyker_head = new()
psyker_head.receive_damage(brute = 50)
if(!psyker_head.replace_limb(src, special = TRUE))
return
qdel(old_head)
var/obj/item/organ/internal/brain/psyker/psyker_brain = new()
old_brain.before_organ_replacement(psyker_brain)
old_brain.Remove(src, special = TRUE, no_id_transfer = TRUE)
qdel(old_brain)
psyker_brain.Insert(src, special = TRUE, drop_if_replaced = FALSE)
if(old_eyes)
qdel(old_eyes)
/datum/religion_rites/nullrod_transformation
name = "Transmogrify"
desc = "Your full power needs a firearm to be realized. You may transform your null rod into one."
ritual_length = 10 SECONDS
///The rod that will be transmogrified.
var/obj/item/nullrod/transformation_target
/datum/religion_rites/nullrod_transformation/perform_rite(mob/living/user, atom/religious_tool)
if(!ishuman(user))
return FALSE
var/mob/living/carbon/human/human_user = user
var/datum/brain_trauma/special/burdened/burden = human_user.has_trauma_type(/datum/brain_trauma/special/burdened)
if(!burden || burden.burden_level < 9)
to_chat(human_user, span_warning("You aren't burdened enough."))
return FALSE
for(var/obj/item/nullrod/null_rod in get_turf(religious_tool))
transformation_target = null_rod
return ..()
to_chat(human_user, span_warning("You need to place a null rod on [religious_tool] to do this!"))
return FALSE
/datum/religion_rites/nullrod_transformation/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
var/obj/item/nullrod/null_rod = transformation_target
transformation_target = null
if(QDELETED(null_rod) || null_rod.loc != get_turf(religious_tool))
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
to_chat(user, span_warning("[null_rod] turns into a gun!"))
user.emote("smile")
qdel(null_rod)
new /obj/item/gun/ballistic/revolver/chaplain(get_turf(religious_tool))
return TRUE
/obj/item/gun/ballistic/revolver/chaplain
name = "chaplain's revolver"
desc = "Holy smokes."
icon_state = "chaplain"
force = 10
fire_sound = 'sound/weapons/gun/revolver/shot.ogg'
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev77
obj_flags = UNIQUE_RENAME
custom_materials = null
actions_types = list(/datum/action/item_action/pray_refill)
/// Needs burden level nine to refill.
var/needs_burden = TRUE
/// List of all possible names and descriptions.
var/static/list/possible_names = list(
"Requiescat" = "May they rest in peace.",
"Requiem" = "They will never reach truth.",
"Vade Retro" = "Having a gun might make exorcisms more effective, who knows?",
"Extra Nos" = "Salvation is given externally.",
"Ordo Salutis" = "First step? Fire.",
"Absolution" = "Free of your sins.",
"Rod of God" = "Splitting the red sea again.",
"Holy Grail" = "You found it!",
"Burning Bush" = "Useful for any burning ambush.",
"Judgement" = "First of all, damn. Alpha much? Dude, so cool, and so are you! Strong, too!",
"Paradiso" = "A divine end to the comedy of life.",
"DVNO" = "Don't need to ask my name to figure out how cool I am.",
"Venus Supermax" = "Did you know nearly everyone working and living on Venus is involved in sulfur extraction? Quite fitting for this weapon of gunpowder.",
"Nirvana" = "The giver of quietude, freedom, and highest happiness.",
"Cerebrum Dispersio" = "Latin for \"brain splitting\". How fitting.",
"Ultimort" = "Your hope dies last.",
"Lifelight" = "No escape, no greater fate to be made.",
"Bendbreaker" = "FRAGILE: Please do not bend or break.",
"Pop Pop" = "The name referring to an onomatopeia (phonetic imitation) of a gun firing.",
"Justice" = "Justice is Splendor.",
"Splendor" = "Splendor is Justice.",
"Revelation" = "Awaken your faith.",
"New Safety M62" = "This model of firearm is popular hundreds of years later due to masculine associations created by the film industry.",
"Unmaker" = "What the !@#%* is this!",
"INKVD" = "Savior of the soul and fighter against dirty thoughts.",
"Life Leech" = "An artifact said to draw its power from the life energy of others.",
"Nullray" = "Starless metal on the barrel imbibes light and routes it to the null place. The grip acrylic is patterned after ley lines.",
"Mortis" = "Put your faith into this weapon working.",
"Ramiel" = "Literally meaning \"God has thundered\". You could even interpret the gunshot as a thunder.",
"Daredevil" = "Hey now, you won't be reckless with this, will you?",
"Lacytanga" = "Rules are written by the strong.",
"A10" = "The fist of God. Keep away from the terrible.",
)
/obj/item/gun/ballistic/revolver/chaplain/Initialize(mapload)
. = ..()
AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE_HOLY)
name = pick(possible_names)
desc = possible_names[name]
/obj/item/gun/ballistic/revolver/chaplain/suicide_act(mob/living/user)
. = ..()
name = "Habemus Papam"
desc = "I announce to you a great joy."
/obj/item/gun/ballistic/revolver/chaplain/attack_self(mob/living/user)
pray_refill(user)
/obj/item/gun/ballistic/revolver/chaplain/proc/pray_refill(mob/living/carbon/human/user)
if(DOING_INTERACTION_WITH_TARGET(user, src) || !istype(user))
return
var/datum/brain_trauma/special/burdened/burden = user.has_trauma_type(/datum/brain_trauma/special/burdened)
if(needs_burden && (!burden || burden.burden_level < 9))
to_chat(user, span_warning("You aren't burdened enough."))
return
user.manual_emote("presses [user.p_their()] palms together...")
if(!do_after(user, 5 SECONDS, src))
balloon_alert(user, "interrupted!")
return
user.say("#Oh great [GLOB.deity], give me the ammunition I need!", forced = "ammo prayer")
magazine.top_off()
user.playsound_local(get_turf(src), 'sound/magic/magic_block_holy.ogg', 50, TRUE)
chamber_round()
/datum/action/item_action/pray_refill
name = "Refill"
desc = "Perform a prayer, to refill your weapon."
/obj/item/ammo_box/magazine/internal/cylinder/rev77
name = "chaplain revolver cylinder"
ammo_type = /obj/item/ammo_casing/c77
caliber = CALIBER_77
max_ammo = 5
/obj/item/ammo_casing/c77
name = ".77 bullet casing"
desc = "A .77 bullet casing."
caliber = CALIBER_77
projectile_type = /obj/projectile/bullet/c77
custom_materials = null
/obj/projectile/bullet/c77
name = ".77 bullet"
damage = 18
ricochets_max = 2
ricochet_chance = 50
ricochet_auto_aim_angle = 10
ricochet_auto_aim_range = 3
wound_bonus = -10
embedding = null
/datum/action/cooldown/spell/pointed/psychic_projection
name = "Psychic Projection"
desc = "Project your psychics into a target to warp their view, and instill absolute terror that will cause them to fire their gun rapidly."
ranged_mousepointer = 'icons/effects/mouse_pointers/cult_target.dmi'
button_icon_state = "blind"
school = SCHOOL_HOLY
cooldown_time = 1 MINUTES
antimagic_flags = MAGIC_RESISTANCE_MIND
spell_max_level = 1
invocation_type = INVOCATION_NONE
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
cast_range = 5
active_msg = "You prepare to psychically project to a target..."
/// Duration of the effects.
var/projection_duration = 10 SECONDS
/datum/action/cooldown/spell/pointed/psychic_projection/is_valid_target(atom/cast_on)
. = ..()
if(!.)
return FALSE
if(!isliving(cast_on))
return FALSE
var/mob/living/living_target = cast_on
return !living_target.has_status_effect(/datum/status_effect/psychic_projection)
/datum/action/cooldown/spell/pointed/psychic_projection/cast(mob/living/cast_on)
. = ..()
if(cast_on.can_block_magic(antimagic_flags))
to_chat(cast_on, span_notice("Your mind feels weird, but it passes momentarily."))
to_chat(owner, span_warning("The spell had no effect!"))
return FALSE
to_chat(cast_on, span_userdanger("Your mind gets twisted!"))
cast_on.emote("scream")
cast_on.apply_status_effect(/datum/status_effect/psychic_projection, projection_duration)
return TRUE
/// Status effect that adds a weird view to its owner and causes them to rapidly shoot a firearm in their general direction.
/datum/status_effect/psychic_projection
id = "psychic_projection"
alert_type = null
remove_on_fullheal = TRUE
tick_interval = 0.1 SECONDS
/// Times the target has dry fired a weapon.
var/times_dry_fired = 0
/// Needs to reach times_dry_fired for the next dry fire to happen.
var/firing_delay = 0
/datum/status_effect/psychic_projection/on_creation(mob/living/new_owner, duration = 10 SECONDS)
src.duration = duration
return ..()
/datum/status_effect/psychic_projection/on_apply()
var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used?.plane_master_controllers[PLANE_MASTERS_GAME]
if(!game_plane_master_controller)
return FALSE
game_plane_master_controller.add_filter("psychic_wave", 10, wave_filter(240, 240, 3, 0, WAVE_SIDEWAYS))
game_plane_master_controller.add_filter("psychic_blur", 10, angular_blur_filter(0, 0, 3))
return TRUE
/datum/status_effect/psychic_projection/on_remove()
var/atom/movable/plane_master_controller/game_plane_master_controller = owner.hud_used?.plane_master_controllers[PLANE_MASTERS_GAME]
if(!game_plane_master_controller)
return
game_plane_master_controller.remove_filter("psychic_blur")
game_plane_master_controller.remove_filter("psychic_wave")
/datum/status_effect/psychic_projection/tick(delta_time, times_fired)
var/obj/item/gun/held_gun = owner?.is_holding_item_of_type(/obj/item/gun)
if(!held_gun)
return
if(!held_gun.can_shoot())
if(firing_delay < times_dry_fired)
firing_delay++
return
firing_delay = 0
times_dry_fired++
else
times_dry_fired = 0
var/turf/target_turf = get_offset_target_turf(get_ranged_target_turf(owner, owner.dir, 7), dx = rand(-1, 1), dy = rand(-1, 1))
held_gun.process_fire(target_turf, owner, TRUE, null, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
held_gun.semicd = FALSE
/datum/action/cooldown/spell/charged/psychic_booster
name = "Psychic Booster"
desc = "Charge up your mind to shoot firearms faster and home in on your targets. Think smarter, not harder."
button_icon_state = "projectile"
sound = 'sound/weapons/gun/shotgun/rack.ogg'
school = SCHOOL_HOLY
cooldown_time = 1 MINUTES
antimagic_flags = MAGIC_RESISTANCE_MIND
spell_max_level = 1
invocation_type = INVOCATION_NONE
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
channel_message = span_notice("You focus on your trigger fingers...")
charge_overlay_icon = 'icons/effects/effects.dmi'
charge_overlay_state = "purplesparkles"
channel_time = 5 SECONDS
/// Are we currently active?
var/boosted = FALSE
/// How long the effect lasts for?
var/effect_time = 10 SECONDS
/datum/action/cooldown/spell/charged/psychic_booster/Destroy()
if(boosted)
stop_effects()
return ..()
/datum/action/cooldown/spell/charged/psychic_booster/Remove(mob/living/remove_from)
if(boosted)
stop_effects()
return ..()
/datum/action/cooldown/spell/charged/psychic_booster/cast(atom/cast_on)
. = ..()
if(boosted)
return
boosted = TRUE
to_chat(owner, span_boldnotice("Your trigger fingers feel stronger."))
ADD_TRAIT(cast_on, TRAIT_DOUBLE_TAP, type)
RegisterSignal(cast_on, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, PROC_REF(modify_projectile))
addtimer(CALLBACK(src, PROC_REF(stop_effects)), effect_time)
/datum/action/cooldown/spell/charged/psychic_booster/proc/stop_effects()
boosted = FALSE
to_chat(owner, span_danger("Your trigger fingers feel weaker."))
REMOVE_TRAIT(owner, TRAIT_DOUBLE_TAP, type)
UnregisterSignal(owner, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE)
/datum/action/cooldown/spell/charged/psychic_booster/proc/modify_projectile(datum/source, obj/projectile/bullet, atom/firer, atom/original_target)
var/atom/target = original_target
if(isturf(target) || (isobj(target) && !target.density)) //if weird target, we try to compensate in our homing
for(var/mob/living/shooting_target in range(1, get_turf(target)))
if(shooting_target == firer)
continue
target = shooting_target
break
if(!bullet.can_hit_target(target, direct_target = TRUE, ignore_loc = TRUE))
return
bullet.original = target
bullet.homing_turn_speed = 30
bullet.set_homing_target(target)
/datum/action/cooldown/spell/forcewall/psychic_wall
name = "Psychic Wall"
desc = "Form a psychic wall, able to deflect projectiles and prevent things from going through."
school = SCHOOL_HOLY
cooldown_time = 30 SECONDS
cooldown_reduction_per_rank = 0 SECONDS
antimagic_flags = MAGIC_RESISTANCE_MIND
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
spell_max_level = 1
invocation_type = INVOCATION_NONE
wall_type = /obj/effect/forcefield/psychic
/datum/action/cooldown/spell/forcewall/psychic_wall/spawn_wall(turf/cast_turf)
. = ..()
play_fov_effect(cast_turf, 5, "forcefield", time = 10 SECONDS)
@@ -0,0 +1,174 @@
///Makes the person holy, but they now also have to follow the honorbound code (CBT). Actually earns favor, convincing others to uphold the code (tm) is not easy
/datum/religion_rites/deaconize
name = "Join Crusade"
desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
They will become honorbound like you, and you will gain a massive favor boost!"
ritual_length = 30 SECONDS
ritual_invocations = list(
"A good, honorable crusade against evil is required.",
"We need the righteous ...",
"... the unflinching ...",
"... and the just.",
"Sinners must be silenced ...",
)
invoke_msg = "... And the code must be upheld!"
///the invited crusader
var/mob/living/carbon/human/new_crusader
/datum/religion_rites/deaconize/perform_rite(mob/living/user, atom/religious_tool)
var/datum/religion_sect/honorbound/sect = GLOB.religious_sect
if(!ismovable(religious_tool))
to_chat(user, span_warning("This rite requires a religious device that individuals can be buckled to."))
return FALSE
var/atom/movable/movable_reltool = religious_tool
if(!movable_reltool)
return FALSE
if(!LAZYLEN(movable_reltool.buckled_mobs))
to_chat(user, span_warning("Nothing is buckled to the altar!"))
return FALSE
for(var/mob/living/carbon/human/possible_crusader in movable_reltool.buckled_mobs)
if(possible_crusader.stat != CONSCIOUS)
to_chat(user, span_warning("[possible_crusader] needs to be alive and conscious to join the crusade!"))
return FALSE
if(TRAIT_GENELESS in possible_crusader.dna.species.inherent_traits)
to_chat(user, span_warning("This species disgusts [GLOB.deity]! They would never be allowed to join the crusade!"))
return FALSE
if(possible_crusader in sect.currently_asking)
to_chat(user, span_warning("Wait for them to decide on whether to join or not!"))
return FALSE
if(!(possible_crusader in sect.possible_crusaders))
INVOKE_ASYNC(sect, /datum/religion_sect/honorbound.proc/invite_crusader, possible_crusader)
to_chat(user, span_notice("They have been given the option to consider joining the crusade against evil. Wait for them to decide and try again."))
return FALSE
new_crusader = possible_crusader
return ..()
/datum/religion_rites/deaconize/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
var/mob/living/carbon/human/joining_now = new_crusader
new_crusader = null
if(!(joining_now in religious_tool.buckled_mobs)) //checks one last time if the right corpse is still buckled
to_chat(user, span_warning("The new member is no longer on the altar!"))
return FALSE
if(joining_now.stat != CONSCIOUS)
to_chat(user, span_warning("The new member has to stay alive for the rite to work!"))
return FALSE
if(!joining_now.mind)
to_chat(user, span_warning("The new member has no mind!"))
return FALSE
if(joining_now.mind.has_antag_datum(/datum/antagonist/cult))//what the fuck?!
to_chat(user, span_warning("[GLOB.deity] has seen a true, dark evil in [joining_now]'s heart, and they have been smitten!"))
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
joining_now.gib(TRUE)
return FALSE
var/datum/brain_trauma/special/honorbound/honor = user.has_trauma_type(/datum/brain_trauma/special/honorbound)
if(joining_now in honor.guilty)
honor.guilty -= joining_now
GLOB.religious_sect.adjust_favor(200, user)
to_chat(user, span_notice("[GLOB.deity] has bound [joining_now] to the code! They are now a holy role! (albeit the lowest level of such)"))
joining_now.mind.holy_role = HOLY_ROLE_DEACON
GLOB.religious_sect.on_conversion(joining_now)
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
return TRUE
///Mostly useless funny rite for forgiving someone, making them innocent once again.
/datum/religion_rites/forgive
name = "Forgive"
desc = "Forgives someone, making them no longer considered guilty. A kind gesture, all things considered!"
invoke_msg = "You are absolved of sin."
var/mob/living/who
/datum/religion_rites/forgive/perform_rite(mob/living/carbon/human/user, atom/religious_tool)
if(!ishuman(user))
return FALSE
var/datum/brain_trauma/special/honorbound/honor = user.has_trauma_type(/datum/brain_trauma/special/honorbound)
if(!honor)
return FALSE
if(!length(honor.guilty))
to_chat(user, span_warning("[GLOB.deity] is holding no grudges to forgive."))
return FALSE
var/forgiven_choice = tgui_input_list(user, "Choose one of [GLOB.deity]'s guilty to forgive", "Forgive", honor.guilty)
if(isnull(forgiven_choice))
return FALSE
who = forgiven_choice
return ..()
/datum/religion_rites/forgive/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
if(in_range(user, religious_tool))
return FALSE
var/datum/brain_trauma/special/honorbound/honor = user.has_trauma_type(/datum/brain_trauma/special/honorbound)
if(!honor) //edge case
return FALSE
honor.guilty -= who
who = null
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
return TRUE
/datum/religion_rites/summon_rules
name = "Summon Honorbound Rules"
desc = "Enscribes a paper with the honorbound rules and regulations."
invoke_msg = "Bring forth the holy writ!"
///paper to turn into holy writ
var/obj/item/paper/writ_target
/datum/religion_rites/summon_rules/perform_rite(mob/living/user, atom/religious_tool)
for(var/obj/item/paper/could_writ in get_turf(religious_tool))
if(istype(could_writ, /obj/item/paper/holy_writ))
continue
if(could_writ.get_total_length()) //blank paper pls
continue
writ_target = could_writ //PLEASE SIGN MY AUTOGRAPH
return ..()
to_chat(user, span_warning("You need to place blank paper on [religious_tool] to do this!"))
return FALSE
/datum/religion_rites/summon_rules/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
var/obj/item/paper/autograph = writ_target
var/turf/tool_turf = get_turf(religious_tool)
writ_target = null
if(QDELETED(autograph) || !(tool_turf == autograph.loc)) //check if the same food is still there
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
autograph.visible_message(span_notice("Words magically form on [autograph]!"))
playsound(tool_turf, 'sound/effects/pray.ogg', 50, TRUE)
new /obj/item/paper/holy_writ(tool_turf)
qdel(autograph)
return TRUE
/obj/item/paper/holy_writ
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll"
slot_flags = null
show_written_words = FALSE
//info set in here because we need GLOB.deity
/obj/item/paper/holy_writ/Initialize(mapload)
add_filter("holy_outline", 9, list("type" = "outline", "color" = "#fdff6c"))
name = "[GLOB.deity]'s honorbound rules"
default_raw_text = {"[GLOB.deity]'s honorbound rules:
<br>
1.) Thou shalt not attack the unready!<br>
Those who are not ready for battle should not be wrought low. The evil of this world must lose
in a fair battle if you are to conquer them completely.
<br>
<br>
2.) Thou shalt not attack the just!<br>
Those who fight for justice and good must not be harmed. Security is uncorruptable and must
be respected. Healers are mostly uncorruptable and if you are truly sure Medical has fallen
to the scourge of evil, use a declaration of evil.
<br>
<br>
3.) Thou shalt not attack the innocent!<br>
There is no honor on a pre-emptive strike, unless they are truly evil vermin.
Those who are guilty will either lay a hand on you first, or you may declare their evil.
<br>
<br>
4.) Thou shalt not use profane magicks!<br>
You are not a warlock, you are an honorable warrior. There is nothing more corruptive than
the vile magicks used by witches, warlocks, and necromancers. There are exceptions to this rule.<br>
You may use holy magic, and, if you recruit one, the mime may use holy mimery. Restoration has also
been allowed as it is a school focused on the light and mending of this world.
"}
return ..()
@@ -1,21 +1,14 @@
///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>"
/datum/brain_trauma/special/honorbound
name = "Dogmatic Compulsions"
desc = "Patient feels compelled to follow supposed \"rules of combat\"."
scan_desc = "damaged frontal lobe"
gain_text = span_notice("You feel honorbound!")
lose_text = span_warning("You feel unshackled from your code of honor!")
/// list of guilty people
var/list/guilty = list()
/datum/mutation/human/honorbound/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
/datum/brain_trauma/special/honorbound/on_gain()
//moodlet
owner.add_mood_event("honorbound", /datum/mood_event/honorbound)
//checking spells cast by honorbound
@@ -31,8 +24,11 @@
//signal that checks for dishonorable attacks
RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(attack_honor))
var/datum/action/cooldown/spell/pointed/declare_evil = new(src)
declare_evil.Grant(owner)
return ..()
/datum/mutation/human/honorbound/on_losing(mob/living/carbon/human/owner)
/datum/brain_trauma/special/honorbound/on_lose(silent)
owner.clear_mood_event("honorbound")
UnregisterSignal(owner, list(
COMSIG_PARENT_ATTACKBY,
@@ -44,11 +40,11 @@
COMSIG_MOB_CLICKON,
COMSIG_MOB_CAST_SPELL,
COMSIG_MOB_FIRED_GUN,
))
. = ..()
))
return ..()
/// 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 to see if the trauma allows us to attack a target
/datum/brain_trauma/special/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])
@@ -69,14 +65,14 @@
return (COMSIG_MOB_CANCEL_CLICKON)
/**
* Called by hooked signals whenever someone attacks the person with this mutation
* Called by hooked signals whenever someone attacks the person with this trauma
* 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)
/datum/brain_trauma/special/honorbound/proc/guilty(mob/living/user, declaration = FALSE)
if(user in guilty)
return
var/datum/mind/guilty_conscience = user.mind
@@ -95,10 +91,10 @@
* Called by attack_honor signal to check whether an attack should be allowed or not
*
* Arguments:
* * honorbound_human: typecasted owner of mutation
* * honorbound_human: typecasted owner of the trauma
* * target_creature: person honorbound_human is attacking
*/
/datum/mutation/human/honorbound/proc/is_honorable(mob/living/carbon/human/honorbound_human, mob/living/target_creature)
/datum/brain_trauma/special/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)
@@ -124,25 +120,25 @@
return TRUE
// SIGNALS THAT ARE FOR BEING ATTACKED FIRST (GUILTY)
/datum/mutation/human/honorbound/proc/attackby_guilt(datum/source, obj/item/I, mob/attacker)
/datum/brain_trauma/special/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)
/datum/brain_trauma/special/honorbound/proc/hulk_guilt(datum/source, mob/attacker)
SIGNAL_HANDLER
guilty(attacker)
/datum/mutation/human/honorbound/proc/hand_guilt(datum/source, mob/living/attacker)
/datum/brain_trauma/special/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)
/datum/brain_trauma/special/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)
/datum/brain_trauma/special/honorbound/proc/bullet_guilt(datum/source, obj/projectile/proj)
SIGNAL_HANDLER
var/mob/living/shot_honorbound = source
var/static/list/guilty_projectiles = typecacheof(list(
@@ -157,7 +153,7 @@
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)
/datum/brain_trauma/special/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
@@ -167,11 +163,11 @@
guilty(thrown_by)
//spell checking
/datum/mutation/human/honorbound/proc/spell_check(mob/user, datum/action/cooldown/spell/spell_cast)
/datum/brain_trauma/special/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)
/datum/brain_trauma/special/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
@@ -182,19 +178,19 @@
* Called when a spell is casted or a magic gun is fired, checks the signal and punishes accordingly
*
* Arguments:
* * user: typecasted owner of mutation
* * user: typecasted owner of trauma
* * school: school of magic casted from the staff/spell
*/
/datum/mutation/human/honorbound/proc/punishment(mob/living/carbon/human/user, school)
/datum/brain_trauma/special/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
qdel(src)
owner.add_mood_event("honorbound", /datum/mood_event/banished) //add mood event after we already cleared our events
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!"))
@@ -219,8 +215,8 @@
/// 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
/// A ref to our owner's honorbound trauma
var/datum/brain_trauma/special/honorbound/honor_trauma
/// 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!"
@@ -230,9 +226,9 @@
/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
if(honor_trauma)
UnregisterSignal(honor_trauma, COMSIG_PARENT_QDELETING)
honor_trauma = null
return ..()
/datum/action/cooldown/spell/pointed/declare_evil/Grant(mob/grant_to)
@@ -240,21 +236,21 @@
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))
var/datum/brain_trauma/special/honorbound/honorbound = human_owner.has_trauma_type(/datum/brain_trauma/special/honorbound)
if(QDELETED(honorbound))
return FALSE
RegisterSignal(honor_mut, COMSIG_PARENT_QDELETING, PROC_REF(on_honor_mutation_lost))
honor_mutation = honor_mut
RegisterSignal(honorbound, COMSIG_PARENT_QDELETING, PROC_REF(on_honor_trauma_lost))
honor_trauma = honorbound
return ..()
/datum/action/cooldown/spell/pointed/declare_evil/Remove(mob/living/remove_from)
. = ..()
UnregisterSignal(honor_mutation, COMSIG_PARENT_QDELETING)
honor_mutation = null
UnregisterSignal(honor_trauma, COMSIG_PARENT_QDELETING)
honor_trauma = 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)
/// If we lose our honor trauma somehow, self-delete (and clear references)
/datum/action/cooldown/spell/pointed/declare_evil/proc/on_honor_trauma_lost(datum/source)
SIGNAL_HANDLER
qdel(src)
@@ -264,9 +260,6 @@
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!"))
@@ -315,4 +308,4 @@
/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)
honor_trauma.guilty(cast_on, declaration = TRUE)
+28 -38
View File
@@ -20,8 +20,6 @@
var/alignment = ALIGNMENT_GOOD
/// Does this require something before being available as an option?
var/starter = TRUE
/// species traits that block you from picking
var/invalidating_qualities = NONE
/// The Sect's 'Mana'
var/favor = 0 //MANA!
/// The max amount of favor the sect can have
@@ -271,6 +269,33 @@
#undef GREEDY_HEAL_COST
/datum/religion_sect/burden
name = "Punished God"
quote = "To feel the freedom, you must first understand captivity."
desc = "Incapacitate yourself in any way possible. Bad mutations, lost limbs, traumas, \
even addictions. You will learn the secrets of the universe from your defeated shell."
tgui_icon = "user-injured"
altar_icon_state = "convertaltar-burden"
alignment = ALIGNMENT_NEUT
candle_overlay = FALSE
rites_list = list(/datum/religion_rites/nullrod_transformation)
/datum/religion_sect/burden/on_conversion(mob/living/carbon/human/new_convert)
..()
if(!ishuman(new_convert))
to_chat(new_convert, span_warning("[GLOB.deity] needs higher level creatures to fully comprehend the suffering. You are not burdened."))
return
new_convert.gain_trauma(/datum/brain_trauma/special/burdened, TRAUMA_RESILIENCE_MAGIC)
/datum/religion_sect/burden/tool_examine(mob/living/carbon/human/burdened) //display burden level
if(!ishuman(burdened))
return FALSE
var/datum/brain_trauma/special/burdened/burden = burdened.has_trauma_type(/datum/brain_trauma/special/burdened)
if(burden)
return "You are at burden level [burden.burden_level]/9."
return "You are not burdened."
/datum/religion_sect/honorbound
name = "Honorbound God"
quote = "A good, honorable crusade against evil is required."
@@ -279,7 +304,6 @@
tgui_icon = "scroll"
altar_icon_state = "convertaltar-white"
alignment = ALIGNMENT_GOOD
invalidating_qualities = TRAIT_GENELESS
rites_list = list(/datum/religion_rites/deaconize, /datum/religion_rites/forgive, /datum/religion_rites/summon_rules)
///people who have agreed to join the crusade, and can be deaconized
var/list/possible_crusaders = list()
@@ -302,41 +326,7 @@
if(!ishuman(new_convert))
to_chat(new_convert, span_warning("[GLOB.deity] has no respect for lower creatures, and refuses to make you honorbound."))
return FALSE
if(TRAIT_GENELESS in new_convert.dna.species.inherent_traits)
to_chat(new_convert, span_warning("[GLOB.deity] has deemed your species as one that could never show honor."))
return FALSE
var/datum/dna/holy_dna = new_convert.dna
holy_dna.add_mutation(/datum/mutation/human/honorbound)
/datum/religion_sect/burden
name = "Punished God"
quote = "To feel the freedom, you must first understand captivity."
desc = "Incapacitate yourself in any way possible. Bad mutations, lost limbs, traumas, \
even addictions. You will learn the secrets of the universe from your defeated shell."
tgui_icon = "user-injured"
altar_icon_state = "convertaltar-burden"
alignment = ALIGNMENT_NEUT
invalidating_qualities = TRAIT_GENELESS
candle_overlay = FALSE
/datum/religion_sect/burden/on_conversion(mob/living/carbon/human/new_convert)
..()
if(!ishuman(new_convert))
to_chat(new_convert, span_warning("[GLOB.deity] needs higher level creatures to fully comprehend the suffering. You are not burdened."))
return
if(TRAIT_GENELESS in new_convert.dna.species.inherent_traits)
to_chat(new_convert, span_warning("[GLOB.deity] cannot help a species such as yourself comprehend the suffering. You are not burdened."))
return
var/datum/dna/holy_dna = new_convert.dna
holy_dna.add_mutation(/datum/mutation/human/burdened)
/datum/religion_sect/burden/tool_examine(mob/living/carbon/human/burdened) //display burden level
if(!ishuman(burdened))
return FALSE
var/datum/mutation/human/burdened/burdenmut = burdened.dna.check_mutation(/datum/mutation/human/burdened)
if(burdenmut)
return "You are at burden level [burdenmut.burden_level]/6."
return "You are not burdened."
new_convert.gain_trauma(/datum/brain_trauma/special/honorbound, TRAUMA_RESILIENCE_MAGIC)
#define MINIMUM_YUCK_REQUIRED 5
+11 -185
View File
@@ -129,14 +129,16 @@
..()
var/altar_turf = get_turf(religious_tool)
var/blessing = pick(
/obj/item/organ/internal/cyberimp/arm/surgery,
/obj/item/organ/internal/cyberimp/eyes/hud/diagnostic,
/obj/item/organ/internal/cyberimp/eyes/hud/medical,
/obj/item/organ/internal/cyberimp/mouth/breathing_tube,
/obj/item/organ/internal/cyberimp/chest/thrusters,
/obj/item/organ/internal/eyes/robotic/glow)
/obj/item/organ/internal/cyberimp/arm/surgery,
/obj/item/organ/internal/cyberimp/eyes/hud/diagnostic,
/obj/item/organ/internal/cyberimp/eyes/hud/medical,
/obj/item/organ/internal/cyberimp/mouth/breathing_tube,
/obj/item/organ/internal/cyberimp/chest/thrusters,
/obj/item/organ/internal/eyes/robotic/glow,
)
new blessing(altar_turf)
return TRUE
/**** Pyre God ****/
///apply a bunch of fire immunity effect to clothing
@@ -307,182 +309,6 @@
playsound(get_turf(religious_tool), 'sound/effects/cashregister.ogg', 60, TRUE)
return TRUE
/*********Honorbound God**********/
///Makes the person holy, but they now also have to follow the honorbound code (CBT). Actually earns favor, convincing others to uphold the code (tm) is not easy
/datum/religion_rites/deaconize
name = "Join Crusade"
desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
They will become honorbound like you, and you will gain a massive favor boost!"
ritual_length = 30 SECONDS
ritual_invocations = list(
"A good, honorable crusade against evil is required.",
"We need the righteous ...",
"... the unflinching ...",
"... and the just.",
"Sinners must be silenced ...",)
invoke_msg = "... And the code must be upheld!"
///the invited crusader
var/mob/living/carbon/human/new_crusader
/datum/religion_rites/deaconize/perform_rite(mob/living/user, atom/religious_tool)
var/datum/religion_sect/honorbound/sect = GLOB.religious_sect
if(!ismovable(religious_tool))
to_chat(user, span_warning("This rite requires a religious device that individuals can be buckled to."))
return FALSE
var/atom/movable/movable_reltool = religious_tool
if(!movable_reltool)
return FALSE
if(!LAZYLEN(movable_reltool.buckled_mobs))
to_chat(user, span_warning("Nothing is buckled to the altar!"))
return FALSE
for(var/mob/living/carbon/human/possible_crusader in movable_reltool.buckled_mobs)
if(possible_crusader.stat != CONSCIOUS)
to_chat(user, span_warning("[possible_crusader] needs to be alive and conscious to join the crusade!"))
return FALSE
if(TRAIT_GENELESS in possible_crusader.dna.species.inherent_traits)
to_chat(user, span_warning("This species disgusts [GLOB.deity]! They would never be allowed to join the crusade!"))
return FALSE
if(possible_crusader in sect.currently_asking)
to_chat(user, span_warning("Wait for them to decide on whether to join or not!"))
return FALSE
if(!(possible_crusader in sect.possible_crusaders))
INVOKE_ASYNC(sect, TYPE_PROC_REF(/datum/religion_sect/honorbound, invite_crusader), possible_crusader)
to_chat(user, span_notice("They have been given the option to consider joining the crusade against evil. Wait for them to decide and try again."))
return FALSE
new_crusader = possible_crusader
return ..()
/datum/religion_rites/deaconize/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
var/mob/living/carbon/human/joining_now = new_crusader
new_crusader = null
if(!(joining_now in religious_tool.buckled_mobs)) //checks one last time if the right corpse is still buckled
to_chat(user, span_warning("The new member is no longer on the altar!"))
return FALSE
if(joining_now.stat != CONSCIOUS)
to_chat(user, span_warning("The new member has to stay alive for the rite to work!"))
return FALSE
if(!joining_now.mind)
to_chat(user, span_warning("The new member has no mind!"))
return FALSE
if(joining_now.mind.has_antag_datum(/datum/antagonist/cult))//what the fuck?!
to_chat(user, span_warning("[GLOB.deity] has seen a true, dark evil in [joining_now]'s heart, and they have been smitten!"))
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
joining_now.gib(TRUE)
return FALSE
var/datum/mutation/human/honorbound/honormut = user.dna.check_mutation(/datum/mutation/human/honorbound)
if(joining_now in honormut.guilty)
honormut.guilty -= joining_now
GLOB.religious_sect.adjust_favor(200, user)
to_chat(user, span_notice("[GLOB.deity] has bound [joining_now] to the code! They are now a holy role! (albeit the lowest level of such)"))
joining_now.mind.holy_role = HOLY_ROLE_DEACON
GLOB.religious_sect.on_conversion(joining_now)
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
return TRUE
///Mostly useless funny rite for forgiving someone, making them innocent once again.
/datum/religion_rites/forgive
name = "Forgive"
desc = "Forgives someone, making them no longer considered guilty. A kind gesture, all things considered!"
invoke_msg = "You are absolved of sin."
var/mob/living/who
/datum/religion_rites/forgive/perform_rite(mob/living/carbon/human/user, atom/religious_tool)
if(!ishuman(user))
return FALSE
var/datum/mutation/human/honorbound/honormut = user.dna.check_mutation(/datum/mutation/human/honorbound)
if(!honormut)
return FALSE
if(!length(honormut.guilty))
to_chat(user, span_warning("[GLOB.deity] is holding no grudges to forgive."))
return FALSE
var/forgiven_choice = tgui_input_list(user, "Choose one of [GLOB.deity]'s guilty to forgive", "Forgive", honormut.guilty)
if(isnull(forgiven_choice))
return FALSE
who = forgiven_choice
return ..()
/datum/religion_rites/forgive/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
..()
if(in_range(user, religious_tool))
return FALSE
var/datum/mutation/human/honorbound/honormut = user.dna.check_mutation(/datum/mutation/human/honorbound)
if(!honormut) //edge case
return FALSE
honormut.guilty -= who
who = null
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
return TRUE
/datum/religion_rites/summon_rules
name = "Summon Honorbound Rules"
desc = "Enscribes a paper with the honorbound rules and regulations."
invoke_msg = "Bring forth the holy writ!"
///paper to turn into holy writ
var/obj/item/paper/writ_target
/datum/religion_rites/summon_rules/perform_rite(mob/living/user, atom/religious_tool)
for(var/obj/item/paper/could_writ in get_turf(religious_tool))
if(istype(could_writ, /obj/item/paper/holy_writ))
continue
if(could_writ.get_total_length()) //blank paper pls
continue
writ_target = could_writ //PLEASE SIGN MY AUTOGRAPH
return ..()
to_chat(user, span_warning("You need to place blank paper on [religious_tool] to do this!"))
return FALSE
/datum/religion_rites/summon_rules/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
var/obj/item/paper/autograph = writ_target
var/turf/tool_turf = get_turf(religious_tool)
writ_target = null
if(QDELETED(autograph) || !(tool_turf == autograph.loc)) //check if the same food is still there
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
autograph.visible_message(span_notice("Words magically form on [autograph]!"))
playsound(tool_turf, 'sound/effects/pray.ogg', 50, TRUE)
new /obj/item/paper/holy_writ(tool_turf)
qdel(autograph)
return TRUE
/obj/item/paper/holy_writ
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll"
slot_flags = null
show_written_words = FALSE
//info set in here because we need GLOB.deity
/obj/item/paper/holy_writ/Initialize(mapload)
add_filter("holy_outline", 9, list("type" = "outline", "color" = "#fdff6c"))
name = "[GLOB.deity]'s honorbound rules"
default_raw_text = {"[GLOB.deity]'s honorbound rules:
<br>
1.) Thou shalt not attack the unready!<br>
Those who are not ready for battle should not be wrought low. The evil of this world must lose
in a fair battle if you are to conquer them completely.
<br>
<br>
2.) Thou shalt not attack the just!<br>
Those who fight for justice and good must not be harmed. Security is uncorruptable and must
be respected. Healers are mostly uncorruptable and if you are truly sure Medical has fallen
to the scourge of evil, use a declaration of evil.
<br>
<br>
3.) Thou shalt not attack the innocent!<br>
There is no honor on a pre-emptive strike, unless they are truly evil vermin.
Those who are guilty will either lay a hand on you first, or you may declare their evil.
<br>
<br>
4.) Thou shalt not use profane magicks!<br>
You are not a warlock, you are an honorable warrior. There is nothing more corruptive than
the vile magicks used by witches, warlocks, and necromancers. There are exceptions to this rule.<br>
You may use holy magic, and, if you recruit one, the mime may use holy mimery. Restoration has also
been allowed as it is a school focused on the light and mending of this world.
"}
. = ..()
/*********Maintenance God**********/
/datum/religion_rites/maint_adaptation
@@ -507,7 +333,7 @@
/datum/religion_rites/maint_adaptation/invoke_effect(mob/living/user, atom/movable/religious_tool)
..()
to_chat(user, span_warning("You feel your genes rattled and reshaped. <b>You're becoming something new.</b>"))
user.emote("laughs")
user.emote("laugh")
ADD_TRAIT(user, TRAIT_HOPELESSLY_ADDICTED, "maint_adaptation")
//addiction sends some nasty mood effects but we want the maint adaption to be enjoyed like a fine wine
user.add_mood_event("maint_adaptation", /datum/mood_event/maintenance_adaptation)
@@ -574,7 +400,7 @@
to_chat(user, span_warning("Your target left the altar!"))
return FALSE
to_chat(user, span_warning("[moldify] becomes rancid!"))
user.emote("laughs")
user.emote("laugh")
new /obj/item/food/badrecipe/moldy(get_turf(religious_tool))
qdel(moldify)
return TRUE
@@ -605,7 +431,7 @@
to_chat(user, span_warning("[padala] reshapes into a totem!"))
if(!padala.use(1))//use one wood
return
user.emote("laughs")
user.emote("laugh")
new /obj/item/ritual_totem(altar_turf)
return TRUE
@@ -11,6 +11,8 @@
var/currently_channeling = FALSE
/// How long it takes to channel the spell.
var/channel_time = 10 SECONDS
/// Flags of the do_after
var/channel_flags = IGNORE_USER_LOC_CHANGE|IGNORE_HELD_ITEM
// Overlay optional, applied when we start channelling
/// What icon should we use for our overlay
@@ -75,7 +77,7 @@
currently_channeling = TRUE
UpdateButtons(status_only = TRUE)
if(!do_after(cast_on, channel_time, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_HELD_ITEM)))
if(!do_after(cast_on, channel_time, timed_action_flags = channel_flags))
stop_channel_effect(cast_on)
return . | SPELL_CANCEL_CAST
@@ -17,15 +17,16 @@
/datum/action/cooldown/spell/forcewall/cast(atom/cast_on)
. = ..()
new wall_type(get_turf(owner), owner)
for(var/turf/cast_turf as anything in get_turfs())
spawn_wall(cast_turf)
if(owner.dir == SOUTH || owner.dir == NORTH)
new wall_type(get_step(owner, EAST), owner, antimagic_flags)
new wall_type(get_step(owner, WEST), owner, antimagic_flags)
/// This proc returns all the turfs on which we will spawn the walls.
/datum/action/cooldown/spell/forcewall/proc/get_turfs()
return list(get_turf(owner), get_step(owner, turn(owner.dir, 90)), get_step(owner, turn(owner.dir, 270)))
else
new wall_type(get_step(owner, NORTH), owner, antimagic_flags)
new wall_type(get_step(owner, SOUTH), owner, antimagic_flags)
/// This proc spawns a wall on the given turf.
/datum/action/cooldown/spell/forcewall/proc/spawn_wall(turf/cast_turf)
new wall_type(cast_turf, owner, antimagic_flags)
/datum/action/cooldown/spell/forcewall/cult
name = "Shield"
+21 -18
View File
@@ -186,28 +186,31 @@
else
return SURGERY_STEP_FAIL
/datum/surgery_step/manipulate_organs/success(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
/datum/surgery_step/manipulate_organs/success(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
if (target_zone == BODY_ZONE_PRECISE_EYES)
target_zone = check_zone(target_zone)
if(current_type == "insert")
var/obj/item/apparatus
if(istype(tool, /obj/item/borg/apparatus/organ_storage))
target_organ = tool.contents[1]
tool.icon_state = initial(tool.icon_state)
tool.desc = initial(tool.desc)
tool.cut_overlays()
tool = target_organ
else
target_organ = tool
apparatus = tool
tool = tool.contents[1]
target_organ = tool
user.temporarilyRemoveItemFromInventory(target_organ, TRUE)
target_organ.Insert(target)
display_results(
user,
target,
span_notice("You insert [tool] into [target]'s [parse_zone(target_zone)]."),
span_notice("[user] inserts [tool] into [target]'s [parse_zone(target_zone)]!"),
span_notice("[user] inserts something into [target]'s [parse_zone(target_zone)]!"),
)
display_pain(target, "Your [parse_zone(target_zone)] throbs with pain as your new [tool] comes to life!")
if(target_organ.Insert(target))
if(apparatus)
apparatus.icon_state = initial(apparatus.icon_state)
apparatus.desc = initial(apparatus.desc)
apparatus.cut_overlays()
display_results(
user,
target,
span_notice("You insert [tool] into [target]'s [parse_zone(target_zone)]."),
span_notice("[user] inserts [tool] into [target]'s [parse_zone(target_zone)]!"),
span_notice("[user] inserts something into [target]'s [parse_zone(target_zone)]!"),
)
display_pain(target, "Your [parse_zone(target_zone)] throbs with pain as your new [tool] comes to life!")
else
target_organ.forceMove(target.loc)
else if(current_type == "extract")
if(target_organ && target_organ.owner == target)
@@ -230,7 +233,7 @@
span_notice("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!"),
span_notice("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!"),
)
return FALSE
return ..()
///You can never use this MUHAHAHAHAHAHAH (because its the byond version of abstract)
/datum/surgery_step/manipulate_organs/proc/can_use_organ(mob/user, obj/item/organ/organ)
+2 -2
View File
@@ -201,9 +201,9 @@
visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car lets out a comedic toot."))
playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100)
for(var/mob/living/L in orange(loc, 6))
L.emote("laughs")
L.emote("laugh")
for(var/mob/living/L as anything in occupants)
L.emote("laughs")
L.emote("laugh")
///resets the icon and iconstate of the clowncar after it was set to singulo states
/obj/vehicle/sealed/car/clowncar/proc/reset_icon()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 KiB

After

Width:  |  Height:  |  Size: 931 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 MiB

After

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 KiB

After

Width:  |  Height:  |  Size: 330 KiB

+5 -2
View File
@@ -816,6 +816,7 @@
#include "code\datums\components\deployable.dm"
#include "code\datums\components\drift.dm"
#include "code\datums\components\earprotection.dm"
#include "code\datums\components\echolocation.dm"
#include "code\datums\components\edit_complainer.dm"
#include "code\datums\components\effect_remover.dm"
#include "code\datums\components\egg_layer.dm"
@@ -1220,8 +1221,6 @@
#include "code\datums\mutations\touch.dm"
#include "code\datums\mutations\void_magnet.dm"
#include "code\datums\mutations\webbing.dm"
#include "code\datums\mutations\holy_mutation\burdened.dm"
#include "code\datums\mutations\holy_mutation\honorbound.dm"
#include "code\datums\proximity_monitor\field.dm"
#include "code\datums\proximity_monitor\proximity_monitor.dm"
#include "code\datums\proximity_monitor\fields\gravity.dm"
@@ -4312,8 +4311,12 @@
#include "code\modules\religion\religion_sects.dm"
#include "code\modules\religion\religion_structures.dm"
#include "code\modules\religion\rites.dm"
#include "code\modules\religion\burdened\burdened_trauma.dm"
#include "code\modules\religion\burdened\psyker.dm"
#include "code\modules\religion\festival\instrument_rites.dm"
#include "code\modules\religion\festival\note_particles.dm"
#include "code\modules\religion\honorbound\honorbound_rites.dm"
#include "code\modules\religion\honorbound\honorbound_trauma.dm"
#include "code\modules\religion\sparring\ceremonial_gear.dm"
#include "code\modules\religion\sparring\sparring_contract.dm"
#include "code\modules\religion\sparring\sparring_datum.dm"