diff --git a/code/game/objects/items/robot/cyborg_gripper.dm b/code/game/objects/items/robot/cyborg_gripper.dm new file mode 100644 index 00000000000..59409c56fa3 --- /dev/null +++ b/code/game/objects/items/robot/cyborg_gripper.dm @@ -0,0 +1,231 @@ +/* CONTENTS: +0. generic define gripper +1. UNIVERSAL GRIPPER +2. MEDICAL GRIPPER +3. SERVICE GRIPPER +4. ENGINEERING GRIPPER +*/ + +// Generic gripper. This should never appear anywhere. +/obj/item/gripper + name = "generic gripper item" + desc = "If you can see this, make an issue report to Github. Something has gone wrong!" + icon = 'icons/obj/device.dmi' + icon_state = "gripper" + actions_types = list(/datum/action/item_action/drop_gripped_item) + /// Set to TRUE to allow interaction with light fixtures and cell-containing machinery. + var/engineering_machine_interaction = FALSE + /// Set to TRUE to allow the gripper to shake people awake/help them up. + var/can_help_up = FALSE + /// Defines what items the gripper can carry. + var/list/can_hold = list() + /// Set to TRUE to allow ANY item to be held, bypassing can_hold checks. + var/can_hold_all_items = FALSE + /// The item currently being held. + var/obj/item/gripped_item + +/obj/item/gripper/examine(mob/user) + . = ..() + if(!gripped_item) + . += "[src] is empty." + return + . += "[src] is currently holding [gripped_item]." + +/obj/item/gripper/examine_more(mob/user) + . = ..() + . += {"Cyborg grippers are well-developed, and despite some anatomical differences that manifest in some models, they can be used just as effectively as a regular hand with enough practice. + +Companies like Nanotrasen use software to limit the items that a cyborg can manipulate to a specific pre-defined list, \ +as part of their multi-layered protections to try and eliminate the chance of a hypothetical synthetic uprising, not wishing to see a repeat of the IPC uprising in 2525."} + + +/obj/item/gripper/Initialize(mapload) + . = ..() + can_hold = typecacheof(can_hold) + +/obj/item/gripper/ui_action_click(mob/user) + drop_gripped_item(user) + +/obj/item/gripper/proc/drop_gripped_item(mob/user, silent = FALSE) + if(!gripped_item) + to_chat(user, "[src] is empty.") + return + if(!silent) + to_chat(user, "You drop [gripped_item].") + gripped_item.forceMove(get_turf(src)) + gripped_item = null + +/obj/item/gripper/attack_self(mob/user) + if(!gripped_item) + to_chat(user, "[src] is empty.") + return + gripped_item.attack_self(user) + +/obj/item/gripper/attack(mob/living/carbon/M, mob/living/carbon/user) + return + +// This is required to ensure that the forceMove checks on some objects don't rip the gripper out of the borg's inventory and toss it on the floor. That would hurt, a lot! +/obj/item/gripper/forceMove(atom/destination) + return + +/obj/item/gripper/afterattack(atom/target, mob/living/user, proximity, params) + //Target is invalid or we are not adjacent. + if(!target || !proximity) + return FALSE + // Shake people awake, get them on their feet. + if(ishuman(target) && can_help_up) + var/mob/living/carbon/human/pickup_target = target + if(!IS_HORIZONTAL(pickup_target)) + return + pickup_target.AdjustSleeping(-10 SECONDS) + pickup_target.AdjustParalysis(-6 SECONDS) + pickup_target.AdjustStunned(-6 SECONDS) + pickup_target.AdjustWeakened(-6 SECONDS) + pickup_target.AdjustKnockDown(-6 SECONDS) + pickup_target.adjustStaminaLoss(-10) + pickup_target.resting = FALSE + pickup_target.stand_up() + playsound(user.loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) + user.visible_message( + "[user] shakes [pickup_target] trying to wake [pickup_target.p_them()] up!", + "You shake [pickup_target] trying to wake [pickup_target.p_them()] up!" + ) + return FALSE + //Already have an item. + if(gripped_item) + + //Pass the attack on to the target. This might delete/relocate gripped_item. + if(!target.attackby(gripped_item, user, params)) + // If the attackby didn't resolve or delete the target or gripped_item, afterattack + // (Certain things, such as mountable frames, rely on afterattack) + gripped_item?.afterattack(target, user, 1, params) + + //If gripped_item either didn't get deleted, or it failed to be transfered to its target + if(!gripped_item && length(contents)) + gripped_item = contents[1] + return FALSE + else if(gripped_item && !contents.len) + gripped_item = null + + //Check that we're not pocketing a mob. + else if(isitem(target)) + var/obj/item/I = target + // Make sure the item is something the gripper can hold + if(can_hold_all_items || is_type_in_typecache(I, can_hold)) + to_chat(user, "You collect [I].") + I.forceMove(src) + gripped_item = I + return TRUE + + to_chat(user, "You hold your gripper over [target], but no matter how hard you try, you cannot make yourself grab it.") + return FALSE + + // Everything past this point requires being able to engineer. + if(!engineering_machine_interaction) + return + // APC cells. + if(istype(target, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = target + if(A.opened && A.cell) + gripped_item = A.cell + + A.cell.add_fingerprint(user) + A.cell.update_icon() + A.cell.forceMove(src) + A.cell = null + + A.charging = APC_NOT_CHARGING + A.update_icon() + + user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") + // Cell Chargers + else if(istype(target, /obj/machinery/cell_charger)) + var/obj/machinery/cell_charger/cell_charger = target + if(cell_charger.charging) + gripped_item = cell_charger.charging + cell_charger.charging.add_fingerprint(user) + cell_charger.charging.forceMove(src) + cell_charger.removecell() + // Putting lights in fixtures. + else if(istype(target, /obj/machinery/light)) + var/obj/machinery/light/light = target + var/obj/item/light/L = light.drop_light_tube() + L.forceMove(src) + gripped_item = L + user.visible_message("[user] removes the light from the fixture.", "You dislodge the light from the fixture.") + + return TRUE + +//////////////////////////////// +// MARK: UNIVERSAL GRIPPER +//////////////////////////////// +/// Universal gripper. Not supplied to any cyborg by default. Could be varedited onto a borg for event stuff. Functions almost like a real hand! +/obj/item/gripper/universal + name = "cyborg gripper" + desc = "A grasping tool for cyborgs. This one is not restricted by any restraining software, allowing it to handle any object the user wishes." + // It's UNIVERSAL so it has all functions enabled. + engineering_machine_interaction = TRUE + can_help_up = TRUE + can_hold_all_items = TRUE + +//////////////////////////////// +// MARK: MEDICAL GRIPPER +//////////////////////////////// +// For medical borgs, for doing medical stuff! +// Not giving this anything to hold yet, but stuff may be added in the future. Organs/implants are currently viewed as too strong to hold. +/obj/item/gripper/medical + name = "medical gripper" + desc = "A grasping tool for cyborgs. This one is covered with hygenic medical-grade silicone rubber. \ + Use it to help patients up once surgery is complete, or to substitute for hands in surgical operations." + can_help_up = TRUE + // REMOVE actions_types from here if you add a can_hold list for this gripper! + actions_types = list() + +//////////////////////////////// +// MARK: SERVICE GRIPPER +//////////////////////////////// +// For service borgs. To make them slightly better at their job. +/obj/item/gripper/service + name = "service gripper" + desc = "A grasping tool for cyborgs. This version is made from hygenic easy-clean material. Maybe some day you'll be able to grab food with it..." + // For waking up drunkards. + can_help_up = TRUE + // Everything in this list is currently for either playing games or otherwise assisting the crew in mundane, non-impactful ways. + can_hold = list( + /obj/item/deck, + /obj/item/cardhand, + /obj/item/coin, + /obj/item/paper, + /obj/item/photo, + /obj/item/toy/plushie + ) + +//////////////////////////////// +// MARK: ENGINEERING GRIPPER +//////////////////////////////// +// For engineering and sabotage borgs, and drones. +/obj/item/gripper/engineering + name = "engineering gripper" + desc = "A grasping tool for cyborgs. This version can hold a wide variety of constructon components for use in engineering work." + engineering_machine_interaction = TRUE + can_hold = list( + /obj/item/firealarm_electronics, + /obj/item/airalarm_electronics, + /obj/item/airlock_electronics, + /obj/item/firelock_electronics, + /obj/item/intercom_electronics, + /obj/item/apc_electronics, + /obj/item/tracker_electronics, + /obj/item/stock_parts, + /obj/item/vending_refill, + /obj/item/mounted/frame, + /obj/item/assembly/prox_sensor, + /obj/item/assembly/igniter, + /obj/item/rack_parts, + /obj/item/camera_assembly, + /obj/item/tank, + /obj/item/circuitboard, + /obj/item/stack/ore/bluespace_crystal, + /obj/item/stack/tile/light, + /obj/item/light + ) diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 680cd80c053..bf588993722 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -369,8 +369,8 @@ if(try_wallmount(I, user, params)) return - // The magnetic gripper does a separate attackby, so bail from this one - if(istype(I, /obj/item/gripper_engineering)) + // The cyborg gripper does a separate attackby, so bail from this one + if(istype(I, /obj/item/gripper)) return return ..() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 47f9ab77c8c..e4ce7921dca 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -213,7 +213,7 @@ "You insert [I] into [src].") return - else if(istype(I, /obj/item/gripper_engineering)) + else if(istype(I, /obj/item/gripper)) if(!try_refill_storage(user)) to_chat(user, "You fail to retrieve any sheets from [src].") return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 09e6f0a9abe..af236704920 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -1,162 +1,3 @@ -//Simple borg hand. -//Limited use. -/// This isn't a drone item, also in engineering cyborg kits -/obj/item/gripper_engineering - name = "magnetic gripper" - desc = "A simple grasping tool for synthetic assets." - icon = 'icons/obj/device.dmi' - icon_state = "gripper" - actions_types = list(/datum/action/item_action/drop_gripped_item) - - //Has a list of items that it can hold. - var/list/can_hold = list( - /obj/item/firealarm_electronics, - /obj/item/airalarm_electronics, - /obj/item/airlock_electronics, - /obj/item/firelock_electronics, - /obj/item/intercom_electronics, - /obj/item/apc_electronics, - /obj/item/tracker_electronics, - /obj/item/stock_parts, - /obj/item/vending_refill, - /obj/item/mounted/frame/light_fixture, - /obj/item/mounted/frame/apc_frame, - /obj/item/mounted/frame/alarm_frame, - /obj/item/mounted/frame/firealarm, - /obj/item/mounted/frame/display/newscaster_frame, - /obj/item/mounted/frame/intercom, - /obj/item/mounted/frame/extinguisher, - /obj/item/mounted/frame/light_switch, - /obj/item/assembly/prox_sensor, - /obj/item/rack_parts, - /obj/item/camera_assembly, - /obj/item/tank, - /obj/item/circuitboard, - /obj/item/stack/tile/light, - /obj/item/stack/ore/bluespace_crystal, - /obj/item/assembly/igniter, - /obj/item/light - ) - - //Item currently being held. - var/obj/item/gripped_item = null - -/obj/item/gripper_medical - name = "medical gripper" - desc = "A grasping tool used to help patients up once surgery is complete, or to substitute for hands in surgical operations." - icon = 'icons/obj/device.dmi' - icon_state = "gripper" - - -/obj/item/gripper_medical/afterattack(atom/target, mob/living/user, proximity, params) - if(!proximity || !target || !ishuman(target)) - return - var/mob/living/carbon/human/pickup_target = target - if(!IS_HORIZONTAL(pickup_target)) - return - pickup_target.AdjustSleeping(-10 SECONDS) - pickup_target.AdjustParalysis(-6 SECONDS) - pickup_target.AdjustStunned(-6 SECONDS) - pickup_target.AdjustWeakened(-6 SECONDS) - pickup_target.AdjustKnockDown(-6 SECONDS) - pickup_target.stand_up() - playsound(user.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - user.visible_message( \ - "[user] shakes [pickup_target] trying to wake [pickup_target.p_them()] up!",\ - "You shake [pickup_target] trying to wake [pickup_target.p_them()] up!",\ - ) - -/obj/item/gripper_engineering/Initialize(mapload) - . = ..() - can_hold = typecacheof(can_hold) - -/obj/item/gripper_engineering/ui_action_click(mob/user) - drop_gripped_item() - -/obj/item/gripper_engineering/attack_self(mob/user) - if(!gripped_item) - to_chat(user, "[src] is empty.") - return - gripped_item.attack_self(user) - -/obj/item/gripper_engineering/proc/drop_gripped_item(silent = FALSE) - if(!gripped_item) - return - if(!silent) - to_chat(loc, "You drop [gripped_item].") - gripped_item.forceMove(get_turf(src)) - gripped_item = null - -/obj/item/gripper_engineering/attack(mob/living/carbon/M, mob/living/carbon/user) - return - -/// Grippers are snowflakey so this is needed to to prevent forceMoving grippers after `if(!user.drop_item())` checks done in certain attackby's. // What does this even MEAN - GDN -/obj/item/gripper_engineering/forceMove(atom/destination) - return - -/obj/item/gripper_engineering/afterattack(atom/target, mob/living/user, proximity, params) - if(!target || !proximity) //Target is invalid or we are not adjacent. - return FALSE - - if(gripped_item) //Already have an item. - - //Pass the attack on to the target. This might delete/relocate gripped_item. - if(!target.attackby(gripped_item, user, params)) - // If the attackby didn't resolve or delete the target or gripped_item, afterattack - // (Certain things, such as mountable frames, rely on afterattack) - gripped_item?.afterattack(target, user, 1, params) - - //If gripped_item either didn't get deleted, or it failed to be transfered to its target - if(!gripped_item && length(contents)) - gripped_item = contents[1] - return FALSE - else if(gripped_item && !length(contents)) - gripped_item = null - - else if(isitem(target)) //Check that we're not pocketing a mob. - var/obj/item/I = target - if(is_type_in_typecache(I, can_hold)) // Make sure the item is something the gripper can hold - to_chat(user, "You collect [I].") - I.forceMove(src) - gripped_item = I - else - to_chat(user, "Your gripper cannot hold [target].") - return FALSE - - else if(istype(target,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = target - if(A.opened) - if(A.cell) - - gripped_item = A.cell - - A.cell.add_fingerprint(user) - A.cell.update_icon() - A.cell.forceMove(src) - A.cell = null - - A.charging = APC_NOT_CHARGING - A.update_icon() - - user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") - - else if(istype(target, /obj/machinery/cell_charger)) - var/obj/machinery/cell_charger/cell_charger = target - if(cell_charger.charging) - gripped_item = cell_charger.charging - cell_charger.charging.add_fingerprint(user) - cell_charger.charging.forceMove(src) - cell_charger.removecell() - - else if(istype(target, /obj/machinery/light)) - var/obj/machinery/light/light = target - var/obj/item/light/L = light.drop_light_tube() - L.forceMove(src) - gripped_item = L - user.visible_message("[user] removes the light from the fixture.", "You dislodge the light from the fixture.") - - return TRUE - /obj/item/matter_decompiler name = "matter decompiler" desc = "Eating trash, bits of glass, or other debris will replenish your stores." diff --git a/code/modules/mob/living/silicon/robot/robot_inventory.dm b/code/modules/mob/living/silicon/robot/robot_inventory.dm index 3e89737f962..29cbd551747 100644 --- a/code/modules/mob/living/silicon/robot/robot_inventory.dm +++ b/code/modules/mob/living/silicon/robot/robot_inventory.dm @@ -116,7 +116,7 @@ return 0 /mob/living/silicon/robot/drop_item() - var/obj/item/gripper_engineering/G = get_active_hand() + var/obj/item/gripper/G = get_active_hand() if(istype(G)) G.drop_gripped_item(silent = TRUE) return TRUE // The gripper is special because it has a normal item inside that we can drop. diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm index c3dd377d2f7..d95d0d9d294 100644 --- a/code/modules/mob/living/silicon/robot/robot_mob.dm +++ b/code/modules/mob/living/silicon/robot/robot_mob.dm @@ -1698,7 +1698,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /// Used in `robot.dm` when the user presses "Q" by default. /mob/living/silicon/robot/proc/on_drop_hotkey_press() - var/obj/item/gripper_engineering/G = get_active_hand() + var/obj/item/gripper/G = get_active_hand() if(istype(G) && G.gripped_item) G.drop_gripped_item() // if the active module is a gripper, try to drop its held item. else diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index e1aa7437c8b..352c05b2808 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -354,7 +354,7 @@ /obj/item/stack/medical/ointment/advanced/cyborg, /obj/item/stack/medical/splint/cyborg, /obj/item/stack/nanopaste/cyborg, - /obj/item/gripper_medical + /obj/item/gripper/medical ) malf_modules = list(/obj/item/gun/syringemalf) special_rechargables = list( @@ -362,6 +362,11 @@ /obj/item/gun/syringemalf ) +/obj/item/robot_module/medical/handle_death(mob/living/silicon/robot/R, gibbed) + var/obj/item/gripper/medical/G = locate(/obj/item/gripper/medical) in modules + if(G) + G.drop_gripped_item(silent = TRUE) + // Disable safeties on the borg's defib. /obj/item/robot_module/medical/emag_act(mob/user) . = ..() @@ -451,7 +456,7 @@ /obj/item/analyzer, /obj/item/geiger_counter/cyborg, /obj/item/holosign_creator/engineering, - /obj/item/gripper_engineering, + /obj/item/gripper/engineering, /obj/item/matter_decompiler, /obj/item/painter, /obj/item/areaeditor/blueprints/cyborg, @@ -471,7 +476,7 @@ special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg, /obj/item/gun/energy/emitter/cyborg) /obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed) - var/obj/item/gripper_engineering/G = locate(/obj/item/gripper_engineering) in modules + var/obj/item/gripper/engineering/G = locate(/obj/item/gripper/engineering) in modules if(G) G.drop_gripped_item(silent = TRUE) @@ -604,7 +609,8 @@ /obj/item/reagent_containers/dropper/cyborg, /obj/item/lighter/zippo, /obj/item/storage/bag/tray/cyborg, - /obj/item/reagent_containers/drinks/shaker + /obj/item/reagent_containers/drinks/shaker, + /obj/item/gripper/service ) emag_override_modules = list(/obj/item/reagent_containers/drinks/cans/beer/sleepy_beer) emag_modules = list(/obj/item/restraints/handcuffs/cable/zipties/cyborg, /obj/item/instrument/guitar/cyborg) @@ -615,6 +621,10 @@ /obj/item/gun/projectile/shotgun/automatic/combat/cyborg ) +/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed) + var/obj/item/gripper/service/G = locate(/obj/item/gripper/service) in modules + if(G) + G.drop_gripped_item(silent = TRUE) // This is a special type of beer given when emagged, one sip and the target falls asleep. /obj/item/reagent_containers/drinks/cans/beer/sleepy_beer @@ -706,7 +716,7 @@ /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg, /obj/item/card/emag, /obj/item/crowbar/cyborg/red, - /obj/item/pinpointer/operative + /obj/item/pinpointer/operative, ) // Sydicate medical cyborg module. @@ -739,7 +749,7 @@ /obj/item/stack/nanopaste/cyborg/syndicate, /obj/item/gun/medbeam, /obj/item/extinguisher/mini, - /obj/item/gripper_medical + /obj/item/gripper/medical, ) special_rechargables = list(/obj/item/extinguisher/mini) @@ -760,7 +770,7 @@ /obj/item/multitool/cyborg, /obj/item/t_scanner, /obj/item/analyzer, - /obj/item/gripper_engineering, + /obj/item/gripper/engineering, /obj/item/melee/energy/sword/cyborg, /obj/item/card/emag, /obj/item/borg_chameleon, @@ -846,7 +856,7 @@ /obj/item/wirecutters/cyborg/drone, /obj/item/multitool/cyborg/drone, /obj/item/lightreplacer/cyborg, - /obj/item/gripper_engineering, + /obj/item/gripper/engineering, /obj/item/matter_decompiler, /obj/item/reagent_containers/spray/cleaner/drone, /obj/item/soap, @@ -870,7 +880,7 @@ ) /obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed) - var/obj/item/gripper_engineering/G = locate(/obj/item/gripper_engineering) in modules + var/obj/item/gripper/engineering/G = locate(/obj/item/gripper/engineering) in modules if(G) G.drop_gripped_item(silent = TRUE) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index fbc2ef27130..6f22fb03501 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -216,7 +216,7 @@ if(accept_hand) if(!tool) success = TRUE - if(isrobot(user) && istype(tool, /obj/item/gripper_medical)) + if(isrobot(user) && istype(tool, /obj/item/gripper/medical)) success = TRUE if(accept_any_item) diff --git a/paradise.dme b/paradise.dme index 132c8970c96..5b67be5b266 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1073,6 +1073,7 @@ #include "code\game\objects\items\mountable_frames\mountables.dm" #include "code\game\objects\items\mountable_frames\newscaster_frame.dm" #include "code\game\objects\items\robot\ai_upgrades.dm" +#include "code\game\objects\items\robot\cyborg_gripper.dm" #include "code\game\objects\items\robot\robot_items.dm" #include "code\game\objects\items\robot\robot_parts.dm" #include "code\game\objects\items\robot\robot_upgrades.dm"