Throwing a bee at someone injects reagents (#80354)

## About The Pull Request

Throwing a bee at someone injects that bee's reagents.
This has a larger code footprint than you might expect because venom
injection is done via an element which in turn gives a callback to a
component.
While I was touching that I also separated `COMSIG_MOVABLE_IMPACT` into
`COMSIG_MOVABLE_PRE_IMPACT` because a lot of effects trigger from
`COMSIG_MOVABLE_IMPACT` despite the fact that the throw impact can be
cancelled after the signal is sent.

I also added an inject check onto the venomous element for mob attacks,
so thick clothing can now protect you from venom injection.
I elected that Giant Spiders have big enough fangs to ignore this such
that this isn't a major balance change, as do moonicorns (that horn is
massive), Fire Sharks, and Clowns (no idea how they are applying chems
at all to be honest).

## Why It's Good For The Game

I thought about someone throwing a bee at someone like a little dart and
thought "hee hee"

## Changelog

🆑
add: If you throw a bee at someone it will hit them sting-first and
inject that bee's reagent
balance: Thick clothing can now protect you from the venom of bees,
snakes, frogs, and (small) spiders
/🆑

---------

Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
This commit is contained in:
Jacquerel
2023-12-17 01:10:06 +01:00
committed by GitHub
co-authored by John Willard
parent 0f54943cc8
commit 29590d467c
14 changed files with 57 additions and 17 deletions
@@ -23,9 +23,11 @@
#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input"
#define DRIFT_ALLOW_INPUT (1<<0)
///from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum)
#define COMSIG_MOVABLE_IMPACT "movable_impact"
#define COMSIG_MOVABLE_PRE_IMPACT "movable_pre_impact"
#define COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH (1<<0) //if true, flip if the impact will push what it hits
#define COMPONENT_MOVABLE_IMPACT_NEVERMIND (1<<1) //return true if you destroyed whatever it was you're impacting and there won't be anything for hitby() to run on
///from base of atom/movable/throw_impact() after confirming a hit: (/atom/hit_atom, /datum/thrownthing/throwingdatum)
#define COMSIG_MOVABLE_IMPACT "movable_impact"
///from base of mob/living/hitby(): (mob/living/target, hit_zone, blocked, datum/thrownthing/throwingdatum)
#define COMSIG_MOVABLE_IMPACT_ZONE "item_impact_zone"
///from /atom/movable/proc/buckle_mob(): (mob/living/M, force, check_loc, buckle_mob_flags)
+14 -1
View File
@@ -11,12 +11,15 @@
var/datum/callback/on_hit_callback
///callback optionally used for more checks
var/datum/callback/extra_check_callback
///optionally should we also apply the effect if thrown at something?
var/thrown_effect
/datum/component/on_hit_effect/Initialize(on_hit_callback, extra_check_callback)
/datum/component/on_hit_effect/Initialize(on_hit_callback, extra_check_callback, thrown_effect = FALSE)
src.on_hit_callback = on_hit_callback
src.extra_check_callback = extra_check_callback
if(!(ismachinery(parent) || isstructure(parent) || isgun(parent) || isprojectilespell(parent) || isitem(parent) || isanimal_or_basicmob(parent) || isprojectile(parent)))
return ELEMENT_INCOMPATIBLE
src.thrown_effect = thrown_effect
/datum/component/on_hit_effect/Destroy(force, silent)
on_hit_callback = null
@@ -33,12 +36,16 @@
else if(isprojectile(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_projectile_self_hit))
if(thrown_effect)
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(on_thrown_hit))
/datum/component/on_hit_effect/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_PROJECTILE_ON_HIT,
COMSIG_ITEM_AFTERATTACK,
COMSIG_HOSTILE_POST_ATTACKINGTARGET,
COMSIG_PROJECTILE_SELF_ON_HIT,
COMSIG_MOVABLE_IMPACT,
))
/datum/component/on_hit_effect/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
@@ -79,3 +86,9 @@
if(!extra_check_callback.Invoke(firer, target))
return
on_hit_callback.Invoke(source, firer, target, body_zone)
/datum/component/on_hit_effect/proc/on_thrown_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
if(extra_check_callback && !extra_check_callback.Invoke(source, hit_atom))
return
on_hit_callback.Invoke(source, source, hit_atom, null)
+2 -2
View File
@@ -54,11 +54,11 @@
/datum/component/tackler/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, PROC_REF(checkTackle))
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(sack))
RegisterSignal(parent, COMSIG_MOVABLE_PRE_IMPACT, PROC_REF(sack))
RegisterSignal(parent, COMSIG_MOVABLE_POST_THROW, PROC_REF(registerTackle))
/datum/component/tackler/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOB_CLICKON, COMSIG_MOVABLE_IMPACT, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_POST_THROW))
UnregisterSignal(parent, list(COMSIG_MOB_CLICKON, COMSIG_MOVABLE_PRE_IMPACT, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_POST_THROW))
///Store the thrownthing datum for later use
/datum/component/tackler/proc/registerTackle(mob/living/carbon/user, datum/thrownthing/tackle)
+11 -2
View File
@@ -8,14 +8,21 @@
argument_hash_start_idx = 2
///Path of the reagent added
var/poison_type
///Details of how we inject our venom
var/injection_flags
///How much of the reagent added. if it's a list, it'll pick a range with the range being list(lower_value, upper_value)
var/list/amount_added
/datum/element/venomous/Attach(datum/target, poison_type, amount_added)
/datum/element/venomous/Attach(datum/target, poison_type, amount_added, injection_flags = NONE, thrown_effect = FALSE)
. = ..()
src.poison_type = poison_type
src.amount_added = amount_added
target.AddComponent(/datum/component/on_hit_effect, CALLBACK(src, PROC_REF(do_venom)))
src.injection_flags = injection_flags
target.AddComponent(\
/datum/component/on_hit_effect,\
on_hit_callback = CALLBACK(src, PROC_REF(do_venom)),\
thrown_effect = thrown_effect,\
)
/datum/element/venomous/Detach(datum/target)
qdel(target.GetComponent(/datum/component/on_hit_effect))
@@ -26,6 +33,8 @@
return
if(target.stat == DEAD)
return
if(isliving(element_owner) && !target.try_inject(element_owner, hit_zone, injection_flags))
return
var/final_amount_added
if(islist(amount_added))
final_amount_added = rand(amount_added[1], amount_added[2])
+2 -1
View File
@@ -1242,7 +1242,7 @@
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = FALSE
var/hitpush = TRUE
var/impact_signal = SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
var/impact_signal = SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum)
if(impact_signal & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
hitpush = FALSE // hacky, tie this to something else or a proper workaround later
@@ -1250,6 +1250,7 @@
return // in case a signal interceptor broke or deleted the thing before we could process our hit
if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
return
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
return hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
/atom/movable/hitby(atom/movable/hitting_atom, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
+3 -1
View File
@@ -772,10 +772,12 @@
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(QDELETED(hit_atom))
return
if(SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum) & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
return
if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
return
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(get_temperature() && isliving(hit_atom))
var/mob/living/L = hit_atom
L.ignite_mob()
+12 -2
View File
@@ -112,7 +112,12 @@
var/static/list/injection_range
if(!injection_range)
injection_range = string_numbers_list(list(1, 5))
AddElement(/datum/element/venomous, /datum/reagent/consumable/laughter, injection_range)
AddElement(\
/datum/element/venomous,\
/datum/reagent/consumable/laughter,\
injection_range,\
injection_flags = INJECT_CHECK_PENETRATE_THICK | INJECT_CHECK_IGNORE_SPECIES,\
)
/mob/living/basic/clown/fleshclown
name = "Fleshclown"
@@ -288,7 +293,12 @@
var/static/list/injection_range
if(!injection_range)
injection_range = string_numbers_list(list(1, 5))
AddElement(/datum/element/venomous, /datum/reagent/peaceborg/confuse, injection_range)
AddElement(\
/datum/element/venomous,\
/datum/reagent/peaceborg/confuse,\
injection_range,\
injection_flags = INJECT_CHECK_PENETRATE_THICK | INJECT_CHECK_IGNORE_SPECIES,\
) // I don't really know what a clown is using to inject people but let's assume it doesn't need to penetrate at all
/mob/living/basic/clown/clownhulk/destroyer
name = "The Destroyer"
@@ -192,11 +192,11 @@
if(!injection_range)
injection_range = string_numbers_list(list(1, 5))
if(beegent) //clear the old since this one is going to have some new value
RemoveElement(/datum/element/venomous, beegent.type, injection_range)
RemoveElement(/datum/element/venomous, beegent.type, injection_range, thrown_effect = TRUE)
beegent = toxin
name = "[initial(name)] ([toxin.name])"
real_name = name
AddElement(/datum/element/venomous, beegent.type, injection_range)
AddElement(/datum/element/venomous, beegent.type, injection_range, thrown_effect = TRUE)
generate_bee_visuals()
/mob/living/basic/bee/queen
@@ -24,7 +24,7 @@
/mob/living/basic/cow/moonicorn/Initialize(mapload)
. = ..()
AddElement(/datum/element/venomous, /datum/reagent/pax, 5)
AddElement(/datum/element/venomous, /datum/reagent/pax, 5, injection_flags = INJECT_CHECK_PENETRATE_THICK | INJECT_CHECK_IGNORE_SPECIES)
AddElement(/datum/element/movement_turf_changer, /turf/open/floor/grass/fairy)
/mob/living/basic/cow/moonicorn/udder_component()
@@ -26,7 +26,7 @@
. = ..()
AddElement(/datum/element/death_gases, /datum/gas/plasma, 40)
AddElement(/datum/element/simple_flying)
AddElement(/datum/element/venomous, /datum/reagent/phlogiston, 2)
AddElement(/datum/element/venomous, /datum/reagent/phlogiston, 2, injection_flags = INJECT_CHECK_PENETRATE_THICK)
AddComponent(/datum/component/swarming)
AddComponent(/datum/component/regenerator, outline_colour = COLOR_DARK_RED)
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
@@ -47,7 +47,7 @@
ADD_TRAIT(src, TRAIT_WEB_SURFER, INNATE_TRAIT)
AddElement(/datum/element/cliff_walking)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
AddElement(/datum/element/venomous, /datum/reagent/toxin/hunterspider, 5)
AddElement(/datum/element/venomous, /datum/reagent/toxin/hunterspider, 5, injection_flags = INJECT_CHECK_PENETRATE_THICK)
AddElement(/datum/element/web_walker, /datum/movespeed_modifier/fast_web)
AddElement(/datum/element/nerfed_pulling, GLOB.typecache_general_bad_things_to_easily_move)
AddElement(/datum/element/prevent_attacking_of_types, GLOB.typecache_general_bad_hostile_attack_targets, "this tastes awful!")
@@ -19,6 +19,7 @@
melee_damage_upper = 25
gold_core_spawnable = HOSTILE_SPAWN
ai_controller = /datum/ai_controller/basic_controller/giant_spider
bite_injection_flags = INJECT_CHECK_PENETRATE_THICK
/// Actions to grant on Initialize
var/list/innate_actions = null
@@ -36,6 +36,8 @@
var/poison_type = /datum/reagent/toxin/hunterspider
/// How much of a reagent the mob injects on attack
var/poison_per_bite = 0
/// How tough is our bite?
var/bite_injection_flags = NONE
/// Multiplier to apply to web laying speed. Fractional numbers make it faster, because it's a multiplier.
var/web_speed = 1
/// Type of webbing ability to learn.
@@ -57,7 +59,7 @@
AddComponent(/datum/component/health_scaling_effects, min_health_slowdown = 1.5)
if(poison_per_bite)
AddElement(/datum/element/venomous, poison_type, poison_per_bite)
AddElement(/datum/element/venomous, poison_type, poison_per_bite, injection_flags = bite_injection_flags)
var/datum/action/cooldown/mob_cooldown/lay_web/webbing = new web_type(src)
webbing.webbing_time *= web_speed
+1 -1
View File
@@ -218,7 +218,7 @@
/obj/item/mod/module/weapon_recall/proc/set_weapon(obj/item/weapon)
linked_weapon = weapon
RegisterSignal(linked_weapon, COMSIG_MOVABLE_IMPACT, PROC_REF(catch_weapon))
RegisterSignal(linked_weapon, COMSIG_MOVABLE_PRE_IMPACT, PROC_REF(catch_weapon))
RegisterSignal(linked_weapon, COMSIG_QDELETING, PROC_REF(deleted_weapon))
/obj/item/mod/module/weapon_recall/proc/recall_weapon(caught = FALSE)