cleans up robot gripper code (#19153)

* cleans up robot gripper code

* try to select next

* .

* .

* wtf

* .

* .

* ye that

* dripper table palcement

* add that

* actually fine

* better

* clean that

* Cell hitting

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
Kashargul
2026-02-08 00:26:15 -05:00
committed by GitHub
co-authored by Cameron Lennox
parent 4fcae21e4d
commit 0c23b94075
20 changed files with 515 additions and 439 deletions
@@ -72,7 +72,7 @@
stripping = TRUE
if(is_robot_module(held) && istype(held, /obj/item/gripper))
var/obj/item/gripper/G = held
var/obj/item/wrapped = G.get_current_pocket()
var/obj/item/wrapped = G.get_wrapped_item()
if(istype(wrapped))
stripping = FALSE
else
@@ -98,7 +98,7 @@
visible_message(span_danger("\The [user] is trying to put \a [held] on \the [src]!"))
else
var/obj/item/gripper/G = held
var/obj/item/wrapped = G.get_current_pocket()
var/obj/item/wrapped = G.get_wrapped_item()
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
@@ -120,12 +120,12 @@
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.get_current_pocket()
var/obj/item/wrapped = G.get_wrapped_item()
if(istype(wrapped))
if(equip_to_slot_if_possible(wrapped, text2num(slot_to_strip), 0, 1, 1))
wrapped = null
G.generate_icons()
G.current_pocket = pick(G.pockets)
if(wrapped.loc != src)
return
G.clear_and_select_item()
else if(user.unEquip(held))
equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1)
if(held.loc != src)
@@ -1347,8 +1347,8 @@
/mob/living/silicon/robot/drop_item(var/atom/Target)
if(module_active && istype(module_active,/obj/item/gripper))
var/obj/item/gripper/G = module_active
G.drop_item_nm()
var/obj/item/gripper/robot_gripper = module_active
robot_gripper.drop_item_nm()
/mob/living/silicon/robot/disable_spoiler_vision()
if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY|BORGANOMALOUS)) // Whyyyyyyyy have seperate defines.
@@ -0,0 +1,439 @@
//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.
//Returns TRUE if we're in use explaining how.
/obj/item/gripper/proc/is_in_use(mob/user, visible = TRUE)
if(gripper_in_use)
if(visible)
to_chat(user, span_danger("You are currently using the gripper on something!"))
return TRUE
if(in_radial_menu)
if(visible)
to_chat(user, span_danger("You are currently in the radial menu! Close it to use the gripper."))
return TRUE
return FALSE
/obj/item/gripper/proc/update_ref(var/datum/weakref/new_ref)
var/had_item = get_wrapped_item()
WR = new_ref
var/holding_item = get_wrapped_item()
// Feedback
update_icon()
if(had_item && !holding_item) // Dropped
our_robot.playsound_local(get_turf(our_robot), 'sound/machines/click.ogg', 50)
return
if(holding_item && !had_item || (holding_item != had_item)) // Pickup or change item
our_robot.playsound_local(get_turf(our_robot), 'sound/machines/click2.ogg', 50)
///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
var/obj/item/wrapped = get_wrapped_item()
if(!wrapped)
return
//Checks two things:
//Is our wrapped object currently in our borg still?
//AND Is it not a gripper pocket? If not, reset WR.
if(item_left_gripper(wrapped))
update_ref(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.
/obj/item/gripper/proc/generate_icons()
if(LAZYLEN(pockets))
pockets_by_name = list()
photo_images = list()
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")
continue
var/obj/item/pocket_content = pocket_to_check.contents[1]
pockets_by_name["[pocket_to_check.name]" + "[pocket_content.name]"] = pocket_content
var/image/pocket_image = image(icon = pocket_content.icon, icon_state = pocket_content.icon_state)
if(pocket_content.color)
pocket_image.color = pocket_content.color
if(pocket_content.overlays)
for(var/overlay in pocket_content.overlays)
pocket_image.overlays += overlay
photo_images["[pocket_to_check.name]" + "[pocket_content.name]"] = pocket_image
/obj/item/gripper/attack_self(mob/user)
. = ..()
if(.)
return TRUE
if(special_handling)
return FALSE
if(is_in_use(user))
return FALSE
generate_icons()
var/list/options = list()
for(var/Iname in pockets_by_name)
options[Iname] = photo_images[Iname]
var/list/choice = list()
in_radial_menu = TRUE
choice = show_radial_menu(user, src, options, radius = 40, require_near = TRUE, autopick_single_option = FALSE)
in_radial_menu = FALSE
var/obj/item/wrapped = get_wrapped_item()
if(choice)
var/obj/item/storage/internal/gripper/selected_pocket = pockets_by_name[choice]
if(!isgripperpocket(selected_pocket)) //The pocket we're selecting is NOT a gripper storage
if(!isgripperpocket(selected_pocket.loc)) //We kept the radial menu opened, used the item, then selected it again.
clear_and_select_pocket() //Pick the next open pocket.
return
update_ref(WEAKREF(selected_pocket))
return
current_pocket = selected_pocket
update_ref(null)
return
if(wrapped)
return wrapped.attack_self(user)
/obj/item/gripper/attackby(var/obj/item/O, var/mob/user)
if(is_in_use(user))
return FALSE
var/obj/item/wrapped = get_wrapped_item()
if(wrapped)
wrapped.loc = loc //Place it in to the robot.
var/resolved = wrapped.attackby(O, user)
wrapped = get_wrapped_item() //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)
clear_and_select_pocket()
return FALSE
//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.
if(item_left_gripper(wrapped))
clear_and_select_pocket()
return TRUE
//We were not given a resolved, the object still exists, AND we hit something. Attack that thing with our wrapped item.
if(!resolved && wrapped && O)
O.afterattack(wrapped,user,1)
wrapped = get_wrapped_item()
//The object still exists, but is not in our contents OR back in the gripper storage.
if(item_left_gripper(wrapped))
clear_and_select_pocket()
return TRUE
//Nothing happened to it. Just put it back into our pocket.
wrapped.loc = current_pocket
return TRUE
return ..()
/obj/item/gripper/afterattack(atom/target, mob/living/user, proximity, params)
if(!proximity)
return // This will prevent them using guns at range but adminbuse can add them directly to modules, so eh.
if(is_in_use(user))
return
var/obj/item/wrapped = get_wrapped_item()
if(wrapped && item_left_gripper(wrapped)) // This is used for items that are moved out during other attack chains
clear_and_select_item()
return
if(use_item(target, user, wrapped)) //Already have an item.
return
update_ref(WEAKREF(wrapped))
if(pick_up_item(target, user))
return
if(handle_afterattack_special(target, user))
return
if(item_left_gripper(wrapped))
clear_and_select_item()
/obj/item/gripper/proc/use_item(atom/target, mob/user, obj/item/wrapped)
if(!wrapped)
return FALSE
var/obj/item/storage/internal/gripper/previous_pocket
if(isgripperpocket(wrapped.loc))
previous_pocket = wrapped.loc
else
previous_pocket = current_pocket
var/original_amount = 0
if(istype(wrapped, /obj/item/reagent_containers))
var/obj/item/reagent_containers/wrapped_container = wrapped
original_amount = wrapped_container.reagents?.total_volume
wrapped.loc = user
var/resolved = target.attackby(wrapped, user)
if(!resolved && wrapped && target)
wrapped.afterattack(target, user, TRUE)
if(item_left_gripper(wrapped))
clear_and_select_pocket()
return TRUE
if(istype(wrapped, /obj/item/reagent_containers))
var/obj/item/reagent_containers/wrapped_container = wrapped
if(wrapped_container.reagents?.total_volume != original_amount || (istype(target, /obj/item/reagent_containers)))
wrapped.loc = previous_pocket
update_ref(WEAKREF(wrapped))
return TRUE
wrapped.loc = previous_pocket
update_ref(WEAKREF(wrapped))
return FALSE
/obj/item/gripper/proc/pick_up_item(atom/target, mob/user)
if(!isitem(target))
return FALSE
var/obj/item/I = target
if(!isturf(I.loc))
return FALSE
if(I.anchored)
to_chat(user, span_notice("You are unable to lift \the [I]."))
return FALSE
var/obj/item/storage/internal/gripper/selected_pocket = find_empty_pocket()
if(!selected_pocket)
to_chat(user, "Your gripper is full!")
return FALSE
if(!is_type_in_list(I, can_hold))
to_chat(user, span_danger("Your gripper cannot hold \the [I]."))
return FALSE
to_chat(user, "You collect \the [I].")
I.loc = selected_pocket
current_pocket = selected_pocket
update_ref(WEAKREF(I))
return TRUE
/obj/item/gripper/proc/handle_afterattack_special(atom/target, mob/living/user)
if(istype(target, /obj/machinery/power/apc))
var/obj/machinery/power/apc/A = target
if(!A.opened)
return TRUE
if(!A.cell || !is_type_in_list(A.cell, can_hold))
return TRUE
if(!grab_cell(A.cell, user))
return TRUE
A.cell = null
A.charging = FALSE
A.update_icon()
user.visible_message(
span_danger("[user] removes the power cell from [A]!"),
"You remove the power cell."
)
return TRUE
if(isrobot(target))
var/mob/living/silicon/robot/A = target
if(!A.opened)
return TRUE
if(!A.cell || !is_type_in_list(A.cell, can_hold))
return TRUE
if(!grab_cell(A.cell, user))
return TRUE
var/datum/robot_component/cell_component = A.components["power cell"]
A.cell = null
cell_component.uninstall(TRUE)
A.update_icon()
user.visible_message(
span_danger("[user] removes the power cell from [A]!"),
"You remove the power cell."
)
return TRUE
return FALSE
/// Returns the first empty gripper pocket, or null
/obj/item/gripper/proc/find_empty_pocket()
for(var/obj/item/storage/internal/gripper/P in pockets)
if(!LAZYLEN(P.contents))
return P
return null
/// Returns TRUE if item is no longer in the gripper
/obj/item/gripper/proc/item_left_gripper(obj/item/I)
if(QDELETED(I))
return TRUE
if(isgripperpocket(I.loc))
return FALSE
if(I.loc == loc)
return FALSE
return TRUE
/// Sets current_pocket to a valid pocket (never an item)
/obj/item/gripper/proc/select_pocket(obj/item/storage/internal/gripper/P)
if(!P)
P = pick(pockets)
current_pocket = P
/// Clears the currently wrapped item and selects the pocket
/obj/item/gripper/proc/clear_and_select_pocket()
update_ref(null)
select_empty_pocket()
/// Clears the currently wrapped item and selects next item
/obj/item/gripper/proc/clear_and_select_item()
update_ref(null)
select_next_item()
/// Selects the first empty pocket, or a random one
/obj/item/gripper/proc/select_empty_pocket()
var/obj/item/storage/internal/gripper/P = find_empty_pocket()
select_pocket(P)
/obj/item/gripper/proc/select_next_item()
for(var/obj/item/storage/internal/gripper/P in reverseList(pockets))
if(!LAZYLEN(P.contents))
continue
var/obj/item/next_item = P.contents[1]
update_ref(WEAKREF(next_item))
return
/obj/item/gripper/proc/drop_item(mob/user)
var/obj/item/wrapped = get_wrapped_item()
if(!wrapped)
to_chat(user, span_warning("You have nothing to drop!"))
return
if(is_in_use(user))
return
if((item_left_gripper(wrapped)))
clear_and_select_pocket()
generate_icons()
return
to_chat(user, span_notice("You drop \the [wrapped]."))
wrapped.forceMove(get_turf(user))
clear_and_select_pocket()
generate_icons()
//FORCES the item onto the ground and resets.
/obj/item/gripper/proc/drop_item_nm(atom/taget)
var/obj/item/wrapped = get_wrapped_item()
if(!wrapped)
return
if((item_left_gripper(wrapped)))
clear_and_select_pocket()
generate_icons()
return
wrapped.forceMove(taget ? taget : get_turf(src))
clear_and_select_pocket()
generate_icons()
/obj/item/gripper/attack(mob/living/carbon/M, mob/living/carbon/user)
if(is_in_use(user))
return FALSE
var/obj/item/wrapped = get_wrapped_item()
//The force of the wrapped obj gets set to zero during the attack() and afterattack().
if(!wrapped)
return FALSE
//If our wrapper was deleted OR it's no longer in our internal gripper storage
if(item_left_gripper(wrapped))
update_ref(null)
return FALSE
wrapped.attack(M, user)
//attackby reportedly gets procced by being clicked on, at least according to Anewbe.
M.attackby(wrapped, user)
//If our wrapper was deleted OR it's no longer in our internal gripper storage
if(item_left_gripper(wrapped))
update_ref(null)
return TRUE
/obj/item/gripper/update_icon()
cut_overlays()
var/obj/item/wrapped = get_wrapped_item()
if(!wrapped)
return
// Draw the held item as a mini-image in the gripper itself
var/mutable_appearance/item_display = new(wrapped)
item_display.SetTransform(0.75, offset_y = -8)
item_display.pixel_x = 0
item_display.pixel_y = 0
item_display.plane = plane
item_display.layer = layer + 0.01
add_overlay(item_display)
//HELPER PROCS
///Use this to get what the current pocket is. Returns NULL if no
/obj/item/gripper/proc/get_wrapped_item() //done as a proc so snowflake code can be found later down the line and consolidated.
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
/obj/item/gripper/proc/consolidate_stacks(var/obj/item/stack/stack_to_consolidate)
if(!stack_to_consolidate || !istype(stack_to_consolidate, /obj/item/stack))
return
for(var/obj/item/storage/internal/gripper/pocket in pockets)
if(!LAZYLEN(pocket.contents))
continue
for(var/obj/item/stack/stack in pocket.contents)
if(istype(stack_to_consolidate, stack))
stack_to_consolidate.transfer_to(stack)
return
/obj/item/gripper/proc/grab_cell(obj/item/cell, mob/user)
var/obj/item/storage/internal/gripper/P = find_empty_pocket()
if(!P)
to_chat(user, "Your gripper is full!")
return FALSE
cell.add_fingerprint(user)
cell.update_icon()
cell.forceMove(P)
current_pocket = P
update_ref(WEAKREF(cell))
return TRUE
@@ -676,6 +676,9 @@
src.modules += new /obj/item/gun/energy/robotic/taser/xeno(src)
src.modules += new /obj/item/xenoarch_multi_tool(src)
src.modules += new /obj/item/pickaxe/excavationdrill(src)
// Anomaly handling
src.modules += new /obj/item/analyzer(src)
src.modules += new /obj/item/assembly/signaler(src)
src.emag += new /obj/item/hand_tele(src)
@@ -543,7 +543,7 @@
//Has a list of items that it can hold.
var/list/can_hold = list(BASIC_GRIPPER)
var/datum/weakref/WR = null //We resolve this to get wrapped. Use get_current_pocket when possible.
var/datum/weakref/WR = null //We resolve this to get wrapped. Use get_wrapped_item when possible.
var/total_pockets = 5 //How many total inventory slots we want to have in the gripper
@@ -603,160 +603,19 @@
/obj/item/gripper/examine(mob/user)
. = ..()
var/obj/item/wrapped = get_current_pocket()
var/obj/item/wrapped = get_wrapped_item()
if(wrapped)
. += span_notice("\The [src] is holding \the [wrapped].")
. += wrapped.examine(user)
/obj/item/gripper/item_ctrl_click(mob/user)
var/obj/item/wrapped = get_current_pocket()
if(wrapped && !is_in_use())
var/obj/item/wrapped = get_wrapped_item()
if(wrapped && !is_in_use(user, FALSE))
wrapped.attack_self(user)
return
/obj/item/gripper/click_alt(mob/user)
if(!is_in_use())
drop_item()
return
//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.
//Returns a string if we're in use explaining how.
/obj/item/gripper/proc/is_in_use()
if(gripper_in_use)
return "You are currently using the gripper on something!"
if(in_radial_menu)
return "You are currently in the radial menu! Close it to use the gripper."
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
var/obj/item/wrapped = get_current_pocket()
if(!wrapped)
return
//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))
update_ref(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.
/obj/item/gripper/proc/generate_icons()
if(LAZYLEN(pockets))
pockets_by_name = list()
photo_images = list()
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")
continue
var/obj/item/pocket_content = pocket_to_check.contents[1]
pockets_by_name["[pocket_to_check.name]" + "[pocket_content.name]"] = pocket_content
var/image/pocket_image = image(icon = pocket_content.icon, icon_state = pocket_content.icon_state)
if(pocket_content.color)
pocket_image.color = pocket_content.color
if(pocket_content.overlays)
for(var/overlay in pocket_content.overlays)
pocket_image.overlays += overlay
photo_images["[pocket_to_check.name]" + "[pocket_content.name]"] = pocket_image
/obj/item/gripper/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
if(special_handling)
return FALSE
var/busy = is_in_use()
if(busy)
to_chat(user, span_danger("[busy]"))
return
generate_icons()
var/list/options = list()
for(var/Iname in pockets_by_name)
options[Iname] = photo_images[Iname]
var/list/choice = list()
in_radial_menu = TRUE
choice = show_radial_menu(user, src, options, radius = 40, require_near = TRUE, autopick_single_option = FALSE)
in_radial_menu = FALSE
var/obj/item/wrapped = get_current_pocket()
if(choice)
current_pocket = pockets_by_name[choice]
if(!isgripperpocket(current_pocket)) //The pocket we're selecting is NOT a gripper storage
if(!isgripperpocket(current_pocket.loc)) //We kept the radial menu opened, used the item, then selected it again.
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE) //Pick the next open pocket.
else
update_ref(WEAKREF(current_pocket))
else
update_ref(null)
else if(wrapped)
return wrapped.attack_self(user)
return ..()
/obj/item/gripper/attackby(var/obj/item/O, var/mob/user)
var/busy = is_in_use()
if(busy)
to_chat(user, span_danger("[busy]"))
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)
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(set_pocket = TRUE, clear_wrapped = 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 && !isgripperpocket(wrapped.loc)))
get_open_pocket(set_pocket = TRUE, clear_wrapped = 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)
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 && !isgripperpocket(wrapped.loc)))
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE)
else //Nothing happened to it. Just put it back into 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(clear_wrapped)
update_ref(null)
if(set_pocket)
if(!pocket_to_select)
pocket_to_select = pick(pockets) //If we don't have an open pocket, pick a random one.
if(!isgripperpocket(pocket_to_select)) //If we picked an item instead of a gripper storage, we need to reset WR.
update_ref(WEAKREF(pocket_to_select)) //We set WR to the pocket we selected.
current_pocket = pocket_to_select
return pocket_to_select
if(!is_in_use(user, FALSE))
drop_item(user)
/obj/item/gripper/verb/drop_gripper_item()
@@ -764,231 +623,7 @@
set desc = "Release an item from your magnetic gripper."
set category = "Abilities.Silicon"
drop_item()
/obj/item/gripper/proc/drop_item()
var/obj/item/wrapped = get_current_pocket()
var/busy = is_in_use()
if(!wrapped)
to_chat(src, span_warning("You have nothing to drop!"))
return
if(busy)
to_chat(src, span_danger("[busy]"))
return
if((wrapped == current_pocket && !isgripperpocket(wrapped.loc))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE)
generate_icons()
return
to_chat(src.loc, span_notice("You drop \the [wrapped]."))
wrapped.loc = get_turf(src)
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE)
generate_icons()
//FORCES the item onto the ground and resets.
/obj/item/gripper/proc/drop_item_nm()
var/obj/item/wrapped = get_current_pocket()
if(!wrapped)
return
if((wrapped == current_pocket && !isgripperpocket(wrapped.loc))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage
update_ref(null)
current_pocket = pick(pockets)
return
wrapped.forceMove(get_turf(src))
update_ref(null)
//Reselect our pocket.
current_pocket = pick(pockets)
/obj/item/gripper/attack(mob/living/carbon/M, mob/living/carbon/user)
var/busy = is_in_use()
if(busy)
to_chat(user, span_danger("[busy]"))
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((wrapped.loc != src.loc && !isgripperpocket(wrapped.loc))) //If our wrapper was deleted OR it's no longer in our internal gripper storage
update_ref(null)
return 0
var/old_loc = wrapped.loc
wrapped.attack(M,user)
M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe.
if((wrapped.loc != old_loc && !isgripperpocket(wrapped.loc))) //If our wrapper was deleted OR it's no longer in our internal gripper storage
update_ref(null)
return 1
/obj/item/gripper/afterattack(var/atom/target, var/mob/living/user, proximity, params)
if(!proximity)
return // This will prevent them using guns at range but adminbuse can add them directly to modules, so eh.
var/busy = is_in_use()
if(busy)
to_chat(user, span_danger("[busy]"))
return
var/current_pocket_full = FALSE
var/obj/item/wrapped = get_current_pocket()
if(wrapped == current_pocket)
current_pocket_full = TRUE
if(!wrapped && current_pocket)
if(LAZYLEN(current_pocket.contents))
wrapped = current_pocket.contents[1]
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(isgripperpocket(current_pocket.loc) && !LAZYLEN(current_pocket.loc.contents)) //If our pocket is a gripper, AND we have no contents, WR = null
update_ref(null)
else if(!isgripperpocket(current_pocket.loc)) //If our pocket is an item and we are not in the gripper, WR = null
update_ref(null)
if(!LAZYLEN(pockets)) //Shouldn't happen, but safety first.
to_chat(user, span_danger("Your gripper has nowhere to hold \the [target]."))
return
var/obj/item/storage/internal/gripper/selected_pocket //Find an open pocket to use in case we're trying to pick something up.
for(var/obj/item/storage/internal/gripper/available_pocket in pockets)
if(LAZYLEN(available_pocket.contents))
continue
selected_pocket = available_pocket
break
if(wrapped) //Already have an item.
//Temporary put wrapped into user so target's attackby() checks pass.
var/obj/previous_pocket
if(isgripperpocket(wrapped.loc))
previous_pocket = wrapped.loc
wrapped.loc = user
//Pass the attack on to the target. This might delete/relocate wrapped.
var/resolved = target.attackby(wrapped,user)
if(!resolved && wrapped && target)
wrapped.afterattack(target,user,1)
if(!WR) //We put our wrapped thing INTO something!
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE)
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
get_open_pocket(set_pocket = TRUE, clear_wrapped = TRUE)
return
else if(current_pocket_full) //Pocket is full. No grabbing more things.
to_chat(user, "Your gripper is currently full! You can't pick anything else up!")
return
else if(isitem(target)) //Check that we're not pocketing a mob.
//...and that the item is not in a container.
if(!isturf(target.loc))
return
var/obj/item/I = target
if(I.anchored)
to_chat(user,span_notice("You are unable to lift \the [I] from \the [I.loc]."))
return
//Check if the item is blacklisted.
var/grab = 0
for(var/typepath in can_hold)
if(istype(I,typepath))
grab = 1
break
//We can grab the item, finally.
if(grab)
to_chat(user, "You collect \the [I].")
I.loc = selected_pocket
if(selected_pocket == current_pocket) //If we put the item into our current pocket, we need to set WR to the item.
update_ref(WEAKREF(I))
current_pocket = I
return
else
to_chat(user, span_danger("Your gripper cannot hold \the [target]."))
else if(istype(target,/obj/machinery/power/apc))
var/obj/machinery/power/apc/A = target
if(A.opened)
if(A.cell && is_type_in_list(A.cell, can_hold))
current_pocket = A.cell
A.cell.add_fingerprint(user)
A.cell.update_icon()
A.update_icon()
A.cell.forceMove(current_pocket)
current_pocket = A.cell
WR = WEAKREF(current_pocket)
A.cell = null
A.charging = 0
A.update_icon()
user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.")
else if(isrobot(target))
var/mob/living/silicon/robot/A = target
if(A.opened)
if(A.cell && is_type_in_list(A.cell, can_hold))
var/datum/robot_component/cell_component = A.components["power cell"]
A.cell.update_icon()
A.cell.add_fingerprint(user)
A.cell.forceMove(current_pocket)
current_pocket = A.cell
WR = WEAKREF(current_pocket)
A.cell = null
cell_component.uninstall(TRUE)
A.update_icon()
user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.")
/obj/item/gripper/proc/update_ref(var/datum/weakref/new_ref)
var/had_item = get_current_pocket()
WR = new_ref
var/holding_item = get_current_pocket()
// Feedback
update_icon()
if(had_item && !holding_item) // Dropped
our_robot.playsound_local(get_turf(our_robot), 'sound/machines/click.ogg', 50)
else if(holding_item && !had_item || (holding_item != had_item)) // Pickup or change item
our_robot.playsound_local(get_turf(our_robot), 'sound/machines/click2.ogg', 50)
/obj/item/gripper/update_icon()
cut_overlays()
var/obj/item/wrapped = get_current_pocket()
if(!wrapped)
return
// Draw the held item as a mini-image in the gripper itself
var/mutable_appearance/item_display = new(wrapped)
item_display.SetTransform(0.75, offset_y = -8)
item_display.pixel_x = 0
item_display.pixel_y = 0
item_display.plane = plane
item_display.layer = layer + 0.01
add_overlay(item_display)
//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.
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
/obj/item/gripper/proc/consolidate_stacks(var/obj/item/stack/stack_to_consolidate)
if(!stack_to_consolidate || !istype(stack_to_consolidate, /obj/item/stack))
return
var/stacked = FALSE //So we can break the for loop 2 forloops deep.
for(var/obj/item/storage/internal/gripper/pocket in pockets)
if(stacked) //We've stacked our item, break!
break
if(LAZYLEN(pocket.contents))
for(var/obj/item/stack/stack in pocket.contents)
if(istype(stack_to_consolidate, stack))
stack_to_consolidate.transfer_to(stack)
stacked = TRUE
break
drop_item(src.loc)
//Different types of grippers!