Cyborgs now use storage datum (#90927)

## About The Pull Request

This moves Cyborgs onto using storage datums, removing the remenants of
the shitcode that was Cyborg inventory. It's now done mostly by
equipping/unequipping/storage items, much like how other mobs do.
This allows borgs to take advantage of more hand support stuff and
things like ``dropped()``, so borgs no longer have to copy paste drop
code to ``cyborg_unequip``

It also:
- Removes ``CYBORG_ITEM_TRAIT``
- Removes all borg items being ``NODROP``


https://github.com/user-attachments/assets/11442a10-3443-41f2-8c72-b38fb0126cdb

## Why It's Good For The Game

Currently borgs are able to have their entire inventory open and a bag
below it, which I thought was a little weird. I always assumed they WERE
storage items, so I guess I'm doing it myself.
Cyborgs using storage code makes it easier for contributors to actually
do stuff with, without risking breaking everything. It also hopefully
will make borg items more resilient against breaking in the future, now
that we're not relying on nodrop.
Also just brings them more in line with other mobs, all of which make
use of storages.

## Changelog

🆑
refactor: Cyborg's modules now use storage (so opening a bag will close
modules instead of overlap one over the other).
qol: Observers can now see Cyborg's inventories (like they can for
humans).
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
John Willard
2025-05-31 21:26:53 -04:00
committed by GitHub
parent 7f139ed9f3
commit c51ee7efa5
43 changed files with 390 additions and 428 deletions

View File

