diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index a036a862371..c6bdc32cfed 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -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
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index d68948ce0bd..f3a78ae88fd 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -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
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index b7b8a10e255..c80830f067e 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -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.
diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm
index 12ba1d90545..5fe001c8f05 100644
--- a/code/__DEFINES/projectiles.dm
+++ b/code/__DEFINES/projectiles.dm
@@ -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.
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index fab32f7e245..7dc9d0f3656 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -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"
diff --git a/code/datums/components/anti_magic.dm b/code/datums/components/anti_magic.dm
index 91cfe5c9b4a..13ec8725aa2 100644
--- a/code/datums/components/anti_magic.dm
+++ b/code/datums/components/anti_magic.dm
@@ -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
diff --git a/code/datums/components/echolocation.dm b/code/datums/components/echolocation.dm
new file mode 100644
index 00000000000..f7d58bb9a1b
--- /dev/null
+++ b/code/datums/components/echolocation.dm
@@ -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)
diff --git a/code/datums/components/religious_tool.dm b/code/datums/components/religious_tool.dm
index f9dcd1164c0..1caab166048 100644
--- a/code/datums/components/religious_tool.dm
+++ b/code/datums/components/religious_tool.dm
@@ -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)
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 8a0e30a6de0..54ac36f838f 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -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())
diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm
index 64b2c013338..6d7f82a47e7 100644
--- a/code/game/objects/effects/forcefields.dm
+++ b/code/game/objects/effects/forcefields.dm
@@ -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
diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm
index dae8eab25d5..4b17727f05e 100644
--- a/code/game/objects/items/cosmetics.dm
+++ b/code/game/objects/items/cosmetics.dm
@@ -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))
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 9a736e3e27a..6bcd09a9dc1 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -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"
diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm
index 1e4792317ff..9aed1d30072 100644
--- a/code/modules/client/client_colour.dm
+++ b/code/modules/client/client_colour.dm
@@ -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
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index bcc85603902..f7a83637136 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -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
diff --git a/code/modules/mob/living/living_fov.dm b/code/modules/mob/living/living_fov.dm
index 4a12b07afee..d562b2cbb79 100644
--- a/code/modules/mob/living/living_fov.dm
+++ b/code/modules/mob/living/living_fov.dm
@@ -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'
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 6b67f53ce3a..483a481afed 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -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)
. = ..()
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 59d1008c567..542e03e2e93 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -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)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 292434ff175..2a22fbd7c62 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -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
diff --git a/code/datums/mutations/holy_mutation/burdened.dm b/code/modules/religion/burdened/burdened_trauma.dm
similarity index 60%
rename from code/datums/mutations/holy_mutation/burdened.dm
rename to code/modules/religion/burdened/burdened_trauma.dm
index 808109f7de0..eaf50c6cac3 100644
--- a/code/datums/mutations/holy_mutation/burdened.dm
+++ b/code/modules/religion/burdened/burdened_trauma.dm
@@ -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 = "You feel burdened!"
- text_lose_indication = "You no longer feel the need to burden yourself!"
- /// 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)
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
new file mode 100644
index 00000000000..3a54e2e7530
--- /dev/null
+++ b/code/modules/religion/burdened/psyker.dm
@@ -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)
diff --git a/code/modules/religion/honorbound/honorbound_rites.dm b/code/modules/religion/honorbound/honorbound_rites.dm
new file mode 100644
index 00000000000..2546c8a0b5b
--- /dev/null
+++ b/code/modules/religion/honorbound/honorbound_rites.dm
@@ -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:
+
+ 1.) Thou shalt not attack the unready!
+ 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.
+
+
+ 2.) Thou shalt not attack the just!
+ 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.
+
+
+ 3.) Thou shalt not attack the innocent!
+ 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.
+
+
+ 4.) Thou shalt not use profane magicks!
+ 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.
+ 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 ..()
diff --git a/code/datums/mutations/holy_mutation/honorbound.dm b/code/modules/religion/honorbound/honorbound_trauma.dm
similarity index 75%
rename from code/datums/mutations/holy_mutation/honorbound.dm
rename to code/modules/religion/honorbound/honorbound_trauma.dm
index e4112afd104..9a0d632522f 100644
--- a/code/datums/mutations/holy_mutation/honorbound.dm
+++ b/code/modules/religion/honorbound/honorbound_trauma.dm
@@ -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 = "You feel honorbound!"
- text_lose_indication = "You feel unshackled from your code of honor!"
+/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)
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index d5b3a7041cb..8dae39b1a62 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -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
diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm
index 8bbc957a47b..f634f1380c0 100644
--- a/code/modules/religion/rites.dm
+++ b/code/modules/religion/rites.dm
@@ -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:
-
- 1.) Thou shalt not attack the unready!
- 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.
-
-
- 2.) Thou shalt not attack the just!
- 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.
-
-
- 3.) Thou shalt not attack the innocent!
- 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.
-
-
- 4.) Thou shalt not use profane magicks!
- 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.
- 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. You're becoming something new."))
- 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
diff --git a/code/modules/spells/spell_types/charged/_charged.dm b/code/modules/spells/spell_types/charged/_charged.dm
index 7646f763956..9cd81c0a834 100644
--- a/code/modules/spells/spell_types/charged/_charged.dm
+++ b/code/modules/spells/spell_types/charged/_charged.dm
@@ -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
diff --git a/code/modules/spells/spell_types/self/forcewall.dm b/code/modules/spells/spell_types/self/forcewall.dm
index 150338a2db0..7a718949845 100644
--- a/code/modules/spells/spell_types/self/forcewall.dm
+++ b/code/modules/spells/spell_types/self/forcewall.dm
@@ -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"
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index 023aa4d414e..4c6ffba81b4 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -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)
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index bdfbf8def77..c7f3828bfa3 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -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()
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 006679c3696..04fa29c44dc 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/effects/fov/fov_effects.dmi b/icons/effects/fov/fov_effects.dmi
index 2ba9d11f579..877f4ad8062 100644
Binary files a/icons/effects/fov/fov_effects.dmi and b/icons/effects/fov/fov_effects.dmi differ
diff --git a/icons/effects/particles/echo.dmi b/icons/effects/particles/echo.dmi
new file mode 100644
index 00000000000..a7a47e340d1
Binary files /dev/null and b/icons/effects/particles/echo.dmi differ
diff --git a/icons/hud/screen_full.dmi b/icons/hud/screen_full.dmi
index 1025f15e82f..0c2ed898030 100644
Binary files a/icons/hud/screen_full.dmi and b/icons/hud/screen_full.dmi differ
diff --git a/icons/mob/species/human/bodyparts.dmi b/icons/mob/species/human/bodyparts.dmi
index 187dd2738f5..642489e4787 100644
Binary files a/icons/mob/species/human/bodyparts.dmi and b/icons/mob/species/human/bodyparts.dmi differ
diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi
index 8402814dcd0..90708c26cc2 100644
Binary files a/icons/obj/medical/organs/organs.dmi and b/icons/obj/medical/organs/organs.dmi differ
diff --git a/icons/obj/weapons/guns/ballistic.dmi b/icons/obj/weapons/guns/ballistic.dmi
index 74ad5d2ec80..f73f411f720 100644
Binary files a/icons/obj/weapons/guns/ballistic.dmi and b/icons/obj/weapons/guns/ballistic.dmi differ
diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi
index a30071be427..024c3917161 100644
Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 3c9fc0746ec..3e752cf7408 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -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"