Skip to content

Commit

Permalink
feign impairment
Browse files Browse the repository at this point in the history
  • Loading branch information
silicons committed Jan 5, 2025
1 parent 68c9637 commit 381c2e3
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 63 deletions.
3 changes: 3 additions & 0 deletions citadel.dme
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@
#include "code\datums\components\items\passive_parry.dm"
#include "code\datums\components\items\wielding.dm"
#include "code\datums\components\mobs\block_frame.dm"
#include "code\datums\components\mobs\mob_feign_impairment.dm"
#include "code\datums\components\mobs\mob_self_horizontal_inversion.dm"
#include "code\datums\components\mobs\mob_self_vertical_inversion.dm"
#include "code\datums\components\mobs\parry_frame.dm"
Expand Down Expand Up @@ -3615,6 +3616,7 @@
#include "code\modules\mob\mob-keybind-triggers.dm"
#include "code\modules\mob\mob-login.dm"
#include "code\modules\mob\mob-logout.dm"
#include "code\modules\mob\mob-status_procs.dm"
#include "code\modules\mob\mob.dm"
#include "code\modules\mob\mob_defines.dm"
#include "code\modules\mob\mob_helpers.dm"
Expand Down Expand Up @@ -4146,6 +4148,7 @@
#include "code\modules\mob\observer\dead\orbit.dm"
#include "code\modules\mob\observer\dead\perspective.dm"
#include "code\modules\mob\observer\dead\say.dm"
#include "code\modules\mob\verbs\feign_impairment.dm"
#include "code\modules\mob\verbs\horizontal_invert_self.dm"
#include "code\modules\mob\verbs\vertical_invert_self.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
Expand Down
9 changes: 5 additions & 4 deletions code/datums/components/_component.dm
Original file line number Diff line number Diff line change
Expand Up @@ -345,11 +345,10 @@
for(var/datum/listening_datum as anything in queued_calls)
. |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments))

// The type arg is casted so initial works, you shouldn't be passing a real instance into this
/**
* Return any component assigned to this datum of the given type
* Return any component assigned to this datum of the given registered component type
*
* If it has a registered type, that'll be used instead!
* * `registered_type` must be set on the component for this to work.
*
* Arguments:
* * datum/component/c_type The type of the component you want to get a reference to. It will be overridden with the type of its [registered_type] if it's set.
Expand All @@ -360,7 +359,9 @@
return . && (length(.) ? .[1] : .)

/**
* Get all components of a given type that are attached to this datum
* Get all components of a given registered component type that are attached to this datum
*
* * `registered_type` must be set on the component for this to work.
*
* Arguments:
* * c_type The component type path
Expand Down
60 changes: 60 additions & 0 deletions code/datums/components/mobs/mob_feign_impairment.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2025 Citadel Station Developers *//

// todo: datumize impairments; the way this works right now is pretty stupid
// it's because we're using different components since i was too
// lazy to do a single tracking component

/**
* Component added to a mob by the mob themselves to feign an impairment
*/
/datum/component/mob_feign_impairment
var/power
var/feign_impairment_type

/datum/component/mob_feign_impairment/Initialize(power)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
if(. == COMPONENT_INCOMPATIBLE)
return
src.power = power

/datum/component/mob_feign_impairment/RegisterWithParent()
. = ..()
// todo: on update stat
RegisterSignal(parent, COMSIG_MOB_ON_UPDATE_MOBILITY, PROC_REF(recheck_stat))

/datum/component/mob_feign_impairment/UnregisterFromParent()
. = ..()
// todo: on update stat
UnregisterSignal(parent, COMSIG_MOB_ON_UPDATE_MOBILITY)
var/mob/mob_parent = parent
LAZYREMOVE(mob_parent.impairments_feigned, feign_impairment_type)

/datum/component/mob_feign_impairment/proc/recheck_stat(mob/source)
SIGNAL_HANDLER
if(IS_CONSCIOUS(source))
return
qdel(src)

/datum/component/mob_feign_impairment/slurring
// this must be set
registered_type = /datum/component/mob_feign_impairment/slurring
feign_impairment_type = /datum/feign_impairment/slurring

/datum/component/mob_feign_impairment/stutter
// this must be set
registered_type = /datum/component/mob_feign_impairment/stutter
feign_impairment_type = /datum/feign_impairment/stutter

/datum/component/mob_feign_impairment/jitter
// this must be set
registered_type = /datum/component/mob_feign_impairment/jitter
feign_impairment_type = /datum/feign_impairment/jitter

