diff --git a/code/__DEFINES/color/colors.dm b/code/__DEFINES/color/colors.dm
index a8896e3f01..d88320d094 100644
--- a/code/__DEFINES/color/colors.dm
+++ b/code/__DEFINES/color/colors.dm
@@ -123,6 +123,8 @@
#define COLOR_THEME_OPERATIVE "#B8221F"
#define COLOR_THEME_GLASS "#75A4C4"
#define COLOR_THEME_CLOCKWORK "#CFBA47"
+#define COLOR_THEME_TRASENKNOX "#3ce375"
+#define COLOR_THEME_DETECTIVE "#c7b08b"
///Colors for eigenstates
#define COLOR_PERIWINKLEE "#9999FF"
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index 94755e3bd2..13882afb77 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -230,3 +230,6 @@
#define SCRN_OBJ_IN_LIST "list"
/// In the collapseable palette
#define SCRN_OBJ_IN_PALETTE "palette"
+
+/// The filter name for the hover outline
+#define HOVER_OUTLINE_FILTER "hover_outline"
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 2fbf087288..047888e420 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -4,6 +4,13 @@
/proc/cmp_numeric_asc(a,b)
return a - b
+// please don't ask
+/proc/cmp_numeric_text_desc(a, b)
+ return text2num(b) - text2num(a)
+
+/proc/cmp_numeric_text_asc(a, b)
+ return text2num(a) - text2num(b)
+
/proc/cmp_text_asc(a,b)
return sorttext(b,a)
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 89eb7b75c3..130243db79 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -689,5 +689,11 @@
return 'modular_citadel/icons/ui/screen_operative.dmi'
if('icons/mob/screen_clockwork.dmi')
return 'modular_citadel/icons/ui/screen_clockwork.dmi'
+ if('icons/mob/screen_glass.dmi')
+ return 'modular_citadel/icons/ui/screen_glass.dmi'
+ if('icons/mob/screen_trasenknox.dmi')
+ return 'modular_citadel/icons/ui/screen_trasenknox.dmi'
+ if('icons/mob/screen_detective.dmi')
+ return 'modular_citadel/icons/ui/screen_detective.dmi'
else
return 'modular_citadel/icons/ui/screen_midnight.dmi'
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 798acad4aa..53d69fe01e 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -11,7 +11,10 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
"Plasmafire" = 'icons/mob/screen_plasmafire.dmi',
"Slimecore" = 'icons/mob/screen_slimecore.dmi',
"Operative" = 'icons/mob/screen_operative.dmi',
- "Clockwork" = 'icons/mob/screen_clockwork.dmi'
+ "Glass" = 'icons/mob/screen_glass.dmi',
+ "Clockwork" = 'icons/mob/screen_clockwork.dmi',
+ "Trasen-Knox" = 'icons/mob/screen_trasenknox.dmi',
+ "Detective" = 'icons/mob/screen_detective.dmi',
))
/proc/ui_style2icon(ui_style)
@@ -646,4 +649,4 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/datum/action_group/listed/refresh_actions()
. = ..()
- owner.palette_actions.refresh_actions() // We effect them, so we gotta refresh em
+ owner?.palette_actions.refresh_actions() // We effect them, so we gotta refresh em
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 6d7c25120a..8e7953bdd5 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -324,11 +324,11 @@
using.hud = src
hotkeybuttons += using
- using = new /atom/movable/screen/rest()
- using.icon = ui_style
- using.screen_loc = ui_pull_resist
- using.hud = src
- static_inventory += using
+ rest_icon = new /atom/movable/screen/rest()
+ rest_icon.icon = ui_style
+ rest_icon.screen_loc = ui_pull_resist
+ rest_icon.hud = src
+ static_inventory += rest_icon
//END OF CIT CHANGES
using = new /atom/movable/screen/human/toggle()
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index b01199c6c2..c5d3cbca89 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -5,6 +5,12 @@
///Whether the menu is currently on the client's screen or not
var/menu_hud_status = TRUE
+/datum/hud/new_player/New(mob/dead/new_player/owner)
+ . = ..()
+ if(!owner.age_verify())
+ return
+ populate_buttons(owner)
+
/datum/hud/new_player/proc/populate_buttons(mob/dead/new_player/owner)
var/list/buttons = subtypesof(/atom/movable/screen/lobby)
for(var/button_type in buttons)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 231f8db6f0..72756c9abf 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -154,9 +154,8 @@
if(!icon_empty)
icon_empty = icon_state
- if(!hud?.mymob || !slot_id || !icon_full)
- return ..()
- icon_state = hud.mymob.get_item_by_slot(slot_id) ? icon_full : icon_empty
+ if(hud?.mymob && slot_id && icon_full)
+ icon_state = hud.mymob.get_item_by_slot(slot_id) ? icon_full : icon_empty
return ..()
/atom/movable/screen/inventory/proc/add_overlays()
@@ -207,7 +206,7 @@
. += blocked_overlay
if(held_index == hud.mymob.active_hand_index)
- . += "hand_active"
+ . += (held_index % 2) ? "lhandactive" : "rhandactive"
/atom/movable/screen/inventory/hand/Click(location, control, params)
@@ -411,6 +410,7 @@
name = "rest"
icon = 'icons/mob/screen_midnight.dmi'
icon_state = "act_rest"
+ base_icon_state = "act_rest"
plane = HUD_PLANE
/atom/movable/screen/rest/Click()
@@ -422,10 +422,7 @@
var/mob/living/user = hud?.mymob
if(!istype(user))
return ..()
- if(!user.resting)
- icon_state = "act_rest"
- else
- icon_state = "act_rest0"
+ icon_state = "[base_icon_state][user.resting ? 0 : null]"
return ..()
/atom/movable/screen/throw_catch
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index 3ed3e521e4..8e9124c306 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -1,17 +1,13 @@
/datum/component/personal_crafting/Initialize()
if(ismob(parent))
- RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
+ RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/create_mob_button)
-/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
+/datum/component/personal_crafting/proc/create_mob_button(mob/user)
var/datum/hud/H = user.hud_used
- for(var/huds in H.static_inventory)
- if(istype(huds, /atom/movable/screen/craft))
- return
- //We don't want to be stacking multiple crafting huds on relogs
var/atom/movable/screen/craft/C = new()
C.icon = H.ui_style
H.static_inventory += C
- CL.screen += C
+ user.client.screen += C
RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
/datum/component/personal_crafting
diff --git a/code/datums/elements/object_reskinning.dm b/code/datums/elements/object_reskinning.dm
index 63e8a35d05..2d994a04d0 100644
--- a/code/datums/elements/object_reskinning.dm
+++ b/code/datums/elements/object_reskinning.dm
@@ -65,7 +65,7 @@
icon = to_reskin.unique_reskin[reskin_option]["icon"] ? to_reskin.unique_reskin[reskin_option]["icon"] : to_reskin.icon,
icon_state = to_reskin.unique_reskin[reskin_option]["icon_state"] ? to_reskin.unique_reskin[reskin_option]["icon_state"] : to_reskin.icon_state)
items += list("[reskin_option]" = item_image)
- sort_list(items)
+ items = sort_list(items)
// Display to the user
var/pick = show_radial_menu(user, to_reskin, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, user, to_reskin), radius = 38, require_near = TRUE)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 1da8c7717a..6a7b78b8dd 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1356,6 +1356,11 @@
if(filter_data && filter_data[name])
return filters[filter_data.Find(name)]
+/// Returns the indice in filters of the given filter name.
+/// If it is not found, returns null.
+/atom/proc/get_filter_index(name)
+ return filter_data?.Find(name)
+
/atom/proc/remove_filter(name_or_names)
if(!filter_data)
return
diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm
index 752a5dff2c..fe216c0623 100644
--- a/code/game/objects/effects/decals/misc.dm
+++ b/code/game/objects/effects/decals/misc.dm
@@ -1,18 +1,3 @@
-/obj/effect/temp_visual/point
- name = "pointer"
- icon = 'icons/mob/screen_gen.dmi'
- icon_state = "arrow"
- layer = POINT_LAYER
- duration = 25
-
-/obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0)
- . = ..()
- var/atom/old_loc = loc
- loc = get_turf(src) // We don't want to actualy trigger anything when it moves
- pixel_x = old_loc.pixel_x
- pixel_y = old_loc.pixel_y
- invisibility = set_invis
-
//Used by spraybottles.
/obj/effect/decal/chempuff
name = "chemicals"
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 0178a8b688..ae94681f56 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -922,13 +922,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
- remove_filter("hover_outline") //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
+ remove_filter(HOVER_OUTLINE_FILTER) //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
/obj/item/MouseExited(location, control, params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
deltimer(usr.client.tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
- remove_filter("hover_outline")
+ remove_filter(HOVER_OUTLINE_FILTER)
/obj/item/proc/apply_outline(outline_color = null)
if(get(src, /mob) != usr || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
@@ -950,12 +950,16 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
if("glass")
outline_color = COLOR_THEME_GLASS
+ if("trasen-knox")
+ outline_color = COLOR_THEME_TRASENKNOX
+ if("detective")
+ outline_color = COLOR_THEME_DETECTIVE
else //this should never happen, hopefully
outline_color = COLOR_WHITE
if(color)
outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
- add_filter("hover_outline", 1, list("type" = "outline", "size" = 1, "color" = outline_color))
+ add_filter(HOVER_OUTLINE_FILTER, 1, list("type" = "outline", "size" = 1, "color" = outline_color))
// Called when a mob tries to use the item as a tool.
// Handles most checks.
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 6b9d1e8018..defdfd6402 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -217,6 +217,7 @@
/obj/item/stack/medical/gauze/cyborg
custom_materials = null
is_cyborg = TRUE
+ source = /datum/robot_energy_storage/medical
cost = 250
/obj/item/stack/medical/suture
@@ -441,6 +442,7 @@
/obj/item/stack/medical/bone_gel/cyborg
custom_materials = null
is_cyborg = TRUE
+ source = /datum/robot_energy_storage/medical
cost = 250
/obj/item/stack/medical/aloe
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 6a0ed45e30..c0fde9ce73 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -39,7 +39,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
/obj/item/stack/rods/update_icon_state()
var/amount = get_amount()
- if(amount <= 5)
+ if(amount <= 5 && amount >= 1)
icon_state = "rods-[amount]"
else
icon_state = "rods"
@@ -77,11 +77,8 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
/obj/item/stack/rods/cyborg
custom_materials = null
is_cyborg = TRUE
- cost = 250
-
-/obj/item/stack/rods/cyborg/ComponentInitialize()
- . = ..()
- AddElement(/datum/element/update_icon_blocker)
+ source = /datum/robot_energy_storage/medical
+ cost = MINERAL_MATERIAL_AMOUNT * 0.125
/obj/item/stack/rods/ten
amount = 10
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 1eb49f350c..929b572e06 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -52,7 +52,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
/obj/item/stack/sheet/glass/cyborg
custom_materials = null
is_cyborg = TRUE
- cost = 500
+ source = /datum/robot_energy_storage/glass
+ cost = MINERAL_MATERIAL_AMOUNT * 0.25
/obj/item/stack/sheet/glass/fifty
amount = 50
@@ -179,9 +180,15 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
/obj/item/stack/sheet/rglass/cyborg
custom_materials = null
is_cyborg = TRUE
- var/datum/robot_energy_storage/glasource
- var/metcost = 250
- var/glacost = 500
+ source = /datum/robot_energy_storage/metal
+ var/datum/robot_energy_storage/glasource = /datum/robot_energy_storage/glass
+ var/metcost = MINERAL_MATERIAL_AMOUNT * 0.125
+ var/glacost = MINERAL_MATERIAL_AMOUNT * 0.25
+
+/obj/item/stack/sheet/rglass/cyborg/prepare_estorage(obj/item/robot_module/module)
+ . = ..()
+ if(glasource)
+ glasource = module.get_or_create_estorage(glasource)
/obj/item/stack/sheet/rglass/cyborg/get_amount()
return min(round(source.energy / metcost), round(glasource.energy / glacost))
@@ -189,10 +196,12 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
/obj/item/stack/sheet/rglass/cyborg/use(used, transfer = FALSE) // Requires special checks, because it uses two storages
source.use_charge(used * metcost)
glasource.use_charge(used * glacost)
+ update_icon()
/obj/item/stack/sheet/rglass/cyborg/add(amount)
source.add_charge(amount * metcost)
glasource.add_charge(amount * glacost)
+ update_icon()
/obj/item/stack/sheet/rglass/get_main_recipes()
. = ..()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 070c5122c4..745fa6a321 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -164,7 +164,8 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
/obj/item/stack/sheet/metal/cyborg
custom_materials = null
is_cyborg = TRUE
- cost = 500
+ source = /datum/robot_energy_storage/metal
+ cost = MINERAL_MATERIAL_AMOUNT * 0.25
/obj/item/stack/sheet/metal/get_main_recipes()
. = ..()
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 7555763f4e..7eca36247f 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -42,6 +42,12 @@
var/matter_amount = 0
/obj/item/stack/Initialize(mapload, new_amount, merge = TRUE)
+ if(is_cyborg)
+ if(!istype(loc, /obj/item/robot_module))
+ stack_trace("Cyborg stack created outside of a robot module, deleting.")
+ return INITIALIZE_HINT_QDEL
+ prepare_estorage(loc)
+
if(new_amount != null)
amount = new_amount
while(amount > max_amount)
@@ -383,7 +389,9 @@
if(check && zero_amount())
return FALSE
if (is_cyborg)
- return source.use_charge(used * cost)
+ . = source.use_charge(used * cost)
+ update_icon()
+ return
if (amount < used)
return FALSE
amount -= used
@@ -486,9 +494,53 @@
return
//get amount from user
var/max = get_amount()
- var/stackmaterial = round(input(user,"How many sheets do you wish to take out of this stack? (Maximum [max])") as null|num)
- max = get_amount()
- stackmaterial = min(max, stackmaterial)
+ var/list/quick_split
+ for(var/option in list(2, 3, 4, 5, 6, 7, "One", "Five", "Ten", "Custom"))
+ var/mutable_appearance/option_display = new(src)
+ option_display.filters = null
+ option_display.cut_overlays()
+ option_display.pixel_x = 0
+ option_display.pixel_y = 0
+
+ switch(option)
+ if("Custom")
+ var/list/sort_numbers = quick_split
+ sort_numbers = sort_list(sort_numbers, /proc/cmp_numeric_text_desc)
+ option_display.maptext = MAPTEXT("?")
+ quick_split = list("Custom" = option_display)
+ quick_split += sort_numbers
+ if("One")
+ option = 1
+ option_display.maptext = MAPTEXT("1")
+ if("Five")
+ if(max > 5)
+ option = 5
+ option_display.maptext = MAPTEXT("5")
+ else
+ continue
+ if("Ten")
+ if(max > 10)
+ option = 10
+ option_display.maptext = MAPTEXT("10")
+ else
+ continue
+ else
+ if(max % option == 0)
+ option_display.maptext = MAPTEXT(max / option)
+ option = max / option
+ else
+ continue
+ if(option != "Custom")
+ LAZYSET(quick_split, "[option]", option_display)
+ var/stackmaterial
+ if(length(quick_split) <= 2)
+ stackmaterial = round(input(user, "How many sheets do you wish to take out of this stack?\nMax: [max]") as null|num)
+ else
+ stackmaterial = show_radial_menu(user, get_atom_on_turf(src), quick_split, require_near = TRUE, tooltips = TRUE)
+ if(stackmaterial == "Custom")
+ stackmaterial = round(input(user, "How many sheets do you wish to take out of this stack?\nMax: [max]") as null|num)
+ stackmaterial = isnum(stackmaterial) ? stackmaterial : text2num(stackmaterial)
+ stackmaterial = min(get_amount(), stackmaterial)
if(stackmaterial == null || stackmaterial <= 0 || !user.canUseTopic(src, BE_CLOSE, TRUE, FALSE)) //, !iscyborg(user)
return
split_stack(user, stackmaterial)
@@ -539,3 +591,12 @@
/obj/item/stack/microwave_act(obj/machinery/microwave/M)
if(istype(M) && M.dirty < 100)
M.dirty += amount
+
+/obj/item/stack/proc/prepare_estorage(obj/item/robot_module/module)
+ if(source)
+ source = module.get_or_create_estorage(source)
+
+/obj/item/stack/Moved(old_loc, dir)
+ . = ..()
+ if(isturf(loc))
+ update_icon()
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 836ef04f31..84ccdfb1c5 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -536,7 +536,8 @@
/obj/item/stack/tile/plasteel/cyborg
custom_materials = null // All other Borg versions of items have no Metal or Glass - RR
is_cyborg = TRUE
- cost = 125
+ source = /datum/robot_energy_storage/metal
+ cost = MINERAL_MATERIAL_AMOUNT * 0.0625
/obj/item/stack/tile/material
name = "floor tile"
diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm
index 9b8c0fa130..5fa4537794 100644
--- a/code/game/objects/items/stacks/wrap.dm
+++ b/code/game/objects/items/stacks/wrap.dm
@@ -40,6 +40,12 @@
resistance_flags = FLAMMABLE
grind_results = list(/datum/reagent/cellulose = 5)
+/obj/item/stack/packageWrap/cyborg
+ custom_materials = null
+ is_cyborg = TRUE
+ source = /datum/robot_energy_storage/wrapping_paper
+ cost = 1
+
/obj/item/stack/packageWrap/suicide_act(mob/living/user)
user.visible_message("[user] begins wrapping [user.p_them()]self in \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
if(use(3))
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 4ccab2df4b..70a58bcffb 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -2631,8 +2631,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/pickedui = input(user, "Choose your UI style.", "Character Preference", UI_style) as null|anything in GLOB.available_ui_styles
if(pickedui)
UI_style = pickedui
- if (parent && parent.mob && parent.mob.hud_used)
- parent.mob.hud_used.update_ui_style(ui_style2icon(UI_style))
+ if (pickedui && parent && parent.mob && parent.mob.hud_used)
+ QDEL_NULL(parent.mob.hud_used)
+ parent.mob.create_mob_hud()
+ parent.mob.hud_used.show_hud(1, parent.mob)
if("pda_style")
var/pickedPDAStyle = input(user, "Choose your PDA style.", "Character Preference", pda_style) as null|anything in GLOB.pda_styles
if(pickedPDAStyle)
@@ -3007,7 +3009,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
outline_enabled = !outline_enabled
if("outline_color")
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
- if(pickedOutlineColor != pickedOutlineColor)
+ if(pickedOutlineColor != outline_color)
outline_color = pickedOutlineColor // nullable
if("screentip_pref")
var/choice = input(user, "Choose your screentip preference", "Screentipping?", screentip_pref) as null|anything in GLOB.screentip_pref_options
diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm
index d8e54ae322..3ec466dad4 100644
--- a/code/modules/mining/equipment/marker_beacons.dm
+++ b/code/modules/mining/equipment/marker_beacons.dm
@@ -31,6 +31,12 @@ GLOBAL_LIST_INIT(marker_beacon_colors, list(
/obj/item/stack/marker_beacon/thirty //and they're bought in stacks of 1, 10, or 30
amount = 30
+/obj/item/stack/marker_beacon/cyborg
+ is_cyborg = TRUE
+ custom_materials = null
+ source = /datum/robot_energy_storage/beacon
+ cost = 1
+
/obj/item/stack/marker_beacon/Initialize(mapload)
. = ..()
update_icon()
diff --git a/code/modules/mob/living/living_mobility.dm b/code/modules/mob/living/living_mobility.dm
index 6aed30ab83..50d377abf7 100644
--- a/code/modules/mob/living/living_mobility.dm
+++ b/code/modules/mob/living/living_mobility.dm
@@ -16,6 +16,7 @@
/mob/living/proc/update_resting(update_mobility = TRUE)
if(update_mobility)
update_mobility()
+ update_rest_hud_icon()
//Force mob to rest, does NOT do stamina damage.
//It's really not recommended to use this proc to give feedback, hence why silent is defaulting to true.
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 7d3427a916..dcddff49d1 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -86,7 +86,7 @@
if(!(m in R.held_items))
. += m
-/obj/item/robot_module/proc/get_or_create_estorage(var/storage_type)
+/obj/item/robot_module/proc/get_or_create_estorage(storage_type)
for(var/datum/robot_energy_storage/S in storages)
if(istype(S, storage_type))
return S
@@ -95,42 +95,9 @@
/obj/item/robot_module/proc/add_module(obj/item/I, nonstandard, requires_rebuild)
rad_flags |= RAD_NO_CONTAMINATE
- if(istype(I, /obj/item/stack))
- var/obj/item/stack/S = I
-
- if(is_type_in_list(S, list(/obj/item/stack/sheet/metal, /obj/item/stack/rods, /obj/item/stack/tile/plasteel)))
- if(S.custom_materials?.len && S.custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)])
- S.cost = S.custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)] * 0.25
- S.source = get_or_create_estorage(/datum/robot_energy_storage/metal)
-
- else if(istype(S, /obj/item/stack/sheet/glass))
- S.cost = 500
- S.source = get_or_create_estorage(/datum/robot_energy_storage/glass)
-
- else if(istype(S, /obj/item/stack/sheet/rglass/cyborg))
- var/obj/item/stack/sheet/rglass/cyborg/G = S
- G.source = get_or_create_estorage(/datum/robot_energy_storage/metal)
- G.glasource = get_or_create_estorage(/datum/robot_energy_storage/glass)
-
- else if(istype(S, /obj/item/stack/medical))
- S.cost = 250
- S.source = get_or_create_estorage(/datum/robot_energy_storage/medical)
-
- else if(istype(S, /obj/item/stack/cable_coil))
- S.cost = 1
- S.source = get_or_create_estorage(/datum/robot_energy_storage/wire)
-
- else if(istype(S, /obj/item/stack/marker_beacon))
- S.cost = 1
- S.source = get_or_create_estorage(/datum/robot_energy_storage/beacon)
-
- else if(istype(S, /obj/item/stack/packageWrap))
- S.cost = 1
- S.source = get_or_create_estorage(/datum/robot_energy_storage/wrapping_paper)
-
- if(S && S.source)
- S.set_custom_materials(null)
- S.is_cyborg = 1
+ var/obj/item/stack/S = I
+ if(istype(I, /obj/item/stack) && !S.is_cyborg) // Now handled in the type itself
+ stack_trace("Non-cyborg variant of /obj/item/stack added to a cyborg's modules.")
if(I.loc != src)
I.forceMove(src)
@@ -935,9 +902,9 @@
/obj/item/gps/cyborg,
/obj/item/gripper/mining,
/obj/item/cyborg_clamp,
- /obj/item/stack/marker_beacon,
+ /obj/item/stack/marker_beacon/cyborg,
/obj/item/destTagger,
- /obj/item/stack/packageWrap,
+ /obj/item/stack/packageWrap/cyborg,
/obj/item/card/id/miningborg)
emag_modules = list(/obj/item/borg/stun)
ratvar_modules = list(
@@ -1056,7 +1023,7 @@
/obj/item/surgicaldrill,
/obj/item/scalpel,
/obj/item/bonesetter,
- /obj/item/stack/medical/bone_gel,
+ /obj/item/stack/medical/bone_gel/cyborg,
/obj/item/melee/transforming/energy/sword/cyborg/saw,
/obj/item/roller/robo,
/obj/item/card/emag,
@@ -1191,7 +1158,7 @@
/obj/item/surgicaldrill,
/obj/item/scalpel,
/obj/item/bonesetter,
- /obj/item/stack/medical/bone_gel,
+ /obj/item/stack/medical/bone_gel/cyborg,
/obj/item/melee/transforming/energy/sword/cyborg/saw,
/obj/item/roller/robo,
/obj/item/stack/medical/gauze/cyborg,
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f2d4f7cd58..1e34bb722d 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -369,27 +369,6 @@
var/msg = "[src] makes eye contact with you."
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, examined_mob, msg), 3)
-//same as above
-//note: ghosts can point, this is intended
-//visible_message will handle invisibility properly
-//overridden here and in /mob/dead/observer for different point span classes and sanity checks
-/mob/verb/pointed(atom/A as mob|obj|turf in fov_view())
- set name = "Point To"
- set category = "Object"
-
- if(!src || !isturf(src.loc) || !(A in view(src.loc)))
- return FALSE
- if(istype(A, /obj/effect/temp_visual/point))
- return FALSE
-
- var/tile = get_turf(A)
- if (!tile)
- return FALSE
-
- new /obj/effect/temp_visual/point(A,invisibility)
- SEND_SIGNAL(src, COMSIG_MOB_POINTED, A)
- return TRUE
-
/mob/proc/can_resist()
return FALSE //overridden in living.dm
diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm
new file mode 100644
index 0000000000..cfadf391c5
--- /dev/null
+++ b/code/modules/point/point.dm
@@ -0,0 +1,110 @@
+#define POINT_TIME (2.5 SECONDS)
+
+/**
+ * Point at an atom
+ *
+ * Intended to enable and standardise the pointing animation for all atoms
+ *
+ * Not intended as a replacement for the mob verb
+ */
+/atom/movable/proc/point_at(atom/pointed_atom)
+ if(!isturf(loc))
+ return
+
+ if (pointed_atom in src)
+ create_point_bubble(pointed_atom)
+ return
+
+ var/turf/tile = get_turf(pointed_atom)
+ if (!tile)
+ return
+
+ var/turf/our_tile = get_turf(src)
+ var/obj/visual = new /obj/effect/temp_visual/point(our_tile, invisibility)
+
+ animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
+
+/atom/movable/proc/create_point_bubble(atom/pointed_atom)
+ var/obj/effect/thought_bubble_effect = new
+
+ var/mutable_appearance/thought_bubble = mutable_appearance(
+ 'icons/effects/effects.dmi',
+ "thought_bubble",
+ layer = POINT_LAYER,
+ appearance_flags = KEEP_APART,
+ )
+
+ var/mutable_appearance/pointed_atom_appearance = new(pointed_atom.appearance)
+ pointed_atom_appearance.blend_mode = BLEND_INSET_OVERLAY
+ pointed_atom_appearance.plane = thought_bubble.plane
+ pointed_atom_appearance.layer = FLOAT_LAYER
+ pointed_atom_appearance.pixel_x = 0
+ pointed_atom_appearance.pixel_y = 0
+ thought_bubble.overlays += pointed_atom_appearance
+
+ var/hover_outline_index = pointed_atom.get_filter_index(HOVER_OUTLINE_FILTER)
+ if (!isnull(hover_outline_index))
+ pointed_atom_appearance.filters.Cut(hover_outline_index, hover_outline_index + 1)
+
+ thought_bubble.pixel_x = 16
+ thought_bubble.pixel_y = 32
+ thought_bubble.alpha = 200
+ thought_bubble.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+ var/mutable_appearance/point_visual = mutable_appearance(
+ 'icons/mob/screen_gen.dmi',
+ "arrow",
+ plane = thought_bubble.plane,
+ )
+
+ thought_bubble.overlays += point_visual
+
+ // vis_contents is used to preserve mouse opacity
+ thought_bubble_effect.appearance = thought_bubble
+ vis_contents += thought_bubble_effect
+
+ QDEL_IN(thought_bubble_effect, POINT_TIME)
+
+/obj/effect/temp_visual/point
+ name = "pointer"
+ icon = 'icons/mob/screen_gen.dmi'
+ icon_state = "arrow"
+ layer = POINT_LAYER
+ duration = POINT_TIME
+
+/obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0)
+ . = ..()
+ var/atom/old_loc = loc
+ abstract_move(get_turf(src))
+ pixel_x = old_loc.pixel_x
+ pixel_y = old_loc.pixel_y
+ invisibility = set_invis
+
+#undef POINT_TIME
+
+/**
+ * Point at an atom
+ *
+ * mob verbs are faster than object verbs. See
+ * [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716)
+ * for why this isn't atom/verb/pointed()
+ *
+ * note: ghosts can point, this is intended
+ *
+ * visible_message will handle invisibility properly
+ *
+ * overridden here and in /mob/dead/observer for different point span classes and sanity checks
+ */
+/mob/verb/pointed(atom/target as mob|obj|turf in fov_view())
+ set name = "Point To"
+ set category = "Object"
+
+ if(client && !(target in view(client.view, src)))
+ return FALSE
+ if(istype(target, /obj/effect/temp_visual/point))
+ return FALSE
+
+ point_at(target)
+
+ SEND_SIGNAL(src, COMSIG_MOB_POINTED, target)
+ return TRUE
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index 5aa16c0c29..32c136dd90 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -517,6 +517,7 @@ By design, d1 is the smallest direction and d2 is the highest
/obj/item/stack/cable_coil/cyborg
is_cyborg = TRUE
custom_materials = null
+ source = /datum/robot_energy_storage/wire
cost = 1
/obj/item/stack/cable_coil/cyborg/attack_self(mob/user)
diff --git a/code/modules/tooltip/tooltip.html b/code/modules/tooltip/tooltip.html
index b66d48d6c6..2f60a2b0b1 100644
--- a/code/modules/tooltip/tooltip.html
+++ b/code/modules/tooltip/tooltip.html
@@ -80,12 +80,21 @@
.slimecore .wrap {border-color: #18640E;}
.slimecore .content {color: #6EA161; border-color: #11450B; background-color: #354E35;}
- .operative .wrap {border-color: #1E0101;}
- .operative .content {color: #FFFFFF; border-color: #750000; background-color: #350000;}
+ .operative .wrap {border-color: #13121b;}
+ .operative .content {color: #b01232; border-color: #13121b; background-color: #282831;}
.clockwork .wrap {border-color: #170800;}
.clockwork .content {color: #B18B25; border-color: #000000; background-color: #5F380E;}
+ .glass .wrap {border-color: #273844;}
+ .glass .content {color: #5b7588; border-color: #273844; background-color: #1f252b;}
+
+ .trasen-knox .wrap {border-color: #998e81;}
+ .trasen-knox .content {color: #3ce375; border-color: #998e81; background-color: #1e1d21;}
+
+ .detective .wrap {border-color: #2c0F0c;}
+ .detective .content {color: #c7b08b; border-color: #2c0F0c; background-color: #221c1a;}
+
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index be99d57a3d..8334c83224 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/hud/64x16_actions.dmi b/icons/hud/64x16_actions.dmi
index ae81ace4d5..23865a80f0 100644
Binary files a/icons/hud/64x16_actions.dmi and b/icons/hud/64x16_actions.dmi differ
diff --git a/icons/mob/screen_alien.dmi b/icons/mob/screen_alien.dmi
index b20bfaa405..ddb99fdb68 100644
Binary files a/icons/mob/screen_alien.dmi and b/icons/mob/screen_alien.dmi differ
diff --git a/icons/mob/screen_clockwork.dmi b/icons/mob/screen_clockwork.dmi
index 0cd15937ed..d84503c768 100644
Binary files a/icons/mob/screen_clockwork.dmi and b/icons/mob/screen_clockwork.dmi differ
diff --git a/icons/mob/screen_detective.dmi b/icons/mob/screen_detective.dmi
new file mode 100644
index 0000000000..5de29ad1e8
Binary files /dev/null and b/icons/mob/screen_detective.dmi differ
diff --git a/icons/mob/screen_glass.dmi b/icons/mob/screen_glass.dmi
new file mode 100644
index 0000000000..4948171883
Binary files /dev/null and b/icons/mob/screen_glass.dmi differ
diff --git a/icons/mob/screen_midnight.dmi b/icons/mob/screen_midnight.dmi
index ec583eb485..c180ed4f37 100644
Binary files a/icons/mob/screen_midnight.dmi and b/icons/mob/screen_midnight.dmi differ
diff --git a/icons/mob/screen_operative.dmi b/icons/mob/screen_operative.dmi
index fd3879e428..8e2eeaa315 100644
Binary files a/icons/mob/screen_operative.dmi and b/icons/mob/screen_operative.dmi differ
diff --git a/icons/mob/screen_plasmafire.dmi b/icons/mob/screen_plasmafire.dmi
index 7e1fb8308f..18f99515b9 100644
Binary files a/icons/mob/screen_plasmafire.dmi and b/icons/mob/screen_plasmafire.dmi differ
diff --git a/icons/mob/screen_retro.dmi b/icons/mob/screen_retro.dmi
index e7c40db5d3..8c8b920908 100644
Binary files a/icons/mob/screen_retro.dmi and b/icons/mob/screen_retro.dmi differ
diff --git a/icons/mob/screen_slimecore.dmi b/icons/mob/screen_slimecore.dmi
index 3d335a846f..91b01910e3 100644
Binary files a/icons/mob/screen_slimecore.dmi and b/icons/mob/screen_slimecore.dmi differ
diff --git a/icons/mob/screen_trasenknox.dmi b/icons/mob/screen_trasenknox.dmi
new file mode 100644
index 0000000000..bf229f7bad
Binary files /dev/null and b/icons/mob/screen_trasenknox.dmi differ
diff --git a/modular_citadel/icons/ui/screen_detective.dmi b/modular_citadel/icons/ui/screen_detective.dmi
new file mode 100644
index 0000000000..ef012d3e84
Binary files /dev/null and b/modular_citadel/icons/ui/screen_detective.dmi differ
diff --git a/modular_citadel/icons/ui/screen_glass.dmi b/modular_citadel/icons/ui/screen_glass.dmi
new file mode 100644
index 0000000000..d03ba9edae
Binary files /dev/null and b/modular_citadel/icons/ui/screen_glass.dmi differ
diff --git a/modular_citadel/icons/ui/screen_operative.dmi b/modular_citadel/icons/ui/screen_operative.dmi
index 5e8abb5431..0f9173091b 100644
Binary files a/modular_citadel/icons/ui/screen_operative.dmi and b/modular_citadel/icons/ui/screen_operative.dmi differ
diff --git a/modular_citadel/icons/ui/screen_trasenknox.dmi b/modular_citadel/icons/ui/screen_trasenknox.dmi
new file mode 100644
index 0000000000..ef418fdf94
Binary files /dev/null and b/modular_citadel/icons/ui/screen_trasenknox.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 2b8e694dd3..45d3f018ad 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3178,6 +3178,7 @@
#include "code\modules\plumbing\plumbers\reaction_chamber.dm"
#include "code\modules\plumbing\plumbers\splitters.dm"
#include "code\modules\plumbing\plumbers\synthesizer.dm"
+#include "code\modules\point\point.dm"
#include "code\modules\pool\pool_controller.dm"
#include "code\modules\pool\pool_drain.dm"
#include "code\modules\pool\pool_effects.dm"