Refactors embedding to use datums instead of storing data in bespoke elements (#84599)

## About The Pull Request

This refactors embedding elements to make them use singleton datums
(similarly to armor) instead being bespoke and creating a new element
every time armor values are supposed to be adjusted.
Default values have been removed from defines due to now being declared
in base class itself.
Additionally fixes vending machines and tackling gloves setting
generated shards (which they instantly embed into their victim) embed
properties to null after running the embedding code, despite said shards
having non-null embedding values by default, making them not be able to
embed into anyone else, also potentially breaking the pain/jostling code
if they somehow get updated.

## Why It's Good For The Game

Current embedding system is an unnecessarily complicated mess as bespoke
elements are hard to work with, and creating a new element every time
you change values is hacky at best. This change should make it easier to
read and work with.

## Changelog
🆑
fix: Fixed glass shards generated from falling vending machines or
tackling windows not being able to embed into anyone.
refactor: Refactored embedding code to use datums instead of bespoke
elements and ugly associated lists.
/🆑
This commit is contained in:
SmArtKar
2024-07-08 00:20:07 +03:00
committed by GitHub
parent a358dc15ec
commit b6c84135c3
55 changed files with 558 additions and 390 deletions

View File

@@ -180,38 +180,11 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define UNARMED_MISS_CHANCE_MAX 80
//Combat object defines
//Embedded objects
///Chance for embedded objects to cause pain (damage user)
#define EMBEDDED_PAIN_CHANCE 15
///Chance for embedded object to fall out (causing pain but removing the object)
#define EMBEDDED_ITEM_FALLOUT 5
///Chance for an object to embed into somebody when thrown
#define EMBED_CHANCE 45
///Coefficient of multiplication for the damage the item does while embedded (this*item.w_class)
#define EMBEDDED_PAIN_MULTIPLIER 2
///Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class)
#define EMBEDDED_IMPACT_PAIN_MULTIPLIER 4
///The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1)
/// The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1)
#define EMBED_THROWSPEED_THRESHOLD 4
///Coefficient of multiplication for the damage the item does when it falls out or is removed without a surgery (this*item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 6
///A Time in ticks, total removal time = (this*item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_TIME 30
///Chance for embedded objects to cause pain every time they move (jostle)
#define EMBEDDED_JOSTLE_CHANCE 5
///Coefficient of multiplication for the damage the item does while
#define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1
///This percentage of all pain will be dealt as stam damage rather than brute (0-1)
#define EMBEDDED_PAIN_STAM_PCT 0.0
///For thrown weapons, every extra speed it's thrown at above its normal throwspeed will add this to the embed chance
/// For thrown embedding weapons, every extra speed it's thrown at above its normal throwspeed will add this to the embed chance
#define EMBED_CHANCE_SPEED_BONUS 10
#define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE)
#define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1)
#define EMBED_POINTY list("ignore_throwspeed_threshold" = TRUE)
#define EMBED_POINTY_SUPERIOR list("embed_chance" = 100, "ignore_throwspeed_threshold" = TRUE)
//Gun weapon weight
#define WEAPON_LIGHT 1
#define WEAPON_MEDIUM 2

View File

@@ -142,11 +142,8 @@
projectile.wound_bonus += var_modifiers["wound_bonus"]
projectile.bare_wound_bonus += var_modifiers["bare_wound_bonus"]
projectile.demolition_mod += var_modifiers["demolition_mod"]
if(islist(var_modifiers["embedding"]))
var/list/embed_params = var_modifiers["embedding"]
for(var/embed_param in embed_params - "ignore_throwspeed_threshold")
LAZYADDASSOC(projectile.embedding, embed_param, embed_params[embed_param])
projectile.updateEmbedding()
if(var_modifiers["embedding"])
projectile.set_embed(var_modifiers["embedding"])
/datum/component/dart_insert/proc/remove_var_modifiers(obj/projectile/projectile)
projectile.damage -= var_modifiers["damage"]
@@ -155,9 +152,6 @@
projectile.wound_bonus -= var_modifiers["wound_bonus"]
projectile.bare_wound_bonus -= var_modifiers["bare_wound_bonus"]
projectile.demolition_mod -= var_modifiers["demolition_mod"]
if(islist(var_modifiers["embedding"]))
var/list/embed_params = var_modifiers["embedding"]
for(var/embed_param in embed_params - "ignore_throwspeed_threshold")
LAZYADDASSOC(projectile.embedding, embed_param, -embed_params[embed_param])
projectile.updateEmbedding()
if(var_modifiers["embedding"])
projectile.set_embed(initial(projectile.embed_type))
var_modifiers.Cut()

View File

@@ -27,63 +27,28 @@
dupe_mode = COMPONENT_DUPE_ALLOWED
var/obj/item/bodypart/limb
var/obj/item/weapon
// all of this stuff is explained in _DEFINES/combat.dm
var/embed_chance // not like we really need it once we're already stuck in but hey
var/fall_chance
var/pain_chance
var/pain_mult
var/impact_pain_mult
var/remove_pain_mult
var/rip_time
var/ignore_throwspeed_threshold
var/jostle_chance
var/jostle_pain_mult
var/pain_stam_pct
///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/I,
/datum/component/embedded/Initialize(obj/item/weapon,
datum/thrownthing/throwingdatum,
obj/item/bodypart/part,
embed_chance = EMBED_CHANCE,
fall_chance = EMBEDDED_ITEM_FALLOUT,
pain_chance = EMBEDDED_PAIN_CHANCE,
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
remove_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME,
ignore_throwspeed_threshold = FALSE,
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT)
obj/item/bodypart/part)
if(!iscarbon(parent) || !isitem(I))
if(!iscarbon(parent) || !isitem(weapon))
return COMPONENT_INCOMPATIBLE
src.weapon = weapon
if(part)
limb = part
src.embed_chance = embed_chance
src.fall_chance = fall_chance
src.pain_chance = pain_chance
src.pain_mult = pain_mult
src.remove_pain_mult = remove_pain_mult
src.rip_time = rip_time
src.impact_pain_mult = impact_pain_mult
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
src.jostle_chance = jostle_chance
src.jostle_pain_mult = jostle_pain_mult
src.pain_stam_pct = pain_stam_pct
src.weapon = I
if(!weapon.isEmbedHarmless())
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))
@@ -95,13 +60,13 @@
playsound(victim,'sound/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 * impact_pain_mult
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].",I.armour_penetration, weak_against_armour = I.weak_against_armour)
limb.receive_damage(brute=(1-pain_stam_pct) * damage, blocked=armor, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness())
victim.adjustStaminaLoss(pain_stam_pct * damage)
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
@@ -135,20 +100,21 @@
if(victim.stat == DEAD)
return
var/damage = weapon.w_class * pain_mult
var/pain_chance_current = SPT_PROB_RATE(pain_chance / 100, seconds_per_tick) * 100
if(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
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-pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(pain_stam_pct * damage)
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(fall_chance / 100, seconds_per_tick) * 100
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
@@ -165,25 +131,27 @@
SIGNAL_HANDLER
var/mob/living/carbon/victim = parent
var/chance = jostle_chance
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 * jostle_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(pain_stam_pct * damage)
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 * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage, wound_bonus = CANT_WOUND)
victim.adjustStaminaLoss(pain_stam_pct * damage)
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()
@@ -195,7 +163,8 @@
if(I != weapon || src.limb != limb)
return
var/mob/living/carbon/victim = parent
var/time_taken = rip_time * weapon.w_class
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
@@ -214,9 +183,10 @@
/// 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/damage = weapon.w_class * remove_pain_mult * ouch_multiplier
limb.receive_damage(brute=(1-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(pain_stam_pct * damage)
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.
@@ -271,12 +241,13 @@
/// 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 = rip_time * (weapon.w_class * 0.3) * (self_pluck ? 1.5 : 1) * tweezer_speed * (tweezer_safe ? 1 : 1.5)
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)])"),\
@@ -313,10 +284,12 @@
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/damage = weapon.w_class * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage * 1.5, sharpness=SHARP_EDGED) // Performs exit wounds and flings the user to the caster if nearby
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(pain_stam_pct * damage)
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

View File

@@ -596,11 +596,9 @@
if(W.type in list(/obj/structure/window, /obj/structure/window/fulltile, /obj/structure/window/unanchored, /obj/structure/window/fulltile/unanchored)) // boring unreinforced windows
for(var/i in 1 to speed)
var/obj/item/shard/shard = new /obj/item/shard(get_turf(user))
shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5)
shard.updateEmbedding()
shard.set_embed(/datum/embed_data/glass_candy)
user.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
shard.embedding = null
shard.updateEmbedding()
shard.set_embed(initial(shard.embed_type))
W.atom_destruction()
user.adjustStaminaLoss(10 * speed)
user.Paralyze(3 SECONDS)

View File

@@ -24,7 +24,6 @@
if(reusable)
if(!ispath(proj.shrapnel_type))
proj.shrapnel_type = shell.type
proj.updateEmbedding()
proj.AddElement(/datum/element/projectile_drop, shell.type)
/datum/element/caseless/proc/on_fired_casing(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro, obj/projectile/proj)

View File