/datum/component/mob_feign_impairment/jitter/RegisterWithParent()
. = ..()
// shitcode but whatever
var/mob/mob_parent = parent
mob_parent.make_jittery(0)
10 changes: 6 additions & 4 deletions code/modules/mob/animations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,20 @@ note dizziness decrements automatically in the mob's Life() proc.

jitteriness = min(1000, jitteriness + amount) // store what will be new value
// clamped to max 1000
if(jitteriness > 100 && !is_jittery)

var/effective_jitteriness = get_effective_impairment_power_jitter()
if(effective_jitteriness > 100 && !is_jittery)
spawn(0)
jittery_process()


/mob/proc/jittery_process()
if(IS_DEAD(src))//Dead people dont twitch around
return

is_jittery = 1
while(jitteriness > 100)
var/amplitude = min(4, jitteriness / 100)
var/effective_jitteriness = get_effective_impairment_power_jitter()
while(effective_jitteriness > 100)
var/amplitude = min(4, effective_jitteriness / 100)
pixel_x = get_managed_pixel_x() + rand(-amplitude, amplitude)
pixel_y = get_managed_pixel_y() + rand(-amplitude/3, amplitude/3)

Expand Down
7 changes: 4 additions & 3 deletions code/modules/mob/living/carbon/human/examine.dm
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,12 @@

//Jitters
if(is_jittery)
if(jitteriness >= 300)
var/effective_jitteriness = get_effective_impairment_power_jitter()
if(effective_jitteriness >= 300)
. += SPAN_DANGER("[T.He] [T.is] convulsing violently!")
else if(jitteriness >= 200)
else if(effective_jitteriness >= 200)
. += SPAN_WARNING("[T.He] [T.is] extremely jittery.")
else if(jitteriness >= 100)
else if(effective_jitteriness >= 100)
. += SPAN_WARNING("[T.He] [T.is] twitching ever so slightly.")

//splints
Expand Down
4 changes: 2 additions & 2 deletions code/modules/mob/living/say.dm
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ var/list/channel_to_radio_key = new
verb = pick("yells","roars","hollers")
whispering = 0
. = 1
if(slurring)
if(get_effective_impairment_power_slurring())
message = slur(message)
verb = pick("slobbers","slurs")
. = 1
if(stuttering)
if(get_effective_impairment_power_stutter())
message = stutter(message)
verb = pick("stammers","stutters")
. = 1
Expand Down
36 changes: 36 additions & 0 deletions code/modules/mob/mob-status_procs.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2025 Citadel Station Developers *//

//* Impairments *//

// hey! listen
// if you're thinking about find-replacing checks for the var with this
// well fucking don't
//
// the reason this is separate from main vars for these things
// is because people keep abusing vars to implement features that have a
// ton of unintended side effects
//
// these should only be used for the actual effects; it is **not**
// allowed to use for any other effects like xenochimera feral,
// traumatic shock, etc

// todo: /datum/mob_impairment ?

/mob/proc/get_effective_impairment_power_slurring()
. = slurring
var/datum/component/mob_feign_impairment/slurring/feigned = GetComponent(/datum/component/mob_feign_impairment/slurring)
if(feigned)
. = max(., feigned.power)

/mob/proc/get_effective_impairment_power_jitter()
. = jitteriness
var/datum/component/mob_feign_impairment/jitter/feigned = GetComponent(/datum/component/mob_feign_impairment/jitter)
if(feigned)
. = max(., feigned.power)

/mob/proc/get_effective_impairment_power_stutter()
. = stuttering
var/datum/component/mob_feign_impairment/stutter/feigned = GetComponent(/datum/component/mob_feign_impairment/stutter)
if(feigned)
. = max(., feigned.power)
5 changes: 5 additions & 0 deletions code/modules/mob/mob.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/mob
//* Impairments *//
/// active feign_impairment types
/// * lazy list
var/list/impairments_feigned

/**
* Intialize a mob
Expand Down
94 changes: 94 additions & 0 deletions code/modules/mob/verbs/feign_impairment.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2025 Citadel Station Developers *//

GLOBAL_LIST_INIT(feign_impairment_types, init_feign_impairment_types())

/proc/init_feign_impairment_types()
. = list()
var/list/component_collision_check = list()
for(var/datum/feign_impairment/path as anything in subtypesof(/datum/feign_impairment))
if(initial(path.abstract_type) == path)
continue
var/datum/feign_impairment/instance = new path
if(component_collision_check[instance.component_type])
stack_trace("collision between [component_collision_check[instance.component_type]:type] and [instance.type] on component type [instance.component_type]")
continue
component_collision_check[instance.component_type] = instance
.[path] = instance

/datum/feign_impairment
abstract_type = /datum/feign_impairment
var/name
var/adjective
var/component_type
var/power_min = 0
var/power_max = 0