@@ -140122,7 +140122,7 @@
/area/station/service/theater)
"uQm" = (
/obj/structure/mop_bucket,
/obj/item/mop/cyborg,
/obj/item/mop,
/obj/effect/turf_decal/siding/thinplating_new/dark{
dir = 1
},

View File

@@ -24,7 +24,6 @@
/// cannot be removed without admin intervention
#define ROUNDSTART_TRAIT "roundstart"
#define JOB_TRAIT "job"
#define CYBORG_ITEM_TRAIT "cyborg-item"
/// Any traits granted by quirks.
#define QUIRK_TRAIT "quirk_trait"
/// (B)admins only.

View File

@@ -38,7 +38,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/sleep_icon
var/atom/movable/screen/throw_icon
var/atom/movable/screen/resist_icon
var/atom/movable/screen/module_store_icon
var/atom/movable/screen/floor_change
var/list/static_inventory = list() //the screen objects which are static
@@ -222,7 +221,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
QDEL_NULL(listed_actions)
QDEL_LIST(floating_actions)
QDEL_NULL(module_store_icon)
QDEL_LIST(static_inventory)
// all already deleted by static inventory clear
@@ -423,12 +421,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/mob/screenmob = viewmob || mymob
hidden_inventory_update(screenmob)
/datum/hud/robot/show_hud(version = 0, mob/viewmob)
. = ..()
if(!.)
return
update_robot_modules_display()
/datum/hud/new_player/show_hud(version = 0, mob/viewmob)
. = ..()
if(.)

View File

@@ -8,16 +8,21 @@
/atom/movable/screen/robot/Click()
if(isobserver(usr))
return 1
return TRUE
/atom/movable/screen/robot/module/Click()
if(..())
//observers can look at borg's inventories
var/mob/living/silicon/robot/robot_owner = hud.mymob
if(robot_owner.model.type != /obj/item/robot_model)
if(usr.active_storage == robot_owner.model.atom_storage)
robot_owner.model.atom_storage.hide_contents(usr)
else
robot_owner.model.atom_storage.open_storage(usr)
return TRUE
. = ..()
if(.)
return
var/mob/living/silicon/robot/R = usr
if(R.model.type != /obj/item/robot_model)
R.hud_used.toggle_show_robot_modules()
return 1
R.pick_model()
robot_owner.pick_model()
/atom/movable/screen/robot/module1
name = "module1"
@@ -62,6 +67,7 @@
/atom/movable/screen/robot/store
name = "store"
icon_state = "store"
screen_loc = ui_borg_store
/atom/movable/screen/robot/store/Click()
if(..())
@@ -160,10 +166,6 @@
robit.hands.screen_loc = ui_borg_module
static_inventory += robit.hands
//Store
module_store_icon = new /atom/movable/screen/robot/store(null, src)
module_store_icon.screen_loc = ui_borg_store
pull_icon = new /atom/movable/screen/pull(null, src)
pull_icon.icon = 'icons/hud/screen_cyborg.dmi'
pull_icon.screen_loc = ui_borg_pull
@@ -175,75 +177,6 @@
zone_select.update_appearance()
static_inventory += zone_select
/datum/hud/proc/toggle_show_robot_modules()
if(!iscyborg(mymob))
return
var/mob/living/silicon/robot/R = mymob
R.shown_robot_modules = !R.shown_robot_modules
update_robot_modules_display()
/datum/hud/proc/update_robot_modules_display(mob/viewer)
if(!iscyborg(mymob))
return
var/mob/living/silicon/robot/R = mymob
var/mob/screenmob = viewer || R
if(!R.model)
return
if(!R.client)
return
//Module is not currently active
screenmob.client.screen -= R.model.get_inactive_modules()
if(!R.shown_robot_modules || !screenmob.hud_used.hud_shown)
//Modules display is hidden
screenmob.client.screen -= module_store_icon //"store" icon
R.shown_robot_modules = 0
screenmob.client.screen -= R.robot_modules_background
return
//Modules display is shown
screenmob.client.screen += module_store_icon //"store" icon
if(!R.model.modules)
to_chat(usr, span_warning("Selected model has no modules to select!"))
return
if(!R.robot_modules_background)
return
var/list/usable_modules = R.model.get_usable_modules()
var/display_rows = max(CEILING(length(usable_modules) / 8, 1),1)
R.robot_modules_background.screen_loc = "CENTER-4:16,SOUTH+1:7 to CENTER+3:16,SOUTH+[display_rows]:7"
screenmob.client.screen += R.robot_modules_background
for(var/i in 1 to length(usable_modules))
var/atom/movable/A = usable_modules[i]
if(A in R.held_items)
//Module is currently active
continue
// Arrange in a grid x=-4 to 3 and y=1 to display_rows
var/x = (i - 1) % 8 - 4
var/y = floor((i - 1) / 8) + 1
screenmob.client.screen += A
if(x < 0)
A.screen_loc = "CENTER[x]:16,SOUTH+[y]:7"
else
A.screen_loc = "CENTER+[x]:16,SOUTH+[y]:7"
SET_PLANE_IMPLICIT(A, ABOVE_HUD_PLANE)
/datum/hud/robot/persistent_inventory_update(mob/viewer)
if(!mymob)
return

View File

@@ -23,6 +23,8 @@
/// List of all the mobs currently viewing the contents of this storage.
VAR_PRIVATE/list/mob/is_using = list()
///The type of storage interface this datum uses.
var/datum/storage_interface/storage_type = /datum/storage_interface
/// Associated list that keeps track of all storage UI datums per person.
VAR_PRIVATE/list/datum/storage_interface/storage_interfaces = null
@@ -860,9 +862,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
/// Called directly from the attack chain if [insert_on_attack] is TRUE.
/// Handles inserting an item into the storage when clicked.
/datum/storage/proc/item_interact_insert(mob/living/user, obj/item/thing)
if(iscyborg(user))
return ITEM_INTERACT_BLOCKING
attempt_insert(thing, user)
return ITEM_INTERACT_SUCCESS
@@ -1041,7 +1040,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
var/ui_style = ui_style2icon(to_show.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
if (isnull(storage_interfaces[to_show]))
storage_interfaces[to_show] = new /datum/storage_interface(ui_style, src)
storage_interfaces[to_show] = new storage_type(ui_style, src, to_show)
orient_storage()

View File

@@ -13,25 +13,27 @@
/// Storage that owns us
var/datum/storage/parent_storage
/datum/storage_interface/New(ui_style, parent_storage)
/datum/storage_interface/New(ui_style, datum/storage/parent_storage, mob/user)
..()
src.parent_storage = parent_storage
closer = new(null, null, parent_storage)
cells = new(null, null, parent_storage)
corner_top_left = new(null, null, parent_storage)
corner_top_right = new(null, null, parent_storage)
corner_bottom_left = new(null, null, parent_storage)
corner_bottom_right = new(null, null, parent_storage)
rowjoin_left = new(null, null, parent_storage)
rowjoin_right = new(null, null, parent_storage)
for (var/atom/movable/screen/ui_elem as anything in list_ui_elements())
var/datum/hud/owner_hud = user.hud_used
closer = new(null, owner_hud, parent_storage)
cells = new(null, owner_hud, parent_storage)
corner_top_left = new(null, owner_hud, parent_storage)
corner_top_right = new(null, owner_hud, parent_storage)
corner_bottom_left = new(null, owner_hud, parent_storage)
corner_bottom_right = new(null, owner_hud, parent_storage)
rowjoin_left = new(null, owner_hud, parent_storage)
rowjoin_right = new(null, owner_hud, parent_storage)
for (var/atom/movable/screen/ui_elem as anything in list_ui_elements(initializing = TRUE))
ui_elem.icon = ui_style
/// Returns all UI elements under this theme
/datum/storage_interface/proc/list_ui_elements()
/datum/storage_interface/proc/list_ui_elements(initializing = FALSE)
return list(cells, corner_top_left, corner_top_right, corner_bottom_left, corner_bottom_right, rowjoin_left, rowjoin_right, closer)
/datum/storage_interface/Destroy(force)
QDEL_NULL(closer)
QDEL_NULL(cells)
QDEL_NULL(corner_top_left)
QDEL_NULL(corner_top_right)
@@ -81,6 +83,20 @@
closer.screen_loc = "[screen_start_x + columns]:[screen_pixel_x - 5],[screen_start_y]:[screen_pixel_y]"
add_items(arglist(args))
/datum/storage_interface/proc/add_items(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
mob/user_looking,
atom/real_location,
list/datum/numbered_display/numbered_contents,
)
var/current_x = screen_start_x
var/current_y = screen_start_y
var/turf/our_turf = get_turf(real_location)
@@ -107,3 +123,67 @@
current_y++
if(current_y - screen_start_y >= rows)
break
///Silicon subtype of storage interface used by their model storage.
/datum/storage_interface/silicon
var/atom/movable/screen/robot/store/store
var/obj/item/robot_model/robot_model
/datum/storage_interface/silicon/New(ui_style, datum/storage/parent_storage, mob/user)
. = ..()
robot_model = parent_storage.real_location
if(iscyborg(user))
store = new(null, user.hud_used)
/datum/storage_interface/silicon/Destroy(force)
QDEL_NULL(store)
return ..()
/datum/storage_interface/silicon/list_ui_elements(initializing = FALSE)
if(initializing || isnull(store))
return ..()
//we're purposely excluding 'store' from having its icon changed from initialization.
return ..() + store
/datum/storage_interface/silicon/add_items(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
mob/user_looking,
atom/real_location,
list/datum/numbered_display/numbered_contents,
)
var/list/usable_modules = robot_model.get_usable_modules()
var/current_x = screen_start_x
var/current_y = screen_start_y
var/turf/our_turf = get_turf(real_location)
for(var/i in 1 to length(usable_modules))
var/atom/movable/item = usable_modules[i]
if(item in robot_model.robot.held_items)
current_x++
if(current_x - screen_start_x < columns)
continue
current_x = screen_start_x
current_y++
if(current_y - screen_start_y >= rows)
break
//Module is currently active
continue
item.mouse_opacity = MOUSE_OPACITY_OPAQUE
SET_PLANE(item, ABOVE_HUD_PLANE, our_turf)
item.screen_loc = "[current_x]:[screen_pixel_x],[current_y]:[screen_pixel_y]"
current_x++
if(current_x - screen_start_x < columns)
continue
current_x = screen_start_x
current_y++
if(current_y - screen_start_y >= rows)
break

View File

@@ -0,0 +1,50 @@
/datum/storage/cyborg_internal_storage
allow_big_nesting = TRUE
max_slots = 99
max_specific_storage = WEIGHT_CLASS_GIGANTIC
max_total_storage = 99
do_rustle = FALSE
silent = TRUE
screen_max_columns = 8
storage_type = /datum/storage_interface/silicon
/datum/storage/cyborg_internal_storage/can_insert(obj/item/to_insert, mob/living/silicon/robot/user, messages = TRUE, force = STORAGE_NOT_LOCKED)
return (to_insert in user.model.modules)
/datum/storage/cyborg_internal_storage/attempt_insert(obj/item/to_insert, mob/living/silicon/robot/user, override = FALSE, force = STORAGE_NOT_LOCKED, messages = TRUE)
user.deactivate_module(to_insert)
/**
* Cyborg internal storage orienting
* We're using the model's total amount of modules as reference for how many slots we fill,
* otherwise having enough items in hand and only 1 item in a row would mean, as we're a static inventory,
* the rows won't fill for all items in the UI.
* We also don't give an additional row if all slots are filled because as a static inventory borgs don't need extra space
* to put items in, you can click on the slot you took it out from, or use the dedicated "store" button.
*/
/datum/storage/cyborg_internal_storage/orient_storage()
var/obj/item/robot_model/model = real_location
var/adjusted_contents = length(model.modules)
var/list/datum/numbered_display/numbered_contents
if(numerical_stacking)
numbered_contents = process_numerical_display()
adjusted_contents = length(numbered_contents)
var/columns = clamp(max_slots, 1, screen_max_columns)
var/rows = clamp(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
for (var/mob/ui_user as anything in storage_interfaces)
if (isnull(storage_interfaces[ui_user]))
continue
storage_interfaces[ui_user].update_position(
screen_start_x,
screen_pixel_x,
screen_start_y,
screen_pixel_y,
columns,
rows,
ui_user,
real_location,
numbered_contents,
)

View File

@@ -644,15 +644,12 @@
return
attack_paw(ayy, modifiers)
/obj/item/attack_ai(mob/user)
if(istype(src.loc, /obj/item/robot_model))
//If the item is part of a cyborg module, equip it
if(!iscyborg(user))
return
var/mob/living/silicon/robot/R = user
if(!R.low_power_mode) //can't equip modules with an empty cell.
R.activate_module(src)
R.hud_used.update_robot_modules_display()
/obj/item/attack_robot(mob/living/silicon/robot/user)
if(!istype(loc, /obj/item/robot_model))
return
if(user.low_power_mode) //can't equip modules with an empty cell.
return
user.activate_module(src)
// afterattack() and attack() prototypes moved to _onclick/item_attack.dm for consistency

View File

@@ -105,11 +105,4 @@
playsound(current_item_loc, 'sound/items/weapons/thudswoosh.ogg', 30, TRUE, -1)
/obj/item/pushbroom/cyborg
name = "cyborg push broom"
/obj/item/pushbroom/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
#undef BROOM_PUSH_LIMIT

View File

@@ -71,8 +71,6 @@
cleanspeed = 2.8 SECONDS //janitor gets this
uses = 300
/obj/item/soap/nanotrasen/cyborg
/obj/item/soap/deluxe
desc = "A deluxe Waffle Corporation brand bar of soap. Smells of high-class luxury."
grind_results = list(/datum/reagent/consumable/aloejuice = 10, /datum/reagent/lye = 10)
@@ -153,6 +151,9 @@
to_chat(user, span_warning("[src] crumbles into tiny bits!"))
qdel(src)
/obj/item/soap/nanotrasen/cyborg
name = "built-in soap"
/obj/item/soap/nanotrasen/cyborg/noUses(mob/user)
to_chat(user, span_warning("[src] has ran out of chemicals! Head to a recharger to refill it."))

View File

@@ -421,14 +421,14 @@
return ..()
/obj/item/shockpaddles/dropped(mob/user)
. = ..()
if(!req_defib)
return ..()
UnregisterSignal(defib, COMSIG_MOVABLE_MOVED)
if(user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
if(req_defib)
if(user)
to_chat(user, span_notice("The paddles snap back into the main unit."))
snap_back()
to_chat(user, span_notice("The paddles snap back into the main unit."))
snap_back()
return ..()
/obj/item/shockpaddles/proc/snap_back()
if(!defib)

View File

@@ -42,10 +42,6 @@
gpstag = "BORG0"
desc = "A mining cyborg internal positioning system. Used as a recovery beacon for damaged cyborg assets, or a collaboration tool for mining teams."
/obj/item/gps/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/gps/mining/internal
icon_state = "gps-m"
gpstag = "MINER"

View File

@@ -317,11 +317,7 @@
return TRUE
/obj/item/lightreplacer/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/lightreplacer/cyborg/advanced
/obj/item/lightreplacer/advanced
name = "high capacity light replacer"
desc = "A higher capacity light replacer. Refill with broken or working lightbulbs, or sheets of glass."
icon_state = "lightreplacer_high"

View File

@@ -13,8 +13,6 @@
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 1.5)
/// Is this T-Ray scanner currently on?
var/on = FALSE
/// Will this T-Ray scanner shut off on de-equip? (Cyborgs only)
var/shut_off_on_unequip = TRUE
/obj/item/t_scanner/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -33,8 +31,6 @@
toggle_on()
/obj/item/t_scanner/cyborg_unequip(mob/user)
if(!shut_off_on_unequip)
return
if(!on)
return
toggle_on()

View File

@@ -20,7 +20,6 @@
var/max_reagent_volume = 15
var/mopspeed = 1.5 SECONDS
force_string = "robust... against germs"
var/insertable = TRUE
var/static/list/clean_blacklist = typecacheof(list(
/obj/item/reagent_containers/cup/bucket,
/obj/structure/mop_bucket,
@@ -72,10 +71,6 @@
val2remove = round(cleaner.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER), 0.1)
reagents.remove_all(val2remove) //reaction() doesn't use up the reagents
/obj/item/mop/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/mop/advanced
desc = "The most advanced tool in a custodian's arsenal, complete with a condenser for self-wetting! Just think of all the viscera you will clean up with this!"
name = "advanced mop"
@@ -118,6 +113,3 @@
/obj/item/mop/advanced/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/mop/advanced/cyborg
insertable = FALSE

View File

@@ -122,10 +122,6 @@
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/pipe_dispenser/cyborg_unequip(mob/user)
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/pipe_dispenser/attack_self(mob/user)
ui_interact(user)

View File

@@ -92,10 +92,6 @@
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/construction/plumbing/cyborg_unequip(mob/user)
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/construction/plumbing/attack_self(mob/user)
. = ..()
ui_interact(user)

View File

@@ -290,7 +290,3 @@
/obj/item/borg/cookbook/dropped(mob/user, silent)
SStgui.close_uis(cooking)
return ..()
/obj/item/borg/cookbook/cyborg_unequip(mob/user)
SStgui.close_uis(cooking)
return ..()

View File

@@ -1,6 +1,21 @@
/obj/item/borg/sight
var/sight_mode = null
icon = 'icons/obj/clothing/glasses.dmi'
///Define to a sight mode that we give to a cyborg while this item is equipped.
var/sight_mode = null
/obj/item/borg/sight/equipped(mob/living/silicon/robot/user, slot, initial = FALSE)
. = ..()
if(!iscyborg(user))
return .
user.sight_mode |= sight_mode
user.update_sight()
/obj/item/borg/sight/dropped(mob/living/silicon/robot/user, silent)
if(!iscyborg(user))
return ..()
user.sight_mode &= ~sight_mode
user.update_sight()
return ..()
/obj/item/borg/sight/xray
name = "\proper X-ray vision"

View File

@@ -110,11 +110,11 @@
return host.loc
return null
/obj/item/borg/projectile_dampen/dropped()
/obj/item/borg/projectile_dampen/equipped()
host = loc
return ..()
/obj/item/borg/projectile_dampen/equipped()
/obj/item/borg/projectile_dampen/dropped()
host = loc
return ..()
@@ -224,7 +224,8 @@
//if all else fails just make a new one from scratch
tool = new reference(user)
ADD_TRAIT(tool, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
//the internal tool is considered part of the tool itself.
tool.item_flags |= ABSTRACT
atoms[reference] = tool
return tool

View File

@@ -67,7 +67,7 @@
for(var/item_to_remove in items)
var/obj/item/module_item = locate(item_to_remove) in borg.model.modules
if (module_item)
borg.model.remove_module(module_item, TRUE)
borg.model.remove_module(module_item)
return TRUE
/obj/item/borg/upgrade/rename
@@ -162,8 +162,8 @@
model_type = list(/obj/item/robot_model/miner)
model_flags = BORG_MODEL_MINER
items_to_add = list(/obj/item/pickaxe/drill/cyborg/diamond)
items_to_remove = list(/obj/item/pickaxe/drill/cyborg, /obj/item/shovel)
items_to_add = list(/obj/item/pickaxe/drill/diamonddrill)
items_to_remove = list(/obj/item/pickaxe/drill, /obj/item/shovel)
/obj/item/borg/upgrade/soh
name = "mining cyborg satchel of holding"
@@ -185,7 +185,7 @@
model_flags = BORG_MODEL_JANITOR
items_to_add = list(/obj/item/storage/bag/trash/bluespace/cyborg)
items_to_remove = list(/obj/item/storage/bag/trash/cyborg)
items_to_remove = list(/obj/item/storage/bag/trash)
/obj/item/borg/upgrade/amop
name = "janitor cyborg advanced mop"
@@ -195,8 +195,8 @@
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
items_to_add = list(/obj/item/mop/advanced/cyborg)
items_to_remove = list(/obj/item/mop/cyborg)
items_to_add = list(/obj/item/mop/advanced)
items_to_remove = list(/obj/item/mop)
/obj/item/borg/upgrade/prt
name = "janitor cyborg plating repair tool"
@@ -216,7 +216,7 @@
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
items_to_add = list(/obj/item/plunger/cyborg)
items_to_add = list(/obj/item/plunger)
/obj/item/borg/upgrade/high_capacity_light_replacer
name = "janitor cyborg high capacity replacer"
@@ -226,8 +226,8 @@
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
items_to_add = list (/obj/item/lightreplacer/cyborg/advanced)
items_to_remove = list(/obj/item/lightreplacer/cyborg)
items_to_add = list (/obj/item/lightreplacer/advanced)
items_to_remove = list(/obj/item/lightreplacer)
/obj/item/borg/upgrade/syndicate
name = "illegal equipment module"
@@ -741,7 +741,7 @@
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
items_to_add = list(/obj/item/pushbroom/cyborg)
items_to_add = list(/obj/item/pushbroom)
/obj/item/borg/upgrade/condiment_synthesizer
name = "Service Cyborg Condiment Synthesiser"

View File

@@ -73,10 +73,6 @@
icon_state = "[initial(icon_state)]"
return ..()
/obj/item/storage/bag/trash/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/storage/bag/trash/filled/PopulateContents()
. = ..()
for(var/i in 1 to rand(1, 7))
@@ -345,6 +341,10 @@
I_copy.layer = FLOAT_LAYER
. += I_copy
/obj/item/storage/bag/tray/cyborg_unequip(mob/user)
. = ..()
atom_storage.remove_all(drop_location())
/obj/item/storage/bag/tray/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
update_appearance()

View File

@@ -124,6 +124,11 @@
dyn_explosion(src, plasmaAmount/5, explosion_cause = src) // 20 plasma in a standard welder has a 4 power explosion. no breaches, but enough to kill/dismember holder
qdel(src)
/obj/item/weldingtool/cyborg_unequip(mob/user)
if(!isOn())
return
switched_on(user)
/obj/item/weldingtool/use_tool(atom/target, mob/living/user, delay, amount, volume, datum/callback/extra_checks)
var/mutable_appearance/sparks = mutable_appearance('icons/effects/welding_effect.dmi', "welding_sparks", GASFIRE_LAYER, src, ABOVE_LIGHTING_PLANE)
target.add_overlay(sparks)
@@ -344,12 +349,6 @@
icon_state = "indwelder_cyborg"
toolspeed = 0.5
/obj/item/weldingtool/largetank/cyborg/cyborg_unequip(mob/user)
if(!isOn())
return
switched_on(user)
/obj/item/weldingtool/mini
name = "emergency welding tool"
desc = "A miniature welder used during emergencies."

View File

@@ -193,7 +193,3 @@
layer_mode_sprite = "reinforced_plunger_layer"
custom_premium_price = PAYCHECK_CREW * 8
/obj/item/plunger/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)

View File

@@ -74,10 +74,6 @@
flags_1 = NONE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/obj/item/pinpointer/syndicate_cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/pinpointer/syndicate_cyborg/cyborg_unequip(mob/user)
if(!active)
return

View File

@@ -291,6 +291,7 @@
/obj/item/assembly/flash/cyborg/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
return
/obj/item/assembly/flash/cyborg/screwdriver_act(mob/living/user, obj/item/I)
return

View File

@@ -49,8 +49,9 @@
/// The range of the scanner in tiles.
var/range = 7
/obj/item/t_scanner/adv_mining_scanner/cyborg
shut_off_on_unequip = FALSE
//get no effects from the t-ray scanner, which auto-shuts off.
/obj/item/t_scanner/adv_mining_scanner/cyborg_unequip(mob/user)
return
/obj/item/t_scanner/adv_mining_scanner/cyborg/Initialize(mapload)
. = ..()

View File

@@ -73,15 +73,6 @@
hitsound = 'sound/items/weapons/drill.ogg'
desc = "An electric mining drill for the especially scrawny."
/obj/item/pickaxe/drill/cyborg
name = "cyborg mining drill"
desc = "An integrated electric mining drill."
flags_1 = NONE
/obj/item/pickaxe/drill/cyborg/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
/obj/item/pickaxe/drill/diamonddrill
name = "diamond-tipped mining drill"
icon_state = "diamonddrill"
@@ -89,12 +80,6 @@
toolspeed = 0.2
desc = "Yours is the drill that will pierce the heavens!"
/obj/item/pickaxe/drill/cyborg/diamond //This is the BORG version!
name = "diamond-tipped cyborg mining drill" //To inherit the NODROP_1 flag, and easier to change borg specific drill mechanics.
icon_state = "diamonddrill"
inhand_icon_state = "diamonddrill"
toolspeed = 0.2
/obj/item/pickaxe/drill/jackhammer
name = "sonic jackhammer"
icon_state = "jackhammer"

View File

@@ -379,53 +379,63 @@
//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC, newloc is for signal handling checks only which hints where you want to move the object after removal
//item MUST BE FORCEMOVE'D OR QDEL'D
/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE, atom/newloc = src)
return doUnEquip(I, force, newloc, TRUE, idrop, silent = TRUE)
//DO NOT CALL THIS PROC
//use one of the above 4 helper procs
//you may override it, but do not modify the args
/mob/proc/doUnEquip(obj/item/I, force, atom/newloc, no_move, invdrop = TRUE, silent = FALSE) //Force overrides TRAIT_NODROP for things like wizarditis and admin undress.
//Use no_move if the item is just gonna be immediately moved afterward
//Invdrop is used to prevent stuff in pockets dropping. only set to false if it's going to immediately be replaced
/mob/proc/temporarilyRemoveItemFromInventory(obj/item/item_dropping, force = FALSE, idrop = TRUE, atom/newloc = src)
return doUnEquip(item_dropping, force, newloc, TRUE, idrop, silent = TRUE)
/**
* ## doUnEquip
* First and most importantly, DO NOT CALL THIS PROC
* Use one of the above 4 helper procs instead!!
* you may override it, but do not modify the args.
* Returns TRUE if it managed to unequip, FALSE if it can't.
* Args:
* item_dropping - The item that we're unequipping.
* force - overrides TRAIT_NODROP for things like wizarditis and admin undress.
* newloc - The location we're dropping the item into, this could be a turf, storage, mob, etc.
* no_move - if the item is just gonna be immediately moved afterward
* invdrop - Arg passed to signals, prevents stuff in pockets dropping. Only set to false if it's going to immediately be replaced
* silent - Arg passed to dropped() and signals, muting things like drop sound.
*/
/mob/proc/doUnEquip(obj/item/item_dropping, force, atom/newloc, no_move, invdrop = TRUE, silent = FALSE)
PROTECTED_PROC(TRUE)
if(!I) //If there's nothing to drop, the drop is automatically successful. If(unEquip) should generally be used to check for TRAIT_NODROP.
if(!item_dropping) //If there's nothing to drop, the drop is automatically successful. If(unEquip) should generally be used to check for TRAIT_NODROP.
return TRUE
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
if(HAS_TRAIT(item_dropping, TRAIT_NODROP) && !force)
return FALSE
if((SEND_SIGNAL(I, COMSIG_ITEM_PRE_UNEQUIP, force, newloc, no_move, invdrop, silent) & COMPONENT_ITEM_BLOCK_UNEQUIP) && !force)
if((SEND_SIGNAL(item_dropping, COMSIG_ITEM_PRE_UNEQUIP, force, newloc, no_move, invdrop, silent) & COMPONENT_ITEM_BLOCK_UNEQUIP) && !force)
return FALSE
var/hand_index = get_held_index_of_item(I)
var/hand_index = get_held_index_of_item(item_dropping)
if(hand_index)
held_items[hand_index] = null
update_held_items()
if(!I)
if(!item_dropping)
return FALSE
if(client)
client.screen -= I
client.screen -= item_dropping
if(observers?.len)
for(var/mob/dead/observe as anything in observers)
if(observe.client)
observe.client.screen -= I
observe.client.screen -= item_dropping
I.layer = initial(I.layer)
SET_PLANE_EXPLICIT(I, initial(I.plane), newloc)
I.appearance_flags &= ~NO_CLIENT_COLOR
if(!no_move && !(I.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
item_dropping.layer = initial(item_dropping.layer)
SET_PLANE_EXPLICIT(item_dropping, initial(item_dropping.plane), newloc)
item_dropping.appearance_flags &= ~NO_CLIENT_COLOR
if(!no_move && !(item_dropping.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
if (isnull(newloc))
I.moveToNullspace()
item_dropping.moveToNullspace()
else
I.forceMove(newloc)
item_dropping.forceMove(newloc)
I.dropped(src, silent)
SEND_SIGNAL(I, COMSIG_ITEM_POST_UNEQUIP, force, newloc, no_move, invdrop, silent)
SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, I, force, newloc, no_move, invdrop, silent)
item_dropping.dropped(src, silent)
SEND_SIGNAL(item_dropping, COMSIG_ITEM_POST_UNEQUIP, force, newloc, no_move, invdrop, silent)
SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, item_dropping, force, newloc, no_move, invdrop, silent)
return TRUE
/**

View File

@@ -1,12 +1,12 @@
// Drone inventory procs
/mob/living/basic/drone/doUnEquip(obj/item/item, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
/mob/living/basic/drone/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
if(..())
update_held_items()
if(item == head)
if(item_dropping == head)
head = null
update_worn_head()
if(item == internal_storage)
if(item_dropping == internal_storage)
internal_storage = null
update_inv_internal_storage()
return TRUE

View File

@@ -36,12 +36,12 @@
// Bullshit related to having a fake pocket begins here
/mob/living/basic/guardian/dextrous/doUnEquip(obj/item/equipped_item, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
/mob/living/basic/guardian/dextrous/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
. = ..()
if (!.)
return FALSE
update_held_items()
if(equipped_item == internal_storage)
if(item_dropping == internal_storage)
internal_storage = null
update_inv_internal_storage()
return TRUE

View File

@@ -1,3 +1,3 @@
//can't unequip since it can't equip anything
/mob/living/carbon/alien/larva/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
/mob/living/carbon/alien/larva/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
return

View File

@@ -208,12 +208,12 @@
/mob/living/carbon/human/get_equipped_speed_mod_items()
return ..() - list(l_store, r_store, s_store)
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
/mob/living/carbon/human/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
. = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should.
if(!. || !I)
if(!. || !item_dropping)
return
var/not_handled = FALSE //if we actually unequipped an item, this is because we dont want to run this proc twice, once for carbons and once for humans
if(I == wear_suit)
if(item_dropping == wear_suit)
if(s_store && invdrop)
dropItemToGround(s_store, TRUE) //It makes no sense for your suit storage to stay on you if you drop your suit.
if(wear_suit.breakouttime) //when unequipping a straightjacket
@@ -223,7 +223,7 @@
wear_suit = null
if(!QDELETED(src)) //no need to update we're getting deleted anyway
update_worn_oversuit()
else if(I == w_uniform)
else if(item_dropping == w_uniform)
w_uniform = null
update_suit_sensors()
if(!QDELETED(src))
@@ -237,43 +237,43 @@
dropItemToGround(wear_id)
if(belt && !can_equip(belt, ITEM_SLOT_BELT, TRUE, ignore_equipped = TRUE))
dropItemToGround(belt)
else if(I == gloves)
else if(item_dropping == gloves)
gloves = null
if(!QDELETED(src))
update_worn_gloves()
else if(I == glasses)
else if(item_dropping == glasses)
glasses = null
var/obj/item/clothing/glasses/old_glasses = I
var/obj/item/clothing/glasses/old_glasses = item_dropping
if(old_glasses.vision_flags || old_glasses.invis_override || old_glasses.invis_view || !isnull(old_glasses.lighting_cutoff))
update_sight()
if(!QDELETED(src))
update_worn_glasses()
else if(I == ears)
else if(item_dropping == ears)
ears = null
if(!QDELETED(src))
update_worn_ears()
else if(I == shoes)
else if(item_dropping == shoes)
shoes = null
if(!QDELETED(src))
update_worn_shoes()
else if(I == belt)
else if(item_dropping == belt)
belt = null
if(!QDELETED(src))
update_worn_belt()
else if(I == wear_id)
else if(item_dropping == wear_id)
wear_id = null
sec_hud_set_ID()
if(!QDELETED(src))
update_worn_id()
else if(I == r_store)
else if(item_dropping == r_store)
r_store = null
if(!QDELETED(src))
update_pockets()
else if(I == l_store)
else if(item_dropping == l_store)
l_store = null
if(!QDELETED(src))
update_pockets()
else if(I == s_store)
else if(item_dropping == s_store)
s_store = null
if(!QDELETED(src))
update_suit_storage()
@@ -284,7 +284,7 @@
return
update_equipment_speed_mods()
update_obscured_slots(I.flags_inv)
update_obscured_slots(item_dropping.flags_inv)
hud_used?.update_locked_slots()
/mob/living/carbon/human/toggle_internals(obj/item/tank, is_external = FALSE)

View File

@@ -215,35 +215,35 @@
/mob/living/carbon/get_equipped_speed_mod_items()
return ..() + get_equipped_items()
/mob/living/carbon/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
/mob/living/carbon/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
. = ..() //Sets the default return value to what the parent returns.
if(!. || !I) //We don't want to set anything to null if the parent returned 0.
if(!. || !item_dropping) //We don't want to set anything to null if the parent returned 0.
return
var/not_handled = FALSE //if we actually unequipped an item, this is because we dont want to run this proc twice, once for carbons and once for humans
if(I == head)
if(item_dropping == head)
head = null
if(!QDELETED(src))
update_worn_head()
else if(I == back)
else if(item_dropping == back)
back = null
if(!QDELETED(src))
update_worn_back()
else if(I == wear_mask)
else if(item_dropping == wear_mask)
wear_mask = null
if(!QDELETED(src))
update_worn_mask()
else if(I == wear_neck)
else if(item_dropping == wear_neck)
wear_neck = null
if(!QDELETED(src))
update_worn_neck(I)
else if(I == handcuffed)
update_worn_neck(item_dropping)
else if(item_dropping == handcuffed)
set_handcuffed(null)
if(buckled?.buckle_requires_restraints)
buckled.unbuckle_mob(src)
if(!QDELETED(src))
update_handcuffed()
else if(I == legcuffed)
else if(item_dropping == legcuffed)
legcuffed = null
if(!QDELETED(src))
update_worn_legcuffs()
@@ -251,7 +251,7 @@
not_handled = TRUE
// Not an else-if because we're probably equipped in another slot
if(I == internal && (QDELETED(src) || QDELETED(I) || I.loc != src))
if(item_dropping == internal && (QDELETED(src) || QDELETED(item_dropping) || item_dropping.loc != src))
cutoff_internals()
if(!QDELETED(src))
update_mob_action_buttons(UPDATE_BUTTON_STATUS)
@@ -260,7 +260,7 @@
return
update_equipment_speed_mods()
update_obscured_slots(I.flags_inv)
update_obscured_slots(item_dropping.flags_inv)
hud_used?.update_locked_slots()
/// Returns TRUE if an air tank compatible helmet is equipped.

View File

@@ -41,96 +41,56 @@
to_chat(src, span_warning("Deactivate a module first!"))
return FALSE
return equip_module_to_slot(item_module, first_free_slot)
return put_in_hand(item_module, first_free_slot)
/**
* Is passed an item and a module slot. Equips the item to that borg slot.
*
* Arguments
* * item_module - the item being equipped to a slot
* * module_num - the slot number being equipped to.
*/
/mob/living/silicon/robot/proc/equip_module_to_slot(obj/item/item_module, module_num)
var/storage_was_closed = FALSE //Just to be consistant and all
//spawning a clientless borg won't init its hud, much like with all mobs, so don't bother showing what's not there
if(hud_used && !shown_robot_modules) //Tools may be invisible if the collection is hidden
hud_used.toggle_show_robot_modules()
storage_was_closed = TRUE
switch(module_num)
if(BORG_CHOOSE_MODULE_ONE)
item_module.screen_loc = inv1.screen_loc
if(BORG_CHOOSE_MODULE_TWO)
item_module.screen_loc = inv2.screen_loc
if(BORG_CHOOSE_MODULE_THREE)
item_module.screen_loc = inv3.screen_loc
held_items[module_num] = item_module
/mob/living/silicon/robot/put_in_hand(obj/item/item_module, hand_index, forced = FALSE, ignore_anim = TRUE, visuals_only = FALSE)
. = ..()
if(!.)
return
item_module.mouse_opacity = initial(item_module.mouse_opacity)
SET_PLANE_EXPLICIT(item_module, ABOVE_HUD_PLANE, src)
item_module.forceMove(src)
observer_screen_update(item_module)
if(istype(item_module, /obj/item/borg/sight))
var/obj/item/borg/sight/borg_sight = item_module
sight_mode |= borg_sight.sight_mode
update_sight()
///Helper for cyborgs unequipping things.
/mob/living/silicon/robot/proc/deactivate_module(obj/item/item_module)
transferItemToLoc(item_module, newloc = model)
observer_screen_update(item_module, TRUE)
/mob/living/silicon/robot/doUnEquip(obj/item/item_dropping, force, atom/newloc, no_move, invdrop, silent)
//borgs can drop items that aren't part of the module (used for apparatus modules, the stored item isn't a module).
if(isnull(model) || !(item_dropping in model.modules))
return ..()
if(hud_used && storage_was_closed)
hud_used.toggle_show_robot_modules()
item_module.on_equipped(src, ITEM_SLOT_HANDS)
return TRUE
if(newloc != model)
to_chat(src, span_notice("You can't drop your [item_dropping.name] module."))
return FALSE
/**
* Unequips item item_module from slot module_num. Deletes it if delete_after = TRUE.
*
* Arguments
* * item_module - the item being unequipped
* * module_num - the slot number being unequipped.
*/
/mob/living/silicon/robot/proc/unequip_module_from_slot(obj/item/item_module, module_num)
if(QDELETED(item_module))
CRASH("unequip_module_from_slot called with improper item_module")
var/module_num = get_selected_module()
. = ..()
if(!.)
return
item_dropping.mouse_opacity = MOUSE_OPACITY_OPAQUE
//this is the cyborg equivalent of dropped(), though we call that too in doUnEquip.
item_dropping.cyborg_unequip(src)
deselect_module(module_num)
if(!(item_module in model.modules))
CRASH("unequip_module_from_slot called with item_module not in model.modules")
item_module.mouse_opacity = MOUSE_OPACITY_OPAQUE
/mob/living/silicon/robot/update_held_items()
. = ..()
if(isnull(client) || isnull(hud_used) || hud_used.hud_version == HUD_STYLE_NOHUD)
return
var/turf/our_turf = get_turf(src)
if(istype(item_module, /obj/item/storage/bag/tray/))
item_module.atom_storage.remove_all(loc)
if(istype(item_module, /obj/item/borg/sight))
var/obj/item/borg/sight/borg_sight = item_module
sight_mode &= ~borg_sight.sight_mode
update_sight()
for(var/obj/item/held in held_items)
SET_PLANE(held, ABOVE_HUD_PLANE, our_turf)
if(held_items[BORG_CHOOSE_MODULE_ONE] == held)
held.screen_loc = inv1.screen_loc
else if(held_items[BORG_CHOOSE_MODULE_TWO] == held)
held.screen_loc = inv2.screen_loc
else if(held_items[BORG_CHOOSE_MODULE_THREE] == held)
held.screen_loc = inv3.screen_loc
client.screen |= held
if(client)
client.screen -= item_module
if(module_active == item_module)
module_active = null
switch(module_num)
if(BORG_CHOOSE_MODULE_ONE)
if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
inv1.icon_state = initial(inv1.icon_state)
if(BORG_CHOOSE_MODULE_TWO)
if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
inv2.icon_state = initial(inv2.icon_state)
if(BORG_CHOOSE_MODULE_THREE)
if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
inv3.icon_state = initial(inv3.icon_state)
if(item_module.item_flags & DROPDEL)
item_module.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly
held_items[module_num] = null
item_module.cyborg_unequip(src)
item_module.forceMove(model) //Return item to configuration so it appears in its contents, so it can be taken out again.
observer_screen_update(item_module, FALSE)
hud_used?.update_robot_modules_display()
return TRUE
/mob/living/silicon/robot/put_in_hand_check(obj/item/item_equipping)
return (item_equipping in model.modules)
/**
* Breaks the slot number, changing the icon.
@@ -143,7 +103,7 @@
return FALSE
if(held_items[module_num]) //If there's a held item, unequip it first.
if(!unequip_module_from_slot(held_items[module_num], module_num)) //If we fail to unequip it, then don't continue
if(!deactivate_module(held_items[module_num])) //If we fail to unequip it, then don't continue
return FALSE
switch(module_num)
@@ -238,40 +198,19 @@
for(var/cyborg_slot in 1 to 3)
repair_cyborg_slot(cyborg_slot)
/**
* Updates the observers's screens with cyborg itemss.
* Arguments
* * item_module - the item being added or removed from the screen
* * add - whether or not the item is being added, or removed.
*/
/mob/living/silicon/robot/proc/observer_screen_update(obj/item/item_module, add = TRUE)
if(observers?.len)
for(var/M in observers)
var/mob/dead/observe = M
if(observe.client && observe.client.eye == src)
if(add)
observe.client.screen += item_module
else
observe.client.screen -= item_module
else
observers -= observe
if(!observers.len)
observers = null
break
/**
* Unequips the active held item, if there is one.
*/
/mob/living/silicon/robot/proc/uneq_active()
if(module_active)
unequip_module_from_slot(module_active, get_selected_module())
deactivate_module(module_active)
// Technically none of the items are dropped, only unequipped
/mob/living/silicon/robot/drop_all_held_items()
for(var/cyborg_slot in 1 to length(held_items))
if(!held_items[cyborg_slot])
continue
unequip_module_from_slot(held_items[cyborg_slot], cyborg_slot)
deactivate_module(held_items[cyborg_slot])
/**
* Checks if the item is currently in a slot.
@@ -318,8 +257,7 @@
/mob/living/silicon/robot/proc/get_selected_module()
if(module_active)
return held_items.Find(module_active)
return 0
return FALSE
/**
* Selects the module in the slot module_num.
@@ -351,14 +289,11 @@
/mob/living/silicon/robot/proc/deselect_module(module_num)
switch(module_num)
if(BORG_CHOOSE_MODULE_ONE)
if(module_active == held_items[module_num])
inv1.icon_state = initial(inv1.icon_state)
inv1.icon_state = initial(inv1.icon_state)
if(BORG_CHOOSE_MODULE_TWO)
if(module_active == held_items[module_num])
inv2.icon_state = initial(inv2.icon_state)
inv2.icon_state = initial(inv2.icon_state)
if(BORG_CHOOSE_MODULE_THREE)
if(module_active == held_items[module_num])
inv3.icon_state = initial(inv3.icon_state)
inv3.icon_state = initial(inv3.icon_state)
module_active = null
return TRUE
@@ -406,3 +341,23 @@
/mob/living/silicon/robot/can_hold_items(obj/item/I)
return (I && (I in model.modules)) //Only if it's part of our model.
/**
* ## Please do not use
* Updates the observers's screens with cyborg items.
* Currently inventory code handling for observers is tied to carbon (get_held_overlays), meaning this snowflake code for borgs is
* necessary so observers watching borgs don't bug out. Once that's moved to the living, replace this with it.
* Removing from the screen is handled by 'doUnEquip'
* Arg:
* * item_module - the item being added to the screen.
*/
/mob/living/silicon/robot/proc/observer_screen_update(obj/item/item_module)
if(!observers?.len)
return
for(var/mob/dead/observe as anything in observers)
if(!observe.client || observe.client.eye != src)
observers -= observe
if(!observers.len)
observers = null
return
observe.client.screen += item_module

View File

@@ -21,10 +21,6 @@
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GOT_DAMPENED), PROC_REF(on_dampen))
robot_modules_background = new()
robot_modules_background.icon_state = "block"
SET_PLANE_EXPLICIT(robot_modules_background, HUD_PLANE, src)
inv1 = new /atom/movable/screen/robot/module1()
inv2 = new /atom/movable/screen/robot/module2()
inv3 = new /atom/movable/screen/robot/module3()
@@ -727,15 +723,11 @@
/mob/living/silicon/robot/proc/ResetModel()
SEND_SIGNAL(src, COMSIG_BORG_SAFE_DECONSTRUCT)
drop_all_held_items()
shown_robot_modules = FALSE
for(var/obj/item/storage/bag in model.contents) // drop all of the items that may be stored by the cyborg
for(var/obj/item in bag)
item.forceMove(drop_location())
if(hud_used)
hud_used.update_robot_modules_display()
if (hasExpanded)
hasExpanded = FALSE
update_transform(0.5)

View File

@@ -447,6 +447,8 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
else
GLOB.lawchanges.Add("[time] <B>:</B> [name]([key]) emagged by external event.")
model.rebuild_modules()
INVOKE_ASYNC(src, PROC_REF(borg_emag_end), user)
return TRUE

View File

@@ -17,6 +17,7 @@
hud_type = /datum/hud/robot
unique_name = TRUE
mouse_drop_zone = TRUE
held_items = list(null, null, null) //we use held_items for the module holding, because that makes sense to do!
default_hand_amount = 3
///Represents the cyborg's model (engineering, medical, etc.)
@@ -74,10 +75,6 @@
var/atom/movable/screen/inv3 = null
var/atom/movable/screen/hands = null
///Used to determine whether they have the module menu shown or not
var/shown_robot_modules = FALSE
var/atom/movable/screen/robot_modules_background
///Lamp button reference
var/atom/movable/screen/robot/lamp/lampButton
@@ -90,7 +87,6 @@
// Modules (tool slots)
var/obj/item/module_active = null
held_items = list(null, null, null) //we use held_items for the module holding, because that makes sense to do!
///For checking which modules are disabled or not.
var/disabled_modules

View File

@@ -15,6 +15,7 @@
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
///Host of this model
var/mob/living/silicon/robot/robot
///Icon of the module selection screen
@@ -56,17 +57,20 @@
/obj/item/robot_model/Initialize(mapload)
. = ..()
robot = loc
create_storage(storage_type = /datum/storage/cyborg_internal_storage)
//src is what we store items visible to borgs, we'll store things in the bot itself otherwise.
for(var/path in basic_modules)
var/obj/item/new_module = new path(src)
var/obj/item/new_module = new path(robot)
basic_modules += new_module
basic_modules -= path
for(var/path in emag_modules)
var/obj/item/new_module = new path(src)
var/obj/item/new_module = new path(robot)
emag_modules += new_module
emag_modules -= path
if(check_holidays(ICE_CREAM_DAY) && !(locate(/obj/item/borg/lollipop) in basic_modules))
basic_modules += new /obj/item/borg/lollipop/ice_cream(src)
basic_modules += new /obj/item/borg/lollipop/ice_cream(robot)
/obj/item/robot_model/Destroy()
basic_modules.Cut()
@@ -101,22 +105,21 @@
if(added_module.loc != src)
added_module.forceMove(src)
modules += added_module
ADD_TRAIT(added_module, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
added_module.mouse_opacity = MOUSE_OPACITY_OPAQUE
added_module.obj_flags |= ABSTRACT
if(nonstandard)
added_modules += added_module
if(requires_rebuild)
rebuild_modules()
return added_module
/obj/item/robot_model/proc/remove_module(obj/item/removed_module, delete_after)
/obj/item/robot_model/proc/remove_module(obj/item/removed_module)
basic_modules -= removed_module
modules -= removed_module
emag_modules -= removed_module
added_modules -= removed_module
rebuild_modules()
if(delete_after)
qdel(removed_module)
qdel(removed_module)
/obj/item/robot_model/proc/rebuild_modules() //builds the usable module list from the modules we have
var/mob/living/silicon/robot/cyborg = loc
@@ -124,7 +127,9 @@
return
var/list/held_modules = cyborg.held_items.Copy()
var/active_module = cyborg.module_active
cyborg.drop_all_held_items()
//move everything out of the model's inventory
for(var/obj/item/module as anything in modules)
module.forceMove(robot)
modules = list()
for(var/obj/item/module as anything in basic_modules)
add_module(module, FALSE, FALSE)
@@ -134,12 +139,10 @@
for(var/obj/item/module as anything in added_modules)
add_module(module, FALSE, FALSE)
for(var/obj/item/module as anything in held_modules & modules)
cyborg.equip_module_to_slot(module, held_modules.Find(module))
cyborg.put_in_hand(module, held_modules.Find(module))
if(active_module)
cyborg.select_module(held_modules.Find(active_module))
if(cyborg.hud_used)
cyborg.hud_used.update_robot_modules_display()
atom_storage.refresh_views()
///Restocks things that don't take mats, generally at a power cost. Returns True if anything was restocked/replaced, and False otherwise.
/obj/item/robot_model/proc/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1)
@@ -229,7 +232,6 @@
/obj/item/robot_model/proc/transform_to(new_config_type, forced = FALSE, transform = TRUE)
var/mob/living/silicon/robot/cyborg = loc
var/obj/item/robot_model/new_model = new new_config_type(cyborg)
new_model.robot = cyborg
if(!new_model.be_transformed_to(src, forced))
qdel(new_model)
return
@@ -320,8 +322,6 @@
cyborg.updatehealth()
cyborg.update_icons()
cyborg.notify_ai(AI_NOTIFICATION_NEW_MODEL)
if(cyborg.hud_used)
cyborg.hud_used.update_robot_modules_display()
SSblackbox.record_feedback("tally", "cyborg_modules", 1, cyborg.model)
/**
@@ -418,13 +418,13 @@
/obj/item/crowbar/cyborg,
/obj/item/stack/tile/iron/base/cyborg, // haha jani will have old tiles >:D
/obj/item/soap/nanotrasen/cyborg,
/obj/item/storage/bag/trash/cyborg,
/obj/item/storage/bag/trash,
/obj/item/melee/flyswatter,
/obj/item/extinguisher/mini,
/obj/item/mop/cyborg,
/obj/item/mop,
/obj/item/reagent_containers/cup/bucket,
/obj/item/paint/paint_remover,
/obj/item/lightreplacer/cyborg,
/obj/item/lightreplacer,
/obj/item/holosign_creator,
/obj/item/reagent_containers/spray/cyborg_drying,
/obj/item/wirebrush,
@@ -716,7 +716,7 @@
/obj/item/assembly/flash/cyborg,
/obj/item/borg/sight/meson,
/obj/item/storage/bag/ore/cyborg,
/obj/item/pickaxe/drill/cyborg,
/obj/item/pickaxe/drill,
/obj/item/shovel,
/obj/item/crowbar/cyborg,
/obj/item/weldingtool/mini,
@@ -874,7 +874,7 @@
var/mob/living/silicon/robot/cyborg = loc
cyborg.faction -= FACTION_SILICON //ai turrets
/obj/item/robot_model/syndicate/remove_module(obj/item/removed_module, delete_after)
/obj/item/robot_model/syndicate/remove_module(obj/item/removed_module)
..()
var/mob/living/silicon/robot/cyborg = loc
cyborg.faction |= FACTION_SILICON //ai is your bff now!
@@ -958,8 +958,8 @@
/obj/item/robot_model/syndicate/kiltborg/do_transform_delay() //AUTO-EQUIPPING THESE TOOLS ANY EARLIER CAUSES RUNTIMES OH YEAH
. = ..()
robot.equip_module_to_slot(locate(/obj/item/claymore/highlander/robot) in basic_modules, 1)
robot.equip_module_to_slot(locate(/obj/item/pinpointer/nuke) in basic_modules, 2)
robot.equip_to_slot(locate(/obj/item/claymore/highlander/robot) in basic_modules, 1)
robot.equip_to_slot(locate(/obj/item/pinpointer/nuke) in basic_modules, 2)
robot.place_on_head(new /obj/item/clothing/head/beret/highlander(robot)) //THE ONLY PART MORE IMPORTANT THAN THE SWORD IS THE HAT
ADD_TRAIT(robot.hat, TRAIT_NODROP, HIGHLANDER_TRAIT)

View File

@@ -141,11 +141,11 @@ GLOBAL_VAR_INIT(animals_spawned, 0)
return
if(!user.combat_mode || (I.item_flags & NOBLUDGEON))
if((I.item_flags & ABSTRACT) || !user.temporarilyRemoveItemFromInventory(I))
if(I.item_flags & ABSTRACT)
return
place_item_in_disposal(I, user)
update_appearance()
return TRUE //no afterattack
if(place_item_in_disposal(I, user))
update_appearance()
return TRUE //no afterattack
else
return ..()
@@ -182,9 +182,14 @@ GLOBAL_VAR_INIT(animals_spawned, 0)
visible_message(span_warning("[new_subject] climbs out of [src]!"))
/// Moves an item into the diposal bin
/obj/machinery/disposal/proc/place_item_in_disposal(obj/item/I, mob/user)
I.forceMove(src)
user.visible_message(span_notice("[user.name] places \the [I] into \the [src]."), span_notice("You place \the [I] into \the [src]."))
/obj/machinery/disposal/proc/place_item_in_disposal(obj/item/disposing_item, mob/user)
if(!user.transferItemToLoc(disposing_item, newloc = src))
return FALSE
user.visible_message(
span_notice("[user.name] places \the [disposing_item] into \the [src]."),
span_notice("You place \the [disposing_item] into \the [src]."),
)
return TRUE
/// Mouse drop another mob or self
/obj/machinery/disposal/mouse_drop_receive(atom/target, mob/living/user, params)
@@ -712,7 +717,7 @@ GLOBAL_VAR_INIT(animals_spawned, 0)
if(!items_to_sweep)
return
for (var/obj/item/garbage in items_to_sweep)
garbage.forceMove(src)
user.transferItemToLoc(garbage, src)
items_to_sweep.Cut()

View File

@@ -379,10 +379,6 @@
. = ..()
UnregisterSignal(user, COMSIG_SURGERY_STARTING)
/obj/item/surgical_processor/cyborg_unequip(mob/user)
. = ..()
UnregisterSignal(user, COMSIG_SURGERY_STARTING)
/obj/item/surgical_processor/interact_with_atom(atom/design_holder, mob/living/user, list/modifiers)
if(!istype(design_holder, /obj/item/disk/surgery) && !istype(design_holder, /obj/machinery/computer/operating))
return NONE

View File

@@ -11,8 +11,7 @@
omnitool = tool
break
TEST_ASSERT_NOTNULL(omnitool, "Could not find /obj/item/borg/cyborg_omnitool/engineering in borg inbuilt modules!")
borg.shown_robot_modules = TRUE //stops hud from updating which would runtime cause our mob does not have one
borg.equip_module_to_slot(omnitool, 1)
borg.put_in_hand(omnitool, 1)
borg.select_module(1)
//these must match
@@ -30,4 +29,4 @@
TEST_ASSERT_EQUAL(test_frame.state, FRAME_STATE_EMPTY, "Machine frame's wires were not cut by the borg omnitool wirecutters!")
//unequip
borg.unequip_module_from_slot(omnitool, 1)
borg.drop_all_held_items()

View File

@@ -2001,6 +2001,7 @@
#include "code\datums\storage\subtypes\bags.dm"
#include "code\datums\storage\subtypes\belts.dm"
#include "code\datums\storage\subtypes\boxes.dm"
#include "code\datums\storage\subtypes\cyborg.dm"
#include "code\datums\storage\subtypes\dufflebags.dm"
#include "code\datums\storage\subtypes\holsters.dm"
#include "code\datums\storage\subtypes\lockboxes.dm"