@@ -10,64 +10,33 @@
*/
/datum/element/embed
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
var/initialized = FALSE /// whether we can skip assigning all the vars (since these are bespoke elements, we don't have to reset the vars every time we attach to something, we already know what we are!)
// all of this stuff is explained in _DEFINES/combat.dm
var/embed_chance
var/fall_chance
var/pain_chance
var/pain_mult
var/remove_pain_mult
var/impact_pain_mult
var/rip_time
var/ignore_throwspeed_threshold
var/jostle_chance
var/jostle_pain_mult
var/pain_stam_pct
var/payload_type
/datum/element/embed/Attach(datum/target, embed_chance, fall_chance, pain_chance, pain_mult, remove_pain_mult, impact_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct, projectile_payload=/obj/item/shard)
/datum/element/embed/Attach(datum/target)
. = ..()
if(!isitem(target) && !isprojectile(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, PROC_REF(severancePackage))
if(isitem(target))
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(checkEmbed))
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examined))
RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, PROC_REF(tryForceEmbed))
RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, PROC_REF(detachFromWeapon))
else
payload_type = projectile_payload
RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(checkEmbedProjectile))
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, PROC_REF(sever_element))
if(isprojectile(target))
RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(check_embed_projectile))
return
if(!initialized)
src.embed_chance = embed_chance
src.fall_chance = fall_chance
src.pain_chance = pain_chance
src.pain_mult = pain_mult
src.remove_pain_mult = remove_pain_mult
src.rip_time = rip_time
src.impact_pain_mult = impact_pain_mult
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
src.jostle_chance = jostle_chance
src.jostle_pain_mult = jostle_pain_mult
src.pain_stam_pct = pain_stam_pct
initialized = TRUE
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(check_embed))
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examined))
RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, PROC_REF(try_force_embed))
RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, PROC_REF(detach_from_weapon))
/datum/element/embed/Detach(obj/target)
. = ..()
if(isitem(target))
UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_ATOM_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED))
else
if(isprojectile(target))
UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT, COMSIG_ELEMENT_ATTACH))
return
UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_ATOM_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED))
/// Checking to see if we're gonna embed into a human
/datum/element/embed/proc/checkEmbed(obj/item/weapon, mob/living/carbon/victim, hit_zone, blocked, datum/thrownthing/throwingdatum, forced=FALSE)
/datum/element/embed/proc/check_embed(obj/item/weapon, mob/living/carbon/victim, hit_zone, blocked, datum/thrownthing/throwingdatum, forced=FALSE)
SIGNAL_HANDLER
if(forced)
@@ -82,7 +51,7 @@
var/flying_speed = throwingdatum?.speed || weapon.throw_speed
if(flying_speed < EMBED_THROWSPEED_THRESHOLD && !ignore_throwspeed_threshold)
if(flying_speed < EMBED_THROWSPEED_THRESHOLD && !weapon.get_embed().ignore_throwspeed_threshold)
return FALSE
if(!roll_embed_chance(weapon, victim, hit_zone, throwingdatum))
@@ -97,27 +66,17 @@
victim.AddComponent(/datum/component/embedded,\
weapon,\
throwingdatum,\
part = limb,\
embed_chance = embed_chance,\
fall_chance = fall_chance,\
pain_chance = pain_chance,\
pain_mult = pain_mult,\
remove_pain_mult = remove_pain_mult,\
rip_time = rip_time,\
ignore_throwspeed_threshold = ignore_throwspeed_threshold,\
jostle_chance = jostle_chance,\
jostle_pain_mult = jostle_pain_mult,\
pain_stam_pct = pain_stam_pct)
part = limb)
///A different embed element has been attached, so we'll detach and let them handle things
/datum/element/embed/proc/severancePackage(obj/weapon, datum/element/E)
/datum/element/embed/proc/sever_element(obj/weapon, datum/element/E)
SIGNAL_HANDLER
if(istype(E, /datum/element/embed))
Detach(weapon)
///If we don't want to be embeddable anymore (deactivating an e-dagger for instance)
/datum/element/embed/proc/detachFromWeapon(obj/weapon)
/datum/element/embed/proc/detach_from_weapon(obj/weapon)
SIGNAL_HANDLER
Detach(weapon)
@@ -126,26 +85,26 @@
/datum/element/embed/proc/examined(obj/item/I, mob/user, list/examine_list)
SIGNAL_HANDLER
if(I.isEmbedHarmless())
if(I.is_embed_harmless())
examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
else
examine_list += "[I] has a fine point, and could probably embed in someone if thrown properly!"
/**
* checkEmbedProjectile() is what we get when a projectile with a defined shrapnel_type impacts a target.
* check_embed_projectile() is what we get when a projectile with a defined shrapnel_type impacts a target.
*
* If we hit a valid target, we create the shrapnel_type object and then forcefully try to embed it on its
* behalf. DO NOT EVER add an embed element to the payload and let it do the rest.
* That's awful, and it'll limit us to drop-deletable shrapnels in the worry of stuff like
* arrows and harpoons being embeddable even when not let loose by their weapons.
*/
/datum/element/embed/proc/checkEmbedProjectile(obj/projectile/source, atom/movable/firer, atom/hit, angle, hit_zone)
/datum/element/embed/proc/check_embed_projectile(obj/projectile/source, atom/movable/firer, atom/hit, angle, hit_zone, blocked)
SIGNAL_HANDLER
if(!source.can_embed_into(hit))
if(!source.can_embed_into(hit) || blocked)
Detach(source)
return // we don't care
var/payload_type = source.shrapnel_type
var/obj/item/payload = new payload_type(get_turf(hit))
if(istype(payload, /obj/item/shrapnel/bullet))
payload.name = source.name
@@ -155,12 +114,12 @@
if(!limb)
limb = C.get_bodypart()
if(!tryForceEmbed(payload, limb))
if(!try_force_embed(payload, limb))
payload.failedEmbed()
Detach(source)
/**
* tryForceEmbed() is called here when we fire COMSIG_EMBED_TRY_FORCE from [/obj/item/proc/tryEmbed]. Mostly, this means we're a piece of shrapnel from a projectile that just impacted something, and we're trying to embed in it.
* try_force_embed() is called here when we fire COMSIG_EMBED_TRY_FORCE from [/obj/item/proc/tryEmbed]. Mostly, this means we're a piece of shrapnel from a projectile that just impacted something, and we're trying to embed in it.
*
* The reason for this extra mucking about is avoiding having to do an extra hitby(), and annoying the target by impacting them once with the projectile, then again with the shrapnel, and possibly
* AGAIN if we actually embed. This way, we save on at least one message.
@@ -171,7 +130,7 @@
* * hit_zone- if our target is a carbon, try to hit them in this zone, if we don't have one, pick a random one. If our target is a bodypart, we already know where we're hitting.
* * forced- if we want this to succeed 100%
*/
/datum/element/embed/proc/tryForceEmbed(obj/item/embedding_item, atom/target, hit_zone, forced=FALSE)
/datum/element/embed/proc/try_force_embed(obj/item/embedding_item, atom/target, hit_zone, forced=FALSE)
SIGNAL_HANDLER
var/obj/item/bodypart/limb
@@ -190,16 +149,16 @@
if(!forced && !roll_embed_chance(embedding_item, victim, hit_zone))
return
return checkEmbed(embedding_item, victim, hit_zone, forced=TRUE) // Don't repeat the embed roll, we already did it
return check_embed(embedding_item, victim, hit_zone, forced=TRUE) // Don't repeat the embed roll, we already did it
/// Calculates the actual chance to embed based on armour penetration and throwing speed, then returns true if we pass that probability check
/datum/element/embed/proc/roll_embed_chance(obj/item/embedding_item, mob/living/victim, hit_zone, datum/thrownthing/throwingdatum)
var/actual_chance = embed_chance
var/actual_chance = embedding_item.get_embed().embed_chance
if(throwingdatum?.speed > embedding_item.throw_speed)
actual_chance += (throwingdatum.speed - embedding_item.throw_speed) * EMBED_CHANCE_SPEED_BONUS
if(embedding_item.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign
if(embedding_item.is_embed_harmless()) // all the armor in the world won't save you from a kick me sign
return prob(actual_chance)
var/armor = max(victim.run_armor_check(hit_zone, BULLET, silent=TRUE), victim.run_armor_check(hit_zone, BOMB, silent=TRUE)) * 0.5 // we'll be nice and take the better of bullet and bomb armor, halved

54
code/datums/embed_data.dm Normal file
View File

@@ -0,0 +1,54 @@
/// Assosciative list of type -> embed data.
GLOBAL_LIST_INIT(embed_by_type, generate_embed_type_cache())
/proc/generate_embed_type_cache()
var/list/embed_cache = list()
for(var/datum/embed_data/embed_type as anything in subtypesof(/datum/embed_data))
var/datum/embed_data/embed = new embed_type
embed_cache[embed_type] = embed
return embed_cache
/proc/get_embed_by_type(embed_type)
var/datum/embed_data/embed = GLOB.embed_by_type[embed_type]
if(embed)
return embed
CRASH("Attempted to get an embed type that did not exist! '[embed_type]'")
/datum/embed_data
/// Chance for an object to embed into somebody when thrown
var/embed_chance = 45
/// Chance for embedded object to fall out (causing pain but removing the object)
var/fall_chance = 5
/// Chance for embedded objects to cause pain (damage user)
var/pain_chance = 15
/// Coefficient of multiplication for the damage the item does while embedded (this*item.w_class)
var/pain_mult = 2
/// Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class)
var/impact_pain_mult = 4
/// Coefficient of multiplication for the damage the item does when it falls out or is removed without a surgery (this*item.w_class)
var/remove_pain_mult = 6
/// Time in ticks, total removal time = (this*item.w_class)
var/rip_time = 30
/// If this should ignore throw speed threshold of 4
var/ignore_throwspeed_threshold = FALSE
/// Chance for embedded objects to cause pain every time they move (jostle)
var/jostle_chance = 5
/// Coefficient of multiplication for the damage the item does while
var/jostle_pain_mult = 1
/// This percentage of all pain will be dealt as stam damage rather than brute (0-1)
var/pain_stam_pct = 0
/datum/embed_data/proc/generate_with_values(embed_chance, fall_chance, pain_chance, pain_mult, impact_pain_mult, remove_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct)
var/datum/embed_data/data = new()
data.embed_chance = !isnull(embed_chance) ? embed_chance : src.embed_chance
data.fall_chance = !isnull(fall_chance) ? fall_chance : src.fall_chance
data.pain_chance = !isnull(pain_chance) ? pain_chance : src.pain_chance
data.pain_mult = !isnull(pain_mult) ? pain_mult : src.pain_mult
data.impact_pain_mult = !isnull(impact_pain_mult) ? impact_pain_mult : src.impact_pain_mult
data.remove_pain_mult = !isnull(remove_pain_mult) ? remove_pain_mult : src.remove_pain_mult
data.rip_time = !isnull(rip_time) ? rip_time : src.rip_time
data.ignore_throwspeed_threshold = !isnull(ignore_throwspeed_threshold) ? ignore_throwspeed_threshold : src.ignore_throwspeed_threshold
data.jostle_chance = !isnull(jostle_chance) ? jostle_chance : src.jostle_chance
data.jostle_pain_mult = !isnull(jostle_pain_mult) ? jostle_pain_mult : src.jostle_pain_mult
data.pain_stam_pct = !isnull(pain_stam_pct) ? pain_stam_pct : src.pain_stam_pct

View File

@@ -48,13 +48,7 @@
force = 2
throwforce = 25
throw_speed = 4
embedding = list(
"impact_pain_mult" = 0,
"embedded_pain_multiplier" = 15,
"embed_chance" = 100,
"embedded_fall_chance" = 0,
"embedded_ignore_throwspeed_threshold" = TRUE,
)
embed_type = /datum/embed_data/tongue_spike
w_class = WEIGHT_CLASS_SMALL
sharpness = SHARP_POINTY
custom_materials = list(/datum/material/biomass = SMALL_MATERIAL_AMOUNT * 5)
@@ -63,6 +57,13 @@
/// if we missed our target
var/missed = TRUE
/datum/embed_data/tongue_spike
impact_pain_mult = 0
pain_mult = 15
embed_chance = 100
fall_chance = 0
ignore_throwspeed_threshold = TRUE
/obj/item/hardened_spike/Initialize(mapload, mob/living/carbon/source)
. = ..()
src.fired_by_ref = WEAKREF(source)
@@ -110,17 +111,14 @@
desc = "Hardened biomass, shaped into... something."
icon_state = "tonguespikechem"
throwforce = 2
embedding = list(
"impact_pain_mult" = 0,
"embedded_pain_multiplier" = 0,
"embed_chance" = 100,
"embedded_fall_chance" = 0,
"embedded_pain_chance" = 0,
"embedded_ignore_throwspeed_threshold" = TRUE, //never hurts once it's in you
)
embed_type = /datum/embed_data/tongue_spike/chem
/// Whether the tongue's already embedded in a target once before
var/embedded_once_alread = FALSE
/datum/embed_data/tongue_spike/chem
pain_mult = 0
pain_chance = 0
/obj/item/hardened_spike/chem/embedded(mob/living/carbon/human/embedded_mob)
. = ..()
if(embedded_once_alread)

View File

@@ -163,8 +163,10 @@
///the icon to indicate this object is being dragged
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
///Does it embed and if yes, what kind of embed
var/list/embedding
/// Does it embed and if yes, what kind of embed
var/embed_type
/// Stores embedding data
var/datum/embed_data/embed_data
///for flags such as [GLASSESCOVERSEYES]
var/flags_cover = 0
@@ -263,8 +265,8 @@
add_weapon_description()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_ITEM, src)
if(LAZYLEN(embedding))
updateEmbedding()
if(get_embed())
AddElement(/datum/element/embed)
setup_reskinning()
@@ -1246,9 +1248,11 @@
return owner.dropItemToGround(src)
///Does the current embedding var meet the criteria for being harmless? Namely, does it have a pain multiplier and jostle pain mult of 0? If so, return true.
/obj/item/proc/isEmbedHarmless()
if(embedding)
return !isnull(embedding["pain_mult"]) && !isnull(embedding["jostle_pain_mult"]) && embedding["pain_mult"] == 0 && embedding["jostle_pain_mult"] == 0
/obj/item/proc/is_embed_harmless()
if (!get_embed())
return FALSE
return !isnull(embed_data.pain_mult) && !isnull(embed_data.jostle_pain_mult) && embed_data.pain_mult == 0 && embed_data.jostle_pain_mult == 0
///In case we want to do something special (like self delete) upon failing to embed in something.
/obj/item/proc/failedEmbed()
@@ -1281,11 +1285,13 @@
/obj/item/proc/tryEmbed(atom/target, forced=FALSE)
if(!isbodypart(target) && !iscarbon(target))
return NONE
if(!forced && !LAZYLEN(embedding))
if(!forced && !get_embed())
return NONE
if(SEND_SIGNAL(src, COMSIG_EMBED_TRY_FORCE, target = target, forced = forced))
return COMPONENT_EMBED_SUCCESS
failedEmbed()
///For when you want to disable an item's embedding capabilities (like transforming weapons and such), this proc will detach any active embed elements from it.
@@ -1293,29 +1299,6 @@
SEND_SIGNAL(src, COMSIG_ITEM_DISABLE_EMBED)
return
///For when you want to add/update the embedding on an item. Uses the vars in [/obj/item/var/embedding], and defaults to config values for values that aren't set. Will automatically detach previous embed elements on this item.
/obj/item/proc/updateEmbedding()
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_EMBEDDING_UPDATE)
if(!LAZYLEN(embedding))
disableEmbedding()
return
AddElement(/datum/element/embed,\
embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\
fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\
pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\
pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\
remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\
rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\
ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\
impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\
jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\
jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT))
return TRUE
/// How many different types of mats will be counted in a bite?
#define MAX_MATS_PER_BITE 2
@@ -1733,6 +1716,18 @@
SEND_SIGNAL(loc, COMSIG_ATOM_CONTENTS_WEIGHT_CLASS_CHANGED, src, old_w_class, new_w_class)
return TRUE
/// Fetches embedding data
/obj/item/proc/get_embed()
RETURN_TYPE(/datum/embed_data)
return embed_type ? (embed_data ||= get_embed_by_type(embed_type)) : null
/obj/item/proc/set_embed(datum/embed_data/embed)
if(embed_data == embed)
return
if(!GLOB.embed_by_type[embed_data?.type])
qdel(embed_data)
embed_data = ispath(embed) ? get_embed_by_type(armor) : embed
SEND_SIGNAL(src, COMSIG_ITEM_EMBEDDING_UPDATE)
/**
* Returns the atom(either itself or an internal module) that will interact/attack the target on behalf of us

View File

@@ -145,9 +145,8 @@
var/obj/item/thrown_weapon = bomb_target
thrown_weapon.throw_speed = max(1, (thrown_weapon.throw_speed - 3))
thrown_weapon.throw_range = max(1, (thrown_weapon.throw_range - 3))
if(thrown_weapon.embedding)
thrown_weapon.embedding["embed_chance"] = 0
thrown_weapon.updateEmbedding()
if(thrown_weapon.get_embed())
thrown_weapon.set_embed(thrown_weapon.get_embed().generate_with_values(embed_chance = 0))
else if(isliving(bomb_target))
plastic_overlay.layer = FLOAT_LAYER

View File

@@ -124,13 +124,19 @@
icon_state = "buckknife"
worn_icon_state = "buckknife"
desc = "A military combat utility survival knife."
embedding = list("pain_mult" = 4, "embed_chance" = 65, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
embed_type = /datum/embed_data/combat_knife
force = 20
throwforce = 20
attack_verb_continuous = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "cuts")
attack_verb_simple = list("slash", "stab", "slice", "tear", "lacerate", "rip", "cut")
slot_flags = ITEM_SLOT_MASK
/datum/embed_data/combat_knife
pain_mult = 4
embed_chance = 65
fall_chance = 10
ignore_throwspeed_threshold = TRUE
/obj/item/knife/combat/Initialize(mapload)
. = ..()
AddComponent(/datum/component/knockoff, 90, list(BODY_ZONE_PRECISE_MOUTH), slot_flags) //90% to knock off when wearing a mask
@@ -155,7 +161,7 @@
icon = 'icons/obj/weapons/stabby.dmi'
icon_state = "survivalknife"
worn_icon_state = "survivalknife"
embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
embed_type = /datum/embed_data/combat_knife/weak
desc = "A hunting grade survival knife."
force = 15
throwforce = 15
@@ -169,13 +175,16 @@
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
desc = "A sharpened bone. The bare minimum in survival."
embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
embed_type = /datum/embed_data/combat_knife/weak
obj_flags = parent_type::obj_flags & ~CONDUCTS_ELECTRICITY
slot_flags = NONE
force = 15
throwforce = 15
custom_materials = null
/datum/embed_data/combat_knife/weak
embed_chance = 35
/obj/item/knife/combat/cyborg
name = "cyborg knife"
icon = 'icons/obj/items_cyborg.dmi'

View File

@@ -113,13 +113,9 @@
SIGNAL_HANDLER
if(active)
if(embedding)
updateEmbedding()
heat = active_heat
START_PROCESSING(SSobj, src)
else
if(embedding)
disableEmbedding()
heat = initial(heat)
STOP_PROCESSING(SSobj, src)
@@ -173,6 +169,10 @@
return (BRUTELOSS|FIRELOSS)
/// Energy swords.
/datum/embed_data/esword
embed_chance = 75
impact_pain_mult = 10
/obj/item/melee/energy/sword
name = "energy sword"
desc = "May the force be within you."
@@ -189,7 +189,7 @@
armour_penetration = 35
block_chance = 50
block_sound = 'sound/weapons/block_blade.ogg'
embedding = list("embed_chance" = 75, "impact_pain_mult" = 10)
embed_type = /datum/embed_data/esword
/obj/item/melee/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))

View File

@@ -186,7 +186,7 @@
icon_state = "gumball"
damage = 0
speed = 0.5
embedding = null
embed_type = null
/obj/projectile/bullet/gumball/Initialize(mapload)
. = ..()
@@ -219,29 +219,30 @@
icon_state = "lollipop_1"
damage = 0
speed = 0.5
embedding = null
embed_type = null
var/head_color
/obj/projectile/bullet/lollipop/harmful
embedding = list(
embed_chance = 35,
fall_chance = 2,
jostle_chance = 0,
ignore_throwspeed_threshold = TRUE,
pain_stam_pct = 0.5,
pain_mult = 3,
rip_time = 10,
)
embed_type = /datum/embed_data/lollipop
damage = 10
shrapnel_type = /obj/item/food/lollipop/cyborg
embed_falloff_tile = 0
/datum/embed_data/lollipop
embed_chance = 35
fall_chance = 2
jostle_chance = 0
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.5
pain_mult = 3
rip_time = 10
/obj/projectile/bullet/lollipop/Initialize(mapload)
. = ..()
var/mutable_appearance/head = mutable_appearance('icons/obj/weapons/guns/projectiles.dmi', "lollipop_2")
head.color = head_color = rgb(rand(0, 255), rand(0, 255), rand(0, 255))
add_overlay(head)
if(!embedding)
if(!embed_type)
AddElement(/datum/element/projectile_drop, /obj/item/food/lollipop/cyborg)
RegisterSignals(src, list(COMSIG_PROJECTILE_ON_SPAWN_DROP, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED), PROC_REF(handle_drop))

View File

@@ -17,8 +17,7 @@
name = "bullet"
icon = 'icons/obj/weapons/guns/ammo.dmi'
icon_state = "s-casing"
embedding = null // embedding vars are taken from the projectile itself
embed_type = null
/obj/projectile/bullet/shrapnel
name = "flying shrapnel shard"
@@ -34,7 +33,12 @@
ignore_range_hit_prone_targets = TRUE
sharpness = SHARP_EDGED
wound_bonus = 30
embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=1)
embed_type = /datum/embed_data/shrapnel
/datum/embed_data/shrapnel
embed_chance = 70
ignore_throwspeed_threshold = TRUE
fall_chance = 1
/obj/projectile/bullet/shrapnel/short_range
range = 5
@@ -70,7 +74,17 @@
ricochet_incidence_leeway = 0
embed_falloff_tile = -2
shrapnel_type = /obj/item/shrapnel/stingball
embedding = list(embed_chance=55, fall_chance=2, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=3, jostle_pain_mult=3, rip_time=15)
embed_type = /datum/embed_data/stingball
/datum/embed_data/stingball
embed_chance = 55
fall_chance = 2
jostle_chance = 7
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.7
pain_mult = 3
jostle_pain_mult = 3
rip_time = 15
/obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A)
hit_prone_targets = TRUE // ducking will save you from the first wave, but not the rebounds
@@ -92,10 +106,20 @@
ricochets_max = 2
ricochet_chance = 140
shrapnel_type = /obj/item/shrapnel/capmine
embedding = list(embed_chance=90, fall_chance=3, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=5, jostle_pain_mult=6, rip_time=15)
embed_type = /datum/embed_data/capmine
wound_falloff_tile = 0
embed_falloff_tile = 0
/datum/embed_data/capmine
embed_chance = 90
fall_chance = 3
jostle_chance = 7
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.7
pain_mult = 5
jostle_pain_mult = 6
rip_time = 15
/obj/item/shrapnel/capmine
name = "\improper AP shrapnel shard"
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5)

View File

@@ -12,7 +12,7 @@
throwforce = 20
throw_speed = 4
demolition_mod = 0.75
embedding = list("impact_pain_mult" = 2, "remove_pain_mult" = 4, "jostle_chance" = 2.5)
embed_type = /datum/embed_data/spear
armour_penetration = 10
custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/glass= HALF_SHEET_MATERIAL_AMOUNT * 2)
hitsound = 'sound/weapons/bladeslice.ogg'
@@ -32,6 +32,11 @@
/// How much damage to do wielded
var/force_wielded = 18
/datum/embed_data/spear
impact_pain_mult = 2
remove_pain_mult = 4
jostle_chance = 2.5
/datum/armor/item_spear
fire = 50
acid = 30

View File

@@ -33,13 +33,16 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
attack_verb_continuous = list("hits", "bludgeons", "whacks")
attack_verb_simple = list("hit", "bludgeon", "whack")
hitsound = 'sound/weapons/gun/general/grenade_launch.ogg'
embedding = list(embed_chance = 50)
embed_type = /datum/embed_data/rods
novariants = TRUE
matter_amount = 2
cost = HALF_SHEET_MATERIAL_AMOUNT
source = /datum/robot_energy_storage/material/iron
merge_type = /obj/item/stack/rods
/datum/embed_data/rods
embed_chance = 50
/obj/item/stack/rods/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] begins to stuff \the [src] down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!"))//it looks like theyre ur mum
return BRUTELOSS

View File

@@ -293,7 +293,16 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
var/shiv_type = /obj/item/knife/shiv
var/craft_time = 3.5 SECONDS
var/obj/item/stack/sheet/weld_material = /obj/item/stack/sheet/glass
embedding = list("embed_chance" = 65)
embed_type = /datum/embed_data/shard
/datum/embed_data/shard
embed_chance = 65
/datum/embed_data/glass_candy
embed_chance = 100
ignore_throwspeed_threshold = TRUE
impact_pain_mult = 1
pain_chance = 5
/datum/armor/item_shard
melee = 100

View File

@@ -14,12 +14,17 @@
grind_results = list(/datum/reagent/cellulose = 5)
splint_factor = 0.65
merge_type = /obj/item/stack/sticky_tape
var/list/conferred_embed = EMBED_HARMLESS
var/conferred_embed = /datum/embed_data/sticky_tape
///The tape type you get when ripping off a piece of tape.
var/obj/tape_gag = /obj/item/clothing/mask/muzzle/tape
greyscale_config = /datum/greyscale_config/tape
greyscale_colors = "#B2B2B2#BD6A62"
/datum/embed_data/sticky_tape
pain_mult = 0
jostle_pain_mult = 0
ignore_throwspeed_threshold = 0
/obj/item/stack/sticky_tape/attack_hand(mob/user, list/modifiers)
if(user.get_inactive_held_item() == src)
if(is_zero_amount(delete_if_zero = TRUE))
@@ -43,7 +48,7 @@
if(!isitem(target))
return NONE
if(target.embedding && target.embedding == conferred_embed)
if(target.get_embed()?.type == conferred_embed)
to_chat(user, span_warning("[target] is already coated in [src]!"))
return ITEM_INTERACT_BLOCKING
@@ -60,12 +65,11 @@
user.put_in_hands(O)
return ITEM_INTERACT_SUCCESS
if(target.embedding && target.embedding == conferred_embed)
if(target.get_embed() && target.get_embed().type == conferred_embed)
to_chat(user, span_warning("[target] is already coated in [src]!"))
return ITEM_INTERACT_BLOCKING
target.embedding = conferred_embed
target.updateEmbedding()
target.set_embed(conferred_embed)
to_chat(user, span_notice("You finish wrapping [target] with [src]."))
target.name = "[prefix] [target.name]"
@@ -80,34 +84,44 @@
singular_name = "super sticky tape"
desc = "Quite possibly the most mischevious substance in the galaxy. Use with extreme lack of caution."
prefix = "super sticky"
conferred_embed = EMBED_HARMLESS_SUPERIOR
conferred_embed = /datum/embed_data/sticky_tape/super
splint_factor = 0.4
merge_type = /obj/item/stack/sticky_tape/super
greyscale_colors = "#4D4D4D#75433F"
tape_gag = /obj/item/clothing/mask/muzzle/tape/super
/datum/embed_data/sticky_tape/super
embed_chance = 100
fall_chance = 0.1
/obj/item/stack/sticky_tape/pointy
name = "pointy tape"
singular_name = "pointy tape"
desc = "Used for sticking to things for sticking said things inside people."
icon_state = "tape_spikes"
prefix = "pointy"
conferred_embed = EMBED_POINTY
conferred_embed = /datum/embed_data/pointy_tape
merge_type = /obj/item/stack/sticky_tape/pointy
greyscale_config = /datum/greyscale_config/tape/spikes
greyscale_colors = "#E64539#808080#AD2F45"
tape_gag = /obj/item/clothing/mask/muzzle/tape/pointy
/datum/embed_data/pointy_tape
ignore_throwspeed_threshold = TRUE
/obj/item/stack/sticky_tape/pointy/super
name = "super pointy tape"
singular_name = "super pointy tape"
desc = "You didn't know tape could look so sinister. Welcome to Space Station 13."
prefix = "super pointy"
conferred_embed = EMBED_POINTY_SUPERIOR
conferred_embed = /datum/embed_data/pointy_tape/super
merge_type = /obj/item/stack/sticky_tape/pointy/super
greyscale_colors = "#8C0A00#4F4F4F#300008"
tape_gag = /obj/item/clothing/mask/muzzle/tape/pointy/super
/datum/embed_data/pointy_tape/super
embed_chance = 100
/obj/item/stack/sticky_tape/surgical
name = "surgical tape"
singular_name = "surgical tape"

View File

@@ -7,7 +7,6 @@
w_class = WEIGHT_CLASS_SMALL
throwforce = 0
throw_speed = 1
embedding = EMBED_HARMLESS
custom_materials = list(/datum/material/iron= HALF_SHEET_MATERIAL_AMOUNT)
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb_continuous = list("pokes", "jabs", "pins the tail on")
@@ -15,6 +14,12 @@
sharpness = SHARP_POINTY
max_integrity = 200
layer = CORGI_ASS_PIN_LAYER
embed_type = /datum/embed_data/corgi_pin
/datum/embed_data/corgi_pin
pain_chance = 0
jostle_pain_mult = 0
ignore_throwspeed_threshold = TRUE
/obj/item/poster/tail_board
name = "party game poster"

View File

@@ -387,7 +387,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 2
throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding
throw_speed = 4
embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0)
embed_type = /datum/embed_data/throwing_star
armour_penetration = 40
w_class = WEIGHT_CLASS_SMALL
@@ -395,11 +395,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 5)
resistance_flags = FIRE_PROOF
/datum/embed_data/throwing_star
pain_mult = 4
embed_chance = 100
fall_chance = 0
/obj/item/throwing_star/stamina
name = "shock throwing star"
desc = "An aerodynamic disc designed to cause excruciating pain when stuck inside fleeing targets, hopefully without causing fatal harm."
throwforce = 5
embedding = list("pain_chance" = 5, "embed_chance" = 100, "fall_chance" = 0, "jostle_chance" = 10, "pain_stam_pct" = 0.8, "jostle_pain_mult" = 3)
embed_type = /datum/embed_data/throwing_star/stamina
/datum/embed_data/throwing_star/stamina
pain_mult = 5
jostle_chance = 10
pain_stam_pct = 0.8
jostle_pain_mult = 3
/obj/item/throwing_star/toy
name = "toy throwing star"
@@ -407,7 +418,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
sharpness = NONE
force = 0
throwforce = 0
embedding = list("pain_mult" = 0, "jostle_pain_mult" = 0, "embed_chance" = 100, "fall_chance" = 0)
embed_type = /datum/embed_data/throwing_star/toy
/datum/embed_data/throwing_star/toy
pain_mult = 0
jostle_pain_mult = 0
/obj/item/switchblade
name = "switchblade"
@@ -1082,7 +1097,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
throwforce = 25
throw_speed = 4
attack_speed = CLICK_CD_HYPER_RAPID
embedding = list("embed_chance" = 100)
embed_type = /datum/embed_data/hfr_blade
block_chance = 25
block_sound = 'sound/weapons/parry.ogg'
sharpness = SHARP_EDGED
@@ -1097,6 +1112,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/// The previous target we attacked
var/datum/weakref/previous_target
/datum/embed_data/hfr_blade
embed_chance = 100
/obj/item/highfrequencyblade/Initialize(mapload)
. = ..()
AddComponent(/datum/component/two_handed, \

View File

@@ -89,7 +89,7 @@
force = 0
throwforce = 0
hitsound = null
embedding = null
embed_type = null
light_color = COLOR_YELLOW
sword_color_icon = "bananium"
active_heat = 0

View File

@@ -15,15 +15,7 @@
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
actions_types = list(/datum/action/item_action/rune_shatter)
embedding = list(
ignore_throwspeed_threshold = TRUE,
embed_chance = 75,
jostle_chance = 2,
jostle_pain_mult = 5,
pain_stam_pct = 0.4,
pain_mult = 3,
rip_time = 15,
)
embed_type = /datum/embed_data/rune_carver
/// Whether we're currently drawing a rune
var/drawing = FALSE
@@ -34,6 +26,15 @@
/// Turfs that you cannot draw carvings on
var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/lava))
/datum/embed_data/rune_carver
ignore_throwspeed_threshold = TRUE
embed_chance = 75
jostle_chance = 2
jostle_pain_mult = 5
pain_stam_pct = 0.4
pain_mult = 3
rip_time = 15
/obj/item/melee/rune_carver/examine(mob/user)
. = ..()
if(!IS_HERETIC_OR_MONSTER(user) && !isobserver(user))

View File

@@ -43,9 +43,12 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
* Makes every item in the world embed when thrown, but also hooks into global signals for new items created to also bless them with embed-ability(??).
*/
/datum/global_funny_embedding
var/embed_type = EMBED_POINTY
var/embed_type = /datum/embed_data/global_funny
var/prefix = "error"
/datum/embed_data/global_funny
ignore_throwspeed_threshold = TRUE
/datum/global_funny_embedding/New()
. = ..()
//second operation takes MUCH longer, so lets set up signals first.
@@ -61,11 +64,11 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
SIGNAL_HANDLER
// this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:)
if(LAZYLEN(created_item.embedding))
return //already embeds to some degree, so whatever 🐀
created_item.embedding = embed_type
if(created_item.get_embed())
return //already embeds to some degree, so whatever // No rat allowed
created_item.name = "[prefix] [created_item.name]"
created_item.updateEmbedding()
created_item.set_embed(embed_type)
/**
* ### handle_current_items
@@ -77,17 +80,20 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
CHECK_TICK
if(!(embed_item.flags_1 & INITIALIZED_1))
continue
if(!embed_item.embedding)
embed_item.embedding = embed_type
embed_item.updateEmbedding()
if(embed_item.get_embed())
continue
embed_item.set_embed(embed_type)
embed_item.name = "[prefix] [embed_item.name]"
///everything will be... POINTY!!!!
/datum/global_funny_embedding/pointy
embed_type = EMBED_POINTY
prefix = "pointy"
///everything will be... sticky? sure, why not
/datum/global_funny_embedding/sticky
embed_type = EMBED_HARMLESS
embed_type = /datum/embed_data/global_funny/sticky
prefix = "sticky"
/datum/embed_data/global_funny/sticky
pain_mult = 0
jostle_pain_mult = 0

View File

@@ -14,7 +14,7 @@
throw_speed = 2
block_chance = 0
throwforce = 0
embedding = null
embed_type = null
sword_color_icon = null
active_throwforce = 0

View File

@@ -478,13 +478,18 @@
throwforce = 15
throw_speed = 4
throw_range = 7
embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
embed_type = /datum/embed_data/hatchet
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*7.5)
attack_verb_continuous = list("chops", "tears", "lacerates", "cuts")
attack_verb_simple = list("chop", "tear", "lacerate", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
/datum/embed_data/hatchet
pain_mult = 4
embed_chance = 35
fall_chance = 10
/obj/item/hatchet/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \

View File

@@ -859,12 +859,15 @@
return
var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
if(our_seed.get_gene(/datum/plant_gene/trait/stinging))
our_plant.embedding = EMBED_POINTY
else
our_plant.embedding = EMBED_HARMLESS
our_plant.updateEmbedding()
our_plant.throwforce = (our_seed.potency/20)
if (!our_plant.get_embed())
return
if(our_seed.get_gene(/datum/plant_gene/trait/stinging))
our_plant.set_embed(our_plant.get_embed().generate_with_values(ignore_throwspeed_threshold = TRUE))
return
our_plant.set_embed(our_plant.get_embed().generate_with_values(ignore_throwspeed_threshold = TRUE, pain_mult = 0, jostle_pain_mult = 0))
/**
* This trait automatically heats up the plant's chemical contents when harvested.

View File

@@ -415,7 +415,7 @@
// this way, we only visibly try to examine ourselves if we have something embedded, otherwise we'll still hug ourselves :)
visible_message(span_notice("[src] examines [p_them()]self."), \
span_notice("You check yourself for shrapnel."))
if(I.isEmbedHarmless())
if(I.is_embed_harmless())
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] stuck to your [LB.name]!</a>")
else
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")

View File

@@ -36,7 +36,7 @@
var/list/msg = list("<span class='warning'>")
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
for(var/obj/item/embedded_item as anything in bodypart.embedded_objects)
if(embedded_item.isEmbedHarmless())
if(embedded_item.is_embed_harmless())
msg += "<B>[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] stuck to [t_his] [bodypart.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] embedded in [t_his] [bodypart.name]!</B>\n"

View File

@@ -138,7 +138,7 @@
disabled += body_part
missing -= body_part.body_zone
for(var/obj/item/I in body_part.embedded_objects)
if(I.isEmbedHarmless())
if(I.is_embed_harmless())
msg += "<B>[t_He] [t_has] [icon2html(I, user)] \a [I] stuck to [t_his] [body_part.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] [icon2html(I, user)] \a [I] embedded in [t_his] [body_part.name]!</B>\n"

View File

@@ -375,7 +375,7 @@
name = "mending globule"
icon_state = "glob_projectile"
shrapnel_type = /obj/item/mending_globule
embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0.5)
embed_type = /datum/embed_data/mending_globule
damage = 0
///This item is what is embedded into the mob, and actually handles healing of mending globules
@@ -384,10 +384,17 @@
desc = "It somehow heals those who touch it."
icon = 'icons/obj/science/vatgrowing.dmi'
icon_state = "globule"
embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0.5)
embed_type = /datum/embed_data/mending_globule
var/obj/item/bodypart/bodypart
var/heals_left = 35
/datum/embed_data/mending_globule
embed_chance = 100
ignore_throwspeed_threshold = TRUE
pain_mult = 0
jostle_pain_mult = 0
fall_chance = 0.5
/obj/item/mending_globule/Destroy()
. = ..()
bodypart = null

View File

@@ -559,7 +559,7 @@
light_range = 1
light_power = 1
light_color = COLOR_LIGHT_ORANGE
embedding = null
embed_type = null
/obj/projectile/bullet/mining_bomb/Initialize(mapload)
. = ..()

View File

@@ -29,7 +29,7 @@
var/degrees = 0
var/font = PEN_FONT
var/requires_gravity = TRUE // can you use this to write in zero-g
embedding = list(embed_chance = 50)
embed_type = /datum/embed_data/pen
sharpness = SHARP_POINTY
var/dart_insert_icon = 'icons/obj/weapons/guns/toy.dmi'
var/dart_insert_casing_icon_state = "overlay_pen"
@@ -37,6 +37,9 @@
/// If this pen can be clicked in order to retract it
var/can_click = TRUE
/datum/embed_data/pen
embed_chance = 50
/obj/item/pen/Initialize(mapload)
. = ..()
AddComponent(/datum/component/dart_insert, \
@@ -86,7 +89,7 @@
return list(
"damage" = max(5, throwforce),
"speed" = max(0, throw_speed - 3),
"embedding" = embedding,
"embedding" = get_embed(),
"armour_penetration" = armour_penetration,
"wound_bonus" = wound_bonus,
"bare_wound_bonus" = bare_wound_bonus,
@@ -191,7 +194,7 @@
"Black and Silver" = "pen-fountain-b",
"Command Blue" = "pen-fountain-cb"
)
embedding = list("embed_chance" = 75)
embed_type = /datum/embed_data/pen/captain
dart_insert_casing_icon_state = "overlay_fountainpen_gold"
dart_insert_projectile_icon_state = "overlay_fountainpen_gold_proj"
var/list/overlay_reskin = list(
@@ -202,6 +205,9 @@
"Command Blue" = "overlay_fountainpen_gold"
)
/datum/embed_data/pen/captain
embed_chance = 50
/obj/item/pen/fountain/captain/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \
@@ -414,7 +420,7 @@
inhand_icon_state = hidden_icon
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
embedding = list(embed_chance = 100) // Rule of cool
set_embed(/datum/embed_data/edagger_active)
else
name = initial(name)
desc = initial(desc)
@@ -422,15 +428,17 @@
inhand_icon_state = initial(inhand_icon_state)
lefthand_file = initial(lefthand_file)
righthand_file = initial(righthand_file)
embedding = list(embed_chance = EMBED_CHANCE)
set_embed(embed_type)
updateEmbedding()
if(user)
balloon_alert(user, "[hidden_name] [active ? "active" : "concealed"]")
playsound(src, active ? 'sound/weapons/saberon.ogg' : 'sound/weapons/saberoff.ogg', 5, TRUE)
set_light_on(active)
return COMPONENT_NO_DEFAULT_MESSAGE
/datum/embed_data/edagger_active
embed_chance = 100
/obj/item/pen/edagger/proc/on_scan(datum/source, mob/user, list/extra_data)
SIGNAL_HANDLER
LAZYADD(extra_data[DETSCAN_CATEGORY_ILLEGAL], "Hard-light generator detected.")

View File

@@ -33,16 +33,17 @@
speed = 1
range = 25
shrapnel_type = null
embedding = list(
embed_chance = 90,
fall_chance = 2,
jostle_chance = 2,
ignore_throwspeed_threshold = TRUE,
pain_stam_pct = 0.5,
pain_mult = 3,
jostle_pain_mult = 3,
embed_type = /datum/embed_data/arrow
/datum/embed_data/arrow
embed_chance = 90
fall_chance = 2
jostle_chance = 2
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.5
pain_mult = 3
jostle_pain_mult = 3
rip_time = 1 SECONDS
)
/// holy arrows
/obj/item/ammo_casing/arrow/holy
@@ -99,7 +100,7 @@
desc = "THE UNMATCHED POWER OF THE SUN"
icon_state = "holy_arrow_projectile"
damage = 20
embedding = null
embed_type = null
/obj/projectile/bullet/arrow/blazing/on_hit(atom/target, blocked, pierce_hit)
. = ..()

View File

@@ -191,7 +191,9 @@
///If defined, on hit we create an item of this type then call hitby() on the hit target with this, mainly used for embedding items (bullets) in targets
var/shrapnel_type
///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target
var/list/embedding
var/embed_type
///Saves embedding data
var/datum/embed_data/embed_data
///If TRUE, hit mobs, even if they are lying on the floor and are not our target within MAX_RANGE_HIT_PRONE_TARGETS tiles
var/hit_prone_targets = FALSE
///if TRUE, ignores the range of MAX_RANGE_HIT_PRONE_TARGETS tiles of hit_prone_targets
@@ -218,8 +220,8 @@
/obj/projectile/Initialize(mapload)
. = ..()
decayedRange = range
if(embedding)
updateEmbedding()
if(get_embed())
AddElement(/datum/element/embed)
AddElement(/datum/element/connect_loc, projectile_connections)
/obj/projectile/proc/Range()
@@ -227,8 +229,8 @@
if(wound_bonus != CANT_WOUND)
wound_bonus += wound_falloff_tile
bare_wound_bonus = max(0, bare_wound_bonus + wound_falloff_tile)
if(embedding)
embedding["embed_chance"] += embed_falloff_tile
if(get_embed())
set_embed(embed_data.generate_with_values(embed_data.embed_chance + embed_falloff_tile)) // Should be rewritten in projecitle refactor
if(damage_falloff_tile && damage >= 0)
damage += damage_falloff_tile
if(stamina_falloff_tile && stamina >= 0)
@@ -1130,26 +1132,6 @@
/obj/projectile/experience_pressure_difference()
return
///Like [/obj/item/proc/updateEmbedding] but for projectiles instead, call this when you want to add embedding or update the stats on the embedding element
/obj/projectile/proc/updateEmbedding()
if(!shrapnel_type || !LAZYLEN(embedding))
return
AddElement(/datum/element/embed,\
embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\
fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\
pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\
pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\
remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\
rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\
ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\
impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\
jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\
jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
projectile_payload = shrapnel_type)
return TRUE
/**
* Is this projectile considered "hostile"?
*
@@ -1169,7 +1151,7 @@
///Checks if the projectile can embed into someone
/obj/projectile/proc/can_embed_into(atom/hit)
return embedding && shrapnel_type && iscarbon(hit) && !HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE)
return get_embed() && shrapnel_type && iscarbon(hit) && !HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE)
/// Reflects the projectile off of something
/obj/projectile/proc/reflect(atom/hit_atom)
@@ -1212,3 +1194,16 @@
bullet.preparePixelProjectile(target, src)
bullet.fire()
return bullet
/// Fetches embedding data
/obj/projectile/proc/get_embed()
return embed_type ? (embed_data ||= get_embed_by_type(embed_type)) : null
/obj/projectile/proc/set_embed(datum/embed_data/embed)
if(embed_data == embed)
return
// GLOB.embed_by_type stores shared "default" embedding values of datums
// Dynamically generated embeds use the base class and thus are not present in there, and should be qdeleted upon being discarded
if(!isnull(embed_data) && !GLOB.embed_by_type[embed_data.type])
qdel(embed_data)
embed_data = ispath(embed) ? get_embed_by_type(armor) : embed

View File

@@ -8,7 +8,7 @@
sharpness = SHARP_POINTY
impact_effect_type = /obj/effect/temp_visual/impact_effect
shrapnel_type = /obj/item/shrapnel/bullet
embedding = list(embed_chance=20, fall_chance=2, jostle_chance=0, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.5, pain_mult=3, rip_time=10)
embed_type = /datum/embed_data/bullet
wound_bonus = 0
wound_falloff_tile = -5
embed_falloff_tile = -3
@@ -16,3 +16,12 @@
/obj/projectile/bullet/smite
name = "divine retribution"
damage = 10
/datum/embed_data/bullet
embed_chance=20
fall_chance=2
jostle_chance=0
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.5
pain_mult=3
rip_time=10

View File

@@ -30,7 +30,7 @@
pass_flags = PASSTABLE | PASSMOB
sharpness = NONE
shrapnel_type = null
embedding = null
embed_type = null
impact_effect_type = null
suppressed = SUPPRESSED_VERY
damage_type = BURN

View File

@@ -8,7 +8,7 @@
dismemberment = 0
paralyze = 5 SECONDS
stutter = 20 SECONDS
embedding = null
embed_type = null
hitsound = 'sound/effects/meteorimpact.ogg'
hitsound_wall = 'sound/weapons/sonic_jackhammer.ogg'
/// If our cannonball hits something, it reduces the damage by this value.

View File

@@ -2,7 +2,7 @@
name = "dart"
icon_state = "cbbolt"
damage = 6
embedding = null
embed_type = null
shrapnel_type = null
var/inject_flags = null

View File

@@ -4,7 +4,7 @@
var/obj/item/dnainjector/injector
damage = 5
hitsound_wall = SFX_SHATTER
embedding = null
embed_type = null
shrapnel_type = null
/obj/projectile/bullet/dnainjector/on_hit(atom/target, blocked = 0, pierce_hit)

View File

@@ -8,7 +8,7 @@
base_icon_state = "foamdart"
range = 10
shrapnel_type = null
embedding = null
embed_type = null
var/modified = FALSE
var/obj/item/pen/pen = null

View File

@@ -5,7 +5,7 @@
desc = "USE A WEEL GUN"
icon_state= "bolter"
damage = 60
embedding = null
embed_type = null
shrapnel_type = null
/obj/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0, pierce_hit)

View File

@@ -4,11 +4,21 @@
name = "junk bullet"
icon_state = "trashball"
damage = 30
embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
embed_type = /datum/embed_data/bullet_junk
var/bane_mob_biotypes = MOB_ROBOTIC
var/bane_multiplier = 1.5
var/bane_added_damage = 0
/datum/embed_data/bullet_junk
embed_chance=15
fall_chance=3
jostle_chance=4
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=5
jostle_pain_mult=6
rip_time=10
/obj/projectile/bullet/junk/Initialize(mapload)
. = ..()
AddElement(/datum/element/bane, mob_biotypes = bane_mob_biotypes, target_type = /mob/living, damage_multiplier = bane_multiplier, added_damage = bane_added_damage, requires_combat_mode = FALSE)
@@ -28,7 +38,7 @@
name = "bundle of live electrical parts"
icon_state = "tesla_projectile"
damage = 15
embedding = null
embed_type = null
shrapnel_type = null
bane_multiplier = 3
@@ -49,10 +59,20 @@
name = "junk ripper bullet"
icon_state = "redtrac"
damage = 10
embedding = list(embed_chance=100, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
embed_type = /datum/embed_data/bullet_junk_ripper
wound_bonus = 10
bare_wound_bonus = 30
/datum/embed_data/bullet_junk_ripper
embed_chance=100
fall_chance=3
jostle_chance=4
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=5
jostle_pain_mult=6
rip_time=10
/obj/projectile/bullet/junk/reaper
name = "junk reaper bullet"
tracer_type = /obj/effect/projectile/tracer/sniper

View File

@@ -3,13 +3,23 @@
/obj/projectile/bullet/c9mm
name = "9mm bullet"
damage = 30
embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
embed_type = /datum/embed_data/bullet_c9mm
/datum/embed_data/bullet_c9mm
embed_chance=15
fall_chance=3
jostle_chance=4
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=5
jostle_pain_mult=6
rip_time=10
/obj/projectile/bullet/c9mm/ap
name = "9mm armor-piercing bullet"
damage = 27
armour_penetration = 40
embedding = null
embed_type = null
shrapnel_type = null
/obj/projectile/bullet/c9mm/hp

View File

@@ -21,9 +21,19 @@
ricochet_auto_aim_range = 3
wound_bonus = -20
bare_wound_bonus = 10
embedding = list(embed_chance=25, fall_chance=2, jostle_chance=2, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=3, jostle_pain_mult=5, rip_time=1 SECONDS)
embed_type = /datum/embed_data/bullet_c38
embed_falloff_tile = -4
/datum/embed_data/bullet_c38
embed_chance=25
fall_chance=2
jostle_chance=2
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=3
jostle_pain_mult=5
rip_time=1 SECONDS
/obj/projectile/bullet/c38/match
name = ".38 Match bullet"
ricochets_max = 4
@@ -45,7 +55,7 @@
ricochet_decay_damage = 0.8
shrapnel_type = null
sharpness = NONE
embedding = null
embed_type = null
// premium .38 ammo from cargo, weak against armor, lower base damage, but excellent at embedding and causing slice wounds at close range
/obj/projectile/bullet/c38/dumdum
@@ -56,10 +66,20 @@
sharpness = SHARP_EDGED
wound_bonus = 20
bare_wound_bonus = 20
embedding = list(embed_chance=75, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=1 SECONDS)
embed_type = /datum/embed_data/bullet_c38_dumdum
wound_falloff_tile = -5
embed_falloff_tile = -15
/datum/embed_data/bullet_c38_dumdum
embed_chance=75
fall_chance=3
jostle_chance=4
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=5
jostle_pain_mult=6
rip_time=1 SECONDS
/obj/projectile/bullet/c38/trac
name = ".38 TRAC bullet"
damage = 10

View File

@@ -48,10 +48,20 @@
armour_penetration = 50
wound_bonus = -20
bare_wound_bonus = 80
embedding = list(embed_chance=100, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
embed_type = /datum/embed_data/harpoon
wound_falloff_tile = -5
shrapnel_type = null
/datum/embed_data/harpoon
embed_chance=100
fall_chance=3
jostle_chance=4
ignore_throwspeed_threshold=TRUE
pain_stam_pct=0.4
pain_mult=5
jostle_pain_mult=6
rip_time=10
// Rebar (Rebar Crossbow)
/obj/projectile/bullet/rebar
name = "rebar"
@@ -62,11 +72,21 @@
armour_penetration = 10
wound_bonus = -20
bare_wound_bonus = 20
embedding = list("embed_chance" = 60, "fall_chance" = 2, "jostle_chance" = 2, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.4, "pain_mult" = 4, "jostle_pain_mult" = 2, "rip_time" = 10)
embed_type = /datum/embed_data/rebar
embed_falloff_tile = -5
wound_falloff_tile = -2
shrapnel_type = /obj/item/ammo_casing/rebar
/datum/embed_data/rebar
embed_chance = 60
fall_chance = 2
jostle_chance = 2
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.4
pain_mult = 4
jostle_pain_mult = 2
rip_time = 10
/obj/projectile/bullet/rebar/proc/handle_drop(datum/source, obj/item/ammo_casing/rebar/newcasing)
/obj/projectile/bullet/rebar/syndie
@@ -78,10 +98,20 @@
armour_penetration = 20 //A bit better versus armor. Gets past anti laser armor or a vest, but doesnt wound proc on sec armor.
wound_bonus = 10
bare_wound_bonus = 20
embedding = list("embed_chance" = 80, "fall_chance" = 1, "jostle_chance" = 3, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.4, "pain_mult" = 3, "jostle_pain_mult" = 2, "rip_time" = 14)
embed_falloff_tile = -3
embed_type = /datum/embed_data/rebar_syndie
shrapnel_type = /obj/item/ammo_casing/rebar/syndie
/datum/embed_data/rebar_syndie
embed_chance = 80
fall_chance = 1
jostle_chance = 3
ignore_throwspeed_threshold
pain_stam_pct = 0.4
pain_mult = 3
jostle_pain_mult = 2
rip_time = 14
/obj/projectile/bullet/rebar/zaukerite
name = "zaukerite shard"
icon_state = "rebar_zaukerite"
@@ -93,10 +123,20 @@
armour_penetration = 20 // not nearly as good, as its not as sharp.
wound_bonus = 10
bare_wound_bonus = 40
embedding = list("embed_chance" =100, "fall_chance" = 0, "jostle_chance" = 5, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.8, "pain_mult" = 6, "jostle_pain_mult" = 2, "rip_time" = 30)
embed_type = /datum/embed_data/rebar_zaukerite
embed_falloff_tile = 0 // very spiky.
shrapnel_type = /obj/item/ammo_casing/rebar/zaukerite
/datum/embed_data/rebar_zaukerite
embed_chance = 100
fall_chance = 0
jostle_chance = 5
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.8
pain_mult = 6
jostle_pain_mult = 2
rip_time = 30
/obj/projectile/bullet/rebar/hydrogen
name = "metallic hydrogen bolt"
icon_state = "rebar_hydrogen"
@@ -108,10 +148,20 @@
projectile_piercing = PASSMOB //felt this might have been a nice compromise for the lower damage for the difficulty of getting it
wound_bonus = -15
bare_wound_bonus = 10
embedding = list("embed_chance" = 50, "fall_chance" = 2, "jostle_chance" = 3, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.6, "pain_mult" = 4, "jostle_pain_mult" = 2, "rip_time" =18)
embed_type = /datum/embed_data/rebar_hydrogen
embed_falloff_tile = -3
shrapnel_type = /obj/item/ammo_casing/rebar/hydrogen
/datum/embed_data/rebar_hydrogen
embed_chance = 50
fall_chance = 2
jostle_chance = 3
ignore_throwspeed_threshold = TRUE
pain_stam_pct = 0.6
pain_mult = 4
jostle_pain_mult = 2
rip_time =18
/obj/projectile/bullet/rebar/healium
name = "healium bolt"
icon_state = "rebar_healium"
@@ -122,7 +172,7 @@
armour_penetration = 100
wound_bonus = -100
bare_wound_bonus = -100
embedding = list(embed_chance = 0)
embed_type = null
embed_falloff_tile = -3
shrapnel_type = /obj/item/ammo_casing/rebar/healium
@@ -139,7 +189,6 @@
return BULLET_ACT_HIT
/obj/projectile/bullet/rebar/supermatter
name = "supermatter bolt"
icon_state = "rebar_supermatter"
@@ -147,6 +196,7 @@
speed = 0.4
dismemberment = 0
damage_type = TOX
embed_type = null
armour_penetration = 100
shrapnel_type = /obj/item/ammo_casing/rebar/supermatter
@@ -164,7 +214,6 @@
return BULLET_ACT_HIT
/obj/projectile/bullet/rebar/supermatter/proc/dust_feedback(atom/target)
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 10, TRUE)
visible_message(span_danger("[target] is hit by [src], turning [target.p_them()] to dust in a brilliant flash of light!"))
@@ -174,7 +223,7 @@
damage = 1 // It's a damn toy.
range = 10
shrapnel_type = null
embedding = null
embed_type = null
name = "paper ball"
desc = "doink!"
damage_type = BRUTE

View File

@@ -22,7 +22,7 @@
stamina = 55
wound_bonus = 20
sharpness = NONE
embedding = null
embed_type = null
/obj/projectile/bullet/shotgun_beanbag/a40mm
name = "rubber slug"
@@ -55,7 +55,7 @@
range = 7
icon_state = "spark"
color = COLOR_YELLOW
embedding = null
embed_type = null
/obj/projectile/bullet/shotgun_frag12
name ="frag12 slug"
@@ -84,7 +84,7 @@
damage = 3
stamina = 11
sharpness = NONE
embedding = null
embed_type = null
speed = 1.2
stamina_falloff_tile = -0.25
ricochets_max = 4
@@ -106,7 +106,7 @@
name = "incapacitating pellet"
damage = 1
stamina = 6
embedding = null
embed_type = null
// Mech Scattershot

View File

@@ -33,7 +33,7 @@
name = "4.6x30mm armor-piercing bullet"
damage = 15
armour_penetration = 40
embedding = null
embed_type = null
/obj/projectile/bullet/incendiary/c46x30mm
name = "4.6x30mm incendiary bullet"

View File

@@ -9,7 +9,7 @@
icon = 'icons/obj/service/hydroponics/harvest.dmi'
icon_state = "banana"
range = 200
embedding = null
embed_type = null
shrapnel_type = null
/obj/projectile/bullet/honker/Initialize(mapload)

View File

@@ -2,7 +2,7 @@
name ="explosive bolt"
icon_state= "bolter"
damage = 50
embedding = null
embed_type = null
shrapnel_type = null
/obj/projectile/bullet/gyro/on_hit(atom/target, blocked = 0, pierce_hit)
@@ -17,7 +17,7 @@
icon_state= "missile"
damage = 50
sharpness = NONE
embedding = null
embed_type = null
shrapnel_type = null
ricochets_max = 0
/// Whether we do extra damage when hitting a mech or silicon

View File

@@ -252,7 +252,7 @@
ricochet_auto_aim_angle = 10
ricochet_auto_aim_range = 3
wound_bonus = -10
embedding = null
embed_type = null
/obj/projectile/bullet/c38/holy/on_hit(atom/target, blocked, pierce_hit)
. = ..()

View File

@@ -351,7 +351,7 @@
check_list += "\t [span_boldwarning("Your [name] is suffering [wound.a_or_from] [LOWER_TEXT(wound.name)]!!!")]"
for(var/obj/item/embedded_thing in embedded_objects)
var/stuck_word = embedded_thing.isEmbedHarmless() ? "stuck" : "embedded"
var/stuck_word = embedded_thing.is_embed_harmless() ? "stuck" : "embedded"
check_list += "\t <a href='?src=[REF(examiner)];embedded_object=[REF(embedded_thing)];embedded_limb=[REF(src)]' class='warning'>There is \a [embedded_thing] [stuck_word] in your [name]!</a>"
/obj/item/bodypart/blob_act()
@@ -1096,15 +1096,15 @@
if(embed in embedded_objects) // go away
return
// We don't need to do anything with projectile embedding, because it will never reach this point
RegisterSignal(embed, COMSIG_ITEM_EMBEDDING_UPDATE, PROC_REF(embedded_object_changed))
embedded_objects += embed
RegisterSignal(embed, COMSIG_ITEM_EMBEDDING_UPDATE, PROC_REF(embedded_object_changed))
refresh_bleed_rate()
/// INTERNAL PROC, DO NOT USE
/// Cleans up any attachment we have to the embedded object, removes it from our list
/obj/item/bodypart/proc/_unembed_object(obj/item/unembed)
UnregisterSignal(unembed, COMSIG_ITEM_EMBEDDING_UPDATE)
embedded_objects -= unembed
UnregisterSignal(unembed, COMSIG_ITEM_EMBEDDING_UPDATE)
refresh_bleed_rate()
/obj/item/bodypart/proc/embedded_object_changed(obj/item/embedded_source)
@@ -1157,7 +1157,7 @@
cached_bleed_rate += 0.5
for(var/obj/item/embeddies in embedded_objects)
if(!embeddies.isEmbedHarmless())
if(!embeddies.is_embed_harmless())
cached_bleed_rate += 0.25
for(var/datum/wound/iter_wound as anything in wounds)

View File

@@ -139,7 +139,7 @@
/mob/living/carbon/proc/has_embedded_objects(include_harmless=FALSE)
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
for(var/obj/item/embedded in bodypart.embedded_objects)
if(!include_harmless && embedded.isEmbedHarmless())
if(!include_harmless && embedded.is_embed_harmless())
continue
return TRUE

View File

@@ -33,10 +33,15 @@
attack_verb_continuous = list("stubs", "pokes")
attack_verb_simple = list("stub", "poke")
sharpness = SHARP_EDGED
embedding = list("pain_mult" = 1, "embed_chance" = 30, "fall_chance" = 70)
embed_type = /datum/embed_data/janicart_key
wound_bonus = -1
bare_wound_bonus = 2
/datum/embed_data/janicart_key
pain_mult = 1
embed_chance = 30
fall_chance = 70
/obj/item/key/janitor/suicide_act(mob/living/carbon/user)
switch(user.mind?.get_skill_level(/datum/skill/cleaning))
if(SKILL_LEVEL_NONE to SKILL_LEVEL_NOVICE) //Their mind is too weak to ascend as a janny

View File

@@ -1074,11 +1074,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
var/mob/living/carbon/carbon_target = atom_target
for(var/i in 1 to num_shards)
var/obj/item/shard/shard = new /obj/item/shard(get_turf(carbon_target))
shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult = 1, pain_chance = 5)
shard.updateEmbedding()
shard.set_embed(/datum/embed_data/glass_candy)
carbon_target.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
shard.embedding = list()
shard.updateEmbedding()
shard.set_embed(initial(shard.embed_type))
return TRUE
if (VENDOR_CRUSH_CRIT_PIN) // pin them beneath the machine until someone untilts it
if (!isliving(atom_target))

View File

@@ -776,6 +776,7 @@
#include "code\datums\dog_fashion.dm"
#include "code\datums\ductnet.dm"
#include "code\datums\eigenstate.dm"
#include "code\datums\embed_data.dm"
#include "code\datums\emotes.dm"
#include "code\datums\ert.dm"
#include "code\datums\hailer_phrase.dm"