PKP Mind Virus: Glorfing Cigs edition (#69904)

* PKP Mind Virus: Glorfing Cigs edition

If you glorf while smoking you'll get the cig stuck in your throat, leaving you unable to breath, speak, or eat. Based off the sort of cig you have a random delay before you vomit up the cig. (Pipes last forever don't philoso-larp kids). Other players can help you by using the help action on you while you're hurt, which leads to something imitating the himelich manuver, a broken chest, and a vomited up blockage. Other forms of vomiting will work too, if that's even applicable.

Oh I fixed a bug in vomit code. Because paralyzing you forces you to the ground, thus changing your dir, paralyzing vomits always went down. This is cringe, easy fix tho.

Ah and I added some extra functionality to sound loops too. You can set them to pick and take from the provided list, then refilling it once it's empty. This keeps some variety while preventing the risk of repeat sounds

Oh and I added some new signals. Some misc ones, for force say and a different kind of help then the existing one, which I renamed. One for breathing, which I used to clean up leroxin. I was planning to hook this but didn't need to in the end.

One for vomiting too. and eating. Also added a way to update looping timer delays. Forgot about that, I also added variable delay for sound loops, because I thought it sounded better here.

Features audio courtesy of our very own san7890, and the quake jump sound.
This commit is contained in:
LemonInTheDark
2022-10-19 06:34:45 -04:00
committed by GitHub
parent 56a8387b96
commit cd3a778915
27 changed files with 485 additions and 25 deletions
@@ -1,8 +1,13 @@
///Called from /mob/living/carbon/help_shake_act, before any hugs have ocurred. (mob/living/helper)
#define COMSIG_CARBON_PRE_HELP_ACT "carbon_pre_help"
/// Stops the rest of help act (hugging, etc) from occuring
///Called from /datum/species/proc/help : (mob/living/carbon/human/helper, datum/martial_art/helper_style)
#define COMSIG_CARBON_PRE_HELP "carbon_pre_help"
/// Stops the rest of the help
#define COMPONENT_BLOCK_HELP_ACT (1<<0)
///Called from /mob/living/carbon/help_shake_act, before any hugs have ocurred. (mob/living/helper)
#define COMSIG_CARBON_PRE_MISC_HELP "carbon_pre_misc_help"
/// Stops the rest of help act (hugging, etc) from occuring
#define COMPONENT_BLOCK_MISC_HELP (1<<0)
///Called from /mob/living/carbon/help_shake_act on the person being helped, after any hugs have ocurred. (mob/living/helper)
#define COMSIG_CARBON_HELP_ACT "carbon_help"
///Called from /mob/living/carbon/help_shake_act on the helper, after any hugs have ocurred. (mob/living/helped)
@@ -84,10 +89,20 @@
#define COMPONENT_OVERRIDE_HEALTH_HUD (1<<0)
///Called when a carbon updates their sanity (source = carbon)
#define COMSIG_CARBON_SANITY_UPDATE "carbon_sanity_update"
///Called when a carbon attempts to breath, before the breath has actually occured
#define COMSIG_CARBON_ATTEMPT_BREATHE "carbon_attempt_breathe"
// Prevents the breath
#define COMSIG_CARBON_BLOCK_BREATH (1 << 0)
///Called when a carbon breathes, before the breath has actually occured
#define COMSIG_CARBON_PRE_BREATHE "carbon_pre_breathe"
///Called when a carbon updates their mood
#define COMSIG_CARBON_MOOD_UPDATE "carbon_mood_update"
///Called when a carbon attempts to eat (eating)
#define COMSIG_CARBON_ATTEMPT_EAT "carbon_attempt_eat"
// Prevents the breath
#define COMSIG_CARBON_BLOCK_EAT (1 << 0)
///Called when a carbon vomits : (distance, force)
#define COMSIG_CARBON_VOMITED "carbon_vomited"
// /mob/living/carbon/human signals
@@ -103,9 +118,11 @@
#define COMSIG_HUMAN_EARLY_UNARMED_ATTACK "human_early_unarmed_attack"
///from mob/living/carbon/human/UnarmedAttack(): (atom/target, proximity, modifiers)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack"
//from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration)
///from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration)
#define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields"
#define SHIELD_BLOCK (1<<0)
///from /mob/living/carbon/human/proc/force_say(): ()
#define COMSIG_HUMAN_FORCESAY "human_forcesay"
// Mob transformation signals
///Called when a human turns into a monkey, from /mob/living/carbon/proc/finish_monkeyize()
+2
View File
@@ -855,6 +855,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define ITEM_SCALING_TRAIT "item_scaling"
/// Trait given by Objects that provide blindsight
#define ITEM_BLIND_TRAIT "blind_item_trait"
/// Trait given by choking
#define CHOKING_TRAIT "choking_trait"
/// Trait given by hallucinations
#define HALLUCINATION_TRAIT "hallucination_trait"
+26
View File
@@ -662,6 +662,32 @@ SUBSYSTEM_DEF(timer)
return null
return timer.timeToRun - (timer.flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time)
/**
* Update the delay on an existing LOOPING timer
* Will come into effect on the next process
*
* Arguments:
* * id a timerid or a /datum/timedevent
* * new_wait the new wait to give this looping timer
*/
/proc/updatetimedelay(id, new_wait, datum/controller/subsystem/timer/timer_subsystem)
if (!id)
return
if (id == TIMER_ID_NULL)
CRASH("Tried to update the wait of null timerid. Use TIMER_STOPPABLE flag")
if (istype(id, /datum/timedevent))
var/datum/timedevent/timer = id
timer.wait = new_wait
return
timer_subsystem = timer_subsystem || SStimer
//id is string
var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id]
if(!timer || timer.spent)
return
if(!(timer.flags & TIMER_LOOP))
CRASH("Tried to update the wait of a non looping timer. This is not supported")
timer.wait = new_wait
#undef BUCKET_LEN
#undef BUCKET_POS
#undef TIMER_MAX
+2
View File
@@ -451,6 +451,8 @@ Behavior that's still missing from this component that original food items had t
var/who = (isnull(feeder) || eater == feeder) ? "your" : "[eater.p_their()]"
to_chat(feeder, span_warning("You have to remove [who] [covered] first!"))
return FALSE
if(SEND_SIGNAL(eater, COMSIG_CARBON_ATTEMPT_EAT, parent) & COMSIG_CARBON_BLOCK_EAT)
return
return TRUE
///Check foodtypes to see if we should send a moodlet
+53 -9
View File
@@ -2,12 +2,14 @@
* A datum for sounds that need to loop, with a high amount of configurability.
*/
/datum/looping_sound
/// The source of the sound, or the recipient of the sound.
var/atom/parent
/// (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
var/mid_sounds
/// The length of time to wait between playing mid_sounds.
var/mid_length
/// Amount of time to add/take away from the mid length, randomly
var/mid_length_vary = 0
/// If we should always play each sound once per loop of all sounds. Weights here only really effect order, and could be disgarded
var/each_once = FALSE
/// Override for volume of start sound.
var/start_volume
/// (soundfile) Played before starting the mid_sounds loop.
@@ -26,26 +28,34 @@
var/vary = FALSE
/// The max amount of loops to run for.
var/max_loops
/// If true, plays directly to provided atoms instead of from them.
var/direct
/// The extra range of the sound in tiles, defaults to 0.
var/extra_range = 0
/// The ID of the timer that's used to loop the sounds.
var/timer_id
/// How much the sound will be affected by falloff per tile.
var/falloff_exponent
/// The falloff distance of the sound,
var/falloff_distance
/// Do we skip the starting sounds?
var/skip_starting_sounds = FALSE
/// Are the sounds affected by pressure? Defaults to TRUE.
var/pressure_affected = TRUE
/// Are the sounds subject to reverb? Defaults to TRUE.
var/use_reverb = TRUE
/// Are we ignoring walls? Defaults to TRUE.
var/ignore_walls = TRUE
// State stuff
/// The source of the sound, or the recipient of the sound.
var/atom/parent
/// The ID of the timer that's used to loop the sounds.
var/timer_id
/// Has the looping started yet?
var/loop_started = FALSE
/// If we're using cut_mid, this is the list we cut from
var/list/cut_list
// Args
/// Do we skip the starting sounds?
var/skip_starting_sounds = FALSE
/// If true, plays directly to provided atoms instead of from them.
var/direct
/datum/looping_sound/New(_parent, start_immediately = FALSE, _direct = FALSE, _skip_starting_sounds = FALSE)
if(!mid_sounds)
@@ -108,6 +118,9 @@
if(max_loops && world.time >= start_time + mid_length * max_loops)
stop()
return
// If we have a timer, we're varying mid length, and this is happening while we're runnin mid_sounds
if(timer_id && mid_length_vary && start_time)
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary))
if(!chance || prob(chance))
play(get_sound())
@@ -140,10 +153,41 @@
/// Returns the sound we should now be playing.
/datum/looping_sound/proc/get_sound(_mid_sounds)
. = _mid_sounds || mid_sounds
var/list/play_from = _mid_sounds || mid_sounds
if(!each_once)
. = play_from
while(!isfile(.) && !isnull(.))
. = pick_weight(.)
return .
if(!length(cut_list))
cut_list = shuffle(play_from.Copy())
var/list/tree = list()
. = cut_list
while(!isfile(.) && !isnull(.))
// Tree is a list of lists containign files
// If an entry in the tree goes to 0 length, we cut it from the list
tree += list(.)
. = pick_weight(.)
if(!isfile(.))
return
// Remove the sound file
tree[length(tree)] -= .
// Walk the tree bottom up, remove any lists that are empty
// Don't do anything for the topmost list, cause we do not care
for(var/i in length(tree) to 2 step -1)
var/list/branch = tree[i]
if(length(branch))
break
tree[i - 1] -= list(branch) // Remove the empty list
return .
/// A proc that's there to handle delaying the main sounds if there's a start_sound, and simply starting the sound loop in general.
/datum/looping_sound/proc/on_start()
var/start_wait = 0
+12
View File
@@ -0,0 +1,12 @@
/datum/looping_sound/choking
mid_sounds = list('sound/creatures/gag1.ogg' = 1, 'sound/creatures/gag2.ogg' = 1, 'sound/creatures/gag3.ogg' = 1, 'sound/creatures/gag4.ogg' = 1, 'sound/creatures/gag5.ogg' = 1)
mid_length = 1.6 SECONDS
mid_length_vary = 0.3 SECONDS
each_once = TRUE
volume = 90
// We want you to be hard to hear far away
falloff_exponent = 12
pressure_affected = TRUE
vary = TRUE
// Same as above
ignore_walls = FALSE
@@ -154,6 +154,10 @@
description = "I'm nervous and on edge and I can't stand still!!"
mood_change = -2
/datum/mood_event/choke
description = "I CAN'T BREATHE!!!"
mood_change = -10
/datum/mood_event/vomit
description = "I just threw up. Gross."
mood_change = -2
+300
View File
@@ -0,0 +1,300 @@
// Status effect given to you if you're choking on something
/datum/status_effect/choke
id = "choke"
tick_interval = 0.2 SECONDS
alert_type = null
/// Weakref to the thing we're choking on
var/datum/weakref/choking_on_ref
/// If the thing we're choking on is on fire
var/flaming
/// The particle holder we're using to run our ash effects
var/obj/effect/abstract/particle_holder/ash
/// Our choking audio loop
var/datum/looping_sound/choke_loop
/// The delta client.pixel_y we've got
var/delta_y = 0
/// The delta client.pixel_x we've got
var/delta_x = 0
/datum/status_effect/choke/on_creation(mob/living/new_owner, atom/movable/choke_on, flaming = FALSE, vomit_delay = -1)
choking_on_ref = WEAKREF(choke_on)
src.flaming = flaming
src.duration = vomit_delay
return ..()
/datum/status_effect/choke/on_apply()
if(!iscarbon(owner)) // Ghosts do not have stomachs, and non carbons can't vomit
return FALSE
var/atom/movable/choking_on = choking_on_ref?.resolve()
if(!choking_on)
return FALSE
// If we've got an item, remove it from storage
if(isitem(choking_on))
var/obj/item/chokin_item = choking_on
if(chokin_item.item_flags & IN_INVENTORY)
var/mob/inventory_in = chokin_item.loc
inventory_in.transferItemToLoc(chokin_item, owner, force = TRUE, silent = TRUE)
else if(!(chokin_item.item_flags & IN_STORAGE) || !chokin_item.remove_item_from_storage(owner))
choking_on.forceMove(owner) // backup
else
choking_on.forceMove(owner)
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBREATH), .proc/no_breathing)
RegisterSignal(owner, COMSIG_MOB_LOGOUT, .proc/on_logout)
RegisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL, .proc/remove_choke)
RegisterSignal(owner, COMSIG_LIVING_DEATH, .proc/on_death)
// Of note, this means plasma lovers lose some methods of vomiting up
RegisterSignal(owner, COMSIG_CARBON_VOMITED, .proc/on_vomit)
RegisterSignal(owner, COMSIG_CARBON_ATTEMPT_EAT, .proc/attempt_eat)
RegisterSignal(owner, COMSIG_CARBON_PRE_HELP, .proc/helped)
RegisterSignal(owner, COMSIG_CARBON_PRE_MISC_HELP, .proc/shook)
RegisterSignal(choking_on, COMSIG_PARENT_QDELETING, .proc/remove_choke)
RegisterSignal(choking_on, COMSIG_MOVABLE_MOVED, .proc/hazard_moved)
ADD_TRAIT(owner, TRAIT_MUTE, CHOKING_TRAIT)
owner.add_mood_event(id, /datum/mood_event/choke)
//stop on death
choke_loop = new /datum/looping_sound/choking(owner)
check_audio_state()
owner.visible_message(span_bolddanger("[owner] tries to speak, but can't! They're choking!"), \
span_userdanger("You try to breath, but there's a block! You're choking!"), \
)
//barticles
if(flaming)
ash = new(owner, /particles/smoke/ash)
var/clear_in = rand(15 SECONDS, 25 SECONDS)
if(duration != -1)
clear_in = min(duration, clear_in)
addtimer(CALLBACK(src, .proc/clear_flame), clear_in)
return TRUE
/datum/status_effect/choke/proc/should_do_effects()
return owner.stat != DEAD && !HAS_TRAIT(owner, TRAIT_NOBREATH)
/datum/status_effect/choke/proc/check_audio_state()
if(!should_do_effects())
choke_loop.stop()
return
choke_loop.start()
/datum/status_effect/choke/on_remove()
owner.clear_mood_event(id)
REMOVE_TRAIT(owner, TRAIT_MUTE, CHOKING_TRAIT)
clean_client()
clear_flame()
if(choke_loop)
QDEL_NULL(choke_loop)
vomit_up()
return ..()
/datum/status_effect/choke/proc/clean_client()
// juuust in case, reset our x and y's
var/client/client_owner = owner.canon_client
if(client_owner)
// Ok listen I want to use non relative animates here but BYOND WILL NOT LET ME
// REEEEEEEEE
animate(client_owner, pixel_x = client_owner.pixel_x - delta_x, pixel_y = client_owner.pixel_y - delta_y, 0.05 SECONDS, ANIMATION_PARALLEL)
delta_x = 0
delta_y = 0
/datum/status_effect/choke/proc/clear_flame()
flaming = FALSE
if(ash)
QDEL_NULL(ash)
/datum/status_effect/choke/proc/vomit_up()
var/atom/movable/choking_on = choking_on_ref?.resolve()
if(choking_on && iscarbon(owner))
var/mob/living/carbon/carbon_owner = owner
// This will yeet the thing we're choking on out of us
carbon_owner.vomit(lost_nutrition = 20, force = TRUE, distance = 2)
/datum/status_effect/choke/proc/on_vomit(mob/source, distance, force)
SIGNAL_HANDLER
var/atom/movable/choking_on = choking_on_ref?.resolve()
if(choking_on)
choking_on_ref = null
choking_on.forceMove(get_turf(source)) // Gotta be on a tile to throw yourself bestie
var/atom/target = get_edge_target_turf(source, source.dir)
choking_on.throw_at(target, distance, 1, source)
/datum/status_effect/choke/get_examine_text()
return span_boldwarning("[owner.p_they(TRUE)] [owner.p_are()] choking!")
/datum/status_effect/choke/proc/remove_choke(datum/source)
SIGNAL_HANDLER
if(!QDELETED(src))
qdel(src)
/datum/status_effect/choke/proc/hazard_moved(atom/movable/source)
SIGNAL_HANDLER
if(QDELETED(src))
return
if(source.loc != owner)
qdel(src)
/datum/status_effect/choke/proc/no_breathing(mob/living/source)
SIGNAL_HANDLER
RegisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_NOBREATH), .proc/on_breathable)
check_audio_state()
/datum/status_effect/choke/proc/on_breathable(mob/living/source)
SIGNAL_HANDLER
UnregisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_NOBREATH))
check_audio_state()
/datum/status_effect/choke/proc/on_logout(datum/source)
SIGNAL_HANDLER
clean_client()
/datum/status_effect/choke/proc/on_death(mob/living/source)
SIGNAL_HANDLER
RegisterSignal(source, COMSIG_LIVING_REVIVE, .proc/on_revive)
check_audio_state()
/datum/status_effect/choke/proc/on_revive(mob/living/source)
SIGNAL_HANDLER
UnregisterSignal(source, COMSIG_LIVING_REVIVE)
check_audio_state()
/datum/status_effect/choke/proc/attempt_eat(mob/source, atom/eating)
SIGNAL_HANDLER
source.balloon_alert(source, "can't get it down!")
return COMSIG_CARBON_BLOCK_EAT
/datum/status_effect/choke/proc/helped(mob/source, mob/helping)
SIGNAL_HANDLER
INVOKE_ASYNC(src, .proc/heimlich, source, helping)
return COMPONENT_BLOCK_HELP_ACT
/datum/status_effect/choke/proc/shook(mob/source, mob/helping)
SIGNAL_HANDLER
INVOKE_ASYNC(src, .proc/heimlich, source, helping)
return COMPONENT_BLOCK_MISC_HELP
/datum/status_effect/choke/proc/heimlich(mob/victim, mob/aggressor)
if(victim == aggressor)
return
if(DOING_INTERACTION_WITH_TARGET(aggressor, victim))
victim.balloon_alert(aggressor, "already helping!")
return
if(DOING_INTERACTION(aggressor, "heimlich"))
victim.balloon_alert(aggressor, "already helping someone!")
return
if(!thrusting_continues(victim, aggressor, before_work = TRUE))
return
aggressor.start_pulling(victim)
// I want the thing to look vaugely like the heimlich. Sue me
victim.setDir(aggressor.dir)
var/hand_name = "hand"
if(!iscarbon(aggressor))
hand_name = "paw" // Fuck you
var/mob/living/livin_victim = victim
if(iscarbon(aggressor) && livin_victim.body_position == STANDING_UP)
owner.visible_message(span_warning("[aggressor] wraps [aggressor.p_their()] arms around [victim]'s stomach, and begins thrusting [aggressor.p_their()] fists towards themselves!"), \
span_boldwarning("[aggressor] wraps [aggressor.p_their()] arms around you, and begins thrusting their hands into your chest. [capitalize(GLOB.deity)] that hurts!"), \
)
else
owner.visible_message(span_warning("[aggressor] places [aggressor.p_their()] [hand_name]s on [victim]'s back, and begins forcefully striking it!"), \
span_boldwarning("You feel [aggressor]\s [hand_name]s on your back, and then repeated striking!"))
if(!do_after_mob(aggressor, victim, 7 SECONDS, extra_checks = CALLBACK(src, .proc/thrusting_continues, victim, aggressor), interaction_key = "heimlich"))
aggressor.stop_pulling()
return
aggressor.stop_pulling()
var/atom/movable/choking_on = choking_on_ref?.resolve()
owner.visible_message(span_green("[victim] vomits up \the[choking_on]. [victim.p_theyre()] gonna make it!"), \
span_green("You vomit up that accursed blockage. YOU CAN BREATHE! The broken chest is a hell of a price to pay."))
if(iscarbon(victim))
var/mob/living/carbon/carbon_victim = victim
var/obj/item/bodypart/chest = carbon_victim.get_bodypart(BODY_ZONE_CHEST)
if(chest)
chest.force_wound_upwards(/datum/wound/blunt/severe)
playsound(owner, 'sound/creatures/crack_vomit.ogg', 120, extrarange = 5, falloff_exponent = 4)
vomit_up()
/datum/status_effect/choke/proc/mirror_dir(atom/source, old_dir, new_dir)
SIGNAL_HANDLER
owner.dir = new_dir
/datum/status_effect/choke/proc/thrusting_continues(mob/living/victim, mob/aggressor, before_work = FALSE)
if(iscarbon(aggressor))
var/free_hands = 0
// Listen bud, you need at least 2 free hands for this
for(var/hand_i in 1 to length(aggressor.held_items))
if(!aggressor.has_hand_for_held_index(hand_i) || aggressor.held_items[hand_i])
continue
free_hands += 1
if(free_hands < 2)
victim.balloon_alert(aggressor, "need 2 free hands!")
return FALSE
if(iscarbon(victim))
var/mob/living/carbon/carbon_victim = victim
if(!carbon_victim.appears_alive())
victim.balloon_alert(aggressor, "too late...")
return FALSE
if(!choking_on_ref)
return FALSE
if(!before_work)
// This check isn't valid at first because it looks dumb if other things fail
if(victim.pulledby != aggressor)
victim.balloon_alert(aggressor, "must be able to move them!")
return FALSE
// Similarly, but also this is a burden of knowhow that's cringe
if(aggressor.dir != get_dir(aggressor, victim))
victim.balloon_alert(aggressor, "must be facing them!")
return FALSE
// See above
if(victim.dir != aggressor.dir)
victim.balloon_alert(aggressor, "must be facing the same way!")
return FALSE
// If we ain't starting, deal a tad bit of brute, as a treat
// Note, we attempt to process 10 times a second, so over 7 seconds this'll deal 14 brute
if(!before_work)
victim.adjustBruteLoss(0.2)
return TRUE
/datum/status_effect/choke/tick(delta_time)
if(!should_do_effects())
return
deal_damage(delta_time)
var/client/client_owner = owner.client
if(client_owner)
do_vfx(client_owner)
/datum/status_effect/choke/proc/deal_damage(delta_time)
owner.losebreath += 1 * delta_time // 1 breath loss a second. This will deal additional breath damage, and prevent breathing
if(flaming)
var/obj/item/bodypart/head = owner.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.receive_damage(0, 2 * delta_time, 2 * delta_time)
/datum/status_effect/choke/proc/do_vfx(client/vfx_on)
var/old_x = delta_x
delta_x = WRAP(delta_x + rand(1, 6), -13, 13)
var/old_y = delta_y
delta_y = WRAP(delta_y + rand(1, 6), -13, 13)
// We run on fast process, which procs us once every 0.2 seconds. We want to move every 0.05, so we do 4 animates
// In other news, ANIMATION_RELATIVE does not work on client.pixel_x/y. Thanks byond
animate(vfx_on, pixel_x = vfx_on.pixel_x + (delta_x - old_x), pixel_y = vfx_on.pixel_y + (delta_y - old_y), time = 0.05 SECONDS, flags = ANIMATION_PARALLEL)
for(var/i in 1 to 3)
old_x = delta_x
delta_x = WRAP(delta_x + rand(1, 6), -13, 13)
old_y = delta_y
delta_y = WRAP(delta_y + rand(1, 6), -13, 13)
animate(pixel_x = vfx_on.pixel_x + (delta_x - old_x), pixel_y = vfx_on.pixel_y + (delta_y - old_y), time = 0.05 SECONDS)
@@ -12,11 +12,11 @@
/datum/status_effect/strandling/on_apply()
RegisterSignal(owner, COMSIG_CARBON_PRE_BREATHE, .proc/on_breathe)
RegisterSignal(owner, COMSIG_ATOM_TOOL_ACT(TOOL_WIRECUTTER), .proc/on_cut)
RegisterSignal(owner, COMSIG_CARBON_PRE_HELP_ACT, .proc/on_self_check)
RegisterSignal(owner, COMSIG_CARBON_PRE_MISC_HELP, .proc/on_self_check)
return TRUE
/datum/status_effect/strandling/on_remove()
UnregisterSignal(owner, list(COMSIG_CARBON_PRE_BREATHE, COMSIG_ATOM_TOOL_ACT(TOOL_WIRECUTTER), COMSIG_CARBON_PRE_HELP_ACT))
UnregisterSignal(owner, list(COMSIG_CARBON_PRE_BREATHE, COMSIG_ATOM_TOOL_ACT(TOOL_WIRECUTTER), COMSIG_CARBON_PRE_MISC_HELP))
/datum/status_effect/strandling/get_examine_text()
return span_warning("[owner.p_they(TRUE)] seem[owner.p_s()] to be being choked by some durathread strands. You may be able to <b>cut</b> them off.")
@@ -40,7 +40,7 @@
INVOKE_ASYNC(src, .proc/try_remove_effect, user, tool)
return COMPONENT_BLOCK_TOOL_ATTACK
/// Signal proc for [COMSIG_CARBON_PRE_HELP_ACT], allowing someone to remove the effect by hand
/// Signal proc for [COMSIG_CARBON_PRE_MISC_HELP], allowing someone to remove the effect by hand
/datum/status_effect/strandling/proc/on_self_check(mob/living/carbon/source, mob/living/helper)
SIGNAL_HANDLER
@@ -48,7 +48,7 @@
return
INVOKE_ASYNC(src, .proc/try_remove_effect, helper)
return COMPONENT_BLOCK_HELP_ACT
return COMPONENT_BLOCK_MISC_HELP
/**
* Attempts a do_after to remove the effect and stop the strangling.
@@ -22,3 +22,13 @@
spawning = 1
velocity = list(0, 0.3, 0)
friction = 0.25
/particles/smoke/ash
icon_state = list("ash_1" = 2, "ash_2" = 2, "ash_3" = 1, "smoke_1" = 3, "smoke_2" = 2)
count = 500
spawning = 1
lifespan = 1 SECONDS
fade = 0.2 SECONDS
fadein = 0.7 SECONDS
position = generator("vector", list(-3, 5, 0), list(3, 6.5, 0), NORMAL_RAND)
velocity = generator("vector", list(-0.1, 0.4, 0), list(0.1, 0.5, 0), NORMAL_RAND)
+23 -1
View File
@@ -163,7 +163,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/smoke_all = FALSE
/// How much damage this deals to the lungs per drag.
var/lung_harm = 1
/// If, when glorf'd, we will choke on this cig forever
var/choke_forever = FALSE
/// When choking, what is the maximum amount of time we COULD choke for
var/choke_time_max = 30 SECONDS // I am mean
/obj/item/clothing/mask/cigarette/Initialize(mapload)
. = ..()
@@ -181,6 +184,21 @@ CIGARETTE PACKETS ARE IN FANCY.DM
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/clothing/mask/cigarette/equipped(mob/equipee, slot)
. = ..()
if(!(slot & ITEM_SLOT_MASK))
UnregisterSignal(equipee, COMSIG_HUMAN_FORCESAY)
return
RegisterSignal(equipee, COMSIG_HUMAN_FORCESAY, .proc/on_forcesay)
/obj/item/clothing/mask/cigarette/dropped(mob/dropee)
. = ..()
UnregisterSignal(dropee, COMSIG_HUMAN_FORCESAY)
/obj/item/clothing/mask/cigarette/proc/on_forcesay(mob/living/source)
SIGNAL_HANDLER
source.apply_status_effect(/datum/status_effect/choke, src, lit, choke_forever ? -1 : rand(25 SECONDS, choke_time_max))
/obj/item/clothing/mask/cigarette/suicide_act(mob/user)
user.visible_message(span_suicide("[user] is huffing [src] as quickly as [user.p_they()] can! It looks like [user.p_theyre()] trying to give [user.p_them()]self cancer."))
return (TOXLOSS|OXYLOSS)
@@ -431,6 +449,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
smoketime = 4 MINUTES
chem_volume = 50
list_reagents = null
choke_time_max = 40 SECONDS
/obj/item/clothing/mask/cigarette/rollie/Initialize(mapload)
name = pick(list(
@@ -503,6 +522,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
heat = 473.15 // Lowered so that the sugar can be carmalized, but not burnt.
lung_harm = 0.5
list_reagents = list(/datum/reagent/consumable/sugar = 20)
choke_time_max = 70 SECONDS // This shit really is deadly
/obj/item/clothing/mask/cigarette/candy/nicotine
desc = "For all ages*! Doesn't contain any* amount of nicotine. Health and safety risks can be read on the tip of the cigarette."
@@ -538,6 +558,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
smoketime = 11 MINUTES
chem_volume = 40
list_reagents = list(/datum/reagent/drug/nicotine = 25)
choke_time_max = 40 SECONDS
/obj/item/clothing/mask/cigarette/cigar/cohiba
name = "\improper Cohiba Robusto cigar"
@@ -589,6 +610,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
chem_volume = 200 // So we can fit densified chemicals plants
list_reagents = null
w_class = WEIGHT_CLASS_SMALL
choke_forever = TRUE
///name of the stuff packed inside this pipe
var/packeditem
+3 -1
View File
@@ -417,6 +417,8 @@
if((HAS_TRAIT(src, TRAIT_NOHUNGER) || HAS_TRAIT(src, TRAIT_TOXINLOVER)) && !force)
return TRUE
SEND_SIGNAL(src, COMSIG_CARBON_VOMITED, distance, force)
var/starting_dir = dir
if(nutrition < 100 && !blood && !force)
if(message)
visible_message(span_warning("[src] dry heaves!"), \
@@ -455,7 +457,7 @@
else
if(T)
T.add_vomit_floor(src, vomit_type, purge_ratio) //toxic barf looks different || call purge when doing detoxicfication to pump more chems out of the stomach.
T = get_step(T, dir)
T = get_step(T, starting_dir)
if (T?.is_blocked_turf())
break
return TRUE
@@ -403,7 +403,7 @@
to_chat(helper, span_warning("You can't put [p_them()] out with just your bare hands!"))
return
if(SEND_SIGNAL(src, COMSIG_CARBON_PRE_HELP_ACT, helper) & COMPONENT_BLOCK_HELP_ACT)
if(SEND_SIGNAL(src, COMSIG_CARBON_PRE_MISC_HELP, helper) & COMPONENT_BLOCK_MISC_HELP)
return
if(helper == src)
@@ -1068,9 +1068,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
/datum/species/proc/spec_fully_heal(mob/living/carbon/human/H)
return
/datum/species/proc/help(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(target.body_position == STANDING_UP || (target.health >= 0 && !HAS_TRAIT(target, TRAIT_FAKEDEATH)))
if(SEND_SIGNAL(target, COMSIG_CARBON_PRE_HELP, user, attacker_style) & COMPONENT_BLOCK_HELP_ACT)
return TRUE
if(target.body_position == STANDING_UP || target.appears_alive())
target.help_shake_act(user)
if(target != user)
log_combat(user, target, "shaken")
+2 -2
View File
@@ -71,7 +71,7 @@
//Second link in a breath chain, calls check_breath()
/mob/living/carbon/proc/breathe(delta_time, times_fired)
var/obj/item/organ/internal/lungs = getorganslot(ORGAN_SLOT_LUNGS)
if(reagents.has_reagent(/datum/reagent/toxin/lexorin, needs_metabolizing = TRUE))
if(SEND_SIGNAL(src, COMSIG_CARBON_ATTEMPT_BREATHE) & COMSIG_CARBON_BLOCK_BREATH)
return
SEND_SIGNAL(src, COMSIG_CARBON_PRE_BREATHE)
@@ -151,7 +151,7 @@
failed_last_breath = TRUE
throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
return FALSE
var/safe_oxy_min = 16
var/safe_co2_max = 10
var/safe_plas_max = 0.05
+4
View File
@@ -761,3 +761,7 @@
/mob/living/proc/get_drunk_amount()
var/datum/status_effect/inebriated/inebriation = has_status_effect(/datum/status_effect/inebriated)
return inebriation?.drunk_value || 0
/// Helper to check if we seem to be alive or not
/mob/living/proc/appears_alive()
return health >= 0 && !HAS_TRAIT(src, TRAIT_FAKEDEATH)
@@ -176,6 +176,16 @@
C.emote("gasp")
..()
/datum/reagent/toxin/lexorin/on_mob_metabolize(mob/living/L)
RegisterSignal(L, COMSIG_CARBON_ATTEMPT_BREATHE, .proc/block_breath)
/datum/reagent/toxin/lexorin/on_mob_end_metabolize(mob/living/L)
UnregisterSignal(L, COMSIG_CARBON_ATTEMPT_BREATHE, .proc/block_breath)
/datum/reagent/toxin/lexorin/proc/block_breath(mob/living/source)
SIGNAL_HANDLER
return COMSIG_CARBON_BLOCK_BREATH
/datum/reagent/toxin/slimejelly
name = "Slime Jelly"
description = "A gooey semi-liquid produced from one of the deadliest lifeforms in existence. SO REAL."
@@ -65,6 +65,7 @@
log_speech_indicators("[key_name(client)] FORCED to stop typing, indicators enabled.")
else
log_speech_indicators("[key_name(client)] FORCED to stop typing, indicators DISABLED.")
SEND_SIGNAL(src, COMSIG_HUMAN_FORCESAY)
/**
* Handles text entry and forced speech.