diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index dbe66b1243..37d495c3b6 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -161,3 +161,8 @@ if (!(DATUM.datum_flags & DF_ISPROCESSING)) {\
#define START_PROCESSING_POWER_OBJECT(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_power_items)
#define STOP_PROCESSING_POWER_OBJECT(Datum) STOP_PROCESSING_IN_LIST(Datum, global.processing_power_items)
+
+// Computer login types
+#define LOGIN_TYPE_NORMAL 1
+#define LOGIN_TYPE_AI 2
+#define LOGIN_TYPE_ROBOT 3
\ No newline at end of file
diff --git a/code/__defines/sound.dm b/code/__defines/sound.dm
index f155f5c736..3c4567bef4 100644
--- a/code/__defines/sound.dm
+++ b/code/__defines/sound.dm
@@ -170,7 +170,7 @@
'sound/ambience/foreboding/foreboding3.ogg',\
'sound/ambience/foreboding/foreboding4.ogg',\
'sound/ambience/foreboding/foreboding5.ogg',\
- 'sound/ambience/foreboding/foreboding6.ogg'\
+ 'sound/ambience/maintenance/maintenance6.ogg'\
)
// Ambience heard when aboveground on Sif and not in a Point of Interest.
diff --git a/code/__defines/tgui.dm b/code/__defines/tgui.dm
index a6708c8bb7..7f5ceec8b9 100644
--- a/code/__defines/tgui.dm
+++ b/code/__defines/tgui.dm
@@ -16,4 +16,14 @@
/// Get a window id based on the provided pool index
#define TGUI_WINDOW_ID(index) "tgui-window-[index]"
/// Get a pool index of the provided window id
-#define TGUI_WINDOW_INDEX(window_id) text2num(copytext(window_id, 13))
\ No newline at end of file
+#define TGUI_WINDOW_INDEX(window_id) text2num(copytext(window_id, 13))
+
+/// Max length for Modal Input
+#define TGUI_MODAL_INPUT_MAX_LENGTH 1024
+/// Max length for Modal Input for names
+#define TGUI_MODAL_INPUT_MAX_LENGTH_NAME 64 // Names for generally anything don't go past 32, let alone 64.
+
+#define TGUI_MODAL_OPEN 1
+#define TGUI_MODAL_DELEGATE 2
+#define TGUI_MODAL_ANSWER 3
+#define TGUI_MODAL_CLOSE 4
\ No newline at end of file
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 0d6598d2f0..7a0d90d650 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -635,4 +635,62 @@ datum/projectile_data
min(list_x),
min(list_y),
max(list_x),
- max(list_y))
\ No newline at end of file
+ max(list_y))
+
+// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents.
+// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects,
+// being unable to hear people due to being in a box within a bag.
+
+/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1)
+
+ //GLOB.debug_mob += O.contents.len
+ if(!recursion_limit)
+ return L
+ for(var/atom/A in O.contents)
+
+ if(ismob(A))
+ var/mob/M = A
+ if(client_check && !M.client)
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ continue
+ if(sight_check && !isInSight(A, O))
+ continue
+ L |= M
+ //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
+
+ else if(include_radio && istype(A, /obj/item/radio))
+ if(sight_check && !isInSight(A, O))
+ continue
+ L |= A
+
+ if(isobj(A) || ismob(A))
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ return L
+
+// The old system would loop through lists for a total of 5000 per function call, in an empty server.
+// This new system will loop at around 1000 in an empty server.
+
+/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE)
+ // Returns a list of mobs in range of R from source. Used in radio and say code.
+
+ var/turf/T = get_turf(source)
+ var/list/hear = list()
+
+ if(!T)
+ return hear
+
+ var/list/range = hear(R, T)
+
+ for(var/atom/A in range)
+ if(ismob(A))
+ var/mob/M = A
+ if(M.client || include_clientless)
+ hear += M
+ //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
+ else if(istype(A, /obj/item/radio))
+ hear += A
+
+ if(isobj(A) || ismob(A))
+ hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
+
+ return hear
\ No newline at end of file
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 871b4dd4bc..909b0792f6 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -319,6 +319,19 @@ var/global/list/PDA_Manifest = list()
var/datum/job/J = SSjob.get_job(assignment)
hidden = J?.offmap_spawn
+ /* Note: Due to cached_character_icon, a number of emergent properties occur due to the initialization
+ * order of readied-up vs latejoiners. Namely, latejoiners will get a uniform in their datacore picture, but readied-up will
+ * not. This is due to the fact that SSticker calls data_core.manifest_inject() inside of ticker/proc/create_characters(),
+ * but does not equip them until ticker/proc/equip_characters(), which is called later. So, this proc is literally called before
+ * they ever get their equipment, and so it can't get a picture of them in their equipment.
+ * Latejoiners do not have this problem, because /mob/new_player/proc/AttemptLateSpawn calls EquipRank() before it calls
+ * this proc, which means that they're already clothed by the time they get their picture taken here.
+ * The COMPILE_OVERLAYS() here is just to bypass SSoverlays taking for-fucking-ever to update the mob, since we're about to
+ * take a picture of them, we want all the overlays.
+ */
+ COMPILE_OVERLAYS(H)
+ SSoverlays.queue -= H
+
var/id = generate_record_id()
//General Record
var/datum/data/record/G = CreateGeneralRecord(H, id, hidden)
@@ -405,8 +418,8 @@ var/global/list/PDA_Manifest = list()
var/icon/side
if(H)
var/icon/charicon = cached_character_icon(H)
- front = icon(charicon, dir = SOUTH)
- side = icon(charicon, dir = WEST)
+ front = icon(charicon, dir = SOUTH, frame = 1)
+ side = icon(charicon, dir = WEST, frame = 1)
else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record.
front = icon('html/images/no_image32.png')
side = icon('html/images/no_image32.png')
@@ -432,6 +445,8 @@ var/global/list/PDA_Manifest = list()
G.fields["religion"] = "Unknown"
G.fields["photo_front"] = front
G.fields["photo_side"] = side
+ G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(front)]'"
+ G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(side)]'"
G.fields["notes"] = "No notes found."
if(hidden)
hidden_general += G
diff --git a/code/game/antagonist/antagonist_update.dm b/code/game/antagonist/antagonist_update.dm
index 16c27f82c7..b118cb91db 100644
--- a/code/game/antagonist/antagonist_update.dm
+++ b/code/game/antagonist/antagonist_update.dm
@@ -15,7 +15,7 @@
spawn(3)
var/mob/living/carbon/human/H = player.current
if(istype(H))
- H.change_appearance(APPEARANCE_ALL, H.loc, H, species_whitelist = valid_species, state = z_state)
+ H.change_appearance(APPEARANCE_ALL, H, species_whitelist = valid_species, state = GLOB.tgui_self_state)
return player.current
/datum/antagonist/proc/update_access(var/mob/living/player)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index f9b4d9dbc5..85ada387a6 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -12,6 +12,8 @@
var/throwpass = 0
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = 1 //filter for actions - used by lighting overlays
+ var/atom_say_verb = "says"
+ var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles
var/fluorescent // Shows up under a UV light.
var/last_bumped = 0
@@ -589,3 +591,20 @@
>>
"}
+
+/atom/proc/atom_say(message)
+ if(!message)
+ return
+ var/list/speech_bubble_hearers = list()
+ for(var/mob/M in get_mobs_in_view(7, src))
+ M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1)
+ if(M.client)
+ speech_bubble_hearers += M.client
+
+ if(length(speech_bubble_hearers))
+ var/image/I = image('icons/mob/talk.dmi', src, "[bubble_icon][say_test(message)]", FLY_LAYER)
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30)
+
+/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
+ return
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index b9b8f6db8a..eb0ad7000c 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -5,6 +5,11 @@
#define DNA2_BUF_UE 2
#define DNA2_BUF_SE 4
+#define PAGE_UI "ui"
+#define PAGE_SE "se"
+#define PAGE_BUFFER "buffer"
+#define PAGE_REJUVENATORS "rejuvenators"
+
//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0),
/datum/dna2/record
var/datum/dna/dna = null
@@ -38,6 +43,22 @@
ser["type"] = "se"
return ser
+/datum/dna2/record/proc/copy()
+ var/datum/dna2/record/newrecord = new /datum/dna2/record
+ newrecord.dna = dna.Clone()
+ newrecord.types = types
+ newrecord.name = name
+ newrecord.mind = mind
+ newrecord.ckey = ckey
+ newrecord.languages = languages
+ newrecord.implant = implant
+ newrecord.flavor = flavor
+ newrecord.gender = gender
+ newrecord.body_descriptors = body_descriptors.Copy()
+ newrecord.genetic_modifiers = genetic_modifiers.Copy()
+ return newrecord
+
+
/////////////////////////// DNA MACHINES
/obj/machinery/dna_scannernew
name = "\improper DNA modifier"
@@ -55,13 +76,28 @@
var/mob/living/carbon/occupant = null
var/obj/item/weapon/reagent_containers/glass/beaker = null
var/opened = 0
+ var/damage_coeff
+ var/scan_level
+ var/precision_coeff
/obj/machinery/dna_scannernew/Initialize()
. = ..()
default_apply_parts()
+ RefreshParts()
+
+/obj/machinery/dna_scannernew/RefreshParts()
+ scan_level = 0
+ damage_coeff = 0
+ precision_coeff = 0
+ for(var/obj/item/weapon/stock_parts/scanning_module/P in component_parts)
+ scan_level += P.rating
+ for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts)
+ precision_coeff = P.rating
+ for(var/obj/item/weapon/stock_parts/micro_laser/P in component_parts)
+ damage_coeff = P.rating
/obj/machinery/dna_scannernew/relaymove(mob/user as mob)
- if (user.stat)
+ if(user.stat)
return
src.go_out()
return
@@ -71,7 +107,7 @@
set category = "Object"
set name = "Eject DNA Scanner"
- if (usr.stat != 0)
+ if(usr.stat != 0)
return
eject_occupant()
@@ -98,15 +134,15 @@
set category = "Object"
set name = "Enter DNA Scanner"
- if (usr.stat != 0)
+ if(usr.stat != 0)
return
- if (!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna
+ if(!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna
to_chat(usr, "Try as you might, you can not climb up into the scanner.")
return
- if (src.occupant)
+ if(src.occupant)
to_chat(usr, "The scanner is already occupied!")
return
- if (usr.abiotic())
+ if(usr.abiotic())
to_chat(usr, "The subject cannot have abiotic items on.")
return
usr.stop_pulling()
@@ -116,7 +152,7 @@
src.occupant = usr
src.icon_state = "scanner_1"
src.add_fingerprint(usr)
- return
+ SStgui.update_uis(src)
/obj/machinery/dna_scannernew/attackby(var/obj/item/weapon/item as obj, var/mob/user as mob)
if(istype(item, /obj/item/weapon/reagent_containers/glass))
@@ -128,10 +164,11 @@
user.drop_item()
item.loc = src
user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!")
+ SStgui.update_uis(src)
return
else if(istype(item, /obj/item/organ/internal/brain))
- if (src.occupant)
+ if(src.occupant)
to_chat(user, "The scanner is already occupied!")
return
var/obj/item/organ/internal/brain/brain = item
@@ -141,19 +178,20 @@
put_in(brain.brainmob)
src.add_fingerprint(user)
user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!")
+ SStgui.update_uis(src)
return
else
to_chat(user, "\The [brain] is not acceptable for genetic sampling!")
- else if (!istype(item, /obj/item/weapon/grab))
+ else if(!istype(item, /obj/item/weapon/grab))
return
var/obj/item/weapon/grab/G = item
- if (!ismob(G.affecting))
+ if(!ismob(G.affecting))
return
- if (src.occupant)
+ if(src.occupant)
to_chat(user, "The scanner is already occupied!")
return
- if (G.affecting.abiotic())
+ if(G.affecting.abiotic())
to_chat(user, "The subject cannot have abiotic items on.")
return
put_in(G.affecting)
@@ -180,12 +218,12 @@
if(ghost.mind == M.mind)
to_chat(ghost, "Your corpse has been placed into a cloning scanner. Return to your body if you want to be resurrected/cloned! (Verbs -> Ghost -> Re-enter corpse)")
break
- return
+ SStgui.update_uis(src)
/obj/machinery/dna_scannernew/proc/go_out()
- if ((!( src.occupant ) || src.locked))
+ if((!( src.occupant ) || src.locked))
return
- if (src.occupant.client)
+ if(src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
if(istype(occupant,/mob/living/carbon/brain))
@@ -198,7 +236,7 @@
src.occupant.loc = src.loc
src.occupant = null
src.icon_state = "scanner_0"
- return
+ SStgui.update_uis(src)
/obj/machinery/dna_scannernew/ex_act(severity)
switch(severity)
@@ -211,7 +249,7 @@
qdel(src)
return
if(2.0)
- if (prob(50))
+ if(prob(50))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
ex_act(severity)
@@ -220,7 +258,7 @@
qdel(src)
return
if(3.0)
- if (prob(25))
+ if(prob(25))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
ex_act(severity)
@@ -251,21 +289,20 @@
var/injector_ready = 0 //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/weapon/disk/data/disk = null
- var/selected_menu_key = null
+ var/selected_menu_key = PAGE_UI
anchored = 1
use_power = USE_POWER_IDLE
idle_power_usage = 10
active_power_usage = 400
- var/waiting_for_user_input=0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob)
- if (istype(I, /obj/item/weapon/disk/data)) //INSERT SOME diskS
- if (!src.disk)
+ if(istype(I, /obj/item/weapon/disk/data)) //INSERT SOME diskS
+ if(!src.disk)
user.drop_item()
I.loc = src
src.disk = I
to_chat(user, "You insert [I].")
- SSnanoui.update_uis(src) // update all UIs attached to src
+ SStgui.update_uis(src) // update all UIs attached to src
return
else
..()
@@ -279,7 +316,7 @@
qdel(src)
return
if(2.0)
- if (prob(50))
+ if(prob(50))
//SN src = null
qdel(src)
return
@@ -315,35 +352,28 @@
/obj/machinery/computer/scan_consolenew/process() //not really used right now
if(stat & (NOPOWER|BROKEN))
return
- if (!( src.status )) //remove this
+ if(!( src.status )) //remove this
return
return
*/
/obj/machinery/computer/scan_consolenew/attack_ai(user as mob)
src.add_hiddenprint(user)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/computer/scan_consolenew/attack_hand(user as mob)
if(!..())
- ui_interact(user)
-
- /**
- * The ui_interact proc is used to open and update Nano UIs
- * If ui_interact is not used then the UI will not update correctly
- * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
- *
- * @param user /mob The mob who is interacting with this ui
- * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
- *
- * @return nothing
- */
-/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ tgui_interact(user)
+/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, datum/tgui/ui)
if(!connected || user == connected.occupant || user.stat)
return
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "DNAModifier", name)
+ ui.open()
+/obj/machinery/computer/scan_consolenew/tgui_data(mob/user)
// this is the data which will be sent to the ui
var/data[0]
data["selectedMenuKey"] = selected_menu_key
@@ -355,7 +385,7 @@
data["hasDisk"] = disk ? 1 : 0
var/diskData[0]
- if (!disk || !disk.buf)
+ if(!disk || !disk.buf)
diskData["data"] = null
diskData["owner"] = null
diskData["label"] = null
@@ -383,7 +413,7 @@
data["selectedUITargetHex"] = selected_ui_target_hex
var/occupantData[0]
- if (!src.connected.occupant || !src.connected.occupant.dna)
+ if(!src.connected.occupant || !src.connected.occupant.dna)
occupantData["name"] = null
occupantData["stat"] = null
occupantData["isViableSubject"] = null
@@ -398,7 +428,7 @@
occupantData["name"] = connected.occupant.real_name
occupantData["stat"] = connected.occupant.stat
occupantData["isViableSubject"] = 1
- if (NOCLONE in connected.occupant.mutations || !src.connected.occupant.dna)
+ if(NOCLONE in connected.occupant.mutations || !src.connected.occupant.dna)
occupantData["isViableSubject"] = 0
occupantData["health"] = connected.occupant.health
occupantData["maxHealth"] = connected.occupant.maxHealth
@@ -414,423 +444,357 @@
data["beakerVolume"] = 0
if(connected.beaker)
data["beakerLabel"] = connected.beaker.label_text ? connected.beaker.label_text : null
- if (connected.beaker.reagents && connected.beaker.reagents.reagent_list.len)
+ if(connected.beaker.reagents && connected.beaker.reagents.reagent_list.len)
for(var/datum/reagent/R in connected.beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.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, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700)
- // 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)
+ // Transfer modal information if there is one
+ data["modal"] = tgui_modal_data(src)
-/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
+ return data
+
+/obj/machinery/computer/scan_consolenew/tgui_act(action, params)
if(..())
- return 0 // don't update uis
+ return TRUE
if(!istype(usr.loc, /turf))
- return 0 // don't update uis
+ return TRUE
if(!src || !src.connected)
- return 0 // don't update uis
+ return TRUE
if(irradiating) // Make sure that it isn't already irradiating someone...
- return 0 // don't update uis
+ return TRUE
add_fingerprint(usr)
- if (href_list["selectMenuKey"])
- selected_menu_key = href_list["selectMenuKey"]
- return 1 // return 1 forces an update to all Nano uis attached to src
+ if(tgui_act_modal(action, params))
+ return TRUE
- if (href_list["toggleLock"])
- if ((src.connected && src.connected.occupant))
- src.connected.locked = !( src.connected.locked )
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["pulseRadiation"])
- irradiating = src.radiation_duration
- var/lock_state = src.connected.locked
- src.connected.locked = 1//lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
-
- if (!src.connected.occupant)
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (prob(95))
- if(prob(75))
- randmutb(src.connected.occupant)
- else
- randmuti(src.connected.occupant)
- else
- if(prob(95))
- randmutg(src.connected.occupant)
- else
- randmuti(src.connected.occupant)
-
- src.connected.occupant.apply_effect(((src.radiation_intensity*3)+src.radiation_duration*3), IRRADIATE, check_protection = 0)
- src.connected.locked = lock_state
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["radiationDuration"])
- if (text2num(href_list["radiationDuration"]) > 0)
- if (src.radiation_duration < 20)
- src.radiation_duration += 2
- else
- if (src.radiation_duration > 2)
- src.radiation_duration -= 2
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["radiationIntensity"])
- if (text2num(href_list["radiationIntensity"]) > 0)
- if (src.radiation_intensity < 10)
- src.radiation_intensity++
- else
- if (src.radiation_intensity > 1)
- src.radiation_intensity--
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if (href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0)
- if (src.selected_ui_target < 15)
- src.selected_ui_target++
- src.selected_ui_target_hex = src.selected_ui_target
- switch(selected_ui_target)
- if(10)
- src.selected_ui_target_hex = "A"
- if(11)
- src.selected_ui_target_hex = "B"
- if(12)
- src.selected_ui_target_hex = "C"
- if(13)
- src.selected_ui_target_hex = "D"
- if(14)
- src.selected_ui_target_hex = "E"
- if(15)
- src.selected_ui_target_hex = "F"
- else
- src.selected_ui_target = 0
- src.selected_ui_target_hex = 0
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1)
- if (src.selected_ui_target > 0)
- src.selected_ui_target--
- src.selected_ui_target_hex = src.selected_ui_target
- switch(selected_ui_target)
- if(10)
- src.selected_ui_target_hex = "A"
- if(11)
- src.selected_ui_target_hex = "B"
- if(12)
- src.selected_ui_target_hex = "C"
- if(13)
- src.selected_ui_target_hex = "D"
- if(14)
- src.selected_ui_target_hex = "E"
- else
- src.selected_ui_target = 15
- src.selected_ui_target_hex = "F"
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click
- var/select_block = text2num(href_list["selectUIBlock"])
- var/select_subblock = text2num(href_list["selectUISubblock"])
- if ((select_block <= DNA_UI_LENGTH) && (select_block >= 1))
- src.selected_ui_block = select_block
- if ((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
- src.selected_ui_subblock = select_subblock
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["pulseUIRadiation"])
- var/block = src.connected.occupant.dna.GetUISubBlock(src.selected_ui_block,src.selected_ui_subblock)
-
- irradiating = src.radiation_duration
- var/lock_state = src.connected.locked
- src.connected.locked = 1//lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
-
- if (!src.connected.occupant)
- return 1
-
- if (prob((80 + (src.radiation_duration / 2))))
- block = miniscrambletarget(num2text(selected_ui_target), src.radiation_intensity, src.radiation_duration)
- src.connected.occupant.dna.SetUISubBlock(src.selected_ui_block,src.selected_ui_subblock,block)
- src.connected.occupant.UpdateAppearance()
- src.connected.occupant.apply_effect((src.radiation_intensity+src.radiation_duration), IRRADIATE, check_protection = 0)
- else
- if (prob(20+src.radiation_intensity))
- randmutb(src.connected.occupant)
- domutcheck(src.connected.occupant,src.connected)
- else
- randmuti(src.connected.occupant)
- src.connected.occupant.UpdateAppearance()
- src.connected.occupant.apply_effect(((src.radiation_intensity*2)+src.radiation_duration), IRRADIATE, check_protection = 0)
- src.connected.locked = lock_state
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if (href_list["injectRejuvenators"])
- if (!connected.occupant)
- return 0
- var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5
- if (inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking
- inject_amount = 0
- if (inject_amount > 50)
- inject_amount = 50
- connected.beaker.reagents.trans_to_mob(connected.occupant, inject_amount, CHEM_BLOOD)
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if (href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
- var/select_block = text2num(href_list["selectSEBlock"])
- var/select_subblock = text2num(href_list["selectSESubblock"])
- if ((select_block <= DNA_SE_LENGTH) && (select_block >= 1))
- src.selected_se_block = select_block
- if ((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
- src.selected_se_subblock = select_subblock
- //testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).")
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if (href_list["pulseSERadiation"])
- var/block = src.connected.occupant.dna.GetSESubBlock(src.selected_se_block,src.selected_se_subblock)
- //var/original_block=block
- //testing("Irradiating SE block [src.selected_se_block]:[src.selected_se_subblock] ([block])...")
-
- irradiating = src.radiation_duration
- var/lock_state = src.connected.locked
- src.connected.locked = 1 //lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
-
- if(src.connected.occupant)
- if (prob((80 + (src.radiation_duration / 2))))
- // FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be.
- //if ((src.selected_se_block != 2 || src.selected_se_block != 12 || src.selected_se_block != 8 || src.selected_se_block || 10) && prob (20))
- var/real_SE_block=selected_se_block
- block = miniscramble(block, src.radiation_intensity, src.radiation_duration)
- if(prob(20))
- if (src.selected_se_block > 1 && src.selected_se_block < DNA_SE_LENGTH/2)
- real_SE_block++
- else if (src.selected_se_block > DNA_SE_LENGTH/2 && src.selected_se_block < DNA_SE_LENGTH)
- real_SE_block--
-
- //testing("Irradiated SE block [real_SE_block]:[src.selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
- connected.occupant.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block)
- src.connected.occupant.apply_effect((src.radiation_intensity+src.radiation_duration), IRRADIATE, check_protection = 0)
- domutcheck(src.connected.occupant,src.connected)
- else
- src.connected.occupant.apply_effect(((src.radiation_intensity*2)+src.radiation_duration), IRRADIATE, check_protection = 0)
- if (prob(80-src.radiation_duration))
- //testing("Random bad mut!")
- randmutb(src.connected.occupant)
- domutcheck(src.connected.occupant,src.connected)
- else
- randmuti(src.connected.occupant)
- //testing("Random identity mut!")
- src.connected.occupant.UpdateAppearance()
- src.connected.locked = lock_state
- return 1 // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["ejectBeaker"])
- if(connected.beaker)
- var/obj/item/weapon/reagent_containers/glass/B = connected.beaker
- B.loc = connected.loc
- connected.beaker = null
- return 1
-
- if(href_list["ejectOccupant"])
- connected.eject_occupant()
- return 1
-
- // Transfer Buffer Management
- if(href_list["bufferOption"])
- var/bufferOption = href_list["bufferOption"]
-
- // These bufferOptions do not require a bufferId
- if (bufferOption == "wipeDisk")
- if ((isnull(src.disk)) || (src.disk.read_only))
- //src.temphtml = "Invalid disk. Please try again."
- return 0
-
- src.disk.buf=null
- //src.temphtml = "Data saved."
- return 1
-
- if (bufferOption == "ejectDisk")
- if (!src.disk)
+ . = TRUE
+ switch(action)
+ if("selectMenuKey")
+ var/key = params["key"]
+ if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS)))
return
- src.disk.loc = get_turf(src)
- src.disk = null
- return 1
+ selected_menu_key = key
+ if("toggleLock")
+ if(connected && connected.occupant)
+ connected.locked = !(connected.locked)
- // All bufferOptions from here on require a bufferId
- if (!href_list["bufferId"])
- return 0
+ if("pulseRadiation")
+ irradiating = radiation_duration
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
- var/bufferId = text2num(href_list["bufferId"])
-
- if (bufferId < 1 || bufferId > 3)
- return 0 // Not a valid buffer id
-
- if (bufferOption == "saveUI")
- if(src.connected.occupant && src.connected.occupant.dna)
- var/datum/dna2/record/databuf=new
- databuf.types = DNA2_BUF_UE
- databuf.dna = src.connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- var/mob/living/carbon/human/H = connected.occupant
- databuf.dna.real_name = H.dna.real_name
- databuf.gender = H.gender
- databuf.body_descriptors = H.descriptors
- databuf.name = "Unique Identifier"
- src.buffers[bufferId] = databuf
- return 1
-
- if (bufferOption == "saveUIAndUE")
- if(src.connected.occupant && src.connected.occupant.dna)
- var/datum/dna2/record/databuf=new
- databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
- databuf.dna = src.connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- var/mob/living/carbon/human/H = connected.occupant
- databuf.dna.real_name = H.dna.real_name
- databuf.gender = H.gender
- databuf.body_descriptors = H.descriptors
- databuf.name = "Unique Identifier + Unique Enzymes"
- src.buffers[bufferId] = databuf
- return 1
-
- if (bufferOption == "saveSE")
- if(src.connected.occupant && src.connected.occupant.dna)
- var/datum/dna2/record/databuf=new
- databuf.types = DNA2_BUF_SE
- databuf.dna = src.connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- var/mob/living/carbon/human/H = connected.occupant
- databuf.dna.real_name = H.dna.real_name
- databuf.gender = H.gender
- databuf.body_descriptors = H.descriptors
- databuf.name = "Structural Enzymes"
- src.buffers[bufferId] = databuf
- return 1
-
- if (bufferOption == "clear")
- src.buffers[bufferId]=new /datum/dna2/record()
- return 1
-
- if (bufferOption == "changeLabel")
- var/datum/dna2/record/buf = src.buffers[bufferId]
- var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null, MAX_NAME_LEN)
- buf.name = text
- src.buffers[bufferId] = buf
- return 1
-
- if (bufferOption == "transfer")
- if (!src.connected.occupant || (NOCLONE in src.connected.occupant.mutations) || !src.connected.occupant.dna)
- return
-
- irradiating = 2
- var/lock_state = src.connected.locked
- src.connected.locked = 1//lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10*2) // sleep for 2 seconds
+ SStgui.update_uis(src) // update all UIs attached to src
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
- src.connected.locked = lock_state
+ connected.locked = lock_state
- var/datum/dna2/record/buf = src.buffers[bufferId]
+ if(!connected.occupant)
+ return
- if ((buf.types & DNA2_BUF_UI))
- if ((buf.types & DNA2_BUF_UE))
- src.connected.occupant.real_name = buf.dna.real_name
- src.connected.occupant.name = buf.dna.real_name
- if(ishuman(connected.occupant))
- var/mob/living/carbon/human/H = connected.occupant
- H.gender = buf.gender
- H.descriptors = buf.body_descriptors
- src.connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
- else if (buf.types & DNA2_BUF_SE)
- src.connected.occupant.dna.SE = buf.dna.SE
- src.connected.occupant.dna.UpdateSE()
- if(ishuman(connected.occupant))
- var/mob/living/carbon/human/H = connected.occupant
- H.gender = buf.gender
- H.descriptors = buf.body_descriptors
- domutcheck(src.connected.occupant,src.connected)
- src.connected.occupant.apply_effect(rand(20,50), IRRADIATE, check_protection = 0)
- return 1
-
- if (bufferOption == "createInjector")
- if (src.injector_ready || waiting_for_user_input)
-
- var/success = 1
- var/obj/item/weapon/dnainjector/I = new /obj/item/weapon/dnainjector
- var/datum/dna2/record/buf = src.buffers[bufferId]
- if(href_list["createBlockInjector"])
- waiting_for_user_input=1
- var/list/selectedbuf
- if(buf.types & DNA2_BUF_SE)
- selectedbuf=buf.dna.SE
- else
- selectedbuf=buf.dna.UI
- var/blk = input(usr,"Select Block","Block") in all_dna_blocks(selectedbuf)
- success = setInjectorBlock(I,blk,buf)
+ if(prob(95))
+ if(prob(75))
+ randmutb(connected.occupant)
else
- I.buf = buf
- waiting_for_user_input=0
- if(success)
- I.loc = src.loc
- I.name += " ([buf.name])"
- //src.temphtml = "Injector created."
- src.injector_ready = 0
- spawn(300)
- src.injector_ready = 1
- //else
- //src.temphtml = "Error in injector creation."
- //else
- //src.temphtml = "Replicator not ready yet."
- return 1
+ randmuti(connected.occupant)
+ else
+ if(prob(95))
+ randmutg(connected.occupant)
+ else
+ randmuti(connected.occupant)
- if (bufferOption == "loadDisk")
- if ((isnull(src.disk)) || (!src.disk.buf))
- //src.temphtml = "Invalid disk. Please try again."
- return 0
+ connected.occupant.apply_effect(((radiation_intensity*3)+radiation_duration*3), IRRADIATE, check_protection = 0)
+ if("radiationDuration")
+ radiation_duration = clamp(text2num(params["value"]), 1, 20)
+ if("radiationIntensity")
+ radiation_intensity = clamp(text2num(params["value"]), 1, 10)
+ ////////////////////////////////////////////////////////
+ if("changeUITarget")
+ selected_ui_target = clamp(text2num(params["value"]), 1, 15)
+ selected_ui_target_hex = num2text(selected_ui_target, 1, 16)
+ if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click
+ var/select_block = text2num(params["block"])
+ var/select_subblock = text2num(params["subblock"])
+ if(!select_block || !select_subblock)
+ return
- src.buffers[bufferId]=src.disk.buf
- //src.temphtml = "Data loaded."
- return 1
+ selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH)
+ selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
+ if("pulseUIRadiation")
+ var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block,selected_ui_subblock)
- if (bufferOption == "saveDisk")
- if ((isnull(src.disk)) || (src.disk.read_only))
- //src.temphtml = "Invalid disk. Please try again."
- return 0
+ irradiating = radiation_duration
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
- var/datum/dna2/record/buf = src.buffers[bufferId]
+ SStgui.update_uis(src) // update all UIs attached to src
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
- src.disk.buf = buf
- src.disk.name = "data disk - '[buf.dna.real_name]'"
- //src.temphtml = "Data saved."
- return 1
+ irradiating = 0
+ connected.locked = lock_state
+
+ if(!connected.occupant)
+ return
+
+ if(prob((80 + (radiation_duration / 2))))
+ block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
+ connected.occupant.dna.SetUISubBlock(selected_ui_block,selected_ui_subblock,block)
+ connected.occupant.UpdateAppearance()
+ connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0)
+ else
+ if(prob(20 + radiation_intensity))
+ randmutb(connected.occupant)
+ domutcheck(connected.occupant,connected)
+ else
+ randmuti(connected.occupant)
+ connected.occupant.UpdateAppearance()
+ connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0)
+ ////////////////////////////////////////////////////////
+ if("injectRejuvenators")
+ if(!connected.occupant || !connected.beaker)
+ return
+ var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
+ if(!inject_amount)
+ return
+ connected.beaker.reagents.trans_to_mob(connected.occupant, inject_amount, CHEM_BLOOD)
+ ////////////////////////////////////////////////////////
+ if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
+ var/select_block = text2num(params["block"])
+ var/select_subblock = text2num(params["subblock"])
+ if(!select_block || !select_subblock)
+ return
+
+ selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH)
+ selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
+ if("pulseSERadiation")
+ var/block = connected.occupant.dna.GetSESubBlock(selected_se_block,selected_se_subblock)
+ //var/original_block=block
+ //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
+
+ irradiating = radiation_duration
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
+
+ SStgui.update_uis(src) // update all UIs attached to src
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
+
+ irradiating = 0
+ connected.locked = lock_state
+
+ if(connected.occupant)
+ if(prob((80 + (radiation_duration / 2))))
+ // FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be.
+ //if((selected_se_block != 2 || selected_se_block != 12 || selected_se_block != 8 || selected_se_block || 10) && prob (20))
+ var/real_SE_block=selected_se_block
+ block = miniscramble(block, radiation_intensity, radiation_duration)
+ if(prob(20))
+ if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
+ real_SE_block++
+ else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
+ real_SE_block--
+
+ //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
+ connected.occupant.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block)
+ connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0)
+ domutcheck(connected.occupant,connected)
+ else
+ connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0)
+ if (prob(80-radiation_duration))
+ //testing("Random bad mut!")
+ randmutb(connected.occupant)
+ domutcheck(connected.occupant,connected)
+ else
+ randmuti(connected.occupant)
+ //testing("Random identity mut!")
+ connected.occupant.UpdateAppearance()
+ if("ejectBeaker")
+ if(connected.beaker)
+ var/obj/item/weapon/reagent_containers/glass/B = connected.beaker
+ B.loc = connected.loc
+ connected.beaker = null
+ if("ejectOccupant")
+ connected.eject_occupant()
+ // Transfer Buffer Management
+ if("bufferOption")
+ var/bufferOption = params["option"]
+ var/bufferId = text2num(params["id"])
+ if(bufferId < 1 || bufferId > 3) // Not a valid buffer id
+ return
+
+ var/datum/dna2/record/buffer = buffers[bufferId]
+ switch(bufferOption)
+ if("saveUI")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf=new
+ databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ var/mob/living/carbon/human/H = connected.occupant
+ databuf.dna.real_name = H.dna.real_name
+ databuf.gender = H.gender
+ databuf.body_descriptors = H.descriptors
+ databuf.name = "Unique Identifier"
+ buffers[bufferId] = databuf
+ if("saveUIAndUE")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf=new
+ databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ var/mob/living/carbon/human/H = connected.occupant
+ databuf.dna.real_name = H.dna.real_name
+ databuf.gender = H.gender
+ databuf.body_descriptors = H.descriptors
+ databuf.name = "Unique Identifier + Unique Enzymes"
+ buffers[bufferId] = databuf
+ if("saveSE")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf=new
+ databuf.types = DNA2_BUF_SE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ var/mob/living/carbon/human/H = connected.occupant
+ databuf.dna.real_name = H.dna.real_name
+ databuf.gender = H.gender
+ databuf.body_descriptors = H.descriptors
+ databuf.name = "Structural Enzymes"
+ buffers[bufferId] = databuf
+ if("clear")
+ buffers[bufferId] = new /datum/dna2/record()
+ if("changeLabel")
+ tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
+ if("transfer")
+ if(!connected.occupant || (NOCLONE in connected.occupant.mutations) || !connected.occupant.dna)
+ return
+
+ irradiating = 2
+ var/lock_state = connected.locked
+ connected.locked = 1//lock it
+
+ SStgui.update_uis(src) // update all UIs attached to src
+ sleep(2 SECONDS) // sleep for 2 seconds
+
+ irradiating = 0
+ connected.locked = lock_state
+
+ var/datum/dna2/record/buf = buffers[bufferId]
+
+ if((buf.types & DNA2_BUF_UI))
+ if((buf.types & DNA2_BUF_UE))
+ connected.occupant.real_name = buf.dna.real_name
+ connected.occupant.name = buf.dna.real_name
+ if(ishuman(connected.occupant))
+ var/mob/living/carbon/human/H = connected.occupant
+ H.gender = buf.gender
+ H.descriptors = buf.body_descriptors
+ connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
+ else if(buf.types & DNA2_BUF_SE)
+ connected.occupant.dna.SE = buf.dna.SE
+ connected.occupant.dna.UpdateSE()
+ if(ishuman(connected.occupant))
+ var/mob/living/carbon/human/H = connected.occupant
+ H.gender = buf.gender
+ H.descriptors = buf.body_descriptors
+ domutcheck(connected.occupant,connected)
+ connected.occupant.apply_effect(rand(20,50), IRRADIATE, check_protection = 0)
+ if("createInjector")
+ if(!injector_ready)
+ return
+ if(text2num(params["block"]) > 0)
+ var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI)
+ tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices)
+ else
+ create_injector(bufferId, TRUE)
+ if("loadDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ buffers[bufferId] = disk.buf.copy()
+ if("saveDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ var/datum/dna2/record/buf = buffers[bufferId]
+ disk.buf = buf.copy()
+ disk.name = "data disk - '[buf.dna.real_name]'"
+
+ if("wipeDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ disk.buf = null
+ if("ejectDisk")
+ if(!disk)
+ return
+ disk.forceMove(get_turf(src))
+ disk = null
+
+/**
+ * Creates a blank injector with the name of the buffer at the given buffer_id
+ *
+ * Arguments:
+ * * buffer_id - The ID of the buffer
+ * * copy_buffer - Whether the injector should copy the buffer contents
+ */
+/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE)
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+
+ // Cooldown
+ injector_ready = FALSE
+ addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS)
+
+ // Create it
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ var/obj/item/weapon/dnainjector/I = new()
+ I.forceMove(loc)
+ I.name += " ([buf.name])"
+ if(copy_buffer)
+ I.buf = buf.copy()
+ return I
+
+/**
+ * Called when the injector creation cooldown finishes
+ */
+/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish()
+ injector_ready = TRUE
+
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params)
+ . = TRUE
+ var/id = params["id"] // The modal's ID
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ if("createInjectorBlock")
+ var/buffer_id = text2num(arguments["id"])
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ var/obj/item/weapon/dnainjector/I = create_injector(buffer_id)
+ setInjectorBlock(I, answer, buf.copy())
+ if("changeBufferLabel")
+ var/buffer_id = text2num(arguments["id"])
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ buf.name = answer
+ buffers[buffer_id] = buf
+ else
+ return FALSE
+ else
+ return FALSE
-/////////////////////////// DNA MACHINES
+#undef PAGE_UI
+#undef PAGE_SE
+#undef PAGE_BUFFER
+#undef PAGE_REJUVENATORS
+
+/////////////////////////// DNA MACHINES
\ No newline at end of file
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 974ba754a6..ce2ae90bce 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -51,7 +51,7 @@
return
if(sleeper)
- return ui_interact(user)
+ return tgui_interact(user)
/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user)
if(computer_deconstruction_screwdriver(user, I))
@@ -66,97 +66,21 @@
else
icon_state = initial(icon_state)
-/obj/machinery/sleep_console/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = outside_state)
- var/data[0]
-
- var/obj/machinery/sleeper/S = sleeper
- var/mob/living/carbon/human/occupant = sleeper.occupant
-
- data["power"] = S.stat & (NOPOWER|BROKEN) ? 0 : 1
-
- var/list/reagents = list()
- for(var/T in S.available_chemicals)
- var/list/reagent = list()
- reagent["id"] = T
- reagent["name"] = S.available_chemicals[T]
- if(occupant)
- reagent["amount"] = occupant.reagents.get_reagent_amount(T)
- reagents += list(reagent)
- data["reagents"] = reagents.Copy()
-
- if(occupant)
- data["occupant"] = 1
- switch(occupant.stat)
- if(CONSCIOUS)
- data["stat"] = "Conscious"
- if(UNCONSCIOUS)
- data["stat"] = "Unconscious"
- if(DEAD)
- data["stat"] = "Dead"
- data["health"] = occupant.health
- data["maxHealth"] = occupant.getMaxHealth()
- if(iscarbon(occupant))
- var/mob/living/carbon/C = occupant
- data["pulse"] = C.get_pulse(GETPULSE_TOOL)
- data["brute"] = occupant.getBruteLoss()
- data["burn"] = occupant.getFireLoss()
- data["oxy"] = occupant.getOxyLoss()
- data["tox"] = occupant.getToxLoss()
- else
- data["occupant"] = 0
- if(S.beaker)
- data["beaker"] = S.beaker.reagents.get_free_space()
- else
- data["beaker"] = -1
- data["filtering"] = S.filtering
- data["pump"] = S.pumping
-
- var/stasis_level_name = "Error!"
- for(var/N in S.stasis_choices)
- if(S.stasis_choices[N] == S.stasis_level)
- stasis_level_name = N
- break
- data["stasis"] = stasis_level_name
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
+/obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper UI", 600, 600, state = state)
- ui.set_initial_data(data)
+ ui = new(user, src, "Sleeper", "Sleeper")
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/sleep_console/Topic(href, href_list)
- if(..())
- return 1
+/obj/machinery/sleep_console/tgui_data(mob/user)
+ if(sleeper)
+ return sleeper.tgui_data(user)
+ return null
- var/obj/machinery/sleeper/S = sleeper
-
- if(usr == S.occupant)
- to_chat(usr, "You can't reach the controls from the inside.")
- return
-
- add_fingerprint(usr)
-
- if(href_list["eject"])
- S.go_out()
- if(href_list["beaker"])
- S.remove_beaker()
- if(href_list["sleeper_filter"])
- if(S.filtering != text2num(href_list["sleeper_filter"]))
- S.toggle_filter()
- if(href_list["pump"])
- if(S.pumping != text2num(href_list["pump"]))
- S.toggle_pump()
- if(href_list["chemical"] && href_list["amount"])
- if(S.occupant && S.occupant.stat != DEAD)
- if(href_list["chemical"] in S.available_chemicals) // Your hacks are bad and you should feel bad
- S.inject_chemical(usr, href_list["chemical"], text2num(href_list["amount"]))
- if(href_list["change_stasis"])
- var/new_stasis = input("Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level") as null|anything in S.stasis_choices
- if(new_stasis && CanUseTopic(usr, default_state) == STATUS_INTERACTIVE)
- S.stasis_level = S.stasis_choices[new_stasis]
-
- return 1
+/obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
+ if(sleeper)
+ return sleeper.tgui_act(action, params, ui, state)
+ return FALSE
/obj/machinery/sleeper
name = "sleeper"
@@ -169,12 +93,19 @@
var/mob/living/carbon/human/occupant = null
var/list/available_chemicals = list()
var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin")
+ var/amounts = list(5, 10)
var/obj/item/weapon/reagent_containers/glass/beaker = null
var/filtering = 0
var/pumping = 0
+ // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper.
+ var/max_chem = 20
+ var/initial_bin_rating = 1
+ var/min_health = -101
var/obj/machinery/sleep_console/console
var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1)
var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0)
+ var/controls_inside = FALSE
+ var/auto_eject_dead = FALSE
use_power = USE_POWER_IDLE
idle_power_usage = 15
@@ -184,6 +115,7 @@
. = ..()
beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
default_apply_parts()
+ update_icon()
/obj/machinery/sleeper/Destroy()
if(console)
@@ -232,14 +164,187 @@
available_chemicals += new_chemicals
return
-/obj/machinery/sleeper/Initialize()
- . = ..()
- update_icon()
+/obj/machinery/sleeper/attack_hand(var/mob/user)
+ if(!controls_inside)
+ return FALSE
+
+ if(user == occupant)
+ tgui_interact(user)
+
+/obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Sleeper", "Sleeper")
+ ui.open()
+
+/obj/machinery/sleeper/tgui_data(mob/user)
+ var/data[0]
+ data["amounts"] = amounts
+ data["hasOccupant"] = occupant ? 1 : 0
+ var/occupantData[0]
+ // var/crisis = 0
+ if(occupant)
+ occupantData["name"] = occupant.name
+ occupantData["stat"] = occupant.stat
+ occupantData["health"] = occupant.health
+ occupantData["maxHealth"] = occupant.maxHealth
+ occupantData["minHealth"] = config.health_threshold_dead
+ occupantData["bruteLoss"] = occupant.getBruteLoss()
+ occupantData["oxyLoss"] = occupant.getOxyLoss()
+ occupantData["toxLoss"] = occupant.getToxLoss()
+ occupantData["fireLoss"] = occupant.getFireLoss()
+ occupantData["paralysis"] = occupant.paralysis
+ occupantData["hasBlood"] = 0
+ occupantData["bodyTemperature"] = occupant.bodytemperature
+ occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations
+ // Because we can put simple_animals in here, we need to do something tricky to get things working nice
+ occupantData["temperatureSuitability"] = 0 // 0 is the baseline
+ if(ishuman(occupant) && occupant.species)
+ // I wanna do something where the bar gets bluer as the temperature gets lower
+ // For now, I'll just use the standard format for the temperature status
+ var/datum/species/sp = occupant.species
+ if(occupant.bodytemperature < sp.cold_level_3)
+ occupantData["temperatureSuitability"] = -3
+ else if(occupant.bodytemperature < sp.cold_level_2)
+ occupantData["temperatureSuitability"] = -2
+ else if(occupant.bodytemperature < sp.cold_level_1)
+ occupantData["temperatureSuitability"] = -1
+ else if(occupant.bodytemperature > sp.heat_level_3)
+ occupantData["temperatureSuitability"] = 3
+ else if(occupant.bodytemperature > sp.heat_level_2)
+ occupantData["temperatureSuitability"] = 2
+ else if(occupant.bodytemperature > sp.heat_level_1)
+ occupantData["temperatureSuitability"] = 1
+ else if(isanimal(occupant))
+ var/mob/living/simple_mob/silly = occupant
+ if(silly.bodytemperature < silly.minbodytemp)
+ occupantData["temperatureSuitability"] = -3
+ else if(silly.bodytemperature > silly.maxbodytemp)
+ occupantData["temperatureSuitability"] = 3
+ // Blast you, imperial measurement system
+ occupantData["btCelsius"] = occupant.bodytemperature - T0C
+ occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32
+
+
+ // crisis = (occupant.health < min_health)
+ // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent
+ // Runtime is aptly named, isn't she?
+ if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel)
+ occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
+ occupantData["hasBlood"] = 1
+ var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
+ occupantData["bloodLevel"] = blood_volume
+ occupantData["bloodMax"] = occupant.species.blood_volume
+ occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here
+
+ occupantData["bloodType"] = occupant.dna.b_type
+
+ data["occupant"] = occupantData
+ data["maxchem"] = max_chem
+ data["minhealth"] = min_health
+ data["dialysis"] = filtering
+ data["stomachpumping"] = pumping
+ data["auto_eject_dead"] = auto_eject_dead
+ if(beaker)
+ data["isBeakerLoaded"] = 1
+ if(beaker.reagents)
+ data["beakerMaxSpace"] = beaker.reagents.maximum_volume
+ data["beakerFreeSpace"] = beaker.reagents.get_free_space()
+ else
+ data["beakerMaxSpace"] = 0
+ data["beakerFreeSpace"] = 0
+ else
+ data["isBeakerLoaded"] = FALSE
+
+
+ var/stasis_level_name = "Error!"
+ for(var/N in stasis_choices)
+ if(stasis_choices[N] == stasis_level)
+ stasis_level_name = N
+ break
+ data["stasis"] = stasis_level_name
+
+ var/chemicals[0]
+ for(var/re in available_chemicals)
+ var/datum/reagent/temp = SSchemistry.chemical_reagents[re]
+ if(temp)
+ var/reagent_amount = 0
+ var/pretty_amount
+ var/injectable = occupant ? 1 : 0
+ var/overdosing = 0
+ var/caution = 0 // To make things clear that you're coming close to an overdose
+ // if(crisis && !(temp.id in emergency_chems))
+ // injectable = 0
+
+ if(occupant && occupant.reagents)
+ reagent_amount = occupant.reagents.get_reagent_amount(temp.id)
+ // If they're mashing the highest concentration, they get one warning
+ if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold))
+ caution = 1
+ if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold))
+ overdosing = 1
+
+ pretty_amount = round(reagent_amount, 0.05)
+
+ chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution)))
+ data["chemicals"] = chemicals
+ return data
+
+
+/obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+ if(!controls_inside && usr == occupant)
+ return
+ if(panel_open)
+ to_chat(usr, "Close the maintenance panel first.")
+ return
+
+ . = TRUE
+ switch(action)
+ if("chemical")
+ if(!occupant)
+ return
+ if(occupant.stat == DEAD)
+ var/datum/gender/G = gender_datums[occupant.get_visible_gender()]
+ to_chat(usr, "This person has no life to preserve anymore. Take [G.him] to a department capable of reanimating [G.him].")
+ return
+ var/chemical = params["chemid"]
+ var/amount = text2num(params["amount"])
+ if(!length(chemical) || amount <= 0)
+ return
+ if(occupant.health > min_health) //|| (chemical in emergency_chems))
+ inject_chemical(usr, chemical, amount)
+ else
+ to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!")
+ if("removebeaker")
+ remove_beaker()
+ if("togglefilter")
+ toggle_filter()
+ if("togglepump")
+ toggle_pump()
+ if("ejectify")
+ go_out()
+ if("changestasis")
+ var/new_stasis = input("Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level") as null|anything in stasis_choices
+ if(new_stasis)
+ stasis_level = stasis_choices[new_stasis]
+ if("auto_eject_dead_on")
+ auto_eject_dead = TRUE
+ if("auto_eject_dead_off")
+ auto_eject_dead = FALSE
+ else
+ return FALSE
+ add_fingerprint(usr)
/obj/machinery/sleeper/process()
if(stat & (NOPOWER|BROKEN))
return
if(occupant)
+ if(auto_eject_dead && occupant.stat == DEAD)
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 40)
+ go_out()
+ return
occupant.Stasis(stasis_level)
if(filtering > 0)
@@ -404,9 +509,11 @@
/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount)
if(stat & (BROKEN|NOPOWER))
return
+ if(!(amount in amounts))
+ return
if(occupant && occupant.reagents)
- if(occupant.reagents.get_reagent_amount(chemical) + amount <= 20)
+ if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem)
use_power(amount * CHEM_SYNTH_ENERGY)
occupant.reagents.add_reagent(chemical, amount)
to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.")
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 758ac976a0..c405ddf1f6 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -1,7 +1,7 @@
// Pretty much everything here is stolen from the dna scanner FYI
/obj/machinery/bodyscanner
- var/mob/living/carbon/occupant
+ var/mob/living/carbon/human/occupant
var/locked
name = "Body Scanner"
icon = 'icons/obj/Cryogenic2.dmi'
@@ -14,6 +14,8 @@
active_power_usage = 10000 //10 kW. It's a big all-body scanner.
light_color = "#00FF00"
var/obj/machinery/body_scanconsole/console
+ var/known_implants = list(/obj/item/weapon/implant/health, /obj/item/weapon/implant/chem, /obj/item/weapon/implant/death_alarm, /obj/item/weapon/implant/loyalty, /obj/item/weapon/implant/tracking, /obj/item/weapon/implant/language, /obj/item/weapon/implant/language/eal)
+ var/printing_text = null
/obj/machinery/bodyscanner/Initialize()
. = ..()
@@ -58,13 +60,14 @@
playsound(src, 'sound/machines/medbayscanner1.ogg', 50) // Beepboop you're being scanned. <3
add_fingerprint(user)
qdel(G)
+ SStgui.update_uis(src)
if(!occupant)
if(default_deconstruction_screwdriver(user, G))
return
if(default_deconstruction_crowbar(user, G))
return
-/obj/machinery/bodyscanner/MouseDrop_T(mob/living/carbon/O, mob/user as mob)
+/obj/machinery/bodyscanner/MouseDrop_T(mob/living/carbon/human/O, mob/user as mob)
if(!istype(O))
return 0 //not a mob
if(user.incapacitated())
@@ -101,6 +104,7 @@
icon_state = "body_scanner_1"
playsound(src, 'sound/machines/medbayscanner1.ogg', 50) // Beepboop you're being scanned. <3
add_fingerprint(user)
+ SStgui.update_uis(src)
/obj/machinery/bodyscanner/relaymove(mob/user as mob)
if(user.incapacitated())
@@ -125,7 +129,8 @@
occupant.client.perspective = MOB_PERSPECTIVE
occupant.loc = src.loc
occupant = null
- icon_state = "body_scanner_0"
+ icon_state = "body_scanner_1"
+ SStgui.update_uis(src)
return
/obj/machinery/bodyscanner/ex_act(severity)
@@ -159,10 +164,362 @@
else
return
+/obj/machinery/bodyscanner/tgui_host(mob/user)
+ if(user == occupant)
+ return src
+ return console ? console : src
+
+/obj/machinery/bodyscanner/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "BodyScanner", "Body Scanner")
+ ui.open()
+
+/obj/machinery/bodyscanner/tgui_data(mob/user)
+ var/list/data = list()
+
+ data["occupied"] = occupant ? TRUE : FALSE
+
+ var/occupantData[0]
+ if(occupant && ishuman(occupant))
+ icon_state = "body_scanner_1"
+ var/mob/living/carbon/human/H = occupant
+ occupantData["name"] = H.name
+ occupantData["stat"] = H.stat
+ occupantData["health"] = H.health
+ occupantData["maxHealth"] = H.getMaxHealth()
+
+ occupantData["hasVirus"] = H.virus2.len
+
+ occupantData["bruteLoss"] = H.getBruteLoss()
+ occupantData["oxyLoss"] = H.getOxyLoss()
+ occupantData["toxLoss"] = H.getToxLoss()
+ occupantData["fireLoss"] = H.getFireLoss()
+
+ occupantData["radLoss"] = H.radiation
+ occupantData["cloneLoss"] = H.getCloneLoss()
+ occupantData["brainLoss"] = H.getBrainLoss()
+ occupantData["paralysis"] = H.paralysis
+ occupantData["paralysisSeconds"] = round(H.paralysis / 4)
+ occupantData["bodyTempC"] = H.bodytemperature-T0C
+ occupantData["bodyTempF"] = (((H.bodytemperature-T0C) * 1.8) + 32)
+
+ occupantData["hasBorer"] = H.has_brain_worms()
+
+ var/bloodData[0]
+ if(H.vessel)
+ var/blood_volume = round(H.vessel.get_reagent_amount("blood"))
+ var/blood_max = H.species.blood_volume
+ bloodData["volume"] = blood_volume
+ bloodData["percent"] = round(((blood_volume / blood_max)*100))
+
+ occupantData["blood"] = bloodData
+
+ var/reagentData[0]
+ if(H.reagents.reagent_list.len >= 1)
+ for(var/datum/reagent/R in H.reagents.reagent_list)
+ reagentData[++reagentData.len] = list(
+ "name" = R.name,
+ "amount" = R.volume,
+ "overdose" = (R.overdose && R.volume > R.overdose) ? TRUE : FALSE,
+ )
+ else
+ reagentData = null
+
+ occupantData["reagents"] = reagentData
+
+ var/ingestedData[0]
+ if(H.ingested.reagent_list.len >= 1)
+ for(var/datum/reagent/R in H.ingested.reagent_list)
+ ingestedData[++ingestedData.len] = list(
+ "name" = R.name,
+ "amount" = R.volume,
+ "overdose" = (R.overdose && R.volume > R.overdose) ? TRUE : FALSE,
+ )
+ else
+ ingestedData = null
+
+ occupantData["ingested"] = ingestedData
+
+ var/extOrganData[0]
+ for(var/obj/item/organ/external/E in H.organs)
+ var/organData[0]
+ organData["name"] = E.name
+ organData["open"] = E.open
+ organData["germ_level"] = E.germ_level
+ organData["bruteLoss"] = E.brute_dam
+ organData["fireLoss"] = E.burn_dam
+ organData["totalLoss"] = E.brute_dam + E.burn_dam
+ organData["maxHealth"] = E.max_damage
+ organData["bruised"] = E.min_bruised_damage
+ organData["broken"] = E.min_broken_damage
+
+ var/implantData[0]
+ for(var/obj/I in E.implants)
+ var/implantSubData[0]
+ implantSubData["name"] = I.name
+ if(is_type_in_list(I, known_implants))
+ implantSubData["known"] = 1
+
+ implantData.Add(list(implantSubData))
+
+ organData["implants"] = implantData
+ organData["implants_len"] = implantData.len
+
+ var/organStatus[0]
+ if(E.status & ORGAN_DESTROYED)
+ organStatus["destroyed"] = 1
+ if(E.status & ORGAN_BROKEN)
+ organStatus["broken"] = E.broken_description
+ if(E.robotic >= ORGAN_ROBOT)
+ organStatus["robotic"] = 1
+ if(E.splinted)
+ organStatus["splinted"] = 1
+ if(E.status & ORGAN_BLEEDING)
+ organStatus["bleeding"] = 1
+ if(E.status & ORGAN_DEAD)
+ organStatus["dead"] = 1
+
+ organData["status"] = organStatus
+
+ if(istype(E, /obj/item/organ/external/chest) && H.is_lung_ruptured())
+ organData["lungRuptured"] = 1
+
+ for(var/datum/wound/W in E.wounds)
+ if(W.internal)
+ organData["internalBleeding"] = 1
+ break
+
+ extOrganData.Add(list(organData))
+
+ occupantData["extOrgan"] = extOrganData
+
+ var/intOrganData[0]
+ for(var/obj/item/organ/I in H.internal_organs)
+ var/organData[0]
+ organData["name"] = I.name
+ if(I.status & ORGAN_ASSISTED)
+ organData["desc"] = "Assisted"
+ else if(I.robotic >= ORGAN_ROBOT)
+ organData["desc"] = "Mechanical"
+ else
+ organData["desc"] = null
+ organData["germ_level"] = I.germ_level
+ organData["damage"] = I.damage
+ organData["maxHealth"] = I.max_damage
+ organData["bruised"] = I.min_bruised_damage
+ organData["broken"] = I.min_broken_damage
+ organData["robotic"] = (I.robotic >= ORGAN_ROBOT)
+ organData["dead"] = (I.status & ORGAN_DEAD)
+
+ intOrganData.Add(list(organData))
+
+ occupantData["intOrgan"] = intOrganData
+
+ occupantData["blind"] = (H.sdisabilities & BLIND)
+ occupantData["nearsighted"] = (H.disabilities & NEARSIGHTED)
+ data["occupant"] = occupantData
+
+ return data
+
+/obj/machinery/bodyscanner/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ . = TRUE
+ switch(action)
+ if("ejectify")
+ eject()
+ if("print_p")
+ var/atom/target = console ? console : src
+ visible_message("[target] rattles and prints out a sheet of paper.")
+ playsound(src, 'sound/machines/printer.ogg', 50, 1)
+ var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(target))
+ var/name = occupant ? occupant.name : "Unknown"
+ P.info = "
Body Scan - [name]
"
+ P.info += "Time of scan: [worldtime2stationtime(world.time)]
"
+ P.info += "[generate_printing_text()]"
+ P.info += "
Notes:
"
+ P.name = "Body Scan - [name] ([worldtime2stationtime(world.time)]"
+ else
+ return FALSE
+
+/obj/machinery/bodyscanner/proc/generate_printing_text()
+ var/dat = ""
+
+ dat = "Occupant Statistics:
" //Blah obvious
+ if(istype(occupant)) //is there REALLY someone in there?
+ var/t1
+ switch(occupant.stat) // obvious, see what their status is
+ if(0)
+ t1 = "Conscious"
+ if(1)
+ t1 = "Unconscious"
+ else
+ t1 = "*dead*"
+ dat += " (occupant.getMaxHealth() / 2) ? "blue" : "red"]>\tHealth %: [(occupant.health / occupant.getMaxHealth())*100], ([t1])
"
+
+ if(occupant.virus2.len)
+ dat += "Viral pathogen detected in blood stream.
"
+
+ var/extra_font = null
+ extra_font = ""
+ dat += "[extra_font]\t-Brute Damage %: [occupant.getBruteLoss()]
"
+
+ extra_font = ""
+ dat += "[extra_font]\t-Respiratory Damage %: [occupant.getOxyLoss()]
"
+
+ extra_font = ""
+ dat += "[extra_font]\t-Toxin Content %: [occupant.getToxLoss()]
"
+
+ extra_font = ""
+ dat += "[extra_font]\t-Burn Severity %: [occupant.getFireLoss()]
"
+
+ extra_font = ""
+ dat += "[extra_font]\tRadiation Level %: [occupant.radiation]
"
+
+ extra_font = ""
+ dat += "[extra_font]\tGenetic Tissue Damage %: [occupant.getCloneLoss()]
"
+
+ extra_font = ""
+ dat += "[extra_font]\tApprox. Brain Damage %: [occupant.getBrainLoss()]
"
+
+ dat += "Paralysis Summary %: [occupant.paralysis] ([round(occupant.paralysis / 4)] seconds left!)
"
+ dat += "Body Temperature: [occupant.bodytemperature-T0C]°C ([occupant.bodytemperature*1.8-459.67]°F)
"
+
+ dat += "
"
+
+ if(occupant.has_brain_worms())
+ dat += "Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
"
+
+ if(occupant.vessel)
+ var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
+ var/blood_max = occupant.species.blood_volume
+ var/blood_percent = blood_volume / blood_max
+ blood_percent *= 100
+
+ extra_font = " 448 ? "blue" : "red"]>"
+ dat += "[extra_font]\tBlood Level %: [blood_percent] ([blood_volume] units)
"
+
+ if(occupant.reagents)
+ for(var/datum/reagent/R in occupant.reagents.reagent_list)
+ dat += "Reagent: [R.name], Amount: [R.volume]
"
+
+ if(occupant.ingested)
+ for(var/datum/reagent/R in occupant.ingested.reagent_list)
+ dat += "Stomach: [R.name], Amount: [R.volume]
"
+
+ dat += "
"
+ dat += ""
+ dat += "| Organ | "
+ dat += "Burn Damage | "
+ dat += "Brute Damage | "
+ dat += "Other Wounds | "
+ dat += "
"
+
+ for(var/obj/item/organ/external/e in occupant.organs)
+ dat += ""
+ var/AN = ""
+ var/open = ""
+ var/infected = ""
+ var/robot = ""
+ var/imp = ""
+ var/bled = ""
+ var/splint = ""
+ var/internal_bleeding = ""
+ var/lung_ruptured = ""
+ var/o_dead = ""
+ for(var/datum/wound/W in e.wounds) if(W.internal)
+ internal_bleeding = "
Internal bleeding"
+ break
+ if(istype(e, /obj/item/organ/external/chest) && occupant.is_lung_ruptured())
+ lung_ruptured = "Lung ruptured:"
+ if(e.splinted)
+ splint = "Splinted:"
+ if(e.status & ORGAN_BLEEDING)
+ bled = "Bleeding:"
+ if(e.status & ORGAN_BROKEN)
+ AN = "[e.broken_description]:"
+ if(e.robotic >= ORGAN_ROBOT)
+ robot = "Prosthetic:"
+ if(e.status & ORGAN_DEAD)
+ o_dead = "Necrotic:"
+ if(e.open)
+ open = "Open:"
+ switch (e.germ_level)
+ if (INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200)
+ infected = "Mild Infection:"
+ if (INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300)
+ infected = "Mild Infection+:"
+ if (INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400)
+ infected = "Mild Infection++:"
+ if (INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200)
+ infected = "Acute Infection:"
+ if (INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300)
+ infected = "Acute Infection+:"
+ if (INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_THREE - 50)
+ infected = "Acute Infection++:"
+ if (INFECTION_LEVEL_THREE -49 to INFINITY)
+ infected = "Gangrene Detected:"
+
+ var/unknown_body = 0
+ for(var/I in e.implants)
+ if(is_type_in_list(I,known_implants))
+ imp += "[I] implanted:"
+ else
+ unknown_body++
+
+ if(unknown_body)
+ imp += "Unknown body present:"
+ if(!AN && !open && !infected & !imp)
+ AN = "None:"
+ if(!(e.status & ORGAN_DESTROYED))
+ dat += "[e.name] | [e.burn_dam] | [e.brute_dam] | [robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][o_dead] | "
+ else
+ dat += "[e.name] | - | - | Not Found | "
+ dat += "
"
+ for(var/obj/item/organ/i in occupant.internal_organs)
+ var/mech = ""
+ var/i_dead = ""
+ if(i.status & ORGAN_ASSISTED)
+ mech = "Assisted:"
+ if(i.robotic >= ORGAN_ROBOT)
+ mech = "Mechanical:"
+ if(i.status & ORGAN_DEAD)
+ i_dead = "Necrotic:"
+ var/infection = "None"
+ switch (i.germ_level)
+ if (INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200)
+ infection = "Mild Infection:"
+ if (INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300)
+ infection = "Mild Infection+:"
+ if (INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400)
+ infection = "Mild Infection++:"
+ if (INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200)
+ infection = "Acute Infection:"
+ if (INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300)
+ infection = "Acute Infection+:"
+ if (INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_THREE - 50)
+ infection = "Acute Infection++:"
+ if (INFECTION_LEVEL_THREE -49 to INFINITY)
+ infection = "Necrosis Detected:"
+
+ dat += ""
+ dat += "| [i.name] | N/A | [i.damage] | [infection]:[mech][i_dead] | | "
+ dat += "
"
+ dat += "
"
+ if(occupant.sdisabilities & BLIND)
+ dat += "Cataracts detected.
"
+ if(occupant.disabilities & NEARSIGHTED)
+ dat += "Retinal misalignment detected.
"
+ else
+ dat += "\The [src] is empty."
+
+ return dat
+
//Body Scan Console
/obj/machinery/body_scanconsole
var/obj/machinery/bodyscanner/scanner
- var/known_implants = list(/obj/item/weapon/implant/health, /obj/item/weapon/implant/chem, /obj/item/weapon/implant/death_alarm, /obj/item/weapon/implant/loyalty, /obj/item/weapon/implant/tracking, /obj/item/weapon/implant/language, /obj/item/weapon/implant/language/eal)
var/delete
var/temphtml
name = "Body Scanner Console"
@@ -173,7 +530,6 @@
anchored = 1
circuit = /obj/item/weapon/circuitboard/scanner_console
var/printing = null
- var/printing_text = null
/obj/machinery/body_scanconsole/New()
..()
@@ -202,6 +558,7 @@
else
return attack_hand(user)
+
/obj/machinery/body_scanconsole/power_change()
if(stat & BROKEN)
icon_state = "body_scannerconsole-p"
@@ -256,343 +613,9 @@
to_chat(user, "Scanner not found!")
return
- if (scanner.panel_open)
+ if(scanner.panel_open)
to_chat(user, "Close the maintenance panel first.")
return
if(scanner)
- return ui_interact(user)
-
-/obj/machinery/body_scanconsole/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- var/data[0]
-
- data["connected"] = scanner ? 1 : 0
-
- if(scanner)
- data["occupied"] = scanner.occupant ? 1 : 0
-
- var/occupantData[0]
- if(scanner.occupant && ishuman(scanner.occupant))
- var/mob/living/carbon/human/H = scanner.occupant
- occupantData["name"] = H.name
- occupantData["stat"] = H.stat
- occupantData["health"] = H.health
- occupantData["maxHealth"] = H.getMaxHealth()
-
- occupantData["hasVirus"] = H.virus2.len
-
- occupantData["bruteLoss"] = H.getBruteLoss()
- occupantData["oxyLoss"] = H.getOxyLoss()
- occupantData["toxLoss"] = H.getToxLoss()
- occupantData["fireLoss"] = H.getFireLoss()
-
- occupantData["radLoss"] = H.radiation
- occupantData["cloneLoss"] = H.getCloneLoss()
- occupantData["brainLoss"] = H.getBrainLoss()
- occupantData["paralysis"] = H.paralysis
- occupantData["paralysisSeconds"] = round(H.paralysis / 4)
- occupantData["bodyTempC"] = H.bodytemperature-T0C
- occupantData["bodyTempF"] = (((H.bodytemperature-T0C) * 1.8) + 32)
-
- occupantData["hasBorer"] = H.has_brain_worms()
-
- var/bloodData[0]
- if(H.vessel)
- var/blood_volume = round(H.vessel.get_reagent_amount("blood"))
- var/blood_max = H.species.blood_volume
- bloodData["volume"] = blood_volume
- bloodData["percent"] = round(((blood_volume / blood_max)*100))
-
- occupantData["blood"] = bloodData
-
- var/reagentData[0]
- if(H.reagents.reagent_list.len >= 1)
- for(var/datum/reagent/R in H.reagents.reagent_list)
- reagentData[++reagentData.len] = list("name" = R.name, "amount" = R.volume, "overdose" = ((R.overdose && R.volume > R.overdose) ? "Overdose" : ""))
- else
- reagentData = null
-
- occupantData["reagents"] = reagentData
-
- var/ingestedData[0]
- if(H.ingested.reagent_list.len >= 1)
- for(var/datum/reagent/R in H.ingested.reagent_list)
- ingestedData[++ingestedData.len] = list("name" = R.name, "amount" = R.volume, "overdose" = ((R.overdose && R.volume > R.overdose) ? "Overdose" : ""))
- else
- ingestedData = null
-
- occupantData["ingested"] = ingestedData
-
- var/extOrganData[0]
- for(var/obj/item/organ/external/E in H.organs)
- var/organData[0]
- organData["name"] = E.name
- organData["open"] = E.open
- organData["germ_level"] = E.germ_level
- organData["bruteLoss"] = E.brute_dam
- organData["fireLoss"] = E.burn_dam
-
- var/implantData[0]
- for(var/obj/I in E.implants)
- var/implantSubData[0]
- implantSubData["name"] = I.name
- if(is_type_in_list(I, known_implants))
- implantSubData["known"] = 1
-
- implantData.Add(list(implantSubData))
-
- organData["implants"] = implantData
- organData["implants_len"] = implantData.len
-
- var/organStatus[0]
- if(E.status & ORGAN_DESTROYED)
- organStatus["destroyed"] = 1
- if(E.status & ORGAN_BROKEN)
- organStatus["broken"] = E.broken_description
- if(E.robotic >= ORGAN_ROBOT)
- organStatus["robotic"] = 1
- if(E.splinted)
- organStatus["splinted"] = 1
- if(E.status & ORGAN_BLEEDING)
- organStatus["bleeding"] = 1
- if(E.status & ORGAN_DEAD)
- organStatus["dead"] = 1
- for(var/datum/wound/W in E.wounds)
- if(W.internal)
- organStatus["internalBleeding"] = 1
- break
-
- organData["status"] = organStatus
-
- if(istype(E, /obj/item/organ/external/chest) && H.is_lung_ruptured())
- organData["lungRuptured"] = 1
-
- extOrganData.Add(list(organData))
-
- occupantData["extOrgan"] = extOrganData
-
- var/intOrganData[0]
- for(var/obj/item/organ/I in H.internal_organs)
- var/organData[0]
- organData["name"] = I.name
- if(I.status & ORGAN_ASSISTED)
- organData["desc"] = "Assisted"
- else if(I.robotic >= ORGAN_ROBOT)
- organData["desc"] = "Mechanical"
- else
- organData["desc"] = null
- organData["germ_level"] = I.germ_level
- organData["damage"] = I.damage
-
- intOrganData.Add(list(organData))
-
- occupantData["intOrgan"] = intOrganData
-
- occupantData["blind"] = (H.sdisabilities & BLIND)
- occupantData["nearsighted"] = (H.disabilities & NEARSIGHTED)
-
- data["occupant"] = occupantData
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "adv_med.tmpl", "Body Scanner", 690, 800)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(1)
-
-
-/obj/machinery/body_scanconsole/Topic(href, href_list)
- if(..())
- return 1
-
- if (href_list["print_p"])
- generate_printing_text()
-
- if (!(printing) && printing_text)
- printing = 1
- visible_message("\The [src] rattles and prints out a sheet of paper.")
- var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
- P.info = "Body Scan - [href_list["name"]]
"
- P.info += "Time of scan: [worldtime2stationtime(world.time)]
"
- P.info += "[printing_text]"
- P.info += "
Notes:
"
- P.name = "Body Scan - [href_list["name"]] ([worldtime2stationtime(world.time)])"
- printing = null
- printing_text = null
-
-/obj/machinery/body_scanconsole/proc/generate_printing_text()
- var/dat = ""
-
- if(scanner)
- var/mob/living/carbon/human/occupant = scanner.occupant
- dat = "Occupant Statistics:
" //Blah obvious
- if(istype(occupant)) //is there REALLY someone in there?
- var/t1
- switch(occupant.stat) // obvious, see what their status is
- if(0)
- t1 = "Conscious"
- if(1)
- t1 = "Unconscious"
- else
- t1 = "*dead*"
- dat += " (occupant.getMaxHealth() / 2) ? "blue" : "red"]>\tHealth %: [(occupant.health / occupant.getMaxHealth())*100], ([t1])
"
-
- if(occupant.virus2.len)
- dat += "Viral pathogen detected in blood stream.
"
-
- var/extra_font = null
- extra_font = ""
- dat += "[extra_font]\t-Brute Damage %: [occupant.getBruteLoss()]
"
-
- extra_font = ""
- dat += "[extra_font]\t-Respiratory Damage %: [occupant.getOxyLoss()]
"
-
- extra_font = ""
- dat += "[extra_font]\t-Toxin Content %: [occupant.getToxLoss()]
"
-
- extra_font = ""
- dat += "[extra_font]\t-Burn Severity %: [occupant.getFireLoss()]
"
-
- extra_font = ""
- dat += "[extra_font]\tRadiation Level %: [occupant.radiation]
"
-
- extra_font = ""
- dat += "[extra_font]\tGenetic Tissue Damage %: [occupant.getCloneLoss()]
"
-
- extra_font = ""
- dat += "[extra_font]\tApprox. Brain Damage %: [occupant.getBrainLoss()]
"
-
- dat += "Paralysis Summary %: [occupant.paralysis] ([round(occupant.paralysis / 4)] seconds left!)
"
- dat += "Body Temperature: [occupant.bodytemperature-T0C]°C ([occupant.bodytemperature*1.8-459.67]°F)
"
-
- dat += "
"
-
- if(occupant.has_brain_worms())
- dat += "Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
"
-
- if(occupant.vessel)
- var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
- var/blood_max = occupant.species.blood_volume
- var/blood_percent = blood_volume / blood_max
- blood_percent *= 100
-
- extra_font = " 448 ? "blue" : "red"]>"
- dat += "[extra_font]\tBlood Level %: [blood_percent] ([blood_volume] units)
"
-
- if(occupant.reagents)
- for(var/datum/reagent/R in occupant.reagents.reagent_list)
- dat += "Reagent: [R.name], Amount: [R.volume]
"
-
- if(occupant.ingested)
- for(var/datum/reagent/R in occupant.ingested.reagent_list)
- dat += "Stomach: [R.name], Amount: [R.volume]
"
-
- dat += "
"
- dat += ""
- dat += "| Organ | "
- dat += "Burn Damage | "
- dat += "Brute Damage | "
- dat += "Other Wounds | "
- dat += "
"
-
- for(var/obj/item/organ/external/e in occupant.organs)
- dat += ""
- var/AN = ""
- var/open = ""
- var/infected = ""
- var/robot = ""
- var/imp = ""
- var/bled = ""
- var/splint = ""
- var/internal_bleeding = ""
- var/lung_ruptured = ""
- var/o_dead = ""
- for(var/datum/wound/W in e.wounds) if(W.internal)
- internal_bleeding = "
Internal bleeding"
- break
- if(istype(e, /obj/item/organ/external/chest) && occupant.is_lung_ruptured())
- lung_ruptured = "Lung ruptured:"
- if(e.splinted)
- splint = "Splinted:"
- if(e.status & ORGAN_BLEEDING)
- bled = "Bleeding:"
- if(e.status & ORGAN_BROKEN)
- AN = "[e.broken_description]:"
- if(e.robotic >= ORGAN_ROBOT)
- robot = "Prosthetic:"
- if(e.status & ORGAN_DEAD)
- o_dead = "Necrotic:"
- if(e.open)
- open = "Open:"
- switch (e.germ_level)
- if (INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200)
- infected = "Mild Infection:"
- if (INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300)
- infected = "Mild Infection+:"
- if (INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400)
- infected = "Mild Infection++:"
- if (INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200)
- infected = "Acute Infection:"
- if (INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300)
- infected = "Acute Infection+:"
- if (INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_THREE - 50)
- infected = "Acute Infection++:"
- if (INFECTION_LEVEL_THREE -49 to INFINITY)
- infected = "Gangrene Detected:"
-
- var/unknown_body = 0
- for(var/I in e.implants)
- if(is_type_in_list(I,known_implants))
- imp += "[I] implanted:"
- else
- unknown_body++
-
- if(unknown_body)
- imp += "Unknown body present:"
- if(!AN && !open && !infected & !imp)
- AN = "None:"
- if(!(e.status & ORGAN_DESTROYED))
- dat += "[e.name] | [e.burn_dam] | [e.brute_dam] | [robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][o_dead] | "
- else
- dat += "[e.name] | - | - | Not Found | "
- dat += "
"
- for(var/obj/item/organ/i in occupant.internal_organs)
- var/mech = ""
- var/i_dead = ""
- if(i.status & ORGAN_ASSISTED)
- mech = "Assisted:"
- if(i.robotic >= ORGAN_ROBOT)
- mech = "Mechanical:"
- if(i.status & ORGAN_DEAD)
- i_dead = "Necrotic:"
- var/infection = "None"
- switch (i.germ_level)
- if (INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200)
- infection = "Mild Infection:"
- if (INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300)
- infection = "Mild Infection+:"
- if (INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400)
- infection = "Mild Infection++:"
- if (INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200)
- infection = "Acute Infection:"
- if (INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300)
- infection = "Acute Infection+:"
- if (INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_THREE - 50)
- infection = "Acute Infection++:"
- if (INFECTION_LEVEL_THREE -49 to INFINITY)
- infection = "Necrosis Detected:"
-
- dat += ""
- dat += "| [i.name] | N/A | [i.damage] | [infection]:[mech][i_dead] | | "
- dat += "
"
- dat += "
"
- if(occupant.sdisabilities & BLIND)
- dat += "Cataracts detected.
"
- if(occupant.disabilities & NEARSIGHTED)
- dat += "Retinal misalignment detected.
"
- else
- dat += "\The [src] is empty."
- else
- dat = " Error: No Body Scanner connected."
-
- printing_text = dat
+ return scanner.tgui_interact(user)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index bbc1cdeebc..39b5748762 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -24,6 +24,7 @@
return selected
#define CLONE_BIOMASS 60
+#define MINIMUM_HEAL_LEVEL 40
/obj/machinery/clonepod
name = "cloning pod"
@@ -46,6 +47,9 @@
var/list/containers = list() // Beakers for our liquid biomass
var/container_limit = 3 // How many beakers can the machine hold?
+ var/speed_coeff
+ var/efficiency
+
/obj/machinery/clonepod/Initialize()
. = ..()
default_apply_parts()
@@ -291,13 +295,16 @@
/obj/machinery/clonepod/RefreshParts()
..()
- var/rating = 0
- for(var/obj/item/weapon/stock_parts/P in component_parts)
- if(istype(P, /obj/item/weapon/stock_parts/scanning_module) || istype(P, /obj/item/weapon/stock_parts/manipulator))
- rating += P.rating
+ speed_coeff = 0
+ efficiency = 0
+ for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts)
+ efficiency += S.rating
+ for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts)
+ speed_coeff += P.rating
+ heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL)
- heal_level = rating * 10 - 20
- heal_rate = round(rating / 4)
+/obj/machinery/clonepod/proc/get_completion()
+ . = (100 * ((occupant.health + 100) / (heal_level + 100)))
/obj/machinery/clonepod/verb/eject()
set name = "Eject Cloner"
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 5caf9edd6e..c965a54b15 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -1,4 +1,4 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
+#define OP_COMPUTER_COOLDOWN 60
/obj/machinery/computer/operating
name = "patient monitoring console"
@@ -8,66 +8,304 @@
icon_keyboard = "med_key"
icon_screen = "crew"
circuit = /obj/item/weapon/circuitboard/operating
- var/mob/living/carbon/human/victim = null
var/obj/machinery/optable/table = null
+ var/mob/living/carbon/human/victim = null
+ var/verbose = 1 //general speaker toggle
+ var/patientName = null
+ var/oxyAlarm = 30 //oxy damage at which the computer will beep
+ var/choice = 0 //just for going into and out of the options menu
+ var/healthAnnounce = 1 //healther announcer toggle
+ var/crit = 1 //crit beeping toggle
+ var/nextTick = OP_COMPUTER_COOLDOWN
+ var/healthAlarm = 50
+ var/oxy = 1 //oxygen beeping toggle
/obj/machinery/computer/operating/New()
..()
for(var/direction in list(NORTH,EAST,SOUTH,WEST))
table = locate(/obj/machinery/optable, get_step(src, direction))
- if (table)
+ if(table)
table.computer = src
break
+/obj/machinery/computer/operating/Destroy()
+ if(table)
+ table.computer = null
+ table = null
+ if(victim)
+ victim = null
+ return ..()
+
/obj/machinery/computer/operating/attack_ai(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/computer/operating/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
+ tgui_interact(user)
-/**
- * Display the NanoUI window for the operating computer.
- *
- * See NanoUI documentation for details.
- */
-/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- var/list/data = list()
- var/list/victim_ui = list()
-
- if(table && (table.check_victim()))
- victim = table.victim
-
- victim_ui = list("real_name" = victim.real_name, "age" = victim.age, "b_type" = victim.b_type, "health" = victim.health,
- "brute" = victim.getBruteLoss(), "tox" = src.victim.getToxLoss(), "burn" = victim.getFireLoss(), "oxy" = victim.getOxyLoss(),
- "stat" = (victim.stat ? "Non-Responsive" : "Stable"), "pulse" = victim.get_pulse(GETPULSE_TOOL))
- else
- victim = null
- victim_ui = null
-
- data["table"] = table
- data["victim"] = victim_ui
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "operating.tmpl", src.name, 380, 400)
- ui.set_initial_data(data)
+/obj/machinery/computer/operating/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "OperatingComputer", "Patient Monitor")
ui.open()
- ui.set_auto_update(5)
-/obj/machinery/computer/operating/Topic(href, href_list)
+/obj/machinery/computer/operating/tgui_data(mob/user)
+ var/data[0]
+ var/mob/living/carbon/human/occupant
+ if(table)
+ occupant = table.victim
+ data["hasOccupant"] = occupant ? 1 : 0
+ var/occupantData[0]
+
+ if(occupant)
+ occupantData["name"] = occupant.name
+ occupantData["stat"] = occupant.stat
+ occupantData["health"] = occupant.health
+ occupantData["maxHealth"] = occupant.maxHealth
+ occupantData["minHealth"] = config.health_threshold_dead
+ occupantData["bruteLoss"] = occupant.getBruteLoss()
+ occupantData["oxyLoss"] = occupant.getOxyLoss()
+ occupantData["toxLoss"] = occupant.getToxLoss()
+ occupantData["fireLoss"] = occupant.getFireLoss()
+ occupantData["paralysis"] = occupant.paralysis
+ occupantData["hasBlood"] = 0
+ occupantData["bodyTemperature"] = occupant.bodytemperature
+ occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations
+ // Because we can put simple_animals in here, we need to do something tricky to get things working nice
+ occupantData["temperatureSuitability"] = 0 // 0 is the baseline
+ if(ishuman(occupant) && occupant.species)
+ // I wanna do something where the bar gets bluer as the temperature gets lower
+ // For now, I'll just use the standard format for the temperature status
+ var/datum/species/sp = occupant.species
+ if(occupant.bodytemperature < sp.cold_level_3)
+ occupantData["temperatureSuitability"] = -3
+ else if(occupant.bodytemperature < sp.cold_level_2)
+ occupantData["temperatureSuitability"] = -2
+ else if(occupant.bodytemperature < sp.cold_level_1)
+ occupantData["temperatureSuitability"] = -1
+ else if(occupant.bodytemperature > sp.heat_level_3)
+ occupantData["temperatureSuitability"] = 3
+ else if(occupant.bodytemperature > sp.heat_level_2)
+ occupantData["temperatureSuitability"] = 2
+ else if(occupant.bodytemperature > sp.heat_level_1)
+ occupantData["temperatureSuitability"] = 1
+ else if(isanimal(occupant))
+ var/mob/living/simple_mob/silly = occupant
+ if(silly.bodytemperature < silly.minbodytemp)
+ occupantData["temperatureSuitability"] = -3
+ else if(silly.bodytemperature > silly.maxbodytemp)
+ occupantData["temperatureSuitability"] = 3
+ // Blast you, imperial measurement system
+ occupantData["btCelsius"] = occupant.bodytemperature - T0C
+ occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32
+
+ if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel)
+ occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
+ occupantData["hasBlood"] = 1
+ var/blood_volume = round(occupant.vessel.get_reagent_amount("blood"))
+ occupantData["bloodLevel"] = blood_volume
+ occupantData["bloodMax"] = occupant.species.blood_volume
+ occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here
+
+ occupantData["bloodType"] = occupant.dna.b_type
+ occupantData["surgery"] = build_surgery_list(user)
+
+ data["occupant"] = occupantData
+ data["verbose"]=verbose
+ data["oxyAlarm"]=oxyAlarm
+ data["choice"]=choice
+ data["health"]=healthAnnounce
+ data["crit"]=crit
+ data["healthAlarm"]=healthAlarm
+ data["oxy"]=oxy
+
+ return data
+
+/obj/machinery/computer/operating/tgui_act(action, params)
if(..())
- return 1
- if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
+ return TRUE
+ if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
- src.add_fingerprint(usr)
- SSnanoui.update_uis(src)
\ No newline at end of file
+ . = TRUE
+ switch(action)
+ if("verboseOn")
+ verbose = TRUE
+ if("verboseOff")
+ verbose = FALSE
+ if("healthOn")
+ healthAnnounce = TRUE
+ if("healthOff")
+ healthAnnounce = FALSE
+ if("critOn")
+ crit = TRUE
+ if("critOff")
+ crit = FALSE
+ if("oxyOn")
+ oxy = TRUE
+ if("oxyOff")
+ oxy = FALSE
+ if("oxy_adj")
+ oxyAlarm = clamp(text2num(params["new"]), -100, 100)
+ if("choiceOn")
+ choice = TRUE
+ if("choiceOff")
+ choice = FALSE
+ if("health_adj")
+ healthAlarm = clamp(text2num(params["new"]), -100, 100)
+ else
+ return FALSE
+
+/obj/machinery/computer/operating/process()
+ if(table && table.check_victim())
+ if(verbose)
+ if(patientName!=table.victim.name)
+ patientName=table.victim.name
+ atom_say("New patient detected, loading stats")
+ victim = table.victim
+ atom_say("[victim.real_name], [victim.dna.b_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]")
+ SStgui.update_uis(src)
+ if(nextTick < world.time)
+ nextTick=world.time + OP_COMPUTER_COOLDOWN
+ if(crit && victim.health <= -50 )
+ playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0)
+ if(oxy && victim.getOxyLoss()>oxyAlarm)
+ playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 50, 0)
+ if(healthAnnounce && ((victim.health / victim.maxHealth) * 100) <= healthAlarm)
+ atom_say("[round(((victim.health / victim.maxHealth) * 100))]% health.")
+
+// Surgery Helpers
+/obj/machinery/computer/operating/proc/build_surgery_list(mob/user)
+ if(!istype(victim))
+ return null
+
+ . = list()
+
+ for(var/limb in victim.organs_by_name)
+ var/obj/item/organ/external/E = victim.organs_by_name[limb]
+ if(E && E.open)
+ . += list(list("name" = E.name, "currentStage" = find_stage(E), "nextSteps" = find_next_steps(user, limb)))
+
+/**
+ * This proc is actually hell. I hate the surgery system Polaris uses.
+ * Basically, surgery is completely stateless, and what "stage" we're on is just dependent
+ * on the current state of 5 separate variables that determine what stages we can perform
+ * next.
+ *
+ * So, here's a little guide to understand this proc:
+ * Surgery is broken down into 5 different variables:
+ * `open`,
+ * `stage`,
+ * `cavity`,
+ * `burn_stage`,
+ * and `brute_stage`.
+ * Naturally, the values assigned to these don't use defines or names or anything, they're just magic numbers.
+ * So, we have to figure out ourselves what we should call each value.
+ * Open can be 4 values, and represents the "openness" of the surgery site.
+ * 1 = Cut Open.
+ * 2 = Retracted.
+ * 2.5 = Bones cut.
+ * 3 = Bones spread.
+ * Stage can be 3 values, and represents the progress in fixing broken bones
+ * 0 = Closed, can be either "we're done" or "we haven't started" FFS.
+ * 1 = Bones glued.
+ * 2 = Bones set.
+ * Cavity is just representing the cavity implant surgeries, and can be 2 values.
+ * 0 = Cavity Closed
+ * 1 = Cavity Open
+ * burn_stage and brute_stage are literally only used for repairing brute/burn damage to limbs
+ * I have no idea why you would ever perform these surgeries, given that Bicaradine and Kelotane exist.
+ * So I'm not even going to bother trying to represent them here. Fuck it.
+ */
+/obj/machinery/computer/operating/proc/find_stage(var/obj/item/organ/external/E)
+ . = "None."
+ switch(E.open)
+ if(1)
+ . = "Incision made."
+ if(2)
+ . = "Surgical site opened."
+ switch(E.stage)
+ // if(0) // Nothing.
+ if(1)
+ . = "Surgical site opened; Bones glued."
+ if(2)
+ . = "Surgical site opened; Bones set."
+ switch(E.cavity)
+ if(1)
+ . = "Surgical site opened; Cavity open."
+ if(2.5) // WHY IS THIS A FLOAT. WHY?
+ . = "Bones cut."
+ switch(E.stage)
+ // if(0) // Nothing.
+ if(1)
+ . = "Bones cut; Bones glued."
+ if(2)
+ . = "Bones cut; Bones set."
+ if(3)
+ . = "Bones retracted."
+ switch(E.stage)
+ // if(0) // Nothing.
+ if(1)
+ . = "Bones retracted; Bones glued."
+ if(2)
+ . = "Bones retracted; Bones reset."
+ switch(E.cavity)
+ if(1)
+ . = "Bones retracted; Cavity open."
+
+/**
+ * This converts a typepath into a pretty name.
+ * As best as it can, anyways.
+ */
+/proc/pretty_type(var/datum/A)
+ var/typeStr = "[A.type]"
+ . = copytext(typeStr, findlasttext(typeStr, "/") + 1, length(typeStr) + 1)
+ . = capitalize(replacetext(., "_", " "))
+
+/proc/get_surgery_steps_without_basetypes()
+ var/static/list/good_surgeries = list()
+ if(LAZYLEN(good_surgeries))
+ return good_surgeries
+ var/static/list/banned_surgery_steps = list(
+ /datum/surgery_step,
+ /datum/surgery_step/generic,
+ /datum/surgery_step/open_encased,
+ /datum/surgery_step/repairflesh,
+ /datum/surgery_step/face,
+ /datum/surgery_step/cavity,
+ /datum/surgery_step/limb,
+ /datum/surgery_step/brainstem,
+ )
+ good_surgeries = surgery_steps
+ for(var/datum/surgery_step/S in good_surgeries)
+ if(S.type in banned_surgery_steps)
+ good_surgeries -= S
+ if(!LAZYLEN(S.allowed_tools))
+ good_surgeries -= S
+ return good_surgeries
+
+/**
+ * Funnily enough, this proc is actually considerably less awful than find_stage.
+ * All we have to do is check what surgeries can be done, like surgery mechanics themselves do.
+ * Then, build a string telling the user what they can do next.
+ */
+/obj/machinery/computer/operating/proc/find_next_steps(mob/user, zone)
+ . = list()
+ for(var/datum/surgery_step/S in get_surgery_steps_without_basetypes())
+ if(S.can_use(user, victim, zone, null) && S.is_valid_target(victim))
+ var/allowed_tools_by_name = list()
+ for(var/tool in S.allowed_tools)
+ // Exempt ghetto tools.
+ if(S.allowed_tools[tool] < 100)
+ continue
+ var/obj/tool_path = tool
+ allowed_tools_by_name += capitalize(initial(tool_path.name))
+ // Please for the love of all that is holy, someone make surgery steps
+ // have names so I don't have to do this stupid pretty_type shit.
+ . += "[pretty_type(S)]: [english_list(allowed_tools_by_name)]"
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 30c15f6d04..cc05d63217 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -1,34 +1,64 @@
+#define MENU_MAIN 1
+#define MENU_RECORDS 2
+
/obj/machinery/computer/cloning
- name = "cloning control console"
- desc = "Used to start cloning cycles, as well as manage clone records."
+ name = "cloning console"
+ icon = 'icons/obj/computer.dmi'
icon_keyboard = "med_key"
icon_screen = "dna"
- light_color = "#315ab4"
circuit = /obj/item/weapon/circuitboard/cloning
req_access = list(access_heads) //Only used for record deletion right now.
var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning.
- var/list/pods = list() //Linked cloning pods.
- var/temp = ""
- var/scantemp = "Scanner unoccupied"
- var/menu = 1 //Which menu screen to display
- var/list/records = list()
+ var/list/pods = null //Linked cloning pods.
+ var/list/temp = null
+ var/list/scantemp = null
+ var/menu = MENU_MAIN //Which menu screen to display
+ var/list/records = null
var/datum/dna2/record/active_record = null
var/obj/item/weapon/disk/data/diskette = null //Mostly so the geneticist can steal everything.
var/loading = 0 // Nice loading text
+ var/autoprocess = 0
+ var/obj/machinery/clonepod/selected_pod
+ // 0: Standard body scan
+ // 1: The "Best" scan available
+ var/scan_mode = 1
+ light_color = "#315ab4"
/obj/machinery/computer/cloning/Initialize()
- . = ..()
+ ..()
+ pods = list()
+ records = list()
+ set_scan_temp("Scanner ready.", "good")
updatemodules()
/obj/machinery/computer/cloning/Destroy()
releasecloner()
- ..()
+ return ..()
+
+/obj/machinery/computer/cloning/process()
+ if(!scanner || !pods.len || !autoprocess || stat & NOPOWER)
+ return
+
+ if(scanner.occupant && can_autoprocess())
+ scan_mob(scanner.occupant)
+
+ if(!LAZYLEN(records))
+ return
+
+ for(var/obj/machinery/clonepod/pod in pods)
+ if(!(pod.occupant || pod.mess) && (pod.efficiency > 5))
+ for(var/datum/dna2/record/R in records)
+ if(!(pod.occupant || pod.mess))
+ if(pod.growclone(R))
+ records.Remove(R)
/obj/machinery/computer/cloning/proc/updatemodules()
scanner = findscanner()
releasecloner()
findcloner()
+ if(!selected_pod && pods.len)
+ selected_pod = pods[1]
/obj/machinery/computer/cloning/proc/findscanner()
var/obj/machinery/dna_scannernew/scannerf = null
@@ -36,16 +66,15 @@
//Try to find scanner on adjacent tiles first
for(dir in list(NORTH,EAST,SOUTH,WEST))
scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, dir))
- if (scannerf)
+ if(scannerf)
return scannerf
//Then look for a free one in the area
if(!scannerf)
- var/area/A = get_area(src)
- for(var/obj/machinery/dna_scannernew/S in A.get_contents())
+ for(var/obj/machinery/dna_scannernew/S in get_area(src))
return S
- return
+ return 0
/obj/machinery/computer/cloning/proc/releasecloner()
for(var/obj/machinery/clonepod/P in pods)
@@ -55,21 +84,20 @@
/obj/machinery/computer/cloning/proc/findcloner()
var/num = 1
- var/area/A = get_area(src)
- for(var/obj/machinery/clonepod/P in A.get_contents())
+ for(var/obj/machinery/clonepod/P in get_area(src))
if(!P.connected)
pods += P
P.connected = src
P.name = "[initial(P.name)] #[num++]"
-/obj/machinery/computer/cloning/attackby(obj/item/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/disk/data)) //INSERT SOME DISKETTES
- if (!diskette)
+/obj/machinery/computer/cloning/attackby(obj/item/W as obj, mob/user as mob, params)
+ if(istype(W, /obj/item/weapon/disk/data)) //INSERT SOME DISKETTES
+ if(!diskette)
user.drop_item()
W.loc = src
diskette = W
to_chat(user, "You insert [W].")
- updateUsrDialog()
+ SStgui.update_uis(src)
return
else if(istype(W, /obj/item/device/multitool))
var/obj/item/device/multitool/M = W
@@ -79,18 +107,8 @@
P.connected = src
P.name = "[initial(P.name)] #[pods.len]"
to_chat(user, "You connect [P] to [src].")
-
- else if (menu == 4 && (istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda)))
- if(check_access(W))
- records.Remove(active_record)
- qdel(active_record)
- temp = "Record deleted."
- menu = 2
- else
- temp = "Access Denied."
else
- ..()
- return
+ return ..()
/obj/machinery/computer/cloning/attack_ai(mob/user as mob)
return attack_hand(user)
@@ -103,233 +121,304 @@
return
updatemodules()
+ tgui_interact(user)
- ui_interact(user)
+/obj/machinery/computer/cloning/resleeving/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/cloning)
+ )
-/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- var/data[0]
-
- var/records_list_ui[0]
- for(var/datum/dna2/record/R in records)
- records_list_ui[++records_list_ui.len] = list("ckey" = R.ckey, "name" = R.dna.real_name)
-
- var/pods_list_ui[0]
- for(var/obj/machinery/clonepod/pod in pods)
- pods_list_ui[++pods_list_ui.len] = list("pod" = pod, "biomass" = pod.get_biomass())
-
- if(pods)
- data["pods"] = pods_list_ui
- else
- data["pods"] = null
-
- if(records)
- data["records"] = records_list_ui
- else
- data["records"] = null
-
- if(active_record)
- data["activeRecord"] = list("ckey" = active_record.ckey, "real_name" = active_record.dna.real_name, \
- "ui" = active_record.dna.uni_identity, "se" = active_record.dna.struc_enzymes)
- else
- data["activeRecord"] = null
-
- data["menu"] = menu
- data["connected"] = scanner
- data["podsLen"] = pods.len
- data["loading"] = loading
- if(!scanner.occupant)
- scantemp = ""
- data["scantemp"] = scantemp
- data["occupant"] = scanner.occupant
- data["locked"] = scanner.locked
- data["diskette"] = diskette
- data["temp"] = temp
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "cloning.tmpl", src.name, 400, 450)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(5)
-
-/obj/machinery/computer/cloning/Topic(href, href_list)
- if(..())
- return 1
-
- if(loading)
+/obj/machinery/computer/cloning/tgui_interact(mob/user, datum/tgui/ui = null)
+ if(stat & (NOPOWER|BROKEN))
return
- if ((href_list["scan"]) && (!isnull(scanner)))
- scantemp = ""
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CloningConsole", "Cloning Console")
+ ui.open()
- loading = 1
+/obj/machinery/computer/cloning/tgui_data(mob/user)
+ var/data[0]
+ data["menu"] = menu
+ data["scanner"] = sanitize("[scanner]")
- spawn(20)
- scan_mob(scanner.occupant)
+ var/canpodautoprocess = 0
+ if(pods.len)
+ data["numberofpods"] = pods.len
- loading = 0
+ var/list/tempods[0]
+ for(var/obj/machinery/clonepod/pod in pods)
+ if(pod.efficiency > 5)
+ canpodautoprocess = 1
- //No locking an open scanner.
- else if ((href_list["lock"]) && (!isnull(scanner)))
- if ((!scanner.locked) && (scanner.occupant))
- scanner.locked = 1
- else
- scanner.locked = 0
+ var/status = "idle"
+ if(pod.mess)
+ status = "mess"
+ else if(pod.occupant && !(pod.stat & NOPOWER))
+ status = "cloning"
+ tempods.Add(list(list(
+ "pod" = "\ref[pod]",
+ "name" = sanitize(capitalize(pod.name)),
+ "biomass" = pod.get_biomass(),
+ "status" = status,
+ "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
+ )))
+ data["pods"] = tempods
- else if ((href_list["eject"]) && (!isnull(scanner)))
- if ((!scanner.locked) && (scanner.occupant))
- scanner.eject_occupant()
+ data["loading"] = loading
+ data["autoprocess"] = autoprocess
+ data["can_brainscan"] = can_brainscan() // You'll need tier 4s for this
+ data["scan_mode"] = scan_mode
- else if (href_list["view_rec"])
- active_record = find_record(href_list["view_rec"])
- if(istype(active_record,/datum/dna2/record))
- if ((isnull(active_record.ckey)))
- qdel(active_record)
- temp = "ERROR: Record Corrupt"
- else
- menu = 3
- else
- active_record = null
- temp = "Record missing."
+ if(scanner && pods.len && ((scanner.scan_level > 2) || canpodautoprocess))
+ data["autoallowed"] = 1
+ else
+ data["autoallowed"] = 0
+ if(scanner)
+ data["occupant"] = scanner.occupant
+ data["locked"] = scanner.locked
+ data["temp"] = temp
+ data["scantemp"] = scantemp
+ data["disk"] = diskette
+ data["selected_pod"] = "\ref[selected_pod]"
+ var/list/temprecords[0]
+ for(var/datum/dna2/record/R in records)
+ var tempRealName = R.dna.real_name
+ temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName))))
+ data["records"] = temprecords
- else if (href_list["del_rec"])
- if ((!active_record) || (menu < 3))
- return
- if (menu == 3) //If we are viewing a record, confirm deletion
- temp = "Delete record?"
- menu = 4
+ if(selected_pod && (selected_pod in pods) && selected_pod.get_biomass() >= CLONE_BIOMASS)
+ data["podready"] = 1
+ else
+ data["podready"] = 0
- else if (href_list["disk"]) //Load or eject.
- switch(href_list["disk"])
- if("load")
- if ((isnull(diskette)) || isnull(diskette.buf))
- temp = "Load error."
+ data["modal"] = tgui_modal_data(src)
+
+ return data
+
+/obj/machinery/computer/cloning/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ . = TRUE
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_ANSWER)
+ if(params["id"] == "del_rec" && active_record)
+ var/obj/item/weapon/card/id/C = usr.get_active_hand()
+ if(!istype(C) && !istype(C, /obj/item/device/pda))
+ set_temp("ID not in hand.", "danger")
return
- if (isnull(active_record))
- temp = "Record error."
- menu = 1
- return
-
- active_record = diskette.buf
-
- temp = "Load successful."
- if("eject")
- if (!isnull(diskette))
- diskette.loc = loc
- diskette = null
-
- else if (href_list["save_disk"]) //Save to disk!
- if ((isnull(diskette)) || (diskette.read_only) || (isnull(active_record)))
- temp = "Save error."
-
- // DNA2 makes things a little simpler.
- diskette.buf = active_record
- diskette.buf.types = 0
- switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se
- if("ui")
- diskette.buf.types = DNA2_BUF_UI
- if("ue")
- diskette.buf.types = DNA2_BUF_UI | DNA2_BUF_UE
- if("se")
- diskette.buf.types = DNA2_BUF_SE
- diskette.name = "data disk - '[active_record.dna.real_name]'"
- temp = "Save \[[href_list["save_disk"]]\] successful."
-
- else if (href_list["refresh"])
- updateUsrDialog()
-
- else if (href_list["clone"])
- var/datum/dna2/record/C = find_record(href_list["clone"])
- //Look for that player! They better be dead!
- if(istype(C))
- //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
- if(!LAZYLEN(pods))
- temp = "Error: No clone pods detected."
- else
- var/obj/machinery/clonepod/pod = pods[1]
- if (pods.len > 1)
- pod = input(usr,"Select a cloning pod to use", "Pod selection") as anything in pods
- if(pod.occupant)
- temp = "Error: Clonepod is currently occupied."
- else if(pod.get_biomass() < CLONE_BIOMASS)
- temp = "Error: Not enough biomass."
- else if(pod.mess)
- temp = "Error: Clonepod malfunction."
- else if(!config.revival_cloning)
- temp = "Error: Unable to initiate cloning cycle."
- else if(pod.growclone(C))
- temp = "Initiating cloning cycle..."
- records.Remove(C)
- qdel(C)
- menu = 1
+ if(check_access(C))
+ records.Remove(active_record)
+ qdel(active_record)
+ set_temp("Record deleted.", "success")
+ menu = MENU_RECORDS
else
+ set_temp("Access denied.", "danger")
+ return
- var/mob/selected = find_dead_player("[C.ckey]")
- selected << 'sound/machines/chime.ogg' //probably not the best sound but I think it's reasonable
- var/answer = alert(selected,"Do you want to return to life?","Cloning","Yes","No")
- if(answer != "No" && pod.growclone(C))
- temp = "Initiating cloning cycle..."
- records.Remove(C)
- qdel(C)
- menu = 1
+ switch(action)
+ if("scan")
+ if(!scanner || !scanner.occupant || loading)
+ return
+ set_scan_temp("Scanner ready.", "good")
+ loading = TRUE
+
+ spawn(20)
+ if(can_brainscan() && scan_mode)
+ scan_mob(scanner.occupant, scan_brain = TRUE)
+ else
+ scan_mob(scanner.occupant)
+ loading = FALSE
+ SStgui.update_uis(src)
+ if("autoprocess")
+ autoprocess = text2num(params["on"]) > 0
+ if("lock")
+ if(isnull(scanner) || !scanner.occupant) //No locking an open scanner.
+ return
+ scanner.locked = !scanner.locked
+ if("view_rec")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ active_record = locate(ref)
+ if(istype(active_record))
+ if(isnull(active_record.ckey))
+ qdel(active_record)
+ set_temp("Error: Record corrupt.", "danger")
+ else
+ var/obj/item/weapon/implant/health/H = null
+ if(active_record.implant)
+ H = locate(active_record.implant)
+ var/list/payload = list(
+ activerecord = "\ref[active_record]",
+ health = (H && istype(H)) ? H.sensehealth() : "",
+ realname = sanitize(active_record.dna.real_name),
+ unidentity = active_record.dna.uni_identity,
+ strucenzymes = active_record.dna.struc_enzymes,
+ )
+ tgui_modal_message(src, action, "", null, payload)
+ else
+ active_record = null
+ set_temp("Error: Record missing.", "danger")
+ if("del_rec")
+ if(!active_record)
+ return
+ tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel")
+ if("disk") // Disk management.
+ if(!length(params["option"]))
+ return
+ switch(params["option"])
+ if("load")
+ if(isnull(diskette) || isnull(diskette.buf))
+ set_temp("Error: The disk's data could not be read.", "danger")
+ return
+ else if(isnull(active_record))
+ set_temp("Error: No active record was found.", "danger")
+ menu = MENU_MAIN
+ return
+
+ active_record = diskette.buf
+ set_temp("Successfully loaded from disk.", "success")
+ if("save")
+ if(isnull(diskette) || diskette.read_only || isnull(active_record))
+ set_temp("Error: The data could not be saved.", "danger")
+ return
+
+ // DNA2 makes things a little simpler.
+ var/types
+ switch(params["savetype"]) // Save as Ui/Ui+Ue/Se
+ if("ui")
+ types = DNA2_BUF_UI
+ if("ue")
+ types = DNA2_BUF_UI|DNA2_BUF_UE
+ if("se")
+ types = DNA2_BUF_SE
+ else
+ set_temp("Error: Invalid save format.", "danger")
+ return
+ diskette.buf = active_record
+ diskette.buf.types = types
+ diskette.name = "data disk - '[active_record.dna.real_name]'"
+ set_temp("Successfully saved to disk.", "success")
+ if("eject")
+ if(!isnull(diskette))
+ diskette.loc = loc
+ diskette = null
+ if("refresh")
+ SStgui.update_uis(src)
+ if("selectpod")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ var/obj/machinery/clonepod/selected = locate(ref)
+ if(istype(selected) && (selected in pods))
+ selected_pod = selected
+ if("clone")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ var/datum/dna2/record/C = locate(ref)
+ //Look for that player! They better be dead!
+ if(istype(C))
+ tgui_modal_clear(src)
+ //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
+ if(!length(pods))
+ set_temp("Error: No cloning pod detected.", "danger")
+ else
+ var/obj/machinery/clonepod/pod = selected_pod
+ var/cloneresult
+ if(!selected_pod)
+ set_temp("Error: No cloning pod selected.", "danger")
+ else if(pod.occupant)
+ set_temp("Error: The cloning pod is currently occupied.", "danger")
+ else if(pod.get_biomass() < CLONE_BIOMASS)
+ set_temp("Error: Not enough biomass.", "danger")
+ else if(pod.mess)
+ set_temp("Error: The cloning pod is malfunctioning.", "danger")
+ else if(!config.revival_cloning)
+ set_temp("Error: Unable to initiate cloning cycle.", "danger")
else
- temp = "Initiating cloning cycle...
Error: Post-initialisation failed. Cloning cycle aborted."
-
+ cloneresult = pod.growclone(C)
+ if(cloneresult)
+ set_temp("Initiating cloning cycle...", "success")
+ playsound(src, 'sound/machines/medbayscanner1.ogg', 100, 1)
+ records.Remove(C)
+ qdel(C)
+ menu = MENU_MAIN
+ else
+ set_temp("Error: Initialisation failure.", "danger")
+ else
+ set_temp("Error: Data corruption.", "danger")
+ if("menu")
+ menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS)
+ if("toggle_mode")
+ if(loading)
+ return
+ if(can_brainscan())
+ scan_mode = !scan_mode
+ else
+ scan_mode = FALSE
+ if("eject")
+ if(usr.incapacitated() || !scanner || loading)
+ return
+ scanner.eject_occupant(usr)
+ scanner.add_fingerprint(usr)
+ if("cleartemp")
+ temp = null
else
- temp = "Error: Data corruption."
+ return FALSE
- else if (href_list["menu"])
- menu = href_list["menu"]
- temp = ""
- scantemp = ""
-
- SSnanoui.update_uis(src)
add_fingerprint(usr)
-/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob)
- var/brain_skip = 0
- if (istype(subject, /mob/living/carbon/brain)) //Brain scans.
- brain_skip = 1
- if ((isnull(subject)) || (!(ishuman(subject)) && !brain_skip) || (!subject.dna))
- scantemp = "Error: Unable to locate valid genetic data."
+/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0)
+ if(stat & NOPOWER)
return
- if (!subject.has_brain() && !brain_skip)
- if(istype(subject, /mob/living/carbon/human))
+ if(scanner.stat & (NOPOWER|BROKEN))
+ return
+ if(scan_brain && !can_brainscan())
+ return
+ if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna))
+ if(isalien(subject))
+ set_scan_temp("Xenomorphs are not scannable.", "bad")
+ SStgui.update_uis(src)
+ return
+ // can add more conditions for specific non-human messages here
+ else
+ set_scan_temp("Subject species is not scannable.", "bad")
+ SStgui.update_uis(src)
+ return
+ if(!subject.has_brain())
+ if(ishuman(subject))
var/mob/living/carbon/human/H = subject
if(H.should_have_organ("brain"))
- scantemp = "Error: No signs of intelligence detected."
+ set_scan_temp("No brain detected in subject.", "bad")
else
- scantemp = "Error: No signs of intelligence detected."
+ set_scan_temp("No brain detected in subject.", "bad")
+ SStgui.update_uis(src)
+ return
+ if(subject.suiciding)
+ set_scan_temp("Subject has committed suicide and is not scannable.", "bad")
+ SStgui.update_uis(src)
+ return
+ if((!subject.ckey) || (!subject.client))
+ set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad")
+ SStgui.update_uis(src)
+ return
+ if((NOCLONE in subject.mutations))
+ set_scan_temp("Subject has incompatible genetic mutations.", "bad")
+ SStgui.update_uis(src)
+ return
+ if(!isnull(find_record(subject.ckey)))
+ set_scan_temp("Subject already in database.")
+ SStgui.update_uis(src)
return
- if(subject.isSynthetic())
- scantemp = "Error: Majority of subject is non-organic."
- return
- if (subject.suiciding)
- scantemp = "Error: Subject's brain is not responding to scanning stimuli."
- return
- if (NOCLONE in subject.mutations)
- scantemp = "Error: Mental interface failure."
- return
- if (subject.species && subject.species.flags & NO_SCAN && !brain_skip)
- scantemp = "Error: Mental interface failure."
- return
- for(var/modifier_type in subject.modifiers) //Can't be cloned, even if they had a previous scan
- if(istype(modifier_type, /datum/modifier/no_clone))
- scantemp = "Error: Mental interface failure."
+ for(var/obj/machinery/clonepod/pod in pods)
+ if(pod.occupant && pod.occupant.mind == subject.mind)
+ set_scan_temp("Subject already getting cloned.")
+ SStgui.update_uis(src)
return
- if ((!subject.ckey) || (!subject.client))
- scantemp = "Error: Mental interface failure."
- if(subject.stat == DEAD && subject.mind && subject.mind.key) // If they're dead and not in their body, tell them to get in it.
- var/mob/observer/dead/ghost = subject.get_ghost()
- if(ghost)
- ghost.notify_revive("Someone is trying to scan your body in the cloner. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg', source = src)
- return
- if (!isnull(find_record(subject.ckey)))
- scantemp = "Subject already in database."
- return
subject.dna.check_integrity()
@@ -342,10 +431,7 @@
R.languages = subject.languages
R.gender = subject.gender
R.body_descriptors = subject.descriptors
- if(!brain_skip) //Brains don't have flavor text.
- R.flavor = subject.flavor_texts.Copy()
- else
- R.flavor = list()
+ R.flavor = subject.flavor_texts.Copy()
for(var/datum/modifier/mod in subject.modifiers)
if(mod.flags & MODIFIER_GENETIC)
R.genetic_modifiers.Add(mod.type)
@@ -364,13 +450,47 @@
R.mind = "\ref[subject.mind]"
records += R
- scantemp = "Subject successfully scanned."
+ set_scan_temp("Subject successfully scanned.", "good")
+ SStgui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
var/selected_record = null
for(var/datum/dna2/record/R in records)
- if (R.ckey == find_key)
+ if(R.ckey == find_key)
selected_record = R
break
return selected_record
+
+/obj/machinery/computer/cloning/proc/can_autoprocess()
+ return (scanner && scanner.scan_level > 2)
+
+/obj/machinery/computer/cloning/proc/can_brainscan()
+ return (scanner && scanner.scan_level > 3)
+
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger
+ */
+/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
+
+/**
+ * Sets a temporary scan message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * color - The color of the message: (color name)
+ */
+/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE)
+ scantemp = list(text = text, color = color)
+ if(update_now)
+ SStgui.update_uis(src)
+
+#undef MENU_MAIN
+#undef MENU_RECORDS
\ No newline at end of file
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index d3daba4b5e..f3c930fe78 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -1,4 +1,11 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
+#define MED_DATA_R_LIST 2 // Record list
+#define MED_DATA_MAINT 3 // Records maintenance
+#define MED_DATA_RECORD 4 // Record
+#define MED_DATA_V_DATA 5 // Virus database
+#define MED_DATA_MEDBOT 6 // Medbot monitor
+
+#define FIELD(N, V, E) list(field = N, value = V, edit = E)
+#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB)
/obj/machinery/computer/med_data//TODO:SANITY
name = "medical records console"
@@ -14,9 +21,50 @@
var/screen = null
var/datum/data/record/active1 = null
var/datum/data/record/active2 = null
- var/a_id = null
- var/temp = null
+ var/list/temp = null
var/printing = null
+ // The below are used to make modal generation more convenient
+ var/static/list/field_edit_questions
+ var/static/list/field_edit_choices
+
+
+/obj/machinery/computer/med_data/Initialize()
+ ..()
+ field_edit_questions = list(
+ // General
+ "sex" = "Please select new sex:",
+ "age" = "Please input new age:",
+ "fingerprint" = "Please input new fingerprint hash:",
+ "p_stat" = "Please select new physical status:",
+ "m_stat" = "Please select new mental status:",
+ // Medical
+ "id_gender" = "Please select new gender identity:",
+ "blood_type" = "Please select new blood type:",
+ "b_dna" = "Please input new DNA:",
+ "mi_dis" = "Please input new minor disabilities:",
+ "mi_dis_d" = "Please summarize minor disabilities:",
+ "ma_dis" = "Please input new major disabilities:",
+ "ma_dis_d" = "Please summarize major disabilities:",
+ "alg" = "Please input new allergies:",
+ "alg_d" = "Please summarize allergies:",
+ "cdi" = "Please input new current diseases:",
+ "cdi_d" = "Please summarize current diseases:",
+ "notes" = "Please input new important notes:",
+ )
+ field_edit_choices = list(
+ // General
+ "sex" = all_genders_text_list,
+ "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"),
+ "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"),
+ // Medical
+ "id_gender" = all_genders_text_list,
+ "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"),
+ )
+
+/obj/machinery/computer/med_data/Destroy()
+ active1 = null
+ active2 = null
+ return ..()
/obj/machinery/computer/med_data/verb/eject_id()
set category = "Object"
@@ -40,396 +88,216 @@
O.loc = src
scan = O
to_chat(user, "You insert \the [O].")
+ tgui_interact(user)
else
..()
/obj/machinery/computer/med_data/attack_ai(user as mob)
- return src.attack_hand(user)
+ return attack_hand(user)
/obj/machinery/computer/med_data/attack_hand(mob/user as mob)
if(..())
return
- var/dat = list()
- if (src.temp)
- dat += text("[src.temp]
Clear Screen")
- else
- dat += text("Confirm Identity: []
", src, (src.scan ? text("[]", src.scan.name) : "----------"))
- if (src.authenticated)
- switch(src.screen)
- if(1.0)
- dat += {"
-Search Records
-
List Records
-
-
Virus Database
-
Medbot Tracking
-
-
Record Maintenance
-
{Log Out}
-"}
- if(2.0)
- dat += "Record List:
"
- if(!isnull(data_core.general))
- for(var/datum/data/record/R in sortRecord(data_core.general))
- dat += text("[]: []
", src, R, R.fields["id"], R.fields["name"])
- //Foreach goto(132)
- dat += text("
Back", src)
- if(3.0)
- dat += text("Records Maintenance
\nBackup To Disk
\nUpload From disk
\nDelete All Records
\n
\nBack", src, src, src, src)
- if(4.0)
- var/icon/front = active1.fields["photo_front"]
- var/icon/side = active1.fields["photo_side"]
- user << browse_rsc(front, "front.png")
- user << browse_rsc(side, "side.png")
- dat += "Medical Record
"
- if ((istype(src.active1, /datum/data/record) && data_core.general.Find(src.active1)))
- dat += "Name: [active1.fields["name"]] \
- ID: [active1.fields["id"]] \n \
- Entity Classification: [active1.fields["brain_type"]] \n \
- Sex: [active1.fields["sex"]] \n"
- if ((istype(src.active2, /datum/data/record) && data_core.medical.Find(src.active2)))
- dat += "Gender identity: [active2.fields["id_gender"]] "
+ add_fingerprint(user)
+ tgui_interact(user)
+
+
+/obj/machinery/computer/med_data/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "MedicalRecords", "Medical Records") // 800, 380
+ ui.open()
+ ui.set_autoupdate(FALSE)
+
+
+/obj/machinery/computer/med_data/tgui_data(mob/user)
+ var/data[0]
+ data["temp"] = temp
+ data["scan"] = scan ? scan.name : null
+ data["authenticated"] = authenticated
+ data["rank"] = rank
+ data["screen"] = screen
+ data["printing"] = printing
+ data["isAI"] = isAI(user)
+ data["isRobot"] = isrobot(user)
+ if(authenticated)
+ switch(screen)
+ if(MED_DATA_R_LIST)
+ if(!isnull(data_core.general))
+ var/list/records = list()
+ data["records"] = records
+ for(var/datum/data/record/R in sortRecord(data_core.general))
+ records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"])
+ if(MED_DATA_RECORD)
+ var/list/general = list()
+ data["general"] = general
+ if(istype(active1, /datum/data/record) && data_core.general.Find(active1))
+ var/list/fields = list()
+ general["fields"] = fields
+ fields[++fields.len] = FIELD("Name", active1.fields["name"], null)
+ fields[++fields.len] = FIELD("ID", active1.fields["id"], null)
+ fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex")
+ fields[++fields.len] = FIELD("Age", active1.fields["age"], "age")
+ fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint")
+ fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat")
+ fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat")
+ var/list/photos = list()
+ general["photos"] = photos
+ photos[++photos.len] = active1.fields["photo-south"]
+ photos[++photos.len] = active1.fields["photo-west"]
+ general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0)
+ general["empty"] = 0
+ else
+ general["empty"] = 1
+
+ var/list/medical = list()
+ data["medical"] = medical
+ if(istype(active2, /datum/data/record) && data_core.medical.Find(active2))
+ var/list/fields = list()
+ medical["fields"] = fields
+ fields[++fields.len] = MED_FIELD("Gender identity", active2.fields["id_gender"], "id_gender", TRUE)
+ fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["b_type"], "blood_type", FALSE)
+ fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE)
+ fields[++fields.len] = MED_FIELD("Brain Type", active2.fields["brain_type"], "brain_type", TRUE)
+ fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE)
+ if(!active2.fields["comments"] || !islist(active2.fields["comments"]))
+ active2.fields["comments"] = list()
+ medical["comments"] = active2.fields["comments"]
+ medical["empty"] = 0
+ else
+ medical["empty"] = 1
+ if(MED_DATA_V_DATA)
+ data["virus"] = list()
+ for(var/ID in virusDB)
+ var/datum/data/record/v = virusDB[ID]
+ data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]"))
+ if(MED_DATA_MEDBOT)
+ data["medbots"] = list()
+ for(var/mob/living/bot/medbot/M in mob_list)
+ if(M.z != z)
+ continue
+ var/turf/T = get_turf(M)
+ if(T)
+ var/medbot = list()
+ var/area/A = get_area(T)
+ medbot["name"] = M.name
+ medbot["area"] = A.name
+ medbot["x"] = T.x
+ medbot["y"] = T.y
+ medbot["on"] = M.on
+ if(!isnull(M.reagent_glass) && M.use_beaker)
+ medbot["use_beaker"] = 1
+ medbot["total_volume"] = M.reagent_glass.reagents.total_volume
+ medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume
else
- dat += "Gender identity: Unknown "
- dat += "Age: [active1.fields["age"]] \n \
- Fingerprint: [active1.fields["fingerprint"]] \n \
- Physical Status: [active1.fields["p_stat"]] \n \
- Mental Status: [active1.fields["m_stat"]]
| \
- Photo:
  |
"
- else
- dat += "General Record Lost!
"
- if ((istype(src.active2, /datum/data/record) && data_core.medical.Find(src.active2)))
- dat += text("
\nMedical Data
\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\nComments/Log
", src, src.active2.fields["b_type"], src, src.active2.fields["b_dna"], src, src.active2.fields["mi_dis"], src, src.active2.fields["mi_dis_d"], src, src.active2.fields["ma_dis"], src, src.active2.fields["ma_dis_d"], src, src.active2.fields["alg"], src, src.active2.fields["alg_d"], src, src.active2.fields["cdi"], src, src.active2.fields["cdi_d"], src, decode(src.active2.fields["notes"]))
- var/counter = 1
- while(src.active2.fields[text("com_[]", counter)])
- dat += text("[]
Delete Entry
", src.active2.fields[text("com_[]", counter)], src, counter)
- counter++
- dat += text("Add Entry
", src)
- dat += text("Delete Record (Medical Only)
", src)
- else
- dat += "Medical Record Lost!
"
- dat += text("New Record
")
- dat += text("\nPrint Record
\nBack
", src, src)
- if(5.0)
- dat += "Virus Database"
- for (var/ID in virusDB)
- var/datum/data/record/v = virusDB[ID]
- dat += "
[v.fields["name"]]"
+ medbot["use_beaker"] = 0
+ data["medbots"] += list(medbot)
- dat += "
Back"
- if(6.0)
- dat += "Medical Robot Monitor"
- dat += "Back"
- dat += "
Medical Robots:"
- var/bdat = null
- for(var/mob/living/bot/medbot/M in mob_list)
+ data["modal"] = tgui_modal_data(src)
+ return data
- if(M.z != src.z) continue //only find medibots on the same z-level as the computer
- var/turf/bl = get_turf(M)
- if(bl) //if it can't find a turf for the medibot, then it probably shouldn't be showing up
- bdat += "[M.name] - \[[bl.x],[bl.y]\] - [M.on ? "Online" : "Offline"]
"
- if((!isnull(M.reagent_glass)) && M.use_beaker)
- bdat += "Reservoir: \[[M.reagent_glass.reagents.total_volume]/[M.reagent_glass.reagents.maximum_volume]\]
"
- else
- bdat += "Using Internal Synthesizer.
"
- if(!bdat)
- dat += "
None detected"
- else
- dat += "
[bdat]"
-
- else
- else
- dat += text("{Log In}", src)
- dat = jointext(dat,null)
- user << browse(text("Medical Records[]", dat), "window=med_rec")
- onclose(user, "med_rec")
- return
-
-/obj/machinery/computer/med_data/Topic(href, href_list)
+/obj/machinery/computer/med_data/tgui_act(action, params)
if(..())
- return 1
+ return TRUE
- if (!( data_core.general.Find(src.active1) ))
- src.active1 = null
+ if(!data_core.general.Find(active1))
+ active1 = null
+ if(!data_core.medical.Find(active2))
+ active2 = null
- if (!( data_core.medical.Find(src.active2) ))
- src.active2 = null
-
- if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
-
- if (href_list["temp"])
- src.temp = null
-
- if (href_list["scan"])
- if (src.scan)
-
- if(ishuman(usr))
- scan.loc = usr.loc
-
- if(!usr.get_active_hand())
- usr.put_in_hands(scan)
-
- scan = null
-
- else
- src.scan.loc = src.loc
- src.scan = null
+ . = TRUE
+ if(tgui_act_modal(action, params))
+ return
+ switch(action)
+ if("cleartemp")
+ temp = null
+ if("scan")
+ if(scan)
+ scan.forceMove(loc)
+ if(ishuman(usr) && !usr.get_active_hand())
+ usr.put_in_hands(scan)
+ scan = null
else
var/obj/item/I = usr.get_active_hand()
- if (istype(I, /obj/item/weapon/card/id))
+ if(istype(I, /obj/item/weapon/card/id))
usr.drop_item()
- I.loc = src
- src.scan = I
-
- else if (href_list["logout"])
- src.authenticated = null
- src.screen = null
- src.active1 = null
- src.active2 = null
-
- else if (href_list["login"])
-
- if (istype(usr, /mob/living/silicon/ai))
- src.active1 = null
- src.active2 = null
- src.authenticated = usr.name
- src.rank = "AI"
- src.screen = 1
-
- else if (istype(usr, /mob/living/silicon/robot))
- src.active1 = null
- src.active2 = null
- src.authenticated = usr.name
+ I.forceMove(src)
+ scan = I
+ if("login")
+ var/login_type = text2num(params["login_type"])
+ if(login_type == LOGIN_TYPE_NORMAL && istype(scan))
+ if(check_access(scan))
+ authenticated = scan.registered_name
+ rank = scan.assignment
+ else if(login_type == LOGIN_TYPE_AI && isAI(usr))
+ authenticated = usr.name
+ rank = "AI"
+ else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr))
+ authenticated = usr.name
var/mob/living/silicon/robot/R = usr
- src.rank = "[R.modtype] [R.braintype]"
- src.screen = 1
+ rank = "[R.modtype] [R.braintype]"
+ if(authenticated)
+ active1 = null
+ active2 = null
+ screen = MED_DATA_R_LIST
+ else
+ . = FALSE
- else if (istype(src.scan, /obj/item/weapon/card/id))
- src.active1 = null
- src.active2 = null
+ if(.)
+ return
- if (src.check_access(src.scan))
- src.authenticated = src.scan.registered_name
- src.rank = src.scan.assignment
- src.screen = 1
-
- if (src.authenticated)
-
- if(href_list["screen"])
- src.screen = text2num(href_list["screen"])
- if(src.screen < 1)
- src.screen = 1
-
- src.active1 = null
- src.active2 = null
-
- if(href_list["vir"])
- var/datum/data/record/v = locate(href_list["vir"])
- src.temp = "GNAv2 based virus lifeform V-[v.fields["id"]]"
- src.temp += "
Name: [v.fields["name"]]"
- src.temp += "
Antigen: [v.fields["antigen"]]"
- src.temp += "
Spread: [v.fields["spread type"]] "
- src.temp += "
Details:
[v.fields["description"]]"
-
- if (href_list["del_all"])
- src.temp = text("Are you sure you wish to delete all records?
\n\tYes
\n\tNo
", src, src)
-
- if (href_list["del_all2"])
+ if(authenticated)
+ . = TRUE
+ switch(action)
+ if("logout")
+ if(scan)
+ scan.forceMove(loc)
+ if(ishuman(usr) && !usr.get_active_hand())
+ usr.put_in_hands(scan)
+ scan = null
+ authenticated = null
+ screen = null
+ active1 = null
+ active2 = null
+ if("screen")
+ screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT)
+ active1 = null
+ active2 = null
+ if("vir")
+ var/datum/data/record/v = locate(params["vir"])
+ if(!istype(v))
+ return FALSE
+ tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"])
+ if("del_all")
for(var/datum/data/record/R in data_core.medical)
- //R = null
qdel(R)
- //Foreach goto(494)
- src.temp = "All records deleted."
-
- if (href_list["field"])
- var/a1 = src.active1
- var/a2 = src.active2
- switch(href_list["field"])
- if("fingerprint")
- if (istype(src.active1, /datum/data/record))
- var/t1 = sanitize(input("Please input fingerprint hash:", "Med. records", src.active1.fields["fingerprint"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1))
- return
- src.active1.fields["fingerprint"] = t1
- if("sex")
- if (istype(src.active1, /datum/data/record))
- src.active1.fields["sex"] = next_in_list(src.active1.fields["sex"], all_genders_text_list)
- if("id_gender")
- if (istype(src.active2, /datum/data/record))
- src.active2.fields["id_gender"] = next_in_list(src.active2.fields["id_gender"], all_genders_text_list)
- if("age")
- if (istype(src.active1, /datum/data/record))
- var/t1 = input("Please input age:", "Med. records", src.active1.fields["age"], null) as num
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1))
- return
- src.active1.fields["age"] = t1
- if("mi_dis")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please input minor disabilities list:", "Med. records", src.active2.fields["mi_dis"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["mi_dis"] = t1
- if("mi_dis_d")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please summarize minor dis.:", "Med. records", src.active2.fields["mi_dis_d"], null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["mi_dis_d"] = t1
- if("ma_dis")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please input major diabilities list:", "Med. records", src.active2.fields["ma_dis"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["ma_dis"] = t1
- if("ma_dis_d")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please summarize major dis.:", "Med. records", src.active2.fields["ma_dis_d"], null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["ma_dis_d"] = t1
- if("alg")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please state allergies:", "Med. records", src.active2.fields["alg"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["alg"] = t1
- if("alg_d")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please summarize allergies:", "Med. records", src.active2.fields["alg_d"], null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["alg_d"] = t1
- if("cdi")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please state diseases:", "Med. records", src.active2.fields["cdi"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["cdi"] = t1
- if("cdi_d")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please summarize diseases:", "Med. records", src.active2.fields["cdi_d"], null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["cdi_d"] = t1
- if("notes")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please summarize notes:", "Med. records", html_decode(src.active2.fields["notes"]), null) as message, extra = 0, max_length = MAX_RECORD_LENGTH)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["notes"] = t1
- if("p_stat")
- if (istype(src.active1, /datum/data/record))
- src.temp = text("Physical Condition:
\n\t*Deceased*
\n\t*SSD*
\n\tActive
\n\tPhysically Unfit
\n\tDisabled
", src, src, src, src, src)
- if("m_stat")
- if (istype(src.active1, /datum/data/record))
- src.temp = text("Mental Condition:
\n\t*Insane*
\n\t*Unstable*
\n\t*Watch*
\n\tStable
", src, src, src, src)
- if("b_type")
- if (istype(src.active2, /datum/data/record))
- src.temp = text("Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
", src, src, src, src, src, src, src, src)
- if("b_dna")
- if (istype(src.active2, /datum/data/record))
- var/t1 = sanitize(input("Please input DNA hash:", "Med. records", src.active2.fields["b_dna"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- src.active2.fields["b_dna"] = t1
- if("vir_name")
- var/datum/data/record/v = locate(href_list["edit_vir"])
- if (v)
- var/t1 = sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1))
- return
- v.fields["name"] = t1
- if("vir_desc")
- var/datum/data/record/v = locate(href_list["edit_vir"])
- if (v)
- var/t1 = sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1))
- return
- v.fields["description"] = t1
- else
-
- if (href_list["p_stat"])
- if (src.active1)
- switch(href_list["p_stat"])
- if("deceased")
- src.active1.fields["p_stat"] = "*Deceased*"
- if("ssd")
- src.active1.fields["p_stat"] = "*SSD*"
- if("active")
- src.active1.fields["p_stat"] = "Active"
- if("unfit")
- src.active1.fields["p_stat"] = "Physically Unfit"
- if("disabled")
- src.active1.fields["p_stat"] = "Disabled"
- if(PDA_Manifest.len)
- PDA_Manifest.Cut()
-
- if (href_list["m_stat"])
- if (src.active1)
- switch(href_list["m_stat"])
- if("insane")
- src.active1.fields["m_stat"] = "*Insane*"
- if("unstable")
- src.active1.fields["m_stat"] = "*Unstable*"
- if("watch")
- src.active1.fields["m_stat"] = "*Watch*"
- if("stable")
- src.active1.fields["m_stat"] = "Stable"
-
-
- if (href_list["b_type"])
- if (src.active2)
- switch(href_list["b_type"])
- if("an")
- src.active2.fields["b_type"] = "A-"
- if("bn")
- src.active2.fields["b_type"] = "B-"
- if("abn")
- src.active2.fields["b_type"] = "AB-"
- if("on")
- src.active2.fields["b_type"] = "O-"
- if("ap")
- src.active2.fields["b_type"] = "A+"
- if("bp")
- src.active2.fields["b_type"] = "B+"
- if("abp")
- src.active2.fields["b_type"] = "AB+"
- if("op")
- src.active2.fields["b_type"] = "O+"
-
-
- if (href_list["del_r"])
- if (src.active2)
- src.temp = text("Are you sure you wish to delete the record (Medical Portion Only)?
\n\tYes
\n\tNo
", src, src)
-
- if (href_list["del_r2"])
- if (src.active2)
- //src.active2 = null
- qdel(src.active2)
-
- if (href_list["d_rec"])
- var/datum/data/record/R = locate(href_list["d_rec"])
- var/datum/data/record/M = locate(href_list["d_rec"])
- if (!( data_core.general.Find(R) ))
- src.temp = "Record Not Found!"
+ set_temp("All medical records deleted.")
+ if("del_r")
+ if(active2)
+ set_temp("Medical record deleted.")
+ qdel(active2)
+ if("d_rec")
+ var/datum/data/record/general_record = locate(params["d_rec"] || "")
+ if(!data_core.general.Find(general_record))
+ set_temp("Record not found.", "danger")
return
- for(var/datum/data/record/E in data_core.medical)
- if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
- M = E
- else
- //Foreach continue //goto(2540)
- src.active1 = R
- src.active2 = M
- src.screen = 4
- if (href_list["new"])
- if ((istype(src.active1, /datum/data/record) && !( istype(src.active2, /datum/data/record) )))
- var/datum/data/record/R = new /datum/data/record( )
- R.fields["name"] = src.active1.fields["name"]
- R.fields["id"] = src.active1.fields["id"]
- R.name = text("Medical Record #[]", R.fields["id"])
+ var/datum/data/record/medical_record
+ for(var/datum/data/record/M in data_core.medical)
+ if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"])
+ medical_record = M
+ break
+
+ active1 = general_record
+ active2 = medical_record
+ screen = MED_DATA_RECORD
+ if("new")
+ if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
+ var/datum/data/record/R = new /datum/data/record()
+ R.fields["name"] = active1.fields["name"]
+ R.fields["id"] = active1.fields["id"]
+ R.name = "Medical Record #[R.fields["id"]]"
R.fields["b_type"] = "Unknown"
R.fields["b_dna"] = "Unknown"
R.fields["mi_dis"] = "None"
@@ -442,79 +310,157 @@
R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
R.fields["notes"] = "No notes."
data_core.medical += R
- src.active2 = R
- src.screen = 4
-
- if (href_list["add_c"])
- if (!( istype(src.active2, /datum/data/record) ))
+ active2 = R
+ screen = MED_DATA_RECORD
+ set_temp("Medical record created.", "success")
+ if("del_c")
+ var/index = text2num(params["del_c"] || "")
+ if(!index || !istype(active2, /datum/data/record))
return
- var/a2 = src.active2
- var/t1 = sanitize(input("Add Comment:", "Med. records", null, null) as message)
- if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active2 != a2))
- return
- var/counter = 1
- while(src.active2.fields[text("com_[]", counter)])
- counter++
- src.active2.fields[text("com_[counter]")] = text("Made by [authenticated] ([rank]) on [time2text(world.realtime, "DDD MMM DD")] [stationtime2text()], [game_year]
[t1]")
- if (href_list["del_c"])
- if ((istype(src.active2, /datum/data/record) && src.active2.fields[text("com_[]", href_list["del_c"])]))
- src.active2.fields[text("com_[]", href_list["del_c"])] = "Deleted"
-
- if (href_list["search"])
- var/t1 = input("Search String: (Name, DNA, or ID)", "Med. records", null, null) as text
- if ((!( t1 ) || usr.stat || !( src.authenticated ) || usr.restrained() || ((!in_range(src, usr)) && (!istype(usr, /mob/living/silicon)))))
+ var/list/comments = active2.fields["comments"]
+ index = clamp(index, 1, length(comments))
+ if(comments[index])
+ comments.Cut(index, index + 1)
+ if("search")
+ active1 = null
+ active2 = null
+ var/t1 = lowertext(params["t1"] || "")
+ if(!length(t1))
return
- src.active1 = null
- src.active2 = null
- t1 = lowertext(t1)
+
for(var/datum/data/record/R in data_core.medical)
- if ((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])))
- src.active2 = R
+ if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
+ active2 = R
+ break
+ if(!active2)
+ set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger")
+ return
+ for(var/datum/data/record/E in data_core.general)
+ if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"])
+ active1 = E
+ break
+ screen = MED_DATA_RECORD
+ if("print_p")
+ if(!printing)
+ printing = TRUE
+ // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
+ SStgui.update_uis(src)
+ addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS)
+ else
+ return FALSE
+
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params)
+ . = TRUE
+ var/id = params["id"] // The modal's ID
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_OPEN)
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/question = field_edit_questions[field]
+ var/choices = field_edit_choices[field]
+ if(length(choices))
+ tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices)
else
- //Foreach continue //goto(3229)
- if (!( src.active2 ))
- src.temp = text("Could not locate record [].", t1)
+ tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"])
+ if("add_c")
+ tgui_modal_input(src, id, "Please enter your message:")
else
- for(var/datum/data/record/E in data_core.general)
- if ((E.fields["name"] == src.active2.fields["name"] || E.fields["id"] == src.active2.fields["id"]))
- src.active1 = E
- else
- //Foreach continue //goto(3334)
- src.screen = 4
+ return FALSE
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/list/choices = field_edit_choices[field]
+ if(length(choices) && !(answer in choices))
+ return
- if (href_list["print_p"])
- if (!( src.printing ))
- src.printing = 1
- var/datum/data/record/record1 = null
- var/datum/data/record/record2 = null
- if ((istype(src.active1, /datum/data/record) && data_core.general.Find(src.active1)))
- record1 = active1
- if ((istype(src.active2, /datum/data/record) && data_core.medical.Find(src.active2)))
- record2 = active2
- sleep(50)
- var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( src.loc )
- P.info = "Medical Record
"
- if (record1)
- P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", record1.fields["name"], record1.fields["id"], record1.fields["sex"], record1.fields["age"], record1.fields["fingerprint"], record1.fields["p_stat"], record1.fields["m_stat"])
- P.name = text("Medical Record ([])", record1.fields["name"])
- else
- P.info += "General Record Lost!
"
- P.name = "Medical Record"
- if (record2)
- P.info += text("
\nMedical Data
\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\nComments/Log
", record2.fields["b_type"], record2.fields["b_dna"], record2.fields["mi_dis"], record2.fields["mi_dis_d"], record2.fields["ma_dis"], record2.fields["ma_dis_d"], record2.fields["alg"], record2.fields["alg_d"], record2.fields["cdi"], record2.fields["cdi_d"], decode(record2.fields["notes"]))
- var/counter = 1
- while(record2.fields[text("com_[]", counter)])
- P.info += text("[]
", record2.fields[text("com_[]", counter)])
- counter++
- else
- P.info += "Medical Record Lost!
"
- P.info += ""
- src.printing = null
+ if(field == "age")
+ answer = text2num(answer)
- src.add_fingerprint(usr)
- src.updateUsrDialog()
- return
+ if(istype(active2) && (field in active2.fields))
+ active2.fields[field] = answer
+ else if(istype(active1) && (field in active1.fields))
+ active1.fields[field] = answer
+ if("add_c")
+ if(!length(answer) || !istype(active2) || !length(authenticated))
+ return
+ active2.fields["comments"] += list(list(
+ header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]",
+ text = answer
+ ))
+ else
+ return FALSE
+ else
+ return FALSE
+
+
+/**
+ * Called when the print timer finishes
+ */
+/obj/machinery/computer/med_data/proc/print_finish()
+ var/obj/item/weapon/paper/P = new(loc)
+ P.info = "Medical Record
"
+ if(istype(active1, /datum/data/record) && data_core.general.Find(active1))
+ P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
+
\nSex: [active1.fields["sex"]]
+
\nAge: [active1.fields["age"]]
+
\nFingerprint: [active1.fields["fingerprint"]]
+
\nPhysical Status: [active1.fields["p_stat"]]
+
\nMental Status: [active1.fields["m_stat"]]
"}
+ else
+ P.info += "General Record Lost!
"
+ if(istype(active2, /datum/data/record) && data_core.medical.Find(active2))
+ P.info += {"
\nMedical Data
+
\nGender Identity: [active2.fields["id_gender"]]
+
\nBlood Type: [active2.fields["b_type"]]
+
\nDNA: [active2.fields["b_dna"]]
\n
+
\nMinor Disabilities: [active2.fields["mi_dis"]]
+
\nDetails: [active2.fields["mi_dis_d"]]
\n
+
\nMajor Disabilities: [active2.fields["ma_dis"]]
+
\nDetails: [active2.fields["ma_dis_d"]]
\n
+
\nAllergies: [active2.fields["alg"]]
+
\nDetails: [active2.fields["alg_d"]]
\n
+
\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
+
\nDetails: [active2.fields["cdi_d"]]
\n
+
\nImportant Notes:
+
\n\t[active2.fields["notes"]]
\n
+
\n
+ Comments/Log
"}
+ for(var/c in active2.fields["comments"])
+ P.info += "[c["header"]]
[c["text"]]
"
+ else
+ P.info += "Medical Record Lost!
"
+ P.info += ""
+ P.name = "paper - 'Medical Record: [active1.fields["name"]]'"
+ printing = FALSE
+ SStgui.update_uis(src)
+
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger, virus
+ */
+/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
/obj/machinery/computer/med_data/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
@@ -554,4 +500,7 @@
icon_keyboard = "laptop_key"
icon_screen = "medlaptop"
circuit = /obj/item/weapon/circuitboard/med_data/laptop
- density = 0
\ No newline at end of file
+ density = 0
+
+#undef FIELD
+#undef MED_FIELD
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index ad77b32cc0..b1c6d68615 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -82,29 +82,30 @@
if(occupant == user && !user.stat)
go_out()
+/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user)
+ tgui_interact(user)
+
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user)
- ui_interact(user)
-
- /**
- * The ui_interact proc is used to open and update Nano UIs
- * If ui_interact is not used then the UI will not update correctly
- * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
- *
- * @param user /mob The mob who is interacting with this ui
- * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
- *
- * @return nothing
- */
-/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
-
- if(user == occupant || user.stat)
+ if(user == occupant)
return
+ if(panel_open)
+ to_chat(usr, "Close the maintenance panel first.")
+ return
+
+ tgui_interact(user)
+
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Cryo", "Cryo Cell") // 520, 470
+ ui.open()
+
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user)
// this is the data which will be sent to the ui
var/data[0]
data["isOperating"] = on
- data["hasOccupant"] = occupant ? 1 : 0
+ data["hasOccupant"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -127,14 +128,7 @@
else if(air_contents.temperature > 225)
data["cellTemperatureStatus"] = "average"
- data["isBeakerLoaded"] = beaker ? 1 : 0
- /* // Removing beaker contents list from front-end, replacing with a total remaining volume
- var beakerContents[0]
- if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
- data["beakerContents"] = beakerContents
- */
+ data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["beakerLabel"] = null
data["beakerVolume"] = 0
if(beaker)
@@ -143,47 +137,33 @@
for(var/datum/reagent/R in beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.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, "cryo.tmpl", "Cryo Cell Control System", 520, 410)
- // 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)
+ return data
-/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
- if(usr == occupant)
- return 0 // don't update UIs attached to this object
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params)
+ if(..() || usr == occupant)
+ return TRUE
- if(..())
- return 0 // don't update UIs attached to this object
-
- if(href_list["switchOn"])
- on = 1
- update_icon()
-
- if(href_list["switchOff"])
- on = 0
- update_icon()
-
- if(href_list["ejectBeaker"])
- if(beaker)
- beaker.loc = get_step(src.loc, SOUTH)
- beaker = null
+ switch(action)
+ if("switchOn")
+ on = 1
update_icon()
-
- if(href_list["ejectOccupant"])
- if(!occupant || isslime(usr) || ispAI(usr))
- return 0 // don't update UIs attached to this object
- go_out()
+ if("switchOff")
+ on = 0
+ update_icon()
+ if("ejectBeaker")
+ if(beaker)
+ beaker.loc = get_step(src.loc, SOUTH)
+ beaker = null
+ update_icon()
+ if("ejectOccupant")
+ if(!occupant || isslime(usr) || ispAI(usr))
+ return FALSE // don't update UIs attached to this object
+ go_out()
+ else
+ return FALSE
add_fingerprint(usr)
- return 1 // update UIs attached to this object
+ return TRUE
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob)
if(istype(G, /obj/item/weapon/reagent_containers/glass))
@@ -195,6 +175,7 @@
user.drop_item()
G.loc = src
user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!")
+ SStgui.update_uis(src)
update_icon()
else if(istype(G, /obj/item/weapon/grab))
var/obj/item/weapon/grab/grab = G
@@ -208,7 +189,7 @@
var/mob/M = grab.affecting
qdel(grab)
put_mob(M)
-
+
return
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance
@@ -292,7 +273,9 @@
occupant = null
current_heat_capacity = initial(current_heat_capacity)
update_use_power(USE_POWER_IDLE)
+ SStgui.update_uis(src)
return
+
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob)
if(stat & (NOPOWER|BROKEN))
to_chat(usr, "The cryo cell is not functioning.")
@@ -326,6 +309,7 @@
// M.metabslow = 1
add_fingerprint(usr)
update_icon()
+ SStgui.update_uis(src)
return 1
/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject()
diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm
index b7b5628dcd..5e93f2d031 100644
--- a/code/game/objects/items/weapons/cosmetics.dm
+++ b/code/game/objects/items/weapons/cosmetics.dm
@@ -97,17 +97,16 @@
w_class = ITEMSIZE_TINY
icon = 'icons/obj/items.dmi'
icon_state = "trinketbox"
- var/list/ui_users = list()
+ var/datum/tgui_module/appearance_changer/mirror/coskit/M
+
+/obj/item/weapon/makeover/Initialize()
+ . = ..()
+ M = new(src, null)
/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob)
if(ishuman(user))
to_chat(user, "You flip open \the [src] and begin to adjust your appearance.")
- var/datum/nano_module/appearance_changer/AC = ui_users[user]
- if(!AC)
- AC = new(src, user)
- AC.name = "SalonPro Porta-Makeover Deluxe™"
- ui_users[user] = AC
- AC.ui_interact(user)
+ M.tgui_interact(user)
var/mob/living/carbon/human/H = user
var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES]
if(istype(E))
diff --git a/code/game/objects/structures/ghost_pods/human.dm b/code/game/objects/structures/ghost_pods/human.dm
index 2eda9b6c37..246c4b3e65 100644
--- a/code/game/objects/structures/ghost_pods/human.dm
+++ b/code/game/objects/structures/ghost_pods/human.dm
@@ -121,7 +121,7 @@
H.adjustBruteLoss(rand(1,20))
if(allow_appearance_change)
- H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1)
+ H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 1)
visible_message("\The [src] [pick("gurgles", "seizes", "clangs")] before releasing \the [H]!")
@@ -241,6 +241,6 @@
H.adjustBruteLoss(rand(1,20))
if(allow_appearance_change)
- H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1)
+ H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 1)
visible_message("\The [src] [pick("gurgles", "seizes", "clangs")] before releasing \the [H]!")
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 4705b39276..1473c8fe90 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -7,10 +7,11 @@
density = 0
anchored = 1
var/shattered = 0
- var/list/ui_users = list()
var/glass = 1
+ var/datum/tgui_module/appearance_changer/mirror/M
/obj/structure/mirror/New(var/loc, var/dir, var/building = 0, mob/user as mob)
+ M = new(src, null)
if(building)
glass = 0
icon_state = "mirror_frame"
@@ -18,17 +19,16 @@
pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0
return
+/obj/structure/mirror/Destroy()
+ QDEL_NULL(M)
+ . = ..()
+
/obj/structure/mirror/attack_hand(mob/user as mob)
if(!glass) return
if(shattered) return
if(ishuman(user))
- var/datum/nano_module/appearance_changer/AC = ui_users[user]
- if(!AC)
- AC = new(src, user)
- AC.name = "SalonPro Nano-Mirror™"
- ui_users[user] = AC
- AC.ui_interact(user)
+ M.tgui_interact(user)
/obj/structure/mirror/proc/shatter()
if(!glass) return
diff --git a/code/modules/admin/verbs/change_appearance.dm b/code/modules/admin/verbs/change_appearance.dm
index e547d03f84..2f7166a2c0 100644
--- a/code/modules/admin/verbs/change_appearance.dm
+++ b/code/modules/admin/verbs/change_appearance.dm
@@ -9,7 +9,7 @@
if(!H) return
log_and_message_admins("is altering the appearance of [H].")
- H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0, state = admin_state)
+ H.change_appearance(APPEARANCE_ALL, usr, check_species_whitelist = 0, state = GLOB.tgui_admin_state)
feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/change_human_appearance_self()
@@ -29,10 +29,10 @@
switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel"))
if("Yes")
log_and_message_admins("has allowed [H] to change [T.his] appearance, without whitelisting of races.")
- H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0)
+ H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 0)
if("No")
log_and_message_admins("has allowed [H] to change [T.his] appearance, with whitelisting of races.")
- H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1)
+ H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 1)
feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/editappear()
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 6f6ab27934..1477d4c532 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -436,3 +436,37 @@
/datum/asset/nanoui/send(client)
send_asset_list(client, common)
+
+
+//Pill sprites for UIs
+/datum/asset/chem_master
+ var/assets = list()
+ var/verify = FALSE
+
+/datum/asset/chem_master/register()
+ for(var/i = 1 to 24)
+ assets["pill[i].png"] = icon('icons/obj/chemical.dmi', "pill[i]")
+
+ for(var/i = 1 to 4)
+ assets["bottle-[i].png"] = icon('icons/obj/chemical.dmi', "bottle-[i]")
+
+ for(var/asset_name in assets)
+ register_asset(asset_name, assets[asset_name])
+
+/datum/asset/chem_master/send(client)
+ send_asset_list(client, assets, verify)
+
+//Cloning pod sprites for UIs
+/datum/asset/cloning
+ var/assets = list()
+ var/verify = FALSE
+
+/datum/asset/cloning/register()
+ assets["pod_idle.gif"] = icon('icons/obj/cloning.dmi', "pod_idle")
+ assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning")
+ assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess")
+ for(var/asset_name in assets)
+ register_asset(asset_name, assets[asset_name])
+
+/datum/asset/cloning/send(client)
+ send_asset_list(client, assets, verify)
diff --git a/code/modules/food/kitchen/smartfridge.dm b/code/modules/food/kitchen/smartfridge.dm
index d76cb754ff..e527df91b9 100644
--- a/code/modules/food/kitchen/smartfridge.dm
+++ b/code/modules/food/kitchen/smartfridge.dm
@@ -213,17 +213,18 @@
is_off = "-off"
// Fridge contents
- switch(contents.len)
- if(0)
- add_overlay("empty[is_off]")
- if(1 to 2)
- add_overlay("[icon_contents]-1[is_off]")
- if(3 to 5)
- add_overlay("[icon_contents]-2[is_off]")
- if(6 to 8)
- add_overlay("[icon_contents]-3[is_off]")
- else
- add_overlay("[icon_contents]-4[is_off]")
+ if(contents)
+ switch(contents.len)
+ if(0)
+ add_overlay("empty[is_off]")
+ if(1 to 2)
+ add_overlay("[icon_contents]-1[is_off]")
+ if(3 to 5)
+ add_overlay("[icon_contents]-2[is_off]")
+ if(6 to 8)
+ add_overlay("[icon_contents]-3[is_off]")
+ else
+ add_overlay("[icon_contents]-4[is_off]")
// Fridge top
var/image/top = image(icon, "[icon_base]-top")
@@ -241,7 +242,6 @@
user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].")
playsound(src, O.usesound, 50, 1)
update_icon()
- SSnanoui.update_uis(src)
return
if(wrenchable && default_unfasten_wrench(user, O, 20))
@@ -261,7 +261,6 @@
stock(O)
user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
-
else if(istype(O, /obj/item/weapon/storage/bag))
var/obj/item/weapon/storage/bag/P = O
var/plants_loaded = 0
@@ -307,11 +306,11 @@
var/datum/stored_item/item = new/datum/stored_item(src, O.type, O.name)
item.add_product(O)
item_records.Add(item)
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/smartfridge/proc/vend(datum/stored_item/I)
I.get_product(get_turf(src))
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/smartfridge/attack_ai(mob/user as mob)
attack_hand(user)
@@ -320,66 +319,59 @@
if(stat & (NOPOWER|BROKEN))
return
wires.Interact(user)
- ui_interact(user)
+ tgui_interact(user)
-/*******************
-* SmartFridge Menu
-********************/
+/obj/machinery/smartfridge/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "SmartVend", name)
+ ui.set_autoupdate(FALSE)
+ ui.open()
-/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/smartfridge/tgui_data(mob/user)
+ . = list()
- var/data[0]
- data["contents"] = null
- data["electrified"] = seconds_electrified > 0
- data["shoot_inventory"] = shoot_inventory
- data["locked"] = locked
- data["secure"] = is_secure
-
- var/list/items[0]
- for (var/i=1 to length(item_records))
+ var/list/items = list()
+ for(var/i=1 to length(item_records))
var/datum/stored_item/I = item_records[i]
var/count = I.get_amount()
if(count > 0)
- items.Add(list(list("display_name" = html_encode(capitalize(I.item_name)), "vend" = i, "quantity" = count)))
+ items.Add(list(list("name" = html_encode(capitalize(I.item_name)), "index" = i, "amount" = count)))
- if(items.len > 0)
- data["contents"] = items
+ .["contents"] = items
+ .["name"] = name
+ .["locked"] = locked
+ .["secure"] = is_secure
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500)
- ui.set_initial_data(data)
- ui.open()
+/obj/machinery/smartfridge/tgui_act(action, params)
+ if(..())
+ return TRUE
-/obj/machinery/smartfridge/Topic(href, href_list)
- if(..()) return 0
+ add_fingerprint(usr)
+ switch(action)
+ if("Release")
+ var/amount = 0
+ if(params["amount"])
+ amount = params["amount"]
+ else
+ amount = input("How many items?", "How many items would you like to take out?", 1) as num|null
+
+ if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src))
+ return FALSE
+
+ var/index = text2num(params["index"])
+ var/datum/stored_item/I = item_records[index]
+ var/count = I.get_amount()
- var/mob/user = usr
- var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
+ // Sanity check, there are probably ways to press the button when it shouldn't be possible.
+ if(count > 0)
+ if((count - amount) < 0)
+ amount = count
+ for(var/i = 1 to amount)
+ vend(I)
- src.add_fingerprint(user)
-
- if(href_list["close"])
- user.unset_machine()
- ui.close()
- return 0
-
- if(href_list["vend"])
- var/index = text2num(href_list["vend"])
- var/amount = text2num(href_list["amount"])
- var/datum/stored_item/I = item_records[index]
- var/count = I.get_amount()
-
- // Sanity check, there are probably ways to press the button when it shouldn't be possible.
- if(count > 0)
- if((count - amount) < 0)
- amount = count
- for(var/i = 1 to amount)
- vend(I)
-
- return 1
- return 0
+ return TRUE
+ return FALSE
/obj/machinery/smartfridge/proc/throw_item()
var/obj/throw_item = null
@@ -398,17 +390,18 @@
spawn(0)
throw_item.throw_at(target,16,3,src)
src.visible_message("[src] launches [throw_item.name] at [target.name]!")
+ SStgui.update_uis(src)
return 1
/************************
* Secure SmartFridges
*************************/
-/obj/machinery/smartfridge/secure/Topic(href, href_list)
+/obj/machinery/smartfridge/secure/tgui_act(action, params)
if(stat & (NOPOWER|BROKEN))
- return 0
+ return TRUE
if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf)))
- if(!allowed(usr) && !emagged && locked != -1 && href_list["vend"])
+ if(!allowed(usr) && !emagged && locked != -1 && action == "Release")
to_chat(usr, "Access denied.")
- return 0
+ return TRUE
return ..()
diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm
index fe1c6ca632..19be7d0865 100644
--- a/code/modules/mob/living/carbon/human/appearance.dm
+++ b/code/modules/mob/living/carbon/human/appearance.dm
@@ -1,7 +1,12 @@
-/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/datum/topic_state/state = default_state)
- var/datum/nano_module/appearance_changer/AC = new(location, src, check_species_whitelist, species_whitelist, species_blacklist)
+/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR,
+ var/mob/user = src,
+ var/check_species_whitelist = 1,
+ var/list/species_whitelist = list(),
+ var/list/species_blacklist = list(),
+ var/datum/tgui_state/state = GLOB.tgui_self_state)
+ var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist)
AC.flags = flags
- AC.ui_interact(user, state = state)
+ AC.tgui_interact(user, custom_state = state)
/mob/living/carbon/human/proc/change_species(var/new_species)
if(!new_species)
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 2bb96bc608..1f32765447 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -114,7 +114,7 @@
return get_turf(src)
-/mob/proc/say_test(var/text)
+/proc/say_test(var/text)
var/ending = copytext(text, length(text))
if(ending == "?")
return "1"
diff --git a/code/modules/nano/modules/human_appearance.dm b/code/modules/nano/modules/human_appearance.dm
deleted file mode 100644
index fd2ed900f2..0000000000
--- a/code/modules/nano/modules/human_appearance.dm
+++ /dev/null
@@ -1,191 +0,0 @@
-/datum/nano_module/appearance_changer
- name = "Appearance Editor"
- var/flags = APPEARANCE_ALL_HAIR
- var/mob/living/carbon/human/owner = null
- var/list/valid_species = list()
- var/list/valid_hairstyles = list()
- var/list/valid_facial_hairstyles = list()
-
- var/check_whitelist
- var/list/whitelist
- var/list/blacklist
-
-/datum/nano_module/appearance_changer/New(var/location, var/mob/living/carbon/human/H, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list())
- ..()
- owner = H
- src.check_whitelist = check_species_whitelist
- src.whitelist = species_whitelist
- src.blacklist = species_blacklist
-
-/datum/nano_module/appearance_changer/Topic(ref, href_list, var/datum/topic_state/state = default_state)
- if(..())
- return 1
-
- if(href_list["race"])
- if(can_change(APPEARANCE_RACE) && (href_list["race"] in valid_species))
- if(owner.change_species(href_list["race"]))
- cut_and_generate_data()
- return 1
- if(href_list["gender"])
- if(can_change(APPEARANCE_GENDER) && (href_list["gender"] in get_genders()))
- if(owner.change_gender(href_list["gender"]))
- cut_and_generate_data()
- return 1
- if(href_list["gender_id"])
- if(can_change(APPEARANCE_GENDER) && (href_list["gender_id"] in all_genders_define_list))
- owner.identifying_gender = href_list["gender_id"]
- return 1
- if(href_list["skin_tone"])
- if(can_change_skin_tone())
- var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -owner.s_tone + 35) as num|null
- if(isnum(new_s_tone) && can_still_topic(state))
- new_s_tone = 35 - max(min( round(new_s_tone), 220),1)
- return owner.change_skin_tone(new_s_tone)
- if(href_list["skin_color"])
- if(can_change_skin_color())
- var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null
- if(new_skin && can_still_topic(state))
- var/r_skin = hex2num(copytext(new_skin, 2, 4))
- var/g_skin = hex2num(copytext(new_skin, 4, 6))
- var/b_skin = hex2num(copytext(new_skin, 6, 8))
- if(owner.change_skin_color(r_skin, g_skin, b_skin))
- update_dna()
- return 1
- if(href_list["hair"])
- if(can_change(APPEARANCE_HAIR) && (href_list["hair"] in valid_hairstyles))
- if(owner.change_hair(href_list["hair"]))
- update_dna()
- return 1
- if(href_list["hair_color"])
- if(can_change(APPEARANCE_HAIR_COLOR))
- var/new_hair = input("Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null
- if(new_hair && can_still_topic(state))
- var/r_hair = hex2num(copytext(new_hair, 2, 4))
- var/g_hair = hex2num(copytext(new_hair, 4, 6))
- var/b_hair = hex2num(copytext(new_hair, 6, 8))
- if(owner.change_hair_color(r_hair, g_hair, b_hair))
- update_dna()
- return 1
- if(href_list["facial_hair"])
- if(can_change(APPEARANCE_FACIAL_HAIR) && (href_list["facial_hair"] in valid_facial_hairstyles))
- if(owner.change_facial_hair(href_list["facial_hair"]))
- update_dna()
- return 1
- if(href_list["facial_hair_color"])
- if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
- var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null
- if(new_facial && can_still_topic(state))
- var/r_facial = hex2num(copytext(new_facial, 2, 4))
- var/g_facial = hex2num(copytext(new_facial, 4, 6))
- var/b_facial = hex2num(copytext(new_facial, 6, 8))
- if(owner.change_facial_hair_color(r_facial, g_facial, b_facial))
- update_dna()
- return 1
- if(href_list["eye_color"])
- if(can_change(APPEARANCE_EYE_COLOR))
- var/new_eyes = input("Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null
- if(new_eyes && can_still_topic(state))
- var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
- var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
- var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
- if(owner.change_eye_color(r_eyes, g_eyes, b_eyes))
- update_dna()
- return 1
-
- return 0
-
-/datum/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
-
- if(!owner || !owner.species)
- return
-
- generate_data(check_whitelist, whitelist, blacklist)
- var/list/data = host.initial_data()
-
- data["specimen"] = owner.species.name
- data["gender"] = owner.gender
- data["gender_id"] = owner.identifying_gender
- data["change_race"] = can_change(APPEARANCE_RACE)
- if(data["change_race"])
- var/species[0]
- for(var/specimen in valid_species)
- species[++species.len] = list("specimen" = specimen)
- data["species"] = species
-
- data["change_gender"] = can_change(APPEARANCE_GENDER)
- if(data["change_gender"])
- var/genders[0]
- for(var/gender in get_genders())
- genders[++genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
- data["genders"] = genders
- var/id_genders[0]
- for(var/gender in all_genders_define_list)
- id_genders[++id_genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
- data["id_genders"] = id_genders
-
-
- data["change_skin_tone"] = can_change_skin_tone()
- data["change_skin_color"] = can_change_skin_color()
- data["change_eye_color"] = can_change(APPEARANCE_EYE_COLOR)
- data["change_hair"] = can_change(APPEARANCE_HAIR)
- if(data["change_hair"])
- var/hair_styles[0]
- for(var/hair_style in valid_hairstyles)
- hair_styles[++hair_styles.len] = list("hairstyle" = hair_style)
- data["hair_styles"] = hair_styles
- data["hair_style"] = owner.h_style
-
- data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
- if(data["change_facial_hair"])
- var/facial_hair_styles[0]
- for(var/facial_hair_style in valid_facial_hairstyles)
- facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_style)
- data["facial_hair_styles"] = facial_hair_styles
- data["facial_hair_style"] = owner.f_style
-
- data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR)
- data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "appearance_changer.tmpl", "[src]", 800, 450, state = state)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(1)
-
-/datum/nano_module/appearance_changer/proc/update_dna()
- if(owner && (flags & APPEARANCE_UPDATE_DNA))
- owner.update_dna()
-
-/datum/nano_module/appearance_changer/proc/can_change(var/flag)
- return owner && (flags & flag)
-
-/datum/nano_module/appearance_changer/proc/can_change_skin_tone()
- return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_TONE
-
-/datum/nano_module/appearance_changer/proc/can_change_skin_color()
- return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_COLOR
-
-/datum/nano_module/appearance_changer/proc/cut_and_generate_data()
- // Making the assumption that the available species remain constant
- valid_facial_hairstyles.Cut()
- valid_facial_hairstyles.Cut()
- generate_data()
-
-/datum/nano_module/appearance_changer/proc/generate_data()
- if(!owner)
- return
- if(!valid_species.len)
- valid_species = owner.generate_valid_species(check_whitelist, whitelist, blacklist)
- if(!valid_hairstyles.len || !valid_facial_hairstyles.len)
- valid_hairstyles = owner.generate_valid_hairstyles(check_gender = 0)
- valid_facial_hairstyles = owner.generate_valid_facial_hairstyles()
-
-
-/datum/nano_module/appearance_changer/proc/get_genders()
- var/datum/species/S = owner.species
- var/list/possible_genders = S.genders
- if(!owner.internal_organs_by_name["cell"])
- return possible_genders
- possible_genders = possible_genders.Copy()
- possible_genders |= NEUTER
- return possible_genders
\ No newline at end of file
diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm
index cf1f251307..3d12e68534 100644
--- a/code/modules/paperwork/filingcabinet.dm
+++ b/code/modules/paperwork/filingcabinet.dm
@@ -36,12 +36,8 @@
to_chat(user, "You put [P] in [src].")
user.drop_item()
P.loc = src
- icon_state = "[initial(icon_state)]-open"
- flick("[initial(icon_state)]-open",src)
- playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
- sleep(40)
- icon_state = initial(icon_state)
- updateUsrDialog()
+ open_animation()
+ SStgui.update_uis(src)
else if(P.is_wrench())
playsound(src, P.usesound, 50, 1)
anchored = !anchored
@@ -65,20 +61,12 @@
to_chat(user, "\The [src] is empty.")
return
- user.set_machine(src)
- var/dat = ""
- for(var/obj/item/P in src)
- dat += "| [P.name] |
"
- dat += "
"
- user << browse("[name][dat]", "window=filingcabinet;size=350x300")
-
- return
+ tgui_interact(user)
/obj/structure/filingcabinet/attack_tk(mob/user)
if(anchored)
- attack_self_tk(user)
- else
- ..()
+ return attack_self_tk(user)
+ return ..()
/obj/structure/filingcabinet/attack_self_tk(mob/user)
if(contents.len)
@@ -91,20 +79,46 @@
return
to_chat(user, "You find nothing in [src].")
-/obj/structure/filingcabinet/Topic(href, href_list)
- if(href_list["retrieve"])
- usr << browse("", "window=filingcabinet") // Close the menu
+/obj/structure/filingcabinet/tgui_state(mob/user)
+ return GLOB.tgui_physical_state
- //var/retrieveindex = text2num(href_list["retrieve"])
- var/obj/item/P = locate(href_list["retrieve"])//contents[retrieveindex]
- if(istype(P) && (P.loc == src) && src.Adjacent(usr))
- usr.put_in_hands(P)
- updateUsrDialog()
- flick("[initial(icon_state)]-open",src)
- playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
- spawn(0)
- sleep(20)
- icon_state = initial(icon_state)
+/obj/structure/filingcabinet/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "FileCabinet", name)
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/obj/structure/filingcabinet/tgui_data(mob/user)
+ var/list/data = list()
+
+ data["contents"] = list()
+ for(var/obj/item/P in src)
+ data["contents"].Add(list(list(
+ "name" = P.name,
+ "ref" = "\ref[P]",
+ )))
+
+ return data
+
+/obj/structure/filingcabinet/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ switch(action)
+ if("retrieve")
+ var/obj/item/P = locate(params["ref"])
+ if(istype(P) && (P.loc == src) && usr.Adjacent(src))
+ usr.put_in_hands(P)
+ open_animation()
+ SStgui.update_uis(src)
+
+/obj/structure/filingcabinet/proc/open_animation()
+ flick("[initial(icon_state)]-open",src)
+ playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
+ spawn(0)
+ sleep(20)
+ icon_state = initial(icon_state)
/*
* Security Record Cabinets
@@ -112,7 +126,6 @@
/obj/structure/filingcabinet/security
var/virgin = 1
-
/obj/structure/filingcabinet/security/proc/populate()
if(virgin)
for(var/datum/data/record/G in data_core.general)
diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm
index b6a934a60d..8598d54115 100644
--- a/code/modules/reagents/Chemistry-Holder.dm
+++ b/code/modules/reagents/Chemistry-Holder.dm
@@ -144,6 +144,13 @@
crash_with("[my_atom] attempted to add a reagent called '[id]' which doesn't exist. ([usr])")
return 0
+/datum/reagents/proc/isolate_reagent(reagent)
+ for(var/A in reagent_list)
+ var/datum/reagent/R = A
+ if(R.id != reagent)
+ del_reagent(R.id)
+ update_total()
+
/datum/reagents/proc/remove_reagent(var/id, var/amount, var/safety = 0)
if(!isnum(amount))
return 0
diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm
index 7a337c7666..39ddc4ae04 100644
--- a/code/modules/reagents/Chemistry-Machinery.dm
+++ b/code/modules/reagents/Chemistry-Machinery.dm
@@ -2,6 +2,14 @@
#define LIQUID 2
#define GAS 3
+#define MAX_PILL_SPRITE 24 //max icon state of the pill sprites
+#define MAX_BOTTLE_SPRITE 4 //max icon state of the pill sprites
+#define MAX_MULTI_AMOUNT 20 // Max number of pills/patches that can be made at once
+#define MAX_UNITS_PER_PILL 60 // Max amount of units in a pill
+#define MAX_UNITS_PER_PATCH 60 // Max amount of units in a patch
+#define MAX_UNITS_PER_BOTTLE 60 // Max amount of units in a bottle (it's volume)
+#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
+
@@ -24,11 +32,11 @@
var/condi = 0
var/useramount = 15 // Last used amount
var/pillamount = 10
- var/bottlesprite = "1"
- var/pillsprite = "1"
+ var/list/bottle_styles
+ var/bottlesprite = 1
+ var/pillsprite = 1
var/max_pill_count = 20
- var/tab = "home"
- var/analyze_data[0]
+ var/printing = FALSE
flags = OPENCONTAINER
clicksound = "button"
@@ -48,6 +56,9 @@
qdel(src)
return
+/obj/machinery/chem_master/update_icon()
+ icon_state = "mixer[beaker ? "1" : "0"]"
+
/obj/machinery/chem_master/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob)
if(istype(B, /obj/item/weapon/reagent_containers/glass) || istype(B, /obj/item/weapon/reagent_containers/food))
@@ -59,7 +70,7 @@
user.drop_item()
B.loc = src
to_chat(user, "You add \the [B] to the machine.")
- icon_state = "mixer1"
+ update_icon()
else if(istype(B, /obj/item/weapon/storage/pill_bottle))
@@ -85,247 +96,420 @@
if(stat & BROKEN)
return
user.set_machine(src)
- ui_interact(user)
+ tgui_interact(user)
+
+/obj/machinery/chem_master/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/chem_master),
+ )
+
+/obj/machinery/chem_master/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ChemMaster", name)
+ ui.open()
/**
* Display the NanoUI window for the chem master.
*
* See NanoUI documentation for details.
*/
-/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
+/obj/machinery/chem_master/tgui_data(mob/user)
var/list/data = list()
- data["tab"] = tab
+
data["condi"] = condi
+ data["loaded_pill_bottle"] = !!loaded_pill_bottle
if(loaded_pill_bottle)
- data["pillBottle"] = list("total" = loaded_pill_bottle.contents.len, "max" = loaded_pill_bottle.max_storage_space)
- else
- data["pillBottle"] = null
+ data["loaded_pill_bottle_name"] = loaded_pill_bottle.name
+ data["loaded_pill_bottle_contents_len"] = loaded_pill_bottle.contents.len
+ data["loaded_pill_bottle_storage_slots"] = loaded_pill_bottle.max_storage_space
+ data["beaker"] = !!beaker
if(beaker)
- var/datum/reagents/R = beaker.reagents
- var/ui_reagent_beaker_list[0]
- for(var/datum/reagent/G in R.reagent_list)
- ui_reagent_beaker_list[++ui_reagent_beaker_list.len] = list("name" = G.name, "volume" = G.volume, "description" = G.description, "id" = G.id)
+ var/list/beaker_reagents_list = list()
+ data["beaker_reagents"] = beaker_reagents_list
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ beaker_reagents_list[++beaker_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "description" = R.description, "id" = R.id)
- data["beaker"] = list("total_volume" = R.total_volume, "reagent_list" = ui_reagent_beaker_list)
- else
- data["beaker"] = null
-
- if(reagents.total_volume)
- var/ui_reagent_list[0]
- for(var/datum/reagent/N in reagents.reagent_list)
- ui_reagent_list[++ui_reagent_list.len] = list("name" = N.name, "volume" = N.volume, "description" = N.description, "id" = N.id)
-
- data["reagents"] = list("total_volume" = reagents.total_volume, "reagent_list" = ui_reagent_list)
- else
- data["reagents"] = null
+ var/list/buffer_reagents_list = list()
+ data["buffer_reagents"] = buffer_reagents_list
+ for(var/datum/reagent/R in reagents.reagent_list)
+ buffer_reagents_list[++buffer_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "id" = R.id, "description" = R.description)
+ data["pillsprite"] = pillsprite
+ data["bottlesprite"] = bottlesprite
data["mode"] = mode
+ data["printing"] = printing
- if(analyze_data)
- data["analyzeData"] = list("name" = analyze_data["name"], "desc" = analyze_data["desc"], "blood_type" = analyze_data["blood_type"], "blood_DNA" = analyze_data["blood_DNA"])
- else
- data["analyzeData"] = null
+ // Transfer modal information if there is one
+ data["modal"] = tgui_modal_data(src)
- data["pillSprite"] = pillsprite
- data["bottleSprite"] = bottlesprite
+ return data
- var/P[24] //how many pill sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png
- for(var/i = 1 to P.len)
- P[i] = i
- data["pillSpritesAmount"] = P
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/chem_master/proc/tgui_act_modal(action, params, datum/tgui/ui, datum/tgui_state/state)
+ . = TRUE
+ var/id = params["id"] // The modal's ID
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_OPEN)
+ switch(id)
+ if("analyze")
+ var/idx = text2num(arguments["idx"]) || 0
+ var/from_beaker = text2num(arguments["beaker"]) || FALSE
+ var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
+ if(idx < 1 || idx > length(reagent_list))
+ return
- data["bottleSpritesAmount"] = list(1, 2, 3, 4) //how many bottle sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png
+ var/datum/reagent/R = reagent_list[idx]
+ var/list/result = list("idx" = idx, "name" = R.name, "desc" = R.description)
+ if(!condi && istype(R, /datum/reagent/blood))
+ var/datum/reagent/blood/B = R
+ result["blood_type"] = B.data["blood_type"]
+ result["blood_dna"] = B.data["blood_DNA"]
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "chem_master.tmpl", src.name, 575, 400)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(5)
+ arguments["analysis"] = result
+ tgui_modal_message(src, id, "", null, arguments)
+ // if("change_pill_bottle_style")
+ // if(!loaded_pill_bottle)
+ // return
+ // if(!pill_bottle_wrappers)
+ // pill_bottle_wrappers = list(
+ // "CLEAR" = "Default",
+ // COLOR_RED = "Red",
+ // COLOR_GREEN = "Green",
+ // COLOR_PALE_BTL_GREEN = "Pale green",
+ // COLOR_BLUE = "Blue",
+ // COLOR_CYAN_BLUE = "Light blue",
+ // COLOR_TEAL = "Teal",
+ // COLOR_YELLOW = "Yellow",
+ // COLOR_ORANGE = "Orange",
+ // COLOR_PINK = "Pink",
+ // COLOR_MAROON = "Brown"
+ // )
+ // var/current = pill_bottle_wrappers[loaded_pill_bottle.wrapper_color] || "Default"
+ // tgui_modal_choice(src, id, "Please select a pill bottle wrapper:", null, arguments, current, pill_bottle_wrappers)
+ if("addcustom")
+ if(!beaker || !beaker.reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please enter the amount to transfer to buffer:", null, arguments, useramount)
+ if("removecustom")
+ if(!reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please enter the amount to transfer to [mode ? "beaker" : "disposal"]:", null, arguments, useramount)
+ if("create_condi_pack")
+ if(!condi || !reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please name your new condiment pack:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN)
+ if("create_pill")
+ if(condi || !reagents.total_volume)
+ return
+ var/num = round(text2num(arguments["num"] || 1))
+ if(!num)
+ return
+ arguments["num"] = num
+ var/amount_per_pill = CLAMP(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL)
+ var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)"
+ var/pills_text = num == 1 ? "new pill" : "[num] new pills"
+ tgui_modal_input(src, id, "Please name your [pills_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
+ if("create_pill_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please enter the amount of pills to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
+ if("change_pill_style")
+ var/list/choices = list()
+ for(var/i = 1 to MAX_PILL_SPRITE)
+ choices += "pill[i].png"
+ tgui_modal_bento(src, id, "Please select the new style for pills:", null, arguments, pillsprite, choices)
+ if("create_patch")
+ if(condi || !reagents.total_volume)
+ return
+ var/num = round(text2num(arguments["num"] || 1))
+ if(!num)
+ return
+ arguments["num"] = num
+ var/amount_per_patch = CLAMP(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH)
+ var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)"
+ var/patches_text = num == 1 ? "new patch" : "[num] new patches"
+ tgui_modal_input(src, id, "Please name your [patches_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
+ if("create_patch_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please enter the amount of patches to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
+ if("create_bottle")
+ if(condi || !reagents.total_volume)
+ return
+ var/num = round(text2num(arguments["num"] || 1))
+ if(!num)
+ return
+ arguments["num"] = num
+ var/amount_per_bottle = CLAMP(reagents.total_volume / num, 0, MAX_UNITS_PER_BOTTLE)
+ var/default_name = "[reagents.get_master_reagent_name()]"
+ var/bottles_text = num == 1 ? "new bottle" : "[num] new bottles"
+ tgui_modal_input(src, id, "Please name your [bottles_text] ([amount_per_bottle]u in bottle):", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
+ if("create_bottle_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_modal_input(src, id, "Please enter the amount of bottles to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
+ if("change_bottle_style")
+ var/list/choices = list()
+ for(var/i = 1 to MAX_BOTTLE_SPRITE)
+ choices += "bottle-[i].png"
+ tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, choices)
+ else
+ return FALSE
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ // if("change_pill_bottle_style")
+ // if(!pill_bottle_wrappers || !loaded_pill_bottle) // wat?
+ // return
+ // var/color = "CLEAR"
+ // for(var/col in pill_bottle_wrappers)
+ // var/col_name = pill_bottle_wrappers[col]
+ // if(col_name == answer)
+ // color = col
+ // break
+ // if(length(color) && color != "CLEAR")
+ // loaded_pill_bottle.wrapper_color = color
+ // loaded_pill_bottle.apply_wrap()
+ // else
+ // loaded_pill_bottle.wrapper_color = null
+ // loaded_pill_bottle.cut_overlays()
+ if("addcustom")
+ var/amount = isgoodnumber(text2num(answer))
+ if(!amount || !arguments["id"])
+ return
+ tgui_act("add", list("id" = arguments["id"], "amount" = amount), ui, state)
+ if("removecustom")
+ var/amount = isgoodnumber(text2num(answer))
+ if(!amount || !arguments["id"])
+ return
+ tgui_act("remove", list("id" = arguments["id"], "amount" = amount), ui, state)
+ if("create_condi_pack")
+ if(!condi || !reagents.total_volume)
+ return
+ if(!length(answer))
+ answer = reagents.get_master_reagent_name()
+ var/obj/item/weapon/reagent_containers/pill/P = new(loc)
+ P.name = "[answer] pack"
+ P.desc = "A small condiment pack. The label says it contains [answer]."
+ P.icon_state = "bouilloncube"//Reskinned monkey cube
+ reagents.trans_to_obj(P, 10)
+ if("create_pill")
+ if(condi || !reagents.total_volume)
+ return
+ var/count = CLAMP(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
+ if(!count)
+ return
-/obj/machinery/chem_master/Topic(href, href_list)
- if(stat & (BROKEN|NOPOWER)) return
- if(usr.stat || usr.restrained()) return
- if(!in_range(src, usr)) return
+ if(!length(answer))
+ answer = reagents.get_master_reagent_name()
+ var/amount_per_pill = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL)
+ while(count--)
+ if(reagents.total_volume <= 0)
+ to_chat(usr, "Not enough reagents to create these pills!")
+ return
- src.add_fingerprint(usr)
+ var/obj/item/weapon/reagent_containers/pill/P = new(loc)
+ P.name = "[answer] pill"
+ P.pixel_x = rand(-7, 7) // Random position
+ P.pixel_y = rand(-7, 7)
+ P.icon_state = "pill[pillsprite]"
+ if(P.icon_state in list("pill1", "pill2", "pill3", "pill4")) // if using greyscale, take colour from reagent
+ P.color = reagents.get_color()
+ reagents.trans_to_obj(P, amount_per_pill)
+ // Load the pills in the bottle if there's one loaded
+ if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.max_storage_space)
+ P.forceMove(loaded_pill_bottle)
+ if("create_pill_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state)
+ if("change_pill_style")
+ var/new_style = CLAMP(text2num(answer) || 0, 0, MAX_PILL_SPRITE)
+ if(!new_style)
+ return
+ pillsprite = new_style
+ if("create_patch")
+ if(condi || !reagents.total_volume)
+ return
+ var/count = CLAMP(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
+ if(!count)
+ return
+
+ if(!length(answer))
+ answer = reagents.get_master_reagent_name()
+ var/amount_per_patch = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH)
+ // var/is_medical_patch = chemical_safety_check(reagents)
+ while(count--)
+ if(reagents.total_volume <= 0)
+ to_chat(usr, "Not enough reagents to create these patches!")
+ return
+
+ var/obj/item/weapon/reagent_containers/pill/patch/P = new(loc)
+ P.name = "[answer] patch"
+ P.pixel_x = rand(-7, 7) // random position
+ P.pixel_y = rand(-7, 7)
+ reagents.trans_to_obj(P, amount_per_patch)
+ // if(is_medical_patch)
+ // P.instant_application = TRUE
+ // P.icon_state = "bandaid_med"
+ if("create_patch_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_act("modal_open", list("id" = "create_patch", "arguments" = list("num" = answer)), ui, state)
+ if("create_bottle")
+ if(condi || !reagents.total_volume)
+ return
+ var/count = CLAMP(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
+ if(!count)
+ return
+
+ if(!length(answer))
+ answer = reagents.get_master_reagent_name()
+ var/amount_per_bottle = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_BOTTLE)
+ while(count--)
+ if(reagents.total_volume <= 0)
+ to_chat(usr, "Not enough reagents to create these bottles!")
+ return
+ var/obj/item/weapon/reagent_containers/glass/bottle/P = new(loc)
+ P.name = "[answer] bottle"
+ P.pixel_x = rand(-7, 7) // random position
+ P.pixel_y = rand(-7, 7)
+ P.icon_state = "bottle-[bottlesprite]" || "bottle-1"
+ reagents.trans_to_obj(P, amount_per_bottle)
+ P.update_icon()
+ if("create_bottle_multiple")
+ if(condi || !reagents.total_volume)
+ return
+ tgui_act("modal_open", list("id" = "create_bottle", "arguments" = list("num" = answer)), ui, state)
+ if("change_bottle_style")
+ var/new_style = CLAMP(text2num(answer) || 0, 0, MAX_BOTTLE_SPRITE)
+ if(!new_style)
+ return
+ bottlesprite = new_style
+ else
+ return FALSE
+ else
+ return FALSE
+
+/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ if(tgui_act_modal(action, params, ui, state))
+ return TRUE
+
+ add_fingerprint(usr)
usr.set_machine(src)
- if(href_list["tab_select"])
- tab = href_list["tab_select"]
-
- if (href_list["ejectp"])
- if(loaded_pill_bottle)
- loaded_pill_bottle.forceMove(get_turf(src))
-
- if(Adjacent(usr))
- usr.put_in_hands(loaded_pill_bottle)
-
- loaded_pill_bottle = null
-
- if(beaker)
- var/datum/reagents/R = beaker.reagents
- if (tab == "analyze")
- analyze_data["name"] = href_list["name"]
- analyze_data["desc"] = href_list["desc"]
- if(!condi)
- if(href_list["name"] == "Blood")
- var/datum/reagent/blood/G
- for(var/datum/reagent/F in R.reagent_list)
- if(F.name == href_list["name"])
- G = F
- break
- analyze_data["name"] = G.name
- analyze_data["blood_type"] = G.data["blood_type"]
- analyze_data["blood_DNA"] = G.data["blood_DNA"]
-
- else if (href_list["add"])
-
- if(href_list["amount"])
- var/id = href_list["add"]
- var/amount = CLAMP((text2num(href_list["amount"])), 0, 200)
- R.trans_id_to(src, id, amount)
-
- else if (href_list["addcustom"])
-
- var/id = href_list["addcustom"]
- useramount = input("Select the amount to transfer.", 30, useramount) as num
- useramount = CLAMP(useramount, 0, 200)
- src.Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
-
- else if (href_list["remove"])
-
- if(href_list["amount"])
- var/id = href_list["remove"]
- var/amount = CLAMP((text2num(href_list["amount"])), 0, 200)
- if(mode)
- reagents.trans_id_to(beaker, id, amount)
- else
- reagents.remove_reagent(id, amount)
-
-
- else if (href_list["removecustom"])
-
- var/id = href_list["removecustom"]
- useramount = input("Select the amount to transfer.", 30, useramount) as num
- useramount = CLAMP(useramount, 0, 200)
- src.Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
-
- else if (href_list["toggle"])
+ . = TRUE
+ switch(action)
+ if("toggle")
mode = !mode
-
- else if (href_list["eject"])
- if(beaker)
- beaker.forceMove(get_turf(src))
-
- if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
- usr.put_in_hands(beaker)
-
- beaker = null
- reagents.clear_reagents()
- icon_state = "mixer0"
- else if (href_list["createpill"] || href_list["createpill_multiple"])
- var/count = 1
-
- if(reagents.total_volume/count < 1) //Sanity checking.
+ if("ejectp")
+ if(loaded_pill_bottle)
+ loaded_pill_bottle.forceMove(get_turf(src))
+ if(Adjacent(usr) && !issilicon(usr))
+ usr.put_in_hands(loaded_pill_bottle)
+ loaded_pill_bottle = null
+ if("print")
+ if(printing || condi)
return
- if (href_list["createpill_multiple"])
- count = input("Select the number of pills to make.", "Max [max_pill_count]", pillamount) as null|num
- if(!count) //Covers 0 and cancel
- return
- count = CLAMP(round(count), 1, max_pill_count) // Fix decimals input and clamp to reasonable amounts
-
- if(reagents.total_volume/count < 1) //Sanity checking.
+ var/idx = text2num(params["idx"]) || 0
+ var/from_beaker = text2num(params["beaker"]) || FALSE
+ var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
+ if(idx < 1 || idx > length(reagent_list))
return
- var/amount_per_pill = reagents.total_volume/count
- if (amount_per_pill > 60) amount_per_pill = 60
+ var/datum/reagent/R = reagent_list[idx]
- var/pill_cube = "pill"
- if(condi)//For the condimaster
- pill_cube = "cube"
+ printing = TRUE
+ visible_message("[src] rattles and prints out a sheet of paper.")
+ // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
+
+ var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
+ P.info = "Chemical Analysis
"
+ P.info += "Time of analysis: [worldtime2stationtime(world.time)]
"
+ P.info += "Chemical name: [R.name]
"
+ if(istype(R, /datum/reagent/blood))
+ var/datum/reagent/blood/B = R
+ P.info += "Description: N/A
Blood Type: [B.data["blood_type"]]
DNA: [B.data["blood_DNA"]]"
else
- pill_cube = "pill"
+ P.info += "Description: [R.description]"
+ P.info += "
Notes:
"
+ P.name = "Chemical Analysis - [R.name]"
+ spawn(50)
+ printing = FALSE
+ else
+ . = FALSE
- var/name = sanitizeSafe(input(usr,"Name:","Name your [pill_cube]!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)") as null|text, MAX_NAME_LEN)
+ if(. || !beaker)
+ return
- if(!name) //Blank name (sanitized to nothing, or left empty) or cancel
+ . = TRUE
+ var/datum/reagents/R = beaker.reagents
+ switch(action)
+ if("add")
+ var/id = params["id"]
+ var/amount = text2num(params["amount"])
+ if(!id || !amount)
return
-
-
- if(reagents.total_volume/count < 1) //Sanity checking.
+ R.trans_id_to(src, id, amount)
+ if("remove")
+ var/id = params["id"]
+ var/amount = text2num(params["amount"])
+ if(!id || !amount)
return
- while(count-- > 0) // Will definitely eventually stop.
- var/obj/item/weapon/reagent_containers/pill/P = new/obj/item/weapon/reagent_containers/pill(src.loc)
- if(!name) name = reagents.get_master_reagent_name()
- P.name = "[name] pill"
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
- if(!condi) //If normal
- P.icon_state = "pill"+pillsprite
- else //If condi is on
- P.icon_state = "bouilloncube"//Reskinned monkey cube
- P.desc = "A dissolvable cube."
-
- if(P.icon_state in list("pill1", "pill2", "pill3", "pill4")) // if using greyscale, take colour from reagent
- P.color = reagents.get_color()
-
- reagents.trans_to_obj(P,amount_per_pill)
- if(src.loaded_pill_bottle)
- if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space)
- P.loc = loaded_pill_bottle
-
- else if (href_list["createbottle"])
- if(!condi)
- var/name = sanitizeSafe(input(usr,"Name:","Name your bottle!",reagents.get_master_reagent_name()), MAX_NAME_LEN)
- var/obj/item/weapon/reagent_containers/glass/bottle/P = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
- if(!name) name = reagents.get_master_reagent_name()
- P.name = "[name] bottle"
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
- P.icon_state = "bottle-"+bottlesprite
- reagents.trans_to_obj(P,60)
- P.update_icon()
+ if(mode)
+ reagents.trans_id_to(beaker, id, amount)
else
- var/obj/item/weapon/reagent_containers/food/condiment/P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc)
- reagents.trans_to_obj(P,50)
-
- else if (href_list["createpatch"])
- if(reagents.total_volume < 1) //Sanity checking.
+ reagents.remove_reagent(id, amount)
+ if("eject")
+ if(!beaker)
return
-
- var/name = sanitizeSafe(input(usr,"Name:","Name your patch!","[reagents.get_master_reagent_name()] ([round(reagents.total_volume)]u)") as null|text, MAX_NAME_LEN)
-
- if(!name) //Blank name (sanitized to nothing, or left empty) or cancel
+ beaker.forceMove(get_turf(src))
+ if(Adjacent(usr) && !issilicon(usr))
+ usr.put_in_hands(beaker)
+ beaker = null
+ reagents.clear_reagents()
+ update_icon()
+ if("create_condi_bottle")
+ if(!condi || !reagents.total_volume)
return
+ var/obj/item/weapon/reagent_containers/food/condiment/P = new(loc)
+ reagents.trans_to_obj(P, 50)
+ else
+ return FALSE
- if(reagents.total_volume < 1) //Sanity checking.
- return
- var/obj/item/weapon/reagent_containers/pill/patch/P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc)
- if(!name) name = reagents.get_master_reagent_name()
- P.name = "[name] patch"
- P.pixel_x = rand(-7, 7) //random position
- P.pixel_y = rand(-7, 7)
+/obj/machinery/chem_master/attack_ai(mob/user)
+ return attack_hand(user)
- reagents.trans_to_obj(P, 60)
- if(src.loaded_pill_bottle)
- if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space)
- P.loc = loaded_pill_bottle
+/obj/machinery/chem_master/proc/isgoodnumber(num)
+ if(isnum(num))
+ if(num > 200)
+ num = 200
+ else if(num < 0)
+ num = 1
+ return num
+ else
+ return FALSE
- else if(href_list["pill_sprite"])
- pillsprite = href_list["pill_sprite"]
- else if(href_list["bottle_sprite"])
- bottlesprite = href_list["bottle_sprite"]
-
- SSnanoui.update_uis(src)
-
-/obj/machinery/chem_master/attack_ai(mob/user as mob)
- return src.attack_hand(user)
+// /obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R)
+// var/all_safe = TRUE
+// for(var/datum/reagent/A in R.reagent_list)
+// if(!GLOB.safe_chem_list.Find(A.id))
+// all_safe = FALSE
+// return all_safe
/obj/machinery/chem_master/condimaster
name = "CondiMaster 3000"
@@ -365,11 +549,41 @@
/obj/item/stack/material/glass/phoronglass = list("platinum", "silicon", "silicon", "silicon"), //5 platinum, 15 silicon,
)
+ var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine")
+ var/static/radial_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject")
+ var/static/radial_grind = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_grind")
+ // var/static/radial_juice = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_juice")
+ // var/static/radial_mix = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_mix")
+
/obj/machinery/reagentgrinder/Initialize()
. = ..()
beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
default_apply_parts()
+/obj/machinery/reagentgrinder/examine(mob/user)
+ . = ..()
+ if(!in_range(user, src) && !issilicon(user) && !isobserver(user))
+ . += "You're too far away to examine [src]'s contents and display!"
+ return
+
+ if(inuse)
+ . += "\The [src] is operating."
+ return
+
+ if(beaker || length(holdingitems))
+ . += "\The [src] contains:"
+ if(beaker)
+ . += "- \A [beaker]."
+ for(var/i in holdingitems)
+ var/obj/item/O = i
+ . += "- \A [O.name]."
+
+ if(!(stat & (NOPOWER|BROKEN)))
+ . += "The status display reads:\n"
+ if(beaker)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ . += "- [R.volume] units of [R.name]."
+
/obj/machinery/reagentgrinder/update_icon()
icon_state = "juicer"+num2text(!isnull(beaker))
return
@@ -444,94 +658,65 @@
user.remove_from_mob(O)
O.loc = src
holdingitems += O
- src.updateUsrDialog()
return 0
+/obj/machinery/reagentgrinder/AltClick(mob/user)
+ . = ..()
+ if(user.incapacitated() || !Adjacent(user))
+ return
+ replace_beaker(user)
+
/obj/machinery/reagentgrinder/attack_hand(mob/user as mob)
- user.set_machine(src)
interact(user)
-/obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu
- var/is_chamber_empty = 0
- var/is_beaker_ready = 0
- var/processing_chamber = ""
- var/beaker_contents = ""
- var/dat = ""
+/obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu //I am reasonably certain that this is not a microwave
+ if(inuse || user.incapacitated())
+ return
- if(!inuse)
- for (var/obj/item/O in holdingitems)
- processing_chamber += "\A [O.name]
"
+ var/list/options = list()
- if (!processing_chamber)
- is_chamber_empty = 1
- processing_chamber = "Nothing."
- if (!beaker)
- beaker_contents = "No beaker attached.
"
- else
- is_beaker_ready = 1
- beaker_contents = "The beaker contains:
"
- var/anything = 0
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- anything = 1
- beaker_contents += "[R.volume] - [R.name]
"
- if(!anything)
- beaker_contents += "Nothing
"
+ if(beaker || length(holdingitems))
+ options["eject"] = radial_eject
+ if(isAI(user))
+ if(stat & NOPOWER)
+ return
+ options["examine"] = radial_examine
- dat = {"
- Processing chamber contains:
- [processing_chamber]
- [beaker_contents]
- "}
- if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
- dat += "Process the reagents
"
- if(holdingitems && holdingitems.len > 0)
- dat += "Eject the reagents
"
- if (beaker)
- dat += "Detach the beaker
"
+ // if there is no power or it's broken, the procs will fail but the buttons will still show
+ if(length(holdingitems))
+ options["grind"] = radial_grind
+
+ var/choice
+ if(length(options) < 1)
+ return
+ if(length(options) == 1)
+ for(var/key in options)
+ choice = key
else
- dat += "Please wait..."
- user << browse("All-In-One Grinder[dat]", "window=reagentgrinder")
- onclose(user, "reagentgrinder")
- return
+ choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
-
-/obj/machinery/reagentgrinder/Topic(href, href_list)
- if(..())
+ // post choice verification
+ if(inuse || (isAI(user) && stat & NOPOWER) || user.incapacitated())
return
- usr.set_machine(src)
- switch(href_list["action"])
- if ("grind")
- grind()
+
+ switch(choice)
if("eject")
- eject()
- if ("detach")
- detach()
- src.updateUsrDialog()
- return
+ eject(user)
+ if("grind")
+ grind(user)
+ if("examine")
+ examine(user)
-/obj/machinery/reagentgrinder/proc/detach()
-
- if (usr.stat != 0)
+/obj/machinery/reagentgrinder/proc/eject(mob/user)
+ if(user.incapacitated())
return
- if (!beaker)
- return
- beaker.loc = src.loc
- beaker = null
- visible_message("\The [usr] remove the container from \the [src].")
- update_icon()
-
-/obj/machinery/reagentgrinder/proc/eject()
-
- if (usr.stat != 0)
- return
- if (!holdingitems || holdingitems.len == 0)
- return
-
for(var/obj/item/O in holdingitems)
O.loc = src.loc
holdingitems -= O
holdingitems.Cut()
+ if(beaker)
+ replace_beaker(user)
/obj/machinery/reagentgrinder/proc/grind()
@@ -549,7 +734,6 @@
// Reset the machine.
spawn(60)
inuse = 0
- interact(usr)
// Process.
for (var/obj/item/O in holdingitems)
@@ -576,13 +760,26 @@
continue
if(O.reagents)
- O.reagents.trans_to(beaker, min(O.reagents.total_volume, remaining_volume))
+ O.reagents.trans_to_obj(beaker, min(O.reagents.total_volume, remaining_volume))
if(O.reagents.total_volume == 0)
holdingitems -= O
qdel(O)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
+/obj/machinery/reagentgrinder/proc/replace_beaker(mob/living/user, obj/item/weapon/reagent_containers/new_beaker)
+ if(!user)
+ return FALSE
+ if(beaker)
+ if(!user.incapacitated() && Adjacent(user))
+ user.put_in_hands(beaker)
+ else
+ beaker.forceMove(drop_location())
+ beaker = null
+ if(new_beaker)
+ beaker = new_beaker
+ update_icon()
+ return TRUE
///////////////
///////////////
@@ -648,4 +845,12 @@
to_chat(user, span("notice", "Scanning of \the [I] complete."))
analyzing = FALSE
update_icon()
- return
\ No newline at end of file
+ return
+
+#undef MAX_PILL_SPRITE
+#undef MAX_BOTTLE_SPRITE
+#undef MAX_MULTI_AMOUNT
+#undef MAX_UNITS_PER_PILL
+#undef MAX_UNITS_PER_PATCH
+#undef MAX_UNITS_PER_BOTTLE
+#undef MAX_CUSTOM_NAME_LEN
diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm
index 9663c68529..7f2fb13f40 100644
--- a/code/modules/reagents/dispenser/dispenser2.dm
+++ b/code/modules/reagents/dispenser/dispenser2.dm
@@ -68,12 +68,12 @@
C.loc = src
cartridges[C.label] = C
cartridges = sortAssoc(cartridges)
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/chemical_dispenser/proc/remove_cartridge(label)
. = cartridges[label]
cartridges -= label
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/chemical_dispenser/attackby(obj/item/weapon/W, mob/user)
if(W.is_wrench())
@@ -119,25 +119,26 @@
user.drop_from_inventory(RC)
RC.loc = src
to_chat(user, "You set \the [RC] on \the [src].")
- SSnanoui.update_uis(src) // update all UIs attached to src
-
else
return ..()
-/obj/machinery/chemical_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null, var/force_open = 1)
- if(stat & (BROKEN|NOPOWER)) return
- if(user.stat || user.restrained()) return
+/obj/machinery/chemical_dispenser/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ChemDispenser", ui_title) // 390, 655
+ ui.open()
- // this is the data which will be sent to the ui
+/obj/machinery/chemical_dispenser/tgui_data(mob/user)
var/data[0]
data["amount"] = amount
data["isBeakerLoaded"] = container ? 1 : 0
data["glass"] = accept_drinking
- var beakerD[0]
+
+ var/beakerContents[0]
if(container && container.reagents && container.reagents.reagent_list.len)
for(var/datum/reagent/R in container.reagents.reagent_list)
- beakerD[++beakerD.len] = list("name" = R.name, "volume" = R.volume)
- data["beakerContents"] = beakerD
+ beakerContents.Add(list(list("name" = R.name, "id" = R.id, "volume" = R.volume))) // list in a list because Byond merges the first list...
+ data["beakerContents"] = beakerContents
if(container)
data["beakerCurrentVolume"] = container.reagents.total_volume
@@ -146,50 +147,59 @@
data["beakerCurrentVolume"] = null
data["beakerMaxVolume"] = null
- var chemicals[0]
+ var/chemicals[0]
for(var/label in cartridges)
var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
- chemicals[++chemicals.len] = list("label" = label, "amount" = C.reagents.total_volume)
+ chemicals.Add(list(list("title" = label, "id" = label, "amount" = C.reagents.total_volume))) // list in a list because Byond merges the first list...
data["chemicals"] = chemicals
+ return data
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "chem_disp.tmpl", ui_title, 390, 680)
- ui.set_initial_data(data)
- ui.open()
+/obj/machinery/chemical_dispenser/tgui_act(action, params)
+ if(..())
+ return TRUE
-/obj/machinery/chemical_dispenser/Topic(href, href_list)
- if(stat & (NOPOWER|BROKEN))
- return 0 // don't update UIs attached to this object
+ . = TRUE
+ switch(action)
+ if("amount")
+ amount = clamp(round(text2num(params["amount"]), 1), 0, 120) // round to nearest 1 and clamp 0 - 120
+ if("dispense")
+ var/label = params["reagent"]
+ if(cartridges[label] && container && container.is_open_container())
+ var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
+ playsound(src, 'sound/machines/reagent_dispense.ogg', 25, 1)
+ C.reagents.trans_to(container, amount)
+ if("remove")
+ var/amount = text2num(params["amount"])
+ if(!container || !amount)
+ return
+ var/datum/reagents/R = container.reagents
+ var/id = params["reagent"]
+ if(amount > 0)
+ R.remove_reagent(id, amount)
+ else if(amount == -1) // Isolate
+ R.isolate_reagent(id)
+ if("ejectBeaker")
+ if(container)
+ container.forceMove(get_turf(src))
- if(href_list["amount"])
- amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
- amount = max(0, min(120, amount)) // Since the user can actually type the commands himself, some sanity checking
+ if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
+ usr.put_in_hands(container)
- else if(href_list["dispense"])
- var/label = href_list["dispense"]
- if(cartridges[label] && container && container.is_open_container())
- var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
- playsound(src, 'sound/machines/reagent_dispense.ogg', 25, 1)
- C.reagents.trans_to(container, amount)
-
- else if(href_list["ejectBeaker"])
- if(container)
- container.forceMove(get_turf(src))
-
- if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
- usr.put_in_hands(container)
-
- container = null
+ container = null
+ else
+ return FALSE
add_fingerprint(usr)
- return 1 // update UIs attached to this object
-/obj/machinery/chemical_dispenser/attack_ai(mob/user as mob)
- src.attack_hand(user)
-
-/obj/machinery/chemical_dispenser/attack_hand(mob/user as mob)
+/obj/machinery/chemical_dispenser/attack_ghost(mob/user)
if(stat & BROKEN)
return
- ui_interact(user)
+ tgui_interact(user)
+
+/obj/machinery/chemical_dispenser/attack_ai(mob/user)
+ attack_hand(user)
+
+/obj/machinery/chemical_dispenser/attack_hand(mob/user)
+ if(stat & BROKEN)
+ return
+ tgui_interact(user)
diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm
index 71f6c66fcd..87817c6e27 100644
--- a/code/modules/surgery/implant.dm
+++ b/code/modules/surgery/implant.dm
@@ -119,6 +119,8 @@
max_duration = 100
/datum/surgery_step/cavity/place_item/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
+ if(!istype(tool))
+ return FALSE
if(..())
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(istype(user,/mob/living/silicon/robot))
diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm
index 391b83fe0c..300616499f 100644
--- a/code/modules/surgery/limb_reattach.dm
+++ b/code/modules/surgery/limb_reattach.dm
@@ -28,6 +28,8 @@
max_duration = 70
/datum/surgery_step/limb/attach/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
+ if(!istype(tool))
+ return FALSE
var/obj/item/organ/external/E = tool
var/obj/item/organ/external/P = target.organs_by_name[E.parent_organ]
var/obj/item/organ/external/affected = target.get_organ(target_zone)
@@ -117,7 +119,7 @@
max_duration = 100
/datum/surgery_step/limb/mechanize/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(..())
+ if(..() && istype(tool))
var/obj/item/robot_parts/p = tool
if (p.part)
if (!(target_zone in p.part))
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index c09d10bebe..071b44263b 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -164,6 +164,9 @@
if (!..())
return 0
+ if(!istype(tool))
+ return FALSE
+
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(!(affected && !(affected.robotic >= ORGAN_ROBOT)))
@@ -227,6 +230,9 @@
if (!..())
return 0
+ if(!istype(tool))
+ return FALSE
+
target.op_stage.current_organ = null
var/list/removable_organs = list()
@@ -281,7 +287,7 @@
var/obj/item/organ/internal/O = tool
var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
+ if(!affected || !istype(O))
return
var/organ_compatible
@@ -361,6 +367,9 @@
if (!..())
return 0
+ if(!istype(tool))
+ return FALSE
+
target.op_stage.current_organ = null
var/list/removable_organs = list()
@@ -417,6 +426,9 @@
if (!..())
return 0
+ if(!istype(tool))
+ return FALSE
+
target.op_stage.current_organ = null
var/list/removable_organs = list()
diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm
index 7e75f9343c..bb39c721d0 100644
--- a/code/modules/tgui/external.dm
+++ b/code/modules/tgui/external.dm
@@ -12,9 +12,10 @@
*
* required user mob The mob who opened/is using the UI.
* optional ui datum/tgui The UI to be updated, if it exists.
+ * optional parent_ui datum/tgui A parent UI that, when closed, closes this UI as well.
*/
-/datum/proc/tgui_interact(mob/user, datum/tgui/ui = null)
+/datum/proc/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null)
return FALSE // Not implemented.
/**
@@ -27,7 +28,7 @@
*
* return list Data to be sent to the UI.
*/
-/datum/proc/tgui_data(mob/user)
+/datum/proc/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
return list() // Not implemented.
/**
@@ -140,6 +141,26 @@
*/
/datum/proc/tgui_close(mob/user)
+/**
+ * verb
+ *
+ * Used by a client to fix broken TGUI windows caused by opening a UI window before assets load.
+ * Probably not very performant and forcibly destroys a bunch of windows, so it has some warnings attached.
+ * Conveniently, also allows devs to force a dev server reattach without relogging, since it yeets windows.
+ */
+/client/verb/tgui_fix_white()
+ set desc = "Only use this if you have a broken TGUI window occupying your screen!"
+ set name = "Fix TGUI"
+ set category = "OOC"
+
+ if(alert(src, "Only use this verb if you have a white TGUI window stuck on your screen.", "Fix TGUI", "Continue", "Nevermind") != "Continue")
+ return
+
+ SStgui.close_user_uis(mob)
+ if(alert(src, "Did that fix the problem?", "Fix TGUI", "Yes", "No") == "No")
+ SStgui.force_close_all_windows(mob)
+ alert(src, "UIs should be fixed now. If not, please cry to your nearest coder.", "Fix TGUI")
+
/**
* verb
*
diff --git a/code/modules/tgui/modal.dm b/code/modules/tgui/modal.dm
new file mode 100644
index 0000000000..5fb4c4cc74
--- /dev/null
+++ b/code/modules/tgui/modal.dm
@@ -0,0 +1,370 @@
+/**
+ * tgui modals
+ *
+ * Allows creation of modals within tgui.
+ */
+
+GLOBAL_LIST(tgui_modals)
+
+/**
+ * Call this from a proc that is called in tgui_act() to process modal actions
+ *
+ * Example: /obj/machinery/chem_master/proc/tgui_act_modal
+ * You can then switch based on the return value and show different
+ * modals depending on the answer.
+ * Arguments:
+ * * source - The source datum
+ * * action - The called action
+ * * params - The params to the action
+ */
+/datum/proc/tgui_modal_act(datum/source = src, action = "", params)
+ ASSERT(istype(source))
+
+ . = null
+ switch(action)
+ if("modal_open") // Params: id, arguments
+ return TGUI_MODAL_OPEN
+ if("modal_answer") // Params: id, answer, arguments
+ params["answer"] = tgui_modal_preprocess_answer(source, params["answer"])
+ if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue
+ . = TGUI_MODAL_DELEGATE
+ else
+ . = TGUI_MODAL_ANSWER
+ tgui_modal_clear(source)
+ if("modal_close") // Params: id
+ tgui_modal_clear(source)
+ return TGUI_MODAL_CLOSE
+
+/**
+ * Call this from tgui_data() to return modal information if needed
+
+ * Arguments:
+ * * source - The source datum
+ */
+/datum/proc/tgui_modal_data(datum/source = src)
+ ASSERT(istype(source))
+
+ var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, REF(source))
+ if(!current)
+ return null
+
+ return current.to_data()
+
+/**
+ * Clears the current modal for a given datum
+ *
+ * Arguments:
+ * * source - The source datum
+ */
+/datum/proc/tgui_modal_clear(datum/source = src)
+ ASSERT(istype(source))
+
+ LAZYINITLIST(GLOB.tgui_modals)
+ var/datum/tgui_modal/previous = GLOB.tgui_modals[REF(source)]
+ if(!previous)
+ return FALSE
+
+ for(var/i in 1 to length(GLOB.tgui_modals))
+ var/key = GLOB.tgui_modals[i]
+ if(previous == GLOB.tgui_modals[key])
+ GLOB.tgui_modals.Cut(i, i + 1)
+ break
+
+ SStgui.update_uis(source)
+ return TRUE
+
+/**
+ * Opens a message TGUI modal
+ *
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * text - The text to display above the answers
+ * * delegate - The proc to call when closed
+ * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
+ */
+/datum/proc/tgui_modal_message(datum/source = src, id, text = "Default modal message", delegate, arguments)
+ ASSERT(length(id))
+
+ var/datum/tgui_modal/modal = new(id, text, delegate, arguments)
+ return tgui_modal_new(source, modal)
+
+/**
+ * Opens a text input TGUI modal
+ *
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * text - The text to display above the answers
+ * * delegate - The proc to call when submitted
+ * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
+ * * value - The default value of the input
+ * * max_length - The maximum char length of the input
+ */
+/datum/proc/tgui_modal_input(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", max_length = TGUI_MODAL_INPUT_MAX_LENGTH)
+ ASSERT(length(id))
+ ASSERT(max_length > 0)
+
+ var/datum/tgui_modal/input/modal = new(id, text, delegate, arguments, value, max_length)
+ return tgui_modal_new(source, modal)
+
+/**
+ * Opens a dropdown input TGUI modal
+ *
+ * Internally checks if the answer is in the list of choices.
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * text - The text to display above the answers
+ * * delegate - The proc to call when submitted
+ * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
+ * * value - The default value of the dropdown
+ * * choices - The list of available choices in the dropdown
+ */
+/datum/proc/tgui_modal_choice(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", choices)
+ ASSERT(length(id))
+
+ var/datum/tgui_modal/input/choice/modal = new(id, text, delegate, arguments, value, choices)
+ return tgui_modal_new(source, modal)
+
+/**
+ * Opens a bento input TGUI modal
+ *
+ * Internally checks if the answer is in the list of choices.
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * text - The text to display above the answers
+ * * delegate - The proc to call when submitted
+ * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
+ * * value - The default value of the bento
+ * * choices - The list of available choices in the bento
+ */
+/datum/proc/tgui_modal_bento(datum/source = src, id, text = "Default modal message", delegate, arguments, value, choices)
+ ASSERT(length(id))
+
+ var/datum/tgui_modal/input/bento/modal = new(id, text, delegate, arguments, value, choices)
+ return tgui_modal_new(source, modal)
+
+/**
+ * Opens a yes/no TGUI modal
+ *
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * text - The text to display above the answers
+ * * delegate - The proc to call when "Yes" is pressed
+ * * delegate_no - The proc to call when "No" is pressed
+ * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
+ * * yes_text - The text to show in the "Yes" button
+ * * no_text - The text to show in the "No" button
+ */
+/datum/proc/tgui_modal_boolean(datum/source = src, id, text = "Default modal message", delegate, delegate_no, arguments, yes_text = "Yes", no_text = "No")
+ ASSERT(length(id))
+
+ var/datum/tgui_modal/boolean/modal = new(id, text, delegate, delegate_no, arguments, yes_text, no_text)
+ return tgui_modal_new(source, modal)
+
+/**
+ * Registers a given modal to a source. Private.
+ *
+ * Arguments:
+ * * source - The source datum
+ * * modal - The datum/tgui_modal to register
+ * * replace_previous - Whether any modal currently assigned to source should be replaced
+ * * instant_update - Whether the changes should reflect immediately
+ */
+/datum/proc/tgui_modal_new(datum/source = src, datum/tgui_modal/modal = null, replace_previous = TRUE, instant_update = TRUE)
+ ASSERT(istype(source))
+ ASSERT(istype(modal))
+
+ var/datum/tgui_modal/previous = LAZYACCESS(GLOB.tgui_modals, REF(source))
+ if(previous && !replace_previous)
+ return FALSE
+
+ modal.owning_source = source
+
+ // Previous one should get GC'd
+ LAZYSET(GLOB.tgui_modals, REF(source), modal)
+ if(instant_update)
+ SStgui.update_uis(source)
+ return TRUE
+
+/**
+ * Calls the source's currently assigned modal's (if there is one) on_answer() proc. Private.
+ *
+ * Arguments:
+ * * source - The source datum
+ * * id - The ID of the modal
+ * * answer - The provided answer
+ */
+/datum/proc/tgui_modal_answer(datum/source = src, id, answer = "")
+ ASSERT(istype(source))
+
+ var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, REF(source))
+ if(!current)
+ return FALSE
+
+ return current.on_answer(answer)
+
+/**
+ * Passes an answer from JS through the modal's proc.
+ *
+ * Used namely for cutting the text short if it's longer
+ * than an input modal's max_length.
+ * Arguments:
+ * * source - The source datum
+ * * answer - The provided answer
+ */
+/datum/proc/tgui_modal_preprocess_answer(datum/source = src, answer = "")
+ ASSERT(istype(source))
+
+ var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, REF(source))
+ if(!current)
+ return answer
+
+ return current.preprocess_answer(answer)
+
+/**
+ * Modal datum (contains base information for a modal)
+ */
+/datum/tgui_modal
+ var/datum/owning_source
+ var/id
+ var/text
+ var/delegate
+ var/list/arguments
+ var/modal_type = "message"
+
+/datum/tgui_modal/New(id, text, delegate, list/arguments)
+ src.id = id
+ src.text = text
+ src.delegate = delegate
+ src.arguments = arguments
+
+/**
+ * Called when it's time to pre-process the answer before using it
+ *
+ * Arguments:
+ * * answer - The answer, a nullable text
+ */
+/datum/tgui_modal/proc/preprocess_answer(answer)
+ return reject_bad_text(answer, TGUI_MODAL_INPUT_MAX_LENGTH) // bleh
+
+/**
+ * Called when a modal receives an answer
+ *
+ * Arguments:
+ * * answer - The answer, a nullable text
+ */
+/datum/tgui_modal/proc/on_answer(answer)
+ if(delegate)
+ return call(owning_source, delegate)(answer, arguments)
+ return FALSE
+
+/**
+ * Creates a list that describes a modal visually to be passed to JS
+ */
+/datum/tgui_modal/proc/to_data()
+ . = list()
+ .["id"] = id
+ .["text"] = text
+ .["args"] = arguments || list()
+ .["type"] = modal_type
+
+/**
+ * Input modal - has a text entry that can be used to enter an answer
+ */
+/datum/tgui_modal/input
+ modal_type = "input"
+ var/value
+ var/max_length
+
+/datum/tgui_modal/input/New(id, text, delegate, list/arguments, value, max_length)
+ ..(id, text, delegate, arguments)
+ src.value = value
+ src.max_length = max_length
+
+/datum/tgui_modal/input/preprocess_answer(answer)
+ . = ..(answer)
+ if(length(answer) > max_length)
+ . = copytext(., 1, max_length + 1)
+
+/datum/tgui_modal/input/to_data()
+ . = ..()
+ .["value"] = value
+
+/**
+ * Choice modal - has a dropdown menu that can be used to select an answer
+ */
+/datum/tgui_modal/input/choice
+ modal_type = "choice"
+ var/choices
+
+/datum/tgui_modal/input/choice/New(id, text, delegate, list/arguments, value, choices)
+ ..(id, text, delegate, arguments, value, TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in dropdowns, but whatever
+ src.choices = choices
+
+/datum/tgui_modal/input/choice/on_answer(answer)
+ if(answer in choices) // Make sure the answer is actually in our choices!
+ return ..(answer, arguments)
+ return FALSE
+
+/datum/tgui_modal/input/choice/to_data()
+ . = ..()
+ .["choices"] = choices
+
+/**
+ * Bento modal - Similar to choice, it displays the choices in a grid of images
+ *
+ * The returned answer is the index of the choice.
+ */
+/datum/tgui_modal/input/bento
+ modal_type = "bento"
+ var/choices
+
+/datum/tgui_modal/input/bento/New(id, text, delegate, list/arguments, value, choices)
+ ..(id, text, delegate, arguments, text2num(value), TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in here, but whatever
+ src.choices = choices
+
+/datum/tgui_modal/input/bento/preprocess_answer(answer)
+ return text2num(answer) || 0
+
+/datum/tgui_modal/input/bento/on_answer(answer)
+ if(answer >= 1 && answer <= length(choices)) // Make sure the answer index is actually in our indexes!
+ return ..(answer, arguments)
+ return FALSE
+
+/datum/tgui_modal/input/bento/to_data()
+ . = ..()
+ .["choices"] = choices
+
+/**
+ * Boolean modal - has yes/no buttons that do different actions depending on which is pressed
+ */
+/datum/tgui_modal/boolean
+ modal_type = "boolean"
+ var/delegate_no
+ var/yes_text
+ var/no_text
+
+/datum/tgui_modal/boolean/New(id, text, delegate, delegate_no, list/arguments, yes_text, no_text)
+ ..(id, text, delegate, arguments)
+ src.delegate_no = delegate_no
+ src.yes_text = yes_text
+ src.no_text = no_text
+
+/datum/tgui_modal/boolean/preprocess_answer(answer)
+ return text2num(answer) || FALSE
+
+/datum/tgui_modal/boolean/on_answer(answer)
+ if(answer)
+ return ..(answer, arguments)
+ else if(delegate_no)
+ return call(owning_source, delegate_no)(arguments)
+ return FALSE
+
+/datum/tgui_modal/boolean/to_data()
+ . = ..()
+ .["yes_text"] = yes_text
+ .["no_text"] = no_text
diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm
index 23b12b8bd1..e0d0a7c645 100644
--- a/code/modules/tgui/modules/_base.dm
+++ b/code/modules/tgui/modules/_base.dm
@@ -12,17 +12,26 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
var/list/using_access
var/tgui_id
+ var/ntos = FALSE
/datum/tgui_module/New(var/host)
src.host = host
+ if(ntos)
+ tgui_id = "Ntos" + tgui_id
/datum/tgui_module/tgui_host()
- return host ? host : src
+ return host ? host.tgui_host() : src
/datum/tgui_module/tgui_close(mob/user)
if(host)
host.tgui_close(user)
+/datum/tgui_module/proc/check_eye(mob/user)
+ return -1
+
+/datum/tgui_module/proc/can_still_topic(mob/user, datum/tgui_state/state)
+ return (tgui_status(user, state) == STATUS_INTERACTIVE)
+
/datum/tgui_module/proc/check_access(mob/user, access)
if(!access)
return 1
@@ -43,4 +52,70 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
if(access in I.access)
return 1
- return 0
\ No newline at end of file
+ return 0
+
+/datum/tgui_module/tgui_static_data()
+ . = ..()
+
+ var/obj/item/modular_computer/host = tgui_host()
+ if(istype(host))
+ . += host.get_header_data()
+
+/datum/tgui_module/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ var/obj/item/modular_computer/host = tgui_host()
+ if(istype(host))
+ if(action == "PC_exit")
+ host.kill_program()
+ return TRUE
+ if(action == "PC_shutdown")
+ host.shutdown_computer()
+ return TRUE
+ if(action == "PC_minimize")
+ host.minimize_program(usr)
+ return TRUE
+
+// Just a nice little default interact in case the subtypes don't need any special behavior here
+/datum/tgui_module/tgui_interact(mob/user, datum/tgui/ui = null)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, tgui_id, name)
+ ui.open()
+
+// This is a helper for anything that wants to render the map.
+/datum/tgui_module/proc/get_plane_masters()
+ . = list()
+ // 'Utility' planes
+ . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
+ . += new /obj/screen/plane_master/lighting //Lighting system (but different!)
+ . += new /obj/screen/plane_master/ghosts //Ghosts!
+ . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
+
+ . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
+ . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
+ . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
+ . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
+ . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
+ . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
+
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
+
+ . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
+ . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
+
+ // Real tangible stuff planes
+ . += new /obj/screen/plane_master/main{plane = TURF_PLANE}
+ . += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
+ . += new /obj/screen/plane_master/main{plane = MOB_PLANE}
+ . += new /obj/screen/plane_master/cloaked //Cloaked atoms!
+
+/datum/tgui_module/proc/relaymove(mob/user, direction)
+ return FALSE
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
new file mode 100644
index 0000000000..667fe7e0a0
--- /dev/null
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -0,0 +1,358 @@
+/datum/tgui_module/appearance_changer
+ name = "Appearance Editor"
+ tgui_id = "AppearanceChanger"
+ var/flags = APPEARANCE_ALL_HAIR
+ var/mob/living/carbon/human/owner = null
+ var/list/valid_species = list()
+ var/list/valid_hairstyles = list()
+ var/list/valid_facial_hairstyles = list()
+
+ var/check_whitelist
+ var/list/whitelist
+ var/list/blacklist
+
+ var/customize_usr = FALSE
+
+ // Stuff needed to render the map
+ var/map_name
+ var/obj/screen/map_view/cam_screen
+ var/list/cam_plane_masters
+ var/obj/screen/background/cam_background
+ var/obj/screen/skybox/local_skybox
+ // Needed for moving camera support
+ var/camera_diff_x = -1
+ var/camera_diff_y = -1
+ var/camera_diff_z = -1
+
+/datum/tgui_module/appearance_changer/New(
+ var/host,
+ mob/living/carbon/human/H,
+ check_species_whitelist = 1,
+ list/species_whitelist = list(),
+ list/species_blacklist = list())
+ . = ..()
+
+ map_name = "appearance_changer_[REF(src)]_map"
+ // Initialize map objects
+ cam_screen = new
+ cam_screen.name = "screen"
+ cam_screen.assigned_map = map_name
+ cam_screen.del_on_map_removal = FALSE
+ cam_screen.screen_loc = "[map_name]:1,1"
+
+ cam_plane_masters = get_plane_masters()
+
+ for(var/plane in cam_plane_masters)
+ var/obj/screen/instance = plane
+ instance.assigned_map = map_name
+ instance.del_on_map_removal = FALSE
+ instance.screen_loc = "[map_name]:CENTER"
+
+ local_skybox = new()
+ local_skybox.assigned_map = map_name
+ local_skybox.del_on_map_removal = FALSE
+ local_skybox.screen_loc = "[map_name]:CENTER,CENTER"
+ cam_plane_masters += local_skybox
+
+ cam_background = new
+ cam_background.assigned_map = map_name
+ cam_background.del_on_map_removal = FALSE
+ reload_cameraview()
+
+ owner = H
+ check_whitelist = check_species_whitelist
+ whitelist = species_whitelist
+ blacklist = species_blacklist
+
+/datum/tgui_module/appearance_changer/Destroy()
+ qdel(cam_screen)
+ QDEL_LIST(cam_plane_masters)
+ qdel(cam_background)
+ return ..()
+
+/datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ switch(action)
+ if("race")
+ if(can_change(APPEARANCE_RACE) && (params["race"] in valid_species))
+ if(target.change_species(params["race"]))
+ cut_and_generate_data()
+ return 1
+ if("gender")
+ if(can_change(APPEARANCE_GENDER) && (params["gender"] in get_genders()))
+ if(target.change_gender(params["gender"]))
+ cut_and_generate_data()
+ return 1
+ if("gender_id")
+ if(can_change(APPEARANCE_GENDER) && (params["gender_id"] in all_genders_define_list))
+ target.identifying_gender = params["gender_id"]
+ return 1
+ if("skin_tone")
+ if(can_change_skin_tone())
+ var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -target.s_tone + 35) as num|null
+ if(isnum(new_s_tone) && can_still_topic(usr, state))
+ new_s_tone = 35 - max(min( round(new_s_tone), 220),1)
+ return target.change_skin_tone(new_s_tone)
+ if("skin_color")
+ if(can_change_skin_color())
+ var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(target.r_skin, target.g_skin, target.b_skin)) as color|null
+ if(new_skin && can_still_topic(usr, state))
+ var/r_skin = hex2num(copytext(new_skin, 2, 4))
+ var/g_skin = hex2num(copytext(new_skin, 4, 6))
+ var/b_skin = hex2num(copytext(new_skin, 6, 8))
+ if(target.change_skin_color(r_skin, g_skin, b_skin))
+ update_dna()
+ return 1
+ if("hair")
+ if(can_change(APPEARANCE_HAIR) && (params["hair"] in valid_hairstyles))
+ if(target.change_hair(params["hair"]))
+ update_dna()
+ return 1
+ if("hair_color")
+ if(can_change(APPEARANCE_HAIR_COLOR))
+ var/new_hair = input("Please select hair color.", "Hair Color", rgb(target.r_hair, target.g_hair, target.b_hair)) as color|null
+ if(new_hair && can_still_topic(usr, state))
+ var/r_hair = hex2num(copytext(new_hair, 2, 4))
+ var/g_hair = hex2num(copytext(new_hair, 4, 6))
+ var/b_hair = hex2num(copytext(new_hair, 6, 8))
+ if(target.change_hair_color(r_hair, g_hair, b_hair))
+ update_dna()
+ return 1
+ if("facial_hair")
+ if(can_change(APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles))
+ if(target.change_facial_hair(params["facial_hair"]))
+ update_dna()
+ return 1
+ if("facial_hair_color")
+ if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
+ var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(target.r_facial, target.g_facial, target.b_facial)) as color|null
+ if(new_facial && can_still_topic(usr, state))
+ var/r_facial = hex2num(copytext(new_facial, 2, 4))
+ var/g_facial = hex2num(copytext(new_facial, 4, 6))
+ var/b_facial = hex2num(copytext(new_facial, 6, 8))
+ if(target.change_facial_hair_color(r_facial, g_facial, b_facial))
+ update_dna()
+ return 1
+ if("eye_color")
+ if(can_change(APPEARANCE_EYE_COLOR))
+ var/new_eyes = input("Please select eye color.", "Eye Color", rgb(target.r_eyes, target.g_eyes, target.b_eyes)) as color|null
+ if(new_eyes && can_still_topic(usr, state))
+ var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
+ var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
+ var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
+ if(target.change_eye_color(r_eyes, g_eyes, b_eyes))
+ update_dna()
+ return 1
+ return FALSE
+
+/datum/tgui_module/appearance_changer/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null, datum/tgui_state/custom_state = GLOB.tgui_default_state)
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(user))
+ return TRUE
+ target = user
+
+ if(!target || !target.species)
+ return
+
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ reload_cameraview()
+ // Register map objects
+ user.client.register_map_obj(cam_screen)
+ for(var/plane in cam_plane_masters)
+ user.client.register_map_obj(plane)
+ user.client.register_map_obj(cam_background)
+ // Open UI
+ ui = new(user, src, tgui_id, name)
+ ui.open()
+ if(custom_state)
+ ui.set_state(custom_state)
+
+/datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ generate_data(check_whitelist, whitelist, blacklist)
+ differential_check()
+
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ data["name"] = target.name
+ data["specimen"] = target.species.name
+ data["gender"] = target.gender
+ data["gender_id"] = target.identifying_gender
+ data["change_race"] = can_change(APPEARANCE_RACE)
+ if(data["change_race"])
+ var/species[0]
+ for(var/specimen in valid_species)
+ species[++species.len] = list("specimen" = specimen)
+ data["species"] = species
+
+ data["change_gender"] = can_change(APPEARANCE_GENDER)
+ if(data["change_gender"])
+ var/genders[0]
+ for(var/gender in get_genders())
+ genders[++genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
+ data["genders"] = genders
+ var/id_genders[0]
+ for(var/gender in all_genders_define_list)
+ id_genders[++id_genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
+ data["id_genders"] = id_genders
+
+ data["change_hair"] = can_change(APPEARANCE_HAIR)
+ if(data["change_hair"])
+ var/hair_styles[0]
+ for(var/hair_style in valid_hairstyles)
+ hair_styles[++hair_styles.len] = list("hairstyle" = hair_style)
+ data["hair_styles"] = hair_styles
+ data["hair_style"] = target.h_style
+
+ data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
+ if(data["change_facial_hair"])
+ var/facial_hair_styles[0]
+ for(var/facial_hair_style in valid_facial_hairstyles)
+ facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_style)
+ data["facial_hair_styles"] = facial_hair_styles
+ data["facial_hair_style"] = target.f_style
+
+ data["change_skin_tone"] = can_change_skin_tone()
+ data["change_skin_color"] = can_change_skin_color()
+ if(data["change_skin_color"])
+ data["skin_color"] = rgb(target.r_skin, target.g_skin, target.b_skin)
+ data["change_eye_color"] = can_change(APPEARANCE_EYE_COLOR)
+ if(data["change_eye_color"])
+ data["eye_color"] = rgb(target.r_eyes, target.g_eyes, target.b_eyes)
+ data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR)
+ if(data["change_hair_color"])
+ data["hair_color"] = rgb(target.r_hair, target.g_hair, target.b_hair)
+ data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
+ if(data["change_facial_hair_color"])
+ data["facial_hair_color"] = rgb(target.r_facial, target.g_facial, target.b_facial)
+ return data
+
+/datum/tgui_module/appearance_changer/tgui_static_data(mob/user)
+ var/list/data = ..()
+ data["mapRef"] = map_name
+ return data
+
+/datum/tgui_module/appearance_changer/proc/differential_check()
+ var/turf/T = get_turf(customize_usr ? tgui_host() : owner)
+ if(T)
+ var/new_x = T.x
+ var/new_y = T.y
+ var/new_z = T.z
+ if((new_x != camera_diff_x) || (new_y != camera_diff_y) || (new_z != camera_diff_z))
+ reload_cameraview()
+
+/datum/tgui_module/appearance_changer/proc/reload_cameraview()
+ var/turf/camTurf = get_turf(customize_usr ? tgui_host() : owner)
+ if(!camTurf)
+ return
+
+ camera_diff_x = camTurf.x
+ camera_diff_y = camTurf.y
+ camera_diff_z = camTurf.z
+
+ var/list/visible_turfs = list()
+ for(var/turf/T in range(1, camTurf))
+ visible_turfs += T
+
+ cam_screen.vis_contents = visible_turfs
+ cam_background.icon_state = "clear"
+ cam_background.fill_rect(1, 1, 3, 3)
+
+ local_skybox.cut_overlays()
+ local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf)))
+ local_skybox.scale_to_view(3)
+ local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y)
+
+/datum/tgui_module/appearance_changer/proc/update_dna()
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ if(target && (flags & APPEARANCE_UPDATE_DNA))
+ target.update_dna()
+
+/datum/tgui_module/appearance_changer/proc/can_change(var/flag)
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ return target && (flags & flag)
+
+/datum/tgui_module/appearance_changer/proc/can_change_skin_tone()
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ return target && (flags & APPEARANCE_SKIN) && target.species.appearance_flags & HAS_SKIN_TONE
+
+/datum/tgui_module/appearance_changer/proc/can_change_skin_color()
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+
+ return target && (flags & APPEARANCE_SKIN) && target.species.appearance_flags & HAS_SKIN_COLOR
+
+/datum/tgui_module/appearance_changer/proc/cut_and_generate_data()
+ // Making the assumption that the available species remain constant
+ valid_facial_hairstyles.Cut()
+ valid_facial_hairstyles.Cut()
+ generate_data()
+
+/datum/tgui_module/appearance_changer/proc/generate_data()
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+ if(!target)
+ return
+ if(!valid_species.len)
+ valid_species = target.generate_valid_species(check_whitelist, whitelist, blacklist)
+ if(!valid_hairstyles.len || !valid_facial_hairstyles.len)
+ valid_hairstyles = target.generate_valid_hairstyles(check_gender = 0)
+ valid_facial_hairstyles = target.generate_valid_facial_hairstyles()
+
+/datum/tgui_module/appearance_changer/proc/get_genders()
+ var/mob/living/carbon/human/target = owner
+ if(customize_usr)
+ if(!ishuman(usr))
+ return TRUE
+ target = usr
+ var/datum/species/S = target.species
+ var/list/possible_genders = S.genders
+ if(!target.internal_organs_by_name["cell"])
+ return possible_genders
+ possible_genders = possible_genders.Copy()
+ possible_genders |= NEUTER
+ return possible_genders
+
+/datum/tgui_module/appearance_changer/mirror
+ name = "SalonPro Nano-Mirror™"
+ flags = APPEARANCE_ALL_HAIR
+ customize_usr = TRUE
+
+/datum/tgui_module/appearance_changer/mirror/coskit
+ name = "SalonPro Porta-Makeover Deluxe™"
\ No newline at end of file
diff --git a/code/modules/tgui/modules/camera.dm b/code/modules/tgui/modules/camera.dm
index 58a8293991..dbb3c7ae97 100644
--- a/code/modules/tgui/modules/camera.dm
+++ b/code/modules/tgui/modules/camera.dm
@@ -37,37 +37,7 @@
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
- cam_plane_masters = list()
-
- // 'Utility' planes
- cam_plane_masters += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
- cam_plane_masters += new /obj/screen/plane_master/lighting //Lighting system (but different!)
- cam_plane_masters += new /obj/screen/plane_master/ghosts //Ghosts!
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
-
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
-
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
-
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
- cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
-
- // Real tangible stuff planes
- cam_plane_masters += new /obj/screen/plane_master/main{plane = TURF_PLANE}
- cam_plane_masters += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
- cam_plane_masters += new /obj/screen/plane_master/main{plane = MOB_PLANE}
- cam_plane_masters += new /obj/screen/plane_master/cloaked //Cloaked atoms!
+ cam_plane_masters = get_plane_masters()
for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane
@@ -144,7 +114,7 @@
return data
/datum/tgui_module/camera/tgui_static_data(mob/user)
- var/list/data = list()
+ var/list/data = ..()
data["mapRef"] = map_name
var/list/cameras = get_available_cameras(user)
data["cameras"] = list()
@@ -160,7 +130,10 @@
/datum/tgui_module/camera/tgui_act(action, params)
if(..())
- return
+ return TRUE
+
+ if(action && !issilicon(usr))
+ playsound(tgui_host(), "terminal_type", 50, 1)
if(action == "switch_camera")
var/c_tag = params["name"]
@@ -168,11 +141,33 @@
var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"]
active_camera = C
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
-
reload_cameraview()
-
return TRUE
+ if(action == "pan")
+ var/dir = params["dir"]
+ var/turf/T = get_turf(active_camera)
+ for(var/i in 1 to 10)
+ T = get_step(T, dir)
+ if(T)
+ var/obj/machinery/camera/target
+ var/best_dist = INFINITY
+
+ var/list/possible_cameras = get_available_cameras(usr)
+ for(var/obj/machinery/camera/C in get_area(T))
+ if(!possible_cameras["[ckey(C.c_tag)]"])
+ continue
+ var/dist = get_dist(C, T)
+ if(dist < best_dist)
+ best_dist = dist
+ target = C
+
+ if(target)
+ active_camera = target
+ playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
+ reload_cameraview()
+ . = TRUE
+
/datum/tgui_module/camera/proc/differential_check()
var/turf/T = get_turf(active_camera)
if(T)
@@ -284,33 +279,7 @@
// If/when that is done, just move all the PC_ specific data and stuff to the modular computers themselves
// instead of copying this approach here.
/datum/tgui_module/camera/ntos
- tgui_id = "NtosCameraConsole"
-
-/datum/tgui_module/camera/ntos/tgui_state()
- return GLOB.tgui_ntos_state
-
-/datum/tgui_module/camera/ntos/tgui_static_data()
- . = ..()
-
- var/datum/computer_file/program/host = tgui_host()
- if(istype(host) && host.computer)
- . += host.computer.get_header_data()
-
-/datum/tgui_module/camera/ntos/tgui_act(action, params)
- if(..())
- return
-
- var/datum/computer_file/program/host = tgui_host()
- if(istype(host) && host.computer)
- if(action == "PC_exit")
- host.computer.kill_program()
- return TRUE
- if(action == "PC_shutdown")
- host.computer.shutdown_computer()
- return TRUE
- if(action == "PC_minimize")
- host.computer.minimize_program(usr)
- return TRUE
+ ntos = TRUE
// ERT Version provides some additional networks.
/datum/tgui_module/camera/ntos/ert
diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm
index ebd6f35263..3f0d8d92b5 100644
--- a/code/modules/tgui/modules/crew_monitor.dm
+++ b/code/modules/tgui/modules/crew_monitor.dm
@@ -5,6 +5,9 @@
/datum/tgui_module/crew_monitor/tgui_act(action, params, datum/tgui/ui)
if(..())
return TRUE
+
+ if(action && !issilicon(usr))
+ playsound(tgui_host(), "terminal_type", 50, 1)
var/turf/T = get_turf(usr)
if(!T || !(T.z in using_map.player_levels))
@@ -21,7 +24,7 @@
return TRUE
if("setZLevel")
ui.set_map_z_level(params["mapZLevel"])
- SStgui.update_uis(src)
+ return TRUE
/datum/tgui_module/crew_monitor/tgui_interact(mob/user, datum/tgui/ui = null)
var/z = get_z(user)
@@ -40,7 +43,7 @@
ui.open()
-/datum/tgui_module/crew_monitor/tgui_data(mob/user, ui_key = "main", datum/tgui_state/state = GLOB.tgui_default_state)
+/datum/tgui_module/crew_monitor/tgui_data(mob/user)
var/data[0]
data["isAI"] = isAI(user)
@@ -56,33 +59,7 @@
return data
/datum/tgui_module/crew_monitor/ntos
- tgui_id = "NtosCrewMonitor"
-
-/datum/tgui_module/crew_monitor/ntos/tgui_state(mob/user)
- return GLOB.tgui_ntos_state
-
-/datum/tgui_module/crew_monitor/ntos/tgui_static_data()
- . = ..()
-
- var/datum/computer_file/program/host = tgui_host()
- if(istype(host) && host.computer)
- . += host.computer.get_header_data()
-
-/datum/tgui_module/crew_monitor/ntos/tgui_act(action, params)
- if(..())
- return
-
- var/datum/computer_file/program/host = tgui_host()
- if(istype(host) && host.computer)
- if(action == "PC_exit")
- host.computer.kill_program()
- return TRUE
- if(action == "PC_shutdown")
- host.computer.shutdown_computer()
- return TRUE
- if(action == "PC_minimize")
- host.computer.minimize_program(usr)
- return TRUE
+ ntos = TRUE
// Subtype for self_state
/datum/tgui_module/crew_monitor/robot
diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm
index 9ccc9c751e..ba8d132e0b 100644
--- a/code/modules/tgui/states/default.dm
+++ b/code/modules/tgui/states/default.dm
@@ -36,13 +36,29 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
return STATUS_DISABLED // Otherwise they can keep the UI open.
/mob/living/silicon/ai/default_can_use_tgui_topic(src_object)
- . = shared_tgui_interaction(src_object)
- if(. < STATUS_INTERACTIVE)
+ . = shared_tgui_interaction()
+ if(. != STATUS_INTERACTIVE)
return
- // The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
- if(!control_disabled && can_see(src_object))
+ // Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras)
+ // unless it's on the same level as the object it's interacting with.
+ var/turf/T = get_turf(src_object)
+ if(!T || !(z == T.z || (T.z in using_map.player_levels)))
+ return STATUS_CLOSE
+
+ // If an object is in view then we can interact with it
+ if(src_object in view(client.view, src))
return STATUS_INTERACTIVE
+
+ // If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view
+ if(is_in_chassis())
+ //stop AIs from leaving windows open and using then after they lose vision
+ if(cameranet && !cameranet.checkTurfVis(get_turf(src_object)))
+ return STATUS_CLOSE
+ return STATUS_INTERACTIVE
+ else if(get_dist(src_object, src) <= client.view) // View does not return what one would expect while installed in an inteliCard
+ return STATUS_INTERACTIVE
+
return STATUS_CLOSE
/mob/living/simple_animal/default_can_use_tgui_topic(src_object)
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index 9af1296ab0..2f928ae6fa 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -34,8 +34,12 @@
var/status = STATUS_INTERACTIVE
/// Topic state used to determine status/interactability.
var/datum/tgui_state/state = null
- // The map z-level to display.
+ /// The map z-level to display.
var/map_z_level = 1
+ /// The Parent UI
+ var/datum/tgui/parent_ui
+ /// Children of this UI
+ var/list/children = list()
/**
* public
@@ -46,12 +50,13 @@
* required src_object datum The object or datum which owns the UI.
* required interface string The interface used to render the UI.
* optional title string The title of the UI.
+ * optional parent_ui datum/tgui The parent of this UI.
* optional ui_x int Deprecated: Window width.
* optional ui_y int Deprecated: Window height.
*
* return datum/tgui The requested UI.
*/
-/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
+/datum/tgui/New(mob/user, datum/src_object, interface, title, datum/tgui/parent_ui, ui_x, ui_y)
src.user = user
src.src_object = src_object
src.window_key = "[REF(src_object)]-main"
@@ -59,6 +64,9 @@
if(title)
src.title = title
src.state = src_object.tgui_state()
+ src.parent_ui = parent_ui
+ if(parent_ui)
+ parent_ui.children += src
// Deprecated
if(ui_x && ui_y)
src.window_size = list(ui_x, ui_y)
@@ -100,10 +108,13 @@
*
* Close the UI, and all its children.
*/
-/datum/tgui/proc/close(can_be_suspended = TRUE)
+/datum/tgui/proc/close(can_be_suspended = TRUE, logout = FALSE)
if(closing)
return
closing = TRUE
+ for(var/datum/tgui/child in children)
+ child.close(can_be_suspended, logout)
+ children.Cut()
// If we don't have window_id, open proc did not have the opportunity
// to finish, therefore it's safe to skip this whole block.
if(window)
@@ -111,10 +122,11 @@
// and we want to keep them around, to allow user to read
// the error message properly.
window.release_lock()
- window.close(can_be_suspended)
+ window.close(can_be_suspended, logout)
src_object.tgui_close(user)
SStgui.on_close(src)
state = null
+ parent_ui = null
qdel(src)
/**
@@ -209,7 +221,7 @@
"observer" = isobserver(user),
),
)
- var/data = custom_data || with_data && src_object.tgui_data(user)
+ var/data = custom_data || with_data && src_object.tgui_data(user, src, state)
if(data)
json_data["data"] = data
var/static_data = with_static_data && src_object.tgui_static_data(user)
@@ -244,7 +256,7 @@
return
// Update through a normal call to ui_interact
if(status != STATUS_DISABLED && (autoupdate || force))
- src_object.tgui_interact(user, src)
+ src_object.tgui_interact(user, src, parent_ui)
return
// Update status only
var/needs_update = process_status()
@@ -262,6 +274,8 @@
/datum/tgui/proc/process_status()
var/prev_status = status
status = src_object.tgui_status(user, state)
+ if(parent_ui)
+ status = min(status, parent_ui.status)
return prev_status != status
/datum/tgui/proc/log_message(message)
diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm
index 18cf715368..c78eda86d3 100644
--- a/code/modules/tgui/tgui_window.dm
+++ b/code/modules/tgui/tgui_window.dm
@@ -133,13 +133,19 @@
*
* optional can_be_suspended bool
*/
-/datum/tgui_window/proc/close(can_be_suspended = TRUE)
+/datum/tgui_window/proc/close(can_be_suspended = TRUE, logout = FALSE)
if(!client)
return
if(can_be_suspended && can_be_suspended())
log_tgui(client, "[id]/close: suspending")
status = TGUI_WINDOW_READY
send_message("suspend")
+ // You would think that BYOND would null out client or make it stop passing istypes or, y'know, ANYTHING during
+ // logout, but nope! It appears to be perfectly valid to call winset by every means we can measure in Logout,
+ // and yet it causes a bad client runtime. To avoid that happening, we just have to know if we're in Logout or
+ // not.
+ if(!logout && client)
+ winset(client, null, "mapwindow.map.focus=true")
return
log_tgui(client, "[id]/close")
release_lock()
@@ -149,7 +155,8 @@
// to read the error message.
if(!fatally_errored)
client << browse(null, "window=[id]")
-
+ if(!logout && client)
+ winset(client, null, "mapwindow.map.focus=true")
/**
* public
*
@@ -189,9 +196,9 @@
/datum/tgui_window/proc/send_asset(datum/asset/asset)
if(!client || !asset)
return
- // if(istype(asset, /datum/asset/spritesheet))
- // var/datum/asset/spritesheet/spritesheet = asset
- // send_message("asset/stylesheet", spritesheet.css_filename())
+ if(istype(asset, /datum/asset/spritesheet))
+ var/datum/asset/spritesheet/spritesheet = asset
+ send_message("asset/stylesheet", spritesheet.css_filename())
send_message("asset/mappings", asset.get_url_mappings())
sent_assets += list(asset)
asset.send(client)
diff --git a/code/modules/virus2/centrifuge.dm b/code/modules/virus2/centrifuge.dm
index 36ec60e8cb..aaa65ce9e7 100644
--- a/code/modules/virus2/centrifuge.dm
+++ b/code/modules/virus2/centrifuge.dm
@@ -26,7 +26,7 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
src.attack_hand(user)
@@ -36,27 +36,32 @@
icon_state = "centrifuge_moving"
/obj/machinery/computer/centrifuge/attack_hand(var/mob/user as mob)
- if(..()) return
- ui_interact(user)
+ if(..())
+ return
+ tgui_interact(user)
-/obj/machinery/computer/centrifuge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/computer/centrifuge/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "IsolationCentrifuge", name)
+ ui.open()
- var/data[0]
+/obj/machinery/computer/centrifuge/tgui_data(mob/user)
+ var/list/data = list()
data["antibodies"] = null
- data["pathogens"] = null
+ data["pathogens"] = list()
data["is_antibody_sample"] = null
+ data["busy"] = null
+ data["sample_inserted"] = !!sample
- if (curing)
+ if(curing)
data["busy"] = "Isolating antibodies..."
- else if (isolating)
+ else if(isolating)
data["busy"] = "Isolating pathogens..."
else
- data["sample_inserted"] = !!sample
-
- if (sample)
+ if(sample)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
- if (B)
+ if(B)
data["antibodies"] = antigens2string(B.data["antibodies"], none=null)
var/list/pathogens[0]
@@ -65,8 +70,7 @@
var/datum/disease2/disease/V = virus[ID]
pathogens.Add(list(list("name" = V.name(), "spread_type" = V.spreadtype, "reference" = "\ref[V]")))
- if (pathogens.len > 0)
- data["pathogens"] = pathogens
+ data["pathogens"] = pathogens
else
var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list
@@ -74,103 +78,90 @@
data["antibodies"] = antigens2string(A.data["antibodies"], none=null)
data["is_antibody_sample"] = 1
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "isolation_centrifuge.tmpl", src.name, 400, 500)
- ui.set_initial_data(data)
- ui.open()
+ return data
/obj/machinery/computer/centrifuge/process()
..()
- if (stat & (NOPOWER|BROKEN)) return
+ if(stat & (NOPOWER|BROKEN)) return
- if (curing)
+ if(curing)
curing -= 1
- if (curing == 0)
+ if(curing == 0)
cure()
- if (isolating)
+ if(isolating)
isolating -= 1
if(isolating == 0)
isolate()
-/obj/machinery/computer/centrifuge/Topic(href, href_list)
- if (..()) return 1
+/obj/machinery/computer/centrifuge/tgui_act(action, params)
+ if(..())
+ return TRUE
var/mob/user = usr
- var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
+ add_fingerprint(user)
- src.add_fingerprint(user)
- if (href_list["close"])
- user.unset_machine()
- ui.close()
- return 0
-
- if (href_list["print"])
- print(user)
- return 1
-
- if(href_list["isolate"])
- var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
- if (B)
- var/datum/disease2/disease/virus = locate(href_list["isolate"])
- virus2 = virus.getcopy()
- isolating = 40
- update_icon()
- return 1
-
- switch(href_list["action"])
- if ("antibody")
+ switch(action)
+ if("print")
+ print(user)
+ . = TRUE
+ if("isolate")
+ var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
+ if(B)
+ var/datum/disease2/disease/virus = locate(params["isolate"])
+ virus2 = virus.getcopy()
+ isolating = 40
+ update_icon()
+ . = TRUE
+ if("antibody")
var/delay = 20
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
- if (!B)
+ if(!B)
state("\The [src] buzzes, \"No antibody carrier detected.\"", "blue")
- return 1
+ return TRUE
var/has_toxins = locate(/datum/reagent/toxin) in sample.reagents.reagent_list
var/has_radium = sample.reagents.has_reagent("radium")
- if (has_toxins || has_radium)
+ if(has_toxins || has_radium)
state("\The [src] beeps, \"Pathogen purging speed above nominal.\"", "blue")
- if (has_toxins)
+ if(has_toxins)
delay = delay/2
- if (has_radium)
+ if(has_radium)
delay = delay/2
curing = round(delay)
playsound(src, 'sound/machines/juicer.ogg', 50, 1)
update_icon()
- return 1
-
+ . = TRUE
if("sample")
if(sample)
sample.loc = src.loc
sample = null
- return 1
+ . = TRUE
- return 0
/obj/machinery/computer/centrifuge/proc/cure()
- if (!sample) return
+ if(!sample) return
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
- if (!B) return
+ if(!B) return
var/list/data = list("antibodies" = B.data["antibodies"])
var/amt= sample.reagents.get_reagent_amount("blood")
sample.reagents.remove_reagent("blood", amt)
sample.reagents.add_reagent("antibodies", amt, data)
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
update_icon()
ping("\The [src] pings, \"Antibody isolated.\"")
/obj/machinery/computer/centrifuge/proc/isolate()
- if (!sample) return
+ if(!sample) return
var/obj/item/weapon/virusdish/dish = new/obj/item/weapon/virusdish(loc)
dish.virus2 = virus2
virus2 = null
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
update_icon()
ping("\The [src] pings, \"Pathogen isolated.\"")
@@ -182,20 +173,20 @@
Sample: [sample.name]
"}
- if (user)
+ if(user)
P.info += "Generated By: [user.name]
"
P.info += "
"
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
- if (B)
+ if(B)
P.info += "Antibodies: "
P.info += antigens2string(B.data["antibodies"])
P.info += "
"
var/list/virus = B.data["virus2"]
P.info += "Pathogens:
"
- if (virus.len > 0)
+ if(virus.len > 0)
for (var/ID in virus)
var/datum/disease2/disease/V = virus[ID]
P.info += "[V.name()]
"
@@ -204,7 +195,7 @@
else
var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list
- if (A)
+ if(A)
P.info += "The following antibodies have been isolated from the blood sample: "
P.info += antigens2string(A.data["antibodies"])
P.info += "
"
diff --git a/code/modules/virus2/disease2.dm b/code/modules/virus2/disease2.dm
index 4681496aef..6d64f35039 100644
--- a/code/modules/virus2/disease2.dm
+++ b/code/modules/virus2/disease2.dm
@@ -238,6 +238,26 @@ var/global/list/virusDB = list()
return r
+/datum/disease2/disease/proc/get_tgui_info()
+ . = list(
+ "name" = name(),
+ "spreadtype" = spreadtype,
+ "antigen" = antigens2string(antigen),
+ "rate" = stageprob * 10,
+ "resistance" = resistance,
+ "species" = jointext(affected_species, ", "),
+ "symptoms" = list(),
+ "ref" = "\ref[src]",
+ )
+
+ for(var/datum/disease2/effectholder/E in effects)
+ .["symptoms"].Add(list(list(
+ "stage" = E.stage,
+ "name" = E.effect.name,
+ "strength" = "[E.multiplier >= 3 ? "Severe" : E.multiplier > 1 ? "Above Average" : "Average"]",
+ "aggressiveness" = E.chance * 15,
+ )))
+
/datum/disease2/disease/proc/addToDB()
if ("[uniqueID]" in virusDB)
return 0
@@ -245,6 +265,8 @@ var/global/list/virusDB = list()
v.fields["id"] = uniqueID
v.fields["name"] = name()
v.fields["description"] = get_info()
+ v.fields["tgui_description"] = get_tgui_info()
+ v.fields["tgui_description"]["record"] = "\ref[v]"
v.fields["antigen"] = antigens2string(antigen)
v.fields["spread type"] = spreadtype
virusDB["[uniqueID]"] = v
diff --git a/code/modules/virus2/diseasesplicer.dm b/code/modules/virus2/diseasesplicer.dm
index 95657498a0..fb682f51d1 100644
--- a/code/modules/virus2/diseasesplicer.dm
+++ b/code/modules/virus2/diseasesplicer.dm
@@ -20,7 +20,7 @@
if(istype(I,/obj/item/weapon/virusdish))
var/mob/living/carbon/c = user
- if (dish)
+ if(dish)
to_chat(user, "\The [src] is already loaded.")
return
@@ -40,36 +40,46 @@
return src.attack_hand(user)
/obj/machinery/computer/diseasesplicer/attack_hand(var/mob/user as mob)
- if(..()) return
- ui_interact(user)
+ if(..())
+ return TRUE
+ tgui_interact(user)
-/obj/machinery/computer/diseasesplicer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/computer/diseasesplicer/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "DiseaseSplicer", name)
+ ui.open()
- var/data[0]
+/obj/machinery/computer/diseasesplicer/tgui_data(mob/user)
+ var/list/data = list()
data["dish_inserted"] = !!dish
- data["growth"] = 0
- data["affected_species"] = null
- if (memorybank)
+ data["buffer"] = null
+ if(memorybank)
data["buffer"] = list("name" = (analysed ? memorybank.effect.name : "Unknown Symptom"), "stage" = memorybank.effect.stage)
- if (species_buffer)
+ data["species_buffer"] = null
+ if(species_buffer)
data["species_buffer"] = analysed ? jointext(species_buffer, ", ") : "Unknown Species"
- if (splicing)
+ data["effects"] = null
+ data["info"] = null
+ data["growth"] = 0
+ data["affected_species"] = null
+ data["busy"] = null
+ if(splicing)
data["busy"] = "Splicing..."
- else if (scanning)
+ else if(scanning)
data["busy"] = "Scanning..."
- else if (burning)
+ else if(burning)
data["busy"] = "Copying data to disk..."
- else if (dish)
+ else if(dish)
data["growth"] = min(dish.growth, 100)
- if (dish.virus2)
- if (dish.virus2.affected_species)
- data["affected_species"] = dish.analysed ? jointext(dish.virus2.affected_species, ", ") : "Unknown"
+ if(dish.virus2)
+ if(dish.virus2.affected_species)
+ data["affected_species"] = dish.analysed ? dish.virus2.affected_species : list()
- if (dish.growth >= 50)
+ if(dish.growth >= 50)
var/list/effects[0]
for (var/datum/disease2/effectholder/e in dish.virus2.effects)
effects.Add(list(list("name" = (dish.analysed ? e.effect.name : "Unknown"), "stage" = (e.stage), "reference" = "\ref[e]")))
@@ -81,11 +91,7 @@
else
data["info"] = "No dish loaded."
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "disease_splicer.tmpl", src.name, 400, 600)
- ui.set_initial_data(data)
- ui.open()
+ return data
/obj/machinery/computer/diseasesplicer/process()
if(stat & (NOPOWER|BROKEN))
@@ -95,100 +101,94 @@
scanning -= 1
if(!scanning)
ping("\The [src] pings, \"Analysis complete.\"")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(splicing)
splicing -= 1
if(!splicing)
ping("\The [src] pings, \"Splicing operation complete.\"")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(burning)
burning -= 1
if(!burning)
var/obj/item/weapon/diseasedisk/d = new /obj/item/weapon/diseasedisk(src.loc)
d.analysed = analysed
if(analysed)
- if (memorybank)
+ if(memorybank)
d.name = "[memorybank.effect.name] GNA disk (Stage: [memorybank.effect.stage])"
d.effect = memorybank
- else if (species_buffer)
+ else if(species_buffer)
d.name = "[jointext(species_buffer, ", ")] GNA disk"
d.species = species_buffer
else
- if (memorybank)
+ if(memorybank)
d.name = "Unknown GNA disk (Stage: [memorybank.effect.stage])"
d.effect = memorybank
- else if (species_buffer)
+ else if(species_buffer)
d.name = "Unknown Species GNA disk"
d.species = species_buffer
ping("\The [src] pings, \"Backup disk saved.\"")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
-/obj/machinery/computer/diseasesplicer/Topic(href, href_list)
- if(..()) return 1
+/obj/machinery/computer/diseasesplicer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
var/mob/user = usr
- var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
+ add_fingerprint(user)
- src.add_fingerprint(user)
+ switch(action)
+ if("grab")
+ if(dish)
+ memorybank = locate(params["grab"])
+ species_buffer = null
+ analysed = dish.analysed
+ dish = null
+ scanning = 10
+ . = TRUE
- if (href_list["close"])
- user.unset_machine()
- ui.close()
- return 0
+ if("affected_species")
+ if(dish)
+ memorybank = null
+ species_buffer = dish.virus2.affected_species
+ analysed = dish.analysed
+ dish = null
+ scanning = 10
+ . = TRUE
- if (href_list["grab"])
- if (dish)
- memorybank = locate(href_list["grab"])
- species_buffer = null
- analysed = dish.analysed
- dish = null
- scanning = 10
- return 1
+ if("eject")
+ if(dish)
+ dish.loc = src.loc
+ dish = null
+ . = TRUE
- if (href_list["affected_species"])
- if (dish)
- memorybank = null
- species_buffer = dish.virus2.affected_species
- analysed = dish.analysed
- dish = null
- scanning = 10
- return 1
+ if("splice")
+ if(dish)
+ var/target = text2num(params["splice"]) // target = 1 to 4 for effects, 5 for species
+ if(memorybank && 0 < target && target <= 4)
+ if(target < memorybank.effect.stage) return // too powerful, catching this for href exploit prevention
- if(href_list["eject"])
- if (dish)
- dish.loc = src.loc
- dish = null
- return 1
+ var/datum/disease2/effectholder/target_holder
+ var/list/illegal_types = list()
+ for(var/datum/disease2/effectholder/e in dish.virus2.effects)
+ if(e.stage == target)
+ target_holder = e
+ else
+ illegal_types += e.effect.type
+ if(memorybank.effect.type in illegal_types) return
+ target_holder.effect = memorybank.effect
- if(href_list["splice"])
- if(dish)
- var/target = text2num(href_list["splice"]) // target = 1 to 4 for effects, 5 for species
- if(memorybank && 0 < target && target <= 4)
- if(target < memorybank.effect.stage) return // too powerful, catching this for href exploit prevention
+ else if(species_buffer && target == 5)
+ dish.virus2.affected_species = species_buffer
- var/datum/disease2/effectholder/target_holder
- var/list/illegal_types = list()
- for(var/datum/disease2/effectholder/e in dish.virus2.effects)
- if(e.stage == target)
- target_holder = e
- else
- illegal_types += e.effect.type
- if(memorybank.effect.type in illegal_types) return
- target_holder.effect = memorybank.effect
+ else
+ return
- else if(species_buffer && target == 5)
- dish.virus2.affected_species = species_buffer
+ splicing = 10
+ dish.virus2.uniqueID = rand(0,10000)
+ . = TRUE
- else
- return
+ if("disk")
+ burning = 10
+ . = TRUE
- splicing = 10
- dish.virus2.uniqueID = rand(0,10000)
- return 1
-
- if(href_list["disk"])
- burning = 10
- return 1
-
- return 0
diff --git a/code/modules/virus2/dishincubator.dm b/code/modules/virus2/dishincubator.dm
index 3ebe688c78..f9ae91f9d8 100644
--- a/code/modules/virus2/dishincubator.dm
+++ b/code/modules/virus2/dishincubator.dm
@@ -30,7 +30,7 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
src.attack_hand(user)
return
@@ -46,17 +46,23 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
src.attack_hand(user)
/obj/machinery/disease2/incubator/attack_hand(mob/user as mob)
- if(stat & (NOPOWER|BROKEN)) return
- ui_interact(user)
+ if(stat & (NOPOWER|BROKEN))
+ return
+ tgui_interact(user)
-/obj/machinery/disease2/incubator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/disease2/incubator/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "DishIncubator", name)
+ ui.set_autoupdate(FALSE)
+ ui.open()
+/obj/machinery/disease2/incubator/tgui_data(mob/user)
var/data[0]
data["chemicals_inserted"] = !!beaker
data["dish_inserted"] = !!dish
@@ -74,23 +80,19 @@
data["can_breed_virus"] = null
data["blood_already_infected"] = null
- if (beaker)
+ if(beaker)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
data["can_breed_virus"] = dish && dish.virus2 && B
- if (B)
- if (!B.data["virus2"])
+ if(B)
+ if(!B.data["virus2"])
B.data["virus2"] = list()
var/list/virus = B.data["virus2"]
for (var/ID in virus)
data["blood_already_infected"] = virus[ID]
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "dish_incubator.tmpl", src.name, 400, 600)
- ui.set_initial_data(data)
- ui.open()
+ return data
/obj/machinery/disease2/incubator/process()
if(dish && on && dish.virus2)
@@ -105,7 +107,7 @@
foodsupply -= 1
dish.growth += 3
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(radiation)
if(radiation > 50 & prob(5))
@@ -118,90 +120,82 @@
else if(prob(5))
dish.virus2.minormutate()
radiation -= 1
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(toxins && prob(5))
dish.virus2.infectionchance -= 1
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(toxins > 50)
dish.growth = 0
dish.virus2 = null
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
else if(!dish)
on = 0
icon_state = "incubator"
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(beaker)
if(foodsupply < 100 && beaker.reagents.remove_reagent("virusfood",5))
if(foodsupply + 10 <= 100)
foodsupply += 10
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
- if (locate(/datum/reagent/toxin) in beaker.reagents.reagent_list && toxins < 100)
+ if(locate(/datum/reagent/toxin) in beaker.reagents.reagent_list && toxins < 100)
for(var/datum/reagent/toxin/T in beaker.reagents.reagent_list)
toxins += max(T.strength,1)
beaker.reagents.remove_reagent(T.id,1)
if(toxins > 100)
toxins = 100
break
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
-/obj/machinery/disease2/incubator/Topic(href, href_list)
- if (..()) return 1
+/obj/machinery/disease2/incubator/tgui_act(action, params)
+ if(..())
+ return TRUE
var/mob/user = usr
- var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
+ add_fingerprint(user)
+ switch(action)
+ if("ejectchem")
+ if(beaker)
+ beaker.loc = src.loc
+ beaker = null
+ . = TRUE
- src.add_fingerprint(user)
+ if("power")
+ if(dish)
+ on = !on
+ icon_state = on ? "incubator_on" : "incubator"
+ . = TRUE
- if (href_list["close"])
- user.unset_machine()
- ui.close()
- return 0
+ if("ejectdish")
+ if(dish)
+ dish.loc = src.loc
+ dish = null
+ . = TRUE
- if (href_list["ejectchem"])
- if(beaker)
- beaker.loc = src.loc
- beaker = null
- return 1
+ if("rad")
+ radiation = min(100, radiation + 10)
+ . = TRUE
- if (href_list["power"])
- if (dish)
- on = !on
- icon_state = on ? "incubator_on" : "incubator"
- return 1
+ if("flush")
+ radiation = 0
+ toxins = 0
+ foodsupply = 0
+ . = TRUE
- if (href_list["ejectdish"])
- if(dish)
- dish.loc = src.loc
- dish = null
- return 1
+ if("virus")
+ if(!dish)
+ return TRUE
- if (href_list["rad"])
- radiation = min(100, radiation + 10)
- return 1
+ var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
+ if(!B)
+ return TRUE
- if (href_list["flush"])
- radiation = 0
- toxins = 0
- foodsupply = 0
- return 1
+ if(!B.data["virus2"])
+ B.data["virus2"] = list()
- if(href_list["virus"])
- if (!dish)
- return 1
+ var/list/virus = list("[dish.virus2.uniqueID]" = dish.virus2.getcopy())
+ B.data["virus2"] += virus
- var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
- if (!B)
- return 1
-
- if (!B.data["virus2"])
- B.data["virus2"] = list()
-
- var/list/virus = list("[dish.virus2.uniqueID]" = dish.virus2.getcopy())
- B.data["virus2"] += virus
-
- ping("\The [src] pings, \"Injection complete.\"")
- return 1
-
- return 0
+ ping("\The [src] pings, \"Injection complete.\"")
+ . = TRUE
diff --git a/code/modules/virus2/isolator.dm b/code/modules/virus2/isolator.dm
index d22b379ee1..d6fbc4fe99 100644
--- a/code/modules/virus2/isolator.dm
+++ b/code/modules/virus2/isolator.dm
@@ -1,8 +1,3 @@
-// UI menu navigation
-#define HOME "home"
-#define LIST "list"
-#define ENTRY "entry"
-
/obj/machinery/disease2/isolator/
name = "pathogenic isolator"
desc = "Used to isolate and identify diseases, allowing for comparison with a remote database."
@@ -11,9 +6,7 @@
icon = 'icons/obj/virology.dmi'
icon_state = "isolator"
var/isolating = 0
- var/state = HOME
var/datum/disease2/disease/virus2 = null
- var/datum/data/record/entry = null
var/obj/item/weapon/reagent_containers/syringe/sample = null
/obj/machinery/disease2/isolator/update_icon()
@@ -44,71 +37,57 @@
S.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
update_icon()
src.attack_hand(user)
/obj/machinery/disease2/isolator/attack_hand(mob/user as mob)
- if(stat & (NOPOWER|BROKEN)) return
- ui_interact(user)
+ if(stat & (NOPOWER|BROKEN))
+ return
+ tgui_interact(user)
-/obj/machinery/disease2/isolator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/disease2/isolator/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PathogenicIsolator", name)
+ ui.open()
- var/data[0]
+
+/obj/machinery/disease2/isolator/tgui_data(mob/user)
+ var/list/data = list()
data["syringe_inserted"] = !!sample
data["isolating"] = isolating
data["pathogen_pool"] = null
- data["state"] = state
- data["entry"] = entry
- data["can_print"] = (state != HOME || sample) && !isolating
+ data["can_print"] = !isolating
- switch (state)
- if (HOME)
- if (sample)
- var/list/pathogen_pool[0]
- for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
- var/list/virus = B.data["virus2"]
- for (var/ID in virus)
- var/datum/disease2/disease/V = virus[ID]
- var/datum/data/record/R = null
- if (ID in virusDB)
- R = virusDB[ID]
+ var/list/pathogen_pool = list()
+ if(sample)
+ for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
+ var/list/virus = B.data["virus2"]
+ for (var/ID in virus)
+ var/datum/disease2/disease/V = virus[ID]
+ var/datum/data/record/R = null
+ if (ID in virusDB)
+ R = virusDB[ID]
- var/mob/living/carbon/human/D = B.data["donor"]
- pathogen_pool.Add(list(list(\
- "name" = "[D.get_species()] [B.name]", \
- "dna" = B.data["blood_DNA"], \
- "unique_id" = V.uniqueID, \
- "reference" = "\ref[V]", \
- "is_in_database" = !!R, \
- "record" = "\ref[R]")))
+ var/mob/living/carbon/human/D = B.data["donor"]
+ pathogen_pool.Add(list(list(\
+ "name" = "[D.get_species()] [B.name]", \
+ "dna" = B.data["blood_DNA"], \
+ "unique_id" = V.uniqueID, \
+ "reference" = "\ref[V]", \
+ "is_in_database" = !!R, \
+ "record" = "\ref[R]")))
+ data["pathogen_pool"] = pathogen_pool
- if (pathogen_pool.len > 0)
- data["pathogen_pool"] = pathogen_pool
-
- if (LIST)
- var/list/db[0]
- for (var/ID in virusDB)
- var/datum/data/record/r = virusDB[ID]
- db.Add(list(list("name" = r.fields["name"], "record" = "\ref[r]")))
-
- if (db.len > 0)
- data["database"] = db
-
- if (ENTRY)
- if (entry)
- var/desc = entry.fields["description"]
- data["entry"] = list(\
- "name" = entry.fields["name"], \
- "description" = replacetext(desc, "\n", ""))
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "pathogenic_isolator.tmpl", src.name, 400, 500)
- ui.set_initial_data(data)
- ui.open()
+ var/list/db = list()
+ for(var/ID in virusDB)
+ var/datum/data/record/r = virusDB[ID]
+ db.Add(list(list("name" = r.fields["name"], "record" = "\ref[r]")))
+ data["database"] = db
+ data["modal"] = tgui_modal_data(src)
+ return data
/obj/machinery/disease2/isolator/process()
if (isolating > 0)
@@ -120,62 +99,54 @@
virus2 = null
ping("\The [src] pings, \"Viral strain isolated.\"")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
update_icon()
-/obj/machinery/disease2/isolator/Topic(href, href_list)
- if (..()) return 1
+/obj/machinery/disease2/isolator/tgui_act(action, list/params)
+ if(..())
+ return TRUE
var/mob/user = usr
- var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
+ add_fingerprint(user)
+
+ . = TRUE
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_ANSWER)
+ return
- src.add_fingerprint(user)
+ switch(action)
+ if("view_entry")
+ var/datum/data/record/v = locate(params["vir"])
+ if(!istype(v))
+ return FALSE
+ tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"])
+ return TRUE
- if (href_list["close"])
- user.unset_machine()
- ui.close()
- return 0
+ if("print")
+ print(user, params)
+ return TRUE
- if (href_list[HOME])
- state = HOME
- return 1
+ if("isolate")
+ var/datum/disease2/disease/V = locate(params["isolate"])
+ if (V)
+ virus2 = V
+ isolating = 20
+ update_icon()
+ return TRUE
- if (href_list[LIST])
- state = LIST
- return 1
-
- if (href_list[ENTRY])
- if (istype(locate(href_list["view"]), /datum/data/record))
- entry = locate(href_list["view"])
-
- state = ENTRY
- return 1
-
- if (href_list["print"])
- print(user)
- return 1
-
- if(!sample) return 1
-
- if (href_list["isolate"])
- var/datum/disease2/disease/V = locate(href_list["isolate"])
- if (V)
- virus2 = V
- isolating = 20
+ if("eject")
+ if(!sample)
+ return FALSE
+ sample.forceMove(loc)
+ sample = null
update_icon()
- return 1
+ return TRUE
- if (href_list["eject"])
- sample.loc = src.loc
- sample = null
- update_icon()
- return 1
-
-/obj/machinery/disease2/isolator/proc/print(var/mob/user)
+/obj/machinery/disease2/isolator/proc/print(mob/user, list/params)
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
- switch (state)
- if (HOME)
+ switch(params["type"])
+ if("patient_diagnosis")
if (!sample) return
P.name = "paper - Patient Diagnostic Report"
P.info = {"
@@ -207,7 +178,7 @@
Additional Notes:
"}
- if (LIST)
+ if("virus_list")
P.name = "paper - Virus List"
P.info = {"
[virology_letterhead("Virus List")]
@@ -225,11 +196,14 @@
Additional Notes:
"}
- if (ENTRY)
+ if("virus_record")
+ var/datum/data/record/v = locate(params["vir"])
+ if(!istype(v))
+ return FALSE
P.name = "paper - Viral Profile"
P.info = {"
[virology_letterhead("Viral Profile")]
- [entry.fields["description"]]
+ [v.fields["description"]]
Additional Notes:
"}
diff --git a/html/font-awesome/css/all.min.css b/html/font-awesome/css/all.min.css
index e7cdfffe75..a5e67e693d 100644
--- a/html/font-awesome/css/all.min.css
+++ b/html/font-awesome/css/all.min.css
@@ -1,5 +1,5 @@
/*!
- * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com
+ * Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
-.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900}
\ No newline at end of file
+.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900}
\ No newline at end of file
diff --git a/html/font-awesome/css/v4-shims.min.css b/html/font-awesome/css/v4-shims.min.css
index 5f3fdc598c..ee29a2c92d 100644
--- a/html/font-awesome/css/v4-shims.min.css
+++ b/html/font-awesome/css/v4-shims.min.css
@@ -1,5 +1,5 @@
/*!
- * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com
+ * Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
-.fa.fa-glass:before{content:"\f000"}.fa.fa-meetup{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-star-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-file-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-pencil:before{content:"\f303"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-share-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart:before{content:"\f080"}.fa.fa-bar-chart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart-o:before{content:"\f080"}.fa.fa-facebook-square,.fa.fa-twitter-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-lemon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-scissors:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-google-plus,.fa.fa-google-plus-square,.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f3fd"}.fa.fa-comment-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard,.fa.fa-paste{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paste:before{content:"\f328"}.fa.fa-lightbulb-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f381"}.fa.fa-cloud-upload:before{content:"\f382"}.fa.fa-bell-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f089"}.fa.fa-star-half-empty{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f089"}.fa.fa-star-half-full{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f089"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before{content:"\f127"}.fa.fa-shield:before{content:"\f3ed"}.fa.fa-calendar-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ticket:before{content:"\f3ff"}.fa.fa-minus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\f155"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\f156"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f15e"}.fa.fa-sort-amount-asc:before{content:"\f160"}.fa.fa-sort-amount-desc:before{content:"\f161"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f163"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube,.fa.fa-youtube-play,.fa.fa-youtube-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\f195"}.fa.fa-plus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-google,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle,.fa.fa-yahoo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-spoon:before{content:"\f2e5"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-cab:before{content:"\f1ba"}.fa.fa-envelope-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-deviantart,.fa.fa-soundcloud{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-life-bouy,.fa.fa-life-ring{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-bouy:before{content:"\f1cd"}.fa.fa-life-buoy{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-buoy:before{content:"\f1cd"}.fa.fa-life-saver{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-saver:before{content:"\f1cd"}.fa.fa-support{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git,.fa.fa-git-square,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-sliders:before{content:"\f1de"}.fa.fa-futbol-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-angellist,.fa.fa-ioxhost,.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-meanpath{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-meanpath:before{content:"\f2b4"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before{content:"\f224"}.fa.fa-facebook-official{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-clone,.fa.fa-hourglass-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-chrome,.fa.fa-creative-commons,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-internet-explorer,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square,.fa.fa-opera,.fa.fa-safari,.fa.fa-tripadvisor,.fa.fa-wikipedia-w{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-snapchat,.fa.fa-snapchat-ghost,.fa.fa-snapchat-square,.fa.fa-themeisle,.fa.fa-viadeo,.fa.fa-viadeo-square,.fa.fa-yoast{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-spotify,.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 5 Brands";font-weight:400}
\ No newline at end of file
+.fa.fa-glass:before{content:"\f000"}.fa.fa-meetup{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-star-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-file-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-pencil:before{content:"\f303"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-share-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-expand:before{content:"\f424"}.fa.fa-compress:before{content:"\f422"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart:before{content:"\f080"}.fa.fa-bar-chart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart-o:before{content:"\f080"}.fa.fa-facebook-square,.fa.fa-twitter-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-lemon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-scissors:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-google-plus,.fa.fa-google-plus-square,.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f3fd"}.fa.fa-comment-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard,.fa.fa-paste{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paste:before{content:"\f328"}.fa.fa-lightbulb-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f381"}.fa.fa-cloud-upload:before{content:"\f382"}.fa.fa-bell-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f089"}.fa.fa-star-half-empty{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f089"}.fa.fa-star-half-full{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f089"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before{content:"\f127"}.fa.fa-shield:before{content:"\f3ed"}.fa.fa-calendar-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ticket:before{content:"\f3ff"}.fa.fa-minus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\f155"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\f156"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f881"}.fa.fa-sort-amount-asc:before{content:"\f160"}.fa.fa-sort-amount-desc:before{content:"\f884"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f886"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube,.fa.fa-youtube-play,.fa.fa-youtube-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\f195"}.fa.fa-plus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-google,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle,.fa.fa-yahoo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-spoon:before{content:"\f2e5"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-envelope-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-deviantart,.fa.fa-soundcloud,.fa.fa-spotify{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-life-bouy,.fa.fa-life-ring{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-bouy:before{content:"\f1cd"}.fa.fa-life-buoy{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-buoy:before{content:"\f1cd"}.fa.fa-life-saver{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-saver:before{content:"\f1cd"}.fa.fa-support{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git,.fa.fa-git-square,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-sliders:before{content:"\f1de"}.fa.fa-futbol-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-angellist,.fa.fa-ioxhost,.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-meanpath{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-meanpath:before{content:"\f2b4"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before{content:"\f224"}.fa.fa-facebook-official{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-clone,.fa.fa-hourglass-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-chrome,.fa.fa-creative-commons,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-internet-explorer,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square,.fa.fa-opera,.fa.fa-safari,.fa.fa-tripadvisor,.fa.fa-wikipedia-w{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-snapchat,.fa.fa-snapchat-ghost,.fa.fa-snapchat-square,.fa.fa-themeisle,.fa.fa-viadeo,.fa.fa-viadeo-square,.fa.fa-yoast{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cab:before{content:"\f1ba"}
\ No newline at end of file
diff --git a/html/font-awesome/webfonts/fa-regular-400.eot b/html/font-awesome/webfonts/fa-regular-400.eot
index d62be2fad8..479b32cecc 100644
Binary files a/html/font-awesome/webfonts/fa-regular-400.eot and b/html/font-awesome/webfonts/fa-regular-400.eot differ
diff --git a/html/font-awesome/webfonts/fa-regular-400.woff b/html/font-awesome/webfonts/fa-regular-400.woff
index 43b1a9ae49..c390c60e2b 100644
Binary files a/html/font-awesome/webfonts/fa-regular-400.woff and b/html/font-awesome/webfonts/fa-regular-400.woff differ
diff --git a/html/font-awesome/webfonts/fa-solid-900.eot b/html/font-awesome/webfonts/fa-solid-900.eot
index c77baa8d46..52883b93c8 100644
Binary files a/html/font-awesome/webfonts/fa-solid-900.eot and b/html/font-awesome/webfonts/fa-solid-900.eot differ
diff --git a/html/font-awesome/webfonts/fa-solid-900.woff b/html/font-awesome/webfonts/fa-solid-900.woff
index 77c1786227..aff125d658 100644
Binary files a/html/font-awesome/webfonts/fa-solid-900.woff and b/html/font-awesome/webfonts/fa-solid-900.woff differ
diff --git a/nano/templates/adv_med.tmpl b/nano/templates/adv_med.tmpl
deleted file mode 100644
index 6309f73285..0000000000
--- a/nano/templates/adv_med.tmpl
+++ /dev/null
@@ -1,267 +0,0 @@
-
-{{if !data.occupied}}
- No occupant detected.
-{{else}}
- Occupant Data:
-
-
- Name:
-
-
- {{:data.occupant.name}}
-
-
-
-
- Health:
-
- {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 25) ? 'average' : 'bad')}}
-
- {{:helper.round(data.occupant.health / data.occupant.maxHealth)*100}}%
-
-
-
- Status:
-
-
- {{if data.occupant.stat==0}}
- Stable
- {{else data.occupant.stat==1}}
- Non-Responsive
- {{else}}
- Dead
- {{/if}}
-
-
- {{:helper.link('Print', 'document', {'print_p' : 1, 'name' : data.occupant.name})}}
-
Damage:
-
-
- | Brute: |
- {{:data.occupant.bruteLoss}} |
- Brain: |
- {{:data.occupant.brainLoss}} |
-
-
- | Burn: |
- {{:data.occupant.fireLoss}} |
- Radiation: |
- {{:data.occupant.radLoss}} |
-
-
- | Oxygen: |
- {{:data.occupant.oxyLoss}} |
- Genetic: |
- {{:data.occupant.cloneLoss}} |
-
-
- | Toxins: |
- {{:data.occupant.toxLoss}} |
- Paralysis: |
- {{:data.occupant.paralysis}}% ({{:data.occupant.paralysisSeconds}} seconds left!) |
-
-
- | Body Temperature: |
- {{:helper.round(data.occupant.bodyTempC*10)/10}}°C, {{:helper.round(data.occupant.bodyTempF*10)/10}}°F |
-
-
-
- {{if data.occupant.hasVirus}}
-
- Viral pathogen detected in blood stream.
-
- {{/if}}
- {{if data.occupant.hasBorer}}
-
- Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
-
- {{/if}}
- {{if data.occupant.blind}}
- Pupils unresponsive.
- {{/if}}
- {{if data.occupant.nearsighted}}
- Retinal Misalignment Detected
- {{/if}}
- Blood
-
-
- | Volume: |
- {{:data.occupant.blood.volume}} |
- Percent: |
- {{:data.occupant.blood.percent}}% |
-
-
- Blood Reagents
- {{if data.occupant.reagents}}
-
- {{for data.occupant.reagents}}
-
- | {{:value.name}}: |
- {{:value.amount}} |
- {{:value.overdose}} |
-
- {{/for}}
-
- {{else}}
- No blood reagents detected.
- {{/if}}
- Stomach Reagents
- {{if data.occupant.ingested}}
-
- {{for data.occupant.ingested}}
-
- | {{:value.name}}: |
- {{:value.amount}} |
- {{:value.overdose}} |
-
- {{/for}}
-
- {{else}}
- No stomach reagents detected.
- {{/if}}
- External Organs
-
- {{for data.occupant.extOrgan}}
-
- {{if value.status.destroyed}}
-
- {{:value.name}} - DESTROYED
-
- {{else}}
-
-
- {{if value.status.broken}}
- {{:value.status.broken}}
- {{else value.status.splinted}}
- Splinted
- {{else value.status.robotic}}
- Robotic
- {{/if}}
-
-
- Brute/Burn
-
-
- {{:value.bruteLoss}}/{{:value.fireLoss}}
-
-
- Injuries
-
-
- {{if !value.status.bleeding}}
- {{if !value.status.internalBleeding}}
- No Injuries Detected
- {{else}}
-
Internal Bleeding Detected
- {{/if}}
- {{else}}
- {{if value.status.internalBleeding}}
-
Internal Bleeding Detected. External Bleeding Detected.
- {{else}}
-
External Bleeding Detected
- {{/if}}
- {{/if}}
-
- {{if value.germ_level > 100}}
-
- Infection
-
-
- {{if value.germ_level < 300}}
- Mild Infection
- {{else value.germ_level < 400}}
- Mild Infection+
- {{else value.germ_level < 500}}
- Mild Infection++
- {{else value.germ_level < 700}}
- Acute Infection
- {{else value.germ_level < 800}}
- Acute Infection+
- {{else value.germ_level < 950}}
- Acute Infection++
- {{else value.germ_level >= 950}}
- Gangrene Detected
- {{/if}}
-
- {{/if}}
- {{if value.status.dead}}
-
- Necrosis
-
-
- Necrotic Tissue Present
-
- {{/if}}
- {{if value.open}}
-
- Operation Status
-
-
- Open Incision
-
- {{/if}}
- {{if value.implants_len}}
-
- Implants
-
- {{for value.implants :impValue:impindex}}
-
- {{:impValue.known ? impValue.name : "Unknown"}}
-
- {{/for}}
- {{/if}}
- {{/if}}
-
- {{/for}}
-
- Internal Organs
-
- {{for data.occupant.intOrgan}}
-
-
- {{:value.name}}
-
-
- {{:value.desc != null ? value.desc : ""}}
-
-
-
- {{if value.germ_level > 100}}
-
- Infection
-
-
- {{if value.germ_level < 300}}
- Mild Infection
- {{else value.germ_level < 400}}
- Mild Infection+
- {{else value.germ_level < 500}}
- Mild Infection++
- {{else value.germ_level < 700}}
- Acute Infection
- {{else value.germ_level < 800}}
- Acute Infection+
- {{else value.germ_level < 950}}
- Acute Infection++
- {{else value.germ_level >= 950}}
- Necrosis Detected
- {{/if}}
-
- {{/if}}
-
- Damage
-
-
- {{:value.damage}}
-
-
- {{/for}}
-
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/appearance_changer.tmpl b/nano/templates/appearance_changer.tmpl
deleted file mode 100644
index 1111add5c5..0000000000
--- a/nano/templates/appearance_changer.tmpl
+++ /dev/null
@@ -1,86 +0,0 @@
-{{if data.change_race}}
-
-
- Species:
-
-
- {{for data.species}}
- {{:helper.link(value.specimen, null, { 'race' : value.specimen}, null, data.specimen == value.specimen ? 'selected' : null)}}
- {{/for}}
-
-
-{{/if}}
-
-{{if data.change_gender}}
-
-
- Biological Gender:
-
-
- {{for data.genders}}
- {{:helper.link(value.gender_name, null, { 'gender' : value.gender_key}, null, data.gender == value.gender_key ? 'selected' : null)}}
- {{/for}}
-
-
-
-
- Gender Identity:
-
-
- {{for data.id_genders}}
- {{:helper.link(value.gender_name, null, { 'gender_id' : value.gender_key}, null, data.gender_id == value.gender_key ? 'selected' : null)}}
- {{/for}}
-
-
-{{/if}}
-
-{{if data.change_eye_color || data.change_skin_tone || data.change_skin_color || data.change_hair_color || data.change_facial_hair_color}}
-
-
- Colors:
-
-
- {{if data.change_eye_color}}
- {{:helper.link('Change eye color', null, { 'eye_color' : 1})}}
- {{/if}}
- {{if data.change_skin_tone}}
- {{:helper.link('Change skin tone', null, { 'skin_tone' : 1})}}
- {{/if}}
- {{if data.change_skin_color}}
- {{:helper.link('Change skin color', null, { 'skin_color' : 1})}}
- {{/if}}
- {{if data.change_hair_color}}
- {{:helper.link('Change hair color', null, { 'hair_color' : 1})}}
- {{/if}}
- {{if data.change_facial_hair_color}}
- {{:helper.link('Change facial hair color', null, { 'facial_hair_color' : 1})}}
- {{/if}}
-
-
-{{/if}}
-
-{{if data.change_hair}}
-
-
- Hair styles:
-
-
- {{for data.hair_styles}}
- {{:helper.link(value.hairstyle, null, { 'hair' : value.hairstyle}, null, data.hair_style == value.hairstyle ? 'selected' : null)}}
- {{/for}}
-
-
-{{/if}}
-
-{{if data.change_facial_hair}}
-
-
- Facial hair styles:
-
-
- {{for data.facial_hair_styles}}
- {{:helper.link(value.facialhairstyle, null, { 'facial_hair' : value.facialhairstyle}, null, data.facial_hair_style == value.facialhairstyle ? 'selected' : null)}}
- {{/for}}
-
-
-{{/if}}
diff --git a/nano/templates/chem_disp.tmpl b/nano/templates/chem_disp.tmpl
deleted file mode 100644
index eaacb6a1dd..0000000000
--- a/nano/templates/chem_disp.tmpl
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
- Dispense:
-
-
- {{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}}
- {{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}}
- {{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}}
- {{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}}
- {{:helper.link('40', 'gear', {'amount' : 40}, (data.amount == 40) ? 'selected' : null)}}
-
- {{:helper.link('--', '', {'amount' : data.amount-10})}}
- {{:helper.link('-', '', {'amount' : data.amount-1})}}
-
{{:data.amount}}
- {{:helper.link('+', '', {'amount' : data.amount+1})}}
- {{:helper.link('++', '', {'amount' : data.amount+10})}}
-
-
-
-
-
- {{if data.chemicals.length}}
- {{for data.chemicals}}
- {{:helper.link(value.label + " ("+value.amount+")", 'circle-arrow-s', {"dispense":value.label}, null, 'fixedLeftWide')}}
- {{/for}}
- {{else}}
- No cartridges installed!
- {{/if}}
-
-
-
-
-
- {{if data.glass}}
- Glass
- {{else}}
- Beaker
- {{/if}} Contents
-
-
- {{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
-
-
-
-
-
- {{if data.isBeakerLoaded}}
- Volume: {{:data.beakerCurrentVolume}} / {{:data.beakerMaxVolume}}
- {{for data.beakerContents}}
- {{:value.volume}} units of {{:value.name}}
- {{empty}}
-
- {{if data.glass}}
- Glass
- {{else}}
- Beaker
- {{/if}} is empty
-
- {{/for}}
- {{else}}
-
- No
- {{if data.glass}}
- Glass
- {{else}}
- Beaker
- {{/if}} loaded
-
- {{/if}}
-
-
-
diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl
deleted file mode 100644
index f1b981ba6e..0000000000
--- a/nano/templates/chem_master.tmpl
+++ /dev/null
@@ -1,123 +0,0 @@
-
-{{if data.tab == 'home'}}
-
-
{{:helper.link(data.pillBottle ? 'Eject Pill Bottle' : 'No pill bottle inserted', 'eject', {'ejectp' : 1}, data.pillBottle ? null : 'linkOff')}}
-
- {{if data.pillBottle}}
-
{{:data.pillBottle.total}} / {{:data.pillBottle.max}}
- {{/if}}
-
-
{{:helper.link(data.beaker ? 'Eject Beaker and Clear Buffer' : 'Please insert beaker', 'eject', {'eject' : 1}, data.beaker ? null : 'linkOff')}}
-
- {{if data.beaker}}
- {{if data.beaker.total_volume}}
- Add to buffer:
- {{for data.beaker.reagent_list}}
-
-
{{:value.name}}
-
{{:value.volume}} Units
-
-
- {{:helper.link('Analyze', 'signal-diag', {'tab_select' : 'analyze', 'desc' : value.description, 'name' : value.name})}}
- {{:helper.link('1', 'plus', {'add' : value.id, 'amount' : 1})}}
- {{:helper.link('5', 'plus', {'add' : value.id, 'amount' : 5})}}
- {{:helper.link('10', 'plus', {'add' : value.id, 'amount' : 10})}}
- {{:helper.link('30', 'plus', {'add' : value.id, 'amount' : 30})}}
- {{:helper.link('60', 'plus', {'add' : value.id, 'amount' : 60})}}
- {{:helper.link('All', 'plus', {'add' : value.id, 'amount' : value.volume})}}
- {{:helper.link('Custom', 'plus', {'addcustom' : value.id})}}
-
- {{/for}}
- {{else}}
- Beaker is empty.
- {{/if}}
-
-
-
Transfer to
- {{:helper.link(!data.mode ? 'disposal' : 'beaker', null, {'toggle' : 1})}}
-
- {{if data.reagents}}
- {{if data.reagents.total_volume}}
- {{for data.reagents.reagent_list}}
-
-
{{:value.name}}
-
{{:value.volume}} Units
-
-
- {{:helper.link('Analyze', 'signal-diag', {'tab_select' : 'analyze', 'desc' : value.description, 'name' : value.name})}}
- {{:helper.link('1', 'minus', {'remove' : value.id, 'amount' : 1})}}
- {{:helper.link('5', 'minus', {'remove' : value.id, 'amount' : 5})}}
- {{:helper.link('10', 'minus', {'remove' : value.id, 'amount' : 10})}}
- {{:helper.link('30', 'minus', {'remove' : value.id, 'amount' : 30})}}
- {{:helper.link('60', 'minus', {'remove' : value.id, 'amount' : 60})}}
- {{:helper.link('All', 'minus', {'remove' : value.id, 'amount' : value.volume})}}
- {{:helper.link('Custom', 'minus', {'removecustom' : value.id})}}
-
- {{/for}}
- {{/if}}
- {{else}}
- Empty
- {{/if}}
- {{if !data.condi}}
-
-
- {{:helper.link('Create pill (60 units max)', null, {'createpill' : 1})}}
- {{:helper.link('Create multiple pills', null, {'createpill_multiple' : 1})}}
- {{:helper.link('Create bottle (60 units max)', null, {'createbottle' : 1})}}
- {{:helper.link('Create patch (60 units max)', null, {'createpatch' : 1})}}
-
-
- {{:helper.link('', 'pill pill' + data.pillSprite, {'tab_select' : 'pill'}, null, 'link32')}}
- {{:helper.link('', 'pill bottle' + data.bottleSprite, {'tab_select' : 'bottle'}, null, 'link32')}}
-
- {{else}}
-
-
- {{:helper.link('Create bottle (50 units max)', null, {'createbottle' : 1})}}
- {{:helper.link('Create bouillon cube (60 units max)', null, {'createpill' : 1})}}
-
- {{/if}}
- {{/if}}
-
-{{else data.tab == 'analyze'}}
- {{if !data.condi}}
-
Chemical Info:
- {{else}}
-
Condiment Info:
- {{/if}}
-
-
Name:
-
{{:data.analyzeData.name}}
-
- {{if data.analyzeData.name == 'Blood'}}
-
-
Blood Type:
-
{{:data.analyzeData.blood_type}}
-
-
-
DNA:
-
{{:data.analyzeData.blood_DNA}}
-
- {{else}}
-
-
Description:
-
{{:data.analyzeData.desc}}
-
- {{/if}}
- {{:helper.link('Back', 'arrowreturn-1-w', {'tab_select' : 'home'})}}
-
-{{else data.tab == 'pill'}}
- {{for data.pillSpritesAmount}}
- {{:helper.link('', 'pill pill' + value, {'pill_sprite' : value}, null, data.pillSprite == value ? 'linkOn link32' : 'link32')}}
- {{/for}}
-
{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}
-
-{{else data.tab == 'bottle'}}
- {{for data.bottleSpritesAmount}}
- {{:helper.link('', 'pill bottle' + value, {'bottle_sprite' : value}, null, data.bottleSprite == value ? 'linkOn link32' : 'link32')}}
- {{/for}}
-
{{:helper.link('Return', 'arrowreturn-1-w', {'tab_select' : 'home'})}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/cloning.tmpl b/nano/templates/cloning.tmpl
deleted file mode 100644
index a927fcc4d9..0000000000
--- a/nano/templates/cloning.tmpl
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
{{:data.temp}}
-
-{{if data.menu == 1}}
-
-
Modules
-
- {{if data.connected}}
- DNA scanner found.
- {{else}}
- DNA scanner not found.
- {{/if}}
-
-
- {{if data.podsLen > 0}}
- {{:data.podsLen}} cloning vat\s found.
- {{else}}
- No cloning vats found.
- {{/if}}
-
-
-
-
Scanner Functions
-
- {{if data.loading}}
- Scanning...
- {{else}}
- {{:data.scantemp}}
- {{/if}}
-
-
- {{if data.connected}}
-
- {{:helper.link(data.occupant ? 'Scan - ' + data.occupant : 'Scanner unoccupied', 'play', {'scan' : 1}, data.occupant ? null : 'linkOff')}}
-
-
- {{:helper.link(data.locked ? 'Unlock' : 'Lock', data.locked ? 'locked' : 'unlocked', {'lock' : 1}, null, data.locked ? 'redButton' : null)}}
-
-
- {{:helper.link('Eject', 'eject', {'eject' : 1}, data.occupant ? null : 'linkOff')}}
-
- {{else}}
- No scanner connected!
- {{/if}}
-
-
- {{if data.podsLen}}
- {{for data.pods}}
-
{{:value.pod}}, biomass: {{:value.biomass}}
- {{/for}}
- {{/if}}
-
-
-
Database Functions
-
- {{:helper.link('View Records', 'list', {'menu' : 2})}}
- {{:helper.link('Eject Disk', 'eject', {'disk' : 'eject'}, data.diskette ? null : 'linkOff')}}
-
-
-{{else data.menu == 2}}
-
Current records
- {{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 1})}}
-
- {{for data.records}}
- {{:helper.link(value.name, 'document', {'view_rec' : value.ckey})}}
- {{/for}}
-
-
-{{else data.menu == 3}}
-
Selected Record
-
{{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 2})}}
-
- {{if data.activeRecord}}
- {{:helper.link('Delete Record', 'trash', {'del_rec' : 1}, null, 'redButton')}}
-
-
Name:
-
{{:data.activeRecord.real_name}}
-
-
- {{:helper.link('Load from disk', 'transfer-e-w', {'disk' : 'load'}, data.disk ? null : 'linkOff')}}
-
-
-
Save:
- {{:helper.link('UI + UE', 'disk', {'save_disk' : 'ue'}, data.disk ? null : 'linkOff')}}
- {{:helper.link('UI', 'disk', {'save_disk' : 'ui'}, data.disk ? null : 'linkOff')}}
- {{:helper.link('SE', 'disk', {'save_disk' : 'se'}, data.disk ? null : 'linkOff')}}
-
-
- {{:helper.link('Clone', 'play', {'clone' : data.activeRecord.ckey}, data.podsLen ? null : 'linkOff')}}
-
- {{else}}
-
ERROR: Record not found.
- {{/if}}
-
-{{else data.menu == 4}}
- {{:data.temp}}
-
Confirm Record Deletion
-
Scan card to confirm.
- {{:helper.link('Cancel', 'cancel', {'menu' : 3})}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/cryo.tmpl b/nano/templates/cryo.tmpl
deleted file mode 100644
index e8302b07df..0000000000
--- a/nano/templates/cryo.tmpl
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
Cryo Cell Status
-
-
- {{if !data.hasOccupant}}
-
Cell Unoccupied
- {{else}}
-
- {{:data.occupant.name}} =>
- {{if data.occupant.stat == 0}}
- Conscious
- {{else data.occupant.stat == 1}}
- Unconscious
- {{else}}
- DEAD
- {{/if}}
-
-
- {{if data.occupant.stat < 2}}
-
-
Health:
- {{if data.occupant.health >= 0}}
- {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
- {{else}}
- {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
- {{/if}}
-
{{:helper.round(data.occupant.health)}}
-
-
-
-
=> Brute Damage:
- {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
-
{{:helper.round(data.occupant.bruteLoss)}}
-
-
-
-
=> Resp. Damage:
- {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
-
{{:helper.round(data.occupant.oxyLoss)}}
-
-
-
-
=> Toxin Damage:
- {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
-
{{:helper.round(data.occupant.toxLoss)}}
-
-
-
-
=> Burn Severity:
- {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
-
{{:helper.round(data.occupant.fireLoss)}}
-
- {{/if}}
- {{/if}}
-
-
-
Cell Temperature:
- {{:data.cellTemperature}} K
-
-
-
-
-
Cryo Cell Operation
-
-
- Cryo Cell Status:
-
-
- {{:helper.link('On', 'power', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}}
-
-
- {{:helper.link('Eject Occupant', 'arrowreturnthick-1-s', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}}
-
-
-
-
-
- Beaker:
-
-
- {{if data.isBeakerLoaded}}
- {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
- {{if data.beakerVolume}}
- {{:data.beakerVolume}} units remaining
- {{else}}
- Beaker is empty
- {{/if}}
- {{else}}
- No beaker loaded
- {{/if}}
-
-
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
-
-
\ No newline at end of file
diff --git a/nano/templates/disease_splicer.tmpl b/nano/templates/disease_splicer.tmpl
deleted file mode 100644
index d2e874afe6..0000000000
--- a/nano/templates/disease_splicer.tmpl
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
- {{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}}
-
-
-
-{{if data.busy}}
-
The Splicer is currently busy.
-
-
{{:data.busy}}
-
-
- Thank you for your patience!
-
-{{else}}
-
-
Virus Dish
-
-
- {{:helper.link('Eject Dish', 'eject', { 'eject' : 1 }, data.dish_inserted ? null : 'disabled')}}
-
-
-
-
- Growth Density:
-
-
- {{:helper.displayBar(data.growth, 0, 100, (data.growth >= 50) ? 'good' : data.growth >= 25 ? 'average' : 'bad', data.growth + '%' )}}
-
-
-
-
-
- {{if !data.info}}
-
- Symptoms:
-
- {{/if}}
-
- {{if data.info}}
-
{{:data.info}}
- {{else}}
- {{for data.effects}}
-
-
- ({{:value.stage}}) {{:value.name}}
- {{if value.badness > 1}}
- Dangerous
- {{/if}}
-
-
- {{/for}}
- {{/if}}
-
-
- {{if data.affected_species && !data.info}}
-
-
- Affected Species:
-
-
- {{:data.affected_species}}
-
-
- {{/if}}
-
- {{if data.effects}}
-
- CAUTION: Reverse engineering will destroy the viral sample.
-
-
-
- Reverse Engineering:
-
-
- {{for data.effects}}
- {{:helper.link(value.stage, 'transferthick-e-w', { 'grab' : value.reference })}}
- {{/for}}
-
-
- {{:helper.link('Species', 'transferthick-e-w', { 'affected_species' : 1 })}}
-
-
- {{/if}}
-
-
-
Storage
-
-
-
-
- Memory Buffer:
-
-
- {{if data.buffer}}
- {{:data.buffer.name}} ({{:data.buffer.stage}})
- {{else}}
- {{if data.species_buffer}}
- {{:data.species_buffer}}
- {{else}}
- Empty
- {{/if}}
- {{/if}}
-
-
- {{:helper.link('Save To Disk', 'disk', { 'disk' : 1 }, (data.buffer || data.species_buffer) ? null : 'disabled')}}
- {{if data.species_buffer}}
- {{:helper.link('Splice Species', 'pencil', { 'splice' : 5 }, (data.species_buffer && !data.info) ? null : 'disabled')}}
- {{else data.buffer}}
- {{:helper.link('Splice #1', 'pencil', { 'splice' : 1 }, data.buffer.stage > 1 ? 'disabled' : null)}}
- {{:helper.link('Splice #2', 'pencil', { 'splice' : 2 }, data.buffer.stage > 2 ? 'disabled' : null)}}
- {{:helper.link('Splice #3', 'pencil', { 'splice' : 3 }, data.buffer.stage > 3 ? 'disabled' : null)}}
- {{:helper.link('Splice #4', 'pencil', { 'splice' : 4 }, data.buffer.stage > 4 ? 'disabled' : null)}}
- {{/if}}
-{{/if}}
-
-
-
-
-
-
-
-
-
-
diff --git a/nano/templates/dish_incubator.tmpl b/nano/templates/dish_incubator.tmpl
deleted file mode 100644
index ae887e6986..0000000000
--- a/nano/templates/dish_incubator.tmpl
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
- {{:helper.link('Close', 'gear', {'close' : '1'}, null, 'fixedLeft')}}
-
-
-
-
-
Environmental Conditions
-
-
-
- Power:
-
-
- {{:helper.link('On', 'power', { 'power' : 1 }, !data.dish_inserted ? 'disabled' : data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', { 'power' : 1 }, data.on ? null : 'selected')}}
-
-
-
- {{:helper.link('Add Radiation', 'radiation', {'rad' : 1})}}
- {{:helper.link('Flush System', 'trash', {'flush' : 1}, data.system_in_use ? null : 'disabled')}}
-
-
-
-
-
- Virus Food:
-
-
- {{:helper.displayBar(data.food_supply, 0, 100, 'good', data.food_supply)}}
-
-
-
-
- Radiation Level:
-
-
- {{:helper.displayBar(data.radiation, 0, 100, (data.radiation >= 50) ? 'bad' : (data.growth >= 25) ? 'average' : 'good')}}
-
- {{:helper.formatNumber(data.radiation * 10000)}}
µSv
-
-
-
-
- Toxicity:
-
-
- {{:helper.displayBar(data.toxins, 0, 100, (data.toxins >= 50) ? 'bad' : (data.toxins >= 25) ? 'average' : 'good', data.toxins + '%')}}
-
-
-
-
-
-
Chemicals
-
-
- {{:helper.link('Eject Chemicals', 'eject', { 'ejectchem' : 1 }, data.chemicals_inserted ? null : 'disabled')}}
- {{:helper.link('Breed Virus', 'circle-arrow-s', { 'virus' : 1 }, data.can_breed_virus ? null : 'disabled')}}
-
-
-{{if data.chemicals_inserted}}
-
-
- Volume:
-
-
- {{:helper.displayBar(data.chemical_volume, 0, data.max_chemical_volume, 'good', data.chemical_volume + ' / ' + data.max_chemical_volume)}}
-
-
-
-
- Breeding Environment:
-
-
-
- {{:!data.dish_inserted ? 'N/A' : data.can_breed_virus ? 'Suitable' : 'No hemolytic samples detected'}}
-
- {{if data.blood_already_infected}}
-
- CAUTION: Viral infection detected in blood sample.
- {{/if}}
-
-
-{{else}}
-
- No chemicals inserted.
-
-{{/if}}
-
-
-
Virus Dish
-
-
- {{:helper.link('Eject Dish', 'eject', {'ejectdish' : 1}, data.dish_inserted ? null : 'disabled')}}
-
-
-{{if data.dish_inserted}}
- {{if data.virus}}
-
-
- Growth Density:
-
-
- {{:helper.displayBar(data.growth, 0, 100, (data.growth >= 50) ? 'good' : (data.growth >= 25) ? 'average' : 'bad', data.growth + '%' )}}
-
-
-
-
- Infection Rate:
-
-
- {{:data.analysed ? data.infection_rate : "Unknown"}}
-
-
- {{else}}
-
- No virus detected.
-
- {{/if}}
-{{else}}
-
- No dish loaded.
-
-{{/if}}
diff --git a/nano/templates/dna_modifier.tmpl b/nano/templates/dna_modifier.tmpl
deleted file mode 100644
index 9a2804ba98..0000000000
--- a/nano/templates/dna_modifier.tmpl
+++ /dev/null
@@ -1,318 +0,0 @@
-
-
Status
-
-
- {{if !data.hasOccupant}}
-
Cell Unoccupied
- {{else}}
-
- {{:data.occupant.name}} =>
- {{if data.occupant.stat == 0}}
- Conscious
- {{else data.occupant.stat == 1}}
- Unconscious
- {{else}}
- DEAD
- {{/if}}
-
-
- {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}}
-
- The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure.
-
- {{else data.occupant.stat < 2}}
-
-
Health:
- {{if data.occupant.health >= 0}}
- {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
- {{else}}
- {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
- {{/if}}
-
{{:helper.round(data.occupant.health)}}
-
-
-
-
Radiation:
- {{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}}
-
{{:helper.round(data.occupant.radiationLevel)}}
-
-
-
-
Unique Enzymes:
-
{{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : 'Unknown'}}
-
-
-
- {{/if}}
- {{/if}}
-
-
-
-
Operations
-
- {{:helper.link('Modify U.I.', 'link', {'selectMenuKey' : 'ui'}, data.selectedMenuKey == 'ui' ? 'selected' : null)}}
- {{:helper.link('Modify S.E.', 'link', {'selectMenuKey' : 'se'}, data.selectedMenuKey == 'se' ? 'selected' : null)}}
- {{:helper.link('Transfer Buffers', 'disk', {'selectMenuKey' : 'buffer'}, data.selectedMenuKey == 'buffer' ? 'selected' : null)}}
- {{:helper.link('Rejuvenators', 'plusthick', {'selectMenuKey' : 'rejuvenators'}, data.selectedMenuKey == 'rejuvenators' ? 'selected' : null)}}
-
-
-
-
-{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}}
-
Modify Unique Identifier
- {{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}}
-
-
-
- Target:
-
-
- {{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}}
-
{{:data.selectedUITargetHex}}
- {{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}}
-
-
-
-
- {{:helper.link('Irradiate Block', 'radiation', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
-
-
-{{else data.selectedMenuKey == 'se'}}
-
Modify Structural Enzymes
- {{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}}
-
-
-
- {{:helper.link('Irradiate Block', 'radiation', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
-
-
-{{else data.selectedMenuKey == 'buffer'}}
-
Transfer Buffers
- {{for data.buffers}}
-
Buffer {{:(index + 1)}}
-
-
-
- Load Data:
-
-
- {{:helper.link('Subject U.I.', 'link', {'bufferOption' : 'saveUI', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
- {{:helper.link('Subject U.I. + U.E.', 'link', {'bufferOption' : 'saveUIAndUE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
- {{:helper.link('Subject S.E.', 'link', {'bufferOption' : 'saveSE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
- {{:helper.link('From Disk', 'disk', {'bufferOption' : 'loadDisk', 'bufferId' : (index + 1)}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
-
-
- {{if value.data}}
-
-
- Label:
-
-
- {{:helper.link(value.label, 'document-b', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}}
-
-
-
-
- Subject:
-
-
- {{:value.owner ? value.owner : 'Unknown'}}
-
-
-
-
- Stored Data:
-
-
- {{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
- {{:value.ue ? ' + Unique Enzymes' : ''}}
-
-
- {{else}}
-
-
- This buffer is empty.
-
-
- {{/if}}
-
-
- Options:
-
-
- {{:helper.link('Clear', 'trash', {'bufferOption' : 'clear', 'bufferId' : (index + 1)}, !value.data ? 'disabled' : null)}}
- {{:helper.link('Injector', data.isInjectorReady ? 'pencil' : 'clock', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1)}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
- {{:helper.link('Block Injector', data.isInjectorReady ? 'pencil' : 'clock', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1), 'createBlockInjector' : 1}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
- {{:helper.link('Transfer', 'radiation', {'bufferOption' : 'transfer', 'bufferId' : (index + 1)}, (!data.hasOccupant || !value.data) ? 'disabled' : null)}}
- {{:helper.link('Save To Disk', 'disk', {'bufferOption' : 'saveDisk', 'bufferId' : (index + 1)}, (!value.data || !data.hasDisk) ? 'disabled' : null)}}
-
-
-
- {{/for}}
-
-
Data Disk
-
- {{if data.hasDisk}}
- {{if data.disk.data}}
-
-
- Label:
-
-
- {{:data.disk.label ? data.disk.label : 'No Label'}}
-
-
-
-
- Subject:
-
-
- {{:data.disk.owner ? data.disk.owner : 'Unknown'}}
-
-
-
-
- Stored Data:
-
-
- {{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
- {{:data.disk.ue ? ' + Unique Enzymes' : ''}}
-
-
- {{else}}
-
- {{/if}}
- {{else}}
-
-
- No disk inserted.
-
-
- {{/if}}
-
-
- Options:
-
-
- {{:helper.link('Wipe Disk', 'trash', {'bufferOption' : 'wipeDisk'}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
- {{:helper.link('Eject Disk', 'eject', {'bufferOption' : 'ejectDisk'}, !data.hasDisk ? 'disabled' : null)}}
-
-
-
-{{else data.selectedMenuKey == 'rejuvenators'}}
-
Rejuvenators
-
-
- Inject:
-
-
- {{:helper.link('5', 'pencil', {'injectRejuvenators' : 5}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
- {{:helper.link('10', 'pencil', {'injectRejuvenators' : 10}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
- {{:helper.link('20', 'pencil', {'injectRejuvenators' : 20}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
- {{:helper.link('30', 'pencil', {'injectRejuvenators' : 30}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
- {{:helper.link('50', 'pencil', {'injectRejuvenators' : 50}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
-
-
-
-
-
- Beaker:
-
-
- {{if data.isBeakerLoaded}}
- {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
- {{if data.beakerVolume}}
- {{:data.beakerVolume}} units remaining
- {{else}}
- Beaker is empty
- {{/if}}
- {{else}}
- No beaker loaded
- {{/if}}
-
-
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
-
-
-{{/if}}
-
-
-
-{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}}
-
Radiation Emitter Settings
-
-
- Intensity:
-
-
- {{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}}
-
{{:data.radiationIntensity}}
- {{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}}
-
-
-
-
- Duration:
-
-
- {{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}}
-
{{:data.radiationDuration}}
- {{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}}
-
-
-
-
-
-
-
- {{:helper.link('Pulse Radiation', 'radiation', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}}
-
-
-{{/if}}
-
-
-
-
-
-
-
- Occupant:
-
-
- {{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}}
-
-
-
-
- Door Lock:
-
-
- {{:helper.link('Engaged', 'locked', {'toggleLock' : 1}, data.locked ? 'selected' : !data.hasOccupant ? 'disabled' : null)}}
- {{:helper.link('Disengaged', 'unlocked', {'toggleLock' : 1}, !data.locked ? 'selected' : data.irradiating ? 'disabled' : null)}}
-
-
-
-{{if data.irradiating}}
-
-
-
Irradiating Subject
- For {{:data.irradiating}} seconds.
-
-
-{{/if}}
-
diff --git a/nano/templates/isolation_centrifuge.tmpl b/nano/templates/isolation_centrifuge.tmpl
deleted file mode 100644
index 4ff0272df0..0000000000
--- a/nano/templates/isolation_centrifuge.tmpl
+++ /dev/null
@@ -1,81 +0,0 @@
-
- {{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}}
- {{:helper.link('Print', 'print', { 'print' : 1 }, data.antibodies || data.pathogens ? null : 'disabled', 'fixedLeft')}}
-
-
-{{if data.busy}}
-
The Centrifuge is currently busy.
-
-
{{:data.busy}}
-
-
- Thank you for your patience!
-
-{{else}}
-
-
{{:data.is_antibody_sample ? 'Antibody Sample' : 'Blood Sample'}}
-
-
- {{:helper.link('Eject Vial', 'eject', { 'action' : 'sample' }, data.sample_inserted ? null : 'disabled')}}
-
- {{if data.sample_inserted}}
- {{if data.antibodies || data.pathogens}}
-
- {{if data.antibodies}}
-
-
- Antibodies:
-
-
- {{:data.antibodies}}
-
-
- {{/if}}
- {{if data.pathogens}}
-
-
- Pathogens:
-
-
- {{for data.pathogens}}
-
- {{:value.name}} ({{:value.spread_type}})
-
- {{/for}}
-
-
- {{/if}}
-
- {{else}}
-
- No antibodies or viral strains detected.
-
- {{/if}}
- {{else}}
-
- No vial detected.
-
- {{/if}}
- {{if data.antibodies && !data.is_antibody_sample}}
-
-
- Isolate Antibodies:
-
-
- {{:helper.link(data.antibodies, 'pencil', { 'action' : 'antibody' })}}
-
-
- {{/if}}
- {{if data.pathogens}}
-
-
- Isolate Strain:
-
-
- {{for data.pathogens}}
- {{:helper.link(value.name, 'pencil', { 'isolate' : value.reference })}}
- {{/for}}
-
-
- {{/if}}
-{{/if}}
diff --git a/nano/templates/operating.tmpl b/nano/templates/operating.tmpl
deleted file mode 100644
index c53bd94c65..0000000000
--- a/nano/templates/operating.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-{{if data.table}}
-
Patient Information:
- {{if data.victim}}
-
-
Name:
-
{{:data.victim.real_name}}
-
-
Age:
-
{{:data.victim.age}}
-
-
Blood Type:
-
{{:data.victim.b_type}}
-
-
-
-
Health:
-
{{:data.victim.health}}
-
-
Brute Damage:
-
{{:data.victim.brute}}
-
-
Toxins Damage:
-
{{:data.victim.tox}}
-
-
Fire Damage:
-
{{:data.victim.burn}}
-
-
Suffocation Damage:
-
{{:data.victim.oxy}}
-
-
Patient Status:
-
{{:data.victim.stat}}
-
-
Heartbeat Rate:
-
{{:data.victim.pulse}}
-
- {{else}}
- No Patient Detected
- {{/if}}
-{{else}}
- No Table Detected
-{{/if}}
diff --git a/nano/templates/pathogenic_isolator.tmpl b/nano/templates/pathogenic_isolator.tmpl
deleted file mode 100644
index 69411d858f..0000000000
--- a/nano/templates/pathogenic_isolator.tmpl
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
Menu
-
-
- {{if !data.isolating}}
- {{:helper.link('Home', 'home', {'home' : 1}, data.state == 'home' ? 'disabled' : null, 'fixedLeft')}}
- {{:helper.link('List', 'note', {'list' : 1}, data.state == 'list' ? 'disabled' : null, 'fixedLeft')}}
- {{:helper.link('Pathogen', 'folder-open', {'entry' : 1}, data.state == 'entry' ? 'disabled' : null, 'fixedLeft')}}
- {{/if}}
-
- {{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}}
- {{:helper.link('Print', 'print', { 'print' : 1 }, data.can_print ? null : 'disabled', 'fixedLeft')}}
-
-
-{{if data.isolating}}
-
The Isolator is currently busy.
-
-
Isolating pathogens...
-
-
- Thank you for your patience!
-
-{{else}}
- {{if data.state =="home"}}
-
-
Blood Sample
-
-
- {{:helper.link('Eject Syringe', 'eject', { 'eject' : 1 }, data.syringe_inserted ? null : 'disabled')}}
-
-
- {{if data.syringe_inserted}}
-
-
Pathogens:
- {{if data.pathogen_pool}}
- {{for data.pathogen_pool}}
-
- {{:index + 1}}. #{{:value.unique_id}} {{:value.is_in_database ? "(Analysed)" : ""}}
- {{:value.name}}: {{:value.dna}}
-
- {{/for}}
- {{else}}
- No pathogens detected.
- {{/if}}
-
- {{else}}
-
- No syringe loaded.
-
- {{/if}}
- {{if data.pathogen_pool}}
-
-
- Isolate Pathogens:
-
-
- {{for data.pathogen_pool}}
- {{:helper.link('#' + value.unique_id, 'pencil', { 'isolate' : value.reference }, null, 'fixedLeft')}}
- {{/for}}
-
-
-
-
- Database Lookup:
-
-
- {{for data.pathogen_pool}}
- {{if value.is_in_database}}
- {{:helper.link('#' + value.unique_id, 'info', { 'entry' : 1, 'view' : value.record }, null, 'fixedLeft')}}
- {{/if}}
- {{/for}}
-
-
- {{/if}}
- {{else}}
- {{if data.state == "list"}}
-
-
View Database
-
-
- {{if data.database}}
- {{for data.database}}
-
-
{{:value.name}}
- {{:helper.link('Details', 'circle-arrow-s', { 'entry' : 1, 'view' : value.record }, null, 'fixedLeft')}}
-
- {{/for}}
- {{else}}
-
The viral database is empty.
- {{/if}}
-
- {{else}}
- {{if data.state == "entry"}}
- {{if data.entry}}
-
-
{{:data.entry.name}}
-
-
- {{:data.entry.description}}
-
- {{else}}
-
No virus selected.
- {{/if}}
- {{/if}}
- {{/if}}
- {{/if}}
-{{/if}}
diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl
deleted file mode 100644
index 885a1c025c..0000000000
--- a/nano/templates/sleeper.tmpl
+++ /dev/null
@@ -1,102 +0,0 @@
-
Sleeper
-{{if !data.power}}
-
-
- NO POWER
-
- {{if data.occupant}}
-
- {{:helper.link("Eject occupant", null, {'eject' : 0})}}
-
- {{/if}}
-{{else}}
- {{if data.occupant}}
-
-
- Occupant status:
-
-
- Health: {{:helper.round(data.health / data.maxHealth)*100}}% ({{:data.stat}}).
-
-
- Pulse:
-
-
- {{:data.pulse}}
-
-
- Brute damage:
-
-
- {{:helper.displayBar(data.brute, 0, 100, (data.brute <= 25) ? 'good' : (data.brute <= 50) ? 'average' : 'bad')}}{{:helper.round(data.brute)}}
-
-
- Burn severity:
-
-
- {{:helper.displayBar(data.burn, 0, 100, (data.burn <= 25) ? 'good' : (data.burn <= 50) ? 'average' : 'bad')}}{{:helper.round(data.burn)}}
-
-
- Respiratory damage:
-
-
- {{:helper.displayBar(data.oxy, 0, 100, (data.oxy <= 25) ? 'good' : (data.oxy <= 50) ? 'average' : 'bad')}}{{:helper.round(data.oxy)}}
-
-
- Toxin content:
-
-
- {{:helper.displayBar(data.tox, 0, 100, (data.tox <= 25) ? 'good' : (data.tox <= 50) ? 'average' : 'bad')}}{{:helper.round(data.tox)}}
-
-
- {{:helper.link(data.filtering ? "Dialysis active" : "Dialysis inactive", null, {'sleeper_filter' : !data.filtering})}}
-
-
- {{:helper.link(data.pump ? "Stomach pump active" : "Stomach pump inactive", null, {'pump' : !data.pump})}}
-
-
- {{:helper.link("Eject occupant", null, {'eject' : 0})}}
-
-
- {{else}}
-
- No occupant.
-
- {{/if}}
-
- {{for data.reagents}}
-
- {{:value.name}}
-
-
- {{if data.occupant}}Occupant: {{:value.amount}} units{{/if}}
- {{:helper.link('Inject 5', null, {'chemical' : value.id, 'amount' : 5}, data.occupant ? null : 'disabled')}}{{:helper.link('Inject 10', null, {'chemical' : value.id, 'amount' : 10}, data.occupant ? null : 'disabled')}}
-
- {{/for}}
-
- {{if data.beaker != -1}}
-
-
- Beaker:
-
-
- {{:data.beaker}} units of free space remaining.
- {{:helper.link("Eject", null, {'beaker' : 0})}}
-
-
- {{else}}
-
-
- No beaker inserted.
-
-
- {{/if}}
-
-
- Stasis Level:
-
-
- {{:helper.link(data.stasis, null, {'change_stasis' : 1})}}
-
-
-{{/if}}
diff --git a/nano/templates/smartfridge.tmpl b/nano/templates/smartfridge.tmpl
deleted file mode 100644
index cc25d760ce..0000000000
--- a/nano/templates/smartfridge.tmpl
+++ /dev/null
@@ -1,36 +0,0 @@
-
- {{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}}
-
-
-
-
Storage
- {{if data.secure}}
-
- {{:data.locked == -1 ? "Sec.re ACC_** //):securi_nt.diag=>##'or 1=1'%($..." : "Secure Access: Please have your identification ready."}}
-
- {{/if}}
-
-
- {{if data.contents}}
- {{for data.contents}}
-
-
{{:value.display_name}} ({{:value.quantity}} available)
-
Vend:
{{:helper.link('x1', 'circle-arrow-s', { "vend" : value.vend, "amount" : 1 }, null, 'statusValue')}}
- {{if value.quantity >= 5}}
- {{:helper.link('x5', 'circle-arrow-s', { "vend" : value.vend, "amount" : 5 }, null, 'statusValue')}}
- {{/if}}
- {{if value.quantity >= 10}}
- {{:helper.link('x10', 'circle-arrow-s', { "vend" : value.vend, "amount" : 10 }, null, 'statusValue')}}
- {{/if}}
- {{if value.quantity >= 25}}
- {{:helper.link('x25', 'circle-arrow-s', { "vend" : value.vend, "amount" : 25 }, null, 'statusValue')}}
- {{/if}}
- {{if value.quantity > 1}}
- {{:helper.link('All', 'circle-arrow-s', { "vend" : value.vend, "amount" : value.quantity }, null, 'statusValue')}}
- {{/if}}
-
- {{/for}}
- {{else}}
-
No products loaded.
- {{/if}}
-
diff --git a/polaris.dme b/polaris.dme
index 1424445368..ca2cac4f3c 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -2506,7 +2506,6 @@
#include "code\modules\nano\interaction\remote.dm"
#include "code\modules\nano\interaction\self.dm"
#include "code\modules\nano\interaction\zlevel.dm"
-#include "code\modules\nano\modules\human_appearance.dm"
#include "code\modules\nano\modules\law_manager.dm"
#include "code\modules\nano\modules\nano_module.dm"
#include "code\modules\organs\blood.dm"
@@ -2936,10 +2935,12 @@
#include "code\modules\tables\update_triggers.dm"
#include "code\modules\tension\tension.dm"
#include "code\modules\tgui\external.dm"
+#include "code\modules\tgui\modal.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\tgui_window.dm"
#include "code\modules\tgui\modules\_base.dm"
+#include "code\modules\tgui\modules\appearance_changer.dm"
#include "code\modules\tgui\modules\camera.dm"
#include "code\modules\tgui\modules\crew_monitor.dm"
#include "code\modules\tgui\states\admin.dm"