diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index f8f9a48102a..a2565905f47 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -1,33 +1,14 @@
-/*
-CONTAINS:
-RCD // no fucking shit sherlock
-*/
-#define RCD_PAGE_MAIN 1
-#define RCD_PAGE_AIRLOCK 2
-#define RCD_MODE_TURF "Turf"
-#define RCD_MODE_AIRLOCK "Airlock"
-#define RCD_MODE_DECON "Deconstruct"
-#define RCD_MODE_WINDOW "Windows"
+#define MATTER_100 100
+#define MATTER_500 500
-GLOBAL_LIST_INIT(rcd_door_types, list(
- /obj/machinery/door/airlock = "Standard", /obj/machinery/door/airlock/glass = "Standard (Glass)",
- /obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/command/glass = "Command (Glass)",
- /obj/machinery/door/airlock/security = "Security", /obj/machinery/door/airlock/security/glass = "Security (Glass)",
- /obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)",
- /obj/machinery/door/airlock/medical = "Medical", /obj/machinery/door/airlock/medical/glass = "Medical (Glass)",
- /obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)",
- /obj/machinery/door/airlock/external = "External", /obj/machinery/door/airlock/external/glass = "External (Glass)",
- /obj/machinery/door/airlock/maintenance/external = "External Maintenance",
- /obj/machinery/door/airlock/maintenance/external/glass = "External Maintenance (Glass)",
- /obj/machinery/door/airlock/freezer = "Freezer",
- /obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/mining/glass = "Mining (Glass)",
- /obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/research/glass = "Research (Glass)",
- /obj/machinery/door/airlock/atmos = "Atmospherics", /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)",
- /obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/science/glass = "Science (Glass)",
- /obj/machinery/door/airlock/hatch = "Airtight Hatch",
- /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch"
-))
+#define TAB_AIRLOCK_TYPE 1
+#define TAB_AIRLOCK_ACCESS 2
+
+#define MODE_TURF "Floors and Walls"
+#define MODE_AIRLOCK "Airlocks"
+#define MODE_WINDOW "Windows"
+#define MODE_DECON "Deconstruction"
/obj/item/rcd
name = "rapid-construction-device (RCD)"
@@ -35,8 +16,8 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
icon = 'icons/obj/tools.dmi'
icon_state = "rcd"
opacity = 0
- density = 0
- anchored = 0
+ density = FALSE
+ anchored = FALSE
flags = CONDUCT | NOBLUDGEON
force = 0
throwforce = 10
@@ -51,24 +32,36 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
req_access = list(ACCESS_ENGINE)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
- // Important shit
+ /// The spark system used to create sparks when the user interacts with the RCD.
var/datum/effect_system/spark_spread/spark_system
- var/matter = 0
- var/max_matter = 100
- var/mode = RCD_MODE_TURF
- var/canRwall = 0
-
- // UI shit
- var/menu = RCD_PAGE_MAIN
+ /// The current amount of matter stored.
+ var/matter = NONE
+ /// The max amount of matter that can be stored.
+ var/max_matter = MATTER_100
+ /// The RCD's current build mode.
+ var/mode = MODE_TURF
+ /// If the RCD can deconstruct reinforced walls.
+ var/canRwall = FALSE
+ /// Is the RCD's airlock access selection menu locked?
var/locked = TRUE
+ /// The current airlock type that will be build.
var/door_type = /obj/machinery/door/airlock
+ /// The name that newly build airlocks will receive.
var/door_name = "Airlock"
- var/list/door_accesses = list()
- var/list/door_accesses_list = list()
- var/one_access
-
- // Stupid shit
- var/static/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock)
+ /// If this is TRUE, any airlocks that gets built will require only ONE of the checked accesses. If FALSE, it will require ALL of them.
+ var/one_access = TRUE
+ /// Which airlock tab the UI is currently set to display.
+ var/ui_tab = TAB_AIRLOCK_TYPE
+ /// A list of access numbers which have been checked off by the user in the UI.
+ var/list/selected_accesses = list()
+ /// A list of objects that block construction if said object is on the tile the RCD is trying to build on.
+ var/static/list/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock)
+ /// An associative list of airlock type paths as keys, and their names as values.
+ var/static/list/rcd_door_types = list()
+ /// An associative list containing an airlock's name, type path, and image. For use with the UI.
+ var/static/list/door_types_ui_list = list()
+ /// An associative list containing all station accesses. Includes their name and access number. For use with the UI.
+ var/static/list/door_accesses_list = list()
/obj/item/rcd/Initialize()
. = ..()
@@ -77,9 +70,49 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
spark_system.attach(src)
GLOB.rcd_list += src
- door_accesses_list = list()
- for(var/access in get_all_accesses())
- door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses))
+ if(!length(rcd_door_types))
+ rcd_door_types = list(
+ /obj/machinery/door/airlock = "Standard",
+ /obj/machinery/door/airlock/glass = "Standard (Glass)",
+ /obj/machinery/door/airlock/command = "Command",
+ /obj/machinery/door/airlock/command/glass = "Command (Glass)",
+ /obj/machinery/door/airlock/security = "Security",
+ /obj/machinery/door/airlock/security/glass = "Security (Glass)",
+ /obj/machinery/door/airlock/engineering = "Engineering",
+ /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)",
+ /obj/machinery/door/airlock/atmos = "Atmospherics",
+ /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)",
+ /obj/machinery/door/airlock/mining = "Mining",
+ /obj/machinery/door/airlock/mining/glass = "Mining (Glass)",
+ /obj/machinery/door/airlock/medical = "Medical",
+ /obj/machinery/door/airlock/medical/glass = "Medical (Glass)",
+ /obj/machinery/door/airlock/research = "Research",
+ /obj/machinery/door/airlock/research/glass = "Research (Glass)",
+ /obj/machinery/door/airlock/science = "Science",
+ /obj/machinery/door/airlock/science/glass = "Science (Glass)",
+ /obj/machinery/door/airlock/maintenance = "Maintenance",
+ /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)",
+ /obj/machinery/door/airlock/maintenance/external = "External Maintenance",
+ /obj/machinery/door/airlock/maintenance/external/glass = "External Maint. (Glass)",
+ /obj/machinery/door/airlock/external = "External",
+ /obj/machinery/door/airlock/external/glass = "External (Glass)",
+ /obj/machinery/door/airlock/hatch = "Airtight Hatch",
+ /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch",
+ /obj/machinery/door/airlock/freezer = "Freezer"
+ )
+ if(!length(door_types_ui_list))
+ for(var/type in rcd_door_types)
+ door_types_ui_list += list(list(
+ "name" = rcd_door_types[type],
+ "type" = type,
+ "image" = get_airlock_image(type)
+ ))
+ if(!length(door_accesses_list))
+ for(var/access in get_all_accesses())
+ door_accesses_list += list(list(
+ "name" = get_access_desc(access),
+ "id" = access
+ ))
/obj/item/rcd/examine(mob/user)
. = ..()
@@ -91,15 +124,29 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
GLOB.rcd_list -= src
return ..()
+/**
+ * Creates and returns a base64 icon of the given `airlock_type`.
+ *
+ * This is used for airlock icon previews in the UI.
+ *
+ * Arugments:
+ * * airlock_type - an airlock typepath.
+ */
/obj/item/rcd/proc/get_airlock_image(airlock_type)
- var/obj/machinery/door/airlock/proto = airlock_type
- var/ic = initial(proto.icon)
- var/mutable_appearance/MA = mutable_appearance(ic, "closed")
- if(!initial(proto.glass))
- MA.overlays += "fill_closed"
- // Not scaling these down to button size because they look horrible then, instead just bumping up radius.
- return MA
+ var/obj/machinery/door/airlock/proto = new airlock_type(null)
+ proto.icon_state = "closed"
+ if(!proto.glass)
+ proto.add_overlay("fill_closed")
+ var/icon/I = getFlatIcon(proto)
+ qdel(proto)
+ return "[icon2base64(I)]"
+/**
+ * Runs a series of pre-checks before opening the radial menu to the user.
+ *
+ * Arguments:
+ * * user - the mob trying to open the radial menu.
+ */
/obj/item/rcd/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
@@ -108,33 +155,41 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
return TRUE
/obj/item/rcd/attackby(obj/item/W, mob/user, params)
- ..()
+ if(!istype(W, /obj/item/rcd_ammo))
+ return ..()
- if(istype(W, /obj/item/rcd_ammo))
- var/obj/item/rcd_ammo/R = W
- if((matter + R.ammoamt) > max_matter)
- to_chat(user, "The RCD can't hold any more matter-units.")
- return
- matter += R.ammoamt
- if(!user.unEquip(R))
- to_chat(user, "[R] is stuck to your hand!")
- return
- qdel(R)
- playsound(loc, 'sound/machines/click.ogg', 50, 1)
- to_chat(user, "The RCD now holds [matter]/[max_matter] matter-units.")
- SSnanoui.update_uis(src)
+ var/obj/item/rcd_ammo/R = W
+ if((matter + R.ammoamt) > max_matter)
+ to_chat(user, "The RCD can't hold any more matter-units.")
+ return
+ matter += R.ammoamt
+ if(!user.unEquip(R))
+ to_chat(user, "[R] is stuck to your hand!")
+ return
+
+ qdel(R)
+ playsound(loc, 'sound/machines/click.ogg', 50, 1)
+ to_chat(user, "The RCD now holds [matter]/[max_matter] matter-units.")
+ SStgui.update_uis(src)
+
+/**
+ * Creates and displays a radial menu to a user when they trigger the `attack_self` of the RCD.
+ *
+ * Arguments:
+ * * user - the mob trying to open the RCD radial.
+ */
/obj/item/rcd/proc/radial_menu(mob/user)
if(!check_menu(user))
return
var/list/choices = list(
- RCD_MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"),
- RCD_MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"),
- RCD_MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"),
- RCD_MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"),
+ MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"),
+ MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"),
+ MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"),
+ MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"),
"UI" = image(icon = 'icons/obj/interface.dmi', icon_state = "ui_interact")
)
- if(mode == RCD_MODE_AIRLOCK)
+ if(mode == MODE_AIRLOCK)
choices += list(
"Change Access" = image(icon = 'icons/obj/interface.dmi', icon_state = "access"),
"Change Airlock Type" = image(icon = 'icons/obj/interface.dmi', icon_state = "airlocktype")
@@ -144,15 +199,18 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
if(!check_menu(user))
return
switch(choice)
- if(RCD_MODE_AIRLOCK, RCD_MODE_DECON, RCD_MODE_WINDOW, RCD_MODE_TURF)
+ if(MODE_AIRLOCK, MODE_DECON, MODE_WINDOW, MODE_TURF)
mode = choice
if("UI")
- menu = RCD_PAGE_MAIN
- ui_interact(user)
+ tgui_interact(user)
return
- if("Change Access", "Change Airlock Type")
- menu = RCD_PAGE_AIRLOCK
- ui_interact(user)
+ if("Change Access")
+ ui_tab = TAB_AIRLOCK_ACCESS
+ tgui_interact(user)
+ return
+ if("Change Airlock Type")
+ ui_tab = TAB_AIRLOCK_TYPE
+ tgui_interact(user)
return
else
return
@@ -167,87 +225,131 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
/obj/item/rcd/attack_self_tk(mob/user)
radial_menu(user)
-/obj/item/rcd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/rcd/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "rcd.tmpl", "[name]", 450, 400, state = state)
+ ui = new(user, src, ui_key, "RCD", "Rapid Construction Device", 471, 673, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-
-/obj/item/rcd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state)
- var/data[0]
- data["mode"] = mode
- data["door_type"] = door_type
- data["door_name"] = door_name
- data["menu"] = menu
- data["matter"] = matter
- data["max_matter"] = max_matter
- data["one_access"] = one_access
- data["locked"] = locked
-
- if(menu == RCD_PAGE_AIRLOCK)
- var/list/door_types_list = list()
- for(var/type in GLOB.rcd_door_types)
- door_types_list[++door_types_list.len] = list("name" = GLOB.rcd_door_types[type], "type" = type)
- data["allowed_door_types"] = door_types_list
-
- data["door_accesses"] = door_accesses_list
+/obj/item/rcd/tgui_data(mob/user)
+ var/list/data = list(
+ "tab" = ui_tab,
+ "mode" = mode,
+ "locked" = locked,
+ "matter" = matter,
+ "door_type" = door_type,
+ "door_name" = door_name,
+ "one_access" = one_access,
+ "selected_accesses" = selected_accesses,
+ "modal" = tgui_modal_data(src)
+ )
return data
-/obj/item/rcd/Topic(href, href_list, nowindow, state)
+/obj/item/rcd/tgui_static_data(mob/user)
+ var/list/data = list(
+ "max_matter" = max_matter,
+ "regions" = get_accesslist_static_data(REGION_GENERAL, REGION_COMMAND),
+ "door_accesses_list" = door_accesses_list,
+ "door_types_ui_list" = door_types_ui_list
+ )
+ return data
+
+/obj/item/rcd/tgui_act(action, list/params)
if(..())
- return 1
+ return
if(prob(20))
spark_system.start()
- if(href_list["mode"])
- mode = href_list["mode"]
- . = 1
+ if(tgui_act_modal(action, params))
+ return TRUE
- if(href_list["door_type"])
- var/new_door_type = text2path(href_list["door_type"])
- if(!(new_door_type in GLOB.rcd_door_types))
- message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!")
- return
- door_type = new_door_type
- . = 1
+ . = TRUE
+ switch(action)
+ if("set_tab")
+ var/tab = text2num(params["tab"])
+ if(!(tab in list(TAB_AIRLOCK_TYPE, TAB_AIRLOCK_ACCESS)))
+ return FALSE
+ ui_tab = tab
- if(href_list["menu"])
- menu = text2num(href_list["menu"])
- . = 1
+ if("mode")
+ var/new_mode = params["mode"]
+ if(!(new_mode in list(MODE_TURF, MODE_AIRLOCK, MODE_DECON, MODE_WINDOW)))
+ return FALSE
+ mode = new_mode
- if(href_list["login"])
- if(allowed(usr))
- locked = FALSE
- . = 1
+ if("door_type")
+ var/new_door_type = text2path(params["door_type"])
+ if(!(new_door_type in rcd_door_types))
+ message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!")
+ return FALSE
+ door_type = new_door_type
- if(href_list["logout"])
- locked = TRUE
- . = 1
+ if("set_lock")
+ if(!allowed(usr))
+ to_chat(usr, "Access denied.")
+ return FALSE
+ locked = params["new_lock"] == "lock" ? TRUE : FALSE
- if(!locked)
- if(href_list["toggle_one_access"])
- one_access = !one_access
- . = 1
+ if("set_one_access")
+ one_access = params["access"] == "one" ? TRUE : FALSE
- if(href_list["toggle_access"])
- var/href_access = text2num(href_list["toggle_access"])
- if(href_access in door_accesses)
- door_accesses -= href_access
+ if("set")
+ var/access = text2num(params["access"])
+ if(isnull(access))
+ return
+ if(access in selected_accesses)
+ selected_accesses -= access
else
- door_accesses += href_access
- door_accesses_list = list()
- for(var/access in get_all_accesses())
- door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses))
- . = 1
+ selected_accesses |= access
- if(href_list["choice"])
- var/temp_t = sanitize(copytext(input("Enter a custom Airlock Name.", "Airlock Name"), 1, MAX_MESSAGE_LEN))
- if(temp_t)
- door_name = temp_t
+ if("grant_region")
+ var/region = text2num(params["region"])
+ if(isnull(region) || region < REGION_GENERAL || region > REGION_COMMAND)
+ return
+ selected_accesses |= get_region_accesses(region)
+ if("deny_region")
+ var/region = text2num(params["region"])
+ if(isnull(region) || region < REGION_GENERAL || region > REGION_COMMAND)
+ return
+ selected_accesses -= get_region_accesses(region)
+
+ if("clear_all")
+ selected_accesses = list()
+
+ if("grant_all")
+ selected_accesses = get_all_accesses()
+
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/item/rcd/proc/tgui_act_modal(action, list/params)
+ . = TRUE
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_OPEN)
+ tgui_modal_input(src, "renameAirlock", "Enter a new name:", value = door_name, max_length = TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ if(!answer)
+ return
+ door_name = sanitize(copytext(answer, 1, TGUI_MODAL_INPUT_MAX_LENGTH_NAME))
+ else
+ return FALSE
+
+/**
+ * Called in `afterattack()` if `mode` is set to `MODE_TURF`.
+ *
+ * Creates either a plating, or a wall, depending on the turf that already exists at the location.
+ *
+ * Arguments:
+ * * A - the location we're trying to build at.
+ * * user - the mob using the RCD.
+ */
/obj/item/rcd/proc/mode_turf(atom/A, mob/user)
if(isspaceturf(A) || istype(A, /obj/structure/lattice))
if(useResource(1, user))
@@ -279,6 +381,15 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
playsound(loc, 'sound/machines/click.ogg', 50, 1)
return FALSE
+/**
+ * Called in `afterattack()` if `mode` is set to `MODE_AIRLOCK`.
+ *
+ * Creates an `door_type` airlock at the given location `A`, and assigns it accesses from `selected_accesses`.
+ *
+ * Arguments:
+ * * A - the location we're trying to build at.
+ * * user - the mob using the RCD.
+ */
/obj/item/rcd/proc/mode_airlock(atom/A, mob/user)
if(isfloorturf(A))
if(checkResource(10, user))
@@ -294,9 +405,9 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
T.name = door_name
T.autoclose = TRUE
if(one_access)
- T.req_one_access = door_accesses.Copy()
+ T.req_one_access = selected_accesses.Copy()
else
- T.req_access = door_accesses.Copy()
+ T.req_access = selected_accesses.Copy()
return FALSE
return FALSE
to_chat(user, "ERROR! Not enough matter in unit to construct this airlock!")
@@ -306,6 +417,16 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
playsound(loc, 'sound/machines/click.ogg', 50, 1)
return FALSE
+/**
+ * Called in `afterattack()` if `mode` is set to `MODE_DECON`.
+ *
+ * Deconstrcts the target atom `A`.
+ * Valid atoms are: basic walls, reinforced walls (if `canRwall` is `TRUE`), airlocks, and windows.
+ *
+ * Arguments:
+ * * A - the location we're trying to build at.
+ * * user - the mob using the RCD.
+ */
/obj/item/rcd/proc/mode_decon(atom/A, mob/user)
if(iswallturf(A))
if(istype(A, /turf/simulated/wall/r_wall) && !canRwall)
@@ -362,13 +483,13 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
if(!checkResource(2, user))
to_chat(user, "ERROR! Not enough matter in unit to deconstruct this window!")
playsound(loc, 'sound/machines/click.ogg', 50, 1)
- return 0
+ return FALSE
to_chat(user, "Deconstructing window...")
playsound(loc, 'sound/machines/click.ogg', 50, 1)
if(!do_after(user, 20 * toolspeed, target = A))
- return 0
+ return FALSE
if(!useResource(2, user))
- return 0
+ return FALSE
playsound(loc, usesound, 50, 1)
var/turf/T1 = get_turf(A)
QDEL_NULL(A)
@@ -388,22 +509,31 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
return TRUE
return FALSE
+/**
+ * Called in `afterattack()` if `mode` is set to `MODE_WINDOW`.
+ *
+ * Constructs a grille and 4 reinforced window panes at the given location `A`.
+ *
+ * Arguments:
+ * * A - the location we're trying to build at.
+ * * user - the mob using the RCD.
+ */
/obj/item/rcd/proc/mode_window(atom/A, mob/user)
if(isfloorturf(A))
if(locate(/obj/structure/grille) in A)
- return 0 // We already have window
+ return FALSE // We already have window
if(!checkResource(2, user))
to_chat(user, "ERROR! Not enough matter in unit to construct this window!")
playsound(loc, 'sound/machines/click.ogg', 50, 1)
- return 0
+ return FALSE
to_chat(user, "Constructing window...")
playsound(loc, 'sound/machines/click.ogg', 50, 1)
if(!do_after(user, 20 * toolspeed, target = A))
- return 0
+ return FALSE
if(locate(/obj/structure/grille) in A)
- return 0 // We already have window
+ return FALSE // We already have window
if(!useResource(2, user))
- return 0
+ return FALSE
playsound(loc, usesound, 50, 1)
new /obj/structure/grille(A)
for(var/obj/structure/window/W in A)
@@ -419,10 +549,10 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
W.dir = cdir
var/turf/AT = A
AT.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows.
- return 1
+ return TRUE
to_chat(user, "ERROR! Location unsuitable for window construction!")
playsound(loc, 'sound/machines/click.ogg', 50, 1)
- return 0
+ return FALSE
/obj/item/rcd/afterattack(atom/A, mob/user, proximity)
if(!proximity)
@@ -433,54 +563,77 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
return FALSE
switch(mode)
- if(RCD_MODE_TURF)
+ if(MODE_TURF)
. = mode_turf(A, user)
- if(RCD_MODE_AIRLOCK)
+ if(MODE_AIRLOCK)
. = mode_airlock(A, user)
- if(RCD_MODE_DECON)
+ if(MODE_DECON)
. = mode_decon(A, user)
- if(RCD_MODE_WINDOW)
+ if(MODE_WINDOW)
. = mode_window(A, user)
else
to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.")
. = 0
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
+/**
+ * Called in each of the four build modes after an object is successfully built.
+ *
+ * Subtracts the amount of matter used from `matter`.
+ *
+ * Arguments:
+ * * amount - the amount of matter that was used.
+ */
/obj/item/rcd/proc/useResource(amount, mob/user)
if(matter < amount)
- return 0
+ return FALSE
matter -= amount
- SSnanoui.update_uis(src)
- return 1
+ SStgui.update_uis(src)
+ return TRUE
+/**
+ * Called in each of the four build modes before an object gets build. Makes sure there is enough matter to build the object.
+ *
+ * Arguments:
+ * * amount - an amount of matter to check for
+ */
/obj/item/rcd/proc/checkResource(amount, mob/user)
return matter >= amount
/obj/item/rcd/borg
- canRwall = 1
- var/use_multiplier = 160
+ canRwall = TRUE
+ /// A multipler which is applied to matter amount checks. A higher number means more power usage per RCD usage.
+ var/power_use_multiplier = 160
/obj/item/rcd/borg/syndicate
- use_multiplier = 80
+ power_use_multiplier = 80
/obj/item/rcd/borg/useResource(amount, mob/user)
if(!isrobot(user))
- return 0
+ return FALSE
var/mob/living/silicon/robot/R = user
- return R.cell.use(amount * use_multiplier)
+ return R.cell.use(amount * power_use_multiplier)
/obj/item/rcd/borg/checkResource(amount, mob/user)
if(!isrobot(user))
- return 0
+ return FALSE
var/mob/living/silicon/robot/R = user
- return R.cell.charge >= (amount * use_multiplier)
+ return R.cell.charge >= (amount * power_use_multiplier)
+/**
+ * Called from malf AI's "detonate RCD" ability.
+ *
+ * Creates a delayed explosion centered around the RCD.
+ */
/obj/item/rcd/proc/detonate_pulse()
audible_message("[src] begins to vibrate and buzz loudly!", "[src] begins vibrating violently!")
// 5 seconds to get rid of it
addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50)
+/**
+ * Called in `/obj/item/rcd/proc/detonate_pulse()` via callback.
+ */
/obj/item/rcd/proc/detonate_pulse_explode()
explosion(src, 0, 0, 3, 1, flame_range = 1)
qdel(src)
@@ -490,9 +643,9 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
/obj/item/rcd/combat
name = "combat RCD"
- max_matter = 500
- matter = 500
- canRwall = 1
+ max_matter = MATTER_500
+ matter = MATTER_500
+ canRwall = TRUE
/obj/item/rcd_ammo
name = "compressed matter cartridge"
@@ -500,12 +653,15 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
icon = 'icons/obj/ammo.dmi'
icon_state = "rcd"
item_state = "rcdammo"
- opacity = 0
- density = 0
- anchored = 0.0
+ opacity = FALSE
+ density = FALSE
+ anchored = FALSE
origin_tech = "materials=3"
materials = list(MAT_METAL=16000, MAT_GLASS=8000)
var/ammoamt = 20
/obj/item/rcd_ammo/large
ammoamt = 100
+
+#undef MATTER_100
+#undef MATTER_500
diff --git a/tgui/packages/tgui/interfaces/RCD.js b/tgui/packages/tgui/interfaces/RCD.js
new file mode 100644
index 00000000000..fd2942b4bec
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/RCD.js
@@ -0,0 +1,258 @@
+import { useBackend } from "../backend";
+import { Button, Section, LabeledList, Box, ProgressBar, Flex, Tabs, Icon } from "../components";
+import { Window } from "../layouts";
+import { ComplexModal, modalOpen } from "./common/ComplexModal";
+import { AccessList } from './common/AccessList';
+import { Fragment } from "inferno";
+
+export const RCD = () => {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const MatterReadout = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ matter,
+ max_matter,
+ } = data;
+ const good_matter = max_matter * 0.70;
+ const average_matter = max_matter * 0.25;
+ return (
+
+
+
+ {matter + " / " + max_matter + " units"}
+
+
+
+ );
+};
+
+const ConstructionType = () => {
+ return (
+
+ );
+};
+
+const ConstructionTypeCheckbox = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { mode_type } = props;
+ return (
+ act('mode', {
+ mode: mode_type,
+ })} />
+ );
+};
+
+const AirlockSettings = (props, context) => {
+ const { data } = useBackend(context);
+ return (
+
+
+
+ {data.door_name}
+
+
+
+ );
+};
+
+const TypesAndAccess = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+ act('set_tab', {
+ tab: 1,
+ })}
+ />
+ act('set_tab', {
+ tab: 2,
+ })}
+ />
+
+ {data.tab === 1 ? (
+
+ ) : (data.tab === 2 && data.locked) ? (
+ act('set_lock', {
+ new_lock: "unlock",
+ })}
+ />
+ }>
+
+
+
+ Airlock access selection is currently locked.
+
+
+
+ ) : (
+ act('set_lock', {
+ new_lock: "lock",
+ })}
+ />
+ }
+ usedByRcd={1}
+ rcdButtons={
+
+ act('set_one_access', {
+ access: "one",
+ })}
+ />
+ act('set_one_access', {
+ access: "all",
+ })}
+ />
+
+ }
+ accesses={data.regions}
+ selectedList={data.selected_accesses}
+ accessMod={ref => act('set', {
+ access: ref,
+ })}
+ grantAll={() => act('grant_all')}
+ denyAll={() => act('clear_all')}
+ grantDep={ref => act('grant_region', {
+ region: ref,
+ })}
+ denyDep={ref => act('deny_region', {
+ region: ref,
+ })}
+ />
+ )}
+
+ );
+};
+
+const AirlockTypeList = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { door_types_ui_list } = data;
+ const { check_number } = props;
+ // Filter either odd or even airlock in the list, based on what `check_number` is.
+ const doors_filtered = [];
+ for (let i = 0; i < door_types_ui_list.length; i++) {
+ if (i % 2 === check_number) {
+ doors_filtered.push(door_types_ui_list[i]);
+ }
+ }
+ return (
+
+ {doors_filtered.map((entry, i) => (
+
+
+
+
+
+ act('door_type', {
+ door_type: entry.type,
+ })}
+ />
+
+
+ ))}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/common/AccessList.js b/tgui/packages/tgui/interfaces/common/AccessList.js
index 1a077027dc3..80b4346fbc5 100644
--- a/tgui/packages/tgui/interfaces/common/AccessList.js
+++ b/tgui/packages/tgui/interfaces/common/AccessList.js
@@ -1,7 +1,7 @@
import { sortBy } from 'common/collections';
import { Fragment } from 'inferno';
import { useLocalState } from '../../backend';
-import { Button, Flex, Grid, Section, Tabs } from '../../components';
+import { Box, Button, Flex, LabeledList, Section, Tabs } from '../../components';
const diffMap = {
0: {
@@ -20,6 +20,10 @@ const diffMap = {
export const AccessList = (props, context) => {
const {
+ sectionButtons = null,
+ sectionFlexGrow = null,
+ usedByRcd,
+ rcdButtons,
accesses = [],
selectedList = [],
accessMod,
@@ -63,6 +67,7 @@ export const AccessList = (props, context) => {
return (