diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index e7213e2e89..f6333041f8 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -197,6 +197,11 @@ ///from base of atom/MouseDrop_T: (/atom/from, /mob/user) #define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" +///from base of atom/MouseDrop_T: do_after(mob/user, delay, atom/target, needhand, progress, incapacitation_flags, ignore_movement, max_distance, exclusive) +#define COMSIG_DO_AFTER_BEGAN "do_after_began" +///from base of atom/MouseDrop_T: do_after(mob/user, delay, atom/target, needhand, progress, incapacitation_flags, ignore_movement, max_distance, exclusive) +#define COMSIG_DO_AFTER_ENDED "do_after_ended" + // /area signals ///from base of area/Entered(): (atom/movable/M) diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 535cc9b58a..c0a447091f 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -294,6 +294,8 @@ Proc for attack log creation, because really why not if (progress) progbar = new(user, delay, target) + SEND_SIGNAL(user, COMSIG_DO_AFTER_BEGAN) + var/endtime = world.time + delay var/starttime = world.time @@ -346,6 +348,7 @@ Proc for attack log creation, because really why not if(progbar) qdel(progbar) + SEND_SIGNAL(user, COMSIG_DO_AFTER_ENDED) /atom/proc/living_mobs(var/range = world.view, var/count_held = FALSE) //CHOMPEdit Start var/list/viewers = oviewers(src,range) diff --git a/code/_macros.dm b/code/_macros.dm index aa1859d79a..632b0eeeb3 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -47,5 +47,6 @@ #define JOINTEXT(X) jointext(X, null) -#define isbelly(A) istype(A, /obj/belly) +#define isbelly(A) istype(A, /obj/belly) +#define isgripperpocket(A) istype(A, /obj/item/storage/internal/gripper) #define iscapturecrystal(A) istype(A, /obj/item/capture_crystal) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index f07c7964af..6038b3920c 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -121,11 +121,12 @@ else if(istype(I,/obj/item/gripper)) //Behold, Grippers and their horribleness. If ..() is called by any computers' attackby() now or in the future, this should let grippers work with them appropriately. var/obj/item/gripper/B = I //B, for Borg. - if(!B.wrapped) + var/obj/item/wrapped = B.get_current_pocket() + if(!wrapped) to_chat(user, "\The [B] is not holding anything.") return else - var/B_held = B.wrapped + var/B_held = wrapped to_chat(user, "You use \the [B] to use \the [B_held] with \the [src].") playsound(src, clicksound, 100, 1, 0) return diff --git a/code/modules/food/kitchen/cooking_machines/_appliance.dm b/code/modules/food/kitchen/cooking_machines/_appliance.dm index feec4641f6..5faab15260 100644 --- a/code/modules/food/kitchen/cooking_machines/_appliance.dm +++ b/code/modules/food/kitchen/cooking_machines/_appliance.dm @@ -271,23 +271,23 @@ if(istype(I, /obj/item/gripper)) var/obj/item/gripper/GR = I - var/obj/item/Wrap = GR.wrapped - if(Wrap) - Wrap.loc = get_turf(src) - var/result = can_insert(Wrap, user) + var/obj/item/wrap = GR.get_current_pocket() + if(wrap) + wrap.loc = get_turf(src) + var/result = can_insert(wrap, user) if(!result) - Wrap.forceMove(GR) + wrap.forceMove(GR) if(!(default_deconstruction_screwdriver(user, I))) default_part_replacement(user, I) return - if(QDELETED(GR.wrapped)) - GR.wrapped = null + if(QDELETED(wrap)) + GR.WR = null - if(GR?.wrapped.loc != src) + if(wrap.loc != src) GR.drop_item_nm() - ToCook = Wrap + ToCook = wrap else attack_hand(user) return diff --git a/code/modules/food/kitchen/cooking_machines/container.dm b/code/modules/food/kitchen/cooking_machines/container.dm index 238514fe2c..82a0b2a1cb 100644 --- a/code/modules/food/kitchen/cooking_machines/container.dm +++ b/code/modules/food/kitchen/cooking_machines/container.dm @@ -39,14 +39,15 @@ /obj/item/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) if(istype(I, /obj/item/gripper)) var/obj/item/gripper/GR = I - if(GR.wrapped) - GR.wrapped.forceMove(get_turf(src)) - attackby(GR.wrapped, user) - if(QDELETED(GR.wrapped)) - GR.wrapped = null + var/obj/item/wrapped = GR.get_current_pocket() + if(wrapped) + wrapped.forceMove(get_turf(src)) + attackby(wrapped, user) + if(QDELETED(wrapped)) + wrapped = null - if(GR?.wrapped.loc != src) - GR.wrapped = null + else if(wrapped.loc != src) + wrapped = null return diff --git a/code/modules/food/kitchen/smartfridge/smartfridge.dm b/code/modules/food/kitchen/smartfridge/smartfridge.dm index 2df984cc58..c3f509cdb7 100644 --- a/code/modules/food/kitchen/smartfridge/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge/smartfridge.dm @@ -155,12 +155,12 @@ else if(istype(O, /obj/item/gripper)) // Grippers. ~Mechoid. var/obj/item/gripper/B = O //B, for Borg. - if(!B.wrapped) + var/obj/item/wrapped = B.get_current_pocket() + if(!wrapped) to_chat(user, span_filter_notice("\The [B] is not holding anything.")) return else - var/B_held = B.wrapped - to_chat(user, span_filter_notice("You use \the [B] to put \the [B_held] into \the [src].")) + to_chat(user, span_filter_notice("You use \the [B] to put \the [wrapped] into \the [src].")) return else diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index 4e8aa9bd5c..fcb68f3e08 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -72,7 +72,8 @@ stripping = TRUE if(is_robot_module(held) && istype(held, /obj/item/gripper)) var/obj/item/gripper/G = held - if(istype(G.wrapped)) + var/obj/item/wrapped = G.get_current_pocket() + if(istype(wrapped)) stripping = FALSE else var/obj/item/holder/holder = held @@ -97,10 +98,11 @@ visible_message(span_danger("\The [user] is trying to put \a [held] on \the [src]!")) else var/obj/item/gripper/G = held - if(slot_to_strip == slot_wear_mask && istype(G.wrapped, /obj/item/grenade)) - visible_message(span_danger("\The [user] is trying to put \a [G.wrapped] in \the [src]'s mouth!")) + var/obj/item/wrapped = G.get_current_pocket() + if(slot_to_strip == slot_wear_mask && istype(wrapped, /obj/item/grenade)) + visible_message(span_danger("\The [user] is trying to put \a [wrapped] in \the [src]'s mouth!")) else - visible_message(span_danger("\The [user] is trying to put \a [G.wrapped] on \the [src]!")) + visible_message(span_danger("\The [user] is trying to put \a [wrapped] on \the [src]!")) if(!do_after(user,HUMAN_STRIP_DELAY,src)) return @@ -118,10 +120,10 @@ unEquip(target_slot) else if(is_robot_module(held) && istype(held, /obj/item/gripper)) var/obj/item/gripper/G = held - var/obj/item/wrapped = G.wrapped + var/obj/item/wrapped = G.get_current_pocket() if(istype(wrapped)) if(equip_to_slot_if_possible(wrapped, text2num(slot_to_strip), 0, 1, 1)) - G.wrapped = null + wrapped = null G.generate_icons() G.current_pocket = pick(G.pockets) else if(user.unEquip(held)) diff --git a/code/modules/mob/living/silicon/robot/robot_simple_items.dm b/code/modules/mob/living/silicon/robot/robot_simple_items.dm index 7228531b67..83a75c6d50 100644 --- a/code/modules/mob/living/silicon/robot/robot_simple_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_simple_items.dm @@ -512,7 +512,7 @@ //Has a list of items that it can hold. var/list/can_hold = list(BASIC_GRIPPER) - var/obj/item/wrapped = null // Item currently being held. //Convert to weak-ref? + var/datum/weakref/WR = null //We resolve this to get wrapped. Use get_current_pocket when possible. var/total_pockets = 5 //How many total inventory slots we want to have in the gripper @@ -524,6 +524,10 @@ var/list/photo_images + var/gripper_in_use = FALSE + + var/mob/living/silicon/robot/our_robot + pickup_sound = 'sound/items/pickup/device.ogg' drop_sound = 'sound/items/drop/device.ogg' @@ -540,21 +544,35 @@ new_pocket.name = "Pocket [i]" pockets += new_pocket current_pocket = peek(pockets) + if(isrobot(loc.loc)) //We're in the module. + our_robot = loc.loc + else if(isrobot(loc)) //We spawned in the robot's module slots...Weird, but whatever. + our_robot = loc + else //We were in neither. Let's qdel ourselves. + return INITIALIZE_HINT_QDEL + RegisterSignal(our_robot, COMSIG_DO_AFTER_BEGAN, PROC_REF(begin_using)) + RegisterSignal(our_robot, COMSIG_DO_AFTER_ENDED, PROC_REF(end_using)) /obj/item/gripper/Destroy() current_pocket = null - qdel_null(wrapped) + qdel_null(WR) qdel_null(pockets) + if(our_robot) //In case we returned INITIALIZE_HINT_QDEL earlier in initalize. + UnregisterSignal(our_robot, COMSIG_DO_AFTER_BEGAN) + UnregisterSignal(our_robot, COMSIG_DO_AFTER_ENDED) + our_robot = null . = ..() /obj/item/gripper/examine(mob/user) . = ..() + var/obj/item/wrapped = get_current_pocket() if(wrapped) . += span_notice("\The [src] is holding \the [wrapped].") . += wrapped.examine(user) /obj/item/gripper/CtrlClick(mob/user) + var/obj/item/wrapped = get_current_pocket() if(wrapped && !is_in_use()) wrapped.attack_self(user) return @@ -567,13 +585,25 @@ //This is used to check if the gripper is currently being used. //If it is, we don't allow any other actions to be performed. /obj/item/gripper/proc/is_in_use() - if(wrapped && !QDELETED(wrapped)) - if(istype(wrapped.loc, /obj/item/storage/internal/gripper)) - return FALSE //We are in a gripper storage or another gripper, so we are not in use. + if(gripper_in_use) return TRUE - else if(QDELETED(wrapped)) //Failsafe. - wrapped = null - return FALSE //Default to 'we are not in use' + return FALSE + +///Stops the gripper from being used multiple times when we're performing a do_after +/obj/item/gripper/proc/begin_using() + gripper_in_use = TRUE + +///Allows use of the gripper (and clears the weakref) after do_after is completed. Clears the weakref if the wrapped item is no longer in our borg's contents (items get moved into the borgs contents when using the gripper) +/obj/item/gripper/proc/end_using() + gripper_in_use = FALSE + if(!WR) + return + var/obj/item/wrapped = get_current_pocket() + //Checks two things: + //Is our wrapped object currently in our borg still? + //AND Is it not a gripper pocket? If not, reset WR. + if(wrapped.loc != loc && !isgripperpocket(wrapped.loc)) + WR = null //This is the code that updates our pockets and decides if they should have icons or not. //This should be called every time we use the gripper and our wrapped item is used up. @@ -584,7 +614,7 @@ photo_images = list() - for(var/obj/item/storage/internal/pocket_to_check in pockets) + for(var/obj/item/storage/internal/gripper/pocket_to_check in pockets) if(!LAZYLEN(pocket_to_check.contents)) pockets_by_name[pocket_to_check.name] = pocket_to_check photo_images[pocket_to_check.name] = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing") @@ -611,16 +641,16 @@ var/list/choice = list() choice = show_radial_menu(user, src, options, radius = 40, require_near = TRUE, autopick_single_option = FALSE) + var/obj/item/wrapped = get_current_pocket() if(choice) current_pocket = pockets_by_name[choice] if(!istype(current_pocket,/obj/item/storage/internal/gripper)) //The pocket we're selecting is NOT a gripper storage if(!istype(current_pocket.loc, /obj/item/storage/internal/gripper)) //We kept the radial menu opened, used the item, then selected it again. - current_pocket = pick(pockets) //Just pick a random pocket. - wrapped = null + get_open_pocket(TRUE, TRUE) //Pick the next open pocket. else - wrapped = current_pocket + WR = WEAKREF(current_pocket) else - wrapped = null + WR = null else if(wrapped) return wrapped.attack_self(user) return ..() @@ -629,27 +659,50 @@ if(is_in_use()) to_chat(user, span_danger("You are currently using the gripper on something!")) return FALSE + var/obj/item/wrapped = get_current_pocket() if(wrapped) wrapped.loc = src.loc //Place it in to the robot. var/resolved = wrapped.attackby(O, user) - if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) - wrapped = null - if(!resolved && wrapped && O) + wrapped = get_current_pocket() //We check to see if the object exists after we do attackby. + + //The object has been deleted. Select a new pocket and stop here. + if(!wrapped) + get_open_pocket(TRUE) + + //Object is not in our contents AND is not in the gripper storage still. AKA, it was moved into something or somewhere. Either way, it's not ours anymore. + else if((wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) + get_open_pocket(TRUE, TRUE) + + //We were not given a resolved, the object still exists, AND we hit something. Attack that thing with our wrapped item. + else if(!resolved && wrapped && O) O.afterattack(wrapped,user,1) - if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) // I don't know of a nicer way to do this. - wrapped = null - if(wrapped && wrapped != current_pocket) + wrapped = get_current_pocket() + //The object still exists, but is not in our contents OR back in the gripper storage. + if((wrapped && wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) + get_open_pocket(TRUE, TRUE) + else //Nothing happened to it. Just put it back into our pocket. wrapped.loc = current_pocket - else if(wrapped) - for(var/obj/item/storage/internal/gripper/our_pocket in pockets) - if(LAZYLEN(our_pocket.contents)) - continue - current_pocket = our_pocket - wrapped.loc = current_pocket return resolved return ..() +///Gets an open pocket in the gripper. +///ARGS: +///set_pocket TRUE/FALSE. If set to TRUE, will set our current_pocket to the first open pocket it finds. +///clear_wrapped TRUE/FALSE. If set to TRUE, will set WR to null. +/obj/item/gripper/proc/get_open_pocket(set_pocket = FALSE, clear_wrapped = FALSE) + var/pocket_to_select + for(var/obj/item/storage/internal/gripper/our_pocket in pockets) + if(LAZYLEN(our_pocket.contents)) + continue + pocket_to_select = our_pocket + break + if(set_pocket) + current_pocket = pocket_to_select + if(clear_wrapped) + WR = null + return pocket_to_select + /obj/item/gripper/verb/drop_gripper_item() set name = "Drop Item" @@ -659,6 +712,7 @@ drop_item() /obj/item/gripper/proc/drop_item() + var/obj/item/wrapped = get_current_pocket() if(!wrapped) to_chat(src, span_warning("You have nothing to drop!")) return @@ -666,38 +720,41 @@ to_chat(src, span_danger("You are currently using the gripper on something!")) return if((wrapped == current_pocket && !istype(wrapped.loc, /obj/item/storage/internal/gripper))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage - wrapped = null + WR = null current_pocket = pick(pockets) generate_icons() return to_chat(src.loc, span_notice("You drop \the [wrapped].")) wrapped.loc = get_turf(src) - wrapped = null + WR = null generate_icons() //update_icon() +//FORCES the item onto the ground and resets the /obj/item/gripper/proc/drop_item_nm() + var/obj/item/wrapped = get_current_pocket() if((wrapped == current_pocket && !istype(wrapped.loc, /obj/item/storage/internal/gripper))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage - wrapped = null + WR = null current_pocket = pick(pockets) return wrapped.loc = get_turf(src) - wrapped = null + WR = null /obj/item/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) if(is_in_use()) to_chat(user, span_danger("You are currently using the gripper on something!")) return FALSE + var/obj/item/wrapped = get_current_pocket() if(wrapped) //The force of the wrapped obj gets set to zero during the attack() and afterattack(). - if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage - wrapped = null //we become null + if((wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage + WR = null //we become null else wrapped.attack(M,user) M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe. - if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage - wrapped = null + if((wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage + WR = null if(wrapped) //In the event nothing happened to wrapped, go back into the gripper. wrapped.loc = current_pocket return 1 @@ -710,7 +767,7 @@ to_chat(user, span_danger("You are currently using the gripper on something!")) return var/current_pocket_full = FALSE - //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + var/obj/item/wrapped = get_current_pocket() if(wrapped == current_pocket) current_pocket_full = TRUE if(!wrapped && current_pocket) @@ -719,10 +776,10 @@ current_pocket_full = TRUE if(current_pocket && !LAZYLEN(current_pocket.contents)) //We have a pocket selected and it has no contents! This means we're an item OR we need to null our wrapper! - if(istype(current_pocket.loc,/obj/item/storage/internal/gripper) && !LAZYLEN(current_pocket.loc.contents)) //If our pocket is a gripper, AND we have no contents, wrapped = null - wrapped = null - else if(!istype(current_pocket.loc,/obj/item/storage/internal/gripper)) //If our pocket is an item and we are not in the gripper, wrapped = null - wrapped = null + if(istype(current_pocket.loc,/obj/item/storage/internal/gripper) && !LAZYLEN(current_pocket.loc.contents)) //If our pocket is a gripper, AND we have no contents, WR = null + WR = null + else if(!istype(current_pocket.loc,/obj/item/storage/internal/gripper)) //If our pocket is an item and we are not in the gripper, WR = null + WR = null if(!LAZYLEN(pockets)) //Shouldn't happen, but safety first. to_chat(user, span_danger("Your gripper has nowhere to hold \the [target].")) @@ -747,15 +804,14 @@ if(!resolved && wrapped && target) wrapped.afterattack(target,user,1) - if((QDELETED(wrapped))) //We put our wrapped thing INTO something! - wrapped = null + if(!WR) //We put our wrapped thing INTO something! current_pocket = pick(pockets) return //If we had a previous pocket and the wrapped isn't put into something, put it back in our pocket. else if((previous_pocket && wrapped.loc == user)) wrapped.loc = previous_pocket else - wrapped = null + WR = null current_pocket = pick(pockets) return else if(current_pocket_full) //Pocket is full. No grabbing more things. @@ -823,7 +879,11 @@ user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.") //HELPER PROCS +///Use this to get what the current pocket is. Returns NULL if no /obj/item/gripper/proc/get_current_pocket() //done as a proc so snowflake code can be found later down the line and consolidated. + if(!WR) + return null + var/obj/item/wrapped = WR.resolve() return wrapped /// Consolidates material stacks by searching our pockets to see if we currently have any stacks. Done in /obj/item/stack/attackby diff --git a/code/modules/reagents/machinery/grinder.dm b/code/modules/reagents/machinery/grinder.dm index e66fbf31a2..3f1312f70a 100644 --- a/code/modules/reagents/machinery/grinder.dm +++ b/code/modules/reagents/machinery/grinder.dm @@ -111,12 +111,12 @@ if(istype(O,/obj/item/gripper)) var/obj/item/gripper/B = O //B, for Borg. - if(!B.wrapped) + var/obj/item/wrapped = B.get_current_pocket() + if(!wrapped) to_chat(user, "\The [B] is not holding anything.") return 0 else - var/B_held = B.wrapped - to_chat(user, "You use \the [B] to load \the [src] with \the [B_held].") + to_chat(user, "You use \the [B] to load \the [src] with \the [wrapped].") return 0 diff --git a/code/modules/refinery/core/industrial_reagent_grinder.dm b/code/modules/refinery/core/industrial_reagent_grinder.dm index 07f864d731..09087c51d5 100644 --- a/code/modules/refinery/core/industrial_reagent_grinder.dm +++ b/code/modules/refinery/core/industrial_reagent_grinder.dm @@ -62,11 +62,12 @@ // Borgos! if(istype(O,/obj/item/gripper)) var/obj/item/gripper/B = O //B, for Borg. - if(!B.wrapped) + var/obj/item/wrapped = B.get_current_pocket() + if(!wrapped) to_chat(user, "\The [B] is not holding anything.") return FALSE else - var/B_held = B.wrapped + var/B_held = wrapped to_chat(user, "You use \the [B] to load \the [src] with \the [B_held].") return FALSE diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 4b15c9c91c..149a9a10a8 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -134,9 +134,10 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if(istype(user,/mob/living/silicon/robot)) if(istype(tool, /obj/item/gripper)) - var/obj/item/gripper/Gripper = tool - if(Gripper.wrapped) - tool = Gripper.wrapped + var/obj/item/gripper/gripper = tool + var/obj/item/wrapped = gripper.get_current_pocket() + if(wrapped) + tool = wrapped else return else @@ -153,7 +154,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if(isrobot(user) && istype(tool, /obj/item/gripper)) var/obj/item/gripper/G = tool - tool = G.wrapped + tool = G.get_current_pocket() user.visible_message(span_notice("[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity."), \ span_notice("You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.") ) //Nobody will probably ever see this, but I made these two blue. ~CK user.balloon_alert_visible("starts putting \the [tool] inside [target]'s [get_cavity(affected)]", "putting \the [tool] inside \the [get_cavity(affected)]") @@ -164,8 +165,8 @@ var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) if(isrobot(user) && istype(tool, /obj/item/gripper)) var/obj/item/gripper/G = tool - tool = G.wrapped - G.drop_item() + tool = G.get_current_pocket() + G.drop_item_nm() else user.drop_item() user.visible_message(span_notice("[user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity."), \