diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm index c5e87e13978..c1150df6de7 100644 --- a/code/datums/elements/embed.dm +++ b/code/datums/elements/embed.dm @@ -70,35 +70,26 @@ /datum/element/embed/proc/checkEmbed(obj/item/weapon, mob/living/carbon/victim, hit_zone, blocked, datum/thrownthing/throwingdatum, forced=FALSE) SIGNAL_HANDLER - if((!forced && blocked) || !istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) - return + if(forced) + embed_object(weapon, victim, hit_zone, throwingdatum) + return TRUE + + if(blocked || !istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) + return FALSE var/flying_speed = throwingdatum?.speed || weapon.throw_speed - if(!forced && (flying_speed < EMBED_THROWSPEED_THRESHOLD && !ignore_throwspeed_threshold)) // check if it's a forced embed, and if not, if it's going fast enough to proc embedding - return + if(flying_speed < EMBED_THROWSPEED_THRESHOLD && !ignore_throwspeed_threshold) + return FALSE - var/actual_chance = embed_chance - var/penetrative_behaviour = 1 //Keep this above 1, as it is a multiplier for the pen_mod for determining actual embed chance. - if(weapon.weak_against_armour) - penetrative_behaviour = ARMOR_WEAKENED_MULTIPLIER + if(!roll_embed_chance(weapon, victim, hit_zone, throwingdatum)) + return FALSE - if(throwingdatum?.speed > weapon.throw_speed) - actual_chance += (throwingdatum.speed - weapon.throw_speed) * EMBED_CHANCE_SPEED_BONUS - - if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign - 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 - - if(armor) // we only care about armor penetration if there's actually armor to penetrate - var/pen_mod = -(armor * penetrative_behaviour) // if our shrapnel is weak into armor, then we restore our armor to the full value. - actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on - if(actual_chance <= 0) - victim.visible_message(span_danger("[weapon] bounces off [victim]'s armor, unable to embed!"), span_notice("[weapon] bounces off your armor, unable to embed!"), vision_distance = COMBAT_MESSAGE_RANGE) - return - - if(!prob(actual_chance)) - return + embed_object(weapon, victim, hit_zone, throwingdatum) + return TRUE +/// Actually sticks the object to a victim +/datum/element/embed/proc/embed_object(obj/item/weapon, mob/living/carbon/victim, hit_zone, datum/thrownthing/throwingdatum) var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || pick(victim.bodyparts) victim.AddComponent(/datum/component/embedded,\ weapon,\ @@ -115,8 +106,6 @@ jostle_pain_mult = jostle_pain_mult,\ pain_stam_pct = pain_stam_pct) - return TRUE - ///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) SIGNAL_HANDLER @@ -148,7 +137,7 @@ /datum/element/embed/proc/checkEmbedProjectile(obj/projectile/P, atom/movable/firer, atom/hit, angle, hit_zone) SIGNAL_HANDLER - if(!iscarbon(hit)) + if(!iscarbon(hit) || HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE)) Detach(P) return // we don't care @@ -172,28 +161,52 @@ * AGAIN if we actually embed. This way, we save on at least one message. * * Arguments: - * * I- the item we're trying to insert into the target + * * embedding_item- the item we're trying to insert into the target * * target- what we're trying to shish-kabob, either a bodypart or a carbon * * 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/I, atom/target, hit_zone, forced=FALSE) +/datum/element/embed/proc/tryForceEmbed(obj/item/embedding_item, atom/target, hit_zone, forced=FALSE) SIGNAL_HANDLER var/obj/item/bodypart/limb - var/mob/living/carbon/C - - if(!forced && !prob(embed_chance)) - return + var/mob/living/carbon/victim if(iscarbon(target)) - C = target + victim = target if(!hit_zone) - limb = pick(C.bodyparts) + limb = pick(victim.bodyparts) hit_zone = limb.body_zone else if(isbodypart(target)) limb = target hit_zone = limb.body_zone - C = limb.owner + victim = limb.owner - return checkEmbed(I, C, hit_zone, forced=TRUE) + 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 + +/// 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 + + 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 + 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 + if(!armor) // we only care about armor penetration if there's actually armor to penetrate + return prob(actual_chance) + + //Keep this above 1, as it is a multiplier for the pen_mod for determining actual embed chance. + var/penetrative_behaviour = embedding_item.weak_against_armour ? ARMOR_WEAKENED_MULTIPLIER : 1 + var/pen_mod = -(armor * penetrative_behaviour) // if our shrapnel is weak into armor, then we restore our armor to the full value. + actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on + if(actual_chance <= 0) + victim.visible_message(span_danger("[embedding_item] bounces off [victim]'s armor, unable to embed!"), span_notice("[embedding_item] bounces off your armor, unable to embed!"), vision_distance = COMBAT_MESSAGE_RANGE) + return FALSE + + return prob(actual_chance) diff --git a/code/game/objects/effects/poster.dm b/code/game/objects/effects/poster.dm index 59ac56732a2..318c2e1c1ac 100644 --- a/code/game/objects/effects/poster.dm +++ b/code/game/objects/effects/poster.dm @@ -57,7 +57,7 @@ return ..() if (poster_structure.trap?.resolve()) - to_chat(user, span_warning("This poster is already booby-trapped!")) + balloon_alert(user, "already trapped!") return if(!user.transferItemToLoc(I, poster_structure)) @@ -215,16 +215,16 @@ return to_chat(user, span_warning("There's something sharp behind this! What the hell?")) - if(!can_embed_trap(user) || !payload.tryEmbed(user.get_active_hand(), TRUE)) + if(!can_embed_trap(user) || !payload.tryEmbed(user.get_active_hand(), forced = TRUE)) visible_message(span_notice("A [payload.name] falls from behind the poster.") ) payload.forceMove(user.drop_location()) else SEND_SIGNAL(src, COMSIG_POSTER_TRAP_SUCCEED, user) /obj/structure/sign/poster/proc/can_embed_trap(mob/living/carbon/human/user) - if (!istype(user)) + if (!istype(user) || HAS_TRAIT(user, TRAIT_PIERCEIMMUNE)) return FALSE - return (!user.gloves && !HAS_TRAIT(user, TRAIT_PIERCEIMMUNE)) + return !user.gloves || !(user.gloves.body_parts_covered & HANDS) || HAS_TRAIT(user, TRAIT_FINGERPRINT_PASSTHROUGH) || HAS_TRAIT(user.gloves, TRAIT_FINGERPRINT_PASSTHROUGH) /obj/structure/sign/poster/proc/roll_and_drop(atom/location) pixel_x = 0 @@ -249,15 +249,14 @@ var/stuff_on_wall = 0 for(var/obj/contained_object in contents) //Let's see if it already has a poster on it or too much stuff if(istype(contained_object, /obj/structure/sign/poster)) - to_chat(user, span_warning("The wall is far too cluttered to place a poster!")) + balloon_alert(user, "no room!") return stuff_on_wall++ if(stuff_on_wall == 3) - to_chat(user, span_warning("The wall is far too cluttered to place a poster!")) + balloon_alert(user, "no room!") return - to_chat(user, span_notice("You start placing the poster on the wall...") ) - + balloon_alert(user, "hanging poster...") var/obj/structure/sign/poster/placed_poster = rolled_poster.poster_structure flick("poster_being_set", placed_poster) @@ -266,7 +265,6 @@ var/turf/user_drop_location = get_turf(user) //cache this so it just falls to the ground if they move. also no tk memes allowed. if(!do_after(user, PLACE_SPEED, placed_poster, extra_checks = CALLBACK(placed_poster, TYPE_PROC_REF(/obj/structure/sign/poster, snowflake_wall_turf_check), src))) - to_chat(user, span_notice("The poster falls down!")) placed_poster.roll_and_drop(user_drop_location) return diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c98abdb30b7..eb7cf12cb67 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1169,13 +1169,13 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e * * target- Either a body part or a carbon. What are we hitting? * * forced- Do we want this to go through 100%? */ -/obj/item/proc/tryEmbed(atom/target, forced=FALSE, silent=FALSE) +/obj/item/proc/tryEmbed(atom/target, forced=FALSE) if(!isbodypart(target) && !iscarbon(target)) return NONE if(!forced && !LAZYLEN(embedding)) return NONE - if(SEND_SIGNAL(src, COMSIG_EMBED_TRY_FORCE, target, forced, silent)) + if(SEND_SIGNAL(src, COMSIG_EMBED_TRY_FORCE, target = target, forced = forced)) return COMPONENT_EMBED_SUCCESS failedEmbed() @@ -1235,7 +1235,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e victim.apply_damage(max(15, force), BRUTE, BODY_ZONE_HEAD, wound_bonus = 10, sharpness = TRUE) victim.losebreath += 2 - if(tryEmbed(victim.get_bodypart(BODY_ZONE_CHEST), TRUE, TRUE)) //and if it embeds successfully in their chest, cause a lot of pain + if(tryEmbed(victim.get_bodypart(BODY_ZONE_CHEST), forced = TRUE)) //and if it embeds successfully in their chest, cause a lot of pain victim.apply_damage(max(25, force*1.5), BRUTE, BODY_ZONE_CHEST, wound_bonus = 7, sharpness = TRUE) victim.losebreath += 6 discover_after = FALSE