/** * Dynamic way to allow holding another item in an inventory slot / hand slot * with a single static item of the item itself. * * Useful for cyborgs and rigsuits and more. */ /obj/item/gripper name = "magnetic gripper" desc = "A simple grasping tool specialized in construction and engineering work." description_info = "Ctrl-Clicking on the gripper will drop whatever it is holding.
\ Using an object on the gripper will interact with the item inside it, if it exists, instead." icon = 'icons/obj/device.dmi' icon_state = "gripper" item_flags = ITEM_NO_BLUDGEON | ITEM_ENCUMBERS_WHILE_HELD /// Has a list of items that it can hold. var/list/can_hold /// currently held item VAR_PRIVATE/obj/item/wrapped var/conf_inject_clickchain_flags /obj/item/gripper/examine(mob/user, dist) . = ..() if(wrapped) . += "\The [src] is holding \the [wrapped]." . += wrapped.examine(user) /obj/item/gripper/Destroy() remove_item(drop_location()) return ..() /** * Checks if an item should be able to be held in here. */ /obj/item/gripper/proc/can_hold(obj/item/item) for(var/typepath in can_hold) if(istype(item,typepath)) return TRUE return FALSE /obj/item/gripper/inv_slot_attached() if(wrapped) return list(wrapped, src) return src //! WARNING WARNING GRIPPERS ARE STILL SHITCODE !// //! This exact combination of hooks will make it work as expected. !// //! It's realistically still pretty shitty. !// /obj/item/gripper/pickup(...) . = ..() wrapped?.pickup(arglist(args)) /obj/item/gripper/dropped(...) . = ..() wrapped?.dropped(arglist(args)) /obj/item/gripper/equipped(...) . = ..() wrapped?.equipped(arglist(args)) /obj/item/gripper/unequipped(...) . = ..() wrapped?.unequipped(arglist(args)) /obj/item/gripper/on_inv_equipped(...) . = ..() wrapped?.on_inv_equipped(arglist(args)) /obj/item/gripper/on_inv_unequipped(...) . = ..() wrapped?.on_inv_unequipped(arglist(args)) /obj/item/gripper/on_inv_pickup(...) . = ..() wrapped?.on_inv_pickup(arglist(args)) /obj/item/gripper/on_inv_dropped(...) . = ..() wrapped?.on_inv_dropped(arglist(args)) /obj/item/gripper/proc/insert_item(obj/item/I) if(QDELETED(I)) return if(wrapped) remove_item(drop_location()) wrapped = I I.forceMove(src) // forcemove will have removed them from inv if they were inside if(worn_slot) I.pickup(inv_inside.owner, NONE, null) I.equipped(inv_inside.owner, isnum(inv_slot_or_index) ? SLOT_ID_HANDS : inv_slot_or_index, NONE) I.on_inv_equipped(inv_inside.owner, inv_inside, inv_slot_or_index, NONE, null) RegisterSignals(I, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED), PROC_REF(unwrap_hook)) /** * newloc false to not move */ /obj/item/gripper/proc/remove_item(atom/newloc = FALSE) if(!wrapped) return var/obj/item/old = wrapped UnregisterSignal(wrapped, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) wrapped = null // in theory the below should pull it out of inventory properly // as worn_slot is set, which allows inventory hooks to fire. switch(newloc) if(null) old.moveToNullspace() if(FALSE) else old.forceMove(newloc) /obj/item/gripper/proc/unwrap_hook(datum/source) ASSERT(isitem(source)) ASSERT(source == wrapped) remove_item(FALSE) /obj/item/gripper/proc/get_item() return wrapped /obj/item/gripper/CtrlClick(mob/user) drop_item() /obj/item/gripper/attack_self(mob/user, datum/event_args/actor/actor) . = ..() if(.) return if(wrapped) return wrapped.attack_self(user) /obj/item/gripper/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier) // todo: items should have a melee_receive_chain or something that // lets us arbitrarily route melee_interaction_chain to something else. if(!isnull(wrapped)) . = wrapped.attackby(I, user, params, clickchain_flags, damage_multiplier) if(.) return return clickchain_flags | I.afterattack(src, user, clickchain_flags, params) return ..() /obj/item/gripper/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) if(!isnull(wrapped)) return wrapped.melee_interaction_chain(clickchain, clickchain_flags | CLICKCHAIN_REDIRECTED | conf_inject_clickchain_flags) return ..() /obj/item/gripper/verb/drop_item() set name = "Drop Item" set desc = "Release an item from your magnetic gripper." set category = "Robot Commands" if(!wrapped) return to_chat(usr, "You drop \the [wrapped].") remove_item(drop_location()) /obj/item/gripper/afterattack(atom/target, mob/user, clickchain_flags, list/params) if(istype(target,/obj/item)) //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,"You are unable to lift \the [I] from \the [I.loc].") return //Check if the item is blacklisted. var/grab = can_hold(I) //We can grab the item, finally. if(grab) to_chat(user, "You collect \the [I].") insert_item(I) return else to_chat(user, "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) insert_item(A.cell) A.cell.add_fingerprint(user) A.cell.update_icon() A.cell = null A.charging = 0 A.update_icon() user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") else if(istype(target,/mob/living/silicon/robot)) var/mob/living/silicon/robot/A = target if(A.opened) if(A.cell) insert_item(A.cell) A.cell.add_fingerprint(user) A.cell.update_icon() A.updateicon() A.cell = null user.visible_message("[user] removes the power cell from [A]!", "You remove the power cell.") /obj/item/gripper/omni name = "omni gripper" desc = "A strange grasping tool that can hold anything a human can, but still maintains the limitations of application its more limited cousins have." icon_state = "gripper-omni" /obj/item/gripper/omni/can_hold(obj/item/item) return TRUE /obj/item/gripper/omni/no_attack conf_inject_clickchain_flags = CLICKCHAIN_DO_NOT_ATTACK /obj/item/gripper/engineering can_hold = list( /obj/item/cell, /obj/item/airlock_electronics, /obj/item/tracker_electronics, /obj/item/module/power_control, /obj/item/stock_parts, /obj/item/frame, /obj/item/camera_assembly, /obj/item/tank, /obj/item/circuitboard, /obj/item/smes_coil, /obj/item/fuelrod, /obj/item/fuel_assembly, ) // VEEEEERY limited version for mining borgs. Basically only for swapping cells and upgrading the drills. /obj/item/gripper/miner name = "drill maintenance gripper" desc = "A simple grasping tool for the maintenance of heavy drilling machines." icon_state = "gripper-mining" can_hold = list( /obj/item/cell, /obj/item/stock_parts, ) /obj/item/gripper/security name = "security gripper" desc = "A simple grasping tool for corporate security work." icon_state = "gripper-sec" can_hold = list( /obj/item/paper, /obj/item/paper_bundle, /obj/item/pen, /obj/item/sample, /obj/item/forensics/sample_kit, /obj/item/tape_recorder, /obj/item/barrier_tape_roll, /obj/item/uv_light, ) /obj/item/gripper/paperwork name = "paperwork gripper" desc = "A simple grasping tool for clerical work." can_hold = list( /obj/item/clipboard, /obj/item/paper, /obj/item/paper_bundle, /obj/item/card/id, /obj/item/book, /obj/item/newspaper, ) /obj/item/gripper/medical name = "medical gripper" desc = "A simple grasping tool for medical work." can_hold = list( /obj/item/reagent_containers/glass, /obj/item/storage/pill_bottle, /obj/item/reagent_containers/pill, /obj/item/reagent_containers/blood, /obj/item/stack/material/phoron, /obj/item/implant, /obj/item/nif, ) /obj/item/gripper/research //A general usage gripper, used for toxins/robotics/xenobio/etc name = "scientific gripper" icon_state = "gripper-sci" desc = "A simple grasping tool suited to assist in a wide array of research applications." can_hold = list( /obj/item/cell, /obj/item/stock_parts, /obj/item/mmi, /obj/item/robot_parts, /obj/item/robot_upgrade, /obj/item/flash, //to build borgs, /obj/item/disk, /obj/item/circuitboard, /obj/item/reagent_containers/glass, /obj/item/assembly/prox_sensor, /obj/item/healthanalyzer, //to build medibots, /obj/item/slime_cube, /obj/item/slime_crystal, /obj/item/disposable_teleporter/slime, /obj/item/slimepotion, /obj/item/slime_extract, /obj/item/reagent_containers/food/snacks/monkeycube, /obj/item/nif, ) /obj/item/gripper/circuit name = "circuit assembly gripper" icon_state = "gripper-circ" desc = "A complex grasping tool used for working with circuitry." can_hold = list( /obj/item/cell, /obj/item/electronic_assembly, /obj/item/assembly/electronic_assembly, /obj/item/clothing/under/circuitry, /obj/item/clothing/gloves/circuitry, /obj/item/clothing/glasses/circuitry, /obj/item/clothing/shoes/circuitry, /obj/item/clothing/head/circuitry, /obj/item/clothing/ears/circuitry, /obj/item/clothing/suit/circuitry, /obj/item/implant/integrated_circuit, /obj/item/integrated_circuit, ) /obj/item/gripper/service //Used to handle food, drinks, and seeds. name = "service gripper" icon_state = "gripper" desc = "A simple grasping tool used to perform tasks in the service sector, such as handling food, drinks, and seeds." can_hold = list( /obj/item/reagent_containers, //simplifies a lot of redundant categorization and spaghetti with reagents /obj/item/glass_extra, /obj/item/seeds, /obj/item/grown, /obj/item/tray, /obj/item/plantspray, /obj/item/reagent_containers/glass, /obj/item/reagent_containers/food/drinks, /obj/item/storage/box/wings, ) /obj/item/gripper/gravekeeper //Used for handling grave things, flowers, etc. name = "grave gripper" icon_state = "gripper" desc = "A specialized grasping tool used in the preparation and maintenance of graves." can_hold = list( /obj/item/seeds, /obj/item/grown, /obj/item/material/gravemarker, ) /obj/item/gripper/no_use //Used when you want to hold and put items in other things, but not able to 'use' the item /obj/item/gripper/no_use/attack_self(mob/user, datum/event_args/actor/actor) return /obj/item/gripper/no_use/organ name = "organ gripper" icon_state = "gripper-flesh" desc = "A specialized grasping tool used to preserve and manipulate organic material." can_hold = list( /obj/item/organ, ) /obj/item/gripper/no_use/organ/Entered(var/atom/movable/AM) ..() if(istype(AM, /obj/item/organ)) var/obj/item/organ/O = AM O.preserve(GRIPPER_TRAIT) /obj/item/gripper/no_use/organ/Exited(var/atom/movable/AM) ..() if(istype(AM, /obj/item/organ)) var/obj/item/organ/O = AM O.unpreserve(GRIPPER_TRAIT) /obj/item/gripper/no_use/organ/robotics name = "robotics organ gripper" icon_state = "gripper-flesh" desc = "A specialized grasping tool used in robotics work." can_hold = list( /obj/item/organ/external, /obj/item/organ/internal/brain, //to insert into MMIs, /obj/item/organ/internal/cell, /obj/item/organ/internal/eyes/robot, ) /obj/item/gripper/no_use/mech name = "exosuit gripper" icon_state = "gripper-mech" desc = "A large, heavy-duty grasping tool used in construction of mechs." can_hold = list( /obj/item/vehicle_part, /obj/item/vehicle_part/micro, /obj/item/vehicle_module, /obj/item/vehicle_tracking_beacon, ) /obj/item/gripper/no_use/loader //This is used to disallow building with metal. name = "sheet loader" desc = "A specialized loading device, designed to pick up and insert sheets of materials inside machines." icon_state = "gripper-sheet" can_hold = list( /obj/item/stack/material, )