Files
Bubberstation/code/datums/components/embedded.dm
grungussuss 58501dce77 Reorganizes the sound folder (#86726)
## About The Pull Request

<details>

- renamed ai folder to announcer

-- announcer --
- moved vox_fem to announcer
- moved approachingTG to announcer

- separated the ambience folder into ambience and instrumental
-- ambience --

- created holy folder moved all related sounds there
- created engineering folder and moved all related sounds there
- created security folder and moved ambidet there
- created general folder and moved ambigen there
- created icemoon folder and moved all icebox-related ambience there
- created medical folder and moved all medbay-related ambi there
- created ruin folder and moves all ruins ambi there
- created beach folder and moved seag and shore there
- created lavaland folder and moved related ambi there
- created aurora_caelus folder and placed its ambi there
- created misc folder and moved the rest of the files that don't have a
specific category into it

-- instrumental --

- moved traitor folder here
- created lobby_music folder and placed our songs there (title0 not used
anywhere? - server-side modification?)

-- items --

- moved secdeath to hailer
- moved surgery to handling

-- effects --

- moved chemistry into effects
- moved hallucinations into effects
- moved health into effects
- moved magic into effects

-- vehicles --

- moved mecha into vehicles


created mobs folder

-- mobs --

- moved creatures folder into mobs
- moved voice into mobs

renamed creatures to non-humanoids
renamed voice to humanoids

-- non-humanoids--

created cyborg folder
created hiss folder
moved harmalarm.ogg to cyborg

-- humanoids --




-- misc --

moved ghostwhisper to misc
moved insane_low_laugh to misc

I give up trying to document this.

</details>

- [X] ambience
- [x] announcer
- [x] effects
- [X] instrumental
- [x] items
- [x] machines
- [x] misc 
- [X] mobs
- [X] runtime
- [X] vehicles

- [ ] attributions

## Why It's Good For The Game

This folder is so disorganized that it's vomit inducing, will make it
easier to find and add new sounds, providng a minor structure to the
sound folder.

## Changelog
🆑 grungussuss
refactor: the sound folder in the source code has been reorganized,
please report any oddities with sounds playing or not playing
server: lobby music has been repathed to sound/music/lobby_music
/🆑
2024-09-23 22:24:50 -07:00

301 lines
16 KiB
Plaintext

/*
This component is responsible for handling individual instances of embedded objects. The embeddable element is what allows an item to be embeddable and stores its embedding stats,
and when it impacts and meets the requirements to stick into something, it instantiates an embedded component. Once the item falls out, the component is destroyed, while the
element survives to embed another day.
- Carbon embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are:
-- Every process tick, there is a chance to randomly proc pain, controlled by pain_chance. There may also be a chance for the object to fall out randomly, per fall_chance
-- Every time the mob moves, there is a chance to proc jostling pain, controlled by jostle_chance (and only 50% as likely if the mob is walking or crawling)
-- Various signals hooking into carbon topic() and the embed removal surgery in order to handle removals.
In addition, there are 2 cases of embedding: embedding, and sticking
- Embedding involves harmful and dangerous embeds, whether they cause brute damage, stamina damage, or a mix. This is the default behavior for embeddings, for when something is "pointy"
- Sticking occurs when an item should not cause any harm while embedding (imagine throwing a sticky ball of tape at someone, rather than a shuriken). An item is considered "sticky"
when it has 0 for both pain multiplier and jostle pain multiplier. It's a bit arbitrary, but fairly straightforward.
Stickables differ from embeds in the following ways:
-- Text descriptors use phrasing like "X is stuck to Y" rather than "X is embedded in Y"
-- There is no slicing sound on impact
-- All damage checks and bloodloss are skipped
*/
/datum/component/embedded
dupe_mode = COMPONENT_DUPE_ALLOWED
var/obj/item/bodypart/limb
var/obj/item/weapon
///if both our pain multiplier and jostle pain multiplier are 0, we're harmless and can omit most of the damage related stuff
var/harmful
/datum/component/embedded/Initialize(obj/item/weapon,
datum/thrownthing/throwingdatum,
obj/item/bodypart/part)
if(!iscarbon(parent) || !isitem(weapon))
return COMPONENT_INCOMPATIBLE
src.weapon = weapon
if(part)
limb = part
if(!weapon.is_embed_harmless())
harmful = TRUE
weapon.embedded(parent, part)
START_PROCESSING(SSdcs, src)
var/mob/living/carbon/victim = parent
var/datum/embed_data/embed_data = weapon.get_embed()
limb._embed_object(weapon) // on the inside... on the inside...
weapon.forceMove(victim)
RegisterSignals(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(weaponDeleted))
victim.visible_message(span_danger("[weapon] [harmful ? "embeds" : "sticks"] itself [harmful ? "in" : "to"] [victim]'s [limb.plaintext_zone]!"), span_userdanger("[weapon] [harmful ? "embeds" : "sticks"] itself [harmful ? "in" : "to"] your [limb.plaintext_zone]!"))
var/damage = weapon.throwforce
if(harmful)
victim.throw_alert(ALERT_EMBEDDED_OBJECT, /atom/movable/screen/alert/embeddedobject)
playsound(victim,'sound/items/weapons/bladeslice.ogg', 40)
if (limb.can_bleed())
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
damage += weapon.w_class * embed_data.impact_pain_mult
victim.add_mood_event("embedded", /datum/mood_event/embedded)
if(damage > 0)
var/armor = victim.run_armor_check(limb.body_zone, MELEE, "Your armor has protected your [limb.plaintext_zone].", "Your armor has softened a hit to your [limb.plaintext_zone].", weapon.armour_penetration, weak_against_armour = weapon.weak_against_armour)
limb.receive_damage(brute = (1 - embed_data.pain_stam_pct) * damage, blocked = armor, wound_bonus = weapon.wound_bonus, bare_wound_bonus = weapon.bare_wound_bonus, sharpness = weapon.get_sharpness())
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
/datum/component/embedded/Destroy()
var/mob/living/carbon/victim = parent
if(victim && !victim.has_embedded_objects())
victim.clear_alert(ALERT_EMBEDDED_OBJECT)
victim.clear_mood_event("embedded")
if(weapon)
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
weapon = null
limb = null
return ..()
/datum/component/embedded/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(jostleCheck))
RegisterSignal(parent, COMSIG_CARBON_EMBED_RIP, PROC_REF(ripOut))
RegisterSignal(parent, COMSIG_CARBON_EMBED_REMOVAL, PROC_REF(safeRemove))
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(checkTweeze))
RegisterSignal(parent, COMSIG_MAGIC_RECALL, PROC_REF(magic_pull))
/datum/component/embedded/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_EMBED_RIP, COMSIG_CARBON_EMBED_REMOVAL, COMSIG_ATOM_ATTACKBY, COMSIG_MAGIC_RECALL))
/datum/component/embedded/process(seconds_per_tick)
var/mob/living/carbon/victim = parent
if(!victim || !limb) // in case the victim and/or their limbs exploded (say, due to a sticky bomb)
weapon.forceMove(get_turf(weapon))
qdel(src)
return
if(victim.stat == DEAD)
return
var/datum/embed_data/embed_data = weapon.get_embed()
var/damage = weapon.w_class * embed_data.pain_mult
var/pain_chance_current = SPT_PROB_RATE(embed_data.pain_chance / 100, seconds_per_tick) * 100
if(embed_data.pain_stam_pct && HAS_TRAIT_FROM(victim, TRAIT_INCAPACITATED, STAMINA)) //if it's a less-lethal embed, give them a break if they're already stamcritted
pain_chance_current *= 0.2
damage *= 0.5
else if(victim.body_position == LYING_DOWN)
pain_chance_current *= 0.2
if(harmful && prob(pain_chance_current))
limb.receive_damage(brute = (1 - embed_data.pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
to_chat(victim, span_userdanger("[weapon] embedded in your [limb.plaintext_zone] hurts!"))
var/fall_chance_current = SPT_PROB_RATE(embed_data.fall_chance / 100, seconds_per_tick) * 100
if(victim.body_position == LYING_DOWN)
fall_chance_current *= 0.2
if(prob(fall_chance_current))
fallOut()
////////////////////////////////////////
////////////BEHAVIOR PROCS//////////////
////////////////////////////////////////
/// Called every time a carbon with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the carbon is crawling or walking.
/datum/component/embedded/proc/jostleCheck()
SIGNAL_HANDLER
var/mob/living/carbon/victim = parent
var/datum/embed_data/embed_data = weapon.get_embed()
var/chance = embed_data.jostle_chance
if(victim.move_intent == MOVE_INTENT_WALK || victim.body_position == LYING_DOWN)
chance *= 0.5
if(harmful && prob(chance))
var/damage = weapon.w_class * embed_data.jostle_pain_mult
limb.receive_damage(brute = (1 - embed_data.pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
to_chat(victim, span_userdanger("[weapon] embedded in your [limb.plaintext_zone] jostles and stings!"))
/// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove()
/datum/component/embedded/proc/fallOut()
var/mob/living/carbon/victim = parent
var/datum/embed_data/embed_data = weapon.get_embed()
if(harmful)
var/damage = weapon.w_class * embed_data.remove_pain_mult
limb.receive_damage(brute= (1 - embed_data.pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
victim.visible_message(span_danger("[weapon] falls [harmful ? "out" : "off"] of [victim.name]'s [limb.plaintext_zone]!"), span_userdanger("[weapon] falls [harmful ? "out" : "off"] of your [limb.plaintext_zone]!"))
safeRemove()
/// Called when a carbon with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove()
/datum/component/embedded/proc/ripOut(datum/source, obj/item/I, obj/item/bodypart/limb)
SIGNAL_HANDLER
if(I != weapon || src.limb != limb)
return
var/mob/living/carbon/victim = parent
var/datum/embed_data/embed_data = weapon.get_embed()
var/time_taken = embed_data.rip_time * weapon.w_class
INVOKE_ASYNC(src, PROC_REF(complete_rip_out), victim, I, limb, time_taken)
/// everything async that ripOut used to do
/datum/component/embedded/proc/complete_rip_out(mob/living/carbon/victim, obj/item/I, obj/item/bodypart/limb, time_taken)
victim.visible_message(span_warning("[victim] attempts to remove [weapon] from [victim.p_their()] [limb.plaintext_zone]."),span_notice("You attempt to remove [weapon] from your [limb.plaintext_zone]... (It will take [DisplayTimeText(time_taken)])"))
if(!do_after(victim, time_taken, target = victim))
return
if(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects))
qdel(src)
return
if(harmful)
damaging_removal(victim, I, limb)
victim.visible_message(span_notice("[victim] successfully rips [weapon] [harmful ? "out" : "off"] of [victim.p_their()] [limb.plaintext_zone]!"), span_notice("You successfully remove [weapon] from your [limb.plaintext_zone]."))
safeRemove(victim)
/// Proc that actually does the damage associated with ripping something out of yourself. Call this before safeRemove.
/datum/component/embedded/proc/damaging_removal(mob/living/carbon/victim, obj/item/removed, obj/item/bodypart/limb, ouch_multiplier = 1)
var/datum/embed_data/embed_data = weapon.get_embed()
var/damage = weapon.w_class * embed_data.remove_pain_mult * ouch_multiplier
limb.receive_damage(brute= (1 - embed_data.pain_stam_pct) * damage, sharpness = SHARP_EDGED) //It hurts to rip it out, get surgery you dingus. unlike the others, this CAN wound + increase slash bloodflow
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
victim.emote("scream")
/// This proc handles the final step and actual removal of an embedded/stuck item from a carbon, whether or not it was actually removed safely.
/// If you want the thing to go into someone's hands rather than the floor, pass them in to_hands
/datum/component/embedded/proc/safeRemove(mob/to_hands)
SIGNAL_HANDLER
var/mob/living/carbon/victim = parent
limb._unembed_object(weapon)
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) // have to do it here otherwise we trigger weaponDeleted()
SEND_SIGNAL(weapon, COMSIG_ITEM_UNEMBEDDED, victim)
if(!weapon.unembedded()) // if it hasn't deleted itself due to drop del
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
if(to_hands)
INVOKE_ASYNC(to_hands, TYPE_PROC_REF(/mob, put_in_hands), weapon)
else
weapon.forceMove(get_turf(victim))
qdel(src)
/// Something deleted or moved our weapon while it was embedded, how rude!
/datum/component/embedded/proc/weaponDeleted()
SIGNAL_HANDLER
var/mob/living/carbon/victim = parent
limb._unembed_object(weapon)
if(victim)
to_chat(victim, span_userdanger("\The [weapon] that was embedded in your [limb.plaintext_zone] disappears!"))
qdel(src)
/// The signal for listening to see if someone is using a hemostat on us to pluck out this object
/datum/component/embedded/proc/checkTweeze(mob/living/carbon/victim, obj/item/possible_tweezers, mob/user)
SIGNAL_HANDLER
if(!istype(victim) || (possible_tweezers.tool_behaviour != TOOL_HEMOSTAT && possible_tweezers.tool_behaviour != TOOL_WIRECUTTER) || user.zone_selected != limb.body_zone)
return
if(weapon != limb.embedded_objects[1]) // just pluck the first one, since we can't easily coordinate with other embedded components affecting this limb who is highest priority
return
if(ishuman(victim)) // check to see if the limb is actually exposed
var/mob/living/carbon/human/victim_human = victim
if(!victim_human.try_inject(user, limb.body_zone, INJECT_CHECK_IGNORE_SPECIES | INJECT_TRY_SHOW_ERROR_MESSAGE))
return TRUE
INVOKE_ASYNC(src, PROC_REF(tweezePluck), possible_tweezers, user)
return COMPONENT_NO_AFTERATTACK
/// The actual action for pulling out an embedded object with a hemostat
/datum/component/embedded/proc/tweezePluck(obj/item/possible_tweezers, mob/user)
var/mob/living/carbon/victim = parent
var/datum/embed_data/embed_data = weapon.get_embed()
var/self_pluck = (user == victim)
// quality of the tool we're using
var/tweezer_speed = possible_tweezers.toolspeed
// is this an actual piece of medical equipment
var/tweezer_safe = (possible_tweezers.tool_behaviour == TOOL_HEMOSTAT)
var/pluck_time = embed_data.rip_time * (weapon.w_class * 0.3) * (self_pluck ? 1.5 : 1) * tweezer_speed * (tweezer_safe ? 1 : 1.5)
if(self_pluck)
user.visible_message(span_danger("[user] begins plucking [weapon] from [user.p_their()] [limb.plaintext_zone] with [possible_tweezers]..."), span_notice("You start plucking [weapon] from your [limb.plaintext_zone] with [possible_tweezers]... (It will take [DisplayTimeText(pluck_time)])"),\
vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=victim)
else
user.visible_message(span_danger("[user] begins plucking [weapon] from [victim]'s [limb.plaintext_zone] with [possible_tweezers]..."),span_notice("You start plucking [weapon] from [victim]'s [limb.plaintext_zone] with [possible_tweezers]... (It will take [DisplayTimeText(pluck_time)])"), \
vision_distance=COMBAT_MESSAGE_RANGE, ignored_mobs=victim)
to_chat(victim, span_userdanger("[user] begins plucking [weapon] from your [limb.plaintext_zone] with [possible_tweezers]... (It will take [DisplayTimeText(pluck_time)])"))
if(!do_after(user, pluck_time, victim))
if(self_pluck)
to_chat(user, span_danger("You fail to pluck [weapon] from your [limb.plaintext_zone]."))
else
to_chat(user, span_danger("You fail to pluck [weapon] from [victim]'s [limb.plaintext_zone]."))
to_chat(victim, span_danger("[user] fails to pluck [weapon] from your [limb.plaintext_zone]."))
return
to_chat(user, span_notice("You successfully pluck [weapon] from [victim]'s [limb.plaintext_zone][tweezer_safe ? "." : ", but hurt [victim.p_them()] in the process."]"))
to_chat(victim, span_notice("[user] plucks [weapon] from your [limb.plaintext_zone][tweezer_safe ? "." : ", but it's not perfect."]"))
if(!tweezer_safe)
// sure it still hurts but it sucks less
damaging_removal(victim, weapon, limb, (0.4 * possible_tweezers.w_class))
safeRemove(user)
/// Called when an object is ripped out of someone's body by magic or other abnormal means
/datum/component/embedded/proc/magic_pull(datum/source, mob/living/caster, obj/marked_item)
SIGNAL_HANDLER
if(marked_item != weapon)
return
var/mob/living/carbon/victim = parent
if(!harmful)
victim.visible_message(span_danger("[marked_item] vanishes from [victim.name]'s [limb.plaintext_zone]!"), span_userdanger("[weapon] vanishes from [limb.plaintext_zone]!"))
return
var/datum/embed_data/embed_data = weapon.get_embed()
var/damage = weapon.w_class * embed_data.remove_pain_mult
limb.receive_damage(brute = (1 - embed_data.pain_stam_pct) * damage * 1.5, sharpness = SHARP_EDGED) // Performs exit wounds and flings the user to the caster if nearby
victim.cause_wound_of_type_and_severity(WOUND_PIERCE, limb, WOUND_SEVERITY_MODERATE)
victim.adjustStaminaLoss(embed_data.pain_stam_pct * damage)
playsound(get_turf(victim), 'sound/effects/wounds/blood2.ogg', 50, TRUE)
var/dist = get_dist(caster, victim) //Check if the caster is close enough to yank them in
if(dist < 7)
victim.throw_at(caster, get_dist(victim, caster) - 1, 1, caster)
victim.Paralyze(1 SECONDS)
victim.visible_message(span_alert("[victim] is sent flying towards [caster] as the [marked_item] tears out of them!"), span_alert("You are launched at [caster] as the [marked_item] tears from your body and towards their hand!"))
victim.visible_message(span_danger("[marked_item] is violently torn from [victim.name]'s [limb.plaintext_zone]!"), span_userdanger("[weapon] is violently torn from your [limb.plaintext_zone]!"))