mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
final new NanoUI fixes and tgui port
This commit is contained in:
@@ -6,9 +6,11 @@ env:
|
|||||||
BYOND_MAJOR="510"
|
BYOND_MAJOR="510"
|
||||||
BYOND_MINOR="1346"
|
BYOND_MINOR="1346"
|
||||||
MACRO_COUNT=986
|
MACRO_COUNT=986
|
||||||
|
NODE_VERSION="4"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
|
- tgui/node_modules
|
||||||
- $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}
|
- $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@@ -19,10 +21,13 @@ addons:
|
|||||||
- libstdc++6:i386
|
- libstdc++6:i386
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
- cd tgui && npm install && cd ..
|
||||||
- chmod +x ./install-byond.sh
|
- chmod +x ./install-byond.sh
|
||||||
- ./install-byond.sh
|
- ./install-byond.sh
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $NODE_VERSION
|
||||||
|
- npm install -g gulp-cli
|
||||||
- pip install --user PyYaml -q
|
- pip install --user PyYaml -q
|
||||||
- pip install --user beautifulsoup4 -q
|
- pip install --user beautifulsoup4 -q
|
||||||
|
|
||||||
@@ -34,6 +39,8 @@ script:
|
|||||||
- awk -f tools/indentation.awk **/*.dm
|
- awk -f tools/indentation.awk **/*.dm
|
||||||
- md5sum -c - <<< "88490b460c26947f5ec1ab1bb9fa9f17 *html/changelogs/example.yml"
|
- md5sum -c - <<< "88490b460c26947f5ec1ab1bb9fa9f17 *html/changelogs/example.yml"
|
||||||
- (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ])
|
- (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ])
|
||||||
|
- cd tgui && gulp
|
||||||
|
- cd ..
|
||||||
- source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup
|
- source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup
|
||||||
- python tools/TagMatcher/tag-matcher.py ../..
|
- python tools/TagMatcher/tag-matcher.py ../..
|
||||||
- echo "#define UNIT_TEST 1" > code/_unit_tests.dm
|
- echo "#define UNIT_TEST 1" > code/_unit_tests.dm
|
||||||
|
|||||||
4
code/__defines/tgui.dm
Normal file
4
code/__defines/tgui.dm
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#define UI_INTERACTIVE 2 // Green/Interactive
|
||||||
|
#define UI_UPDATE 1 // Orange/Updates Only
|
||||||
|
#define UI_DISABLED 0 // Red/Disabled
|
||||||
|
#define UI_CLOSE -1 // Closed
|
||||||
5
code/__defines/tick.dm
Normal file
5
code/__defines/tick.dm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#define TICK_LIMIT_RUNNING 90
|
||||||
|
#define TICK_LIMIT_TO_RUN 85
|
||||||
|
|
||||||
|
#define TICK_CHECK ( world.tick_usage > TICK_LIMIT_RUNNING ? stoplag() : 0 )
|
||||||
|
#define CHECK_TICK if(world.tick_usage > TICK_LIMIT_RUNNING) stoplag()
|
||||||
@@ -1,6 +1,22 @@
|
|||||||
#if DM_VERSION < 510
|
#if DM_VERSION < 510
|
||||||
|
|
||||||
|
//Case Sensitive!
|
||||||
|
/proc/text2listEx(text, delimiter="\n")
|
||||||
|
var/delim_len = length(delimiter)
|
||||||
|
if(delim_len < 1) return list(text)
|
||||||
|
. = list()
|
||||||
|
var/last_found = 1
|
||||||
|
var/found
|
||||||
|
do
|
||||||
|
found = findtextEx(text, delimiter, last_found, 0)
|
||||||
|
. += copytext(text, last_found, found)
|
||||||
|
last_found = found + delim_len
|
||||||
|
while(found)
|
||||||
|
|
||||||
/proc/replacetext(text, find, replacement)
|
/proc/replacetext(text, find, replacement)
|
||||||
return jointext(splittext(text, find), replacement)
|
return jointext(splittext(text, find), replacement)
|
||||||
|
|
||||||
|
/proc/replacetextEx(text, find, replacement)
|
||||||
|
return jointext(text2listEx(text, find), replacement)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
28
code/_helpers/atom_movables.dm
Normal file
28
code/_helpers/atom_movables.dm
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/proc/get_turf_pixel(atom/movable/AM)
|
||||||
|
if(!istype(AM))
|
||||||
|
return
|
||||||
|
|
||||||
|
//Find AM's matrix so we can use it's X/Y pixel shifts
|
||||||
|
var/matrix/M = matrix(AM.transform)
|
||||||
|
|
||||||
|
var/pixel_x_offset = AM.pixel_x + M.get_x_shift()
|
||||||
|
var/pixel_y_offset = AM.pixel_y + M.get_y_shift()
|
||||||
|
|
||||||
|
//Irregular objects
|
||||||
|
if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size)
|
||||||
|
var/icon/AMicon = icon(AM.icon, AM.icon_state)
|
||||||
|
pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||||
|
pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||||
|
qdel(AMicon)
|
||||||
|
|
||||||
|
//DY and DX
|
||||||
|
var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size)
|
||||||
|
var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size)
|
||||||
|
|
||||||
|
//Find coordinates
|
||||||
|
var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom
|
||||||
|
var/final_x = T.x + rough_x
|
||||||
|
var/final_y = T.y + rough_y
|
||||||
|
|
||||||
|
if(final_x || final_y)
|
||||||
|
return locate(final_x, final_y, T.z)
|
||||||
@@ -323,6 +323,30 @@ proc/listclearnulls(list/list)
|
|||||||
return (result + L.Copy(Li, 0))
|
return (result + L.Copy(Li, 0))
|
||||||
return (result + R.Copy(Ri, 0))
|
return (result + R.Copy(Ri, 0))
|
||||||
|
|
||||||
|
/proc/sortByVar(var/list/L, var/key)
|
||||||
|
if(L.len < 2)
|
||||||
|
return L
|
||||||
|
var/middle = L.len / 2 + 1
|
||||||
|
return mergeVaredLists(sortByVar(L.Copy(0, middle), key), sortByVar(L.Copy(middle), key), key)
|
||||||
|
|
||||||
|
/proc/mergeVaredLists(var/list/L, var/list/R, var/key)
|
||||||
|
var/Li=1
|
||||||
|
var/Ri=1
|
||||||
|
var/list/result = new()
|
||||||
|
while(Li <= L.len && Ri <= R.len)
|
||||||
|
var/datum/LO = L[Li]
|
||||||
|
var/datum/RO = R[Ri]
|
||||||
|
if(LO.vars[key] > RO.vars[key])
|
||||||
|
// Works around list += list2 merging lists; it's not pretty but it works
|
||||||
|
result += "temp item"
|
||||||
|
result[result.len] = R[Ri++]
|
||||||
|
else
|
||||||
|
result += "temp item"
|
||||||
|
result[result.len] = L[Li++]
|
||||||
|
|
||||||
|
if(Li <= L.len)
|
||||||
|
return (result + L.Copy(Li, 0))
|
||||||
|
return (result + R.Copy(Ri, 0))
|
||||||
|
|
||||||
//Mergesort: any value in a list, preserves key=value structure
|
//Mergesort: any value in a list, preserves key=value structure
|
||||||
/proc/sortAssoc(var/list/L)
|
/proc/sortAssoc(var/list/L)
|
||||||
|
|||||||
@@ -15,3 +15,11 @@
|
|||||||
animate(src, transform = m120, time = speed, loops)
|
animate(src, transform = m120, time = speed, loops)
|
||||||
animate(transform = m240, time = speed)
|
animate(transform = m240, time = speed)
|
||||||
animate(transform = m360, time = speed)
|
animate(transform = m360, time = speed)
|
||||||
|
|
||||||
|
//The X pixel offset of this matrix
|
||||||
|
/matrix/proc/get_x_shift()
|
||||||
|
. = c
|
||||||
|
|
||||||
|
//The Y pixel offset of this matrix
|
||||||
|
/matrix/proc/get_y_shift()
|
||||||
|
. = f
|
||||||
@@ -1315,3 +1315,15 @@ var/mob/dview/dview_mob = new
|
|||||||
tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1))))
|
tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1))))
|
||||||
return locate(tX, tY, tZ)
|
return locate(tX, tY, tZ)
|
||||||
|
|
||||||
|
//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm.
|
||||||
|
/proc/stoplag()
|
||||||
|
. = 1
|
||||||
|
sleep(world.tick_lag)
|
||||||
|
if(world.tick_usage > TICK_LIMIT_TO_RUN) //woke up, still not enough tick, sleep for more.
|
||||||
|
. += 2
|
||||||
|
sleep(world.tick_lag*2)
|
||||||
|
if(world.tick_usage > TICK_LIMIT_TO_RUN) //woke up, STILL not enough tick, sleep for more.
|
||||||
|
. += 4
|
||||||
|
sleep(world.tick_lag*4)
|
||||||
|
//you might be thinking of adding more steps to this, or making it use a loop and a counter var
|
||||||
|
// not worth it.
|
||||||
@@ -66,6 +66,9 @@ world/loop_checks = 0
|
|||||||
testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
|
testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
|
||||||
#endif
|
#endif
|
||||||
if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||||
|
#ifdef GC_FINDREF
|
||||||
|
LocateReferences(A)
|
||||||
|
#endif
|
||||||
// Something's still referring to the qdel'd object. Kill it.
|
// Something's still referring to the qdel'd object. Kill it.
|
||||||
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
|
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
|
||||||
logging["[A.type]"]++
|
logging["[A.type]"]++
|
||||||
@@ -88,29 +91,47 @@ world/loop_checks = 0
|
|||||||
#undef GC_COLLECTIONS_PER_TICK
|
#undef GC_COLLECTIONS_PER_TICK
|
||||||
|
|
||||||
#ifdef GC_FINDREF
|
#ifdef GC_FINDREF
|
||||||
/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ)
|
|
||||||
|
/datum/controller/process/garbage_collector/proc/LocateReferences(var/atom/A)
|
||||||
|
testing("GC: Attempting to locate references to [A] | [A.type]. This is a potentially long-running operation.")
|
||||||
|
if(istype(A))
|
||||||
|
if(A.loc != null)
|
||||||
|
testing("GC: [A] | [A.type] is located in [A.loc] instead of null")
|
||||||
|
if(A.contents.len)
|
||||||
|
testing("GC: [A] | [A.type] has contents: [jointext(A.contents)]")
|
||||||
|
var/ref_count = 0
|
||||||
|
for(var/atom/atom)
|
||||||
|
ref_count += LookForRefs(atom, A)
|
||||||
|
for(var/datum/datum) // This is strictly /datum, not subtypes.
|
||||||
|
ref_count += LookForRefs(datum, A)
|
||||||
|
for(var/client/client)
|
||||||
|
ref_count += LookForRefs(client, A)
|
||||||
|
var/message = "GC: References found to [A] | [A.type]: [ref_count]."
|
||||||
|
if(!ref_count)
|
||||||
|
message += " Has likely been supplied as an 'in list' argment to a proc."
|
||||||
|
testing(message)
|
||||||
|
|
||||||
|
/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/datum/A)
|
||||||
. = 0
|
. = 0
|
||||||
for(var/V in D.vars)
|
for(var/V in D.vars)
|
||||||
if(V == "contents")
|
if(V == "contents")
|
||||||
continue
|
continue
|
||||||
if(istype(D.vars[V], /atom))
|
if(!islist(D.vars[V]))
|
||||||
var/atom/A = D.vars[V]
|
if(D.vars[V] == A)
|
||||||
if(A in targ)
|
|
||||||
testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]")
|
testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]")
|
||||||
. += 1
|
. += 1
|
||||||
else if(islist(D.vars[V]))
|
else
|
||||||
. += LookForListRefs(D.vars[V], targ, D, V)
|
. += LookForListRefs(D.vars[V], A, D, V)
|
||||||
|
|
||||||
/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/list/targ, var/datum/D, var/V)
|
/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/datum/A, var/datum/D, var/V)
|
||||||
. = 0
|
. = 0
|
||||||
for(var/F in L)
|
for(var/F in L)
|
||||||
if(istype(F, /atom))
|
if(!islist(F))
|
||||||
var/atom/A = F
|
if(F == A || L[F] == A)
|
||||||
if(A in targ)
|
|
||||||
testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]")
|
testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]")
|
||||||
. += 1
|
. += 1
|
||||||
if(islist(F))
|
else
|
||||||
. += LookForListRefs(F, targ, D, "[F] in list [V]")
|
. += LookForListRefs(F, A, D, "[F] in list [V]")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/datum/controller/process/garbage_collector/proc/AddTrash(datum/A)
|
/datum/controller/process/garbage_collector/proc/AddTrash(datum/A)
|
||||||
|
|||||||
28
code/controllers/Processes/tgui.dm
Normal file
28
code/controllers/Processes/tgui.dm
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
var/global/datum/controller/process/tgui/tgui_process
|
||||||
|
|
||||||
|
/datum/controller/process/tgui
|
||||||
|
var/list/tg_open_uis = list() // A list of open UIs, grouped by src_object and ui_key.
|
||||||
|
var/list/processing_uis = list() // A list of processing UIs, ungrouped.
|
||||||
|
var/basehtml // The HTML base used for all UIs.
|
||||||
|
|
||||||
|
/datum/controller/process/tgui/setup()
|
||||||
|
name = "tgui"
|
||||||
|
schedule_interval = 10 // every 2 seconds
|
||||||
|
start_delay = 23
|
||||||
|
|
||||||
|
basehtml = file2text('tgui/tgui.html') // Read the HTML from disk.
|
||||||
|
tgui_process = src
|
||||||
|
|
||||||
|
/datum/controller/process/tgui/doWork()
|
||||||
|
for(var/gui in processing_uis)
|
||||||
|
var/datum/tgui/ui = gui
|
||||||
|
if(ui && ui.user && ui.src_object)
|
||||||
|
ui.process()
|
||||||
|
SCHECK
|
||||||
|
continue
|
||||||
|
processing_uis.Remove(ui)
|
||||||
|
SCHECK
|
||||||
|
|
||||||
|
/datum/controller/process/tgui/statProcess()
|
||||||
|
..()
|
||||||
|
stat(null, "[tgui_process.processing_uis.len] UI\s")
|
||||||
31
code/datums/weakref.dm
Normal file
31
code/datums/weakref.dm
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/datum
|
||||||
|
var/weakref
|
||||||
|
|
||||||
|
/datum/Destroy()
|
||||||
|
weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
//obtain a weak reference to a datum
|
||||||
|
/proc/weakref(datum/D)
|
||||||
|
if(D.gcDestroyed)
|
||||||
|
return
|
||||||
|
if(!D.weakref)
|
||||||
|
D.weakref = new /datum/weakref(D)
|
||||||
|
return D.weakref
|
||||||
|
|
||||||
|
/datum/weakref
|
||||||
|
var/ref
|
||||||
|
|
||||||
|
/datum/weakref/New(datum/D)
|
||||||
|
ref = "\ref[D]"
|
||||||
|
|
||||||
|
/datum/weakref/Destroy()
|
||||||
|
// A weakref datum should not be manually destroyed as it is a shared resource,
|
||||||
|
// rather it should be automatically collected by the BYOND GC when all references are gone.
|
||||||
|
return 0
|
||||||
|
|
||||||
|
/datum/weakref/proc/resolve()
|
||||||
|
var/datum/D = locate(ref)
|
||||||
|
if(D && D.weakref == src)
|
||||||
|
return D
|
||||||
|
return null
|
||||||
@@ -265,20 +265,17 @@ update_flag
|
|||||||
return src.attack_hand(user)
|
return src.attack_hand(user)
|
||||||
|
|
||||||
/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
|
/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
|
||||||
return src.ui_interact(user)
|
return src.tg_ui_interact(user)
|
||||||
|
|
||||||
/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
/obj/machinery/portable_atmospherics/canister/ui_data(mob/user)
|
||||||
if (src.destroyed)
|
var/list/data = list()
|
||||||
return
|
|
||||||
|
|
||||||
// this is the data which will be sent to the ui
|
|
||||||
var/data[0]
|
|
||||||
data["name"] = name
|
data["name"] = name
|
||||||
data["canLabel"] = can_label ? 1 : 0
|
data["canLabel"] = can_label ? 1 : 0
|
||||||
data["portConnected"] = connected_port ? 1 : 0
|
data["portConnected"] = connected_port ? 1 : 0
|
||||||
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
|
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
|
||||||
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
|
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
|
||||||
data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
|
data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
|
||||||
|
data["defaultReleasePressure"] = ONE_ATMOSPHERE
|
||||||
data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
|
data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
|
||||||
data["valveOpen"] = valve_open ? 1 : 0
|
data["valveOpen"] = valve_open ? 1 : 0
|
||||||
|
|
||||||
@@ -286,83 +283,70 @@ update_flag
|
|||||||
if (holding)
|
if (holding)
|
||||||
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
|
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
|
||||||
|
|
||||||
// update the ui if it exists, returns null if no ui is passed/found
|
return data
|
||||||
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
|
|
||||||
if (!ui)
|
|
||||||
// the ui does not exist, so we'll create a new() one
|
|
||||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
|
||||||
ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400)
|
|
||||||
// when the ui is first opened this is the data it will use
|
|
||||||
ui.set_initial_data(data)
|
|
||||||
// open the new ui window
|
|
||||||
ui.open()
|
|
||||||
// auto update every Master Controller tick
|
|
||||||
ui.set_auto_update(1)
|
|
||||||
|
|
||||||
/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)
|
/obj/machinery/portable_atmospherics/canister/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = tg_physical_state)
|
||||||
|
if (src.destroyed)
|
||||||
//Do not use "if(..()) return" here, canisters will stop working in unpowered areas like space or on the derelict. // yeah but without SOME sort of Topic check any dick can mess with them via exploits as he pleases -walter0o
|
|
||||||
//First comment might be outdated.
|
|
||||||
if (!istype(src.loc, /turf))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) // exploit protection -walter0o
|
|
||||||
usr << browse(null, "window=canister")
|
|
||||||
onclose(usr, "canister")
|
|
||||||
return
|
return
|
||||||
|
ui = tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
|
if(!ui)
|
||||||
|
ui = new(user, src, ui_key, "canister", name, 400, 400, master_ui, state)
|
||||||
|
ui.open()
|
||||||
|
|
||||||
if(href_list["toggle"])
|
/obj/machinery/portable_atmospherics/canister/ui_status(mob/user, datum/ui_state/state)
|
||||||
if (valve_open)
|
if(!istype(src.loc, /turf))
|
||||||
if (holding)
|
return UI_CLOSE
|
||||||
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
|
return ..()
|
||||||
else
|
|
||||||
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the <font color='red'><b>air</b></font><br>"
|
|
||||||
else
|
|
||||||
if (holding)
|
|
||||||
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the [holding]<br>"
|
|
||||||
else
|
|
||||||
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the <font color='red'><b>air</b></font><br>"
|
|
||||||
log_open()
|
|
||||||
valve_open = !valve_open
|
|
||||||
|
|
||||||
if (href_list["remove_tank"])
|
/obj/machinery/portable_atmospherics/canister/ui_act(action, params)
|
||||||
if(holding)
|
switch(action)
|
||||||
|
if("relabel")
|
||||||
|
if (can_label)
|
||||||
|
var/list/colors = list(\
|
||||||
|
"\[N2O\]" = "redws", \
|
||||||
|
"\[N2\]" = "red", \
|
||||||
|
"\[O2\]" = "blue", \
|
||||||
|
"\[Phoron\]" = "orange", \
|
||||||
|
"\[CO2\]" = "black", \
|
||||||
|
"\[Air\]" = "grey", \
|
||||||
|
"\[CAUTION\]" = "yellow", \
|
||||||
|
)
|
||||||
|
var/label = input("Choose canister label", "Gas canister") as null|anything in colors
|
||||||
|
if (label)
|
||||||
|
src.canister_color = colors[label]
|
||||||
|
src.icon_state = colors[label]
|
||||||
|
src.name = "\improper Canister: [label]"
|
||||||
|
if("pressure")
|
||||||
|
var/diff = text2num(params["adjust"])
|
||||||
|
if(diff > 0)
|
||||||
|
release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
|
||||||
|
else
|
||||||
|
release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)
|
||||||
|
|
||||||
|
if("valve")
|
||||||
if (valve_open)
|
if (valve_open)
|
||||||
valve_open = 0
|
if (holding)
|
||||||
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
|
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
|
||||||
if(istype(holding, /obj/item/weapon/tank))
|
else
|
||||||
holding.manipulated_by = usr.real_name
|
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the <font color='red'><b>air</b></font><br>"
|
||||||
holding.loc = loc
|
else
|
||||||
holding = null
|
if (holding)
|
||||||
|
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the [holding]<br>"
|
||||||
|
else
|
||||||
|
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the <font color='red'><b>air</b></font><br>"
|
||||||
|
log_open()
|
||||||
|
valve_open = !valve_open
|
||||||
|
|
||||||
if (href_list["pressure_adj"])
|
if("eject")
|
||||||
var/diff = text2num(href_list["pressure_adj"])
|
if(holding)
|
||||||
if(diff > 0)
|
if (valve_open)
|
||||||
release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
|
valve_open = 0
|
||||||
else
|
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
|
||||||
release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)
|
if(istype(holding, /obj/item/weapon/tank))
|
||||||
|
holding.manipulated_by = usr.real_name
|
||||||
if (href_list["relabel"])
|
holding.loc = loc
|
||||||
if (can_label)
|
holding = null
|
||||||
var/list/colors = list(\
|
return TRUE
|
||||||
"\[N2O\]" = "redws", \
|
|
||||||
"\[N2\]" = "red", \
|
|
||||||
"\[O2\]" = "blue", \
|
|
||||||
"\[Phoron\]" = "orange", \
|
|
||||||
"\[CO2\]" = "black", \
|
|
||||||
"\[Air\]" = "grey", \
|
|
||||||
"\[CAUTION\]" = "yellow", \
|
|
||||||
)
|
|
||||||
var/label = input("Choose canister label", "Gas canister") as null|anything in colors
|
|
||||||
if (label)
|
|
||||||
src.canister_color = colors[label]
|
|
||||||
src.icon_state = colors[label]
|
|
||||||
src.name = "Canister: [label]"
|
|
||||||
|
|
||||||
src.add_fingerprint(usr)
|
|
||||||
update_icon()
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
/obj/machinery/portable_atmospherics/canister/phoron/New()
|
/obj/machinery/portable_atmospherics/canister/phoron/New()
|
||||||
..()
|
..()
|
||||||
|
|||||||
@@ -11,103 +11,95 @@
|
|||||||
req_access = list(access_engine)
|
req_access = list(access_engine)
|
||||||
|
|
||||||
var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken
|
var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken
|
||||||
var/list/conf_access = null
|
var/list/conf_access = list()
|
||||||
var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access
|
var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access
|
||||||
var/last_configurator = null
|
var/last_configurator = null
|
||||||
var/locked = 1
|
var/locked = 1
|
||||||
|
|
||||||
attack_self(mob/user as mob)
|
|
||||||
if (!ishuman(user) && !istype(user,/mob/living/silicon/robot))
|
|
||||||
return ..(user)
|
|
||||||
|
|
||||||
var/t1 = text("<B>Access control</B><br>\n")
|
|
||||||
|
|
||||||
if (last_configurator)
|
/obj/item/weapon/airlock_electronics/attack_self(mob/user as mob)
|
||||||
t1 += "Operator: [last_configurator]<br>"
|
if (!ishuman(user) && !istype(user,/mob/living/silicon/robot))
|
||||||
|
return ..(user)
|
||||||
|
|
||||||
if (locked)
|
tg_ui_interact(user)
|
||||||
t1 += "<a href='?src=\ref[src];login=1'>Swipe ID</a><hr>"
|
|
||||||
else
|
|
||||||
t1 += "<a href='?src=\ref[src];logout=1'>Block</a><hr>"
|
|
||||||
|
|
||||||
t1 += "Access requirement is set to "
|
|
||||||
t1 += one_access ? "<a style='color: green' href='?src=\ref[src];one_access=1'>ONE</a><hr>" : "<a style='color: red' href='?src=\ref[src];one_access=1'>ALL</a><hr>"
|
|
||||||
|
|
||||||
t1 += conf_access == null ? "<font color=red>All</font><br>" : "<a href='?src=\ref[src];access=all'>All</a><br>"
|
|
||||||
|
|
||||||
t1 += "<br>"
|
//tgui interact code generously lifted from tgstation.
|
||||||
|
/obj/item/weapon/airlock_electronics/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
|
||||||
|
datum/tgui/master_ui = null, datum/ui_state/state = hands_state)
|
||||||
|
|
||||||
var/list/accesses = get_all_station_access()
|
tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
for (var/acc in accesses)
|
if(!ui)
|
||||||
var/aname = get_access_desc(acc)
|
ui = new(user, src, ui_key, "airlock_electronics", src.name, 1000, 500, master_ui, state)
|
||||||
|
ui.open()
|
||||||
|
|
||||||
if (!conf_access || !conf_access.len || !(acc in conf_access))
|
/obj/item/weapon/airlock_electronics/ui_data(mob/user)
|
||||||
t1 += "<a href='?src=\ref[src];access=[acc]'>[aname]</a><br>"
|
var/list/data = list()
|
||||||
else if(one_access)
|
var/list/regions = list()
|
||||||
t1 += "<a style='color: green' href='?src=\ref[src];access=[acc]'>[aname]</a><br>"
|
|
||||||
else
|
|
||||||
t1 += "<a style='color: red' href='?src=\ref[src];access=[acc]'>[aname]</a><br>"
|
|
||||||
|
|
||||||
t1 += text("<p><a href='?src=\ref[];close=1'>Close</a></p>\n", src)
|
for(var/i in ACCESS_REGION_SECURITY to ACCESS_REGION_SUPPLY) //code/game/jobs/_access_defs.dm
|
||||||
|
var/list/region = list()
|
||||||
|
var/list/accesses = list()
|
||||||
|
for(var/j in get_region_accesses(i))
|
||||||
|
var/list/access = list()
|
||||||
|
access["name"] = get_access_desc(j)
|
||||||
|
access["id"] = j
|
||||||
|
access["req"] = (j in src.conf_access)
|
||||||
|
accesses[++accesses.len] = access
|
||||||
|
region["name"] = get_region_accesses_name(i)
|
||||||
|
region["accesses"] = accesses
|
||||||
|
regions[++regions.len] = region
|
||||||
|
data["regions"] = regions
|
||||||
|
data["oneAccess"] = one_access
|
||||||
|
data["locked"] = locked
|
||||||
|
|
||||||
user << browse(t1, "window=airlock_electronics")
|
return data
|
||||||
onclose(user, "airlock")
|
|
||||||
|
|
||||||
Topic(href, href_list)
|
/obj/item/weapon/airlock_electronics/ui_act(action, params)
|
||||||
..()
|
if(..())
|
||||||
if (usr.stat || usr.restrained() || (!ishuman(usr) && !istype(usr,/mob/living/silicon)))
|
return TRUE
|
||||||
return
|
switch(action)
|
||||||
if (href_list["close"])
|
if("clear")
|
||||||
usr << browse(null, "window=airlock")
|
conf_access = list()
|
||||||
return
|
one_access = 0
|
||||||
|
return TRUE
|
||||||
if (href_list["login"])
|
if("one_access")
|
||||||
|
one_access = !one_access
|
||||||
|
return TRUE
|
||||||
|
if("set")
|
||||||
|
var/access = text2num(params["access"])
|
||||||
|
if (!(access in conf_access))
|
||||||
|
conf_access += access
|
||||||
|
else
|
||||||
|
conf_access -= access
|
||||||
|
return TRUE
|
||||||
|
if("unlock")
|
||||||
if(istype(usr,/mob/living/silicon))
|
if(istype(usr,/mob/living/silicon))
|
||||||
src.locked = 0
|
locked = 0
|
||||||
src.last_configurator = usr.name
|
last_configurator = usr.name
|
||||||
|
return TRUE
|
||||||
else
|
else
|
||||||
var/obj/item/I = usr.get_active_hand()
|
var/obj/item/I = usr.get_active_hand()
|
||||||
if (istype(I, /obj/item/device/pda))
|
if (istype(I, /obj/item/device/pda))
|
||||||
var/obj/item/device/pda/pda = I
|
var/obj/item/device/pda/pda = I
|
||||||
I = pda.id
|
I = pda.id
|
||||||
|
if(!istype(I, /obj/item/weapon/card/id))
|
||||||
|
usr << "<span class='warning'>[\src] flashes a yellow LED near the ID scanner. Did you remember to scan your ID or PDA?</span>"
|
||||||
|
return TRUE
|
||||||
if (I && src.check_access(I))
|
if (I && src.check_access(I))
|
||||||
src.locked = 0
|
locked = 0
|
||||||
src.last_configurator = I:registered_name
|
last_configurator = I:registered_name
|
||||||
|
|
||||||
if (locked)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (href_list["logout"])
|
|
||||||
locked = 1
|
|
||||||
|
|
||||||
if (href_list["one_access"])
|
|
||||||
one_access = !one_access
|
|
||||||
|
|
||||||
if (href_list["access"])
|
|
||||||
toggle_access(href_list["access"])
|
|
||||||
|
|
||||||
attack_self(usr)
|
|
||||||
|
|
||||||
proc
|
|
||||||
toggle_access(var/acc)
|
|
||||||
if (acc == "all")
|
|
||||||
conf_access = null
|
|
||||||
else
|
|
||||||
var/req = text2num(acc)
|
|
||||||
|
|
||||||
if (conf_access == null)
|
|
||||||
conf_access = list()
|
|
||||||
|
|
||||||
if (!(req in conf_access))
|
|
||||||
conf_access += req
|
|
||||||
else
|
else
|
||||||
conf_access -= req
|
usr << "<span class='warning'>[\src] flashes a red LED near the ID scanner, indicating your access has been denied.</span>"
|
||||||
if (!conf_access.len)
|
return TRUE
|
||||||
conf_access = null
|
if("lock")
|
||||||
|
locked = 1
|
||||||
|
. = TRUE
|
||||||
|
|
||||||
/obj/item/weapon/airlock_electronics/secure
|
/obj/item/weapon/airlock_electronics/secure
|
||||||
name = "secure airlock electronics"
|
name = "secure airlock electronics"
|
||||||
desc = "designed to be somewhat more resistant to hacking than standard electronics."
|
desc = "designed to be somewhat more resistant to hacking than standard electronics."
|
||||||
origin_tech = list(TECH_DATA = 2)
|
origin_tech = list(TECH_DATA = 2)
|
||||||
secure = 1
|
secure = 1
|
||||||
@@ -53,7 +53,6 @@
|
|||||||
return
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
//Main door timer loop, if it's timing and time is >0 reduce time by 1.
|
//Main door timer loop, if it's timing and time is >0 reduce time by 1.
|
||||||
// if it's less than 0, open door, reset timer
|
// if it's less than 0, open door, reset timer
|
||||||
// update the door_timer window and the icon
|
// update the door_timer window and the icon
|
||||||
@@ -68,12 +67,10 @@
|
|||||||
if(timeleft > 1e5)
|
if(timeleft > 1e5)
|
||||||
src.releasetime = 0
|
src.releasetime = 0
|
||||||
|
|
||||||
|
|
||||||
if(world.timeofday > src.releasetime)
|
if(world.timeofday > src.releasetime)
|
||||||
src.timer_end() // open doors, reset timer, clear status screen
|
src.timer_end() // open doors, reset timer, clear status screen
|
||||||
src.timing = 0
|
src.timing = 0
|
||||||
|
|
||||||
src.updateUsrDialog()
|
|
||||||
src.update_icon()
|
src.update_icon()
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -81,14 +78,12 @@
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
// has the door power situation changed, if so update icon.
|
// has the door power situation changed, if so update icon.
|
||||||
/obj/machinery/door_timer/power_change()
|
/obj/machinery/door_timer/power_change()
|
||||||
..()
|
..()
|
||||||
update_icon()
|
update_icon()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
// open/closedoor checks if door_timer has power, if so it checks if the
|
// open/closedoor checks if door_timer has power, if so it checks if the
|
||||||
// linked door is open/closed (by density) then opens it/closes it.
|
// linked door is open/closed (by density) then opens it/closes it.
|
||||||
|
|
||||||
@@ -99,6 +94,9 @@
|
|||||||
// Set releasetime
|
// Set releasetime
|
||||||
releasetime = world.timeofday + timetoset
|
releasetime = world.timeofday + timetoset
|
||||||
|
|
||||||
|
//set timing
|
||||||
|
timing = 1
|
||||||
|
|
||||||
for(var/obj/machinery/door/window/brigdoor/door in targets)
|
for(var/obj/machinery/door/window/brigdoor/door in targets)
|
||||||
if(door.density) continue
|
if(door.density) continue
|
||||||
spawn(0)
|
spawn(0)
|
||||||
@@ -111,7 +109,6 @@
|
|||||||
C.icon_state = C.icon_locked
|
C.icon_state = C.icon_locked
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
// Opens and unlocks doors, power check
|
// Opens and unlocks doors, power check
|
||||||
/obj/machinery/door_timer/proc/timer_end()
|
/obj/machinery/door_timer/proc/timer_end()
|
||||||
if(stat & (NOPOWER|BROKEN)) return 0
|
if(stat & (NOPOWER|BROKEN)) return 0
|
||||||
@@ -119,6 +116,9 @@
|
|||||||
// Reset releasetime
|
// Reset releasetime
|
||||||
releasetime = 0
|
releasetime = 0
|
||||||
|
|
||||||
|
//reset timing
|
||||||
|
timing = 0
|
||||||
|
|
||||||
for(var/obj/machinery/door/window/brigdoor/door in targets)
|
for(var/obj/machinery/door/window/brigdoor/door in targets)
|
||||||
if(!door.density) continue
|
if(!door.density) continue
|
||||||
spawn(0)
|
spawn(0)
|
||||||
@@ -132,7 +132,6 @@
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
// Check for releasetime timeleft
|
// Check for releasetime timeleft
|
||||||
/obj/machinery/door_timer/proc/timeleft()
|
/obj/machinery/door_timer/proc/timeleft()
|
||||||
. = (releasetime - world.timeofday)/10
|
. = (releasetime - world.timeofday)/10
|
||||||
@@ -152,116 +151,62 @@
|
|||||||
/obj/machinery/door_timer/attack_ai(var/mob/user as mob)
|
/obj/machinery/door_timer/attack_ai(var/mob/user as mob)
|
||||||
return src.attack_hand(user)
|
return src.attack_hand(user)
|
||||||
|
|
||||||
|
|
||||||
//Allows humans to use door_timer
|
|
||||||
//Opens dialog window when someone clicks on door timer
|
|
||||||
// Allows altering timer and the timing boolean.
|
|
||||||
// Flasher activation limited to 150 seconds
|
|
||||||
/obj/machinery/door_timer/attack_hand(var/mob/user as mob)
|
/obj/machinery/door_timer/attack_hand(var/mob/user as mob)
|
||||||
if(..())
|
tg_ui_interact(user)
|
||||||
return
|
|
||||||
|
|
||||||
// Used for the 'time left' display
|
/obj/machinery/door_timer/ui_data(mob/user)
|
||||||
var/second = round(timeleft() % 60)
|
var/list/data = list()
|
||||||
var/minute = round((timeleft() - second) / 60)
|
|
||||||
|
|
||||||
// Used for 'set timer'
|
data["timing"] = timing
|
||||||
var/setsecond = round((timetoset / 10) % 60)
|
data["releasetime"] = releasetime
|
||||||
var/setminute = round(((timetoset / 10) - setsecond) / 60)
|
data["timetoset"] = timetoset
|
||||||
|
data["timeleft"] = timeleft()
|
||||||
|
|
||||||
user.set_machine(src)
|
var/list/flashes = list()
|
||||||
|
|
||||||
// dat
|
for(var/obj/machinery/flasher/flash in targets)
|
||||||
var/dat = "<HTML><BODY><TT>"
|
var/list/flashdata = list()
|
||||||
|
if(flash.last_flash && (flash.last_flash + 150) > world.time)
|
||||||
dat += "<HR>Timer System:</hr>"
|
flashdata["status"] = 0
|
||||||
dat += " <b>Door [src.id] controls</b><br/>"
|
|
||||||
|
|
||||||
// Start/Stop timer
|
|
||||||
if (src.timing)
|
|
||||||
dat += "<a href='?src=\ref[src];timing=0'>Stop Timer and open door</a><br/>"
|
|
||||||
else
|
|
||||||
dat += "<a href='?src=\ref[src];timing=1'>Activate Timer and close door</a><br/>"
|
|
||||||
|
|
||||||
// Time Left display (uses releasetime)
|
|
||||||
dat += "Time Left: [(minute ? text("[minute]:") : null)][second] <br/>"
|
|
||||||
dat += "<br/>"
|
|
||||||
|
|
||||||
// Set Timer display (uses timetoset)
|
|
||||||
if(src.timing)
|
|
||||||
dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] <a href='?src=\ref[src];change=1'>Set</a><br/>"
|
|
||||||
else
|
|
||||||
dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond]<br/>"
|
|
||||||
|
|
||||||
// Controls
|
|
||||||
dat += "<a href='?src=\ref[src];tp=-60'>-</a> <a href='?src=\ref[src];tp=-1'>-</a> <a href='?src=\ref[src];tp=1'>+</a> <A href='?src=\ref[src];tp=60'>+</a><br/>"
|
|
||||||
|
|
||||||
// Mounted flash controls
|
|
||||||
for(var/obj/machinery/flasher/F in targets)
|
|
||||||
if(F.last_flash && (F.last_flash + 150) > world.time)
|
|
||||||
dat += "<br/><A href='?src=\ref[src];fc=1'>Flash Charging</A>"
|
|
||||||
else
|
else
|
||||||
dat += "<br/><A href='?src=\ref[src];fc=1'>Activate Flash</A>"
|
flashdata["status"] = 1
|
||||||
|
flashes[++flashes.len] = flashdata
|
||||||
|
|
||||||
dat += "<br/><br/><a href='?src=\ref[user];mach_close=computer'>Close</a>"
|
data["flashes"] = flashes
|
||||||
dat += "</TT></BODY></HTML>"
|
return data
|
||||||
|
|
||||||
user << browse(dat, "window=computer;size=400x500")
|
/obj/machinery/door_timer/ui_act(action, params)
|
||||||
onclose(user, "computer")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
//Function for using door_timer dialog input, checks if user has permission
|
|
||||||
// href_list to
|
|
||||||
// "timing" turns on timer
|
|
||||||
// "tp" value to modify timer
|
|
||||||
// "fc" activates flasher
|
|
||||||
// "change" resets the timer to the timetoset amount while the timer is counting down
|
|
||||||
// Also updates dialog window and timer icon
|
|
||||||
/obj/machinery/door_timer/Topic(href, href_list)
|
|
||||||
if(..())
|
if(..())
|
||||||
return
|
return TRUE
|
||||||
if(!src.allowed(usr))
|
|
||||||
return
|
|
||||||
|
|
||||||
usr.set_machine(src)
|
|
||||||
|
|
||||||
if(href_list["timing"])
|
|
||||||
src.timing = text2num(href_list["timing"])
|
|
||||||
|
|
||||||
if(src.timing)
|
|
||||||
src.timer_start()
|
|
||||||
else
|
|
||||||
src.timer_end()
|
|
||||||
|
|
||||||
else
|
|
||||||
if(href_list["tp"]) //adjust timer, close door if not already closed
|
|
||||||
var/tp = text2num(href_list["tp"])
|
|
||||||
var/addtime = (timetoset / 10)
|
|
||||||
addtime += tp
|
|
||||||
addtime = min(max(round(addtime), 0), 3600)
|
|
||||||
|
|
||||||
timeset(addtime)
|
|
||||||
|
|
||||||
if(href_list["fc"])
|
|
||||||
for(var/obj/machinery/flasher/F in targets)
|
|
||||||
F.flash()
|
|
||||||
|
|
||||||
if(href_list["change"])
|
|
||||||
src.timer_start()
|
|
||||||
|
|
||||||
src.add_fingerprint(usr)
|
src.add_fingerprint(usr)
|
||||||
src.updateUsrDialog()
|
|
||||||
|
if(!src.allowed(usr))
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
if("start")
|
||||||
|
if(timetoset > 18000)
|
||||||
|
log_admin("[key_name(usr)] has started a brig timer over 30 minutes in length!")
|
||||||
|
message_admins("[key_name_admin(usr)] has started a brig timer over 30 minutes in length!")
|
||||||
|
timer_start()
|
||||||
|
if("stop")
|
||||||
|
timer_end()
|
||||||
|
if("flash")
|
||||||
|
for(var/obj/machinery/flasher/F in targets)
|
||||||
|
F.flash()
|
||||||
|
if("time")
|
||||||
|
timetoset += text2num(params["adjust"])
|
||||||
|
timetoset = Clamp(timetoset, 0, 36000)
|
||||||
|
|
||||||
src.update_icon()
|
src.update_icon()
|
||||||
|
return TRUE
|
||||||
|
|
||||||
/* if(src.timing)
|
/obj/machinery/door_timer/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||||
src.timer_start()
|
ui = tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
|
if(!ui)
|
||||||
else
|
ui = new(user, src, ui_key, "brig_timer", name , 300, 150, master_ui, state)
|
||||||
src.timer_end() */
|
ui.open()
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
//icon update function
|
//icon update function
|
||||||
// if NOPOWER, display blank
|
// if NOPOWER, display blank
|
||||||
@@ -282,7 +227,9 @@
|
|||||||
disp2 = "Error"
|
disp2 = "Error"
|
||||||
update_display(disp1, disp2)
|
update_display(disp1, disp2)
|
||||||
else
|
else
|
||||||
if(maptext) maptext = ""
|
if(maptext)
|
||||||
|
maptext = ""
|
||||||
|
update_display("Set","Time") // would be nice to have some default printed text
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
var/close_door_at = 0 //When to automatically close the door, if possible
|
var/close_door_at = 0 //When to automatically close the door, if possible
|
||||||
|
|
||||||
//Multi-tile doors
|
//Multi-tile doors
|
||||||
dir = EAST
|
|
||||||
var/width = 1
|
var/width = 1
|
||||||
|
|
||||||
// turf animation
|
// turf animation
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//Terribly sorry for the code doubling, but things go derpy otherwise.
|
//Terribly sorry for the code doubling, but things go derpy otherwise.
|
||||||
/obj/machinery/door/airlock/multi_tile
|
/obj/machinery/door/airlock/multi_tile
|
||||||
width = 2
|
width = 2
|
||||||
|
dir = EAST
|
||||||
|
|
||||||
/obj/machinery/door/airlock/multi_tile/New()
|
/obj/machinery/door/airlock/multi_tile/New()
|
||||||
..()
|
..()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
title = title_name
|
title = title_name
|
||||||
sound = audio
|
sound = audio
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/
|
/obj/machinery/media/jukebox
|
||||||
name = "space jukebox"
|
name = "space jukebox"
|
||||||
icon = 'icons/obj/jukebox.dmi'
|
icon = 'icons/obj/jukebox.dmi'
|
||||||
icon_state = "jukebox2-nopower"
|
icon_state = "jukebox2-nopower"
|
||||||
@@ -44,10 +44,11 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
|
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
|
||||||
component_parts += new /obj/item/stack/cable_coil(src, 5)
|
component_parts += new /obj/item/stack/cable_coil(src, 5)
|
||||||
RefreshParts()
|
RefreshParts()
|
||||||
|
update_icon()
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/Destroy()
|
/obj/machinery/media/jukebox/Destroy()
|
||||||
StopPlaying()
|
StopPlaying()
|
||||||
..()
|
. = ..()
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/power_change()
|
/obj/machinery/media/jukebox/power_change()
|
||||||
if(!powered(power_channel) || !anchored)
|
if(!powered(power_channel) || !anchored)
|
||||||
@@ -74,10 +75,7 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
else
|
else
|
||||||
overlays += "[state_base]-running"
|
overlays += "[state_base]-running"
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/Topic(href, href_list)
|
/obj/machinery/media/jukebox/interact(mob/user)
|
||||||
if(..() || !(Adjacent(usr) || istype(usr, /mob/living/silicon)))
|
|
||||||
return
|
|
||||||
|
|
||||||
if(!anchored)
|
if(!anchored)
|
||||||
usr << "<span class='warning'>You must secure \the [src] first.</span>"
|
usr << "<span class='warning'>You must secure \the [src] first.</span>"
|
||||||
return
|
return
|
||||||
@@ -86,70 +84,74 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
usr << "\The [src] doesn't appear to function."
|
usr << "\The [src] doesn't appear to function."
|
||||||
return
|
return
|
||||||
|
|
||||||
if(href_list["change_track"])
|
tg_ui_interact(user)
|
||||||
for(var/datum/track/T in tracks)
|
|
||||||
if(T.title == href_list["title"])
|
|
||||||
current_track = T
|
|
||||||
StartPlaying()
|
|
||||||
break
|
|
||||||
else if(href_list["stop"])
|
|
||||||
StopPlaying()
|
|
||||||
else if(href_list["play"])
|
|
||||||
if(emagged)
|
|
||||||
playsound(src.loc, 'sound/items/AirHorn.ogg', 100, 1)
|
|
||||||
for(var/mob/living/carbon/M in ohearers(6, src))
|
|
||||||
if(M.get_ear_protection() >= 2)
|
|
||||||
continue
|
|
||||||
M.sleeping = 0
|
|
||||||
M.stuttering += 20
|
|
||||||
M.ear_deaf += 30
|
|
||||||
M.Weaken(3)
|
|
||||||
if(prob(30))
|
|
||||||
M.Stun(10)
|
|
||||||
M.Paralyse(4)
|
|
||||||
else
|
|
||||||
M.make_jittery(500)
|
|
||||||
spawn(15)
|
|
||||||
explode()
|
|
||||||
else if(current_track == null)
|
|
||||||
usr << "No track selected."
|
|
||||||
else
|
|
||||||
StartPlaying()
|
|
||||||
|
|
||||||
return 1
|
/obj/machinery/media/jukebox/ui_status(mob/user, datum/ui_state/state)
|
||||||
|
if(!anchored || inoperable())
|
||||||
|
return UI_CLOSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/interact(mob/user)
|
/obj/machinery/media/jukebox/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = tg_default_state)
|
||||||
if(stat & (NOPOWER|BROKEN))
|
ui = tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
usr << "\The [src] doesn't appear to function."
|
if(!ui)
|
||||||
return
|
ui = new(user, src, ui_key, "jukebox", "RetroBox - Space Style", 340, 440, master_ui, state)
|
||||||
|
|
||||||
ui_interact(user)
|
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/ui_interact(mob/user, ui_key = "jukebox", var/datum/nanoui/ui = null, var/force_open = 1)
|
|
||||||
var/title = "RetroBox - Space Style"
|
|
||||||
var/data[0]
|
|
||||||
|
|
||||||
if(!(stat & (NOPOWER|BROKEN)))
|
|
||||||
data["current_track"] = current_track != null ? current_track.title : ""
|
|
||||||
data["playing"] = playing
|
|
||||||
|
|
||||||
var/list/nano_tracks = new
|
|
||||||
for(var/datum/track/T in tracks)
|
|
||||||
nano_tracks[++nano_tracks.len] = list("track" = T.title)
|
|
||||||
|
|
||||||
data["tracks"] = nano_tracks
|
|
||||||
|
|
||||||
// update the ui if it exists, returns null if no ui is passed/found
|
|
||||||
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
|
|
||||||
if (!ui)
|
|
||||||
// the ui does not exist, so we'll create a new() one
|
|
||||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
|
||||||
ui = new(user, src, ui_key, "jukebox.tmpl", title, 450, 600)
|
|
||||||
// when the ui is first opened this is the data it will use
|
|
||||||
ui.set_initial_data(data)
|
|
||||||
// open the new ui window
|
|
||||||
ui.open()
|
ui.open()
|
||||||
|
|
||||||
|
/obj/machinery/media/jukebox/ui_data()
|
||||||
|
var/list/juke_tracks = new
|
||||||
|
for(var/datum/track/T in tracks)
|
||||||
|
juke_tracks.Add(T.title)
|
||||||
|
|
||||||
|
var/list/data = list(
|
||||||
|
"current_track" = current_track != null ? current_track.title : "No track selected",
|
||||||
|
"playing" = playing,
|
||||||
|
"tracks" = juke_tracks
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
/obj/machinery/media/jukebox/ui_act(action, params)
|
||||||
|
if(..())
|
||||||
|
return TRUE
|
||||||
|
switch(action)
|
||||||
|
if("change_track")
|
||||||
|
for(var/datum/track/T in tracks)
|
||||||
|
if(T.title == params["title"])
|
||||||
|
current_track = T
|
||||||
|
StartPlaying()
|
||||||
|
break
|
||||||
|
. = TRUE
|
||||||
|
if("stop")
|
||||||
|
StopPlaying()
|
||||||
|
. = TRUE
|
||||||
|
if("play")
|
||||||
|
if(emagged)
|
||||||
|
emag_play()
|
||||||
|
else if(!current_track)
|
||||||
|
usr << "No track selected."
|
||||||
|
else
|
||||||
|
StartPlaying()
|
||||||
|
. = TRUE
|
||||||
|
|
||||||
|
/obj/machinery/media/jukebox/proc/emag_play()
|
||||||
|
playsound(loc, 'sound/items/AirHorn.ogg', 100, 1)
|
||||||
|
for(var/mob/living/carbon/M in ohearers(6, src))
|
||||||
|
if(istype(M, /mob/living/carbon/human))
|
||||||
|
var/mob/living/carbon/human/H = M
|
||||||
|
if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs))
|
||||||
|
continue
|
||||||
|
M.sleeping = 0
|
||||||
|
M.stuttering += 20
|
||||||
|
M.ear_deaf += 30
|
||||||
|
M.Weaken(3)
|
||||||
|
if(prob(30))
|
||||||
|
M.Stun(10)
|
||||||
|
M.Paralyse(4)
|
||||||
|
else
|
||||||
|
M.make_jittery(500)
|
||||||
|
spawn(15)
|
||||||
|
explode()
|
||||||
|
|
||||||
/obj/machinery/media/jukebox/attack_ai(mob/user as mob)
|
/obj/machinery/media/jukebox/attack_ai(mob/user as mob)
|
||||||
return src.attack_hand(user)
|
return src.attack_hand(user)
|
||||||
|
|
||||||
@@ -172,10 +174,6 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
|
/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
|
||||||
src.add_fingerprint(user)
|
src.add_fingerprint(user)
|
||||||
|
|
||||||
if(default_deconstruction_screwdriver(user, W))
|
|
||||||
return
|
|
||||||
if(default_deconstruction_crowbar(user, W))
|
|
||||||
return
|
|
||||||
if(istype(W, /obj/item/weapon/wrench))
|
if(istype(W, /obj/item/weapon/wrench))
|
||||||
if(playing)
|
if(playing)
|
||||||
StopPlaying()
|
StopPlaying()
|
||||||
@@ -200,7 +198,6 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
// Always kill the current sound
|
// Always kill the current sound
|
||||||
for(var/mob/living/M in mobs_in_area(main_area))
|
for(var/mob/living/M in mobs_in_area(main_area))
|
||||||
M << sound(null, channel = 1)
|
M << sound(null, channel = 1)
|
||||||
|
|
||||||
main_area.forced_ambience = null
|
main_area.forced_ambience = null
|
||||||
playing = 0
|
playing = 0
|
||||||
update_use_power(1)
|
update_use_power(1)
|
||||||
@@ -220,4 +217,4 @@ datum/track/New(var/title_name, var/audio)
|
|||||||
|
|
||||||
playing = 1
|
playing = 1
|
||||||
update_use_power(2)
|
update_use_power(2)
|
||||||
update_icon()
|
update_icon()
|
||||||
@@ -90,6 +90,8 @@
|
|||||||
|
|
||||||
/datum/feed_network/proc/insert_message_in_channel(var/datum/feed_channel/FC, var/datum/feed_message/newMsg)
|
/datum/feed_network/proc/insert_message_in_channel(var/datum/feed_channel/FC, var/datum/feed_message/newMsg)
|
||||||
FC.messages += newMsg
|
FC.messages += newMsg
|
||||||
|
if(newMsg.img)
|
||||||
|
register_asset("newscaster_photo_[sanitize(FC.channel_name)]_[FC.messages.len].png", newMsg.img)
|
||||||
newMsg.parent_channel = FC
|
newMsg.parent_channel = FC
|
||||||
FC.update()
|
FC.update()
|
||||||
alert_readers(FC.announcement)
|
alert_readers(FC.announcement)
|
||||||
@@ -377,11 +379,12 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
|
|||||||
else
|
else
|
||||||
var/i = 0
|
var/i = 0
|
||||||
for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages)
|
for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages)
|
||||||
i++
|
++i
|
||||||
dat+="-[MESSAGE.body] <BR>"
|
dat+="-[MESSAGE.body] <BR>"
|
||||||
if(MESSAGE.img)
|
if(MESSAGE.img)
|
||||||
usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
|
var/resourc_name = "newscaster_photo_[sanitize(viewing_channel.channel_name)]_[i].png"
|
||||||
dat+="<img src='tmp_photo[i].png' width = '180'><BR>"
|
send_asset(usr.client, resourc_name)
|
||||||
|
dat+="<img src='[resourc_name]' width = '180'><BR>"
|
||||||
if(MESSAGE.caption)
|
if(MESSAGE.caption)
|
||||||
dat+="<FONT SIZE=1><B>[MESSAGE.caption]</B></FONT><BR>"
|
dat+="<FONT SIZE=1><B>[MESSAGE.caption]</B></FONT><BR>"
|
||||||
dat+="<BR>"
|
dat+="<BR>"
|
||||||
@@ -874,11 +877,12 @@ obj/item/weapon/newspaper/attack_self(mob/user as mob)
|
|||||||
dat+="<ul>"
|
dat+="<ul>"
|
||||||
var/i = 0
|
var/i = 0
|
||||||
for(var/datum/feed_message/MESSAGE in C.messages)
|
for(var/datum/feed_message/MESSAGE in C.messages)
|
||||||
i++
|
++i
|
||||||
dat+="-[MESSAGE.body] <BR>"
|
dat+="-[MESSAGE.body] <BR>"
|
||||||
if(MESSAGE.img)
|
if(MESSAGE.img)
|
||||||
user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
|
var/resourc_name = "newscaster_photo_[sanitize(C.channel_name)]_[i].png"
|
||||||
dat+="<img src='tmp_photo[i].png' width = '180'><BR>"
|
send_asset(user.client, resourc_name)
|
||||||
|
dat+="<img src='[resourc_name]' width = '180'><BR>"
|
||||||
dat+="<FONT SIZE=1>\[[MESSAGE.message_type] by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR><BR>"
|
dat+="<FONT SIZE=1>\[[MESSAGE.message_type] by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR><BR>"
|
||||||
dat+="</ul>"
|
dat+="</ul>"
|
||||||
if(scribble_page==curr_page)
|
if(scribble_page==curr_page)
|
||||||
|
|||||||
@@ -620,9 +620,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
|||||||
if(!FC.censored)
|
if(!FC.censored)
|
||||||
var/index = 0
|
var/index = 0
|
||||||
for(var/datum/feed_message/FM in FC.messages)
|
for(var/datum/feed_message/FM in FC.messages)
|
||||||
index++
|
++index
|
||||||
if(FM.img)
|
if(FM.img)
|
||||||
usr << browse_rsc(FM.img, "pda_news_tmp_photo_[feed["channel"]]_[index].png")
|
send_asset(usr.client, "newscaster_photo_[sanitize(FC.channel_name)]_[index].png")
|
||||||
// News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI
|
// News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI
|
||||||
var/body = replacetext(FM.body, "\n", "<br>")
|
var/body = replacetext(FM.body, "\n", "<br>")
|
||||||
messages[++messages.len] = list("author" = FM.author, "body" = body, "message_type" = FM.message_type, "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, "index" = index)
|
messages[++messages.len] = list("author" = FM.author, "body" = body, "message_type" = FM.message_type, "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, "index" = index)
|
||||||
@@ -651,6 +651,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
|||||||
|
|
||||||
//NOTE: graphic resources are loaded on client login
|
//NOTE: graphic resources are loaded on client login
|
||||||
/obj/item/device/pda/attack_self(mob/user as mob)
|
/obj/item/device/pda/attack_self(mob/user as mob)
|
||||||
|
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda)
|
||||||
|
assets.send(user)
|
||||||
|
|
||||||
user.set_machine(src)
|
user.set_machine(src)
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
|
|
||||||
/obj/attack_ghost(mob/user)
|
/obj/attack_ghost(mob/user)
|
||||||
ui_interact(user)
|
ui_interact(user)
|
||||||
|
tg_ui_interact(user)
|
||||||
..()
|
..()
|
||||||
|
|
||||||
/obj/proc/interact(mob/user)
|
/obj/proc/interact(mob/user)
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
return
|
return
|
||||||
|
|
||||||
if("marked datum")
|
if("marked datum")
|
||||||
current = holder.marked_datum
|
current = holder.marked_datum()
|
||||||
if(!current)
|
if(!current)
|
||||||
switch(alert("You do not currently have a marked datum; do you want to pass null instead?",, "Yes", "Cancel"))
|
switch(alert("You do not currently have a marked datum; do you want to pass null instead?",, "Yes", "Cancel"))
|
||||||
if("Yes")
|
if("Yes")
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ var/list/admin_datums = list()
|
|||||||
var/rights = 0
|
var/rights = 0
|
||||||
var/fakekey = null
|
var/fakekey = null
|
||||||
|
|
||||||
var/datum/marked_datum
|
var/datum/weakref/marked_datum_weak
|
||||||
|
|
||||||
var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description
|
var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description
|
||||||
var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders.
|
var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders.
|
||||||
var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel
|
var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel
|
||||||
var/admincaster_signature //What you'll sign the newsfeeds as
|
var/admincaster_signature //What you'll sign the newsfeeds as
|
||||||
|
|
||||||
|
/datum/admins/proc/marked_datum()
|
||||||
|
if(marked_datum_weak)
|
||||||
|
return marked_datum_weak.resolve()
|
||||||
|
|
||||||
/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey)
|
/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey)
|
||||||
if(!ckey)
|
if(!ckey)
|
||||||
error("Admin datum created without a ckey argument. Datum has been deleted")
|
error("Admin datum created without a ckey argument. Datum has been deleted")
|
||||||
|
|||||||
@@ -1603,6 +1603,7 @@
|
|||||||
where = "onfloor"
|
where = "onfloor"
|
||||||
|
|
||||||
if ( where == "inmarked" )
|
if ( where == "inmarked" )
|
||||||
|
var/marked_datum = marked_datum()
|
||||||
if ( !marked_datum )
|
if ( !marked_datum )
|
||||||
usr << "You don't have any object marked. Abandoning spawn."
|
usr << "You don't have any object marked. Abandoning spawn."
|
||||||
return
|
return
|
||||||
@@ -1620,7 +1621,7 @@
|
|||||||
if ("relative")
|
if ("relative")
|
||||||
target = locate(loc.x + X,loc.y + Y,loc.z + Z)
|
target = locate(loc.x + X,loc.y + Y,loc.z + Z)
|
||||||
if ( "inmarked" )
|
if ( "inmarked" )
|
||||||
target = marked_datum
|
target = marked_datum()
|
||||||
|
|
||||||
if(target)
|
if(target)
|
||||||
for (var/path in paths)
|
for (var/path in paths)
|
||||||
|
|||||||
@@ -27,20 +27,20 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
src.modify_variables(ticker)
|
src.modify_variables(ticker)
|
||||||
feedback_add_details("admin_verb","ETV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
feedback_add_details("admin_verb","ETV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||||
|
|
||||||
/client/proc/mod_list_add_ass() //haha
|
/client/proc/mod_list_add_ass()
|
||||||
|
|
||||||
var/class = "text"
|
var/class = "text"
|
||||||
if(src.holder && src.holder.marked_datum)
|
var/list/class_input = list("text","num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
||||||
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
|
if(src.holder)
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default","marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
else
|
if(marked_datum)
|
||||||
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
|
class_input += "marked datum ([marked_datum.type])"
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
|
||||||
|
|
||||||
|
class = input("What kind of variable?","Variable Type") as null|anything in class_input
|
||||||
if(!class)
|
if(!class)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
|
if(marked_datum && class == "marked datum ([marked_datum.type])")
|
||||||
class = "marked datum"
|
class = "marked datum"
|
||||||
|
|
||||||
var/var_value = null
|
var/var_value = null
|
||||||
@@ -69,7 +69,7 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
var_value = input("Pick icon:","Icon") as null|icon
|
var_value = input("Pick icon:","Icon") as null|icon
|
||||||
|
|
||||||
if("marked datum")
|
if("marked datum")
|
||||||
var_value = holder.marked_datum
|
var_value = holder.marked_datum()
|
||||||
|
|
||||||
if(!var_value) return
|
if(!var_value) return
|
||||||
|
|
||||||
@@ -79,17 +79,18 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
/client/proc/mod_list_add(var/list/L, atom/O, original_name, objectvar)
|
/client/proc/mod_list_add(var/list/L, atom/O, original_name, objectvar)
|
||||||
|
|
||||||
var/class = "text"
|
var/class = "text"
|
||||||
if(src.holder && src.holder.marked_datum)
|
var/list/class_input = list("text","num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
||||||
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
|
if(src.holder)
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default","marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
else
|
if(marked_datum)
|
||||||
class = input("What kind of variable?","Variable Type") as null|anything in list("text",
|
class_input += "marked datum ([marked_datum.type])"
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
|
||||||
|
|
||||||
|
class = input("What kind of variable?","Variable Type") as null|anything in class_input
|
||||||
if(!class)
|
if(!class)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
|
if(marked_datum && class == "marked datum ([marked_datum.type])")
|
||||||
class = "marked datum"
|
class = "marked datum"
|
||||||
|
|
||||||
var/var_value = null
|
var/var_value = null
|
||||||
@@ -118,7 +119,7 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
var_value = input("Pick icon:","Icon") as icon
|
var_value = input("Pick icon:","Icon") as icon
|
||||||
|
|
||||||
if("marked datum")
|
if("marked datum")
|
||||||
var_value = holder.marked_datum
|
var_value = holder.marked_datum()
|
||||||
|
|
||||||
if(!var_value) return
|
if(!var_value) return
|
||||||
|
|
||||||
@@ -244,17 +245,21 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
usr << "If a direction, direction is: [dir]"
|
usr << "If a direction, direction is: [dir]"
|
||||||
|
|
||||||
var/class = "text"
|
var/class = "text"
|
||||||
if(src.holder && src.holder.marked_datum)
|
var/list/class_input = list("text","num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
||||||
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
|
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default","marked datum ([holder.marked_datum.type])", "DELETE FROM LIST")
|
if(src.holder)
|
||||||
else
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
|
if(marked_datum)
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default", "DELETE FROM LIST")
|
class_input += "marked datum ([marked_datum.type])"
|
||||||
|
|
||||||
|
class_input += "DELETE FROM LIST"
|
||||||
|
class = input("What kind of variable?","Variable Type",default) as null|anything in class_input
|
||||||
|
|
||||||
if(!class)
|
if(!class)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
|
if(marked_datum && class == "marked datum ([marked_datum.type])")
|
||||||
class = "marked datum"
|
class = "marked datum"
|
||||||
|
|
||||||
var/original_var
|
var/original_var
|
||||||
@@ -336,7 +341,9 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
L[L.Find(variable)] = new_var
|
L[L.Find(variable)] = new_var
|
||||||
|
|
||||||
if("marked datum")
|
if("marked datum")
|
||||||
new_var = holder.marked_datum
|
new_var = holder.marked_datum()
|
||||||
|
if(!new_var)
|
||||||
|
return
|
||||||
if(assoc)
|
if(assoc)
|
||||||
L[assoc_key] = new_var
|
L[assoc_key] = new_var
|
||||||
else
|
else
|
||||||
@@ -501,12 +508,12 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
if(dir)
|
if(dir)
|
||||||
usr << "If a direction, direction is: [dir]"
|
usr << "If a direction, direction is: [dir]"
|
||||||
|
|
||||||
if(src.holder && src.holder.marked_datum)
|
var/list/class_input = list("text","num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
||||||
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
|
if(src.holder)
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default","marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
else
|
if(marked_datum)
|
||||||
class = input("What kind of variable?","Variable Type",default) as null|anything in list("text",
|
class_input += "marked datum ([marked_datum.type])"
|
||||||
"num","type","reference","mob reference", "icon","file","list","edit referenced object","restore to default")
|
class = input("What kind of variable?","Variable Type",default) as null|anything in class_input
|
||||||
|
|
||||||
if(!class)
|
if(!class)
|
||||||
return
|
return
|
||||||
@@ -518,7 +525,8 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
else
|
else
|
||||||
original_name = O:name
|
original_name = O:name
|
||||||
|
|
||||||
if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])")
|
var/datum/marked_datum = holder.marked_datum()
|
||||||
|
if(marked_datum && class == "marked datum ([marked_datum.type])")
|
||||||
class = "marked datum"
|
class = "marked datum"
|
||||||
|
|
||||||
switch(class)
|
switch(class)
|
||||||
@@ -584,7 +592,7 @@ var/list/VVckey_edit = list("key", "ckey")
|
|||||||
O.vars[variable] = var_new
|
O.vars[variable] = var_new
|
||||||
|
|
||||||
if("marked datum")
|
if("marked datum")
|
||||||
O.vars[variable] = holder.marked_datum
|
O.vars[variable] = holder.marked_datum()
|
||||||
|
|
||||||
world.log << "### VarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]"
|
world.log << "### VarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]"
|
||||||
log_admin("[key_name(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
|
log_admin("[key_name(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
|
||||||
|
|||||||
@@ -223,7 +223,7 @@
|
|||||||
usr << "This can only be done to instances of type /datum"
|
usr << "This can only be done to instances of type /datum"
|
||||||
return
|
return
|
||||||
|
|
||||||
src.holder.marked_datum = D
|
src.holder.marked_datum_weak = weakref(D)
|
||||||
href_list["datumrefresh"] = href_list["mark_object"]
|
href_list["datumrefresh"] = href_list["mark_object"]
|
||||||
|
|
||||||
else if(href_list["rotatedatum"])
|
else if(href_list["rotatedatum"])
|
||||||
@@ -476,7 +476,10 @@
|
|||||||
usr << "This can only be done on mobs with clients"
|
usr << "This can only be done on mobs with clients"
|
||||||
return
|
return
|
||||||
|
|
||||||
H.client.reload_nanoui_resources()
|
nanomanager.close_uis(H)
|
||||||
|
H.client.cache.Cut()
|
||||||
|
var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
|
||||||
|
assets.send(H)
|
||||||
|
|
||||||
usr << "Resource files sent"
|
usr << "Resource files sent"
|
||||||
H << "Your NanoUI Resource files have been refreshed"
|
H << "Your NanoUI Resource files have been refreshed"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</tr></table>
|
</tr></table>
|
||||||
<div align='center'>
|
<div align='center'>
|
||||||
<b><font size='1'>[replacetext("[D.type]", "/", "/<wbr>")]</font></b>
|
<b><font size='1'>[replacetext("[D.type]", "/", "/<wbr>")]</font></b>
|
||||||
[holder.marked_datum == D ? "<br/><font size='1' color='red'><b>Marked Object</b></font>" : ""]
|
[holder.marked_datum() == D ? "<br/><font size='1' color='red'><b>Marked Object</b></font>" : ""]
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td width='50%'>
|
<td width='50%'>
|
||||||
|
|||||||
@@ -200,10 +200,45 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
|||||||
"large_stamp-law.png" = 'icons/paper_icons/large_stamp-law.png',
|
"large_stamp-law.png" = 'icons/paper_icons/large_stamp-law.png',
|
||||||
"large_stamp-cent.png" = 'icons/paper_icons/large_stamp-cent.png',
|
"large_stamp-cent.png" = 'icons/paper_icons/large_stamp-cent.png',
|
||||||
"talisman.png" = 'icons/paper_icons/talisman.png',
|
"talisman.png" = 'icons/paper_icons/talisman.png',
|
||||||
"ntlogo.png" = 'icons/paper_icons/ntlogo.png'
|
"ntlogo.png" = 'icons/paper_icons/ntlogo.png',
|
||||||
"sglogo.png" = 'icons/paper_icons/sglogo.png'
|
"sglogo.png" = 'icons/paper_icons/sglogo.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/datum/asset/simple/tgui
|
||||||
|
assets = list(
|
||||||
|
"tgui.css" = 'tgui/assets/tgui.css',
|
||||||
|
"tgui.js" = 'tgui/assets/tgui.js'
|
||||||
|
)
|
||||||
|
|
||||||
|
/datum/asset/simple/pda
|
||||||
|
assets = list(
|
||||||
|
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
|
||||||
|
"pda_back.png" = 'icons/pda_icons/pda_back.png',
|
||||||
|
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
|
||||||
|
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
|
||||||
|
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
|
||||||
|
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
|
||||||
|
"pda_chatroom.png" = 'icons/pda_icons/pda_chatroom.png',
|
||||||
|
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
|
||||||
|
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
|
||||||
|
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
|
||||||
|
"pda_exit.png" = 'icons/pda_icons/pda_exit.png',
|
||||||
|
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
|
||||||
|
"pda_locked.png" = 'icons/pda_icons/pda_locked.png',
|
||||||
|
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
|
||||||
|
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
|
||||||
|
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
|
||||||
|
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
|
||||||
|
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
|
||||||
|
"pda_power.png" = 'icons/pda_icons/pda_power.png',
|
||||||
|
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
|
||||||
|
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
|
||||||
|
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
|
||||||
|
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
|
||||||
|
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
|
||||||
|
"pda_status.png" = 'icons/pda_icons/pda_status.png'
|
||||||
|
)
|
||||||
|
|
||||||
/datum/asset/nanoui
|
/datum/asset/nanoui
|
||||||
var/list/common = list()
|
var/list/common = list()
|
||||||
|
|
||||||
@@ -239,4 +274,4 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
|||||||
uncommon = list(uncommon)
|
uncommon = list(uncommon)
|
||||||
|
|
||||||
send_asset_list(client, uncommon)
|
send_asset_list(client, uncommon)
|
||||||
send_asset_list(client, common)
|
send_asset_list(client, common)
|
||||||
@@ -150,6 +150,18 @@ var/list/_client_preferences_by_type
|
|||||||
enabled_description = "Fancy"
|
enabled_description = "Fancy"
|
||||||
disabled_description = "Plain"
|
disabled_description = "Plain"
|
||||||
|
|
||||||
|
/datum/client_preference/tgui_style
|
||||||
|
description ="tgui Style"
|
||||||
|
key = "TGUI_FANCY"
|
||||||
|
enabled_description = "Fancy"
|
||||||
|
disabled_description = "Plain"
|
||||||
|
|
||||||
|
/datum/client_preference/tgui_monitor
|
||||||
|
description ="tgui Monitor"
|
||||||
|
key = "TGUI_MONITOR"
|
||||||
|
enabled_description = "Primary"
|
||||||
|
disabled_description = "All"
|
||||||
|
|
||||||
/********************
|
/********************
|
||||||
* Staff Preferences *
|
* Staff Preferences *
|
||||||
********************/
|
********************/
|
||||||
|
|||||||
@@ -798,6 +798,9 @@
|
|||||||
return 0
|
return 0
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
/obj/item/weapon/rig/check_access(obj/item/I)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
/obj/item/weapon/rig/proc/force_rest(var/mob/user)
|
/obj/item/weapon/rig/proc/force_rest(var/mob/user)
|
||||||
if(!ai_can_move_suit(user, check_user_module = 1))
|
if(!ai_can_move_suit(user, check_user_module = 1))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -720,7 +720,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
|||||||
/mob/observer/dead/canface()
|
/mob/observer/dead/canface()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
/mob/observer/dead/proc/can_admin_interact()
|
/mob/proc/can_admin_interact()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
/mob/observer/dead/can_admin_interact()
|
||||||
return check_rights(R_ADMIN, 0, src)
|
return check_rights(R_ADMIN, 0, src)
|
||||||
|
|
||||||
/mob/observer/dead/verb/toggle_ghostsee()
|
/mob/observer/dead/verb/toggle_ghostsee()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/mob/Logout()
|
/mob/Logout()
|
||||||
nanomanager.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
nanomanager.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
||||||
|
tgui_process.on_logout(src)
|
||||||
player_list -= src
|
player_list -= src
|
||||||
log_access("Logout: [key_name(src)]")
|
log_access("Logout: [key_name(src)]")
|
||||||
if(admin_datums[src.ckey])
|
if(admin_datums[src.ckey])
|
||||||
|
|||||||
@@ -576,4 +576,10 @@ var/list/global/organ_rel_size = list(
|
|||||||
)
|
)
|
||||||
|
|
||||||
/mob/proc/flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, visual = FALSE, type = /obj/screen/fullscreen/flash)
|
/mob/proc/flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, visual = FALSE, type = /obj/screen/fullscreen/flash)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
/proc/get_both_hands(mob/living/carbon/M)
|
||||||
|
if(!istype(M))
|
||||||
|
return
|
||||||
|
var/list/hands = list(M.l_hand, M.r_hand)
|
||||||
|
return hands
|
||||||
9
code/modules/nano/interaction/hands.dm
Normal file
9
code/modules/nano/interaction/hands.dm
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
This state only checks if user is conscious.
|
||||||
|
*/
|
||||||
|
/var/global/datum/topic_state/hands/hands_state = new()
|
||||||
|
|
||||||
|
/datum/topic_state/hands/can_use_topic(var/src_object, var/mob/user)
|
||||||
|
. = user.shared_ui_interaction(src_object)
|
||||||
|
if(. > STATUS_CLOSE)
|
||||||
|
. = min(., user.hands_can_use_topic(src_object))
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/datum/nano_module
|
/datum/nano_module
|
||||||
var/name
|
var/name
|
||||||
var/host
|
var/datum/host
|
||||||
|
|
||||||
/datum/nano_module/New(var/host)
|
/datum/nano_module/New(var/host)
|
||||||
src.host = host
|
src.host = host
|
||||||
|
|||||||
@@ -57,22 +57,21 @@
|
|||||||
world.log << "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>"
|
world.log << "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>"
|
||||||
usr << "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>"
|
usr << "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>"
|
||||||
|
|
||||||
var/count = 0;
|
|
||||||
for(var/WorldX = startX, WorldX <= endX, WorldX++)
|
for(var/WorldX = startX, WorldX <= endX, WorldX++)
|
||||||
for(var/WorldY = startY, WorldY <= endY, WorldY++)
|
for(var/WorldY = startY, WorldY <= endY, WorldY++)
|
||||||
|
var/turf/T = locate(WorldX, WorldY, currentZ)
|
||||||
|
var/list/atoms = T.contents + T
|
||||||
|
atoms = sortByVar(atoms, "layer")
|
||||||
|
for(var/atom/A in atoms)
|
||||||
|
if(A.type == /turf/space|| istype(A, /mob) || A.invisibility > 0)
|
||||||
|
continue
|
||||||
|
|
||||||
var/atom/Turf = locate(WorldX, WorldY, currentZ)
|
var/icon/TurfIcon = new(A.icon, A.icon_state, A.dir, 1, 0)
|
||||||
|
TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
|
||||||
|
|
||||||
var/icon/TurfIcon = new(Turf.icon, Turf.icon_state)
|
Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE) + (A.pixel_x * NANOMAP_ICON_SIZE / 32), ((WorldY - 1) * NANOMAP_ICON_SIZE) + (A.pixel_y * NANOMAP_ICON_SIZE / 32))
|
||||||
TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
|
|
||||||
|
|
||||||
Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE))
|
CHECK_TICK
|
||||||
|
|
||||||
count++
|
|
||||||
|
|
||||||
if (count % 8000 == 0)
|
|
||||||
world.log << "NanoMapGen: <B>[count] tiles done</B>"
|
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
var/mapFilename = "nanomap_z[currentZ]-new.png"
|
var/mapFilename = "nanomap_z[currentZ]-new.png"
|
||||||
|
|
||||||
|
|||||||
98
code/modules/tgui/external.dm
Normal file
98
code/modules/tgui/external.dm
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
/datum/proc/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = tg_default_state)
|
||||||
|
return -1 // 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.
|
||||||
|
**/
|
||||||
|
/datum/proc/ui_data(mob/user, ui_key = "main")
|
||||||
|
return list() // Not implemented.
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 || ui.status != UI_INTERACTIVE)
|
||||||
|
return 1 // If UI is not interactive or usr calling Topic is not the UI user, bail.
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
return src // Default src.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* global
|
||||||
|
*
|
||||||
|
* Used to track the current screen.
|
||||||
|
**/
|
||||||
|
/datum/var/ui_screen = "home"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* global
|
||||||
|
*
|
||||||
|
* Used to track UIs for a mob.
|
||||||
|
**/
|
||||||
|
/mob/var/list/tg_open_uis = list()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
set hidden = 1
|
||||||
|
|
||||||
|
// Get the UI based on the ref.
|
||||||
|
var/datum/tgui/ui = locate(ref)
|
||||||
|
|
||||||
|
// If we found the UI, close it.
|
||||||
|
if(istype(ui))
|
||||||
|
ui.close()
|
||||||
|
// Unset machine just to be sure.
|
||||||
|
if(src && src.mob)
|
||||||
|
src.mob.unset_machine()
|
||||||
223
code/modules/tgui/process.dm
Normal file
223
code/modules/tgui/process.dm
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
/**
|
||||||
|
* tgui process
|
||||||
|
*
|
||||||
|
* Contains all tgui state and process 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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = 0)
|
||||||
|
if(isnull(ui)) // No UI was passed, so look for one.
|
||||||
|
ui = get_open_ui(user, src_object, ui_key)
|
||||||
|
|
||||||
|
if(!isnull(ui))
|
||||||
|
var/data = src_object.ui_data(user) // Get data from the src_object.
|
||||||
|
if(!force_open) // UI is already open; update it.
|
||||||
|
ui.push_data(data)
|
||||||
|
else // Re-open it anyways.
|
||||||
|
ui.reinitialize(null, data)
|
||||||
|
return ui // We found the UI, return it.
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key)
|
||||||
|
var/src_object_key = "\ref[src_object]"
|
||||||
|
if(isnull(tg_open_uis[src_object_key]) || !istype(tg_open_uis[src_object_key], /list))
|
||||||
|
return null // No UIs open.
|
||||||
|
else if(isnull(tg_open_uis[src_object_key][ui_key]) || !istype(tg_open_uis[src_object_key][ui_key], /list))
|
||||||
|
return null // No UIs open for this object.
|
||||||
|
|
||||||
|
for(var/datum/tgui/ui in tg_open_uis[src_object_key][ui_key]) // Find UIs for this object.
|
||||||
|
if(ui.user == user) // Make sure we have the right user
|
||||||
|
return ui
|
||||||
|
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/update_uis(datum/src_object)
|
||||||
|
var/src_object_key = "\ref[src_object]"
|
||||||
|
if(isnull(tg_open_uis[src_object_key]) || !istype(tg_open_uis[src_object_key], /list))
|
||||||
|
return 0 // Couldn't find any UIs for this object.
|
||||||
|
|
||||||
|
var/update_count = 0
|
||||||
|
for(var/ui_key in tg_open_uis[src_object_key])
|
||||||
|
for(var/datum/tgui/ui in tg_open_uis[src_object_key][ui_key])
|
||||||
|
if(ui && ui.src_object && ui.user && ui.src_object.ui_host()) // Check the UI is valid.
|
||||||
|
ui.process(force = 1) // Update the UI.
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/close_uis(datum/src_object)
|
||||||
|
var/src_object_key = "\ref[src_object]"
|
||||||
|
if(isnull(tg_open_uis[src_object_key]) || !istype(tg_open_uis[src_object_key], /list))
|
||||||
|
return 0 // Couldn't find any UIs for this object.
|
||||||
|
|
||||||
|
var/close_count = 0
|
||||||
|
for(var/ui_key in tg_open_uis[src_object_key])
|
||||||
|
for(var/datum/tgui/ui in tg_open_uis[src_object_key][ui_key])
|
||||||
|
if(ui && ui.src_object && ui.user && ui.src_object.ui_host()) // Check the UI is valid.
|
||||||
|
ui.close() // Close the UI.
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
|
||||||
|
if(isnull(user.tg_open_uis) || !istype(user.tg_open_uis, /list) || tg_open_uis.len == 0)
|
||||||
|
return 0 // Couldn't find any UIs for this user.
|
||||||
|
|
||||||
|
var/update_count = 0
|
||||||
|
for(var/datum/tgui/ui in user.tg_open_uis)
|
||||||
|
if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
|
||||||
|
ui.process(force = 1) // Update the UI.
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
|
||||||
|
if(isnull(user.tg_open_uis) || !istype(user.tg_open_uis, /list) || tg_open_uis.len == 0)
|
||||||
|
return 0 // Couldn't find any UIs for this user.
|
||||||
|
|
||||||
|
var/close_count = 0
|
||||||
|
for(var/datum/tgui/ui in user.tg_open_uis)
|
||||||
|
if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
|
||||||
|
ui.close() // Close the UI.
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/on_open(datum/tgui/ui)
|
||||||
|
var/src_object_key = "\ref[ui.src_object]"
|
||||||
|
if(isnull(tg_open_uis[src_object_key]) || !istype(tg_open_uis[src_object_key], /list))
|
||||||
|
tg_open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
|
||||||
|
else if(isnull(tg_open_uis[src_object_key][ui.ui_key]) || !istype(tg_open_uis[src_object_key][ui.ui_key], /list))
|
||||||
|
tg_open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key.
|
||||||
|
|
||||||
|
// Append the UI to all the lists.
|
||||||
|
ui.user.tg_open_uis |= ui
|
||||||
|
var/list/uis = tg_open_uis[src_object_key][ui.ui_key]
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/on_close(datum/tgui/ui)
|
||||||
|
var/src_object_key = "\ref[ui.src_object]"
|
||||||
|
if(isnull(tg_open_uis[src_object_key]) || !istype(tg_open_uis[src_object_key], /list))
|
||||||
|
return 0 // It wasn't open.
|
||||||
|
else if(isnull(tg_open_uis[src_object_key][ui.ui_key]) || !istype(tg_open_uis[src_object_key][ui.ui_key], /list))
|
||||||
|
return 0 // It wasn't open.
|
||||||
|
|
||||||
|
processing_uis.Remove(ui) // Remove it from the list of processing UIs.
|
||||||
|
if(ui.user) // If the user exists, remove it from them too.
|
||||||
|
ui.user.tg_open_uis.Remove(ui)
|
||||||
|
var/list/uis = tg_open_uis[src_object_key][ui.ui_key] // Remove it from the list of open UIs.
|
||||||
|
uis.Remove(ui)
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/controller/process/tgui/proc/on_logout(mob/user)
|
||||||
|
return close_user_uis(user)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private
|
||||||
|
*
|
||||||
|
* Handle clients switching mobs, by transfering 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/process/tgui/proc/on_transfer(mob/source, mob/target)
|
||||||
|
if(!source || isnull(source.tg_open_uis) || !istype(source.tg_open_uis, /list) || tg_open_uis.len == 0)
|
||||||
|
return 0 // The old mob had no open UIs.
|
||||||
|
|
||||||
|
if(isnull(target.tg_open_uis) || !istype(target.tg_open_uis, /list))
|
||||||
|
target.tg_open_uis = list() // Create a list for the new mob if needed.
|
||||||
|
|
||||||
|
for(var/datum/tgui/ui in source.tg_open_uis)
|
||||||
|
ui.user = target // Inform the UIs of their new owner.
|
||||||
|
target.tg_open_uis.Add(ui) // Transfer all the UIs.
|
||||||
|
|
||||||
|
source.tg_open_uis.Cut() // Clear the old list.
|
||||||
|
return 1 // Let the caller know we did it.
|
||||||
110
code/modules/tgui/states.dm
Normal file
110
code/modules/tgui/states.dm
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
/datum/proc/ui_status(mob/user, datum/ui_state/state)
|
||||||
|
var/datum/src_object = ui_host()
|
||||||
|
if(src_object != src)
|
||||||
|
return src_object.ui_status(user, state)
|
||||||
|
|
||||||
|
if(isobserver(user)) // Special-case ghosts.
|
||||||
|
if(user.can_admin_interact())
|
||||||
|
return UI_INTERACTIVE // If they turn it on, admins can interact.
|
||||||
|
if(get_dist(src_object, src) < user.client.view)
|
||||||
|
return UI_UPDATE // Regular ghosts can only view.
|
||||||
|
return UI_CLOSE // To keep too many UIs from being opened.
|
||||||
|
return state.can_use_topic(src_object, user) // Check if the state allows interaction.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
/mob/proc/shared_ui_interaction(src_object)
|
||||||
|
if(!client) // Close UIs if mindless.
|
||||||
|
return UI_CLOSE
|
||||||
|
else if(stat) // Disable UIs if unconcious.
|
||||||
|
return UI_DISABLED
|
||||||
|
else if(incapacitated() || lying) // Update UIs if incapicitated but concious.
|
||||||
|
return UI_UPDATE
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
|
||||||
|
/mob/living/silicon/ai/shared_ui_interaction(src_object)
|
||||||
|
if(lacks_power()) // Disable UIs if the AI is unpowered.
|
||||||
|
return UI_DISABLED
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/silicon/robot/shared_ui_interaction(src_object)
|
||||||
|
if(cell.charge <= 0 || lockcharge) // Disable UIs if the Borg is unpowered or locked.
|
||||||
|
return UI_DISABLED
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/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.
|
||||||
|
return UI_CLOSE
|
||||||
|
|
||||||
|
var/dist = get_dist(src_object, src)
|
||||||
|
if(dist <= 1) // Open and interact if 1-0 tiles away.
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
else if(dist <= 2) // View only if 2-3 tiles away.
|
||||||
|
return UI_UPDATE
|
||||||
|
else if(dist <= 5) // Disable if 5 tiles away.
|
||||||
|
return UI_DISABLED
|
||||||
|
return UI_CLOSE // Otherwise, we got nothing.
|
||||||
|
|
||||||
|
/mob/living/carbon/human/shared_living_ui_distance(atom/movable/src_object)
|
||||||
|
if((TK in mutations))
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return ..()
|
||||||
12
code/modules/tgui/states/admin.dm
Normal file
12
code/modules/tgui/states/admin.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: admin_state
|
||||||
|
*
|
||||||
|
* Checks that the user is an admin, end-of-story.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/admin_state/tg_admin_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/admin_state/can_use_topic(src_object, mob/user)
|
||||||
|
if(check_rights(R_ADMIN, 0, user))
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_CLOSE
|
||||||
12
code/modules/tgui/states/conscious.dm
Normal file
12
code/modules/tgui/states/conscious.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: conscious_state
|
||||||
|
*
|
||||||
|
* Only checks if the user is conscious.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/conscious_state/tg_conscious_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/conscious_state/can_use_topic(src_object, mob/user)
|
||||||
|
if(user.stat == CONSCIOUS)
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_CLOSE
|
||||||
12
code/modules/tgui/states/contained.dm
Normal file
12
code/modules/tgui/states/contained.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: contained_state
|
||||||
|
*
|
||||||
|
* Checks that the user is inside the src_object.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/contained_state/tg_contained_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/contained_state/can_use_topic(atom/src_object, mob/user)
|
||||||
|
if(!src_object.contains(user))
|
||||||
|
return UI_CLOSE
|
||||||
|
return user.shared_ui_interaction(src_object)
|
||||||
12
code/modules/tgui/states/deep_inventory.dm
Normal file
12
code/modules/tgui/states/deep_inventory.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: deep_inventory_state
|
||||||
|
*
|
||||||
|
* Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/deep_inventory_state/tg_deep_inventory_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/deep_inventory_state/can_use_topic(src_object, mob/user)
|
||||||
|
if(!user.contains(src_object))
|
||||||
|
return UI_CLOSE
|
||||||
|
return user.shared_ui_interaction(src_object)
|
||||||
55
code/modules/tgui/states/default.dm
Normal file
55
code/modules/tgui/states/default.dm
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: default_state
|
||||||
|
*
|
||||||
|
* Checks a number of things -- mostly physical distance for humans and view for robots.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/default/tg_default_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/default/can_use_topic(src_object, mob/user)
|
||||||
|
return user.tg_default_can_use_topic(src_object) // Call the individual mob-overriden procs.
|
||||||
|
|
||||||
|
/mob/proc/tg_default_can_use_topic(src_object)
|
||||||
|
return UI_CLOSE // Don't allow interaction by default.
|
||||||
|
|
||||||
|
/mob/living/tg_default_can_use_topic(src_object)
|
||||||
|
. = shared_ui_interaction(src_object)
|
||||||
|
if(. > UI_CLOSE && loc)
|
||||||
|
. = min(., loc.contents_ui_distance(src_object, src)) // Check the distance...
|
||||||
|
if(. == UI_INTERACTIVE) // Non-human living mobs can only look, not touch.
|
||||||
|
return UI_UPDATE
|
||||||
|
|
||||||
|
/mob/living/carbon/human/tg_default_can_use_topic(src_object)
|
||||||
|
. = shared_ui_interaction(src_object)
|
||||||
|
if(. > UI_CLOSE)
|
||||||
|
. = min(., shared_living_ui_distance(src_object)) // Check the distance...
|
||||||
|
// Derp a bit if we have brain loss.
|
||||||
|
if(prob(getBrainLoss()))
|
||||||
|
return UI_UPDATE
|
||||||
|
|
||||||
|
/mob/living/silicon/robot/tg_default_can_use_topic(src_object)
|
||||||
|
. = shared_ui_interaction(src_object)
|
||||||
|
if(. <= UI_DISABLED)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Robots can interact with anything they can see.
|
||||||
|
if(get_dist(src, src_object) <= client.view)
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_DISABLED // Otherwise they can keep the UI open.
|
||||||
|
|
||||||
|
/mob/living/silicon/ai/tg_default_can_use_topic(src_object)
|
||||||
|
. = shared_ui_interaction(src_object)
|
||||||
|
if(. < UI_INTERACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
|
// The AI can interact with anything it can see nearby, or with cameras.
|
||||||
|
if((get_dist(src, src_object) <= client.view) || cameranet.checkTurfVis(get_turf_pixel(src_object)))
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_CLOSE
|
||||||
|
|
||||||
|
/mob/living/silicon/pai/tg_default_can_use_topic(src_object)
|
||||||
|
// pAIs can only use themselves and the owner's radio.
|
||||||
|
if((src_object == src || src_object == radio) && !stat)
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
else
|
||||||
|
return ..()
|
||||||
20
code/modules/tgui/states/hands.dm
Normal file
20
code/modules/tgui/states/hands.dm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: hands_state
|
||||||
|
*
|
||||||
|
* Checks that the src_object is in the user's hands.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/hands_state/tg_hands_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/hands_state/can_use_topic(src_object, mob/user)
|
||||||
|
. = user.shared_ui_interaction(src_object)
|
||||||
|
if(. > UI_CLOSE)
|
||||||
|
return min(., user.hands_can_use_topic(src_object))
|
||||||
|
|
||||||
|
/mob/proc/hands_can_use_topic(src_object)
|
||||||
|
return UI_CLOSE
|
||||||
|
|
||||||
|
/mob/living/hands_can_use_topic(src_object)
|
||||||
|
if(src_object in get_both_hands(src))
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_CLOSE
|
||||||
12
code/modules/tgui/states/inventory.dm
Normal file
12
code/modules/tgui/states/inventory.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: inventory_state
|
||||||
|
*
|
||||||
|
* Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/inventory_state/tg_inventory_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/inventory_state/can_use_topic(src_object, mob/user)
|
||||||
|
if(!(src_object in user))
|
||||||
|
return UI_CLOSE
|
||||||
|
return user.shared_ui_interaction(src_object)
|
||||||
26
code/modules/tgui/states/notcontained.dm
Normal file
26
code/modules/tgui/states/notcontained.dm
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: notcontained_state
|
||||||
|
*
|
||||||
|
* Checks that the user is not inside src_object, and then makes the default checks.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/notcontained_state/tg_notcontained_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/notcontained_state/can_use_topic(atom/src_object, mob/user)
|
||||||
|
. = user.shared_ui_interaction(src_object)
|
||||||
|
if(. > UI_CLOSE)
|
||||||
|
return min(., user.notcontained_can_use_topic(src_object))
|
||||||
|
|
||||||
|
/mob/proc/notcontained_can_use_topic(src_object)
|
||||||
|
return UI_CLOSE
|
||||||
|
|
||||||
|
/mob/living/notcontained_can_use_topic(atom/src_object)
|
||||||
|
if(src_object.contains(src))
|
||||||
|
return UI_CLOSE // Close if we're inside it.
|
||||||
|
return tg_default_can_use_topic(src_object)
|
||||||
|
|
||||||
|
/mob/living/silicon/notcontained_can_use_topic(src_object)
|
||||||
|
return tg_default_can_use_topic(src_object) // Silicons use default bevhavior.
|
||||||
|
|
||||||
|
/mob/living/simple_animal/drone/notcontained_can_use_topic(src_object)
|
||||||
|
return tg_default_can_use_topic(src_object) // Drones use default bevhavior.
|
||||||
24
code/modules/tgui/states/physical.dm
Normal file
24
code/modules/tgui/states/physical.dm
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: physical_state
|
||||||
|
*
|
||||||
|
* Short-circuits the default state to only check physical distance.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/physical/tg_physical_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/physical/can_use_topic(src_object, mob/user)
|
||||||
|
. = user.shared_ui_interaction(src_object)
|
||||||
|
if(. > UI_CLOSE)
|
||||||
|
return min(., user.physical_can_use_topic(src_object))
|
||||||
|
|
||||||
|
/mob/proc/physical_can_use_topic(src_object)
|
||||||
|
return UI_CLOSE
|
||||||
|
|
||||||
|
/mob/living/physical_can_use_topic(src_object)
|
||||||
|
return shared_living_ui_distance(src_object)
|
||||||
|
|
||||||
|
/mob/living/silicon/physical_can_use_topic(src_object)
|
||||||
|
return max(UI_UPDATE, shared_living_ui_distance(src_object)) // Silicons can always see.
|
||||||
|
|
||||||
|
/mob/living/silicon/ai/physical_can_use_topic(src_object)
|
||||||
|
return UI_UPDATE // AIs are not physical.
|
||||||
12
code/modules/tgui/states/self.dm
Normal file
12
code/modules/tgui/states/self.dm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: self_state
|
||||||
|
*
|
||||||
|
* Only checks that the user and src_object are the same.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/self_state/tg_self_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/self_state/can_use_topic(src_object, mob/user)
|
||||||
|
if(src_object != user)
|
||||||
|
return UI_CLOSE
|
||||||
|
return user.shared_ui_interaction(src_object)
|
||||||
14
code/modules/tgui/states/zlevel.dm
Normal file
14
code/modules/tgui/states/zlevel.dm
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* tgui state: z_state
|
||||||
|
*
|
||||||
|
* Only checks that the Z-level of the user and src_object are the same.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/var/global/datum/ui_state/z_state/tg_z_state = new()
|
||||||
|
|
||||||
|
/datum/ui_state/z_state/can_use_topic(src_object, mob/user)
|
||||||
|
var/turf/turf_obj = get_turf(src_object)
|
||||||
|
var/turf/turf_usr = get_turf(user)
|
||||||
|
if(turf_obj && turf_usr && turf_obj.z == turf_usr.z)
|
||||||
|
return UI_INTERACTIVE
|
||||||
|
return UI_CLOSE
|
||||||
369
code/modules/tgui/tgui.dm
Normal file
369
code/modules/tgui/tgui.dm
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
/**
|
||||||
|
* tgui
|
||||||
|
*
|
||||||
|
* /tg/station user interface library
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tgui datum (represents a UI).
|
||||||
|
**/
|
||||||
|
/datum/tgui
|
||||||
|
var/mob/user // The mob who opened/is using the UI.
|
||||||
|
var/datum/src_object // The object which owns the UI.
|
||||||
|
var/title // The title of te UI.
|
||||||
|
var/ui_key // The ui_key of the UI. This allows multiple UIs for one src_object.
|
||||||
|
var/window_id // The window_id for browse() and onclose().
|
||||||
|
var/width = 0 // The window width.
|
||||||
|
var/height = 0 // The window height
|
||||||
|
var/window_options = list( // Extra options to winset().
|
||||||
|
"focus" = FALSE,
|
||||||
|
"titlebar" = TRUE,
|
||||||
|
"can_resize" = TRUE,
|
||||||
|
"can_minimize" = TRUE,
|
||||||
|
"can_maximize" = FALSE,
|
||||||
|
"can_close" = TRUE,
|
||||||
|
"auto_format" = FALSE
|
||||||
|
)
|
||||||
|
var/style = "nanotrasen" // The style to be used for this UI.
|
||||||
|
var/interface // The interface (template) to be used for this UI.
|
||||||
|
var/autoupdate = TRUE // Update the UI every MC tick.
|
||||||
|
var/initialized = FALSE // If the UI has been initialized yet.
|
||||||
|
var/list/initial_data // The data (and datastructure) used to initialize the UI.
|
||||||
|
var/status = UI_INTERACTIVE // The status/visibility of the UI.
|
||||||
|
var/datum/ui_state/state = null // Topic state used to determine status/interactability.
|
||||||
|
var/datum/tgui/master_ui // The parent UI.
|
||||||
|
var/list/datum/tgui/children = list() // Children of this UI.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = tg_default_state)
|
||||||
|
src.user = user
|
||||||
|
src.src_object = src_object
|
||||||
|
src.ui_key = ui_key
|
||||||
|
src.window_id = "\ref[src_object]-[ui_key]"
|
||||||
|
|
||||||
|
set_interface(interface)
|
||||||
|
|
||||||
|
if(title)
|
||||||
|
src.title = sanitize(title)
|
||||||
|
if(width)
|
||||||
|
src.width = width
|
||||||
|
if(height)
|
||||||
|
src.height = height
|
||||||
|
|
||||||
|
src.master_ui = master_ui
|
||||||
|
if(master_ui)
|
||||||
|
master_ui.children += src
|
||||||
|
src.state = state
|
||||||
|
|
||||||
|
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/tgui)
|
||||||
|
assets.send(user)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public
|
||||||
|
*
|
||||||
|
* Open this UI (and initialize it with data).
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/open()
|
||||||
|
if(!user.client)
|
||||||
|
return // Bail if there is no client.
|
||||||
|
|
||||||
|
update_status(push = 0) // Update the window status.
|
||||||
|
if(status < UI_UPDATE)
|
||||||
|
return // Bail if we're not supposed to open.
|
||||||
|
|
||||||
|
if(!initial_data)
|
||||||
|
set_initial_data(src_object.ui_data(user)) // Get the UI data.
|
||||||
|
|
||||||
|
var/window_size = ""
|
||||||
|
if(width && height) // If we have a width and height, use them.
|
||||||
|
window_size = "size=[width]x[height];"
|
||||||
|
|
||||||
|
var/debugable = check_rights(R_DEBUG, 0, user)
|
||||||
|
user << browse(get_html(debugable), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
|
||||||
|
winset(user, window_id, "on-close=\"uiclose \ref[src]\"") // Instruct the client to signal UI when the window is closed.
|
||||||
|
tgui_process.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.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/reinitialize(interface, list/data)
|
||||||
|
if(interface)
|
||||||
|
set_interface(interface) // Set a new interface.
|
||||||
|
if(data)
|
||||||
|
set_initial_data(data) // Replace the initial_data.
|
||||||
|
open()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public
|
||||||
|
*
|
||||||
|
* Close the UI, and all its children.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/close()
|
||||||
|
user << browse(null, "window=[window_id]") // Close the window.
|
||||||
|
tgui_process.on_close(src)
|
||||||
|
for(var/datum/tgui/child in children) // Loop through and close all children.
|
||||||
|
child.close()
|
||||||
|
children.Cut()
|
||||||
|
state = null
|
||||||
|
master_ui = null
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public
|
||||||
|
*
|
||||||
|
* Sets the browse() window options for this UI.
|
||||||
|
*
|
||||||
|
* required window_options list The window options to set.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/set_window_options(list/window_options)
|
||||||
|
src.window_options = window_options
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/set_autoupdate(state = 1)
|
||||||
|
autoupdate = state
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private
|
||||||
|
*
|
||||||
|
* Set the data to initialize the UI with.
|
||||||
|
* The datastructure cannot be changed by subsequent updates.
|
||||||
|
*
|
||||||
|
* optional data list The data/datastructure to initialize the UI with.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/set_initial_data(list/data)
|
||||||
|
initial_data = data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private
|
||||||
|
*
|
||||||
|
* Generate HTML for this UI.
|
||||||
|
*
|
||||||
|
* optional bool inline If the JSON should be inlined into the HTML (for debugging).
|
||||||
|
*
|
||||||
|
* return string UI HTML output.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/get_html(var/inline)
|
||||||
|
var/html
|
||||||
|
// Poplate HTML with JSON if we're supposed to inline.
|
||||||
|
if(inline)
|
||||||
|
html = replacetextEx(tgui_process.basehtml, "{}", get_json(initial_data))
|
||||||
|
else
|
||||||
|
html = tgui_process.basehtml
|
||||||
|
html = replacetextEx(html, "\[ref]", "\ref[src]")
|
||||||
|
html = replacetextEx(html, "\[style]", style)
|
||||||
|
return html
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private
|
||||||
|
*
|
||||||
|
* Get the config data/datastructure to initialize the UI with.
|
||||||
|
*
|
||||||
|
* return list The config data.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/get_config_data()
|
||||||
|
var/list/config_data = list(
|
||||||
|
"title" = title,
|
||||||
|
"status" = status,
|
||||||
|
"screen" = src_object.ui_screen,
|
||||||
|
"style" = style,
|
||||||
|
"interface" = interface,
|
||||||
|
"fancy" = user.is_preference_enabled(/datum/client_preference/tgui_style),
|
||||||
|
"locked" = user.is_preference_enabled(/datum/client_preference/tgui_monitor),
|
||||||
|
"window" = window_id,
|
||||||
|
"ref" = "\ref[src]",
|
||||||
|
"user" = list(
|
||||||
|
"name" = user.name,
|
||||||
|
"ref" = "\ref[user]"
|
||||||
|
),
|
||||||
|
"srcObject" = list(
|
||||||
|
"name" = "[src_object]",
|
||||||
|
"ref" = "\ref[src_object]"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return config_data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
var/list/json_data = list()
|
||||||
|
|
||||||
|
json_data["config"] = get_config_data()
|
||||||
|
if(!isnull(data))
|
||||||
|
json_data["data"] = data
|
||||||
|
|
||||||
|
// Generate the JSON.
|
||||||
|
var/json = json_encode(json_data)
|
||||||
|
// Strip #255/improper.
|
||||||
|
json = replacetext(json, "\proper", "")
|
||||||
|
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.
|
||||||
|
**/
|
||||||
|
/datum/tgui/Topic(href, href_list)
|
||||||
|
if(user != usr)
|
||||||
|
return // Something is not right here.
|
||||||
|
|
||||||
|
var/action = href_list["action"]
|
||||||
|
var/params = href_list; params -= "action"
|
||||||
|
|
||||||
|
switch(action)
|
||||||
|
if("tgui:initialize")
|
||||||
|
user << output(url_encode(get_json(initial_data)), "[window_id].browser:initialize")
|
||||||
|
initialized = TRUE
|
||||||
|
if("tgui:view")
|
||||||
|
if(params["screen"])
|
||||||
|
src_object.ui_screen = params["screen"]
|
||||||
|
tgui_process.update_uis(src_object)
|
||||||
|
if("tgui:link")
|
||||||
|
user << link(params["url"])
|
||||||
|
if("tgui:fancy")
|
||||||
|
user.set_preference(/datum/client_preference/tgui_style, TRUE)
|
||||||
|
if("tgui:nofrills")
|
||||||
|
user.set_preference(/datum/client_preference/tgui_style, FALSE)
|
||||||
|
else
|
||||||
|
update_status(push = 0) // Update the window state.
|
||||||
|
if(src_object.ui_act(action, params, src, state)) // Call ui_act() on the src_object.
|
||||||
|
tgui_process.update_uis(src_object) // Update if the object requested it.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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/proc/process(force = 0)
|
||||||
|
var/datum/host = src_object.ui_host()
|
||||||
|
if(!src_object || !host || !user) // If the object or user died (or something else), abort.
|
||||||
|
close()
|
||||||
|
return
|
||||||
|
|
||||||
|
if(status && (force || autoupdate))
|
||||||
|
update() // Update the UI if the status and update settings allow it.
|
||||||
|
else
|
||||||
|
update_status(push = 1) // 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.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/push_data(data, force = 0)
|
||||||
|
update_status(push = 0) // Update the window state.
|
||||||
|
if(!initialized)
|
||||||
|
return // Cannot update UI if it is not set up yet.
|
||||||
|
if(status <= UI_DISABLED && !force)
|
||||||
|
return // Cannot update UI, we have no visibility.
|
||||||
|
|
||||||
|
// Send the new JSON to the update() Javascript function.
|
||||||
|
user << output(url_encode(get_json(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.
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/update(force_open = 0)
|
||||||
|
src_object.tg_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).
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/update_status(push = 0)
|
||||||
|
var/status = src_object.ui_status(user, state)
|
||||||
|
if(master_ui)
|
||||||
|
status = min(status, master_ui.status)
|
||||||
|
|
||||||
|
set_status(status, push)
|
||||||
|
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).
|
||||||
|
**/
|
||||||
|
/datum/tgui/proc/set_status(status, push = 0)
|
||||||
|
if(src.status != status) // Only update if status has changed.
|
||||||
|
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.
|
||||||
|
push_data(null, force = 1)
|
||||||
@@ -3504,6 +3504,120 @@ div.resize
|
|||||||
{
|
{
|
||||||
background-position: -112px -128px;
|
background-position: -112px -128px;
|
||||||
}
|
}
|
||||||
|
/*pill and bottle icons used in chem master*/
|
||||||
|
.pillIcon32
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.pillIcon
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: 2px 2px 0 2px;
|
||||||
|
|
||||||
|
background-image: url(pills32.png);
|
||||||
|
}
|
||||||
|
.pillIcon.pill1
|
||||||
|
{
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
.pillIcon.pill2
|
||||||
|
{
|
||||||
|
background-position: -32px 0;
|
||||||
|
}
|
||||||
|
.pillIcon.pill3
|
||||||
|
{
|
||||||
|
background-position: -64px 0;
|
||||||
|
}
|
||||||
|
.pillIcon.pill4
|
||||||
|
{
|
||||||
|
background-position: -96px 0;
|
||||||
|
}
|
||||||
|
.pillIcon.pill5
|
||||||
|
{
|
||||||
|
background-position: -128px 0;
|
||||||
|
}
|
||||||
|
.pillIcon.pill6
|
||||||
|
{
|
||||||
|
background-position: 0 -32px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill7
|
||||||
|
{
|
||||||
|
background-position: -32px -32px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill8
|
||||||
|
{
|
||||||
|
background-position: -64px -32px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill9
|
||||||
|
{
|
||||||
|
background-position: -96px -32px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill10
|
||||||
|
{
|
||||||
|
background-position: -128px -32px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill11
|
||||||
|
{
|
||||||
|
background-position: 0 -64px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill12
|
||||||
|
{
|
||||||
|
background-position: -32px -64px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill13
|
||||||
|
{
|
||||||
|
background-position: -64px -64px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill14
|
||||||
|
{
|
||||||
|
background-position: -96px -64px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill15
|
||||||
|
{
|
||||||
|
background-position: -128px -64px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill16
|
||||||
|
{
|
||||||
|
background-position: 0 -96px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill17
|
||||||
|
{
|
||||||
|
background-position: -32px -96px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill18
|
||||||
|
{
|
||||||
|
background-position: -64px -96px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill19
|
||||||
|
{
|
||||||
|
background-position: -96px -96px;
|
||||||
|
}
|
||||||
|
.pillIcon.pill20
|
||||||
|
{
|
||||||
|
background-position: -128px -96px;
|
||||||
|
}
|
||||||
|
.pillIcon.bottle1
|
||||||
|
{
|
||||||
|
background-position: 0 -128px;
|
||||||
|
}
|
||||||
|
.pillIcon.bottle2
|
||||||
|
{
|
||||||
|
background-position: -32px -128px;
|
||||||
|
}
|
||||||
|
.pillIcon.bottle3
|
||||||
|
{
|
||||||
|
background-position: -64px -128px;
|
||||||
|
}
|
||||||
|
.pillIcon.bottle4
|
||||||
|
{
|
||||||
|
background-position: -96px -128px;
|
||||||
|
}
|
||||||
ul
|
ul
|
||||||
{
|
{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 775 KiB After Width: | Height: | Size: 313 KiB |
BIN
nano/images/nanomap_z3.png
Normal file
BIN
nano/images/nanomap_z3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
nano/images/nanomap_z4.png
Normal file
BIN
nano/images/nanomap_z4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
nano/images/nanomap_z5.png
Normal file
BIN
nano/images/nanomap_z5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -59,4 +59,44 @@
|
|||||||
/* Security Positions */
|
/* Security Positions */
|
||||||
.mapIcon16.rank-warden { background-position: -112px -128px; }
|
.mapIcon16.rank-warden { background-position: -112px -128px; }
|
||||||
.mapIcon16.rank-detective { background-position: -112px -128px; }
|
.mapIcon16.rank-detective { background-position: -112px -128px; }
|
||||||
.mapIcon16.rank-securityofficer { background-position: -112px -128px; }
|
.mapIcon16.rank-securityofficer { background-position: -112px -128px; }
|
||||||
|
|
||||||
|
/*pill and bottle icons used in chem master*/
|
||||||
|
.pillIcon32 {
|
||||||
|
float: left;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillIcon {
|
||||||
|
float: left;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: 2px 2px 0 2px;
|
||||||
|
background-image: url(pills32.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillIcon.pill1 { background-position: 0 0; }
|
||||||
|
.pillIcon.pill2 { background-position: -32px 0; }
|
||||||
|
.pillIcon.pill3 { background-position: -64px 0; }
|
||||||
|
.pillIcon.pill4 { background-position: -96px 0; }
|
||||||
|
.pillIcon.pill5 { background-position: -128px 0; }
|
||||||
|
.pillIcon.pill6 { background-position: 0 -32px; }
|
||||||
|
.pillIcon.pill7 { background-position: -32px -32px; }
|
||||||
|
.pillIcon.pill8 { background-position: -64px -32px; }
|
||||||
|
.pillIcon.pill9 { background-position: -96px -32px; }
|
||||||
|
.pillIcon.pill10 { background-position: -128px -32px; }
|
||||||
|
.pillIcon.pill11 { background-position: 0 -64px; }
|
||||||
|
.pillIcon.pill12 { background-position: -32px -64px; }
|
||||||
|
.pillIcon.pill13 { background-position: -64px -64px; }
|
||||||
|
.pillIcon.pill14 { background-position: -96px -64px; }
|
||||||
|
.pillIcon.pill15 { background-position: -128px -64px; }
|
||||||
|
.pillIcon.pill16 { background-position: 0 -96px; }
|
||||||
|
.pillIcon.pill17 { background-position: -32px -96px; }
|
||||||
|
.pillIcon.pill18 { background-position: -64px -96px; }
|
||||||
|
.pillIcon.pill19 { background-position: -96px -96px; }
|
||||||
|
.pillIcon.pill20 { background-position: -128px -96px; }
|
||||||
|
.pillIcon.bottle1 { background-position: 0 -128px; }
|
||||||
|
.pillIcon.bottle2 { background-position: -32px -128px; }
|
||||||
|
.pillIcon.bottle3 { background-position: -64px -128px; }
|
||||||
|
.pillIcon.bottle4 { background-position: -96px -128px; }
|
||||||
@@ -69,8 +69,8 @@
|
|||||||
{{:helper.link('Create bottle (60 units max)', null, {'createbottle' : 1})}}
|
{{:helper.link('Create bottle (60 units max)', null, {'createbottle' : 1})}}
|
||||||
</div>
|
</div>
|
||||||
<div class='item'>
|
<div class='item'>
|
||||||
{{:helper.link('', 'pill pill' + data.pillSprite, {'tab_select' : 'pill'}, null, 'link32')}}
|
{{:helper.link("<div class='pillIcon pill" + data.pillSprite + "'></div>", null, {'tab_select' : 'pill'}, null, 'link pillIcon32')}}
|
||||||
{{:helper.link('', 'pill bottle' + data.bottleSprite, {'tab_select' : 'bottle'}, null, 'link32')}}
|
{{:helper.link("<div class='pillIcon bottle" + data.bottleSprite + "'></div>", null, {'tab_select' : 'bottle'}, null, 'link pillIcon32')}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -104,13 +104,13 @@
|
|||||||
|
|
||||||
{{else data.tab == 'pill'}}
|
{{else data.tab == 'pill'}}
|
||||||
{{for data.pillSpritesAmount}}
|
{{for data.pillSpritesAmount}}
|
||||||
{{:helper.link('', 'pill pill' + value, {'pill_sprite' : value}, null, data.pillSprite == value ? 'linkOn link32' : 'link32')}}
|
{{:helper.link("<div class='pillIcon pill" + value + "'></div>", null, {'pill_sprite' : value}, null, data.pillSprite == value ? 'linkOn pillIcon32' : 'link pillIcon32')}}
|
||||||
{{/for}}
|
{{/for}}
|
||||||
<div class='item'><br>{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}</div>
|
<div class='item'><br>{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}</div>
|
||||||
|
|
||||||
{{else data.tab == 'bottle'}}
|
{{else data.tab == 'bottle'}}
|
||||||
{{for data.bottleSpritesAmount}}
|
{{for data.bottleSpritesAmount}}
|
||||||
{{:helper.link('', 'pill bottle' + value, {'bottle_sprite' : value}, null, data.bottleSprite == value ? 'linkOn link32' : 'link32')}}
|
{{:helper.link("<div class='pillIcon bottle" + value + "'></div>", null, {'bottle_sprite' : value}, null, data.bottleSprite == value ? 'linkOn pillIcon32' : 'link pillIcon32')}}
|
||||||
{{/for}}
|
{{/for}}
|
||||||
<div class='item'><br>{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}</div>
|
<div class='item'><br>{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Title: Crew Monitoring Console (Main content)
|
Title: Crew Monitoring Console (Main content)
|
||||||
Used In File(s): \code\game\machinery\computer\crew.dm
|
Used In File(s): \code\game\machinery\computer\crew.dm
|
||||||
-->
|
-->
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
table.wideTable {
|
table.wideTable {
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -892,7 +892,7 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm
|
|||||||
{{for data.feed.messages}}
|
{{for data.feed.messages}}
|
||||||
-{{:value.body}}<br>
|
-{{:value.body}}<br>
|
||||||
{{if value.has_image}}
|
{{if value.has_image}}
|
||||||
<img src='pda_news_tmp_photo_{{:data.feed.channel}}_{{:value.index}}.png' width = '180'><br>
|
<img src='newscaster_photo_{{:data.feed.channel}}_{{:value.index}}.png' width = '180'><br>
|
||||||
{{if value.caption}}
|
{{if value.caption}}
|
||||||
<span class="caption">{{:value.caption}}</span><br>
|
<span class="caption">{{:value.caption}}</span><br>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Used In File(s): \code\game\machinery\computer\camera.dm
|
|||||||
-->
|
-->
|
||||||
{{for data.cameras}}
|
{{for data.cameras}}
|
||||||
{{if value.z == 1}}
|
{{if value.z == 1}}
|
||||||
<div class="mapIcon mapIcon16" style="left: {{:(value.x)}}px; bottom: {{:(value.y - 14.75)}}px;">
|
<div class="mapIcon mapIcon16" style="left: {{:(value.x - 9.75)}}px; bottom: {{:(value.y - 14.25)}}px;">
|
||||||
{{if data.current && value.name == data.current.name}}
|
{{if data.current && value.name == data.current.name}}
|
||||||
{{:helper.link("#", '', {'switch_camera' : value.camera}, 'selected')}}
|
{{:helper.link("#", '', {'switch_camera' : value.camera}, 'selected')}}
|
||||||
{{else value.deact}}
|
{{else value.deact}}
|
||||||
|
|||||||
21
polaris.dme
21
polaris.dme
@@ -36,6 +36,8 @@
|
|||||||
#include "code\__defines\research.dm"
|
#include "code\__defines\research.dm"
|
||||||
#include "code\__defines\species_languages.dm"
|
#include "code\__defines\species_languages.dm"
|
||||||
#include "code\__defines\targeting.dm"
|
#include "code\__defines\targeting.dm"
|
||||||
|
#include "code\__defines\tgui.dm"
|
||||||
|
#include "code\__defines\tick.dm"
|
||||||
#include "code\__defines\turfs.dm"
|
#include "code\__defines\turfs.dm"
|
||||||
#include "code\__defines\unit_tests.dm"
|
#include "code\__defines\unit_tests.dm"
|
||||||
#include "code\_compatibility\509\_JSON.dm"
|
#include "code\_compatibility\509\_JSON.dm"
|
||||||
@@ -45,6 +47,7 @@
|
|||||||
#include "code\_compatibility\509\type2type.dm"
|
#include "code\_compatibility\509\type2type.dm"
|
||||||
#include "code\_helpers\_global_objects.dm"
|
#include "code\_helpers\_global_objects.dm"
|
||||||
#include "code\_helpers\atmospherics.dm"
|
#include "code\_helpers\atmospherics.dm"
|
||||||
|
#include "code\_helpers\atom_movables.dm"
|
||||||
#include "code\_helpers\datum_pool.dm"
|
#include "code\_helpers\datum_pool.dm"
|
||||||
#include "code\_helpers\files.dm"
|
#include "code\_helpers\files.dm"
|
||||||
#include "code\_helpers\game.dm"
|
#include "code\_helpers\game.dm"
|
||||||
@@ -149,6 +152,7 @@
|
|||||||
#include "code\controllers\Processes\Shuttle.dm"
|
#include "code\controllers\Processes\Shuttle.dm"
|
||||||
#include "code\controllers\Processes\sun.dm"
|
#include "code\controllers\Processes\sun.dm"
|
||||||
#include "code\controllers\Processes\supply.dm"
|
#include "code\controllers\Processes\supply.dm"
|
||||||
|
#include "code\controllers\Processes\tgui.dm"
|
||||||
#include "code\controllers\Processes\ticker.dm"
|
#include "code\controllers\Processes\ticker.dm"
|
||||||
#include "code\controllers\Processes\turf.dm"
|
#include "code\controllers\Processes\turf.dm"
|
||||||
#include "code\controllers\Processes\vote.dm"
|
#include "code\controllers\Processes\vote.dm"
|
||||||
@@ -170,6 +174,7 @@
|
|||||||
#include "code\datums\progressbar.dm"
|
#include "code\datums\progressbar.dm"
|
||||||
#include "code\datums\recipe.dm"
|
#include "code\datums\recipe.dm"
|
||||||
#include "code\datums\sun.dm"
|
#include "code\datums\sun.dm"
|
||||||
|
#include "code\datums\weakref.dm"
|
||||||
#include "code\datums\autolathe\arms.dm"
|
#include "code\datums\autolathe\arms.dm"
|
||||||
#include "code\datums\autolathe\autolathe.dm"
|
#include "code\datums\autolathe\autolathe.dm"
|
||||||
#include "code\datums\autolathe\devices.dm"
|
#include "code\datums\autolathe\devices.dm"
|
||||||
@@ -1659,6 +1664,7 @@
|
|||||||
#include "code\modules\nano\interaction\conscious.dm"
|
#include "code\modules\nano\interaction\conscious.dm"
|
||||||
#include "code\modules\nano\interaction\contained.dm"
|
#include "code\modules\nano\interaction\contained.dm"
|
||||||
#include "code\modules\nano\interaction\default.dm"
|
#include "code\modules\nano\interaction\default.dm"
|
||||||
|
#include "code\modules\nano\interaction\hands.dm"
|
||||||
#include "code\modules\nano\interaction\interactive.dm"
|
#include "code\modules\nano\interaction\interactive.dm"
|
||||||
#include "code\modules\nano\interaction\inventory.dm"
|
#include "code\modules\nano\interaction\inventory.dm"
|
||||||
#include "code\modules\nano\interaction\inventory_deep.dm"
|
#include "code\modules\nano\interaction\inventory_deep.dm"
|
||||||
@@ -2028,6 +2034,21 @@
|
|||||||
#include "code\modules\tables\rack.dm"
|
#include "code\modules\tables\rack.dm"
|
||||||
#include "code\modules\tables\tables.dm"
|
#include "code\modules\tables\tables.dm"
|
||||||
#include "code\modules\tables\update_triggers.dm"
|
#include "code\modules\tables\update_triggers.dm"
|
||||||
|
#include "code\modules\tgui\external.dm"
|
||||||
|
#include "code\modules\tgui\process.dm"
|
||||||
|
#include "code\modules\tgui\states.dm"
|
||||||
|
#include "code\modules\tgui\tgui.dm"
|
||||||
|
#include "code\modules\tgui\states\admin.dm"
|
||||||
|
#include "code\modules\tgui\states\conscious.dm"
|
||||||
|
#include "code\modules\tgui\states\contained.dm"
|
||||||
|
#include "code\modules\tgui\states\deep_inventory.dm"
|
||||||
|
#include "code\modules\tgui\states\default.dm"
|
||||||
|
#include "code\modules\tgui\states\hands.dm"
|
||||||
|
#include "code\modules\tgui\states\inventory.dm"
|
||||||
|
#include "code\modules\tgui\states\notcontained.dm"
|
||||||
|
#include "code\modules\tgui\states\physical.dm"
|
||||||
|
#include "code\modules\tgui\states\self.dm"
|
||||||
|
#include "code\modules\tgui\states\zlevel.dm"
|
||||||
#include "code\modules\vehicles\cargo_train.dm"
|
#include "code\modules\vehicles\cargo_train.dm"
|
||||||
#include "code\modules\vehicles\train.dm"
|
#include "code\modules\vehicles\train.dm"
|
||||||
#include "code\modules\vehicles\vehicle.dm"
|
#include "code\modules\vehicles\vehicle.dm"
|
||||||
|
|||||||
BIN
sound/machines/buttonbeep.ogg
Normal file
BIN
sound/machines/buttonbeep.ogg
Normal file
Binary file not shown.
BIN
sound/machines/dotprinter.ogg
Normal file
BIN
sound/machines/dotprinter.ogg
Normal file
Binary file not shown.
5
tgui/.babelrc
Normal file
5
tgui/.babelrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"es2015"
|
||||||
|
]
|
||||||
|
}
|
||||||
1
tgui/.gitattributes
vendored
Normal file
1
tgui/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assets/* binary
|
||||||
2
tgui/.gitignore
vendored
Normal file
2
tgui/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
npm-debug.log
|
||||||
|
node_modules/
|
||||||
18
tgui/LICENSE.md
Normal file
18
tgui/LICENSE.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Copyright (c) 2016 Bjorn Neergaard (neersighted), tgui contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
139
tgui/README.md
Normal file
139
tgui/README.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<!-- TOC depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->
|
||||||
|
|
||||||
|
- [tgui](#tgui)
|
||||||
|
- [Concepts](#concepts)
|
||||||
|
- [Using It](#using-it)
|
||||||
|
- [Copypasta](#copypasta)
|
||||||
|
|
||||||
|
<!-- /TOC -->
|
||||||
|
|
||||||
|
# tgui
|
||||||
|
tgui is the user interface library of /tg/station. It is rendered clientside, based on JSON data sent from the server. Clicks are processed on the server, in a similar method to native BYOND `Topic()`.
|
||||||
|
|
||||||
|
Basic tgui consists of defining a few procs. In these procs you will handle a request to open or update a UI (typically by updating a UI if it exists or setting up and opening it if it does not), a request for data, in which you build a list to be passed as JSON to the UI, and an action handler, which handles any user input. In addition, you will write a HTML template file which renders your data and provides actionable inputs.
|
||||||
|
|
||||||
|
tgui is very different from most UIs you will encounter in BYOND programming, and is heavily reliant of Javascript and web technologies as opposed to DM. However, 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.
|
||||||
|
|
||||||
|
tgui is a fork of NanoUI. The server-side code (DM) is similar and derived from NanoUI, while the clientside is a wholly new project with no code in common.
|
||||||
|
|
||||||
|
## Concepts
|
||||||
|
tgui is loosely based a MVVM architecture. MVVM stands for model, view, view model.
|
||||||
|
- A model is the object that a UI represents. This is the atom a UI corresponds to in the game world in most cases, and is known as the `src_object` in tgui.
|
||||||
|
- The view model is how data is represented in terms of the view. In tgui, this is the `ui_data` proc which munges whatever complex data your `src_object` has into a list.
|
||||||
|
- The view is how the data is rendered. This is the template, a HTML (plus mustaches and other goodies) file which is compiled into the tgui blob that the browser executes.
|
||||||
|
|
||||||
|
Not included in the MVVM model are other important concepts:
|
||||||
|
- The action/topic handler, `ui_act`, is what recieves input from the user and acts on it.
|
||||||
|
- The request/update proc, `ui_interact` is where you open your UI and set options like title, size, autoupdate, theme, and more.
|
||||||
|
- Finally, `ui_state`s (set in `ui_interact`) dictate under what conditions a UI may be interacted with. This may be the standard checks that check if you are in range and conscious, or more.
|
||||||
|
|
||||||
|
States are easy to write and extend, and what make tgui interactions so powerful. Because states can over overridden from other procs, you can build powerful interactions for embedded objects or remote access.
|
||||||
|
|
||||||
|
## Using It
|
||||||
|
All these examples and abstracts sound great, you might say. But you also might say, "How do I use it?"
|
||||||
|
|
||||||
|
Examples can be as simple or as complex as you would like. Let's start with a very basic hello world.
|
||||||
|
|
||||||
|
```DM
|
||||||
|
/obj/machinery/my_machine/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||||
|
ui = tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
|
if(!ui)
|
||||||
|
ui = new(user, src, ui_key, "my_machine", name, 300, 300, master_ui, state)
|
||||||
|
ui.open()
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the proc that defines our interface. There's a bit going on here, so let's break it down. First, we override the ui_interact proc on our object. This will be called by `interact` for you, which is in turn called by `attack_hand` (or `attack_self` for items). `ui_interact` is also called to update a UI (hence the `try_update_ui`), so we accept an existing UI to update. The `state` is a default argument so that a caller can overload it with named arguments (`ui_interact(state = overloaded_state)`) if needed.
|
||||||
|
|
||||||
|
Inside the `if(!ui)` block (which means we are creating a new UI), we choose our template, title, and size; we can also set various options like `style` (for themes), or autoupdate. These options will be elaborated on later (as will `ui_state`s).
|
||||||
|
|
||||||
|
After `ui_interact`, we need to define `ui_data`. This just returns a list of data for our object to use. Let's imagine our object has a few vars:
|
||||||
|
|
||||||
|
```DM
|
||||||
|
/obj/machinery/my_machine/ui_data(mob/user)
|
||||||
|
var/list/data = list()
|
||||||
|
data["health"] = health
|
||||||
|
data["color"] = color
|
||||||
|
|
||||||
|
return data
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ui_data` proc is what people often find the hardest about tgui, but its really quite simple! You just need to represent your object as numbers, strings, and lists, instead of atoms and datums.
|
||||||
|
|
||||||
|
Finally, the `ui_act` proc is called by the interface whenever the user used an input. The input's `action` and `params` are passed to the proc.
|
||||||
|
|
||||||
|
```DM
|
||||||
|
/obj/machinery/my_machine/ui_act(action, params)
|
||||||
|
if(..())
|
||||||
|
return
|
||||||
|
switch(action)
|
||||||
|
if("change_color")
|
||||||
|
var/new_color = params["color"]
|
||||||
|
if(!(color in allowed_coors))
|
||||||
|
return
|
||||||
|
color = new_color
|
||||||
|
. = TRUE
|
||||||
|
update_icon()
|
||||||
|
```
|
||||||
|
|
||||||
|
The `..()` (parent call) is very important here, as it is how we check that the user is allowed to use this interface (to avoid so-called href exploits). It is also very important to clamp and sanitize all input here. Always assume the user is attempting to exploit the game.
|
||||||
|
|
||||||
|
Also note the use of `. = TRUE` (or `FALSE`), which is used to notify the UI that this input caused an update. This is especially important for UIs that do not auto-update, as otherwise the user will never see their change.
|
||||||
|
|
||||||
|
Finally, you have a template. This is also a source of confusion for many new users. Some basic HTML knowledge will get you a long way, however.
|
||||||
|
|
||||||
|
A template is regular HTML, with mustache for logic and built-in components to quickly build UIs. Here's how we might show some data (components will be elaborated on later).
|
||||||
|
|
||||||
|
In a template there are a few special values. `config` is always the same and is part of core tgui (it will be explained later), `data` is the data returned from `ui_data`, and `adata` is the same, but with certain values (numbers at this time) interpolated in order to allow animation.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ui-display>
|
||||||
|
<ui-section label='Health'>
|
||||||
|
<span>{{data.health}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Color'>
|
||||||
|
<span>{{data.color}}</span>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
```
|
||||||
|
|
||||||
|
Templates can be very confusing at first, as ternary operators, computed properties, and iterators are used quite a bit in more complex interfaces. Start with the basics, and work your way up. Much of the complexity stems from performance concerns. If in doubt, take the simpler approach and refactor if performance becomes an issue.
|
||||||
|
|
||||||
|
## Copypasta
|
||||||
|
We all do it, even the best of us. If you just want to make a tgui **fast**, here's what you need (note that you'll probably be forced to clean your shit up upon code review):
|
||||||
|
|
||||||
|
```DM
|
||||||
|
/obj/copypasta/tg_ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) // Remember to use the appropriate state.
|
||||||
|
ui = tgui_process.try_update_ui(user, src, ui_key, ui, force_open)
|
||||||
|
if(!ui)
|
||||||
|
ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state)
|
||||||
|
ui.open()
|
||||||
|
|
||||||
|
/obj/copypasta/ui_data(mob/user)
|
||||||
|
var/list/data = list()
|
||||||
|
data["var"] = var
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
/obj/copypasta/ui_act(action, params)
|
||||||
|
if(..())
|
||||||
|
return
|
||||||
|
switch(action)
|
||||||
|
if("copypasta")
|
||||||
|
var/newvar = params["var"]
|
||||||
|
var = Clamp(newvar, min_val, max_val) // Just a demo of proper input sanitation.
|
||||||
|
. = TRUE
|
||||||
|
update_icon() // Not applicable to all objects.
|
||||||
|
```
|
||||||
|
|
||||||
|
And the template:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ui-display title='My Copypasta Section'>
|
||||||
|
<ui-section label='Var'>
|
||||||
|
<span>{{data.var}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Animated Var'>
|
||||||
|
<span>{{adata.var}}</span>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
```
|
||||||
1
tgui/assets/tgui.css
Normal file
1
tgui/assets/tgui.css
Normal file
File diff suppressed because one or more lines are too long
9
tgui/assets/tgui.js
Normal file
9
tgui/assets/tgui.js
Normal file
File diff suppressed because one or more lines are too long
6
tgui/build_assets.bat
Normal file
6
tgui/build_assets.bat
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@echo off
|
||||||
|
echo node.js and all dependencies must be installed for this script to work.
|
||||||
|
echo If this script fails try installing dependencies again.
|
||||||
|
REM Build minified assets
|
||||||
|
cmd /c gulp --min
|
||||||
|
pause
|
||||||
31
tgui/gulp/css.js
Normal file
31
tgui/gulp/css.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import * as f from './flags'
|
||||||
|
import { gulp as g, postcss as s } from './plugins'
|
||||||
|
|
||||||
|
const entry = 'tgui.styl'
|
||||||
|
|
||||||
|
import gulp from 'gulp'
|
||||||
|
export function css () {
|
||||||
|
return gulp.src(`${f.src}/${entry}`)
|
||||||
|
.pipe(g.if(f.debug, g.sourcemaps.init({loadMaps: true})))
|
||||||
|
.pipe(g.stylus({
|
||||||
|
url: 'data-url',
|
||||||
|
paths: [ f.src ]
|
||||||
|
}))
|
||||||
|
.pipe(g.postcss([
|
||||||
|
s.autoprefixer({ browsers: ['last 2 versions', 'ie >= 8'] }),
|
||||||
|
s.gradient,
|
||||||
|
s.opacity,
|
||||||
|
s.rgba({oldie: true}),
|
||||||
|
s.plsfilters({oldIE: true}),
|
||||||
|
s.fontweights
|
||||||
|
]))
|
||||||
|
.pipe(g.bytediff.start())
|
||||||
|
.pipe(g.if(f.min, g.cssnano({autoprefixer: false})))
|
||||||
|
.pipe(g.if(f.debug, g.sourcemaps.write()))
|
||||||
|
.pipe(g.bytediff.stop())
|
||||||
|
.pipe(gulp.dest(f.dest))
|
||||||
|
}
|
||||||
|
export function watch_css () {
|
||||||
|
gulp.watch(`${f.src}/**/*.styl`, css)
|
||||||
|
return css()
|
||||||
|
}
|
||||||
7
tgui/gulp/flags.js
Normal file
7
tgui/gulp/flags.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const flags = require('minimist')(process.argv.slice(2))
|
||||||
|
|
||||||
|
export const src = flags.src || 'src'
|
||||||
|
export const dest = flags.dest || 'assets'
|
||||||
|
|
||||||
|
export const debug = flags.debug || flags.d
|
||||||
|
export const min = flags.min || flags.m
|
||||||
62
tgui/gulp/js.js
Normal file
62
tgui/gulp/js.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import * as f from './flags'
|
||||||
|
import { browserify as b, gulp as g } from './plugins'
|
||||||
|
|
||||||
|
const entry = 'tgui.js'
|
||||||
|
|
||||||
|
import { transform as babel } from 'babel-core'
|
||||||
|
import { readFileSync as read } from 'fs'
|
||||||
|
b.componentify.compilers['text/javascript'] = function (source, file) {
|
||||||
|
const config = { sourceMaps: true }
|
||||||
|
Object.assign(config, JSON.parse(read(`${f.src}/.babelrc`, 'utf8')))
|
||||||
|
const compiled = babel(source, config)
|
||||||
|
|
||||||
|
return { source: compiled.code, map: compiled.map }
|
||||||
|
}
|
||||||
|
import { render as stylus } from 'stylus'
|
||||||
|
b.componentify.compilers['text/stylus'] = function (source, file) {
|
||||||
|
const config = { filename: file }
|
||||||
|
const compiled = stylus(source, config)
|
||||||
|
|
||||||
|
return { source: compiled }
|
||||||
|
}
|
||||||
|
|
||||||
|
import browserify from 'browserify'
|
||||||
|
const bundle = browserify(`${f.src}/${entry}`, {
|
||||||
|
debug: f.debug,
|
||||||
|
cache: {},
|
||||||
|
packageCache: {},
|
||||||
|
extensions: [ '.js', '.ract' ],
|
||||||
|
paths: [ f.src ]
|
||||||
|
})
|
||||||
|
if (f.min) bundle.plugin(b.collapse)
|
||||||
|
bundle
|
||||||
|
.transform(b.babelify)
|
||||||
|
.plugin(b.helpers)
|
||||||
|
.transform(b.componentify)
|
||||||
|
.transform(b.globify)
|
||||||
|
.transform(b.es3ify)
|
||||||
|
|
||||||
|
import buffer from 'vinyl-buffer'
|
||||||
|
import gulp from 'gulp'
|
||||||
|
import source from 'vinyl-source-stream'
|
||||||
|
export function js () {
|
||||||
|
return bundle.bundle()
|
||||||
|
.pipe(source(entry))
|
||||||
|
.pipe(buffer())
|
||||||
|
.pipe(g.if(f.debug, g.sourcemaps.init({loadMaps: true})))
|
||||||
|
.pipe(g.bytediff.start())
|
||||||
|
.pipe(g.if(f.min, g.uglify({mangle: true, compress: {unsafe: true}})))
|
||||||
|
.pipe(g.if(f.debug, g.sourcemaps.write()))
|
||||||
|
.pipe(g.bytediff.stop())
|
||||||
|
.pipe(gulp.dest(f.dest))
|
||||||
|
}
|
||||||
|
import gulplog from 'gulplog'
|
||||||
|
export function watch_js () {
|
||||||
|
bundle.plugin(b.watchify)
|
||||||
|
bundle.on('update', js)
|
||||||
|
bundle.on('error', err => {
|
||||||
|
gulplog.error(err.toString())
|
||||||
|
this.emit('end')
|
||||||
|
})
|
||||||
|
return js()
|
||||||
|
}
|
||||||
20
tgui/gulp/plugins.js
Normal file
20
tgui/gulp/plugins.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export const browserify = {
|
||||||
|
babelify: require('babelify'),
|
||||||
|
collapse: require('bundle-collapser/plugin'),
|
||||||
|
componentify: require('ractive-componentify'),
|
||||||
|
es3ify: require('es3ify'),
|
||||||
|
globify: require('require-globify'),
|
||||||
|
helpers: require('babelify-external-helpers'),
|
||||||
|
watchify: require('watchify')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gulp = require('gulp-load-plugins')({ replaceString: /^gulp(-|\.)|-/g })
|
||||||
|
|
||||||
|
export const postcss = {
|
||||||
|
autoprefixer: require('autoprefixer'),
|
||||||
|
fontweights: require('postcss-font-weights'),
|
||||||
|
gradient: require('postcss-filter-gradient'),
|
||||||
|
opacity: require('postcss-opacity'),
|
||||||
|
plsfilters: require('pleeease-filters'),
|
||||||
|
rgba: require('postcss-color-rgba-fallback')
|
||||||
|
}
|
||||||
10
tgui/gulp/reload.js
Normal file
10
tgui/gulp/reload.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const out = 'assets'
|
||||||
|
|
||||||
|
import { exec } from 'child_process'
|
||||||
|
export function reload () {
|
||||||
|
return exec('reload.bat')
|
||||||
|
}
|
||||||
|
import gulp from 'gulp'
|
||||||
|
export function watch_reload () {
|
||||||
|
gulp.watch(`${out}/**`, reload)
|
||||||
|
}
|
||||||
9
tgui/gulp/size.js
Normal file
9
tgui/gulp/size.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { gulp as g } from './plugins'
|
||||||
|
|
||||||
|
const out = 'assets'
|
||||||
|
|
||||||
|
import gulp from 'gulp'
|
||||||
|
export function size () {
|
||||||
|
return gulp.src(`${out}/**`)
|
||||||
|
.pipe(g.size())
|
||||||
|
}
|
||||||
12
tgui/gulpfile.babel.js
Normal file
12
tgui/gulpfile.babel.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import gulp from 'gulp'
|
||||||
|
|
||||||
|
import { css, watch_css } from './gulp/css'
|
||||||
|
import { js, watch_js } from './gulp/js'
|
||||||
|
import { reload, watch_reload } from './gulp/reload'
|
||||||
|
import { size } from './gulp/size'
|
||||||
|
|
||||||
|
gulp.task(reload)
|
||||||
|
gulp.task(size)
|
||||||
|
|
||||||
|
gulp.task('default', gulp.series(gulp.parallel(css, js), size))
|
||||||
|
gulp.task('watch', gulp.parallel(watch_css, watch_js, watch_reload))
|
||||||
14
tgui/install_dependencies.bat
Normal file
14
tgui/install_dependencies.bat
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@echo off
|
||||||
|
echo node.js 5.3.0 or newer must be installed for this script to work.
|
||||||
|
echo If this script fails, try closing editors and running it again first.
|
||||||
|
echo Any warnings about optional dependencies can be safely ignored.
|
||||||
|
pause
|
||||||
|
REM Install Gulp
|
||||||
|
cmd /c npm install gulp-cli -g
|
||||||
|
REM Install tgui dependencies
|
||||||
|
cmd /c npm install
|
||||||
|
REM Flatten dependency tree
|
||||||
|
cmd /c npm dedupe
|
||||||
|
REM Clean dependency tree
|
||||||
|
cmd /c npm prune
|
||||||
|
pause
|
||||||
63
tgui/package.json
Normal file
63
tgui/package.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"name": "tgui",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"autoprefixer": "6.3.3",
|
||||||
|
"babel-core": "6.5.2",
|
||||||
|
"babel-plugin-external-helpers": "6.5.0",
|
||||||
|
"babel-polyfill": "6.5.0",
|
||||||
|
"babel-preset-es2015": "6.5.0",
|
||||||
|
"babel-preset-es2015-loose": "7.0.0",
|
||||||
|
"babel-register": "6.5.2",
|
||||||
|
"babelify": "7.2.0",
|
||||||
|
"babelify-external-helpers": "1.1.0",
|
||||||
|
"browserify": "13.0.0",
|
||||||
|
"bulkify": "1.1.1",
|
||||||
|
"bundle-collapser": "1.2.1",
|
||||||
|
"dom4": "1.7.0",
|
||||||
|
"es3ify": "0.2.1",
|
||||||
|
"fg-loadcss": "1.0.0-0",
|
||||||
|
"fontfaceobserver": "1.6.3",
|
||||||
|
"gulp": "github:gulpjs/gulp#4.0",
|
||||||
|
"gulp-bytediff": "1.0.0",
|
||||||
|
"gulp-cssnano": "2.1.1",
|
||||||
|
"gulp-if": "2.0.0",
|
||||||
|
"gulp-load-plugins": "1.2.0",
|
||||||
|
"gulp-postcss": "6.1.0",
|
||||||
|
"gulp-size": "2.0.0",
|
||||||
|
"gulp-sourcemaps": "1.6.0",
|
||||||
|
"gulp-stylus": "2.3.0",
|
||||||
|
"gulp-uglify": "1.5.2",
|
||||||
|
"gulplog": "1.0.0",
|
||||||
|
"html5shiv": "3.7.3",
|
||||||
|
"ie8": "0.3.2",
|
||||||
|
"minimist": "1.2.0",
|
||||||
|
"paths-js": "0.4.2",
|
||||||
|
"pleeease-filters": "2.0.0",
|
||||||
|
"postcss": "5.0.16",
|
||||||
|
"postcss-color-rgba-fallback": "2.2.0",
|
||||||
|
"postcss-filter-gradient": "0.2.2",
|
||||||
|
"postcss-font-weights": "2.0.1",
|
||||||
|
"postcss-opacity": "3.0.0",
|
||||||
|
"ractive": "0.7.3",
|
||||||
|
"ractive-componentify": "0.2.4",
|
||||||
|
"ractive-events-keys": "0.2.1",
|
||||||
|
"ractive-transitions-fade": "0.3.1",
|
||||||
|
"require-globify": "1.3.0",
|
||||||
|
"stylus": "0.53.0",
|
||||||
|
"vinyl-buffer": "1.0.0",
|
||||||
|
"vinyl-source-stream": "1.1.0",
|
||||||
|
"watchify": "3.7.0"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"ractive": "ractive/ractive-legacy.runtime"
|
||||||
|
},
|
||||||
|
"require-globify": {
|
||||||
|
"appliesTo": {
|
||||||
|
"includeExtensions": [
|
||||||
|
".js",
|
||||||
|
".ract"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
tgui/reload.bat
Normal file
3
tgui/reload.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
REM Copy assets to the BYOND cache
|
||||||
|
cmd /c copy assets\* "%USERPROFILE%\Documents\BYOND\cache" /y
|
||||||
8
tgui/src/.babelrc
Normal file
8
tgui/src/.babelrc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"es2015-loose"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"external-helpers"
|
||||||
|
]
|
||||||
|
}
|
||||||
51
tgui/src/Unimplemented/airalarm.ract
Normal file
51
tgui/src/Unimplemented/airalarm.ract
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<link rel='ractive' href='./airalarm/scrubbers.ract'>
|
||||||
|
<link rel='ractive' href='./airalarm/status.ract'>
|
||||||
|
<link rel='ractive' href='./airalarm/thresholds.ract'>
|
||||||
|
<link rel='ractive' href='./airalarm/modes.ract'>
|
||||||
|
<link rel='ractive' href='./airalarm/vents.ract'>
|
||||||
|
|
||||||
|
<ui-notice>
|
||||||
|
{{#if data.siliconUser}}
|
||||||
|
<ui-section label='Interface Lock'>
|
||||||
|
<ui-button icon='{{data.locked ? "lock" : "unlock"}}' action='lock'>{{data.locked ? "Engaged" : "Disengaged"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{else}}
|
||||||
|
<span>Swipe an ID card to {{data.locked ? "unlock" : "lock"}} this interface.</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-notice>
|
||||||
|
<status/>
|
||||||
|
{{#if !data.locked || data.siliconUser}}
|
||||||
|
{{#if config.screen == "home"}}
|
||||||
|
<ui-display title='Air Controls'>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='{{data.atmos_alarm ? "exclamation-triangle" : "exclamation"}}' style='{{data.atmos_alarm ? "caution" : null}}'
|
||||||
|
action='{{data.atmos_alarm ? "reset" : "alarm"}}'>Area Atmosphere Alarm</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='{{data.mode == 3 ? "exclamation-triangle" : "exclamation"}}' style='{{data.mode == 3 ? "danger" : null}}'
|
||||||
|
action='mode' params='{"mode": {{data.mode == 3 ? 1 : 3}}}'>Panic Siphon</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<br/>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='sign-out' action='tgui:view' params='{"screen": "vents"}'>Vent Controls</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='filter' action='tgui:view' params='{"screen": "scrubbers"}'>Scrubber Controls</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='cog' action='tgui:view' params='{"screen": "modes"}'>Operating Mode</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='bar-chart' action='tgui:view' params='{"screen": "thresholds"}'>Alarm Thresholds</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
{{elseif config.screen == "vents"}}
|
||||||
|
<vents/>
|
||||||
|
{{elseif config.screen == "scrubbers"}}
|
||||||
|
<scrubbers/>
|
||||||
|
{{elseif config.screen == "modes"}}
|
||||||
|
<modes/>
|
||||||
|
{{elseif config.screen == "thresholds"}}
|
||||||
|
<thresholds/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
1
tgui/src/Unimplemented/airalarm/back.ract
Normal file
1
tgui/src/Unimplemented/airalarm/back.ract
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<ui-button icon='arrow-left' action='tgui:view' params='{"screen": "home"}'>Back</ui-button>
|
||||||
14
tgui/src/Unimplemented/airalarm/modes.ract
Normal file
14
tgui/src/Unimplemented/airalarm/modes.ract
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<link rel='ractive' href='./back.ract'>
|
||||||
|
|
||||||
|
<ui-display title='Operating Modes' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<back/>
|
||||||
|
{{/partial}}
|
||||||
|
{{#each data.modes}}
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='{{selected ? "check-square-o" : "square-o"}}'
|
||||||
|
state='{{selected ? danger ? "danger" : "selected" : null}}'
|
||||||
|
action='mode' params='{"mode": {{mode}}}'>{{name}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{/each}}
|
||||||
|
</ui-display>
|
||||||
33
tgui/src/Unimplemented/airalarm/scrubbers.ract
Normal file
33
tgui/src/Unimplemented/airalarm/scrubbers.ract
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<link rel='ractive' href='./back.ract'>
|
||||||
|
|
||||||
|
<ui-display title='Scrubber Controls' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<back/>
|
||||||
|
{{/partial}}
|
||||||
|
{{#each data.scrubbers}}
|
||||||
|
<ui-subdisplay title='{{long_name}}'>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{power ? "power-off" : "close"}}' style='{{power ? "selected" : null}}'
|
||||||
|
action='power' params='{"id_tag": "{{id_tag}}", "val": {{+!power}}}'>{{power ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Mode'>
|
||||||
|
<ui-button icon='{{scrubbing ? "filter" : "sign-in"}}' style='{{scrubbing ? null : "danger"}}'
|
||||||
|
action='scrubbing' params='{"id_tag": "{{id_tag}}", "val": {{+!scrubbing}}}'>{{scrubbing ? "Scrubbing" : "Siphoning"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Range'>
|
||||||
|
<ui-button icon='{{widenet ? "expand" : "compress"}}' style='{{widenet ? "selected" : null}}'
|
||||||
|
action='widenet' params='{"id_tag": "{{id_tag}}", "val": {{+!widenet}}}'>{{widenet ? "Expanded" : "Normal"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Filters'>
|
||||||
|
<ui-button icon='{{filter_co2 ? "check-square-o" : "square-o"}}' style='{{filter_co2 ? "selected" : null}}'
|
||||||
|
action='co2_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_co2}}}'>CO2</ui-button>
|
||||||
|
<ui-button icon='{{filter_n2o ? "check-square-o" : "square-o"}}' style='{{filter_n2o ? "selected" : null}}'
|
||||||
|
action='n2o_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_n2o}}}'>N2O</ui-button>
|
||||||
|
<ui-button icon='{{filter_toxins ? "check-square-o" : "square-o"}}' style='{{filter_toxins ? "selected" : null}}'
|
||||||
|
action='tox_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_toxins}}}'>Plasma</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-subdisplay>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Error: No scrubbers connected.</span>
|
||||||
|
{{/each}}
|
||||||
|
</ui-display>
|
||||||
30
tgui/src/Unimplemented/airalarm/status.ract
Normal file
30
tgui/src/Unimplemented/airalarm/status.ract
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<ui-display title='Air Status'>
|
||||||
|
{{#if data.environment_data}}
|
||||||
|
{{#each adata.environment_data}}
|
||||||
|
<ui-section label='{{name}}'>
|
||||||
|
<span class='{{danger_level == 2 ? "bad" : danger_level == 1 ? "average" : "good"}}'>
|
||||||
|
{{Math.fixed(value, 2)}}{{unit}}
|
||||||
|
</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/each}}
|
||||||
|
<ui-section label='Local Status'>
|
||||||
|
<span class='{{data.danger_level == 2 ? "bad bold" : data.danger_level == 1 ? "average bold" : "good"}}'>
|
||||||
|
{{data.danger_level == 2 ? "Danger (Internals Required)" : data.danger_level == 1 ? "Caution" : "Optimal"}}
|
||||||
|
</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Area Status'>
|
||||||
|
<span class='{{data.atmos_alarm || data.fire_alarm ? "bad bold" : "good"}}'>
|
||||||
|
{{data.atmos_alarm ? "Atmosphere Alarm" : fire_alarm ? "Fire Alarm" : "Nominal"}}
|
||||||
|
</span>
|
||||||
|
</ui-section>
|
||||||
|
{{else}}
|
||||||
|
<ui-section label='Warning'>
|
||||||
|
<span class='bad bold'>Cannot obtain air sample for analysis.</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
{{#if data.emagged}}
|
||||||
|
<ui-section label='Warning'>
|
||||||
|
<span class='bad bold'>Safety measures offline. Device may exhibit abnormal behavior.</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
31
tgui/src/Unimplemented/airalarm/thresholds.ract
Normal file
31
tgui/src/Unimplemented/airalarm/thresholds.ract
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<link rel='ractive' href='./back.ract'>
|
||||||
|
|
||||||
|
<ui-display title='Alarm Thresholds' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<back/>
|
||||||
|
{{/partial}}
|
||||||
|
<table>
|
||||||
|
<thead><tr>
|
||||||
|
<th></th>
|
||||||
|
<th><span class="bad">min2</span></th>
|
||||||
|
<th><span class="average">min1</span></th>
|
||||||
|
<th><span class="average">max1</span></th>
|
||||||
|
<th><span class="bad">max2</span></th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each data.thresholds}}<tr>
|
||||||
|
<th>{{{name}}}</th>
|
||||||
|
{{#each settings}}<td>
|
||||||
|
<ui-button action='threshold' params='{"env": "{{env}}", "var": "{{val}}"}'>{{Math.fixed(selected, 2)}}</ui-button>
|
||||||
|
</td>{{/each}}
|
||||||
|
</tr>{{/each}}
|
||||||
|
</tbody>
|
||||||
|
<table>
|
||||||
|
</ui-display>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
th, td {
|
||||||
|
padding-right: 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
tgui/src/Unimplemented/airalarm/vents.ract
Normal file
32
tgui/src/Unimplemented/airalarm/vents.ract
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<link rel='ractive' href='./back.ract'>
|
||||||
|
|
||||||
|
<ui-display title='Vent Controls' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<back/>
|
||||||
|
{{/partial}}
|
||||||
|
{{#each data.vents}}
|
||||||
|
<ui-subdisplay title='{{long_name}}'>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{power ? "power-off" : "close"}}' style='{{power ? "selected" : null}}'
|
||||||
|
action='power' params='{"id_tag": "{{id_tag}}", "val": {{+!power}}}'>{{power ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Mode'>
|
||||||
|
<span>{{direction == "release" ? "Pressurizing" : "Siphoning"}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Pressure Regulator'>
|
||||||
|
<ui-button icon='sign-in' style='{{incheck ? "selected" : null}}'
|
||||||
|
action='incheck' params='{"id_tag": "{{id_tag}}", "val": {{checks}}}'>Internal</ui-button>
|
||||||
|
<ui-button icon='sign-out' style='{{excheck ? "selected" : null}}'
|
||||||
|
action='excheck' params='{"id_tag": "{{id_tag}}", "val": {{checks}}}'>External</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Target Pressure'>
|
||||||
|
<ui-button icon='pencil' action='set_external_pressure'
|
||||||
|
params='{"id_tag": "{{id_tag}}"}'>{{Math.fixed(external)}}</ui-button>
|
||||||
|
<ui-button icon='refresh' state='{{extdefault ? "disabled" : null}}' action='reset_external_pressure'
|
||||||
|
params='{"id_tag": "{{id_tag}}"}'>Reset</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-subdisplay>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Error: No vents connected.</span>
|
||||||
|
{{/each}}
|
||||||
|
</ui-display>
|
||||||
46
tgui/src/Unimplemented/airlock_electronics.ract
Normal file
46
tgui/src/Unimplemented/airlock_electronics.ract
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<ui-display>
|
||||||
|
{{#if data.locked}}
|
||||||
|
<ui-section label='Card locked!'>
|
||||||
|
Swipe ID to continue: <ui-button icon='unlock' action='unlock'>Unlock</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{else}}
|
||||||
|
<ui-section>
|
||||||
|
<ui-button icon='{{data.oneAccess ? "unlock" : "lock"}}' action='one_access'>{{data.oneAccess ? "One" : "All"}} Required</ui-button>
|
||||||
|
<ui-button icon='refresh' action='clear'>Clear</ui-button>
|
||||||
|
<ui-button icon='lock' action='lock'>Lock</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<hr/>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>{{#each data.regions}}
|
||||||
|
<th><span class='highlight bold'>{{name}}</span></th>
|
||||||
|
{{/each}}</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>{{#each data.regions}}
|
||||||
|
<td>{{#each accesses}}
|
||||||
|
<ui-button icon='{{req ? "check-square-o" : "square-o"}}' style='{{req ? "selected" : null}}'
|
||||||
|
action='set' params='{"access": "{{id}}"}'>{{name}}</ui-button>
|
||||||
|
<br/>
|
||||||
|
{{/each}}</td>
|
||||||
|
{{/each}}</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-spacing: 2px;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
td .button {
|
||||||
|
margin-top: 4px
|
||||||
|
}
|
||||||
|
</style>
|
||||||
125
tgui/src/Unimplemented/apc.ract
Normal file
125
tgui/src/Unimplemented/apc.ract
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<script>
|
||||||
|
component.exports = {
|
||||||
|
data: {
|
||||||
|
powerState (status) {
|
||||||
|
switch (status) {
|
||||||
|
case 2: return 'good'
|
||||||
|
case 1: return 'average'
|
||||||
|
default: return 'bad'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
malfAction () {
|
||||||
|
switch (this.get('data.malfStatus')) {
|
||||||
|
case 1: return 'hack'
|
||||||
|
case 2: return 'occupy'
|
||||||
|
case 3: return 'deoccupy'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
malfButton () {
|
||||||
|
switch (this.get('data.malfStatus')) {
|
||||||
|
case 1: return 'Override Programming'
|
||||||
|
case 2:
|
||||||
|
case 4: return 'Shunt Core Process'
|
||||||
|
case 3: return 'Return to Main Core'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
malfIcon () {
|
||||||
|
switch (this.get('data.malfStatus')) {
|
||||||
|
case 1: return 'terminal'
|
||||||
|
case 2:
|
||||||
|
case 4: return 'caret-square-o-down'
|
||||||
|
case 3: return 'caret-square-o-left'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
powerCellStatusState () {
|
||||||
|
const status = this.get('data.powerCellStatus')
|
||||||
|
if (status > 50) return 'good'
|
||||||
|
else if (status > 25) return 'average'
|
||||||
|
else return 'bad'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ui-notice>
|
||||||
|
{{#if data.siliconUser}}
|
||||||
|
<ui-section label='Interface Lock'>
|
||||||
|
<ui-button icon='{{data.locked ? "lock" : "unlock"}}' action='lock'>{{data.locked ? "Engaged" : "Disengaged"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{else}}
|
||||||
|
<span>Swipe an ID card to {{data.locked ? "unlock" : "lock"}} this interface.</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-notice>
|
||||||
|
<ui-display title='Power Status'>
|
||||||
|
<ui-section label='Main Breaker'>
|
||||||
|
{{#if data.locked && !data.siliconUser}}
|
||||||
|
<span class='{{data.isOperating ? "good" : "bad"}}'>{{data.isOperating ? "On" : "Off"}}</span>
|
||||||
|
{{else}}
|
||||||
|
<ui-button icon='{{data.isOperating ? "power-off" : "close"}}' style='{{data.isOperating ? "selected" : null}}'
|
||||||
|
action='breaker'>{{data.isOperating ? "On" : "Off"}}</ui-button>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='External Power'>
|
||||||
|
<span class='{{powerState(data.externalPower)}}'>{{data.externalPower == 2 ? "Good" : data.externalPower == 1 ? "Low" : "None"}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Power Cell'>
|
||||||
|
{{#if data.powerCellStatus != null}}
|
||||||
|
<ui-bar min='0' max='100' value='{{data.powerCellStatus}}' state='{{powerCellStatusState}}'>{{Math.fixed(adata.powerCellStatus)}}%</ui-bar>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Removed</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
{{#if data.powerCellStatus != null}}
|
||||||
|
<ui-section label='Charge Mode'>
|
||||||
|
{{#if data.locked && !data.siliconUser}}
|
||||||
|
<span class='{{data.chargeMode ? "good" : "bad"}}'>{{data.chargeMode ? "Auto" : "Off"}}</span>
|
||||||
|
{{else}}
|
||||||
|
<ui-button icon='{{data.chargeMode ? "refresh" : "close"}}' style='{{data.chargeMode ? "selected" : null}}'
|
||||||
|
action='charge'>{{data.chargeMode ? "Auto" : "Off"}}</ui-button>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
[<span class='{{powerState(data.chargingStatus)}}'>{{data.chargingStatus == 2 ? "Fully Charged" : data.chargingStatus == 1 ? "Charging" : "Not Charging"}}</span>]
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Power Channels'>
|
||||||
|
{{#each data.powerChannels}}
|
||||||
|
<ui-section label='{{title}}' nowrap>
|
||||||
|
<div class='content'>{{Math.round(adata.powerChannels[@index].powerLoad)}} W</div>
|
||||||
|
<div class='content'><span class='{{status >= 2 ? "good" : "bad"}}'>{{status >= 2 ? "On" : "Off"}}</span></div>
|
||||||
|
<div class='content'>[<span>{{status == 1 || status == 3 ? "Auto" : "Manual"}}</span>]</div>
|
||||||
|
<div class='content' style='float:right'>
|
||||||
|
{{#if !data.locked || data.siliconUser}}
|
||||||
|
<ui-button icon='refresh' state='{{status == 1 || status == 3 ? "selected" : null}}'
|
||||||
|
action='channel' params='{{topicParams.auto}}'>Auto</ui-button>
|
||||||
|
<ui-button icon='power-off' state='{{status == 2 ? "selected" : null}}' action='channel'
|
||||||
|
params='{{topicParams.on}}'>On</ui-button>
|
||||||
|
<ui-button icon='close' state='{{status == 0 ? "selected" : null}}' action='channel'
|
||||||
|
params='{{topicParams.off}}'>Off</ui-button>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</ui-section>
|
||||||
|
{{/each}}
|
||||||
|
<ui-section label='Total Load'>
|
||||||
|
<span class='bold'>{{Math.round(adata.totalLoad)}} W</span>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
{{#if data.siliconUser}}
|
||||||
|
<ui-display title='System Overrides'>
|
||||||
|
<ui-button icon='lightbulb-o' action='overload'>Overload</ui-button>
|
||||||
|
{{#if data.malfStatus}}
|
||||||
|
<ui-button icon='{{malfIcon}}' state='{{data.malfStatus == 4 ? "disabled" : null}}' action='{{malfAction}}'>{{malfButton}}</ui-button>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
|
{{/if}}
|
||||||
|
<ui-notice>
|
||||||
|
<ui-section label='Cover Lock'>
|
||||||
|
{{#if data.locked && !data.siliconUser}}
|
||||||
|
<span>{{data.coverLocked ? "Engaged" : "Disengaged"}}</span>
|
||||||
|
{{else}}
|
||||||
|
<ui-button icon='{{data.coverLocked ? "lock" : "unlock"}}' action='cover'>{{data.coverLocked ? "Engaged" : "Disengaged"}}</ui-button>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
</ui-notice>
|
||||||
14
tgui/src/Unimplemented/atmos_alert.ract
Normal file
14
tgui/src/Unimplemented/atmos_alert.ract
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<ui-display title='Alarms'>
|
||||||
|
<ul>
|
||||||
|
{{#each data.priority}}
|
||||||
|
<li><ui-button icon='close' style='danger' action='clear' params='{"zone": "{{.}}"}'>{{.}}</ui-button></li>
|
||||||
|
{{else}}
|
||||||
|
<li><span class='good'>No Priority Alerts</span></li>
|
||||||
|
{{/each}}
|
||||||
|
{{#each data.minor}}
|
||||||
|
<li><ui-button icon='close' style='caution' action='clear' params='{"zone": "{{.}}"}'>{{.}}</ui-button></li>
|
||||||
|
{{else}}
|
||||||
|
<li><span class='good'>No Minor Alerts</span></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</ui-display>
|
||||||
40
tgui/src/Unimplemented/atmos_control.ract
Normal file
40
tgui/src/Unimplemented/atmos_control.ract
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<ui-display title='{{data.tank ? data.sensors[0].long_name : null}}'>
|
||||||
|
{{#each adata.sensors}}
|
||||||
|
<ui-subdisplay title='{{!data.tank ? long_name : null}}'>
|
||||||
|
<ui-section label='Pressure'>
|
||||||
|
<span>{{Math.fixed(pressure, 2)}} kPa</span>
|
||||||
|
</ui-section>
|
||||||
|
{{#if temperature}}
|
||||||
|
<ui-section label='Temperature'>
|
||||||
|
<span>{{Math.fixed(temperature, 2)}} K</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
{{#each gases:id}}
|
||||||
|
<ui-section label='{{id}}'>
|
||||||
|
<span>{{Math.fixed(., 2)}}%</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/each}}
|
||||||
|
</ui-subdisplay>
|
||||||
|
{{/each}}
|
||||||
|
</ui-display>
|
||||||
|
{{#if data.tank}}
|
||||||
|
<ui-display title='Controls' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<ui-button icon='refresh' action='reconnect'>Reconnect</ui-button>
|
||||||
|
{{/partial}}
|
||||||
|
<ui-section label='Input Injector'>
|
||||||
|
<ui-button icon='{{data.inputting ? "power-off" : "close"}}' style='{{data.inputting ? "selected" : null}}' action='input'>
|
||||||
|
{{data.inputting ? "Injecting": "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Input Rate'>
|
||||||
|
<span>{{Math.fixed(adata.inputRate)}} L/s</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Output Regulator'>
|
||||||
|
<ui-button icon='{{data.outputting ? "power-off" : "close"}}' style='{{data.outputting ? "selected" : null}}' action='output'>
|
||||||
|
{{data.outputting ? "Open": "Closed"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Output Pressure'>
|
||||||
|
<ui-button icon='pencil' action='pressure'>{{Math.round(adata.outputPressure)}} kPa</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
{{/if}}
|
||||||
25
tgui/src/Unimplemented/atmos_filter.ract
Normal file
25
tgui/src/Unimplemented/atmos_filter.ract
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<ui-display>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
|
||||||
|
action='power'>{{data.on ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Output Pressure'>
|
||||||
|
<ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
|
||||||
|
<ui-button icon='plus' state='{{data.pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
|
||||||
|
<span>{{Math.round(adata.pressure)}} kPa</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Filter'>
|
||||||
|
<ui-button state='{{data.filter_type == "" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": ""}'>Nothing</ui-button>
|
||||||
|
<ui-button state='{{data.filter_type == "plasma" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": "plasma"}'>Plasma</ui-button>
|
||||||
|
<ui-button state='{{data.filter_type == "o2" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": "o2"}'>O2</ui-button>
|
||||||
|
<ui-button state='{{data.filter_type == "n2" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": "n2"}'>N2</ui-button>
|
||||||
|
<ui-button state='{{data.filter_type == "co2" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": "co2"}'>CO2</ui-button>
|
||||||
|
<ui-button state='{{data.filter_type == "n2o" ? "selected" : null}}'
|
||||||
|
action='filter' params='{"mode": "n2o"}'>N2O</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
33
tgui/src/Unimplemented/atmos_mixer.ract
Normal file
33
tgui/src/Unimplemented/atmos_mixer.ract
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<ui-display>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
|
||||||
|
action='power'>{{data.on ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Output Pressure'>
|
||||||
|
<ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
|
||||||
|
<ui-button icon='plus' state='{{data.set_pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
|
||||||
|
<span>{{Math.round(adata.set_pressure)}} kPa</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Node 1'>
|
||||||
|
<ui-button icon='fast-backward' state='{{data.node1_concentration == 0 ? "disabled" : null}}'
|
||||||
|
action='node1' params='{"concentration": -0.1}'/>
|
||||||
|
<ui-button icon='backward' state='{{data.node1_concentration == 0 ? "disabled" : null}}'
|
||||||
|
action='node1' params='{"concentration": -0.01}'/>
|
||||||
|
<ui-button icon='forward' state='{{data.node1_concentration == 100 ? "disabled" : null}}'
|
||||||
|
action='node1' params='{"concentration": 0.01}'/>
|
||||||
|
<ui-button icon='fast-forward' state='{{data.node1_concentration == 100 ? "disabled" : null}}'
|
||||||
|
action='node1' params='{"concentration": 0.1}'/>
|
||||||
|
<span>{{Math.round(adata.node1_concentration)}}%</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Node 2'>
|
||||||
|
<ui-button icon='fast-backward' state='{{data.node2_concentration == 0 ? "disabled" : null}}'
|
||||||
|
action='node2' params='{"concentration": -0.1}'/>
|
||||||
|
<ui-button icon='backward' state='{{data.node2_concentration == 0 ? "disabled" : null}}'
|
||||||
|
action='node2' params='{"concentration": -0.01}'/>
|
||||||
|
<ui-button icon='forward' state='{{data.node2_concentration == 100 ? "disabled" : null}}'
|
||||||
|
action='node2' params='{"concentration": 0.01}'/>
|
||||||
|
<ui-button icon='fast-forward' state='{{data.node2_concentration == 100 ? "disabled" : null}}'
|
||||||
|
action='node2' params='{"concentration": 0.1}'/>
|
||||||
|
<span>{{Math.round(adata.node2_concentration)}}%</span>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
19
tgui/src/Unimplemented/atmos_pump.ract
Normal file
19
tgui/src/Unimplemented/atmos_pump.ract
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<ui-display>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
|
||||||
|
action='power'>{{data.on ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{#if data.max_rate}}
|
||||||
|
<ui-section label='Transfer Rate'>
|
||||||
|
<ui-button icon='pencil' action='rate' params='{"rate": "input"}'>Set</ui-button>
|
||||||
|
<ui-button icon='plus' state='{{data.rate == data.max_rate ? "disabled" : null}}' action='transfer' params='{"rate": "max"}'>Max</ui-button>
|
||||||
|
<span>{{Math.round(adata.rate)}} L/s</span>
|
||||||
|
</ui-section>
|
||||||
|
{{else}}
|
||||||
|
<ui-section label='Output Pressure'>
|
||||||
|
<ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
|
||||||
|
<ui-button icon='plus' state='{{data.pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
|
||||||
|
<span>{{Math.round(adata.pressure)}} kPa</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
38
tgui/src/Unimplemented/chem_dispenser.ract
Normal file
38
tgui/src/Unimplemented/chem_dispenser.ract
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<ui-display title='Status'>
|
||||||
|
<ui-section label='Energy'>
|
||||||
|
<ui-bar min='0' max='{{data.maxEnergy}}' value='{{data.energy}}'>{{Math.fixed(adata.energy)}} Units</ui-bar>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Dispense' button>
|
||||||
|
{{#partial button}}
|
||||||
|
{{#each data.beakerTransferAmounts}}
|
||||||
|
<ui-button icon='plus' state='{{data.amount == . ? "selected" : null}}' action='amount' params='{"target": {{.}}}'>{{.}}</ui-button>
|
||||||
|
{{/each}}
|
||||||
|
{{/partial}}
|
||||||
|
<ui-section>
|
||||||
|
{{#each data.chemicals}}
|
||||||
|
<ui-button grid icon='tint' action='dispense' params='{"reagent": "{{id}}"}'>{{title}}</ui-button>
|
||||||
|
{{/each}}
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Beaker' button>
|
||||||
|
{{#partial button}}
|
||||||
|
{{#each data.beakerTransferAmounts}}
|
||||||
|
<ui-button icon='minus' action='remove' params='{"amount": {{.}}}'>{{.}}</ui-button>
|
||||||
|
{{/each}}
|
||||||
|
<ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='eject'>Eject</ui-button>
|
||||||
|
{{/partial}}
|
||||||
|
<ui-section label='Contents'>
|
||||||
|
{{#if data.isBeakerLoaded}}
|
||||||
|
<span>{{Math.round(adata.beakerCurrentVolume)}}/{{data.beakerMaxVolume}} Units</span>
|
||||||
|
<br/>
|
||||||
|
{{#each adata.beakerContents}}
|
||||||
|
<span class='highlight' intro-outro='fade'>{{Math.fixed(volume, 2)}} units of {{name}}</span><br/>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Beaker Empty</span>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<span class='average'>No Beaker</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
29
tgui/src/Unimplemented/chem_heater.ract
Normal file
29
tgui/src/Unimplemented/chem_heater.ract
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<ui-display title='Thermostat'>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{data.isActive ? "power-off" : "close"}}'
|
||||||
|
style='{{data.isActive ? "selected" : null}}'
|
||||||
|
state='{{data.isBeakerLoaded ? null : "disabled"}}'
|
||||||
|
action='power'>{{data.isActive ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Target'>
|
||||||
|
<ui-button icon='pencil' action='temperature' params='{"target": "input"}'>{{Math.round(adata.targetTemp)}} K</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Beaker' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='eject'>Eject</ui-button>
|
||||||
|
{{/partial}}
|
||||||
|
<ui-section label='Contents'>
|
||||||
|
{{#if data.isBeakerLoaded}}
|
||||||
|
<span>Temperature: {{Math.round(adata.currentTemp)}} K</span>
|
||||||
|
<br />
|
||||||
|
{{#each adata.beakerContents}}
|
||||||
|
<span class='highlight' intro-outro='fade'>{{Math.fixed(volume, 2)}} units of {{name}}</span><br/>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Beaker Empty</span>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<span class='average'>No Beaker</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
74
tgui/src/Unimplemented/cryo.ract
Normal file
74
tgui/src/Unimplemented/cryo.ract
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<script>
|
||||||
|
component.exports = {
|
||||||
|
data: {
|
||||||
|
temperatureStatus (temp) {
|
||||||
|
if (temp < 225) return 'good'
|
||||||
|
else if (temp < 273.15) return 'average'
|
||||||
|
else return 'bad'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
occupantStatState () {
|
||||||
|
switch (this.get('data.occupant.stat')) {
|
||||||
|
case 0: return 'good'
|
||||||
|
case 1: return 'average'
|
||||||
|
default: return 'bad'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ui-display title='Occupant'>
|
||||||
|
<ui-section label='Occupant'>
|
||||||
|
<span>{{data.occupant.name ? data.occupant.name : "No Occupant"}}</span>
|
||||||
|
</ui-section>
|
||||||
|
{{#if data.hasOccupant}}
|
||||||
|
<ui-section label='State'>
|
||||||
|
<span class='{{occupantStatState}}'>{{data.occupant.stat == 0 ? "Conscious" : data.occupant.stat == 1 ? "Unconcious" : "Dead"}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Temperature'>
|
||||||
|
<span class='{{temperatureStatus(adata.occupant.bodyTemperature)}}'>{{Math.round(adata.occupant.bodyTemperature)}} K</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Health'>
|
||||||
|
<ui-bar min='{{data.occupant.minHealth}}' max='{{data.occupant.maxHealth}}' value='{{data.occupant.health}}'
|
||||||
|
state='{{data.occupant.health >= 0 ? "good" : "average"}}'>{{Math.round(adata.occupant.health)}}</ui-bar>
|
||||||
|
</ui-section>
|
||||||
|
{{#each [{label: "Brute", type: "bruteLoss"}, {label: "Respiratory", type: "oxyLoss"}, {label: "Toxin", type: "toxLoss"}, {label: "Burn", type: "fireLoss"}]}}
|
||||||
|
<ui-section label='{{label}}'>
|
||||||
|
<ui-bar min='0' max='{{data.occupant.maxHealth}}' value='{{data.occupant[type]}}' state='bad'>{{Math.round(adata.occupant[type])}}</ui-bar>
|
||||||
|
</ui-section>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Cell'>
|
||||||
|
<ui-section label='Power'>
|
||||||
|
<ui-button icon='{{data.isOperating ? "power-off" : "close"}}'
|
||||||
|
style='{{data.isOperating ? "selected" : null}}'
|
||||||
|
state='{{data.isOpen ? "disabled" : null}}'
|
||||||
|
action='power'>{{data.isOperating ? "On" : "Off"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Temperature'>
|
||||||
|
<span class='{{temperatureStatus(adata.cellTemperature)}}'>{{Math.round(adata.cellTemperature)}} K</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Door'>
|
||||||
|
<ui-button icon='{{data.isOpen ? "unlock" : "lock"}}' action='door'>{{data.isOpen ? "Open" : "Closed"}}</ui-button>
|
||||||
|
<ui-button icon='{{data.autoEject ? "sign-out" : "sign-in"}}' action='autoeject'>{{data.autoEject ? "Auto" : "Manual"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
|
<ui-display title='Beaker' button>
|
||||||
|
{{#partial button}}
|
||||||
|
<ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='ejectbeaker'>Eject</ui-button>
|
||||||
|
{{/partial}}
|
||||||
|
<ui-section label='Contents'>
|
||||||
|
{{#if data.isBeakerLoaded}}
|
||||||
|
{{#each adata.beakerContents}}
|
||||||
|
<span class='highlight' intro-outro='fade'>{{Math.fixed(volume, 2)}} units of {{name}}</span><br/>
|
||||||
|
{{else}}
|
||||||
|
<span class='bad'>Beaker Empty</span>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<span class='average'>No Beaker</span>
|
||||||
|
{{/if}}
|
||||||
|
</ui-section>
|
||||||
|
</ui-display>
|
||||||
29
tgui/src/Unimplemented/firealarm.ract
Normal file
29
tgui/src/Unimplemented/firealarm.ract
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script>
|
||||||
|
component.exports = {
|
||||||
|
computed: {
|
||||||
|
seclevelState () {
|
||||||
|
switch (this.get('data.seclevel')) {
|
||||||
|
case 'blue': return 'average'
|
||||||
|
case 'red': return 'bad'
|
||||||
|
case 'delta': return 'bad bold'
|
||||||
|
default: return 'good'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ui-display>
|
||||||
|
<ui-section label='Alert Level'>
|
||||||
|
<span class='{{seclevelState}}'>{{text.titleCase(data.seclevel)}}</span>
|
||||||
|
</ui-section>
|
||||||
|
<ui-section label='Controls'>
|
||||||
|
<ui-button icon='{{data.alarm ? "close" : "bell-o"}}' action='{{data.alarm ? "reset" : "alarm"}}'>
|
||||||
|
{{data.alarm ? "Reset" : "Activate"}}</ui-button>
|
||||||
|
</ui-section>
|
||||||
|
{{#if data.emagged}}
|
||||||
|
<ui-section label='Warning'>
|
||||||
|
<span class='bad bold'>Safety measures offline. Device may exhibit abnormal behavior.</span>
|
||||||
|
</ui-section>
|
||||||
|
{{/if}}
|
||||||
|
</ui-display>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user