diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 4998f10849e..c1d9e88786a 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -12,6 +12,8 @@ /// from /obj/item/toy/crayon/spraycan/afterattack: (user, spraycan, color_is_dark) #define COMSIG_OBJ_PAINTED "obj_painted" #define DONT_USE_SPRAYCAN_CHARGES (1<<0) +/// from /obj/obj_reskin: (mob/user, skin) +#define COMSIG_OBJ_RESKIN "obj_reskin" // /obj/machinery signals @@ -361,9 +363,9 @@ // /obj/projectile signals (sent to the firer) -///from base of /obj/projectile/proc/on_hit(), like COMSIG_PROJECTILE_ON_HIT but on the projectile itself and with the hit limb (if any): (atom/movable/firer, atom/target, angle, hit_limb) +///from base of /obj/projectile/proc/on_hit(), like COMSIG_PROJECTILE_ON_HIT but on the projectile itself and with the hit limb (if any): (atom/movable/firer, atom/target, angle, hit_limb, blocked) #define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_on_hit" -///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, angle, hit_limb) +///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, angle, hit_limb, blocked) #define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" ///from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target) #define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" @@ -387,6 +389,9 @@ ///sent to targets during the process_hit proc of projectiles #define COMSIG_FIRE_CASING "fire_casing" +///from the base of /obj/item/ammo_casing/ready_proj() : (atom/target, mob/living/user, quiet, zone_override, atom/fired_from) +#define COMSIG_CASING_READY_PROJECTILE "casing_ready_projectile" + ///sent to the projectile after an item is spawned by the projectile_drop element: (new_item) #define COMSIG_PROJECTILE_ON_SPAWN_DROP "projectile_on_spawn_drop" ///sent to the projectile when spawning the item (shrapnel) that may be embedded: (new_item) @@ -448,6 +453,12 @@ #define COMSIG_ITEM_AFTERATTACK_SECONDARY "item_afterattack_secondary" ///from base of obj/item/attack_qdeleted(): (atom/target, mob/user, params) #define COMSIG_ITEM_ATTACK_QDELETED "item_attack_qdeleted" +///from base of obj/item/embedded(): (atom/target, obj/item/bodypart/part) +#define COMSIG_ITEM_EMBEDDED "item_embedded" +///from base of datum/component/embedded/safeRemove(): (mob/living/carbon/victim) +#define COMSIG_ITEM_UNEMBEDDED "item_unembedded" +/// from base of obj/item/failedEmbed() +#define COMSIG_ITEM_FAILED_EMBED "item_failed_embed" ///from /obj/item/assembly/proc/pulsed(mob/pulser) #define COMSIG_ASSEMBLY_PULSED "assembly_pulsed" @@ -481,3 +492,26 @@ /// from /obj/structure/cursed_slot_machine/determine_victor() when someone finally wins. #define COMSIG_GLOB_CURSED_SLOT_MACHINE_WON "cursed_slot_machine_won" + +/// from /datum/component/dart_insert/add_to_dart() : (obj/item/ammo_casing, mob/user) +#define COMSIG_DART_INSERT_ADDED "dart_insert_added" + +/// from /datum/component/dart_insert/remove_from_dart() : (obj/ammo_casing/dart, mob/user) +#define COMSIG_DART_INSERT_REMOVED "dart_insert_removed" + +/** + * from /datum/component/dart_insert/get_dart_var_modifiers() : (list/out_modifiers) + * + * valid indices for `out_modifiers` are: + * - `damage`: number + * - `speed`: number + * - `armour_penetration`: number + * - `wound_bonus`: number + * - `bare_wound_bonus`: number + * - `demolition_mod`: number + * - `embedding`: list with embedding params + */ +#define COMSIG_DART_INSERT_GET_VAR_MODIFIERS "dart_insert_get_var_modifiers" + +/// from /datum/component/dart_insert/on_reskin() +#define COMSIG_DART_INSERT_PARENT_RESKINNED "dart_insert_parent_reskinned" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 536b1023a5a..d9db85b32da 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -976,4 +976,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait given to mobs that we do not want to mindswap #define TRAIT_NO_MINDSWAP "no_mindswap" +/// Trait given to foam darts that have an insert in them +#define TRAIT_DART_HAS_INSERT "dart_has_insert" // END TRAIT DEFINES diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 116fb34fad5..c28357eb478 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -217,3 +217,8 @@ return max(new_value, threshold) if(sign == -1) return min(new_value, threshold * -1) + +/// Takes two values x and y, and returns 1/((1/x) + y) +/// Useful for providing an additive modifier to a value that is used as a divisor, such as `/obj/projectile/var/speed` +/proc/reciprocal_add(x, y) + return 1/((1/x)+y) diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index b1d1c5eaac3..6af47e3ab10 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -472,6 +472,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_UNCATCHABLE" = TRAIT_UNCATCHABLE, "TRAIT_WIELDED" = TRAIT_WIELDED, ), + /obj/item/ammo_casing = list( + "TRAIT_DART_HAS_INSERT" = TRAIT_DART_HAS_INSERT, + ), /obj/item/bodypart = list( "TRAIT_DISABLED_BY_WOUND" = TRAIT_DISABLED_BY_WOUND, "TRAIT_IGNORED_BY_LIVING_FLESH" = TRAIT_IGNORED_BY_LIVING_FLESH, diff --git a/code/datums/components/dart_insert.dm b/code/datums/components/dart_insert.dm new file mode 100644 index 00000000000..03d1d689357 --- /dev/null +++ b/code/datums/components/dart_insert.dm @@ -0,0 +1,161 @@ +/** + * Component for allowing items to be inserted into foam darts. + * The parent can register signal handlers for `COMSIG_DART_INSERT_ADDED`, + * `COMSIG_DART_INSERT_REMOVED` to define custom behavior for when the item + * is added to/removed from a dart, and `COMSIG_DART_INSERT_GET_VAR_MODIFIERS` + * to define the modifications the item makes to the vars of the fired projectile. + */ +/datum/component/dart_insert + /// List for tracking the modifications this component has made to the vars of the containing projectile + var/list/var_modifiers + /// A reference to the ammo casing this component's parent was inserted into + var/obj/item/ammo_casing/holder_casing + /// A reference to the projectile this component's parent was inserted into + var/obj/projectile/holder_projectile + /// The icon file used for the overlay applied over the containing ammo casing + var/casing_overlay_icon + /// The icon state used for the overlay applied over the containing ammo casing + var/casing_overlay_icon_state + /// The icon file used for the overlay applied over the containing projectile + var/projectile_overlay_icon + /// The icon state used for the overlay applied over the containing projectile + var/projectile_overlay_icon_state + +/datum/component/dart_insert/Initialize(_casing_overlay_icon, _casing_overlay_icon_state, _projectile_overlay_icon, _projectile_overlay_icon_state) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + casing_overlay_icon = _casing_overlay_icon + casing_overlay_icon_state = _casing_overlay_icon_state + projectile_overlay_icon = _projectile_overlay_icon + projectile_overlay_icon_state = _projectile_overlay_icon_state + +/datum/component/dart_insert/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(on_preattack)) + RegisterSignal(parent, COMSIG_OBJ_RESKIN, PROC_REF(on_reskin)) + +/datum/component/dart_insert/UnregisterFromParent() + . = ..() + var/obj/item/parent_item = parent + var/parent_loc = parent_item.loc + if(parent_loc && (parent_loc == holder_casing || parent_loc == holder_projectile)) + parent_item.forceMove(get_turf(parent_item)) + remove_from_dart(holder_casing, holder_projectile) + UnregisterSignal(parent, COMSIG_ITEM_PRE_ATTACK) + +/datum/component/dart_insert/proc/on_preattack(datum/source, atom/target, mob/user, params) + SIGNAL_HANDLER + var/obj/item/ammo_casing/foam_dart/dart = target + if(!istype(dart)) + return + if(!dart.modified) + to_chat(user, span_warning("The safety cap prevents you from inserting [parent] into [dart].")) + return COMPONENT_CANCEL_ATTACK_CHAIN + if(HAS_TRAIT(dart, TRAIT_DART_HAS_INSERT)) + to_chat(user, span_warning("There's already something in [dart].")) + return COMPONENT_CANCEL_ATTACK_CHAIN + add_to_dart(dart, user) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/datum/component/dart_insert/proc/on_reskin(datum/source, mob/user, skin) + SIGNAL_HANDLER + SEND_SIGNAL(parent, COMSIG_DART_INSERT_PARENT_RESKINNED) + +/datum/component/dart_insert/proc/add_to_dart(obj/item/ammo_casing/dart, mob/user) + var/obj/projectile/dart_projectile = dart.loaded_projectile + var/obj/item/parent_item = parent + if(user) + if(!user.transferItemToLoc(parent_item, dart_projectile)) + return + to_chat(user, span_notice("You insert [parent_item] into [dart].")) + else + parent_item.forceMove(dart_projectile) + ADD_TRAIT(dart, TRAIT_DART_HAS_INSERT, REF(src)) + RegisterSignal(dart, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_dart_attack_self)) + RegisterSignal(dart, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_dart_examine_more)) + RegisterSignals(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED), PROC_REF(on_leave_dart)) + RegisterSignal(dart, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_casing_update_overlays)) + RegisterSignal(dart_projectile, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_projectile_update_overlays)) + RegisterSignals(dart_projectile, list(COMSIG_PROJECTILE_ON_SPAWN_DROP, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED), PROC_REF(on_spawn_drop)) + apply_var_modifiers(dart_projectile) + dart.harmful = dart_projectile.damage > 0 || dart_projectile.wound_bonus > 0 || dart_projectile.bare_wound_bonus > 0 + SEND_SIGNAL(parent, COMSIG_DART_INSERT_ADDED, dart) + dart.update_appearance() + dart_projectile.update_appearance() + holder_casing = dart + holder_projectile = dart_projectile + +/datum/component/dart_insert/proc/remove_from_dart(obj/item/ammo_casing/dart, obj/projectile/projectile, mob/user) + holder_casing = null + holder_projectile = null + if(istype(dart)) + UnregisterSignal(dart, list(COMSIG_ITEM_ATTACK_SELF, COMSIG_ATOM_EXAMINE_MORE, COMSIG_ATOM_UPDATE_OVERLAYS)) + REMOVE_TRAIT(dart, TRAIT_DART_HAS_INSERT, REF(src)) + dart.update_appearance() + if(istype(projectile)) + remove_var_modifiers(projectile) + UnregisterSignal(projectile, list(COMSIG_PROJECTILE_ON_SPAWN_DROP, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED, COMSIG_ATOM_UPDATE_OVERLAYS)) + if(dart?.loaded_projectile == projectile) + dart.harmful = projectile.damage > 0 || projectile.wound_bonus > 0 || projectile.bare_wound_bonus > 0 + projectile.update_appearance() + SEND_SIGNAL(parent, COMSIG_DART_INSERT_REMOVED, dart, projectile, user) + UnregisterSignal(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED)) + if(user) + INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, put_in_hands), parent) + to_chat(user, span_notice("You remove [parent] from [dart].")) + +/datum/component/dart_insert/proc/on_dart_attack_self(datum/source, mob/user) + SIGNAL_HANDLER + remove_from_dart(holder_casing, holder_projectile, user) + +/datum/component/dart_insert/proc/on_dart_examine_more(datum/source, mob/user, list/examine_list) + var/obj/item/parent_item = parent + examine_list += span_notice("You can see a [parent_item.name] inserted into it.") + +/datum/component/dart_insert/proc/on_leave_dart() + SIGNAL_HANDLER + remove_from_dart(holder_casing, holder_projectile) + +/datum/component/dart_insert/proc/on_spawn_drop(datum/source, obj/item/ammo_casing/new_casing) + SIGNAL_HANDLER + UnregisterSignal(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED)) + add_to_dart(new_casing) + +/datum/component/dart_insert/proc/on_casing_update_overlays(datum/source, list/new_overlays) + SIGNAL_HANDLER + new_overlays += mutable_appearance(casing_overlay_icon, casing_overlay_icon_state) + +/datum/component/dart_insert/proc/on_projectile_update_overlays(datum/source, list/new_overlays) + SIGNAL_HANDLER + new_overlays += mutable_appearance(projectile_overlay_icon, projectile_overlay_icon_state) + +/datum/component/dart_insert/proc/apply_var_modifiers(obj/projectile/projectile) + LAZYINITLIST(var_modifiers) + SEND_SIGNAL(parent, COMSIG_DART_INSERT_GET_VAR_MODIFIERS, var_modifiers) + projectile.damage += var_modifiers["damage"] + if(var_modifiers["speed"]) + var_modifiers["speed"] = reciprocal_add(projectile.speed, var_modifiers["speed"]) - projectile.speed + projectile.speed += var_modifiers["speed"] + projectile.armour_penetration += var_modifiers["armour_penetration"] + 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() + +/datum/component/dart_insert/proc/remove_var_modifiers(obj/projectile/projectile) + projectile.damage -= var_modifiers["damage"] + projectile.speed -= var_modifiers["speed"] + projectile.armour_penetration -= var_modifiers["armour_penetration"] + 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() + var_modifiers.Cut() diff --git a/code/datums/components/embedded.dm b/code/datums/components/embedded.dm index f4d7b5d7369..b62121d30d0 100644 --- a/code/datums/components/embedded.dm +++ b/code/datums/components/embedded.dm @@ -228,6 +228,7 @@ limb._unembed_object(weapon) UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) // have to do it here otherwise we trigger weaponDeleted() + SEND_SIGNAL(weapon, COMSIG_ITEM_UNEMBEDDED, victim) if(!weapon.unembedded()) // if it hasn't deleted itself due to drop del UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) if(to_hands) diff --git a/code/datums/elements/caseless.dm b/code/datums/elements/caseless.dm index a8a1d3df3e4..587a32f2b30 100644 --- a/code/datums/elements/caseless.dm +++ b/code/datums/elements/caseless.dm @@ -13,15 +13,23 @@ if(!isammocasing(target)) return ELEMENT_INCOMPATIBLE src.reusable = reusable + RegisterSignal(target, COMSIG_CASING_READY_PROJECTILE, PROC_REF(on_ready_projectile)) RegisterSignal(target, COMSIG_FIRE_CASING, PROC_REF(on_fired_casing)) -/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) +/datum/element/caseless/proc/on_ready_projectile(obj/item/ammo_casing/shell, atom/target, mob/living/user, quiet, zone_override, atom/fired_from) SIGNAL_HANDLER + var/obj/projectile/proj = shell.loaded_projectile if(isnull(proj)) return 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) + SIGNAL_HANDLER + if(isgun(fired_from)) var/obj/item/gun/shot_from = fired_from if(shot_from.chambered == shell) diff --git a/code/datums/mutations/tongue_spike.dm b/code/datums/mutations/tongue_spike.dm index b33b78d7d1b..e6249041250 100644 --- a/code/datums/mutations/tongue_spike.dm +++ b/code/datums/mutations/tongue_spike.dm @@ -73,6 +73,7 @@ unembedded() /obj/item/hardened_spike/embedded(atom/target) + . = ..() if(isbodypart(target)) missed = FALSE @@ -121,6 +122,7 @@ var/embedded_once_alread = FALSE /obj/item/hardened_spike/chem/embedded(mob/living/carbon/human/embedded_mob) + . = ..() if(embedded_once_alread) return embedded_once_alread = TRUE diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index d592cd0790b..55cdf19f960 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1177,7 +1177,8 @@ return ..() /obj/item/proc/embedded(atom/embedded_target, obj/item/bodypart/part) - return + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ITEM_EMBEDDED, embedded_target, part) /obj/item/proc/unembedded() if(item_flags & DROPDEL && !QDELETED(src)) @@ -1206,6 +1207,8 @@ ///In case we want to do something special (like self delete) upon failing to embed in something. /obj/item/proc/failedEmbed() + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ITEM_FAILED_EMBED) if(item_flags & DROPDEL && !QDELETED(src)) qdel(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1c34e75c3d1..a7263520ad8 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -292,7 +292,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) * Arguments: * * M The mob choosing a reskin option */ -/obj/proc/reskin_obj(mob/M) +/obj/proc/reskin_obj(mob/user) if(!LAZYLEN(unique_reskin)) return @@ -302,14 +302,15 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) items += list("[reskin_option]" = item_image) sort_list(items) - var/pick = show_radial_menu(M, src, items, custom_check = CALLBACK(src, PROC_REF(check_reskin_menu), M), radius = 38, require_near = TRUE) + var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, PROC_REF(check_reskin_menu), user), radius = 38, require_near = TRUE) if(!pick) return if(!unique_reskin[pick]) return current_skin = pick icon_state = unique_reskin[pick] - to_chat(M, "[src] is now skinned as '[pick].'") + to_chat(user, "[src] is now skinned as '[pick].'") + SEND_SIGNAL(src, COMSIG_OBJ_RESKIN, user, pick) /** * Checks if we are allowed to interact with a radial menu for reskins diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 69af56d3419..3750acddc42 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -31,6 +31,32 @@ var/requires_gravity = TRUE // can you use this to write in zero-g embedding = list(embed_chance = 50) sharpness = SHARP_POINTY + var/dart_insert_icon = 'icons/obj/weapons/guns/toy.dmi' + var/dart_insert_casing_icon_state = "overlay_pen" + var/dart_insert_projectile_icon_state = "overlay_pen_proj" + +/obj/item/pen/Initialize(mapload) + . = ..() + AddComponent(/datum/component/dart_insert, dart_insert_icon, dart_insert_casing_icon_state, dart_insert_icon, dart_insert_projectile_icon_state) + RegisterSignal(src, COMSIG_DART_INSERT_ADDED, PROC_REF(on_inserted_into_dart)) + RegisterSignal(src, COMSIG_DART_INSERT_REMOVED, PROC_REF(on_removed_from_dart)) + RegisterSignal(src, COMSIG_DART_INSERT_GET_VAR_MODIFIERS, PROC_REF(get_dart_var_modifiers)) + +/obj/item/pen/proc/on_inserted_into_dart(datum/source, obj/projectile/dart, mob/user, embedded = FALSE) + SIGNAL_HANDLER + +/obj/item/pen/proc/get_dart_var_modifiers(datum/source, list/modifiers) + SIGNAL_HANDLER + modifiers["damage"] = max(5, throwforce) + modifiers["speed"] = max(0, throw_speed - 3) + modifiers["embedding"] = embedding + modifiers["armour_penetration"] = armour_penetration + modifiers["wound_bonus"] = wound_bonus + modifiers["bare_wound_bonus"] = bare_wound_bonus + modifiers["demolition_mod"] = demolition_mod + +/obj/item/pen/proc/on_removed_from_dart(datum/source, obj/projectile/dart, mob/user) + SIGNAL_HANDLER /obj/item/pen/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is scribbling numbers all over [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit sudoku...")) @@ -69,7 +95,7 @@ if("#FF0000") colour = "#00FF00" chosen_color = "green" - throw_speed = initial(throw_speed) + throw_speed-- if("#00FF00") colour = "#0000FF" chosen_color = "blue" @@ -84,6 +110,8 @@ icon_state = "pen-fountain" font = FOUNTAIN_PEN_FONT requires_gravity = FALSE // fancy spess pens + dart_insert_casing_icon_state = "overlay_fountainpen" + dart_insert_projectile_icon_state = "overlay_fountainpen_proj" /obj/item/pen/charcoal name = "charcoal stylus" @@ -113,13 +141,23 @@ custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT*7.5) sharpness = SHARP_EDGED resistance_flags = FIRE_PROOF - unique_reskin = list("Oak" = "pen-fountain-o", - "Gold" = "pen-fountain-g", - "Rosewood" = "pen-fountain-r", - "Black and Silver" = "pen-fountain-b", - "Command Blue" = "pen-fountain-cb" - ) + unique_reskin = list( + "Oak" = "pen-fountain-o", + "Gold" = "pen-fountain-g", + "Rosewood" = "pen-fountain-r", + "Black and Silver" = "pen-fountain-b", + "Command Blue" = "pen-fountain-cb" + ) embedding = list("embed_chance" = 75) + dart_insert_casing_icon_state = "overlay_fountainpen_gold" + dart_insert_projectile_icon_state = "overlay_fountainpen_gold_proj" + var/list/overlay_reskin = list( + "Oak" = "overlay_fountainpen_gold", + "Gold" = "overlay_fountainpen_gold", + "Rosewood" = "overlay_fountainpen_gold", + "Black and Silver" = "overlay_fountainpen", + "Command Blue" = "overlay_fountainpen_gold" + ) /obj/item/pen/fountain/captain/Initialize(mapload) . = ..() @@ -128,12 +166,19 @@ effectiveness = 115, \ ) //the pen is mightier than the sword + RegisterSignal(src, COMSIG_DART_INSERT_PARENT_RESKINNED, PROC_REF(reskin_dart_insert)) /obj/item/pen/fountain/captain/reskin_obj(mob/M) ..() if(current_skin) desc = "It's an expensive [current_skin] fountain pen. The nib is quite sharp." +/obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp) + if(!istype(insert_comp)) //You really shouldn't be sending this signal from anything other than a dart_insert component + return + insert_comp.casing_overlay_icon_state = overlay_reskin[current_skin] + insert_comp.projectile_overlay_icon_state = "[overlay_reskin[current_skin]]_proj" + /obj/item/pen/attack_self(mob/living/carbon/user) . = ..() if(.) @@ -247,6 +292,23 @@ reagents.add_reagent(/datum/reagent/toxin/mutetoxin, 15) reagents.add_reagent(/datum/reagent/toxin/staminatoxin, 10) +/obj/item/pen/sleepy/on_inserted_into_dart(datum/source, obj/item/ammo_casing/dart, mob/user) + . = ..() + var/obj/projectile/proj = dart.loaded_projectile + RegisterSignal(proj, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_dart_hit)) + +/obj/item/pen/sleepy/on_removed_from_dart(datum/source, obj/item/ammo_casing/dart, obj/projectile/proj, mob/user) + . = ..() + if(istype(proj)) + UnregisterSignal(proj, COMSIG_PROJECTILE_SELF_ON_HIT) + +/obj/item/pen/sleepy/proc/on_dart_hit(datum/source, atom/movable/firer, atom/target, angle, hit_limb, blocked) + SIGNAL_HANDLER + var/mob/living/carbon/carbon_target = target + if(!istype(carbon_target) || blocked == 100) + return + if(carbon_target.can_inject(target_zone = hit_limb)) + reagents.trans_to(carbon_target, reagents.total_volume, transferred_by = firer, methods = INJECT) /* * (Alan) Edaggers */ @@ -262,6 +324,7 @@ light_power = 0.75 light_color = COLOR_SOFT_RED light_on = FALSE + dart_insert_projectile_icon_state = "overlay_edagger" /// The real name of our item when extended. var/hidden_name = "energy dagger" /// The real desc of our item when extended. @@ -287,6 +350,62 @@ RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) RegisterSignal(src, COMSIG_DETECTIVE_SCANNED, PROC_REF(on_scan)) +/obj/item/pen/edagger/on_inserted_into_dart(datum/source, obj/item/ammo_casing/dart, mob/user) + . = ..() + var/datum/component/transforming/transform_comp = GetComponent(/datum/component/transforming) + if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) + transform_comp.do_transform(src, user) + RegisterSignal(dart.loaded_projectile, COMSIG_PROJECTILE_FIRE, PROC_REF(on_containing_dart_fired)) + RegisterSignal(dart.loaded_projectile, COMSIG_PROJECTILE_ON_SPAWN_DROP, PROC_REF(on_containing_dart_drop)) + RegisterSignal(dart.loaded_projectile, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED, PROC_REF(on_containing_dart_embedded)) + +/obj/item/pen/edagger/on_removed_from_dart(datum/source, obj/item/ammo_casing/dart, obj/projectile/projectile, mob/user) + . = ..() + if(istype(dart)) + UnregisterSignal(dart, list(COMSIG_ITEM_UNEMBEDDED, COMSIG_ITEM_FAILED_EMBED)) + if(istype(projectile)) + UnregisterSignal(projectile, list(COMSIG_PROJECTILE_FIRE, COMSIG_PROJECTILE_ON_SPAWN_DROP, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED)) + +/obj/item/pen/edagger/get_dart_var_modifiers(datum/source, list/modifiers) + . = ..() + var/datum/component/transforming/transform_comp = GetComponent(/datum/component/transforming) + modifiers["damage"] = max(5, transform_comp.throwforce_on) + modifiers["speed"] = max(0, transform_comp.throw_speed_on - 3) + var/list/embed_params = modifiers["embedding"] + embed_params["embed_chance"] = 100 + +/obj/item/pen/edagger/proc/on_containing_dart_fired(obj/projectile/source) + SIGNAL_HANDLER + playsound(source, 'sound/weapons/saberon.ogg', 5, TRUE) + var/datum/component/transforming/transform_comp = GetComponent(/datum/component/transforming) + source.hitsound = transform_comp.hitsound_on + source.set_light(light_range, light_power, light_color, l_on = TRUE) + +/obj/item/pen/edagger/proc/on_containing_dart_drop(datum/source, obj/item/ammo_casing/new_casing) + SIGNAL_HANDLER + playsound(new_casing, 'sound/weapons/saberoff.ogg', 5, TRUE) + +/obj/item/pen/edagger/proc/on_containing_dart_embedded(datum/source, obj/item/ammo_casing/new_casing) + SIGNAL_HANDLER + RegisterSignal(new_casing, COMSIG_ITEM_UNEMBEDDED, PROC_REF(on_embedded_removed)) + RegisterSignal(new_casing, COMSIG_ITEM_FAILED_EMBED, PROC_REF(on_containing_dart_failed_embed)) + +/obj/item/pen/edagger/proc/on_containing_dart_failed_embed(obj/item/ammo_casing/source) + SIGNAL_HANDLER + playsound(source, 'sound/weapons/saberoff.ogg', 5, TRUE) + UnregisterSignal(source, list(COMSIG_ITEM_UNEMBEDDED, COMSIG_ITEM_FAILED_EMBED)) + +/obj/item/pen/edagger/proc/on_embedded_removed(obj/item/ammo_casing/source, mob/living/carbon/victim) + SIGNAL_HANDLER + playsound(source, 'sound/weapons/saberoff.ogg', 5, TRUE) + UnregisterSignal(source, list(COMSIG_ITEM_UNEMBEDDED, COMSIG_ITEM_FAILED_EMBED)) + victim.visible_message( + message = span_warning("The blade of the [hidden_name] retracts as the [source.name] is removed from [victim]!"), + self_message = span_warning("The blade of the [hidden_name] retracts as the [source.name] is removed from you!"), + blind_message = span_warning("You hear an energy blade retract!"), + vision_distance = 1 + ) + /obj/item/pen/edagger/suicide_act(mob/living/user) if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) user.visible_message(span_suicide("[user] forcefully rams the pen into their mouth!")) @@ -348,6 +467,25 @@ toolspeed = 10 //You will never willingly choose to use one of these over a shovel. font = FOUNTAIN_PEN_FONT colour = "#0000FF" + dart_insert_casing_icon_state = "overlay_survivalpen" + dart_insert_projectile_icon_state = "overlay_survivalpen_proj" + +/obj/item/pen/survival/on_inserted_into_dart(datum/source, obj/item/ammo_casing/dart, mob/user) + . = ..() + RegisterSignal(dart.loaded_projectile, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_dart_hit)) + +/obj/item/pen/survival/on_removed_from_dart(datum/source, obj/item/ammo_casing/dart, obj/projectile/proj, mob/user) + . = ..() + if(istype(proj)) + UnregisterSignal(proj, COMSIG_PROJECTILE_SELF_ON_HIT) + +/obj/item/pen/survival/proc/on_dart_hit(obj/projectile/source, atom/movable/firer, atom/target) + var/turf/target_turf = get_turf(target) + if(!target_turf) + target_turf = get_turf(src) + if(ismineralturf(target_turf)) + var/turf/closed/mineral/mineral_turf = target_turf + mineral_turf.gets_drilled(firer, TRUE) /obj/item/pen/destroyer name = "Fine Tipped Pen" @@ -362,6 +500,7 @@ desc = "A pen with an extendable screwdriver tip. This one has a yellow cap." icon_state = "pendriver" toolspeed = 1.2 // gotta have some downside + dart_insert_projectile_icon_state = "overlay_pendriver" /obj/item/pen/screwdriver/get_all_tool_behaviours() return list(TOOL_SCREWDRIVER) diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm index dee2c087c68..009c52b227e 100644 --- a/code/modules/projectiles/ammunition/_firing.dm +++ b/code/modules/projectiles/ammunition/_firing.dm @@ -66,6 +66,7 @@ if(reagents && loaded_projectile.reagents) reagents.trans_to(loaded_projectile, reagents.total_volume, transferred_by = user) //For chemical darts/bullets qdel(reagents) + SEND_SIGNAL(src, COMSIG_CASING_READY_PROJECTILE, target, user, quiet, zone_override, fired_from) /obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread, atom/fired_from) var/turf/curloc = get_turf(fired_from) diff --git a/code/modules/projectiles/ammunition/ballistic/foam.dm b/code/modules/projectiles/ammunition/ballistic/foam.dm index 21ceeb6918b..2895d74555b 100644 --- a/code/modules/projectiles/ammunition/ballistic/foam.dm +++ b/code/modules/projectiles/ammunition/ballistic/foam.dm @@ -9,6 +9,7 @@ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.1125) harmful = FALSE var/modified = FALSE + var/static/list/insertable_items_hint = list(/obj/item/pen) /obj/item/ammo_casing/foam_dart/Initialize(mapload) . = ..() @@ -18,47 +19,37 @@ . = ..() if(modified) icon_state = "[base_icon_state]_empty" - loaded_projectile?.icon_state = "[base_icon_state]_empty" + loaded_projectile?.icon_state = "[loaded_projectile.base_icon_state]_empty_proj" return icon_state = "[base_icon_state]" - loaded_projectile?.icon_state = "[loaded_projectile.base_icon_state]" + loaded_projectile?.icon_state = "[loaded_projectile.base_icon_state]_proj" /obj/item/ammo_casing/foam_dart/update_desc() . = ..() desc = "It's Donk or Don't! [modified ? "... Although, this one doesn't look too safe." : "Ages 8 and up."]" -/obj/item/ammo_casing/foam_dart/attackby(obj/item/A, mob/user, params) - var/obj/projectile/bullet/foam_dart/FD = loaded_projectile - if (A.tool_behaviour == TOOL_SCREWDRIVER && !modified) +/obj/item/ammo_casing/foam_dart/examine_more(mob/user) + . = ..() + if(!HAS_TRAIT(src, TRAIT_DART_HAS_INSERT)) + var/list/type_initial_names = list() + for(var/type in insertable_items_hint) + var/obj/item/type_item = type + type_initial_names += "\a [initial(type_item.name)]" + . += span_notice("[modified ? "You can" : "If you removed the safety cap with a screwdriver, you could"] insert a small item\ + [length(type_initial_names) ? ", such as [english_list(type_initial_names, and_text = "or ", final_comma_text = ", ")]" : ""].") + + +/obj/item/ammo_casing/foam_dart/attackby(obj/item/attacking_item, mob/user, params) + var/obj/projectile/bullet/foam_dart/dart = loaded_projectile + if (attacking_item.tool_behaviour == TOOL_SCREWDRIVER && !modified) modified = TRUE - FD.modified = TRUE - FD.damage_type = BRUTE + dart.modified = TRUE + dart.damage_type = BRUTE to_chat(user, span_notice("You pop the safety cap off [src].")) update_appearance() - else if (istype(A, /obj/item/pen)) - if(modified) - if(!FD.pen) - harmful = TRUE - if(!user.transferItemToLoc(A, FD)) - return - FD.pen = A - FD.damage = 5 - to_chat(user, span_notice("You insert [A] into [src].")) - else - to_chat(user, span_warning("There's already something in [src].")) - else - to_chat(user, span_warning("The safety cap prevents you from inserting [A] into [src].")) else return ..() -/obj/item/ammo_casing/foam_dart/attack_self(mob/living/user) - var/obj/projectile/bullet/foam_dart/FD = loaded_projectile - if(FD.pen) - FD.damage = initial(FD.damage) - user.put_in_hands(FD.pen) - to_chat(user, span_notice("You remove [FD.pen] from [src].")) - FD.pen = null - /obj/item/ammo_casing/foam_dart/riot name = "riot foam dart" desc = "Whose smart idea was it to use toys as crowd control? Ages 18 and up." diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm index a3bb54a07e0..713790049b5 100644 --- a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm +++ b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm @@ -32,6 +32,7 @@ damage = 50 speed = 1 range = 25 + shrapnel_type = null embedding = list( embed_chance = 90, fall_chance = 2, @@ -42,7 +43,6 @@ jostle_pain_mult = 3, rip_time = 1 SECONDS ) - shrapnel_type = /obj/item/ammo_casing/arrow /// holy arrows /obj/item/ammo_casing/arrow/holy @@ -59,7 +59,6 @@ desc = "Here it comes, cultist scum!" icon_state = "holy_arrow_projectile" damage = 20 //still a lot but this is roundstart gear so far less - shrapnel_type =/obj/item/ammo_casing/arrow/holy embedding = list( embed_chance = 50, fall_chance = 2, diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 18c0b557f88..a7beb1a7102 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -282,8 +282,8 @@ var/mob/living/L = target hit_limb_zone = L.check_hit_limb_zone_name(def_zone) if(fired_from) - SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle, hit_limb_zone) - SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle, hit_limb_zone) + SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle, hit_limb_zone, blocked) + SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle, hit_limb_zone, blocked) if(QDELETED(src)) // in case one of the above signals deleted the projectile for whatever reason return BULLET_ACT_BLOCK diff --git a/code/modules/projectiles/projectile/bullets/foam_dart.dm b/code/modules/projectiles/projectile/bullets/foam_dart.dm index 6d4cffd4524..3f086166e6a 100644 --- a/code/modules/projectiles/projectile/bullets/foam_dart.dm +++ b/code/modules/projectiles/projectile/bullets/foam_dart.dm @@ -5,27 +5,24 @@ damage_type = OXY icon = 'icons/obj/weapons/guns/toy.dmi' icon_state = "foamdart_proj" - base_icon_state = "foamdart_proj" + base_icon_state = "foamdart" range = 10 + shrapnel_type = null embedding = null var/modified = FALSE var/obj/item/pen/pen = null /obj/projectile/bullet/foam_dart/Initialize(mapload) . = ..() - RegisterSignal(src, COMSIG_PROJECTILE_ON_SPAWN_DROP, PROC_REF(handle_drop)) + RegisterSignals(src, list(COMSIG_PROJECTILE_ON_SPAWN_DROP, COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED), PROC_REF(handle_drop)) /obj/projectile/bullet/foam_dart/proc/handle_drop(datum/source, obj/item/ammo_casing/foam_dart/newcasing) SIGNAL_HANDLER newcasing.modified = modified + newcasing.update_appearance() var/obj/projectile/bullet/foam_dart/newdart = newcasing.loaded_projectile newdart.modified = modified newdart.damage_type = damage_type - if(pen) - newdart.pen = pen - pen.forceMove(newdart) - pen = null - newdart.damage = 5 newdart.update_appearance() /obj/projectile/bullet/foam_dart/Destroy() @@ -35,5 +32,5 @@ /obj/projectile/bullet/foam_dart/riot name = "riot foam dart" icon_state = "foamdart_riot_proj" - base_icon_state = "foamdart_riot_proj" + base_icon_state = "foamdart_riot" stamina = 25 diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm index de7e59facc3..d76b2de9d6a 100644 --- a/code/modules/projectiles/projectile/bullets/rifle.dm +++ b/code/modules/projectiles/projectile/bullets/rifle.dm @@ -46,7 +46,7 @@ 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) wound_falloff_tile = -5 - shrapnel_type = /obj/item/ammo_casing/harpoon + shrapnel_type = null // Rebar (Rebar Crossbow) /obj/projectile/bullet/rebar @@ -75,4 +75,3 @@ 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 shrapnel_type = /obj/item/stack/rods - diff --git a/icons/obj/weapons/guns/toy.dmi b/icons/obj/weapons/guns/toy.dmi index 83a85e7e447..3f7e509b699 100644 Binary files a/icons/obj/weapons/guns/toy.dmi and b/icons/obj/weapons/guns/toy.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 5ae674aea32..35b9780d62d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1117,6 +1117,7 @@ #include "code\datums\components\customizable_reagent_holder.dm" #include "code\datums\components\damage_aura.dm" #include "code\datums\components\damage_chain.dm" +#include "code\datums\components\dart_insert.dm" #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\death_linked.dm" #include "code\datums\components\dejavu.dm"