[MIRROR] Bibles, Lighters, and Cowboy Hats can block bullets [MDB IGNORE] (#22282)

* Bibles, Lighters, and Cowboy Hats can block bullets (#76521)

## About The Pull Request

If you are wearing a bible or lighter in your suit slot, or a cowboy hat
on your head, there is a small (2%) chance that bullets (only bullets,
not lasers) will hit them instead of you.
This destroys lighters, removes the storage capacity of the bible, or
sends the hat flying off your head.

The Bounty Hunter's cowboy hat has a significantly higher chance to
intercept bullets.

## Why It's Good For The Game

Adds some fun flavour to these items.

## Changelog

🆑
add: A bible or lighter in your suit slot, or cowboy hat on your head
will occasionally intercept a bullet.
/🆑

* Bibles, Lighters, and Cowboy Hats can block bullets

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
Co-authored-by: Bloop <vinylspiders@gmail.com>
This commit is contained in:
SkyratBot
2023-07-11 23:59:06 -04:00
committed by GitHub
co-authored by Jacquerel Bloop
parent 08ddf2a09e
commit 4a779bb4b6
9 changed files with 137 additions and 2 deletions
@@ -352,6 +352,7 @@
#define COMSIG_PROJECTILE_FIRE "projectile_fire"
///sent to targets during the process_hit proc of projectiles
#define COMSIG_PROJECTILE_PREHIT "com_proj_prehit"
#define PROJECTILE_INTERRUPT_HIT (1<<0)
///from the base of /obj/projectile/Range(): ()
#define COMSIG_PROJECTILE_RANGE "projectile_range"
///from the base of /obj/projectile/on_range(): ()
@@ -0,0 +1,67 @@
/**
* Component which allows an equipped item to occasionally absorb a projectile.
*/
/datum/component/bullet_intercepting
/// Chance to intercept a projectile
var/block_chance
/// Type of bullet to intercept
var/block_type
/// Slots in which effect can be active
var/active_slots
/// Person currently wearing us
var/mob/wearer
/// Callback called when we catch a projectile
var/datum/callback/on_intercepted
/datum/component/bullet_intercepting/Initialize(block_chance = 2, block_type = BULLET, active_slots, datum/callback/on_intercepted)
. = ..()
if (!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.block_chance = block_chance
src.block_type = block_type
src.active_slots = active_slots
src.on_intercepted = on_intercepted
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_parent_equipped))
RegisterSignal(parent, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_unequipped))
/datum/component/bullet_intercepting/Destroy(force, silent)
QDEL_NULL(on_intercepted)
return ..()
/// Called when item changes slots, check if we're in a valid location to take bullets
/datum/component/bullet_intercepting/proc/on_parent_equipped(obj/item/clothing/source, mob/equipper, slot)
SIGNAL_HANDLER
if (wearer)
if (!(active_slots & slot))
on_unequipped()
return
if (!(active_slots & slot))
return
RegisterSignal(equipper, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_wearer_shot))
RegisterSignal(equipper, COMSIG_QDELETING, PROC_REF(on_wearer_deleted))
wearer = equipper
/// Called when item is unequipped, stop tracking bullets
/datum/component/bullet_intercepting/proc/on_unequipped()
SIGNAL_HANDLER
if (!wearer)
return
UnregisterSignal(wearer, list(COMSIG_PROJECTILE_PREHIT, COMSIG_QDELETING))
wearer = null
/// Called when wearer is shot, check if we're going to block the hit
/datum/component/bullet_intercepting/proc/on_wearer_shot(mob/living/victim, list/signal_args, obj/projectile/bullet)
SIGNAL_HANDLER
if (victim != wearer || victim.stat == DEAD || bullet.armor_flag != block_type )
return
if (!prob(block_chance))
return
on_intercepted?.Invoke(victim, bullet)
return PROJECTILE_INTERRUPT_HIT
/// Called when wearer is deleted, stop tracking them
/datum/component/bullet_intercepting/proc/on_wearer_deleted()
SIGNAL_HANDLER
wearer = null
+15
View File
@@ -740,8 +740,23 @@ CIGARETTE PACKETS ARE IN FANCY.DM
. = ..()
if(!overlay_state)
overlay_state = pick(overlay_list)
AddComponent(\
/datum/component/bullet_intercepting,\
block_chance = 0.5,\
active_slots = ITEM_SLOT_SUITSTORE,\
on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\
)
update_appearance()
/// Destroy the lighter when it's shot by a bullet
/obj/item/lighter/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet)
victim.visible_message(span_warning("\The [bullet] shatters on [victim]'s lighter!"))
playsound(victim, get_sfx(SFX_RICOCHET), 100, TRUE)
new /obj/effect/decal/cleanable/oil(get_turf(src))
do_sparks(1, TRUE, src)
victim.dropItemToGround(src, force = TRUE, silent = TRUE)
qdel(src)
/obj/item/lighter/cyborg_unequip(mob/user)
if(!lit)
return
@@ -100,7 +100,7 @@
name = "Bounty Hunter - Armored"
uniform = /obj/item/clothing/under/rank/prisoner
back = /obj/item/storage/backpack
head = /obj/item/clothing/head/cowboy
head = /obj/item/clothing/head/cowboy/bounty
suit = /obj/item/clothing/suit/space/hunter
belt = /obj/item/gun/ballistic/automatic/pistol/fire_mag
gloves = /obj/item/clothing/gloves/tackler/combat
+22
View File
@@ -99,6 +99,24 @@
inhand_icon_state = null
armor_type = /datum/armor/head_cowboy
resistance_flags = FIRE_PROOF | ACID_PROOF
/// Chance that the hat will catch a bullet for you
var/deflect_chance = 2
/obj/item/clothing/head/cowboy/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/bullet_intercepting,\
block_chance = deflect_chance,\
active_slots = ITEM_SLOT_HEAD,\
on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\
)
/// When we catch a bullet, fling away
/obj/item/clothing/head/cowboy/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet)
victim.visible_message(span_warning("\The [bullet] sends [victim]'s hat flying!"))
victim.dropItemToGround(src, force = TRUE, silent = TRUE)
throw_at(get_edge_target_turf(loc, pick(GLOB.alldirs)), range = 3, speed = 3)
playsound(victim, get_sfx(SFX_RICOCHET), 100, TRUE)
/datum/armor/head_cowboy
melee = 5
@@ -106,6 +124,10 @@
laser = 5
energy = 15
/// Bounty hunter's hat, very likely to intercept bullets
/obj/item/clothing/head/cowboy/bounty
deflect_chance = 50
/obj/item/clothing/head/cowboy/black
name = "desperado hat"
desc = "People with ropes around their necks don't always hang."
+27
View File
@@ -77,12 +77,39 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
unique = TRUE
/// Deity this bible is related to
var/deity_name = "Space Jesus"
/// Component which catches bullets for us
var/datum/component/bullet_catcher
/obj/item/book/bible/Initialize(mapload)
. = ..()
AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE_HOLY)
bullet_catcher = AddComponent(\
/datum/component/bullet_intercepting,\
active_slots = ITEM_SLOT_SUITSTORE,\
on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\
)
carve_out()
/obj/item/book/bible/Destroy(force)
QDEL_NULL(bullet_catcher)
return ..()
/// Destroy the bible when it's shot by a bullet
/obj/item/book/bible/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet)
victim.add_mood_event("blessing", /datum/mood_event/blessing)
playsound(victim, 'sound/magic/magic_block_holy.ogg', 50, TRUE)
victim.visible_message(span_warning("\The [src] takes \the [bullet] in [victim]'s place!"))
var/obj/structure/fluff/paper/stack/pages = new(get_turf(src))
pages.dir = pick(GLOB.alldirs)
name = "punctured bible"
desc = "A memento of good luck, or perhaps divine intervention?"
icon_state = "shot"
if (!GLOB.bible_icon_state)
GLOB.bible_icon_state = "shot" // New symbol of your religion if you hadn't picked one
atom_storage?.remove_all(get_turf(src))
QDEL_NULL(atom_storage)
QDEL_NULL(bullet_catcher)
/obj/item/book/bible/examine(mob/user)
. = ..()
if(deity_name)
+3 -1
View File
@@ -503,7 +503,9 @@
return process_hit(T, select_target(T, target, bumped), bumped, hit_something) // try to hit something else
// at this point we are going to hit the thing
// in which case send signal to it
SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args)
if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT)
qdel(src)
return BULLET_ACT_BLOCK
if(mode == PROJECTILE_PIERCE_HIT)
++pierces
hit_something = TRUE
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

+1
View File
@@ -961,6 +961,7 @@
#include "code\datums\components\blood_walk.dm"
#include "code\datums\components\bloodysoles.dm"
#include "code\datums\components\boomerang.dm"
#include "code\datums\components\bullet_intercepting.dm"
#include "code\datums\components\bumpattack.dm"
#include "code\datums\components\burning.dm"
#include "code\datums\components\butchering.dm"