From fb69fbb8a200bc9472eeba0ac55162b9593b1b1b Mon Sep 17 00:00:00 2001 From: Cirrial Date: Thu, 22 May 2025 08:02:28 +0100 Subject: [PATCH] Refactor: Moves throwing and giving items from /mob/living/carbon to /mob/living (#91049) Given the existence of basic mobs with hand slots, it feels like throwing and giving items shouldn't be something exclusive to carbon mobs, so I've pulled things around to make this happen. The only basic mobs with hands at time of writing are gorillas and dextrous holoparasites, but the inability to throw things when you're a gorilla just doesn't seem right to me. Some more details about what I've done here: - Made the dextrous component optionally enable throwing for the mob it's added to. - Moved offer/give item functionality to /mob/living (I can't see any reason why only carbon mobs should have this option) - Moved throwing and give item hotkeys from carbon to "human" (where all the other /mob/living hotkeys go) and, as a result, removed carbon hotkeys (nothing is left in them). - Moved throwing code and item offering code to its own file because living.dm is 3000+ lines long and should probably be broken up some day (I'm not brave enough for that) - Cleaned up an unused global signal that hasn't been used since dogs got moved to basic mobs. - Other miscellaneous cleanup where I noticed it. - In terms of testing: Tested using gorillas (only checked the dextrous holoparasite to confirm the button and hotkeys worked). Things that were working: - Can throw items if the mob is set up to allow it. - Can give items as a gorilla to a human, as a human to a gorilla, and as a human to a human. - Can give a high five to a gorilla (and the gorilla can receive it). Gorillas can't give a high five back, though (they don't have the emote), this already ballooned in scope, someone else can make that happen. - There are an alarmingly high amount of niche emote-into-item-into-giving behaviours I suspect half the playerbase or more aren't even aware of (does anyone offer their hand to someone to get them up off of the ground?) and I don't know if I broke any of them with this, but the fact high fives work gives me some hope they're probably still fine. Lets gorillas and dextrous holoparasites throw things and give things, but most importantly sets up more framework for any future dextrous basic mobs to also be able to do this. There's no real reason to keep this functionality confined to carbon mobs when dextrous basic mobs are a thing. :cl: add: Gorillas can now throw things and offer items to players. refactor: Moved throwing and offering item code to be based on living mobs, not just carbon mobs. /:cl: --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/__DEFINES/dcs/signals/signals_global.dm | 2 - .../signals/signals_mob/signals_mob_carbon.dm | 3 - .../signals/signals_mob/signals_mob_living.dm | 4 +- code/__DEFINES/dcs/signals/signals_object.dm | 4 +- code/__DEFINES/keybinding.dm | 6 +- code/__DEFINES/traits/declarations.dm | 2 + code/_globalvars/traits/_traits.dm | 1 + code/_globalvars/traits/admin_tooling.dm | 1 + code/_onclick/hud/alert.dm | 31 ++- code/_onclick/hud/generic_dextrous.dm | 5 + code/_onclick/hud/screen_objects.dm | 6 +- code/datums/elements/dextrous.dm | 4 +- code/datums/elements/high_fiver.dm | 4 +- code/datums/keybinding/carbon.dm | 58 ----- code/datums/keybinding/living.dm | 62 +++++ code/datums/status_effects/neutral.dm | 38 +-- .../dna_infuser/organ_sets/rat_organs.dm | 4 +- code/game/objects/items.dm | 23 +- code/game/objects/items/hand_items.dm | 12 +- code/modules/basketball/basketball.dm | 2 +- .../basic/farm_animals/gorilla/gorilla.dm | 2 +- .../basic/guardian/guardian_types/dextrous.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 123 +--------- code/modules/mob/living/carbon/inventory.dm | 89 ------- .../mob/living/living_item_handling.dm | 227 ++++++++++++++++++ tgstation.dme | 2 +- 26 files changed, 380 insertions(+), 337 deletions(-) delete mode 100644 code/datums/keybinding/carbon.dm create mode 100644 code/modules/mob/living/living_item_handling.dm diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index 0c2c4be8685..4e5a8938fec 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -43,8 +43,6 @@ #define COMSIG_GLOB_RANDOM_EVENT "!random_event" /// Do not allow this random event to continue. #define CANCEL_RANDOM_EVENT (1<<0) -/// a person somewhere has thrown something : (mob/living/carbon/carbon_thrower, target) -#define COMSIG_GLOB_CARBON_THROW_THING "!throw_thing" /// a trapdoor remote has sent out a signal to link with a trapdoor #define COMSIG_GLOB_TRAPDOOR_LINK "!trapdoor_link" ///successfully linked to a trapdoor! diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index 650dc560a4a..1aa28be695a 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -146,9 +146,6 @@ ///from base of /mob/living/carbon/regenerate_limbs(): (excluded_limbs) #define COMSIG_CARBON_REGENERATE_LIMBS "living_regen_limbs" -///from /atom/movable/screen/alert/give/proc/handle_transfer(): (taker, item) -#define COMSIG_CARBON_ITEM_GIVEN "carbon_item_given" - /// Sent from /mob/living/carbon/human/handle_blood(): (seconds_per_tick, times_fired) #define COMSIG_HUMAN_ON_HANDLE_BLOOD "human_on_handle_blood" /// Return to prevent all default blood handling diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index f849817d24c..5a6e982d32a 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -293,8 +293,10 @@ ///cancel post eating #define COMSIG_MOB_TERMINATE_EAT (1<<0) -///From mob/living/carbon/proc/throw_mode_on and throw_mode_off +///From mob/living/proc/throw_mode_on and throw_mode_off #define COMSIG_LIVING_THROW_MODE_TOGGLE "living_throw_mode_toggle" +///from /atom/movable/screen/alert/give/proc/handle_transfer(): (taker, item) +#define COMSIG_LIVING_ITEM_GIVEN "living_item_given" /// From mob/living/proc/on_fall #define COMSIG_LIVING_THUD "living_thud" ///From /datum/component/happiness() diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index a113f0b88e8..b693999cddb 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -179,11 +179,11 @@ #define COMSIG_ARMOR_PLATED "armor_plated" ///Called when an item gets recharged by the ammo powerup #define COMSIG_ITEM_RECHARGED "item_recharged" -///Called when an item is being offered, from [/obj/item/proc/on_offered(mob/living/carbon/offerer)] +///Called when an item is being offered, from [/obj/item/proc/on_offered(mob/living/offerer)] #define COMSIG_ITEM_OFFERING "item_offering" ///Interrupts the offer proc #define COMPONENT_OFFER_INTERRUPT (1<<0) -///Called when an someone tries accepting an offered item, from [/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)] +///Called when an someone tries accepting an offered item, from [/obj/item/proc/on_offer_taken(mob/living/offerer, mob/living/taker)] #define COMSIG_ITEM_OFFER_TAKEN "item_offer_taken" ///Interrupts the offer acceptance #define COMPONENT_OFFER_TAKE_INTERRUPT (1<<0) diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 8ae95933e64..61f73e5c584 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -22,9 +22,6 @@ //Carbon #define COMSIG_KB_CARBON_HOLDRUNMOVEINTENT_DOWN "keybinding_carbon_holdrunmoveintent_down" #define COMSIG_KB_CARBON_HOLDRUNMOVEINTENT_UP "keybinding_carbon_holdrunmoveintent_up" -#define COMSIG_KB_CARBON_TOGGLETHROWMODE_DOWN "keybinding_carbon_togglethrowmode_down" -#define COMSIG_KB_CARBON_HOLDTHROWMODE_DOWN "keybinding_carbon_holdthrowmode_down" -#define COMSIG_KB_CARBON_GIVEITEM_DOWN "keybinding_carbon_giveitem_down" //Client #define COMSIG_KB_CLIENT_GETHELP_DOWN "keybinding_client_gethelp_down" @@ -57,6 +54,9 @@ #define COMSIG_KB_LIVING_DISABLE_COMBAT_DOWN "keybinding_living_disable_combat_down" #define COMSIG_KB_LIVING_TOGGLEMOVEINTENT_DOWN "keybinding_mob_togglemoveintent_down" #define COMSIG_KB_LIVING_TOGGLEMOVEINTENTALT_DOWN "keybinding_mob_togglemoveintentalt_down" +#define COMSIG_KB_LIVING_TOGGLETHROWMODE_DOWN "keybinding_living_togglethrowmode_down" +#define COMSIG_KB_LIVING_HOLDTHROWMODE_DOWN "keybinding_living_holdthrowmode_down" +#define COMSIG_KB_LIVING_GIVEITEM_DOWN "keybinding_living_giveitem_down" #define COMSIG_KB_LIVING_VIEW_PET_COMMANDS "keybinding_living_view_pet_commands" //Mob diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 1ec5a3191ff..9203aa63286 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -60,6 +60,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CLUMSY "clumsy" /// Trait that means you are capable of holding items in some form #define TRAIT_CAN_HOLD_ITEMS "can_hold_items" +/// Trait that means you're capable of throwing things +#define TRAIT_CAN_THROW_ITEMS "can_throw_items" /// Trait which lets you clamber over a barrier #define TRAIT_FENCE_CLIMBER "can_climb_fences" /// means that you can't use weapons with normal trigger guards. diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index e26587019ea..a8df896fa82 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -210,6 +210,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CAN_MOUNT_HUMANS" = TRAIT_CAN_MOUNT_HUMANS, "TRAIT_CAN_SIGN_ON_COMMS" = TRAIT_CAN_SIGN_ON_COMMS, "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP, + "TRAIT_CAN_THROW_ITEMS" = TRAIT_CAN_THROW_ITEMS, "TRAIT_CAN_USE_NUKE" = TRAIT_CAN_USE_NUKE, "TRAIT_CATLIKE_GRACE" = TRAIT_CATLIKE_GRACE, "TRAIT_CHANGELING_HIVEMIND_MUTE" = TRAIT_CHANGELING_HIVEMIND_MUTE, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 74c2b86e291..5f90572f0c5 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -62,6 +62,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_BYPASS_MEASURES" = TRAIT_BYPASS_MEASURES, "TRAIT_CAN_HOLD_ITEMS" = TRAIT_CAN_HOLD_ITEMS, "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP, + "TRAIT_CAN_THROW_ITEMS" = TRAIT_CAN_THROW_ITEMS, "TRAIT_CAN_USE_NUKE" = TRAIT_CAN_USE_NUKE, "TRAIT_CANNOT_BE_UNBUCKLED" = TRAIT_CANNOT_BE_UNBUCKLED, "TRAIT_CANNOT_OPEN_PRESENTS" = TRAIT_CANNOT_OPEN_PRESENTS, diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 2178554bf00..1b4a043678a 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -343,14 +343,13 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /** * Handles assigning most of the variables for the alert that pops up when an item is offered * - * Handles setting the name, description and icon of the alert and tracking the person giving + * Handles setting the name, description and icon of the alert and tracking the living mob giving * and the item being offered. * Arguments: - * * taker - The person receiving the alert - * * offerer - The person giving the alert and item - * * receiving - The item being given by the offerer + * * taker - The living mob receiving the alert + * * offer - The status effect connected to the offer being made */ -/atom/movable/screen/alert/give/proc/setup(mob/living/carbon/taker, datum/status_effect/offering/offer) +/atom/movable/screen/alert/give/proc/setup(mob/living/taker, datum/status_effect/offering/offer) src.offer = offer var/mob/living/offerer = offer.owner @@ -377,7 +376,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." * Returns a string that will be displayed in the alert, which is `receiving.name` * by default. */ -/atom/movable/screen/alert/give/proc/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) +/atom/movable/screen/alert/give/proc/get_receiving_name(mob/living/taker, mob/living/offerer, obj/item/receiving) return receiving.name /atom/movable/screen/alert/give/Click(location, control, params) @@ -385,7 +384,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(!.) return - if(!iscarbon(usr)) + if(!isliving(usr)) CRASH("User for [src] is of type \[[usr.type]\]. This should never happen.") handle_transfer() @@ -402,11 +401,11 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /// An overrideable proc used simply to hand over the item when claimed, this is a proc so that high-fives can override them since nothing is actually transferred /atom/movable/screen/alert/give/proc/handle_transfer() - var/mob/living/carbon/taker = owner + var/mob/living/taker = owner var/mob/living/offerer = offer.owner var/obj/item/receiving = offer.offered_item taker.take(offerer, receiving) - SEND_SIGNAL(offerer, COMSIG_CARBON_ITEM_GIVEN, taker, receiving) + SEND_SIGNAL(offerer, COMSIG_LIVING_ITEM_GIVEN, taker, receiving) /atom/movable/screen/alert/give/highfive additional_desc_text = "Click this alert to slap it." @@ -415,10 +414,10 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /// Tracks active "to slow"ing so we can't spam click var/too_slowing_this_guy = FALSE -/atom/movable/screen/alert/give/highfive/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) +/atom/movable/screen/alert/give/highfive/get_receiving_name(mob/living/taker, mob/living/offerer, obj/item/receiving) return "a high-five" -/atom/movable/screen/alert/give/highfive/setup(mob/living/carbon/taker, datum/status_effect/offering/offer) +/atom/movable/screen/alert/give/highfive/setup(mob/living/taker, datum/status_effect/offering/offer) . = ..() RegisterSignal(offer.owner, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(check_fake_out)) @@ -426,7 +425,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(too_slowing_this_guy) return - var/mob/living/carbon/taker = owner + var/mob/living/taker = owner var/mob/living/offerer = offer.owner var/obj/item/receiving = offer.offered_item if(!QDELETED(receiving) && offerer.is_holding(receiving)) @@ -437,7 +436,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /// If the person who offered the high five no longer has it when we try to accept it, we get pranked hard /atom/movable/screen/alert/give/highfive/proc/too_slow_p1() - var/mob/living/carbon/rube = owner + var/mob/living/rube = owner var/mob/living/offerer = offer?.owner if(QDELETED(rube) || QDELETED(offerer)) qdel(src) @@ -450,7 +449,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /// Part two of the ultimate prank /atom/movable/screen/alert/give/highfive/proc/too_slow_p2() - var/mob/living/carbon/rube = owner + var/mob/living/rube = owner var/mob/living/offerer = offer?.owner if(!QDELETED(rube) && !QDELETED(offerer)) offerer.visible_message(span_danger("[offerer] pulls away from [rube]'s slap at the last second, dodging the high-five entirely!"), span_nicegreen("[rube] fails to make contact with your hand, making an utter fool of [rube.p_them()]self!"), span_hear("You hear a disappointing sound of flesh not hitting flesh!"), ignored_mobs=rube) @@ -474,13 +473,13 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." screentip_override_text = "Take Hand" examinable = FALSE -/atom/movable/screen/alert/give/hand/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) +/atom/movable/screen/alert/give/hand/get_receiving_name(mob/living/taker, mob/living/offerer, obj/item/receiving) additional_desc_text = "Click this alert to take it and let [offerer.p_them()] pull you around!" return "[offerer.p_their()] [receiving.name]" /atom/movable/screen/alert/give/hand/helping -/atom/movable/screen/alert/give/hand/helping/get_receiving_name(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving) +/atom/movable/screen/alert/give/hand/helping/get_receiving_name(mob/living/taker, mob/living/offerer, obj/item/receiving) . = ..() additional_desc_text = "Click this alert to let them help you up!" diff --git a/code/_onclick/hud/generic_dextrous.dm b/code/_onclick/hud/generic_dextrous.dm index 134f91a2b8d..b7f7f7af739 100644 --- a/code/_onclick/hud/generic_dextrous.dm +++ b/code/_onclick/hud/generic_dextrous.dm @@ -31,6 +31,11 @@ floor_change.icon = 'icons/hud/screen_midnight.dmi' static_inventory += floor_change + if(HAS_TRAIT(owner, TRAIT_CAN_THROW_ITEMS)) + throw_icon = new /atom/movable/screen/throw_catch(null, src) + throw_icon.icon = ui_style + throw_icon.screen_loc = ui_drop_throw + static_inventory += throw_icon zone_select = new /atom/movable/screen/zone_sel(null, src) zone_select.icon = ui_style diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index a7eab74eebc..036e2e4b988 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -544,9 +544,9 @@ mouse_over_pointer = MOUSE_HAND_POINTER /atom/movable/screen/throw_catch/Click() - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.toggle_throw_mode() + if(isliving(usr)) + var/mob/living/user = usr + user.toggle_throw_mode() /atom/movable/screen/zone_sel name = "damage zone" diff --git a/code/datums/elements/dextrous.dm b/code/datums/elements/dextrous.dm index 1896257eeec..f242a7e9163 100644 --- a/code/datums/elements/dextrous.dm +++ b/code/datums/elements/dextrous.dm @@ -4,13 +4,15 @@ */ /datum/element/dextrous -/datum/element/dextrous/Attach(datum/target, hands_count = 2, hud_type = /datum/hud/dextrous) +/datum/element/dextrous/Attach(datum/target, hands_count = 2, hud_type = /datum/hud/dextrous, can_throw = FALSE) . = ..() if (!isliving(target) || iscarbon(target)) return ELEMENT_INCOMPATIBLE // Incompatible with the carbon typepath because that already has its own hand handling and doesn't need hand holding var/mob/living/mob_parent = target set_available_hands(mob_parent, hands_count) + if(can_throw) + ADD_TRAIT(target, TRAIT_CAN_THROW_ITEMS, REF(src)) // need to add before hud setup mob_parent.hud_type = hud_type if (mob_parent.hud_used) mob_parent.set_hud_used(new hud_type(target)) diff --git a/code/datums/elements/high_fiver.dm b/code/datums/elements/high_fiver.dm index 249a9f4059d..09372937725 100644 --- a/code/datums/elements/high_fiver.dm +++ b/code/datums/elements/high_fiver.dm @@ -18,7 +18,7 @@ UnregisterSignal(source, list(COMSIG_ITEM_OFFERING, COMSIG_ITEM_OFFER_TAKEN)) /// Signal proc for [COMSIG_ITEM_OFFERING] to set up the high-five on offer -/datum/element/high_fiver/proc/on_offer(obj/item/source, mob/living/carbon/offerer) +/datum/element/high_fiver/proc/on_offer(obj/item/source, mob/living/offerer) SIGNAL_HANDLER offerer.visible_message( @@ -31,7 +31,7 @@ return COMPONENT_OFFER_INTERRUPT /// Signal proc for [COMSIG_ITEM_OFFER_TAKEN] to continue through with the high-five on take -/datum/element/high_fiver/proc/on_offer_taken(obj/item/source, mob/living/carbon/offerer, mob/living/carbon/taker) +/datum/element/high_fiver/proc/on_offer_taken(obj/item/source, mob/living/offerer, mob/living/taker) SIGNAL_HANDLER var/open_hands_taker = 0 diff --git a/code/datums/keybinding/carbon.dm b/code/datums/keybinding/carbon.dm deleted file mode 100644 index 92f36b06529..00000000000 --- a/code/datums/keybinding/carbon.dm +++ /dev/null @@ -1,58 +0,0 @@ -/datum/keybinding/carbon - category = CATEGORY_CARBON - weight = WEIGHT_MOB - -/datum/keybinding/carbon/can_use(client/user) - return iscarbon(user.mob) - -/datum/keybinding/carbon/toggle_throw_mode - hotkey_keys = list("R", "Southwest") // END - name = "toggle_throw_mode" - full_name = "Toggle throw mode" - description = "Toggle throwing the current item or not." - category = CATEGORY_CARBON - keybind_signal = COMSIG_KB_CARBON_TOGGLETHROWMODE_DOWN - -/datum/keybinding/carbon/toggle_throw_mode/down(client/user, turf/target) - . = ..() - if(.) - return - var/mob/living/carbon/C = user.mob - C.toggle_throw_mode() - return TRUE - -/datum/keybinding/carbon/hold_throw_mode - hotkey_keys = list("Space") - name = "hold_throw_mode" - full_name = "Hold throw mode" - description = "Hold this to turn on throw mode, and release it to turn off throw mode" - category = CATEGORY_CARBON - keybind_signal = COMSIG_KB_CARBON_HOLDTHROWMODE_DOWN - -/datum/keybinding/carbon/hold_throw_mode/down(client/user, turf/target) - . = ..() - if(.) - return - var/mob/living/carbon/carbon_user = user.mob - carbon_user.throw_mode_on(THROW_MODE_HOLD) - -/datum/keybinding/carbon/hold_throw_mode/up(client/user, turf/target) - . = ..() - if(.) - return - var/mob/living/carbon/carbon_user = user.mob - carbon_user.throw_mode_off(THROW_MODE_HOLD) -/datum/keybinding/carbon/give - hotkey_keys = list("G") - name = "Give_Item" - full_name = "Give item" - description = "Give the item you're currently holding" - keybind_signal = COMSIG_KB_CARBON_GIVEITEM_DOWN - -/datum/keybinding/carbon/give/down(client/user, turf/target) - . = ..() - if(.) - return - var/mob/living/carbon/carbon_user = user.mob - carbon_user.give() - return TRUE diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm index 43369700fc1..d1f2031ea3b 100644 --- a/code/datums/keybinding/living.dm +++ b/code/datums/keybinding/living.dm @@ -167,3 +167,65 @@ var/mob/living/M = user.mob M.toggle_move_intent() return TRUE + +/datum/keybinding/living/toggle_throw_mode + hotkey_keys = list("R", "Southwest") // END + name = "toggle_throw_mode" + full_name = "Toggle throw mode" + description = "Toggle throwing the current item or not." + keybind_signal = COMSIG_KB_LIVING_TOGGLETHROWMODE_DOWN + +/datum/keybinding/living/toggle_throw_mode/down(client/user) + . = ..() + if(.) + return + var/mob/living/living_user = user.mob + living_user.toggle_throw_mode() + return TRUE + +/datum/keybinding/living/hold_throw_mode + hotkey_keys = list("Space") + name = "hold_throw_mode" + full_name = "Hold throw mode" + description = "Hold this to turn on throw mode, and release it to turn off throw mode" + keybind_signal = COMSIG_KB_LIVING_HOLDTHROWMODE_DOWN + +/datum/keybinding/living/hold_throw_mode/down(client/user, turf/target) + . = ..() + if(.) + return + var/mob/living/living_user = user.mob + living_user.throw_mode_on(THROW_MODE_HOLD) + +/datum/keybinding/living/hold_throw_mode/up(client/user, turf/target) + . = ..() + if(.) + return + var/mob/living/living_user = user.mob + living_user.throw_mode_off(THROW_MODE_HOLD) + +/datum/keybinding/living/give + hotkey_keys = list("G") + name = "Give_Item" + full_name = "Give item" + description = "Give the item you're currently holding" + keybind_signal = COMSIG_KB_LIVING_GIVEITEM_DOWN + +/datum/keybinding/living/give/can_use(client/user) + . = ..() + if (!.) + return FALSE + if(!user.mob) + return FALSE + if(!HAS_TRAIT(user.mob, TRAIT_CAN_HOLD_ITEMS)) + return FALSE + return TRUE + +/datum/keybinding/living/give/down(client/user, turf/target) + . = ..() + if(.) + return + var/mob/living/living_user = user.mob + if(!HAS_TRAIT(living_user, TRAIT_CAN_HOLD_ITEMS)) + return + living_user.give() diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index fb83ade3aa3..673eb71a350 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -200,7 +200,7 @@ /// The type of alert given to people when offered, in case you need to override some behavior (like for high-fives) var/give_alert_type = /atom/movable/screen/alert/give -/datum/status_effect/offering/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override, mob/living/carbon/offered) +/datum/status_effect/offering/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override, mob/living/offered) . = ..() if(!.) return @@ -211,7 +211,7 @@ if(offered && is_taker_elligible(offered)) register_candidate(offered) else - for(var/mob/living/carbon/possible_taker in orange(1, owner)) + for(var/mob/living/possible_taker in orange(1, owner)) if(!is_taker_elligible(possible_taker)) continue @@ -225,14 +225,14 @@ RegisterSignals(offered_item, list(COMSIG_QDELETING, COMSIG_ITEM_DROPPED), PROC_REF(dropped_item)) /datum/status_effect/offering/Destroy() - for(var/mob/living/carbon/removed_taker as anything in possible_takers) + for(var/mob/living/removed_taker as anything in possible_takers) remove_candidate(removed_taker) LAZYCLEARLIST(possible_takers) offered_item = null return ..() -/// Hook up the specified carbon mob to be offered the item in question, give them the alert and signals and all -/datum/status_effect/offering/proc/register_candidate(mob/living/carbon/possible_candidate) +/// Hook up the specified living mob to be offered the item in question, give them the alert and signals and all +/datum/status_effect/offering/proc/register_candidate(mob/living/possible_candidate) var/atom/movable/screen/alert/give/G = possible_candidate.throw_alert("[owner]", give_alert_type) if(!G) return @@ -240,8 +240,8 @@ RegisterSignal(possible_candidate, COMSIG_MOVABLE_MOVED, PROC_REF(check_taker_in_range)) G.setup(possible_candidate, src) -/// Remove the alert and signals for the specified carbon mob. Automatically removes the status effect when we lost the last taker -/datum/status_effect/offering/proc/remove_candidate(mob/living/carbon/removed_candidate) +/// Remove the alert and signals for the specified living mob. Automatically removes the status effect when we lost the last taker +/datum/status_effect/offering/proc/remove_candidate(mob/living/removed_candidate) removed_candidate.clear_alert("[owner]") LAZYREMOVE(possible_takers, removed_candidate) UnregisterSignal(removed_candidate, COMSIG_MOVABLE_MOVED) @@ -249,7 +249,7 @@ qdel(src) /// One of our possible takers moved, see if they left us hanging -/datum/status_effect/offering/proc/check_taker_in_range(mob/living/carbon/taker) +/datum/status_effect/offering/proc/check_taker_in_range(mob/living/taker) SIGNAL_HANDLER if(owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker)) return @@ -258,10 +258,10 @@ remove_candidate(taker) /// The offerer moved, see if anyone is out of range now -/datum/status_effect/offering/proc/check_owner_in_range(mob/living/carbon/source) +/datum/status_effect/offering/proc/check_owner_in_range(mob/living/source) SIGNAL_HANDLER - for(var/mob/living/carbon/checking_taker as anything in possible_takers) + for(var/mob/living/checking_taker as anything in possible_takers) if(!istype(checking_taker) || !owner.CanReach(checking_taker) || IS_DEAD_OR_INCAP(checking_taker)) remove_candidate(checking_taker) @@ -276,7 +276,7 @@ * * Returns `TRUE` if the taker is valid as a target for the offering. */ -/datum/status_effect/offering/proc/is_taker_elligible(mob/living/carbon/taker) +/datum/status_effect/offering/proc/is_taker_elligible(mob/living/taker) return owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker) && additional_taker_check(taker) /** @@ -287,7 +287,7 @@ * Returns `TRUE` if the taker is valid as a target for the offering based on these * additional checks. */ -/datum/status_effect/offering/proc/additional_taker_check(mob/living/carbon/taker) +/datum/status_effect/offering/proc/additional_taker_check(mob/living/taker) return taker.can_hold_items() /** @@ -296,7 +296,7 @@ */ /datum/status_effect/offering/no_item_received -/datum/status_effect/offering/no_item_received/additional_taker_check(mob/living/carbon/taker) +/datum/status_effect/offering/no_item_received/additional_taker_check(mob/living/taker) return taker.usable_hands > 0 /** @@ -307,23 +307,23 @@ */ /datum/status_effect/offering/no_item_received/needs_resting -/datum/status_effect/offering/no_item_received/needs_resting/additional_taker_check(mob/living/carbon/taker) +/datum/status_effect/offering/no_item_received/needs_resting/additional_taker_check(mob/living/taker) return taker.body_position == LYING_DOWN -/datum/status_effect/offering/no_item_received/needs_resting/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override, mob/living/carbon/offered) +/datum/status_effect/offering/no_item_received/needs_resting/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override, mob/living/offered) . = ..() RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(check_owner_standing)) -/datum/status_effect/offering/no_item_received/needs_resting/register_candidate(mob/living/carbon/possible_candidate) +/datum/status_effect/offering/no_item_received/needs_resting/register_candidate(mob/living/possible_candidate) . = ..() RegisterSignal(possible_candidate, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(check_candidate_resting)) -/datum/status_effect/offering/no_item_received/needs_resting/remove_candidate(mob/living/carbon/removed_candidate) +/datum/status_effect/offering/no_item_received/needs_resting/remove_candidate(mob/living/removed_candidate) UnregisterSignal(removed_candidate, COMSIG_LIVING_SET_BODY_POSITION) return ..() /// Simple signal handler that ensures that, if the owner stops standing, the offer no longer stands either! -/datum/status_effect/offering/no_item_received/needs_resting/proc/check_owner_standing(mob/living/carbon/owner) +/datum/status_effect/offering/no_item_received/needs_resting/proc/check_owner_standing(mob/living/owner) if(src.owner.body_position == STANDING_UP) return @@ -331,7 +331,7 @@ qdel(src) /// Simple signal handler that ensures that, should a candidate now be standing up, the offer won't be standing for them anymore! -/datum/status_effect/offering/no_item_received/needs_resting/proc/check_candidate_resting(mob/living/carbon/candidate) +/datum/status_effect/offering/no_item_received/needs_resting/proc/check_candidate_resting(mob/living/candidate) SIGNAL_HANDLER if(candidate.body_position == LYING_DOWN) diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm index d9034ac5687..a39b43643e8 100644 --- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm @@ -113,11 +113,11 @@ /obj/item/organ/tongue/rat/on_mob_insert(mob/living/carbon/tongue_owner, special, movement_flags) . = ..() - RegisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN, PROC_REF(its_on_the_mouse)) + RegisterSignal(tongue_owner, COMSIG_LIVING_ITEM_GIVEN, PROC_REF(its_on_the_mouse)) /obj/item/organ/tongue/rat/on_mob_remove(mob/living/carbon/tongue_owner) . = ..() - UnregisterSignal(tongue_owner, COMSIG_CARBON_ITEM_GIVEN) + UnregisterSignal(tongue_owner, COMSIG_LIVING_ITEM_GIVEN) /obj/item/organ/tongue/rat/proc/on_item_given(mob/living/carbon/offerer, mob/living/taker, obj/item/given) SIGNAL_HANDLER diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c4be7baa14f..6548001e09c 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1482,28 +1482,35 @@ stack_trace("Undefined handle_openspace_click() behaviour. Ascertain the openspace_item_click_handler element has been attached to the right item and that its proc override doesn't call parent.") /** - * * An interrupt for offering an item to other people, called mainly from [/mob/living/carbon/proc/give], in case you want to run your own offer behavior instead. + * * An interrupt for offering an item to other people, called mainly from [/mob/living/proc/give], in case you want to run your own offer behavior instead. * * * Return TRUE if you want to interrupt the offer. * * * Arguments: - * * offerer - The person offering the item. - * * offered - The person being offered the item. + * * offerer - The living mob offering the item. + * * offered - The living mob being offered the item. */ -/obj/item/proc/on_offered(mob/living/carbon/offerer, mob/living/carbon/offered) +/obj/item/proc/on_offered(mob/living/offerer, mob/living/offered) + if(!offered) // item has just been offered to anyone around + if(!(HAS_TRAIT(offerer, TRAIT_CAN_HOLD_ITEMS))) + return TRUE + else if(!(HAS_TRAIT(offerer, TRAIT_CAN_HOLD_ITEMS) && HAS_TRAIT(offered, TRAIT_CAN_HOLD_ITEMS))) + return TRUE // both must be able to hold items for this to make sense if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT) return TRUE /** - * * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/carbon/proc/take], in case you want to run your own take behavior instead. + * * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/proc/take], in case you want to run your own take behavior instead. * * * Return TRUE if you want to interrupt the taking. * * * Arguments: - * * offerer - the person offering the item - * * taker - the person trying to accept the offer + * * offerer - the living mob offering the item + * * taker - the living mob trying to accept the offer */ -/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) +/obj/item/proc/on_offer_taken(mob/living/offerer, mob/living/taker) + if(!(HAS_TRAIT(offerer, TRAIT_CAN_HOLD_ITEMS) && HAS_TRAIT(taker, TRAIT_CAN_HOLD_ITEMS))) + return TRUE // both must be able to hold items for this to make sense if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT) return TRUE diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index 0e9ea7a28c5..1f6e888394c 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -341,7 +341,7 @@ // Successful takes will qdel our hand after /obj/item/hand_item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) . = ..() - if(!.) + if(.) return qdel(src) @@ -408,7 +408,11 @@ /obj/item/hand_item/hand/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) - . = TRUE + . = ..() + if(!offerer || !taker) + return TRUE // this doesn't make sense unless both are carbons + if(.) + return if(taker.body_position == LYING_DOWN) taker.help_shake_act(offerer) @@ -518,6 +522,10 @@ return TRUE /obj/item/hand_item/kisser/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker) + . = ..() + if(.) + return + var/obj/projectile/blown_kiss = new kiss_type(get_turf(offerer)) offerer.visible_message("[offerer] gives [taker] \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!!", span_notice("You give [taker] \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!"), ignored_mobs = taker) to_chat(taker, span_nicegreen("[offerer] gives you \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!")) diff --git a/code/modules/basketball/basketball.dm b/code/modules/basketball/basketball.dm index 919d26daeee..274a9602546 100644 --- a/code/modules/basketball/basketball.dm +++ b/code/modules/basketball/basketball.dm @@ -56,7 +56,7 @@ // unlike on_equip, this signal is triggered after the ball is removed from hands // so we can just use is_holding_item_of_type() proc to check for multiple balls if(!wielder.is_holding_item_of_type(/obj/item/toy/basketball)) - UnregisterSignal(wielder, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_EMOTED("spin"), COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOB_THROW)) + UnregisterSignal(wielder, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_EMOTED("spin"), COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN)) wielder = null diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm index dd8805fbf8c..6fb3c5ea9af 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm @@ -57,7 +57,7 @@ . = ..() add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_CHUNKYFINGERS), ROUNDSTART_TRAIT) AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE) - AddElement(/datum/element/dextrous) + AddElement(/datum/element/dextrous, can_throw = TRUE) AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT) AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = gorilla_food) AddComponent( diff --git a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm index fb6fa276d98..3abafd5be1b 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm @@ -16,7 +16,7 @@ /mob/living/basic/guardian/dextrous/Initialize(mapload, datum/guardian_fluff/theme) . = ..() add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP), ROUNDSTART_TRAIT) - AddElement(/datum/element/dextrous, hud_type = hud_type) + AddElement(/datum/element/dextrous, hud_type = hud_type, can_throw = TRUE) AddComponent(/datum/component/personal_crafting) AddComponent(/datum/component/basic_inhands) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 5dd394e2439..c4e40c78ad2 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -8,6 +8,7 @@ GLOB.carbon_list += src ADD_TRAIT(src, TRAIT_CAN_HOLD_ITEMS, INNATE_TRAIT) // Carbons are assumed to be innately capable of having arms, we check their arms count instead + ADD_TRAIT(src, TRAIT_CAN_THROW_ITEMS, INNATE_TRAIT) // same here breathing_loop = new(src, _direct = TRUE) /mob/living/carbon/Destroy() @@ -39,11 +40,6 @@ return ITEM_INTERACT_SUCCESS return . -/mob/living/carbon/click_ctrl_shift(mob/user) - if(iscarbon(user)) - var/mob/living/carbon/carbon_user = user - carbon_user.give(src) - /mob/living/carbon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) . = ..() var/hurt = TRUE @@ -105,123 +101,6 @@ if(oof_noise) playsound(src,'sound/items/weapons/punch1.ogg',50,TRUE) -//Throwing stuff -/mob/living/carbon/proc/toggle_throw_mode() - if(stat) - return - if(throw_mode) - throw_mode_off(THROW_MODE_TOGGLE) - else - throw_mode_on(THROW_MODE_TOGGLE) - - -/mob/living/carbon/proc/throw_mode_off(method) - if(throw_mode > method) //A toggle doesnt affect a hold - return - throw_mode = THROW_MODE_DISABLED - if(hud_used) - hud_used.throw_icon.icon_state = "act_throw" - SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode) - - -/mob/living/carbon/proc/throw_mode_on(mode = THROW_MODE_TOGGLE) - throw_mode = mode - if(hud_used) - hud_used.throw_icon.icon_state = "act_throw_on" - SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode) - -/mob/proc/throw_item(atom/target) - SEND_SIGNAL(src, COMSIG_MOB_THROW, target) - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CARBON_THROW_THING, src, target) - return TRUE - -/mob/living/carbon/throw_item(atom/target) - . = ..() - throw_mode_off(THROW_MODE_TOGGLE) - if(!target || !isturf(loc)) - return FALSE - if(istype(target, /atom/movable/screen)) - return FALSE - var/atom/movable/thrown_thing - var/obj/item/held_item = get_active_held_item() - var/verb_text = pick("throw", "toss", "hurl", "chuck", "fling") - if(prob(0.5)) - verb_text = "yeet" - var/neckgrab_throw = FALSE // we can't check for if it's a neckgrab throw when totaling up power_throw since we've already stopped pulling them by then, so get it early - var/frequency_number = 1 //We assign a default frequency number for the sound of the throw. - if(!held_item) - if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE) - var/mob/living/throwable_mob = pulling - if(!throwable_mob.buckled) - thrown_thing = throwable_mob - if(grab_state >= GRAB_NECK) - neckgrab_throw = TRUE - stop_pulling() - if(HAS_TRAIT(src, TRAIT_PACIFISM) || HAS_TRAIT(src, TRAIT_NO_THROWING)) - to_chat(src, span_notice("You gently let go of [throwable_mob].")) - return FALSE - else - thrown_thing = held_item.on_thrown(src, target) - if(!thrown_thing) - return FALSE - if(isliving(thrown_thing)) - var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors - var/turf/end_T = get_turf(target) - if(start_T && end_T) - log_combat(src, thrown_thing, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") - var/power_throw = 0 - var/extra_throw_range = HAS_TRAIT(src, TRAIT_THROWINGARM) ? 2 : 0 - - var/obj/item/organ/cyberimp/chest/spine/potential_spine = get_organ_slot(ORGAN_SLOT_SPINE) - if(istype(potential_spine)) - power_throw += potential_spine.added_throw_speed - extra_throw_range += potential_spine.added_throw_range - - if(HAS_TRAIT(src, TRAIT_HULK)) - power_throw++ - if(HAS_TRAIT(src, TRAIT_DWARF)) - power_throw-- - if(HAS_TRAIT(thrown_thing, TRAIT_DWARF)) - power_throw++ - //SKYRAT EDIT ADDITION - if(HAS_TRAIT(src, TRAIT_OVERSIZED)) - power_throw++ - if(HAS_TRAIT(thrown_thing, TRAIT_OVERSIZED)) - power_throw-- - //SKYRAT EDIT END - if(neckgrab_throw) - power_throw++ - if(HAS_TRAIT(src, TRAIT_TOSS_GUN_HARD) && isgun(thrown_thing)) - power_throw++ - if(isitem(thrown_thing)) - var/obj/item/thrown_item = thrown_thing - frequency_number = 1-(thrown_item.w_class-3)/8 //At normal weight, the frequency is at 1. For tiny, it is 1.25. For huge, it is 0.75. - if(thrown_item.throw_verb) - verb_text = thrown_item.throw_verb - do_attack_animation(target, no_effect = 1) - var/sound/throwsound = 'sound/items/weapons/throw.ogg' - var/power_throw_text = "." - if(power_throw > 0) //If we have anything that boosts our throw power like hulk, we use the rougher heavier variant. - throwsound = 'sound/items/weapons/throwhard.ogg' - power_throw_text = " really hard!" - if(power_throw < 0) //if we have anything that weakens our throw power like dward, we use a slower variant. - throwsound = 'sound/items/weapons/throwsoft.ogg' - power_throw_text = " flimsily." - frequency_number = frequency_number + (rand(-5,5)/100); //Adds a bit of randomness in the frequency to not sound exactly the same. - //The volume of the sound takes the minimum between the distance thrown or the max range an item, but no more than 50. Short throws are quieter. A fast throwing speed also makes the noise sharper. - playsound(src, throwsound, clamp(8*min(get_dist(loc,target),thrown_thing.throw_range), 10, 50), vary = TRUE, extrarange = -1, frequency = frequency_number) - visible_message(span_danger("[src] [verb_text][plural_s(verb_text)] [thrown_thing][power_throw_text]"), \ - span_danger("You [verb_text] [thrown_thing][power_throw_text]")) - log_message("has thrown [thrown_thing] [power_throw_text]", LOG_ATTACK) - - var/drift_force = max(0.5 NEWTONS, 1 NEWTONS + power_throw) - if (isitem(thrown_thing)) - var/obj/item/thrown_item = thrown_thing - drift_force *= WEIGHT_TO_NEWTONS(thrown_item.w_class) - - newtonian_move(get_angle(target, src), drift_force = drift_force) - thrown_thing.safe_throw_at(target, thrown_thing.throw_range + extra_throw_range, max(1,thrown_thing.throw_speed + power_throw), src, null, null, null, move_force) - /mob/living/carbon/proc/canBeHandcuffed() return FALSE diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index 546c7fa35d6..f21aad08c28 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -417,95 +417,6 @@ var/index = get_held_index_of_item(I) return index && hand_bodyparts[index] -/** - * Proc called when offering an item to another player - * - * This handles creating an alert and adding an overlay to it - */ -/mob/living/carbon/proc/give(mob/living/carbon/offered) - if(has_status_effect(/datum/status_effect/offering)) - to_chat(src, span_warning("You're already offering something!")) - return - - if(IS_DEAD_OR_INCAP(src)) - to_chat(src, span_warning("You're unable to offer anything in your current state!")) - return - - var/obj/item/offered_item = get_active_held_item() - // if it's an abstract item, should consider it to be non-existent (unless it's a HAND_ITEM, which means it's an obj/item that is just a representation of our hand) - if(!offered_item || ((offered_item.item_flags & ABSTRACT) && !(offered_item.item_flags & HAND_ITEM))) - to_chat(src, span_warning("You're not holding anything to offer!")) - return - - if(offered) - if(offered == src) - if(!swap_hand(get_inactive_hand_index())) //have to swap hands first to take something - to_chat(src, span_warning("You try to take [offered_item] from yourself, but fail.")) - return - if(!put_in_active_hand(offered_item)) - to_chat(src, span_warning("You try to take [offered_item] from yourself, but fail.")) - return - else - to_chat(src, span_notice("You take [offered_item] from yourself.")) - return - - if(IS_DEAD_OR_INCAP(offered)) - to_chat(src, span_warning("[offered.p_Theyre()] unable to take anything in [offered.p_their()] current state!")) - return - - if(!CanReach(offered)) - to_chat(src, span_warning("You have to be beside [offered.p_them()]!")) - return - else - if(!(locate(/mob/living/carbon) in orange(1, src))) - to_chat(src, span_warning("There's nobody beside you to take it!")) - return - - if(offered_item.on_offered(src)) // see if the item interrupts with its own behavior - return - - balloon_alert_to_viewers("offers something") - visible_message(span_notice("[src] is offering [offered ? "[offered] " : ""][offered_item]."), \ - span_notice("You offer [offered ? "[offered] " : ""][offered_item]."), null, 2) - - apply_status_effect(/datum/status_effect/offering, offered_item, null, offered) - -/** - * Proc called when the player clicks the give alert - * - * Handles checking if the player taking the item has open slots and is in range of the offerer - * Also deals with the actual transferring of the item to the players hands - * Arguments: - * * offerer - The person giving the original item - * * I - The item being given by the offerer - */ -/mob/living/carbon/proc/take(mob/living/carbon/offerer, obj/item/I) - clear_alert("[offerer]") - if(IS_DEAD_OR_INCAP(src)) - to_chat(src, span_warning("You're unable to take anything in your current state!")) - return - if(get_dist(src, offerer) > 1) - to_chat(src, span_warning("[offerer] is out of range!")) - return - if(!I || offerer.get_active_held_item() != I) - to_chat(src, span_warning("[offerer] is no longer holding the item they were offering!")) - return - if(!get_empty_held_indexes()) - to_chat(src, span_warning("You have no empty hands!")) - return - - if(I.on_offer_taken(offerer, src)) // see if the item has special behavior for being accepted - return - - if(!offerer.temporarilyRemoveItemFromInventory(I)) - visible_message(span_notice("[offerer] tries to hand over [I] but it's stuck to them....")) - return - - visible_message(span_notice("[src] takes [I] from [offerer]."), \ - span_notice("You take [I] from [offerer].")) - I.do_pickup_animation(src, offerer) - put_in_hands(I) - ///Returns a list of all body_zones covered by clothing /mob/living/carbon/proc/get_covered_body_zones() RETURN_TYPE(/list) diff --git a/code/modules/mob/living/living_item_handling.dm b/code/modules/mob/living/living_item_handling.dm new file mode 100644 index 00000000000..60d683887b1 --- /dev/null +++ b/code/modules/mob/living/living_item_handling.dm @@ -0,0 +1,227 @@ +// Throwing stuff +/mob/living/proc/toggle_throw_mode() + if(stat) + return + if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) + return + if(throw_mode) + throw_mode_off(THROW_MODE_TOGGLE) + else + throw_mode_on(THROW_MODE_TOGGLE) + + +/mob/living/proc/throw_mode_off(method) + if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) + return + if(throw_mode > method) //A toggle doesnt affect a hold + return + throw_mode = THROW_MODE_DISABLED + if(hud_used) + hud_used.throw_icon.icon_state = "act_throw" + SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode) + + +/mob/living/proc/throw_mode_on(mode = THROW_MODE_TOGGLE) + if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) + return + throw_mode = mode + if(hud_used) + hud_used.throw_icon.icon_state = "act_throw_on" + SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode) + +/mob/proc/throw_item(atom/target) + if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) + return FALSE + SEND_SIGNAL(src, COMSIG_MOB_THROW, target) + return TRUE + +/mob/living/throw_item(atom/target) + . = ..() + throw_mode_off(THROW_MODE_TOGGLE) + if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) + stack_trace("[src] tried to throw [target], but they shouldn't be able to throw things") + return FALSE + if(!target || !isturf(loc)) + return FALSE + if(istype(target, /atom/movable/screen)) + return FALSE + var/atom/movable/thrown_thing + var/obj/item/held_item = get_active_held_item() + var/verb_text = pick("throw", "toss", "hurl", "chuck", "fling") + if(prob(0.5)) + verb_text = "yeet" + var/neckgrab_throw = FALSE // we can't check for if it's a neckgrab throw when totaling up power_throw since we've already stopped pulling them by then, so get it early + var/frequency_number = 1 //We assign a default frequency number for the sound of the throw. + if(!held_item) + if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE) + var/mob/living/throwable_mob = pulling + if(!throwable_mob.buckled) + thrown_thing = throwable_mob + if(grab_state >= GRAB_NECK) + neckgrab_throw = TRUE + stop_pulling() + if(HAS_TRAIT(src, TRAIT_PACIFISM) || HAS_TRAIT(src, TRAIT_NO_THROWING)) + to_chat(src, span_notice("You gently let go of [throwable_mob].")) + return FALSE + else + thrown_thing = held_item.on_thrown(src, target) + if(!thrown_thing) + return FALSE + if(isliving(thrown_thing)) + var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors + var/turf/end_T = get_turf(target) + if(start_T && end_T) + log_combat(src, thrown_thing, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") + var/power_throw = 0 + var/extra_throw_range = HAS_TRAIT(src, TRAIT_THROWINGARM) ? 2 : 0 + + var/obj/item/organ/cyberimp/chest/spine/potential_spine = get_organ_slot(ORGAN_SLOT_SPINE) + if(istype(potential_spine)) + power_throw += potential_spine.added_throw_speed + extra_throw_range += potential_spine.added_throw_range + + if(HAS_TRAIT(src, TRAIT_HULK)) + power_throw++ + if(HAS_TRAIT(src, TRAIT_DWARF)) + power_throw-- + if(HAS_TRAIT(thrown_thing, TRAIT_DWARF)) + power_throw++ + //SKYRAT EDIT ADDITION + if(HAS_TRAIT(src, TRAIT_OVERSIZED)) + power_throw++ + if(HAS_TRAIT(thrown_thing, TRAIT_OVERSIZED)) + power_throw-- + //SKYRAT EDIT END + if(neckgrab_throw) + power_throw++ + if(HAS_TRAIT(src, TRAIT_TOSS_GUN_HARD) && isgun(thrown_thing)) + power_throw++ + if(isitem(thrown_thing)) + var/obj/item/thrown_item = thrown_thing + frequency_number = 1-(thrown_item.w_class-3)/8 //At normal weight, the frequency is at 1. For tiny, it is 1.25. For huge, it is 0.75. + if(thrown_item.throw_verb) + verb_text = thrown_item.throw_verb + do_attack_animation(target, no_effect = 1) + var/sound/throwsound = 'sound/items/weapons/throw.ogg' + var/power_throw_text = "." + if(power_throw > 0) //If we have anything that boosts our throw power like hulk, we use the rougher heavier variant. + throwsound = 'sound/items/weapons/throwhard.ogg' + power_throw_text = " really hard!" + if(power_throw < 0) //if we have anything that weakens our throw power like dward, we use a slower variant. + throwsound = 'sound/items/weapons/throwsoft.ogg' + power_throw_text = " flimsily." + frequency_number = frequency_number + (rand(-5,5)/100); //Adds a bit of randomness in the frequency to not sound exactly the same. + //The volume of the sound takes the minimum between the distance thrown or the max range an item, but no more than 50. Short throws are quieter. A fast throwing speed also makes the noise sharper. + playsound(src, throwsound, clamp(8*min(get_dist(loc,target),thrown_thing.throw_range), 10, 50), vary = TRUE, extrarange = -1, frequency = frequency_number) + visible_message(span_danger("[src] [verb_text][plural_s(verb_text)] [thrown_thing][power_throw_text]"), \ + span_danger("You [verb_text] [thrown_thing][power_throw_text]")) + log_message("has thrown [thrown_thing] [power_throw_text]", LOG_ATTACK) + + var/drift_force = max(0.5 NEWTONS, 1 NEWTONS + power_throw) + if (isitem(thrown_thing)) + var/obj/item/thrown_item = thrown_thing + drift_force *= WEIGHT_TO_NEWTONS(thrown_item.w_class) + + newtonian_move(get_angle(target, src), drift_force = drift_force) + thrown_thing.safe_throw_at(target, thrown_thing.throw_range + extra_throw_range, max(1,thrown_thing.throw_speed + power_throw), src, null, null, null, move_force) + +// Giving stuff +/** + * Proc called when offering an item to another player + * + * This handles creating an alert and adding an overlay to it + * Arguments: + * * offered - The player being offered the item (optional, if null the offer is to everyone around) + */ +/mob/living/proc/give(mob/living/offered) + if(has_status_effect(/datum/status_effect/offering)) + to_chat(src, span_warning("You're already offering something!")) + return + + if(IS_DEAD_OR_INCAP(src)) + to_chat(src, span_warning("You're unable to offer anything in your current state!")) + return + + var/obj/item/offered_item = get_active_held_item() + // if it's an abstract item, should consider it to be non-existent (unless it's a HAND_ITEM, which means it's an obj/item that is just a representation of our hand) + if(!offered_item || ((offered_item.item_flags & ABSTRACT) && !(offered_item.item_flags & HAND_ITEM))) + to_chat(src, span_warning("You're not holding anything to offer!")) + return + + if(offered) + if(offered == src) + if(!swap_hand(get_inactive_hand_index())) //have to swap hands first to take something + to_chat(src, span_warning("You try to take [offered_item] from yourself, but fail.")) + return + if(!put_in_active_hand(offered_item)) + to_chat(src, span_warning("You try to take [offered_item] from yourself, but fail.")) + return + else + to_chat(src, span_notice("You take [offered_item] from yourself.")) + return + + if(IS_DEAD_OR_INCAP(offered)) + to_chat(src, span_warning("[offered.p_Theyre()] unable to take anything in [offered.p_their()] current state!")) + return + + if(!CanReach(offered)) + to_chat(src, span_warning("You have to be beside [offered.p_them()]!")) + return + + if(!HAS_TRAIT(offered, TRAIT_CAN_HOLD_ITEMS)) + to_chat(src, span_warning("[offered.p_They()] can't hold anything you offer!")) + return + else if(!(locate(/mob/living) in orange(1, src))) + to_chat(src, span_warning("There's nobody beside you to take it!")) + return + + if(offered_item.on_offered(src)) // see if the item interrupts with its own behavior + return + + balloon_alert_to_viewers("offers something") + visible_message(span_notice("[src] is offering [offered ? "[offered] " : ""][offered_item]."), \ + span_notice("You offer [offered ? "[offered] " : ""][offered_item]."), null, 2) + + apply_status_effect(/datum/status_effect/offering, offered_item, null, offered) + +/** + * Proc called when the player clicks the give alert + * + * Handles checking if the player taking the item has open slots and is in range of the offerer + * Also deals with the actual transferring of the item to the players hands + * Arguments: + * * offerer - The living mob giving the original item + * * offered_item - The item being given by the offerer + */ +/mob/living/proc/take(mob/living/offerer, obj/item/offered_item) + clear_alert("[offerer]") + if(IS_DEAD_OR_INCAP(src)) + to_chat(src, span_warning("You're unable to take anything in your current state!")) + return + if(get_dist(src, offerer) > 1) + to_chat(src, span_warning("[offerer] is out of range!")) + return + if(!offered_item || offerer.get_active_held_item() != offered_item) + to_chat(src, span_warning("[offerer] is no longer holding the item they were offering!")) + return + if(!get_empty_held_indexes()) + to_chat(src, span_warning("You have no empty hands!")) + return + + if(offered_item.on_offer_taken(offerer, src)) // see if the item has special behavior for being accepted + return + + if(!offerer.temporarilyRemoveItemFromInventory(offered_item)) + visible_message(span_notice("[offerer] tries to hand over [offered_item] but it's stuck to them....")) + return + + visible_message(span_notice("[src] takes [offered_item] from [offerer]."), \ + span_notice("You take [offered_item] from [offerer].")) + offered_item.do_pickup_animation(src, offerer) + put_in_hands(offered_item) + + +/mob/living/click_ctrl_shift(mob/user) + if(HAS_TRAIT(src, TRAIT_CAN_HOLD_ITEMS)) + var/mob/living/living_user = user + living_user.give(src) diff --git a/tgstation.dme b/tgstation.dme index 951b52b3531..67afb96f3a6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1835,7 +1835,6 @@ #include "code\datums\keybinding\_keybindings.dm" #include "code\datums\keybinding\admin.dm" #include "code\datums\keybinding\artificial_intelligence.dm" -#include "code\datums\keybinding\carbon.dm" #include "code\datums\keybinding\client.dm" #include "code\datums\keybinding\communication.dm" #include "code\datums\keybinding\emote.dm" @@ -5083,6 +5082,7 @@ #include "code\modules\mob\living\living_defense.dm" #include "code\modules\mob\living\living_defines.dm" #include "code\modules\mob\living\living_fov.dm" +#include "code\modules\mob\living\living_item_handling.dm" #include "code\modules\mob\living\living_movement.dm" #include "code\modules\mob\living\living_say.dm" #include "code\modules\mob\living\living_update_icons.dm"