diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index e4a3ff1f9a..f034228bb9 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -7,10 +7,9 @@ SUBSYSTEM_DEF(shuttle)
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME
- var/obj/machinery/shuttle_manipulator/manipulator
-
var/list/mobile = list()
var/list/stationary = list()
+ var/list/beacons = list()
var/list/transit = list()
var/list/transit_requesters = list()
@@ -59,6 +58,15 @@ SUBSYSTEM_DEF(shuttle)
var/realtimeofstart = 0
+ var/datum/map_template/shuttle/selected
+
+ var/obj/docking_port/mobile/existing_shuttle
+
+ var/obj/docking_port/mobile/preview_shuttle
+ var/datum/map_template/shuttle/preview_template
+
+ var/datum/turf_reservation/preview_reservation
+
/datum/controller/subsystem/shuttle/Initialize(timeofday)
ordernum = rand(1, 9000)
@@ -82,9 +90,6 @@ SUBSYSTEM_DEF(shuttle)
return ..()
/datum/controller/subsystem/shuttle/proc/initial_load()
- if(!istype(manipulator))
- CRASH("No shuttle manipulator found.")
-
for(var/s in stationary)
var/obj/docking_port/stationary/S = s
S.load_roundstart()
@@ -148,11 +153,13 @@ SUBSYSTEM_DEF(shuttle)
++alive
var/total = GLOB.joined_player_list.len
+ if(total <= 0)
+ return //no players no autoevac
if(alive / total <= threshold)
- var/msg = "Automatically dispatching shuttle due to crew death."
+ var/msg = "Automatically dispatching emergency shuttle due to crew death."
message_admins(msg)
- log_game("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
+ log_shuttle("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
emergencyNoRecall = TRUE
priority_announce("Catastrophic casualties detected: crisis shuttle protocols activated - jamming recall signals across all frequencies.")
if(emergency.timeLeft(1) > emergencyCallTime * 0.4)
@@ -177,6 +184,34 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
+/datum/controller/subsystem/shuttle/proc/canEvac(mob/user)
+ var/srd = CONFIG_GET(number/shuttle_refuel_delay)
+ if(world.time - SSticker.round_start_time < srd)
+ to_chat(user, "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before trying again. ")
+ return FALSE
+
+ switch(emergency.mode)
+ if(SHUTTLE_RECALL)
+ to_chat(user, "The emergency shuttle may not be called while returning to CentCom. ")
+ return FALSE
+ if(SHUTTLE_CALL)
+ to_chat(user, "The emergency shuttle is already on its way. ")
+ return FALSE
+ if(SHUTTLE_DOCKED)
+ to_chat(user, "The emergency shuttle is already here. ")
+ return FALSE
+ if(SHUTTLE_IGNITING)
+ to_chat(user, "The emergency shuttle is firing its engines to leave. ")
+ return FALSE
+ if(SHUTTLE_ESCAPE)
+ to_chat(user, "The emergency shuttle is moving away to a safe distance. ")
+ return FALSE
+ if(SHUTTLE_STRANDED)
+ to_chat(user, "The emergency shuttle has been disabled by CentCom. ")
+ return FALSE
+
+ return TRUE
+
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
if(!emergency)
WARNING("requestEvac(): There is no emergency shuttle, but the \
@@ -190,35 +225,14 @@ SUBSYSTEM_DEF(shuttle)
manually, and then calling register() on the mobile docking port. \
Good luck.")
emergency = backup_shuttle
- var/srd = CONFIG_GET(number/shuttle_refuel_delay)
- if(world.time - SSticker.round_start_time < srd)
- to_chat(user, "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before trying again.")
- return
- switch(emergency.mode)
- if(SHUTTLE_RECALL)
- to_chat(user, "The emergency shuttle may not be called while returning to CentCom.")
- return
- if(SHUTTLE_CALL)
- to_chat(user, "The emergency shuttle is already on its way.")
- return
- if(SHUTTLE_DOCKED)
- to_chat(user, "The emergency shuttle is already here.")
- return
- if(SHUTTLE_IGNITING)
- to_chat(user, "The emergency shuttle is firing its engines to leave.")
- return
- if(SHUTTLE_ESCAPE)
- to_chat(user, "The emergency shuttle is moving away to a safe distance.")
- return
- if(SHUTTLE_STRANDED)
- to_chat(user, "The emergency shuttle has been disabled by CentCom.")
- return
+ if(!canEvac(user))
+ return
call_reason = trim(html_encode(call_reason))
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && seclevel2num(get_security_level()) > SEC_LEVEL_GREEN)
- to_chat(user, "You must provide a reason.")
+ to_chat(user, "You must provide a reason. ")
return
var/area/signal_origin = get_area(user)
@@ -240,11 +254,11 @@ SUBSYSTEM_DEF(shuttle)
var/area/A = get_area(user)
- log_game("[key_name(user)] has called the shuttle.")
- deadchat_broadcast("[user.real_name] has called the shuttle at [A.name] . ", user)
+ log_shuttle("[key_name(user)] has called the emergency shuttle.")
+ deadchat_broadcast(" has called the shuttle at [A.name] .", "[user.real_name] ", user, message_type=DEADCHAT_ANNOUNCEMENT)
if(call_reason)
SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
- log_game("Shuttle call reason: [call_reason]")
+ log_shuttle("Shuttle call reason: [call_reason]")
message_admins("[ADMIN_LOOKUPFLW(user)] has called the shuttle. (TRIGGER CENTCOM RECALL )")
/datum/controller/subsystem/shuttle/proc/centcom_recall(old_timer, admiral_message)
@@ -277,7 +291,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/cancelEvac(mob/user)
if(canRecall())
emergency.cancel(get_area(user))
- log_game("[key_name(user)] has recalled the shuttle.")
+ log_shuttle("[key_name(user)] has recalled the shuttle.")
message_admins("[ADMIN_LOOKUPFLW(user)] has recalled the shuttle.")
deadchat_broadcast("[user.real_name] has recalled the shuttle from [get_area_name(user, TRUE)] . ", user)
return 1
@@ -327,7 +341,7 @@ SUBSYSTEM_DEF(shuttle)
if(callShuttle)
if(EMERGENCY_IDLE_OR_RECALLED)
emergency.request(null, set_coefficient = 2.5)
- log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
+ log_shuttle("There is no means of calling the emergency shuttle anymore. Shuttle automatically called.")
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
/datum/controller/subsystem/shuttle/proc/registerHostileEnvironment(datum/bad)
@@ -380,7 +394,7 @@ SUBSYSTEM_DEF(shuttle)
emergency.setTimer(emergencyDockTime)
priority_announce("Hostile environment resolved. \
You have 3 minutes to board the Emergency Shuttle.",
- null, "shuttledock", "Priority")
+ null, 'sound/ai/shuttledock.ogg', "Priority")
//try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons
/datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
@@ -565,6 +579,14 @@ SUBSYSTEM_DEF(shuttle)
shuttle_purchased = SSshuttle.shuttle_purchased
lockdown = SSshuttle.lockdown
+ selected = SSshuttle.selected
+
+ existing_shuttle = SSshuttle.existing_shuttle
+
+ preview_shuttle = SSshuttle.preview_shuttle
+ preview_template = SSshuttle.preview_template
+
+ preview_reservation = SSshuttle.preview_reservation
/datum/controller/subsystem/shuttle/proc/is_in_shuttle_bounds(atom/A)
var/area/current = get_area(A)
@@ -647,3 +669,252 @@ SUBSYSTEM_DEF(shuttle)
message_admins("Round end vote passed. Shuttle has been auto-called.")
emergencyNoRecall = TRUE
endvote_passed = TRUE
+
+/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port)
+ // Check for an existing preview
+ if(preview_shuttle && (loading_template != preview_template))
+ preview_shuttle.jumpToNullSpace()
+ preview_shuttle = null
+ preview_template = null
+ QDEL_NULL(preview_reservation)
+
+ if(!preview_shuttle)
+ if(load_template(loading_template))
+ preview_shuttle.linkup(loading_template, destination_port)
+ preview_template = loading_template
+
+ // get the existing shuttle information, if any
+ var/timer = 0
+ var/mode = SHUTTLE_IDLE
+ var/obj/docking_port/stationary/D
+
+ if(istype(destination_port))
+ D = destination_port
+ else if(existing_shuttle)
+ timer = existing_shuttle.timer
+ mode = existing_shuttle.mode
+ D = existing_shuttle.get_docked()
+
+ if(!D)
+ D = generate_transit_dock(preview_shuttle)
+
+ if(!D)
+ CRASH("No dock found for preview shuttle ([preview_template.name]), aborting.")
+
+ var/result = preview_shuttle.canDock(D)
+ // truthy value means that it cannot dock for some reason
+ // but we can ignore the someone else docked error because we'll
+ // be moving into their place shortly
+ if((result != SHUTTLE_CAN_DOCK) && (result != SHUTTLE_SOMEONE_ELSE_DOCKED))
+ WARNING("Template shuttle [preview_shuttle] cannot dock at [D] ([result]).")
+ return
+
+ if(existing_shuttle)
+ existing_shuttle.jumpToNullSpace()
+
+ var/list/force_memory = preview_shuttle.movement_force
+ preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
+ preview_shuttle.initiate_docking(D)
+ preview_shuttle.movement_force = force_memory
+
+ . = preview_shuttle
+
+ // Shuttle state involves a mode and a timer based on world.time, so
+ // plugging the existing shuttles old values in works fine.
+ preview_shuttle.timer = timer
+ preview_shuttle.mode = mode
+
+ preview_shuttle.register()
+
+ // TODO indicate to the user that success happened, rather than just
+ // blanking the modification tab
+ preview_shuttle = null
+ preview_template = null
+ existing_shuttle = null
+ selected = null
+ QDEL_NULL(preview_reservation)
+
+/datum/controller/subsystem/shuttle/proc/load_template(datum/map_template/shuttle/S)
+ . = FALSE
+ // load shuttle template, centred at shuttle import landmark,
+ preview_reservation = SSmapping.RequestBlockReservation(S.width, S.height, SSmapping.transit.z_value, /datum/turf_reservation/transit)
+ if(!preview_reservation)
+ CRASH("failed to reserve an area for shuttle template loading")
+ var/turf/BL = TURF_FROM_COORDS_LIST(preview_reservation.bottom_left_coords)
+ S.load(BL, centered = FALSE, register = FALSE)
+
+ var/affected = S.get_affected_turfs(BL, centered=FALSE)
+
+ var/found = 0
+ // Search the turfs for docking ports
+ // - We need to find the mobile docking port because that is the heart of
+ // the shuttle.
+ // - We need to check that no additional ports have slipped in from the
+ // template, because that causes unintended behaviour.
+ for(var/T in affected)
+ for(var/obj/docking_port/P in T)
+ if(istype(P, /obj/docking_port/mobile))
+ found++
+ if(found > 1)
+ qdel(P, force=TRUE)
+ log_world("Map warning: Shuttle Template [S.mappath] has multiple mobile docking ports.")
+ else
+ preview_shuttle = P
+ if(istype(P, /obj/docking_port/stationary))
+ log_world("Map warning: Shuttle Template [S.mappath] has a stationary docking port.")
+ if(!found)
+ var/msg = "load_template(): Shuttle Template [S.mappath] has no mobile docking port. Aborting import."
+ for(var/T in affected)
+ var/turf/T0 = T
+ T0.empty()
+
+ message_admins(msg)
+ WARNING(msg)
+ return
+ //Everything fine
+ S.post_load(preview_shuttle)
+ return TRUE
+
+/datum/controller/subsystem/shuttle/proc/unload_preview()
+ if(preview_shuttle)
+ preview_shuttle.jumpToNullSpace()
+ preview_shuttle = null
+
+
+/datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state)
+ ui.open()
+
+
+/datum/controller/subsystem/shuttle/ui_data(mob/user)
+ var/list/data = list()
+ data["tabs"] = list("Status", "Templates", "Modification")
+
+ // Templates panel
+ data["templates"] = list()
+ var/list/templates = data["templates"]
+ data["templates_tabs"] = list()
+ data["selected"] = list()
+
+ for(var/shuttle_id in SSmapping.shuttle_templates)
+ var/datum/map_template/shuttle/S = SSmapping.shuttle_templates[shuttle_id]
+
+ if(!templates[S.port_id])
+ data["templates_tabs"] += S.port_id
+ templates[S.port_id] = list(
+ "port_id" = S.port_id,
+ "templates" = list())
+
+ var/list/L = list()
+ L["name"] = S.name
+ L["shuttle_id"] = S.shuttle_id
+ L["port_id"] = S.port_id
+ L["description"] = S.description
+ L["admin_notes"] = S.admin_notes
+
+ if(selected == S)
+ data["selected"] = L
+
+ templates[S.port_id]["templates"] += list(L)
+
+ data["templates_tabs"] = sortList(data["templates_tabs"])
+
+ data["existing_shuttle"] = null
+
+ // Status panel
+ data["shuttles"] = list()
+ for(var/i in mobile)
+ var/obj/docking_port/mobile/M = i
+ var/timeleft = M.timeLeft(1)
+ var/list/L = list()
+ L["name"] = M.name
+ L["id"] = M.id
+ L["timer"] = M.timer
+ L["timeleft"] = M.getTimerStr()
+ if (timeleft > 1 HOURS)
+ L["timeleft"] = "Infinity"
+ L["can_fast_travel"] = M.timer && timeleft >= 50
+ L["can_fly"] = TRUE
+ if(istype(M, /obj/docking_port/mobile/emergency))
+ L["can_fly"] = FALSE
+ else if(!M.destination)
+ L["can_fast_travel"] = FALSE
+ if (M.mode != SHUTTLE_IDLE)
+ L["mode"] = capitalize(M.mode)
+ L["status"] = M.getDbgStatusText()
+ if(M == existing_shuttle)
+ data["existing_shuttle"] = L
+
+ data["shuttles"] += list(L)
+
+ return data
+
+/datum/controller/subsystem/shuttle/ui_act(action, params)
+ if(..())
+ return
+
+ var/mob/user = usr
+
+ // Preload some common parameters
+ var/shuttle_id = params["shuttle_id"]
+ var/datum/map_template/shuttle/S = SSmapping.shuttle_templates[shuttle_id]
+
+ switch(action)
+ if("select_template")
+ if(S)
+ existing_shuttle = getShuttle(S.port_id)
+ selected = S
+ . = TRUE
+ if("jump_to")
+ if(params["type"] == "mobile")
+ for(var/i in mobile)
+ var/obj/docking_port/mobile/M = i
+ if(M.id == params["id"])
+ user.forceMove(get_turf(M))
+ . = TRUE
+ break
+
+ if("fly")
+ for(var/i in mobile)
+ var/obj/docking_port/mobile/M = i
+ if(M.id == params["id"])
+ . = TRUE
+ M.admin_fly_shuttle(user)
+ break
+
+ if("fast_travel")
+ for(var/i in mobile)
+ var/obj/docking_port/mobile/M = i
+ if(M.id == params["id"] && M.timer && M.timeLeft(1) >= 50)
+ M.setTimer(50)
+ . = TRUE
+ message_admins("[key_name_admin(usr)] fast travelled [M]")
+ log_admin("[key_name(usr)] fast travelled [M]")
+ SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[M.name]")
+ break
+
+ if("preview")
+ if(S)
+ . = TRUE
+ unload_preview()
+ load_template(S)
+ if(preview_shuttle)
+ preview_template = S
+ user.forceMove(get_turf(preview_shuttle))
+ if("load")
+ if(existing_shuttle == backup_shuttle)
+ // TODO make the load button disabled
+ WARNING("The shuttle that the selected shuttle will replace \
+ is the backup shuttle. Backup shuttle is required to be \
+ intact for round sanity.")
+ else if(S)
+ . = TRUE
+ // If successful, returns the mobile docking port
+ var/obj/docking_port/mobile/mdp = action_load(S)
+ if(mdp)
+ user.forceMove(get_turf(mdp))
+ message_admins("[key_name_admin(usr)] loaded [mdp] with the shuttle manipulator.")
+ log_admin("[key_name(usr)] loaded [mdp] with the shuttle manipulator.")
+ SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]")
diff --git a/code/datums/components/crafting/craft.dm b/code/datums/components/crafting/crafting.dm
similarity index 82%
rename from code/datums/components/crafting/craft.dm
rename to code/datums/components/crafting/crafting.dm
index 8ebd52522a..b9a1b5ec3b 100644
--- a/code/datums/components/crafting/craft.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -1,21 +1,18 @@
/datum/component/personal_crafting/Initialize()
- if(!ismob(parent))
- return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
+ if(ismob(parent))
+ RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
var/datum/hud/H = user.hud_used
var/obj/screen/craft/C = new()
C.icon = H.ui_style
H.static_inventory += C
- if(!CL.prefs.widescreenpref)
- C.screen_loc = ui_boxcraft
CL.screen += C
RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
/datum/component/personal_crafting
var/busy
- var/viewing_category = 1
+ var/viewing_category = 1 //typical powergamer starting on the Weapons tab
var/viewing_subcategory = 1
var/list/categories = list(
CAT_WEAPONRY = list(
@@ -58,9 +55,6 @@
var/display_craftable_only = FALSE
var/display_compact = TRUE
-
-
-
/* This is what procs do:
get_environment - gets a list of things accessable for crafting by user
get_surroundings - takes a list of things and makes a list of key-types to values-amounts of said type in the list
@@ -70,8 +64,6 @@
del_reqs - takes recipe and a user, loops over the recipes reqs var and tries to find everything in the list make by get_environment and delete it/add to parts list, then returns the said list
*/
-
-
/**
* Check that the contents of the recipe meet the requirements.
*
@@ -79,7 +71,7 @@
* R: The /datum/crafting_recipe being attempted.
* contents: List of items to search for R's reqs.
*/
-/datum/component/personal_crafting/proc/check_contents(mob/user, datum/crafting_recipe/R, list/contents)
+/datum/component/personal_crafting/proc/check_contents(atom/a, datum/crafting_recipe/R, list/contents)
var/list/item_instances = contents["instances"]
contents = contents["other"]
@@ -91,7 +83,7 @@
var/needed_amount = R.reqs[requirement_path]
for(var/content_item_path in contents)
// Right path and not blacklisted
- if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(requirement_path))
+ if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(content_item_path))
continue
needed_amount -= contents[content_item_path]
@@ -113,32 +105,25 @@
if(contents[requirement_path] < R.chem_catalysts[requirement_path])
return FALSE
- return R.check_requirements(user, requirements_list)
+ return R.check_requirements(a, requirements_list)
-/datum/component/personal_crafting/proc/get_environment(mob/user)
+/datum/component/personal_crafting/proc/get_environment(atom/a, list/blacklist = null, radius_range = 1)
. = list()
- for(var/obj/item/I in user.held_items)
- . += I
- if(!isturf(user.loc))
- return
- var/list/L = block(get_step(user, SOUTHWEST), get_step(user, NORTHEAST))
- for(var/A in L)
- var/turf/T = A
- if(T.Adjacent(user))
- for(var/B in T)
- var/atom/movable/AM = B
- if(AM.flags_1 & HOLOGRAM_1)
- continue
- . += AM
- for(var/slot in list(SLOT_R_STORE, SLOT_L_STORE))
- . += user.get_item_by_slot(slot)
-/datum/component/personal_crafting/proc/get_surroundings(mob/user)
+ if(!isturf(a.loc))
+ return
+
+ for(var/atom/movable/AM in range(radius_range, a))
+ if(AM.flags_1 & HOLOGRAM_1)
+ continue
+ . += AM
+
+/datum/component/personal_crafting/proc/get_surroundings(atom/a)
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
.["instances"] = list()
- for(var/obj/item/I in get_environment(user))
+ for(var/obj/item/I in get_environment(a))
if(I.flags_1 & HOLOGRAM_1)
continue
if(.["instances"][I.type])
@@ -159,13 +144,13 @@
.["other"][A.type] += A.volume
.["other"][I.type] += 1
-/datum/component/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents)
+/datum/component/personal_crafting/proc/check_tools(atom/a, datum/crafting_recipe/R, list/contents)
if(!R.tools.len)
return TRUE
var/list/possible_tools = list()
var/list/present_qualities = list()
present_qualities |= contents["tool_behaviour"]
- for(var/obj/item/I in user.contents)
+ for(var/obj/item/I in a.contents)
if(istype(I, /obj/item/storage))
for(var/obj/item/SI in I.contents)
possible_tools += SI.type
@@ -190,43 +175,28 @@
return FALSE
return TRUE
-/datum/component/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
- var/list/contents = get_surroundings(user)
- var/send_feedback = TRUE
- if(check_contents(user, R, contents))
- if(check_tools(user, R, contents))
- if(do_after(user, R.time, target = user))
- contents = get_surroundings(user)
- if(!check_contents(user, R, contents))
- return ", missing component."
- if(!check_tools(user, R, contents))
- return ", missing tool."
- var/list/parts = del_reqs(R, user)
- var/atom/movable/I = new R.result (get_turf(user.loc))
- I.CheckParts(parts, R)
- if(isitem(I))
- if(isfood(I))
- var/obj/item/reagent_containers/food/food_result = I
- var/total_quality = 0
- var/total_items = 0
- for(var/obj/item/ingredient in parts)
- var/obj/item/reagent_containers/food/food_ingredient = ingredient
- total_items += 1
- total_quality += food_ingredient.food_quality
- if(total_items == 0)
- food_result.adjust_food_quality(50)
- else
- food_result.adjust_food_quality(total_quality / total_items)
- user.put_in_hands(I)
- if(send_feedback)
- SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
- log_craft("[I] crafted by [user] at [loc_name(I.loc)]")
- return FALSE
- return "."
+/datum/component/personal_crafting/proc/construct_item(atom/a, datum/crafting_recipe/R)
+ var/list/contents = get_surroundings(a)
+ var/send_feedback = 1
+ if(check_contents(a, R, contents))
+ if(check_tools(a, R, contents))
+ //If we're a mob we'll try a do_after; non mobs will instead instantly construct the item
+ if(ismob(a) && !do_after(a, R.time, target = a))
+ return "."
+ contents = get_surroundings(a)
+ if(!check_contents(a, R, contents))
+ return ", missing component."
+ if(!check_tools(a, R, contents))
+ return ", missing tool."
+ var/list/parts = del_reqs(R, a)
+ var/atom/movable/I = new R.result (get_turf(a.loc))
+ I.CheckParts(parts, R)
+ if(send_feedback)
+ SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
+ return I //Send the item back to whatever called this proc so it can handle whatever it wants to do with the new item
return ", missing tool."
return ", missing component."
-
/*Del reqs works like this:
Loop over reqs var of the recipe
@@ -251,7 +221,7 @@
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
*/
-/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, mob/user)
+/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, atom/a)
var/list/surroundings
var/list/Deletion = list()
. = list()
@@ -260,7 +230,7 @@
main_loop:
for(var/A in R.reqs)
amt = R.reqs[A]
- surroundings = get_environment(user)
+ surroundings = get_environment(a, R.blacklist)
surroundings -= Deletion
if(ispath(A, /datum/reagent))
var/datum/reagent/RG = new A
@@ -353,6 +323,7 @@
if(user == parent)
ui_interact(user)
+//For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom
/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
@@ -362,10 +333,9 @@
cur_subcategory = subcats[1]
else
cur_subcategory = CAT_NONE
- ui = new(user, src, ui_key, "personal_crafting", "Crafting Menu", 700, 800, master_ui, state)
+ ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state)
ui.open()
-
/datum/component/personal_crafting/ui_data(mob/user)
var/list/data = list()
data["busy"] = busy
@@ -417,23 +387,24 @@
data["crafting_recipes"] = crafting_recipes
return data
-
/datum/component/personal_crafting/ui_act(action, params)
if(..())
return
switch(action)
if("make")
+ var/mob/user = usr
var/datum/crafting_recipe/TR = locate(params["recipe"]) in GLOB.crafting_recipes
- ui_interact(usr)
- if(busy)
- to_chat(usr, "You are already making something! ")
- return
busy = TRUE
- var/fail_msg = construct_item(usr, TR)
- if(!fail_msg)
- to_chat(usr, "[TR.name] constructed. ")
+ ui_interact(user)
+ var/atom/movable/result = construct_item(user, TR)
+ if(!istext(result)) //We made an item and didn't get a fail message
+ if(ismob(user) && isitem(result)) //In case the user is actually possessing a non mob like a machine
+ user.put_in_hands(result)
+ else
+ result.forceMove(user.drop_location())
+ to_chat(user, "[TR.name] constructed. ")
else
- to_chat(usr, "Construction failed[fail_msg] ")
+ to_chat(user, "Construction failed[result] ")
busy = FALSE
if("toggle_recipes")
display_craftable_only = !display_craftable_only
diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm
new file mode 100644
index 0000000000..200ec2b956
--- /dev/null
+++ b/code/datums/components/gps.dm
@@ -0,0 +1,153 @@
+///Global GPS_list. All GPS components get saved in here for easy reference.
+GLOBAL_LIST_EMPTY(GPS_list)
+///GPS component. Atoms that have this show up on gps. Pretty simple stuff.
+/datum/component/gps
+ var/gpstag = "COM0"
+ var/tracking = TRUE
+ var/emped = FALSE
+
+/datum/component/gps/Initialize(_gpstag = "COM0")
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+ gpstag = _gpstag
+ GLOB.GPS_list += src
+
+/datum/component/gps/Destroy()
+ GLOB.GPS_list -= src
+ return ..()
+
+///GPS component subtype. Only gps/item's can be used to open the UI.
+/datum/component/gps/item
+ var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
+ var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
+
+/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE)
+ . = ..()
+ if(. == COMPONENT_INCOMPATIBLE || !isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/atom/A = parent
+ A.add_overlay("working")
+ A.name = "[initial(A.name)] ([gpstag])"
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact)
+ if(!emp_proof)
+ RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp_act)
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+ RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/on_AltClick)
+
+///Called on COMSIG_ITEM_ATTACK_SELF
+/datum/component/gps/item/proc/interact(datum/source, mob/user)
+ if(user)
+ ui_interact(user)
+
+///Called on COMSIG_PARENT_EXAMINE
+/datum/component/gps/item/proc/on_examine(datum/source, mob/user, list/examine_list)
+ examine_list += "Alt-click to switch it [tracking ? "off":"on"]. "
+
+///Called on COMSIG_ATOM_EMP_ACT
+/datum/component/gps/item/proc/on_emp_act(datum/source, severity)
+ emped = TRUE
+ var/atom/A = parent
+ A.cut_overlay("working")
+ A.add_overlay("emp")
+ addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_UNIQUE|TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early
+ SStgui.close_uis(src) //Close the UI control if it is open.
+
+///Restarts the GPS after getting turned off by an EMP.
+/datum/component/gps/item/proc/reboot()
+ emped = FALSE
+ var/atom/A = parent
+ A.cut_overlay("emp")
+ A.add_overlay("working")
+
+///Calls toggletracking
+/datum/component/gps/item/proc/on_AltClick(datum/source, mob/user)
+ toggletracking(user)
+
+///Toggles the tracking for the gps
+/datum/component/gps/item/proc/toggletracking(mob/user)
+ if(!user.canUseTopic(parent, BE_CLOSE))
+ return //user not valid to use gps
+ if(emped)
+ to_chat(user, "It's busted! ")
+ return
+ var/atom/A = parent
+ if(tracking)
+ A.cut_overlay("working")
+ to_chat(user, "[parent] is no longer tracking, or visible to other GPS devices. ")
+ tracking = FALSE
+ else
+ A.add_overlay("working")
+ to_chat(user, "[parent] is now tracking, and visible to other GPS devices. ")
+ tracking = TRUE
+
+/datum/component/gps/item/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
+ if(emped)
+ to_chat(user, "[parent] fizzles weakly. ")
+ return
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ // Variable window height, depending on how many GPS units there are
+ // to show, clamped to relatively safe range.
+ var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
+ ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
+ ui.open()
+
+ ui.set_autoupdate(state = updating)
+
+/datum/component/gps/item/ui_data(mob/user)
+ var/list/data = list()
+ data["power"] = tracking
+ data["tag"] = gpstag
+ data["updating"] = updating
+ data["globalmode"] = global_mode
+ if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed
+ return data
+
+ var/turf/curr = get_turf(parent)
+ data["currentArea"] = "[get_area_name(curr, TRUE)]"
+ data["currentCoords"] = "[curr.x], [curr.y], [curr.z]"
+
+ var/list/signals = list()
+ data["signals"] = list()
+
+ for(var/gps in GLOB.GPS_list)
+ var/datum/component/gps/G = gps
+ if(G.emped || !G.tracking || G == src)
+ continue
+ var/turf/pos = get_turf(G.parent)
+ if(!pos || !global_mode && pos.z != curr.z)
+ continue
+ var/list/signal = list()
+ signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
+ signal["coords"] = "[pos.x], [pos.y], [pos.z]"
+ if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
+ signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
+ signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
+ signals += list(signal) //Add this signal to the list of signals
+ data["signals"] = signals
+ return data
+
+/datum/component/gps/item/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("rename")
+ var/atom/parentasatom = parent
+ var/a = stripped_input(usr, "Please enter desired tag.", parentasatom.name, gpstag, 20)
+
+ if (!a)
+ return
+
+ gpstag = a
+ . = TRUE
+ parentasatom.name = "global positioning system ([gpstag])"
+
+ if("power")
+ toggletracking(usr)
+ . = TRUE
+ if("updating")
+ updating = !updating
+ . = TRUE
+ if("globalmode")
+ global_mode = !global_mode
+ . = TRUE
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index dde8961482..7242b47698 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -140,9 +140,8 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
+ ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
- ui.set_style("syndicate")
ui.open()
/datum/component/uplink/ui_host(mob/user)
@@ -157,8 +156,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
- data["compact_mode"] = compact_mode
-
+ data["compactMode"] = compact_mode
return data
/datum/component/uplink/ui_static_data(mob/user)
diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm
index 1adcb6fde6..d66445b8a1 100644
--- a/code/datums/spawners_menu.dm
+++ b/code/datums/spawners_menu.dm
@@ -9,7 +9,7 @@
/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "spawners_menu", "Spawners Menu", 700, 600, master_ui, state)
+ ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state)
ui.open()
/datum/spawners_menu/ui_data(mob/user)
diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm
index ad24126e45..be87503981 100644
--- a/code/datums/wires/_wires.dm
+++ b/code/datums/wires/_wires.dm
@@ -214,8 +214,8 @@
/datum/wires/ui_interact(mob/user, ui_key = "wires", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
+ if (!ui)
+ ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
ui.open()
/datum/wires/ui_data(mob/user)
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 42904383bb..6c6b550cc5 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -202,7 +202,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "sleeper", name, 550, 700, master_ui, state)
+ ui = new(user, src, ui_key, "Sleeper", name, 550, 700, master_ui, state)
ui.open()
/obj/machinery/sleeper/ui_data()
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index 7eef48ebdd..20e703333d 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -105,12 +105,10 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/ui_interact(mob/user)
. = ..()
- if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
- return
- if(stat & BROKEN)
- visible_message("[src] buzzes. ", "You hear a faint buzz. ")
- playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1)
- return
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "AutomatedAnnouncement", "Automated Announcement System", 500, 225, master_ui, state)
+ ui.open()
var/contents = "Arrival Announcement: ([(arrivalToggle ? "On" : "Off")]) \n[arrival] \n"
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 41867a8520..fabf8e3343 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -56,7 +56,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "bank_machine", name, 320, 165, master_ui, state)
+ ui = new(user, src, ui_key, "BankMachine", name, 320, 165, master_ui, state)
ui.open()
/obj/machinery/computer/bank_machine/ui_data(mob/user)
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 7439fd1b8b..c8efe319db 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -46,7 +46,7 @@
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "operating_computer", name, 350, 470, master_ui, state)
+ ui = new(user, src, ui_key, "OperatingComputer", name, 350, 470, master_ui, state)
ui.open()
/obj/machinery/computer/operating/ui_data(mob/user)
@@ -108,6 +108,49 @@
))
else
data["patient"] = null
+ return data
+ switch(patient.stat)
+ if(CONSCIOUS)
+ data["patient"]["stat"] = "Conscious"
+ data["patient"]["statstate"] = "good"
+ if(SOFT_CRIT)
+ data["patient"]["stat"] = "Conscious"
+ data["patient"]["statstate"] = "average"
+ if(UNCONSCIOUS)
+ data["patient"]["stat"] = "Unconscious"
+ data["patient"]["statstate"] = "average"
+ if(DEAD)
+ data["patient"]["stat"] = "Dead"
+ data["patient"]["statstate"] = "bad"
+ data["patient"]["health"] = patient.health
+ data["patient"]["blood_type"] = patient.dna.blood_type
+ data["patient"]["maxHealth"] = patient.maxHealth
+ data["patient"]["minHealth"] = HEALTH_THRESHOLD_DEAD
+ data["patient"]["bruteLoss"] = patient.getBruteLoss()
+ data["patient"]["fireLoss"] = patient.getFireLoss()
+ data["patient"]["toxLoss"] = patient.getToxLoss()
+ data["patient"]["oxyLoss"] = patient.getOxyLoss()
+ data["procedures"] = list()
+ if(patient.surgeries.len)
+ for(var/datum/surgery/procedure in patient.surgeries)
+ var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
+ var/chems_needed = surgery_step.get_chem_list()
+ var/alternative_step
+ var/alt_chems_needed = ""
+ if(surgery_step.repeatable)
+ var/datum/surgery_step/next_step = procedure.get_surgery_next_step()
+ if(next_step)
+ alternative_step = capitalize(next_step.name)
+ alt_chems_needed = next_step.get_chem_list()
+ else
+ alternative_step = "Finish operation"
+ data["procedures"] += list(list(
+ "name" = capitalize("[parse_zone(procedure.location)] [procedure.name]"),
+ "next_step" = capitalize(surgery_step.name),
+ "chems_needed" = chems_needed,
+ "alternative_step" = alternative_step,
+ "alt_chems_needed" = alt_chems_needed
+ ))
return data
/obj/machinery/computer/operating/ui_act(action, params)
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index 9ff46c021b..d8553854b2 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -8,6 +8,8 @@
icon_keyboard = "tech_key"
icon_screen = "ai-fixer"
light_color = LIGHT_COLOR_PINK
+ ui_x = 370
+ ui_y = 360
/obj/machinery/computer/aifixer/attackby(obj/I, mob/user, params)
if(occupier && istype(I, /obj/item/screwdriver))
@@ -18,8 +20,12 @@
else
return ..()
-/obj/machinery/computer/aifixer/ui_interact(mob/user)
- . = ..()
+/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "AiRestorer", name, ui_x, ui_y, master_ui, state)
+ ui.open()
var/dat = ""
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 6df7120dcc..fbedf62734 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -23,7 +23,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_alert", name, 350, 300, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosAlertConsole", name, 350, 300, master_ui, state)
ui.open()
/obj/machinery/computer/atmos_alert/ui_data(mob/user)
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index 22e102b54b..b891941056 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -127,7 +127,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/atmos_control/ui_data(mob/user)
@@ -269,7 +269,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/atmos_control/tank/ui_data(mob/user)
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index eb7f194229..9c1177cb90 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -4,109 +4,166 @@
icon_screen = "cameras"
icon_keyboard = "security_key"
circuit = /obj/item/circuitboard/computer/security
- var/last_pic = 1
- var/list/network = list("ss13")
- var/list/watchers = list() //who's using the console, associated with the camera they're on.
-
light_color = LIGHT_COLOR_RED
+ ui_x = 870
+ ui_y = 708
+
+ var/list/network = list("ss13")
+ var/obj/machinery/camera/active_camera
+ var/list/concurrent_users = list()
+
+ // Stuff needed to render the map
+ var/map_name
+ var/const/default_map_size = 15
+ var/obj/screen/cam_screen
+ var/obj/screen/plane_master/lighting/cam_plane_master
+ var/obj/screen/background/cam_background
/obj/machinery/computer/security/Initialize()
. = ..()
+ // Map name has to start and end with an A-Z character,
+ // and definitely NOT with a square bracket or even a number.
+ // I wasted 6 hours on this. :agony:
+ map_name = "camera_console_[REF(src)]_map"
+ // Convert networks to lowercase
for(var/i in network)
network -= i
network += lowertext(i)
-
-/obj/machinery/computer/security/check_eye(mob/user)
- if(!can_interact(user) || !(user in watchers) || !watchers[user])
- user.unset_machine()
- return
- var/obj/machinery/camera/C = watchers[user]
- if(!C.can_use())
- user.unset_machine()
- return
-
-/obj/machinery/computer/security/on_unset_machine(mob/user)
- watchers.Remove(user)
- user.reset_perspective(null)
+ // Initialize map objects
+ cam_screen = new
+ cam_screen.name = "screen"
+ cam_screen.assigned_map = map_name
+ cam_screen.del_on_map_removal = FALSE
+ cam_screen.screen_loc = "[map_name]:1,1"
+ cam_plane_master = new
+ cam_plane_master.name = "plane_master"
+ cam_plane_master.assigned_map = map_name
+ cam_plane_master.del_on_map_removal = FALSE
+ cam_plane_master.screen_loc = "[map_name]:CENTER"
+ cam_background = new
+ cam_background.assigned_map = map_name
+ cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
- if(watchers.len)
- for(var/mob/M in watchers)
- M.unset_machine() //to properly reset the view of the users if the console is deleted.
+ qdel(cam_screen)
+ qdel(cam_plane_master)
+ qdel(cam_background)
return ..()
-/obj/machinery/computer/security/can_interact(mob/user)
- if((!hasSiliconAccessInArea(user) && !Adjacent(user)) || is_blind(user) || !in_view_range(user, src))
- return FALSE
- return ..()
+/obj/machinery/computer/security/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ for(var/i in network)
+ network -= i
+ network += "[idnum][i]"
-/obj/machinery/computer/security/interact(mob/user, special_state)
+/obj/machinery/computer/security/ui_interact(\
+ mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ // Update UI
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ // Show static if can't use the camera
+ if(!active_camera?.can_use())
+ show_camera_static()
+ if(!ui)
+ var/user_ref = REF(user)
+ var/is_living = isliving(user)
+ // Ghosts shouldn't count towards concurrent users, which produces
+ // an audible terminal_on click.
+ if(is_living)
+ concurrent_users += user_ref
+ // Turn on the console
+ if(length(concurrent_users) == 1 && is_living)
+ playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
+ use_power(active_power_usage)
+ // Register map objects
+ user.client.register_map_obj(cam_screen)
+ user.client.register_map_obj(cam_plane_master)
+ user.client.register_map_obj(cam_background)
+ // Open UI
+ ui = new(user, src, ui_key, "CameraConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/security/ui_data()
+ var/list/data = list()
+ data["network"] = network
+ data["activeCamera"] = null
+ if(active_camera)
+ data["activeCamera"] = list(
+ name = active_camera.c_tag,
+ status = active_camera.status,
+ )
+ return data
+
+/obj/machinery/computer/security/ui_static_data()
+ var/list/data = list()
+ data["mapRef"] = map_name
+ var/list/cameras = get_available_cameras()
+ data["cameras"] = list()
+ for(var/i in cameras)
+ var/obj/machinery/camera/C = cameras[i]
+ data["cameras"] += list(list(
+ name = C.c_tag,
+ ))
+ return data
+
+/obj/machinery/computer/security/ui_act(action, params)
. = ..()
- if (ismob(user) && !isliving(user)) // ghosts don't need cameras
- return
- if (!network)
- stack_trace("No camera network")
- user.unset_machine()
- return FALSE
- if (!(islist(network)))
- stack_trace("Camera network is not a list")
- user.unset_machine()
- return FALSE
-
- var/list/camera_list = get_available_cameras()
- if(!(user in watchers))
- for(var/Num in camera_list)
- var/obj/machinery/camera/CAM = camera_list[Num]
- if(istype(CAM) && CAM.can_use())
- watchers[user] = CAM //let's give the user the first usable camera, and then let him change to the camera he wants.
- break
- if(!(user in watchers))
- user.unset_machine() // no usable camera on the network, we disconnect the user from the computer.
- return FALSE
- playsound(src, 'sound/machines/terminal_prompt.ogg', 25, 0)
- use_camera_console(user)
-
-/obj/machinery/computer/security/proc/use_camera_console(mob/user)
- var/list/camera_list = get_available_cameras()
- var/t = input(user, "Which camera should you change to?") as null|anything in camera_list
- if(!src || user.machine != src) //while we were choosing we got disconnected from our computer or are using another machine.
- return
- if(!t || t == "Cancel")
- user.unset_machine()
- playsound(src, 'sound/machines/terminal_off.ogg', 25, 0)
+ if(.)
return
- var/obj/machinery/camera/C = camera_list[t]
+ if(action == "switch_camera")
+ var/c_tag = params["name"]
+ var/list/cameras = get_available_cameras()
+ var/obj/machinery/camera/C = cameras[c_tag]
+ active_camera = C
+ playsound(src, get_sfx("terminal_type"), 25, FALSE)
- if(!C || !C.can_use() || !can_interact(user))
- user.unset_machine()
- return FALSE
+ // Show static if can't use the camera
+ if(!active_camera?.can_use())
+ show_camera_static()
+ return TRUE
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, 0)
- if(isAI(user))
- var/mob/living/silicon/ai/A = user
- A.eyeobj.setLoc(get_turf(C))
- A.client.eye = A.eyeobj
- else
- user.reset_perspective(C)
- user.overlay_fullscreen("flash", /obj/screen/fullscreen/flash/static)
- user.clear_fullscreen("flash", 5)
- watchers[user] = C
- use_power(50)
- addtimer(CALLBACK(src, .proc/use_camera_console, user), 5)
+ var/list/visible_turfs = list()
+ for(var/turf/T in (C.isXRay() \
+ ? range(C.view_range, C) \
+ : view(C.view_range, C)))
+ visible_turfs += T
-//returns the list of cameras accessible from this computer
+ var/list/bbox = get_bbox_of_atoms(visible_turfs)
+ var/size_x = bbox[3] - bbox[1] + 1
+ var/size_y = bbox[4] - bbox[2] + 1
+
+ cam_screen.vis_contents = visible_turfs
+ cam_background.icon_state = "clear"
+ cam_background.fill_rect(1, 1, size_x, size_y)
+
+ return TRUE
+
+/obj/machinery/computer/security/ui_close(mob/user)
+ var/user_ref = REF(user)
+ var/is_living = isliving(user)
+ // Living creature or not, we remove you anyway.
+ concurrent_users -= user_ref
+ // Unregister map objects
+ user.client.clear_map(map_name)
+ // Turn off the console
+ if(length(concurrent_users) == 0 && is_living)
+ active_camera = null
+ playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
+ use_power(0)
+
+/obj/machinery/computer/security/proc/show_camera_static()
+ cam_screen.vis_contents.Cut()
+ cam_background.icon_state = "scanline2"
+ cam_background.fill_rect(1, 1, default_map_size, default_map_size)
+
+// Returns the list of cameras accessible from this computer
/obj/machinery/computer/security/proc/get_available_cameras()
var/list/L = list()
for (var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if((is_away_level(z) || is_away_level(C.z)) && (C.z != z))//if on away mission, can only receive feed from same z_level cameras
continue
L.Add(C)
-
- camera_sort(L)
-
var/list/D = list()
- D["Cancel"] = "Cancel"
for(var/obj/machinery/camera/C in L)
if(!C.network)
stack_trace("Camera in a cameranet has no camera network")
@@ -114,9 +171,9 @@
if(!(islist(C.network)))
stack_trace("Camera in a cameranet has a non-list camera network")
continue
- var/list/tempnetwork = C.network&network
+ var/list/tempnetwork = C.network & network
if(tempnetwork.len)
- D["[C.c_tag][(C.status ? null : " (Deactivated)")]"] = C
+ D["[C.c_tag]"] = C
return D
// SECURITY MONITORS
@@ -127,7 +184,6 @@
icon_state = "television"
icon_keyboard = null
icon_screen = "detective_tv"
- clockwork = TRUE //it'd look weird
pass_flags = PASSTABLE
/obj/machinery/computer/security/mining
@@ -145,7 +201,7 @@
circuit = /obj/item/circuitboard/computer/research
/obj/machinery/computer/security/hos
- name = "Head of Security's camera console"
+ name = "\improper Head of Security's camera console"
desc = "A custom security console with added access to the labor camp network."
network = list("ss13", "labor")
circuit = null
@@ -157,7 +213,7 @@
circuit = null
/obj/machinery/computer/security/qm
- name = "Quartermaster's camera console"
+ name = "\improper Quartermaster's camera console"
desc = "A console with access to the mining, auxillary base and vault camera networks."
network = list("mine", "auxbase", "vault")
circuit = null
@@ -169,10 +225,10 @@
desc = "Used for watching an empty arena."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "telescreen"
+ layer = SIGN_LAYER
network = list("thunder")
density = FALSE
circuit = null
- clockwork = TRUE //it'd look very weird
light_power = 0
/obj/machinery/computer/security/telescreen/update_icon_state()
@@ -184,38 +240,62 @@
name = "entertainment monitor"
desc = "Damn, they better have the /tg/ channel on these things."
icon = 'icons/obj/status_display.dmi'
- icon_state = "entertainment"
+ icon_state = "entertainment_blank"
network = list("thunder")
+ density = FALSE
+ circuit = null
+ interaction_flags_atom = NONE // interact() is called by BigClick()
+ var/icon_state_off = "entertainment_blank"
+ var/icon_state_on = "entertainment"
+
+/obj/machinery/computer/security/telescreen/entertainment/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_CLICK, .proc/BigClick)
+
+// Bypass clickchain to allow humans to use the telescreen from a distance
+/obj/machinery/computer/security/telescreen/entertainment/proc/BigClick()
+ interact(usr)
+
+/obj/machinery/computer/security/telescreen/entertainment/proc/notify(on)
+ if(on && icon_state == icon_state_off)
+ say(pick(
+ "Feats of bravery live now at the thunderdome!",
+ "Two enter, one leaves! Tune in now!",
+ "Violence like you've never seen it before!",
+ "Spears! Camera! Action! LIVE NOW!"))
+ icon_state = icon_state_on
+ else
+ icon_state = icon_state_off
/obj/machinery/computer/security/telescreen/rd
- name = "Research Director's telescreen"
+ name = "\improper Research Director's telescreen"
desc = "Used for watching the AI and the RD's goons from the safety of his office."
network = list("rd", "aicore", "aiupload", "minisat", "xeno", "test")
-/obj/machinery/computer/security/telescreen/circuitry
- name = "circuitry telescreen"
- desc = "Used for watching the other eggheads from the safety of the circuitry lab."
+/obj/machinery/computer/security/telescreen/research
+ name = "research telescreen"
+ desc = "A telescreen with access to the research division's camera network."
network = list("rd")
/obj/machinery/computer/security/telescreen/ce
- name = "Chief Engineer's telescreen"
+ name = "\improper Chief Engineer's telescreen"
desc = "Used for watching the engine, telecommunications and the minisat."
network = list("engine", "singularity", "tcomms", "minisat")
/obj/machinery/computer/security/telescreen/cmo
- name = "Chief Medical Officer's telescreen"
+ name = "\improper Chief Medical Officer's telescreen"
desc = "A telescreen with access to the medbay's camera network."
network = list("medbay")
/obj/machinery/computer/security/telescreen/vault
- name = "Vault monitor"
+ name = "vault monitor"
desc = "A telescreen that connects to the vault's camera network."
network = list("vault")
/obj/machinery/computer/security/telescreen/toxins
- name = "Bomb test site monitor"
+ name = "bomb test site monitor"
desc = "A telescreen that connects to the bomb test site's camera."
- network = list("toxin")
+ network = list("toxins")
/obj/machinery/computer/security/telescreen/engine
name = "engine monitor"
@@ -248,6 +328,6 @@
network = list("minisat")
/obj/machinery/computer/security/telescreen/aiupload
- name = "AI upload monitor"
+ name = "\improper AI upload monitor"
desc = "A telescreen that connects to the AI upload's camera network."
network = list("aiupload")
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 01a1d043a2..cd8b5c1c59 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -80,7 +80,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if (!ui)
- ui = new(user, src, ui_key, "crew", "crew monitor", 800, 600 , master_ui, state)
+ ui = new(user, src, ui_key, "CrewConsole", "crew monitor", 800, 600 , master_ui, state)
ui.open()
/datum/crewmonitor/proc/show(mob/M, source)
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index b9bd3da95e..38bc5198db 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -441,19 +441,9 @@
var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations.
- if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to
- var/list/mutations = get_mutation_list(TRUE)
- if(mutation in mutations)
- location = mutations.Find(mutation)
- if(mutation == current_mutation)
- class = "selected"
- if(location > DNA_MUTATION_BLOCKS)
- temp_html += " "
- else if(mutation in stored_research.discovered_mutations)
- temp_html += " "
- else
- temp_html += " "
- return temp_html
+ if(!ui)
+ ui = new(user, src, ui_key, "DnaConsole", name, 539, 710, master_ui, state)
+ ui.open()
/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations
var/temp_html = ""
diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm
index 1924cd9f23..fdbe2f1060 100644
--- a/code/game/machinery/computer/launchpad_control.dm
+++ b/code/game/machinery/computer/launchpad_control.dm
@@ -56,7 +56,7 @@
/obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "launchpad_console", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "LaunchpadConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/launchpad/ui_data(mob/user)
diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
index e4a6a0b2d4..9055ff62ec 100644
--- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm
+++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
@@ -25,7 +25,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "gulag_console", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "GulagTeleporterConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 2621616759..0f03c9405e 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -8,6 +8,8 @@
var/temp = null
light_color = LIGHT_COLOR_PINK
+ ui_x = 500
+ ui_y = 460
/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
if(!istype(R))
@@ -25,14 +27,25 @@
return FALSE
return TRUE
-/obj/machinery/computer/robotics/ui_interact(mob/user)
- . = ..()
- if (src.z > 6)
- to_chat(user, "Unable to establish a connection : \black You're too far away from the station!")
- return
- user.set_machine(src)
- var/dat
- var/robots = 0
+/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "RoboticsControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/robotics/ui_data(mob/user)
+ var/list/data = list()
+
+ data["can_hack"] = FALSE
+ if(issilicon(user))
+ var/mob/living/silicon/S = user
+ if(S.hack_software)
+ data["can_hack"] = TRUE
+ else if(IsAdminGhost(user))
+ data["can_hack"] = TRUE
+
+ data["cyborgs"] = list()
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
if(!can_control(user, R))
continue
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index fcb18b4dff..b4340b9350 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -20,7 +20,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "station_alert", name, 325, 500, master_ui, state)
+ ui = new(user, src, ui_key, "StationAlertConsole", name, 325, 500, master_ui, state)
ui.open()
/obj/machinery/computer/station_alert/ui_data(mob/user)
diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm
index 6710258626..e7957a5b75 100644
--- a/code/game/machinery/computer/teleporter.dm
+++ b/code/game/machinery/computer/teleporter.dm
@@ -38,7 +38,7 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "teleporter", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "Teleporter", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/teleporter/ui_data(mob/user)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 3bc8aff809..929de49293 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1434,7 +1434,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ai_airlock", name, 500, 390, master_ui, state)
+ ui = new(user, src, ui_key, "AiAirlock", name, 500, 390, master_ui, state)
ui.open()
return TRUE
diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm
index 7b90715f2b..32ebdba7a7 100644
--- a/code/game/machinery/doors/airlock_electronics.dm
+++ b/code/game/machinery/doors/airlock_electronics.dm
@@ -14,7 +14,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "airlock_electronics", name, 420, 485, master_ui, state)
+ ui = new(user, src, ui_key, "AirlockElectronics", name, 420, 485, master_ui, state)
ui.open()
/obj/item/electronics/airlock/ui_data()
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index adc1748ded..05f4c03eb0 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -143,7 +143,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "brig_timer", name, 300, 138, master_ui, state)
+ ui = new(user, src, ui_key, "BrigTimer", name, 300, 138, master_ui, state)
ui.open()
//icon update function
diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm
index 55b1e34022..45484caa77 100644
--- a/code/game/machinery/gulag_item_reclaimer.dm
+++ b/code/game/machinery/gulag_item_reclaimer.dm
@@ -31,7 +31,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "gulag_item_reclaimer", name, 300, 400, master_ui, state)
+ ui = new(user, src, ui_key, "GulagItemReclaimer", name, 300, 400, master_ui, state)
ui.open()
/obj/machinery/gulag_item_reclaimer/ui_data(mob/user)
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index d01f7e3e40..d3247253c6 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -285,8 +285,7 @@
/obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "launchpad_remote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height
- ui.set_style("syndicate")
+ ui = new(user, src, ui_key, "LaunchpadRemote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height
ui.open()
ui.set_autoupdate(TRUE)
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 41dfe8e3ac..af8f1af9f9 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -170,7 +170,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "space_heater", name, 400, 305, master_ui, state)
+ ui = new(user, src, ui_key, "SpaceHeater", name, 400, 305, master_ui, state)
ui.open()
/obj/machinery/space_heater/ui_data()
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index cf6b2b4bf4..7de414fd63 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -383,7 +383,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state)
+ ui = new(user, src, ui_key, "SuitStorageUnit", name, 400, 305, master_ui, state)
ui.open()
/obj/machinery/suit_storage_unit/ui_data()
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index bcf1b948e2..dcd8a12b38 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -84,7 +84,7 @@
/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "mech_bay_power_console", "Mech Bay Power Control Console", 400, 200, master_ui, state)
+ ui = new(user, src, ui_key, "MechBayPowerConsole", "Mech Bay Power Control Console", 400, 200, master_ui, state)
ui.open()
/obj/machinery/computer/mech_bay_power_console/ui_act(action, params)
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index 91c97ff14a..e492f01709 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -8,22 +8,15 @@
var/list/located = list()
var/screen = 0
var/stored_data
-
-/obj/machinery/computer/mecha/ui_interact(mob/user)
- . = ..()
- var/dat = "
[src.name] "
- if(screen == 0)
- dat += "Tracking beacons data "
- var/list/trackerlist = list()
- for(var/obj/mecha/MC in GLOB.mechas_list)
- trackerlist += MC.trackers
- for(var/obj/item/mecha_parts/mecha_tracking/TR in trackerlist)
- var/answer = TR.get_mecha_info()
- if(answer)
- dat += {" [answer]
- Send message
- Show exosuit log
- [TR.recharging?"Recharging EMP Pulse... ":"(EMP Pulse) "]"}
+ ui_x = 500
+ ui_y = 500
+
+/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "ExosuitControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
if(screen==1)
dat += "Log contents "
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm
index 665082d1d2..93d73b2b5f 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/RPD.dm
@@ -249,7 +249,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes)
assets.send(user)
- ui = new(user, src, ui_key, "rpd", name, 425, 472, master_ui, state)
+ ui = new(user, src, ui_key, "RapidPipeDispenser", name, 425, 515, master_ui, state)
ui.open()
/obj/item/pipe_dispenser/ui_data(mob/user)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 09c045497c..5012170b4b 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -147,7 +147,7 @@
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "crayon", name, 600, 600,
+ ui = new(user, src, ui_key, "Crayon", name, 600, 600,
master_ui, state)
ui.open()
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 38b117b132..bfb9348d97 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -58,7 +58,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "intellicard", name, 500, 500, master_ui, state)
+ ui = new(user, src, ui_key, "Intellicard", name, 500, 500, master_ui, state)
ui.open()
/obj/item/aicard/ui_data()
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index 1eee083c80..c364d1eb48 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -16,6 +16,9 @@
var/on = TRUE
var/shock_cooldown = FALSE
+ var/ui_x = 260
+ var/ui_y = 137
+
/obj/item/electropack/suicide_act(mob/living/carbon/user)
user.visible_message("[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide! ")
return (FIRELOSS)
@@ -128,22 +131,12 @@
if(!ishuman(user))
return
- user.set_machine(src)
- var/dat = {"
-
-Turned [on ? "On" : "Off"] - Toggle
-Frequency/Code for electropack:
-Frequency:
-[format_frequency(src.frequency)]
-Set
-
-Code:
-[src.code]
-Set
- "}
- user << browse(dat, "window=radio")
- onclose(user, "radio")
- return
+/obj/item/electropack/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Electropack", name, ui_x, ui_y, master_ui, state)
+ ui.open()
/obj/item/electropack/shockcollar
name = "shock collar"
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 60b93d9461..ae705168a9 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -119,7 +119,7 @@
ui_height += 6 + channels.len * 21
else
ui_height += 24
- ui = new(user, src, ui_key, "radio", name, ui_width, ui_height, master_ui, state)
+ ui = new(user, src, ui_key, "Radio", name, ui_width, ui_height, master_ui, state)
ui.open()
/obj/item/radio/ui_data(mob/user)
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 4bab5a5bcd..f48b9e2a0f 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -76,6 +76,9 @@ effective or pretty fucking useless.
var/used = 0 // is it cooling down?
var/stealth = FALSE
+ var/ui_x = 320
+ var/ui_y = 335
+
/obj/item/healthanalyzer/rad_laser/attack(mob/living/M, mob/living/user)
if(!stealth || !irradiate)
..()
@@ -111,7 +114,12 @@ effective or pretty fucking useless.
ui_interact(user)
/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user)
- . = ..()
+/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "RadioactiveMicrolaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state)
+ ui.open()
var/dat = "Irradiation: [irradiate ? "On" : "Off"] "
dat += "Stealth Mode (NOTE: Deactivates automatically while Irradiation is off): [stealth ? "On" : "Off"] "
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index b929642f33..6124138aeb 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -13,6 +13,8 @@
var/mob/attacher = null
var/valve_open = FALSE
var/toggle = 1
+ var/ui_x = 310
+ var/ui_y = 320
/obj/item/transfer_valve/IsAssemblyHolder()
return TRUE
@@ -235,3 +237,52 @@
// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs
/obj/item/transfer_valve/proc/c_state()
return
+
+/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "TransferValve", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/transfer_valve/ui_data(mob/user)
+ var/list/data = list()
+ data["tank_one"] = tank_one
+ data["tank_two"] = tank_two
+ data["attached_device"] = attached_device
+ data["valve"] = valve_open
+ return data
+
+/obj/item/transfer_valve/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("tankone")
+ if(tank_one)
+ split_gases()
+ valve_open = FALSE
+ tank_one.forceMove(drop_location())
+ tank_one = null
+ . = TRUE
+ if("tanktwo")
+ if(tank_two)
+ split_gases()
+ valve_open = FALSE
+ tank_two.forceMove(drop_location())
+ tank_two = null
+ . = TRUE
+ if("toggle")
+ toggle_valve()
+ . = TRUE
+ if("device")
+ if(attached_device)
+ attached_device.attack_self(usr)
+ . = TRUE
+ if("remove_device")
+ if(attached_device)
+ attached_device.on_detach()
+ attached_device = null
+ . = TRUE
+
+ update_icon()
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index c4a15a1871..837f57ceb5 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -196,7 +196,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "eightball", name, 400, 600, master_ui, state)
+ ui = new(user, src, ui_key, "EightBallVote", name, 400, 600, master_ui, state)
ui.open()
/obj/item/toy/eightball/haunted/ui_data(mob/user)
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 58e16ad615..c76977947f 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -159,7 +159,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "tanks", name, 400, 120, master_ui, state)
+ ui = new(user, src, ui_key, "Tank", name, 400, 120, master_ui, state)
ui.open()
/obj/item/tank/ui_data(mob/user)
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 405e697d3b..627a41349e 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -35,101 +35,368 @@
else
painting = null
-
-//////////////
-// CANVASES //
-//////////////
-
-#define AMT_OF_CANVASES 4 //Keep this up to date or shit will break.
-
-//To safe memory on making /icons we cache the blanks..
-GLOBAL_LIST_INIT(globalBlankCanvases, new(AMT_OF_CANVASES))
-
/obj/item/canvas
name = "canvas"
desc = "Draw out your soul on this canvas!"
icon = 'icons/obj/artstuff.dmi'
icon_state = "11x11"
resistance_flags = FLAMMABLE
- var/whichGlobalBackup = 1 //List index
+ var/width = 11
+ var/height = 11
+ var/list/grid
+ var/canvas_color = "#ffffff" //empty canvas color
+ var/ui_x = 400
+ var/ui_y = 400
+ var/used = FALSE
+ var/painting_name //Painting name, this is set after framing.
+ var/finalized = FALSE //Blocks edits
+ var/author_ckey
+ var/icon_generated = FALSE
+ var/icon/generated_icon
-/obj/item/canvas/nineteenXnineteen
- icon_state = "19x19"
- whichGlobalBackup = 2
+ // Painting overlay offset when framed
+ var/framed_offset_x = 11
+ var/framed_offset_y = 10
-/obj/item/canvas/twentythreeXnineteen
- icon_state = "23x19"
- whichGlobalBackup = 3
+ pixel_x = 10
+ pixel_y = 9
-/obj/item/canvas/twentythreeXtwentythree
- icon_state = "23x23"
- whichGlobalBackup = 4
-
-//HEY YOU
-//ARE YOU READING THE CODE FOR CANVASES?
-//ARE YOU AWARE THEY CRASH HALF THE SERVER WHEN SOMEONE DRAWS ON THEM...
-//...AND NOBODY CAN FIGURE OUT WHY?
-//THEN GO ON BRAVE TRAVELER
-//TRY TO FIX THEM AND REMOVE THIS CODE
/obj/item/canvas/Initialize()
- ..()
- return INITIALIZE_HINT_QDEL //Delete on creation
+ . = ..()
+ reset_grid()
-//Find the right size blank canvas
-/obj/item/canvas/proc/getGlobalBackup()
- . = null
- if(GLOB.globalBlankCanvases[whichGlobalBackup])
- . = GLOB.globalBlankCanvases[whichGlobalBackup]
- else
- var/icon/I = icon(initial(icon),initial(icon_state))
- GLOB.globalBlankCanvases[whichGlobalBackup] = I
- . = I
+/obj/item/canvas/proc/reset_grid()
+ grid = new/list(width,height)
+ for(var/x in 1 to width)
+ for(var/y in 1 to height)
+ grid[x][y] = canvas_color
+/obj/item/canvas/attack_self(mob/user)
+ . = ..()
+ ui_interact(user)
+/obj/item/canvas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
-//One pixel increments
-/obj/item/canvas/attackby(obj/item/I, mob/user, params)
- //Click info
- var/list/click_params = params2list(params)
- var/pixX = text2num(click_params["icon-x"])
- var/pixY = text2num(click_params["icon-y"])
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Canvas", name, ui_x, ui_y, master_ui, state)
+ ui.set_autoupdate(FALSE)
+ ui.open()
- //Should always be true, otherwise you didn't click the object, but let's check because SS13~
- if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
- return
-
- //Cleaning one pixel with a soap or rag
- if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/rag))
- //Pixel info created only when needed
- var/icon/masterpiece = icon(icon,icon_state)
- var/thePix = masterpiece.GetPixel(pixX,pixY)
- var/icon/Ico = getGlobalBackup()
- if(!Ico)
- qdel(masterpiece)
- return
-
- var/theOriginalPix = Ico.GetPixel(pixX,pixY)
- if(thePix != theOriginalPix) //colour changed
- DrawPixelOn(theOriginalPix,pixX,pixY)
- qdel(masterpiece)
-
- //Drawing one pixel with a crayon
- else if(istype(I, /obj/item/toy/crayon))
- var/obj/item/toy/crayon/C = I
- DrawPixelOn(C.paint_color, pixX, pixY)
+/obj/item/canvas/attackby(obj/item/I, mob/living/user, params)
+ if(user.a_intent == INTENT_HELP)
+ ui_interact(user)
else
return ..()
+/obj/item/canvas/ui_data(mob/user)
+ . = ..()
+ .["grid"] = grid
+ .["name"] = painting_name
+ .["finalized"] = finalized
-//Clean the whole canvas
-/obj/item/canvas/attack_self(mob/user)
- if(!user)
+/obj/item/canvas/examine(mob/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/item/canvas/ui_act(action, params)
+ . = ..()
+ if(. || finalized)
return
- var/icon/blank = getGlobalBackup()
- if(blank)
- //it's basically a giant etch-a-sketch
- icon = blank
- user.visible_message("[user] cleans the canvas. ","You clean the canvas. ")
+ var/mob/user = usr
+ switch(action)
+ if("paint")
+ var/obj/item/I = user.get_active_held_item()
+ var/color = get_paint_tool_color(I)
+ if(!color)
+ return FALSE
+ var/x = text2num(params["x"])
+ var/y = text2num(params["y"])
+ grid[x][y] = color
+ used = TRUE
+ update_icon()
+ . = TRUE
+ if("finalize")
+ . = TRUE
+ if(!finalized)
+ finalize(user)
+
+/obj/item/canvas/proc/finalize(mob/user)
+ finalized = TRUE
+ author_ckey = user.ckey
+ generate_proper_overlay()
+ try_rename(user)
+
+/obj/item/canvas/update_overlays()
+ . = ..()
+ if(!icon_generated)
+ if(used)
+ var/mutable_appearance/detail = mutable_appearance(icon,"[icon_state]wip")
+ detail.pixel_x = 1
+ detail.pixel_y = 1
+ . += detail
+ else
+ var/mutable_appearance/detail = mutable_appearance(generated_icon)
+ detail.pixel_x = 1
+ detail.pixel_y = 1
+ . += detail
+
+/obj/item/canvas/proc/generate_proper_overlay()
+ if(icon_generated)
+ return
+ var/png_filename = "data/paintings/temp_painting.png"
+ var/result = rustg_dmi_create_png(png_filename,"[width]","[height]",get_data_string())
+ if(result)
+ CRASH("Error generating painting png : [result]")
+ generated_icon = new(png_filename)
+ icon_generated = TRUE
+ update_icon()
+
+/obj/item/canvas/proc/get_data_string()
+ var/list/data = list()
+ for(var/y in 1 to height)
+ for(var/x in 1 to width)
+ data += grid[x][y]
+ return data.Join("")
+
+//Todo make this element ?
+/obj/item/canvas/proc/get_paint_tool_color(obj/item/I)
+ if(!I)
+ return
+ if(istype(I, /obj/item/toy/crayon))
+ var/obj/item/toy/crayon/C = I
+ return C.paint_color
+ else if(istype(I, /obj/item/pen))
+ var/obj/item/pen/P = I
+ switch(P.colour)
+ if("black")
+ return "#000000"
+ if("blue")
+ return "#0000ff"
+ if("red")
+ return "#ff0000"
+ return P.colour
+ else if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/glass/rag))
+ return canvas_color
+
+/obj/item/canvas/proc/try_rename(mob/user)
+ var/new_name = stripped_input(user,"What do you want to name the painting?")
+ if(!painting_name && new_name && user.canUseTopic(src,BE_CLOSE))
+ painting_name = new_name
+ SStgui.update_uis(src)
+
+/obj/item/canvas/nineteenXnineteen
+ icon_state = "19x19"
+ width = 19
+ height = 19
+ ui_x = 600
+ ui_y = 600
+ pixel_x = 6
+ pixel_y = 9
+ framed_offset_x = 8
+ framed_offset_y = 9
+
+/obj/item/canvas/twentythreeXnineteen
+ icon_state = "23x19"
+ width = 23
+ height = 19
+ ui_x = 800
+ ui_y = 600
+ pixel_x = 4
+ pixel_y = 10
+ framed_offset_x = 6
+ framed_offset_y = 8
+
+/obj/item/canvas/twentythreeXtwentythree
+ icon_state = "23x23"
+ width = 23
+ height = 23
+ ui_x = 800
+ ui_y = 800
+ pixel_x = 5
+ pixel_y = 9
+ framed_offset_x = 5
+ framed_offset_y = 6
+
+/obj/item/wallframe/painting
+ name = "painting frame"
+ desc = "The perfect showcase for your favorite deathtrap memories."
+ icon = 'icons/obj/decals.dmi'
+ custom_materials = null
+ flags_1 = 0
+ icon_state = "frame-empty"
+ result_path = /obj/structure/sign/painting
+
+/obj/structure/sign/painting
+ name = "Painting"
+ desc = "Art or \"Art\"? You decide."
+ icon = 'icons/obj/decals.dmi'
+ icon_state = "frame-empty"
+ buildable_sign = FALSE
+ var/obj/item/canvas/C
+ var/persistence_id
+
+/obj/structure/sign/painting/Initialize(mapload, dir, building)
+ . = ..()
+ SSpersistence.painting_frames += src
+ AddComponent(/datum/component/art, 20)
+ if(dir)
+ setDir(dir)
+ if(building)
+ pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
+ pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0
+
+/obj/structure/sign/painting/Destroy()
+ . = ..()
+ SSpersistence.painting_frames -= src
+
+/obj/structure/sign/painting/attackby(obj/item/I, mob/user, params)
+ if(!C && istype(I, /obj/item/canvas))
+ frame_canvas(user,I)
+ else if(C && !C.painting_name && istype(I,/obj/item/pen))
+ try_rename(user)
+ else
+ return ..()
+
+/obj/structure/sign/painting/examine(mob/user)
+ . = ..()
+ if(C)
+ C.ui_interact(user,state = GLOB.physical_obscured_state)
+
+/obj/structure/sign/painting/wirecutter_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(C)
+ C.forceMove(drop_location())
+ C = null
+ to_chat(user, "You remove the painting from the frame. ")
+ update_icon()
+ return TRUE
+
+/obj/structure/sign/painting/proc/frame_canvas(mob/user,obj/item/canvas/new_canvas)
+ if(user.transferItemToLoc(new_canvas,src))
+ C = new_canvas
+ if(!C.finalized)
+ C.finalize(user)
+ to_chat(user,"You frame [C]. ")
+ update_icon()
+
+/obj/structure/sign/painting/proc/try_rename(mob/user)
+ if(!C.painting_name)
+ C.try_rename(user)
+
+/obj/structure/sign/painting/update_icon_state()
+ . = ..()
+ if(C && C.generated_icon)
+ icon_state = null
+ else
+ icon_state = "frame-empty"
-#undef AMT_OF_CANVASES
+/obj/structure/sign/painting/update_overlays()
+ . = ..()
+ if(C && C.generated_icon)
+ var/mutable_appearance/MA = mutable_appearance(C.generated_icon)
+ MA.pixel_x = C.framed_offset_x
+ MA.pixel_y = C.framed_offset_y
+ . += MA
+ var/mutable_appearance/frame = mutable_appearance(C.icon,"[C.icon_state]frame")
+ frame.pixel_x = C.framed_offset_x - 1
+ frame.pixel_y = C.framed_offset_y - 1
+ . += frame
+
+/obj/structure/sign/painting/proc/load_persistent()
+ if(!persistence_id)
+ return
+ if(!SSpersistence.paintings || !SSpersistence.paintings[persistence_id] || !length(SSpersistence.paintings[persistence_id]))
+ return
+ var/list/chosen = pick(SSpersistence.paintings[persistence_id])
+ var/title = chosen["title"]
+ var/author = chosen["ckey"]
+ var/png = "data/paintings/[persistence_id]/[chosen["md5"]].png"
+ if(!fexists(png))
+ stack_trace("Persistent painting [chosen["md5"]].png was not found in [persistence_id] directory.")
+ return
+ var/icon/I = new(png)
+ var/obj/item/canvas/new_canvas
+ var/w = I.Width()
+ var/h = I.Height()
+ for(var/T in typesof(/obj/item/canvas))
+ new_canvas = T
+ if(initial(new_canvas.width) == w && initial(new_canvas.height) == h)
+ new_canvas = new T(src)
+ break
+ new_canvas.fill_grid_from_icon(I)
+ new_canvas.generated_icon = I
+ new_canvas.icon_generated = TRUE
+ new_canvas.finalized = TRUE
+ new_canvas.painting_name = title
+ new_canvas.author_ckey = author
+ C = new_canvas
+ update_icon()
+
+/obj/structure/sign/painting/proc/save_persistent()
+ if(!persistence_id || !C)
+ return
+ if(sanitize_filename(persistence_id) != persistence_id)
+ stack_trace("Invalid persistence_id - [persistence_id]")
+ return
+ var/data = C.get_data_string()
+ var/md5 = md5(data)
+ var/list/current = SSpersistence.paintings[persistence_id]
+ if(!current)
+ current = list()
+ for(var/list/entry in current)
+ if(entry["md5"] == md5)
+ return
+ var/png_directory = "data/paintings/[persistence_id]/"
+ var/png_path = png_directory + "[md5].png"
+ var/result = rustg_dmi_create_png(png_path,"[C.width]","[C.height]",data)
+ if(result)
+ CRASH("Error saving persistent painting: [result]")
+ current += list(list("title" = C.painting_name , "md5" = md5, "ckey" = C.author_ckey))
+ SSpersistence.paintings[persistence_id] = current
+
+/obj/item/canvas/proc/fill_grid_from_icon(icon/I)
+ var/h = I.Height() + 1
+ for(var/x in 1 to width)
+ for(var/y in 1 to height)
+ grid[x][y] = I.GetPixel(x,h-y)
+
+//Presets for art gallery mapping, for paintings to be shared across stations
+/obj/structure/sign/painting/library
+ persistence_id = "library"
+
+/obj/structure/sign/painting/library_secure
+ persistence_id = "library_secure"
+
+/obj/structure/sign/painting/library_private // keep your smut away from prying eyes, or non-librarians at least
+ persistence_id = "library_private"
+
+/obj/structure/sign/painting/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION(VV_HK_REMOVE_PAINTING, "Remove Persistent Painting")
+
+/obj/structure/sign/painting/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_REMOVE_PAINTING])
+ if(!check_rights(NONE))
+ return
+ var/mob/user = usr
+ if(!persistence_id || !C)
+ to_chat(user,"This is not a persistent painting. ")
+ return
+ var/md5 = md5(C.get_data_string())
+ var/author = C.author_ckey
+ var/list/current = SSpersistence.paintings[persistence_id]
+ if(current)
+ for(var/list/entry in current)
+ if(entry["md5"] == md5)
+ current -= entry
+ var/png = "data/paintings/[persistence_id]/[md5].png"
+ fdel(png)
+ for(var/obj/structure/sign/painting/P in SSpersistence.painting_frames)
+ if(P.C && md5(P.C.get_data_string()) == md5)
+ QDEL_NULL(P.C)
+ log_admin("[key_name(user)] has deleted a persistent painting made by [author].")
+ message_admins("[key_name_admin(user)] has deleted persistent painting made by [author]. ")
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index 6a8175b921..cf9cf2a830 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -71,7 +71,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "tank_dispenser", name, 275, 103, master_ui, state)
+ ui = new(user, src, ui_key, "TankDispenser", name, 275, 103, master_ui, state)
ui.open()
/obj/structure/tank_dispenser/ui_data(mob/user)
diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm
index 0d855f15bb..2101a72960 100644
--- a/code/modules/NTNet/relays.dm
+++ b/code/modules/NTNet/relays.dm
@@ -69,7 +69,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
ui.open()
diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm
index 83f2839438..beec54265e 100644
--- a/code/modules/admin/verbs/borgpanel.dm
+++ b/code/modules/admin/verbs/borgpanel.dm
@@ -36,7 +36,7 @@
/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "borgopanel", "Borg Panel", 700, 700, master_ui, state)
+ ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state)
ui.open()
/datum/borgpanel/ui_data(mob/user)
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index b2c1a52a4a..6abefeefe7 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -16,7 +16,7 @@
/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "cellular_emporium", name, 900, 480, master_ui, state)
+ ui = new(user, src, ui_key, "CellularEmporium", name, 900, 480, master_ui, state)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index b4f896fa08..ae8a23aaf5 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -7,6 +7,9 @@
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ ui_x = 350
+ ui_y = 442
+
var/timer_set = 90
var/default_timer_set = 90
var/minimum_timer_set = 90
@@ -262,8 +265,7 @@
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nuclear_bomb", name, 350, 442, master_ui, state)
- ui.set_style(ui_style)
+ ui = new(user, src, ui_key, "NuclearBomb", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm
index cddc4fb08f..0af6c85fb6 100644
--- a/code/modules/assembly/health.dm
+++ b/code/modules/assembly/health.dm
@@ -4,7 +4,6 @@
icon_state = "health"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
- secured = FALSE
var/scanning = FALSE
var/health_scan
@@ -12,7 +11,8 @@
/obj/item/assembly/health/examine(mob/user)
. = ..()
- . += "Use a multitool to swap between \"detect death\" mode and \"detect critical state\" mode. "
+ . += "Use it in hand to turn it off/on and Alt-click to swap between \"detect death\" mode and \"detect critical state\" mode."
+ . += "[src.scanning ? "The sensor is on and you can see [health_scan] displayed on the screen" : "The sensor is off"]."
/obj/item/assembly/health/activate()
if(!..())
@@ -30,14 +30,13 @@
update_icon()
return secured
-/obj/item/assembly/health/multitool_act(mob/living/user, obj/item/I)
+/obj/item/assembly/health/AltClick(mob/living/user)
if(alarm_health == HEALTH_THRESHOLD_CRIT)
alarm_health = HEALTH_THRESHOLD_DEAD
to_chat(user, "You toggle [src] to \"detect death\" mode. ")
else
alarm_health = HEALTH_THRESHOLD_CRIT
to_chat(user, "You toggle [src] to \"detect critical state\" mode. ")
- return TRUE
/obj/item/assembly/health/process()
if(!scanning || !secured)
@@ -46,7 +45,6 @@
var/atom/A = src
if(connected && connected.holder)
A = connected.holder
-
for(A, A && !ismob(A), A=A.loc);
// like get_turf(), but for mobs.
var/mob/living/M = A
@@ -71,36 +69,7 @@
STOP_PROCESSING(SSobj, src)
return
-/obj/item/assembly/health/ui_interact(mob/user as mob)//TODO: Change this to the wires thingy
+/obj/item/assembly/health/attack_self(mob/user)
. = ..()
- if(!secured)
- user.show_message("The [name] is unsecured! ")
- return FALSE
- var/dat = "Health Sensor "
- dat += "[scanning?"On":"Off"] "
- if(scanning && health_scan)
- dat += " Health: [health_scan]"
- user << browse(dat, "window=hscan")
- onclose(user, "hscan")
-
-/obj/item/assembly/health/Topic(href, href_list)
- ..()
- if(!ismob(usr))
- return
-
- var/mob/user = usr
-
- if(!user.canUseTopic(src))
- usr << browse(null, "window=hscan")
- onclose(usr, "hscan")
- return
-
- if(href_list["scanning"])
- toggle_scan()
-
- if(href_list["close"])
- usr << browse(null, "window=hscan")
- return
-
- attack_self(user)
- return
+ to_chat(user, "You toggle [src] [src.scanning ? "off" : "on"]. ")
+ toggle_scan()
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 33c6d46045..aa42d3e662 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -4,7 +4,10 @@
icon_state = "infrared"
custom_materials = list(/datum/material/iron=1000, /datum/material/glass=500)
is_position_sensitive = TRUE
-
+ drop_sound = 'sound/items/handling/component_drop.ogg'
+ pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ var/ui_x = 225
+ var/ui_y = 110
var/on = FALSE
var/visible = FALSE
var/maxlength = 8
@@ -38,7 +41,7 @@
/obj/item/assembly/infra/activate()
if(!..())
- return FALSE//Cooldown check
+ return FALSE //Cooldown check
on = !on
refreshBeam()
update_icon()
@@ -69,7 +72,7 @@
holder.update_icon()
return
-/obj/item/assembly/infra/dropped(mob/user)
+/obj/item/assembly/infra/dropped()
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
@@ -133,7 +136,7 @@
. = ..()
setDir(t)
-/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE)
. = ..()
olddir = dir
@@ -176,55 +179,56 @@
return
return refreshBeam()
-/obj/item/assembly/infra/ui_interact(mob/user)//TODO: change this this to the wire control panel
- . = ..()
- if(is_secured(user))
- user.set_machine(src)
- var/dat = "Infrared Laser "
- dat += "Status : [on ? "On " : "Off "]"
- dat += "Visibility : [visible ? "Visible " : "Invisible "]"
- dat += "Refresh "
- dat += "Close "
- user << browse(dat, "window=infra")
- onclose(user, "infra")
- return
-
-/obj/item/assembly/infra/Topic(href, href_list)
- ..()
- if(usr.incapacitated() || !in_range(loc, usr))
- usr << browse(null, "window=infra")
- onclose(usr, "infra")
- return
- if(href_list["state"])
- on = !(on)
- update_icon()
- refreshBeam()
- if(href_list["visible"])
- visible = !(visible)
- update_icon()
- refreshBeam()
- if(href_list["close"])
- usr << browse(null, "window=infra")
- return
- if(usr)
- attack_self(usr)
-
/obj/item/assembly/infra/setDir()
. = ..()
refreshBeam()
+/obj/item/assembly/infra/ui_status(mob/user)
+ if(is_secured(user))
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/infra/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "InfraredEmitter", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/assembly/infra/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["visible"] = visible
+ return data
+
+/obj/item/assembly/infra/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("power")
+ on = !on
+ . = TRUE
+ if("visibility")
+ visible = !visible
+ . = TRUE
+
+ update_icon()
+ refreshBeam()
+
/***************************IBeam*********************************/
/obj/effect/beam/i_beam
name = "infrared beam"
icon = 'icons/obj/projectiles.dmi'
icon_state = "ibeam"
- var/obj/item/assembly/infra/master
anchored = TRUE
density = FALSE
pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
+ var/obj/item/assembly/infra/master
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
+ . = ..()
if(istype(AM, /obj/effect/beam))
return
if (isitem(AM))
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index f1a4ce47cc..ef931279de 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -4,7 +4,10 @@
icon_state = "prox"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
-
+ drop_sound = 'sound/items/handling/component_drop.ogg'
+ pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ var/ui_x = 250
+ var/ui_y = 185
var/scanning = FALSE
var/timing = FALSE
var/time = 10
@@ -26,7 +29,7 @@
/obj/item/assembly/prox_sensor/activate()
if(!..())
- return FALSE//Cooldown check
+ return FALSE //Cooldown check
if(!scanning)
timing = !timing
else
@@ -41,7 +44,6 @@
else
proximity_monitor.SetHost(src,src)
-
/obj/item/assembly/prox_sensor/toggle_secure()
secured = !secured
if(!secured)
@@ -56,8 +58,6 @@
update_icon()
return secured
-
-
/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM as mob|obj)
if (istype(AM, /obj/effect/beam))
return
@@ -75,7 +75,6 @@
next_activate = world.time + 30
return TRUE
-
/obj/item/assembly/prox_sensor/process()
if(!timing)
return
@@ -111,50 +110,47 @@
holder.update_icon()
return
-/obj/item/assembly/prox_sensor/ui_interact(mob/user)//TODO: Change this to the wires thingy
- . = ..()
+/obj/item/assembly/prox_sensor/ui_status(mob/user)
if(is_secured(user))
- var/second = time % 60
- var/minute = (time - second) / 60
- var/dat = "Proximity Sensor "
- if(!scanning)
- dat += " [(timing ? "Arming " : "Not Arming ")] [minute]:[second]"
- dat += "- - + + "
- dat += "Armed":"1'>Unarmed (Movement sensor active when armed!)"] "
- dat += " Detection range: - [sensitivity] + "
- dat += "Refresh "
- dat += "Close "
- user << browse(dat, "window=prox")
- onclose(user, "prox")
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/prox_sensor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "ProximitySensor", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/assembly/prox_sensor/ui_data(mob/user)
+ var/list/data = list()
+ data["seconds"] = round(time % 60)
+ data["minutes"] = round((time - data["seconds"]) / 60)
+ data["timing"] = timing
+ data["scanning"] = scanning
+ data["sensitivity"] = sensitivity
+ return data
+
+/obj/item/assembly/prox_sensor/ui_act(action, params)
+ if(..())
return
-
-/obj/item/assembly/prox_sensor/Topic(href, href_list)
- ..()
- if(usr.incapacitated() || !in_range(loc, usr))
- usr << browse(null, "window=prox")
- onclose(usr, "prox")
- return
-
- if(href_list["sense"])
- sensitivity_change(((href_list["sense"] == "up") ? 1 : -1))
-
- if(href_list["scanning"])
- toggle_scan(text2num(href_list["scanning"]))
-
- if(href_list["time"])
- timing = text2num(href_list["time"])
- update_icon()
-
- if(href_list["tp"])
- var/tp = text2num(href_list["tp"])
- time += tp
- time = min(max(round(time), 0), 600)
-
- if(href_list["close"])
- usr << browse(null, "window=prox")
- return
-
- if(usr)
- attack_self(usr)
-
+ switch(action)
+ if("scanning")
+ toggle_scan(!scanning)
+ . = TRUE
+ if("sense")
+ var/value = text2num(params["range"])
+ if(value)
+ sensitivity_change(value)
+ . = TRUE
+ if("time")
+ timing = !timing
+ update_icon()
+ . = TRUE
+ if("input")
+ var/value = text2num(params["adjust"])
+ if(value)
+ value = round(time + value)
+ time = clamp(value, 0, 600)
+ . = TRUE
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index cee0a9054c..178437c2ff 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -8,32 +8,49 @@
custom_materials = list(/datum/material/iron=400, /datum/material/glass=120)
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = TRUE
-
+ drop_sound = 'sound/items/handling/component_drop.ogg'
+ pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ var/ui_x = 280
+ var/ui_y = 132
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
var/datum/radio_frequency/radio_connection
- var/suicider = null
+ ///Holds the mind that commited suicide.
+ var/datum/mind/suicider
+ ///Holds a reference string to the mob, decides how much of a gamer you are.
+ var/suicide_mob
var/hearing_range = 1
/obj/item/assembly/signaler/suicide_act(mob/living/carbon/user)
user.visible_message("[user] eats \the [src]! If it is signaled, [user.p_they()] will die! ")
playsound(src, 'sound/items/eatfood.ogg', 50, TRUE)
- user.transferItemToLoc(src, user, TRUE)
- suicider = user
- return MANUAL_SUICIDE
+ moveToNullspace()
+ suicider = user.mind
+ suicide_mob = REF(user)
+ return MANUAL_SUICIDE_NONLETHAL
-/obj/item/assembly/signaler/proc/manual_suicide(mob/living/carbon/user)
- user.visible_message("[user]'s \the [src] receives a signal, killing [user.p_them()] instantly! ")
+/obj/item/assembly/signaler/proc/manual_suicide(datum/mind/suicidee)
+ var/mob/living/user = suicidee.current
+ if(!istype(user))
+ return
+ if(suicide_mob == REF(user))
+ user.visible_message("[user]'s [src] receives a signal, killing [user.p_them()] instantly! ")
+ else
+ user.visible_message("[user]'s [src] receives a signal and [user.p_they()] die[user.p_s()] like a gamer! ")
user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something.
user.death(0)
+ user.set_suicide(TRUE)
+ user.suicide_log()
+ playsound(user, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ qdel(src)
/obj/item/assembly/signaler/Initialize()
. = ..()
set_frequency(frequency)
-
/obj/item/assembly/signaler/Destroy()
SSradio.remove_object(src,frequency)
+ suicider = null
. = ..()
/obj/item/assembly/signaler/activate()
@@ -47,14 +64,16 @@
holder.update_icon()
return
-/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
- if(!is_secured(user))
- return
+/obj/item/assembly/signaler/ui_status(mob/user)
+ if(is_secured(user))
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- var/ui_width = 280
- var/ui_height = 132
- ui = new(user, src, ui_key, "signaler", name, ui_width, ui_height, master_ui, state)
+ ui = new(user, src, ui_key, "Signaler", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/item/assembly/signaler/ui_data(mob/user)
@@ -63,12 +82,12 @@
data["code"] = code
data["minFrequency"] = MIN_FREE_FREQ
data["maxFrequency"] = MAX_FREE_FREQ
-
return data
/obj/item/assembly/signaler/ui_act(action, params)
if(..())
return
+
switch(action)
if("signal")
INVOKE_ASYNC(src, .proc/signal)
@@ -90,7 +109,7 @@
. = TRUE
update_icon()
-
+
/obj/item/assembly/signaler/attackby(obj/item/W, mob/user, params)
if(issignaler(W))
var/obj/item/assembly/signaler/signaler2 = W
@@ -112,9 +131,6 @@
if(usr)
GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
-
- return
-
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
. = FALSE
if(!signal)
@@ -125,6 +141,7 @@
return
if(suicider)
manual_suicide(suicider)
+ return
pulse(TRUE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
@@ -133,7 +150,6 @@
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
-
/obj/item/assembly/signaler/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
@@ -162,7 +178,6 @@
return
return ..(signal)
-
// Embedded signaller used in anomalies.
/obj/item/assembly/signaler/anomaly
name = "anomaly core"
@@ -179,6 +194,8 @@
return FALSE
if(signal.data["code"] != code)
return FALSE
+ if(suicider)
+ manual_suicide(suicider)
for(var/obj/effect/anomaly/A in get_turf(src))
A.anomalyNeutralize()
return TRUE
@@ -191,4 +208,4 @@
/obj/item/assembly/signaler/cyborg/attackby(obj/item/W, mob/user, params)
return
/obj/item/assembly/signaler/cyborg/screwdriver_act(mob/living/user, obj/item/I)
- return
\ No newline at end of file
+ return
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index bbcddbdb93..3224b2d373 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -4,7 +4,10 @@
icon_state = "timer"
custom_materials = list(/datum/material/iron=500, /datum/material/glass=50)
attachable = TRUE
-
+ drop_sound = 'sound/items/handling/component_drop.ogg'
+ pickup_sound = 'sound/items/handling/component_pickup.ogg'
+ var/ui_x = 275
+ var/ui_y = 115
var/timing = FALSE
var/time = 5
var/saved_time = 5
@@ -41,7 +44,6 @@
update_icon()
return TRUE
-
/obj/item/assembly/timer/toggle_secure()
secured = !secured
if(secured)
@@ -52,7 +54,6 @@
update_icon()
return secured
-
/obj/item/assembly/timer/proc/timer_end()
if(!secured || next_activate > world.time)
return FALSE
@@ -66,7 +67,6 @@
timing = TRUE
update_icon()
-
/obj/item/assembly/timer/process()
if(!timing)
return
@@ -76,7 +76,6 @@
timer_end()
time = saved_time
-
/obj/item/assembly/timer/update_icon()
cut_overlays()
attached_overlays = list()
@@ -86,50 +85,44 @@
if(holder)
holder.update_icon()
-
-/obj/item/assembly/timer/ui_interact(mob/user)//TODO: Have this use the wires
- . = ..()
+/obj/item/assembly/timer/ui_status(mob/user)
if(is_secured(user))
- var/second = time % 60
- var/minute = (time - second) / 60
- var/dat = "Timing Unit "
- dat += " [(timing ? "Timing " : "Not Timing ")] [minute]:[second]"
- dat += "- - + + "
- dat += "Stop repeating" : "1'>Set to repeat")] "
- dat += "Refresh "
- dat += "Close "
- var/datum/browser/popup = new(user, "timer", name)
- popup.set_content(dat)
- popup.open()
+ return ..()
+ return UI_CLOSE
+/obj/item/assembly/timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Timer", name, ui_x, ui_y, master_ui, state)
+ ui.open()
-/obj/item/assembly/timer/Topic(href, href_list)
- ..()
- if(!usr.canUseTopic(src, BE_CLOSE))
- usr << browse(null, "window=timer")
- onclose(usr, "timer")
+/obj/item/assembly/timer/ui_data(mob/user)
+ var/list/data = list()
+ data["seconds"] = round(time % 60)
+ data["minutes"] = round((time - data["seconds"]) / 60)
+ data["timing"] = timing
+ data["loop"] = loop
+ return data
+
+/obj/item/assembly/timer/ui_act(action, params)
+ if(..())
return
- if(href_list["time"])
- timing = text2num(href_list["time"])
- if(timing && istype(holder, /obj/item/transfer_valve))
- var/timer_message = "[ADMIN_LOOKUPFLW(usr)] activated [src] attachment on [holder]."
- message_admins(timer_message)
- GLOB.bombers += timer_message
- log_game("[key_name(usr)] activated [src] attachment on [holder]")
- update_icon()
- if(href_list["repeat"])
- loop = text2num(href_list["repeat"])
-
- if(href_list["tp"])
- var/tp = text2num(href_list["tp"])
- time += tp
- time = min(max(round(time), 1), 600)
- saved_time = time
-
- if(href_list["close"])
- usr << browse(null, "window=timer")
- return
-
- if(usr)
- attack_self(usr)
+ switch(action)
+ if("time")
+ timing = !timing
+ if(timing && istype(holder, /obj/item/transfer_valve))
+ log_bomber(usr, "activated a", src, "attachment on [holder]")
+ update_icon()
+ . = TRUE
+ if("repeat")
+ loop = !loop
+ . = TRUE
+ if("input")
+ var/value = text2num(params["adjust"])
+ if(value)
+ value = round(time + value)
+ time = clamp(value, 1, 600)
+ saved_time = time
+ . = TRUE
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index a4617462fe..e25aa9b612 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -239,7 +239,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state)
+ ui = new(user, src, ui_key, "AirAlarm", name, 440, 650, master_ui, state)
ui.open()
/obj/machinery/airalarm/ui_data(mob/user)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
index 02eb95acab..19711c6e5f 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
@@ -26,6 +26,21 @@ Passive gate is similar to the regular pump except:
construction_type = /obj/item/pipe/directional
pipe_state = "passivegate"
+ ui_x = 335
+ ui_y = 115
+
+/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user)
+ if(can_interact(user))
+ on = !on
+ update_icon()
+ return ..()
+
+/obj/machinery/atmospherics/components/binary/passive_gate/AltClick(mob/user)
+ if(can_interact(user))
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+ return ..()
+
/obj/machinery/atmospherics/components/binary/passive_gate/Destroy()
SSradio.remove_object(src,frequency)
return ..()
@@ -91,7 +106,7 @@ Passive gate is similar to the regular pump except:
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_pump", name, 335, 115, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/passive_gate/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index 9e49953df5..ec9b320d2c 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -111,7 +111,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_pump", name, 335, 115, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosPump", name, 335, 115, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/pump/ui_data()
@@ -212,4 +212,4 @@
/obj/machinery/atmospherics/components/binary/pump/on/layer3
piping_layer = 3
- icon_state= "pump_on_map-3"
\ No newline at end of file
+ icon_state= "pump_on_map-3"
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
index 864e3eef5e..d62d52ce43 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
@@ -96,7 +96,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_pump", name, 310, 115, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosPump", name, 310, 115, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/binary/volume_pump/ui_data()
@@ -200,4 +200,4 @@
/obj/machinery/atmospherics/components/binary/volume_pump/on/layer3
piping_layer = 3
- icon_state = "volpump_map-3"
\ No newline at end of file
+ icon_state = "volpump_map-3"
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
index 1099020662..068930ab6a 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
@@ -138,7 +138,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_filter", name, 475, 185, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosFilter", name, 475, 185, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/filter/ui_data()
@@ -280,4 +280,4 @@
critical_machine = TRUE
/obj/machinery/atmospherics/components/trinary/filter/flipped/critical
- critical_machine = TRUE
\ No newline at end of file
+ critical_machine = TRUE
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
index 5b929452fe..a4519017a9 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
@@ -131,7 +131,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_mixer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosMixer", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/mixer/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index e013a86fd2..2a516b5877 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -317,7 +317,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state)
+ ui = new(user, src, ui_key, "Cryo", name, 400, 550, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
index 87ab4fa643..e170109987 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
@@ -20,6 +20,21 @@
pipe_state = "injector"
+ ui_x = 310
+ ui_y = 115
+
+/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user)
+ if(can_interact(user))
+ on = !on
+ update_icon()
+ return ..()
+
+/obj/machinery/atmospherics/components/unary/outlet_injector/AltClick(mob/user)
+ if(can_interact(user))
+ volume_rate = MAX_TRANSFER_RATE
+ update_icon()
+ return ..()
+
/obj/machinery/atmospherics/components/unary/outlet_injector/Destroy()
SSradio.remove_object(src,frequency)
return ..()
@@ -140,7 +155,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "atmos_pump", name, 310, 115, master_ui, state)
+ ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/outlet_injector/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index 877826c1c1..db49efc781 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -128,7 +128,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "thermomachine", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "ThermoMachine", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user)
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 0b26cfc2f0..8dcf7e7b16 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -321,7 +321,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "canister", name, 420, 405, master_ui, state)
+ ui = new(user, src, ui_key, "Canister", name, 420, 405, master_ui, state)
ui.open()
/obj/machinery/portable_atmospherics/canister/ui_data()
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index 3603e46490..9e2456cc2d 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -8,6 +8,8 @@
name = "portable air pump"
icon_state = "psiphon:0"
density = TRUE
+ ui_x = 300
+ ui_y = 315
var/on = FALSE
var/direction = PUMP_OUT
@@ -32,7 +34,6 @@
/obj/machinery/portable_atmospherics/pump/update_icon_state()
icon_state = "psiphon:[on]"
-
/obj/machinery/portable_atmospherics/pump/update_overlays()
. = ..()
if(holding)
@@ -79,14 +80,14 @@
on = FALSE
update_icon()
else if(on && holding && direction == PUMP_OUT)
- investigate_log("[key_name(user)] started a transfer into [holding]. ", INVESTIGATE_ATMOS)
+ investigate_log("[key_name(user)] started a transfer into [holding].", INVESTIGATE_ATMOS)
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "portable_pump", name, 300, 315, master_ui, state)
+ ui = new(user, src, ui_key, "PortablePump", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/portable_atmospherics/pump/ui_data()
@@ -121,14 +122,14 @@
message_admins("[ADMIN_LOOKUPFLW(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [ADMIN_VERBOSEJMP(src)]")
log_admin("[key_name(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [AREACOORD(src)]")
else if(on && direction == PUMP_OUT)
- investigate_log("[key_name(usr)] started a transfer into [holding]. ", INVESTIGATE_ATMOS)
+ investigate_log("[key_name(usr)] started a transfer into [holding].", INVESTIGATE_ATMOS)
. = TRUE
if("direction")
if(direction == PUMP_OUT)
direction = PUMP_IN
else
if(on && holding)
- investigate_log("[key_name(usr)] started a transfer into [holding]. ", INVESTIGATE_ATMOS)
+ investigate_log("[key_name(usr)] started a transfer into [holding].", INVESTIGATE_ATMOS)
direction = PUMP_OUT
. = TRUE
if("pressure")
@@ -142,19 +143,14 @@
else if(pressure == "max")
pressure = PUMP_MAX_PRESSURE
. = TRUE
- else if(pressure == "input")
- pressure = input("New release pressure ([PUMP_MIN_PRESSURE]-[PUMP_MAX_PRESSURE] kPa):", name, pump.target_pressure) as num|null
- if(!isnull(pressure) && !..())
- . = TRUE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
. = TRUE
if(.)
- pump.target_pressure = CLAMP(round(pressure), PUMP_MIN_PRESSURE, PUMP_MAX_PRESSURE)
+ pump.target_pressure = clamp(round(pressure), PUMP_MIN_PRESSURE, PUMP_MAX_PRESSURE)
investigate_log("was set to [pump.target_pressure] kPa by [key_name(usr)].", INVESTIGATE_ATMOS)
if("eject")
if(holding)
- holding.forceMove(drop_location())
- holding = null
+ replace_tank(usr, FALSE)
. = TRUE
update_icon()
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index 3dfce7c1bf..f4556750c8 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -2,6 +2,8 @@
name = "portable air scrubber"
icon_state = "pscrubber:0"
density = TRUE
+ ui_x = 320
+ ui_y = 350
var/on = FALSE
var/volume_rate = 1000
@@ -71,7 +73,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "portable_scrubber", name, 320, 335, master_ui, state)
+ ui = new(user, src, ui_key, "PortableScrubber", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/portable_atmospherics/scrubber/ui_data()
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index 4f155d4174..6ddda43bc8 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -1,246 +1,327 @@
+/// Station home gateway
GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
+/// List of possible gateway destinations.
+GLOBAL_LIST_EMPTY(gateway_destinations)
+
+/**
+ * Corresponds to single entry in gateway control.
+ *
+ * Will NOT be added automatically to GLOB.gateway_destinations list.
+ */
+/datum/gateway_destination
+ var/name = "Unknown Destination"
+ var/wait = 0 /// How long after roundstart this destination becomes active
+ var/enabled = TRUE /// If disabled, the destination won't be availible
+ var/hidden = FALSE /// Will not show on gateway controls at all.
+
+/* Can a gateway link to this destination right now. */
+/datum/gateway_destination/proc/is_availible()
+ return enabled && (world.time - SSticker.round_start_time >= wait)
+
+/* Returns user-friendly description why you can't connect to this destination, displayed in UI */
+/datum/gateway_destination/proc/get_availible_reason()
+ . = "Unreachable"
+ if(world.time - SSticker.round_start_time < wait)
+ . = "Connection desynchronized. Recalibration in progress."
+
+/* Check if the movable is allowed to arrive at this destination (exile implants mostly) */
+/datum/gateway_destination/proc/incoming_pass_check(atom/movable/AM)
+ return TRUE
+
+/* Get the actual turf we'll arrive at */
+/datum/gateway_destination/proc/get_target_turf()
+ CRASH("get target turf not implemented for this destination type")
+
+/* Called after moving the movable to target turf */
+/datum/gateway_destination/proc/post_transfer(atom/movable/AM)
+ if (ismob(AM))
+ var/mob/M = AM
+ if (M.client)
+ M.client.move_delay = max(world.time + 5, M.client.move_delay)
+
+/* Called when gateway activates with this destination. */
+/datum/gateway_destination/proc/activate(obj/machinery/gateway/activated)
+ return
+
+/* Called when gateway targeting this destination deactivates. */
+/datum/gateway_destination/proc/deactivate(obj/machinery/gateway/deactivated)
+ return
+
+/* Returns data used by gateway controller ui */
+/datum/gateway_destination/proc/get_ui_data()
+ . = list()
+ .["ref"] = REF(src)
+ .["name"] = name
+ .["availible"] = is_availible()
+ .["reason"] = get_availible_reason()
+ if(wait)
+ .["timeout"] = max(1 - (wait - (world.time - SSticker.round_start_time)) / wait, 0)
+
+/* Destination is another gateway */
+/datum/gateway_destination/gateway
+ /// The gateway this destination points at
+ var/obj/machinery/gateway/target_gateway
+
+/* We set the target gateway target to activator gateway */
+/datum/gateway_destination/gateway/activate(obj/machinery/gateway/activated)
+ if(!target_gateway.target)
+ target_gateway.activate(activated)
+
+/* We turn off the target gateway if it's linked with us */
+/datum/gateway_destination/gateway/deactivate(obj/machinery/gateway/deactivated)
+ if(target_gateway.target == deactivated.destination)
+ target_gateway.deactivate()
+
+/datum/gateway_destination/gateway/is_availible()
+ return ..() && target_gateway.calibrated && !target_gateway.target && target_gateway.powered()
+
+/datum/gateway_destination/gateway/get_availible_reason()
+ . = ..()
+ if(!target_gateway.calibrated)
+ . = "Exit gateway malfunction. Manual recalibration required."
+ if(target_gateway.target)
+ . = "Exit gateway in use."
+ if(!target_gateway.powered())
+ . = "Exit gateway unpowered."
+
+/datum/gateway_destination/gateway/get_target_turf()
+ return get_step(target_gateway.portal,SOUTH)
+
+/datum/gateway_destination/gateway/post_transfer(atom/movable/AM)
+ . = ..()
+ addtimer(CALLBACK(AM,/atom/movable.proc/setDir,SOUTH),0)
+
+/* Special home destination, so we can check exile implants */
+/datum/gateway_destination/gateway/home
+
+/datum/gateway_destination/gateway/home/incoming_pass_check(atom/movable/AM)
+ if(isliving(AM))
+ if(check_exile_implant(AM))
+ return FALSE
+ else
+ for(var/mob/living/L in AM.contents)
+ if(check_exile_implant(L))
+ target_gateway.say("Rejecting [AM]: Exile implant detected in contained lifeform.")
+ return FALSE
+ if(AM.has_buckled_mobs())
+ for(var/mob/living/L in AM.buckled_mobs)
+ if(check_exile_implant(L))
+ target_gateway.say("Rejecting [AM]: Exile implant detected in close proximity lifeform.")
+ return FALSE
+ return TRUE
+
+/datum/gateway_destination/gateway/home/proc/check_exile_implant(mob/living/L)
+ for(var/obj/item/implant/exile/E in L.implants)//Checking that there is an exile implant
+ to_chat(L, "The station gate has detected your exile implant and is blocking your entry. ")
+ return TRUE
+ return FALSE
+
+
+/* Destination is one ore more turfs - created by landmarks */
+/datum/gateway_destination/point
+ var/list/target_turfs = list()
+ /// Used by away landmarks
+ var/id
+
+/datum/gateway_destination/point/get_target_turf()
+ return pick(target_turfs)
+
+/* Dense invisible object starting the teleportation. Created by gateways on activation. */
+/obj/effect/gateway_portal_bumper
+ var/obj/machinery/gateway/gateway
+ density = TRUE
+ invisibility = INVISIBILITY_ABSTRACT
+
+/obj/effect/gateway_portal_bumper/Bumped(atom/movable/AM)
+ if(get_dir(src,AM) == SOUTH)
+ gateway.Transfer(AM)
+
+/obj/effect/gateway_portal_bumper/Destroy(force)
+ . = ..()
+ gateway = null
/obj/machinery/gateway
name = "gateway"
desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations."
icon = 'icons/obj/machines/gateway.dmi'
icon_state = "off"
- density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- var/active = 0
- var/checkparts = TRUE
- var/list/obj/effect/landmark/randomspawns = list()
+
+ // 3x2 offset by one row
+ pixel_x = -32
+ pixel_y = -32
+ bound_height = 64
+ bound_width = 96
+ bound_x = -32
+ bound_y = 0
+ density = TRUE
+
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 100
+ active_power_usage = 5000
+
var/calibrated = TRUE
- var/list/linked = list()
- var/can_link = FALSE //Is this the centerpiece?
+ /// Type of instanced gateway destination, needs to be subtype of /datum/gateway_destination/gateway
+ var/destination_type = /datum/gateway_destination/gateway
+ /// Name of the generated destination
+ var/destination_name = "Unknown Gateway"
+ /// This is our own destination, pointing at this gateway
+ var/datum/gateway_destination/gateway/destination
+ /// This is current active destination
+ var/datum/gateway_destination/target
+ /// bumper object, the thing that starts actual teleport
+ var/obj/effect/gateway_portal_bumper/portal
/obj/machinery/gateway/Initialize()
- randomspawns = GLOB.awaydestinations
+ generate_destination()
update_icon()
- if(!istype(src, /obj/machinery/gateway/centerstation) && !istype(src, /obj/machinery/gateway/centeraway))
- switch(dir)
- if(SOUTH,SOUTHEAST,SOUTHWEST)
- density = FALSE
return ..()
-/obj/machinery/gateway/proc/toggleoff()
- for(var/obj/machinery/gateway/G in linked)
- G.active = 0
- G.update_icon()
- active = 0
+/obj/machinery/gateway/proc/generate_destination()
+ destination = new destination_type
+ destination.name = destination_name
+ destination.target_gateway = src
+ GLOB.gateway_destinations += destination
+
+/obj/machinery/gateway/proc/deactivate()
+ var/datum/gateway_destination/dest = target
+ target = null
+ dest.deactivate(src)
+ QDEL_NULL(portal)
+ use_power = IDLE_POWER_USE
update_icon()
-/obj/machinery/gateway/proc/detect()
- if(!can_link)
- return FALSE
- linked = list() //clear the list
- var/turf/T = loc
- var/ready = FALSE
-
- for(var/i in GLOB.alldirs)
- T = get_step(loc, i)
- var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T
- if(G)
- linked.Add(G)
- continue
-
- //this is only done if we fail to find a part
- ready = FALSE
- toggleoff()
- break
-
- if((linked.len == 8) || !checkparts)
- ready = TRUE
- return ready
+/obj/machinery/gateway/process()
+ if((stat & (NOPOWER)) && use_power)
+ if(target)
+ deactivate()
+ return
/obj/machinery/gateway/update_icon_state()
- icon_state = active ? "on" : "off"
+ if(target)
+ icon_state = "on"
+ else
+ icon_state = "off"
-/obj/machinery/gateway/attack_hand(mob/user)
- . = ..()
- if(.)
- return
- if(!detect())
- return
- if(!active)
- toggleon(user)
- return
- toggleoff()
-
-/obj/machinery/gateway/proc/toggleon(mob/user)
- return FALSE
-
-/obj/machinery/gateway/safe_throw_at()
+/obj/machinery/gateway/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
return
+/obj/machinery/gateway/proc/generate_bumper()
+ portal = new(get_turf(src))
+ portal.gateway = src
+
+/obj/machinery/gateway/proc/activate(datum/gateway_destination/D)
+ if(!powered() || target)
+ return
+ target = D
+ target.activate(destination)
+ generate_bumper()
+ use_power = ACTIVE_POWER_USE
+ update_icon()
+
+/obj/machinery/gateway/proc/Transfer(atom/movable/AM)
+ if(!target || !target.incoming_pass_check(AM))
+ return
+ AM.forceMove(target.get_target_turf())
+ target.post_transfer(AM)
+
+/* Station's primary gateway */
+/obj/machinery/gateway/centerstation
+ destination_type = /datum/gateway_destination/gateway/home
+ destination_name = "Home Gateway"
+
/obj/machinery/gateway/centerstation/Initialize()
. = ..()
if(!GLOB.the_gateway)
GLOB.the_gateway = src
- update_icon()
- wait = world.time + CONFIG_GET(number/gateway_delay) //+ thirty minutes default
- awaygate = locate(/obj/machinery/gateway/centeraway)
/obj/machinery/gateway/centerstation/Destroy()
if(GLOB.the_gateway == src)
GLOB.the_gateway = null
return ..()
-//this is da important part wot makes things go
-/obj/machinery/gateway/centerstation
- density = TRUE
- icon_state = "offcenter"
- use_power = IDLE_POWER_USE
-
- //warping vars
- var/wait = 0 //this just grabs world.time at world start
- var/obj/machinery/gateway/centeraway/awaygate = null
- can_link = TRUE
-
-/obj/machinery/gateway/centerstation/update_icon_state()
- icon_state = active ? "oncenter" : "offcenter"
-
-/obj/machinery/gateway/centerstation/process()
- if((stat & (NOPOWER)) && use_power)
- if(active)
- toggleoff()
- return
-
- if(active)
- use_power(5000)
-
-/obj/machinery/gateway/centerstation/toggleon(mob/user)
- if(!detect())
- return
- if(!powered())
- return
- if(!awaygate)
- to_chat(user, "Error: No destination found. ")
- return
- if(world.time < wait)
- to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [DisplayTimeText(wait - world.time)]. ")
- return
-
- for(var/obj/machinery/gateway/G in linked)
- G.active = 1
- G.update_icon()
- active = 1
- update_icon()
-
-//okay, here's the good teleporting stuff
-/obj/machinery/gateway/centerstation/Bumped(atom/movable/AM)
- if(!active)
- return
- if(!detect())
- return
- if(!awaygate || QDELETED(awaygate))
- return
-
- if(awaygate.calibrated)
- AM.forceMove(get_step(awaygate.loc, SOUTH))
- AM.setDir(SOUTH)
- if (ismob(AM))
- var/mob/M = AM
- if (M.client)
- M.client.move_delay = max(world.time + 5, M.client.move_delay)
- return
+/obj/machinery/gateway/multitool_act(mob/living/user, obj/item/I)
+ if(calibrated)
+ to_chat(user, "The gate is already calibrated, there is no work for you to do here. ")
else
- var/obj/effect/landmark/dest = pick(randomspawns)
- if(dest)
- AM.forceMove(get_turf(dest))
- AM.setDir(SOUTH)
- use_power(5000)
- return
-
-/obj/machinery/gateway/centeraway/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/multitool))
- if(calibrated)
- to_chat(user, "\black The gate is already calibrated, there is no work for you to do here.")
- return
- else
- to_chat(user, "Recalibration successful! : \black This gate's systems have been fine tuned. Travel to this gate will now be on target.")
- calibrated = TRUE
- return
-
-/////////////////////////////////////Away////////////////////////
-
-
-/obj/machinery/gateway/centeraway
- density = TRUE
- icon_state = "offcenter"
- use_power = NO_POWER_USE
- var/obj/machinery/gateway/centerstation/stationgate = null
- can_link = TRUE
-
-
-/obj/machinery/gateway/centeraway/Initialize()
- . = ..()
- update_icon()
- stationgate = locate(/obj/machinery/gateway/centerstation)
-
-
-/obj/machinery/gateway/centeraway/update_icon_state()
- icon_state = active ? "oncenter" : "offcenter"
-
-/obj/machinery/gateway/centeraway/toggleon(mob/user)
- if(!detect())
- return
- if(!stationgate)
- to_chat(user, "Error: No destination found. ")
- return
-
- for(var/obj/machinery/gateway/G in linked)
- G.active = 1
- G.update_icon()
- active = 1
- update_icon()
-
-/obj/machinery/gateway/centeraway/proc/check_exile_implant(mob/living/L)
- for(var/obj/item/implant/exile/E in L.implants)//Checking that there is an exile implant
- to_chat(L, "\black The station gate has detected your exile implant and is blocking your entry.")
- return TRUE
- return FALSE
-
-/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM)
- if(!detect())
- return
- if(!active)
- return
- if(!stationgate || QDELETED(stationgate))
- return
- if(isliving(AM))
- if(check_exile_implant(AM))
- return
- else
- for(var/mob/living/L in AM.contents)
- if(check_exile_implant(L))
- say("Rejecting [AM]: Exile implant detected in contained lifeform.")
- return
- if(AM.has_buckled_mobs())
- for(var/mob/living/L in AM.buckled_mobs)
- if(check_exile_implant(L))
- say("Rejecting [AM]: Exile implant detected in close proximity lifeform.")
- return
- AM.forceMove(get_step(stationgate.loc, SOUTH))
- AM.setDir(SOUTH)
- if (ismob(AM))
- var/mob/M = AM
- if (M.client)
- M.client.move_delay = max(world.time + 5, M.client.move_delay)
-
-
-/obj/machinery/gateway/centeraway/admin
- desc = "A mysterious gateway built by unknown hands, this one seems more compact."
-
-/obj/machinery/gateway/centeraway/admin/Initialize()
- . = ..()
- if(stationgate && !stationgate.awaygate)
- stationgate.awaygate = src
-
-/obj/machinery/gateway/centeraway/admin/detect()
+ to_chat(user, "Recalibration successful! : \black This gate's systems have been fine tuned. Travel to this gate will now be on target.")
+ calibrated = TRUE
return TRUE
+/* Doesn't need control console or power, always links to home when interacting. */
+/obj/machinery/gateway/away
+ density = TRUE
+ use_power = NO_POWER_USE
+
+/obj/machinery/gateway/away/interact(mob/user, special_state)
+ . = ..()
+ if(!target)
+ if(!GLOB.the_gateway)
+ to_chat(user,"Home gateway is not responding! ")
+ if(GLOB.the_gateway.target)
+ to_chat(user,"Home gateway already in use! ")
+ return
+ activate(GLOB.the_gateway.destination)
+ else
+ deactivate()
+
+/* Gateway control computer */
+/obj/machinery/computer/gateway_control
+ name = "Gateway Control"
+ desc = "Human friendly interface to the mysterious gate next to it."
+ var/obj/machinery/gateway/G
+
+/obj/machinery/computer/gateway_control/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ try_to_linkup()
+
+/obj/machinery/computer/gateway_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Gateway", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/gateway_control/ui_data(mob/user)
+ . = ..()
+ .["gateway_present"] = G
+ .["gateway_status"] = G ? G.powered() : FALSE
+ .["current_target"] = G?.target?.get_ui_data()
+ var/list/destinations = list()
+ if(G)
+ for(var/datum/gateway_destination/D in GLOB.gateway_destinations)
+ if(D == G.destination)
+ continue
+ destinations += list(D.get_ui_data())
+ .["destinations"] = destinations
+
+/obj/machinery/computer/gateway_control/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("linkup")
+ try_to_linkup()
+ return TRUE
+ if("activate")
+ var/datum/gateway_destination/D = locate(params["destination"]) in GLOB.gateway_destinations
+ try_to_connect(D)
+ return TRUE
+ if("deactivate")
+ if(G && G.target)
+ G.deactivate()
+ return TRUE
+
+/obj/machinery/computer/gateway_control/proc/try_to_linkup()
+ G = locate(/obj/machinery/gateway) in view(7,get_turf(src))
+
+/obj/machinery/computer/gateway_control/proc/try_to_connect(datum/gateway_destination/D)
+ if(!D || !G)
+ return
+ if(!D.is_availible() || G.target)
+ return
+ G.activate(D)
/obj/item/paper/fluff/gateway
- info = "Congratulations, Your station has been selected to carry out the Gateway Project. The equipment will be shipped to you at the start of the next quarter. You are to prepare a secure location to house the equipment as outlined in the attached documents. --Nanotrasen Blue Space Research"
+ info = "Congratulations, Your station has been selected to carry out the Gateway Project. The equipment will be shipped to you at the start of the next quarter. You are to prepare a secure location to house the equipment as outlined in the attached documents. --Nanotrasen Bluespace Research"
name = "Confidential Correspondence, Pg 1"
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index 3418f78dbd..f33ea7059b 100644
--- a/code/modules/cargo/centcom_podlauncher.dm
+++ b/code/modules/cargo/centcom_podlauncher.dm
@@ -55,7 +55,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "centcom_podlauncher", "Config/Launch Supplypod", 700, 700, master_ui, state)
+ ui = new(user, src, ui_key, "CentcomPodLauncher", "Config/Launch Supplypod", 700, 700, master_ui, state)
ui.open()
/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI.
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index 64b208a90b..0f0a7975ed 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -68,15 +68,18 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "cargo", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "Cargo", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/cargo/ui_data()
var/list/data = list()
data["location"] = SSshuttle.supply.getStatusText()
+ /*var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ data["points"] = D.account_balance*/
data["away"] = SSshuttle.supply.getDockedId() == "supply_away"
+ //data["self_paid"] = self_paid
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
- data["points"] = SSshuttle.points
data["loan"] = !!SSshuttle.shuttle_loan
data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched
var/message = "Remember to stamp and send back the supply manifests."
@@ -92,6 +95,7 @@
"cost" = SO.pack.cost,
"id" = SO.id,
"orderer" = SO.orderer,
+ "paid" = !isnull(SO.paying_account) //paid by requester
))
data["requests"] = list()
@@ -124,6 +128,7 @@
"cost" = P.cost,
"id" = pack,
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
+ //"small_item" = P.small_item,
"access" = P.access
))
return data
@@ -162,6 +167,8 @@
else
SSshuttle.shuttle_loan.loan_shuttle()
say("The supply shuttle has been loaned to CentCom.")
+ investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO)
+ log_game("[key_name(usr)] accepted a shuttle loan event.")
. = TRUE
if("add")
var/id = text2path(params["id"])
@@ -182,19 +189,36 @@
name = usr.real_name
rank = "Silicon"
+ /* var/datum/bank_account/account
+ if(self_paid && ishuman(usr))
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/card/id/id_card = H.get_idcard(TRUE)
+ if(!istype(id_card))
+ say("No ID card detected.")
+ return
+ account = id_card.registered_account
+ if(!istype(account))
+ say("Invalid bank account.")
+ return
+ */
var/reason = ""
- if(requestonly)
+ if(requestonly && !self_paid)
reason = stripped_input("Reason:", name, "")
if(isnull(reason) || ..())
return
var/turf/T = get_turf(src)
- var/datum/supply_order/SO = new(pack, name, rank, ckey, reason)
+ var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account)
SO.generateRequisition(T)
- if(requestonly)
+ if(requestonly && !self_paid)
SSshuttle.requestlist += SO
else
SSshuttle.shoppinglist += SO
+ if(self_paid)
+ say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.")
+ if(requestonly && message_cooldown < world.time)
+ radio.talk_into(src, "A new order has been requested.", RADIO_CHANNEL_SUPPLY)
+ message_cooldown = world.time + 30 SECONDS
. = TRUE
if("remove")
var/id = text2num(params["id"])
@@ -224,6 +248,9 @@
if("denyall")
SSshuttle.requestlist.Cut()
. = TRUE
+ /* if("toggleprivate")
+ self_paid = !self_paid
+ */ . = TRUE
if(.)
post_signal("supply")
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index a65d8cad40..3761a34f73 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -1,5 +1,5 @@
#define MAX_EMAG_ROCKETS 8
-#define BEACON_COST 5000
+#define BEACON_COST 500
#define SP_LINKED 1
#define SP_READY 2
#define SP_LAUNCH 3
@@ -13,8 +13,11 @@
All sales are near instantaneous - please choose carefully"
icon_screen = "supply_express"
circuit = /obj/item/circuitboard/computer/cargo/express
+ ui_x = 600
+ ui_y = 700
blockade_warning = "Bluespace instability detected. Delivery impossible."
req_access = list(ACCESS_QM)
+
var/message
var/printed_beacons = 0 //number of beacons printed. Used to determine beacon names.
var/list/meme_pack_data
@@ -40,7 +43,7 @@
to_chat(user, "You [locked ? "lock" : "unlock"] the interface. ")
return
else if(istype(W, /obj/item/disk/cargo/bluespace_pod))
- podType = /obj/structure/closet/supplypod/bluespacepod
+ podType = /obj/structure/closet/supplypod/bluespacepod//doesnt effect circuit board, making reversal possible
to_chat(user, "You insert the disk into [src], allowing for advanced supply delivery vehicles. ")
qdel(W)
return TRUE
@@ -50,22 +53,20 @@
sb.link_console(src, user)
return TRUE
else
- to_chat(user, "[src] is already linked to [sb]. ")
+ to_chat(user, "[src] is already linked to [sb]. ")
..()
/obj/machinery/computer/cargo/express/emag_act(mob/living/user)
- . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
if(obj_flags & EMAGGED)
return
- user.visible_message("[user] swipes a suspicious card through [src]! ",
- "You change the routing protocols, allowing the Supply Pod to land anywhere on the station. ")
+ if(user)
+ user.visible_message("[user] swipes a suspicious card through [src]! ",
+ "You change the routing protocols, allowing the Supply Pod to land anywhere on the station. ")
obj_flags |= EMAGGED
// This also sets this on the circuit board
var/obj/item/circuitboard/computer/cargo/board = circuit
board.obj_flags |= EMAGGED
packin_up()
- req_access = list()
- return TRUE
/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry
meme_pack_data = list() // sorry for what?
@@ -90,23 +91,25 @@
/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "cargo_express", name, 600, 700, master_ui, state)
+ ui = new(user, src, ui_key, "CargoExpress", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/cargo/express/ui_data(mob/user)
var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location?
var/list/data = list()
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ data["points"] = D.account_balance
data["locked"] = locked//swipe an ID to unlock
- data["siliconUser"] = hasSiliconAccessInArea(user)
+ data["siliconUser"] = user.has_unlimited_silicon_privilege
data["beaconzone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui
data["usingBeacon"] = usingBeacon //is the mode set to deliver to the beacon or the cargobay?
data["canBeacon"] = !usingBeacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
- data["canBuyBeacon"] = cooldown <= 0 && SSshuttle.points >= BEACON_COST
+ data["canBuyBeacon"] = cooldown <= 0 && D.account_balance >= BEACON_COST
data["beaconError"] = usingBeacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary
data["hasBeacon"] = beacon != null//is there a linked beacon?
data["beaconName"] = beacon ? beacon.name : "No Beacon Found"
data["printMsg"] = cooldown > 0 ? "Print Beacon for [BEACON_COST] credits ([cooldown])" : "Print Beacon for [BEACON_COST] credits"//buttontext for printing beacons
- data["points"] = SSshuttle.points
data["supplies"] = list()
message = "Sales are near-instantaneous - please choose carefully."
if(SSshuttle.supplyBlocked)
@@ -137,13 +140,15 @@
if (beacon)
beacon.update_status(SP_READY) //turns on the beacon's ready light
if("printBeacon")
- if (SSshuttle.points >= BEACON_COST)
- cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam
- var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location())
- C.link_console(src, usr)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
- printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
- beacon.name = "Supply Pod Beacon #[printed_beacons]"
- SSshuttle.points -= BEACON_COST
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ if(D.adjust_money(-BEACON_COST))
+ cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam
+ var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location())
+ C.link_console(src, usr)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
+ printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
+ beacon.name = "Supply Pod Beacon #[printed_beacons]"
+
if("add")//Generate Supply Order first
var/id = text2path(params["id"])
@@ -163,8 +168,12 @@
var/reason = ""
var/list/empty_turfs
var/datum/supply_order/SO = new(pack, name, rank, ckey, reason)
+ var/points_to_check
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ points_to_check = D.account_balance
if(!(obj_flags & EMAGGED))
- if(SO.pack.cost <= SSshuttle.points)
+ if(SO.pack.cost <= points_to_check)
var/LZ
if (istype(beacon) && usingBeacon)//prioritize beacons over landing in cargobay
LZ = get_turf(beacon)
@@ -181,14 +190,13 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
LZ = pick(empty_turfs)
- if (SO.pack.cost <= SSshuttle.points && LZ)//we need to call the cost check again because of the CHECK_TICK call
- SSshuttle.points -= SO.pack.cost
- SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]"))
- new /obj/effect/abstract/DPtarget(LZ, podType, SO)
+ if (SO.pack.cost <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call
+ D.adjust_money(-SO.pack.cost)
+ new /obj/effect/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
else
- if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^)
+ if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= points_to_check) // bulk discount :^)
landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)] //override default landing zone
for(var/turf/open/floor/T in landingzone.contents)
if(is_blocked_turf(T))
@@ -196,13 +204,13 @@
LAZYADD(empty_turfs, T)
CHECK_TICK
if(empty_turfs && empty_turfs.len)
- SSshuttle.points -= SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)
- SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]"))
+ D.adjust_money(-(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)))
+
SO.generateRequisition(get_turf(src))
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)
LAZYREMOVE(empty_turfs, LZ)
- new /obj/effect/abstract/DPtarget(LZ, podType, SO)
+ new /obj/effect/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
CHECK_TICK
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 86ff1ed0da..464db74c9e 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -5,7 +5,7 @@
max_occurrences = 1
min_players = 10
earliest_start = 30 MINUTES
- gamemode_blacklist = list("nuclear","dynamic")
+ gamemode_blacklist = list("nuclear")
/datum/round_event_control/pirates/preRunEvent()
if (!SSmapping.empty_space)
@@ -15,8 +15,9 @@
/datum/round_event/pirates
startWhen = 60 //2 minutes to answer
- var/datum/comm_message/threat_message
+ var/datum/comm_message/threat
var/payoff = 0
+ var/payoff_min = 20000
var/paid_off = FALSE
var/ship_name = "Space Privateers Association"
var/shuttle_spawned = FALSE
@@ -25,30 +26,38 @@
ship_name = pick(strings(PIRATE_NAMES_FILE, "ship_names"))
/datum/round_event/pirates/announce(fake)
- priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") // CITADEL EDIT metabreak
+ priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", 'sound/ai/commandreport.ogg')
if(fake)
return
- threat_message = new
- payoff = round(SSshuttle.points * 0.80)
- threat_message.title = "Business proposition"
- threat_message.content = "This is [ship_name]. Pay up [payoff] credits or you'll walk the plank."
- threat_message.possible_answers = list("We'll pay.","No way.")
- threat_message.answer_callback = CALLBACK(src,.proc/answered)
- SScommunications.send_message(threat_message,unique = TRUE)
+ threat = new
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ payoff = max(payoff_min, FLOOR(D.account_balance * 0.80, 1000))
+ threat.title = "Business proposition"
+ threat.content = "This is [ship_name]. Pay up [payoff] credits or you'll walk the plank."
+ threat.possible_answers = list("We'll pay.","No way.")
+ threat.answer_callback = CALLBACK(src,.proc/answered)
+ SScommunications.send_message(threat,unique = TRUE)
/datum/round_event/pirates/proc/answered()
- if(threat_message && threat_message.answered == 1)
- if(SSshuttle.points >= payoff)
- SSshuttle.points -= payoff
- priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name)
- paid_off = TRUE
- return
- else
- priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
+ if(threat && threat.answered == 1)
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ if(D.adjust_money(-payoff))
+ priority_announce("Thanks for the credits, landlubbers.", sender_override = ship_name)
+ paid_off = TRUE
+ return
+ else
+ priority_announce("Trying to cheat us? You'll regret this!", sender_override = ship_name)
if(!shuttle_spawned)
spawn_shuttle()
+ else
+ priority_announce("Too late to beg for mercy!", sender_override = ship_name)
/datum/round_event/pirates/start()
+ if(threat && !threat.answered)
+ threat.possible_answers = list("Too late")
+ threat.answered = 1
if(!paid_off && !shuttle_spawned)
spawn_shuttle()
@@ -59,8 +68,8 @@
shuffle_inplace(candidates)
var/datum/map_template/shuttle/pirate/default/ship = new
- var/x = rand(TRANSITIONEDGE,world.maxx - TRANSITIONEDGE - ship.width)
- var/y = rand(TRANSITIONEDGE,world.maxy - TRANSITIONEDGE - ship.height)
+ var/x = rand(TRANSITIONEDGE, world.maxx - TRANSITIONEDGE - ship.width)
+ var/y = rand(TRANSITIONEDGE, world.maxy - TRANSITIONEDGE - ship.height)
var/z = SSmapping.empty_space.z_value
var/turf/T = locate(x,y,z)
if(!T)
@@ -68,16 +77,18 @@
if(!ship.load(T))
CRASH("Loading pirate ship failed!")
+
for(var/turf/A in ship.get_affected_turfs(T))
for(var/obj/effect/mob_spawn/human/pirate/spawner in A)
if(candidates.len > 0)
var/mob/M = candidates[1]
spawner.create(M.ckey)
candidates -= M
+ announce_to_ghosts(M)
else
- notify_ghosts("Space pirates are waking up!", source = spawner, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
+ announce_to_ghosts(spawner)
- priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") //CITADEL EDIT also metabreak here too
+ priority_announce("Unidentified armed ship detected near the station.")
//Shuttle equipment
@@ -94,16 +105,16 @@
/obj/machinery/shuttle_scrambler/Initialize(mapload)
. = ..()
- gps = new/obj/item/gps/internal/pirate(src)
- gps.tracking = FALSE
update_icon()
/obj/machinery/shuttle_scrambler/process()
if(active)
if(is_station_level(z))
- var/siphoned = min(SSshuttle.points,siphon_per_tick)
- SSshuttle.points -= siphoned
- credits_stored += siphoned
+ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(D)
+ var/siphoned = min(D.account_balance,siphon_per_tick)
+ D.adjust_money(-siphoned)
+ credits_stored += siphoned
interrupt_research()
else
return
@@ -122,7 +133,7 @@
if(!active)
if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel")
return
- if(active || !user.canUseTopic(src))
+ if(active || !user.canUseTopic(src, BE_CLOSE))
return
toggle_on(user)
update_icon()
@@ -139,14 +150,12 @@
new /obj/effect/temp_visual/emp(get_turf(S))
/obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user)
- if(credits_stored < 200)
- to_chat(user,"Not enough credits to retrieve. ")
- return
- while(credits_stored >= 200)
- new /obj/item/stack/spacecash/c200(drop_location())
- credits_stored -= 200
- to_chat(user,"You retrieve the siphoned credits! ")
- credits_stored = 0
+ if(credits_stored) // Prevents spamming empty holochips
+ new /obj/item/holochip(drop_location(), credits_stored)
+ to_chat(user,"You retrieve the siphoned credits! ")
+ credits_stored = 0
+ else
+ to_chat(user,"There's nothing to withdraw. ")
/obj/machinery/shuttle_scrambler/proc/send_notification()
priority_announce("Data theft signal detected, source registered on local gps units.")
@@ -171,7 +180,6 @@
/obj/item/gps/internal/pirate
gpstag = "Nautical Signal"
desc = "You can hear shanties over the static."
-
/obj/machinery/computer/shuttle/pirate
name = "pirate shuttle console"
shuttleId = "pirateship"
@@ -195,40 +203,19 @@
/obj/docking_port/mobile/pirate
name = "pirate shuttle"
id = "pirateship"
- var/engines_cooling = FALSE
- var/engine_cooldown = 3 MINUTES
-
-/obj/docking_port/mobile/pirate/getStatusText()
- . = ..()
- if(engines_cooling)
- return "[.] - Engines cooling."
-
-/obj/docking_port/mobile/pirate/initiate_docking(obj/docking_port/stationary/new_dock, movement_direction, force=FALSE)
- . = ..()
- if(. == DOCKING_SUCCESS && !is_reserved_level(new_dock.z))
- engines_cooling = TRUE
- addtimer(CALLBACK(src,.proc/reset_cooldown),engine_cooldown,TIMER_UNIQUE)
-
-/obj/docking_port/mobile/pirate/proc/reset_cooldown()
- engines_cooling = FALSE
-
-/obj/docking_port/mobile/pirate/canMove()
- if(engines_cooling)
- return FALSE
- return ..()
+ rechargeTime = 3 MINUTES
/obj/machinery/suit_storage_unit/pirate
suit_type = /obj/item/clothing/suit/space
helmet_type = /obj/item/clothing/head/helmet/space
mask_type = /obj/item/clothing/mask/breath
- storage_type = /obj/item/tank/jetpack/void
+ storage_type = /obj/item/tank/internals/oxygen
/obj/machinery/loot_locator
name = "Booty Locator"
desc = "This sophisticated machine scans the nearby space for items of value."
icon = 'icons/obj/machines/research.dmi'
icon_state = "tdoppler"
- resistance_flags = INDESTRUCTIBLE
density = TRUE
var/cooldown = 300
var/next_use = 0
@@ -262,13 +249,13 @@
name = "cargo hold pad"
icon = 'icons/obj/telescience.dmi'
icon_state = "lpad-idle-o"
- resistance_flags = INDESTRUCTIBLE
var/idle_state = "lpad-idle-o"
var/warmup_state = "lpad-idle"
var/sending_state = "lpad-beam"
var/cargo_hold_id
/obj/machinery/piratepad/multitool_act(mob/living/user, obj/item/multitool/I)
+ . = ..()
if (istype(I))
to_chat(user, "You register [src] in [I]s buffer. ")
I.buffer = src
@@ -276,8 +263,9 @@
/obj/machinery/computer/piratepad_control
name = "cargo hold control terminal"
- resistance_flags = INDESTRUCTIBLE
- var/status_report = "Idle"
+ ui_x = 600
+ ui_y = 230
+ var/status_report = "Ready for delivery."
var/obj/machinery/piratepad/pad
var/warmup_time = 100
var/sending = FALSE
@@ -291,10 +279,10 @@
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I)
+ . = ..()
if (istype(I) && istype(I.buffer,/obj/machinery/piratepad))
to_chat(user, "You link [src] with [I.buffer] in [I] buffer. ")
pad = I.buffer
- updateDialog()
return TRUE
/obj/machinery/computer/piratepad_control/LateInitialize()
@@ -307,29 +295,44 @@
else
pad = locate() in range(4,src)
-/obj/machinery/computer/piratepad_control/ui_interact(mob/user)
- . = ..()
- var/list/t = list()
- t += "Cargo Hold Control "
- t += "Current cargo value : [points]"
- t += "
"
- if(!pad)
- t += "No pad located.
"
- else
- t += " [status_report] "
- if(!sending)
- t += "Recalculate Value Send "
- else
- t += "Stop sending "
+/obj/machinery/computer/piratepad_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "CargoHoldTerminal", name, ui_x, ui_y, master_ui, state)
+ ui.open()
- var/datum/browser/popup = new(user, "piratepad", name, 300, 500)
- popup.set_content(t.Join())
- popup.open()
+/obj/machinery/computer/piratepad_control/ui_data(mob/user)
+ var/list/data = list()
+ data["points"] = points
+ data["pad"] = pad ? TRUE : FALSE
+ data["sending"] = sending
+ data["status_report"] = status_report
+ return data
+
+/obj/machinery/computer/piratepad_control/ui_act(action, params)
+ if(..())
+ return
+ if(!pad)
+ return
+
+ switch(action)
+ if("recalc")
+ recalc()
+ . = TRUE
+ if("send")
+ start_sending()
+ . = TRUE
+ if("stop")
+ stop_sending()
+ . = TRUE
/obj/machinery/computer/piratepad_control/proc/recalc()
if(sending)
return
- status_report = "Predicted value: "
+
+ status_report = "Predicted value: "
+ var/value = 0
var/datum/export_report/ex = new
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
@@ -337,7 +340,12 @@
export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, dry_run = TRUE, external_report = ex)
for(var/datum/export/E in ex.total_amount)
- status_report += E.total_printout(ex,notes = FALSE) + " "
+ status_report += E.total_printout(ex,notes = FALSE)
+ status_report += " "
+ value += ex.total_value[E]
+
+ if(!value)
+ status_report += "0"
/obj/machinery/computer/piratepad_control/proc/send()
if(!sending)
@@ -350,14 +358,15 @@
continue
export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, delete_unsold = FALSE, external_report = ex)
- status_report = "Sold: "
+ status_report = "Sold: "
var/value = 0
for(var/datum/export/E in ex.total_amount)
var/export_text = E.total_printout(ex,notes = FALSE) //Don't want nanotrasen messages, makes no sense here.
if(!export_text)
continue
- status_report += export_text + " "
+ status_report += export_text
+ status_report += " "
value += ex.total_value[E]
if(!total_report)
@@ -370,11 +379,13 @@
points += value
+ if(!value)
+ status_report += "Nothing"
+
pad.visible_message("[pad] activates! ")
flick(pad.sending_state,pad)
pad.icon_state = pad.idle_state
sending = FALSE
- updateDialog()
/obj/machinery/computer/piratepad_control/proc/start_sending()
if(sending)
@@ -389,24 +400,10 @@
if(!sending)
return
sending = FALSE
- status_report = "Idle"
+ status_report = "Ready for delivery."
pad.icon_state = pad.idle_state
deltimer(sending_timer)
-/obj/machinery/computer/piratepad_control/Topic(href, href_list)
- if(..())
- return
- if(pad)
- if(href_list["recalc"])
- recalc()
- if(href_list["send"])
- start_sending()
- if(href_list["stop"])
- stop_sending()
- updateDialog()
- else
- updateDialog()
-
/datum/export/pirate
export_category = EXPORT_PIRATE
@@ -431,6 +428,8 @@
var/mob/living/carbon/human/H = AM
if(H.stat != CONSCIOUS || !H.mind || !H.mind.assigned_role) //mint condition only
return 0
+ else if("pirate" in H.faction) //can't ransom your fellow pirates to CentCom!
+ return 0
else
if(H.mind.assigned_role in GLOB.command_positions)
return 3000
@@ -456,3 +455,12 @@
/datum/export/pirate/cash/get_amount(obj/O)
var/obj/item/stack/spacecash/C = O
return ..() * C.amount * C.value
+
+/datum/export/pirate/holochip
+ cost = 1
+ unit_name = "holochip"
+ export_types = list(/obj/item/holochip)
+
+/datum/export/pirate/holochip/get_cost(atom/movable/AM)
+ var/obj/item/holochip/H = AM
+ return H.credits
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index 67a636eb9c..17cd513c6e 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -160,7 +160,7 @@
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "smartvend", name, 440, 550, master_ui, state)
+ ui = new(user, src, ui_key, "SmartVend", name, 440, 550, master_ui, state)
ui.set_autoupdate(FALSE)
ui.open()
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index 65c69b995f..d72e1b1fc8 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -83,7 +83,7 @@
/obj/machinery/computer/holodeck/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "holodeck", name, 400, 500, master_ui, state)
+ ui = new(user, src, ui_key, "Holodeck", name, 400, 500, master_ui, state)
ui.open()
/obj/machinery/computer/holodeck/ui_data(mob/user)
diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm
index eea87f1d80..d707c653e5 100644
--- a/code/modules/language/language_menu.dm
+++ b/code/modules/language/language_menu.dm
@@ -11,7 +11,7 @@
/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "language_menu", "Language Menu", 700, 600, master_ui, state)
+ ui = new(user, src, ui_key, "LanguageMenu", "Language Menu", 700, 600, master_ui, state)
ui.open()
/datum/language_menu/ui_data(mob/user)
diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm
index 57bf37d528..146c4221f8 100644
--- a/code/modules/library/lib_codex_gigas.dm
+++ b/code/modules/library/lib_codex_gigas.dm
@@ -99,7 +99,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "codex_gigas", name, 450, 450, master_ui, state)
+ ui = new(user, src, ui_key, "CodexGigas", name, 450, 450, master_ui, state)
ui.open()
/obj/item/book/codex_gigas/ui_data(mob/user)
diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm
index 272e39957e..50f984c44d 100644
--- a/code/modules/library/soapstone.dm
+++ b/code/modules/library/soapstone.dm
@@ -209,7 +209,7 @@
/obj/structure/chisel_message/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "engraved_message", name, 600, 300, master_ui, state)
+ ui = new(user, src, ui_key, "EngravedMessage", name, 600, 300, master_ui, state)
ui.open()
/obj/structure/chisel_message/ui_data(mob/user)
diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm
index 14a277a66c..44aa3ebec7 100644
--- a/code/modules/mining/laborcamp/laborstacker.dm
+++ b/code/modules/mining/laborcamp/laborstacker.dm
@@ -8,6 +8,9 @@ GLOBAL_LIST(labor_sheet_values)
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "console"
density = FALSE
+ ui_x = 315
+ ui_y = 430
+
var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null
var/machinedir = SOUTH
var/obj/machinery/door/airlock/release_door
@@ -36,7 +39,7 @@ GLOBAL_LIST(labor_sheet_values)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "labor_claim_console", name, 315, 430, master_ui, state)
+ ui = new(user, src, ui_key, "LaborClaimConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/mineral/labor_claim_console/ui_data(mob/user)
@@ -100,7 +103,6 @@ GLOBAL_LIST(labor_sheet_values)
Radio.talk_into(src, "A prisoner has returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY)
to_chat(usr, "Shuttle received message and will be sent shortly. ")
. = TRUE
-
/obj/machinery/mineral/labor_claim_console/proc/locate_stacking_machine()
stacking_machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir))
@@ -110,19 +112,16 @@ GLOBAL_LIST(labor_sheet_values)
qdel(src)
/obj/machinery/mineral/labor_claim_console/emag_act(mob/user)
- . = ..()
- if(obj_flags & EMAGGED)
- return
- obj_flags |= EMAGGED
- to_chat(user, "PZZTTPFFFT ")
- return TRUE
+ if(!(obj_flags & EMAGGED))
+ obj_flags |= EMAGGED
+ to_chat(user, "PZZTTPFFFT ")
/**********************Prisoner Collection Unit**************************/
/obj/machinery/mineral/stacking_machine/laborstacker
force_connect = TRUE
var/points = 0 //The unclaimed value of ore stacked.
-
+ damage_deflection = 21
/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
points += inp.point_value * inp.amount
..()
diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm
index 2f6e8a9b7c..d4703b8d94 100644
--- a/code/modules/mining/machine_redemption.dm
+++ b/code/modules/mining/machine_redemption.dm
@@ -193,7 +193,7 @@
/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ore_redemption_machine", "Ore Redemption Machine", 440, 550, master_ui, state)
+ ui = new(user, src, ui_key, "OreRedemptionMachine", "Ore Redemption Machine", 440, 550, master_ui, state)
ui.open()
/obj/machinery/mineral/ore_redemption/ui_data(mob/user)
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index 0182dec254..909807b9b1 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -93,10 +93,22 @@
else
icon_state = "[initial(icon_state)]-off"
-/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user)
- . = ..()
- var/list/dat = list()
- dat += "Equipment point cost list: "
+/obj/machinery/mineral/equipment_vendor/ui_base_html(html)
+ var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending)
+ . = replacetext(html, "", assets.css_tag())
+
+/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending)
+ assets.send(user)
+ ui = new(user, src, ui_key, "MiningVendor", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user)
+ . = list()
+ .["product_records"] = list()
for(var/datum/data/mining_equipment/prize in prize_list)
dat += "[prize.equipment_name] [prize.cost] Purchase "
dat += "
"
diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm
index d04c0104e5..81a9f873e4 100644
--- a/code/modules/mining/mint.dm
+++ b/code/modules/mining/mint.dm
@@ -6,11 +6,14 @@
icon = 'icons/obj/economy.dmi'
icon_state = "coinpress0"
density = TRUE
- var/newCoins = 0 //how many coins the machine made in it's last load
+ input_dir = EAST
+ ui_x = 300
+ ui_y = 250
+ needs_item_input = TRUE
+
+ var/produced_coins = 0 // how many coins the machine has made in it's last cycle
var/processing = FALSE
var/chosen = /datum/material/iron //which material will be used to make coins
- var/coinsToProduce = 10
- speed_process = TRUE
/obj/machinery/mineral/mint/Initialize()
@@ -28,89 +31,102 @@
/datum/material/mythril,
/datum/material/plastic,
/datum/material/runite
- ), MINERAL_MATERIAL_AMOUNT * 50, FALSE, /obj/item/stack)
- chosen = SSmaterials.GetMaterialRef(chosen)
+ ), MINERAL_MATERIAL_AMOUNT * 75, FALSE, /obj/item/stack)
+ chosen = SSmaterials.GetMaterialRef(chosen)
-/obj/machinery/mineral/mint/process()
- var/turf/T = get_step(src, input_dir)
- if(!T)
+
+/obj/machinery/mineral/mint/pickup_item(datum/source, atom/movable/target, atom/oldLoc)
+ if(!istype(target, /obj/item/stack))
return
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- for(var/obj/item/stack/sheet/O in T)
- var/inserted = materials.insert_item(O)
- if(inserted)
- qdel(O)
+ var/obj/item/stack/S = target
-/obj/machinery/mineral/mint/attack_hand(mob/user)
+ if(materials.insert_item(S))
+ qdel(S)
+
+/obj/machinery/mineral/mint/process()
+ if(processing)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ var/datum/material/M = chosen
+
+ if(!M)
+ processing = FALSE
+ icon_state = "coinpress0"
+ return
+
+ icon_state = "coinpress1"
+ var/coin_mat = MINERAL_MATERIAL_AMOUNT
+
+ for(var/sheets in 1 to 2)
+ if(materials.use_amount_mat(coin_mat, chosen))
+ for(var/coin_to_make in 1 to 5)
+ create_coins()
+ produced_coins++
+ else
+ var/found_new = FALSE
+ for(var/datum/material/inserted_material in materials.materials)
+ var/amount = materials.get_material_amount(inserted_material)
+
+ if(amount)
+ chosen = inserted_material
+ found_new = TRUE
+
+ if(!found_new)
+ processing = FALSE
+ else
+ end_processing()
+ icon_state = "coinpress0"
+
+/obj/machinery/mineral/mint/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Mint", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/mineral/mint/ui_data()
+ var/list/data = list()
+ data["inserted_materials"] = list()
+ data["chosen_material"] = null
+
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ for(var/datum/material/inserted_material in materials.materials)
+ var/amount = materials.get_material_amount(inserted_material)
+ if(!amount)
+ continue
+ data["inserted_materials"] += list(list(
+ "material" = inserted_material.name,
+ "amount" = amount,
+ ))
+ if(chosen == inserted_material)
+ data["chosen_material"] = inserted_material.name
+
+ data["produced_coins"] = produced_coins
+ data["processing"] = processing
+
+ return data;
+
+/obj/machinery/mineral/mint/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
return
- var/dat = "Coin Press "
-
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- for(var/datum/material/M in materials.materials)
- var/amount = materials.get_material_amount(M)
- if(!amount && chosen != M)
- continue
- dat += "[M.name] amount: [amount] cm3 "
- if (chosen == M)
- dat += "Chosen "
- else
- dat += "Choose "
-
- var/datum/material/M = chosen
-
- dat += " Will produce [coinsToProduce] [lowertext(M.name)] coins if enough materials are available. "
- dat += "-10 "
- dat += "-5 "
- dat += "-1 "
- dat += "+1 "
- dat += "+5 "
- dat += "+10 "
-
- dat += " In total this machine produced [newCoins] coins."
- dat += "Make coins "
- user << browse(dat, "window=mint")
-
-/obj/machinery/mineral/mint/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- if(processing==1)
- to_chat(usr, "The machine is processing. ")
- return
- var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
- if(href_list["choose"])
- var/datum/material/new_material = locate(href_list["choose"])
- if(istype(new_material))
- chosen = new_material
- if(href_list["chooseAmt"])
- coinsToProduce = CLAMP(coinsToProduce + text2num(href_list["chooseAmt"]), 0, 1000)
- updateUsrDialog()
- if(href_list["makeCoins"])
- var/temp_coins = coinsToProduce
+ if(action == "startpress")
+ if (!processing)
+ produced_coins = 0
processing = TRUE
- icon_state = "coinpress1"
- var/coin_mat = MINERAL_MATERIAL_AMOUNT * 0.2
- var/datum/material/M = chosen
- if(!M)
- updateUsrDialog()
- return
-
- while(coinsToProduce > 0 && materials.use_amount_mat(coin_mat, chosen))
- create_coins()
- coinsToProduce--
- newCoins++
- src.updateUsrDialog()
- sleep(5)
-
- icon_state = "coinpress0"
+ begin_processing()
+ return TRUE
+ if (action == "stoppress")
processing = FALSE
- coinsToProduce = temp_coins
- src.updateUsrDialog()
- return
+ end_processing()
+ return TRUE
+ if (action == "changematerial")
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ for(var/datum/material/mat in materials.materials)
+ if (params["material_name"] == mat.name)
+ chosen = mat
+ return TRUE
/obj/machinery/mineral/mint/proc/create_coins()
var/turf/T = get_step(src,output_dir)
diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm
index 1d803371be..e689a37b25 100644
--- a/code/modules/mining/satchel_ore_boxdm.dm
+++ b/code/modules/mining/satchel_ore_boxdm.dm
@@ -9,6 +9,9 @@
density = TRUE
pressure_resistance = 5*ONE_ATMOSPHERE
+ var/ui_x = 335
+ var/ui_y = 415
+
/obj/structure/ore_box/attackby(obj/item/W, mob/user, params)
if (istype(W, /obj/item/stack/ore))
user.transferItemToLoc(W, src)
@@ -24,16 +27,16 @@
/obj/structure/ore_box/crowbar_act(mob/living/user, obj/item/I)
if(I.use_tool(src, user, 50, volume=50))
- user.visible_message("[user] pries \the [src] apart.",
+ user.visible_message("[user] pries \the [src] apart. ",
"You pry apart \the [src]. ",
- "You hear splitting wood. ")
+ "You hear splitting wood. ")
deconstruct(TRUE, user)
return TRUE
/obj/structure/ore_box/examine(mob/living/user)
if(Adjacent(user) && istype(user))
ui_interact(user)
- return ..()
+ . = ..()
/obj/structure/ore_box/attack_hand(mob/user)
. = ..()
@@ -62,7 +65,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ore_box", name, 335, 415, master_ui, state)
+ ui = new(user, src, ui_key, "OreBox", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/structure/ore_box/ui_data()
diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm
index 6c1d76eaf3..160abd57e1 100644
--- a/code/modules/mob/dead/observer/notificationprefs.dm
+++ b/code/modules/mob/dead/observer/notificationprefs.dm
@@ -24,7 +24,7 @@
/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "notificationpanel", "Notification Preferences", 270, 360, master_ui, state)
+ ui = new(user, src, ui_key, "NotificationPreferences", "Notification Preferences", 270, 360, master_ui, state)
ui.open()
/datum/notificationpanel/ui_data(mob/user)
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 775f36cfc7..854182bd99 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -302,7 +302,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "slime_swap_body", name, 400, 400, master_ui, state)
+ ui = new(user, src, ui_key, "SlimeBodySwapper", name, 400, 400, master_ui, state)
ui.open()
/datum/action/innate/swap_body/ui_data(mob/user)
diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm
new file mode 100644
index 0000000000..0eaea103f2
--- /dev/null
+++ b/code/modules/mob/living/silicon/ai/robot_control.dm
@@ -0,0 +1,74 @@
+/datum/robot_control
+ var/mob/living/silicon/ai/owner
+
+/datum/robot_control/New(mob/living/silicon/ai/new_owner)
+ if(!istype(new_owner))
+ qdel(src)
+ owner = new_owner
+
+/datum/robot_control/proc/is_interactable(mob/user)
+ if(user != owner || owner.incapacitated())
+ return FALSE
+ if(owner.control_disabled)
+ to_chat(user, "Wireless control is disabled. ")
+ return FALSE
+ return TRUE
+
+/datum/robot_control/ui_status(mob/user)
+ if(is_interactable(user))
+ return ..()
+ return UI_CLOSE
+
+/datum/robot_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "RemoteRobotControl", "Remote Robot Control", 500, 500, master_ui, state)
+ ui.open()
+
+/datum/robot_control/ui_data(mob/user)
+ if(!owner || user != owner)
+ return
+ var/list/data = list()
+ var/turf/ai_current_turf = get_turf(owner)
+ var/ai_zlevel = ai_current_turf.z
+
+ data["robots"] = list()
+ for(var/mob/living/simple_animal/bot/B in GLOB.bots_list)
+ if(B.z != ai_zlevel || B.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
+ continue
+ var/list/robot_data = list(
+ name = B.name,
+ model = B.model,
+ mode = B.get_mode(),
+ hacked = B.hacked,
+ location = get_area_name(B, TRUE),
+ ref = REF(B)
+ )
+ data["robots"] += list(robot_data)
+
+ return data
+
+/datum/robot_control/ui_act(action, params)
+ if(..())
+ return
+ if(!is_interactable(usr))
+ return
+
+ switch(action)
+ if("callbot") //Command a bot to move to a selected location.
+ if(owner.call_bot_cooldown > world.time)
+ to_chat(usr, "Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route. ")
+ return
+ owner.Bot = locate(params["ref"]) in GLOB.bots_list
+ if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
+ return
+ owner.waypoint_mode = TRUE
+ to_chat(usr, "Set your waypoint by clicking on a valid location free of obstructions. ")
+ . = TRUE
+ if("interface") //Remotely connect to a bot!
+ owner.Bot = locate(params["ref"]) in GLOB.bots_list
+ if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
+ return
+ owner.Bot.attack_ai(usr)
+ . = TRUE
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 917fdcf113..6721a504c4 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -174,7 +174,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "mulebot", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "Mule", name, ui_x, ui_y, master_ui, state)
ui.open()
/mob/living/simple_animal/bot/mulebot/ui_data(mob/user)
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index 11f5145478..6266daad2b 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -37,9 +37,9 @@
if (!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
assets.send(user)
-
- ui = new(user, src, ui_key, "ntos_main", "NtOS Main menu", 400, 500, master_ui, state)
- ui.set_style("ntos")
+ assets = get_asset_datum(/datum/asset/simple/arcade)
+ assets.send(user)
+ ui = new(user, src, ui_key, "NtosMain", "NtOS Main menu", 400, 500, master_ui, state)
ui.open()
ui.set_autoupdate(state = 1)
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 1aa292f247..8b9f98ca73 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -7,9 +7,9 @@
requires_ntnet = 0
usage_flags = PROGRAM_CONSOLE
transfer_access = ACCESS_HEADS
- available_on_ntnet = 1
- tgui_id = "ntos_ai_restorer"
- ui_x = 600
+ available_on_ntnet = TRUE
+ tgui_id = "NtosAiRestorer"
+ ui_x = 370
ui_y = 400
var/restoring = FALSE
@@ -118,4 +118,4 @@
/datum/computer_file/program/aidiag/kill_program(forced)
restoring = FALSE
- return ..(forced)
\ No newline at end of file
+ return ..(forced)
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
index ca075b51e4..ebfd3c0ad8 100644
--- a/code/modules/modular_computers/file_system/programs/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -7,7 +7,7 @@
requires_ntnet = 1
network_destination = "alarm monitoring network"
size = 5
- tgui_id = "ntos_station_alert"
+ tgui_id = "NtosStationAlertConsole"
ui_x = 315
ui_y = 500
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
index 35470cdee9..326f885ee7 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm
@@ -8,9 +8,8 @@
available_on_ntnet = 0
unsendable = 1
undeletable = 1
- tgui_id = "synd_contract"
- ui_style = "syndicate"
- ui_x = 600
+ tgui_id = "SyndContractor"
+ ui_x = 500
ui_y = 600
var/error = ""
var/page = CONTRACT_UPLINK_PAGE_CONTRACTS
@@ -165,4 +164,4 @@
screen_to_be = "assign"
program_icon_state = screen_to_be
update_computer_icon()
- return data
\ No newline at end of file
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
index 337e98acaa..a2bc2df49b 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
@@ -4,11 +4,10 @@
program_icon_state = "hostile"
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
size = 20
- requires_ntnet = 1
- available_on_ntnet = 0
- available_on_syndinet = 1
- tgui_id = "ntos_net_dos"
- ui_style = "syndicate"
+ requires_ntnet = TRUE
+ available_on_ntnet = FALSE
+ available_on_syndinet = TRUE
+ tgui_id = "NtosNetDos"
ui_x = 400
ui_y = 250
@@ -97,4 +96,4 @@
data["relays"] += list(list("id" = R.uid))
data["focus"] = target ? target.uid : null
- return data
\ No newline at end of file
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
index 103b70e496..5b8f6a0ae3 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -4,11 +4,10 @@
program_icon_state = "hostile"
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
size = 13
- requires_ntnet = 0
- available_on_ntnet = 0
- available_on_syndinet = 1
- tgui_id = "ntos_revelation"
- ui_style = "syndicate"
+ requires_ntnet = FALSE
+ available_on_ntnet = FALSE
+ available_on_syndinet = TRUE
+ tgui_id = "NtosRevelation"
ui_x = 400
ui_y = 250
@@ -68,4 +67,4 @@
data["armed"] = armed
- return data
\ No newline at end of file
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
new file mode 100644
index 0000000000..efd82a9d88
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -0,0 +1,176 @@
+/datum/computer_file/program/arcade
+ filename = "arcade"
+ filedesc = "Nanotrasen Micro Arcade"
+ program_icon_state = "arcade"
+ extended_desc = "This port of the classic game 'Outbomb Cuban Pete', redesigned to run on tablets, with thrilling graphics and chilling storytelling."
+ requires_ntnet = FALSE
+ network_destination = "arcade network"
+ size = 6
+ tgui_id = "NtosArcade"
+ ui_x = 450
+ ui_y = 350
+
+ ///Returns TRUE if the game is being played.
+ var/game_active = TRUE
+ ///This disables buttom actions from having any impact if TRUE. Resets to FALSE when the player is allowed to make an action again.
+ var/pause_state = FALSE
+ var/boss_hp = 45
+ var/boss_mp = 15
+ var/player_hp = 30
+ var/player_mp = 10
+ var/ticket_count = 0
+ ///Shows what text is shown on the app, usually showing the log of combat actions taken by the player.
+ var/heads_up = "Nanotrasen says, winners make us money."
+ var/boss_name = "Cuban Pete's Minion"
+ ///Determines which boss image to use on the UI.
+ var/boss_id = 1
+
+/datum/computer_file/program/arcade/proc/game_check(mob/user)
+ sleep(5)
+ user?.mind?.adjust_experience(/datum/skill/gaming, 1)
+ if(boss_hp <= 0)
+ heads_up = "You have crushed [boss_name]! Rejoice!"
+ playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ game_active = FALSE
+ program_icon_state = "arcade_off"
+ if(istype(computer))
+ computer.update_icon()
+ ticket_count += 1
+ user?.mind?.adjust_experience(/datum/skill/gaming, 50)
+ sleep(10)
+ else if(player_hp <= 0 || player_mp <= 0)
+ heads_up = "You have been defeated... how will the station survive?"
+ playsound(computer.loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ game_active = FALSE
+ program_icon_state = "arcade_off"
+ if(istype(computer))
+ computer.update_icon()
+ user?.mind?.adjust_experience(/datum/skill/gaming, 10)
+ sleep(10)
+
+/datum/computer_file/program/arcade/proc/enemy_check(mob/user)
+ var/boss_attackamt = 0 //Spam protection from boss attacks as well.
+ var/boss_mpamt = 0
+ var/bossheal = 0
+ if(pause_state == TRUE)
+ boss_attackamt = rand(3,6)
+ boss_mpamt = rand (2,4)
+ bossheal = rand (4,6)
+ if(game_active == FALSE)
+ return
+ if (boss_mp <= 5)
+ heads_up = "[boss_mpamt] magic power has been stolen from you!"
+ playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ player_mp -= boss_mpamt
+ boss_mp += boss_mpamt
+ else if(boss_mp > 5 && boss_hp <12)
+ heads_up = "[boss_name] heals for [bossheal] health!"
+ playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ boss_hp += bossheal
+ boss_mp -= boss_mpamt
+ else
+ heads_up = "[boss_name] attacks you for [boss_attackamt] damage!"
+ playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ player_hp -= boss_attackamt
+
+ pause_state = FALSE
+ game_check()
+
+/datum/computer_file/program/arcade/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state)
+ . = ..()
+ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/arcade)
+ assets.send(user)
+
+/datum/computer_file/program/arcade/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ data["Hitpoints"] = boss_hp
+ data["PlayerHitpoints"] = player_hp
+ data["PlayerMP"] = player_mp
+ data["TicketCount"] = ticket_count
+ data["GameActive"] = game_active
+ data["PauseState"] = pause_state
+ data["Status"] = heads_up
+ data["BossID"] = "boss[boss_id].gif"
+ return data
+
+/datum/computer_file/program/arcade/ui_act(action, list/params)
+ if(..())
+ return TRUE
+ var/obj/item/computer_hardware/printer/printer
+ if(computer)
+ printer = computer.all_components[MC_PRINT]
+
+ var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming)
+ var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)
+ switch(action)
+ if("Attack")
+ var/attackamt = 0 //Spam prevention.
+ if(pause_state == FALSE)
+ attackamt = rand(2,6) + rand(0, gamerSkill)
+ pause_state = TRUE
+ heads_up = "You attack for [attackamt] damage."
+ playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ boss_hp -= attackamt
+ sleep(10)
+ game_check()
+ enemy_check()
+ return TRUE
+ if("Heal")
+ var/healamt = 0 //More Spam Prevention.
+ var/healcost = 0
+ if(pause_state == FALSE)
+ healamt = rand(6,8) + rand(0, gamerSkill)
+ var/maxPointCost = 3
+ if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN)
+ maxPointCost = 2
+ healcost = rand(1, maxPointCost)
+ pause_state = TRUE
+ heads_up = "You heal for [healamt] damage."
+ playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ player_hp += healamt
+ player_mp -= healcost
+ sleep(10)
+ game_check()
+ enemy_check()
+ return TRUE
+ if("Recharge_Power")
+ var/rechargeamt = 0 //As above.
+ if(pause_state == FALSE)
+ rechargeamt = rand(4,7) + rand(0, gamerSkill)
+ pause_state = TRUE
+ heads_up = "You regain [rechargeamt] magic power."
+ playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3, falloff = 10)
+ player_mp += rechargeamt
+ sleep(10)
+ game_check()
+ enemy_check()
+ return TRUE
+ if("Dispense_Tickets")
+ if(!printer)
+ to_chat(usr, "Hardware error: A printer is required to redeem tickets. ")
+ return
+ if(printer.stored_paper <= 0)
+ to_chat(usr, "Hardware error: Printer is out of paper. ")
+ return
+ else
+ computer.visible_message("\The [computer] prints out paper. ")
+ if(ticket_count >= 1)
+ new /obj/item/stack/arcadeticket((get_turf(computer)), 1)
+ to_chat(usr, "[src] dispenses a ticket! ")
+ ticket_count -= 1
+ printer.stored_paper -= 1
+ else
+ to_chat(usr, "You don't have any stored tickets! ")
+ return TRUE
+ if("Start_Game")
+ game_active = TRUE
+ boss_hp = 45
+ player_hp = 30
+ player_mp = 10
+ heads_up = "You stand before [boss_name]! Prepare for battle!"
+ program_icon_state = "arcade"
+ boss_id = rand(1,6)
+ pause_state = FALSE
+ if(istype(computer))
+ computer.update_icon()
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
new file mode 100644
index 0000000000..fe3833facd
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -0,0 +1,33 @@
+/datum/computer_file/program/atmosscan
+ filename = "atmosscan"
+ filedesc = "Atmospheric Scanner"
+ program_icon_state = "air"
+ extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
+ network_destination = "atmos scan"
+ size = 4
+ tgui_id = "NtosAtmos"
+ ui_x = 300
+ ui_y = 350
+
+/datum/computer_file/program/atmosscan/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/list/airlist = list()
+ var/turf/T = get_turf(ui_host())
+ if(T)
+ var/datum/gas_mixture/environment = T.return_air()
+ var/list/env_gases = environment.gases
+ var/pressure = environment.return_pressure()
+ var/total_moles = environment.total_moles()
+ data["AirPressure"] = round(pressure,0.1)
+ data["AirTemp"] = round(environment.temperature-T0C)
+ if (total_moles)
+ for(var/id in env_gases)
+ var/gas_level = env_gases[id][MOLES]/total_moles
+ if(gas_level > 0)
+ airlist += list(list("name" = "[env_gases[id][GAS_META][META_GAS_NAME]]", "percentage" = round(gas_level*100, 0.01)))
+ data["AirData"] = airlist
+ return data
+
+/datum/computer_file/program/atmosscan/ui_act(action, list/params)
+ if(..())
+ return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
new file mode 100644
index 0000000000..e3e43dcf84
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -0,0 +1,69 @@
+/datum/computer_file/program/borg_monitor
+ filename = "cyborgmonitor"
+ filedesc = "Cyborg Remote Monitoring"
+ ui_header = "borg_mon.gif"
+ program_icon_state = "generic"
+ extended_desc = "This program allows for remote monitoring of station cyborgs."
+ requires_ntnet = TRUE
+ transfer_access = ACCESS_ROBOTICS
+ network_destination = "cyborg remote monitoring"
+ size = 5
+ tgui_id = "NtosCyborgRemoteMonitor"
+ ui_x = 600
+ ui_y = 800
+
+/datum/computer_file/program/borg_monitor/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ data["card"] = FALSE
+ if(computer.GetID())
+ data["card"] = TRUE
+
+ data["cyborgs"] = list()
+ for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
+ if((get_turf(computer)).z != (get_turf(R)).z)
+ continue
+ if(R.scrambledcodes)
+ continue
+
+ var/list/upgrade
+ for(var/obj/item/borg/upgrade/I in R.upgrades)
+ upgrade += "\[[I.name]\] "
+
+ var/shell = FALSE
+ if(R.shell && !R.ckey)
+ shell = TRUE
+
+ var/list/cyborg_data = list(
+ name = R.name,
+ locked_down = R.lockcharge,
+ status = R.stat,
+ shell_discon = shell,
+ charge = R.cell ? round(R.cell.percent()) : null,
+ module = R.module ? "[R.module.name] Module" : "No Module Detected",
+ upgrades = upgrade,
+ ref = REF(R)
+ )
+ data["cyborgs"] += list(cyborg_data)
+ return data
+
+/datum/computer_file/program/borg_monitor/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("messagebot")
+ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
+ if(!istype(R))
+ return
+ var/obj/item/card/id/ID = computer.GetID()
+ if(!ID)
+ return
+ var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message")
+ if(!message)
+ return
+ to_chat(R, "Message from [ID.registered_name] -- \"[message]\" ")
+ SEND_SOUND(R, 'sound/machines/twobeep_high.ogg')
+ if(R.connected_ai)
+ to_chat(R.connected_ai, "Message from [ID.registered_name] to [R] -- \"[message]\" ")
+ SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index bf58b120f2..6309a1a89f 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -6,91 +6,84 @@
transfer_access = ACCESS_HEADS
requires_ntnet = 0
size = 8
- tgui_id = "ntos_card"
- ui_x = 600
- ui_y = 700
+ tgui_id = "NtosCard"
+ ui_x = 450
+ ui_y = 520
- var/mod_mode = 1
- var/is_centcom = 0
- var/show_assignments = 0
- var/minor = 0
- var/authenticated = 0
- var/list/reg_ids = list()
- var/list/region_access = null
- var/list/head_subordinates = null
- var/target_dept = 0 //Which department this computer has access to. 0=all departments
- var/change_position_cooldown = 30
- //Jobs you cannot open new positions for
- var/list/blacklisted = list(
- "AI",
- "Assistant",
- "Cyborg",
- "Captain",
- "Head of Personnel",
- "Head of Security",
- "Chief Engineer",
- "Research Director",
- "Chief Medical Officer")
+ var/is_centcom = FALSE
+ var/minor = FALSE
+ var/authenticated = FALSE
+ var/list/region_access
+ var/list/head_subordinates
+ ///Which departments this computer has access to. Defined as access regions. null = all departments
+ var/target_dept
- //The scaling factor of max total positions in relation to the total amount of people on board the station in %
- var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players
+ //For some reason everything was exploding if this was static.
+ var/list/sub_managers
- //This is used to keep track of opened positions for jobs to allow instant closing
- //Assoc array: "JobName" = (int)
- var/list/opened_positions = list();
+/datum/computer_file/program/card_mod/New(obj/item/modular_computer/comp)
+ . = ..()
+ sub_managers = list(
+ "[ACCESS_HOP]" = list(
+ "department" = list(CARDCON_DEPARTMENT_SERVICE, CARDCON_DEPARTMENT_COMMAND),
+ "region" = 1,
+ "head" = "Head of Personnel"
+ ),
+ "[ACCESS_HOS]" = list(
+ "department" = CARDCON_DEPARTMENT_SECURITY,
+ "region" = 2,
+ "head" = "Head of Security"
+ ),
+ "[ACCESS_CMO]" = list(
+ "department" = CARDCON_DEPARTMENT_MEDICAL,
+ "region" = 3,
+ "head" = "Chief Medical Officer"
+ ),
+ "[ACCESS_RD]" = list(
+ "department" = CARDCON_DEPARTMENT_SCIENCE,
+ "region" = 4,
+ "head" = "Research Director"
+ ),
+ "[ACCESS_CE]" = list(
+ "department" = CARDCON_DEPARTMENT_ENGINEERING,
+ "region" = 5,
+ "head" = "Chief Engineer"
+ )
+ )
-/datum/computer_file/program/card_mod/New()
- ..()
- addtimer(CALLBACK(src, .proc/SetConfigCooldown), 0)
+/datum/computer_file/program/card_mod/proc/authenticate(mob/user, obj/item/card/id/id_card)
+ if(!id_card)
+ return
-/datum/computer_file/program/card_mod/proc/SetConfigCooldown()
- change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
+ region_access = list()
+ if(!target_dept && (ACCESS_CHANGE_IDS in id_card.access))
+ minor = FALSE
+ authenticated = TRUE
+ update_static_data(user)
+ return TRUE
-/datum/computer_file/program/card_mod/event_idremoved(background, slot)
- if(!slot || slot == 2)// slot being false means both are removed
- minor = 0
- authenticated = 0
- head_subordinates = null
- region_access = null
+ var/list/head_types = list()
+ for(var/access_text in sub_managers)
+ var/list/info = sub_managers[access_text]
+ var/access = text2num(access_text)
+ if((access in id_card.access) && ((info["region"] in target_dept) || !length(target_dept)))
+ region_access += info["region"]
+ //I don't even know what I'm doing anymore
+ head_types += info["head"]
+ head_subordinates = list()
+ if(length(head_types))
+ for(var/j in SSjob.occupations)
+ var/datum/job/job = j
+ for(var/head in head_types)//god why
+ if(head in job.department_head)
+ head_subordinates += job.title
-/datum/computer_file/program/card_mod/proc/job_blacklisted(jobtitle)
- return (jobtitle in blacklisted)
-
-
-//Logic check for if you can open the job
-/datum/computer_file/program/card_mod/proc/can_open_job(datum/job/job)
- if(job)
- if(!job_blacklisted(job.title))
- if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100)))
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
- return 1
- return -2
- return 0
- return 0
-
-//Logic check for if you can close the job
-/datum/computer_file/program/card_mod/proc/can_close_job(datum/job/job)
- if(job)
- if(!job_blacklisted(job.title))
- if(job.total_positions > job.current_positions)
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
- return 1
- return -2
- return 0
- return 0
-
-/datum/computer_file/program/card_mod/proc/format_jobs(list/jobs)
- var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
- var/obj/item/card/id/id_card = card_slot.stored_card
- var/list/formatted = list()
- for(var/job in jobs)
- formatted.Add(list(list(
- "display_name" = replacetext(job, " ", " "),
- "target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned",
- "job" = job)))
+ if(length(region_access))
+ minor = TRUE
+ authenticated = TRUE
+ update_static_data(user)
+ return TRUE
return formatted
diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm
new file mode 100644
index 0000000000..39543adfa5
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/cargoship.dm
@@ -0,0 +1,74 @@
+/datum/computer_file/program/shipping
+ filename = "shipping"
+ filedesc = "Nanotrasen Shipping Scanner"
+ program_icon_state = "shipping"
+ extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping."
+ network_destination = "ship scanner"
+ size = 6
+ tgui_id = "NtosShipping"
+ ui_x = 450
+ ui_y = 350
+ ///Account used for creating barcodes.
+ var/datum/bank_account/payments_acc
+ ///The amount which the tagger will recieve for the sale.
+ var/percent_cut = 20
+
+/datum/computer_file/program/shipping/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
+ var/obj/item/computer_hardware/printer/printer = computer.all_components[MC_PRINT]
+ var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null
+ data["has_id_slot"] = !!card_slot
+ data["has_printer"] = !!printer
+ data["paperamt"] = printer ? "[printer.stored_paper] / [printer.max_paper]" : null
+ data["card_owner"] = card_slot && card_slot.stored_card ? id_card.registered_name : "No Card Inserted."
+ data["current_user"] = payments_acc ? payments_acc.account_holder : null
+ data["barcode_split"] = percent_cut
+ return data
+
+/datum/computer_file/program/shipping/ui_act(action, list/params)
+ if(..())
+ return TRUE
+ if(!computer)
+ return
+
+ // Get components
+ var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
+ var/obj/item/computer_hardware/printer/printer = computer.all_components[MC_PRINT]
+ var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null
+ if(!card_slot || !printer) //We need both to successfully use this app.
+ return
+
+ switch(action)
+ if("ejectid")
+ if(id_card)
+ card_slot.try_eject(TRUE, usr)
+ if("selectid")
+ if(!id_card)
+ return
+ if(!id_card.registered_account)
+ playsound(get_turf(ui_host()), 'sound/machines/buzz-sigh.ogg', 50, TRUE, -1)
+ return
+ payments_acc = id_card.registered_account
+ playsound(get_turf(ui_host()), 'sound/machines/ping.ogg', 50, TRUE, -1)
+ if("resetid")
+ payments_acc = null
+ if("setsplit")
+ var/potential_cut = input("How much would you like to payout to the registered card?","Percentage Profit") as num|null
+ percent_cut = potential_cut ? clamp(round(potential_cut, 1), 1, 50) : 20
+ if("print")
+ if(!printer)
+ to_chat(usr, "Hardware error: A printer is required to print barcodes. ")
+ return
+ if(printer.stored_paper <= 0)
+ to_chat(usr, "Hardware error: Printer is out of paper. ")
+ return
+ if(!payments_acc)
+ to_chat(usr, "Software error: Please set a current user first. ")
+ return
+ var/obj/item/barcode/barcode = new /obj/item/barcode(get_turf(ui_host()))
+ barcode.payments_acc = payments_acc
+ barcode.percent_cut = percent_cut
+ printer.stored_paper--
+ to_chat(usr, "The computer prints out a barcode. ")
diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm
index 2d60323d10..76da58ea11 100644
--- a/code/modules/modular_computers/file_system/programs/configurator.dm
+++ b/code/modules/modular_computers/file_system/programs/configurator.dm
@@ -14,7 +14,7 @@
ui_y = 630
available_on_ntnet = 0
requires_ntnet = 0
- tgui_id = "ntos_configuration"
+ tgui_id = "NtosConfiguration"
var/obj/item/modular_computer/movable = null
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
new file mode 100644
index 0000000000..662c867a39
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -0,0 +1,50 @@
+/datum/computer_file/program/crew_manifest
+ filename = "crewmani"
+ filedesc = "Crew Manifest"
+ program_icon_state = "id"
+ extended_desc = "Program for viewing and printing the current crew manifest"
+ transfer_access = ACCESS_HEADS
+ requires_ntnet = FALSE
+ size = 4
+ tgui_id = "NtosCrewManifest"
+ ui_x = 400
+ ui_y = 480
+
+/datum/computer_file/program/crew_manifest/ui_static_data(mob/user)
+ var/list/data = list()
+ data["manifest"] = GLOB.data_core.get_manifest()
+ return data
+
+/datum/computer_file/program/crew_manifest/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ var/obj/item/computer_hardware/printer/printer
+ if(computer)
+ printer = computer.all_components[MC_PRINT]
+
+ if(computer)
+ data["have_printer"] = !!printer
+ else
+ data["have_printer"] = FALSE
+ return data
+
+/datum/computer_file/program/crew_manifest/ui_act(action, params, datum/tgui/ui)
+ if(..())
+ return
+
+ var/obj/item/computer_hardware/printer/printer
+ if(computer)
+ printer = computer.all_components[MC_PRINT]
+
+ switch(action)
+ if("PRG_print")
+ if(computer && printer) //This option should never be called if there is no printer
+ var/contents = {"Crew Manifest
+
+ [GLOB.data_core ? GLOB.data_core.get_manifest_html(0) : ""]
+ "}
+ if(!printer.print_text(contents,text("crew manifest ([])", station_time_timestamp())))
+ to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper. ")
+ return
+ else
+ computer.visible_message("\The [computer] prints out a paper. ")
diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm
index cba82eac18..fd3b81be25 100644
--- a/code/modules/modular_computers/file_system/programs/file_browser.dm
+++ b/code/modules/modular_computers/file_system/programs/file_browser.dm
@@ -4,10 +4,10 @@
extended_desc = "This program allows management of files."
program_icon_state = "generic"
size = 8
- requires_ntnet = 0
- available_on_ntnet = 0
- undeletable = 1
- tgui_id = "ntos_file_manager"
+ requires_ntnet = FALSE
+ available_on_ntnet = FALSE
+ undeletable = TRUE
+ tgui_id = "NtosFileManager"
var/open_file
var/error
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
new file mode 100644
index 0000000000..7b847c123e
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -0,0 +1,141 @@
+/datum/computer_file/program/job_management
+ filename = "job_manage"
+ filedesc = "Job Manager"
+ program_icon_state = "id"
+ extended_desc = "Program for viewing and changing job slot avalibility."
+ transfer_access = ACCESS_HEADS
+ requires_ntnet = 0
+ size = 4
+ tgui_id = "NtosJobManager"
+ ui_x = 400
+ ui_y = 620
+
+ var/change_position_cooldown = 30
+ //Jobs you cannot open new positions for
+ var/list/blacklisted = list(
+ "AI",
+ "Assistant",
+ "Cyborg",
+ "Captain",
+ "Head of Personnel",
+ "Head of Security",
+ "Chief Engineer",
+ "Research Director",
+ "Chief Medical Officer")
+
+ //The scaling factor of max total positions in relation to the total amount of people on board the station in %
+ var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players
+
+ //This is used to keep track of opened positions for jobs to allow instant closing
+ //Assoc array: "JobName" = (int)
+ var/list/opened_positions = list()
+
+/datum/computer_file/program/job_management/New()
+ ..()
+ change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
+
+/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
+ if(!(job?.title in blacklisted))
+ if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
+ return TRUE
+ return FALSE
+
+/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
+ if(!(job?.title in blacklisted))
+ if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100))
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
+ return TRUE
+ return FALSE
+
+/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui)
+ if(..())
+ return
+
+ var/authed = FALSE
+ var/mob/user = usr
+ var/obj/item/card/id/user_id = user.get_idcard()
+ if(user_id)
+ if(ACCESS_CHANGE_IDS in user_id.access)
+ authed = TRUE
+
+ if(!authed)
+ return
+
+ switch(action)
+ if("PRG_open_job")
+ var/edit_job_target = params["target"]
+ var/datum/job/j = SSjob.GetJob(edit_job_target)
+ if(!j || !can_open_job(j))
+ return
+ if(opened_positions[edit_job_target] >= 0)
+ GLOB.time_last_changed_position = world.time / 10
+ j.total_positions++
+ opened_positions[edit_job_target]++
+ playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ return TRUE
+ if("PRG_close_job")
+ var/edit_job_target = params["target"]
+ var/datum/job/j = SSjob.GetJob(edit_job_target)
+ if(!j || !can_close_job(j))
+ return
+ //Allow instant closing without cooldown if a position has been opened before
+ if(opened_positions[edit_job_target] <= 0)
+ GLOB.time_last_changed_position = world.time / 10
+ j.total_positions--
+ opened_positions[edit_job_target]--
+ playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ return TRUE
+ if("PRG_priority")
+ if(length(SSjob.prioritized_jobs) >= 5)
+ return
+ var/priority_target = params["target"]
+ var/datum/job/j = SSjob.GetJob(priority_target)
+ if(!j)
+ return
+ if(j.total_positions <= j.current_positions)
+ return
+ if(j in SSjob.prioritized_jobs)
+ SSjob.prioritized_jobs -= j
+ else
+ SSjob.prioritized_jobs += j
+ playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ return TRUE
+
+
+/datum/computer_file/program/job_management/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ var/authed = FALSE
+ var/obj/item/card/id/user_id = user.get_idcard(FALSE)
+ if(user_id)
+ if(ACCESS_CHANGE_IDS in user_id.access)
+ authed = TRUE
+
+ data["authed"] = authed
+
+ var/list/pos = list()
+ for(var/j in SSjob.occupations)
+ var/datum/job/job = j
+ if(job.title in blacklisted)
+ continue
+
+ pos += list(list(
+ "title" = job.title,
+ "current" = job.current_positions,
+ "total" = job.total_positions,
+ "status_open" = authed ? can_open_job(job) : FALSE,
+ "status_close" = authed ? can_close_job(job) : FALSE,
+ ))
+ data["slots"] = pos
+ var/delta = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1)
+ data["cooldown"] = delta < 0 ? 0 : delta
+ var/list/priority = list()
+ for(var/j in SSjob.prioritized_jobs)
+ var/datum/job/job = j
+ priority += job.title
+ data["prioritized"] = priority
+ return data
+
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index 92e1453dc6..80c014bd03 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -10,7 +10,9 @@
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
available_on_ntnet = 0
ui_header = "downloader_finished.gif"
- tgui_id = "ntos_net_downloader"
+ tgui_id = "NtosNetDownloader"
+ ui_x = 480
+ ui_y = 735
var/datum/computer_file/program/downloaded_file = null
var/hacked_download = 0
@@ -168,4 +170,4 @@
/datum/computer_file/program/ntnetdownload/kill_program(forced)
abort_file_download()
- return ..(forced)
\ No newline at end of file
+ return ..(forced)
diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
index 2312db7b11..656c7614ab 100644
--- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
@@ -6,8 +6,8 @@
size = 12
requires_ntnet = 1
required_access = ACCESS_NETWORK //NETWORK CONTROL IS A MORE SECURE PROGRAM.
- available_on_ntnet = 1
- tgui_id = "ntos_net_monitor"
+ available_on_ntnet = TRUE
+ tgui_id = "NtosNetMonitor"
/datum/computer_file/program/ntnetmonitor/ui_act(action, params)
if(..())
@@ -78,4 +78,4 @@
data["ntnetlogs"] += list(list("entry" = i))
data["ntnetmaxlogs"] = SSnetworks.station_network.setting_maxlogcount
- return data
\ No newline at end of file
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
index d8b3f96f42..4ae5bd326b 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -9,7 +9,7 @@
network_destination = "NTNRC server"
ui_header = "ntnrc_idle.gif"
available_on_ntnet = 1
- tgui_id = "ntos_net_chat"
+ tgui_id = "NtosNetChat"
ui_x = 900
ui_y = 675
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index f7c734667b..d2d57b1447 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -11,8 +11,7 @@
requires_ntnet = 0
network_destination = "power monitoring system"
size = 9
- tgui_id = "ntos_power_monitor"
- ui_style = "ntos"
+ tgui_id = "NtosPowerMonitor"
ui_x = 550
ui_y = 700
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
new file mode 100644
index 0000000000..910f923327
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -0,0 +1,86 @@
+
+/datum/computer_file/program/robocontrol
+ filename = "robocontrol"
+ filedesc = "Bot Remote Controller"
+ program_icon_state = "robot"
+ extended_desc = "A remote controller used for giving basic commands to non-sentient robots."
+ transfer_access = ACCESS_ROBOTICS
+ requires_ntnet = TRUE
+ network_destination = "robotics control network"
+ size = 12
+ tgui_id = "NtosRoboControl"
+ ui_x = 550
+ ui_y = 550
+ ///Number of simple robots on-station.
+ var/botcount = 0
+ ///Used to find the location of the user for the purposes of summoning robots.
+ var/mob/current_user
+ ///Access granted by the used to summon robots.
+ var/list/current_access = list()
+
+/datum/computer_file/program/robocontrol/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/turf/current_turf = get_turf(ui_host())
+ var/zlevel = current_turf.z
+ var/list/botlist = list()
+ var/list/mulelist = list()
+
+ var/obj/item/computer_hardware/card_slot/card_slot = computer ? computer.all_components[MC_CARD] : null
+ data["have_id_slot"] = !!card_slot
+ if(computer)
+ var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null
+ data["has_id"] = !!id_card
+ data["id_owner"] = id_card ? id_card.registered_name : "No Card Inserted."
+ data["access_on_card"] = id_card ? id_card.access : null
+
+ botcount = 0
+ current_user = user
+
+ for(var/B in GLOB.bots_list)
+ var/mob/living/simple_animal/bot/Bot = B
+ if(!Bot.on || Bot.z != zlevel || Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
+ continue //Also, the PDA must have access to the bot type.
+ var/list/newbot = list("name" = Bot.name, "mode" = Bot.get_mode_ui(), "model" = Bot.model, "locat" = get_area(Bot), "bot_ref" = REF(Bot), "mule_check" = FALSE)
+ if(Bot.bot_type == MULE_BOT)
+ var/mob/living/simple_animal/bot/mulebot/MULE = Bot
+ mulelist += list(list("name" = MULE.name, "dest" = MULE.destination, "power" = MULE.cell ? MULE.cell.percent() : 0, "home" = MULE.home_destination, "autoReturn" = MULE.auto_return, "autoPickup" = MULE.auto_pickup, "reportDelivery" = MULE.report_delivery, "mule_ref" = REF(MULE)))
+ if(MULE.load)
+ data["load"] = MULE.load.name
+ newbot["mule_check"] = TRUE
+ botlist += list(newbot)
+
+ data["bots"] = botlist
+ data["mules"] = mulelist
+ data["botcount"] = botlist.len
+
+ return data
+
+/datum/computer_file/program/robocontrol/ui_act(action, list/params)
+ if(..())
+ return TRUE
+ var/obj/item/computer_hardware/card_slot/card_slot
+ var/obj/item/card/id/id_card
+ if(computer)
+ card_slot = computer.all_components[MC_CARD]
+ if(card_slot)
+ id_card = card_slot.stored_card
+
+ var/list/standard_actions = list("patroloff", "patrolon", "ejectpai")
+ var/list/MULE_actions = list("stop", "go", "home", "destination", "setid", "sethome", "unload", "autoret", "autopick", "report", "ejectpai")
+ var/mob/living/simple_animal/bot/Bot = locate(params["robot"]) in GLOB.bots_list
+ if (action in standard_actions)
+ Bot.bot_control(action, current_user, current_access)
+ if (action in MULE_actions)
+ Bot.bot_control(action, current_user, current_access, TRUE)
+ switch(action)
+ if("summon")
+ Bot.bot_control(action, current_user, id_card ? id_card.access : current_access)
+ if("ejectcard")
+ if(!computer || !card_slot)
+ return
+ if(id_card)
+ GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment)
+ card_slot.try_eject(TRUE, current_user)
+ else
+ playsound(get_turf(ui_host()) , 'sound/machines/buzz-sigh.ogg', 25, FALSE)
+ return
diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
index dbee59bb3e..0c30319c2a 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -8,8 +8,7 @@
transfer_access = ACCESS_CONSTRUCTION
network_destination = "supermatter monitoring system"
size = 5
- tgui_id = "ntos_supermatter_monitor"
- ui_style = "ntos"
+ tgui_id = "NtosSupermatterMonitor"
ui_x = 600
ui_y = 350
var/last_status = SUPERMATTER_INACTIVE
@@ -124,4 +123,4 @@
for(var/obj/machinery/power/supermatter_crystal/S in supermatters)
if(S.uid == newuid)
active = S
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm
index 2bed456c41..ffda79dabc 100644
--- a/code/modules/modular_computers/laptop_vendor.dm
+++ b/code/modules/modular_computers/laptop_vendor.dm
@@ -232,7 +232,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if (!ui)
- ui = new(user, src, ui_key, "computer_fabricator", "Personal Computer Vendor", ui_x, ui_y, state = state)
+ ui = new(user, src, ui_key, "ComputerFabricator", "Personal Computer Vendor", ui_x, ui_y, state = state)
ui.open()
/obj/machinery/lapvend/attackby(obj/item/I, mob/user)
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 1b23b1bb5d..fa023b0e3a 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -859,7 +859,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "apc", name, 450, 460, master_ui, state)
+ ui = new(user, src, ui_key, "Apc", name, 450, 460, master_ui, state)
ui.open()
/obj/machinery/power/apc/ui_data(mob/user)
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 582d72f6c0..d2ffc216ba 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/sprite_number = 0
-/obj/machinery/gravity_generator/safe_throw_at()
+/obj/machinery/gravity_generator/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
return FALSE
/obj/machinery/gravity_generator/ex_act(severity, target)
@@ -56,7 +56,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
qdel(src)
/obj/machinery/gravity_generator/proc/set_broken()
- stat |= BROKEN
+ obj_break()
/obj/machinery/gravity_generator/proc/set_fix()
stat &= ~BROKEN
@@ -116,6 +116,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
sprite_number = 8
use_power = IDLE_POWER_USE
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE
+ ui_x = 400
+ ui_y = 165
var/on = TRUE
var/breaker = TRUE
var/list/parts = list()
@@ -187,14 +189,14 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
/obj/machinery/gravity_generator/main/attackby(obj/item/I, mob/user, params)
switch(broken_state)
if(GRAV_NEEDS_SCREWDRIVER)
- if(istype(I, /obj/item/screwdriver))
+ if(I.tool_behaviour == TOOL_SCREWDRIVER)
to_chat(user, "You secure the screws of the framework. ")
I.play_tool_sound(src)
broken_state++
update_icon()
return
if(GRAV_NEEDS_WELDING)
- if(istype(I, /obj/item/weldingtool))
+ if(I.tool_behaviour == TOOL_WELDER)
if(I.use_tool(src, user, 0, volume=50, amount=1))
to_chat(user, "You mend the damaged framework. ")
broken_state++
@@ -206,14 +208,14 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
if(PS.get_amount() >= 10)
PS.use(10)
to_chat(user, "You add the plating to the framework. ")
- playsound(src.loc, 'sound/machines/click.ogg', 75, 1)
+ playsound(src.loc, 'sound/machines/click.ogg', 75, TRUE)
broken_state++
update_icon()
else
to_chat(user, "You need 10 sheets of plasteel! ")
return
if(GRAV_NEEDS_WRENCH)
- if(istype(I, /obj/item/wrench))
+ if(I.tool_behaviour == TOOL_WRENCH)
to_chat(user, "You secure the plating to the framework. ")
I.play_tool_sound(src)
set_fix()
@@ -224,7 +226,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "gravity_generator", name, 400, 200, master_ui, state)
+ ui = new(user, src, ui_key, "GravityGenerator", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/gravity_generator/main/ui_data(mob/user)
@@ -241,16 +243,18 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
/obj/machinery/gravity_generator/main/ui_act(action, params)
if(..())
return
+
switch(action)
if("gentoggle")
breaker = !breaker
investigate_log("was toggled [breaker ? "ON " : "OFF "] by [key_name(usr)].", INVESTIGATE_GRAVITY)
set_power()
+ . = TRUE
// Power and Icon States
/obj/machinery/gravity_generator/main/power_change()
- ..()
+ . = ..()
investigate_log("has [stat & NOPOWER ? "lost" : "regained"] power.", INVESTIGATE_GRAVITY)
set_power()
@@ -313,7 +317,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
charge_count -= 2
if(charge_count % 4 == 0 && prob(75)) // Let them know it is charging/discharging.
- playsound(src.loc, 'sound/effects/empulse.ogg', 100, 1)
+ playsound(src.loc, 'sound/effects/empulse.ogg', 100, TRUE)
updateDialog()
if(prob(25)) // To help stop "Your clothes feel warm." spam.
diff --git a/code/modules/power/monitor.dm b/code/modules/power/monitor.dm
index f4ee102ccc..974fb1b9e2 100644
--- a/code/modules/power/monitor.dm
+++ b/code/modules/power/monitor.dm
@@ -10,6 +10,9 @@
idle_power_usage = 20
active_power_usage = 100
circuit = /obj/item/circuitboard/computer/powermonitor
+ tgui_id = "PowerMonitor"
+ ui_x = 550
+ ui_y = 700
var/obj/structure/cable/attached_wire
var/obj/machinery/power/apc/local_apc
@@ -19,8 +22,6 @@
var/record_interval = 50
var/next_record = 0
var/is_secret_monitor = FALSE
- tgui_id = "power_monitor"
- ui_style = "ntos"
/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming.
name = "outdated power monitoring console"
@@ -87,7 +88,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, tgui_id, name, 550, 700, master_ui, state)
+ ui = new(user, src, ui_key, "PowerMonitor", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/monitor/ui_data()
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index 3c20f2f69c..e2ec3378c2 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -1,4 +1,3 @@
-
//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power).
/obj/machinery/power/port_gen
name = "portable generator"
@@ -8,10 +7,11 @@
density = TRUE
anchored = FALSE
use_power = NO_POWER_USE
+ ui_x = 450
+ ui_y = 340
- var/active = 0
+ var/active = FALSE
var/power_gen = 5000
- var/recent_fault = 0
var/power_output = 1
var/consumption = 0
var/base_icon = "portgen0"
@@ -27,8 +27,16 @@
QDEL_NULL(soundloop)
return ..()
+/obj/machinery/power/port_gen/should_have_node()
+ return anchored
+
+/obj/machinery/power/port_gen/connect_to_network()
+ if(!anchored)
+ return FALSE
+ . = ..()
+
/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check.
- return 1
+ return TRUE
/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use.
return
@@ -39,26 +47,38 @@
/obj/machinery/power/port_gen/proc/handleInactive()
return
+/obj/machinery/power/port_gen/proc/TogglePower()
+ if(active)
+ active = FALSE
+ update_icon()
+ soundloop.stop()
+ else if(HasFuel())
+ active = TRUE
+ START_PROCESSING(SSmachines, src)
+ update_icon()
+ soundloop.start()
+
/obj/machinery/power/port_gen/update_icon_state()
icon_state = "[base_icon]_[active]"
/obj/machinery/power/port_gen/process()
- if(active && HasFuel() && !crit_fail && anchored && powernet)
- add_avail(power_gen * power_output)
+ if(active)
+ if(!HasFuel() || !anchored)
+ TogglePower()
+ return
+ if(powernet)
+ add_avail(power_gen * power_output)
UseFuel()
- src.updateDialog()
- soundloop.start()
-
else
- active = 0
handleInactive()
- update_icon()
- soundloop.stop()
/obj/machinery/power/port_gen/examine(mob/user)
. = ..()
. += "It is[!active?"n't":""] running."
+/////////////////
+// P.A.C.M.A.N //
+/////////////////
/obj/machinery/power/port_gen/pacman
name = "\improper P.A.C.M.A.N.-type portable generator"
circuit = /obj/item/circuitboard/machine/pacman
@@ -78,8 +98,8 @@
/obj/machinery/power/port_gen/pacman/Initialize()
. = ..()
- var/obj/sheet = new sheet_path(null)
- sheet_name = sheet.name
+ var/obj/S = sheet_path
+ sheet_name = initial(S.name)
/obj/machinery/power/port_gen/pacman/Destroy()
DropFuel()
@@ -100,16 +120,16 @@
/obj/machinery/power/port_gen/pacman/examine(mob/user)
. = ..()
- . += "The generator has [sheets] units of [sheet_name] fuel left, producing [power_gen] per cycle. "
- if(crit_fail)
- . += "The generator seems to have broken down. "
+ . += "The generator has [sheets] units of [sheet_name] fuel left, producing [DisplayPower(power_gen)] per cycle. "
+ if(anchored)
+ . += "It is anchored to the ground. "
if(in_range(user, src) || isobserver(user))
. += "The status display reads: Fuel efficiency increased by [(consumption*100)-100]% . "
/obj/machinery/power/port_gen/pacman/HasFuel()
if(sheets >= 1 / (time_per_sheet / power_output) - sheet_left)
- return 1
- return 0
+ return TRUE
+ return FALSE
/obj/machinery/power/port_gen/pacman/DropFuel()
if(sheets)
@@ -145,13 +165,11 @@
if (current_heat > 300)
overheat()
qdel(src)
- return
/obj/machinery/power/port_gen/pacman/handleInactive()
-
- if (current_heat > 0)
- current_heat = max(current_heat - 2, 0)
- src.updateDialog()
+ current_heat = max(current_heat - 2, 0)
+ if(current_heat == 0)
+ STOP_PROCESSING(SSmachines, src)
/obj/machinery/power/port_gen/pacman/proc/overheat()
explosion(src.loc, 2, 5, 2, -1)
@@ -166,24 +184,21 @@
to_chat(user, "You add [amount] sheets to the [src.name]. ")
sheets += amount
addstack.use(amount)
- updateUsrDialog()
return
else if(!active)
-
- if(istype(O, /obj/item/wrench))
-
+ if(O.tool_behaviour == TOOL_WRENCH)
if(!anchored && !isinspace())
+ anchored = TRUE
connect_to_network()
to_chat(user, "You secure the generator to the floor. ")
- anchored = TRUE
else if(anchored)
+ anchored = FALSE
disconnect_from_network()
to_chat(user, "You unsecure the generator from the floor. ")
- anchored = FALSE
- playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
+ playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
return
- else if(istype(O, /obj/item/screwdriver))
+ else if(O.tool_behaviour == TOOL_SCREWDRIVER)
panel_open = !panel_open
O.play_tool_sound(src)
if(panel_open)
@@ -196,12 +211,10 @@
return ..()
/obj/machinery/power/port_gen/pacman/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
emp_act(EMP_HEAVY)
- return TRUE
/obj/machinery/power/port_gen/pacman/attack_ai(mob/user)
interact(user)
@@ -209,60 +222,52 @@
/obj/machinery/power/port_gen/pacman/attack_paw(mob/user)
interact(user)
-/obj/machinery/power/port_gen/pacman/ui_interact(mob/user)
- . = ..()
- if (get_dist(src, user) > 1 )
- if(!isAI(user))
- user.unset_machine()
- user << browse(null, "window=port_gen")
- return
+/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "PortableGenerator", name, ui_x, ui_y, master_ui, state)
+ ui.open()
- var/dat = text("[name] ")
- if (active)
- dat += text("Generator: On ")
- else
- dat += text("Generator: Off ")
- dat += text("[capitalize(sheet_name)]: [sheets] - Eject ")
- var/stack_percent = round(sheet_left * 100, 1)
- dat += text("Current stack: [stack_percent]% ")
- dat += text("Power output: - [power_gen * power_output] + ")
- dat += text("Power current: [(powernet == null ? "Unconnected" : "[DisplayPower(avail())]")] ")
- dat += text("Heat: [current_heat] ")
- dat += "Close "
- user << browse(dat, "window=port_gen")
- onclose(user, "port_gen")
+/obj/machinery/power/port_gen/pacman/ui_data()
+ var/data = list()
-/obj/machinery/power/port_gen/pacman/Topic(href, href_list)
+ data["active"] = active
+ data["sheet_name"] = capitalize(sheet_name)
+ data["sheets"] = sheets
+ data["stack_percent"] = round(sheet_left * 100, 0.1)
+
+ data["anchored"] = anchored
+ data["connected"] = (powernet == null ? 0 : 1)
+ data["ready_to_boot"] = anchored && HasFuel()
+ data["power_generated"] = DisplayPower(power_gen)
+ data["power_output"] = DisplayPower(power_gen * power_output)
+ data["power_available"] = (powernet == null ? 0 : DisplayPower(avail()))
+ data["current_heat"] = current_heat
+ . = data
+
+/obj/machinery/power/port_gen/pacman/ui_act(action, params)
if(..())
return
+ switch(action)
+ if("toggle_power")
+ TogglePower()
+ . = TRUE
- src.add_fingerprint(usr)
- if(href_list["action"])
- if(href_list["action"] == "enable")
- if(!active && HasFuel() && !crit_fail)
- active = 1
- src.updateUsrDialog()
- update_icon()
- if(href_list["action"] == "disable")
- if (active)
- active = 0
- src.updateUsrDialog()
- update_icon()
- if(href_list["action"] == "eject")
+ if("eject")
if(!active)
DropFuel()
- src.updateUsrDialog()
- if(href_list["action"] == "lower_power")
+ . = TRUE
+
+ if("lower_power")
if (power_output > 1)
power_output--
- src.updateUsrDialog()
- if (href_list["action"] == "higher_power")
+ . = TRUE
+
+ if("higher_power")
if (power_output < 4 || (obj_flags & EMAGGED))
power_output++
- src.updateUsrDialog()
- if (href_list["action"] == "close")
- usr << browse(null, "window=port_gen")
- usr.unset_machine()
+ . = TRUE
/obj/machinery/power/port_gen/pacman/super
name = "\improper S.U.P.E.R.P.A.C.M.A.N.-type portable generator"
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index af83f7ebca..12132b5bae 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -9,15 +9,17 @@
idle_power_usage = 500
active_power_usage = 10000
dir = NORTH
- var/strength_upper_limit = 2
- var/interface_control = 1
- var/list/obj/structure/particle_accelerator/connected_parts
- var/assembled = 0
- var/construction_state = PA_CONSTRUCTION_UNSECURED
- var/active = 0
- var/strength = 0
- var/powered = 0
mouse_opacity = MOUSE_OPACITY_OPAQUE
+ ui_x = 350
+ ui_y = 185
+ var/strength_upper_limit = 2
+ var/interface_control = TRUE
+ var/list/obj/structure/particle_accelerator/connected_parts
+ var/assembled = FALSE
+ var/construction_state = PA_CONSTRUCTION_UNSECURED
+ var/active = FALSE
+ var/strength = 0
+ var/powered = FALSE
/obj/machinery/particle_accelerator/control_box/Initialize()
. = ..()
@@ -34,30 +36,27 @@
QDEL_NULL(wires)
return ..()
-/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user)
+/obj/machinery/particle_accelerator/control_box/multitool_act(mob/living/user, obj/item/I)
. = ..()
- if(.)
- return
- if(construction_state == PA_CONSTRUCTION_COMPLETE)
- interact(user)
- else if(construction_state == PA_CONSTRUCTION_PANEL_OPEN)
+ if(construction_state == PA_CONSTRUCTION_PANEL_OPEN)
wires.interact(user)
+ return TRUE
/obj/machinery/particle_accelerator/control_box/proc/update_state()
if(construction_state < PA_CONSTRUCTION_COMPLETE)
use_power = NO_POWER_USE
- assembled = 0
- active = 0
+ assembled = FALSE
+ active = FALSE
for(var/CP in connected_parts)
var/obj/structure/particle_accelerator/part = CP
part.strength = null
- part.powered = 0
+ part.powered = FALSE
part.update_icon()
connected_parts.Cut()
return
if(!part_scan())
use_power = IDLE_POWER_USE
- active = 0
+ active = FALSE
connected_parts.Cut()
/obj/machinery/particle_accelerator/control_box/update_icon_state()
@@ -78,36 +77,6 @@
else
icon_state = "control_boxc"
-/obj/machinery/particle_accelerator/control_box/Topic(href, href_list)
- if(..())
- return
-
- if(!interface_control)
- to_chat(usr, "ERROR: Request timed out. Check wire contacts. ")
- return
-
- if(href_list["close"])
- usr << browse(null, "window=pacontrol")
- usr.unset_machine()
- return
- if(href_list["togglep"])
- if(!wires.is_cut(WIRE_POWER))
- toggle_power()
-
- else if(href_list["scan"])
- part_scan()
-
- else if(href_list["strengthup"])
- if(!wires.is_cut(WIRE_STRENGTH))
- add_strength()
-
- else if(href_list["strengthdown"])
- if(!wires.is_cut(WIRE_STRENGTH))
- remove_strength()
-
- updateDialog()
- update_icon()
-
/obj/machinery/particle_accelerator/control_box/proc/strength_change()
for(var/CP in connected_parts)
var/obj/structure/particle_accelerator/part = CP
@@ -123,7 +92,6 @@
log_game("PA Control Computer increased to [strength] by [key_name(usr)] in [AREACOORD(src)]")
investigate_log("increased to [strength] by [key_name(usr)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
-
/obj/machinery/particle_accelerator/control_box/proc/remove_strength(s)
if(assembled && (strength > 0))
strength--
@@ -133,11 +101,10 @@
log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in [AREACOORD(src)]")
investigate_log("decreased to [strength] by [key_name(usr)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
-
/obj/machinery/particle_accelerator/control_box/power_change()
- ..()
+ . = ..()
if(stat & NOPOWER)
- active = 0
+ active = FALSE
use_power = NO_POWER_USE
else if(!stat && construction_state == PA_CONSTRUCTION_COMPLETE)
use_power = IDLE_POWER_USE
@@ -160,49 +127,48 @@
var/odir = turn(dir,180)
var/turf/T = loc
- assembled = 0
+ assembled = FALSE
critical_machine = FALSE
var/obj/structure/particle_accelerator/fuel_chamber/F = locate() in orange(1,src)
if(!F)
- return 0
+ return FALSE
setDir(F.dir)
connected_parts.Cut()
T = get_step(T,rdir)
if(!check_part(T, /obj/structure/particle_accelerator/fuel_chamber))
- return 0
+ return FALSE
T = get_step(T,odir)
if(!check_part(T, /obj/structure/particle_accelerator/end_cap))
- return 0
+ return FALSE
T = get_step(T,dir)
T = get_step(T,dir)
if(!check_part(T, /obj/structure/particle_accelerator/power_box))
- return 0
+ return FALSE
T = get_step(T,dir)
if(!check_part(T, /obj/structure/particle_accelerator/particle_emitter/center))
- return 0
+ return FALSE
T = get_step(T,ldir)
if(!check_part(T, /obj/structure/particle_accelerator/particle_emitter/left))
- return 0
+ return FALSE
T = get_step(T,rdir)
T = get_step(T,rdir)
if(!check_part(T, /obj/structure/particle_accelerator/particle_emitter/right))
- return 0
+ return FALSE
- assembled = 1
+ assembled = TRUE
critical_machine = TRUE //Only counts if the PA is actually assembled.
- return 1
+ return TRUE
/obj/machinery/particle_accelerator/control_box/proc/check_part(turf/T, type)
var/obj/structure/particle_accelerator/PA = locate(/obj/structure/particle_accelerator) in T
if(istype(PA, type) && (PA.construction_state == PA_CONSTRUCTION_COMPLETE))
if(PA.connect_master(src))
connected_parts.Add(PA)
- return 1
- return 0
-
+ return TRUE
+ return FALSE
/obj/machinery/particle_accelerator/control_box/proc/toggle_power()
active = !active
@@ -214,47 +180,16 @@
for(var/CP in connected_parts)
var/obj/structure/particle_accelerator/part = CP
part.strength = strength
- part.powered = 1
+ part.powered = TRUE
part.update_icon()
else
use_power = IDLE_POWER_USE
for(var/CP in connected_parts)
var/obj/structure/particle_accelerator/part = CP
part.strength = null
- part.powered = 0
+ part.powered = FALSE
part.update_icon()
- return 1
-
-
-/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user)
- . = ..()
- if((get_dist(src, user) > 1) || (stat & (BROKEN|NOPOWER)))
- if(!issilicon(user))
- user.unset_machine()
- user << browse(null, "window=pacontrol")
- return
-
- var/dat = ""
- dat += "Close "
- dat += "Status "
- if(!assembled)
- dat += "Unable to detect all parts! "
- dat += "Run Scan "
- else
- dat += "All parts in place. "
- dat += "Power:"
- if(active)
- dat += "On "
- else
- dat += "Off "
- dat += "Toggle Power "
- dat += "Particle Strength: [strength] "
- dat += "-- |++ "
-
- var/datum/browser/popup = new(user, "pacontrol", name, 420, 300)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
- popup.open()
+ return TRUE
/obj/machinery/particle_accelerator/control_box/examine(mob/user)
. = ..()
@@ -266,49 +201,48 @@
if(PA_CONSTRUCTION_PANEL_OPEN)
. += "The panel is open."
-
/obj/machinery/particle_accelerator/control_box/attackby(obj/item/W, mob/user, params)
var/did_something = FALSE
switch(construction_state)
if(PA_CONSTRUCTION_UNSECURED)
- if(istype(W, /obj/item/wrench) && !isinspace())
+ if(W.tool_behaviour == TOOL_WRENCH && !isinspace())
W.play_tool_sound(src, 75)
anchored = TRUE
- user.visible_message("[user.name] secures the [name] to the floor.", \
- "You secure the external bolts.")
+ user.visible_message("[user.name] secures the [name] to the floor. ", \
+ "You secure the external bolts. ")
construction_state = PA_CONSTRUCTION_UNWIRED
did_something = TRUE
if(PA_CONSTRUCTION_UNWIRED)
- if(istype(W, /obj/item/wrench))
+ if(W.tool_behaviour == TOOL_WRENCH)
W.play_tool_sound(src, 75)
anchored = FALSE
- user.visible_message("[user.name] detaches the [name] from the floor.", \
- "You remove the external bolts.")
+ user.visible_message("[user.name] detaches the [name] from the floor. ", \
+ "You remove the external bolts. ")
construction_state = PA_CONSTRUCTION_UNSECURED
did_something = TRUE
else if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/CC = W
if(CC.use(1))
- user.visible_message("[user.name] adds wires to the [name].", \
- "You add some wires.")
+ user.visible_message("[user.name] adds wires to the [name]. ", \
+ "You add some wires. ")
construction_state = PA_CONSTRUCTION_PANEL_OPEN
did_something = TRUE
if(PA_CONSTRUCTION_PANEL_OPEN)
- if(istype(W, /obj/item/wirecutters))//TODO:Shock user if its on?
- user.visible_message("[user.name] removes some wires from the [name].", \
- "You remove some wires.")
+ if(W.tool_behaviour == TOOL_WIRECUTTER)//TODO:Shock user if its on?
+ user.visible_message("[user.name] removes some wires from the [name]. ", \
+ "You remove some wires. ")
construction_state = PA_CONSTRUCTION_UNWIRED
did_something = TRUE
- else if(istype(W, /obj/item/screwdriver))
- user.visible_message("[user.name] closes the [name]'s access panel.", \
- "You close the access panel.")
+ else if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ user.visible_message("[user.name] closes the [name]'s access panel. ", \
+ "You close the access panel. ")
construction_state = PA_CONSTRUCTION_COMPLETE
did_something = TRUE
if(PA_CONSTRUCTION_COMPLETE)
- if(istype(W, /obj/item/screwdriver))
- user.visible_message("[user.name] opens the [name]'s access panel.", \
- "You open the access panel.")
+ if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ user.visible_message("[user.name] opens the [name]'s access panel. ", \
+ "You open the access panel. ")
construction_state = PA_CONSTRUCTION_PANEL_OPEN
did_something = TRUE
@@ -324,6 +258,65 @@
if(prob(50))
qdel(src)
+/obj/machinery/particle_accelerator/control_box/interact(mob/user)
+ if(construction_state == PA_CONSTRUCTION_PANEL_OPEN)
+ wires.interact(user)
+ else
+ ..()
+
+/obj/machinery/particle_accelerator/control_box/proc/is_interactive(mob/user)
+ if(!interface_control)
+ to_chat(user, "ERROR: Request timed out. Check wire contacts. ")
+ return FALSE
+ if(construction_state != PA_CONSTRUCTION_COMPLETE)
+ return FALSE
+ return TRUE
+
+/obj/machinery/particle_accelerator/control_box/ui_status(mob/user)
+ if(is_interactive(user))
+ return ..()
+ return UI_CLOSE
+
+/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "ParticleAccelerator", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/particle_accelerator/control_box/ui_data(mob/user)
+ var/list/data = list()
+ data["assembled"] = assembled
+ data["power"] = active
+ data["strength"] = strength
+ return data
+
+/obj/machinery/particle_accelerator/control_box/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("power")
+ if(wires.is_cut(WIRE_POWER))
+ return
+ toggle_power()
+ . = TRUE
+ if("scan")
+ part_scan()
+ . = TRUE
+ if("add_strength")
+ if(wires.is_cut(WIRE_STRENGTH))
+ return
+ add_strength()
+ . = TRUE
+ if("remove_strength")
+ if(wires.is_cut(WIRE_STRENGTH))
+ return
+ remove_strength()
+ . = TRUE
+
+ update_icon()
+
#undef PA_CONSTRUCTION_UNSECURED
#undef PA_CONSTRUCTION_UNWIRED
#undef PA_CONSTRUCTION_PANEL_OPEN
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index 47de07cd71..5aa60a245b 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -313,7 +313,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "smes", name, 340, 440, master_ui, state)
+ ui = new(user, src, ui_key, "Smes", name, 340, 440, master_ui, state)
ui.open()
/obj/machinery/power/smes/ui_data()
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 73ea3ccd59..d228a1e1f0 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -352,7 +352,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "solar_control", name, 380, 230, master_ui, state)
+ ui = new(user, src, ui_key, "SolarControl", name, 380, 230, master_ui, state)
ui.open()
/obj/machinery/power/solar_control/ui_data()
diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm
index b40e5c6c41..e6cacd81f1 100644
--- a/code/modules/power/turbine.dm
+++ b/code/modules/power/turbine.dm
@@ -41,6 +41,11 @@
var/comp_id = 0
var/efficiency
+/obj/machinery/power/compressor/Destroy()
+ if(turbine && turbine.compressor == src)
+ turbine.compressor = null
+ turbine = null
+ return ..()
/obj/machinery/power/turbine
name = "gas turbine generator"
@@ -51,12 +56,20 @@
resistance_flags = FIRE_PROOF
CanAtmosPass = ATMOS_PASS_DENSITY
circuit = /obj/item/circuitboard/machine/power_turbine
+ ui_x = 310
+ ui_y = 150
var/opened = 0
var/obj/machinery/power/compressor/compressor
var/turf/outturf
var/lastgen
var/productivity = 1
+/obj/machinery/power/turbine/Destroy()
+ if(compressor && compressor.turbine == src)
+ compressor.turbine = null
+ compressor = null
+ return ..()
+
// the inlet stage of the gas turbine electricity generator
/obj/machinery/power/compressor/Initialize()
@@ -66,12 +79,10 @@
inturf = get_step(src, dir)
locate_machinery()
if(!turbine)
- stat |= BROKEN
-
+ obj_break()
#define COMPFRICTION 5e5
-
/obj/machinery/power/compressor/locate_machinery()
if(turbine)
return
@@ -103,7 +114,7 @@
stat &= ~BROKEN
else
to_chat(user, "Turbine not connected. ")
- stat |= BROKEN
+ obj_break()
return
default_deconstruction_crowbar(I)
@@ -129,9 +140,9 @@
// RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy
+ rpm = min(rpm, (COMPFRICTION*efficiency)/2)
rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency))
-
if(starter && !(stat & NOPOWER))
use_power(2800)
if(rpm<1000)
@@ -140,8 +151,6 @@
if(rpm<1000)
rpmtarget = 0
-
-
if(rpm>50000)
add_overlay(mutable_appearance(icon, "comp-o4", FLY_LAYER))
else if(rpm>10000)
@@ -164,7 +173,7 @@
outturf = get_step(src, dir)
locate_machinery()
if(!compressor)
- stat |= BROKEN
+ obj_break()
connect_to_network()
/obj/machinery/power/turbine/RefreshParts()
@@ -222,8 +231,6 @@
if(lastgen > 100)
add_overlay(mutable_appearance(icon, "turb-o", FLY_LAYER))
- updateDialog()
-
/obj/machinery/power/turbine/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I))
return
@@ -237,53 +244,42 @@
stat &= ~BROKEN
else
to_chat(user, "Compressor not connected. ")
- stat |= BROKEN
+ obj_break()
return
default_deconstruction_crowbar(I)
-/obj/machinery/power/turbine/ui_interact(mob/user)
+/obj/machinery/power/turbine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state)
+ ui.open()
- if(!Adjacent(user) || (stat & (NOPOWER|BROKEN)) && !issilicon(user))
- user.unset_machine(src)
- user << browse(null, "window=turbine")
- return
+/obj/machinery/power/turbine/ui_data(mob/user)
+ var/list/data = list()
+ data["compressor"] = compressor ? TRUE : FALSE
+ data["compressor_broke"] = (!compressor || (compressor.stat & BROKEN)) ? TRUE : FALSE
+ data["turbine"] = compressor?.turbine ? TRUE : FALSE
+ data["turbine_broke"] = (!compressor || !compressor.turbine || (compressor.turbine.stat & BROKEN)) ? TRUE : FALSE
+ data["online"] = compressor?.starter
+ data["power"] = DisplayPower(compressor?.turbine?.lastgen)
+ data["rpm"] = compressor?.rpm
+ data["temp"] = compressor?.gas_contained.temperature
+ return data
- var/t = "Gas Turbine Generator "
-
- t += "Generated power : [DisplayPower(lastgen)] "
-
- t += "Turbine: [round(compressor.rpm)] RPM "
-
- t += "Starter: [ compressor.starter ? "Off On " : "Off On "]"
-
- t += " Close "
-
- t += " "
- var/datum/browser/popup = new(user, "turbine", name)
- popup.set_content(t)
- popup.open()
-
- return
-
-/obj/machinery/power/turbine/Topic(href, href_list)
+/obj/machinery/power/turbine/ui_act(action, params)
if(..())
return
- if( href_list["close"] )
- usr << browse(null, "window=turbine")
- usr.unset_machine(src)
- return
-
- else if( href_list["str"] )
- if(compressor)
- compressor.starter = !compressor.starter
-
- updateDialog()
-
-
-
-
+ switch(action)
+ if("toggle_power")
+ if(compressor && compressor.turbine)
+ compressor.starter = !compressor.starter
+ . = TRUE
+ if("reconnect")
+ locate_machinery()
+ . = TRUE
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -296,6 +292,8 @@
icon_screen = "turbinecomp"
icon_keyboard = "tech_key"
circuit = /obj/item/circuitboard/computer/turbine_computer
+ ui_x = 310
+ ui_y = 150
var/obj/machinery/power/compressor/compressor
var/id = 0
@@ -319,27 +317,25 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "turbine_computer", name, 300, 200, master_ui, state)
+ ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/turbine_computer/ui_data(mob/user)
var/list/data = list()
-
data["compressor"] = compressor ? TRUE : FALSE
data["compressor_broke"] = (!compressor || (compressor.stat & BROKEN)) ? TRUE : FALSE
data["turbine"] = compressor?.turbine ? TRUE : FALSE
data["turbine_broke"] = (!compressor || !compressor.turbine || (compressor.turbine.stat & BROKEN)) ? TRUE : FALSE
data["online"] = compressor?.starter
-
data["power"] = DisplayPower(compressor?.turbine?.lastgen)
data["rpm"] = compressor?.rpm
data["temp"] = compressor?.gas_contained.temperature
-
return data
/obj/machinery/computer/turbine_computer/ui_act(action, params)
if(..())
return
+
switch(action)
if("toggle_power")
if(compressor && compressor.turbine)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 7f57dd1723..d6d9dd5d7e 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -182,7 +182,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "chem_dispenser", name, 565, 550, master_ui, state)
+ ui = new(user, src, ui_key, "ChemDispenser", name, 565, 550, master_ui, state)
if(user.hallucinating())
ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals
ui.open()
diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm
index 63e9d724a4..d2e7b5f883 100644
--- a/code/modules/reagents/chemistry/machinery/chem_heater.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm
@@ -103,7 +103,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "chem_heater", name, 275, 400, master_ui, state)
+ ui = new(user, src, ui_key, "ChemHeater", name, 275, 400, master_ui, state)
ui.open()
/obj/machinery/chem_heater/ui_data()
@@ -155,4 +155,4 @@
if("eject")
on = FALSE
replace_beaker(usr)
- . = TRUE
\ No newline at end of file
+ . = TRUE
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index 9899d219bf..d5cd4ab8d0 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -160,7 +160,8 @@
if(!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills)
assets.send(user)
- ui = new(user, src, ui_key, "chem_master", name, 500, 550, master_ui, state)
+
+ ui = new(user, src, ui_key, "ChemMaster", name, 500, 550, master_ui, state)
ui.open()
//Insert our custom spritesheet css link into the html
diff --git a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
index ed23e7c75c..66c2616972 100644
--- a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm
@@ -16,7 +16,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "chem_synthesizer", name, 390, 330, master_ui, state)
+ ui = new(user, src, ui_key, "ChemDebugSynthesizer", name, 390, 330, master_ui, state)
ui.open()
/obj/machinery/chem_dispenser/chem_synthesizer/ui_act(action, params)
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 25315f457d..a6f5468c74 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -123,7 +123,7 @@
/obj/machinery/computer/pandemic/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open = FALSE, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "pandemic", name, 520, 550, master_ui, state)
+ ui = new(user, src, ui_key, "Pandemic", name, 520, 550, master_ui, state)
ui.open()
/obj/machinery/computer/pandemic/ui_data(mob/user)
diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
index 0a08395c1b..cac90d1c14 100644
--- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm
+++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
@@ -104,7 +104,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "smoke_machine", name, 350, 350, master_ui, state)
+ ui = new(user, src, ui_key, "SmokeMachine", name, 350, 350, master_ui, state)
ui.open()
/obj/machinery/smoke_machine/ui_data(mob/user)
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 65bfa1d98f..c4d9afd448 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -305,7 +305,7 @@
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "disposal_unit", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "DisposalUnit", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/disposal/bin/ui_data(mob/user)
diff --git a/code/modules/research/nanites/nanite_chamber_computer.dm b/code/modules/research/nanites/nanite_chamber_computer.dm
index 0750d3d268..b9623b751d 100644
--- a/code/modules/research/nanites/nanite_chamber_computer.dm
+++ b/code/modules/research/nanites/nanite_chamber_computer.dm
@@ -3,8 +3,8 @@
desc = "Controls a connected nanite chamber. Can inoculate nanites, load programs, and analyze existing nanite swarms."
var/obj/machinery/nanite_chamber/chamber
var/obj/item/disk/nanite_program/disk
- circuit = /obj/item/circuitboard/computer/nanite_chamber_control
icon_screen = "nanite_chamber_control"
+ circuit = /obj/item/circuitboard/computer/nanite_chamber_control
ui_x = 380
ui_y = 570
@@ -28,7 +28,7 @@
/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nanite_chamber_control", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NaniteChamberControl", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/nanite_chamber_control/ui_data()
@@ -72,14 +72,14 @@
if("set_safety")
var/threshold = text2num(params["value"])
if(!isnull(threshold))
- chamber.set_safety(CLAMP(round(threshold, 1),0,500))
+ chamber.set_safety(clamp(round(threshold, 1),0,500))
playsound(src, "terminal_type", 25, FALSE)
chamber.occupant.investigate_log("'s nanites' safety threshold was set to [threshold] by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
if("set_cloud")
var/cloud_id = text2num(params["value"])
if(!isnull(cloud_id))
- chamber.set_cloud(CLAMP(round(cloud_id, 1),0,100))
+ chamber.set_cloud(clamp(round(cloud_id, 1),0,100))
playsound(src, "terminal_type", 25, FALSE)
chamber.occupant.investigate_log("'s nanites' cloud id was set to [cloud_id] by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm
index 439d0c5750..c688260329 100644
--- a/code/modules/research/nanites/nanite_cloud_controller.dm
+++ b/code/modules/research/nanites/nanite_cloud_controller.dm
@@ -56,7 +56,7 @@
/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nanite_cloud_control", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NaniteCloudControl", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/nanite_cloud_controller/ui_data()
diff --git a/code/modules/research/nanites/nanite_program_hub.dm b/code/modules/research/nanites/nanite_program_hub.dm
index ea4392f236..abec47813d 100644
--- a/code/modules/research/nanites/nanite_program_hub.dm
+++ b/code/modules/research/nanites/nanite_program_hub.dm
@@ -50,7 +50,7 @@
/obj/machinery/nanite_program_hub/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nanite_program_hub", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NaniteProgramHub", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nanite_program_hub/ui_data()
diff --git a/code/modules/research/nanites/nanite_programmer.dm b/code/modules/research/nanites/nanite_programmer.dm
index d81880c6d9..89614f428d 100644
--- a/code/modules/research/nanites/nanite_programmer.dm
+++ b/code/modules/research/nanites/nanite_programmer.dm
@@ -37,7 +37,7 @@
/obj/machinery/nanite_programmer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nanite_programmer", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NaniteProgrammer", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nanite_programmer/ui_data()
diff --git a/code/modules/research/nanites/nanite_remote.dm b/code/modules/research/nanites/nanite_remote.dm
index 71aecc8f2c..3ca49f6d67 100644
--- a/code/modules/research/nanites/nanite_remote.dm
+++ b/code/modules/research/nanites/nanite_remote.dm
@@ -83,7 +83,7 @@
/obj/item/nanite_remote/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "nanite_remote", name, 420, 500, master_ui, state)
+ ui = new(user, src, ui_key, "NaniteRemote", name, 420, 500, master_ui, state)
ui.open()
/obj/item/nanite_remote/ui_data()
diff --git a/code/modules/ruins/spaceruin_code/TheDerelict.dm b/code/modules/ruins/spaceruin_code/TheDerelict.dm
index 58e257d587..2e2765b1a9 100644
--- a/code/modules/ruins/spaceruin_code/TheDerelict.dm
+++ b/code/modules/ruins/spaceruin_code/TheDerelict.dm
@@ -17,3 +17,196 @@
desc = "Looks like someone started shakily writing a will in space common, but were interrupted by something bloody..."
info = "I, Victor Belyakov, do hereby leave my _- "
+
+/// Vault controller for use on the derelict/KS13.
+/obj/machinery/computer/vaultcontroller
+ name = "vault controller"
+ desc = "It seems to be powering and controlling the vault locks."
+ icon_screen = "power"
+ icon_keyboard = "power_key"
+ light_color = LIGHT_COLOR_YELLOW
+ use_power = NO_POWER_USE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+ var/obj/structure/cable/attached_cable
+ var/obj/machinery/door/airlock/vault/derelict/door1
+ var/obj/machinery/door/airlock/vault/derelict/door2
+ var/locked = TRUE
+ var/siphoned_power = 0
+ var/siphon_max = 1e7
+
+ ui_x = 300
+ ui_y = 120
+
+
+/obj/machinery/computer/monitor/examine(mob/user)
+ . = ..()
+ . += "It appears to be powered via a cable connector. "
+
+
+//Checks for cable connection, charges if possible.
+/obj/machinery/computer/vaultcontroller/process()
+ if(siphoned_power >= siphon_max)
+ return
+ update_cable()
+ if(attached_cable)
+ attempt_siphon()
+
+
+///Looks for a cable connection beneath the machine.
+/obj/machinery/computer/vaultcontroller/proc/update_cable()
+ var/turf/T = get_turf(src)
+ attached_cable = locate(/obj/structure/cable) in T
+
+
+///Initializes airlock links.
+/obj/machinery/computer/vaultcontroller/proc/find_airlocks()
+ for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
+ if(A.id_tag == "derelictvault")
+ if(!door1)
+ door1 = A
+ continue
+ if(door1 && !door2)
+ door2 = A
+ break
+
+
+///Tries to charge from powernet excess, no upper limit except max charge.
+/obj/machinery/computer/vaultcontroller/proc/attempt_siphon()
+ var/surpluspower = clamp(attached_cable.surplus(), 0, (siphon_max - siphoned_power))
+ if(surpluspower)
+ attached_cable.add_load(surpluspower)
+ siphoned_power += surpluspower
+
+
+///Handles the doors closing
+/obj/machinery/computer/vaultcontroller/proc/cycle_close(obj/machinery/door/airlock/A)
+ A.safe = FALSE //Make sure its forced closed, always
+ A.unbolt()
+ A.close()
+ A.bolt()
+
+
+///Handles the doors opening
+/obj/machinery/computer/vaultcontroller/proc/cycle_open(obj/machinery/door/airlock/A)
+ A.unbolt()
+ A.open()
+ A.bolt()
+
+
+///Attempts to lock the vault doors
+/obj/machinery/computer/vaultcontroller/proc/lock_vault()
+ if(door1 && !door1.density)
+ cycle_close(door1)
+ if(door2 && !door2.density)
+ cycle_close(door2)
+ if(door1.density && door1.locked && door2.density && door2.locked)
+ locked = TRUE
+
+
+///Attempts to unlock the vault doors
+/obj/machinery/computer/vaultcontroller/proc/unlock_vault()
+ if(door1 && door1.density)
+ cycle_open(door1)
+ if(door2 && door2.density)
+ cycle_open(door2)
+ if(!door1.density && door1.locked && !door2.density && door2.locked)
+ locked = FALSE
+
+
+///Attempts to lock/unlock vault doors, if machine is charged.
+/obj/machinery/computer/vaultcontroller/proc/activate_lock()
+ if(siphoned_power < siphon_max)
+ return
+ if(!door1 || !door2)
+ find_airlocks()
+ if(locked)
+ unlock_vault()
+ else
+ lock_vault()
+
+
+/obj/machinery/computer/vaultcontroller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "VaultController", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+
+/obj/machinery/computer/vaultcontroller/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("togglelock")
+ activate_lock()
+
+
+/obj/machinery/computer/vaultcontroller/ui_data()
+ var/list/data = list()
+ data["stored"] = siphoned_power
+ data["max"] = siphon_max
+ data["doorstatus"] = locked
+ return data
+
+
+///Airlock that can't be deconstructed, broken or hacked.
+/obj/machinery/door/airlock/vault/derelict
+ locked = TRUE
+ move_resist = INFINITY
+ use_power = NO_POWER_USE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ id_tag = "derelictvault"
+
+
+///Overrides screwdriver attack to prevent all deconstruction and hacking.
+/obj/machinery/door/airlock/vault/derelict/attackby(obj/item/C, mob/user, params)
+ if(C.tool_behaviour == TOOL_SCREWDRIVER)
+ return
+ ..()
+
+
+// So drones can teach borgs and AI dronespeak. For best effect, combine with mother drone lawset.
+/obj/item/dronespeak_manual
+ name = "dronespeak manual"
+ desc = "The book's cover reads: \"Understanding Dronespeak - An exercise in futility.\""
+ icon = 'icons/obj/library.dmi'
+ icon_state = "book2"
+
+/obj/item/dronespeak_manual/attack_self(mob/living/user)
+ ..()
+ if(isdrone(user) || issilicon(user))
+ if(user.has_language(/datum/language/drone))
+ to_chat(user, "You start skimming through [src], but you already know dronespeak. ")
+ else
+ to_chat(user, "You start skimming through [src], and suddenly the drone chittering makes sense. ")
+ user.grant_language(/datum/language/drone, TRUE, TRUE, LANGUAGE_MIND)
+ return
+
+ if(user.has_language(/datum/language/drone))
+ to_chat(user, "You start skimming through [src], but you already know dronespeak. ")
+ else
+ to_chat(user, "You start skimming through [src], but you can't make any sense of the contents. ")
+
+/obj/item/dronespeak_manual/attack(mob/living/M, mob/living/user)
+ if(!istype(M) || !istype(user))
+ return
+ if(M == user)
+ attack_self(user)
+ return
+
+ playsound(loc, "punch", 25, TRUE, -1)
+ if(isdrone(M) || issilicon(M))
+ if(M.has_language(/datum/language/drone))
+ M.visible_message("[user] beats [M] over the head with [src]! ", "[user] beats you over the head with [src]! ", "You hear smacking. ")
+ else
+ M.visible_message("[user] teaches [M] by beating [M.p_them()] over the head with [src]! ", "As [user] hits you with [src], chitters resonate in your mind. ", "You hear smacking. ")
+ M.grant_language(/datum/language/drone, TRUE, TRUE, LANGUAGE_MIND)
+ return
+
+/obj/structure/fluff/oldturret
+ name = "broken turret"
+ desc = "An obsolete model of turret, long non-functional."
+ icon = 'icons/obj/turrets.dmi'
+ icon_state = "turretCover"
+ density = TRUE
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index 8a58563407..fed256e98c 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -15,6 +15,9 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
power_channel = ENVIRON
req_access = list(ACCESS_KEYCARD_AUTH)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ ui_x = 375
+ ui_y = 125
+
var/datum/callback/ev
var/event = ""
var/obj/machinery/keycard_auth/event_source
@@ -34,7 +37,7 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "keycard_auth", name, 375, 125, master_ui, state)
+ ui = new(user, src, ui_key, "KeycardAuth", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/keycard_auth/ui_data()
@@ -99,13 +102,13 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
/obj/machinery/keycard_auth/proc/trigger_event(confirmer)
log_game("[key_name(triggerer)] triggered and [key_name(confirmer)] confirmed event [event]")
- message_admins("[key_name(triggerer)] triggered and [key_name(confirmer)] confirmed event [event]")
+ message_admins("[ADMIN_LOOKUPFLW(triggerer)] triggered and [ADMIN_LOOKUPFLW(confirmer)] confirmed event [event]")
var/area/A1 = get_area(triggerer)
- deadchat_broadcast("[triggerer] triggered [event] at [A1.name] . ", triggerer)
+ deadchat_broadcast(" triggered [event] at [A1.name] .", "[triggerer] ", triggerer, message_type=DEADCHAT_ANNOUNCEMENT)
var/area/A2 = get_area(confirmer)
- deadchat_broadcast("[confirmer] confirmed [event] at [A2.name] . ", confirmer)
+ deadchat_broadcast(" confirmed [event] at [A2.name] .", "[confirmer] ", confirmer, message_type=DEADCHAT_ANNOUNCEMENT)
switch(event)
if(KEYCARD_RED_ALERT)
set_security_level(SEC_LEVEL_RED)
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index cd08b7290a..12466c0f03 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -49,8 +49,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "emergency_shuttle_console", name,
- 400, 350, master_ui, state)
+ ui = new(user, src, ui_key, "EmergencyShuttleConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/emergency_shuttle/ui_data()
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 183462a8f7..2ca0e65477 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -223,7 +223,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "bsa", name, 400, 220, master_ui, state)
+ ui = new(user, src, ui_key, "BluespaceArtillery", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/bsa_control/ui_data()
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 2aae17b0f3..3b0ab29308 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -178,7 +178,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
roll_powers(user)
- ui = new(user, src, ui_key, "dna_vault", name, 350, 400, master_ui, state)
+ ui = new(user, src, ui_key, "DnaVault", name, ui_x, ui_y, master_ui, state)
ui.open()
diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm
index cf0d79c742..299fda4a26 100644
--- a/code/modules/station_goals/shield.dm
+++ b/code/modules/station_goals/shield.dm
@@ -45,7 +45,7 @@
/obj/machinery/computer/sat_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "sat_control", name, 400, 305, master_ui, state)
+ ui = new(user, src, ui_key, "SatelliteControl", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/sat_control/ui_act(action, params)
diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm
index 38a5a27e0c..5de54c439c 100644
--- a/code/modules/tgui/external.dm
+++ b/code/modules/tgui/external.dm
@@ -1,133 +1,142 @@
- /**
- * tgui external
- *
- * Contains all external tgui declarations.
- **/
+/**
+ * tgui external
+ *
+ * Contains all external tgui declarations.
+ */
- /**
- * public
- *
- * Used to open and update UIs.
- * If this proc is not implemented properly, the UI will not update correctly.
- *
- * required user mob The mob who opened/is using the UI.
- * optional ui_key string The ui_key of the UI.
- * optional ui datum/tgui The UI to be updated, if it exists.
- * optional force_open bool If the UI should be re-opened instead of updated.
- * optional master_ui datum/tgui The parent UI.
- * optional state datum/ui_state The state used to determine status.
- **/
+/**
+ * public
+ *
+ * Used to open and update UIs.
+ * If this proc is not implemented properly, the UI will not update correctly.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional ui_key string The ui_key of the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ * optional master_ui datum/tgui The parent UI.
+ * optional state datum/ui_state The state used to determine status.
+ */
/datum/proc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
return FALSE // Not implemented.
- /**
- * public
- *
- * Data to be sent to the UI.
- * This must be implemented for a UI to work.
- *
- * required user mob The mob interacting with the UI.
- *
- * return list Data to be sent to the UI.
- **/
+/**
+ * public
+ *
+ * Data to be sent to the UI.
+ * This must be implemented for a UI to work.
+ *
+ * required user mob The mob interacting with the UI.
+ *
+ * return list Data to be sent to the UI.
+ */
/datum/proc/ui_data(mob/user)
return list() // Not implemented.
- /**
- * public
- *
- * Static Data to be sent to the UI.
- * Static data differs from normal data in that it's large data that should be sent infrequently
- * This is implemented optionally for heavy uis that would be sending a lot of redundant data
- * frequently.
- * Gets squished into one object on the frontend side, but the static part is cached.
- *
- * required user mob The mob interacting with the UI.
- *
- * return list Statuic Data to be sent to the UI.
- **/
+/**
+ * public
+ *
+ * Static Data to be sent to the UI.
+ * Static data differs from normal data in that it's large data that should be sent infrequently
+ * This is implemented optionally for heavy uis that would be sending a lot of redundant data
+ * frequently.
+ * Gets squished into one object on the frontend side, but the static part is cached.
+ *
+ * required user mob The mob interacting with the UI.
+ *
+ * return list Statuic Data to be sent to the UI.
+ */
/datum/proc/ui_static_data(mob/user)
return list()
/**
- * public
- *
- * Forces an update on static data. Should be done manually whenever something happens to change static data.
- *
- * required user the mob currently interacting with the ui
- * optional ui ui to be updated
- * optional ui_key ui key of ui to be updated
- *
-**/
+ * public
+ *
+ * Forces an update on static data. Should be done manually whenever something happens to change static data.
+ *
+ * required user the mob currently interacting with the ui
+ * optional ui ui to be updated
+ * optional ui_key ui key of ui to be updated
+ */
/datum/proc/update_static_data(mob/user, datum/tgui/ui, ui_key = "main")
ui = SStgui.try_update_ui(user, src, ui_key, ui)
+ // If there was no ui to update, there's no static data to update either.
if(!ui)
- return //If there was no ui to update, there's no static data to update either.
+ return
ui.push_data(null, ui_static_data(), TRUE)
- /**
- * public
- *
- * Called on a UI when the UI receieves a href.
- * Think of this as Topic().
- *
- * required action string The action/button that has been invoked by the user.
- * required params list A list of parameters attached to the button.
- *
- * return bool If the UI should be updated or not.
- **/
+/**
+ * public
+ *
+ * Called on a UI when the UI receieves a href.
+ * Think of this as Topic().
+ *
+ * required action string The action/button that has been invoked by the user.
+ * required params list A list of parameters attached to the button.
+ *
+ * return bool If the UI should be updated or not.
+ */
/datum/proc/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ // If UI is not interactive or usr calling Topic is not the UI user, bail.
if(!ui || ui.status != UI_INTERACTIVE)
- return 1 // If UI is not interactive or usr calling Topic is not the UI user, bail.
+ return 1
- /**
- * public
- *
- * Called on an object when a tgui object is being created, allowing you to customise the html
- * For example: inserting a custom stylesheet that you need in the head
- *
- * For this purpose, some tags are available in the html, to be parsed out with replacetext
- * (customheadhtml) - Additions to the head tag
- *
- * required html the html base text
- *
- **/
+/**
+ * public
+ *
+ * Called on an object when a tgui object is being created, allowing you to
+ * customise the html
+ * For example: inserting a custom stylesheet that you need in the head
+ *
+ * For this purpose, some tags are available in the html, to be parsed out
+ ^ with replacetext
+ * (customheadhtml) - Additions to the head tag
+ *
+ * required html the html base text
+ */
/datum/proc/ui_base_html(html)
return html
- /**
- * private
- *
- * The UI's host object (usually src_object).
- * This allows modules/datums to have the UI attached to them,
- * and be a part of another object.
- **/
+/**
+ * private
+ *
+ * The UI's host object (usually src_object).
+ * This allows modules/datums to have the UI attached to them,
+ * and be a part of another object.
+ */
/datum/proc/ui_host(mob/user)
return src // Default src.
- /**
- * global
- *
- * Used to track UIs for a mob.
- **/
-/mob/var/list/open_uis = list()
- /**
- * public
- *
- * Called on a UI's object when the UI is closed, not to be confused with client/verb/uiclose(), which closes the ui window
- *
- *
- **/
-/datum/proc/ui_close()
+/**
+ * global
+ *
+ * Associative list of JSON-encoded shared states that were set by
+ * tgui clients.
+ */
+/datum/var/list/tgui_shared_states
- /**
- * verb
- *
- * Called by UIs when they are closed.
- * Must be a verb so winset() can call it.
- *
- * required uiref ref The UI that was closed.
- **/
+/**
+ * global
+ *
+ * Used to track UIs for a mob.
+ */
+/mob/var/list/open_uis = list()
+/**
+ * public
+ *
+ * Called on a UI's object when the UI is closed, not to be confused with
+ * client/verb/uiclose(), which closes the ui window
+ */
+/datum/proc/ui_close(mob/user)
+
+/**
+ * verb
+ *
+ * Called by UIs when they are closed.
+ * Must be a verb so winset() can call it.
+ *
+ * required uiref ref The UI that was closed.
+ */
/client/verb/uiclose(ref as text)
// Name the verb, and hide it from the user panel.
set name = "uiclose"
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index b0808cef5e..5ddbd81b25 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -1,19 +1,19 @@
- /**
- * tgui states
- *
- * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks.
- **/
+/**
+ * tgui states
+ *
+ * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks.
+ */
- /**
- * public
- *
- * Checks the UI state for a mob.
- *
- * required user mob The mob who opened/is using the UI.
- * required state datum/ui_state The state to check.
- *
- * return UI_state The state of the UI.
- **/
+/**
+ * public
+ *
+ * Checks the UI state for a mob.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required state datum/ui_state The state to check.
+ *
+ * return UI_state The state of the UI.
+ */
/datum/proc/ui_status(mob/user, datum/ui_state/state)
var/src_object = ui_host(user)
. = UI_CLOSE
@@ -34,27 +34,27 @@
var/result = state.can_use_topic(src_object, user)
. = max(., result)
- /**
- * private
- *
- * Checks if a user can use src_object's UI, and returns the state.
- * Can call a mob proc, which allows overrides for each mob.
- *
- * required src_object datum The object/datum which owns the UI.
- * required user mob The mob who opened/is using the UI.
- *
- * return UI_state The state of the UI.
- **/
+/**
+ * private
+ *
+ * Checks if a user can use src_object's UI, and returns the state.
+ * Can call a mob proc, which allows overrides for each mob.
+ *
+ * required src_object datum The object/datum which owns the UI.
+ * required user mob The mob who opened/is using the UI.
+ *
+ * return UI_state The state of the UI.
+ */
/datum/ui_state/proc/can_use_topic(src_object, mob/user)
return UI_CLOSE // Don't allow interaction by default.
- /**
- * public
- *
- * Standard interaction/sanity checks. Different mob types may have overrides.
- *
- * return UI_state The state of the UI.
- **/
+/**
+ * public
+ *
+ * Standard interaction/sanity checks. Different mob types may have overrides.
+ *
+ * return UI_state The state of the UI.
+ */
/mob/proc/shared_ui_interaction(src_object)
if(!client) // Close UIs if mindless.
return UI_CLOSE
@@ -75,31 +75,31 @@
return ..()
/**
- * public
- *
- * Check the distance for a living mob.
- * Really only used for checks outside the context of a mob.
- * Otherwise, use shared_living_ui_distance().
- *
- * required src_object The object which owns the UI.
- * required user mob The mob who opened/is using the UI.
- *
- * return UI_state The state of the UI.
- **/
+ * public
+ *
+ * Check the distance for a living mob.
+ * Really only used for checks outside the context of a mob.
+ * Otherwise, use shared_living_ui_distance().
+ *
+ * required src_object The object which owns the UI.
+ * required user mob The mob who opened/is using the UI.
+ *
+ * return UI_state The state of the UI.
+ */
/atom/proc/contents_ui_distance(src_object, mob/living/user)
return user.shared_living_ui_distance(src_object) // Just call this mob's check.
- /**
- * public
- *
- * Distance versus interaction check.
- *
- * required src_object atom/movable The object which owns the UI.
- *
- * return UI_state The state of the UI.
- **/
-/mob/living/proc/shared_living_ui_distance(atom/movable/src_object)
- if(!(src_object in view(src))) // If the object is obscured, close it.
+/**
+ * public
+ *
+ * Distance versus interaction check.
+ *
+ * required src_object atom/movable The object which owns the UI.
+ *
+ * return UI_state The state of the UI.
+ */
+/mob/living/proc/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE)
+ if(viewcheck && !(src_object in view(src))) // If the object is obscured, close it.
return UI_CLOSE
var/dist = get_dist(src_object, src)
diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm
index 945a864430..61fc373118 100644
--- a/code/modules/tgui/states/admin.dm
+++ b/code/modules/tgui/states/admin.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: admin_state
- *
- * Checks that the user is an admin, end-of-story.
- **/
+/**
+ * tgui state: admin_state
+ *
+ * Checks that the user is an admin, end-of-story.
+ */
GLOBAL_DATUM_INIT(admin_state, /datum/ui_state/admin_state, new)
diff --git a/code/modules/tgui/states/always.dm b/code/modules/tgui/states/always.dm
index b6c689d5d8..a741e2e3d4 100644
--- a/code/modules/tgui/states/always.dm
+++ b/code/modules/tgui/states/always.dm
@@ -1,9 +1,8 @@
-
- /**
- * tgui state: always_state
- *
- * Always grants the user UI_INTERACTIVE. Period.
- **/
+/**
+ * tgui state: always_state
+ *
+ * Always grants the user UI_INTERACTIVE. Period.
+ */
GLOBAL_DATUM_INIT(always_state, /datum/ui_state/always_state, new)
diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm
index 4323c1391c..4e2793d130 100644
--- a/code/modules/tgui/states/conscious.dm
+++ b/code/modules/tgui/states/conscious.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: conscious_state
- *
- * Only checks if the user is conscious.
- **/
+/**
+ * tgui state: conscious_state
+ *
+ * Only checks if the user is conscious.
+ */
GLOBAL_DATUM_INIT(conscious_state, /datum/ui_state/conscious_state, new)
diff --git a/code/modules/tgui/states/contained.dm b/code/modules/tgui/states/contained.dm
index 7387f7e6cb..f02424d01e 100644
--- a/code/modules/tgui/states/contained.dm
+++ b/code/modules/tgui/states/contained.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: contained_state
- *
- * Checks that the user is inside the src_object.
- **/
+/**
+ * tgui state: contained_state
+ *
+ * Checks that the user is inside the src_object.
+ */
GLOBAL_DATUM_INIT(contained_state, /datum/ui_state/contained_state, new)
diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm
index 06bdb92f3a..43758cbab1 100644
--- a/code/modules/tgui/states/deep_inventory.dm
+++ b/code/modules/tgui/states/deep_inventory.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: deep_inventory_state
- *
- * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
- **/
+/**
+ * tgui state: deep_inventory_state
+ *
+ * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
+ */
GLOBAL_DATUM_INIT(deep_inventory_state, /datum/ui_state/deep_inventory_state, new)
diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm
index c6741f20b8..6bb159640e 100644
--- a/code/modules/tgui/states/default.dm
+++ b/code/modules/tgui/states/default.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: default_state
- *
- * Checks a number of things -- mostly physical distance for humans and view for robots.
- **/
+/**
+ * tgui state: default_state
+ *
+ * Checks a number of things -- mostly physical distance for humans and view for robots.
+ */
GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
diff --git a/code/modules/tgui/states/hands.dm b/code/modules/tgui/states/hands.dm
index 5da0e5d500..aedae477dd 100644
--- a/code/modules/tgui/states/hands.dm
+++ b/code/modules/tgui/states/hands.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: hands_state
- *
- * Checks that the src_object is in the user's hands.
- **/
+/**
+ * tgui state: hands_state
+ *
+ * Checks that the src_object is in the user's hands.
+ */
GLOBAL_DATUM_INIT(hands_state, /datum/ui_state/hands_state, new)
diff --git a/code/modules/tgui/states/human_adjacent.dm b/code/modules/tgui/states/human_adjacent.dm
index 0ab20b36ff..7aefe43e44 100644
--- a/code/modules/tgui/states/human_adjacent.dm
+++ b/code/modules/tgui/states/human_adjacent.dm
@@ -1,10 +1,9 @@
-
- /**
- * tgui state: human_adjacent_state
- *
- * In addition to default checks, only allows interaction for a
- * human adjacent user.
- **/
+/**
+ * tgui state: human_adjacent_state
+ *
+ * In addition to default checks, only allows interaction for a
+ * human adjacent user.
+ */
GLOBAL_DATUM_INIT(human_adjacent_state, /datum/ui_state/human_adjacent_state, new)
diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm
index b8b1ad3b6a..43fe2cb451 100644
--- a/code/modules/tgui/states/inventory.dm
+++ b/code/modules/tgui/states/inventory.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: inventory_state
- *
- * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
- **/
+/**
+ * tgui state: inventory_state
+ *
+ * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
+ */
GLOBAL_DATUM_INIT(inventory_state, /datum/ui_state/inventory_state, new)
diff --git a/code/modules/tgui/states/language_menu.dm b/code/modules/tgui/states/language_menu.dm
index fedc4320e4..5c816c8922 100644
--- a/code/modules/tgui/states/language_menu.dm
+++ b/code/modules/tgui/states/language_menu.dm
@@ -1,6 +1,6 @@
- /**
- * tgui state: language_menu_state
- */
+/**
+ * tgui state: language_menu_state
+ */
GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new)
diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm
index 12fe266bc5..364b59424d 100644
--- a/code/modules/tgui/states/not_incapacitated.dm
+++ b/code/modules/tgui/states/not_incapacitated.dm
@@ -1,16 +1,16 @@
- /**
- * tgui state: not_incapacitated_state
- *
- * Checks that the user isn't incapacitated
- **/
+/**
+ * tgui state: not_incapacitated_state
+ *
+ * Checks that the user isn't incapacitated
+ */
GLOBAL_DATUM_INIT(not_incapacitated_state, /datum/ui_state/not_incapacitated_state, new)
- /**
- * tgui state: not_incapacitated_turf_state
- *
- * Checks that the user isn't incapacitated and that their loc is a turf
- **/
+/**
+ * tgui state: not_incapacitated_turf_state
+ *
+ * Checks that the user isn't incapacitated and that their loc is a turf
+ */
GLOBAL_DATUM_INIT(not_incapacitated_turf_state, /datum/ui_state/not_incapacitated_state, new(no_turfs = TRUE))
diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm
index 77a7fe01b0..642c6ce95f 100644
--- a/code/modules/tgui/states/notcontained.dm
+++ b/code/modules/tgui/states/notcontained.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: notcontained_state
- *
- * Checks that the user is not inside src_object, and then makes the default checks.
- **/
+/**
+ * tgui state: notcontained_state
+ *
+ * Checks that the user is not inside src_object, and then makes the default checks.
+ */
GLOBAL_DATUM_INIT(notcontained_state, /datum/ui_state/notcontained_state, new)
diff --git a/code/modules/tgui/states/observer.dm b/code/modules/tgui/states/observer.dm
index ade0ce66bb..86ad776b13 100644
--- a/code/modules/tgui/states/observer.dm
+++ b/code/modules/tgui/states/observer.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: observer_state
- *
- * Checks that the user is an observer/ghost.
- **/
+/**
+ * tgui state: observer_state
+ *
+ * Checks that the user is an observer/ghost.
+ */
GLOBAL_DATUM_INIT(observer_state, /datum/ui_state/observer_state, new)
diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm
index a4cea7c7c2..88c8a291aa 100644
--- a/code/modules/tgui/states/physical.dm
+++ b/code/modules/tgui/states/physical.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: physical_state
- *
- * Short-circuits the default state to only check physical distance.
- **/
+/**
+ * tgui state: physical_state
+ *
+ * Short-circuits the default state to only check physical distance.
+ */
GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new)
@@ -22,3 +22,29 @@ GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new)
/mob/living/silicon/ai/physical_can_use_topic(src_object)
return UI_UPDATE // AIs are not physical.
+
+
+/**
+ * tgui state: physical_obscured_state
+ *
+ * Short-circuits the default state to only check physical distance, being in view doesn't matter
+ */
+
+GLOBAL_DATUM_INIT(physical_obscured_state, /datum/ui_state/physical_obscured_state, new)
+
+/datum/ui_state/physical_obscured_state/can_use_topic(src_object, mob/user)
+ . = user.shared_ui_interaction(src_object)
+ if(. > UI_CLOSE)
+ return min(., user.physical_obscured_can_use_topic(src_object))
+
+/mob/proc/physical_obscured_can_use_topic(src_object)
+ return UI_CLOSE
+
+/mob/living/physical_obscured_can_use_topic(src_object)
+ return shared_living_ui_distance(src_object, viewcheck = FALSE)
+
+/mob/living/silicon/physical_obscured_can_use_topic(src_object)
+ return max(UI_UPDATE, shared_living_ui_distance(src_object, viewcheck = FALSE)) // Silicons can always see.
+
+/mob/living/silicon/ai/physical_obscured_can_use_topic(src_object)
+ return UI_UPDATE // AIs are not physical.
diff --git a/code/modules/tgui/states/self.dm b/code/modules/tgui/states/self.dm
index 10849772c6..b0c9500fbc 100644
--- a/code/modules/tgui/states/self.dm
+++ b/code/modules/tgui/states/self.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: self_state
- *
- * Only checks that the user and src_object are the same.
- **/
+/**
+ * tgui state: self_state
+ *
+ * Only checks that the user and src_object are the same.
+ */
GLOBAL_DATUM_INIT(self_state, /datum/ui_state/self_state, new)
diff --git a/code/modules/tgui/states/zlevel.dm b/code/modules/tgui/states/zlevel.dm
index 6ccfd0fe7d..5e3ccfb7de 100644
--- a/code/modules/tgui/states/zlevel.dm
+++ b/code/modules/tgui/states/zlevel.dm
@@ -1,8 +1,8 @@
- /**
- * tgui state: z_state
- *
- * Only checks that the Z-level of the user and src_object are the same.
- **/
+/**
+ * tgui state: z_state
+ *
+ * Only checks that the Z-level of the user and src_object are the same.
+ */
GLOBAL_DATUM_INIT(z_state, /datum/ui_state/z_state, new)
diff --git a/code/modules/tgui/subsystem.dm b/code/modules/tgui/subsystem.dm
index 90a00fb607..cbe94e2d7f 100644
--- a/code/modules/tgui/subsystem.dm
+++ b/code/modules/tgui/subsystem.dm
@@ -1,22 +1,22 @@
- /**
- * tgui subsystem
- *
- * Contains all tgui state and subsystem code.
- **/
+/**
+ * tgui subsystem
+ *
+ * Contains all tgui state and subsystem code.
+ */
- /**
- * public
- *
- * Get a open UI given a user, src_object, and ui_key and try to update it with data.
- *
- * required user mob The mob who opened/is using the UI.
- * required src_object datum The object/datum which owns the UI.
- * required ui_key string The ui_key of the UI.
- * optional ui datum/tgui The UI to be updated, if it exists.
- * optional force_open bool If the UI should be re-opened instead of updated.
- *
- * return datum/tgui The found UI.
- **/
+/**
+ * public
+ *
+ * Get a open UI given a user, src_object, and ui_key and try to update it with data.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ *
+ * return datum/tgui The found UI.
+ */
/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE)
if(isnull(ui)) // No UI was passed, so look for one.
ui = get_open_ui(user, src_object, ui_key)
@@ -31,17 +31,17 @@
else
return null // We couldn't find a UI.
- /**
- * private
- *
- * Get a open UI given a user, src_object, and ui_key.
- *
- * required user mob The mob who opened/is using the UI.
- * required src_object datum The object/datum which owns the UI.
- * required ui_key string The ui_key of the UI.
- *
- * return datum/tgui The found UI.
- **/
+/**
+ * private
+ *
+ * Get a open UI given a user, src_object, and ui_key.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ *
+ * return datum/tgui The found UI.
+ */
/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key)
var/src_object_key = "[REF(src_object)]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
@@ -55,15 +55,15 @@
return null // Couldn't find a UI!
- /**
- * private
- *
- * Update all UIs attached to src_object.
- *
- * required src_object datum The object/datum which owns the UIs.
- *
- * return int The number of UIs updated.
- **/
+/**
+ * private
+ *
+ * Update all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs updated.
+ */
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
var/src_object_key = "[REF(src_object)]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
@@ -77,15 +77,15 @@
update_count++ // Count each UI we update.
return update_count
- /**
- * private
- *
- * Close all UIs attached to src_object.
- *
- * required src_object datum The object/datum which owns the UIs.
- *
- * return int The number of UIs closed.
- **/
+/**
+ * private
+ *
+ * Close all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs closed.
+ */
/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
var/src_object_key = "[REF(src_object)]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
@@ -99,13 +99,13 @@
close_count++ // Count each UI we close.
return close_count
- /**
- * private
- *
- * Close *ALL* UIs
- *
- * return int The number of UIs closed.
- **/
+/**
+ * private
+ *
+ * Close *ALL* UIs
+ *
+ * return int The number of UIs closed.
+ */
/datum/controller/subsystem/tgui/proc/close_all_uis()
var/close_count = 0
for(var/src_object_key in open_uis)
@@ -116,17 +116,17 @@
close_count++ // Count each UI we close.
return close_count
- /**
- * private
- *
- * Update all UIs belonging to a user.
- *
- * required user mob The mob who opened/is using the UI.
- * optional src_object datum If provided, only update UIs belonging this src_object.
- * optional ui_key string If provided, only update UIs with this UI key.
- *
- * return int The number of UIs updated.
- **/
+/**
+ * private
+ *
+ * Update all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only update UIs belonging this src_object.
+ * optional ui_key string If provided, only update UIs with this UI key.
+ *
+ * return int The number of UIs updated.
+ */
/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
return 0 // Couldn't find any UIs for this user.
@@ -138,17 +138,17 @@
update_count++ // Count each UI we upadte.
return update_count
- /**
- * private
- *
- * Close all UIs belonging to a user.
- *
- * required user mob The mob who opened/is using the UI.
- * optional src_object datum If provided, only close UIs belonging this src_object.
- * optional ui_key string If provided, only close UIs with this UI key.
- *
- * return int The number of UIs closed.
- **/
+/**
+ * private
+ *
+ * Close all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only close UIs belonging this src_object.
+ * optional ui_key string If provided, only close UIs with this UI key.
+ *
+ * return int The number of UIs closed.
+ */
/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
return 0 // Couldn't find any UIs for this user.
@@ -160,13 +160,13 @@
close_count++ // Count each UI we close.
return close_count
- /**
- * private
- *
- * Add a UI to the list of open UIs.
- *
- * required ui datum/tgui The UI to be added.
- **/
+/**
+ * private
+ *
+ * Add a UI to the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be added.
+ */
/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
var/src_object_key = "[REF(ui.src_object)]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
@@ -180,15 +180,15 @@
uis |= ui
processing_uis |= ui
- /**
- * private
- *
- * Remove a UI from the list of open UIs.
- *
- * required ui datum/tgui The UI to be removed.
- *
- * return bool If the UI was removed or not.
- **/
+/**
+ * private
+ *
+ * Remove a UI from the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be removed.
+ *
+ * return bool If the UI was removed or not.
+ */
/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
var/src_object_key = "[REF(ui.src_object)]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
@@ -210,28 +210,28 @@
return 1 // Let the caller know we did it.
- /**
- * private
- *
- * Handle client logout, by closing all their UIs.
- *
- * required user mob The mob which logged out.
- *
- * return int The number of UIs closed.
- **/
+/**
+ * private
+ *
+ * Handle client logout, by closing all their UIs.
+ *
+ * required user mob The mob which logged out.
+ *
+ * return int The number of UIs closed.
+ */
/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
return close_user_uis(user)
- /**
- * private
- *
- * Handle clients switching mobs, by transferring their UIs.
- *
- * required user source The client's original mob.
- * required user target The client's new mob.
- *
- * return bool If the UIs were transferred.
- **/
+/**
+ * private
+ *
+ * Handle clients switching mobs, by transferring their UIs.
+ *
+ * required user source The client's original mob.
+ * required user target The client's new mob.
+ *
+ * return bool If the UIs were transferred.
+ */
/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
if(!source || isnull(source.open_uis) || !istype(source.open_uis, /list) || open_uis.len == 0)
return 0 // The old mob had no open UIs.
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index 55fe8f0bb5..7971a940d4 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -1,12 +1,12 @@
- /**
- * tgui
- *
- * /tg/station user interface library
- **/
+/**
+ * tgui
+ *
+ * /tg/station user interface library
+ */
- /**
- * tgui datum (represents a UI).
- **/
+/**
+ * tgui datum (represents a UI).
+ */
/datum/tgui
/// The mob who opened/is using the UI.
var/mob/user
@@ -22,8 +22,6 @@
var/width = 0
/// The window height
var/height = 0
- /// The style to be used for this UI.
- var/style = "nanotrasen"
/// The interface (template) to be used for this UI.
var/interface
/// Update the UI every MC tick.
@@ -34,6 +32,8 @@
var/list/initial_data
/// The static data used to initialize the UI.
var/list/initial_static_data
+ /// Holder for the json string, that is sent during the initial update
+ var/_initial_update
/// The status/visibility of the UI.
var/status = UI_INTERACTIVE
/// Topic state used to determine status/interactability.
@@ -42,34 +42,31 @@
var/datum/tgui/master_ui
/// Children of this UI.
var/list/datum/tgui/children = list()
- var/custom_browser_id = FALSE
- var/ui_screen = "home"
- /**
- * public
- *
- * Create a new UI.
- *
- * required user mob The mob who opened/is using the UI.
- * required src_object datum The object or datum which owns the UI.
- * required ui_key string The ui_key of the UI.
- * required interface string The interface used to render the UI.
- * optional title string The title of the UI.
- * optional width int The window width.
- * optional height int The window height.
- * optional master_ui datum/tgui The parent UI.
- * optional state datum/ui_state The state used to determine status.
- *
- * return datum/tgui The requested UI.
- **/
-/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state, browser_id = null)
+/**
+ * public
+ *
+ * Create a new UI.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object or datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ * required interface string The interface used to render the UI.
+ * optional title string The title of the UI.
+ * optional width int The window width.
+ * optional height int The window height.
+ * optional master_ui datum/tgui The parent UI.
+ * optional state datum/ui_state The state used to determine status.
+ *
+ * return datum/tgui The requested UI.
+ */
+/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
src.user = user
src.src_object = src_object
src.ui_key = ui_key
- src.window_id = browser_id ? browser_id : "[REF(src_object)]-[ui_key]" // DO NOT replace with \ref here. src_object could potentially be tagged
- src.custom_browser_id = browser_id ? TRUE : FALSE
-
- set_interface(interface)
+ // DO NOT replace with \ref here. src_object could potentially be tagged
+ src.window_id = "[REF(src_object)]-[ui_key]"
+ src.interface = interface
if(title)
src.title = sanitize(title)
@@ -86,11 +83,11 @@
var/datum/asset/assets = get_asset_datum(/datum/asset/group/tgui)
assets.send(user)
- /**
- * public
- *
- * Open this UI (and initialize it with data).
- **/
+/**
+ * public
+ *
+ * Open this UI (and initialize it with data).
+ */
/datum/tgui/proc/open()
if(!user.client)
return // Bail if there is no client.
@@ -99,18 +96,16 @@
if(status < UI_UPDATE)
return // Bail if we're not supposed to open.
- var/window_size
- if(width && height) // If we have a width and height, use them.
- window_size = "size=[width]x[height];"
- else
- window_size = ""
-
+ // Build window options
+ var/window_options = "can_minimize=0;auto_format=0;"
+ // If we have a width and height, use them.
+ if(width && height)
+ window_options += "size=[width]x[height];"
// Remove titlebar and resize handles for a fancy window
- var/have_title_bar
if(user.client.prefs.tgui_fancy)
- have_title_bar = "titlebar=0;can_resize=0;"
+ window_options += "titlebar=0;can_resize=0;"
else
- have_title_bar = "titlebar=1;can_resize=1;"
+ window_options += "titlebar=1;can_resize=1;"
// Generate page html
var/html
@@ -120,50 +115,54 @@
// Replace template tokens with important UI data
// NOTE: Intentional \ref usage; tgui datums can't/shouldn't
// be tagged, so this is an effective unwrap
- html = replacetextEx(html, "\[ref]", "\ref[src]")
- html = replacetextEx(html, "\[style]", style)
+ html = replacetextEx(html, "\[tgui:ref]", "\ref[src]")
// Open the window.
- user << browse(html, "window=[window_id];can_minimize=0;auto_format=0;[window_size][have_title_bar]")
- if (!custom_browser_id)
- // Instruct the client to signal UI when the window is closed.
- // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
- // be tagged, so this is an effective unwrap
- winset(user, window_id, "on-close=\"uiclose \ref[src]\"")
+ user << browse(html, "window=[window_id];[window_options]")
- if(!initial_data)
+ // Instruct the client to signal UI when the window is closed.
+ // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
+ // be tagged, so this is an effective unwrap
+ winset(user, window_id, "on-close=\"uiclose \ref[src]\"")
+
+ // Pre-fetch initial state while browser is still loading in
+ // another thread
+ if(!initial_data) {
initial_data = src_object.ui_data(user)
- if(!initial_static_data)
+ }
+ if(!initial_static_data) {
initial_static_data = src_object.ui_static_data(user)
+ }
+ _initial_update = url_encode(get_json(initial_data, initial_static_data))
SStgui.on_open(src)
- /**
- * public
- *
- * Reinitialize the UI.
- * (Possibly with a new interface and/or data).
- *
- * optional template string The name of the new interface.
- * optional data list The new initial data.
- **/
+/**
+ * public
+ *
+ * Reinitialize the UI.
+ * (Possibly with a new interface and/or data).
+ *
+ * optional template string The name of the new interface.
+ * optional data list The new initial data.
+ */
/datum/tgui/proc/reinitialize(interface, list/data, list/static_data)
if(interface)
- set_interface(interface) // Set a new interface.
+ src.interface = interface
if(data)
initial_data = data
if(static_data)
initial_static_data = static_data
open()
- /**
- * public
- *
- * Close the UI, and all its children.
- **/
+/**
+ * public
+ *
+ * Close the UI, and all its children.
+ */
/datum/tgui/proc/close()
user << browse(null, "window=[window_id]") // Close the window.
- src_object.ui_close()
+ src_object.ui_close(user)
SStgui.on_close(src)
for(var/datum/tgui/child in children) // Loop through and close all children.
child.close()
@@ -172,67 +171,49 @@
master_ui = null
qdel(src)
- /**
- * public
- *
- * Set the style for this UI.
- *
- * required style string The new UI style.
- **/
-/datum/tgui/proc/set_style(style)
- src.style = lowertext(style)
-
- /**
- * public
- *
- * Set the interface (template) for this UI.
- *
- * required interface string The new UI interface.
- **/
-/datum/tgui/proc/set_interface(interface)
- src.interface = lowertext(interface)
-
- /**
- * public
- *
- * Enable/disable auto-updating of the UI.
- *
- * required state bool Enable/disable auto-updating.
- **/
+/**
+ * public
+ *
+ * Enable/disable auto-updating of the UI.
+ *
+ * required state bool Enable/disable auto-updating.
+ */
/datum/tgui/proc/set_autoupdate(state = TRUE)
autoupdate = state
- /**
- * private
- *
- * Package the data to send to the UI, as JSON.
- * This includes the UI data and config_data.
- *
- * return string The packaged JSON.
- **/
+/**
+ * private
+ *
+ * Package the data to send to the UI, as JSON.
+ * This includes the UI data and config_data.
+ *
+ * return string The packaged JSON.
+ */
/datum/tgui/proc/get_json(list/data, list/static_data)
var/list/json_data = list()
json_data["config"] = list(
"title" = title,
"status" = status,
- "screen" = ui_screen,
- "style" = style,
"interface" = interface,
"fancy" = user.client.prefs.tgui_fancy,
- "locked" = user.client.prefs.tgui_lock && !custom_browser_id,
+ "locked" = user.client.prefs.tgui_lock,
"observer" = isobserver(user),
"window" = window_id,
// NOTE: Intentional \ref usage; tgui datums can't/shouldn't
// be tagged, so this is an effective unwrap
"ref" = "\ref[src]"
)
-
+
if(!isnull(data))
json_data["data"] = data
if(!isnull(static_data))
json_data["static_data"] = static_data
+ // Send shared states
+ if(src_object.tgui_shared_states)
+ json_data["shared"] = src_object.tgui_shared_states
+
// Generate the JSON.
var/json = json_encode(json_data)
// Strip #255/improper.
@@ -240,13 +221,13 @@
json = replacetext(json, "\improper", "")
return json
- /**
- * private
- *
- * Handle clicks from the UI.
- * Call the src_object's ui_act() if status is UI_INTERACTIVE.
- * If the src_object's ui_act() returns 1, update all UIs attacked to it.
- **/
+/**
+ * private
+ *
+ * Handle clicks from the UI.
+ * Call the src_object's ui_act() if status is UI_INTERACTIVE.
+ * If the src_object's ui_act() returns 1, update all UIs attacked to it.
+ */
/datum/tgui/Topic(href, href_list)
if(user != usr)
return // Something is not right here.
@@ -256,35 +237,47 @@
switch(action)
if("tgui:initialize")
- user << output(url_encode(get_json(initial_data, initial_static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:initialize")
+ user << output(_initial_update, "[window_id].browser:update")
initialized = TRUE
- if("tgui:view")
- if(params["screen"])
- ui_screen = params["screen"]
+ if("tgui:setSharedState")
+ // Update the window state.
+ update_status(push = FALSE)
+ // Bail if UI is not interactive or usr calling Topic
+ // is not the UI user.
+ if(status != UI_INTERACTIVE)
+ return
+ var/key = params["key"]
+ var/value = params["value"]
+ if(!src_object.tgui_shared_states)
+ src_object.tgui_shared_states = list()
+ src_object.tgui_shared_states[key] = value
SStgui.update_uis(src_object)
+ if("tgui:setFancy")
+ var/value = text2num(params["value"])
+ user.client.prefs.tgui_fancy = value
if("tgui:log")
// Force window to show frills on fatal errors
if(params["fatal"])
winset(user, window_id, "titlebar=1;can-resize=1;size=600x600")
+ log_message(params["log"])
if("tgui:link")
user << link(params["url"])
- if("tgui:fancy")
- user.client.prefs.tgui_fancy = TRUE
- if("tgui:nofrills")
- user.client.prefs.tgui_fancy = FALSE
else
- update_status(push = FALSE) // Update the window state.
- if(src_object.ui_act(action, params, src, state)) // Call ui_act() on the src_object.
- SStgui.update_uis(src_object) // Update if the object requested it.
+ // Update the window state.
+ update_status(push = FALSE)
+ // Call ui_act() on the src_object.
+ if(src_object.ui_act(action, params, src, state))
+ // Update if the object requested it.
+ SStgui.update_uis(src_object)
- /**
- * private
- *
- * Update the UI.
- * Only updates the data if update is true, otherwise only updates the status.
- *
- * optional force bool If the UI should be forced to update.
- **/
+/**
+ * private
+ *
+ * Update the UI.
+ * Only updates the data if update is true, otherwise only updates the status.
+ *
+ * optional force bool If the UI should be forced to update.
+ */
/datum/tgui/process(force = FALSE)
var/datum/host = src_object.ui_host(user)
if(!src_object || !host || !user) // If the object or user died (or something else), abort.
@@ -296,42 +289,46 @@
else
update_status(push = TRUE) // Otherwise only update status.
- /**
- * private
- *
- * Push data to an already open UI.
- *
- * required data list The data to send.
- * optional force bool If the update should be sent regardless of state.
- **/
+/**
+ * private
+ *
+ * Push data to an already open UI.
+ *
+ * required data list The data to send.
+ * optional force bool If the update should be sent regardless of state.
+ */
/datum/tgui/proc/push_data(data, static_data, force = FALSE)
- update_status(push = FALSE) // Update the window state.
+ // Update the window state.
+ update_status(push = FALSE)
+ // Cannot update UI if it is not set up yet.
if(!initialized)
- return // Cannot update UI if it is not set up yet.
+ return
+ // Cannot update UI, we have no visibility.
if(status <= UI_DISABLED && !force)
- return // Cannot update UI, we have no visibility.
-
+ return
// Send the new JSON to the update() Javascript function.
- user << output(url_encode(get_json(data, static_data)), "[custom_browser_id ? window_id : "[window_id].browser"]:update")
+ user << output(
+ url_encode(get_json(data, static_data)),
+ "[window_id].browser:update")
- /**
- * private
- *
- * Updates the UI by interacting with the src_object again, which will hopefully
- * call try_ui_update on it.
- *
- * optional force_open bool If force_open should be passed to ui_interact.
- **/
+/**
+ * private
+ *
+ * Updates the UI by interacting with the src_object again, which will hopefully
+ * call try_ui_update on it.
+ *
+ * optional force_open bool If force_open should be passed to ui_interact.
+ */
/datum/tgui/proc/update(force_open = FALSE)
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
- /**
- * private
- *
- * Update the status/visibility of the UI for its user.
- *
- * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
- **/
+/**
+ * private
+ *
+ * Update the status/visibility of the UI for its user.
+ *
+ * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
+ */
/datum/tgui/proc/update_status(push = FALSE)
var/status = src_object.ui_status(user, state)
if(master_ui)
@@ -340,25 +337,26 @@
if(status == UI_CLOSE)
close()
- /**
- * private
- *
- * Set the status/visibility of the UI.
- *
- * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE).
- * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
- **/
+/**
+ * private
+ *
+ * Set the status/visibility of the UI.
+ *
+ * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE).
+ * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
+ */
/datum/tgui/proc/set_status(status, push = FALSE)
- if(src.status != status) // Only update if status has changed.
+ // Only update if status has changed.
+ if(src.status != status)
if(src.status == UI_DISABLED)
src.status = status
if(push)
update()
else
src.status = status
- if(status == UI_DISABLED || push) // Update if the UI just because disabled, or a push is requested.
+ // Update if the UI just because disabled, or a push is requested.
+ if(status == UI_DISABLED || push)
push_data(null, force = TRUE)
/datum/tgui/proc/log_message(message)
log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]")
-
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 4f7502bb02..83151423e5 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -515,7 +515,7 @@ GLOBAL_LIST_EMPTY(vending_products)
if(!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending)
assets.send(user)
- ui = new(user, src, ui_key, "vending", name, 450, 600, master_ui, state)
+ ui = new(user, src, ui_key, "Vending", ui_key, 450, 600, master_ui, state)
ui.open()
/obj/machinery/vending/ui_static_data(mob/user)
diff --git a/tgstation.dme b/tgstation.dme
index 3b3bb74df1..525cf1ed9e 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -426,7 +426,6 @@
#include "code\datums\components\virtual_reality.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
-#include "code\datums\components\crafting\craft.dm"
#include "code\datums\components\crafting\guncrafting.dm"
#include "code\datums\components\crafting\recipes.dm"
#include "code\datums\components\crafting\glassware\glassware.dm"
diff --git a/tgui-next/docs/converting-old-tgui-interfaces.md b/tgui-next/docs/converting-old-tgui-interfaces.md
index c92cefca5e..a42724e05c 100644
--- a/tgui-next/docs/converting-old-tgui-interfaces.md
+++ b/tgui-next/docs/converting-old-tgui-interfaces.md
@@ -86,9 +86,13 @@ Similarly to the previous example, just add a `||` operator to handle the
```jsx
{!!data.condition && (
- value
+
+ value
+
) || (
- other value
+
+ other value
+
)}
```
@@ -140,7 +144,9 @@ This ensures that you'll never be reading a null entry by mistake. Substitute `{
If it's an array, you'll want to do this in the template
```jsx
{things.map(thing => (
- Thing {thing.number} is here!
+
+ Thing {thing.number} is here!
+
))}
```
@@ -155,11 +161,11 @@ This is quite a bit higher concept than ractive's each statements, so feel free
Now for objects, there's a genuinely pretty gross syntax here. We apoligize, it's related to ie8 compatibility nonsense.
```jsx
-{map((value, key) => {
- return (
- Key is {key}, value is {value}
- );
-})(fooObject)}
+{map((value, key) => (
+
+ Key is {key}, value is {value}
+
+))(fooObject)}
```
Again, sorry for this syntax. `fooObject` would be the object being iterated on, value would be the value of the iterated entry on the list, and key would be the key. the naming of value and key isn't important here, but knowing that it goes `value`, `key` in that order is important.
@@ -196,7 +202,9 @@ To do a similar thing in JSX, just check if array is empty like this:
```jsx
{fooArray.length === 0 && 'fooArray is empty.'}
{fooArray.map(foo => (
- Foo is {foo}
+
+ Foo is {foo}
+
))}
```
@@ -314,9 +322,11 @@ The equivalent of `ui-button` is `Button` but it works quite a bit differently.
becomes
-```
+```jsx
act(ref, "ui_action", {param: value})}/>
+ onClick={() => act('ui_action', {
+ param: value,
+ })}/>
```
diff --git a/tgui-next/packages/common/string.js b/tgui-next/packages/common/string.js
index 10680a460f..d05dbfb6fc 100644
--- a/tgui-next/packages/common/string.js
+++ b/tgui-next/packages/common/string.js
@@ -32,10 +32,13 @@ export const multiline = str => {
};
/**
+ * Creates a glob pattern matcher.
+ *
* Matches strings with wildcards.
- * Example: testGlobPattern('*@domain')('user@domain') === true
+ *
+ * Example: createGlobPattern('*@domain')('user@domain') === true
*/
-export const testGlobPattern = pattern => {
+export const createGlobPattern = pattern => {
const escapeString = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
const regex = new RegExp('^'
+ pattern.split(/\*+/).map(escapeString).join('.*')
@@ -43,6 +46,32 @@ export const testGlobPattern = pattern => {
return str => regex.test(str);
};
+/**
+ * Creates a search terms matcher.
+ *
+ * Returns true if given string matches the search text.
+ *
+ * @template T
+ * @param {string} searchText
+ * @param {(obj: T) => string} stringifier
+ * @returns {(obj: T) => boolean}
+ */
+export const createSearch = (searchText, stringifier) => {
+ const preparedSearchText = searchText.toLowerCase().trim();
+ return obj => {
+ if (!preparedSearchText) {
+ return true;
+ }
+ const str = stringifier ? stringifier(obj) : obj;
+ if (!str) {
+ return false;
+ }
+ return str
+ .toLowerCase()
+ .includes(preparedSearchText);
+ };
+};
+
export const capitalize = str => {
// Handle array
if (Array.isArray(str)) {
diff --git a/tgui-next/packages/tgui-dev-server/link/client.js b/tgui-next/packages/tgui-dev-server/link/client.js
index bd33bcd614..774b7ed6db 100644
--- a/tgui-next/packages/tgui-dev-server/link/client.js
+++ b/tgui-next/packages/tgui-dev-server/link/client.js
@@ -38,8 +38,24 @@ const subscribe = fn => subscribers.push(fn);
*/
const serializeObject = obj => {
let refs = [];
- const json = JSON.stringify(obj, (key, value) => {
- if (typeof value === 'object' && value !== null) {
+ const primitiveReviver = value => {
+ if (typeof value === 'number' && !Number.isFinite(value)) {
+ return {
+ __number__: String(value),
+ };
+ }
+ if (typeof value === 'undefined') {
+ return {
+ __undefined__: true,
+ };
+ }
+ return value;
+ };
+ const objectReviver = (key, value) => {
+ if (typeof value === 'object') {
+ if (value === null) {
+ return value;
+ }
// Circular reference
if (refs.includes(value)) {
return '[circular ref]';
@@ -53,15 +69,15 @@ const serializeObject = obj => {
stack: value.stack,
};
}
+ // Array
+ if (Array.isArray(value)) {
+ return value.map(primitiveReviver);
+ }
return value;
}
- if (typeof value === 'number' && !Number.isFinite(value)) {
- return {
- __number__: String(value),
- };
- }
- return value;
- });
+ return primitiveReviver(value);
+ };
+ const json = JSON.stringify(obj, objectReviver);
refs = null;
return json;
};
diff --git a/tgui-next/packages/tgui/components/Chart.js b/tgui-next/packages/tgui/components/Chart.js
index f54aeac11d..2f5ad2aedd 100644
--- a/tgui-next/packages/tgui/components/Chart.js
+++ b/tgui-next/packages/tgui/components/Chart.js
@@ -1,8 +1,8 @@
import { map, zipWith } from 'common/collections';
-import { Component, createRef } from 'inferno';
-import { Box } from './Box';
import { pureComponentHooks } from 'common/react';
-import { tridentVersion } from '../byond';
+import { Component, createRef } from 'inferno';
+import { IS_IE8 } from '../byond';
+import { Box } from './Box';
const normalizeData = (data, scale, rangeX, rangeY) => {
if (data.length === 0) {
@@ -117,5 +117,5 @@ const Stub = props => null;
// IE8: No inline svg support
export const Chart = {
- Line: tridentVersion <= 4 ? Stub : LineChart,
+ Line: IS_IE8 ? Stub : LineChart,
};
diff --git a/tgui-next/packages/tgui/components/NoticeBox.js b/tgui-next/packages/tgui/components/NoticeBox.js
index f57e4d9082..c8a387d4ce 100644
--- a/tgui-next/packages/tgui/components/NoticeBox.js
+++ b/tgui-next/packages/tgui/components/NoticeBox.js
@@ -2,11 +2,23 @@ import { classes, pureComponentHooks } from 'common/react';
import { Box } from './Box';
export const NoticeBox = props => {
- const { className, ...rest } = props;
+ const {
+ className,
+ color,
+ info,
+ warning,
+ success,
+ danger,
+ ...rest
+ } = props;
return (
diff --git a/tgui-next/packages/tgui/interfaces/AiAirlock.js b/tgui-next/packages/tgui/interfaces/AiAirlock.js
index 55a3cd4a22..4886e71034 100644
--- a/tgui-next/packages/tgui/interfaces/AiAirlock.js
+++ b/tgui-next/packages/tgui/interfaces/AiAirlock.js
@@ -1,196 +1,200 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const AiAirlock = props => {
- const { act, data } = useBackend(props);
- const dangerMap = {
- 2: {
- color: 'good',
- localStatusText: 'Offline',
- },
- 1: {
- color: 'average',
- localStatusText: 'Caution',
- },
- 0: {
- color: 'bad',
- localStatusText: 'Optimal',
- },
- };
+const dangerMap = {
+ 2: {
+ color: 'good',
+ localStatusText: 'Offline',
+ },
+ 1: {
+ color: 'average',
+ localStatusText: 'Caution',
+ },
+ 0: {
+ color: 'bad',
+ localStatusText: 'Optimal',
+ },
+};
+
+export const AiAirlock = (props, context) => {
+ const { act, data } = useBackend(context);
const statusMain = dangerMap[data.power.main] || dangerMap[0];
const statusBackup = dangerMap[data.power.backup] || dangerMap[0];
const statusElectrify = dangerMap[data.shock] || dangerMap[0];
return (
-
-
-
- act('disrupt-main')} />
- )}>
- {data.power.main ? 'Online' : 'Offline'}
- {' '}
- {(!data.wires.main_1 || !data.wires.main_2)
- && '[Wires have been cut!]'
- || (data.power.main_timeleft > 0
- && `[${data.power.main_timeleft}s]`)}
-
- act('disrupt-backup')} />
- )}>
- {data.power.backup ? 'Online' : 'Offline'}
- {' '}
- {(!data.wires.backup_1 || !data.wires.backup_2)
- && '[Wires have been cut!]'
- || (data.power.backup_timeleft > 0
- && `[${data.power.backup_timeleft}s]`)}
-
-
+
+
+
+
+ act('shock-restore')} />
+ icon="lightbulb-o"
+ disabled={!data.power.main}
+ content="Disrupt"
+ onClick={() => act('disrupt-main')} />
+ )}>
+ {data.power.main ? 'Online' : 'Offline'}
+ {' '}
+ {(!data.wires.main_1 || !data.wires.main_2)
+ && '[Wires have been cut!]'
+ || (data.power.main_timeleft > 0
+ && `[${data.power.main_timeleft}s]`)}
+
+ act('shock-temp')} />
+ icon="lightbulb-o"
+ disabled={!data.power.backup}
+ content="Disrupt"
+ onClick={() => act('disrupt-backup')} />
+ )}>
+ {data.power.backup ? 'Online' : 'Offline'}
+ {' '}
+ {(!data.wires.backup_1 || !data.wires.backup_2)
+ && '[Wires have been cut!]'
+ || (data.power.backup_timeleft > 0
+ && `[${data.power.backup_timeleft}s]`)}
+
+
+ act('shock-restore')} />
+ act('shock-temp')} />
+ act('shock-perm')} />
+
+ )}>
+ {data.shock === 2 ? 'Safe' : 'Electrified'}
+ {' '}
+ {!data.wires.shock
+ && '[Wires have been cut!]'
+ || (data.shock_timeleft > 0
+ && `[${data.shock_timeleft}s]`)
+ || (data.shock_timeleft === -1
+ && '[Permanent]')}
+
+
+
+
+
+ act('shock-perm')} />
-
- )}>
- {data.shock === 2 ? 'Safe' : 'Electrified'}
- {' '}
- {!data.wires.shock
- && '[Wires have been cut!]'
- || (data.shock_timeleft > 0
- && `[${data.shock_timeleft}s]`)
- || (data.shock_timeleft === -1
- && '[Permanent]')}
-
-
-
-
-
- act('idscan-toggle')} />
- )}>
- {!data.wires.id_scanner && '[Wires have been cut!]'}
-
- act('emergency-toggle')} />
- )} />
-
- act('bolt-toggle')} />
- )}>
- {!data.wires.bolts && '[Wires have been cut!]'}
-
- act('light-toggle')} />
- )}>
- {!data.wires.lights && '[Wires have been cut!]'}
-
- act('safe-toggle')} />
- )}>
- {!data.wires.safe && '[Wires have been cut!]'}
-
- act('speed-toggle')} />
- )}>
- {!data.wires.timing && '[Wires have been cut!]'}
-
-
- act('open-close')} />
- )}>
- {!!(data.locked || data.welded) && (
-
- [Door is {data.locked ? 'bolted' : ''}
- {(data.locked && data.welded) ? ' and ' : ''}
- {data.welded ? 'welded' : ''}!]
-
- )}
-
-
-
-
+ icon={data.id_scanner ? 'power-off' : 'times'}
+ content={data.id_scanner ? 'Enabled' : 'Disabled'}
+ selected={data.id_scanner}
+ disabled={!data.wires.id_scanner}
+ onClick={() => act('idscan-toggle')} />
+ )}>
+ {!data.wires.id_scanner && '[Wires have been cut!]'}
+
+ act('emergency-toggle')} />
+ )} />
+
+ act('bolt-toggle')} />
+ )}>
+ {!data.wires.bolts && '[Wires have been cut!]'}
+
+ act('light-toggle')} />
+ )}>
+ {!data.wires.lights && '[Wires have been cut!]'}
+
+ act('safe-toggle')} />
+ )}>
+ {!data.wires.safe && '[Wires have been cut!]'}
+
+ act('speed-toggle')} />
+ )}>
+ {!data.wires.timing && '[Wires have been cut!]'}
+
+
+ act('open-close')} />
+ )}>
+ {!!(data.locked || data.welded) && (
+
+ [Door is {data.locked ? 'bolted' : ''}
+ {(data.locked && data.welded) ? ' and ' : ''}
+ {data.welded ? 'welded' : ''}!]
+
+ )}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/AirAlarm.js b/tgui-next/packages/tgui/interfaces/AirAlarm.js
index 20d808606c..c6f440985a 100644
--- a/tgui-next/packages/tgui/interfaces/AirAlarm.js
+++ b/tgui-next/packages/tgui/interfaces/AirAlarm.js
@@ -1,31 +1,30 @@
import { toFixed } from 'common/math';
import { decodeHtmlEntities } from 'common/string';
import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
+import { useBackend, useLocalState } from '../backend';
import { Box, Button, LabeledList, NumberInput, Section } from '../components';
import { getGasLabel } from '../constants';
+import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
-export const AirAlarm = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const AirAlarm = (props, context) => {
+ const { act, data } = useBackend(context);
const locked = data.locked && !data.siliconUser;
return (
-
- act('lock')} />
-
- {!locked && (
-
- )}
-
+
+
+
+
+ {!locked && (
+
+ )}
+
+
);
};
-const AirAlarmStatus = props => {
- const { data } = useBackend(props);
+const AirAlarmStatus = (props, context) => {
+ const { data } = useBackend(context);
const entries = (data.environment_data || [])
.filter(entry => entry.value >= 0.01);
const dangerMap = {
@@ -114,23 +113,20 @@ const AIR_ALARM_ROUTES = {
},
};
-const AirAlarmControl = props => {
- const { state } = props;
- const { act, config } = useBackend(props);
- const route = AIR_ALARM_ROUTES[config.screen] || AIR_ALARM_ROUTES.home;
+const AirAlarmControl = (props, context) => {
+ const [screen, setScreen] = useLocalState(context, 'screen');
+ const route = AIR_ALARM_ROUTES[screen] || AIR_ALARM_ROUTES.home;
const Component = route.component();
return (
act('tgui:view', {
- screen: 'home',
- })} />
+ onClick={() => setScreen()} />
)}>
-
+
);
};
@@ -139,8 +135,9 @@ const AirAlarmControl = props => {
// Home screen
// --------------------------------------------------------
-const AirAlarmControlHome = props => {
- const { act, data } = useBackend(props);
+const AirAlarmControlHome = (props, context) => {
+ const { act, data } = useBackend(context);
+ const [screen, setScreen] = useLocalState(context, 'screen');
const {
mode,
atmos_alarm,
@@ -168,30 +165,22 @@ const AirAlarmControlHome = props => {
act('tgui:view', {
- screen: 'vents',
- })} />
+ onClick={() => setScreen('vents')} />
act('tgui:view', {
- screen: 'scrubbers',
- })} />
+ onClick={() => setScreen('scrubbers')} />
act('tgui:view', {
- screen: 'modes',
- })} />
+ onClick={() => setScreen('modes')} />
act('tgui:view', {
- screen: 'thresholds',
- })} />
+ onClick={() => setScreen('thresholds')} />
);
};
@@ -200,21 +189,22 @@ const AirAlarmControlHome = props => {
// Vents
// --------------------------------------------------------
-const AirAlarmControlVents = props => {
- const { state } = props;
- const { data } = useBackend(props);
+const AirAlarmControlVents = (props, context) => {
+ const { data } = useBackend(context);
const { vents } = data;
if (!vents || vents.length === 0) {
return 'Nothing to show';
}
return vents.map(vent => (
-
+
));
};
-const Vent = props => {
+const Vent = (props, context) => {
+ const { vent } = props;
+ const { act } = useBackend(context);
const {
id_tag,
long_name,
@@ -227,8 +217,7 @@ const Vent = props => {
internal,
extdefault,
intdefault,
- } = props;
- const { act } = useBackend(props);
+ } = vent;
return (
{
// Scrubbers
// --------------------------------------------------------
-const AirAlarmControlScrubbers = props => {
- const { state } = props;
- const { data } = useBackend(props);
+const AirAlarmControlScrubbers = (props, context) => {
+ const { data } = useBackend(context);
const { scrubbers } = data;
if (!scrubbers || scrubbers.length === 0) {
return 'Nothing to show';
@@ -328,12 +316,13 @@ const AirAlarmControlScrubbers = props => {
return scrubbers.map(scrubber => (
+ scrubber={scrubber} />
));
};
-const Scrubber = props => {
+const Scrubber = (props, context) => {
+ const { scrubber } = props;
+ const { act } = useBackend(context);
const {
long_name,
power,
@@ -341,8 +330,7 @@ const Scrubber = props => {
id_tag,
widenet,
filter_types,
- } = props;
- const { act } = useBackend(props);
+ } = scrubber;
return (
{
// Modes
// --------------------------------------------------------
-const AirAlarmControlModes = props => {
- const { act, data } = useBackend(props);
+const AirAlarmControlModes = (props, context) => {
+ const { act, data } = useBackend(context);
const { modes } = data;
if (!modes || modes.length === 0) {
return 'Nothing to show';
@@ -423,8 +411,8 @@ const AirAlarmControlModes = props => {
// Thresholds
// --------------------------------------------------------
-const AirAlarmControlThresholds = props => {
- const { act, data } = useBackend(props);
+const AirAlarmControlThresholds = (props, context) => {
+ const { act, data } = useBackend(context);
const { thresholds } = data;
return (
{
- const { act, data } = useBackend(props);
+export const AtmosAlertConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const priorityAlerts = data.priority || [];
const minorAlerts = data.minor || [];
return (
-
-
- {priorityAlerts.length > 0 ? (
- priorityAlerts.map(alert => (
-
- act('clear', { zone: alert })} />
-
- ))
- ) : (
-
- No Priority Alerts
-
- )}
- {minorAlerts.length > 0 ? (
- minorAlerts.map(alert => (
-
- act('clear', { zone: alert })} />
-
- ))
- ) : (
-
- No Minor Alerts
-
- )}
-
-
+
+
+
+
+ {priorityAlerts.length === 0 && (
+
+ No Priority Alerts
+
+ )}
+ {priorityAlerts.map(alert => (
+
+ act('clear', { zone: alert })} />
+
+ ))}
+ {minorAlerts.length > 0 && (
+
+ No Minor Alerts
+
+ )}
+ {minorAlerts.map(alert => (
+
+ act('clear', { zone: alert })} />
+
+ ))}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/AtmosControlConsole.js b/tgui-next/packages/tgui/interfaces/AtmosControlConsole.js
index 80c23d434b..7a35db6870 100644
--- a/tgui-next/packages/tgui/interfaces/AtmosControlConsole.js
+++ b/tgui-next/packages/tgui/interfaces/AtmosControlConsole.js
@@ -1,100 +1,100 @@
import { map } from 'common/collections';
import { toFixed } from 'common/math';
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const AtmosControlConsole = props => {
- const { act, data } = useBackend(props);
+export const AtmosControlConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const sensors = data.sensors || [];
return (
-
-
- {sensors.map(sensor => {
- const gases = sensor.gases || {};
- return (
-
-
-
- {toFixed(sensor.pressure, 2) + ' kPa'}
-
- {!!sensor.temperature && (
-
- {toFixed(sensor.temperature, 2) + ' K'}
+
+
+
+ {sensors.map(sensor => {
+ const gases = sensor.gases || {};
+ return (
+
+
+
+ {toFixed(sensor.pressure, 2) + ' kPa'}
- )}
- {map((gasPercent, gasId) => {
- return (
+ {!!sensor.temperature && (
+
+ {toFixed(sensor.temperature, 2) + ' K'}
+
+ )}
+ {map((gasPercent, gasId) => (
{toFixed(gasPercent, 2) + '%'}
- );
- })(gases)}
-
-
- );
- })}
-
- {data.tank && (
- act('reconnect')} />
- )}>
-
-
- act('input')} />
-
-
- act('rate', {
- rate: value,
- })} />
-
-
- act('output')} />
-
-
- act('pressure', {
- pressure: value,
- })} />
-
-
+ ))(gases)}
+
+
+ );
+ })}
- )}
-
+ {data.tank && (
+ act('reconnect')} />
+ )}>
+
+
+ act('input')} />
+
+
+ act('rate', {
+ rate: value,
+ })} />
+
+
+ act('output')} />
+
+
+ act('pressure', {
+ pressure: value,
+ })} />
+
+
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/AtmosFilter.js b/tgui-next/packages/tgui/interfaces/AtmosFilter.js
index 35bbf34686..1bc7e8f63b 100644
--- a/tgui-next/packages/tgui/interfaces/AtmosFilter.js
+++ b/tgui-next/packages/tgui/interfaces/AtmosFilter.js
@@ -1,52 +1,57 @@
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
import { getGasLabel } from '../constants';
+import { Window } from '../layouts';
-export const AtmosFilter = props => {
- const { act, data } = useBackend(props);
+export const AtmosFilter = (props, context) => {
+ const { act, data } = useBackend(context);
const filterTypes = data.filter_types || [];
return (
-
-
-
- act('power')} />
-
-
- act('rate', {
- rate: value,
- })} />
- act('rate', {
- rate: 'max',
- })} />
-
-
- {filterTypes.map(filter => (
- act('filter', {
- mode: filter.id,
- })} />
- ))}
-
-
-
+
+
+
+
+
+ act('power')} />
+
+
+ act('rate', {
+ rate: value,
+ })} />
+ act('rate', {
+ rate: 'max',
+ })} />
+
+
+ {filterTypes.map(filter => (
+ act('filter', {
+ mode: filter.id,
+ })} />
+ ))}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/AtmosMixer.js b/tgui-next/packages/tgui/interfaces/AtmosMixer.js
index a944bfb686..067118dea1 100644
--- a/tgui-next/packages/tgui/interfaces/AtmosMixer.js
+++ b/tgui-next/packages/tgui/interfaces/AtmosMixer.js
@@ -1,66 +1,71 @@
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const AtmosMixer = props => {
- const { act, data } = useBackend(props);
+export const AtmosMixer = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
-
- act('power')} />
-
-
- act('pressure', {
- pressure: value,
- })} />
- act('pressure', {
- pressure: 'max',
- })} />
-
-
- act('node1', {
- concentration: value,
- })} />
-
-
- act('node2', {
- concentration: value,
- })} />
-
-
-
+
+
+
+
+
+ act('power')} />
+
+
+ act('pressure', {
+ pressure: value,
+ })} />
+ act('pressure', {
+ pressure: 'max',
+ })} />
+
+
+ act('node1', {
+ concentration: value,
+ })} />
+
+
+ act('node2', {
+ concentration: value,
+ })} />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/AtmosPump.js b/tgui-next/packages/tgui/interfaces/AtmosPump.js
index 01c50de4d4..ec3285cd44 100644
--- a/tgui-next/packages/tgui/interfaces/AtmosPump.js
+++ b/tgui-next/packages/tgui/interfaces/AtmosPump.js
@@ -1,63 +1,68 @@
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const AtmosPump = props => {
- const { act, data } = useBackend(props);
+export const AtmosPump = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
-
- act('power')} />
-
- {data.max_rate ? (
-
- act('rate', {
- rate: value,
- })} />
- act('rate', {
- rate: 'max',
- })} />
-
- ) : (
-
- act('pressure', {
- pressure: value,
- })} />
- act('pressure', {
- pressure: 'max',
- })} />
-
- )}
-
-
+
+
+
+
+
+ act('power')} />
+
+ {data.max_rate ? (
+
+ act('rate', {
+ rate: value,
+ })} />
+ act('rate', {
+ rate: 'max',
+ })} />
+
+ ) : (
+
+ act('pressure', {
+ pressure: value,
+ })} />
+ act('pressure', {
+ pressure: 'max',
+ })} />
+
+ )}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/BankMachine.js b/tgui-next/packages/tgui/interfaces/BankMachine.js
index a3d52097ec..e8c8c2f57d 100644
--- a/tgui-next/packages/tgui/interfaces/BankMachine.js
+++ b/tgui-next/packages/tgui/interfaces/BankMachine.js
@@ -1,33 +1,35 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
-export const BankMachine = props => {
- const { act, data } = useBackend(props);
+export const BankMachine = (props, context) => {
+ const { act, data } = useBackend(context);
const {
current_balance,
siphoning,
station_name,
} = data;
return (
-
-
-
- act(siphoning ? 'halt' : 'siphon')} />
- )}>
- {current_balance + ' cr'}
-
-
-
-
- Authorized personnel only
-
-
+
+
+
+
+ act(siphoning ? 'halt' : 'siphon')} />
+ )}>
+ {current_balance + ' cr'}
+
+
+
+
+ Authorized personnel only
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/BluespaceArtillery.js b/tgui-next/packages/tgui/interfaces/BluespaceArtillery.js
index b9f07eabe9..6d2d94f7bf 100644
--- a/tgui-next/packages/tgui/interfaces/BluespaceArtillery.js
+++ b/tgui-next/packages/tgui/interfaces/BluespaceArtillery.js
@@ -1,9 +1,10 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
-export const BluespaceArtillery = props => {
- const { act, data } = useBackend(props);
+export const BluespaceArtillery = (props, context) => {
+ const { act, data } = useBackend(context);
const {
notice,
connected,
@@ -11,68 +12,70 @@ export const BluespaceArtillery = props => {
target,
} = data;
return (
-
- {!!notice && (
-
- {notice}
-
- )}
- {connected ? (
-
- act('recalibrate')} />
- )}>
-
- {target || 'No Target Set'}
-
-
-
- {unlocked ? (
-
+
+
+ {!!notice && (
+
+ {notice}
+
+ )}
+ {connected ? (
+
+ act('fire')} />
+ icon="crosshairs"
+ disabled={!unlocked}
+ onClick={() => act('recalibrate')} />
+ )}>
+
+ {target || 'No Target Set'}
- ) : (
-
-
- Bluespace artillery is currently locked.
+
+
+ {unlocked ? (
+
+ act('fire')} />
-
- Awaiting authorization via keycard reader from at minimum
- two station heads.
-
-
- )}
+ ) : (
+
+
+ Bluespace artillery is currently locked.
+
+
+ Awaiting authorization via keycard reader from at minimum
+ two station heads.
+
+
+ )}
+
+
+ ) : (
+
+
+
+ act('build')} />
+
+
-
- ) : (
-
-
-
- act('build')} />
-
-
-
- )}
-
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/BrigTimer.js b/tgui-next/packages/tgui/interfaces/BrigTimer.js
index a83d643334..d7292d356d 100644
--- a/tgui-next/packages/tgui/interfaces/BrigTimer.js
+++ b/tgui-next/packages/tgui/interfaces/BrigTimer.js
@@ -1,55 +1,60 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, Section } from '../components';
+import { Window } from '../layouts';
-export const BrigTimer = props => {
- const { act, data } = useBackend(props);
+export const BrigTimer = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
+
+
+
+ act(data.timing ? 'stop' : 'start')} />
+ act('flash')} />
+
+ )}>
act(data.timing ? 'stop' : 'start')} />
+ icon="fast-backward"
+ onClick={() => act('time', { adjust: -600 })} />
act('flash')} />
-
- )}>
- act('time', { adjust: -600 })} />
- act('time', { adjust: -100 })} />
- {' '}
- {String(data.minutes).padStart(2, '0')}:
- {String(data.seconds).padStart(2, '0')}
- {' '}
- act('time', { adjust: 100 })} />
- act('time', { adjust: 600 })} />
-
- act('preset', { preset: 'short' })} />
- act('preset', { preset: 'medium' })} />
- act('preset', { preset: 'long' })} />
-
+ icon="backward"
+ onClick={() => act('time', { adjust: -100 })} />
+ {' '}
+ {String(data.minutes).padStart(2, '0')}:
+ {String(data.seconds).padStart(2, '0')}
+ {' '}
+ act('time', { adjust: 100 })} />
+ act('time', { adjust: 600 })} />
+
+ act('preset', { preset: 'short' })} />
+ act('preset', { preset: 'medium' })} />
+ act('preset', { preset: 'long' })} />
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/CellularEmporium.js b/tgui-next/packages/tgui/interfaces/CellularEmporium.js
index 50f3cc5c49..8e8d58cb94 100644
--- a/tgui-next/packages/tgui/interfaces/CellularEmporium.js
+++ b/tgui-next/packages/tgui/interfaces/CellularEmporium.js
@@ -1,54 +1,57 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const CellularEmporium = props => {
- const { act, data } = useBackend(props);
+export const CellularEmporium = (props, context) => {
+ const { act, data } = useBackend(context);
const { abilities } = data;
return (
-
-
-
- act('readapt')} />
- )}>
- {data.genetic_points_remaining}
-
-
-
-
-
- {abilities.map(ability => (
+
+
+
+
- {ability.dna_cost}
- {' '}
- act('evolve', {
- name: ability.name,
- })} />
-
+ act('readapt')} />
)}>
- {ability.desc}
-
- {ability.helptext}
-
+ {data.genetic_points_remaining}
- ))}
-
-
-
+
+
+
+
+ {abilities.map(ability => (
+
+ {ability.dna_cost}
+ {' '}
+ act('evolve', {
+ name: ability.name,
+ })} />
+
+ )}>
+ {ability.desc}
+
+ {ability.helptext}
+
+
+ ))}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ChemAcclimator.js b/tgui-next/packages/tgui/interfaces/ChemAcclimator.js
index 0145e80874..9613a69b8b 100644
--- a/tgui-next/packages/tgui/interfaces/ChemAcclimator.js
+++ b/tgui-next/packages/tgui/interfaces/ChemAcclimator.js
@@ -1,76 +1,78 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemAcclimator = props => {
- const { act, data } = useBackend(props);
+export const ChemAcclimator = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
-
-
- {data.chem_temp} K
-
-
- act('set_target_temperature', {
- temperature: value,
- })} />
-
-
- {
- act('set_allowed_temperature_difference', {
+
+
+
+
+
+ {data.chem_temp} K
+
+
+ act('set_target_temperature', {
temperature: value,
- });
- }} />
-
-
-
- act('toggle_power')} />
- )}>
-
-
- act('change_volume', {
- volume: value,
- })} />
-
-
- {data.acclimate_state}
-
-
- {data.emptying ? 'Emptying' : 'Filling'}
-
-
-
-
+ })} />
+
+
+ {
+ act('set_allowed_temperature_difference', {
+ temperature: value,
+ });
+ }} />
+
+
+
+ act('toggle_power')} />
+ )}>
+
+
+ act('change_volume', {
+ volume: value,
+ })} />
+
+
+ {data.acclimate_state}
+
+
+ {data.emptying ? 'Emptying' : 'Filling'}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ChemDebugSynthesizer.js b/tgui-next/packages/tgui/interfaces/ChemDebugSynthesizer.js
index 83456eea38..94b939eeae 100644
--- a/tgui-next/packages/tgui/interfaces/ChemDebugSynthesizer.js
+++ b/tgui-next/packages/tgui/interfaces/ChemDebugSynthesizer.js
@@ -1,9 +1,10 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemDebugSynthesizer = props => {
- const { act, data } = useBackend(props);
+export const ChemDebugSynthesizer = (props, context) => {
+ const { act, data } = useBackend(context);
const {
amount,
beakerCurrentVolume,
@@ -12,62 +13,66 @@ export const ChemDebugSynthesizer = props => {
beakerContents = [],
} = data;
return (
-
- act('ejectBeaker')} />
- act('amount', {
- amount: value,
- })} />
- act('input')} />
-
- ) : (
- act('makecup')} />
- )}>
- {isBeakerLoaded ? (
-
-
-
- {' / ' + beakerMaxVolume + ' u'}
-
- {beakerContents.length > 0 ? (
-
- {beakerContents.map(chem => (
-
- {chem.volume} u
-
- ))}
-
+
+
+
+ act('ejectBeaker')} />
+ act('amount', {
+ amount: value,
+ })} />
+ act('input')} />
+
) : (
-
- Recipient Empty
+ act('makecup')} />
+ )}>
+ {isBeakerLoaded ? (
+
+
+
+ {' / ' + beakerMaxVolume + ' u'}
+
+ {beakerContents.length > 0 ? (
+
+ {beakerContents.map(chem => (
+
+ {chem.volume} u
+
+ ))}
+
+ ) : (
+
+ Recipient Empty
+
+ )}
+
+ ) : (
+
+ No Recipient
)}
-
- ) : (
-
- No Recipient
-
- )}
-
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ChemFilter.js b/tgui-next/packages/tgui/interfaces/ChemFilter.js
index 508c7b9ce6..2b5bd059db 100644
--- a/tgui-next/packages/tgui/interfaces/ChemFilter.js
+++ b/tgui-next/packages/tgui/interfaces/ChemFilter.js
@@ -1,17 +1,16 @@
-import { Component, Fragment } from 'inferno';
-import { useBackend } from '../backend';
-import { Button, Grid, Section, Input } from '../components';
+import { Fragment } from 'inferno';
+import { useBackend, useLocalState } from '../backend';
+import { Button, Flex, Input, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemFilterPane = props => {
- const { act } = useBackend(props);
+export const ChemFilterPane = (props, context) => {
+ const { act } = useBackend(context);
const { title, list, reagentName, onReagentInput } = props;
const titleKey = title.toLowerCase();
return (
{
);
};
-export class ChemFilter extends Component {
- constructor() {
- super();
- this.state = {
- leftReagentName: '',
- rightReagentName: '',
- };
- }
-
- setLeftReagentName(leftReagentName) {
- this.setState({
- leftReagentName,
- });
- }
-
- setRightReagentName(rightReagentName) {
- this.setState({
- rightReagentName,
- });
- }
-
- render() {
- const { state } = this.props;
- const { data } = state;
- const {
- left = [],
- right = [],
- } = data;
- return (
-
-
- this.setLeftReagentName(value)}
- state={state} />
-
-
- this.setRightReagentName(value)}
- state={state} />
-
-
- );
- }
-}
+export const ChemFilter = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ left = [],
+ right = [],
+ } = data;
+ const [leftName, setLeftName] = useLocalState(context, 'leftName', '');
+ const [rightName, setRightName] = useLocalState(context, 'rightName', '');
+ return (
+
+
+
+
+ setLeftName(value)} />
+
+
+ setRightName(value)} />
+
+
+
+
+ );
+};
diff --git a/tgui-next/packages/tgui/interfaces/ChemPress.js b/tgui-next/packages/tgui/interfaces/ChemPress.js
index 155fa371fd..07774b2ac2 100644
--- a/tgui-next/packages/tgui/interfaces/ChemPress.js
+++ b/tgui-next/packages/tgui/interfaces/ChemPress.js
@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Box, Button, Input, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemPress = props => {
- const { act, data } = useBackend(props);
+export const ChemPress = (props, context) => {
+ const { act, data } = useBackend(context);
const {
pill_size,
pill_name,
@@ -10,44 +11,48 @@ export const ChemPress = props => {
pill_styles = [],
} = data;
return (
-
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ChemReactionChamber.js b/tgui-next/packages/tgui/interfaces/ChemReactionChamber.js
index 8c829258ec..9af7ef7ddb 100644
--- a/tgui-next/packages/tgui/interfaces/ChemReactionChamber.js
+++ b/tgui-next/packages/tgui/interfaces/ChemReactionChamber.js
@@ -1,98 +1,85 @@
-import { Component } from 'inferno';
-import { act } from '../byond';
-import { Box, Button, LabeledList, NumberInput, Section, Input } from '../components';
import { map } from 'common/collections';
import { classes } from 'common/react';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Input, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-
-export class ChemReactionChamber extends Component {
- constructor() {
- super();
- this.state = {
- reagentName: "",
- reagentQuantity: 1,
- };
- }
-
- setReagentName(reagentName) {
- this.setState({
- reagentName,
- });
- }
-
- setReagentQuantity(reagentQuantity) {
- this.setState({
- reagentQuantity,
- });
- }
-
- render() {
- const { state } = this.props;
- const { config, data } = state;
- const { ref } = config;
- const emptying = data.emptying;
- const reagents = data.reagents || [];
- return (
-
- {emptying ? "Emptying" : "Filling"}
-
- )} >
-
-
-
- this.setReagentName(value)} />
-
-
- this.setReagentQuantity(value)} />
-
- act(ref, 'add', {
- chem: this.state.reagentName,
- amount: this.state.reagentQuantity,
- })} />
-
-
- {map((amount, reagent) => (
- {
+ const { act, data } = useBackend(context);
+ const [
+ reagentName,
+ setReagentName,
+ ] = useLocalState(context, 'reagentName', '');
+ const [
+ reagentQuantity,
+ setReagentQuantity,
+ ] = useLocalState(context, 'reagentQuantity', 1);
+ const emptying = data.emptying;
+ const reagents = data.reagents || [];
+ return (
+
+
+
+
+
+ );
+};
diff --git a/tgui-next/packages/tgui/interfaces/ChemSplitter.js b/tgui-next/packages/tgui/interfaces/ChemSplitter.js
index 8e64c43e0f..c11a374ac7 100644
--- a/tgui-next/packages/tgui/interfaces/ChemSplitter.js
+++ b/tgui-next/packages/tgui/interfaces/ChemSplitter.js
@@ -1,48 +1,53 @@
import { toFixed } from 'common/math';
import { useBackend } from '../backend';
import { LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemSplitter = props => {
- const { act, data } = useBackend(props);
+export const ChemSplitter = (props, context) => {
+ const { act, data } = useBackend(context);
const {
straight,
side,
max_transfer,
} = data;
return (
-
-
-
- toFixed(value, 2)}
- step={0.05}
- stepPixelSize={4}
- onChange={(e, value) => act('set_amount', {
- target: 'straight',
- amount: value,
- })} />
-
-
- toFixed(value, 2)}
- step={0.05}
- stepPixelSize={4}
- onChange={(e, value) => act('set_amount', {
- target: 'side',
- amount: value,
- })} />
-
-
-
+
+
+
+
+
+ toFixed(value, 2)}
+ step={0.05}
+ stepPixelSize={4}
+ onChange={(e, value) => act('set_amount', {
+ target: 'straight',
+ amount: value,
+ })} />
+
+
+ toFixed(value, 2)}
+ step={0.05}
+ stepPixelSize={4}
+ onChange={(e, value) => act('set_amount', {
+ target: 'side',
+ amount: value,
+ })} />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ChemSynthesizer.js b/tgui-next/packages/tgui/interfaces/ChemSynthesizer.js
index df56dfdc07..26db58cb3a 100644
--- a/tgui-next/packages/tgui/interfaces/ChemSynthesizer.js
+++ b/tgui-next/packages/tgui/interfaces/ChemSynthesizer.js
@@ -1,9 +1,10 @@
import { toFixed } from 'common/math';
import { useBackend } from '../backend';
import { Box, Button, Section } from '../components';
+import { Window } from '../layouts';
-export const ChemSynthesizer = props => {
- const { act, data } = useBackend(props);
+export const ChemSynthesizer = (props, context) => {
+ const { act, data } = useBackend(context);
const {
amount,
current_reagent,
@@ -11,32 +12,36 @@ export const ChemSynthesizer = props => {
possible_amounts = [],
} = data;
return (
-
-
- {possible_amounts.map(possible_amount => (
- act('amount', {
- target: possible_amount,
- })} />
- ))}
-
-
- {chemicals.map(chemical => (
- act('select', {
- reagent: chemical.id,
- })} />
- ))}
-
-
+
+
+
+
+ {possible_amounts.map(possible_amount => (
+ act('amount', {
+ target: possible_amount,
+ })} />
+ ))}
+
+
+ {chemicals.map(chemical => (
+ act('select', {
+ reagent: chemical.id,
+ })} />
+ ))}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/CodexGigas.js b/tgui-next/packages/tgui/interfaces/CodexGigas.js
index f96ce9380b..b55911750b 100644
--- a/tgui-next/packages/tgui/interfaces/CodexGigas.js
+++ b/tgui-next/packages/tgui/interfaces/CodexGigas.js
@@ -1,98 +1,107 @@
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
+
+const PREFIXES = [
+ "Dark",
+ "Hellish",
+ "Fallen",
+ "Fiery",
+ "Sinful",
+ "Blood",
+ "Fluffy",
+];
+
+const TITLES = [
+ "Lord",
+ "Prelate",
+ "Count",
+ "Viscount",
+ "Vizier",
+ "Elder",
+ "Adept",
+];
+
+const NAMES = [
+ "hal",
+ "ve",
+ "odr",
+ "neit",
+ "ci",
+ "quon",
+ "mya",
+ "folth",
+ "wren",
+ "geyr",
+ "hil",
+ "niet",
+ "twou",
+ "phi",
+ "coa",
+];
+
+const SUFFIXES = [
+ "the Red",
+ "the Soulless",
+ "the Master",
+ "the Lord of all things",
+ "Jr.",
+];
// TODO: refactor the backend of this it's a trainwreck
-export const CodexGigas = props => {
- const { act, data } = useBackend(props);
- const prefixes = [
- "Dark",
- "Hellish",
- "Fallen",
- "Fiery",
- "Sinful",
- "Blood",
- "Fluffy",
- ];
- const titles = [
- "Lord",
- "Prelate",
- "Count",
- "Viscount",
- "Vizier",
- "Elder",
- "Adept",
- ];
- const names = [
- "hal",
- "ve",
- "odr",
- "neit",
- "ci",
- "quon",
- "mya",
- "folth",
- "wren",
- "geyr",
- "hil",
- "niet",
- "twou",
- "phi",
- "coa",
- ];
- const suffixes = [
- "the Red",
- "the Soulless",
- "the Master",
- "the Lord of all things",
- "Jr.",
- ];
+export const CodexGigas = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
- {data.name}
-
-
- {prefixes.map(prefix => (
- act(prefix + ' ')} />
- ))}
-
-
- {titles.map(title => (
- 2}
- onClick={() => act(title + ' ')} />
- ))}
-
-
- {names.map(name => (
- 4}
- onClick={() => act(name)} />
- ))}
-
-
- {suffixes.map(suffix => (
- act(' ' + suffix)} />
- ))}
-
-
- act('search')} />
-
-
-
+
+
+
+ {data.name}
+
+
+ {PREFIXES.map(prefix => (
+ act(prefix + ' ')} />
+ ))}
+
+
+ {TITLES.map(title => (
+ 2}
+ onClick={() => act(title + ' ')} />
+ ))}
+
+
+ {NAMES.map(name => (
+ 4}
+ onClick={() => act(name)} />
+ ))}
+
+
+ {SUFFIXES.map(suffix => (
+ act(' ' + suffix)} />
+ ))}
+
+
+ act('search')} />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ComputerFabricator.js b/tgui-next/packages/tgui/interfaces/ComputerFabricator.js
index 87366f6f73..4504a8124d 100644
--- a/tgui-next/packages/tgui/interfaces/ComputerFabricator.js
+++ b/tgui-next/packages/tgui/interfaces/ComputerFabricator.js
@@ -2,402 +2,417 @@ import { multiline } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Grid, Section, Table, Tooltip } from '../components';
+import { Window } from '../layouts';
-export const ComputerFabricator = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const ComputerFabricator = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
- Your perfect device, only three steps away...
-
- {data.state !== 0 && (
- act('clean_order')} />
- )}
-
-
+
+
+
+ Your perfect device, only three steps away...
+
+ {data.state !== 0 && (
+ act('clean_order')} />
+ )}
+ {data.state === 0 && (
+
+ )}
+ {data.state === 1 && (
+
+ )}
+ {data.state === 2 && (
+
+ )}
+ {data.state === 3 && (
+
+ )}
+
+
);
};
// This had a pretty gross backend so this was unfortunately one of the
// best ways of doing it.
-const CFScreen = props => {
- const { act, data } = useBackend(props);
- if (data.state === 0) {
- return (
-
-
- Choose your Device
-
-
-
-
- act('pick_device', {
- pick: '1',
- })} />
-
-
- act('pick_device', {
- pick: '2',
- })} />
-
-
-
-
- );
- }
- if (data.state === 1) {
- return (
-
- {data.totalprice} cr
-
- )}>
-
-
-
- Battery:
-
-
-
- act('hw_battery', {
- battery: '1',
- })} />
-
-
- act('hw_battery', {
- battery: '2',
- })} />
-
-
- act('hw_battery', {
- battery: '3',
- })} />
-
-
-
-
- Hard Drive:
-
-
-
- act('hw_disk', {
- disk: '1',
- })} />
-
-
- act('hw_disk', {
- disk: '2',
- })} />
-
-
- act('hw_disk', {
- disk: '3',
- })} />
-
-
-
-
- Network Card:
-
-
-
- act('hw_netcard', {
- netcard: '0',
- })} />
-
-
- act('hw_netcard', {
- netcard: '1',
- })} />
-
-
- act('hw_netcard', {
- netcard: '2',
- })} />
-
-
-
-
- Nano Printer:
-
-
-
- act('hw_nanoprint', {
- print: '0',
- })} />
-
-
- act('hw_nanoprint', {
- print: '1',
- })} />
-
-
-
-
- Card Reader:
-
-
-
- act('hw_card', {
- card: '0',
- })} />
-
-
- act('hw_card', {
- card: '1',
- })} />
-
-
- {data.devtype !== 2 && (
-
-
-
- Processor Unit:
-
-
-
- act('hw_cpu', {
- cpu: '1',
- })} />
-
-
- act('hw_cpu', {
- cpu: '2',
- })} />
-
-
-
-
- Tesla Relay:
-
-
-
- act('hw_tesla', {
- tesla: '0',
- })} />
-
-
- act('hw_tesla', {
- tesla: '1',
- })} />
-
-
-
- )}
-
- act('confirm_order')} />
-
- );
- }
- if (data.state === 2) {
- return (
-
-
- Your device is ready for fabrication...
-
-
-
- Please insert the required
-
- {' '}
-
- {data.totalprice} cr
-
-
-
- Current:
-
- = data.totalprice ? "good" : "bad"}>
- {data.credits} cr
-
- act('purchase')} />
-
- );
- }
- if (data.state === 3) {
- return (
-
-
- Thank you for your purchase!
-
-
- If you experience any difficulties with your new device, please
- contact your local network administrator.
-
-
- );
- }
- return null;
+const CfStep1 = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+ Choose your Device
+
+
+
+
+ act('pick_device', {
+ pick: '1',
+ })} />
+
+
+ act('pick_device', {
+ pick: '2',
+ })} />
+
+
+
+
+ );
+};
+
+const CfStep2 = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+ {data.totalprice} cr
+
+ )}>
+
+
+
+ Battery:
+
+
+
+ act('hw_battery', {
+ battery: '1',
+ })} />
+
+
+ act('hw_battery', {
+ battery: '2',
+ })} />
+
+
+ act('hw_battery', {
+ battery: '3',
+ })} />
+
+
+
+
+ Hard Drive:
+
+
+
+ act('hw_disk', {
+ disk: '1',
+ })} />
+
+
+ act('hw_disk', {
+ disk: '2',
+ })} />
+
+
+ act('hw_disk', {
+ disk: '3',
+ })} />
+
+
+
+
+ Network Card:
+
+
+
+ act('hw_netcard', {
+ netcard: '0',
+ })} />
+
+
+ act('hw_netcard', {
+ netcard: '1',
+ })} />
+
+
+ act('hw_netcard', {
+ netcard: '2',
+ })} />
+
+
+
+
+ Nano Printer:
+
+
+
+ act('hw_nanoprint', {
+ print: '0',
+ })} />
+
+
+ act('hw_nanoprint', {
+ print: '1',
+ })} />
+
+
+
+
+ Card Reader:
+
+
+
+ act('hw_card', {
+ card: '0',
+ })} />
+
+
+ act('hw_card', {
+ card: '1',
+ })} />
+
+
+ {data.devtype !== 2 && (
+
+
+
+ Processor Unit:
+
+
+
+ act('hw_cpu', {
+ cpu: '1',
+ })} />
+
+
+ act('hw_cpu', {
+ cpu: '2',
+ })} />
+
+
+
+
+ Tesla Relay:
+
+
+
+ act('hw_tesla', {
+ tesla: '0',
+ })} />
+
+
+ act('hw_tesla', {
+ tesla: '1',
+ })} />
+
+
+
+ )}
+
+ act('confirm_order')} />
+
+ );
+};
+
+const CfStep3 = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+ Your device is ready for fabrication...
+
+
+
+ Please insert the required
+
+ {' '}
+
+ {data.totalprice} cr
+
+
+
+ Current:
+
+ = data.totalprice ? "good" : "bad"}>
+ {data.credits} cr
+
+ act('purchase')} />
+
+ );
+};
+
+const CfStep4 = (props, context) => {
+ return (
+
+
+ Thank you for your purchase!
+
+
+ If you experience any difficulties with your new device, please
+ contact your local network administrator.
+
+
+ );
};
diff --git a/tgui-next/packages/tgui/interfaces/Crayon.js b/tgui-next/packages/tgui/interfaces/Crayon.js
index 6bf9ab83d4..68d3bb46fc 100644
--- a/tgui-next/packages/tgui/interfaces/Crayon.js
+++ b/tgui-next/packages/tgui/interfaces/Crayon.js
@@ -1,61 +1,63 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const Crayon = props => {
- const { act, data } = useBackend(props);
+export const Crayon = (props, context) => {
+ const { act, data } = useBackend(context);
const capOrChanges = data.has_cap || data.can_change_colour;
const drawables = data.drawables || [];
return (
-
- {!!capOrChanges && (
-
+
+
+ {!!capOrChanges && (
+
+
+
+ act('toggle_cap')} />
+
+
+ act('select_colour')} />
+
+ )}
+
-
- act('toggle_cap')} />
+ {drawables.map(drawable => {
+ const items = drawable.items || [];
+ return (
+
+ {items.map(item => (
+ act('select_stencil', {
+ item: item.item,
+ })} />
+ ))}
+
+ );
+ })}
+
+
+
+
+
+ {data.text_buffer}
act('select_colour')} />
+ content="New Text"
+ onClick={() => act('enter_text')} />
- )}
-
-
- {drawables.map(drawable => {
- const items = drawable.items || [];
- return (
-
- {items.map(item => (
- act('select_stencil', {
- item: item.item,
- })} />
- ))}
-
- );
- })}
-
-
-
-
-
- {data.text_buffer}
-
-
- act('enter_text')} />
-
-
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/CrewConsole.js b/tgui-next/packages/tgui/interfaces/CrewConsole.js
index daa27cb7de..8236ab82a3 100644
--- a/tgui-next/packages/tgui/interfaces/CrewConsole.js
+++ b/tgui-next/packages/tgui/interfaces/CrewConsole.js
@@ -1,6 +1,7 @@
import { useBackend } from '../backend';
import { Box, Button, ColorBox, Section, Table } from '../components';
import { COLORS } from '../constants';
+import { Window } from '../layouts';
const HEALTH_COLOR_BY_LEVEL = [
'#17d568',
@@ -57,75 +58,85 @@ const HealthStat = props => {
);
};
-export const CrewConsole = props => {
- const { act, data } = useBackend(props);
+export const CrewConsole = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+const CrewTable = (props, context) => {
+ const { act, data } = useBackend(context);
const sensors = data.sensors || [];
return (
-
-
-
-
- Name
+
+
+
+ Name
+
+
+
+ Vitals
+
+
+ Position
+
+ {!!data.link_allowed && (
+
+ Tracking
-
-
- Vitals
+ )}
+
+ {sensors.map(sensor => (
+
+
+ {sensor.name} ({sensor.assignment})
-
- Position
+
+
+
+
+ {sensor.oxydam !== null ? (
+
+
+ {'/'}
+
+ {'/'}
+
+ {'/'}
+
+
+ ) : (
+ sensor.life_status ? 'Alive' : 'Dead'
+ )}
+
+
+ {sensor.pos_x !== null ? sensor.area : 'N/A'}
{!!data.link_allowed && (
-
- Tracking
+
+ act('select_person', {
+ name: sensor.name,
+ })} />
)}
- {sensors.map(sensor => (
-
-
- {sensor.name} ({sensor.assignment})
-
-
-
-
-
- {sensor.oxydam !== null ? (
-
-
- {'/'}
-
- {'/'}
-
- {'/'}
-
-
- ) : (
- sensor.life_status ? 'Alive' : 'Dead'
- )}
-
-
- {sensor.pos_x !== null ? sensor.area : 'N/A'}
-
- {!!data.link_allowed && (
-
- act('select_person', {
- name: sensor.name,
- })} />
-
- )}
-
- ))}
-
-
+ ))}
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/DisposalUnit.js b/tgui-next/packages/tgui/interfaces/DisposalUnit.js
index 105573dba6..e734e3b30e 100644
--- a/tgui-next/packages/tgui/interfaces/DisposalUnit.js
+++ b/tgui-next/packages/tgui/interfaces/DisposalUnit.js
@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Button, LabeledList, ProgressBar, Section } from '../components';
+import { Window } from '../layouts';
-export const DisposalUnit = props => {
- const { act, data } = useBackend(props);
+export const DisposalUnit = (props, context) => {
+ const { act, data } = useBackend(context);
let stateColor;
let stateText;
if (data.full_pressure) {
@@ -22,50 +23,54 @@ export const DisposalUnit = props => {
stateText = 'Off';
}
return (
-
-
-
- {stateText}
-
-
-
-
-
- act(data.flush
- ? 'handle-0'
- : 'handle-1')}
- />
-
-
- act('eject')} />
-
-
- act(data.pressure_charging
- ? 'pump-0'
- : 'pump-1')}
- />
-
-
-
+
+
+
+
+
+ {stateText}
+
+
+
+
+
+ act(data.flush
+ ? 'handle-0'
+ : 'handle-1')}
+ />
+
+
+ act('eject')} />
+
+
+ act(data.pressure_charging
+ ? 'pump-0'
+ : 'pump-1')}
+ />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/EightBallVote.js b/tgui-next/packages/tgui/interfaces/EightBallVote.js
index 95ddfdfdce..340751e1fa 100644
--- a/tgui-next/packages/tgui/interfaces/EightBallVote.js
+++ b/tgui-next/packages/tgui/interfaces/EightBallVote.js
@@ -1,24 +1,34 @@
import { useBackend } from '../backend';
import { Box, Button, Grid, Section, NoticeBox } from '../components';
import { toTitleCase } from 'common/string';
+import { Window } from '../layouts';
-export const EightBallVote = props => {
- const { act, data } = useBackend(props);
+export const EightBallVote = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ shaking,
+ } = data;
+ return (
+
+
+ {!shaking && (
+
+ No question is currently being asked.
+
+ ) || (
+
+ )}
+
+
+ );
+};
+const EightBallVoteQuestion = (props, context) => {
+ const { act, data } = useBackend(context);
const {
question,
- shaking,
answers = [],
} = data;
-
- if (!shaking) {
- return (
-
- No question is currently being asked.
-
- );
- }
-
return (
{
- const { act, data } = useBackend(props);
-
+export const EmergencyShuttleConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const {
timer_str,
enabled,
@@ -12,104 +12,107 @@ export const EmergencyShuttleConsole = props => {
authorizations_remaining,
authorizations = [],
} = data;
-
return (
-
-
- {timer_str}
-
-
-
- ENGINES:
-
-
- {engines_started ? 'Online' : 'Idle'}
-
-
- act('abort')} />
- )}>
-
-
- act('authorize')} />
-
-
- act('repeal')} />
-
-
-
+
+
+
+ {timer_str}
+
+
- {emagged ? 'ERROR' : 'Remaining: ' + authorizations_remaining}
+ bold>
+ ENGINES:
- )}>
- {authorizations.length > 0 ? (
- authorizations.map(authorization => (
-
- {authorization.name} ({authorization.job})
-
- ))
- ) : (
- No Active Authorizations
+ inline
+ color={engines_started ? 'good' : 'average'}
+ ml={1}>
+ {engines_started ? 'Online' : 'Idle'}
- )}
- {authorizations.map(authorization => (
-
- {authorization.name} ({authorization.job})
-
- ))}
+
+ act('abort')} />
+ )}>
+
+
+ act('authorize')} />
+
+
+ act('repeal')} />
+
+
+
+ {emagged ? 'ERROR' : 'Remaining: ' + authorizations_remaining}
+
+ )}>
+ {authorizations.length > 0 ? (
+ authorizations.map(authorization => (
+
+ {authorization.name} ({authorization.job})
+
+ ))
+ ) : (
+
+ No Active Authorizations
+
+ )}
+ {authorizations.map(authorization => (
+
+ {authorization.name} ({authorization.job})
+
+ ))}
+
+
-
-
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/EngravedMessage.js b/tgui-next/packages/tgui/interfaces/EngravedMessage.js
index 8fa230483d..40d34d0427 100644
--- a/tgui-next/packages/tgui/interfaces/EngravedMessage.js
+++ b/tgui-next/packages/tgui/interfaces/EngravedMessage.js
@@ -1,10 +1,10 @@
import { decodeHtmlEntities } from 'common/string';
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Grid, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const EngravedMessage = props => {
- const { act, data } = useBackend(props);
+export const EngravedMessage = (props, context) => {
+ const { act, data } = useBackend(context);
const {
admin_mode,
creator_key,
@@ -18,81 +18,83 @@ export const EngravedMessage = props => {
realdate,
} = data;
return (
-
-
-
- {decodeHtmlEntities(hidden_message)}
-
-
-
- act('like')} />
-
-
- act('neutral')} />
-
-
- act('dislike')} />
-
-
-
-
-
- {!!admin_mode && (
- act('delete')} />
- )}>
+
+
+
+
+ {decodeHtmlEntities(hidden_message)}
+
+
+
+ act('like')} />
+
+
+ act('neutral')} />
+
+
+ act('dislike')} />
+
+
+
+
-
- {creator_key}
-
-
- {creator_name}
+
+ {realdate}
- )}
-
+
+ {!!admin_mode && (
+ act('delete')} />
+ )}>
+
+
+ {creator_key}
+
+
+ {creator_name}
+
+
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Gps.js b/tgui-next/packages/tgui/interfaces/Gps.js
index 752c76b6d8..a5fcb8d194 100644
--- a/tgui-next/packages/tgui/interfaces/Gps.js
+++ b/tgui-next/packages/tgui/interfaces/Gps.js
@@ -5,11 +5,12 @@ import { vecLength, vecSubtract } from 'common/vector';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Icon, LabeledList, Section, Table } from '../components';
+import { Window } from '../layouts';
const coordsToVec = coords => map(parseFloat)(coords.split(', '));
-export const Gps = props => {
- const { act, data } = useBackend(props);
+export const Gps = (props, context) => {
+ const { act, data } = useBackend(context);
const {
currentArea,
currentCoords,
@@ -36,87 +37,89 @@ export const Gps = props => {
signal => signal.entrytag),
])(data.signals || []);
return (
-
- act('power')} />
- )}>
-
-
+
+
+
-
- act('updating')} />
-
-
- act('globalmode')} />
-
-
-
- {!!power && (
-
-
-
- {currentArea} ({currentCoords})
-
-
-
-
-
-
-
-
-
- {signals.map(signal => (
-
-
- {signal.entrytag}
-
-
- {signal.degrees !== undefined && (
-
- )}
- {signal.dist !== undefined && (
- signal.dist + 'm'
- )}
-
-
- {signal.coords}
-
+ icon="power-off"
+ content={power ? "On" : "Off"}
+ selected={power}
+ onClick={() => act('power')} />
+ )}>
+
+
+ act('rename')} />
+
+
+ act('updating')} />
+
+
+ act('globalmode')} />
+
+
+
+ {!!power && (
+
+
+
+ {currentArea} ({currentCoords})
+
+
+
-
- )}
-
+ {signals.map(signal => (
+
+
+ {signal.entrytag}
+
+
+ {signal.degrees !== undefined && (
+
+ )}
+ {signal.dist !== undefined && (
+ signal.dist + 'm'
+ )}
+
+
+ {signal.coords}
+
+
+ ))}
+
+
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/GulagTeleporterConsole.js b/tgui-next/packages/tgui/interfaces/GulagTeleporterConsole.js
index 7291cedcbc..3b709cbcb0 100644
--- a/tgui-next/packages/tgui/interfaces/GulagTeleporterConsole.js
+++ b/tgui-next/packages/tgui/interfaces/GulagTeleporterConsole.js
@@ -1,10 +1,10 @@
-import { useBackend } from '../backend';
-import { Section, LabeledList, Button, NumberInput } from '../components';
import { Fragment } from 'inferno';
+import { useBackend } from '../backend';
+import { Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const GulagTeleporterConsole = props => {
- const { act, data } = useBackend(props);
-
+export const GulagTeleporterConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const {
teleporter,
teleporter_lock,
@@ -18,86 +18,82 @@ export const GulagTeleporterConsole = props => {
goal = 0,
prisoner = {},
} = data;
-
return (
-
-
- act('toggle_open')} />
- act('teleporter_lock')}
- />
-
- )}>
-
-
+
+
act('scan_teleporter')}
- />
- )}>
- {teleporter ? teleporter_location : 'Not Connected'}
-
- act('toggle_open')} />
act('scan_beacon')}
- />
- )}>
- {beacon ? beacon_location : 'Not Connected'}
-
-
-
-
-
-
- act('handle_id')}
- />
-
-
- act('set_goal', { value: value })}
- />
-
-
- {prisoner.name ? prisoner.name : "No Occupant"}
-
-
- {prisoner.crimstat ? prisoner.crimstat : 'No Status'}
-
-
-
- act('teleport')}
- />
-
+ icon={teleporter_lock ? 'lock' : 'unlock'}
+ content={teleporter_lock ? 'Locked' : 'Unlocked'}
+ selected={teleporter_lock}
+ disabled={teleporter_state_open}
+ onClick={() => act('teleporter_lock')} />
+
+ )}>
+
+ act('scan_teleporter')}
+ />
+ )}>
+ {teleporter ? teleporter_location : 'Not Connected'}
+
+ act('scan_beacon')} />
+ )}>
+ {beacon ? beacon_location : 'Not Connected'}
+
+
+
+
+
+
+ act('handle_id')} />
+
+
+ act('set_goal', { value })} />
+
+
+ {prisoner.name || 'No Occupant'}
+
+
+ {prisoner.crimstat || 'No Status'}
+
+
+
+ act('teleport')} />
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Holodeck.js b/tgui-next/packages/tgui/interfaces/Holodeck.js
index 0383d45b7f..bc664d2090 100644
--- a/tgui-next/packages/tgui/interfaces/Holodeck.js
+++ b/tgui-next/packages/tgui/interfaces/Holodeck.js
@@ -1,9 +1,9 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, Section } from '../components';
+import { Window } from '../layouts';
-export const Holodeck = props => {
- const { act, data } = useBackend(props);
+export const Holodeck = (props, context) => {
+ const { act, data } = useBackend(context);
const {
can_toggle_safety,
default_programs = [],
@@ -12,46 +12,48 @@ export const Holodeck = props => {
program,
} = data;
return (
-
- act('safety')} />
- )}>
- {default_programs.map(def_program => (
- act('load_program', {
- type: def_program.type,
- })} />
- ))}
-
- {!!emagged && (
-
- {emag_programs.map(emag_program => (
+
+
+ act('safety')} />
+ )}>
+ {default_programs.map(def_program => (
act('load_program', {
- type: emag_program.type,
+ type: def_program.type,
})} />
))}
- )}
-
+ {!!emagged && (
+
+ {emag_programs.map(emag_program => (
+ act('load_program', {
+ type: emag_program.type,
+ })} />
+ ))}
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ImplantChair.js b/tgui-next/packages/tgui/interfaces/ImplantChair.js
index a83cf79c1a..38da8c27be 100644
--- a/tgui-next/packages/tgui/interfaces/ImplantChair.js
+++ b/tgui-next/packages/tgui/interfaces/ImplantChair.js
@@ -1,71 +1,73 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, Icon, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const ImplantChair = props => {
- const { act, data } = useBackend(props);
+export const ImplantChair = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
-
-
- {data.occupant.name ? data.occupant.name : 'No Occupant'}
-
- {!!data.occupied && (
-
- {data.occupant.stat === 0
- ? 'Conscious'
- : data.occupant.stat === 1
- ? 'Unconcious'
- : 'Dead'}
+
+
+
+
+
+ {data.occupant.name || 'No Occupant'}
- )}
-
-
-
-
-
- act('door')} />
-
-
- act('implant')} />
- {data.ready === 0 && (
-
+ {!!data.occupied && (
+
+ {data.occupant.stat === 0
+ ? 'Conscious'
+ : data.occupant.stat === 1
+ ? 'Unconcious'
+ : 'Dead'}
+
)}
-
-
- {data.ready_implants}
- {data.replenishing === 1 && (
-
- )}
-
-
-
-
+
+
+
+
+
+ act('door')} />
+
+
+ act('implant')} />
+ {data.ready === 0 && (
+
+ )}
+
+
+ {data.ready_implants}
+ {data.replenishing === 1 && (
+
+ )}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Intellicard.js b/tgui-next/packages/tgui/interfaces/Intellicard.js
index 4dc3956e5a..99e653ed31 100644
--- a/tgui-next/packages/tgui/interfaces/Intellicard.js
+++ b/tgui-next/packages/tgui/interfaces/Intellicard.js
@@ -1,11 +1,9 @@
import { useBackend } from '../backend';
-import { Section, LabeledList, ProgressBar, Button, BlockQuote } from '../components';
-
-export const Intellicard = props => {
- const { act, data } = useBackend(props);
-
- const offline = (isDead || isBraindead);
+import { BlockQuote, Button, LabeledList, ProgressBar, Section } from '../components';
+import { Window } from '../layouts';
+export const Intellicard = (props, context) => {
+ const { act, data } = useBackend(context);
const {
name,
isDead,
@@ -16,57 +14,61 @@ export const Intellicard = props => {
wiping,
laws = [],
} = data;
-
+ const offline = isDead || isBraindead;
return (
- act('wipe')} />
- )}>
- {!!name && (
-
-
- {offline ? 'Offline' : 'Operation'}
-
-
-
-
-
+
+
+ act('wireless')} />
- act('radio')} />
-
-
- {laws.map(law => (
-
- {law}
-
- ))}
-
-
- )}
-
+ icon="trash"
+ content={wiping ? 'Stop Wiping' : 'Wipe'}
+ disabled={isDead}
+ onClick={() => act('wipe')} />
+ )}>
+ {!!name && (
+
+
+ {offline ? 'Offline' : 'Operation'}
+
+
+
+
+
+ act('wireless')} />
+ act('radio')} />
+
+
+ {laws.map(law => (
+
+ {law}
+
+ ))}
+
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/KeycardAuth.js b/tgui-next/packages/tgui/interfaces/KeycardAuth.js
index d1ebbdde3c..eb4dafeb28 100644
--- a/tgui-next/packages/tgui/interfaces/KeycardAuth.js
+++ b/tgui-next/packages/tgui/interfaces/KeycardAuth.js
@@ -1,53 +1,58 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Section } from '../components';
+import { Window } from '../layouts';
-export const KeycardAuth = props => {
- const { act, data } = useBackend(props);
+export const KeycardAuth = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
- {data.waiting === 1 && (
- Waiting for another device to confirm your request...
- )}
-
-
- {data.waiting === 0 && (
-
- {!!data.auth_required && (
- act('auth_swipe')}
- content="Authorize" />
+
+
+
+
+ {data.waiting === 1 && (
+ Waiting for another device to confirm your request...
)}
- {data.auth_required === 0 && (
+
+
+ {data.waiting === 0 && (
- {
- return act('red_alert');
- }}
- content="Red Alert" />
- act('emergency_maint')}
- content="Emergency Maintenance Access" />
- act('bsa_unlock')}
- content="Bluespace Artillery Unlock" />
+ {!!data.auth_required && (
+ act('auth_swipe')}
+ content="Authorize" />
+ )}
+ {data.auth_required === 0 && (
+
+ {
+ return act('red_alert');
+ }}
+ content="Red Alert" />
+ act('emergency_maint')}
+ content="Emergency Maintenance Access" />
+ act('bsa_unlock')}
+ content="Bluespace Artillery Unlock" />
+
+ )}
)}
-
- )}
-
-
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/LaborClaimConsole.js b/tgui-next/packages/tgui/interfaces/LaborClaimConsole.js
index 07a46b2cde..b3ee427844 100644
--- a/tgui-next/packages/tgui/interfaces/LaborClaimConsole.js
+++ b/tgui-next/packages/tgui/interfaces/LaborClaimConsole.js
@@ -1,10 +1,10 @@
import { toTitleCase } from 'common/string';
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section, Table } from '../components';
+import { Window } from '../layouts';
-export const LaborClaimConsole = props => {
- const { act, data } = useBackend(props);
+export const LaborClaimConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const {
can_go_home,
id_points,
@@ -13,57 +13,59 @@ export const LaborClaimConsole = props => {
unclaimed_points,
} = data;
return (
-
-
-
-
- {status_info}
-
-
- act('move_shuttle')} />
-
-
- {id_points}
-
-
+
+
+
+
+ {status_info}
+
+
act('claim_points')} />
- )}>
- {unclaimed_points}
-
-
-
-
-
-
-
- Material
-
-
- Value
-
-
- {ores.map(ore => (
-
+ content="Move shuttle"
+ disabled={!can_go_home}
+ onClick={() => act('move_shuttle')} />
+
+
+ {id_points}
+
+ act('claim_points')} />
+ )}>
+ {unclaimed_points}
+
+
+
+
+
+
- {toTitleCase(ore.ore)}
+ Material
-
- {ore.value}
-
+ Value
- ))}
-
-
-
+ {ores.map(ore => (
+
+
+ {toTitleCase(ore.ore)}
+
+
+
+ {ore.value}
+
+
+
+ ))}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/MechBayPowerConsole.js b/tgui-next/packages/tgui/interfaces/MechBayPowerConsole.js
index 11c4d2ec11..bed15d261e 100644
--- a/tgui-next/packages/tgui/interfaces/MechBayPowerConsole.js
+++ b/tgui-next/packages/tgui/interfaces/MechBayPowerConsole.js
@@ -1,58 +1,73 @@
import { useBackend } from '../backend';
import { AnimatedNumber, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
+import { Window } from '../layouts';
-export const MechBayPowerConsole = props => {
- const { act, data } = useBackend(props);
+export const MechBayPowerConsole = (props, context) => {
+ const { act, data } = useBackend(context);
const { recharge_port } = data;
const mech = recharge_port && recharge_port.mech;
const cell = mech && mech.cell;
return (
- act('reconnect')} />
- )}>
-
-
- {!recharge_port && (
- No power port detected. Please re-sync.
- ) || !mech && (
- No mech detected.
- ) || (
-
- )}
-
-
- {!recharge_port && (
- No power port detected. Please re-sync.
- ) || !mech && (
- No mech detected.
- ) || !cell && (
- No cell is installed.
- ) || (
-
-
- {' / ' + cell.maxcharge}
-
- )}
-
-
-
+
+
+ act('reconnect')} />
+ )}>
+
+
+ {!recharge_port && (
+
+ No power port detected. Please re-sync.
+
+ ) || !mech && (
+
+ No mech detected.
+
+ ) || (
+
+ )}
+
+
+ {!recharge_port && (
+
+ No power port detected. Please re-sync.
+
+ ) || !mech && (
+
+ No mech detected.
+
+ ) || !cell && (
+
+ No cell is installed.
+
+ ) || (
+
+
+ {' / ' + cell.maxcharge}
+
+ )}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/MedicalKiosk.js b/tgui-next/packages/tgui/interfaces/MedicalKiosk.js
index b3f3b9035b..8e221d169e 100644
--- a/tgui-next/packages/tgui/interfaces/MedicalKiosk.js
+++ b/tgui-next/packages/tgui/interfaces/MedicalKiosk.js
@@ -1,300 +1,358 @@
import { multiline } from 'common/string';
-import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
-import { AnimatedNumber, Box, Button, LabeledList, ProgressBar, Section, Tabs } from '../components';
+import { useBackend, useSharedState } from '../backend';
+import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from '../components';
+import { Window } from '../layouts';
-export const MedicalKiosk = props => {
- const { act, data } = useBackend(props);
+export const MedicalKiosk = (props, context) => {
+ const { act, data } = useBackend(context);
+ const [scanIndex] = useSharedState(context, 'scanIndex');
+ const {
+ active_status_1,
+ active_status_2,
+ active_status_3,
+ active_status_4,
+ } = data;
return (
-
-
-
- Greetings Valued Employee. Please select your desired diagnosis.
- Diagnosis costs {data.kiosk_cost} credits.
-
-
- Current patient targeted for scanning: {data.patient_name} |
-
-
- act('beginScan_1')}
- content="General Health Scan" />
- act('beginScan_2')}
- content="Symptom Based Checkup" />
- act('clearTarget')}
- content="Reset Scanner" />
-
-
- act('beginScan_3')}
- content="Neurological/Radiological Scan" />
- act('beginScan_4')}
- content="Chemical Analysis and Psychoactive Scan" />
-
-
-
-
- {() => (
-
- {data.active_status_1 === 0 && (
-
-
-
-
- %
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
-
- )}
-
-
- {() => (
-
- {data.active_status_2 === 0 && (
-
-
-
- {data.patient_status}
-
-
-
- {data.patient_illness}
-
-
- {data.illness_info}
-
-
-
- {data.bleed_status}
-
-
-
-
-
-
- {data.blood_status}
-
-
-
- )}
-
- )}
-
-
- {() => (
-
- {data.active_status_3 === 0 && (
-
-
-
-
-
-
-
-
-
-
-
-
- {data.brain_health}
-
-
- {data.rad_status}
-
-
- {data.rad_value}%
-
-
- {data.trauma_status}
-
-
- )}
-
- )}
-
-
- {() => (
-
- {data.active_status_4 === 0 && (
-
-
- {data.are_chems_present ? (
- data.chemical_list.length ? (
- data.chemical_list.map(specificChem => (
-
- {specificChem.volume} units of {specificChem.name}
-
- ))
- ) : (
-
- No reagents detected.
-
- )
- ) : (
-
- No reagents detected.
-
- )}
-
-
- {data.are_overdoses_present ? (
- data.overdose_status.length ? (
- data.overdose_status.map(specificOD => (
-
- Overdosing on {specificOD.name}
-
- ))
- ) : (
-
- No reagents detected.
-
- )
- ) : (
-
- Patient is not overdosing.
-
- )}
-
-
- {data.are_addictions_present ? (
- data.addiction_status.length ? (
- data.addiction_status.map(specificAddict => (
-
- Addicted to {specificAddict.name}
-
- ))
- ) : (
-
- Patient has no addictions.
-
- )
- ) : (
-
- Patient has no addictions detected.
-
- )}
-
-
- {data.hallucinating_status}
-
-
- )}
-
- )}
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {!!active_status_1 && scanIndex === 1 && (
+
+ )}
+ {!!active_status_2 && scanIndex === 2 && (
+
+ )}
+ {!!active_status_3 && scanIndex === 3 && (
+
+ )}
+ {!!active_status_4 && scanIndex === 4 && (
+
+ )}
+
+
+ );
+};
+
+const MedicalKioskScanButton = (props, context) => {
+ const {
+ index,
+ name,
+ description,
+ icon,
+ } = props;
+ const { act, data } = useBackend(context);
+ const [scanIndex, setScanIndex] = useSharedState(context, 'scanIndex');
+ const paid = data[`active_status_${index}`];
+ return (
+
+
+
+
+
+ {
+ act(`beginScan_${index}`);
+ setScanIndex(index);
+ }} />
+
+
+ );
+};
+
+const MedicalKioskInstructions = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ kiosk_cost,
+ patient_name,
+ } = data;
+ return (
+
+
+ Greetings Valued Employee! Please select a desired automatic
+ health check procedure. Diagnosis costs {kiosk_cost} credits.
+
+
+
+ Patient:
+
+ {patient_name}
+
+ act('clearTarget')}
+ content="Reset Scanner" />
+
+ );
+};
+
+const MedicalKioskScanResults1 = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ patient_health,
+ brute_health,
+ burn_health,
+ suffocation_health,
+ toxin_health,
+ } = data;
+ return (
+
+
+
+
+ %
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const MedicalKioskScanResults2 = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ patient_status,
+ patient_illness,
+ illness_info,
+ bleed_status,
+ blood_levels,
+ blood_status,
+ } = data;
+ return (
+
+
+
+ {patient_status}
+
+
+
+ {patient_illness}
+
+
+ {illness_info}
+
+
+
+
+
+
+
+ {bleed_status}
+
+
+
+ {blood_status}
+
+
+
+ );
+};
+
+const MedicalKioskScanResults3 = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ clone_health,
+ brain_damage,
+ brain_health,
+ rad_status,
+ rad_value,
+ trauma_status,
+ } = data;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {brain_health}
+
+
+ {trauma_status}
+
+
+
+ {rad_status}
+
+
+ {rad_value}%
+
+
+
+ );
+};
+
+const MedicalKioskScanResults4 = (props, context) => {
+ const { data } = useBackend(context);
+ const {
+ chemical_list = [],
+ overdose_list = [],
+ addict_list = [],
+ hallucinating_status,
+ } = data;
+ return (
+
+
+
+ {chemical_list.length === 0 && (
+
+ No reagents detected.
+
+ )}
+ {chemical_list.map(chem => (
+
+ {chem.volume} units of {chem.name}
+
+ ))}
+
+
+ {overdose_list.length === 0 && (
+
+ Patient is not overdosing.
+
+ )}
+ {overdose_list.map(chem => (
+
+ Overdosing on {chem.name}
+
+ ))}
+
+
+ {addict_list.length === 0 && (
+
+ Patient has no addictions.
+
+ )}
+ {addict_list.map(chem => (
+
+ Addicted to {chem.name}
+
+ ))}
+
+
+ {hallucinating_status}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Mule.js b/tgui-next/packages/tgui/interfaces/Mule.js
index d4dfddd40e..b6ae6c1f24 100644
--- a/tgui-next/packages/tgui/interfaces/Mule.js
+++ b/tgui-next/packages/tgui/interfaces/Mule.js
@@ -1,15 +1,12 @@
-import { useBackend } from '../backend';
-import { Section, LabeledList, Button, NumberInput, ProgressBar, Grid, Input, Dropdown } from '../components';
import { Fragment } from 'inferno';
+import { useBackend } from '../backend';
+import { Button, Dropdown, Grid, Input, LabeledList, ProgressBar, Section, Flex } from '../components';
+import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
-export const Mule = props => {
- const { act, data } = useBackend(props);
-
- const locked = data.locked && !data.siliconUser;
-
+export const Mule = (props, context) => {
+ const { act, data } = useBackend(context);
const {
- siliconUser,
on,
cell,
cellPercent,
@@ -25,123 +22,116 @@ export const Mule = props => {
id,
destinations = [],
} = data;
-
+ const locked = data.locked && !data.siliconUser;
return (
-
-
- act('power')}
- />
- )} >
-
-
-
-
-
- {mode}
-
-
-
-
-
-
- {load || 'None'}
-
-
-
-
-
- {!locked && (
+
+
+
- {!!load && (
- act('unload')} />
- )}
- {!!haspai && (
- act('ejectpai')} />
- )}
-
+ title="Status"
+ minHeight="110px"
+ buttons={!locked && (
+ act('power')} />
)}>
-
-
- act('setid', { value: value })}
- />
-
-
- act('destination', { value: val })}
- />
- act('stop')} />
- act('go')} />
-
-
- act('destination', { value: val })} />
- act('home')} />
-
-
- act('autored')} />
-
- act('autopick')}
- />
-
- act('report')}
- />
-
-
+
+
+
+
+
+ {mode}
+
+
+
+
+
+
+ {load || 'None'}
+
+
+
+
- )}
-
+ {!locked && (
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NaniteChamberControl.js b/tgui-next/packages/tgui/interfaces/NaniteChamberControl.js
index e18525c98c..2d16c643b2 100644
--- a/tgui-next/packages/tgui/interfaces/NaniteChamberControl.js
+++ b/tgui-next/packages/tgui/interfaces/NaniteChamberControl.js
@@ -1,9 +1,20 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Collapsible, Grid, LabeledList, NoticeBox, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const NaniteChamberControl = props => {
- const { act, data } = useBackend(props);
+export const NaniteChamberControl = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
+
+export const NaniteChamberControlContent = (props, context) => {
+ const { act, data } = useBackend(context);
const {
status_msg,
locked,
diff --git a/tgui-next/packages/tgui/interfaces/NaniteCloudControl.js b/tgui-next/packages/tgui/interfaces/NaniteCloudControl.js
index 3f93fe1bf9..40f3dd3b21 100644
--- a/tgui-next/packages/tgui/interfaces/NaniteCloudControl.js
+++ b/tgui-next/packages/tgui/interfaces/NaniteCloudControl.js
@@ -1,10 +1,10 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Collapsible, Grid, LabeledList, NoticeBox, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const NaniteDiskBox = props => {
- const { state } = props;
- const { data } = state;
+export const NaniteDiskBox = (props, context) => {
+ const { data } = useBackend(context);
const {
has_disk,
has_program,
@@ -32,7 +32,7 @@ export const NaniteDiskBox = props => {
);
};
-export const NaniteInfoBox = props => {
+export const NaniteInfoBox = (props, context) => {
const { program } = props;
const {
@@ -162,8 +162,8 @@ export const NaniteInfoBox = props => {
);
};
-export const NaniteCloudBackupList = props => {
- const { act, data } = useBackend(props);
+export const NaniteCloudBackupList = (props, context) => {
+ const { act, data } = useBackend(context);
const cloud_backups = data.cloud_backups || [];
return (
cloud_backups.map(backup => (
@@ -179,8 +179,8 @@ export const NaniteCloudBackupList = props => {
);
};
-export const NaniteCloudBackupDetails = props => {
- const { act, data } = useBackend(props);
+export const NaniteCloudBackupDetails = (props, context) => {
+ const { act, data } = useBackend(context);
const {
current_view,
disk,
@@ -271,9 +271,8 @@ export const NaniteCloudBackupDetails = props => {
);
};
-export const NaniteCloudControl = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const NaniteCloudControl = (props, context) => {
+ const { act, data } = useBackend(context);
const {
has_disk,
current_view,
@@ -281,52 +280,54 @@ export const NaniteCloudControl = props => {
} = data;
return (
-
- act('eject')} />
- )}>
-
-
-
+
+ act('set_view', {
- view: 0,
- })} />
- ) : (
-
- {"New Backup: "}
- act('update_new_backup_value', {
- value: value,
- })} />
+ icon="eject"
+ content="Eject"
+ disabled={!has_disk}
+ onClick={() => act('eject')} />
+ )}>
+
+
+ act('create_backup')} />
-
- )
- )}>
- {!data.current_view ? (
-
- ) : (
-
- )}
-
-
+ icon="arrow-left"
+ content="Return"
+ onClick={() => act('set_view', {
+ view: 0,
+ })} />
+ ) : (
+
+ {"New Backup: "}
+ act('update_new_backup_value', {
+ value: value,
+ })} />
+ act('create_backup')} />
+
+ )
+ )}>
+ {!data.current_view ? (
+
+ ) : (
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NaniteProgramHub.js b/tgui-next/packages/tgui/interfaces/NaniteProgramHub.js
index b035b02bdf..198fad0b83 100644
--- a/tgui-next/packages/tgui/interfaces/NaniteProgramHub.js
+++ b/tgui-next/packages/tgui/interfaces/NaniteProgramHub.js
@@ -1,10 +1,11 @@
import { map } from 'common/collections';
import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
-import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
+import { useBackend, useSharedState } from '../backend';
+import { Button, Flex, LabeledList, NoticeBox, Section, Tabs } from '../components';
+import { Window } from '../layouts';
-export const NaniteProgramHub = props => {
- const { act, data } = useBackend(props);
+export const NaniteProgramHub = (props, context) => {
+ const { act, data } = useBackend(context);
const {
detail_view,
disk,
@@ -12,74 +13,111 @@ export const NaniteProgramHub = props => {
has_program,
programs = {},
} = data;
+ const [
+ selectedCategory,
+ setSelectedCategory,
+ ] = useSharedState(context, 'category');
+ const programsInCategory = programs
+ && programs[selectedCategory]
+ || [];
return (
-
-
- act('eject')} />
- act('clear')} />
-
- )}>
- {has_disk ? (
- has_program ? (
-
-
- {disk.name}
-
-
- {disk.desc}
-
-
+
+
+
+ act('eject')} />
+ act('clear')} />
+
+ )}>
+ {has_disk ? (
+ has_program ? (
+
+
+ {disk.name}
+
+
+ {disk.desc}
+
+
+ ) : (
+
+ No Program Installed
+
+ )
) : (
- No Program Installed
+ Insert Disk
- )
- ) : (
-
- Insert Disk
-
- )}
-
-
- act('toggle_details')} />
- act('refresh')} />
-
- )}>
- {programs !== null ? (
-
- {map((cat_contents, category) => {
- const progs = cat_contents || [];
- // Backend was sending stupid data that would have been
- // annoying to fix
- const tabLabel = category.substring(0, category.length - 8);
- return (
-
- {detail_view ? (
- progs.map(program => (
-
+
+ act('toggle_details')} />
+ act('refresh')} />
+
+ )}>
+ {programs !== null ? (
+
+
+
+ {map((cat_contents, category) => {
+ const progs = cat_contents || [];
+ // Backend was sending stupid data that would have been
+ // annoying to fix
+ const tabLabel = category
+ .substring(0, category.length - 8);
+ return (
+ setSelectedCategory(category)}>
+ {tabLabel}
+
+ );
+ })(programs)}
+
+
+
+ {detail_view ? (
+ programsInCategory.map(program => (
+ act('download', {
+ program_id: program.id,
+ })} />
+ )}>
+ {program.desc}
+
+ ))
+ ) : (
+
+ {programsInCategory.map(program => (
+ {
onClick={() => act('download', {
program_id: program.id,
})} />
- )}>
- {program.desc}
-
- ))
- ) : (
-
- {progs.map(program => (
- act('download', {
- program_id: program.id,
- })} />
- )} />
- ))}
-
- )}
-
- );
- })(programs)}
-
- ) : (
-
- No nanite programs are currently researched.
-
- )}
-
-
+ )} />
+ ))}
+
+ )}
+
+
+ ) : (
+
+ No nanite programs are currently researched.
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NaniteProgrammer.js b/tgui-next/packages/tgui/interfaces/NaniteProgrammer.js
index 470363925d..1c9f7fdebb 100644
--- a/tgui-next/packages/tgui/interfaces/NaniteProgrammer.js
+++ b/tgui-next/packages/tgui/interfaces/NaniteProgrammer.js
@@ -1,9 +1,11 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, Dropdown, Grid, Input, LabeledList, NoticeBox, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
+
+export const NaniteCodes = (props, context) => {
+ const { act, data } = useBackend(context);
-export const NaniteCodes = props => {
- const { act, data } = useBackend(props);
return (
{
);
};
-export const NaniteDelays = props => {
- const { act, data } = useBackend(props);
+export const NaniteDelays = (props, context) => {
+ const { act, data } = useBackend(context);
return (
{
);
};
-export const NaniteExtraEntry = props => {
- const { act, extra_setting } = props;
+export const NaniteExtraEntry = (props, context) => {
+ const { extra_setting } = props;
const {
name,
type,
} = extra_setting;
const typeComponentMap = {
- number: ,
- text: ,
- type: ,
- boolean: ,
+ number: ,
+ text: ,
+ type: ,
+ boolean: ,
};
return (
@@ -142,8 +144,9 @@ export const NaniteExtraEntry = props => {
);
};
-export const NaniteExtraNumber = props => {
- const { act, extra_setting } = props;
+export const NaniteExtraNumber = (props, context) => {
+ const { extra_setting } = props;
+ const { act } = useBackend(context);
const {
name,
value,
@@ -165,8 +168,9 @@ export const NaniteExtraNumber = props => {
);
};
-export const NaniteExtraText = props => {
- const { act, extra_setting } = props;
+export const NaniteExtraText = (props, context) => {
+ const { extra_setting } = props;
+ const { act } = useBackend(context);
const {
name,
value,
@@ -182,8 +186,9 @@ export const NaniteExtraText = props => {
);
};
-export const NaniteExtraType = props => {
- const { act, extra_setting } = props;
+export const NaniteExtraType = (props, context) => {
+ const { extra_setting } = props;
+ const { act } = useBackend(context);
const {
name,
value,
@@ -202,8 +207,9 @@ export const NaniteExtraType = props => {
);
};
-export const NaniteExtraBoolean = props => {
- const { act, extra_setting } = props;
+export const NaniteExtraBoolean = (props, context) => {
+ const { extra_setting } = props;
+ const { act } = useBackend(context);
const {
name,
value,
@@ -220,8 +226,18 @@ export const NaniteExtraBoolean = props => {
);
};
-export const NaniteProgrammer = props => {
- const { act, data } = useBackend(props);
+export const NaniteProgrammer = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
+
+export const NaniteProgrammerContent = (props, context) => {
+ const { act, data } = useBackend(context);
const {
has_disk,
has_program,
@@ -306,10 +322,10 @@ export const NaniteProgrammer = props => {
)}>
-
+
-
+
{!!has_extra_settings && (
@@ -320,7 +336,6 @@ export const NaniteProgrammer = props => {
{extra_settings.map(setting => (
))}
diff --git a/tgui-next/packages/tgui/interfaces/NotificationPreferences.js b/tgui-next/packages/tgui/interfaces/NotificationPreferences.js
index 7ef1e4801e..b46e12504c 100644
--- a/tgui-next/packages/tgui/interfaces/NotificationPreferences.js
+++ b/tgui-next/packages/tgui/interfaces/NotificationPreferences.js
@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Section, Button } from '../components';
+import { Window } from '../layouts';
-export const NotificationPreferences = props => {
- const { act, data } = useBackend(props);
+export const NotificationPreferences = (props, context) => {
+ const { act, data } = useBackend(context);
const ignoresPreSort = data.ignore || [];
const ignores = ignoresPreSort.sort((a, b) => {
@@ -18,16 +19,20 @@ export const NotificationPreferences = props => {
});
return (
-
- {ignores.map(ignore => (
- act('toggle_ignore', { key: ignore.key })} />
- ))}
-
+
+
+
+ {ignores.map(ignore => (
+ act('toggle_ignore', { key: ignore.key })} />
+ ))}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NtnetRelay.js b/tgui-next/packages/tgui/interfaces/NtnetRelay.js
index 98a9b097f6..69d6b4dd8d 100644
--- a/tgui-next/packages/tgui/interfaces/NtnetRelay.js
+++ b/tgui-next/packages/tgui/interfaces/NtnetRelay.js
@@ -1,8 +1,9 @@
import { useBackend } from '../backend';
import { Box, Button, ProgressBar, Section, AnimatedNumber } from '../components';
+import { Window } from '../layouts';
-export const NtnetRelay = props => {
- const { act, data } = useBackend(props);
+export const NtnetRelay = (props, context) => {
+ const { act, data } = useBackend(context);
const {
enabled,
@@ -12,55 +13,59 @@ export const NtnetRelay = props => {
} = data;
return (
- act('toggle')}
- />
- )}>
- {!dos_crashed ? (
-
- GQ
- {' / '}
- {dos_capacity} GQ
-
- ) : (
-
-
- NETWORK BUFFER OVERFLOW
-
-
- OVERLOAD RECOVERY MODE
-
-
- This system is suffering temporary outage due to overflow
- of traffic buffers. Until buffered traffic is processed,
- all further requests will be dropped. Frequent occurences
- of this error may indicate insufficient hardware capacity
- of your network. Please contact your network planning
- department for instructions on how to resolve this issue.
-
-
- ADMINISTRATOR OVERRIDE
-
-
- CAUTION - DATA LOSS MAY OCCUR
-
- act('restart')} />
-
- )}
-
+
+
+ act('toggle')}
+ />
+ )}>
+ {!dos_crashed ? (
+
+ GQ
+ {' / '}
+ {dos_capacity} GQ
+
+ ) : (
+
+
+ NETWORK BUFFER OVERFLOW
+
+
+ OVERLOAD RECOVERY MODE
+
+
+ This system is suffering temporary outage due to overflow
+ of traffic buffers. Until buffered traffic is processed,
+ all further requests will be dropped. Frequent occurences
+ of this error may indicate insufficient hardware capacity
+ of your network. Please contact your network planning
+ department for instructions on how to resolve this issue.
+
+
+ ADMINISTRATOR OVERRIDE
+
+
+ CAUTION - DATA LOSS MAY OCCUR
+
+ act('restart')} />
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NtosNetChat.js b/tgui-next/packages/tgui/interfaces/NtosNetChat.js
index e6b2ff5f9d..7c5c4656b6 100644
--- a/tgui-next/packages/tgui/interfaces/NtosNetChat.js
+++ b/tgui-next/packages/tgui/interfaces/NtosNetChat.js
@@ -1,12 +1,10 @@
import { useBackend } from '../backend';
-import { AnimatedNumber, Box, Button, Grid, LabeledList, ProgressBar, Section, Input, Table, Icon, Flex } from '../components';
+import { Box, Button, Section, Input, Table, Icon } from '../components';
import { Fragment } from 'inferno';
-import { createLogger } from '../logging';
+import { NtosWindow } from '../layouts';
-const logger = createLogger('ntos chat');
-
-export const NtosNetChat = props => {
- const { act, data } = useBackend(props);
+export const NtosNetChat = (props, context) => {
+ const { act, data } = useBackend(context);
const {
can_admin,
@@ -24,147 +22,151 @@ export const NtosNetChat = props => {
const authorized = (authed || adminmode);
return (
-
-
-
-
-
- act('PRG_newchannel', {
- new_channel_name: value,
- })} />
- {all_channels.map(channel => (
- act('PRG_joinchannel', {
- id: channel.id,
- })} />
- ))}
-
- act('PRG_changename', {
- new_name: value,
- })} />
- {!!can_admin && (
- act('PRG_toggleadmin')} />
- )}
-
-
-
- {in_channel && (
- authorized ? (
- messages.map(message => (
-
- {message.msg}
-
- ))
- ) : (
-
-
-
- THIS CHANNEL IS PASSWORD PROTECTED
-
-
- INPUT PASSWORD TO ACCESS
-
-
- )
- )}
-
-
- act('PRG_speak', {
- message: value,
- })} />
-
-
-
- {clients.map(client => (
-
- {client.name}
+
+
+
+
+
+
+
+ act('PRG_newchannel', {
+ new_channel_name: value,
+ })} />
+ {all_channels.map(channel => (
+ act('PRG_joinchannel', {
+ id: channel.id,
+ })} />
+ ))}
- ))}
-
- {(in_channel && authorized) && (
-
act('PRG_savelog', {
- log_name: value,
- })} />
- act('PRG_leavechannel')} />
-
- )}
- {!!is_operator && authed && (
-
- act('PRG_deletechannel')} />
- act('PRG_renamechannel', {
+ mt={1}
+ content={username + '...'}
+ currentValue={username}
+ onCommit={(e, value) => act('PRG_changename', {
new_name: value,
})} />
- act('PRG_toggleadmin')} />
+ )}
+
+
+
+ {in_channel && (
+ authorized ? (
+ messages.map(message => (
+
+ {message.msg}
+
+ ))
+ ) : (
+
+
+
+ THIS CHANNEL IS PASSWORD PROTECTED
+
+
+ INPUT PASSWORD TO ACCESS
+
+
+ )
+ )}
+
+
+ act('PRG_setpassword', {
- new_password: value,
+ selfClear
+ mt={1}
+ onEnter={(e, value) => act('PRG_speak', {
+ message: value,
})} />
-
- )}
-
-
-
-
+
+
+
+ {clients.map(client => (
+
+ {client.name}
+
+ ))}
+
+ {(in_channel && authorized) && (
+
+ act('PRG_savelog', {
+ log_name: value,
+ })} />
+ act('PRG_leavechannel')} />
+
+ )}
+ {!!is_operator && authed && (
+
+ act('PRG_deletechannel')} />
+ act('PRG_renamechannel', {
+ new_name: value,
+ })} />
+ act('PRG_setpassword', {
+ new_password: value,
+ })} />
+
+ )}
+
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/NtosNetDownloader.js b/tgui-next/packages/tgui/interfaces/NtosNetDownloader.js
index da3359fb87..e1f8871167 100644
--- a/tgui-next/packages/tgui/interfaces/NtosNetDownloader.js
+++ b/tgui-next/packages/tgui/interfaces/NtosNetDownloader.js
@@ -1,10 +1,9 @@
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Flex, Icon, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
+import { NtosWindow } from '../layouts';
-export const NtosNetDownloader = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const NtosNetDownloader = (props, context) => {
+ const { act, data } = useBackend(context);
const {
disk_size,
disk_used,
@@ -14,58 +13,58 @@ export const NtosNetDownloader = props => {
hackedavailable,
} = data;
return (
-
- {!!error && (
-
-
- {error}
-
- act('PRG_reseterror')} />
-
- )}
-
-
-
-
- {`${disk_used} GQ / ${disk_size} GQ`}
-
-
-
-
-
- {downloadable_programs.map(program => (
-
- ))}
-
- {!!hackedavailable && (
-
-
- Please note that Nanotrasen does not recommend download
- of software from non-official servers.
+
+
+ {!!error && (
+
+
+ {error}
+
+ act('PRG_reseterror')} />
- {hacked_programs.map(program => (
+ )}
+
+
+
+
+ {`${disk_used} GQ / ${disk_size} GQ`}
+
+
+
+
+
+ {downloadable_programs.map(program => (
))}
- )}
-
+ {!!hackedavailable && (
+
+
+ Please note that Nanotrasen does not recommend download
+ of software from non-official servers.
+
+ {hacked_programs.map(program => (
+
+ ))}
+
+ )}
+
+
);
};
-const Program = props => {
+const Program = (props, context) => {
const { program } = props;
- const { act, data } = useBackend(props);
+ const { act, data } = useBackend(context);
const {
disk_size,
disk_used,
diff --git a/tgui-next/packages/tgui/interfaces/NuclearBomb.js b/tgui-next/packages/tgui/interfaces/NuclearBomb.js
index 419fa7473e..a708602469 100644
--- a/tgui-next/packages/tgui/interfaces/NuclearBomb.js
+++ b/tgui-next/packages/tgui/interfaces/NuclearBomb.js
@@ -1,13 +1,14 @@
import { classes } from 'common/react';
import { useBackend } from '../backend';
import { Box, Button, Flex, Grid, Icon } from '../components';
+import { Window } from '../layouts';
// This ui is so many manual overrides and !important tags
// and hand made width sets that changing pretty much anything
// is going to require a lot of tweaking it get it looking correct again
// I'm sorry, but it looks bangin
-const NukeKeypad = props => {
- const { act } = useBackend(props);
+const NukeKeypad = (props, context) => {
+ const { act } = useBackend(context);
const keypadKeys = [
['1', '4', '7', 'C'],
['2', '5', '8', '0'],
@@ -43,9 +44,8 @@ const NukeKeypad = props => {
);
};
-export const NuclearBomb = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const NuclearBomb = (props, context) => {
+ const { act, data } = useBackend(context);
const {
anchored,
disk_present,
@@ -53,69 +53,73 @@ export const NuclearBomb = props => {
status2,
} = data;
return (
-
-
- {status1}
-
-
-
-
- {status2}
+
+
+
+
+ {status1}
-
-
- act('eject_disk')} />
-
-
-
-
-
-
-
-
- act('arm')} />
- act('anchor')} />
-
-
-
-
-
-
-
-
+
+
+
+ {status2}
+
+
+
+ act('eject_disk')} />
+
+
+
+
+
+
+
+
+ act('arm')} />
+ act('anchor')} />
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/OreBox.js b/tgui-next/packages/tgui/interfaces/OreBox.js
index 2abd7182f7..fedf59beff 100644
--- a/tgui-next/packages/tgui/interfaces/OreBox.js
+++ b/tgui-next/packages/tgui/interfaces/OreBox.js
@@ -1,52 +1,53 @@
import { toTitleCase } from 'common/string';
-import { Fragment } from 'inferno';
-import { act } from '../byond';
import { Box, Button, Section, Table } from '../components';
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
-export const OreBox = props => {
- const { state } = props;
- const { config, data } = state;
- const { ref } = config;
+export const OreBox = (props, context) => {
+ const { act, data } = useBackend(context);
const { materials } = data;
return (
-
- act(ref, 'removeall')} />
- )}>
-
-
-
- Ore
-
-
- Amount
-
-
- {materials.map(material => (
-
+
+
+ act('removeall')} />
+ )}>
+
+
- {toTitleCase(material.name)}
+ Ore
-
- {material.amount}
-
+ Amount
- ))}
-
-
-
-
- All ores will be placed in here when you are wearing a mining stachel
- on your belt or in a pocket while dragging the ore box.
- Gibtonite is not accepted.
-
-
-
+ {materials.map(material => (
+
+
+ {toTitleCase(material.name)}
+
+
+
+ {material.amount}
+
+
+
+ ))}
+
+
+
+
+ All ores will be placed in here when you are wearing a mining
+ stachel on your belt or in a pocket while dragging the ore box.
+
+ Gibtonite is not accepted.
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/OreRedemptionMachine.js b/tgui-next/packages/tgui/interfaces/OreRedemptionMachine.js
index 45cee75bc8..cccf75ebeb 100644
--- a/tgui-next/packages/tgui/interfaces/OreRedemptionMachine.js
+++ b/tgui-next/packages/tgui/interfaces/OreRedemptionMachine.js
@@ -2,9 +2,10 @@ import { toTitleCase } from 'common/string';
import { Component, Fragment } from 'inferno';
import { useBackend } from '../backend';
import { BlockQuote, Box, Button, NumberInput, Section, Table } from '../components';
+import { Window } from '../layouts';
-export const OreRedemptionMachine = props => {
- const { act, data } = useBackend(props);
+export const OreRedemptionMachine = (props, context) => {
+ const { act, data } = useBackend(context);
const {
unclaimedPoints,
materials,
@@ -13,85 +14,87 @@ export const OreRedemptionMachine = props => {
hasDisk,
} = data;
return (
-
-
-
- This machine only accepts ore.
- Gibtonite and Slag are not accepted.
-
-
-
- Unclaimed points:
-
- {unclaimedPoints}
- act('Claim')} />
-
-
-
- {hasDisk && (
-
-
- act('diskEject')} />
+
+
+
+
+ This machine only accepts ore.
+ Gibtonite and Slag are not accepted.
+
+
+
+ Unclaimed points:
-
- {diskDesigns.map(design => (
-
-
- File {design.index}: {design.name}
-
-
- act('diskUpload', {
- design: design.index,
- })} />
-
-
- ))}
-
-
- ) || (
- act('diskInsert')} />
- )}
-
-
-
- {materials.map(material => (
- act('Release', {
- id: material.id,
- sheets: amount,
- })} />
- ))}
-
-
-
-
- {alloys.map(material => (
- act('Smelt', {
- id: material.id,
- sheets: amount,
- })} />
- ))}
-
-
-
+ {unclaimedPoints}
+ act('Claim')} />
+
+
+
+ {hasDisk && (
+
+
+ act('diskEject')} />
+
+
+ {diskDesigns.map(design => (
+
+
+ File {design.index}: {design.name}
+
+
+ act('diskUpload', {
+ design: design.index,
+ })} />
+
+
+ ))}
+
+
+ ) || (
+ act('diskInsert')} />
+ )}
+
+
+
+ {materials.map(material => (
+ act('Release', {
+ id: material.id,
+ sheets: amount,
+ })} />
+ ))}
+
+
+
+
+ {alloys.map(material => (
+ act('Smelt', {
+ id: material.id,
+ sheets: amount,
+ })} />
+ ))}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Pandemic.js b/tgui-next/packages/tgui/interfaces/Pandemic.js
index 8e3b8a7860..50555525f5 100644
--- a/tgui-next/packages/tgui/interfaces/Pandemic.js
+++ b/tgui-next/packages/tgui/interfaces/Pandemic.js
@@ -2,9 +2,10 @@ import { map } from 'common/collections';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Collapsible, Grid, Input, LabeledList, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
-export const PandemicBeakerDisplay = props => {
- const { act, data } = useBackend(props);
+export const PandemicBeakerDisplay = (props, context) => {
+ const { act, data } = useBackend(context);
const {
has_beaker,
@@ -68,8 +69,8 @@ export const PandemicBeakerDisplay = props => {
);
};
-export const PandemicDiseaseDisplay = props => {
- const { act, data } = useBackend(props);
+export const PandemicDiseaseDisplay = (props, context) => {
+ const { act, data } = useBackend(context);
const {
is_ready,
@@ -169,7 +170,7 @@ export const PandemicDiseaseDisplay = props => {
);
};
-export const PandemicSymptomDisplay = props => {
+export const PandemicSymptomDisplay = (props, context) => {
const { symptom } = props;
const {
name,
@@ -242,8 +243,8 @@ export const PandemicSymptomDisplay = props => {
};
-export const PandemicAntibodyDisplay = props => {
- const { act, data } = useBackend(props);
+export const PandemicAntibodyDisplay = (props, context) => {
+ const { act, data } = useBackend(context);
const resistances = data.resistances || [];
@@ -277,18 +278,20 @@ export const PandemicAntibodyDisplay = props => {
);
};
-export const Pandemic = props => {
- const { data } = useBackend(props);
+export const Pandemic = (props, context) => {
+ const { data } = useBackend(context);
return (
-
-
- {!!data.has_blood && (
-
-
-
-
- )}
-
+
+
+
+ {!!data.has_blood && (
+
+
+
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/PortableGenerator.js b/tgui-next/packages/tgui/interfaces/PortableGenerator.js
index b29c71f38f..5a1b0cd46c 100644
--- a/tgui-next/packages/tgui/interfaces/PortableGenerator.js
+++ b/tgui-next/packages/tgui/interfaces/PortableGenerator.js
@@ -1,92 +1,93 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
+import { Window } from '../layouts';
-export const PortableGenerator = props => {
- const { act, data } = useBackend(props);
- let stackPercentState;
- if (data.stack_percent > 50) {
- stackPercentState = 'good';
- }
- else if (data.stack_percent > 15) {
- stackPercentState = 'average';
- }
- else {
- stackPercentState = 'bad';
- }
+export const PortableGenerator = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ stack_percent,
+ } = data;
+ const stackPercentState = (
+ stack_percent > 50 && 'good'
+ || stack_percent > 15 && 'average'
+ || 'bad'
+ );
return (
-
- {!data.anchored && (
- Generator not anchored.
- )}
-
-
-
- act('toggle_power')}
- disabled={!data.ready_to_boot}>
- {data.active ? 'On' : 'Off'}
-
-
-
- {data.sheets}
- {(data.sheets >= 1) && (
+
+
+ {!data.anchored && (
+ Generator not anchored.
+ )}
+
+
+
act('eject')}>
- Eject
+ icon={data.active ? 'power-off' : 'times'}
+ onClick={() => act('toggle_power')}
+ disabled={!data.ready_to_boot}>
+ {data.active ? 'On' : 'Off'}
- )}
-
-
-
-
-
- {data.current_heat < 100 ? (
- Nominal
- ) : (
- data.current_heat < 200 ? (
- Caution
+
+
+ {data.sheets}
+ {(data.sheets >= 1) && (
+ act('eject')}>
+ Eject
+
+ )}
+
+
+
+
+
+ {data.current_heat < 100 ? (
+ Nominal
) : (
- DANGER
- )
- )}
-
-
-
-
-
-
- {data.power_output}
-
-
- act('lower_power')}>
- {data.power_generated}
-
- act('higher_power')}>
- {data.power_generated}
-
-
-
-
- {data.connected ? data.power_available : "Unconnected"}
-
-
-
-
-
+ data.current_heat < 200 ? (
+ Caution
+ ) : (
+ DANGER
+ )
+ )}
+
+
+
+
+
+
+ {data.power_output}
+
+
+ act('lower_power')}>
+ {data.power_generated}
+
+ act('higher_power')}>
+ {data.power_generated}
+
+
+
+
+ {data.connected ? data.power_available : "Unconnected"}
+
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Radio.js b/tgui-next/packages/tgui/interfaces/Radio.js
index e335cf1cac..68b48d0230 100644
--- a/tgui-next/packages/tgui/interfaces/Radio.js
+++ b/tgui-next/packages/tgui/interfaces/Radio.js
@@ -3,9 +3,10 @@ import { toFixed } from 'common/math';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NumberInput, Section } from '../components';
import { RADIO_CHANNELS } from '../constants';
+import { Window } from '../layouts';
-export const Radio = props => {
- const { act, data } = useBackend(props);
+export const Radio = (props, context) => {
+ const { act, data } = useBackend(context);
const {
freqlock,
frequency,
@@ -25,84 +26,88 @@ export const Radio = props => {
status: !!value,
}))(data.channels);
return (
-
-
-
- {freqlock && (
-
- {toFixed(frequency / 10, 1) + ' kHz'}
-
- ) || (
- toFixed(value, 1)}
- onDrag={(e, value) => act('frequency', {
- adjust: (value - frequency / 10),
- })} />
- )}
- {tunedChannel && (
-
- [{tunedChannel.name}]
-
- )}
-
-
- act('listen')} />
- act('broadcast')} />
- {!!command && (
- act('command')} />
- )}
- {!!subspaceSwitchable && (
- act('subspace')} />
- )}
-
- {!!subspace && (
-
- {channels.length === 0 && (
-
- No encryption keys installed.
-
- )}
- {channels.map(channel => (
-
- act('channel', {
- channel: channel.name,
+
+
+
+
+
+ {freqlock && (
+
+ {toFixed(frequency / 10, 1) + ' kHz'}
+
+ ) || (
+ toFixed(value, 1)}
+ onDrag={(e, value) => act('frequency', {
+ adjust: (value - frequency / 10),
})} />
-
- ))}
-
- )}
-
-
+ )}
+ {tunedChannel && (
+
+ [{tunedChannel.name}]
+
+ )}
+
+
+ act('listen')} />
+ act('broadcast')} />
+ {!!command && (
+ act('command')} />
+ )}
+ {!!subspaceSwitchable && (
+ act('subspace')} />
+ )}
+
+ {!!subspace && (
+
+ {channels.length === 0 && (
+
+ No encryption keys installed.
+
+ )}
+ {channels.map(channel => (
+
+ act('channel', {
+ channel: channel.name,
+ })} />
+
+ ))}
+
+ )}
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/SatelliteControl.js b/tgui-next/packages/tgui/interfaces/SatelliteControl.js
index 73a7f1ffbb..26aadee269 100644
--- a/tgui-next/packages/tgui/interfaces/SatelliteControl.js
+++ b/tgui-next/packages/tgui/interfaces/SatelliteControl.js
@@ -1,45 +1,48 @@
import { useBackend } from '../backend';
-import { Button, LabeledList, ProgressBar, Section, Table, Box } from '../components';
+import { Button, LabeledList, ProgressBar, Section, Box } from '../components';
import { Fragment } from 'inferno';
import { LabeledListItem } from '../components/LabeledList';
+import { Window } from '../layouts';
-export const SatelliteControl = props => {
- const { act, data } = useBackend(props);
+export const SatelliteControl = (props, context) => {
+ const { act, data } = useBackend(context);
const satellites = data.satellites || [];
return (
-
- {data.meteor_shield && (
-
-
-
-
+
+ {data.meteor_shield && (
+
+
+
+
-
-
+ ranges={{
+ good: [1, Infinity],
+ average: [0.30, 1],
+ bad: [-Infinity, 0.30],
+ }} />
+
+
+
+ )}
+
+
+ {satellites.map(satellite => (
+ act('toggle', {
+ id: satellite.id,
+ })}
+ />
+ ))}
+
- )}
-
-
- {satellites.map(satellite => (
- act('toggle', {
- id: satellite.id,
- })}
- />
- ))}
-
-
-
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ScannerGate.js b/tgui-next/packages/tgui/interfaces/ScannerGate.js
index f1e82729bc..e390284d57 100644
--- a/tgui-next/packages/tgui/interfaces/ScannerGate.js
+++ b/tgui-next/packages/tgui/interfaces/ScannerGate.js
@@ -2,6 +2,7 @@ import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NumberInput, Section } from '../components';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
+import { Window } from '../layouts';
const DISEASE_THEASHOLD_LIST = [
'Positive',
@@ -67,18 +68,18 @@ const TARGET_NUTRITION_LIST = [
},
];
-export const ScannerGate = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+export const ScannerGate = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
- act('toggle_lock')} />
- {!data.locked && (
-
- )}
-
+
+
+ act('toggle_lock')} />
+ {!data.locked && (
+
+ )}
+
+
);
};
@@ -117,9 +118,8 @@ const SCANNER_GATE_ROUTES = {
},
};
-const ScannerGateControl = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+const ScannerGateControl = (props, context) => {
+ const { act, data } = useBackend(context);
const { scan_mode } = data;
const route = SCANNER_GATE_ROUTES[scan_mode]
|| SCANNER_GATE_ROUTES.off;
@@ -133,13 +133,13 @@ const ScannerGateControl = props => {
content="back"
onClick={() => act('set_mode', { new_mode: 'Off' })} />
)}>
-
+
);
};
-const ScannerGateOff = props => {
- const { act } = useBackend(props);
+const ScannerGateOff = (props, context) => {
+ const { act } = useBackend(context);
return (
@@ -172,9 +172,8 @@ const ScannerGateOff = props => {
);
};
-const ScannerGateWanted = props => {
- const { state } = props;
- const { data } = useBackend(props);
+const ScannerGateWanted = (props, context) => {
+ const { data } = useBackend(context);
const { reverse } = data;
return (
@@ -182,14 +181,13 @@ const ScannerGateWanted = props => {
Trigger if the person scanned {reverse ? 'does not have' : 'has'}
{' '}any warrants for their arrest.
-
+
);
};
-const ScannerGateGuns = props => {
- const { state } = props;
- const { data } = useBackend(props);
+const ScannerGateGuns = (props, context) => {
+ const { data } = useBackend(context);
const { reverse } = data;
return (
@@ -197,14 +195,13 @@ const ScannerGateGuns = props => {
Trigger if the person scanned {reverse ? 'does not have' : 'has'}
{' '}any guns.
-
+
);
};
-const ScannerGateMindshield = props => {
- const { state } = props;
- const { data } = useBackend(props);
+const ScannerGateMindshield = (props, context) => {
+ const { data } = useBackend(context);
const { reverse } = data;
return (
@@ -212,14 +209,13 @@ const ScannerGateMindshield = props => {
Trigger if the person scanned {reverse ? 'does not have' : 'has'}
{' '}a mindshield.
-
+
);
};
-const ScannerGateDisease = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+const ScannerGateDisease = (props, context) => {
+ const { act, data } = useBackend(context);
const { reverse, disease_threshold } = data;
return (
@@ -238,14 +234,13 @@ const ScannerGateDisease = props => {
})} />
))}
-
+
);
};
-const ScannerGateSpecies = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+const ScannerGateSpecies = (props, context) => {
+ const { act, data } = useBackend(context);
const { reverse, target_species } = data;
const species = TARGET_SPECIES_LIST.find(species => {
return species.value === target_species;
@@ -270,14 +265,13 @@ const ScannerGateSpecies = props => {
})} />
))}
-
+
);
};
-const ScannerGateNutrition = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+const ScannerGateNutrition = (props, context) => {
+ const { act, data } = useBackend(context);
const { reverse, target_nutrition } = data;
const nutrition = TARGET_NUTRITION_LIST.find(nutrition => {
return nutrition.value === target_nutrition;
@@ -299,14 +293,13 @@ const ScannerGateNutrition = props => {
})} />
))}
-
+
);
};
-const ScannerGateNanites = props => {
- const { state } = props;
- const { act, data } = useBackend(props);
+const ScannerGateNanites = (props, context) => {
+ const { act, data } = useBackend(context);
const { reverse, nanite_cloud } = data;
return (
@@ -329,13 +322,13 @@ const ScannerGateNanites = props => {
-
+
);
};
-const ScannerGateMode = props => {
- const { act, data } = useBackend(props);
+const ScannerGateMode = (props, context) => {
+ const { act, data } = useBackend(context);
const { reverse } = data;
return (
diff --git a/tgui-next/packages/tgui/interfaces/ShuttleManipulator.js b/tgui-next/packages/tgui/interfaces/ShuttleManipulator.js
index 1cc361bc95..606a0700aa 100644
--- a/tgui-next/packages/tgui/interfaces/ShuttleManipulator.js
+++ b/tgui-next/packages/tgui/interfaces/ShuttleManipulator.js
@@ -1,200 +1,242 @@
import { map } from 'common/collections';
import { Fragment } from 'inferno';
-import { useBackend } from '../backend';
-import { Button, LabeledList, Section, Table, Tabs } from '../components';
+import { useBackend, useLocalState } from '../backend';
+import { Button, Flex, LabeledList, Section, Table, Tabs } from '../components';
+import { Window } from '../layouts';
-export const ShuttleManipulator = props => {
- const { act, data } = useBackend(props);
+export const ShuttleManipulator = (props, context) => {
+ const [tab, setTab] = useLocalState(context, 'tab', 1);
+ return (
+
+
+
+ setTab(1)}>
+ Status
+
+ setTab(2)}>
+ Templates
+
+ setTab(3)}>
+ Modification
+
+
+ {tab === 1 && (
+
+ )}
+ {tab === 2 && (
+
+ )}
+ {tab === 3 && (
+
+ )}
+
+
+ );
+};
+
+export const ShuttleManipulatorStatus = (props, context) => {
+ const { act, data } = useBackend(context);
const shuttles = data.shuttles || [];
+ return (
+
+
+ {shuttles.map(shuttle => (
+
+
+ act('jump_to', {
+ type: 'mobile',
+ id: shuttle.id,
+ })} />
+
+
+ act('fly', {
+ id: shuttle.id,
+ })} />
+
+
+ {shuttle.name}
+
+
+ {shuttle.id}
+
+
+ {shuttle.status}
+
+
+ {shuttle.mode}
+ {!!shuttle.timer && (
+
+ ({shuttle.timeleft})
+ act('fast_travel', {
+ id: shuttle.id,
+ })} />
+
+ )}
+
+
+ ))}
+
+
+ );
+};
+
+export const ShuttleManipulatorTemplates = (props, context) => {
+ const { act, data } = useBackend(context);
const templateObject = data.templates || {};
const selected = data.selected || {};
- const existingShuttle = data.existing_shuttle || {};
+ const [
+ selectedTemplateId,
+ setSelectedTemplateId,
+ ] = useLocalState(context, 'templateId', Object.keys(templateObject)[0]);
+ const actualTemplates = templateObject[selectedTemplateId]?.templates;
return (
-
-
- {() => (
-
-
- {shuttles.map(shuttle => (
-
-
- act('jump_to', {
- type: 'mobile',
- id: shuttle.id,
- })} />
-
-
- act('fly', {
- id: shuttle.id,
- })} />
-
-
- {shuttle.name}
-
-
- {shuttle.id}
-
-
- {shuttle.status}
-
-
- {shuttle.mode}
- {!!shuttle.timer && (
-
- ({shuttle.timeleft})
- act('fast_travel', {
- id: shuttle.id,
- })} />
-
- )}
-
-
- ))}
-
-
- )}
-
-
- {() => (
-
-
- {map((template, templateId) => {
- const templates = template.templates || [];
- return (
-
- {templates.map(actualTemplate => {
- const isSelected = (
- actualTemplate.shuttle_id === selected.shuttle_id
- );
- // Whoever made the structure being sent is an asshole
- return (
- act('select_template', {
- shuttle_id: actualTemplate.shuttle_id,
- })} />
- )}>
- {(!!actualTemplate.description
- || !!actualTemplate.admin_notes
- ) && (
-
- {!!actualTemplate.description && (
-
- {actualTemplate.description}
-
- )}
- {!!actualTemplate.admin_notes && (
-
- {actualTemplate.admin_notes}
-
- )}
-
- )}
-
- );
- })}
-
- );
- })(templateObject)}
-
-
- )}
-
-
-
- {selected ? (
-
+
+
+
+
+ {map((template, templateId) => (
+ setSelectedTemplateId(templateId)}>
+ {template.port_id}
+
+ ))(templateObject)}
+
+
+
+ {actualTemplates.map(actualTemplate => {
+ const isSelected = (
+ actualTemplate.shuttle_id === selected.shuttle_id
+ );
+ // Whoever made the structure being sent is an asshole
+ return (
- {(!!selected.description || !!selected.admin_notes) && (
+ key={actualTemplate.shuttle_id}
+ buttons={(
+ act('select_template', {
+ shuttle_id: actualTemplate.shuttle_id,
+ })} />
+ )}>
+ {(!!actualTemplate.description
+ || !!actualTemplate.admin_notes
+ ) && (
- {!!selected.description && (
+ {!!actualTemplate.description && (
- {selected.description}
+ {actualTemplate.description}
)}
- {!!selected.admin_notes && (
+ {!!actualTemplate.admin_notes && (
- {selected.admin_notes}
+ {actualTemplate.admin_notes}
)}
)}
- {existingShuttle ? (
-
-
- act('jump_to', {
- type: 'mobile',
- id: existingShuttle.id,
- })} />
- )}>
- {existingShuttle.status}
- {!!existingShuttle.timer && (
-
- ({existingShuttle.timeleft})
-
- )}
-
-
-
- ) : (
-
- )}
-
- act('preview', {
- shuttle_id: selected.shuttle_id,
- })} />
- act('load', {
- shuttle_id: selected.shuttle_id,
- })} />
-
-
- ) : 'No shuttle selected'}
-
-
-
+ );
+ })}
+
+
+
+ );
+};
+
+export const ShuttleManipulatorModification = (props, context) => {
+ const { act, data } = useBackend(context);
+ const selected = data.selected || {};
+ const existingShuttle = data.existing_shuttle || {};
+ return (
+
+ {selected ? (
+
+
+ {(!!selected.description || !!selected.admin_notes) && (
+
+ {!!selected.description && (
+
+ {selected.description}
+
+ )}
+ {!!selected.admin_notes && (
+
+ {selected.admin_notes}
+
+ )}
+
+ )}
+
+ {existingShuttle ? (
+
+
+ act('jump_to', {
+ type: 'mobile',
+ id: existingShuttle.id,
+ })} />
+ )}>
+ {existingShuttle.status}
+ {!!existingShuttle.timer && (
+
+ ({existingShuttle.timeleft})
+
+ )}
+
+
+
+ ) : (
+
+ )}
+
+ act('preview', {
+ shuttle_id: selected.shuttle_id,
+ })} />
+ act('load', {
+ shuttle_id: selected.shuttle_id,
+ })} />
+
+
+ ) : 'No shuttle selected'}
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Signaler.js b/tgui-next/packages/tgui/interfaces/Signaler.js
index c656613141..bf06e02d1e 100644
--- a/tgui-next/packages/tgui/interfaces/Signaler.js
+++ b/tgui-next/packages/tgui/interfaces/Signaler.js
@@ -1,9 +1,10 @@
import { Grid, NumberInput, Button, Section } from '../components';
import { useBackend } from '../backend';
import { toFixed } from 'common/math';
+import { Window } from '../layouts';
-export const Signaler = props => {
- const { act, data } = useBackend(props);
+export const Signaler = (props, context) => {
+ const { act, data } = useBackend(context);
const {
code,
frequency,
@@ -12,74 +13,78 @@ export const Signaler = props => {
} = data;
return (
-
-
-
- Frequency:
-
-
- toFixed(value, 1)}
- width={13}
- onDrag={(e, value) => act('freq', {
- freq: value,
- })} />
-
-
- act('reset', {
- reset: "freq",
- })} />
-
-
-
-
- Code:
-
-
- act('code', {
- code: value,
- })} />
-
-
- act('reset', {
- reset: "code",
- })} />
-
-
-
-
- act('signal')} />
-
-
-
+
+
+
+
+
+ Frequency:
+
+
+ toFixed(value, 1)}
+ width={13}
+ onDrag={(e, value) => act('freq', {
+ freq: value,
+ })} />
+
+
+ act('reset', {
+ reset: "freq",
+ })} />
+
+
+
+
+ Code:
+
+
+ act('code', {
+ code: value,
+ })} />
+
+
+ act('reset', {
+ reset: "code",
+ })} />
+
+
+
+
+ act('signal')} />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/SlimeBodySwapper.js b/tgui-next/packages/tgui/interfaces/SlimeBodySwapper.js
index 9b41120b42..51fbd8bce3 100644
--- a/tgui-next/packages/tgui/interfaces/SlimeBodySwapper.js
+++ b/tgui-next/packages/tgui/interfaces/SlimeBodySwapper.js
@@ -1,7 +1,8 @@
import { useBackend } from '../backend';
-import { Section, LabeledList, ProgressBar, Button, BlockQuote, Grid, Box } from '../components';
+import { Section, LabeledList, Button, Box } from '../components';
+import { Window } from '../layouts';
-export const BodyEntry = props => {
+export const BodyEntry = (props, context) => {
const { body, swapFunc } = props;
const statusMap = {
@@ -50,21 +51,25 @@ export const BodyEntry = props => {
);
};
-export const SlimeBodySwapper = props => {
- const { act, data } = useBackend(props);
+export const SlimeBodySwapper = (props, context) => {
+ const { act, data } = useBackend(context);
const {
bodies = [],
} = data;
return (
-
- {bodies.map(body => (
- act('swap', { ref: body.ref })} />
- ))}
-
+
+
+
+ {bodies.map(body => (
+ act('swap', { ref: body.ref })} />
+ ))}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/SmartVend.js b/tgui-next/packages/tgui/interfaces/SmartVend.js
index d0c13ad259..b835c3996e 100644
--- a/tgui-next/packages/tgui/interfaces/SmartVend.js
+++ b/tgui-next/packages/tgui/interfaces/SmartVend.js
@@ -1,61 +1,66 @@
import { map } from 'common/collections';
import { useBackend } from '../backend';
import { Button, NoticeBox, Section, Table } from '../components';
+import { Window } from '../layouts';
-export const SmartVend = props => {
- const { act, data } = useBackend(props);
+export const SmartVend = (props, context) => {
+ const { act, data } = useBackend(context);
return (
- act('Dry')}>
- {data.drying ? 'Stop drying' : 'Dry'}
-
- )}>
- {data.contents.length === 0 && (
-
- Unfortunately, this {data.name} is empty.
-
- ) || (
-
-
-
- Item
-
-
-
- {data.verb ? data.verb : 'Dispense'}
-
-
- {map((value, key) => (
-
-
- {value.name}
-
-
- {value.amount}
-
-
- act('Release', {
- name: value.name,
- amount: 1,
- })} />
- act('Release', {
- name: value.name,
- })} />
-
-
- ))(data.contents)}
-
- )}
-
+
+
+ act('Dry')}>
+ {data.drying ? 'Stop drying' : 'Dry'}
+
+ )}>
+ {data.contents.length === 0 && (
+
+ Unfortunately, this {data.name} is empty.
+
+ ) || (
+
+
+
+ Item
+
+
+
+ {data.verb ? data.verb : 'Dispense'}
+
+
+ {map((value, key) => (
+
+
+ {value.name}
+
+
+ {value.amount}
+
+
+ act('Release', {
+ name: value.name,
+ amount: 1,
+ })} />
+ act('Release', {
+ name: value.name,
+ })} />
+
+
+ ))(data.contents)}
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/SpawnersMenu.js b/tgui-next/packages/tgui/interfaces/SpawnersMenu.js
index 27d8b1f9ff..ba5c5d74ec 100644
--- a/tgui-next/packages/tgui/interfaces/SpawnersMenu.js
+++ b/tgui-next/packages/tgui/interfaces/SpawnersMenu.js
@@ -1,51 +1,56 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Section } from '../components';
+import { Window } from '../layouts';
-export const SpawnersMenu = props => {
- const { act, data } = useBackend(props);
+export const SpawnersMenu = (props, context) => {
+ const { act, data } = useBackend(context);
const spawners = data.spawners || [];
return (
-
- {spawners.map(spawner => (
-
- act('jump', {
- name: spawner.name,
- })} />
- act('spawn', {
- name: spawner.name,
- })} />
-
- )}>
-
- {spawner.short_desc}
-
-
- {spawner.flavor_text}
-
- {!!spawner.important_info && (
-
- {spawner.important_info}
-
- )}
+
+
+
+ {spawners.map(spawner => (
+
+ act('jump', {
+ name: spawner.name,
+ })} />
+ act('spawn', {
+ name: spawner.name,
+ })} />
+
+ )}>
+
+ {spawner.short_desc}
+
+
+ {spawner.flavor_text}
+
+ {!!spawner.important_info && (
+
+ {spawner.important_info}
+
+ )}
+
+ ))}
- ))}
-
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/StationAlertConsole.js b/tgui-next/packages/tgui/interfaces/StationAlertConsole.js
index c372b532e9..504467f985 100644
--- a/tgui-next/packages/tgui/interfaces/StationAlertConsole.js
+++ b/tgui-next/packages/tgui/interfaces/StationAlertConsole.js
@@ -1,9 +1,20 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Section } from '../components';
+import { Window } from '../layouts';
-export const StationAlertConsole = props => {
- const { data } = useBackend(props);
+export const StationAlertConsole = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export const StationAlertConsoleContent = (props, context) => {
+ const { data } = useBackend(context);
const categories = data.alarms || [];
const fire = categories['Fire'] || [];
const atmos = categories['Atmosphere'] || [];
diff --git a/tgui-next/packages/tgui/interfaces/SuitStorageUnit.js b/tgui-next/packages/tgui/interfaces/SuitStorageUnit.js
index f858505a37..65c990d763 100644
--- a/tgui-next/packages/tgui/interfaces/SuitStorageUnit.js
+++ b/tgui-next/packages/tgui/interfaces/SuitStorageUnit.js
@@ -1,9 +1,10 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Icon, LabeledList, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
-export const SuitStorageUnit = props => {
- const { act, data } = useBackend(props);
+export const SuitStorageUnit = (props, context) => {
+ const { act, data } = useBackend(context);
const {
locked,
open,
@@ -16,96 +17,98 @@ export const SuitStorageUnit = props => {
storage,
} = data;
return (
-
- {!!(occupied && safeties) && (
-
- Biological entity detected in suit chamber. Please remove
- before continuing with operation.
-
- )}
- {uv_active && (
-
- Contents are currently being decontaminated. Please wait.
-
- ) || (
-
- {!open && (
- act('lock')} />
- )}
- {!locked && (
- act('door')} />
- )}
-
- )}>
- {locked && (
-
- Unit Locked
-
-
- ) || open && (
-
-
- act('dispense', {
- item: 'helmet',
- })} />
-
-
- act('dispense', {
- item: 'suit',
- })} />
-
-
- act('dispense', {
- item: 'mask',
- })} />
-
-
- act('dispense', {
- item: 'storage',
- })} />
-
-
- ) || (
- act('uv')} />
- )}
-
- )}
-
+
+
+ {!!(occupied && safeties) && (
+
+ Biological entity detected in suit chamber. Please remove
+ before continuing with operation.
+
+ )}
+ {uv_active && (
+
+ Contents are currently being decontaminated. Please wait.
+
+ ) || (
+
+ {!open && (
+ act('lock')} />
+ )}
+ {!locked && (
+ act('door')} />
+ )}
+
+ )}>
+ {locked && (
+
+ Unit Locked
+
+
+ ) || open && (
+
+
+ act('dispense', {
+ item: 'helmet',
+ })} />
+
+
+ act('dispense', {
+ item: 'suit',
+ })} />
+
+
+ act('dispense', {
+ item: 'mask',
+ })} />
+
+
+ act('dispense', {
+ item: 'storage',
+ })} />
+
+
+ ) || (
+ act('uv')} />
+ )}
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/TankDispenser.js b/tgui-next/packages/tgui/interfaces/TankDispenser.js
index de05a5750b..a11476df2c 100644
--- a/tgui-next/packages/tgui/interfaces/TankDispenser.js
+++ b/tgui-next/packages/tgui/interfaces/TankDispenser.js
@@ -1,34 +1,39 @@
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const TankDispenser = props => {
- const { act, data } = useBackend(props);
+export const TankDispenser = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
- act('plasma')} />
- )}>
- {data.plasma}
-
- act('oxygen')} />
- )}>
- {data.oxygen}
-
-
-
+
+
+
+
+ act('plasma')} />
+ )}>
+ {data.plasma}
+
+ act('oxygen')} />
+ )}>
+ {data.oxygen}
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/ThermoMachine.js b/tgui-next/packages/tgui/interfaces/ThermoMachine.js
index acb31e34d7..e79a112c15 100644
--- a/tgui-next/packages/tgui/interfaces/ThermoMachine.js
+++ b/tgui-next/packages/tgui/interfaces/ThermoMachine.js
@@ -1,77 +1,79 @@
import { toFixed } from 'common/math';
-import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { AnimatedNumber, Button, LabeledList, NumberInput, Section } from '../components';
+import { Window } from '../layouts';
-export const ThermoMachine = props => {
- const { act, data } = useBackend(props);
+export const ThermoMachine = (props, context) => {
+ const { act, data } = useBackend(context);
return (
-
-
-
-
- toFixed(value, 2)} />
- {' K'}
-
-
- toFixed(value, 2)} />
- {' kPa'}
-
-
-
- act('power')} />
- )}>
-
-
- act('target', {
- target: value,
- })} />
-
-
+
+
+
+
+
+ toFixed(value, 2)} />
+ {' K'}
+
+
+ toFixed(value, 2)} />
+ {' kPa'}
+
+
+
+ act('target', {
- target: data.min,
- })} />
- act('target', {
- target: data.initial,
- })} />
- act('target', {
- target: data.max,
- })} />
-
-
-
-
+ icon={data.on ? 'power-off' : 'times'}
+ content={data.on ? 'On' : 'Off'}
+ selected={data.on}
+ onClick={() => act('power')} />
+ )}>
+
+
+ act('target', {
+ target: value,
+ })} />
+
+
+ act('target', {
+ target: data.min,
+ })} />
+ act('target', {
+ target: data.initial,
+ })} />
+ act('target', {
+ target: data.max,
+ })} />
+
+
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/TurbineComputer.js b/tgui-next/packages/tgui/interfaces/TurbineComputer.js
index 4f632cc149..1f85128682 100644
--- a/tgui-next/packages/tgui/interfaces/TurbineComputer.js
+++ b/tgui-next/packages/tgui/interfaces/TurbineComputer.js
@@ -1,64 +1,69 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const TurbineComputer = props => {
- const { act, data } = useBackend(props);
+export const TurbineComputer = (props, context) => {
+ const { act, data } = useBackend(context);
const operational = Boolean(data.compressor
&& !data.compressor_broke
&& data.turbine
&& !data.turbine_broke);
return (
-
- act('toggle_power')} />
- act('reconnect')} />
-
- )}>
- {!operational && (
-
-
- {data.compressor_broke
- ? data.compressor ? 'Offline' : 'Missing'
- : 'Online'}
-
-
- {data.turbine_broke
- ? data.turbine ? 'Offline' : 'Missing'
- : 'Online'}
-
-
- ) || (
-
-
- {data.rpm} RPM
-
-
- {data.temp} K
-
-
- {data.power}
-
-
- )}
-
+
+
+
+ act('toggle_power')} />
+ act('reconnect')} />
+
+ )}>
+ {!operational && (
+
+
+ {data.compressor_broke
+ ? data.compressor ? 'Offline' : 'Missing'
+ : 'Online'}
+
+
+ {data.turbine_broke
+ ? data.turbine ? 'Offline' : 'Missing'
+ : 'Online'}
+
+
+ ) || (
+
+
+ {data.rpm} RPM
+
+
+ {data.temp} K
+
+
+ {data.power}
+
+
+ )}
+
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/Uplink.js b/tgui-next/packages/tgui/interfaces/Uplink.js
index 4f2b047c9b..4121c37086 100644
--- a/tgui-next/packages/tgui/interfaces/Uplink.js
+++ b/tgui-next/packages/tgui/interfaces/Uplink.js
@@ -1,186 +1,191 @@
-import { decodeHtmlEntities } from 'common/string';
-import { Component, Fragment } from 'inferno';
-import { act } from '../byond';
-import { Box, Button, Input, Section, Table, Tabs } from '../components';
+import { createSearch, decodeHtmlEntities } from 'common/string';
+import { Fragment } from 'inferno';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Flex, Input, Section, Table, Tabs, NoticeBox } from '../components';
+import { formatMoney } from '../format';
+import { Window } from '../layouts';
-// It's a class because we need to store state in the form of the current
-// hovered item, and current search terms
-export class Uplink extends Component {
- constructor() {
- super();
- this.state = {
- hoveredItem: {},
- currentSearch: '',
- };
- }
+const MAX_SEARCH_RESULTS = 25;
- setHoveredItem(hoveredItem) {
- this.setState({
- hoveredItem,
- });
- }
+export const Uplink = (props, context) => {
+ const { data } = useBackend(context);
+ const { telecrystals } = data;
+ return (
+
+
+
+
+
+ );
+};
- setSearchText(currentSearch) {
- this.setState({
- currentSearch,
- });
- }
-
- render() {
- const { state } = this.props;
- const { config, data } = state;
- const { ref } = config;
- const {
- compact_mode,
- lockable,
- telecrystals,
- categories = [],
- } = data;
- const { hoveredItem, currentSearch } = this.state;
- return (
-
- );
- }
-}
-
-const ItemList = props => {
+export const GenericUplink = (props, context) => {
const {
- items,
- hoveredItem,
- telecrystals,
- compact,
- onBuy,
- onBuyMouseOver,
- onBuyMouseOut,
+ currencyAmount = 0,
+ currencySymbol = 'cr',
} = props;
+ const { act, data } = useBackend(context);
+ const {
+ compactMode,
+ lockable,
+ categories = [],
+ } = data;
+ const [
+ searchText,
+ setSearchText,
+ ] = useLocalState(context, 'searchText', '');
+ const [
+ selectedCategory,
+ setSelectedCategory,
+ ] = useLocalState(context, 'category', categories[0]?.name);
+ const testSearch = createSearch(searchText, item => {
+ return item.name + item.desc;
+ });
+ const items = searchText.length > 0
+ // Flatten all categories and apply search to it
+ && categories
+ .flatMap(category => category.items || [])
+ .filter(testSearch)
+ .filter((item, i) => i < MAX_SEARCH_RESULTS)
+ // Select a category and show all items in it
+ || categories
+ .find(category => category.name === selectedCategory)
+ ?.items
+ // If none of that results in a list, return an empty list
+ || [];
+ return (
+
+ );
+};
+
+const ItemList = (props, context) => {
+ const {
+ compactMode,
+ currencyAmount,
+ currencySymbol,
+ } = props;
+ const { act } = useBackend(context);
+ const [
+ hoveredItem,
+ setHoveredItem,
+ ] = useLocalState(context, 'hoveredItem', {});
const hoveredCost = hoveredItem && hoveredItem.cost || 0;
- if (compact) {
+ // Append extra hover data to items
+ const items = props.items.map(item => {
+ const notSameItem = hoveredItem && hoveredItem.name !== item.name;
+ const notEnoughHovered = currencyAmount - hoveredCost < item.cost;
+ const disabledDueToHovered = notSameItem && notEnoughHovered;
+ const disabled = currencyAmount < item.cost || disabledDueToHovered;
+ return {
+ ...item,
+ disabled,
+ };
+ });
+ if (compactMode) {
return (
- {items.map(item => {
- const notSameItem = hoveredItem && hoveredItem.name !== item.name;
- const notEnoughHovered = telecrystals - hoveredCost < item.cost;
- const disabledDueToHovered = notSameItem && notEnoughHovered;
- return (
-
-
- {decodeHtmlEntities(item.name)}
-
-
- onBuyMouseOver(item)}
- onmouseout={() => onBuyMouseOut(item)}
- onClick={() => onBuy(item)} />
-
-
- );
- })}
+ {items.map(item => (
+
+
+ {decodeHtmlEntities(item.name)}
+
+
+ setHoveredItem(item)}
+ onmouseout={() => setHoveredItem({})}
+ onClick={() => act('buy', {
+ item: item.name,
+ })} />
+
+
+ ))}
);
}
- return items.map(item => {
- const notSameItem = hoveredItem && hoveredItem.name !== item.name;
- const notEnoughHovered = telecrystals - hoveredCost < item.cost;
- const disabledDueToHovered = notSameItem && notEnoughHovered;
- return (
- onBuyMouseOver(item)}
- onmouseout={() => onBuyMouseOut(item)}
- onClick={() => onBuy(item)} />
- )}>
- {decodeHtmlEntities(item.desc)}
-
- );
- });
+ return items.map(item => (
+ setHoveredItem(item)}
+ onmouseout={() => setHoveredItem({})}
+ onClick={() => act('buy', {
+ item: item.name,
+ })} />
+ )}>
+ {decodeHtmlEntities(item.desc)}
+
+ ));
};
diff --git a/tgui-next/packages/tgui/interfaces/Wires.js b/tgui-next/packages/tgui/interfaces/Wires.js
index f33b261738..6e9ac9b160 100644
--- a/tgui-next/packages/tgui/interfaces/Wires.js
+++ b/tgui-next/packages/tgui/interfaces/Wires.js
@@ -1,59 +1,62 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
-export const Wires = props => {
- const { act, data } = useBackend(props);
+export const Wires = (props, context) => {
+ const { act, data } = useBackend(context);
const wires = data.wires || [];
const statuses = data.status || [];
return (
-
-
-
- {wires.map(wire => (
-
- act('cut', {
- wire: wire.color,
- })} />
- act('pulse', {
- wire: wire.color,
- })} />
- act('attach', {
- wire: wire.color,
- })} />
-
- )}>
- {!!wire.wire && (
-
- ({wire.wire})
-
- )}
-
- ))}
-
-
- {!!statuses.length && (
+
+
- {statuses.map(status => (
-
- {status}
-
- ))}
+
+ {wires.map(wire => (
+
+ act('cut', {
+ wire: wire.color,
+ })} />
+ act('pulse', {
+ wire: wire.color,
+ })} />
+ act('attach', {
+ wire: wire.color,
+ })} />
+
+ )}>
+ {!!wire.wire && (
+
+ ({wire.wire})
+
+ )}
+
+ ))}
+
- )}
-
+ {!!statuses.length && (
+
+ {statuses.map(status => (
+
+ {status}
+
+ ))}
+
+ )}
+
+
);
};
diff --git a/tgui-next/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js b/tgui-next/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js
index 6554e3ff6f..dabc124967 100644
--- a/tgui-next/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js
+++ b/tgui-next/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js
@@ -1,16 +1,32 @@
+import { useBackend } from '../../backend';
import { Button, Flex, NoticeBox } from '../../components';
-export const InterfaceLockNoticeBox = props => {
+/**
+ * This component by expects the following fields to be returned
+ * from ui_data:
+ *
+ * - siliconUser: boolean
+ * - locked: boolean
+ *
+ * And expects the following ui_act action to be implemented:
+ *
+ * - lock - for toggling the lock as a silicon user.
+ *
+ * All props can be redefined if you want custom behavior, but
+ * it's preferred to stick to defaults.
+ */
+export const InterfaceLockNoticeBox = (props, context) => {
+ const { act, data } = useBackend(context);
const {
- siliconUser,
- locked,
- onLockStatusChange,
- accessText,
+ siliconUser = data.siliconUser,
+ locked = data.locked,
+ onLockStatusChange = () => act('lock'),
+ accessText = 'an ID card',
} = props;
// For silicon users
if (siliconUser) {
return (
-
+
Interface lock status:
@@ -19,7 +35,7 @@ export const InterfaceLockNoticeBox = props => {
{
@@ -35,7 +51,7 @@ export const InterfaceLockNoticeBox = props => {
// For everyone else
return (
- Swipe {accessText || 'an ID card'}{' '}
+ Swipe {accessText}{' '}
to {locked ? 'unlock' : 'lock'} this interface.
);
diff --git a/tgui-next/packages/tgui/logging.js b/tgui-next/packages/tgui/logging.js
index b0b6f67158..592e10836b 100644
--- a/tgui-next/packages/tgui/logging.js
+++ b/tgui-next/packages/tgui/logging.js
@@ -1,5 +1,5 @@
import { sendLogEntry } from 'tgui-dev-server/link/client';
-import { act } from './byond';
+import { callByond } from './byond';
const LEVEL_DEBUG = 0;
const LEVEL_LOG = 1;
@@ -27,7 +27,9 @@ const log = (level, ns, ...args) => {
.filter(value => value)
.join(' ')
+ '\nUser Agent: ' + navigator.userAgent;
- act(window.__ref__, 'tgui:log', {
+ callByond('', {
+ src: window.__ref__,
+ action: 'tgui:log',
log: logEntry,
});
}
@@ -42,3 +44,10 @@ export const createLogger = ns => {
error: (...args) => log(LEVEL_ERROR, ns, ...args),
};
};
+
+/**
+ * A generic instance of the logger.
+ *
+ * Does not have a namespace associated with it.
+ */
+export const logger = createLogger();
diff --git a/tgui-next/packages/tgui/styles/components/BlockQuote.scss b/tgui-next/packages/tgui/styles/components/BlockQuote.scss
index 9ab1de08e2..caedbf11f7 100644
--- a/tgui-next/packages/tgui/styles/components/BlockQuote.scss
+++ b/tgui-next/packages/tgui/styles/components/BlockQuote.scss
@@ -7,4 +7,9 @@ $color-default: colors.fg(colors.$label) !default;
color: $color-default;
border-left: 2px solid $color-default;
padding-left: 6px;
+ margin-bottom: 6px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
}
diff --git a/tgui-next/packages/tgui/styles/components/NoticeBox.scss b/tgui-next/packages/tgui/styles/components/NoticeBox.scss
index 048904ed75..2785038799 100644
--- a/tgui-next/packages/tgui/styles/components/NoticeBox.scss
+++ b/tgui-next/packages/tgui/styles/components/NoticeBox.scss
@@ -1,13 +1,16 @@
+@use 'sass:color';
+@use '../colors.scss';
@use '../functions.scss' as *;
// NoticeBox
-$color-first: #bb9b68 !default;
-$color-second: #b1905d !default;
+$color-background: #bb9b68 !default;
+$color-stripes: rgba(0, 0, 0, 0.1) !default;
$color-border: #272727 !default;
+$bg-map: colors.$bg-map !default;
.NoticeBox {
// Adapt text color to background luminance to ensure high contast
- $luminance: luminance($color-first);
+ $luminance: luminance($color-background);
$text-color: if($luminance > 0.35,
rgba(0, 0, 0, 1),
rgba(255, 255, 255, 1));
@@ -18,11 +21,45 @@ $color-border: #272727 !default;
font-weight: bold;
font-style: italic;
color: $text-color;
- background-color: $color-first;
+ background-color: $color-background;
background-image: repeating-linear-gradient(
-45deg,
- $color-first,
- $color-first 10px,
- $color-second 10px,
- $color-second 20px);
+ transparent,
+ transparent 10px,
+ $color-stripes 10px,
+ $color-stripes 20px);
+}
+
+@mixin box-color($color) {
+ $luminance: luminance($color);
+ $text-color: if($luminance > 0.35,
+ rgba(0, 0, 0, 1),
+ rgba(255, 255, 255, 1));
+ color: $text-color;
+ background-color: color.adjust(
+ $color,
+ $saturation: -15%,
+ $lightness: -15%);
+}
+
+@each $color-name, $color-value in $bg-map {
+ .NoticeBox--color--#{$color-name} {
+ @include box-color($color-value)
+ }
+}
+
+.NoticeBox--type--info {
+ @include box-color(colors.$blue);
+}
+
+.NoticeBox--type--success {
+ @include box-color(colors.$green);
+}
+
+.NoticeBox--type--warning {
+ @include box-color(colors.$orange);
+}
+
+.NoticeBox--type--danger {
+ @include box-color(colors.$red);
}
diff --git a/tgui-next/packages/tgui/styles/components/Tabs.scss b/tgui-next/packages/tgui/styles/components/Tabs.scss
index d195b5ac58..991f92e0ef 100644
--- a/tgui-next/packages/tgui/styles/components/Tabs.scss
+++ b/tgui-next/packages/tgui/styles/components/Tabs.scss
@@ -1,39 +1,57 @@
-.Tabs__content {
- padding-top: 6px;
- border-top: 2px solid rgba(255, 255, 255, 0.1);
+@use '../base.scss';
+
+$border-radius: base.$border-radius !default;
+
+.Tabs--horizontal {
+ border-bottom: 2px solid rgba(255, 255, 255, 0.1);
+ margin-bottom: 6px;
+
+ .Tabs__tab--altSelection::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ height: 2px;
+ width: 100%;
+ background-color: #fff;
+ border-radius: $border-radius;
+ }
}
.Tabs--vertical {
- display: table-row;
-}
+ margin-right: 9px;
-// NOTE: These selectors have to be specific in order to stop cascading
-// from influencing nested tabs.
-.Tabs--vertical > .Tabs__content {
- display: table-cell;
- width: 100%;
- padding-top: 0;
- padding-left: 9px;
- border-top: 0;
-}
+ .Tabs__tabBox {
+ // padding-right: 2px;
+ border-right: 2px solid rgba(255, 255, 255, 0.1);
+ // Disable baseline alignment when doing vertical tabs
+ vertical-align: top;
+ }
-.Tabs--vertical > .Tabs__tabBox {
- display: table-cell;
- // padding-right: 2px;
- border-right: 2px solid rgba(255, 255, 255, 0.1);
- // Disable baseline alignment when doing vertical tabs
- vertical-align: top;
-}
+ .Tabs__tab {
+ // Force display block because Button theme overrides it via cascading.
+ display: block !important;
+ // Override to stop themed buttons from taking priority over it.
+ margin-right: 0 !important;
+ margin-bottom: 0;
+ padding: 1px 9px 0px 6px;
+ border-bottom: 2px solid rgba(255, 255, 255, 0.1);
-.Tabs--vertical > .Tabs__tabBox > .Tabs__tab {
- // Force display block because Button theme overrides it via cascading.
- display: block !important;
- margin-right: 0;
- margin-bottom: 0;
- padding: 1px 9px 0px 6px;
- border-bottom: 2px solid rgba(255, 255, 255, 0.1);
+ &:last-child {
+ border-bottom: 0;
+ }
+ }
- &:last-child {
- border-bottom: 0;
+ .Tabs__tab--altSelection::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ height: 100%;
+ width: 3px;
+ background-color: #fff;
+ border-radius: $border-radius;
}
}
diff --git a/tgui-next/packages/tgui/styles/themes/cardtable.scss b/tgui-next/packages/tgui/styles/themes/cardtable.scss
index 67ae46c220..6b6c456357 100644
--- a/tgui-next/packages/tgui/styles/themes/cardtable.scss
+++ b/tgui-next/packages/tgui/styles/themes/cardtable.scss
@@ -25,16 +25,18 @@
'color-caution': #be6209,
'color-danger': #9a9d00,
));
- @include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/NumberInput.scss', $with: (
'border-color': #FFFFFF,
));
-
@include meta.load-css('../components/ProgressBar.scss', $with: (
'color-background': rgba(0, 0, 0, 0.5),
));
@include meta.load-css('../components/Section.scss');
- @include meta.load-css('../components/TitleBar.scss', $with: (
+
+ // Layouts
+ @include meta.load-css('../layouts/Layout.scss');
+ @include meta.load-css('../layouts/Window.scss');
+ @include meta.load-css('../layouts/TitleBar.scss', $with: (
'color-background': #381608,
));
diff --git a/tgui-next/packages/tgui/styles/themes/hackerman.scss b/tgui-next/packages/tgui/styles/themes/hackerman.scss
index 73745ce6c8..d5059596c0 100644
--- a/tgui-next/packages/tgui/styles/themes/hackerman.scss
+++ b/tgui-next/packages/tgui/styles/themes/hackerman.scss
@@ -25,9 +25,13 @@
@include meta.load-css('../components/Input.scss', $with: (
'border-color': colors.$primary,
));
- @include meta.load-css('../components/Layout.scss');
+ @include meta.load-css('../components/Modal.scss');
@include meta.load-css('../components/Section.scss');
- @include meta.load-css('../components/TitleBar.scss', $with: (
+
+ // Layouts
+ @include meta.load-css('../layouts/Layout.scss');
+ @include meta.load-css('../layouts/Window.scss');
+ @include meta.load-css('../layouts/TitleBar.scss', $with: (
'color-background': #223d22,
));
diff --git a/tgui-next/packages/tgui/styles/themes/retro.scss b/tgui-next/packages/tgui/styles/themes/retro.scss
index 2c69e1d04d..800628427c 100644
--- a/tgui-next/packages/tgui/styles/themes/retro.scss
+++ b/tgui-next/packages/tgui/styles/themes/retro.scss
@@ -26,12 +26,15 @@
'color-caution': #be6209,
'color-danger': #9a9d00,
));
- @include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/ProgressBar.scss', $with: (
'color-background': rgba(0, 0, 0, 0.5),
));
@include meta.load-css('../components/Section.scss');
- @include meta.load-css('../components/TitleBar.scss', $with: (
+
+ // Layouts
+ @include meta.load-css('../layouts/Layout.scss');
+ @include meta.load-css('../layouts/Window.scss');
+ @include meta.load-css('../layouts/TitleBar.scss', $with: (
'color-background': #585337,
));
diff --git a/tgui-next/packages/tgui/styles/themes/syndicate.scss b/tgui-next/packages/tgui/styles/themes/syndicate.scss
index 35cfedc421..a8ca2b1649 100644
--- a/tgui-next/packages/tgui/styles/themes/syndicate.scss
+++ b/tgui-next/packages/tgui/styles/themes/syndicate.scss
@@ -27,10 +27,8 @@
@include meta.load-css('../components/Input.scss', $with: (
'border-color': #87ce87,
));
- @include meta.load-css('../components/Layout.scss');
@include meta.load-css('../components/NoticeBox.scss', $with: (
- 'color-first': #750000,
- 'color-second': #910101,
+ 'color-background': #910101,
));
@include meta.load-css('../components/NumberInput.scss', $with: (
'border-color': #87ce87,
@@ -39,13 +37,17 @@
'color-background': rgba(0, 0, 0, 0.5),
));
@include meta.load-css('../components/Section.scss');
- @include meta.load-css('../components/TitleBar.scss', $with: (
- 'color-background': #910101,
- ));
@include meta.load-css('../components/Tooltip.scss', $with: (
'color-background': #4a0202,
));
+ // Layouts
+ @include meta.load-css('../layouts/Layout.scss');
+ @include meta.load-css('../layouts/Window.scss');
+ @include meta.load-css('../layouts/TitleBar.scss', $with: (
+ 'color-background': #910101,
+ ));
+
.Layout__content {
background-image: url('../../assets/bg-syndicate.svg');
}
diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml
new file mode 100644
index 0000000000..67e74085c7
--- /dev/null
+++ b/tgui/.eslintrc.yml
@@ -0,0 +1,755 @@
+parser: babel-eslint
+parserOptions:
+ ecmaVersion: 2019
+ sourceType: module
+ ecmaFeatures:
+ jsx: true
+env:
+ es6: true
+ browser: true
+ node: true
+plugins:
+ - react
+settings:
+ react:
+ version: '16.10'
+rules:
+
+ ## Possible Errors
+ ## ----------------------------------------
+
+ ## Enforce “for” loop update clause moving the counter in the right
+ ## direction.
+ # for-direction: error
+ ## Enforce return statements in getters
+ # getter-return: error
+ ## Disallow using an async function as a Promise executor
+ no-async-promise-executor: error
+ ## Disallow await inside of loops
+ # no-await-in-loop: error
+ ## Disallow comparing against -0
+ # no-compare-neg-zero: error
+ ## Disallow assignment operators in conditional expressions
+ no-cond-assign: error
+ ## Disallow the use of console
+ # no-console: error
+ ## Disallow constant expressions in conditions
+ # no-constant-condition: error
+ ## Disallow control characters in regular expressions
+ # no-control-regex: error
+ ## Disallow the use of debugger
+ no-debugger: error
+ ## Disallow duplicate arguments in function definitions
+ no-dupe-args: error
+ ## Disallow duplicate keys in object literals
+ no-dupe-keys: error
+ ## Disallow duplicate case labels
+ no-duplicate-case: error
+ ## Disallow empty block statements
+ # no-empty: error
+ ## Disallow empty character classes in regular expressions
+ no-empty-character-class: error
+ ## Disallow reassigning exceptions in catch clauses
+ no-ex-assign: error
+ ## Disallow unnecessary boolean casts
+ no-extra-boolean-cast: error
+ ## Disallow unnecessary parentheses
+ # no-extra-parens: warn
+ ## Disallow unnecessary semicolons
+ no-extra-semi: error
+ ## Disallow reassigning function declarations
+ no-func-assign: error
+ ## Disallow assigning to imported bindings
+ no-import-assign: error
+ ## Disallow variable or function declarations in nested blocks
+ no-inner-declarations: error
+ ## Disallow invalid regular expression strings in RegExp constructors
+ no-invalid-regexp: error
+ ## Disallow irregular whitespace
+ no-irregular-whitespace: error
+ ## Disallow characters which are made with multiple code points in character
+ ## class syntax
+ no-misleading-character-class: error
+ ## Disallow calling global object properties as functions
+ no-obj-calls: error
+ ## Disallow calling some Object.prototype methods directly on objects
+ no-prototype-builtins: error
+ ## Disallow multiple spaces in regular expressions
+ no-regex-spaces: error
+ ## Disallow sparse arrays
+ no-sparse-arrays: error
+ ## Disallow template literal placeholder syntax in regular strings
+ no-template-curly-in-string: error
+ ## Disallow confusing multiline expressions
+ no-unexpected-multiline: error
+ ## Disallow unreachable code after return, throw, continue, and break
+ ## statements
+ # no-unreachable: warn
+ ## Disallow control flow statements in finally blocks
+ no-unsafe-finally: error
+ ## Disallow negating the left operand of relational operators
+ no-unsafe-negation: error
+ ## Disallow assignments that can lead to race conditions due to usage of
+ ## await or yield
+ # require-atomic-updates: error
+ ## Require calls to isNaN() when checking for NaN
+ use-isnan: error
+ ## Enforce comparing typeof expressions against valid strings
+ valid-typeof: error
+
+ ## Best practices
+ ## ----------------------------------------
+ ## Enforce getter and setter pairs in objects and classes
+ # accessor-pairs: error
+ ## Enforce return statements in callbacks of array methods
+ # array-callback-return: error
+ ## Enforce the use of variables within the scope they are defined
+ # block-scoped-var: error
+ ## Enforce that class methods utilize this
+ # class-methods-use-this: error
+ ## Enforce a maximum cyclomatic complexity allowed in a program
+ complexity: [error, { max: 50 }]
+ ## Require return statements to either always or never specify values
+ # consistent-return: error
+ ## Enforce consistent brace style for all control statements
+ curly: [error, all]
+ ## Require default cases in switch statements
+ # default-case: error
+ ## Enforce default parameters to be last
+ # default-param-last: error
+ ## Enforce consistent newlines before and after dots
+ dot-location: [error, property]
+ ## Enforce dot notation whenever possible
+ # dot-notation: error
+ ## Require the use of === and !==
+ eqeqeq: [error, always]
+ ## Require for-in loops to include an if statement
+ # guard-for-in: error
+ ## Enforce a maximum number of classes per file
+ # max-classes-per-file: error
+ ## Disallow the use of alert, confirm, and prompt
+ # no-alert: error
+ ## Disallow the use of arguments.caller or arguments.callee
+ # no-caller: error
+ ## Disallow lexical declarations in case clauses
+ no-case-declarations: error
+ ## Disallow division operators explicitly at the beginning of regular
+ ## expressions
+ # no-div-regex: error
+ ## Disallow else blocks after return statements in if statements
+ # no-else-return: error
+ ## Disallow empty functions
+ # no-empty-function: error
+ ## Disallow empty destructuring patterns
+ no-empty-pattern: error
+ ## Disallow null comparisons without type-checking operators
+ # no-eq-null: error
+ ## Disallow the use of eval()
+ # no-eval: error
+ ## Disallow extending native types
+ # no-extend-native: error
+ ## Disallow unnecessary calls to .bind()
+ # no-extra-bind: error
+ ## Disallow unnecessary labels
+ # no-extra-label: error
+ ## Disallow fallthrough of case statements
+ no-fallthrough: error
+ ## Disallow leading or trailing decimal points in numeric literals
+ # no-floating-decimal: error
+ ## Disallow assignments to native objects or read-only global variables
+ no-global-assign: error
+ ## Disallow shorthand type conversions
+ # no-implicit-coercion: error
+ ## Disallow variable and function declarations in the global scope
+ # no-implicit-globals: error
+ ## Disallow the use of eval()-like methods
+ # no-implied-eval: error
+ ## Disallow this keywords outside of classes or class-like objects
+ # no-invalid-this: error
+ ## Disallow the use of the __iterator__ property
+ # no-iterator: error
+ ## Disallow labeled statements
+ # no-labels: error
+ ## Disallow unnecessary nested blocks
+ # no-lone-blocks: error
+ ## Disallow function declarations that contain unsafe references inside
+ ## loop statements
+ # no-loop-func: error
+ ## Disallow magic numbers
+ # no-magic-numbers: error
+ ## Disallow multiple spaces
+ no-multi-spaces: warn
+ ## Disallow multiline strings
+ # no-multi-str: error
+ ## Disallow new operators outside of assignments or comparisons
+ # no-new: error
+ ## Disallow new operators with the Function object
+ # no-new-func: error
+ ## Disallow new operators with the String, Number, and Boolean objects
+ # no-new-wrappers: error
+ ## Disallow octal literals
+ no-octal: error
+ ## Disallow octal escape sequences in string literals
+ no-octal-escape: error
+ ## Disallow reassigning function parameters
+ # no-param-reassign: error
+ ## Disallow the use of the __proto__ property
+ # no-proto: error
+ ## Disallow variable redeclaration
+ no-redeclare: error
+ ## Disallow certain properties on certain objects
+ # no-restricted-properties: error
+ ## Disallow assignment operators in return statements
+ no-return-assign: error
+ ## Disallow unnecessary return await
+ # no-return-await: error
+ ## Disallow javascript: urls
+ # no-script-url: error
+ ## Disallow assignments where both sides are exactly the same
+ no-self-assign: error
+ ## Disallow comparisons where both sides are exactly the same
+ # no-self-compare: error
+ ## Disallow comma operators
+ no-sequences: error
+ ## Disallow throwing literals as exceptions
+ # no-throw-literal: error
+ ## Disallow unmodified loop conditions
+ # no-unmodified-loop-condition: error
+ ## Disallow unused expressions
+ # no-unused-expressions: error
+ ## Disallow unused labels
+ no-unused-labels: warn
+ ## Disallow unnecessary calls to .call() and .apply()
+ # no-useless-call: error
+ ## Disallow unnecessary catch clauses
+ # no-useless-catch: error
+ ## Disallow unnecessary concatenation of literals or template literals
+ # no-useless-concat: error
+ ## Disallow unnecessary escape characters
+ no-useless-escape: warn
+ ## Disallow redundant return statements
+ # no-useless-return: error
+ ## Disallow void operators
+ # no-void: error
+ ## Disallow specified warning terms in comments
+ # no-warning-comments: error
+ ## Disallow with statements
+ no-with: error
+ ## Enforce using named capture group in regular expression
+ # prefer-named-capture-group: error
+ ## Require using Error objects as Promise rejection reasons
+ # prefer-promise-reject-errors: error
+ ## Disallow use of the RegExp constructor in favor of regular expression
+ ## literals
+ # prefer-regex-literals: error
+ ## Enforce the consistent use of the radix argument when using parseInt()
+ radix: error
+ ## Disallow async functions which have no await expression
+ # require-await: error
+ ## Enforce the use of u flag on RegExp
+ # require-unicode-regexp: error
+ ## Require var declarations be placed at the top of their containing scope
+ # vars-on-top: error
+ ## Require parentheses around immediate function invocations
+ # wrap-iife: error
+ ## Require or disallow “Yoda” conditions
+ # yoda: error
+
+ ## Strict mode
+ ## ----------------------------------------
+ ## Require or disallow strict mode directives
+ strict: error
+
+ ## Variables
+ ## ----------------------------------------
+ ## Require or disallow initialization in variable declarations
+ # init-declarations: error
+ ## Disallow deleting variables
+ no-delete-var: error
+ ## Disallow labels that share a name with a variable
+ # no-label-var: error
+ ## Disallow specified global variables
+ # no-restricted-globals: error
+ ## Disallow variable declarations from shadowing variables declared in
+ ## the outer scope
+ # no-shadow: error
+ ## Disallow identifiers from shadowing restricted names
+ no-shadow-restricted-names: error
+ ## Disallow the use of undeclared variables unless mentioned
+ ## in /*global*/ comments
+ no-undef: error
+ ## Disallow initializing variables to undefined
+ no-undef-init: error
+ ## Disallow the use of undefined as an identifier
+ # no-undefined: error
+ ## Disallow unused variables
+ # no-unused-vars: error
+ ## Disallow the use of variables before they are defined
+ # no-use-before-define: error
+
+ ## Code style
+ ## ----------------------------------------
+ ## Enforce linebreaks after opening and before closing array brackets
+ array-bracket-newline: [error, consistent]
+ ## Enforce consistent spacing inside array brackets
+ array-bracket-spacing: [error, never]
+ ## Enforce line breaks after each array element
+ # array-element-newline: error
+ ## Disallow or enforce spaces inside of blocks after opening block and
+ ## before closing block
+ block-spacing: [error, always]
+ ## Enforce consistent brace style for blocks
+ # brace-style: [error, stroustrup, { allowSingleLine: false }]
+ ## Enforce camelcase naming convention
+ # camelcase: error
+ ## Enforce or disallow capitalization of the first letter of a comment
+ # capitalized-comments: error
+ ## Require or disallow trailing commas
+ comma-dangle: [error, {
+ arrays: always-multiline,
+ objects: always-multiline,
+ imports: always-multiline,
+ exports: always-multiline,
+ functions: only-multiline, ## Optional on functions
+ }]
+ ## Enforce consistent spacing before and after commas
+ comma-spacing: [error, { before: false, after: true }]
+ ## Enforce consistent comma style
+ comma-style: [error, last]
+ ## Enforce consistent spacing inside computed property brackets
+ computed-property-spacing: [error, never]
+ ## Enforce consistent naming when capturing the current execution context
+ # consistent-this: error
+ ## Require or disallow newline at the end of files
+ # eol-last: error
+ ## Require or disallow spacing between function identifiers and their
+ ## invocations
+ func-call-spacing: [error, never]
+ ## Require function names to match the name of the variable or property
+ ## to which they are assigned
+ # func-name-matching: error
+ ## Require or disallow named function expressions
+ # func-names: error
+ ## Enforce the consistent use of either function declarations or expressions
+ func-style: [error, expression]
+ ## Enforce line breaks between arguments of a function call
+ # function-call-argument-newline: error
+ ## Enforce consistent line breaks inside function parentheses
+ ## NOTE: This rule does not honor a newline on opening paren.
+ # function-paren-newline: [error, never]
+ ## Disallow specified identifiers
+ # id-blacklist: error
+ ## Enforce minimum and maximum identifier lengths
+ # id-length: error
+ ## Require identifiers to match a specified regular expression
+ # id-match: error
+ ## Enforce the location of arrow function bodies
+ # implicit-arrow-linebreak: error
+ ## Enforce consistent indentation
+ indent: [error, 2, { SwitchCase: 1 }]
+ ## Enforce the consistent use of either double or single quotes in JSX
+ ## attributes
+ jsx-quotes: [error, prefer-double]
+ ## Enforce consistent spacing between keys and values in object literal
+ ## properties
+ key-spacing: [error, { beforeColon: false, afterColon: true }]
+ ## Enforce consistent spacing before and after keywords
+ keyword-spacing: [error, { before: true, after: true }]
+ ## Enforce position of line comments
+ # line-comment-position: error
+ ## Enforce consistent linebreak style
+ # linebreak-style: error
+ ## Require empty lines around comments
+ # lines-around-comment: error
+ ## Require or disallow an empty line between class members
+ # lines-between-class-members: error
+ ## Enforce a maximum depth that blocks can be nested
+ # max-depth: error
+ ## Enforce a maximum line length
+ max-len: [error, {
+ code: 80,
+ ## Ignore imports
+ ignorePattern: '^(import\s.+\sfrom\s|.*require\()',
+ ignoreUrls: true,
+ ignoreRegExpLiterals: true,
+ }]
+ ## Enforce a maximum number of lines per file
+ # max-lines: error
+ ## Enforce a maximum number of line of code in a function
+ # max-lines-per-function: error
+ ## Enforce a maximum depth that callbacks can be nested
+ # max-nested-callbacks: error
+ ## Enforce a maximum number of parameters in function definitions
+ # max-params: error
+ ## Enforce a maximum number of statements allowed in function blocks
+ # max-statements: error
+ ## Enforce a maximum number of statements allowed per line
+ # max-statements-per-line: error
+ ## Enforce a particular style for multiline comments
+ # multiline-comment-style: error
+ ## Enforce newlines between operands of ternary expressions
+ multiline-ternary: [error, always-multiline]
+ ## Require constructor names to begin with a capital letter
+ # new-cap: error
+ ## Enforce or disallow parentheses when invoking a constructor with no
+ ## arguments
+ # new-parens: error
+ ## Require a newline after each call in a method chain
+ # newline-per-chained-call: error
+ ## Disallow Array constructors
+ # no-array-constructor: error
+ ## Disallow bitwise operators
+ # no-bitwise: error
+ ## Disallow continue statements
+ # no-continue: error
+ ## Disallow inline comments after code
+ # no-inline-comments: error
+ ## Disallow if statements as the only statement in else blocks
+ # no-lonely-if: error
+ ## Disallow mixed binary operators
+ # no-mixed-operators: error
+ ## Disallow mixed spaces and tabs for indentation
+ no-mixed-spaces-and-tabs: error
+ ## Disallow use of chained assignment expressions
+ # no-multi-assign: error
+ ## Disallow multiple empty lines
+ # no-multiple-empty-lines: error
+ ## Disallow negated conditions
+ # no-negated-condition: error
+ ## Disallow nested ternary expressions
+ # no-nested-ternary: error
+ ## Disallow Object constructors
+ # no-new-object: error
+ ## Disallow the unary operators ++ and --
+ # no-plusplus: error
+ ## Disallow specified syntax
+ # no-restricted-syntax: error
+ ## Disallow all tabs
+ # no-tabs: error
+ ## Disallow ternary operators
+ # no-ternary: error
+ ## Disallow trailing whitespace at the end of lines
+ # no-trailing-spaces: error
+ ## Disallow dangling underscores in identifiers
+ # no-underscore-dangle: error
+ ## Disallow ternary operators when simpler alternatives exist
+ # no-unneeded-ternary: error
+ ## Disallow whitespace before properties
+ no-whitespace-before-property: error
+ ## Enforce the location of single-line statements
+ # nonblock-statement-body-position: error
+ ## Enforce consistent line breaks inside braces
+ # object-curly-newline: [error, { multiline: true }]
+ ## Enforce consistent spacing inside braces
+ object-curly-spacing: [error, always]
+ ## Enforce placing object properties on separate lines
+ # object-property-newline: error
+ ## Enforce variables to be declared either together or separately in
+ ## functions
+ # one-var: error
+ ## Require or disallow newlines around variable declarations
+ # one-var-declaration-per-line: error
+ ## Require or disallow assignment operator shorthand where possible
+ # operator-assignment: error
+ ## Enforce consistent linebreak style for operators
+ operator-linebreak: [error, before]
+ ## Require or disallow padding within blocks
+ # padded-blocks: error
+ ## Require or disallow padding lines between statements
+ # padding-line-between-statements: error
+ ## Disallow using Object.assign with an object literal as the first
+ ## argument and prefer the use of object spread instead.
+ # prefer-object-spread: error
+ ## Require quotes around object literal property names
+ # quote-props: error
+ ## Enforce the consistent use of either backticks, double, or single quotes
+ # quotes: [error, single]
+ ## Require or disallow semicolons instead of ASI
+ semi: error
+ ## Enforce consistent spacing before and after semicolons
+ semi-spacing: [error, { before: false, after: true }]
+ ## Enforce location of semicolons
+ semi-style: [error, last]
+ ## Require object keys to be sorted
+ # sort-keys: error
+ ## Require variables within the same declaration block to be sorted
+ # sort-vars: error
+ ## Enforce consistent spacing before blocks
+ space-before-blocks: [error, always]
+ ## Enforce consistent spacing before function definition opening parenthesis
+ space-before-function-paren: [error, {
+ anonymous: always,
+ named: never,
+ asyncArrow: always,
+ }]
+ ## Enforce consistent spacing inside parentheses
+ space-in-parens: [error, never]
+ ## Require spacing around infix operators
+ # space-infix-ops: error
+ ## Enforce consistent spacing before or after unary operators
+ # space-unary-ops: error
+ ## Enforce consistent spacing after the // or /* in a comment
+ spaced-comment: [error, always]
+ ## Enforce spacing around colons of switch statements
+ switch-colon-spacing: [error, { before: false, after: true }]
+ ## Require or disallow spacing between template tags and their literals
+ template-tag-spacing: [error, never]
+ ## Require or disallow Unicode byte order mark (BOM)
+ # unicode-bom: [error, never]
+ ## Require parenthesis around regex literals
+ # wrap-regex: error
+
+ ## ES6
+ ## ----------------------------------------
+ ## Require braces around arrow function bodies
+ # arrow-body-style: error
+ ## Require parentheses around arrow function arguments
+ arrow-parens: [error, as-needed]
+ ## Enforce consistent spacing before and after the arrow in arrow functions
+ arrow-spacing: [error, { before: true, after: true }]
+ ## Require super() calls in constructors
+ # constructor-super: error
+ ## Enforce consistent spacing around * operators in generator functions
+ generator-star-spacing: [error, { before: false, after: true }]
+ ## Disallow reassigning class members
+ no-class-assign: error
+ ## Disallow arrow functions where they could be confused with comparisons
+ # no-confusing-arrow: error
+ ## Disallow reassigning const variables
+ no-const-assign: error
+ ## Disallow duplicate class members
+ no-dupe-class-members: error
+ ## Disallow duplicate module imports
+ # no-duplicate-imports: error
+ ## Disallow new operators with the Symbol object
+ no-new-symbol: error
+ ## Disallow specified modules when loaded by import
+ # no-restricted-imports: error
+ ## Disallow this/super before calling super() in constructors
+ no-this-before-super: error
+ ## Disallow unnecessary computed property keys in object literals
+ # no-useless-computed-key: error
+ ## Disallow unnecessary constructors
+ # no-useless-constructor: error
+ ## Disallow renaming import, export, and destructured assignments to the
+ ## same name
+ # no-useless-rename: error
+ ## Require let or const instead of var
+ no-var: error
+ ## Require or disallow method and property shorthand syntax for object
+ ## literals
+ # object-shorthand: error
+ ## Require using arrow functions for callbacks
+ prefer-arrow-callback: error
+ ## Require const declarations for variables that are never reassigned after
+ ## declared
+ # prefer-const: error
+ ## Require destructuring from arrays and/or objects
+ # prefer-destructuring: error
+ ## Disallow parseInt() and Number.parseInt() in favor of binary, octal, and
+ ## hexadecimal literals
+ # prefer-numeric-literals: error
+ ## Require rest parameters instead of arguments
+ # prefer-rest-params: error
+ ## Require spread operators instead of .apply()
+ # prefer-spread: error
+ ## Require template literals instead of string concatenation
+ # prefer-template: error
+ ## Require generator functions to contain yield
+ # require-yield: error
+ ## Enforce spacing between rest and spread operators and their expressions
+ # rest-spread-spacing: error
+ ## Enforce sorted import declarations within modules
+ # sort-imports: error
+ ## Require symbol descriptions
+ # symbol-description: error
+ ## Require or disallow spacing around embedded expressions of template
+ ## strings
+ # template-curly-spacing: error
+ ## Require or disallow spacing around the * in yield* expressions
+ yield-star-spacing: [error, { before: false, after: true }]
+
+ ## React
+ ## ----------------------------------------
+ ## Enforces consistent naming for boolean props
+ react/boolean-prop-naming: error
+ ## Forbid "button" element without an explicit "type" attribute
+ react/button-has-type: error
+ ## Prevent extraneous defaultProps on components
+ react/default-props-match-prop-types: error
+ ## Rule enforces consistent usage of destructuring assignment in component
+ # react/destructuring-assignment: [error, always, { ignoreClassFields: true }]
+ ## Prevent missing displayName in a React component definition
+ react/display-name: error
+ ## Forbid certain props on Components
+ # react/forbid-component-props: error
+ ## Forbid certain props on DOM Nodes
+ # react/forbid-dom-props: error
+ ## Forbid certain elements
+ # react/forbid-elements: error
+ ## Forbid certain propTypes
+ # react/forbid-prop-types: error
+ ## Forbid foreign propTypes
+ # react/forbid-foreign-prop-types: error
+ ## Prevent using this.state inside this.setState
+ react/no-access-state-in-setstate: error
+ ## Prevent using Array index in key props
+ # react/no-array-index-key: error
+ ## Prevent passing children as props
+ react/no-children-prop: error
+ ## Prevent usage of dangerous JSX properties
+ react/no-danger: error
+ ## Prevent problem with children and props.dangerouslySetInnerHTML
+ react/no-danger-with-children: error
+ ## Prevent usage of deprecated methods, including component lifecycle
+ ## methods
+ react/no-deprecated: error
+ ## Prevent usage of setState in componentDidMount
+ react/no-did-mount-set-state: error
+ ## Prevent usage of setState in componentDidUpdate
+ react/no-did-update-set-state: error
+ ## Prevent direct mutation of this.state
+ react/no-direct-mutation-state: error
+ ## Prevent usage of findDOMNode
+ react/no-find-dom-node: error
+ ## Prevent usage of isMounted
+ react/no-is-mounted: error
+ ## Prevent multiple component definition per file
+ # react/no-multi-comp: error
+ ## Prevent usage of shouldComponentUpdate when extending React.PureComponent
+ react/no-redundant-should-component-update: error
+ ## Prevent usage of the return value of React.render
+ react/no-render-return-value: error
+ ## Prevent usage of setState
+ # react/no-set-state: error
+ ## Prevent common casing typos
+ react/no-typos: error
+ ## Prevent using string references in ref attribute.
+ react/no-string-refs: error
+ ## Prevent using this in stateless functional components
+ react/no-this-in-sfc: error
+ ## Prevent invalid characters from appearing in markup
+ react/no-unescaped-entities: error
+ ## Prevent usage of unknown DOM property (fixable)
+ # react/no-unknown-property: error
+ ## Prevent usage of unsafe lifecycle methods
+ react/no-unsafe: error
+ ## Prevent definitions of unused prop types
+ react/no-unused-prop-types: error
+ ## Prevent definitions of unused state properties
+ react/no-unused-state: error
+ ## Prevent usage of setState in componentWillUpdate
+ react/no-will-update-set-state: error
+ ## Enforce ES5 or ES6 class for React Components
+ react/prefer-es6-class: error
+ ## Enforce that props are read-only
+ react/prefer-read-only-props: error
+ ## Enforce stateless React Components to be written as a pure function
+ react/prefer-stateless-function: error
+ ## Prevent missing props validation in a React component definition
+ # react/prop-types: error
+ ## Prevent missing React when using JSX
+ # react/react-in-jsx-scope: error
+ ## Enforce a defaultProps definition for every prop that is not a required
+ ## prop
+ # react/require-default-props: error
+ ## Enforce React components to have a shouldComponentUpdate method
+ # react/require-optimization: error
+ ## Enforce ES5 or ES6 class for returning value in render function
+ react/require-render-return: error
+ ## Prevent extra closing tags for components without children (fixable)
+ react/self-closing-comp: error
+ ## Enforce component methods order (fixable)
+ # react/sort-comp: error
+ ## Enforce propTypes declarations alphabetical sorting
+ # react/sort-prop-types: error
+ ## Enforce the state initialization style to be either in a constructor or
+ ## with a class property
+ react/state-in-constructor: error
+ ## Enforces where React component static properties should be positioned.
+ # react/static-property-placement: error
+ ## Enforce style prop value being an object
+ react/style-prop-object: error
+ ## Prevent void DOM elements (e.g. , ) from receiving children
+ react/void-dom-elements-no-children: error
+
+ ## JSX-specific rules
+ ## ----------------------------------------
+ ## Enforce boolean attributes notation in JSX (fixable)
+ react/jsx-boolean-value: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions.
+ # react/jsx-child-element-spacing: error
+ ## Validate closing bracket location in JSX (fixable)
+ react/jsx-closing-bracket-location: [error, {
+ ## NOTE: Not really sure about enforcing this one
+ selfClosing: false,
+ nonEmpty: after-props,
+ }]
+ ## Validate closing tag location in JSX (fixable)
+ react/jsx-closing-tag-location: error
+ ## Enforce or disallow newlines inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-newline: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-spacing: error
+ ## Enforce or disallow spaces around equal signs in JSX attributes (fixable)
+ react/jsx-equals-spacing: error
+ ## Restrict file extensions that may contain JSX
+ # react/jsx-filename-extension: error
+ ## Enforce position of the first prop in JSX (fixable)
+ # react/jsx-first-prop-new-line: error
+ ## Enforce event handler naming conventions in JSX
+ react/jsx-handler-names: error
+ ## Validate JSX indentation (fixable)
+ react/jsx-indent: [error, 2, {
+ checkAttributes: true,
+ }]
+ ## Validate props indentation in JSX (fixable)
+ react/jsx-indent-props: [error, 2]
+ ## Validate JSX has key prop when in array or iterator
+ react/jsx-key: error
+ ## Validate JSX maximum depth
+ react/jsx-max-depth: [error, { max: 10 }] ## Generous
+ ## Limit maximum of props on a single line in JSX (fixable)
+ # react/jsx-max-props-per-line: error
+ ## Prevent usage of .bind() and arrow functions in JSX props
+ # react/jsx-no-bind: error
+ ## Prevent comments from being inserted as text nodes
+ react/jsx-no-comment-textnodes: error
+ ## Prevent duplicate props in JSX
+ react/jsx-no-duplicate-props: error
+ ## Prevent usage of unwrapped JSX strings
+ # react/jsx-no-literals: error
+ ## Prevent usage of unsafe target='_blank'
+ react/jsx-no-target-blank: error
+ ## Disallow undeclared variables in JSX
+ react/jsx-no-undef: error
+ ## Disallow unnecessary fragments (fixable)
+ react/jsx-no-useless-fragment: error
+ ## Limit to one expression per line in JSX
+ # react/jsx-one-expression-per-line: error
+ ## Enforce curly braces or disallow unnecessary curly braces in JSX
+ # react/jsx-curly-brace-presence: error
+ ## Enforce shorthand or standard form for React fragments
+ react/jsx-fragments: error
+ ## Enforce PascalCase for user-defined JSX components
+ react/jsx-pascal-case: error
+ ## Disallow multiple spaces between inline JSX props (fixable)
+ react/jsx-props-no-multi-spaces: error
+ ## Disallow JSX props spreading
+ # react/jsx-props-no-spreading: error
+ ## Enforce default props alphabetical sorting
+ # react/jsx-sort-default-props: error
+ ## Enforce props alphabetical sorting (fixable)
+ # react/jsx-sort-props: error
+ ## Validate whitespace in and around the JSX opening and closing brackets
+ ## (fixable)
+ react/jsx-tag-spacing: error
+ ## Prevent React to be incorrectly marked as unused
+ react/jsx-uses-react: error
+ ## Prevent variables used in JSX to be incorrectly marked as unused
+ react/jsx-uses-vars: error
+ ## Prevent missing parentheses around multilines JSX (fixable)
+ react/jsx-wrap-multilines: error
diff --git a/tgui/README.md b/tgui/README.md
index e3ed2a20ee..b2b528b45a 100644
--- a/tgui/README.md
+++ b/tgui/README.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
- [tgui](#tgui)
@@ -137,3 +138,192 @@ And the template:
```
+=======
+# tgui
+
+## Introduction
+
+tgui is a robust user interface framework of /tg/station.
+
+tgui is very different from most UIs you will encounter in BYOND programming.
+It is heavily reliant on Javascript and web technologies as opposed to DM.
+If you are familiar with NanoUI (a library which can be found on almost
+every other SS13 codebase), tgui should be fairly easy to pick up.
+
+## Learn tgui
+
+People come to tgui from different backgrounds and with different
+learning styles. Whether you prefer a more theoretical or a practical
+approach, we hope you’ll find this section helpful.
+
+### Practical Tutorial
+
+If you are completely new to frontend and prefer to **learn by doing**,
+start with our [practical tutorial](docs/tutorial-and-examples.md).
+
+### Guides
+
+This project uses **Inferno** - a very fast UI rendering engine with a similar
+API to React. Take your time to read these guides:
+
+- [React guide](https://reactjs.org/docs/hello-world.html)
+- [Inferno documentation](https://infernojs.org/docs/guides/components) -
+highlights differences with React.
+
+If you were already familiar with an older, Ractive-based tgui, and want
+to translate concepts between old and new tgui, read this
+[interface conversion guide](docs/converting-old-tgui-interfaces.md).
+
+## Pre-requisites
+
+You will need these programs to start developing in tgui:
+
+- [Node v12.13+](https://nodejs.org/en/download/)
+- [Yarn v1.19+](https://yarnpkg.com/en/docs/install)
+- [MSys2](https://www.msys2.org/) (optional)
+
+> MSys2 closely replicates a unix-like environment which is necessary for
+> the `bin/tgui` script to run. It comes with a robust "mintty" terminal
+> emulator which is better than any standard Windows shell, it supports
+> "git" out of the box (almost like Git for Windows, but better), has
+> a "pacman" package manager, and you can install a text editor like "vim"
+> for a full boomer experience.
+
+## Usage
+
+**For MSys2, Git Bash, WSL, Linux or macOS users:**
+
+First and foremost, change your directory to `tgui`.
+
+Run `bin/tgui --install-git-hooks` (optional) to install merge drivers
+which will assist you in conflict resolution when rebasing your branches.
+
+Run one of the following:
+
+- `bin/tgui` - build the project in production mode.
+- `bin/tgui --dev` - launch a development server.
+ - tgui development server provides you with incremental compilation,
+ hot module replacement and logging facilities in all running instances
+ of tgui. In short, this means that you will instantly see changes in the
+ game as you code it. Very useful, highly recommended.
+ - In order to use it, you should start the game server first, connect to it
+ and wait until the world has been properly loaded and you are no longer
+ in the lobby. Start tgui dev server. You'll know that it's hooked correctly
+ if data gets dumped to the log when tgui windows are opened.
+- `bin/tgui --dev --reload` - reload byond cache once.
+- `bin/tgui --dev --debug` - run server with debug logging enabled.
+- `bin/tgui --dev --no-hot` - disable hot module replacement (helps when
+doing development on IE8).
+- `bin/tgui --lint` - show problems with the code.
+- `bin/tgui --lint --fix` - auto-fix problems with the code.
+- `bin/tgui --analyze` - run a bundle analyzer.
+- `bin/tgui --clean` - clean up project repo.
+- `bin/tgui [webpack options]` - build the project with custom webpack
+options.
+
+**For everyone else:**
+
+If you haven't opened the console already, you can do that by holding
+Shift and right clicking on the `tgui` folder, then pressing
+either `Open command window here` or `Open PowerShell window here`.
+
+Run `yarn install` to install npm dependencies, then one of the following:
+
+- `yarn run build` - build the project in production mode.
+- `yarn run watch` - launch a development server.
+- `yarn run lint` - show problems with the code.
+- `yarn run lint --fix` - auto-fix problems with the code.
+- `yarn run analyze` - run a bundle analyzer.
+
+We also got some batch files in store, for those who don't like fiddling
+with the console:
+
+- `bin/tgui-build.bat` - build the project in production mode.
+- `bin/tgui-dev-server.bat` - launch a development server.
+
+> Remember to always run a full build before submitting a PR. It creates
+> a compressed javascript bundle which is then referenced from DM code.
+> We prefer to keep it version controlled, so that people could build the
+> game just by using Dream Maker.
+
+## Troubleshooting
+
+**Development server doesn't find my BYOND cache!**
+
+This happens if your Documents folder in Windows has a custom location, for
+example in `E:\Libraries\Documents`. Development server has no knowledge
+of these non-standard locations, therefore you have to run the dev server
+with an additional environmental variable, with a full path to BYOND cache.
+
+```
+export BYOND_CACHE="E:/Libraries/Documents/BYOND/cache"
+bin/tgui --dev
+```
+
+Note that in Windows, you have to go through Advanced System Settings,
+System Properties and then open Environment Variables window to do the
+same thing. You may need to reboot after this.
+
+## Developer Tools
+
+When developing with `tgui-dev-server`, you will have access to certain
+development only features.
+
+**Debug Logs.**
+When running server via `bin/tgui --dev --debug`, server will print debug
+logs and time spent on rendering. Use this information to optimize your
+code, and try to keep re-renders below 16ms.
+
+**Kitchen Sink.**
+Press `Ctrl+Alt+=` to open the KitchenSink interface. This interface is a
+playground to test various tgui components.
+
+**Layout Debugger.**
+Press `Ctrl+Alt+-` to toggle the *layout debugger*. It will show outlines of
+all tgui elements, which makes it easy to understand how everything comes
+together, and can reveal certain layout bugs which are not normally visible.
+
+## Project Structure
+
+- `/packages` - Each folder here represents a self-contained Node module.
+- `/packages/common` - Helper functions
+- `/packages/tgui/index.js` - Application entry point.
+- `/packages/tgui/components` - Basic UI building blocks.
+- `/packages/tgui/interfaces` - Actual in-game interfaces.
+Interface takes data via the `state` prop and outputs an html-like stucture,
+which you can build using existing UI components.
+- `/packages/tgui/layouts` - Root level UI components, that affect the final
+look and feel of the browser window. They usually hold various window
+elements, like the titlebar and resize handlers, and control the UI theme.
+- `/packages/tgui/routes.js` - This is where tgui decides which interface to
+pull and render.
+- `/packages/tgui/layout.js` - A root-level component, holding the
+window elements, like the titlebar, buttons, resize handlers. Calls
+`routes.js` to decide which component to render.
+- `/packages/tgui/styles/main.scss` - CSS entry point.
+- `/packages/tgui/styles/functions.scss` - Useful SASS functions.
+Stuff like `lighten`, `darken`, `luminance` are defined here.
+- `/packages/tgui/styles/atomic` - Atomic CSS classes.
+These are very simple, tiny, reusable CSS classes which you can use and
+combine to change appearance of your elements. Keep them small.
+- `/packages/tgui/styles/components` - CSS classes which are used
+in UI components. These stylesheets closely follow the
+[BEM](https://en.bem.info/methodology/) methodology.
+- `/packages/tgui/styles/interfaces` - Custom stylesheets for your interfaces.
+Add stylesheets here if you really need a fine control over your UI styles.
+- `/packages/tgui/styles/layouts` - Layout-related styles.
+- `/packages/tgui/styles/themes` - Contains all the various themes you can
+use in tgui. Each theme must be registered in `webpack.config.js` file.
+
+## Component Reference
+
+See: [Component Reference](docs/component-reference.md).
+
+## License
+
+All code is licensed with the parent license of *tgstation*, **AGPL-3.0**.
+
+See the main [README](../README.md) for more details.
+
+The Authors retain all copyright to their respective work here submitted.
+>>>>>>> 2185124... Merge pull request #50497 from stylemistake/tgui-3.0
diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md
new file mode 100644
index 0000000000..1e9f9f3f9a
--- /dev/null
+++ b/tgui/docs/component-reference.md
@@ -0,0 +1,981 @@
+# Component Reference
+
+> Notice: This documentation might be out of date, so always check the source
+> code to see the most up-to-date information.
+
+
+
+- [General Concepts](#general-concepts)
+- [`tgui/components`](#tguicomponents)
+ - [`AnimatedNumber`](#animatednumber)
+ - [`BlockQuote`](#blockquote)
+ - [`Box`](#box)
+ - [`Button`](#button)
+ - [`Button.Checkbox`](#buttoncheckbox)
+ - [`Button.Confirm`](#buttonconfirm)
+ - [`Button.Input`](#buttoninput)
+ - [`ByondUi`](#byondui)
+ - [`Collapsible`](#collapsible)
+ - [`ColorBox`](#colorbox)
+ - [`Dimmer`](#dimmer)
+ - [`Divider`](#divider)
+ - [`Dropdown`](#dropdown)
+ - [`Flex`](#flex)
+ - [`Flex.Item`](#flexitem)
+ - [`Grid`](#grid)
+ - [`Grid.Column`](#gridcolumn)
+ - [`Icon`](#icon)
+ - [`Input`](#input)
+ - [`Knob`](#knob)
+ - [`LabeledList`](#labeledlist)
+ - [`LabeledList.Item`](#labeledlistitem)
+ - [`LabeledList.Divider`](#labeledlistdivider)
+ - [`Modal`](#modal)
+ - [`NoticeBox`](#noticebox)
+ - [`NumberInput`](#numberinput)
+ - [`ProgressBar`](#progressbar)
+ - [`Section`](#section)
+ - [`Slider`](#slider)
+ - [`Table`](#table)
+ - [`Table.Row`](#tablerow)
+ - [`Table.Cell`](#tablecell)
+ - [`Tabs`](#tabs)
+ - [`Tabs.Tab`](#tabstab)
+ - [`Tooltip`](#tooltip)
+- [`tgui/layouts`](#tguilayouts)
+ - [`Window`](#window)
+ - [`Window.Content`](#windowcontent)
+
+## General Concepts
+
+These are the components which you can use for interface construction.
+If you have trouble finding the exact prop you need on a component,
+please note, that most of these components inherit from other basic
+components, such as `Box`. This component in particular provides a lot
+of styling options for all components, e.g. `color` and `opacity`, thus
+it is used a lot in this framework.
+
+There are a few important semantics you need to know about:
+
+- Some elements support a `content` prop, which is a synonym to a
+`children` prop.
+ - `content` is better used when your element is a self-closing tag
+ (like ` `), and when content is small and simple
+ enough to fit in a prop. Keep in mind, that this prop is **not** native
+ to React, and is only available on these components: `Button`, `Tooltip`.
+ - You should never use `children` explicitly as a prop on an element.
+ Instead open a full tag, and place children or text inside the tag.
+- Inferno supports both camelcase (`onClick`) and lowercase (`onclick`)
+event names.
+ - Camel case names are what's called "synthetic" events, and are the
+ *preferred way* of handling events in React, for efficiency and
+ performance reasons. Please read
+ [Inferno Event Handling](https://infernojs.org/docs/guides/event-handling)
+ to understand what this is about.
+ - Lower case names are native browser events and should be used sparingly,
+ for example when you need an explicit IE8 support. **DO NOT** use
+ lowercase event handlers unless you really know what you are doing.
+ - [Button](#button) component does not support lowercase `onclick` event.
+ Use the camel case `onClick` instead.
+
+## `tgui/components`
+
+### `AnimatedNumber`
+
+This component provides animations for numeric values.
+
+Props:
+
+- `value: number` - Value to animate.
+- `initial: number` - Initial value to use in animation when element
+first appears. If you set initial to `0` for example, number will always
+animate starting from `0`, and if omitted, it will not play an initial
+animation.
+- `format: value => value` - Output formatter.
+ - Example: `value => Math.round(value)`.
+- `children: (formattedValue, rawValue) => any` - Pull the animated number to
+animate more complex things deeper in the DOM tree.
+ - Example: `(_, value) => `
+
+### `BlockQuote`
+
+Just a block quote, just like this example in markdown:
+
+> Here's an example of a block quote.
+
+Props:
+
+- See inherited props: [Box](#box)
+
+### `Box`
+
+The Box component serves as a wrapper component for most of the CSS utility
+needs. It creates a new DOM element, a `