[MIRROR] Gripper update (#11330)

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-08-06 15:01:36 -07:00
committed by GitHub
parent 911234df75
commit 4239f5ab7c
12 changed files with 155 additions and 80 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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."), \