/datum/feign_impairment/slurring
name = "Slurring"
adjective = "slurring"
component_type = /datum/component/mob_feign_impairment/slurring
power_min = 10
power_max = 500

/datum/feign_impairment/stutter
name = "Stuttering"
adjective = "stuttering"
component_type = /datum/component/mob_feign_impairment/stutter
power_min = 10
power_max = 500

/datum/feign_impairment/jitter
name = "Jittering"
adjective = "jittering"
component_type = /datum/component/mob_feign_impairment/jitter
power_min = 10
power_max = 2000

// todo: DECLARE_MOB_VERB
/mob/verb/feign_impairment()
set name = "Feign Impairment"
set category = VERB_CATEGORY_IC
set desc = "Pretend like you're slurring, stuttering, jittering, and more."

var/list/name_to_type = list()
for(var/datum/feign_impairment/path as anything in subtypesof(/datum/feign_impairment))
if(initial(path.abstract_type) == path)
continue
var/is_active = impairments_feigned?[path]
name_to_type["[initial(path.name)] (Currently: [is_active ? "Active" : "Inactive"])"] = path

var/choice = tgui_input_list(src, "Choose an impairment to toggle.", "Feign Impairment", name_to_type)
if(!choice)
return

var/path = name_to_type[choice]
var/new_active = !impairments_feigned?[path]
var/datum/feign_impairment/impairment = GLOB.feign_impairment_types[path]

var/power

if(new_active)
power = tgui_input_number(
src,
"What power? ([impairment.power_min] - [impairment.power_max])",
"Feign Impairment",
impairment.power_min,
impairment.power_max,
impairment.power_min,
round_value = TRUE,
)

// todo: better logging
log_game("[key_name(src)] toggled [impairment] to [new_active ?"on, with power [power]" : "off"]")

if(new_active)
AddComponent(impairment.component_type, power)
else
qdel(GetComponent(impairment.component_type))

to_chat(src, SPAN_NOTICE("You are now <b>[new_active ? "pretending" : "no longer pretending"]</b> to be [impairment.adjective]. This will be automatically reset should you lose consciousness."))

/mob/proc/clear_feign_impairment()
QDEL_LIST_ASSOC_VAL(impairments_feigned)
impairments_feigned = null
50 changes: 0 additions & 50 deletions code/modules/preferences/preferences_toggle_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,3 @@
else
src.painmsg = 1
to_chat(src,"You will [ (painmsg) ? "now" : "no longer"] see your own pain messages.")

/mob/living/carbon/human/verb/acting()
set name = "Feign Impairment"
set category = VERB_CATEGORY_IC
set desc = "Allows user to manually enable drunkenness, stutter, jitter, etc."
set src = usr

if(!IS_CONSCIOUS(src))
to_chat(src, "You need to be conscious to do that")
return

var/list/choices = list("Drunkenness", "Stuttering", "Jittering")
if(src.slurring >= 10 || src.stuttering >= 10 || src.jitteriness >= 100)
var/disable = alert(src, "Stop performing impairment? (Do NOT abuse this)", "Impairments", "Yes", "No")
if(disable == "Yes")
acting_expiry()
return

var/impairment = input(src, "Select an impairment to perform:", "Impairments") as null|anything in choices
if(!impairment)
return
var/duration = input(src,"Choose a duration to perform [impairment]. (1 - 60 seconds)","Duration in seconds",25) as num|null
if(!isnum(duration))
return
if(duration > 60 && !check_rights(R_EVENT, 0)) // admins can do as they please
to_chat(src, "Please choose a duration in seconds between 1 to 60.")
return
if(duration >= 1000) // unreachable code for anyone but admins who have set the number very high, logging for my sanity
message_admins("[src] has set their [impairment] to [duration] via Feign Impairment.")
if(duration >= 2000)
to_chat(src, "Please choose a duration less than 2000.")
return
if(impairment == "Drunkenness")
slurring = duration
if(impairment == "Stuttering")
stuttering = duration
if(impairment == "Jittering")
make_jittery(duration + 100)

if(duration)
addtimer(CALLBACK(src, PROC_REF(acting_expiry)), duration SECONDS)
var/aduration = duration SECONDS / 10
to_chat(src,"You will now performatively act as if you were experiencing [impairment] for [aduration] seconds. (Do NOT abuse this)")
feedback_add_details("admin_verb","actimpaired") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

/mob/living/carbon/human/proc/acting_expiry()
to_chat(src,"You are no longer acting impaired.") // tick down from 1 to allow the effects to end 'naturally'
slurring = 1
stuttering = 1
jitteriness = 1

0 comments on commit 381c2e3

Please sign in to comment.