[MIRROR] cleans up robot gripper code (#12388)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2026-02-07 23:14:07 -07:00
committed by GitHub
parent a450bdfcf0
commit cbb200e0db
20 changed files with 515 additions and 439 deletions
+1 -9
View File
@@ -345,14 +345,6 @@ Checks if a list has the same entries and values as an element of big.
* Sorting
*/
//Reverses the order of items in the list
/proc/reverselist(list/L)
var/list/output = list()
if(L)
for(var/i = L.len; i >= 1; i--)
output += L[i]
return output
//Randomize: Return the list in a random order
/proc/shuffle(var/list/L)
if(!L)
@@ -608,7 +600,7 @@ Checks if a list has the same entries and values as an element of big.
//to_world_log("descending len input: [L.len]")
var/list/out = insertion_sort_numeric_list_ascending(L)
//to_world_log(" output: [out.len]")
return reverselist(out)
return reverseList(out)
/proc/dd_sortedObjectList(var/list/L, var/cache=list())
if(L.len < 2)
+1 -1
View File
@@ -64,7 +64,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
return A.attackby(src, user, attack_modifier, click_parameters)
// No comment
/atom/proc/attackby(obj/item/W, mob/user, var/attack_modifier, var/click_parameters)
/atom/proc/attackby(obj/item/W, mob/user, attack_modifier, click_parameters)
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACKBY, W, user, click_parameters) & COMPONENT_CANCEL_ATTACK_CHAIN)
return TRUE
return FALSE
@@ -171,6 +171,12 @@ handles linking back and forth.
if(istype(target, /obj/item/storage/bag/sheetsnatcher))
return mat_container.OnSheetSnatcher(source, user, target)
if(istype(target, /obj/item/gripper))
var/obj/item/gripper/robot_gripper = target
target = robot_gripper.get_wrapped_item()
attempt_insert(user, target)
return FALSE
return attempt_insert(user, target)
/// Insert mats into silo
+1 -1
View File
@@ -121,7 +121,7 @@
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.
var/obj/item/wrapped = B.get_current_pocket()
var/obj/item/wrapped = B.get_wrapped_item()
if(!wrapped)
to_chat(user, "\The [B] is not holding anything.")
return
@@ -294,8 +294,10 @@
max_days = 30
if(12)
max_days = 31
var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", pref.read_preference(/datum/preference/numeric/human/bday_day), max_days, 0)
var/old_days = pref.read_preference(/datum/preference/numeric/human/bday_day)
if(old_days > max_days)
old_days = max_days
var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", old_days, max_days, 0)
if(new_day)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, new_day)
else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes"))
@@ -271,40 +271,32 @@
if(istype(I, /obj/item/gripper))
var/obj/item/gripper/GR = I
var/obj/item/wrap = GR.get_current_pocket()
var/obj/item/wrap = GR.get_wrapped_item()
if(wrap)
wrap.loc = get_turf(src)
var/result = can_insert(wrap, user)
if(!result)
wrap.forceMove(GR)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
return
return TRUE
add_content(wrap, user)
update_icon()
return FALSE
if(QDELETED(wrap))
GR.update_ref(null)
attack_hand(user)
return TRUE
if(wrap.loc != src)
GR.drop_item_nm()
var/result = can_insert(I, user)
if(!result)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
return
ToCook = wrap
else
attack_hand(user)
if(result == 2)
var/obj/item/grab/G = I
if (G && istype(G) && G.affecting)
cook_mob(G.affecting, user)
return
else
var/result = can_insert(I, user)
if(!result)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
return
if(result == 2)
var/obj/item/grab/G = I
if (G && istype(G) && G.affecting)
cook_mob(G.affecting, user)
return
//From here we can start cooking food
add_content(ToCook, user)
update_icon()
@@ -39,17 +39,10 @@
/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
var/obj/item/wrapped = GR.get_current_pocket()
var/obj/item/wrapped = GR.get_wrapped_item()
if(wrapped)
wrapped.forceMove(get_turf(src))
attackby(wrapped, user)
if(QDELETED(wrapped))
wrapped = null
else if(wrapped.loc != src)
wrapped = null
return
return FALSE
for (var/possible_type in insertable)
if (istype(I, possible_type))
@@ -155,24 +155,24 @@
else if(istype(O, /obj/item/gripper)) // Grippers. ~Mechoid.
var/obj/item/gripper/B = O //B, for Borg.
var/obj/item/wrapped = B.get_current_pocket()
var/obj/item/wrapped = B.get_wrapped_item()
if(!wrapped)
to_chat(user, span_filter_notice("\The [B] is not holding anything."))
return
return TRUE
else
to_chat(user, span_filter_notice("You use \the [B] to put \the [wrapped] into \the [src]."))
return
return FALSE
else
to_chat(user, span_notice("\The [src] smartly refuses [O]."))
return 1
return TRUE
/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
emagged = 1
locked = -1
to_chat(user, span_filter_notice("You short out the product lock on [src]."))
return 1
return TRUE
/obj/machinery/smartfridge/proc/find_record(var/obj/item/O)
for(var/datum/stored_item/I as anything in item_records)
@@ -53,7 +53,7 @@
var/mob/living/silicon/robot/robot = user
if(istype(robot.module_active, /obj/item/gripper))
var/obj/item/gripper/gripper = robot.module_active
I = gripper.get_current_pocket()
I = gripper.get_wrapped_item()
else if(!I)
return FALSE
@@ -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)
@@ -1351,8 +1351,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
@@ -693,6 +693,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!
+7
View File
@@ -177,6 +177,13 @@
. += "It has a power rating of [maxcharge]."
. += "The charge meter reads [round(src.percent() )]%."
/obj/item/cell/attack(mob/living/M, mob/living/user, var/target_zone, var/attack_modifier)
if(isrobot(M))
var/mob/living/silicon/robot/target = M
if(target.opened)
return FALSE
..()
/obj/item/cell/attackby(obj/item/W, mob/user)
..()
if(istype(W, /obj/item/reagent_containers/syringe))
+1 -1
View File
@@ -109,7 +109,7 @@
if(istype(O,/obj/item/gripper))
var/obj/item/gripper/B = O //B, for Borg.
var/obj/item/wrapped = B.get_current_pocket()
var/obj/item/wrapped = B.get_wrapped_item()
if(!wrapped)
to_chat(user, "\The [B] is not holding anything.")
return 0
@@ -62,7 +62,7 @@
// Borgos!
if(istype(O,/obj/item/gripper))
var/obj/item/gripper/B = O //B, for Borg.
var/obj/item/wrapped = B.get_current_pocket()
var/obj/item/wrapped = B.get_wrapped_item()
if(!wrapped)
to_chat(user, "\The [B] is not holding anything.")
return FALSE
+3 -3
View File
@@ -135,7 +135,7 @@
if(istype(user,/mob/living/silicon/robot))
if(istype(tool, /obj/item/gripper))
var/obj/item/gripper/gripper = tool
var/obj/item/wrapped = gripper.get_current_pocket()
var/obj/item/wrapped = gripper.get_wrapped_item()
if(wrapped)
tool = wrapped
else
@@ -154,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.get_current_pocket()
tool = G.get_wrapped_item()
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)]")
@@ -165,7 +165,7 @@
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.get_current_pocket()
tool = G.get_wrapped_item()
G.drop_item_nm()
else
user.drop_item()
+8 -2
View File
@@ -84,7 +84,7 @@
return ..()
/obj/structure/table/attackby(obj/item/W as obj, mob/user as mob, var/hit_modifier, var/click_parameters)
/obj/structure/table/attackby(obj/item/W, mob/user, hit_modifier, click_parameters)
if (!W) return
// Handle harm intent grabbing/tabling.
@@ -128,7 +128,13 @@
// Handle dismantling or placing things on the table from here on.
if(isrobot(user))
return
if(istype(W, /obj/item/gripper))
var/obj/item/gripper/robot_gripper = W
var/obj/item/item_to_drop = robot_gripper.get_wrapped_item()
robot_gripper.drop_item_nm(loc)
item_to_drop.do_drop_animation(user)
auto_align(item_to_drop, click_parameters)
return FALSE
if(W.loc != user) // This should stop mounted modules ending up outside the module.
return
+1
View File
@@ -3716,6 +3716,7 @@
#include "code\modules\mob\living\silicon\robot\robot_animation_vr.dm"
#include "code\modules\mob\living\silicon\robot\robot_bellies.dm"
#include "code\modules\mob\living\silicon\robot\robot_damage.dm"
#include "code\modules\mob\living\silicon\robot\robot_gripper_handling.dm"
#include "code\modules\mob\living\silicon\robot\robot_items.dm"
#include "code\modules\mob\living\silicon\robot\robot_movement.dm"
#include "code\modules\mob\living\silicon\robot\robot_remote_control.dm"