diff --git a/.travis.yml b/.travis.yml
index 58cfbe98583..e4443d14ee0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,8 +19,9 @@ jobs:
- tools/travis/install_dreamchecker.sh
script:
- find . -name "*.php" -print0 | xargs -0 -n1 php -l
- - find . -name "*.json" -not -path "./nano/node_modules/*" -print0 | xargs -0 python3 ./tools/travis/json_verifier.py
+ - find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/travis/json_verifier.py
- tools/travis/build_nanoui.sh
+ - tools/travis/build_tgui.sh
- tools/travis/check_grep.sh
- python3 tools/travis/check_line_endings.py
- ~/dreamchecker
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 9ab4061ce6a..1c3b2d12286 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -2,6 +2,7 @@
"recommendations": [
"gbasood.byond-dm-language-support",
"platymuus.dm-langclient",
- "EditorConfig.EditorConfig"
+ "EditorConfig.EditorConfig",
+ "dbaeumer.vscode-eslint"
]
}
diff --git a/_build_dependencies.sh b/_build_dependencies.sh
index a6ce9f31cb5..1c164d3035d 100644
--- a/_build_dependencies.sh
+++ b/_build_dependencies.sh
@@ -1,8 +1,8 @@
# This file has all the information on what versions of libraries are thrown into the code
# For dreamchecker
export SPACEMANDMM_TAG=suite-1.4
-# For NanoUI
-export NODE_VERSION=9
+# For NanoUI + TGUI
+export NODE_VERSION=12
# For the scripts in tools
export PHP_VERSION=5.6
# Byond Major
diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm
index 54ad9d08447..93815774e61 100644
--- a/_maps/map_files/Delta/delta.dmm
+++ b/_maps/map_files/Delta/delta.dmm
@@ -92226,6 +92226,9 @@
pressure_checks = 1
},
/obj/effect/decal/warning_stripes/yellow/hollow,
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "neutralfull"
@@ -94897,6 +94900,9 @@
on = 1
},
/obj/effect/decal/warning_stripes/yellow/hollow,
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "neutralfull"
diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
index 0c387859c7a..f7eda3fe8ee 100644
--- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
@@ -42197,9 +42197,6 @@
},
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/hologram/holopad,
-/obj/effect/landmark/start{
- name = "Cyborg"
- },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -43998,6 +43995,9 @@
d2 = 8;
icon_state = "4-8"
},
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -44010,6 +44010,9 @@
d2 = 8;
icon_state = "4-8"
},
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -44025,6 +44028,9 @@
d2 = 8;
icon_state = "4-8"
},
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -45008,6 +45014,9 @@
icon_state = "1-4"
},
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm
index 081839d2033..f3bc26c9600 100644
--- a/_maps/map_files/cyberiad/cyberiad.dmm
+++ b/_maps/map_files/cyberiad/cyberiad.dmm
@@ -91252,6 +91252,9 @@
initialize_directions = 11;
level = 1
},
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "grimy"
},
@@ -91732,6 +91735,9 @@
icon_state = "1-2";
pixel_y = 0
},
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
/turf/simulated/floor/plasteel{
icon_state = "grimy"
},
@@ -96174,6 +96180,24 @@
},
/turf/simulated/floor/bluegrid,
/area/tcommsat/chamber)
+"rWq" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/effect/landmark/start{
+ name = "Cyborg"
+ },
+/turf/simulated/floor/plasteel{
+ dir = 5;
+ icon_state = "dark";
+ tag = "icon-vault (NORTHEAST)"
+ },
+/area/turret_protected/aisat_interior)
"rYq" = (
/obj/machinery/door/airlock/centcom{
id_tag = "adminshuttle";
@@ -133007,7 +133031,7 @@ diC
diE
diM
djD
-djJ
+rWq
dka
djJ
dkf
diff --git a/_maps/map_files/generic/Lavaland.dmm b/_maps/map_files/generic/Lavaland.dmm
index a3d4b951265..e44b9f52458 100644
--- a/_maps/map_files/generic/Lavaland.dmm
+++ b/_maps/map_files/generic/Lavaland.dmm
@@ -924,6 +924,7 @@
/obj/structure/cable{
icon_state = "1-2"
},
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/production)
"cn" = (
@@ -1008,6 +1009,11 @@
/obj/effect/turf_decal/tile/brown{
dir = 2
},
+/obj/machinery/camera{
+ c_tag = "Shuttle Docking Foyer North";
+ dir = 8;
+ network = list("Mining Outpost")
+ },
/turf/simulated/floor/plasteel,
/area/mine/production)
"cx" = (
@@ -1428,11 +1434,6 @@
/obj/machinery/light{
dir = 4
},
-/obj/machinery/camera{
- c_tag = "Shuttle Docking Foyer";
- dir = 8;
- network = list("Mining Outpost")
- },
/obj/machinery/newscaster{
pixel_x = 30;
pixel_y = 1
@@ -1443,6 +1444,11 @@
/obj/effect/turf_decal/tile/brown{
dir = 2
},
+/obj/machinery/camera{
+ c_tag = "Shuttle Docking Foyer South";
+ dir = 8;
+ network = list("Mining Outpost")
+ },
/turf/simulated/floor/plasteel,
/area/mine/production)
"dw" = (
@@ -1897,6 +1903,7 @@
/obj/structure/cable{
icon_state = "1-4"
},
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/living_quarters)
"eu" = (
@@ -2019,6 +2026,7 @@
/obj/structure/cable{
icon_state = "2-8"
},
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/production)
"eD" = (
@@ -2396,6 +2404,7 @@
/obj/effect/turf_decal/tile/purple{
dir = 1
},
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/living_quarters)
"fv" = (
@@ -2794,6 +2803,7 @@
dir = 1
},
/obj/effect/turf_decal/tile/bar,
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/living_quarters)
"gj" = (
@@ -3056,6 +3066,7 @@
dir = 6
},
/obj/effect/baseturf_helper/lava_land/surface,
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel/dark,
/area/mine/maintenance)
"gJ" = (
@@ -4119,6 +4130,7 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
+/obj/machinery/hologram/holopad,
/turf/simulated/floor/plasteel,
/area/mine/production)
"BD" = (
@@ -4266,6 +4278,10 @@
},
/turf/simulated/floor/plasteel,
/area/mine/production)
+"Ww" = (
+/obj/machinery/hologram/holopad,
+/turf/simulated/floor/plasteel,
+/area/mine/eva)
"WO" = (
/obj/effect/spawner/window,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -20693,7 +20709,7 @@ ab
bg
bs
bY
-bY
+Ww
bU
cp
bf
diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm
index 002b3ecc691..8dd95119aff 100644
--- a/code/ATMOSPHERICS/atmospherics.dm
+++ b/code/ATMOSPHERICS/atmospherics.dm
@@ -59,7 +59,7 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
for(var/mob/living/L in src) //ventcrawling is serious business
L.remove_ventcrawl()
L.forceMove(get_turf(src))
- QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else
+ QDEL_NULL(pipe_image) //we have to qdel it, or it might keep a ref somewhere else
return ..()
// Icons/overlays/underlays
diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm
index 6ec505d9f9a..30c56f93cc8 100644
--- a/code/LINDA/LINDA_fire.dm
+++ b/code/LINDA/LINDA_fire.dm
@@ -176,7 +176,7 @@
if(istype(loc, /turf/simulated))
var/turf/simulated/T = loc
- if(T.to_be_destroyed)
+ if(T.to_be_destroyed && !T.changing_turf)
var/chance_of_deletion
if(T.heat_capacity) //beware of division by zero
chance_of_deletion = T.max_fire_temperature_sustained / T.heat_capacity * 8 //there is no problem with prob(23456), min() was redundant --rastaf0
diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm
index c39226f37e6..acfa1983f72 100644
--- a/code/LINDA/LINDA_turf_tile.dm
+++ b/code/LINDA/LINDA_turf_tile.dm
@@ -74,8 +74,8 @@
air.temperature = temperature
/turf/simulated/Destroy()
- visibilityChanged()
QDEL_NULL(active_hotspot)
+ QDEL_NULL(wet_overlay)
return ..()
/turf/simulated/assume_air(datum/gas_mixture/giver)
diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm
index 58571e8b1ca..5fd85513501 100644
--- a/code/__DEFINES/genetics.dm
+++ b/code/__DEFINES/genetics.dm
@@ -145,7 +145,6 @@
#define RESISTCOLD "resist_cold"
#define NO_EXAMINE "no_examine"
#define CAN_WINGDINGS "can_wingdings"
-#define NOZOMBIE "no_zombie"
#define NO_GERMS "no_germs"
#define NO_DECAY "no_decay"
#define PIERCEIMMUNE "pierce_immunity"
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index b52d08c1d23..2b8c515e81a 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -3,7 +3,6 @@
// Mobs
#define ismegafauna(A) istype(A, /mob/living/simple_animal/hostile/megafauna)
-#define iszombie(A) (is_species(A, /datum/species/zombie))
//Simple animals
#define isshade(A) (istype(A, /mob/living/simple_animal/shade))
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index e1e15a926d6..719274857ec 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -196,7 +196,7 @@
#define isslimeperson(A) (is_species(A, /datum/species/slime))
#define isgrey(A) (is_species(A, /datum/species/grey))
#define isdiona(A) (is_species(A, /datum/species/diona))
-#define ismachine(A) (is_species(A, /datum/species/machine))
+#define ismachineperson(A) (is_species(A, /datum/species/machine))
#define isdrask(A) (is_species(A, /datum/species/drask))
#define isanimal(A) (istype((A), /mob/living/simple_animal))
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index b0274e9d802..c15dfd78b0f 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -15,3 +15,57 @@
#define SOUND_MINIMUM_PRESSURE 10
#define FALLOFF_SOUNDS 0.5
+
+//Ambience types
+
+#define GENERIC_SOUNDS list('sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg',\
+ 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg',\
+ 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen7.ogg',\
+ 'sound/ambience/ambigen8.ogg', 'sound/ambience/ambigen9.ogg',\
+ 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambigen11.ogg',\
+ 'sound/ambience/ambigen12.ogg', 'sound/ambience/ambigen14.ogg', 'sound/ambience/ambigen15.ogg')
+
+#define HOLY_SOUNDS list('sound/ambience/ambicha1.ogg', 'sound/ambience/ambicha2.ogg', 'sound/ambience/ambicha3.ogg',\
+ 'sound/ambience/ambicha4.ogg', 'sound/ambience/ambiholy.ogg', 'sound/ambience/ambiholy2.ogg',\
+ 'sound/ambience/ambiholy3.ogg')
+
+#define HIGHSEC_SOUNDS list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg')
+
+#define RUINS_SOUNDS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambitech3.ogg',\
+ 'sound/ambience/ambimystery.ogg', 'sound/ambience/ambimaint1.ogg')
+
+#define ENGINEERING_SOUNDS list('sound/ambience/ambisin1.ogg', 'sound/ambience/ambisin2.ogg', 'sound/ambience/ambisin3.ogg', 'sound/ambience/ambisin4.ogg',\
+ 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg')
+
+#define MINING_SOUNDS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg',\
+ 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava2.ogg', 'sound/ambience/ambilava3.ogg')
+
+#define MEDICAL_SOUNDS list('sound/ambience/ambinice.ogg')
+
+#define SPOOKY_SOUNDS list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/ambience/ambiruin7.ogg','sound/ambience/ambiruin6.ogg',\
+ 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambimystery.ogg')
+
+#define SPACE_SOUNDS list('sound/ambience/ambispace.ogg', 'sound/ambience/ambispace2.ogg', 'sound/music/title2.ogg', 'sound/ambience/ambiatmos.ogg')
+
+#define MAINTENANCE_SOUNDS list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg',\
+ 'sound/ambience/ambimaint5.ogg', 'sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg', 'sound/ambience/ambitech2.ogg' )
+
+#define AWAY_MISSION_SOUNDS list('sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint.ogg',\
+ 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambiodd.ogg')
+
+
+
+#define CREEPY_SOUNDS list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\
+ 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
+ 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
+ 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
+ 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm
index b0a74b6c702..c9763e4836c 100644
--- a/code/__DEFINES/stat.dm
+++ b/code/__DEFINES/stat.dm
@@ -3,7 +3,7 @@
#define UNCONSCIOUS 1
#define DEAD 2
-// NanoUI flags
+// NanoUI & TGUI flags
#define STATUS_INTERACTIVE 2 // GREEN Visability
#define STATUS_UPDATE 1 // ORANGE Visability
#define STATUS_DISABLED 0 // RED Visability
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index f5d582f5e42..a3154834fe4 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -143,6 +143,9 @@ GLOBAL_VAR_INIT(log_end, (world.system_type == UNIX ? ascii2text(13) : ""))
/proc/log_runtime_summary(text)
WRITE_LOG(GLOB.runtime_summary_log, "[text]")
+/proc/log_tgui(text)
+ WRITE_LOG(GLOB.tgui_log, "[text]")
+
/**
* Standardized method for tracking startup times.
*/
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index b519130bb4f..bd077204f00 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -344,7 +344,7 @@
for(var/i = 1; i <= GLOB.player_list.len; i++)
var/mob/M = GLOB.player_list[i]
if(M && M.client)
- if(istype(M, /mob/new_player)) // exclude people in the lobby
+ if(isnewplayer(M)) // exclude people in the lobby
continue
else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
var/mob/dead/observer/O = M
diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm
index 549f5c71016..b66b1a11b90 100644
--- a/code/__HELPERS/unique_ids.dm
+++ b/code/__HELPERS/unique_ids.dm
@@ -16,8 +16,6 @@
GLOBAL_VAR_INIT(next_unique_datum_id, 1)
-// /client/var/tmp/unique_datum_id = null
-
/datum/proc/UID()
if(!unique_datum_id)
var/tag_backup = tag
@@ -37,8 +35,6 @@ GLOBAL_VAR_INIT(next_unique_datum_id, 1)
var/datum/D = locate(copytext(uid, 1, splitat))
- // We might locate a client instead of a datum, but just using : is easier
- // than actually checking and typecasting
- if(D && D:unique_datum_id == uid)
+ if(D && D.unique_datum_id == uid)
return D
return null
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 75d9f5c79f4..b3b44335a6a 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -329,8 +329,9 @@ Turf and target are seperate in case you want to teleport some distance from a t
/proc/select_active_ai_with_fewest_borgs()
var/mob/living/silicon/ai/selected
var/list/active = active_ais()
- for(var/mob/living/silicon/ai/A in active)
- if(!selected || (selected.connected_robots > A.connected_robots))
+ for(var/thing in active)
+ var/mob/living/silicon/ai/A = thing
+ if(!selected || (length(selected.connected_robots) > length(A.connected_robots)))
selected = A
return selected
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 931a0e394e8..21af95cab48 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -16,7 +16,6 @@ GLOBAL_LIST_INIT(prisoncomputer_list, list())
GLOBAL_LIST_INIT(celltimers_list, list()) // list of all cell timers
GLOBAL_LIST_INIT(cell_logs, list())
GLOBAL_LIST_INIT(navigation_computers, list())
-GLOBAL_LIST_INIT(zombie_infection_list, list())
GLOBAL_LIST_INIT(all_areas, list())
GLOBAL_LIST_INIT(machines, list())
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 0ea513708a1..f5f71e65c57 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -15,6 +15,8 @@ GLOBAL_VAR(world_asset_log)
GLOBAL_PROTECT(world_asset_log)
GLOBAL_VAR(runtime_summary_log)
GLOBAL_PROTECT(runtime_summary_log)
+GLOBAL_VAR(tgui_log)
+GLOBAL_PROTECT(tgui_log)
GLOBAL_LIST_EMPTY(jobMax)
GLOBAL_PROTECT(jobMax)
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index 74591eb8070..c541e40768a 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -4,7 +4,7 @@ GLOBAL_DATUM(slmaster, /obj/effect/overlay)
GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule
GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
-// Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it.
+// Announcer intercom, because too much stuff creates an intercom for one message then qdel()s it.
GLOBAL_DATUM_INIT(global_announcer, /obj/item/radio/intercom, create_global_announcer())
GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_command_announcer())
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 6f38814758a..f88244c0cd8 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -75,8 +75,8 @@
if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
- user.lastattacked = M
- M.lastattacker = user
+ M.lastattacker = user.real_name
+ M.lastattackerckey = user.ckey
user.do_attack_animation(M)
. = M.attacked_by(src, user, def_zone)
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 80a9651f6ea..3452400edbb 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -63,7 +63,7 @@
if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the health_scan var.
return
if(user.client && user.health_scan)
- if(issilicon(src) || ismachine(src))
+ if(issilicon(src) || ismachineperson(src))
robot_healthscan(user, src)
else if(ishuman(src))
healthscan(user, src, 1, TRUE)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index d348747fb80..083b347e52c 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -36,7 +36,7 @@
var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi)
var/vote_no_dead = 0 // dead people can't vote (tbi)
// var/enable_authentication = 0 // goon authentication
- var/del_new_on_log = 1 // del's new players if they log before they spawn in
+ var/del_new_on_log = 1 // qdel's new players if they log before they spawn in
var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard
var/traitor_scaling = 0 //if amount of traitors scales based on amount of players
var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other
diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm
index 037b9a2bd0a..951a7fb1786 100644
--- a/code/controllers/subsystem/afk.dm
+++ b/code/controllers/subsystem/afk.dm
@@ -49,7 +49,7 @@ SUBSYSTEM_DEF(afk)
if(mins_afk >= config.auto_cryo_afk && A.can_get_auto_cryod)
if(A.fast_despawn)
toRemove += H.ckey
- warn(H, "You are have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.")
+ warn(H, "You have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.")
log_afk_action(H, mins_afk, T, "despawned", "AFK in a fast despawn area")
force_cryo_human(H)
else
@@ -67,7 +67,7 @@ SUBSYSTEM_DEF(afk)
else if(afk_players[H.ckey] != AFK_ADMINS_WARNED && mins_afk >= config.auto_despawn_afk)
log_afk_action(H, mins_afk, T, "despawned")
- warn(H, "You are have been despawned after being AFK for [mins_afk] minutes.")
+ warn(H, "You have been despawned after being AFK for [mins_afk] minutes.")
toRemove += H.ckey
force_cryo_human(H)
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index cbf17d05fbf..619508544a6 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -7,7 +7,7 @@ SUBSYSTEM_DEF(garbage)
init_order = INIT_ORDER_GARBAGE
offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended."
- var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+ var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
//Stat tracking
var/delslasttick = 0 // number of del()'s we've done this tick
diff --git a/code/controllers/subsystem/nano_mob_hunter.dm b/code/controllers/subsystem/nano_mob_hunter.dm
index 9094bf9fe2b..19b57f59a70 100644
--- a/code/controllers/subsystem/nano_mob_hunter.dm
+++ b/code/controllers/subsystem/nano_mob_hunter.dm
@@ -103,12 +103,12 @@ SUBSYSTEM_DEF(mob_hunt)
return
if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready)
battle_turn = pick("Red", "Blue")
- red_terminal.audible_message("Battle starting!", null, 5)
- blue_terminal.audible_message("Battle starting!", null, 5)
+ red_terminal.atom_say("Battle starting!")
+ blue_terminal.atom_say("Battle starting!")
if(battle_turn == "Red")
- red_terminal.audible_message("Red Player's Turn!", null, 5)
+ red_terminal.atom_say("Red Player's Turn!")
else if(battle_turn == "Blue")
- blue_terminal.audible_message("Blue Player's Turn!", null, 5)
+ blue_terminal.atom_say("Blue Player's Turn!")
/datum/controller/subsystem/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type)
if(!team || !raw_damage)
@@ -135,11 +135,11 @@ SUBSYSTEM_DEF(mob_hunt)
winner_terminal.ready = 0
loser_terminal.ready = 0
if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle
- winner_terminal.audible_message("Your rival surrendered!", null, 2)
+ winner_terminal.atom_say("Your rival surrendered!")
else
var/progress_message = winner_terminal.mob_info.gain_exp()
- winner_terminal.audible_message("[winner_terminal.team] Player wins!", null, 5)
- winner_terminal.audible_message(progress_message, null, 2)
+ winner_terminal.atom_say("[winner_terminal.team] Player wins!")
+ winner_terminal.atom_say(progress_message)
/datum/controller/subsystem/mob_hunt/proc/end_turn()
red_terminal.updateUsrDialog()
@@ -148,7 +148,7 @@ SUBSYSTEM_DEF(mob_hunt)
return
if(battle_turn == "Red")
battle_turn = "Blue"
- blue_terminal.audible_message("Blue's turn.", null, 5)
+ blue_terminal.atom_say("Blue's turn.")
else if(battle_turn == "Blue")
battle_turn = "Red"
- blue_terminal.audible_message("Red's turn.", null, 5)
+ blue_terminal.atom_say("Red's turn.")
diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm
new file mode 100644
index 00000000000..c4240021137
--- /dev/null
+++ b/code/controllers/subsystem/tgui.dm
@@ -0,0 +1,286 @@
+ /**
+ * tgui subsystem
+ *
+ * Contains all tgui state and subsystem code.
+ **/
+
+
+SUBSYSTEM_DEF(tgui)
+ name = "TGUI"
+ wait = 9
+ flags = SS_NO_INIT
+ priority = FIRE_PRIORITY_NANOUI // Yes I am aware that this is TGUI and I used the nanoUI fire priority. Dont @ me.
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+ offline_implications = "All TGUIs will no longer process. Shuttle call recommended."
+
+ var/list/currentrun = list()
+ var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key.
+ var/list/processing_uis = list() // A list of processing UIs, ungrouped.
+ var/basehtml // The HTML base used for all UIs.
+
+/datum/controller/subsystem/tgui/PreInit()
+ basehtml = file2text('tgui/packages/tgui/public/tgui.html')
+
+/datum/controller/subsystem/tgui/Shutdown()
+ close_all_uis()
+
+/datum/controller/subsystem/tgui/stat_entry()
+ ..("P:[processing_uis.len]")
+
+/datum/controller/subsystem/tgui/fire(resumed = 0)
+ if (!resumed)
+ src.currentrun = processing_uis.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ var/datum/tgui/ui = currentrun[currentrun.len]
+ currentrun.len--
+ if(ui && ui.user && ui.src_object)
+ ui.process()
+ else
+ processing_uis.Remove(ui)
+ if (MC_TICK_CHECK)
+ return
+
+ /**
+ * public
+ *
+ * Get a open UI given a user, src_object, and ui_key and try to update it with data.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ *
+ * return datum/tgui The found UI.
+ **/
+/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE)
+ if(isnull(ui)) // No UI was passed, so look for one.
+ ui = get_open_ui(user, src_object, ui_key)
+
+ if(!isnull(ui))
+ var/data = src_object.tgui_data(user) // Get data from the src_object.
+ if(!force_open) // UI is already open; update it.
+ ui.push_data(data)
+ else // Re-open it anyways.
+ ui.reinitialize(null, data)
+ return ui // We found the UI, return it.
+ else
+ return null // We couldn't find a UI.
+
+ /**
+ * private
+ *
+ * Get a open UI given a user, src_object, and ui_key.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ *
+ * return datum/tgui The found UI.
+ **/
+/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return null // No UIs open.
+ else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
+ return null // No UIs open for this object.
+
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object.
+ if(ui.user == user) // Make sure we have the right user
+ return ui
+
+ return null // Couldn't find a UI!
+
+ /**
+ * private
+ *
+ * Update all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs updated.
+ **/
+/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return 0 // Couldn't find any UIs for this object.
+
+ var/update_count = 0
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.process(force = 1) // Update the UI.
+ update_count++ // Count each UI we update.
+ return update_count
+
+ /**
+ * private
+ *
+ * Close all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return 0 // Couldn't find any UIs for this object.
+
+ var/close_count = 0
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Close *ALL* UIs
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_all_uis()
+ var/close_count = 0
+ for(var/src_object_key in open_uis)
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Update all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only update UIs belonging this src_object.
+ * optional ui_key string If provided, only update UIs with this UI key.
+ *
+ * return int The number of UIs updated.
+ **/
+/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
+ if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0)
+ return 0 // Couldn't find any UIs for this user.
+
+ var/update_count = 0
+ for(var/datum/tgui/ui in user.open_tguis)
+ if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
+ ui.process(force = 1) // Update the UI.
+ update_count++ // Count each UI we upadte.
+ return update_count
+
+ /**
+ * private
+ *
+ * Close all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only close UIs belonging this src_object.
+ * optional ui_key string If provided, only close UIs with this UI key.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
+ if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0)
+ return 0 // Couldn't find any UIs for this user.
+
+ var/close_count = 0
+ for(var/datum/tgui/ui in user.open_tguis)
+ if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Add a UI to the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be added.
+ **/
+/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
+ var/src_object_key = "[ui.src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
+ else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
+ open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key.
+
+ // Append the UI to all the lists.
+ ui.user.open_tguis |= ui
+ var/list/uis = open_uis[src_object_key][ui.ui_key]
+ uis |= ui
+ processing_uis |= ui
+
+ /**
+ * private
+ *
+ * Remove a UI from the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be removed.
+ *
+ * return bool If the UI was removed or not.
+ **/
+/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
+ var/src_object_key = "[ui.src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return FALSE // It wasn't open.
+ else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
+ return FALSE // It wasn't open.
+
+ processing_uis.Remove(ui) // Remove it from the list of processing UIs.
+ if(ui.user) // If the user exists, remove it from them too.
+ ui.user.open_tguis.Remove(ui)
+ var/Ukey = ui.ui_key
+ var/list/uis = open_uis[src_object_key][Ukey] // Remove it from the list of open UIs.
+ uis.Remove(ui)
+ if(!uis.len)
+ var/list/uiobj = open_uis[src_object_key]
+ uiobj.Remove(Ukey)
+ if(!uiobj.len)
+ open_uis.Remove(src_object_key)
+
+ return TRUE // Let the caller know we did it.
+
+ /**
+ * private
+ *
+ * Handle client logout, by closing all their UIs.
+ *
+ * required user mob The mob which logged out.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
+ return close_user_uis(user)
+
+ /**
+ * private
+ *
+ * Handle clients switching mobs, by transferring their UIs.
+ *
+ * required user source The client's original mob.
+ * required user target The client's new mob.
+ *
+ * return bool If the UIs were transferred.
+ **/
+/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
+ if(!source || isnull(source.open_tguis) || !istype(source.open_tguis, /list) || open_uis.len == 0)
+ return FALSE // The old mob had no open UIs.
+
+ if(isnull(target.open_tguis) || !istype(target.open_tguis, /list))
+ target.open_tguis = list() // Create a list for the new mob if needed.
+
+ for(var/datum/tgui/ui in source.open_tguis)
+ ui.user = target // Inform the UIs of their new owner.
+ target.open_tguis.Add(ui) // Transfer all the UIs.
+
+ source.open_tguis.Cut() // Clear the old list.
+ return TRUE // Let the caller know we did it.
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 475bdaccb3c..6f8dd85b79b 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -407,7 +407,7 @@ SUBSYSTEM_DEF(ticker)
EquipCustomItems(player)
if(captainless)
for(var/mob/M in GLOB.player_list)
- if(!istype(M,/mob/new_player))
+ if(!isnewplayer(M))
to_chat(M, "Captainship not forced on anyone.")
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
diff --git a/code/datums/components/ducttape.dm b/code/datums/components/ducttape.dm
index 98512589a46..49931db8edc 100644
--- a/code/datums/components/ducttape.dm
+++ b/code/datums/components/ducttape.dm
@@ -37,10 +37,10 @@
I.anchored = initial(I.anchored)
for(var/datum/action/item_action/remove_tape/RT in I.actions)
RT.Remove(user)
- RT.Destroy()
+ qdel(RT)
I.overlays.Cut(tape_overlay)
user.transfer_fingerprints_to(I)
- Destroy()
+ qdel(src)
/datum/component/ducttape/proc/afterattack(obj/item/I, atom/target, mob/user, proximity, params)
if(!proximity)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index b2b09003934..e8d0b881ccb 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -819,6 +819,59 @@
message_admins("[key_name(usr)] has made [A] process normally")
return TRUE
+ else if(href_list["modifyarmor"])
+ if(!check_rights(R_DEBUG|R_ADMIN))
+ return
+ var/obj/A = locateUID(href_list["modifyarmor"])
+ if(!istype(A))
+ return
+ A.var_edited = TRUE
+ var/list/armorlist = A.armor.getList()
+ var/list/displaylist
+
+ var/result
+ do
+ displaylist = list()
+ for(var/key in armorlist)
+ displaylist += "[key] = [armorlist[key]]"
+ result = input(usr, "Select an armor type to modify..", "Modify armor") as null|anything in displaylist + "(ADD ALL)" + "(SET ALL)" + "(DONE)"
+
+ if(result == "(DONE)")
+ break
+ else if(result == "(ADD ALL)" || result == "(SET ALL)")
+ var/new_amount = input(usr, result == "(ADD ALL)" ? "Enter armor to add to all types:" : "Enter new armor value for all types:", "Modify all types") as num|null
+ if(isnull(new_amount))
+ continue
+ var/proper_amount = text2num(new_amount)
+ if(isnull(proper_amount))
+ continue
+ for(var/key in armorlist)
+ armorlist[key] = (result == "(ADD ALL)" ? armorlist[key] : 0) + proper_amount
+ else if(result)
+ var/list/fields = splittext(result, " = ")
+ if(length(fields) != 2)
+ continue
+ var/type = fields[1]
+ if(isnull(armorlist[type]))
+ continue
+ var/new_amount = input(usr, "Enter new armor value for [type]:", "Modify [type]") as num|null
+ if(isnull(new_amount))
+ continue
+ var/proper_amount = text2num(new_amount)
+ if(isnull(proper_amount))
+ continue
+ armorlist[type] = proper_amount
+ while(result)
+
+ if(!result || !A)
+ return TRUE
+
+ A.armor = A.armor.setRating(armorlist["melee"], armorlist["bullet"], armorlist["laser"], armorlist["energy"], armorlist["bomb"], armorlist["bio"], armorlist["rad"], armorlist["fire"], armorlist["acid"], armorlist["magic"])
+
+ log_admin("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]")
+ message_admins("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]")
+ return TRUE
+
else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */
if(!check_rights(R_DEBUG|R_ADMIN)) return
diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm
index 3afb48ce534..e8486a8f408 100644
--- a/code/datums/diseases/critical.dm
+++ b/code/datums/diseases/critical.dm
@@ -159,7 +159,7 @@
var/mob/living/carbon/human/H = affected_mob
if(NO_HUNGER in H.dna.species.species_traits)
return TRUE
- if(ismachine(H))
+ if(ismachineperson(H))
return TRUE
return ..()
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 7d3a73abf24..54635f01b0a 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -39,7 +39,6 @@
var/role_alt_title
var/datum/job/assigned_job
- var/list/kills = list()
var/list/datum/objective/objectives = list()
var/list/datum/objective/special_verbs = list()
var/list/targets = list()
@@ -90,6 +89,9 @@
if(antag_datum.delete_on_mind_deletion)
qdel(i)
antag_datums = null
+ current = null
+ original = null
+ soulOwner = null
return ..()
/datum/mind/proc/transfer_to(mob/living/new_character)
diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm
index 2a8e1d94526..a0c41b57001 100644
--- a/code/datums/spells/summonitem.dm
+++ b/code/datums/spells/summonitem.dm
@@ -75,8 +75,6 @@
B.transfer_identity(C)
C.death()
add_attack_logs(target, C, "Magically debrained INTENT: [uppertext(target.a_intent)]")*/
- if(C.stomach_contents && (item_to_retrieve in C.stomach_contents))
- C.stomach_contents -= item_to_retrieve
for(var/X in C.bodyparts)
var/obj/item/organ/external/part = X
if(item_to_retrieve in part.embedded_objects)
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index a0c9a19e4b0..8dbdfd95420 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -188,15 +188,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/storage/box/syndie_kit/fake_revolver
cost = 1
job = list("Clown")
-/*
-/datum/uplink_item/stealthy_weapons/romerol_kit
- name = "Romerol"
- reference = "ROM"
- desc = "A highly experimental bioterror agent which creates dormant nodules to be etched into the grey matter of the brain. On death, these nodules take control of the dead body, causing limited revivification, along with slurred speech, aggression, and the ability to infect others with this agent."
- item = /obj/item/storage/box/syndie_kit/romerol
- cost = 25
- cant_discount = TRUE
-*/
//mime
/datum/uplink_item/jobspecific/caneshotgun
name = "Cane Shotgun and Assassination Shells"
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index e3d90b8d075..dcde08d7ec1 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -166,9 +166,9 @@
//raises them if they are down (only if power's on)
if(!A.locked)
if(A.lock())
- A.audible_message("You hear a click from the bottom of the door.", null, 1)
+ A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1)
else if(A.unlock())
- A.audible_message("You hear a click from the bottom of the door.", null, 1)
+ A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1)
if(AIRLOCK_WIRE_BACKUP_POWER1)
//two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter).
diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm
index 094e4f9ca2b..8f3eeeaa6fa 100644
--- a/code/defines/procs/statistics.dm
+++ b/code/defines/procs/statistics.dm
@@ -29,8 +29,9 @@
var/laname
var/lakey
if(H.lastattacker)
- laname = sanitizeSQL(H.lastattacker:real_name)
- lakey = sanitizeSQL(H.lastattacker:key)
+ laname = sanitizeSQL(H.lastattacker)
+ if(H.lastattackerckey)
+ lakey = sanitizeSQL(H.lastattackerckey)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])")
@@ -64,8 +65,9 @@
var/laname
var/lakey
if(H.lastattacker)
- laname = sanitizeSQL(H.lastattacker:real_name)
- lakey = sanitizeSQL(H.lastattacker:key)
+ laname = sanitizeSQL(H.lastattacker)
+ if(H.lastattackerckey)
+ lakey = sanitizeSQL(H.lastattackerckey)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])")
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index a57f48ffc11..9070d1409f6 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/traitor.ogg')
+ ambientsounds = SPACE_SOUNDS
/area/space/nearstation
icon_state = "space_near"
@@ -117,39 +117,33 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape
name = "\improper Emergency Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle2"
nad_allowed = TRUE
/area/shuttle/pod_1
name = "\improper Escape Pod One"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
/area/shuttle/pod_2
name = "\improper Escape Pod Two"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
/area/shuttle/pod_3
name = "\improper Escape Pod Three"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
parallax_movedir = EAST
/area/shuttle/pod_4
name = "\improper Escape Pod Four"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
parallax_movedir = EAST
/area/shuttle/escape_pod1
name = "\improper Escape Pod One"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod1/station
@@ -163,7 +157,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod2
name = "\improper Escape Pod Two"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod2/station
@@ -177,7 +170,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod3
name = "\improper Escape Pod Three"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod3/station
@@ -191,7 +183,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod5 //Pod 4 was lost to meteors
name = "\improper Escape Pod Five"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod5/station
@@ -205,7 +196,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/mining
name = "\improper Mining Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/transport
@@ -246,7 +236,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/siberia
name = "\improper Labor Camp Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/specops
@@ -329,7 +318,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/research
name = "\improper Research Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/research/station
@@ -498,6 +486,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
nad_allowed = TRUE
+ ambientsounds = HIGHSEC_SOUNDS
/area/syndicate_mothership/control
name = "\improper Syndicate Control Room"
@@ -519,6 +508,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
valid_territory = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+ ambientsounds = MINING_SOUNDS
/area/asteroid/cave // -- TLE
name = "\improper Asteroid - Underground"
@@ -633,7 +623,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Maintenance
/area/maintenance
- ambientsounds = list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg', 'sound/ambience/ambimaint5.ogg')
+ ambientsounds = MAINTENANCE_SOUNDS
valid_territory = FALSE
/area/maintenance/atmos_control
@@ -803,12 +793,11 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/bridge
name = "\improper Bridge"
icon_state = "bridge"
- music = "signal"
+ ambientsounds = list('sound/ambience/signal.ogg')
/area/bridge/meeting_room
name = "\improper Heads of Staff Meeting Room"
icon_state = "meeting"
- music = null
/area/crew_quarters/captain
name = "\improper Captain's Office"
@@ -969,10 +958,12 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "\improper Abandoned Library"
icon_state = "library"
+/area/chapel
+ icon_state = "chapel"
+ ambientsounds = HOLY_SOUNDS
+
/area/chapel/main
name = "\improper Chapel"
- icon_state = "chapel"
- ambientsounds = list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
/area/chapel/office
name = "\improper Chapel Office"
@@ -1107,7 +1098,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Engineering
/area/engine
- ambientsounds = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
+ ambientsounds = ENGINEERING_SOUNDS
/area/engine/engine_smes
name = "\improper Engineering SMES"
@@ -1161,6 +1152,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
valid_territory = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
+ ambientsounds = ENGINEERING_SOUNDS
/area/solar/auxport
name = "\improper Fore Port Solar Array"
@@ -1227,18 +1219,17 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/teleporter
name = "\improper Teleporter"
icon_state = "teleporter"
- music = "signal"
+ ambientsounds = ENGINEERING_SOUNDS
/area/gateway
name = "\improper Gateway"
icon_state = "teleporter"
- music = "signal"
+ ambientsounds = ENGINEERING_SOUNDS
/area/AIsattele
name = "\improper Abandoned Teleporter"
icon_state = "teleporter"
- music = "signal"
- ambientsounds = list('sound/ambience/ambimalf.ogg')
+ ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/signal.ogg')
/area/toxins/explab
name = "\improper E.X.P.E.R.I-MENTOR Lab"
@@ -1250,42 +1241,39 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//MedBay
+/area/medical
+ ambientsounds = MEDICAL_SOUNDS
+
/area/medical/medbay
name = "\improper Medbay"
icon_state = "medbay"
- music = 'sound/ambience/signal.ogg'
//Medbay is a large area, these additional areas help level out APC load.
/area/medical/medbay2
name = "\improper Medbay"
icon_state = "medbay2"
- music = 'sound/ambience/signal.ogg'
/area/medical/medbay3
name = "\improper Medbay"
icon_state = "medbay3"
- music = 'sound/ambience/signal.ogg'
/area/medical/biostorage
name = "\improper Medical Storage"
icon_state = "medbaysecstorage"
- music = 'sound/ambience/signal.ogg'
/area/medical/reception
name = "\improper Medbay Reception"
icon_state = "medbay"
- music = 'sound/ambience/signal.ogg'
/area/medical/psych
name = "\improper Psych Room"
icon_state = "medbaypsych"
- music = 'sound/ambience/signal.ogg'
+ ambientsounds = list('sound/ambience/aurora_caelus_short.ogg')
/area/medical/medbreak
name = "\improper Break Room"
icon_state = "medbaybreak"
- music = 'sound/ambience/signal.ogg'
/area/medical/patients_rooms
name = "\improper Patient's Rooms"
@@ -1342,7 +1330,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/medical/morgue
name = "\improper Morgue"
icon_state = "morgue"
- ambientsounds = list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg')
+ ambientsounds = SPOOKY_SOUNDS
/area/medical/chemistry
name = "\improper Chemistry"
@@ -1390,6 +1378,9 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Security
+/area/security
+ ambientsounds = HIGHSEC_SOUNDS
+
/area/security/main
name = "\improper Security Office"
icon_state = "securityoffice"
@@ -1522,6 +1513,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/security/detectives_office
name = "\improper Detective's Office"
icon_state = "detective"
+ ambientsounds = list('sound/ambience/ambidet1.ogg', 'sound/ambience/ambidet2.ogg')
/area/security/range
name = "\improper Firing Range"
@@ -1870,6 +1862,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/construction
name = "\improper Construction Area"
icon_state = "yellow"
+ ambientsounds = ENGINEERING_SOUNDS
/area/mining_construction
name = "Auxillary Base Construction"
@@ -1962,6 +1955,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/ai_monitored/storage/eva
name = "EVA Storage"
icon_state = "eva"
+ ambientsounds = HIGHSEC_SOUNDS
/area/ai_monitored/storage/secure
name = "Secure Storage"
@@ -1972,7 +1966,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
icon_state = "storage"
/area/turret_protected/
- ambientsounds = list('sound/ambience/ambimalf.ogg')
+ ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg')
/area/turret_protected/ai_upload
name = "\improper AI Upload Chamber"
@@ -2034,7 +2028,8 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
// Telecommunications Satellite
/area/tcommsat
- ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
+ ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambitech.ogg',\
+ 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg', 'sound/ambience/ambimystery.ogg')
/area/tcommsat/chamber
name = "\improper Telecoms Central Compartment"
@@ -2082,6 +2077,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "\improper Strange Location"
icon_state = "away"
report_alerts = FALSE
+ ambientsounds = AWAY_MISSION_SOUNDS
/area/awaymission/example
name = "\improper Strange Station"
@@ -2096,7 +2092,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
icon_state = "beach"
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
requires_power = FALSE
- ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg')
+ ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambinice.ogg')
/area/awaymission/undersea
name = "Undersea"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 6ad45532641..da80a6e775c 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -26,7 +26,6 @@
var/power_equip = TRUE
var/power_light = TRUE
var/power_environ = TRUE
- var/music = null
var/used_equip = FALSE
var/used_light = FALSE
var/used_environ = FALSE
@@ -56,12 +55,7 @@
var/global/global_uid = 0
var/uid
- var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\
- 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\
- 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\
- 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\
- 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\
- 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
+ var/list/ambientsounds = GENERIC_SOUNDS
var/fast_despawn = FALSE
var/can_get_auto_cryod = TRUE
@@ -379,20 +373,24 @@
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys
- L.client.ambience_playing = 1
- L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ)
+ L.client.ambience_playing = TRUE
+ SEND_SOUND(L, sound('sound/ambience/shipambience.ogg', repeat = TRUE, wait = FALSE, volume = 35, channel = CHANNEL_BUZZ))
else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ))
- L.client.ambience_playing = 0
+ L.client.ambience_playing = FALSE
if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE))
var/sound = pick(ambientsounds)
if(!L.client.played)
- L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE)
- L.client.played = 1
- spawn(600) //ewww - this is very very bad
- if(L && L.client)
- L.client.played = 0
+ SEND_SOUND(L, sound(sound, repeat = FALSE, wait = FALSE, volume = 25, channel = CHANNEL_AMBIENCE))
+ L.client.played = TRUE
+ addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600)
+
+/**
+ * Reset the played var to false on the client
+ */
+/client/proc/ResetAmbiencePlayed()
+ played = FALSE
/area/proc/gravitychange(var/gravitystate = 0, var/area/A)
A.has_gravity = gravitystate
diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm
index 0b07fffda5e..b621bade493 100644
--- a/code/game/area/areas/mining.dm
+++ b/code/game/area/areas/mining.dm
@@ -7,7 +7,6 @@
/area/mine/explored
name = "Mine"
icon_state = "explored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
@@ -15,7 +14,7 @@
power_equip = FALSE
power_light = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = MINING_SOUNDS
flags = NONE
/area/mine/dangerous/explored/golem
@@ -24,7 +23,6 @@
/area/mine/unexplored
name = "Mine"
icon_state = "unexplored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
@@ -32,7 +30,7 @@
power_equip = FALSE
power_light = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = MINING_SOUNDS
flags = NONE
/area/mine/lobby
@@ -80,6 +78,7 @@
/area/mine/laborcamp/security
name = "Labor Camp Security"
icon_state = "security"
+ ambientsounds = HIGHSEC_SOUNDS
/area/mine/podbay
name = "Mining Podbay"
@@ -95,26 +94,24 @@
/area/lavaland/surface
name = "Lavaland"
icon_state = "explored"
- music = null
always_unpowered = TRUE
poweralm = FALSE
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
requires_power = TRUE
- ambientsounds = list('sound/ambience/ambilava.ogg')
+ ambientsounds = MINING_SOUNDS
/area/lavaland/underground
name = "Lavaland Caves"
icon_state = "unexplored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
- ambientsounds = list('sound/ambience/ambilava.ogg')
+ ambientsounds = MINING_SOUNDS
/area/lavaland/surface/outdoors
name = "Lavaland Wastes"
diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm
index 5baf1fe315e..53544d8643b 100644
--- a/code/game/area/areas/ruins/lavaland.dm
+++ b/code/game/area/areas/ruins/lavaland.dm
@@ -5,6 +5,7 @@
/area/ruin/powered/clownplanet
icon_state = "dk_yellow"
+ ambientsounds = list('sound/music/clown.ogg')
/area/ruin/powered/animal_hospital
icon_state = "dk_yellow"
@@ -38,7 +39,7 @@
/area/ruin/unpowered/syndicate_lava_base
name = "Secret Base"
icon_state = "dk_yellow"
- ambientsounds = list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg')
+ ambientsounds = HIGHSEC_SOUNDS
report_alerts = FALSE
hide_attacklogs = TRUE
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 5d3c6d688ac..776a2bccd12 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -14,6 +14,7 @@
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = TRUE //filter for actions - used by lighting overlays
var/atom_say_verb = "says"
+ var/bubble_icon = "default" ///what icon the mob uses for speechbubbles
var/dont_save = FALSE // For atoms that are temporary by necessity - like lighting overlays
///Chemistry.
@@ -721,17 +722,38 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
var/mob/M = loc
M.update_inv_shoes()
-/mob/living/carbon/human/clean_blood()
- if(gloves)
+/mob/living/carbon/human/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE)
+ if(w_uniform && !(wear_suit && wear_suit.flags_inv & HIDEJUMPSUIT))
+ if(w_uniform.clean_blood())
+ update_inv_w_uniform()
+ if(gloves && !(wear_suit && wear_suit.flags_inv & HIDEGLOVES))
if(gloves.clean_blood())
- clean_blood()
- update_inv_gloves()
- gloves.germ_level = 0
- else
- ..() // Clear the Blood_DNA list
- if(bloody_hands)
- bloody_hands = 0
update_inv_gloves()
+ gloves.germ_level = 0
+ clean_hands = FALSE
+ if(shoes && !(wear_suit && wear_suit.flags_inv & HIDESHOES))
+ if(shoes.clean_blood())
+ update_inv_shoes()
+ clean_feet = FALSE
+ if(s_store && !(wear_suit && wear_suit.flags_inv & HIDESUITSTORAGE))
+ if(s_store.clean_blood())
+ update_inv_s_store()
+ if(lip_style && !(head && head.flags_inv & HIDEMASK))
+ lip_style = null
+ update_body()
+ if(glasses && !(wear_mask && wear_mask.flags_inv & HIDEEYES))
+ if(glasses.clean_blood())
+ update_inv_glasses()
+ if(l_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS))
+ if(l_ear.clean_blood())
+ update_inv_ears()
+ if(r_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS))
+ if(r_ear.clean_blood())
+ update_inv_ears()
+ if(belt)
+ if(belt.clean_blood())
+ update_inv_belt()
+ ..(clean_hands, clean_mask, clean_feet)
update_icons() //apply the now updated overlays to the mob
/atom/proc/add_vomit_floor(toxvomit = FALSE, green = FALSE)
@@ -819,7 +841,16 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/proc/atom_say(message)
if(!message)
return
- audible_message("[src] [atom_say_verb], \"[message]\"")
+ 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/atoms_movable.dm b/code/game/atoms_movable.dm
index aed4a4c5c2d..ab5ff19462a 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -50,6 +50,7 @@
loc.handle_atom_del(src)
for(var/atom/movable/AM in contents)
qdel(AM)
+ LAZYCLEARLIST(client_mobs_in_contents)
loc = null
if(pulledby)
pulledby.stop_pulling()
diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm
index a11e5d922ba..641167c768c 100644
--- a/code/game/gamemodes/blob/blob.dm
+++ b/code/game/gamemodes/blob/blob.dm
@@ -46,7 +46,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
var/datum/mind/blob = pick(possible_blobs)
infected_crew += blob
blob.special_role = SPECIAL_ROLE_BLOB
- update_blob_icons_added(blob)
blob.restricted_roles = restricted_jobs
log_game("[key_name(blob)] has been selected as a Blob")
possible_blobs -= blob
@@ -152,6 +151,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
for(var/datum/mind/blob in infected_crew)
greet_blob(blob)
+ update_blob_icons_added(blob)
if(SSshuttle)
SSshuttle.emergencyNoEscape = 1
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index 38da76a348b..46b1972e565 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -8,6 +8,7 @@
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB
faction = list(ROLE_BLOB)
+ bubble_icon = "blob"
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 360
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index 672ab037cc5..be794b9092b 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -89,41 +89,40 @@
..()
-/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay)
+/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay)
if(overmind_get_delay > world.time && !override_delay)
return
- overmind_get_delay = world.time + 3000 // 5 minutes
+ overmind_get_delay = world.time + 5 MINUTES
if(overmind)
qdel(overmind)
+ INVOKE_ASYNC(src, .proc/get_new_overmind, new_overmind)
+
+/obj/structure/blob/core/proc/get_new_overmind(client/new_overmind)
var/mob/C = null
var/list/candidates = list()
-
- spawn()
- if(!new_overmind)
- if(is_offspring)
- candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
- else
- candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
-
- if(candidates.len)
- C = pick(candidates)
+ if(!new_overmind)
+ if(is_offspring)
+ candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
else
- C = new_overmind
+ candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
- if(C)
- var/mob/camera/blob/B = new(src.loc)
- B.key = C.key
- B.blob_core = src
- src.overmind = B
- color = overmind.blob_reagent_datum.color
- if(B.mind && !B.mind.special_role)
- B.mind.make_Overmind()
- spawn(0)
- if(is_offspring)
- B.is_offspring = TRUE
+ if(length(candidates))
+ C = pick(candidates)
+ else
+ C = new_overmind
+
+ if(C && !QDELETED(src))
+ var/mob/camera/blob/B = new(loc)
+ B.key = C.key
+ B.blob_core = src
+ overmind = B
+ color = overmind.blob_reagent_datum.color
+ if(B.mind && !B.mind.special_role)
+ B.mind.make_Overmind()
+ B.is_offspring = is_offspring
/obj/structure/blob/core/proc/lateblobtimer()
addtimer(CALLBACK(src, .proc/lateblobcheck), 50)
diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm
index 94bde26674b..fbfb75b8b6a 100644
--- a/code/game/gamemodes/changeling/powers/tiny_prick.dm
+++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm
@@ -36,11 +36,9 @@
to_chat(user, "We haven't prepared our sting yet!")
if(!iscarbon(target))
return
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if(H.isSynthetic())
- to_chat(user, "This won't work on synthetics.")
- return
+ if(ismachineperson(target))
+ to_chat(user, "This won't work on synthetics.")
+ return
if(!isturf(user.loc))
return
if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0))
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index d3e8f7d6bbd..02cd7b6db65 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -808,6 +808,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
allow_excess_invokers = 1
icon_state = "5"
invoke_damage = 5
+ var/summoning = FALSE
var/summontime = 0
/obj/effect/rune/summon/invoke(var/list/invokers)
@@ -819,6 +820,11 @@ GLOBAL_LIST_EMPTY(teleport_runes)
var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of [SSticker.cultdat.entity_title3]") as null|anything in cultists
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
return
+ if(summoning)
+ to_chat(user, "You are already summoning a target!")
+ fail_invoke()
+ return
+
if(!cultist_to_summon)
to_chat(user, "You require a summoning target!")
fail_invoke()
@@ -841,18 +847,20 @@ GLOBAL_LIST_EMPTY(teleport_runes)
new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning
log_game("Summon Cultist rune failed - holywater in target")
return
-
+ summoning = TRUE
..()
if(hard_summon)
summontime = 20
- if(do_after(user, summontime, target = loc))
+ if(do_after(user, summontime, target = src))
+ summoning = FALSE // Here incase the proc stops after the qdel
cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \
"Overwhelming vertigo consumes you as you are hurled through the air!")
visible_message("A foggy shape materializes atop [src] and solidifies into [cultist_to_summon]!")
cultist_to_summon.forceMove(get_turf(src))
qdel(src)
+ summoning = FALSE
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
/obj/effect/rune/blood_boil
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index c7ca587b34f..a0519fcbe56 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -480,8 +480,8 @@ Congratulations! You are now trained for invasive xenobiology research!"}
H.update_inv_r_hand()
/obj/item/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user)
- user.lastattacked = L
- L.lastattacker = user
+ L.lastattacker = user.real_name
+ L.lastattackerckey = user.ckey
L.Stun(7)
L.Weaken(7)
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 059eed3541c..8da5b4468d7 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -61,6 +61,7 @@
icon = 'icons/mob/swarmer.dmi'
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
speak_emote = list("tones")
+ bubble_icon = "swarmer"
health = 40
maxHealth = 40
status_flags = CANPUSH
@@ -362,6 +363,10 @@
to_chat(S, "This cryopod control computer should be preserved, it contains useful items and information about the inhabitants. Aborting.")
return FALSE
+/obj/structure/spacepoddoor/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ to_chat(S, "Disrupting this energy field would overload us. Aborting.")
+ return FALSE
+
/turf/simulated/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
var/isonshuttle = istype(loc, /area/shuttle)
for(var/turf/T in range(1, src))
@@ -604,7 +609,7 @@
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
L.electrocute_act(0, src, 1, TRUE, TRUE)
- if(isrobot(L) || L.isSynthetic())
+ if(isrobot(L) || ismachineperson(L))
L.Weaken(5)
qdel(src)
..()
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index 5f9256e18b4..f647ae5a6d1 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -3,6 +3,7 @@
real_name = "Guardian Spirit"
desc = "A mysterious being that stands by it's charge, ever vigilant."
speak_emote = list("intones")
+ bubble_icon = "guardian"
response_help = "passes through"
response_disarm = "flails at"
response_harm = "punches"
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 0da5d8a3a0a..0b46264cb33 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/assassinate/check_completion()
if(target && target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 1
if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
return 1
@@ -111,7 +111,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/maroon/check_completion()
if(target && target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 1
if(!target.current.ckey)
return 1
@@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(!target) //If it's a free objective.
return 1
if(target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 0
if(issilicon(target.current))
return 0
@@ -262,7 +262,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
return 0
if(isbrain(owner.current))
return 0
- if(!owner.current || owner.current.stat == DEAD || iszombie(owner))
+ if(!owner.current || owner.current.stat == DEAD)
return 0
if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith
return 1
@@ -317,7 +317,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
explanation_text = "Die a glorious death."
/datum/objective/die/check_completion()
- if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner))
+ if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current))
return 1
if(issilicon(owner.current) && owner.current != owner.original)
return 1
@@ -573,9 +573,10 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
// /vg/; Vox Inviolate for humans :V
/datum/objective/minimize_casualties
explanation_text = "Minimise casualties."
+
/datum/objective/minimize_casualties/check_completion()
- if(owner.kills.len>5) return 0
- return 1
+ return TRUE
+
//Vox heist objectives.
@@ -781,16 +782,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
explanation_text = "Follow the Inviolate. Minimise death and loss of resources."
/datum/objective/heist/inviolate_death/check_completion()
- var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail.
- var/vox_total_kills = 0
-
- var/datum/game_mode/heist/H = SSticker.mode
- for(var/datum/mind/raider in H.raiders)
- vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills
-
- if(vox_total_kills > vox_allowed_kills) return 0
- return 1
-
+ return TRUE
// Traders
// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for.
diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm
index 2e43096ce8e..b46bc3c6c51 100644
--- a/code/game/gamemodes/shadowling/shadowling.dm
+++ b/code/game/gamemodes/shadowling/shadowling.dm
@@ -194,8 +194,8 @@ Made by Xhuis
return 1
var/mob/living/M = thrall_mind.current
if(issilicon(M))
- M.audible_message("[M] lets out a short blip.", \
- "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...")
+ M.audible_message("[M] lets out a short blip.")
+ to_chat(M, "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...")
else
M.visible_message("[M] looks like [M.p_their()] mind is [M.p_their()] own again!", \
"A piercing white light floods your eyes. Your mind is your own again! Though you try, you cannot remember anything about the shadowlings or your time \
@@ -244,8 +244,8 @@ Made by Xhuis
ling_mind.RemoveSpell(S)
var/mob/living/M = ling_mind.current
if(issilicon(M))
- M.audible_message("[M] lets out a short blip.", \
- "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...")
+ M.audible_message("[M] lets out a short blip.")
+ to_chat(M, "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...")
else
M.visible_message("[M] screams and contorts!", \
"THE LIGHT-- YOUR MIND-- BURNS--")
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 205fd63d9c6..09b228bdd0c 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -470,7 +470,7 @@ GLOBAL_LIST_EMPTY(multiverse)
M.equip_to_slot_or_del(sword, slot_r_hand)
if("cyborg")
- if(!ismachine(M))
+ if(!ismachineperson(M))
for(var/obj/item/organ/O in M.bodyparts)
O.robotize(make_tough = 1)
M.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/eyepatch(M), slot_glasses)
diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm
index b6321765861..c510518716a 100644
--- a/code/game/jobs/job/silicon.dm
+++ b/code/game/jobs/job/silicon.dm
@@ -24,8 +24,8 @@
title = "Cyborg"
flag = JOB_CYBORG
department_flag = JOBCAT_ENGSEC
- total_positions = 1
- spawn_positions = 1
+ total_positions = 2
+ spawn_positions = 2
supervisors = "your laws and the AI" //Nodrak
department_head = list("AI")
selection_color = "#ddffdd"
diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm
index c56d27bb408..1d627f131b2 100644
--- a/code/game/jobs/job/supervisor.dm
+++ b/code/game/jobs/job/supervisor.dm
@@ -223,6 +223,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
id = /obj/item/card/id/nanotrasen
l_pocket = /obj/item/flash
+ r_pocket = /obj/item/clothing/accessory/lawyers_badge
pda = /obj/item/pda/heads/magistrate
backpack_contents = list(
/obj/item/melee/classic_baton/telescopic = 1
@@ -262,9 +263,12 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
glasses = /obj/item/clothing/glasses/hud/security/sunglasses/read_only
id = /obj/item/card/id/security
l_pocket = /obj/item/laser_pointer
- r_pocket = /obj/item/flash
+ r_pocket = /obj/item/clothing/accessory/lawyers_badge
l_hand = /obj/item/storage/briefcase
pda = /obj/item/pda/lawyer
+ backpack_contents = list(
+ /obj/item/flash = 1
+ )
implants = list(/obj/item/implant/mindshield)
satchel = /obj/item/storage/backpack/satchel_sec
dufflebag = /obj/item/storage/backpack/duffel/security
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index f2b8cf31671..09328865b59 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -282,14 +282,14 @@
if(visualsOnly)
return
- if(ismachine(H))
+ if(ismachineperson(H))
var/obj/item/organ/internal/cyberimp/brain/clown_voice/implant = new
implant.insert(H)
H.dna.SetSEState(GLOB.clumsyblock, TRUE)
genemutcheck(H, GLOB.clumsyblock, null, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.clumsyblock)
- if(!ismachine(H))
+ if(!ismachineperson(H))
H.dna.SetSEState(GLOB.comicblock, TRUE)
genemutcheck(H, GLOB.comicblock, null, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.comicblock)
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 6174f4a497d..9fda2cfe45c 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -322,16 +322,18 @@
apply_mode()
-/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment, var/datum/tlv/cur_tlv, var/turf/simulated/location)
+/obj/machinery/alarm/proc/handle_heating_cooling(datum/gas_mixture/environment, datum/tlv/cur_tlv, turf/simulated/location)
cur_tlv = TLV["temperature"]
//Handle temperature adjustment here.
if(environment.temperature < target_temperature - 2 || environment.temperature > target_temperature + 2 || regulating_temperature)
//If it goes too far, we should adjust ourselves back before stopping.
if(!cur_tlv.get_danger_level(target_temperature))
+ var/datum/gas_mixture/gas = location.remove_air(0.25 * environment.total_moles())
+ if(!gas)
+ return
if(!regulating_temperature)
regulating_temperature = 1
- visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click and a faint electronic hum.")
+ visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click and a faint electronic hum.")
if(target_temperature > MAX_TEMPERATURE)
target_temperature = MAX_TEMPERATURE
@@ -339,9 +341,8 @@
if(target_temperature < MIN_TEMPERATURE)
target_temperature = MIN_TEMPERATURE
- var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles())
var/heat_capacity = gas.heat_capacity()
- var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
+ var/energy_used = max(abs(heat_capacity * (gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
//Use power. Assuming that each power unit represents 1000 watts....
use_power(energy_used/1000, ENVIRON)
@@ -349,16 +350,15 @@
//We need to cool ourselves.
if(heat_capacity)
if(environment.temperature > target_temperature)
- gas.temperature -= energy_used/heat_capacity
+ gas.temperature -= energy_used / heat_capacity
else
- gas.temperature += energy_used/heat_capacity
+ gas.temperature += energy_used / heat_capacity
environment.merge(gas)
if(abs(environment.temperature - target_temperature) <= 0.5)
regulating_temperature = 0
- visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click as a faint electronic humming stops.")
+ visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click as a faint electronic humming stops.")
/obj/machinery/alarm/update_icon()
if(wiresexposed)
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index a832e2b5d16..131956ca3c1 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -8,7 +8,7 @@
active_power_usage = 500
light_color = LIGHT_COLOR_DARKBLUE
circuit = /obj/item/circuitboard/crew
- var/datum/nano_module/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
/obj/machinery/computer/crew/New()
crew_monitor = new(src)
@@ -20,17 +20,15 @@
/obj/machinery/computer/crew/attack_ai(mob/user)
attack_hand(user)
- ui_interact(user)
-
/obj/machinery/computer/crew/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- crew_monitor.ui_interact(user, ui_key, ui, force_open)
+/obj/machinery/computer/crew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ crew_monitor.tgui_interact(user, ui_key, ui, force_open)
/obj/machinery/computer/crew/interact(mob/user)
- crew_monitor.ui_interact(user)
+ crew_monitor.tgui_interact(user)
diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm
index 05198a7a55f..a4b9510945a 100644
--- a/code/game/machinery/doppler_array.dm
+++ b/code/game/machinery/doppler_array.dm
@@ -89,12 +89,12 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user)
if(!logged_explosions.len)
- atom_say("No logs currently stored in internal database.")
+ atom_say("No logs currently stored in internal database.")
return
if(active_timers)
to_chat(user, "[src] is already printing something, please wait.")
return
- atom_say("Printing explosive log. Standby...")
+ atom_say("Printing explosive log. Standby...")
addtimer(CALLBACK(src, .proc/print), 50)
/obj/machinery/doppler_array/proc/print()
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index a17f9dfb9bd..b6da46a67c8 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -177,8 +177,8 @@
F.broken = 0
F.times_used = 0
F.icon_state = "flash"
- if(istype(O,/obj/item/gun/energy/disabler/cyborg))
- var/obj/item/gun/energy/disabler/cyborg/D = O
+ if(istype(O,/obj/item/gun/energy))
+ var/obj/item/gun/energy/D = O
if(D.cell.charge < D.cell.maxcharge)
var/obj/item/ammo_casing/energy/E = D.ammo_type[D.select]
D.cell.give(E.e_cost)
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 6529dd4b73d..81379df97c4 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -225,7 +225,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
message_log += "Message sent to [recipient] at [station_time_timestamp()]
[message]"
Radio.autosay("Alert; a new requests console message received for [recipient] from [department]", null, "[radiochannel]")
else
- audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4)
+ atom_say("No server detected!")
//Handle screen switching
if(href_list["setScreen"])
@@ -256,7 +256,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
else if(world.time < print_cooldown)
error_message = "Please allow the printer time to prepare the next shipping label."
if(error_message)
- audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: [error_message]'"),,4)
+ atom_say("[error_message]")
return
print_label(ship_tag_name, ship_tag_index)
shipping_log += "Shipping Label printed for [ship_tag_name]
[msgVerified]"
diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm
index 7e468a548bf..dc933099faa 100644
--- a/code/game/machinery/tcomms/_base.dm
+++ b/code/game/machinery/tcomms/_base.dm
@@ -306,7 +306,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
if(is_admin(R) && !R.get_preference(CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
- if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes.
+ if(isnewplayer(R)) // we don't want new players to hear messages. rare but generates runtimes.
continue
// --- Can understand the speech ---
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 7dac9de2b9a..9acdcba48de 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -166,10 +166,6 @@
var/mob/M = A
if(istype(firer, /mob))
add_attack_logs(firer, M, "Mecha-shot with [src]")
- if(!iscarbon(firer))
- M.LAssailant = null
- else
- M.LAssailant = firer
else
add_attack_logs(null, M, "Mecha-shot with [src]")
if(life <= 0)
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index 04322c63093..896ce886887 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -5,8 +5,8 @@
/turf/simulated/floor/mech_bay_recharge_floor/airless
icon_state = "recharge_floor_asteroid"
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/obj/machinery/mech_bay_recharge_port
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 4c912cf8e55..90d466baf3e 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -11,6 +11,7 @@
force = 5
max_integrity = 300 //max_integrity is base health
armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100)
+ bubble_icon = "machine"
var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5)
var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming.
var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items)
@@ -1446,8 +1447,10 @@
diag_hud_set_mechtracking()
-/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
- flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30)
+/obj/mecha/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
+ var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER)
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30)
/obj/mecha/update_remote_sight(mob/living/user)
if(occupant_sight_flags)
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index ec717b3db32..4978a4ee314 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -6,7 +6,7 @@
var/lootdoubles = 1 //if the same item can be spawned twice
var/list/loot //a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
-/obj/effect/spawner/lootdrop/Initialize(mapload)
+/obj/effect/spawner/lootdrop/New()
..()
if(loot && loot.len)
for(var/i = lootcount, i > 0, i--)
@@ -17,7 +17,7 @@
if(lootspawn)
new lootspawn(loc)
- return INITIALIZE_HINT_QDEL
+ qdel(src)
/obj/effect/spawner/lootdrop/armory_contraband
name = "armory contraband gun spawner"
diff --git a/code/game/objects/effects/spawners/vaultspawner.dm b/code/game/objects/effects/spawners/vaultspawner.dm
index 0d882cc9bd2..a78c6111eb7 100644
--- a/code/game/objects/effects/spawners/vaultspawner.dm
+++ b/code/game/objects/effects/spawners/vaultspawner.dm
@@ -21,6 +21,7 @@
if(i == lowBoundX || i == hiBoundX || j == lowBoundY || j == hiBoundY)
new /turf/simulated/wall/vault(locate(i,j,z),type)
else
- new /turf/simulated/floor/vault(locate(i,j,z),type)
+ var/turf/T = new /turf/simulated/floor/vault(locate(i, j, z))
+ T.icon_state = "[type]vault"
qdel(src)
diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm
index 77aef9bb71d..5246652aa0e 100644
--- a/code/game/objects/items/contraband.dm
+++ b/code/game/objects/items/contraband.dm
@@ -56,7 +56,7 @@
desc = "Huh."
allow_wrap = FALSE
-/obj/item/storage/pill_bottle/random_drug_bottle/New()
- ..()
+/obj/item/storage/pill_bottle/random_drug_bottle/Initialize(mapload)
+ . = ..()
for(var/i in 1 to 5)
new /obj/item/reagent_containers/food/pill/random_drugs(src)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 97c5817f05b..95b36194f56 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -241,6 +241,7 @@
user.base_icon = disguise
user.icon_state = disguise
user.cham_proj = src
+ user.bubble_icon = "robot"
active = TRUE
user.update_icons()
@@ -249,6 +250,7 @@
S = user
user.base_icon = initial(user.base_icon)
user.icon_state = initial(user.icon_state)
+ user.bubble_icon = "syndibot"
active = FALSE
user.update_icons()
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 58848f3f7e4..8bfbf22f05d 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -113,7 +113,6 @@
/obj/item/flash/attack(mob/living/M, mob/user)
if(!try_use_flash(user))
return 0
-
if(iscarbon(M))
flash_carbon(M, user, 5, 1)
if(overcharged)
@@ -121,22 +120,12 @@
M.IgniteMob()
burn_out()
return 1
-
else if(issilicon(M))
- if(isrobot(M))
- var/mob/living/silicon/robot/R = M
- if(R.module) // Perhaps they didn't choose a module yet
- for(var/obj/item/borg/combat/shield/S in R.module.modules)
- if(R.activated(S))
- add_attack_logs(user, M, "Flashed with [src]")
- user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!")
- return 1
add_attack_logs(user, M, "Flashed with [src]")
if(M.flash_eyes(affect_silicon = 1))
M.Weaken(rand(5,10))
user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!")
return 1
-
user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!")
diff --git a/code/game/objects/items/devices/pizza_bomb.dm b/code/game/objects/items/devices/pizza_bomb.dm
index dd824332ad3..e5c237938d6 100644
--- a/code/game/objects/items/devices/pizza_bomb.dm
+++ b/code/game/objects/items/devices/pizza_bomb.dm
@@ -52,7 +52,7 @@
if(disarmed)
visible_message("[bicon(src)] Sparks briefly jump out of the [correct_wire] wire on \the [src], but it's disarmed!")
return
- src.audible_message("[bicon(src)] [src] beeps, \"Enjoy the pizza!\"")
+ atom_say("Enjoy the pizza!")
src.visible_message("\The [src] violently explodes!")
explosion(src.loc,1,2,4,flame_range = 2) //Identical to a minibomb
qdel(src)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 97aa706060a..009ec01ef43 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -623,6 +623,9 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
..()
set_frequency(ERT_FREQ)
+/obj/item/radio/borg/ert/specops
+ keyslot = new /obj/item/encryptionkey/centcom
+
/obj/item/radio/borg/attackby(obj/item/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/encryptionkey/))
user.set_machine(src)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 0d8873a6377..f20a2cc5875 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -141,7 +141,7 @@ REAGENT SCANNER
// Used by the PDA medical scanner too
/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE)
- if(!ishuman(M) || M.isSynthetic())
+ if(!ishuman(M) || ismachineperson(M))
//these sensors are designed for organic life
to_chat(user, "Analyzing Results for ERROR:\n\t Overall Status: ERROR")
to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute")
diff --git a/code/game/objects/items/devices/sensor_device.dm b/code/game/objects/items/devices/sensor_device.dm
index 8c69402ed70..b83128cf4a5 100644
--- a/code/game/objects/items/devices/sensor_device.dm
+++ b/code/game/objects/items/devices/sensor_device.dm
@@ -6,7 +6,7 @@
w_class = WEIGHT_CLASS_SMALL
slot_flags = SLOT_BELT
origin_tech = "programming=3;materials=3;magnets=3"
- var/datum/nano_module/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
/obj/item/sensor_device/New()
..()
@@ -17,7 +17,7 @@
return ..()
/obj/item/sensor_device/attack_self(mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
-/obj/item/sensor_device/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- crew_monitor.ui_interact(user, ui_key, ui, force_open)
+/obj/item/sensor_device/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ crew_monitor.tgui_interact(user, ui_key, ui, force_open)
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 43178a85ef8..8b82341a06c 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -9,23 +9,23 @@
origin_tech = "programming=2"
var/locked = 0
var/installed = 0
- var/require_module = 0
+ var/require_module = FALSE
var/module_type = null
/obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R)
if(R.stat == DEAD)
to_chat(usr, "[src] will not function on a deceased cyborg.")
- return 1
+ return TRUE
if(module_type && !istype(R.module, module_type))
to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!")
to_chat(usr, "There's no mounting point for the module!")
- return 1
+ return TRUE
/obj/item/borg/upgrade/reset
name = "cyborg module reset board"
desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the cyborg."
icon_state = "cyborg_upgrade1"
- require_module = 1
+ require_module = TRUE
/obj/item/borg/upgrade/reset/action(mob/living/silicon/robot/R)
if(..())
@@ -55,7 +55,7 @@
R.custom_name = heldname
R.real_name = heldname
- return 1
+ return TRUE
/obj/item/borg/upgrade/restart
name = "cyborg emergency reboot module"
@@ -77,14 +77,14 @@
GLOB.alive_mob_list += R
R.notify_ai(1)
- return 1
+ return TRUE
/obj/item/borg/upgrade/vtec
name = "robotic VTEC Module"
desc = "Used to kick in a robot's VTEC systems, increasing their speed."
icon_state = "cyborg_upgrade2"
- require_module = 1
+ require_module = TRUE
origin_tech = "engineering=4;materials=5;programming=4"
/obj/item/borg/upgrade/vtec/action(var/mob/living/silicon/robot/R)
@@ -97,14 +97,14 @@
R.speed = -1 // Gotta go fast.
- return 1
+ return TRUE
/obj/item/borg/upgrade/disablercooler
name = "cyborg rapid disabler cooling module"
desc = "Used to cool a mounted disabler, increasing the potential current in it and thus its recharge rate."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;powerstorage=4;combat=4"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/security
/obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R)
@@ -122,7 +122,7 @@
T.charge_delay = max(2 , T.charge_delay - 4)
- return 1
+ return TRUE
/obj/item/borg/upgrade/thrusters
name = "ion thruster upgrade"
@@ -139,14 +139,14 @@
return
R.ionpulse = 1
- return 1
+ return TRUE
/obj/item/borg/upgrade/ddrill
name = "mining cyborg diamond drill"
desc = "A diamond drill replacement for the mining module's standard drill."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;materials=5"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/miner
/obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R)
@@ -161,14 +161,14 @@
R.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(R.module)
R.module.rebuild()
- return 1
+ return TRUE
/obj/item/borg/upgrade/soh
name = "mining cyborg satchel of holding"
desc = "A satchel of holding replacement for mining cyborg's ore satchel module."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;materials=4;bluespace=4"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/miner
/obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R)
@@ -181,35 +181,46 @@
R.module.modules += new /obj/item/storage/bag/ore/holding(R.module)
R.module.rebuild()
- return 1
+ return TRUE
/obj/item/borg/upgrade/syndicate
- name = "illegal equipment module"
+ name = "safety override module"
desc = "Unlocks the hidden, deadlier functions of a cyborg. Also prevents emag subversion."
icon_state = "cyborg_upgrade3"
origin_tech = "combat=4;syndicate=1"
- require_module = 1
+ require_module = TRUE
/obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/R)
if(..())
return
-
if(R.emagged)
return
-
if(R.weapons_unlock)
to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.");
return
-
R.emagged = 1
+ return TRUE
- return 1
+/obj/item/borg/upgrade/lavaproof
+ name = "mining cyborg lavaproof chassis"
+ desc = "An upgrade kit to apply specialized coolant systems and insulation layers to a mining cyborg's chassis, enabling them to withstand exposure to molten rock."
+ icon_state = "ash_plating"
+ resistance_flags = LAVA_PROOF | FIRE_PROOF
+ require_module = TRUE
+ module_type = /obj/item/robot_module/miner
+
+/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R)
+ if(..())
+ return
+ if(istype(R))
+ R.weather_immunities += "lava"
+ return TRUE
/obj/item/borg/upgrade/selfrepair
name = "self-repair module"
desc = "This module will repair the cyborg over time."
icon_state = "cyborg_upgrade5"
- require_module = 1
+ require_module = TRUE
var/repair_amount = -1
var/repair_tick = 1
var/msg_cooldown = 0
@@ -230,7 +241,7 @@
icon_state = "selfrepair_off"
var/datum/action/A = new /datum/action/item_action/toggle(src)
A.Grant(R)
- return 1
+ return TRUE
/obj/item/borg/upgrade/selfrepair/Destroy()
cyborg = null
diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm
index 708070e1478..ff469ba3c59 100644
--- a/code/game/objects/items/stacks/nanopaste.dm
+++ b/code/game/objects/items/stacks/nanopaste.dm
@@ -57,7 +57,7 @@
E.heal_damage(0, remheal, 0, 1) //Healing Burn
remheal = nremheal
user.visible_message("\The [user] applies some nanite paste at \the [M]'s [E.name] with \the [src].")
- if(H.bleed_rate && H.isSynthetic())
+ if(H.bleed_rate && ismachineperson(H))
H.bleed_rate = 0
else
to_chat(user, "Nothing to fix here.")
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 14ccc103ab1..ab592cc0402 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -204,7 +204,7 @@
if(amount < 1) // Just in case a stack's amount ends up fractional somehow
var/oldsrc = src
- src = null //dont kill proc after del()
+ src = null //dont kill proc after qdel()
usr.unEquip(oldsrc, 1)
qdel(oldsrc)
if(istype(O, /obj/item))
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 40d659ccf6b..66f35a169f9 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1408,6 +1408,7 @@ obj/item/toy/cards/deck/syndicate/black
name = "xenomorph action figure"
desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use."
w_class = WEIGHT_CLASS_SMALL
+ bubble_icon = "alien"
var/cooldown = 0
/obj/item/toy/toy_xeno/attack_self(mob/user)
@@ -1416,7 +1417,7 @@ obj/item/toy/cards/deck/syndicate/black
user.visible_message("[user] pulls back the string on [src].")
icon_state = "[initial(icon_state)]_used"
sleep(5)
- audible_message("[bicon(src)] Hiss!")
+ atom_say("Hiss!")
var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg')
playsound(get_turf(src), pick(possible_sounds), 50, 1)
spawn(45)
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index d6397025424..f8f9a48102a 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -477,9 +477,7 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
return R.cell.charge >= (amount * use_multiplier)
/obj/item/rcd/proc/detonate_pulse()
- audible_message("[src] begins to vibrate and \
- buzz loudly!","[src] begins \
- vibrating violently!")
+ audible_message("[src] begins to vibrate and buzz loudly!", "[src] begins vibrating violently!")
// 5 seconds to get rid of it
addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50)
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index 8f11f65fe71..45047e97156 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -147,10 +147,6 @@
to_chat(user, "You inject yourself with [src].")
add_attack_logs(user, M, attack_log, ATKLOG_ALL)
- if(!iscarbon(user))
- M.LAssailant = null
- else
- M.LAssailant = user
inject(M, user)
used = TRUE
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index f965907c5ba..6cf011bc2c3 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -121,19 +121,6 @@
desc = "A sleek, sturdy box"
icon_state = "box_of_doom"
-/obj/item/storage/box/syndie_kit/romerol
- name = "Romerol Kit"
- desc = "A box containing a deadly virus capable of reanimating dead as zombies."
- max_w_class = WEIGHT_CLASS_NORMAL
- can_hold = list(/obj/item/reagent_containers/glass/bottle/romerol,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/dropper)
-
-/obj/item/storage/box/syndie_kit/romerol/New()
- ..()
- new /obj/item/reagent_containers/glass/bottle/romerol(src)
- new /obj/item/reagent_containers/syringe(src)
- new /obj/item/reagent_containers/dropper(src)
- return
-
/obj/item/storage/box/syndie_kit/space
name = "Boxed Space Suit and Helmet"
can_hold = list(/obj/item/clothing/suit/space/syndicate/black/red, /obj/item/clothing/head/helmet/space/syndicate/black/red, /obj/item/tank/emergency_oxygen/syndi, /obj/item/clothing/mask/gas/syndicate)
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 6782057d928..a39050cba47 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -189,8 +189,8 @@
L.apply_effect(STUTTER, stunforce)
if(user)
- user.lastattacked = L
- L.lastattacker = user
+ L.lastattacker = user.real_name
+ L.lastattackerckey = user.ckey
L.visible_message("[user] has stunned [L] with [src]!", \
"[user] has stunned you with [src]!")
add_attack_logs(user, L, "stunned")
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 43244c470ff..d40c628ed90 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -347,6 +347,7 @@ a {
.["Make speed process"] = "?_src_=vars;makespeedy=[UID()]"
else
.["Make normal process"] = "?_src_=vars;makenormalspeed=[UID()]"
+ .["Modify armor values"] = "?_src_=vars;modifyarmor=[UID()]"
/obj/proc/check_uplink_validity()
return TRUE
diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
index 448fd6feb34..0da39d2255d 100644
--- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
@@ -115,6 +115,8 @@
new /obj/item/clothing/shoes/black(src)
new /obj/item/clothing/glasses/sunglasses/big(src)
new /obj/item/clothing/glasses/sunglasses/big(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
//Paramedic
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 8576afe14b8..be004a82912 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -466,3 +466,4 @@
new /obj/item/gavelhammer(src)
new /obj/item/clothing/head/justice_wig(src)
new /obj/item/clothing/accessory/medal/legal(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 87107f97abe..1a5b275bc01 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -403,75 +403,7 @@
L.ExtinguishMob()
L.adjust_fire_stacks(-20) //Douse ourselves with water to avoid fire more easily
to_chat(L, "You've been drenched in water!")
- if(iscarbon(O))
- var/mob/living/carbon/M = O
- if(M.r_hand)
- M.r_hand.clean_blood()
- if(M.l_hand)
- M.l_hand.clean_blood()
- if(M.back)
- if(M.back.clean_blood())
- M.update_inv_back()
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/washgloves = 1
- var/washshoes = 1
- var/washmask = 1
- var/washears = 1
- var/washglasses = 1
-
- if(H.wear_suit)
- washgloves = !(H.wear_suit.flags_inv & HIDEGLOVES)
- washshoes = !(H.wear_suit.flags_inv & HIDESHOES)
-
- if(H.head)
- washmask = !(H.head.flags_inv & HIDEMASK)
- washglasses = !(H.head.flags_inv & HIDEEYES)
- washears = !(H.head.flags_inv & HIDEEARS)
-
- if(H.wear_mask)
- if(washears)
- washears = !(H.wear_mask.flags_inv & HIDEEARS)
- if(washglasses)
- washglasses = !(H.wear_mask.flags_inv & HIDEEYES)
-
- if(H.head)
- if(H.head.clean_blood())
- H.update_inv_head()
- if(H.wear_suit)
- if(H.wear_suit.clean_blood())
- H.update_inv_wear_suit()
- else if(H.w_uniform)
- if(H.w_uniform.clean_blood())
- H.update_inv_w_uniform()
- if(H.gloves && washgloves)
- if(H.gloves.clean_blood())
- H.update_inv_gloves()
- if(H.shoes && washshoes)
- if(H.shoes.clean_blood())
- H.update_inv_shoes()
- if(H.wear_mask && washmask)
- if(H.wear_mask.clean_blood())
- H.update_inv_wear_mask()
- if(H.glasses && washglasses)
- if(H.glasses.clean_blood())
- H.update_inv_glasses()
- if(H.l_ear && washears)
- if(H.l_ear.clean_blood())
- H.update_inv_ears()
- if(H.r_ear && washears)
- if(H.r_ear.clean_blood())
- H.update_inv_ears()
- if(H.belt)
- if(H.belt.clean_blood())
- H.update_inv_belt()
- else
- if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags
- if(M.wear_mask.clean_blood())
- M.update_inv_wear_mask()
-
- else
- O.clean_blood()
+ L.clean_blood()
if(isturf(loc))
var/turf/tile = loc
diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm
index 19717991593..6bc1c4ba142 100644
--- a/code/game/turfs/simulated.dm
+++ b/code/game/turfs/simulated.dm
@@ -8,12 +8,6 @@
nitrogen = MOLES_N2STANDARD
var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to
- var/dirtoverlay = null
-
-/turf/simulated/New()
- ..()
- levelupdate()
- visibilityChanged()
/turf/simulated/proc/break_tile()
return
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 55b4f336917..614b8232b0b 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -31,8 +31,8 @@ GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","dama
var/list/burnt_states = list("floorscorched1", "floorscorched2")
var/list/prying_tool_list = list(TOOL_CROWBAR) //What tool/s can we use to pry up the tile?
-/turf/simulated/floor/New()
- ..()
+/turf/simulated/floor/Initialize(mapload)
+ . = ..()
if(icon_state in GLOB.icons_to_ignore_at_floor_init) //so damaged/burned tiles or plating icons aren't saved as the default
icon_regular_floor = "floor"
else
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index f8dc414ef85..bfbfa70331e 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -49,9 +49,9 @@
floor_tile = /obj/item/stack/tile/grass
broken_states = list("sand")
-/turf/simulated/floor/grass/Initialize()
+/turf/simulated/floor/grass/Initialize(mapload)
+ . = ..()
update_icon()
- ..()
/turf/simulated/floor/grass/update_icon()
icon_state = "grass[pick("1","2","3","4")]"
@@ -80,10 +80,9 @@
)
-/turf/simulated/floor/carpet/New()
- ..()
- if(broken || burnt)
- make_plating()
+/turf/simulated/floor/carpet/Initialize(mapload)
+ . = ..()
+ update_icon()
/turf/simulated/floor/carpet/update_icon()
if(!..())
@@ -119,18 +118,18 @@
broken_states = list("damaged")
plane = PLANE_SPACE
-/turf/simulated/floor/fakespace/New()
- ..()
- icon_state = "[rand(0,25)]"
-
-/turf/simulated/floor/carpet/arcade
- icon = 'icons/goonstation/turf/floor.dmi'
- icon_state = "arcade"
- floor_tile = /obj/item/stack/tile/arcade_carpet
- smooth = SMOOTH_FALSE
+/turf/simulated/floor/fakespace/Initialize(mapload)
+ . = ..()
+ icon_state = SPACE_ICON_STATE
/turf/simulated/floor/fakespace/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
underlay_appearance.icon = 'icons/turf/space.dmi'
underlay_appearance.icon_state = SPACE_ICON_STATE
underlay_appearance.plane = PLANE_SPACE
return TRUE
+
+/turf/simulated/floor/carpet/arcade
+ icon = 'icons/goonstation/turf/floor.dmi'
+ icon_state = "arcade"
+ floor_tile = /obj/item/stack/tile/arcade_carpet
+ smooth = SMOOTH_FALSE
diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm
index b32580b9bab..14a08639132 100644
--- a/code/game/turfs/simulated/floor/lava.dm
+++ b/code/game/turfs/simulated/floor/lava.dm
@@ -8,9 +8,6 @@
light_power = 0.75
light_color = LIGHT_COLOR_LAVA
-/turf/simulated/floor/plating/lava/New()
- ..()
-
/turf/simulated/floor/plating/lava/ex_act()
return
diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm
index 2c24a422343..8276c0de33f 100644
--- a/code/game/turfs/simulated/floor/light_floor.dm
+++ b/code/game/turfs/simulated/floor/light_floor.dm
@@ -20,8 +20,8 @@
var/state = LIGHTFLOOR_ON
var/can_modify_colour = TRUE
-/turf/simulated/floor/light/New()
- ..()
+/turf/simulated/floor/light/Initialize(mapload)
+ . = ..()
update_icon()
/turf/simulated/floor/light/update_icon()
diff --git a/code/game/turfs/simulated/floor/mineral.dm b/code/game/turfs/simulated/floor/mineral.dm
index ffb1cce0b5c..dd975dc80d9 100644
--- a/code/game/turfs/simulated/floor/mineral.dm
+++ b/code/game/turfs/simulated/floor/mineral.dm
@@ -14,8 +14,8 @@
icon_state = ""
var/list/icons = list()
-/turf/simulated/floor/mineral/New()
- ..()
+/turf/simulated/floor/mineral/Initialize(mapload)
+ . = ..()
broken_states = list("[initial(icon_state)]_dam")
/turf/simulated/floor/mineral/update_icon()
@@ -97,24 +97,24 @@
broken_states = list("titanium_dam1","titanium_dam2","titanium_dam3","titanium_dam4","titanium_dam5")
/turf/simulated/floor/mineral/titanium/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/turf/simulated/floor/mineral/titanium/blue
icon_state = "titanium_blue"
/turf/simulated/floor/mineral/titanium/blue/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/turf/simulated/floor/mineral/titanium/yellow
icon_state = "titanium_yellow"
/turf/simulated/floor/mineral/titanium/yellow/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/turf/simulated/floor/mineral/titanium/purple
@@ -122,8 +122,8 @@
floor_tile = /obj/item/stack/tile/mineral/titanium/purple
/turf/simulated/floor/mineral/titanium/purple/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
//PLASTITANIUM (syndieshuttle)
@@ -137,8 +137,8 @@
icon_state = "plastitanium_red"
/turf/simulated/floor/mineral/plastitanium/red/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/turf/simulated/floor/mineral/plastitanium/red/brig
@@ -179,8 +179,8 @@
spam_flag = world.time + 10
/turf/simulated/floor/mineral/bananium/airless
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
@@ -254,8 +254,8 @@
floor_tile = /obj/item/stack/tile/mineral/abductor
icons = list("alienpod1", "alienpod2", "alienpod3", "alienpod4", "alienpod5", "alienpod6", "alienpod7", "alienpod8", "alienpod9")
-/turf/simulated/floor/mineral/abductor/New()
- ..()
+/turf/simulated/floor/mineral/abductor/Initialize(mapload)
+ . = ..()
icon_state = "alienpod[rand(1,9)]"
/turf/simulated/floor/mineral/abductor/break_tile()
diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm
index 95d5f816aa4..83835cd947f 100644
--- a/code/game/turfs/simulated/floor/misc_floor.dm
+++ b/code/game/turfs/simulated/floor/misc_floor.dm
@@ -3,19 +3,11 @@
icon_state = "rockvault"
smooth = SMOOTH_FALSE
-/turf/simulated/floor/vault/New(location, vtype)
- ..()
- icon_state = "[vtype]vault"
-
/turf/simulated/wall/vault
icon = 'icons/turf/walls.dmi'
icon_state = "rockvault"
smooth = SMOOTH_FALSE
-/turf/simulated/wall/vault/New(location, vtype)
- ..()
- icon_state = "[vtype]vault"
-
/turf/simulated/floor/bluegrid
icon = 'icons/turf/floors.dmi'
icon_state = "bcircuit"
@@ -35,12 +27,12 @@
/turf/simulated/floor/greengrid/airless
icon_state = "gcircuit"
name = "airless floor"
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
-/turf/simulated/floor/greengrid/airless/New()
- ..()
+/turf/simulated/floor/greengrid/airless/Initialize(mapload)
+ . = ..()
name = "floor"
/turf/simulated/floor/redgrid
@@ -78,8 +70,8 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
var/obj/machinery/poolcontroller/linkedcontroller = null
-/turf/simulated/floor/beach/water/New()
- ..()
+/turf/simulated/floor/beach/water/Initialize(mapload)
+ . = ..()
var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = "water5", layer = ABOVE_MOB_LAYER)
overlay_image.plane = GAME_PLANE
overlays += overlay_image
diff --git a/code/game/turfs/simulated/floor/plasteel_floor.dm b/code/game/turfs/simulated/floor/plasteel_floor.dm
index ee19b4eeeb5..116d57dc1ef 100644
--- a/code/game/turfs/simulated/floor/plasteel_floor.dm
+++ b/code/game/turfs/simulated/floor/plasteel_floor.dm
@@ -12,12 +12,12 @@
/turf/simulated/floor/plasteel/airless
name = "airless floor"
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
-/turf/simulated/floor/plasteel/airless/New()
- ..()
+/turf/simulated/floor/plasteel/airless/Initialize(mapload)
+ . = ..()
name = "floor"
/turf/simulated/floor/plasteel/airless/indestructible // For bomb testing range
diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm
index c692135dc44..a6dbe0de281 100644
--- a/code/game/turfs/simulated/floor/plating.dm
+++ b/code/game/turfs/simulated/floor/plating.dm
@@ -14,17 +14,17 @@
"xeno" = list('sound/effects/footstep/plating_xeno.ogg')
)
-/turf/simulated/floor/plating/New()
- ..()
+/turf/simulated/floor/plating/Initialize(mapload)
+ . = ..()
icon_plating = icon_state
update_icon()
-/turf/simulated/floor/plating/damaged/New()
- ..()
+/turf/simulated/floor/plating/damaged/Initialize(mapload)
+ . = ..()
break_tile()
-/turf/simulated/floor/plating/burnt/New()
- ..()
+/turf/simulated/floor/plating/burnt/Initialize(mapload)
+ . = ..()
burn_tile()
/turf/simulated/floor/plating/update_icon()
@@ -113,12 +113,12 @@
/turf/simulated/floor/plating/airless
icon_state = "plating"
name = "airless plating"
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
-/turf/simulated/floor/plating/airless/New()
- ..()
+/turf/simulated/floor/plating/airless/Initialize(mapload)
+ . = ..()
name = "plating"
/turf/simulated/floor/engine
@@ -190,8 +190,8 @@
name = "engraved floor"
icon_state = "cult"
-/turf/simulated/floor/engine/cult/New()
- ..()
+/turf/simulated/floor/engine/cult/Initialize(mapload)
+ . = ..()
if(SSticker.mode)//only do this if the round is going..otherwise..fucking asteroid..
icon_state = SSticker.cultdat.cult_floor_icon_state
@@ -275,8 +275,8 @@
icon = 'icons/turf/floors/ironsand.dmi'
icon_state = "ironsand1"
-/turf/simulated/floor/plating/ironsand/New()
- ..()
+/turf/simulated/floor/plating/ironsand/Initialize(mapload)
+ . = ..()
icon_state = "ironsand[rand(1,15)]"
/turf/simulated/floor/plating/ironsand/remove_plating()
@@ -359,8 +359,8 @@
name = "alien floor"
icon_state = "alienpod1"
-/turf/simulated/floor/plating/abductor/New()
- ..()
+/turf/simulated/floor/plating/abductor/Initialize(mapload)
+ . = ..()
icon_state = "alienpod[rand(1,9)]"
/turf/simulated/floor/plating/ice
diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm
index f34465fd60f..56908a7ecae 100644
--- a/code/game/turfs/simulated/walls_misc.dm
+++ b/code/game/turfs/simulated/walls_misc.dm
@@ -9,8 +9,8 @@
sheet_amount = 1
girder_type = /obj/structure/girder/cult
-/turf/simulated/wall/cult/New()
- ..()
+/turf/simulated/wall/cult/Initialize(mapload)
+ . = ..()
if(SSticker.mode)//game hasn't started offically don't do shit..
new /obj/effect/temp_visual/cult/turf(src)
icon_state = SSticker.cultdat.cult_wall_icon_state
@@ -72,10 +72,7 @@
realappearance.linked = src
/turf/simulated/wall/clockwork/Destroy()
- if(realappearance)
- qdel(realappearance)
- realappearance = null
-
+ QDEL_NULL(realappearance)
return ..()
/turf/simulated/wall/clockwork/ReplaceWithLattice()
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index f78f520cfd3..1a8ccdae550 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -21,6 +21,11 @@
/turf/space/Initialize(mapload)
if(!istype(src, /turf/space/transit))
icon_state = SPACE_ICON_STATE
+ vis_contents.Cut() //removes inherited overlays
+
+ if(initialized)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ initialized = TRUE
var/area/A = loc
if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A))
@@ -34,12 +39,6 @@
return INITIALIZE_HINT_NORMAL
-/turf/space/Destroy(force)
- if(force)
- . = ..()
- else
- return QDEL_HINT_LETMELIVE
-
/turf/space/BeforeChange()
..()
var/datum/space_level/S = GLOB.space_manager.get_zlev(z)
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index 2a14230d467..a96cbae1269 100644
--- a/code/game/turfs/space/transit.dm
+++ b/code/game/turfs/space/transit.dm
@@ -125,9 +125,9 @@
/turf/space/transit/attackby()
return
-/turf/space/transit/New()
+/turf/space/transit/Initialize(mapload)
+ . = ..()
update_icon()
- ..()
/turf/space/transit/proc/update_icon()
var/p = 9
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index d1541dcc4fa..e174634123d 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -31,21 +31,29 @@
var/image/obscured //camerachunks
+ var/changing_turf = FALSE
+
var/list/blueprint_data //for the station blueprints, images of objects eg: pipes
var/list/footstep_sounds
var/shoe_running_volume = 50
var/shoe_walking_volume = 20
-/turf/New()
- ..()
+/turf/Initialize(mapload)
+ if(initialized)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ initialized = TRUE
+
+ // by default, vis_contents is inherited from the turf that was here before
+ vis_contents.Cut()
+
+ levelupdate()
+ if(smooth)
+ queue_smooth(src)
+ visibilityChanged()
+
for(var/atom/movable/AM in src)
Entered(AM)
- if(smooth && SSticker && SSticker.current_state == GAME_STATE_PLAYING)
- queue_smooth(src)
-
-/turf/Initialize(mapload)
- . = ..()
var/area/A = loc
if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A))
@@ -54,9 +62,11 @@
if(light_power && light_range)
update_light()
- if (opacity)
+ if(opacity)
has_opaque_atom = TRUE
+ return INITIALIZE_HINT_NORMAL
+
/hook/startup/proc/smooth_world()
var/watch = start_watch()
log_startup_progress("Smoothing atoms...")
@@ -70,16 +80,29 @@
log_startup_progress(" Smoothed atoms in [stop_watch(watch)]s.")
return TRUE
-/turf/Destroy()
-// Adds the adjacent turfs to the current atmos processing
- if(SSair)
- for(var/direction in GLOB.cardinal)
- if(atmos_adjacent_turfs & direction)
- var/turf/simulated/T = get_step(src, direction)
- if(istype(T))
- SSair.add_to_active(T)
+/turf/Destroy(force)
+ . = QDEL_HINT_IWILLGC
+ if(!changing_turf)
+ stack_trace("Incorrect turf deletion")
+ changing_turf = FALSE
+ if(force)
+ ..()
+ //this will completely wipe turf state
+ var/turf/B = new world.turf(src)
+ for(var/A in B.contents)
+ qdel(A)
+ return
+ // Adds the adjacent turfs to the current atmos processing
+ for(var/direction in GLOB.cardinal)
+ if(atmos_adjacent_turfs & direction)
+ var/turf/simulated/T = get_step(src, direction)
+ if(istype(T))
+ SSair.add_to_active(T)
+ SSair.remove_from_active(src)
+ visibilityChanged()
+ QDEL_LIST(blueprint_data)
+ initialized = FALSE
..()
- return QDEL_HINT_HARDDEL_NOW
/turf/attack_hand(mob/user as mob)
user.Move_Pulled(src)
@@ -209,10 +232,10 @@
var/old_corners = corners
BeforeChange()
- if(SSair)
- SSair.remove_from_active(src)
var/old_baseturf = baseturf
+ changing_turf = TRUE
+ qdel(src) //Just get the side effects and call Destroy
var/turf/W = new path(src)
W.baseturf = old_baseturf
@@ -225,7 +248,6 @@
if(SSlighting.initialized)
recalc_atom_opacity()
lighting_object = old_lighting_object
-
affecting_lights = old_affecting_lights
corners = old_corners
if(old_opacity != opacity || dynamic_lighting != old_dynamic_lighting)
@@ -524,12 +546,9 @@
I.appearance = AM.appearance
I.appearance_flags = RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM
I.loc = src
- I.dir = AM.dir
+ I.setDir(AM.dir)
I.alpha = 128
-
- if(!blueprint_data)
- blueprint_data = list()
- blueprint_data += I
+ LAZYADD(blueprint_data, I)
/turf/proc/add_blueprints_preround(atom/movable/AM)
if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm
index 688dc778ce3..02af9173e2b 100644
--- a/code/game/turfs/unsimulated.dm
+++ b/code/game/turfs/unsimulated.dm
@@ -40,6 +40,6 @@
nitrogen = 0
temperature = TCMB
-/turf/unsimulated/floor/plating/airless/New()
- ..()
+/turf/unsimulated/floor/plating/airless/Initialize(mapload)
+ . = ..()
name = "plating"
diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm
index f6a3a62c324..a7c73971e99 100644
--- a/code/game/turfs/unsimulated/beach.dm
+++ b/code/game/turfs/unsimulated/beach.dm
@@ -3,9 +3,9 @@
icon = 'icons/misc/beach.dmi'
var/water_overlay_image = null
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
-/turf/unsimulated/beach/New()
- ..()
+
+/turf/unsimulated/beach/Initialize(mapload)
+ . = ..()
if(water_overlay_image)
var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER)
overlay_image.plane = GAME_PLANE
@@ -15,10 +15,10 @@
name = "Sand"
icon_state = "desert"
mouse_opacity = MOUSE_OPACITY_ICON
-
-/turf/unsimulated/beach/sand/New() //adds some aesthetic randomness to the beach sand
+
+/turf/unsimulated/beach/sand/Initialize(mapload)
+ . = ..() //adds some aesthetic randomness to the beach sand
icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4")
- ..()
/turf/unsimulated/beach/sand/dense //for boundary "walls"
density = 1
@@ -42,7 +42,7 @@
/turf/unsimulated/beach/water/Entered(atom/movable/AM, atom/OldLoc)
. = ..()
if(!linkedcontroller)
- return
+ return
if(ismob(AM))
linkedcontroller.mobinpool += AM
@@ -79,8 +79,8 @@
/turf/unsimulated/beach/water/edge_drop)
var/obj/effect/beach_drop_overlay/water_overlay
-/turf/unsimulated/beach/water/drop/New()
- ..()
+/turf/unsimulated/beach/water/drop/Initialize(mapload)
+ . = ..()
water_overlay = new(src)
/turf/unsimulated/beach/water/drop/Destroy()
diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm
index 28f4e359223..750ebe015ae 100644
--- a/code/game/turfs/unsimulated/floor.dm
+++ b/code/game/turfs/unsimulated/floor.dm
@@ -7,8 +7,8 @@
name = "grass patch"
icon_state = "grass1"
-/turf/unsimulated/floor/grass/New()
- ..()
+/turf/unsimulated/floor/grass/Initialize(mapload)
+ . = ..()
icon_state = "grass[rand(1,4)]"
/turf/unsimulated/floor/snow
@@ -20,8 +20,8 @@
name = "alien floor"
icon_state = "alienpod1"
-/turf/unsimulated/floor/abductor/New()
- ..()
+/turf/unsimulated/floor/abductor/Initialize(mapload)
+ . = ..()
icon_state = "alienpod[rand(1,9)]"
/turf/unsimulated/floor/vox
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index 2683e236269..95b11353b86 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -27,7 +27,7 @@
entry += " - Observing"
else
entry += " - DEAD"
- else if(istype(C.mob, /mob/new_player))
+ else if(isnewplayer(C.mob))
entry += " - New Player"
else
entry += " - DEAD"
@@ -90,7 +90,7 @@
if(isobserver(C.mob))
msg += " - Observing"
- else if(istype(C.mob,/mob/new_player))
+ else if(isnewplayer(C.mob))
msg += " - Lobby"
else
msg += " - Playing"
@@ -106,7 +106,7 @@
if(isobserver(C.mob))
modmsg += " - Observing"
- else if(istype(C.mob,/mob/new_player))
+ else if(isnewplayer(C.mob))
modmsg += " - Lobby"
else
modmsg += " - Playing"
diff --git a/code/game/world.dm b/code/game/world.dm
index 4b1ccc641cf..355c6280aaf 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -232,7 +232,7 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
if(!C)
return "No client with that name on server"
- del(C)
+ qdel(C)
return "Kick Successful"
@@ -425,10 +425,12 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0)
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log"
GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
+ GLOB.tgui_log = "[GLOB.log_directory]/tgui.log"
start_log(GLOB.world_game_log)
start_log(GLOB.world_href_log)
start_log(GLOB.world_runtime_log)
start_log(GLOB.world_qdel_log)
+ start_log(GLOB.tgui_log)
// This log follows a special format and this path should NOT be used for anything else
GLOB.runtime_summary_log = "data/logs/runtime_summary.log"
diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm
index 21b59586bfd..25693b58f47 100644
--- a/code/modules/admin/DB ban/functions.dm
+++ b/code/modules/admin/DB ban/functions.dm
@@ -146,7 +146,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
if(kickbannedckey)
if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey)
- del(banned_mob.client)
+ qdel(banned_mob.client)
if(isjobban)
jobban_client_fullban(ckey, job)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index df156c5b0df..1eed8083a97 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -72,7 +72,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]"
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
body += " Hasn't Entered Game "
else
body += " \[Heal\] "
@@ -147,7 +147,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += {" | Cryo "}
if(M.client)
- if(!istype(M, /mob/new_player))
+ if(!isnewplayer(M))
body += "
"
body += "Transformation:"
body += "
"
@@ -1004,13 +1004,13 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
/proc/kick_clients_in_lobby(message, kick_only_afk = 0)
var/list/kicked_client_names = list()
for(var/client/C in GLOB.clients)
- if(istype(C.mob, /mob/new_player))
+ if(isnewplayer(C.mob))
if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk
continue
if(message)
to_chat(C, message)
kicked_client_names.Add("[C.ckey]")
- del(C)
+ qdel(C)
return kicked_client_names
//returns 1 to let the dragdrop code know we are trapping this event
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 4e3d2d65f30..80a60957c75 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -337,7 +337,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
ghost.reenter_corpse()
log_admin("[key_name(usr)] re-entered their body")
feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- else if(istype(mob,/mob/new_player))
+ else if(isnewplayer(mob))
to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.")
else
//ghostize
@@ -542,7 +542,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban")
log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban")
to_chat(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.")
- del(C)
+ qdel(C)
else
message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index efb5ac32f97..e89b829f421 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -268,7 +268,7 @@
else
M_job = "Living"
- else if(istype(M,/mob/new_player))
+ else if(isnewplayer(M))
M_job = "New player"
else if(isobserver(M))
@@ -356,7 +356,7 @@
dat += "
[M.real_name] | "
else if(istype(M, /mob/living/silicon/pai))
dat += "pAI | "
- else if(istype(M, /mob/new_player))
+ else if(isnewplayer(M))
dat += "New Player | "
else if(isobserver(M))
dat += "Ghost | "
diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm
index 2db6e09b354..a438a43eaec 100644
--- a/code/modules/admin/sql_notes.dm
+++ b/code/modules/admin/sql_notes.dm
@@ -187,12 +187,12 @@
log_game("SQL ERROR obtaining ckey from notes table. Error : \[[err]\]\n")
return
to_chat(usr, "Started regex note search for [search]. Please wait for results...")
- message_admins("[usr] has started a note search with regex [search]. CPU usage may be higher.")
+ message_admins("[usr.ckey] has started a note search with the following regex: [search] | CPU usage may be higher.")
while(query_list_notes.NextRow())
index_ckey = query_list_notes.item[1]
output += "[index_ckey]
"
CHECK_TICK
- message_admins("The note search started by [usr] has complete. CPU should return to normal.")
+ message_admins("The note search started by [usr.ckey] has complete. CPU should return to normal.")
else
output += "\[Add Note\]"
output += ruler
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 1a040d46e90..ac8adc5fdf0 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -909,7 +909,7 @@
log_admin("[key_name(usr)] booted [key_name(M)].")
message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1)
//M.client = null
- del(M.client)
+ qdel(M.client)
//Player Notes
else if(href_list["addnote"])
@@ -1011,8 +1011,7 @@
log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
- del(M.client)
- //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends.
+ qdel(M.client)
if("No")
var/reason = input(usr,"Please state the reason","Reason") as message|null
if(!reason)
@@ -1032,8 +1031,7 @@
feedback_inc("ban_perma",1)
DB_ban_record(BANTYPE_PERMA, M, -1, reason)
- del(M.client)
- //qdel(M)
+ qdel(M.client)
if("Cancel")
return
@@ -1507,7 +1505,7 @@
if(!check_rights(R_SPAWN)) return
var/mob/M = locateUID(href_list["makeanimal"])
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
to_chat(usr, "This cannot be used on instances of type /mob/new_player")
return
if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes")
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 164d499e04a..dba9a5c26aa 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -19,7 +19,7 @@
var/list/client/targets[0]
for(var/client/T)
if(T.mob)
- if(istype(T.mob, /mob/new_player))
+ if(isnewplayer(T.mob))
targets["(New Player) - [T]"] = T
else if(istype(T.mob, /mob/dead/observer))
targets["[T.mob.name](Ghost) - [T]"] = T
@@ -42,7 +42,7 @@
var/list/client/targets[0]
for(var/client/T)
if(T.mob)
- if(istype(T.mob, /mob/new_player))
+ if(isnewplayer(T.mob))
targets["[T] - (New Player)"] = T
else if(istype(T.mob, /mob/dead/observer))
targets["[T] - [T.mob.name](Ghost)"] = T
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 329efb8e067..496292c919b 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -251,7 +251,7 @@ GLOBAL_PROTECT(AdminProcCaller)
alert("That mob doesn't seem to exist, close the panel and try again.")
return
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
alert("The mob must not be a new_player.")
return
@@ -382,7 +382,7 @@ GLOBAL_PROTECT(AdminProcCaller)
if(!check_rights(R_DEBUG))
return
- //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all
+ //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with qdel-all
var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No")
if(confirm != "Yes")
return
diff --git a/code/modules/admin/verbs/onlyoneteam.dm b/code/modules/admin/verbs/onlyoneteam.dm
index 9575cc5b340..974b65e05d2 100644
--- a/code/modules/admin/verbs/onlyoneteam.dm
+++ b/code/modules/admin/verbs/onlyoneteam.dm
@@ -73,9 +73,11 @@
..()
if((ishuman(hit_atom)))
var/mob/living/carbon/human/H = hit_atom
- if(H.r_hand == src) return
- if(H.l_hand == src) return
- var/mob/A = H.LAssailant
+ if(H.r_hand == src)
+ return
+ if(H.l_hand == src)
+ return
+ var/mob/A = thrownby
if((H in GLOB.team_alpha) && (A in GLOB.team_alpha))
to_chat(A, "He's on your team!")
return
@@ -89,4 +91,3 @@
playsound(src, 'sound/items/dodgeball.ogg', 50, 1)
visible_message("[H] HAS BEEN ELIMINATED!")
H.melt()
- return
diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm
index 94337a5cfac..cb144c99f01 100644
--- a/code/modules/arcade/mob_hunt/battle_computer.dm
+++ b/code/modules/arcade/mob_hunt/battle_computer.dm
@@ -80,7 +80,7 @@
/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0)
if(!override)
if(ready && SSmob_hunt.battle_turn != team)
- audible_message("You can't recall on your rival's turn!", null, 2)
+ atom_say("You can't recall on your rival's turn!")
return
card.mob_data = mob_info
mob_info = null
@@ -202,7 +202,7 @@
start_battle()
else if(option == 2)
ready = 0
- audible_message("[team] Player cancels their battle challenge.", null, 5)
+ atom_say("[team] Player cancels their battle challenge.")
updateUsrDialog()
@@ -246,7 +246,7 @@
var/message = "[mob_info.mob_name] attacks!"
if(mob_info.nickname)
message = "[mob_info.nickname] attacks!"
- audible_message(message, null, 5)
+ atom_say(message)
SSmob_hunt.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type())
/obj/machinery/computer/mob_battle_terminal/proc/start_battle()
@@ -255,7 +255,7 @@
if(!card) //don't do anything if there isn't a card inserted
return
ready = 1
- audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5)
+ atom_say("[team] Player is ready for battle! Waiting for rival...")
SSmob_hunt.start_check()
/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type)
@@ -268,7 +268,7 @@
SSmob_hunt.end_turn()
/obj/machinery/computer/mob_battle_terminal/proc/surrender()
- audible_message("[team] Player surrenders the battle!", null, 5)
+ atom_say("[team] Player surrenders the battle!")
SSmob_hunt.end_battle(team, 1)
//////////////////////////////
diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm
index 89eda41be7c..d1077bc81e1 100644
--- a/code/modules/arcade/mob_hunt/mob_avatar.dm
+++ b/code/modules/arcade/mob_hunt/mob_avatar.dm
@@ -55,7 +55,7 @@
var/datum/data/pda/app/mob_hunter_game/client = P.current_app
var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee
if(!client.connected) //must be connected to attempt captures
- P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4)
+ P.atom_say("No server connection. Capture aborted.")
return
if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check
@@ -79,7 +79,7 @@
return
else //deal with the new hunter by either running away or getting caught
clients_encountered += client
- var/message = "[bicon(P)] "
+ var/message = null
var/effective_run_chance = mob_info.run_chance + total_catch_mod
if((effective_run_chance > 0) && prob(effective_run_chance))
message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!"
@@ -91,7 +91,7 @@
else
message += "Capture error! Try again."
clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob
- P.audible_message(message, null, 4)
+ P.atom_say(message)
/obj/effect/nanomob/proc/despawn()
if(SSmob_hunt)
diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm
index a83df5501c9..a0a7c43a7a9 100644
--- a/code/modules/assembly/health.dm
+++ b/code/modules/assembly/health.dm
@@ -55,7 +55,7 @@
health_scan = M.health
if(health_scan <= alarm_health)
pulse()
- audible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*")
+ audible_message("[bicon(src)] *beep* *beep*")
toggle_scan()
return
return
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 400b50614a6..9de2f32ed6f 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -129,7 +129,7 @@
return FALSE
cooldown = 2
pulse(FALSE)
- audible_message("[bicon(src)] *beep* *beep*", null, 3)
+ audible_message("[bicon(src)] *beep* *beep*", hearing_distance = 3)
if(first)
qdel(first)
addtimer(CALLBACK(src, .proc/process_cooldown), 10)
diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm
index bd9890aa43a..92fa90891e0 100644
--- a/code/modules/awaymissions/mission_code/centcomAway.dm
+++ b/code/modules/awaymissions/mission_code/centcomAway.dm
@@ -7,32 +7,32 @@
/area/awaymission/centcomAway/general
name = "XCC-P5831"
- music = "music/ambigen3.ogg"
+ ambientsounds = list('sound/ambience/ambigen3.ogg')
/area/awaymission/centcomAway/maint
name = "XCC-P5831 Maintenance"
icon_state = "away1"
- music = "music/ambisin1.ogg"
+ ambientsounds = list('sound/ambience/ambisin1.ogg')
/area/awaymission/centcomAway/thunderdome
name = "XCC-P5831 Thunderdome"
icon_state = "away2"
- music = "music/ambisin2.ogg"
+ ambientsounds = list('sound/ambience/ambisin2.ogg')
/area/awaymission/centcomAway/cafe
name = "XCC-P5831 Kitchen Arena"
icon_state = "away3"
- music = "music/ambisin3.ogg"
+ ambientsounds = list('sound/ambience/ambisin3.ogg')
/area/awaymission/centcomAway/courtroom
name = "XCC-P5831 Courtroom"
icon_state = "away4"
- music = "music/ambisin4.ogg"
+ ambientsounds = list('sound/ambience/ambisin4.ogg')
/area/awaymission/centcomAway/hangar
name = "XCC-P5831 Hangars"
icon_state = "away4"
- music = "music/ambigen5.ogg"
+ ambientsounds = list('sound/ambience/ambigen5.ogg')
//centcomAway items
diff --git a/code/modules/awaymissions/mission_code/ruins/oldstation.dm b/code/modules/awaymissions/mission_code/ruins/oldstation.dm
index 4d3c54f8f4b..9489ddec45b 100644
--- a/code/modules/awaymissions/mission_code/ruins/oldstation.dm
+++ b/code/modules/awaymissions/mission_code/ruins/oldstation.dm
@@ -321,6 +321,7 @@
name = "Beta Station Atmospherics"
icon_state = "red"
has_gravity = FALSE
+ ambientsounds = ENGINEERING_SOUNDS
/area/ruin/space/ancientstation/betanorth
name = "Beta Station North Corridor"
@@ -333,6 +334,7 @@
/area/ruin/space/ancientstation/engi
name = "Charlie Station Engineering"
icon_state = "engine"
+ ambientsounds = ENGINEERING_SOUNDS
/area/ruin/space/ancientstation/comm
name = "Charlie Station Command"
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index e1e68bd0617..8ca8bf0fbb1 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -177,6 +177,12 @@ GLOBAL_LIST_EMPTY(asset_datums)
//DEFINITIONS FOR ASSET DATUMS START HERE.
+/datum/asset/simple/tgui
+ assets = list(
+ "tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js',
+ "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css'
+)
+
/datum/asset/simple/paper
assets = list(
"large_stamp-clown.png" = 'icons/paper_icons/large_stamp-clown.png',
@@ -344,3 +350,15 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/mob_hunt/send(client)
send_asset_list(client, assets, verify)
+
+// Fontawesome
+/datum/asset/simple/fontawesome
+ verify = FALSE
+ assets = list(
+ "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
+ "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
+ "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
+ "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
+ "font-awesome.css" = 'html/font-awesome/css/all.min.css',
+ "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
+ )
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 3cd6458afdf..9a3a1215df3 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -59,10 +59,6 @@
var/karma = 0
var/karma_spent = 0
var/karma_tab = 0
- /////////////////////////////////////////////
- // /vg/: MEDIAAAAAAAA
- // Set on login.
- var/datum/media_manager/media = null
var/topic_debugging = 0 //if set to true, allows client to see nanoUI errors -- yes i realize this is messy but it'll make live testing infinitely easier
@@ -85,9 +81,6 @@
// If set to true, this client can interact with atoms such as buttons and doors on top of regular machinery interaction
var/advanced_admin_interaction = FALSE
- // Has the client been varedited by an admin? [Inherits from datum now]
- // var/var_edited = FALSE
-
var/client_keysend_amount = 0
var/next_keysend_reset = 0
var/next_keysend_trip_reset = 0
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 561b0b48149..c4604fcf9ab 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -432,7 +432,13 @@
//////////////
//DISCONNECT//
//////////////
+
/client/Del()
+ if(!gc_destroyed)
+ Destroy() //Clean up signals and timers.
+ return ..()
+
+/client/Destroy()
if(holder)
holder.owner = null
GLOB.admins -= src
@@ -442,7 +448,8 @@
movingmob.client_mobs_in_contents -= mob
UNSETEMPTY(movingmob.client_mobs_in_contents)
Master.UpdateTickRate()
- return ..()
+ ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
+ return QDEL_HINT_HARDDEL_NOW
/client/proc/donator_check()
@@ -556,7 +563,7 @@
if(GLOB.panic_bunker_enabled)
var/threshold = config.panic_bunker_threshold
src << "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later."
- del(src)
+ qdel(src)
return // Dont insert or they can just go in again
var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')")
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index d34584ff425..7ac87e5d5bc 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -113,7 +113,7 @@
/obj/item/clothing/head/soft/sec/corp
name = "corporate security cap"
- desc = "It's baseball hat in corpotate colours."
+ desc = "It's a baseball hat in corporate colours."
icon_state = "corpsoft"
item_color = "corp"
diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm
index 673b6a14b9a..76bcd87921d 100644
--- a/code/modules/clothing/under/accessories/accessory.dm
+++ b/code/modules/clothing/under/accessories/accessory.dm
@@ -318,6 +318,36 @@
if(isliving(user))
user.visible_message("[user] invades [M]'s personal space, thrusting [src] into [M.p_their()] face insistently.","You invade [M]'s personal space, thrusting [src] into [M.p_their()] face insistently. You are the law.")
+//////////////
+//OBJECTION!//
+//////////////
+
+/obj/item/clothing/accessory/lawyers_badge
+ name = "attorney's badge"
+ desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
+ icon_state = "lawyerbadge"
+ item_state = "lawyerbadge"
+ item_color = "lawyerbadge"
+ var/cached_bubble_icon = null
+
+/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user)
+ if(prob(1))
+ user.say("The testimony contradicts the evidence!")
+ user.visible_message("[user] shows [user.p_their()] attorney's badge.", "You show your attorney's badge.")
+
+/obj/item/clothing/accessory/lawyers_badge/on_attached(obj/item/clothing/under/S, mob/user)
+ ..()
+ if(has_suit && ismob(has_suit.loc))
+ var/mob/M = has_suit.loc
+ cached_bubble_icon = M.bubble_icon
+ M.bubble_icon = "lawyer"
+
+/obj/item/clothing/accessory/lawyers_badge/on_removed(mob/user)
+ if(has_suit && ismob(has_suit.loc))
+ var/mob/M = has_suit.loc
+ M.bubble_icon = cached_bubble_icon
+ ..()
+
///////////
//SCARVES//
///////////
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index f8d2029651d..1a5da52ffce 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -48,7 +48,7 @@
var/mob/living/carbon/human/target = M
- if(ismachine(target))
+ if(ismachineperson(target))
to_chat(user, "[target] has no skin, how do you expect to tattoo [target.p_them()]?")
return
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 563ef98697d..a11ef107a8f 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -16,7 +16,7 @@
density = 1
anchored = 1
var/strength = 2 //ex_act severity number
- var/life = 2 //how many things we hit before del(src)
+ var/life = 2 //how many things we hit before qdel(src)
var/atom/goal = null
/obj/effect/space_dust/weak
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index d6395bf3d43..4515a62d86b 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -115,7 +115,7 @@
//The reagents in the bottle splash all over the target, thanks for the idea Nodrak
SplashReagents(target)
- //Finally, smash the bottle. This kills (del) the bottle.
+ //Finally, smash the bottle. This kills (qdel) the bottle.
smash(target, user)
/obj/item/reagent_containers/food/drinks/bottle/proc/SplashReagents(mob/M)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index 7d17ac07720..db078675db1 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -278,11 +278,6 @@
if(!UserOverride)
add_attack_logs(user, occupant, "Gibbed in [src]", !!occupant.ckey ? ATKLOG_FEW : ATKLOG_ALL)
- if(!iscarbon(user))
- occupant.LAssailant = null
- else
- occupant.LAssailant = user
-
else //this looks ugly but it's better than a copy-pasted startgibbing proc override
occupant.create_attack_log("Was gibbed by an autogibber (\the [src])")
add_attack_logs(src, occupant, "gibbed")
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index adb43034bc2..a3b522187e2 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -184,3 +184,4 @@
D.consume(src)
else
return ..()
+
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 5bc63d4bace..1be8f11527d 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -978,7 +978,7 @@
/obj/machinery/hydroponics/proc/spawnplant() // why would you put strange reagent in a hydro tray you monster I bet you also feed them blood
var/list/livingplants = list(/mob/living/simple_animal/hostile/tree, /mob/living/simple_animal/hostile/killertomato)
var/chosen = pick(livingplants)
- var/mob/living/simple_animal/hostile/C = new chosen
+ var/mob/living/simple_animal/hostile/C = new chosen(get_turf(src))
C.faction = list("plants")
/obj/machinery/hydroponics/proc/become_self_sufficient() // Ambrosia Gaia effect
diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm
index 5200c4bb00b..f9aae71b2a5 100644
--- a/code/modules/library/computers/checkout.dm
+++ b/code/modules/library/computers/checkout.dm
@@ -207,7 +207,7 @@
var/obj/item/barcodescanner/scanner = W
scanner.computer = src
to_chat(user, "[scanner]'s associated machine has been set to [src].")
- audible_message("[src] lets out a low, short blip.", 2)
+ audible_message("[src] lets out a low, short blip.", hearing_distance = 2)
return 1
else
return ..()
@@ -413,7 +413,7 @@
return
if(bibledelay)
- audible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
+ visible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
else
bibledelay = 1
spawn(60)
diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm
index 7b0b782bdca..9398bd9683c 100644
--- a/code/modules/lighting/lighting_source.dm
+++ b/code/modules/lighting/lighting_source.dm
@@ -220,13 +220,14 @@
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(T in view(CEILING(light_range, 1), source_turf))
- if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources) || T.has_opaque_atom)
+ if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources))
continue
- if(!T.lighting_corners_initialised)
- T.generate_missing_corners()
- for(thing in T.corners)
- C = thing
- corners[C] = 0
+ if(!T.has_opaque_atom)
+ if(!T.lighting_corners_initialised)
+ T.generate_missing_corners()
+ for(thing in T.corners)
+ C = thing
+ corners[C] = 0
turfs += T
source_turf.luminosity = oldlum
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 5f18a27adc9..99e0af0ca6a 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -135,7 +135,7 @@
if(message)
for(var/mob/M in GLOB.player_list)
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
continue
if(check_rights(R_ADMIN|R_MOD, 0, M) && M.get_preference(CHAT_DEAD)) // Show the emote to admins/mods
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 92d936227cc..a46268daf50 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -767,17 +767,6 @@
desc = "Bark bark bark."
key = "vu"
-/datum/language/zombie
- name = "Zombie"
- desc = "BRAAAAAAINS!"
- speech_verb = "moans"
- whisper_verb = "mutters"
- exclaim_verb = "wails"
- colour = "zombie"
- key = "zom"
- flags = RESTRICTED
- syllables = list("BRAAAAAAAAAAAAAAAAINS", "BRAAINS", "BRAINS")
-
/mob/proc/grant_all_babel_languages()
for(var/la in GLOB.all_languages)
var/datum/language/new_language = GLOB.all_languages[la]
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index b12f62623e8..f6354b61940 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -2,6 +2,7 @@
name = "alien"
voice_name = "alien"
speak_emote = list("hisses")
+ bubble_icon = "alien"
icon = 'icons/mob/alien.dmi'
gender = NEUTER
dna = null
diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
index fec3012529e..58bdb3a9ecc 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
@@ -136,14 +136,11 @@ Doesn't work on other aliens/AI.*/
set category = "Alien"
if(powerc())
- if(stomach_contents.len)
+ if(LAZYLEN(stomach_contents))
for(var/mob/M in src)
- if(M in stomach_contents)
- stomach_contents.Remove(M)
- M.forceMove(loc)
- //Paralyse(10)
- src.visible_message("[src] hurls out the contents of [p_their()] stomach!")
- return
+ LAZYREMOVE(stomach_contents, M)
+ M.forceMove(drop_location())
+ visible_message("[src] hurls out the contents of [p_their()] stomach!")
/mob/living/carbon/proc/getPlasma()
var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/empress.dm b/code/modules/mob/living/carbon/alien/humanoid/empress.dm
index 63514686f3d..a28b3f33a53 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/empress.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/empress.dm
@@ -6,6 +6,7 @@
icon_state = "alienq_s"
status_flags = CANPARALYSE
mob_size = MOB_SIZE_LARGE
+ bubble_icon = "alienroyal"
large = 1
ventcrawler = 0
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 3aef7135679..98585262518 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -22,8 +22,7 @@
return ..()
/mob/living/carbon/handle_atom_del(atom/A)
- if(A in processing_patches)
- processing_patches -= A
+ LAZYREMOVE(processing_patches, A)
return ..()
/mob/living/carbon/blob_act(obj/structure/blob/B)
@@ -51,41 +50,40 @@
/mob/living/carbon/var/last_stomach_attack //defining this here because no one would look in carbon_defines for it
-/mob/living/carbon/relaymove(var/mob/user, direction)
- if(user in src.stomach_contents)
- if(last_stomach_attack + STOMACH_ATTACK_DELAY > world.time) return
+/mob/living/carbon/relaymove(mob/user, direction)
+ if(LAZYLEN(stomach_contents))
+ if(user in stomach_contents)
+ if(last_stomach_attack + STOMACH_ATTACK_DELAY > world.time)
+ return
- last_stomach_attack = world.time
- for(var/mob/M in hearers(4, src))
- if(M.client)
- M.show_message(text("You hear something rumbling inside [src]'s stomach..."), 2)
-
- var/obj/item/I = user.get_active_hand()
- if(I && I.force)
- var/d = rand(round(I.force / 4), I.force)
-
- if(istype(src, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = src
- var/obj/item/organ/external/organ = H.get_organ("chest")
- if(istype(organ))
- if(organ.receive_damage(d, 0))
- H.UpdateDamageIcon()
-
- H.updatehealth("stomach attack")
-
- else
- src.take_organ_damage(d)
-
- for(var/mob/M in viewers(user, null))
+ last_stomach_attack = world.time
+ for(var/mob/M in hearers(4, src))
if(M.client)
- M.show_message(text("[user] attacks [src]'s stomach wall with the [I.name]!"), 2)
- playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1)
+ M.show_message(text("You hear something rumbling inside [src]'s stomach..."), 2)
- if(prob(src.getBruteLoss() - 50))
- for(var/atom/movable/A in stomach_contents)
- A.forceMove(drop_location())
- stomach_contents.Remove(A)
- src.gib()
+ var/obj/item/I = user.get_active_hand()
+ if(I && I.force)
+ var/d = rand(round(I.force / 4), I.force)
+
+ if(istype(src, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/external/organ = H.get_organ("chest")
+ if(istype(organ))
+ if(organ.receive_damage(d, 0))
+ H.UpdateDamageIcon()
+
+ H.updatehealth("stomach attack")
+
+ else
+ take_organ_damage(d)
+
+ for(var/mob/M in viewers(user, null))
+ if(M.client)
+ M.show_message(text("[user] attacks [src]'s stomach wall with the [I.name]!"), 2)
+ playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1)
+
+ if(prob(getBruteLoss() - 50))
+ gib()
#undef STOMACH_ATTACK_DELAY
@@ -140,9 +138,8 @@
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
for(var/mob/M in src)
- if(M in src.stomach_contents)
- src.stomach_contents.Remove(M)
- M.forceMove(get_turf(src))
+ LAZYREMOVE(stomach_contents, M)
+ M.forceMove(drop_location())
visible_message("[M] bursts out of [src]!")
/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
@@ -1069,10 +1066,6 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
/mob/living/carbon/proc/forceFedAttackLog(var/obj/item/reagent_containers/food/toEat, mob/user)
add_attack_logs(user, src, "Fed [toEat]. Reagents: [toEat.reagents.log_list(toEat)]", toEat.reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
- if(!iscarbon(user))
- LAssailant = null
- else
- LAssailant = user
/*TO DO - If/when stomach organs are introduced, override this at the human level sending the item to the stomach
@@ -1205,3 +1198,18 @@ so that different stomachs can handle things in different ways VB*/
I.acid_level = 0 //washes off the acid on our clothes
I.extinguish() //extinguishes our clothes
..()
+
+/mob/living/carbon/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE)
+ if(head)
+ if(head.clean_blood())
+ update_inv_head()
+ if(head.flags_inv & HIDEMASK)
+ clean_mask = FALSE
+ if(wear_suit)
+ if(wear_suit.clean_blood())
+ update_inv_wear_suit()
+ if(wear_suit.flags_inv & HIDESHOES)
+ clean_feet = FALSE
+ if(wear_suit.flags_inv & HIDEGLOVES)
+ clean_hands = FALSE
+ ..(clean_hands, clean_mask, clean_feet)
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 97ecf4dd08b..667fd86204e 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -1,11 +1,10 @@
/mob/living/carbon
gender = MALE
pressure_resistance = 15
- var/list/stomach_contents = list()
- var/list/processing_patches = list()
+ var/list/stomach_contents
+ var/list/processing_patches
var/list/internal_organs = list()
var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
- var/antibodies = 0
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 6b7a0a2b15d..d213ee7206f 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -6,7 +6,7 @@
canmove = 0
icon = null
invisibility = 101
- if(!isSynthetic())
+ if(!ismachineperson(src))
animation = new(loc)
animation.icon_state = "blank"
animation.icon = 'icons/mob/mob.dmi'
@@ -32,12 +32,11 @@
E.droplimb(DROPLIMB_SHARP)
for(var/mob/M in src)
- if(M in stomach_contents)
- stomach_contents.Remove(M)
- M.forceMove(get_turf(src))
+ LAZYREMOVE(stomach_contents, M)
+ M.forceMove(drop_location())
visible_message("[M] bursts out of [src]!")
- if(!isSynthetic())
+ if(!ismachineperson(src))
flick("gibbed-h", animation)
hgibs(loc, dna)
else
@@ -106,11 +105,6 @@
//Handle species-specific deaths.
dna.species.handle_death(gibbed, src)
- if(ishuman(LAssailant))
- var/mob/living/carbon/human/H=LAssailant
- if(H.mind)
- H.mind.kills += "[key_name(src)]"
-
if(SSticker && SSticker.mode)
// log_world("k")
sql_report_death(src)
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index fed50d442c9..10c2ac8f119 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -34,7 +34,7 @@
//Cooldown-inducing emotes
if("ping", "pings", "buzz", "buzzes", "beep", "beeps", "yes", "no", "buzz2")
var/found_machine_head = FALSE
- if(ismachine(src)) //Only Machines can beep, ping, and buzz, yes, no, and make a silly sad trombone noise.
+ if(ismachineperson(src)) //Only Machines can beep, ping, and buzz, yes, no, and make a silly sad trombone noise.
on_CD = handle_emote_CD() //proc located in code\modules\mob\emote.dm
found_machine_head = TRUE
else
@@ -947,7 +947,7 @@
if("Skrell")
emotelist += "\nSkrell specific emotes :- warble(s)"
- if(ismachine(src))
+ if(ismachineperson(src))
emotelist += "\nMachine specific emotes :- beep(s)-(none)/mob, buzz(es)-none/mob, no-(none)/mob, ping(s)-(none)/mob, yes-(none)/mob, buzz2-(none)/mob"
else
var/obj/item/organ/external/head/H = get_organ("head") // If you have a robotic head, you can make beep-boop noises
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index b67e01924c8..31825ea3765 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -208,7 +208,7 @@
if(!E)
wound_flavor_text["[organ_tag]"] = "[p_they(TRUE)] [p_are()] missing [p_their()] [organ_descriptor].\n"
else
- if(!isSynthetic())
+ if(!ismachineperson(src))
if(E.is_robotic())
wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a robotic [E.name]!\n"
@@ -252,7 +252,7 @@
var/temp = getBruteLoss() //no need to calculate each of these twice
if(temp)
- var/brute_message = !isSynthetic() ? "bruising" : "denting"
+ var/brute_message = !ismachineperson(src) ? "bruising" : "denting"
if(temp < 30)
msg += "[p_they(TRUE)] [p_have()] minor [brute_message ].\n"
else
@@ -301,13 +301,13 @@
else if(nutrition >= NUTRITION_LEVEL_FAT)
msg += "[p_they(TRUE)] [p_are()] quite chubby.\n"
- if(!isSynthetic() && blood_volume < BLOOD_VOLUME_SAFE)
+ if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE)
msg += "[p_they(TRUE)] [p_have()] pale skin.\n"
if(bleedsuppress)
msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n"
else if(bleed_rate)
- var/bleed_message = !isSynthetic() ? "bleeding" : "leaking"
+ var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking"
msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n"
if(reagents.has_reagent("teslium"))
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 9df498328fa..16f1738d2f6 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -18,7 +18,7 @@
dna = new /datum/dna(null)
// Species name is handled by set_species()
- ..()
+ . = ..()
set_species(new_species, 1, delay_icon_update = 1, skip_same_check = TRUE)
@@ -68,55 +68,55 @@
status_flags = GODMODE|CANPUSH
/mob/living/carbon/human/skrell/Initialize(mapload)
- ..(mapload, /datum/species/skrell)
+ . = ..(mapload, /datum/species/skrell)
/mob/living/carbon/human/tajaran/Initialize(mapload)
- ..(mapload, /datum/species/tajaran)
+ . = ..(mapload, /datum/species/tajaran)
/mob/living/carbon/human/vulpkanin/Initialize(mapload)
- ..(mapload, /datum/species/vulpkanin)
+ . = ..(mapload, /datum/species/vulpkanin)
/mob/living/carbon/human/unathi/Initialize(mapload)
- ..(mapload, /datum/species/unathi)
+ . = ..(mapload, /datum/species/unathi)
/mob/living/carbon/human/vox/Initialize(mapload)
- ..(mapload, /datum/species/vox)
+ . = ..(mapload, /datum/species/vox)
/mob/living/carbon/human/voxarmalis/Initialize(mapload)
- ..(mapload, /datum/species/vox/armalis)
+ . = ..(mapload, /datum/species/vox/armalis)
/mob/living/carbon/human/skeleton/Initialize(mapload)
- ..(mapload, /datum/species/skeleton)
+ . = ..(mapload, /datum/species/skeleton)
/mob/living/carbon/human/kidan/Initialize(mapload)
- ..(mapload, /datum/species/kidan)
+ . = ..(mapload, /datum/species/kidan)
/mob/living/carbon/human/plasma/Initialize(mapload)
- ..(mapload, /datum/species/plasmaman)
+ . = ..(mapload, /datum/species/plasmaman)
/mob/living/carbon/human/slime/Initialize(mapload)
- ..(mapload, /datum/species/slime)
+ . = ..(mapload, /datum/species/slime)
/mob/living/carbon/human/grey/Initialize(mapload)
- ..(mapload, /datum/species/grey)
+ . = ..(mapload, /datum/species/grey)
/mob/living/carbon/human/abductor/Initialize(mapload)
- ..(mapload, /datum/species/abductor)
+ . = ..(mapload, /datum/species/abductor)
/mob/living/carbon/human/diona/Initialize(mapload)
- ..(mapload, /datum/species/diona)
+ . = ..(mapload, /datum/species/diona)
/mob/living/carbon/human/pod_diona/Initialize(mapload)
- ..(mapload, /datum/species/diona/pod)
+ . = ..(mapload, /datum/species/diona/pod)
/mob/living/carbon/human/machine/Initialize(mapload)
- ..(mapload, /datum/species/machine)
+ . = ..(mapload, /datum/species/machine)
/mob/living/carbon/human/machine/created
name = "Integrated Robotic Chassis"
/mob/living/carbon/human/machine/created/Initialize(mapload)
- ..()
+ . = ..()
rename_character(null, "Integrated Robotic Chassis ([rand(1, 9999)])")
update_dna()
for(var/obj/item/organ/external/E in bodyparts)
@@ -129,34 +129,34 @@
death()
/mob/living/carbon/human/shadow/Initialize(mapload)
- ..(mapload, /datum/species/shadow)
+ . = ..(mapload, /datum/species/shadow)
/mob/living/carbon/human/golem/Initialize(mapload)
- ..(mapload, /datum/species/golem)
+ . = ..(mapload, /datum/species/golem)
/mob/living/carbon/human/wryn/Initialize(mapload)
- ..(mapload, /datum/species/wryn)
+ . = ..(mapload, /datum/species/wryn)
/mob/living/carbon/human/nucleation/Initialize(mapload)
- ..(mapload, /datum/species/nucleation)
+ . = ..(mapload, /datum/species/nucleation)
/mob/living/carbon/human/drask/Initialize(mapload)
- ..(mapload, /datum/species/drask)
+ . = ..(mapload, /datum/species/drask)
/mob/living/carbon/human/monkey/Initialize(mapload)
- ..(mapload, /datum/species/monkey)
+ . = ..(mapload, /datum/species/monkey)
/mob/living/carbon/human/farwa/Initialize(mapload)
- ..(mapload, /datum/species/monkey/tajaran)
+ . = ..(mapload, /datum/species/monkey/tajaran)
/mob/living/carbon/human/wolpin/Initialize(mapload)
- ..(mapload, /datum/species/monkey/vulpkanin)
+ . = ..(mapload, /datum/species/monkey/vulpkanin)
/mob/living/carbon/human/neara/Initialize(mapload)
- ..(mapload, /datum/species/monkey/skrell)
+ . = ..(mapload, /datum/species/monkey/skrell)
/mob/living/carbon/human/stok/Initialize(mapload)
- ..(mapload, /datum/species/monkey/unathi)
+ . = ..(mapload, /datum/species/monkey/unathi)
/mob/living/carbon/human/Stat()
..()
@@ -1162,18 +1162,6 @@
custom_pain("You feel a stabbing pain in your chest!")
L.damage = L.min_bruised_damage
-//returns 1 if made bloody, returns 0 otherwise
-
-/mob/living/carbon/human/clean_blood(var/clean_feet)
- .=..()
- if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len)
- feet_blood_color = null
- qdel(feet_blood_DNA)
- bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0)
- blood_state = BLOOD_STATE_NOT_BLOODY
- update_inv_shoes()
- return 1
-
/mob/living/carbon/human/cuff_resist(obj/item/I)
if(HULK in mutations)
say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
@@ -1701,14 +1689,14 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
/mob/living/carbon/human/forceFed(var/obj/item/reagent_containers/food/toEat, mob/user, fullness)
if(!check_has_mouth())
- if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachine(src)))))
+ if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachineperson(src)))))
to_chat(user, "Where do you intend to put \the [toEat]? \The [src] doesn't have a mouth!")
return 0
return ..()
/mob/living/carbon/human/selfDrink(var/obj/item/reagent_containers/food/drinks/toDrink)
if(!check_has_mouth())
- if(!ismachine(src))
+ if(!ismachineperson(src))
to_chat(src, "Where do you intend to put \the [src]? You don't have a mouth!")
return 0
else
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index a849235af64..21a3e8bf274 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -84,7 +84,7 @@ emp_act
E.heal_damage(rembrute,0,0,1)
rembrute = nrembrute
user.visible_message("[user] patches some dents on [src]'s [E.name] with [I].")
- if(bleed_rate && isSynthetic())
+ if(bleed_rate && ismachineperson(src))
bleed_rate = 0
user.visible_message("[user] patches some leaks on [src] with [I].")
if(IgniteMob())
@@ -387,11 +387,6 @@ emp_act
to_chat(user, "You hack off a chunk of meat from [name]")
if(!meatleft)
add_attack_logs(user, src, "Chopped up into meat")
- if(!iscarbon(user))
- LAssailant = null
- else
- LAssailant = user
-
qdel(src)
var/obj/item/organ/external/affecting = get_organ(ran_zone(user.zone_selected))
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 60b6da987dd..3d365afa940 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -37,7 +37,6 @@ GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new())
var/obj/item/s_store = null
var/icon/stand_icon = null
- var/icon/lying_icon = null
var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life()
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 5030c5c4170..90653a6750b 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -635,7 +635,7 @@
else
overeatduration -= 2
- if(!ismachine(src) && nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) //Gosh damn snowflakey IPCs
+ if(!ismachineperson(src) && nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) //Gosh damn snowflakey IPCs
var/datum/disease/D = new /datum/disease/critical/hypoglycemia
ForceContractDisease(D)
@@ -697,7 +697,7 @@
alcohol_strength /= sober_str
var/obj/item/organ/internal/liver/L
- if(!isSynthetic())
+ if(!ismachineperson(src))
L = get_int_organ(/obj/item/organ/internal/liver)
if(L)
alcohol_strength *= L.alcohol_intensity
@@ -719,7 +719,7 @@
AdjustConfused(3 / sober_str)
if(alcohol_strength >= blur_start) //blurry eyes
EyeBlurry(10 / sober_str)
- if(!isSynthetic()) //stuff only for non-synthetics
+ if(!ismachineperson(src)) //stuff only for non-synthetics
if(alcohol_strength >= vomit_start) //vomiting
if(prob(8))
fakevomit()
diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm
index 41c108dadb9..9dcf83f2e9a 100644
--- a/code/modules/mob/living/carbon/human/npcs.dm
+++ b/code/modules/mob/living/carbon/human/npcs.dm
@@ -7,7 +7,7 @@
species_exception = list(/datum/species/monkey)
/mob/living/carbon/human/monkey/punpun/Initialize(mapload)
- ..()
+ . = ..()
name = "Pun Pun"
real_name = name
equip_to_slot(new /obj/item/clothing/under/punpun(src), slot_w_uniform)
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 21695e25e78..f65cefff49d 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -53,7 +53,6 @@
var/stun_mod = 1 // If a species is more/less impacated by stuns/weakens/paralysis
var/speed_mod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
var/blood_damage_type = OXY //What type of damage does this species take if it's low on blood?
- var/obj/item/mutanthands
var/total_health = 100
var/punchdamagelow = 0 //lowest possible punch damage
var/punchdamagehigh = 9 //highest possible punch damage
@@ -352,21 +351,6 @@
if(organ.receive_damage(damage, 0, sharp, used_weapon))
H.UpdateDamageIcon()
- if(H.LAssailant && ishuman(H.LAssailant)) //superheros still get the comical hit markers
- var/mob/living/carbon/human/A = H.LAssailant
- if(A.mind && (A.mind in (SSticker.mode.superheroes) || SSticker.mode.supervillains || SSticker.mode.greyshirts))
- var/list/attack_bubble_recipients = list()
- var/mob/living/user
- for(var/mob/O in viewers(user, src))
- if(O.client && O.has_vision(information_only=TRUE))
- attack_bubble_recipients.Add(O.client)
- spawn(0)
- var/image/dmgIcon = image('icons/effects/hit_blips.dmi', src, "dmg[rand(1,2)]",MOB_LAYER+1)
- dmgIcon.pixel_x = (!H.lying) ? rand(-3,3) : rand(-11,12)
- dmgIcon.pixel_y = (!H.lying) ? rand(-11,9) : rand(-10,1)
- flick_overlay(dmgIcon, attack_bubble_recipients, 9)
-
-
if(BURN)
H.damageoverlaytemp = 20
damage = damage * burn_mod
@@ -443,6 +427,9 @@
else
target.LAssailant = user
+ target.lastattacker = user.real_name
+ target.lastattackerckey = user.ckey
+
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
damage += attack.damage
if(!damage)
diff --git a/code/modules/mob/living/carbon/human/species/zombies.dm b/code/modules/mob/living/carbon/human/species/zombies.dm
deleted file mode 100644
index 1f1c4d979b7..00000000000
--- a/code/modules/mob/living/carbon/human/species/zombies.dm
+++ /dev/null
@@ -1,102 +0,0 @@
-#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin
-
-/datum/species/zombie
- // 1spooky
- name = "High-Functioning Zombie"
- name_plural = "High-Functioning Zombies"
- icobase = 'icons/mob/human_races/r_zombie.dmi'
- deform = 'icons/mob/human_races/r_def_zombie.dmi'
- dies_at_threshold = TRUE
- language = "Zombie"
- species_traits = list(NO_BLOOD, NOZOMBIE, NOTRANSSTING, NO_BREATHE, RADIMMUNE, NO_SCAN)
- var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
- warning_low_pressure = -1
- hazard_low_pressure = -1
- hazard_high_pressure = 999999999
- warning_high_pressure = 999999999
- cold_level_1 = -1
- cold_level_2 = -1
- cold_level_3 = -1
- tox_mod = 0
- flesh_color = "#00FF00" // for green examine text
- bodyflags = HAS_SKIN_COLOR
- dietflags = DIET_CARN
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes,
- "ears" = /obj/item/organ/internal/ears)
-
-
-/datum/species/zombie/infectious
- name = "Infectious Zombie"
- mutanthands = /obj/item/zombie_hand
- icobase = 'icons/mob/human_races/r_zombie.dmi'
- deform = 'icons/mob/human_races/r_def_zombie.dmi'
- brute_mod = 0.8 // 120 damage to KO a zombie, which kills it
- burn_mod = 0.8
- clone_mod = 0.8
- brain_mod = 0.8
- stamina_mod = 0.8
- speed_mod = 1.6
- default_language = "Zombie"
- var/heal_rate = 1
- var/regen_cooldown = 0
-
-/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H, amount)
- . = min(20, amount)
-
-/datum/species/zombie/infectious/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null)
- . = ..()
- if(damage)
- regen_cooldown = world.time + REGENERATION_DELAY
-
-/datum/species/zombie/infectious/handle_life(mob/living/carbon/human/H)
- . = ..()
- H.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW
-
- //Zombies never actually die, they just fall down until they regenerate enough to rise back up.
- //They must be restrained, beheaded or gibbed to stop being a threat.
- if(regen_cooldown < world.time)
- var/heal_amt = heal_rate
- if(H.InCritical())
- heal_amt *= 2
- H.heal_overall_damage(heal_amt,heal_amt)
- H.adjustToxLoss(-heal_amt)
- if(!H.InCritical() && prob(4))
- playsound(H, pick(spooks), 50, TRUE, 10)
-
-//Congrats you somehow died so hard you stopped being a zombie
-/datum/species/zombie/infectious/handle_death(gibbed, mob/living/carbon/C)
- . = ..()
- var/obj/item/organ/internal/zombie_infection/infection
- infection = C.get_organ_slot("zombie_infection")
- if(infection)
- qdel(infection)
-
-/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
- . = ..()
- // Deal with the source of this zombie corruption
- // Infection organ needs to be handled separately from mutant_organs
- // because it persists through species transitions
- if(mutanthands)
- H.drop_l_hand()
- H.drop_r_hand()
- H.put_in_hands(new mutanthands())
- H.put_in_hands(new mutanthands())
- var/obj/item/organ/internal/zombie_infection/infection
- infection = H.get_organ_slot("zombie_infection")
- if(!infection)
- infection = new()
- infection.insert(H)
-
-/datum/species/zombie/infectious/on_species_loss(mob/living/carbon/human/C, datum/species/old_species)
- QDEL_NULL(C.r_hand)
- QDEL_NULL(C.l_hand)
- return ..()
-
-#undef REGENERATION_DELAY
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 2cce48d51c2..81907ba516f 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -376,7 +376,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
//base icons
var/icon/hair_standing = new /icon('icons/mob/human_face.dmi',"bald_s")
- if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(isSynthetic())))
+ if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(ismachineperson(src))))
var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style]
if(hair_style && hair_style.species_allowed)
if((head_organ.dna.species.name in hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics...
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index a2df4b30e9e..c11eaca5293 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -11,6 +11,10 @@
if(stat != DEAD)
handle_organs()
+ //stuff in the stomach
+ if(LAZYLEN(stomach_contents))
+ handle_stomach(times_fired)
+
. = ..()
if(QDELETED(src))
@@ -254,14 +258,15 @@
if(times_fired % 20==2) //dry off a bit once every 20 ticks or so
wetlevel = max(wetlevel - 1,0)
-/mob/living/carbon/handle_stomach(times_fired)
- for(var/mob/living/M in stomach_contents)
+/mob/living/carbon/proc/handle_stomach(times_fired)
+ for(var/thing in stomach_contents)
+ var/mob/living/M = thing
if(M.loc != src)
- stomach_contents.Remove(M)
+ LAZYREMOVE(stomach_contents, M)
continue
if(stat != DEAD)
if(M.stat == DEAD)
- stomach_contents.Remove(M)
+ LAZYREMOVE(stomach_contents, M)
qdel(M)
continue
if(times_fired % 3 == 1)
@@ -476,7 +481,7 @@
P.reagents.remove_any(applied_amount * 0.5)
else
if(!P.reagents || P.reagents.total_volume <= 0)
- processing_patches -= P
+ LAZYREMOVE(processing_patches, P)
qdel(P)
/mob/living/carbon/proc/handle_germs()
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 6d020ccdad1..3062ef461f9 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -58,9 +58,6 @@
handle_fire()
- //stuff in the stomach
- handle_stomach(times_fired)
-
update_gravity(mob_has_gravity())
if(pulling)
@@ -117,9 +114,6 @@
/mob/living/proc/handle_environment(datum/gas_mixture/environment)
return
-/mob/living/proc/handle_stomach(times_fired)
- return
-
/mob/living/proc/update_pulling()
if(incapacitated())
stop_pulling()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9f768ffc76d..f3d23ca3f51 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -682,9 +682,6 @@
*/////////////////////
/mob/living/proc/resist_grab()
var/resisting = 0
- for(var/obj/O in requests)
- qdel(O)
- resisting++
for(var/X in grabbed_by)
var/obj/item/grab/G = X
resisting++
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index fb633f116bd..630b2223321 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -61,7 +61,7 @@ proc/get_radio_key_from_channel(var/channel)
return default_language
/mob/living/proc/handle_speech_problems(list/message_pieces, var/verb)
- var/robot = isSynthetic()
+ var/robot = ismachineperson(src)
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking && S.speaking.flags & NO_STUTTER)
continue
@@ -269,12 +269,12 @@ proc/get_radio_key_from_channel(var/channel)
M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency)
if(M.client)
speech_bubble_recipients.Add(M.client)
- spawn(0)
- if(loc && !isturf(loc))
- var/atom/A = loc //Non-turf, let it handle the speech bubble
- A.speech_bubble("hR[speech_bubble_test]", A, speech_bubble_recipients)
- else //Turf, leave speech bubbles to the mob
- speech_bubble("h[speech_bubble_test]", src, speech_bubble_recipients)
+
+ if(loc && !isturf(loc))
+ var/atom/A = loc //Non-turf, let it handle the speech bubble
+ A.speech_bubble("[A.bubble_icon][speech_bubble_test]", A, speech_bubble_recipients)
+ else //Turf, leave speech bubbles to the mob
+ speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients)
for(var/obj/O in listening_obj)
spawn(0)
@@ -462,10 +462,7 @@ proc/get_radio_key_from_channel(var/channel)
if(M.client)
speech_bubble_recipients.Add(M.client)
- spawn(0)
- var/image/I = image('icons/mob/talk.dmi', src, "h[speech_bubble_test]", MOB_LAYER + 1)
- I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
- flick_overlay(I, speech_bubble_recipients, 30)
+ speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients)
if(watching.len)
var/rendered = "[name] [not_heard]."
@@ -474,7 +471,7 @@ proc/get_radio_key_from_channel(var/channel)
return 1
-/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
- var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, MOB_LAYER + 1)
+/mob/living/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
+ var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
- flick_overlay(I, bubble_recipients, 30)
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 85c4ceac427..1c232f9f43c 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -243,7 +243,8 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/proc/show_borg_info()
stat(null, text("Connected cyborgs: [connected_robots.len]"))
- for(var/mob/living/silicon/robot/R in connected_robots)
+ for(var/thing in connected_robots)
+ var/mob/living/silicon/robot/R = thing
var/robot_status = "Nominal"
if(R.stat || !R.client)
robot_status = "OFFLINE"
@@ -251,8 +252,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
robot_status = "DEPOWERED"
// Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
var/area/A = get_area(R)
+ var/area_name = A ? sanitize(A.name) : "Unknown"
stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \
- Module: [R.designation] | Loc: [sanitize(A.name)] | Status: [robot_status]"))
+ Module: [R.designation] | Loc: [area_name] | Status: [robot_status]"))
/mob/living/silicon/ai/rename_character(oldname, newname)
if(!..(oldname, newname))
diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm
index 26b5683d1a3..99d135df93b 100644
--- a/code/modules/mob/living/silicon/robot/component.dm
+++ b/code/modules/mob/living/silicon/robot/component.dm
@@ -253,7 +253,7 @@ proc/robot_healthscan(mob/user, mob/living/M)
to_chat(user, "You can't analyze non-robotic things!")
return
-
+
switch(scan_type)
if("robot")
var/BU = M.getFireLoss() > 50 ? "[M.getFireLoss()]" : M.getFireLoss()
@@ -269,7 +269,7 @@ proc/robot_healthscan(mob/user, mob/living/M)
to_chat(user, "Localized Damage:")
if(!LAZYLEN(damaged) && !LAZYLEN(missing))
to_chat(user, "\t Components are OK.")
- else
+ else
if(LAZYLEN(damaged))
for(var/datum/robot_component/org in damaged)
user.show_message(text("\t []: [][] - [] - [] - []", \
@@ -282,7 +282,7 @@ proc/robot_healthscan(mob/user, mob/living/M)
if(LAZYLEN(missing))
for(var/datum/robot_component/org in missing)
user.show_message("\t [capitalize(org.name)]: MISSING")
-
+
if(H.emagged && prob(5))
to_chat(user, "\t ERROR: INTERNAL SYSTEMS COMPROMISED")
@@ -313,7 +313,7 @@ proc/robot_healthscan(mob/user, mob/living/M)
if(!organ_found)
to_chat(user, "No prosthetics located.")
- if(H.isSynthetic())
+ if(ismachineperson(H))
to_chat(user, "Internal Fluid Level:[H.blood_volume]/[H.max_blood]")
if(H.bleed_rate)
to_chat(user, "Warning:External component leak detected!")
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 39bedc78c77..f932f4270ba 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -6,6 +6,7 @@
icon_state = "repairbot"
maxHealth = 35
health = 35
+ bubble_icon = "machine"
universal_speak = 0
universal_understand = 1
gender = NEUTER
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 2eb1e4669ab..d7f42a9ceb5 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
icon_state = "robot"
maxHealth = 100
health = 100
+ bubble_icon = "robot"
universal_understand = 1
deathgasp_on_death = TRUE
@@ -16,8 +17,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/custom_name = ""
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
-//Hud stuff
-
+ //Hud stuff
var/obj/screen/inv1 = null
var/obj/screen/inv2 = null
var/obj/screen/inv3 = null
@@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not
var/obj/screen/robot_modules_background
-//3 Modules can be activated at any one time.
+ //3 Modules can be activated at any one time.
var/obj/item/robot_module/module = null
var/module_active = null
var/module_state_1 = null
@@ -57,6 +57,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/is_emaggable = TRUE
var/eye_protection = 0
var/ear_protection = 0
+ var/damage_protection = 0
+ var/emp_protection = FALSE
+ var/xeno_disarm_chance = 85
var/list/force_modules = list()
var/allow_rename = TRUE
@@ -86,7 +89,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/tracking_entities = 0 //The number of known entities currently accessing the internal camera
var/braintype = "Cyborg"
var/base_icon = ""
- var/crisis = 0
var/modules_break = TRUE
var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting.
@@ -97,6 +99,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD)
+ var/default_cell_type = /obj/item/stock_parts/cell/high
var/magpulse = 0
var/ionpulse = 0 // Jetpack-like effect.
var/ionpulse_on = 0 // Jetpack-like effect.
@@ -132,7 +135,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
init()
- if(!scrambledcodes && !camera)
+ if(!camera && (!scrambledcodes || designation == "ERT"))
camera = new /obj/machinery/camera(src)
camera.c_tag = real_name
camera.network = list("SS13","Robots")
@@ -144,7 +147,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
mmi.icon_state = "boris"
if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly
- cell = new /obj/item/stock_parts/cell/high(src)
+ cell = new default_cell_type(src)
initialize_components()
//if(!unfinished)
@@ -288,12 +291,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/proc/pick_module()
if(module)
return
- var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security")
+ var/list/modules = list("Generalist", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security")
if(islist(force_modules) && force_modules.len)
modules = force_modules.Copy()
- if(GLOB.security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis)
- to_chat(src, "Crisis mode active. The combat module is now available.")
- modules += "Combat"
if(mmi != null && mmi.alien)
modules = list("Hunter")
modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules
@@ -306,9 +306,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
return
switch(modtype)
- if("Standard")
+ if("Generalist")
module = new /obj/item/robot_module/standard(src)
- module.channels = list("Service" = 1)
+ module.channels = list("Engineering" = 1, "Medical" = 1, "Security" = 1, "Service" = 1)
module_sprites["Basic"] = "robot_old"
module_sprites["Android"] = "droid"
module_sprites["Default"] = "Standard"
@@ -337,6 +337,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
module_sprites["Standard"] = "Standard-Mine"
module_sprites["Noble-DIG"] = "Noble-DIG"
module_sprites["Cricket"] = "Cricket-MINE"
+ module_sprites["Lavaland"] = "lavaland"
if("Medical")
module = new /obj/item/robot_module/medical(src)
@@ -353,6 +354,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
status_flags &= ~CANPUSH
if("Security")
+ if(!weapons_unlock)
+ var/count_secborgs = 0
+ for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list)
+ if(R && R.stat != DEAD && R.module && istype(R.module, /obj/item/robot_module/security))
+ count_secborgs++
+ var/max_secborgs = 2
+ if(GLOB.security_level == SEC_LEVEL_GREEN)
+ max_secborgs = 1
+ if(count_secborgs >= max_secborgs)
+ to_chat(src, "There are too many Security cyborgs active. Please choose another module.")
+ return
module = new /obj/item/robot_module/security(src)
module.channels = list("Security" = 1)
module_sprites["Basic"] = "secborg"
@@ -387,10 +399,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
module_sprites["Noble-CLN"] = "Noble-CLN"
module_sprites["Cricket"] = "Cricket-JANI"
- if("Combat")
- module = new /obj/item/robot_module/combat(src)
+ if("Destroyer") // Rolling Borg
+ module = new /obj/item/robot_module/destroyer(src)
module.channels = list("Security" = 1)
icon_state = "droidcombat"
+ status_flags &= ~CANPUSH
+
+ if("Combat") // Gamma ERT
+ module = new /obj/item/robot_module/combat(src)
+ icon_state = "ertgamma"
+ status_flags &= ~CANPUSH
if("Hunter")
module = new /obj/item/robot_module/alien/hunter(src)
@@ -442,6 +460,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
ionpulse = FALSE
magpulse = FALSE
add_language("Robot Talk", 1)
+ if("lava" in weather_immunities) // Remove the lava-immunity effect given by a printable upgrade
+ weather_immunities -= "lava"
status_flags |= CANPUSH
@@ -916,7 +936,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
return 0
/mob/living/silicon/robot/update_icons()
-
overlays.Cut()
if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned.
if(custom_panel in custom_eye_names)
@@ -925,36 +944,24 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
overlays += "eyes-[icon_state]"
else
overlays -= "eyes"
-
if(opened)
var/panelprefix = "ov"
if(custom_sprite) //Custom borgs also have custom panels, heh
panelprefix = "[ckey]"
-
if(custom_panel in custom_panel_names) //For default borgs with different panels
panelprefix = custom_panel
-
if(wiresexposed)
overlays += "[panelprefix]-openpanel +w"
else if(cell)
overlays += "[panelprefix]-openpanel +c"
else
overlays += "[panelprefix]-openpanel -c"
-
- var/combat = list("Combat")
- if(modtype in combat)
- if(base_icon == "")
- base_icon = icon_state
- if(module_active && istype(module_active,/obj/item/borg/combat/mobility))
- icon_state = "[base_icon]-roll"
- else
- icon_state = base_icon
- if(module)
- for(var/obj/item/borg/combat/shield/S in module.modules)
- if(activated(S))
- overlays += "[base_icon]-shield"
+ borg_icons()
update_fire()
+/mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it
+ return
+
/mob/living/silicon/robot/proc/installed_modules()
if(weapon_lock)
to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]")
@@ -1309,6 +1316,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
..()
update_module_icon()
+/mob/living/silicon/robot/emp_act(severity)
+ if(emp_protection)
+ return
+ ..()
+ switch(severity)
+ if(1)
+ disable_component("comms", 160)
+ if(2)
+ disable_component("comms", 60)
+
/mob/living/silicon/robot/deathsquad
base_icon = "nano_bloodhound"
icon_state = "nano_bloodhound"
@@ -1321,44 +1338,29 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
pdahide = 1
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
ear_protection = 1 // Immunity to the audio part of flashbangs
+ damage_protection = 10 // Reduce all incoming damage by this number
+ xeno_disarm_chance = 20
allow_rename = FALSE
modtype = "Commando"
faction = list("nanotrasen")
is_emaggable = FALSE
-
-/mob/living/silicon/robot/deathsquad/New(loc)
- ..()
- cell = new /obj/item/stock_parts/cell/hyper(src)
+ default_cell_type = /obj/item/stock_parts/cell/bluespace
/mob/living/silicon/robot/deathsquad/init()
laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/deathsquad(src)
-
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
radio = new /obj/item/radio/borg/deathsquad(src)
radio.recalculateChannels()
-
playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
-/mob/living/silicon/robot/combat
- base_icon = "droidcombat"
- icon_state = "droidcombat"
- modtype = "Combat"
- designation = "Combat"
+/mob/living/silicon/robot/deathsquad/bullet_act(var/obj/item/projectile/P)
+ if(istype(P) && P.is_reflectable && P.starting)
+ visible_message("The [P.name] gets reflected by [src]!", "The [P.name] gets reflected by [src]!")
+ P.reflect_back(src)
+ return -1
+ return ..(P)
-/mob/living/silicon/robot/combat/init()
- ..()
- module = new /obj/item/robot_module/combat(src)
- module.channels = list("Security" = 1)
- //languages
- module.add_languages(src)
- //subsystems
- module.add_subsystems_and_actions(src)
-
- status_flags &= ~CANPUSH
-
- radio.config(module.channels)
- notify_ai(2)
/mob/living/silicon/robot/ert
designation = "ERT"
@@ -1366,11 +1368,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
scrambledcodes = 1
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
-
force_modules = list("Engineering", "Medical", "Security")
static_radio_channels = 1
allow_rename = FALSE
weapons_unlock = TRUE
+ default_cell_type = /obj/item/stock_parts/cell/super
+ var/eprefix = "Amber"
/mob/living/silicon/robot/ert/init()
@@ -1379,11 +1382,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
radio.recalculateChannels()
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
-/mob/living/silicon/robot/ert/New(loc, cyborg_unlock)
+/mob/living/silicon/robot/ert/New(loc)
..(loc)
- cell = new /obj/item/stock_parts/cell/hyper(src)
var/rnum = rand(1,1000)
- var/borgname = "ERT [rnum]"
+ var/borgname = "[eprefix] ERT [rnum]"
name = borgname
custom_name = borgname
real_name = name
@@ -1392,22 +1394,63 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
mind.original = src
mind.assigned_role = SPECIAL_ROLE_ERT
mind.special_role = SPECIAL_ROLE_ERT
- if(cyborg_unlock)
- crisis = 1
if(!(mind in SSticker.minds))
SSticker.minds += mind
SSticker.mode.ert += mind
-/mob/living/silicon/robot/ert/gamma
- crisis = 1
-/mob/living/silicon/robot/emp_act(severity)
+/mob/living/silicon/robot/ert/red
+ eprefix = "Red"
+ default_cell_type = /obj/item/stock_parts/cell/hyper
+
+/mob/living/silicon/robot/ert/gamma
+ default_cell_type = /obj/item/stock_parts/cell/bluespace
+ force_modules = list("Combat", "Engineering", "Medical")
+ damage_protection = 5 // Reduce all incoming damage by this number
+ eprefix = "Gamma"
+ magpulse = 1
+ xeno_disarm_chance = 40
+
+
+/mob/living/silicon/robot/destroyer
+ // admin-only borg, the seraph / special ops officer of borgs
+ base_icon = "droidcombat"
+ icon_state = "droidcombat"
+ modtype = "Destroyer"
+ designation = "Destroyer"
+ lawupdate = 0
+ scrambledcodes = 1
+ req_one_access = list(ACCESS_CENT_SPECOPS)
+ ionpulse = 1
+ magpulse = 1
+ pdahide = 1
+ eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
+ ear_protection = 1 // Immunity to the audio part of flashbangs
+ emp_protection = TRUE // Immunity to EMP, due to heavy shielding
+ damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
+ xeno_disarm_chance = 10
+ default_cell_type = /obj/item/stock_parts/cell/bluespace
+
+/mob/living/silicon/robot/destroyer/init()
..()
- switch(severity)
- if(1)
- disable_component("comms", 160)
- if(2)
- disable_component("comms", 60)
+ module = new /obj/item/robot_module/destroyer(src)
+ module.add_languages(src)
+ module.add_subsystems_and_actions(src)
+ status_flags &= ~CANPUSH
+ if(radio)
+ qdel(radio)
+ radio = new /obj/item/radio/borg/ert/specops(src)
+ radio.recalculateChannels()
+
+/mob/living/silicon/robot/destroyer/borg_icons()
+ if(base_icon == "")
+ base_icon = icon_state
+ if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility))
+ icon_state = "[base_icon]-roll"
+ else
+ icon_state = base_icon
+ overlays += "[base_icon]-shield"
+
/mob/living/silicon/robot/extinguish_light()
update_headlamp(1, 150)
diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm
index f4290d764d6..b7b81ed40b7 100644
--- a/code/modules/mob/living/silicon/robot/robot_damage.dm
+++ b/code/modules/mob/living/silicon/robot/robot_damage.dm
@@ -76,29 +76,6 @@
if(!LAZYLEN(components))
return
- //Combat shielding absorbs a percentage of damage directly into the cell.
- var/obj/item/borg/combat/shield/shield
- if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield))
- shield = module_state_1
- else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield))
- shield = module_state_2
- else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield))
- shield = module_state_3
- if(shield)
- //Shields absorb a certain percentage of damage based on their power setting.
- var/absorb_brute = brute * shield.shield_level
- var/absorb_burn = burn * shield.shield_level
- var/cost = (absorb_brute+absorb_burn) * 100
-
- cell.charge -= cost
- if(cell.charge <= 0)
- cell.charge = 0
- to_chat(src, "Your shield has overloaded!")
- else
- brute -= absorb_brute
- burn -= absorb_burn
- to_chat(src, "Your shield absorbs some of the impact!")
-
var/datum/robot_component/armour/A = get_armour()
if(A)
A.take_damage(brute, burn, sharp, updating_health)
@@ -127,32 +104,15 @@
updatehealth("heal overall damage")
/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0)
- if(status_flags & GODMODE) return //godmode
+ if(status_flags & GODMODE)
+ return
+
+ if(damage_protection)
+ brute = clamp(brute - damage_protection, 0, brute)
+ burn = clamp(burn - damage_protection, 0, burn)
+
var/list/datum/robot_component/parts = get_damageable_components()
- //Combat shielding absorbs a percentage of damage directly into the cell.
- var/obj/item/borg/combat/shield/shield
- if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield))
- shield = module_state_1
- else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield))
- shield = module_state_2
- else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield))
- shield = module_state_3
- if(shield)
- //Shields absorb a certain percentage of damage based on their power setting.
- var/absorb_brute = brute * shield.shield_level
- var/absorb_burn = burn * shield.shield_level
- var/cost = (absorb_brute+absorb_burn) * 100
-
- cell.charge -= cost
- if(cell.charge <= 0)
- cell.charge = 0
- to_chat(src, "Your shield has overloaded!")
- else
- brute -= absorb_brute
- burn -= absorb_burn
- to_chat(src, "Your shield absorbs some of the impact!")
-
var/datum/robot_component/armour/A = get_armour()
if(A)
A.take_damage(brute, burn, sharp)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 9b4f1089f46..8445a399582 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -2,7 +2,7 @@
if(M.a_intent == INTENT_DISARM)
if(!lying)
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- if(prob(85))
+ if(prob(xeno_disarm_chance))
Stun(7)
step(src, get_dir(M,src))
spawn(5)
diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm
index 92115986b2e..b1abb49d2ef 100644
--- a/code/modules/mob/living/silicon/robot/robot_items.dm
+++ b/code/modules/mob/living/silicon/robot/robot_items.dm
@@ -73,24 +73,7 @@
/obj/item/borg
var/powerneeded // Percentage of power remaining required to run item
-/obj/item/borg/combat/shield
- name = "personal shielding"
- desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge."
- icon = 'icons/obj/decals.dmi'
- icon_state = "shock"
- powerneeded = 25
- var/shield_level = 0.5 //Percentage of damage absorbed by the shield.
-
-/obj/item/borg/combat/shield/verb/set_shield_level()
- set name = "Set shield level"
- set category = "Object"
- set src in range(0)
-
- var/N = input("How much damage should the shield absorb?") in list("5","10","25","50","75","100")
- if(N)
- shield_level = text2num(N)/100
-
-/obj/item/borg/combat/mobility
+/obj/item/borg/destroyer/mobility
name = "mobility module"
desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds."
icon = 'icons/obj/decals.dmi'
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 3260775c674..8e8c67693c5 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -118,20 +118,48 @@
return
/obj/item/robot_module/standard
- name = "standard robot module"
+ // if station is fine, assist with constructing station goal room, cleaning, and repairing cables chewed by rats
+ // if medical crisis, assist by providing basic healthcare, retrieving corpses, and monitoring crew lifesigns
+ // if eng crisis, assist by helping repair hull breaches
+ // if sec crisis, assist by opening doors for sec and providing backup zipties on patrols
+ name = "generalist robot module"
module_type = "Standard"
+ subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor, /mob/living/silicon/proc/subsystem_crew_monitor)
+ stacktypes = list(
+ /obj/item/stack/sheet/metal/cyborg = 50,
+ /obj/item/stack/cable_coil/cyborg = 50,
+ /obj/item/stack/rods/cyborg = 60,
+ /obj/item/stack/tile/plasteel = 20
+ )
/obj/item/robot_module/standard/New()
..()
- modules += new /obj/item/melee/baton/loaded(src)
- modules += new /obj/item/extinguisher(src)
- modules += new /obj/item/wrench/cyborg(src)
+ // sec
+ modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src)
+ // janitorial
+ modules += new /obj/item/soap/nanotrasen(src)
+ modules += new /obj/item/lightreplacer/cyborg(src)
+ // eng
modules += new /obj/item/crowbar/cyborg(src)
+ modules += new /obj/item/wrench/cyborg(src)
+ modules += new /obj/item/extinguisher(src) // for firefighting, and propulsion in space
+ modules += new /obj/item/weldingtool/largetank/cyborg(src)
+ // mining
+ modules += new /obj/item/pickaxe(src)
+ modules += new /obj/item/t_scanner/adv_mining_scanner(src)
+ modules += new /obj/item/storage/bag/ore/cyborg(src)
+ // med
modules += new /obj/item/healthanalyzer(src)
+ modules += new /obj/item/reagent_containers/borghypo/basic(src)
+ modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way
emag = new /obj/item/melee/energy/sword/cyborg(src)
-
+ for(var/G in stacktypes)
+ var/obj/item/stack/sheet/M = new G(src)
+ M.amount = stacktypes[G]
+ modules += M
fix_modules()
+
/obj/item/robot_module/medical
name = "medical robot module"
module_type = "Medical"
@@ -259,6 +287,7 @@
modules += new /obj/item/mop/advanced/cyborg(src)
modules += new /obj/item/lightreplacer/cyborg(src)
modules += new /obj/item/holosign_creator(src)
+ modules += new /obj/item/extinguisher/mini(src)
emag = new /obj/item/reagent_containers/spray(src)
emag.reagents.add_reagent("lube", 250)
@@ -483,25 +512,44 @@
fix_modules()
-/obj/item/robot_module/combat
- name = "combat robot module"
+/obj/item/robot_module/destroyer
+ name = "destroyer robot module"
module_type = "Malf"
module_actions = list(
/datum/action/innate/robot_sight/thermal,
)
+/obj/item/robot_module/destroyer/New()
+ ..()
+
+ modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // See comments on /robot_module/combat below
+ modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats
+ modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src)
+ modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves
+ modules += new /obj/item/borg/destroyer/mobility(src)
+ emag = null
+ fix_modules()
+
+
+/obj/item/robot_module/combat
+ name = "combat robot module"
+ module_type = "Malf"
+ module_actions = list()
+
/obj/item/robot_module/combat/New()
..()
+ modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot.
+ // Borg gets 40 shots of this weapon. Gamma Sec ERT gets 10.
+ // So, borg has way more burst damage, but also takes way longer to recharge / get back in the fight once depleted. Has to find a borg recharger and sit in it for ages.
+ // Organic gamma sec ERT carries alternate weapons, including a box of flashbangs, and can load up on a huge number of guns from science. Borg cannot do either.
+ // Overall, gamma borg has higher skill floor but lower skill ceiling.
+ modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats
modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src)
- modules += new /obj/item/gun/energy/gun/cyborg(src)
- modules += new /obj/item/pickaxe/drill/jackhammer(src)
- modules += new /obj/item/borg/combat/shield(src)
- modules += new /obj/item/borg/combat/mobility(src)
- modules += new /obj/item/wrench/cyborg(src)
- emag = new /obj/item/gun/energy/lasercannon/cyborg(src)
-
+ modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves
+ emag = null
fix_modules()
+
/obj/item/robot_module/alien/hunter
name = "alien hunter module"
module_type = "Standard"
diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm
index 4b645b02849..1d3e1cade6d 100644
--- a/code/modules/mob/living/silicon/robot/robot_movement.dm
+++ b/code/modules/mob/living/silicon/robot/robot_movement.dm
@@ -9,7 +9,7 @@
/mob/living/silicon/robot/movement_delay()
. = ..()
. += speed
- if(module_active && istype(module_active,/obj/item/borg/combat/mobility))
+ if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility))
. -= 3
. += config.robot_delay
diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm
index f5ac3e5532b..98afb8285d1 100644
--- a/code/modules/mob/living/silicon/robot/syndicate.dm
+++ b/code/modules/mob/living/silicon/robot/syndicate.dm
@@ -5,6 +5,7 @@
scrambledcodes = 1
pdahide = 1
faction = list("syndicate")
+ bubble_icon = "syndibot"
designation = "Syndicate Assault"
modtype = "Syndicate"
req_access = list(ACCESS_SYNDICATE)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 50af43f454d..c923e0912b7 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -2,7 +2,9 @@
gender = NEUTER
robot_talk_understand = 1
voice_name = "synthesized voice"
+ bubble_icon = "machine"
has_unlimited_silicon_privilege = 1
+ weather_immunities = list("ash")
var/syndicate = 0
var/const/MAIN_CHANNEL = "Main Frequency"
var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws
diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm
index 5f00beff1e4..d18d41a40cf 100644
--- a/code/modules/mob/living/silicon/subsystems.dm
+++ b/code/modules/mob/living/silicon/subsystems.dm
@@ -2,7 +2,7 @@
var/register_alarms = 1
var/datum/nano_module/alarm_monitor/all/alarm_monitor
var/datum/nano_module/atmos_control/atmos_control
- var/datum/nano_module/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
var/datum/nano_module/law_manager/law_manager
var/datum/nano_module/power_monitor/silicon/power_monitor
@@ -71,8 +71,7 @@
/mob/living/silicon/proc/subsystem_crew_monitor()
set category = "Subsystems"
set name = "Crew Monitor"
-
- crew_monitor.ui_interact(usr, state = GLOB.self_state)
+ crew_monitor.tgui_interact(usr, state = GLOB.tgui_self_state)
/****************
* Law Manager *
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 962b4722f39..cb45fd98e8b 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -18,7 +18,7 @@
speak_emote = list("states")
friendly = "boops"
-
+ bubble_icon = "machine"
faction = list("neutral", "silicon")
var/obj/machinery/bot_core/bot_core = null
@@ -63,6 +63,7 @@
var/new_destination // pending new destination (waiting for beacon response)
var/destination // destination description tag
var/next_destination // the next destination in the patrol route
+ var/ignorelistcleanuptimer = 1 // This ticks up every automated action, at 300 we clean the ignore list
var/robot_arm = /obj/item/robot_parts/r_arm
var/blockcount = 0 //number of times retried a blocked path
@@ -254,6 +255,15 @@
/mob/living/simple_animal/bot/handle_automated_action()
diag_hud_set_botmode()
+ if(ignorelistcleanuptimer % 300 == 0) // Every 300 actions, clean up the ignore list from old junk
+ for(var/uid in ignore_list)
+ var/atom/referredatom = locateUID(uid)
+ if(!referredatom || QDELETED(referredatom))
+ ignore_list -= uid
+ ignorelistcleanuptimer = 1
+ else
+ ignorelistcleanuptimer++
+
if(!on)
return
@@ -439,14 +449,15 @@ Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
The proc would return a human next to the bot to be set to the patient var.
Pass the desired type path itself, declaring a temporary var beforehand is not required.
*/
-/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE)
+/mob/living/simple_animal/bot/proc/scan(atom/scan_type, atom/old_target, scan_range = DEFAULT_SCAN_RANGE)
var/final_result
for(var/scan in shuffle(view(scan_range, src))) //Search for something in range!
- if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
+ var/atom/A = scan
+ if(!istype(A, scan_type)) //Check that the thing we found is the type we want!
continue //If not, keep searching!
- if( (scan in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
+ if((A.UID() in ignore_list) || (A == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
continue
- var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
+ var/scan_result = process_scan(A) //Some bots may require additional processing when a result is selected.
if(scan_result)
final_result = scan_result
else
@@ -454,17 +465,16 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r
return final_result
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
-/mob/living/simple_animal/bot/proc/process_scan(scan_target)
+/mob/living/simple_animal/bot/proc/process_scan(atom/scan_target)
return scan_target
-/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
+/mob/living/simple_animal/bot/proc/add_to_ignore(atom/A)
if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
- ignore_list |= subject
- else if(ignore_list.len >= subject) //If the list is full, insert newest, delete oldest.
- ignore_list -= ignore_list[1]
- ignore_list |= subject
-
+ ignore_list |= A.UID()
+ else //If the list is full, insert newest, delete oldest.
+ ignore_list.Cut(1, 2)
+ ignore_list |= A.UID()
/*
Movement proc for stepping a bot through a path generated through A-star.
Pass a positive integer as an argument to override a bot's default speed.
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index f9747238138..4cb4a532d6b 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -50,7 +50,7 @@
/mob/living/simple_animal/bot/cleanbot/bot_reset()
..()
- ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
+ ignore_list.Cut() //Allows the bot to clean targets it previously ignored due to being unreachable.
target = null
oldloc = null
@@ -106,7 +106,7 @@
audible_message("[src] makes an excited beeping booping sound!")
if(!target) //Search for cleanables it can see.
- target = scan(/obj/effect/decal/cleanable/)
+ target = scan(/obj/effect/decal/cleanable)
if(!target && auto_patrol) //Search for cleanables it can see.
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm
index 5b0ea28a72a..df96cae3b6b 100644
--- a/code/modules/mob/living/simple_animal/bot/floorbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm
@@ -56,7 +56,7 @@
..()
target = null
oldloc = null
- ignore_list = list()
+ ignore_list.Cut()
nagged = 0
anchored = FALSE
update_icon()
@@ -270,7 +270,7 @@
return 1
//Floorbots, having several functions, need sort out special conditions here.
-/mob/living/simple_animal/bot/floorbot/process_scan(scan_target)
+/mob/living/simple_animal/bot/floorbot/process_scan(atom/scan_target)
var/result
var/turf/simulated/floor/F
switch(process_type)
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index d9097a82d2e..62c133fffd7 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -33,7 +33,7 @@
/obj/machinery/bot_core/honkbot
req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME)
-/mob/living/simple_animal/bot/honkbot/Initialize()
+/mob/living/simple_animal/bot/honkbot/Initialize(mapload)
. = ..()
update_icon()
auto_patrol = TRUE
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 728b050dd54..f1278918898 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -323,13 +323,13 @@
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
switch(type)
if(SIGH)
- audible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.")
+ audible_message("[src] makes a sighing buzz.")
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
if(ANNOYED)
- audible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.")
+ audible_message("[src] makes an annoyed buzzing sound.")
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
if(DELIGHT)
- audible_message("[src] makes a delighted ping!", "You hear a ping.")
+ audible_message("[src] makes a delighted ping!")
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
@@ -601,7 +601,7 @@
/mob/living/simple_animal/bot/mulebot/proc/at_target()
if(!reached_target)
radio_channel = "Supply" //Supply channel
- audible_message("[src] makes a chiming sound!", "You hear a chime.")
+ audible_message("[src] makes a chiming sound!")
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
reached_target = 1
diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm
index 10460d11e65..8d0ad5d0e2b 100644
--- a/code/modules/mob/living/simple_animal/corpse.dm
+++ b/code/modules/mob/living/simple_animal/corpse.dm
@@ -103,9 +103,9 @@
name = "Space Wizard Corpse"
outfit = /datum/outfit/wizardcorpse
-/obj/effect/mob_spawn/human/corpse/clownoff/Initialize()
+/obj/effect/mob_spawn/human/corpse/clownoff/Initialize(mapload)
mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]"
- ..()
+ . = ..()
/datum/outfit/wizardcorpse
name = "Space Wizard Corpse"
diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm
index 65b0c492fe6..7c7b4dd6a2f 100644
--- a/code/modules/mob/living/simple_animal/hostile/alien.dm
+++ b/code/modules/mob/living/simple_animal/hostile/alien.dm
@@ -20,6 +20,7 @@
melee_damage_upper = 25
attacktext = "slashes"
speak_emote = list("hisses")
+ bubble_icon = "alien"
a_intent = INTENT_HARM
attack_sound = 'sound/weapons/bladeslice.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
@@ -127,6 +128,7 @@
icon_state = "queen_s"
icon_living = "queen_s"
icon_dead = "queen_dead"
+ bubble_icon = "alienroyal"
move_to_delay = 4
maxHealth = 400
health = 400
diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
index ced4a61d8c5..08eb7128055 100644
--- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
@@ -25,7 +25,7 @@
var/list/human_overlays = list()
/mob/living/simple_animal/hostile/headcrab/Life(seconds, times_fired)
- if(..())
+ if(..() && !stat)
if(!is_zombie && isturf(src.loc))
for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile
if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD))
@@ -35,7 +35,7 @@
if(cycles >= 4)
for(var/mob/living/simple_animal/K in oview(src, 1)) //Only for corpse right next to/on same tile
if(K.stat == DEAD || (!K.check_death_method() && K.health <= HEALTH_THRESHOLD_DEAD))
- visible_message("[src] consumes [target] whole!")
+ visible_message("[src] consumes [K] whole!")
if(health < maxHealth)
health += 10
qdel(K)
diff --git a/code/modules/mob/living/simple_animal/hostile/headslug.dm b/code/modules/mob/living/simple_animal/hostile/headslug.dm
index 801514a3256..6800598fc7f 100644
--- a/code/modules/mob/living/simple_animal/hostile/headslug.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headslug.dm
@@ -72,7 +72,7 @@
/obj/item/organ/internal/body_egg/changeling_egg/proc/Pop()
var/mob/living/carbon/human/monkey/M = new(owner)
- owner.stomach_contents += M
+ LAZYADD(owner.stomach_contents, M)
for(var/obj/item/organ/internal/I in src)
I.insert(M, 1)
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
index d2a97949c8e..c6f3ac845e3 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
@@ -25,6 +25,7 @@
gold_core_spawnable = HOSTILE_SPAWN
loot = list(/obj/effect/decal/cleanable/blood/gibs/robot)
deathmessage = "blows apart!"
+ bubble_icon = "machine"
del_on_death = 1
/mob/living/simple_animal/hostile/hivebot/range
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 6dca834169b..50531f976e9 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -534,7 +534,7 @@ obj/effect/temp_visual/fireball
duration = 9
pixel_z = 270
-/obj/effect/temp_visual/fireball/Initialize()
+/obj/effect/temp_visual/fireball/Initialize(mapload)
. = ..()
animate(src, pixel_z = 0, time = duration)
diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm
index 848a285abf4..fbf65ccfa9b 100644
--- a/code/modules/mob/living/simple_animal/hostile/mimic.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm
@@ -48,8 +48,8 @@
var/attempt_open = 0
// Pickup loot
-/mob/living/simple_animal/hostile/mimic/crate/Initialize()
- ..()
+/mob/living/simple_animal/hostile/mimic/crate/Initialize(mapload)
+ . = ..()
for(var/obj/item/I in loc)
I.loc = src
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index e8ce4027b4e..89164167005 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -31,7 +31,7 @@
deathmessage = "wails as its form turns into a pulpy mush."
death_sound = 'sound/voice/hiss6.ogg'
-/mob/living/simple_animal/hostile/netherworld/migo/Initialize()
+/mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload)
. = ..()
migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers
@@ -82,14 +82,14 @@
/obj/structure/spawner/nether/examine(mob/user)
. = ..()
- if(isskeleton(user) || iszombie(user))
+ if(isskeleton(user))
. += "A direct link to another dimension full of creatures very happy to see you. You can see your house from here!"
else
. += "A direct link to another dimension full of creatures not very happy to see you. Entering the link would be a very bad idea."
/obj/structure/spawner/nether/attack_hand(mob/user)
. = ..()
- if(isskeleton(user) || iszombie(user))
+ if(isskeleton(user))
to_chat(user, "You don't feel like going home yet...")
else
user.visible_message("[user] is violently pulled into the link!", \
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 6f40dfe2a3f..decfc7146cf 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -260,7 +260,7 @@
alert_on_shield_breach = TRUE
/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload)
- ..()
+ . = ..()
if(prob(50))
// 50% chance of switching to extremely dangerous ranged variant
melee_damage_lower = 10
@@ -363,6 +363,7 @@
minbodytemp = 0
mob_size = MOB_SIZE_TINY
flying = 1
+ bubble_icon = "syndibot"
gold_core_spawnable = HOSTILE_SPAWN
del_on_death = 1
deathmessage = "is smashed into pieces!"
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 8c7d14b23f4..15f483bef76 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -17,6 +17,7 @@
response_harm = "stomps on"
emote_see = list("jiggles", "bounces in place")
speak_emote = list("blorbles")
+ bubble_icon = "slime"
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm
index 8bdb0d89332..91bf5f0ab9c 100644
--- a/code/modules/mob/logout.dm
+++ b/code/modules/mob/logout.dm
@@ -1,5 +1,6 @@
/mob/Logout()
SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
+ SStgui.on_logout(src) // Cleanup any TGUIs the user has open
unset_machine()
GLOB.player_list -= src
log_access_out(src)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d7f05bd943d..c98ca77a594 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -20,10 +20,10 @@
AA.viewers -= src
viewing_alternate_appearances = null
logs.Cut()
- ..()
- return QDEL_HINT_HARDDEL
+ LAssailant = null
+ return ..()
-/mob/Initialize()
+/mob/Initialize(mapload)
GLOB.mob_list += src
if(stat == DEAD)
GLOB.dead_mob_list += src
@@ -31,7 +31,7 @@
GLOB.alive_mob_list += src
set_focus(src)
prepare_huds()
- ..()
+ . = ..()
/atom/proc/prepare_huds()
hud_list = list()
@@ -127,14 +127,12 @@
// self_message (optional) is what the src mob hears.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
-/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message)
+/mob/audible_message(message, deaf_message, hearing_distance)
var/range = 7
if(hearing_distance)
range = hearing_distance
var/msg = message
for(var/mob/M in get_mobs_in_view(range, src))
- if(self_message && M == src)
- msg = self_message
M.show_message(msg, 2, deaf_message, 1)
// based on say code
@@ -156,12 +154,12 @@
// message is the message output to anyone who can hear.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
-/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance)
+/atom/proc/audible_message(message, deaf_message, hearing_distance)
var/range = 7
if(hearing_distance)
range = hearing_distance
for(var/mob/M in get_mobs_in_view(range, src))
- M.show_message( message, 2, deaf_message, 1)
+ M.show_message(message, 2, deaf_message, 1)
/mob/proc/findname(msg)
for(var/mob/M in GLOB.mob_list)
@@ -754,7 +752,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
if(client.holder && (client.holder.rights & R_ADMIN))
is_admin = 1
- else if(stat != DEAD || istype(src, /mob/new_player))
+ else if(stat != DEAD || isnewplayer(src))
to_chat(usr, "You must be observing to use this!")
return
@@ -1390,3 +1388,29 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
///Force set the mob nutrition
/mob/proc/set_nutrition(change)
nutrition = max(0, change)
+
+/mob/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE)
+ . = ..()
+ if(bloody_hands && clean_hands)
+ bloody_hands = 0
+ update_inv_gloves()
+ if(l_hand)
+ if(l_hand.clean_blood())
+ update_inv_l_hand()
+ if(r_hand)
+ if(r_hand.clean_blood())
+ update_inv_r_hand()
+ if(back)
+ if(back.clean_blood())
+ update_inv_back()
+ if(wear_mask && clean_mask)
+ if(wear_mask.clean_blood())
+ update_inv_wear_mask()
+ if(clean_feet)
+ feet_blood_color = null
+ qdel(feet_blood_DNA)
+ bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0)
+ blood_state = BLOOD_STATE_NOT_BLOODY
+ update_inv_shoes()
+ update_icons() //apply the now updated overlays to the mob
+
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index dc77610b97e..1477be47d89 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -31,8 +31,8 @@
var/use_me = 1 //Allows all mobs to use the me verb by default, will have to manually specify they cannot
var/damageoverlaytemp = 0
var/computer_id = null
- var/lastattacker = null
- var/lastattacked = null
+ var/lastattacker = null // real name of the person doing the attacking
+ var/lastattackerckey = null // their ckey
var/list/attack_log_old = list( )
var/list/debug_log = null
@@ -50,7 +50,6 @@
var/med_record = ""
var/sec_record = ""
var/gen_record = ""
- var/bhunger = 0 //Carbon
var/lying = 0
var/lying_prev = 0
var/lastpuke = 0
@@ -61,8 +60,7 @@
var/emote_type = 1 // Define emote default type, 1 for seen emotes, 2 for heard emotes
var/name_archive //For admin things like possession
- var/timeofdeath = 0.0//Living
-
+ var/timeofdeath = 0 //Living
var/bodytemperature = 310.055 //98.7 F
var/flying = 0
@@ -71,22 +69,22 @@
var/hunger_drain = HUNGER_FACTOR // how quickly the mob gets hungry; largely utilized by species.
var/overeatduration = 0 // How long this guy is overeating //Carbon
- var/intent = null//Living
+ var/intent = null //Living
var/shakecamera = 0
- var/a_intent = INTENT_HELP//Living
- var/m_intent = MOVE_INTENT_RUN//Living
+ var/a_intent = INTENT_HELP //Living
+ var/m_intent = MOVE_INTENT_RUN //Living
var/lastKnownIP = null
/// movable atoms buckled to this mob
- var/atom/movable/buckled = null//Living
+ var/atom/movable/buckled = null //Living
/// movable atom we are buckled to
var/atom/movable/buckling
- var/obj/item/l_hand = null//Living
- var/obj/item/r_hand = null//Living
- var/obj/item/back = null//Human/Monkey
- var/obj/item/tank/internal = null//Human/Monkey
- var/obj/item/storage/s_active = null//Carbon
- var/obj/item/clothing/mask/wear_mask = null//Carbon
+ var/obj/item/l_hand = null //Living
+ var/obj/item/r_hand = null //Living
+ var/obj/item/back = null //Human
+ var/obj/item/tank/internal = null //Human
+ var/obj/item/storage/s_active = null //Carbon
+ var/obj/item/clothing/mask/wear_mask = null //Carbon
var/datum/hud/hud_used = null
@@ -95,7 +93,6 @@
var/research_scanner = 0 //For research scanner equipped mobs. Enable to show research data when examining.
var/list/grabbed_by = list()
- var/list/requests = list()
var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
var/list/mapobjs = list()
@@ -103,9 +100,9 @@
var/emote_cd = 0 // Used to supress emote spamming. 1 if on CD, 2 if disabled by admin (manually set), else 0
- var/job = null//Living
+ var/job = null //Living
- var/datum/dna/dna = null//Carbon
+ var/datum/dna/dna = null //Carbon
var/radiation = 0 //Carbon
var/list/mutations = list() //Carbon -- Doohl
@@ -179,8 +176,6 @@
var/turf/listed_turf = null //the current turf being examined in the stat panel
var/list/shouldnt_see = list() //list of objects that this mob shouldn't see in the stat panel. this silliness is needed because of AI alt+click and cult blood runes
- var/kills = 0
-
var/stance_damage = 0 //Whether this mob's ability to stand has been affected
var/list/active_genes = list()
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 640ffb09d4f..37e116b4376 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -408,7 +408,7 @@
add_attack_logs(attacker, affecting, "Devoured")
affecting.forceMove(user)
- attacker.stomach_contents.Add(affecting)
+ LAZYADD(attacker.stomach_contents, affecting)
qdel(src)
/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab
@@ -434,7 +434,7 @@
if(affecting)
if(!affecting.buckled)
affecting.pixel_x = 0
- affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing
+ affecting.pixel_y = 0 //used to be an animate, not quick enough for qdel'ing
affecting.layer = initial(affecting.layer)
affecting.grabbed_by -= src
affecting = null
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index df2a39c0930..5fb5bcb06c8 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -12,14 +12,6 @@
return 1
return 0
-/mob/proc/isSynthetic()
- return 0
-
-/mob/living/carbon/human/isSynthetic()
- if(ismachine(src))
- return TRUE
- return FALSE
-
/mob/proc/get_screen_colour()
/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length.
@@ -467,7 +459,7 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
name = realname
for(var/mob/M in GLOB.player_list)
- if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD))
+ if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD))
var/follow
var/lname
if(subject)
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index c3ec68c9793..1e704ea3449 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -4,7 +4,7 @@
//Note that this proc does NOT do MMI related stuff!
/mob/proc/change_mob_type(var/new_type = null, var/turf/location = null, var/new_name = null as text, var/delete_old_mob = 0 as num, var/forcekey = 0)
- if(istype(src,/mob/new_player))
+ if(isnewplayer(src))
to_chat(usr, "cannot convert players who have not entered yet.")
return
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index af3d439df59..f53daba6626 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -12,8 +12,13 @@
stat = 2
canmove = 0
-/mob/new_player/New()
+/mob/new_player/Initialize(mapload)
+ SHOULD_CALL_PARENT(FALSE)
+ if(initialized)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ initialized = TRUE
GLOB.mob_list += src
+ return INITIALIZE_HINT_NORMAL
/mob/new_player/verb/new_player_panel()
set src = usr
@@ -205,6 +210,7 @@
if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed.
observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing!
observer.key = key
+ QDEL_NULL(mind)
GLOB.respawnable_list += observer
qdel(src)
return 1
diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
index 476861861a3..d4834eccc80 100644
--- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
@@ -48,7 +48,7 @@
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
// Restrict some styles to specific species
- var/list/species_allowed = list("Human", "Slime People", "Infectious Zombie", "High-Functioning Zombie")
+ var/list/species_allowed = list("Human", "Slime People")
var/list/sprite_sheets = list() //For accessories common across species but need to use 'fitted' sprites (like underwear). e.g. list("Vox" = 'icons/mob/species/vox/iconfile.dmi')
var/list/models_allowed = list() //Specifies which, if any, hairstyles or markings can be accessed by which prosthetics. Should equal the manufacturing company name in robolimbs.dm.
var/list/heads_allowed = null //Specifies which, if any, alt heads a head marking, hairstyle or facial hair style is compatible with.
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 81d22e40a0b..0417e28f546 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(text)
var/ending = copytext(text, length(text))
if(ending == "?")
return "1"
diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm
index 4dfbddc2d84..a2ca226147c 100644
--- a/code/modules/mob/typing_indicator.dm
+++ b/code/modules/mob/typing_indicator.dm
@@ -5,31 +5,32 @@ mob/var/typing
mob/var/last_typed
mob/var/last_typed_time
-GLOBAL_DATUM(typing_indicator, /image)
+GLOBAL_LIST_EMPTY(typing_indicator)
-/mob/proc/set_typing_indicator(var/state)
+/mob/proc/set_typing_indicator(state)
- if(!GLOB.typing_indicator)
- GLOB.typing_indicator = image('icons/mob/talk.dmi', null, "typing", MOB_LAYER + 1)
- GLOB.typing_indicator.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ if(!GLOB.typing_indicator[bubble_icon])
+ GLOB.typing_indicator[bubble_icon] = image('icons/mob/talk.dmi', null, "[bubble_icon]typing", FLY_LAYER)
+ var/image/I = GLOB.typing_indicator[bubble_icon]
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
if(ishuman(src))
var/mob/living/carbon/human/H = src
if((MUTE in H.mutations) || H.silent)
- overlays -= GLOB.typing_indicator
+ overlays -= GLOB.typing_indicator[bubble_icon]
return
if(client)
if((client.prefs.toggles & SHOW_TYPING) || stat != CONSCIOUS || is_muzzled())
- overlays -= GLOB.typing_indicator
+ overlays -= GLOB.typing_indicator[bubble_icon]
else
if(state)
if(!typing)
- overlays += GLOB.typing_indicator
+ overlays += GLOB.typing_indicator[bubble_icon]
typing = 1
else
if(typing)
- overlays -= GLOB.typing_indicator
+ overlays -= GLOB.typing_indicator[bubble_icon]
typing = 0
return state
diff --git a/code/modules/nano/modules/crew_monitor.dm b/code/modules/nano/modules/crew_monitor.dm
deleted file mode 100644
index 4ed7fc03de1..00000000000
--- a/code/modules/nano/modules/crew_monitor.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-/datum/nano_module/crew_monitor
- name = "Crew monitor"
-
-/datum/nano_module/crew_monitor/Topic(href, href_list)
- if(..())
- return 1
- var/turf/T = get_turf(nano_host())
- if(!T || !is_level_reachable(T.z))
- to_chat(usr, "Unable to establish a connection: You're too far away from the station!")
- return 0
- if(href_list["track"])
- if(isAI(usr))
- var/mob/living/silicon/ai/AI = usr
- var/mob/living/carbon/human/H = locate(href_list["track"]) in GLOB.mob_list
- if(hassensorlevel(H, SUIT_SENSOR_TRACKING))
- AI.ai_actual_track(H)
- return 1
-
-/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800)
-
- // adding a template with the key "mapContent" enables the map ui functionality
- ui.add_template("mapContent", "crew_monitor_map_content.tmpl")
- // adding a template with the key "mapHeader" replaces the map header content
- ui.add_template("mapHeader", "crew_monitor_map_header.tmpl")
-
- ui.open()
-
- // should make the UI auto-update; doesn't seem to?
- ui.set_auto_update(1)
-
-/datum/nano_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- var/turf/T = get_turf(nano_host())
-
- data["isAI"] = isAI(user)
- data["crewmembers"] = GLOB.crew_repository.health_data(T)
-
- return data
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 5ca53396253..d5cbbdc0ad6 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -340,7 +340,7 @@
return 0
else
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
- atom_say("Attention: Posterior Placed on Printing Plaque!")
+ atom_say("Attention: Posterior Placed on Printing Plaque!")
return 1
/obj/machinery/photocopier/emag_act(user as mob)
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 5934dfa4f7d..0cb571d576c 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -562,13 +562,13 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
talk_into(M, msg)
for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
if(T.watchers[M] == camera)
- T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2)
+ T.atom_say(msg)
/obj/item/videocam/hear_message(mob/M as mob, msg)
if(camera && on)
for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
if(T.watchers[M] == camera)
- T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2)
+ T.atom_say(msg)
///hauntings, like hallucinations but more spooky
diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm
index c48ebdd4e9e..c72aa6d0d41 100644
--- a/code/modules/pda/mob_hunt_game_app.dm
+++ b/code/modules/pda/mob_hunt_game_app.dm
@@ -47,7 +47,7 @@
SSmob_hunt.connected_clients += src
connected = 1
if(pda)
- pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2)
+ pda.atom_say("Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!")
return 1
/datum/data/pda/app/mob_hunter_game/proc/get_player()
@@ -67,7 +67,7 @@
connected = 0
//show a disconnect message if we were disconnected involuntarily (reason argument provided)
if(pda && reason)
- pda.audible_message("[bicon(pda)] Disconnected from server. Reason: [reason].", null, 2)
+ pda.atom_say("Disconnected from server. Reason: [reason].")
/datum/data/pda/app/mob_hunter_game/program_process()
if(!SSmob_hunt || !connected)
diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm
index e0eb509aa9d..bcadfc62e08 100644
--- a/code/modules/projectiles/ammunition/energy.dm
+++ b/code/modules/projectiles/ammunition/energy.dm
@@ -12,7 +12,7 @@
select_name = "kill"
/obj/item/ammo_casing/energy/laser/cyborg //to balance cyborg energy cost seperately
- e_cost = 250
+ e_cost = 250
/obj/item/ammo_casing/energy/lasergun
projectile_type = /obj/item/projectile/beam/laser
@@ -75,6 +75,10 @@
e_cost = 125
select_name = "precise"
+/obj/item/ammo_casing/energy/immolator/strong/cyborg
+ // Used by gamma ERT borgs
+ e_cost = 1000 // 5x that of the standard laser, for 2.25x the damage (if 1/1 shots hit) plus ignite. Not energy-efficient, but can be used for sniping.
+
/obj/item/ammo_casing/energy/immolator/scatter
projectile_type = /obj/item/projectile/beam/immolator/weak
e_cost = 125
@@ -82,6 +86,10 @@
variance = 25
select_name = "scatter"
+/obj/item/ammo_casing/energy/immolator/scatter/cyborg
+ // Used by gamma ERT borgs
+ e_cost = 1000 // 5x that of the standard laser, for 7.5x the damage (if 6/6 shots hit) plus ignite. Efficient only if you hit with at least 4/6 of the shots.
+
/obj/item/ammo_casing/energy/electrode
projectile_type = /obj/item/projectile/energy/electrode
select_name = "stun"
diff --git a/code/modules/projectiles/guns/dartgun.dm b/code/modules/projectiles/guns/dartgun.dm
index 0a50b844f4a..8c46e542b4a 100644
--- a/code/modules/projectiles/guns/dartgun.dm
+++ b/code/modules/projectiles/guns/dartgun.dm
@@ -175,11 +175,6 @@
for(var/datum/reagent/A in D.reagents.reagent_list)
R += A.id + " ("
R += num2text(A.volume) + "),"
- if(istype(M, /mob))
- if(!iscarbon(user))
- M.LAssailant = null
- else
- M.LAssailant = user
add_attack_logs(user, M, "Shot with dartgun containing [R]")
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index ecf5eeed77e..19316cb9540 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -253,7 +253,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "modkit"
origin_tech = "programming=2;materials=2;magnets=4"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/miner
usesound = 'sound/items/screwdriver.ogg'
var/denied_type = null
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 9d4cfaee3d4..3e9c882458e 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -139,6 +139,12 @@
var/append = shot.select_name
overlays += image(icon = icon, icon_state = "multilensimmolator-[append]")
+
+/obj/item/gun/energy/immolator/multi/cyborg
+ name = "cyborg immolator cannon"
+ ammo_type = list(/obj/item/ammo_casing/energy/immolator/scatter/cyborg, /obj/item/ammo_casing/energy/immolator/strong/cyborg) // scatter is default, because it is more useful
+
+
////////Laser Tag////////////////////
/obj/item/gun/energy/laser/tag
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index dc70eda9cec..4505d984f84 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -28,7 +28,7 @@
/obj/item/projectile/magic/death/on_hit(mob/living/carbon/C)
. = ..()
if(isliving(C))
- if(ismachine(C)) //speshul snowfleks deserv speshul treetment
+ if(ismachineperson(C)) //speshul snowfleks deserv speshul treetment
C.adjustFireLoss(6969) //remember - slimes love fire
else
C.death()
diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm
index dec256ac455..c6ff39aaead 100644
--- a/code/modules/reagents/chemistry/reagents/toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/toxins.dm
@@ -226,37 +226,6 @@
if(B && islist(B.data) && !data)
data = B.data.Copy()
-/datum/reagent/romerol
- name = "romerol"
- // the REAL zombie powder
- id = "romerol"
- description = "Romerol is a highly experimental bioterror agent \
- which causes dormant nodules to be etched into the grey matter of \
- the subject. These nodules only become active upon death of the \
- host, upon which, the secondary structures activate and take control \
- of the host body."
- color = "#123524" // RGB (18, 53, 36)
- metabolization_rate = INFINITY
- can_synth = FALSE
- taste_description = "CAAAARL"
-
-/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method = REAGENT_TOUCH, volume)
- if(!istype(H))
- return
- // Silently add the zombie infection organ to be activated upon death
- if(!H.get_organ_slot("zombie_infection"))
- var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new()
- ZI.insert(H)
- ..()
-
-/datum/reagent/romerol/on_mob_life(mob/living/M)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(!H.get_organ_slot("zombie_infection"))
- var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new()
- ZI.insert(H)
- return ..()
-
/datum/reagent/uranium
name ="Uranium"
id = "uranium"
diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm
index f975cac7293..186315ecc56 100644
--- a/code/modules/reagents/chemistry/reagents/water.dm
+++ b/code/modules/reagents/chemistry/reagents/water.dm
@@ -83,37 +83,7 @@
M.adjustToxLoss(rand(5, 10))
/datum/reagent/space_cleaner/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)
- if(iscarbon(M))
- var/mob/living/carbon/C = M
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.lip_style)
- H.lip_style = null
- H.update_body()
- if(C.r_hand)
- C.r_hand.clean_blood()
- if(C.l_hand)
- C.l_hand.clean_blood()
- if(C.wear_mask)
- if(C.wear_mask.clean_blood())
- C.update_inv_wear_mask()
- if(ishuman(M))
- var/mob/living/carbon/human/H = C
- if(H.head)
- if(H.head.clean_blood())
- H.update_inv_head()
- if(H.wear_suit)
- if(H.wear_suit.clean_blood())
- H.update_inv_wear_suit()
- else if(H.w_uniform)
- if(H.w_uniform.clean_blood())
- H.update_inv_w_uniform()
- if(H.shoes)
- if(H.shoes.clean_blood())
- H.update_inv_shoes()
- M.clean_blood()
- ..()
-
+ M.clean_blood()
/datum/reagent/blood
data = list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=null,"blood_colour"="#A10808","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null, "dna" = null)
diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm
index 1fa4930b0f6..8338253fc6c 100644
--- a/code/modules/reagents/reagent_containers/borghydro.dm
+++ b/code/modules/reagents/reagent_containers/borghydro.dm
@@ -47,7 +47,7 @@
/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg
charge_tick++
- if(charge_tick < recharge_time)
+ if(charge_tick < recharge_time)
return FALSE
charge_tick = 0
@@ -97,9 +97,7 @@
var/contained = injected.name
var/trans = R.trans_to(M, amount_per_transfer_from_this)
add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null)
- M.LAssailant = user
to_chat(user, "[trans] units injected. [R.total_volume] units remaining.")
- return
/obj/item/reagent_containers/borghypo/attack_self(mob/user)
playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode
@@ -126,4 +124,9 @@
if(empty)
. += "It is currently empty. Allow some time for the internal syntheszier to produce more."
+/obj/item/reagent_containers/borghypo/basic
+ name = "Basic Medical Hypospray"
+ desc = "A very basic medical hypospray, capable of providing simple medical treatment in emergencies."
+ reagent_ids = list("salglu_solution", "epinephrine")
+
#undef BORGHYPO_REFILL_VALUE
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index 61552eae789..6c836988e8b 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -12,11 +12,6 @@
container_type = OPENCONTAINER
volume = 30
-/obj/item/reagent_containers/glass/bottle/romerol
- name = "romerol bottle"
- desc = "A small bottle of Romerol. The REAL zombie powder."
- list_reagents = list("romerol" = 30)
-
/obj/item/reagent_containers/glass/bottle/on_reagent_change()
update_icon()
diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm
index 6f32411a261..1e42899111b 100644
--- a/code/modules/reagents/reagent_containers/glass_containers.dm
+++ b/code/modules/reagents/reagent_containers/glass_containers.dm
@@ -43,10 +43,6 @@
M.visible_message("[user] splashes the contents of [src] onto [M]!", \
"[user] splashes the contents of [src] onto [M]!")
add_attack_logs(user, M, "Splashed with [name] containing [contained]", !!M.ckey ? null : ATKLOG_ALL)
- if(!iscarbon(user))
- M.LAssailant = null
- else
- M.LAssailant = user
reagents.reaction(M, REAGENT_TOUCH)
reagents.clear_reagents()
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index 582e157004c..17112901b29 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -20,7 +20,7 @@
if(M.eat(src, user))
user.drop_item()
forceMove(M)
- M.processing_patches += src
+ LAZYADD(M.processing_patches, src)
return TRUE
return FALSE
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index 2aec9412def..13d88100042 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -207,7 +207,10 @@
else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis)
msg = "[user.name] stuffs [target.name] into the [src]!"
to_chat(user, "You stuff [target.name] into the [src]!")
-
+ if(!iscarbon(user))
+ target.LAssailant = null
+ else
+ target.LAssailant = user
add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL)
else
return
@@ -237,10 +240,10 @@
// ai as human but can't flush
/obj/machinery/disposal/attack_ai(mob/user as mob)
src.add_hiddenprint(user)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/disposal/attack_ghost(mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
// human interact with machine
/obj/machinery/disposal/attack_hand(mob/user as mob)
@@ -256,76 +259,68 @@
// Clumsy folks can only flush it.
if(user.IsAdvancedToolUser())
- ui_interact(user)
+ tgui_interact(user)
else
flush = !flush
update()
return
-/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/disposal/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250)
+ ui = new(user, src, ui_key, "DisposalBin", name, 300, 250, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- var/pressure = clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100)
- var/pressure_round = round(pressure,1)
+/obj/machinery/disposal/tgui_data(mob/user)
+ var/list/data = list()
data["isAI"] = isAI(user)
data["flushing"] = flush
data["mode"] = mode
- if(mode <= 0)
- data["pumpstatus"] = "N/A"
- else if(mode == 1)
- data["pumpstatus"] = "Pressurizing"
- else if(mode == 2)
- data["pumpstatus"] = "Ready"
- else
- data["pumpstatus"] = "Idle"
- data["pressure"] = pressure_round
+ data["pressure"] = round(clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100),1)
return data
-/obj/machinery/disposal/Topic(href, href_list)
+/obj/machinery/disposal/tgui_act(action, params)
+ if(..())
+ return
if(usr.loc == src)
to_chat(usr, "You cannot reach the controls from inside.")
return
- if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1
+ if(mode==-1 && action != "eject") // If the mode is -1, only allow ejection
to_chat(usr, "The disposal units power is disabled.")
return
- if(..())
- return
-
if(stat & BROKEN)
return
src.add_fingerprint(usr)
- if(usr.stat || usr.restrained() || src.flushing)
+ if(src.flushing)
return
if(istype(src.loc, /turf))
- if(href_list["pump"])
- if(text2num(href_list["pump"]))
- mode = 1
- else
- mode = 0
+ if(action == "pumpOn")
+ mode = 1
+ update()
+ if(action == "pumpOff")
+ mode = 0
update()
- if(!isAI(usr))
- if(href_list["handle"])
- flush = text2num(href_list["handle"])
+ if(!issilicon(usr))
+ if(action == "engageHandle")
+ flush = 1
+ update()
+ if(action == "disengageHandle")
+ flush = 0
update()
- if(href_list["eject"])
+ if(action == "eject")
eject()
- return
+
+ return TRUE
// eject the contents of the disposal unit
/obj/machinery/disposal/proc/eject()
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 5b5dba286fd..76eabe46761 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1081,8 +1081,17 @@
construction_time = 120
category = list("Cyborg Upgrade Modules")
+/datum/design/borg_upgrade_lavaproof
+ name = "Cyborg Upgrade (Lavaproof Chassis)"
+ id = "borg_upgrade_lavaproof"
+ build_type = MECHFAB
+ build_path = /obj/item/borg/upgrade/lavaproof
+ materials = list(MAT_METAL = 10000, MAT_PLASMA = 4000, MAT_TITANIUM = 5000)
+ construction_time = 120
+ category = list("Cyborg Upgrade Modules")
+
/datum/design/borg_syndicate_module
- name = "Cyborg Upgrade (Illegal Modules)"
+ name = "Cyborg Upgrade (Safety Override)"
id = "borg_syndicate_module"
build_type = MECHFAB
req_tech = list("combat" = 4, "syndicate" = 2)
diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm
index df3a7945e92..91306d910f1 100644
--- a/code/modules/research/message_server.dm
+++ b/code/modules/research/message_server.dm
@@ -103,12 +103,12 @@ GLOBAL_LIST_EMPTY(message_servers)
if(2)
if(!Console.silent)
playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1)
- Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5)
+ Console.atom_say("PRIORITY Alert in [sender]")
Console.message_log += "High Priority message from [sender]
[authmsg]"
else
if(!Console.silent)
playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1)
- Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4)
+ Console.atom_say("Message from [sender]")
Console.message_log += "Message from [sender]
[authmsg]"
Console.set_light(2)
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 6155c1de6b9..a25be3814a7 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -135,8 +135,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
/client/proc/create_response_team(new_gender, role, turf/spawn_location)
if(role == "Cyborg")
- var/cyborg_unlock = GLOB.active_team.getCyborgUnlock()
- var/mob/living/silicon/robot/ert/R = new /mob/living/silicon/robot/ert(spawn_location, cyborg_unlock)
+ var/mob/living/silicon/robot/ert/R = new GLOB.active_team.borg_path(spawn_location)
return R
var/mob/living/carbon/human/M = new(null)
@@ -208,7 +207,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
var/security_outfit
var/janitor_outfit
var/paranormal_outfit
- var/cyborg_unlock = 0
+ var/borg_path = /mob/living/silicon/robot/ert
/datum/response_team/proc/setSlots(com=1, sec=3, med=3, eng=3, jan=0, par=0, cyb=0)
slots["Commander"] = com
@@ -223,9 +222,6 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
slots[role]--
count++
-/datum/response_team/proc/getCyborgUnlock()
- return cyborg_unlock
-
/datum/response_team/proc/get_slot_list()
RETURN_TYPE(/list)
var/list/slots_available = list()
@@ -285,6 +281,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
command_outfit = /datum/outfit/job/centcom/response_team/commander/red
janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/red
paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/red
+ borg_path = /mob/living/silicon/robot/ert/red
/datum/response_team/red/announce_team()
GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code RED Emergency Response Team. Standby.", "ERT En-Route")
@@ -298,7 +295,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
command_outfit = /datum/outfit/job/centcom/response_team/commander/gamma
janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/gamma
paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/gamma
- cyborg_unlock = 1
+ borg_path = /mob/living/silicon/robot/ert/gamma
/datum/response_team/gamma/announce_team()
GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code GAMMA elite Emergency Response Team. Standby.", "ERT En-Route")
diff --git a/code/modules/ruins/ruin_areas.dm b/code/modules/ruins/ruin_areas.dm
index 754f8d44f29..cf3b08aa0f2 100644
--- a/code/modules/ruins/ruin_areas.dm
+++ b/code/modules/ruins/ruin_areas.dm
@@ -5,8 +5,8 @@
icon_state = "away"
has_gravity = TRUE
there_can_be_many = TRUE
- ambientsounds = list('sound/ambience/ambimine.ogg')
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+ ambientsounds = RUINS_SOUNDS
/area/ruin/unpowered
always_unpowered = FALSE
diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm
index 0f07107a00b..00c575e53b0 100644
--- a/code/modules/security_levels/security levels.dm
+++ b/code/modules/security_levels/security levels.dm
@@ -31,15 +31,6 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
// Mark down this time to prevent shuttle cheese
SSshuttle.emergency_sec_level_time = world.time
- // Reset gamma borgs if the new security level is lower than Gamma.
- if(level < SEC_LEVEL_GAMMA)
- for(var/M in GLOB.silicon_mob_list)
- if(isrobot(M))
- var/mob/living/silicon/robot/R = M
- if(istype(R.module, /obj/item/robot_module/combat) && !R.crisis)
- R.reset_module()
- to_chat(R, "Crisis mode deactivated. The combat module is no longer available and your module has been reset.")
-
switch(level)
if(SEC_LEVEL_GREEN)
GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.")
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index f82598b1e14..de08d5cdf82 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -178,8 +178,6 @@
continue
if(player.stat == DEAD) // Corpses
continue
- if(iszombie(player)) // Walking corpses
- continue
if(issilicon(player)) //Borgs are technically dead anyways
continue
if(isanimal(player)) //Poly does not own the shuttle
diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm
index 2e3c89017ef..56e7f8f96ba 100644
--- a/code/modules/spacepods/spacepod.dm
+++ b/code/modules/spacepods/spacepod.dm
@@ -973,7 +973,7 @@
var/obj/I = pick(true_contents)
if(user.put_in_any_hand_if_possible(I))
src.contents -= I
- to_chat(user, "You find a [I] [pick("under the seat", "under the console", "in the mainenance access")]!")
+ to_chat(user, "You find a [I] [pick("under the seat", "under the console", "in the maintenance access")]!")
else
to_chat(user, "You think you saw something shiny, but you can't reach it!")
else
diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm
index db1ad04aef3..c43c3f373d5 100644
--- a/code/modules/surgery/limb_reattach.dm
+++ b/code/modules/surgery/limb_reattach.dm
@@ -32,7 +32,7 @@
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(ismachine(target))
+ if(ismachineperson(target))
// RIP bi-centennial man
return 0
if(!affected)
diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm
index e01c75dcbd0..b0403d8ae49 100644
--- a/code/modules/surgery/organs/blood.dm
+++ b/code/modules/surgery/organs/blood.dm
@@ -54,7 +54,7 @@
var/obj/item/organ/external/BP = X
var/brutedamage = BP.brute_dam
- if(BP.is_robotic() && !isSynthetic())
+ if(BP.is_robotic() && !ismachineperson(src))
continue
//We want an accurate reading of .len
diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm
index c2df537e771..2a3b94ca249 100644
--- a/code/modules/surgery/organs/organ_icon.dm
+++ b/code/modules/surgery/organs/organ_icon.dm
@@ -138,7 +138,7 @@ GLOBAL_LIST_EMPTY(limb_icon_cache)
add_overlay(facial_s)
if(h_style)
- if(!owner.isSynthetic() || (owner.isSynthetic() && ((owner.head && (owner.head.flags & BLOCKHEADHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHEADHAIR)))))
+ if(!ismachineperson(owner) || (ismachineperson(owner) && ((owner.head && (owner.head.flags & BLOCKHEADHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHEADHAIR)))))
var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style]
if(hair_style && ((dna.species.name in hair_style.species_allowed) || (dna.species.bodyflags & ALL_RPARTS)))
var/icon/hair_s = new /icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s")
diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm
new file mode 100644
index 00000000000..840f0cd267c
--- /dev/null
+++ b/code/modules/tgui/external.dm
@@ -0,0 +1,155 @@
+/**
+ * tgui external
+ *
+ * Contains all external tgui declarations.
+ */
+
+/**
+ * public
+ *
+ * Used to open and update UIs.
+ * If this proc is not implemented properly, the UI will not update correctly.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional ui_key string The ui_key of the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ * optional master_ui datum/tgui The parent UI.
+ * optional state datum/ui_state The state used to determine status.
+ */
+
+/datum/proc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ return FALSE // Not implemented.
+
+/**
+ * public
+ *
+ * Data to be sent to the UI.
+ * This must be implemented for a UI to work.
+ *
+ * required user mob The mob interacting with the UI.
+ *
+ * return list Data to be sent to the UI.
+ */
+/datum/proc/tgui_data(mob/user)
+ return list() // Not implemented.
+
+/**
+ * public
+ *
+ * Static Data to be sent to the UI.
+ * Static data differs from normal data in that it's large data that should be sent infrequently
+ * This is implemented optionally for heavy uis that would be sending a lot of redundant data
+ * frequently.
+ * Gets squished into one object on the frontend side, but the static part is cached.
+ *
+ * required user mob The mob interacting with the UI.
+ *
+ * return list Statuic Data to be sent to the UI.
+ */
+/datum/proc/tgui_static_data(mob/user)
+ return list()
+
+/**
+ * public
+ *
+ * Forces an update on static data. Should be done manually whenever something happens to change static data.
+ *
+ * required user the mob currently interacting with the ui
+ * optional ui ui to be updated
+ * optional ui_key ui key of ui to be updated
+ */
+/datum/proc/update_tgui_static_data(mob/user, datum/tgui/ui, ui_key = "main")
+ ui = SStgui.try_update_ui(user, src, ui_key, ui)
+ // If there was no ui to update, there's no static data to update either.
+ if(!ui)
+ return
+ ui.push_data(null, tgui_static_data(), TRUE)
+
+/**
+ * public
+ *
+ * Called on a UI when the UI receieves a href.
+ * Think of this as Topic().
+ *
+ * required action string The action/button that has been invoked by the user.
+ * required params list A list of parameters attached to the button.
+ *
+ * return bool If the UI should be updated or not.
+ */
+/datum/proc/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ // If UI is not interactive or usr calling Topic is not the UI user, bail.
+ if(!ui || ui.status != STATUS_INTERACTIVE)
+ return TRUE
+
+/**
+ * public
+ *
+ * Called on an object when a tgui object is being created, allowing you to
+ * customise the html
+ * For example: inserting a custom stylesheet that you need in the head
+ *
+ * For this purpose, some tags are available in the html, to be parsed out
+ ^ with replacetext
+ * (customheadhtml) - Additions to the head tag
+ *
+ * required html the html base text
+ */
+/datum/proc/tgui_base_html(html)
+ return html
+
+/**
+ * private
+ *
+ * The UI's host object (usually src_object).
+ * This allows modules/datums to have the UI attached to them,
+ * and be a part of another object.
+ */
+/datum/proc/tgui_host(mob/user)
+ return src // Default src.
+
+/**
+ * global
+ *
+ * Associative list of JSON-encoded shared states that were set by
+ * tgui clients.
+ */
+
+/datum/var/list/tgui_shared_states
+
+/**
+ * global
+ *
+ * Used to track UIs for a mob.
+ */
+/mob/var/list/open_tguis = list()
+/**
+ * public
+ *
+ * Called on a UI's object when the UI is closed, not to be confused with
+ * client/verb/uiclose(), which closes the ui window
+ */
+/datum/proc/tgui_close(mob/user)
+
+/**
+ * verb
+ *
+ * Called by UIs when they are closed.
+ * Must be a verb so winset() can call it.
+ *
+ * required uiref ref The UI that was closed.
+ */
+/client/verb/tguiclose(uid as text)
+ // Name the verb, and hide it from the user panel.
+ set name = "uiclose"
+ set hidden = TRUE
+
+ // Get the UI based on the ref.
+ var/datum/tgui/ui = locateUID(uid)
+
+ // If we found the UI, close it.
+ if(istype(ui))
+ ui.close()
+ // Unset machine just to be sure.
+ if(src && src.mob)
+ src.mob.unset_machine()
diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm
new file mode 100644
index 00000000000..36be6295710
--- /dev/null
+++ b/code/modules/tgui/modules/_base.dm
@@ -0,0 +1,21 @@
+/*
+TGUI MODULES
+
+This allows for datum-based TGUIs that can be hooked into objects.
+This is useful for things such as the power monitor, which needs to exist on a physical console in the world, but also as a virtual device the AI can use
+
+Code is pretty much ripped verbatim from nano modules, but with un-needed stuff removed
+*/
+/datum/tgui_module
+ var/name
+ var/datum/host
+
+/datum/tgui_module/New(var/host)
+ src.host = host
+
+/datum/tgui_module/tgui_host()
+ return host ? host : src
+
+/datum/tgui_module/tgui_close(mob/user)
+ if(host)
+ host.tgui_close(user)
diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm
new file mode 100644
index 00000000000..783498542e4
--- /dev/null
+++ b/code/modules/tgui/modules/crew_monitor.dm
@@ -0,0 +1,39 @@
+/datum/tgui_module/crew_monitor
+ name = "Crew monitor"
+
+/datum/tgui_module/crew_monitor/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ var/turf/T = get_turf(tgui_host())
+ if(!T || !is_level_reachable(T.z))
+ to_chat(usr, "Unable to establish a connection: You're too far away from the station!")
+ return FALSE
+
+ switch(action)
+ if("track")
+ if(isAI(usr))
+ var/mob/living/silicon/ai/AI = usr
+ var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.mob_list
+ if(hassensorlevel(H, SUIT_SENSOR_TRACKING))
+ AI.ai_actual_track(H)
+ return TRUE
+
+
+/datum/tgui_module/crew_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ // The 557 may seem random, but its the perfectsize for margins on the nanomap
+ ui = new(user, src, ui_key, "CrewMonitor", name, 1400, 557, master_ui, state)
+ ui.autoupdate = TRUE
+ ui.open()
+
+
+/datum/tgui_module/crew_monitor/tgui_data(mob/user)
+ var/data[0]
+ var/turf/T = get_turf(tgui_host())
+
+ data["isAI"] = isAI(user)
+ data["crewmembers"] = GLOB.crew_repository.health_data(T)
+
+ return data
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
new file mode 100644
index 00000000000..81152426d06
--- /dev/null
+++ b/code/modules/tgui/states.dm
@@ -0,0 +1,117 @@
+/**
+ * tgui states
+ *
+ * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks.
+ */
+
+/**
+ * public
+ *
+ * Checks the UI state for a mob.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required state datum/ui_state The state to check.
+ *
+ * return UI_state The state of the UI.
+ */
+/datum/proc/tgui_status(mob/user, datum/tgui_state/state)
+ var/src_object = tgui_host(user)
+ . = STATUS_CLOSE
+ if(!state)
+ return
+
+ if(isobserver(user))
+ // If they turn on ghost AI control, admins can always interact.
+ if(user.client.advanced_admin_interaction)
+ . = max(., STATUS_INTERACTIVE)
+
+ // Regular ghosts can always at least view if in range.
+ var/clientviewlist = getviewsize(user.client.view)
+ if(get_dist(src_object, user) < max(clientviewlist[1],clientviewlist[2]))
+ . = max(., STATUS_UPDATE)
+
+ // Check if the state allows interaction
+ var/result = state.can_use_topic(src_object, user)
+ . = max(., result)
+
+/**
+ * private
+ *
+ * Checks if a user can use src_object's UI, and returns the state.
+ * Can call a mob proc, which allows overrides for each mob.
+ *
+ * required src_object datum The object/datum which owns the UI.
+ * required user mob The mob who opened/is using the UI.
+ *
+ * return UI_state The state of the UI.
+ */
+/datum/tgui_state/proc/can_use_topic(src_object, mob/user)
+ return STATUS_CLOSE // Don't allow interaction by default.
+
+/**
+ * public
+ *
+ * Standard interaction/sanity checks. Different mob types may have overrides.
+ *
+ * return UI_state The state of the UI.
+ */
+/mob/proc/shared_tgui_interaction(src_object)
+ if(!client) // Close UIs if mindless.
+ return STATUS_CLOSE
+ else if(stat) // Disable UIs if unconcious.
+ return STATUS_DISABLED
+ else if(incapacitated()) // Update UIs if incapicitated but concious.
+ return STATUS_UPDATE
+ return STATUS_INTERACTIVE
+
+/mob/living/silicon/ai/shared_tgui_interaction(src_object)
+ if(lacks_power()) // Disable UIs if the AI is unpowered.
+ return STATUS_DISABLED
+ return ..()
+
+/mob/living/silicon/robot/shared_tgui_interaction(src_object)
+ if(!cell || cell.charge <= 0 || lockcharge) // Disable UIs if the Borg is unpowered or locked.
+ return STATUS_DISABLED
+ return ..()
+
+/**
+ * public
+ *
+ * Check the distance for a living mob.
+ * Really only used for checks outside the context of a mob.
+ * Otherwise, use shared_living_ui_distance().
+ *
+ * required src_object The object which owns the UI.
+ * required user mob The mob who opened/is using the UI.
+ *
+ * return UI_state The state of the UI.
+ */
+/atom/proc/contents_tgui_distance(src_object, mob/living/user)
+ return user.shared_living_tgui_distance(src_object) // Just call this mob's check.
+
+/**
+ * public
+ *
+ * Distance versus interaction check.
+ *
+ * required src_object atom/movable The object which owns the UI.
+ *
+ * return UI_state The state of the UI.
+ */
+/mob/living/proc/shared_living_tgui_distance(atom/movable/src_object, viewcheck = TRUE)
+ if(viewcheck && !(src_object in view(src))) // If the object is obscured, close it.
+ return STATUS_CLOSE
+
+ var/dist = get_dist(src_object, src)
+ if(dist <= 1) // Open and interact if 1-0 tiles away.
+ return STATUS_INTERACTIVE
+ else if(dist <= 2) // View only if 2-3 tiles away.
+ return STATUS_UPDATE
+ else if(dist <= 5) // Disable if 5 tiles away.
+ return STATUS_DISABLED
+ return STATUS_CLOSE // Otherwise, we got nothing.
+
+/mob/living/carbon/human/shared_living_tgui_distance(atom/movable/src_object)
+ if((TK in mutations) && (get_dist(src, src_object) <= 2))
+ return STATUS_INTERACTIVE
+ return ..()
diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm
new file mode 100644
index 00000000000..6d1c680927a
--- /dev/null
+++ b/code/modules/tgui/states/admin.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: admin_state
+ *
+ * Checks that the user is an admin, end-of-story.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_admin_state, /datum/tgui_state/admin_state, new)
+
+/datum/tgui_state/admin_state/can_use_topic(src_object, mob/user)
+ if(check_rights_for(user.client, R_ADMIN))
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
diff --git a/code/modules/tgui/states/always.dm b/code/modules/tgui/states/always.dm
new file mode 100644
index 00000000000..30915785442
--- /dev/null
+++ b/code/modules/tgui/states/always.dm
@@ -0,0 +1,11 @@
+
+ /**
+ * tgui state: always_state
+ *
+ * Always grants the user UI_INTERACTIVE. Period.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_always_state, /datum/tgui_state/always_state, new)
+
+/datum/tgui_state/always_state/can_use_topic(src_object, mob/user)
+ return STATUS_INTERACTIVE
diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm
new file mode 100644
index 00000000000..6bc0c7ec031
--- /dev/null
+++ b/code/modules/tgui/states/conscious.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: conscious_state
+ *
+ * Only checks if the user is conscious.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_conscious_state, /datum/tgui_state/conscious_state, new)
+
+/datum/tgui_state/conscious_state/can_use_topic(src_object, mob/user)
+ if(user.stat == CONSCIOUS)
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
diff --git a/code/modules/tgui/states/contained.dm b/code/modules/tgui/states/contained.dm
new file mode 100644
index 00000000000..c2fbd0b6b06
--- /dev/null
+++ b/code/modules/tgui/states/contained.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: contained_state
+ *
+ * Checks that the user is inside the src_object.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_contained_state, /datum/tgui_state/contained_state, new)
+
+/datum/tgui_state/contained_state/can_use_topic(atom/src_object, mob/user)
+ if(!src_object.contains(user))
+ return STATUS_CLOSE
+ return user.shared_tgui_interaction(src_object)
diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm
new file mode 100644
index 00000000000..137f262a0ea
--- /dev/null
+++ b/code/modules/tgui/states/deep_inventory.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: deep_inventory_state
+ *
+ * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_deep_inventory_state, /datum/tgui_state/deep_inventory_state, new)
+
+/datum/tgui_state/deep_inventory_state/can_use_topic(src_object, mob/user)
+ if(!user.contains(src_object))
+ return STATUS_CLOSE
+ return user.shared_tgui_interaction(src_object)
diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm
new file mode 100644
index 00000000000..4da729aa7e5
--- /dev/null
+++ b/code/modules/tgui/states/default.dm
@@ -0,0 +1,63 @@
+ /**
+ * tgui state: default_state
+ *
+ * Checks a number of things -- mostly physical distance for humans and view for robots.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
+
+/datum/tgui_state/default/can_use_topic(src_object, mob/user)
+ return user.default_can_use_tgui_topic(src_object) // Call the individual mob-overridden procs.
+
+/mob/proc/default_can_use_tgui_topic(src_object)
+ return STATUS_CLOSE // Don't allow interaction by default.
+
+/mob/living/default_can_use_tgui_topic(src_object)
+ . = shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE && loc)
+ . = min(., loc.contents_tgui_distance(src_object, src)) // Check the distance...
+ if(. == STATUS_INTERACTIVE) // Non-human living mobs can only look, not touch.
+ return STATUS_UPDATE
+
+/mob/living/carbon/human/default_can_use_tgui_topic(src_object)
+ . = shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE)
+ . = min(., shared_living_tgui_distance(src_object)) // Check the distance...
+
+/mob/living/silicon/robot/default_can_use_tgui_topic(src_object)
+ . = shared_tgui_interaction(src_object)
+ if(. <= STATUS_DISABLED)
+ return
+
+ // Robots can interact with anything they can see.
+ var/list/clientviewlist = getviewsize(client.view)
+ if((src_object in view(src)) && (get_dist(src, src_object) <= min(clientviewlist[1],clientviewlist[2])))
+ return STATUS_INTERACTIVE
+ 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)
+ 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))
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
+
+/mob/living/simple_animal/default_can_use_tgui_topic(src_object)
+ . = shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE)
+ . = min(., shared_living_tgui_distance(src_object)) //simple animals can only use things they're near.
+
+/mob/living/silicon/pai/default_can_use_tgui_topic(src_object)
+ // pAIs can only use themselves and the owner's radio.
+ if((src_object == src || src_object == radio) && !stat)
+ return STATUS_INTERACTIVE
+ else
+ return ..()
+
+/mob/dead/observer/default_can_use_tgui_topic()
+ if(can_admin_interact())
+ return STATUS_INTERACTIVE // Admins are more equal
+ return STATUS_UPDATE // Ghosts can view updates
diff --git a/code/modules/tgui/states/hands.dm b/code/modules/tgui/states/hands.dm
new file mode 100644
index 00000000000..6dc1e2fc1a4
--- /dev/null
+++ b/code/modules/tgui/states/hands.dm
@@ -0,0 +1,25 @@
+ /**
+ * tgui state: hands_state
+ *
+ * Checks that the src_object is in the user's hands.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_hands_state, /datum/tgui_state/hands_state, new)
+
+/datum/tgui_state/hands_state/can_use_topic(src_object, mob/user)
+ . = user.shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE)
+ return min(., user.hands_can_use_tgui_topic(src_object))
+
+/mob/proc/hands_can_use_tgui_topic(src_object)
+ return STATUS_CLOSE
+
+/mob/living/hands_can_use_tgui_topic(src_object)
+ if(is_in_active_hand(src_object) || is_in_inactive_hand(src_object))
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
+
+/mob/living/silicon/robot/hands_can_use_tgui_topic(src_object)
+ if(activated(src_object))
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
diff --git a/code/modules/tgui/states/human_adjacent.dm b/code/modules/tgui/states/human_adjacent.dm
new file mode 100644
index 00000000000..8164d5f9cee
--- /dev/null
+++ b/code/modules/tgui/states/human_adjacent.dm
@@ -0,0 +1,17 @@
+
+ /**
+ * tgui state: human_adjacent_state
+ *
+ * In addition to default checks, only allows interaction for a
+ * human adjacent user.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_human_adjacent_state, /datum/tgui_state/human_adjacent_state, new)
+
+/datum/tgui_state/human_adjacent_state/can_use_topic(src_object, mob/user)
+ . = user.default_can_use_tgui_topic(src_object)
+
+ var/dist = get_dist(src_object, user)
+ if((dist > 1) || (!ishuman(user)))
+ // Can't be used unless adjacent and human, even with TK
+ . = min(., STATUS_UPDATE)
diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm
new file mode 100644
index 00000000000..92274cc1f2e
--- /dev/null
+++ b/code/modules/tgui/states/inventory.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: inventory_state
+ *
+ * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_inventory_state, /datum/tgui_state/inventory_state, new)
+
+/datum/tgui_state/inventory_state/can_use_topic(src_object, mob/user)
+ if(!(src_object in user))
+ return STATUS_CLOSE
+ return user.shared_tgui_interaction(src_object)
diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm
new file mode 100644
index 00000000000..1185b516478
--- /dev/null
+++ b/code/modules/tgui/states/not_incapacitated.dm
@@ -0,0 +1,29 @@
+ /**
+ * tgui state: not_incapacitated_state
+ *
+ * Checks that the user isn't incapacitated
+ **/
+
+GLOBAL_DATUM_INIT(tgui_not_incapacitated_state, /datum/tgui_state/not_incapacitated_state, new)
+
+ /**
+ * tgui state: not_incapacitated_turf_state
+ *
+ * Checks that the user isn't incapacitated and that their loc is a turf
+ **/
+
+GLOBAL_DATUM_INIT(tgui_not_incapacitated_turf_state, /datum/tgui_state/not_incapacitated_state, new(no_turfs = TRUE))
+
+/datum/tgui_state/not_incapacitated_state
+ var/turf_check = FALSE
+
+/datum/tgui_state/not_incapacitated_state/New(loc, no_turfs = FALSE)
+ ..()
+ turf_check = no_turfs
+
+/datum/tgui_state/not_incapacitated_state/can_use_topic(src_object, mob/user)
+ if(user.stat)
+ return STATUS_CLOSE
+ if(user.incapacitated() || (turf_check && !isturf(user.loc)))
+ return STATUS_DISABLED
+ return STATUS_INTERACTIVE
diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm
new file mode 100644
index 00000000000..01811d427f2
--- /dev/null
+++ b/code/modules/tgui/states/notcontained.dm
@@ -0,0 +1,26 @@
+ /**
+ * tgui state: notcontained_state
+ *
+ * Checks that the user is not inside src_object, and then makes the default checks.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_notcontained_state, /datum/tgui_state/notcontained_state, new)
+
+/datum/tgui_state/notcontained_state/can_use_topic(atom/src_object, mob/user)
+ . = user.shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE)
+ return min(., user.notcontained_can_use_tgui_topic(src_object))
+
+/mob/proc/notcontained_can_use_tgui_topic(src_object)
+ return STATUS_CLOSE
+
+/mob/living/notcontained_can_use_tgui_topic(atom/src_object)
+ if(src_object.contains(src))
+ return STATUS_CLOSE // Close if we're inside it.
+ return default_can_use_tgui_topic(src_object)
+
+/mob/living/silicon/notcontained_can_use_tgui_topic(src_object)
+ return default_can_use_tgui_topic(src_object) // Silicons use default bevhavior.
+
+/mob/living/simple_animal/drone/notcontained_can_use_tgui_topic(src_object)
+ return default_can_use_tgui_topic(src_object) // Drones use default bevhavior.
diff --git a/code/modules/tgui/states/observer.dm b/code/modules/tgui/states/observer.dm
new file mode 100644
index 00000000000..bf98f444654
--- /dev/null
+++ b/code/modules/tgui/states/observer.dm
@@ -0,0 +1,15 @@
+ /**
+ * tgui state: observer_state
+ *
+ * Checks that the user is an observer/ghost.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_observer_state, /datum/tgui_state/observer_state, new)
+
+/datum/tgui_state/observer_state/can_use_topic(src_object, mob/user)
+ if(isobserver(user))
+ return STATUS_INTERACTIVE
+ if(check_rights(R_ADMIN, 0, src))
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
+
diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm
new file mode 100644
index 00000000000..a57a321e958
--- /dev/null
+++ b/code/modules/tgui/states/physical.dm
@@ -0,0 +1,24 @@
+ /**
+ * tgui state: physical_state
+ *
+ * Short-circuits the default state to only check physical distance.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_physical_state, /datum/tgui_state/physical, new)
+
+/datum/tgui_state/physical/can_use_topic(src_object, mob/user)
+ . = user.shared_tgui_interaction(src_object)
+ if(. > STATUS_CLOSE)
+ return min(., user.physical_can_use_tgui_topic(src_object))
+
+/mob/proc/physical_can_use_tgui_topic(src_object)
+ return STATUS_CLOSE
+
+/mob/living/physical_can_use_tgui_topic(src_object)
+ return shared_living_tgui_distance(src_object)
+
+/mob/living/silicon/physical_can_use_tgui_topic(src_object)
+ return max(STATUS_UPDATE, shared_living_tgui_distance(src_object)) // Silicons can always see.
+
+/mob/living/silicon/ai/physical_can_use_tgui_topic(src_object)
+ return STATUS_UPDATE // AIs are not physical.
diff --git a/code/modules/tgui/states/self.dm b/code/modules/tgui/states/self.dm
new file mode 100644
index 00000000000..109fd6ae440
--- /dev/null
+++ b/code/modules/tgui/states/self.dm
@@ -0,0 +1,12 @@
+ /**
+ * tgui state: self_state
+ *
+ * Only checks that the user and src_object are the same.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_self_state, /datum/tgui_state/self_state, new)
+
+/datum/tgui_state/self_state/can_use_topic(src_object, mob/user)
+ if(src_object != user)
+ return STATUS_CLOSE
+ return user.shared_tgui_interaction(src_object)
diff --git a/code/modules/tgui/states/zlevel.dm b/code/modules/tgui/states/zlevel.dm
new file mode 100644
index 00000000000..a589a4b64d7
--- /dev/null
+++ b/code/modules/tgui/states/zlevel.dm
@@ -0,0 +1,14 @@
+ /**
+ * tgui state: z_state
+ *
+ * Only checks that the Z-level of the user and src_object are the same.
+ **/
+
+GLOBAL_DATUM_INIT(tgui_z_state, /datum/tgui_state/z_state, new)
+
+/datum/tgui_state/z_state/can_use_topic(src_object, mob/user)
+ var/turf/turf_obj = get_turf(src_object)
+ var/turf/turf_usr = get_turf(user)
+ if(turf_obj && turf_usr && turf_obj.z == turf_usr.z)
+ return STATUS_INTERACTIVE
+ return STATUS_CLOSE
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
new file mode 100644
index 00000000000..5157d0c9fed
--- /dev/null
+++ b/code/modules/tgui/tgui.dm
@@ -0,0 +1,363 @@
+/**
+ * tgui
+ *
+ * /tg/station user interface library
+ */
+
+/**
+ * tgui datum (represents a UI).
+ */
+/datum/tgui
+ /// The mob who opened/is using the UI.
+ var/mob/user
+ /// The object which owns the UI.
+ var/datum/src_object
+ /// The title of te UI.
+ var/title
+ /// The ui_key of the UI. This allows multiple UIs for one src_object.
+ var/ui_key
+ /// The window_id for browse() and onclose().
+ var/window_id
+ /// The window width.
+ var/width = 0
+ /// The window height
+ var/height = 0
+ /// The interface (template) to be used for this UI.
+ var/interface
+ /// Update the UI every MC tick.
+ var/autoupdate = TRUE
+ /// If the UI has been initialized yet.
+ var/initialized = FALSE
+ /// The data (and datastructure) used to initialize the UI.
+ var/list/initial_data
+ /// The static data used to initialize the UI.
+ var/list/initial_static_data
+ /// Holder for the json string, that is sent during the initial update
+ var/_initial_update
+ /// The status/visibility of the UI.
+ var/status = STATUS_INTERACTIVE
+ /// Topic state used to determine status/interactability.
+ var/datum/tgui_state/state = null
+ /// The parent UI.
+ var/datum/tgui/master_ui
+ /// Children of this UI.
+ var/list/datum/tgui/children = list()
+
+/**
+ * public
+ *
+ * Create a new UI.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object or datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ * required interface string The interface used to render the UI.
+ * optional title string The title of the UI.
+ * optional width int The window width.
+ * optional height int The window height.
+ * optional master_ui datum/tgui The parent UI.
+ * optional state datum/ui_state The state used to determine status.
+ *
+ * return datum/tgui The requested UI.
+ */
+/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ src.user = user
+ src.src_object = src_object
+ src.ui_key = ui_key
+ src.window_id = "[src_object.UID()]-[ui_key]"
+ src.interface = interface
+
+ if(title)
+ src.title = sanitize(title)
+ if(width)
+ src.width = width
+ if(height)
+ src.height = height
+
+ src.master_ui = master_ui
+ if(master_ui)
+ master_ui.children += src
+ src.state = state
+
+ var/datum/asset/tgui_assets = get_asset_datum(/datum/asset/simple/tgui)
+ var/datum/asset/fa = get_asset_datum(/datum/asset/simple/fontawesome)
+ tgui_assets.send(user)
+ fa.send(user)
+
+/**
+ * public
+ *
+ * Open this UI (and initialize it with data).
+ */
+/datum/tgui/proc/open()
+ if(!user.client)
+ return // Bail if there is no client.
+
+ update_status(push = FALSE) // Update the window status.
+ if(status < STATUS_UPDATE)
+ return // Bail if we're not supposed to open.
+
+
+
+ // Build window options
+ var/window_options = "can_minimize=0;auto_format=0;"
+ // If we have a width and height, use them.
+ if(width && height)
+ window_options += "size=[width]x[height];"
+
+ // Remove titlebar and resize handles for a fancy window
+ if(user.client.prefs.nanoui_fancy)
+ window_options += "titlebar=0;can_resize=0;"
+ else
+ window_options += "titlebar=1;can_resize=1;"
+
+ // Generate page html
+ var/html
+ html = SStgui.basehtml
+ // Allow the src object to override the html if needed
+ html = src_object.tgui_base_html(html)
+ // Replace template tokens with important UI data
+ html = replacetextEx(html, "\[tgui:ref]", "[src.UID()]")
+
+ // Open the window.
+ user << browse(html, "window=[window_id];[window_options]")
+
+ // Instruct the client to signal UI when the window is closed.
+ // NOTE: Intentional \ref usage; tgui datums can't/shouldn't
+ // be tagged, so this is an effective unwrap
+ winset(user, window_id, "on-close=\"uiclose [UID()]\"")
+
+ // Pre-fetch initial state while browser is still loading in
+ // another thread
+ if(!initial_data)
+ initial_data = src_object.tgui_data(user)
+ if(!initial_static_data)
+ initial_static_data = src_object.tgui_static_data(user)
+ _initial_update = url_encode(get_json(initial_data, initial_static_data))
+
+ SStgui.on_open(src)
+
+/**
+ * public
+ *
+ * Reinitialize the UI.
+ * (Possibly with a new interface and/or data).
+ *
+ * optional template string The name of the new interface.
+ * optional data list The new initial data.
+ */
+/datum/tgui/proc/reinitialize(interface, list/data, list/static_data)
+ if(interface)
+ src.interface = interface
+ if(data)
+ initial_data = data
+ if(static_data)
+ initial_static_data = static_data
+ open()
+
+/**
+ * public
+ *
+ * Close the UI, and all its children.
+ */
+/datum/tgui/proc/close()
+ user << browse(null, "window=[window_id]") // Close the window.
+ src_object.tgui_close(user)
+ SStgui.on_close(src)
+ for(var/datum/tgui/child in children) // Loop through and close all children.
+ child.close()
+ children.Cut()
+ state = null
+ master_ui = null
+ qdel(src)
+
+/**
+ * public
+ *
+ * Enable/disable auto-updating of the UI.
+ *
+ * required state bool Enable/disable auto-updating.
+ */
+/datum/tgui/proc/set_autoupdate(state = TRUE)
+ autoupdate = state
+
+/**
+ * private
+ *
+ * Package the data to send to the UI, as JSON.
+ * This includes the UI data and config_data.
+ *
+ * return string The packaged JSON.
+ */
+/datum/tgui/proc/get_json(list/data, list/static_data)
+ var/list/json_data = list()
+
+ json_data["config"] = list(
+ "title" = title,
+ "status" = status,
+ "interface" = interface,
+ "fancy" = user.client.prefs.nanoui_fancy,
+ "observer" = isobserver(user),
+ "window" = window_id,
+ "map" = (GLOB.using_map && GLOB.using_map.name) ? GLOB.using_map.name : "Unknown",
+
+ "ref" = "[src.UID()]"
+ )
+
+ if(!isnull(data))
+ json_data["data"] = data
+ if(!isnull(static_data))
+ json_data["static_data"] = static_data
+
+ // Send shared states
+ if(src_object.tgui_shared_states)
+ json_data["shared"] = src_object.tgui_shared_states
+
+ // Generate the JSON.
+ var/json = json_encode(json_data)
+ // Strip #255/improper.
+ json = replacetext(json, "\proper", "")
+ json = replacetext(json, "\improper", "")
+ return json
+
+/**
+ * private
+ *
+ * Handle clicks from the UI.
+ * Call the src_object's ui_act() if status is UI_INTERACTIVE.
+ * If the src_object's ui_act() returns 1, update all UIs attacked to it.
+ */
+/datum/tgui/Topic(href, href_list)
+ if(user != usr)
+ return // Something is not right here.
+
+ var/action = href_list["action"]
+ var/params = href_list; params -= "action"
+
+ switch(action)
+ if("tgui:initialize")
+ user << output(_initial_update, "[window_id].browser:update")
+ initialized = TRUE
+ if("tgui:setSharedState")
+ // Update the window state.
+ update_status(push = FALSE)
+ // Bail if UI is not interactive or usr calling Topic
+ // is not the UI user.
+ if(status != STATUS_INTERACTIVE)
+ return
+ var/key = params["key"]
+ var/value = params["value"]
+ if(!src_object.tgui_shared_states)
+ src_object.tgui_shared_states = list()
+ src_object.tgui_shared_states[key] = value
+ SStgui.update_uis(src_object)
+ if("tgui:setFancy")
+ var/value = text2num(params["value"])
+ user.client.prefs.nanoui_fancy = value
+ if("tgui:log")
+ // Force window to show frills on fatal errors
+ if(params["fatal"])
+ winset(user, window_id, "titlebar=1;can-resize=1;size=600x600")
+ log_message(params["log"])
+ if("tgui:link")
+ user << link(params["url"])
+
+ else
+ // Update the window state.
+ update_status(push = FALSE)
+ // Call tgui_act() on the src_object.
+ if(src_object.tgui_act(action, params, src, state))
+ // Update if the object requested it.
+ SStgui.update_uis(src_object)
+
+/**
+ * private
+ *
+ * Update the UI.
+ * Only updates the data if update is true, otherwise only updates the status.
+ *
+ * optional force bool If the UI should be forced to update.
+ */
+/datum/tgui/process(force = FALSE)
+ var/datum/host = src_object.tgui_host(user)
+ if(!src_object || !host || !user) // If the object or user died (or something else), abort.
+ close()
+ return
+
+ if(status && (force || autoupdate))
+ update() // Update the UI if the status and update settings allow it.
+ else
+ update_status(push = TRUE) // Otherwise only update status.
+
+/**
+ * private
+ *
+ * Push data to an already open UI.
+ *
+ * required data list The data to send.
+ * optional force bool If the update should be sent regardless of state.
+ */
+/datum/tgui/proc/push_data(data, static_data, force = FALSE)
+ // Update the window state.
+ update_status(push = FALSE)
+ // Cannot update UI if it is not set up yet.
+ if(!initialized)
+ return
+ // Cannot update UI, we have no visibility.
+ if(status <= STATUS_DISABLED && !force)
+ return
+ // Send the new JSON to the update() Javascript function.
+ user << output(
+ url_encode(get_json(data, static_data)),
+ "[window_id].browser:update")
+
+/**
+ * private
+ *
+ * Updates the UI by interacting with the src_object again, which will hopefully
+ * call try_ui_update on it.
+ *
+ * optional force_open bool If force_open should be passed to ui_interact.
+ */
+/datum/tgui/proc/update(force_open = FALSE)
+ src_object.tgui_interact(user, ui_key, src, force_open, master_ui, state)
+
+/**
+ * private
+ *
+ * Update the status/visibility of the UI for its user.
+ *
+ * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
+ */
+/datum/tgui/proc/update_status(push = FALSE)
+ var/status = src_object.tgui_status(user, state)
+ if(master_ui)
+ status = min(status, master_ui.status)
+
+ set_status(status, push)
+ if(status == STATUS_CLOSE)
+ close()
+
+/**
+ * private
+ *
+ * Set the status/visibility of the UI.
+ *
+ * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE).
+ * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
+ */
+/datum/tgui/proc/set_status(status, push = FALSE)
+ // Only update if status has changed.
+ if(src.status != status)
+ if(src.status == STATUS_DISABLED)
+ src.status = status
+ if(push)
+ update()
+ else
+ src.status = status
+ // Update if the UI just because disabled, or a push is requested.
+ if(status == STATUS_DISABLED || push)
+ push_data(null, force = TRUE)
+
+/datum/tgui/proc/log_message(message)
+ log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]")
diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm
deleted file mode 100644
index 0f0f85b088b..00000000000
--- a/code/modules/zombie/items.dm
+++ /dev/null
@@ -1,69 +0,0 @@
-/obj/item/zombie_hand
- name = "zombie claw"
- desc = "A zombie's claw is its primary tool, capable of infecting \
- unconscious or dead humans, butchering all other living things to \
- sustain the zombie, forcing open airlock doors and opening \
- child-safe caps on bottles."
- flags = NODROP|ABSTRACT|DROPDEL
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- icon = 'icons/effects/blood.dmi'
- icon_state = "bloodhand_left"
- var/icon_left = "bloodhand_left"
- var/icon_right = "bloodhand_right"
- hitsound = 'sound/hallucinations/growl1.ogg'
- force = 21 // Just enough to break airlocks with melee attacks
- damtype = "brute"
-
-/obj/item/zombie_hand/equipped(mob/user, slot)
- . = ..()
- switch(slot)
- // Yes, these intentionally don't match
- if(slot_l_hand)
- icon_state = icon_right
- if(slot_r_hand)
- icon_state = icon_left
-
-/obj/item/zombie_hand/afterattack(atom/target, mob/user, proximity_flag)
- . = ..()
- if(!proximity_flag)
- return
-
- else if(isliving(target))
- if(ishuman(target))
- try_to_zombie_infect(target)
- else
- check_feast(target, user)
-
-/proc/try_to_zombie_infect(mob/living/carbon/human/target)
- CHECK_DNA_AND_SPECIES(target)
-
- if(NOZOMBIE in target.dna.species.species_traits)
- // cannot infect any NOZOMBIE subspecies (such as high functioning
- // zombies)
- return
-
- var/obj/item/organ/internal/zombie_infection/infection
- infection = target.get_organ_slot("zombie_infection")
- if(!infection)
- infection = new()
- infection.insert(target)
-
-/obj/item/zombie_hand/proc/check_feast(mob/living/target, mob/living/user)
- if(target.stat == DEAD)
- var/hp_gained = target.maxHealth
- target.gib()
- user.adjustBruteLoss(-hp_gained, FALSE)
- user.adjustToxLoss(-hp_gained, FALSE)
- user.adjustFireLoss(-hp_gained, FALSE)
- user.adjustCloneLoss(-hp_gained, FALSE)
- user.adjustBrainLoss(-hp_gained, FALSE) // Zom Bee gibbers "BRAAAAISNSs!1!"
- user.updatehealth()
-
-/obj/item/zombie_hand/suicide_act(mob/living/carbon/human/user)
- user.visible_message("[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!")
- if(ishuman(user))
- var/mob/living/carbon/human/L = user
- var/obj/item/organ/external/O = L.get_organ("head")
- if(O)
- O.droplimb()
- return (BRUTELOSS)
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
deleted file mode 100644
index 6ea40c90e96..00000000000
--- a/code/modules/zombie/organs.dm
+++ /dev/null
@@ -1,99 +0,0 @@
-/obj/item/organ/internal/zombie_infection
- name = "festering ooze"
- desc = "A black web of pus and viscera."
- parent_organ = "head"
- slot = "zombie_infection"
- icon_state = "blacktumor"
- var/causes_damage = TRUE
- var/datum/species/old_species = /datum/species/human
- var/living_transformation_time = 30
- var/converts_living = FALSE
-
- var/revive_time_min = 450
- var/revive_time_max = 700
- var/timer_id
-
-/obj/item/organ/internal/zombie_infection/New(mob/living/carbon/holder)
- ..()
- GLOB.zombie_infection_list += src
-
-/obj/item/organ/internal/zombie_infection/Destroy()
- GLOB.zombie_infection_list -= src
- . = ..()
-
-/obj/item/organ/internal/zombie_infection/insert(mob/living/carbon/human/M, special = 0)
- ..()
- START_PROCESSING(SSobj, src)
-
-/obj/item/organ/internal/zombie_infection/remove(mob/living/carbon/human/M, special = 0)
- STOP_PROCESSING(SSobj, src)
- if(iszombie(M) && old_species)
- M.set_species(old_species, retain_damage = TRUE)
- if(timer_id)
- deltimer(timer_id)
- . = ..()
-
-/obj/item/organ/internal/zombie_infection/on_find(mob/living/finder)
- to_chat(finder, "Inside the head is a disgusting black \
- web of pus and viscera, bound tightly around the brain like some \
- biological harness.")
-
-/obj/item/organ/internal/zombie_infection/process()
- if(!owner)
- return
- if(!(src in owner.internal_organs))
- remove(owner)
- if(causes_damage && !iszombie(owner) && owner.stat != DEAD)
- owner.adjustToxLoss(1)
- if (prob(10))
- to_chat(owner, "You feel sick...")
- if(timer_id)
- return
- if(owner.suiciding)
- return
- if(owner.stat != DEAD && !converts_living)
- return
- if(!owner.get_int_organ(/obj/item/organ/internal/brain))
- return
- if(!iszombie(owner))
- to_chat(owner, "You can feel your heart stopping, but something isn't right... \
- life has not abandoned your broken form. You can only feel a deep and immutable hunger that \
- not even death can stop, you will rise again!")
- var/revive_time = rand(revive_time_min, revive_time_max)
- var/flags = TIMER_STOPPABLE
- timer_id = addtimer(CALLBACK(src, .proc/zombify), revive_time, flags)
-
-/obj/item/organ/internal/zombie_infection/proc/zombify()
- timer_id = null
-
- if(!converts_living && owner.stat != DEAD)
- return
-
- if(!iszombie(owner))
- old_species = owner.dna.species.type
- owner.set_species(/datum/species/zombie/infectious)
- for(var/datum/disease/critical/crit in owner.viruses) // cure any new crit viruses
- crit.cure(0)
-
- var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS)
- //Fully heal the zombie's damage the first time they rise
- owner.setToxLoss(0)
- owner.setOxyLoss(0)
- owner.setBrainLoss(0)
- owner.setCloneLoss(0)
- owner.SetLoseBreath(0)
- owner.heal_overall_damage(INFINITY, INFINITY, TRUE, TRUE, FALSE)
- owner.setStaminaLoss(0)
-
- if(!owner.update_revive())
- return
-
- owner.grab_ghost()
- owner.visible_message("[owner] suddenly convulses, as [owner.p_they()][stand_up ? " stagger to [owner.p_their()] feet and" : ""] gain a ravenous hunger in [owner.p_their()] eyes!", "You HUNGER!")
- playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, TRUE)
- owner.do_jitter_animation(living_transformation_time)
- owner.Stun(living_transformation_time * 0.05)
- to_chat(owner, "You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.")
-
-/obj/item/organ/internal/zombie_infection/nodamage
- causes_damage = FALSE
diff --git a/html/font-awesome/README.MD b/html/font-awesome/README.MD
new file mode 100644
index 00000000000..7d693c36f03
--- /dev/null
+++ b/html/font-awesome/README.MD
@@ -0,0 +1,6 @@
+Due to the fact browse_rsc can't create subdirectories, every time you update font-awesome you'll need to change relative webfont references in all.min.css
+eg ../webfonts/fa-regular-400.ttf => fa-regular-400.ttf (or whatever you call it in asset datum)
+
+Second change is ripping out file types other than woff and eot(ie8) from the css
+
+Finally, removing brand related css.
\ No newline at end of file
diff --git a/html/font-awesome/css/all.min.css b/html/font-awesome/css/all.min.css
new file mode 100644
index 00000000000..e7cdfffe751
--- /dev/null
+++ b/html/font-awesome/css/all.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.9.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
diff --git a/html/font-awesome/css/v4-shims.min.css b/html/font-awesome/css/v4-shims.min.css
new file mode 100644
index 00000000000..5f3fdc598c8
--- /dev/null
+++ b/html/font-awesome/css/v4-shims.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.9.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
diff --git a/html/font-awesome/webfonts/fa-regular-400.eot b/html/font-awesome/webfonts/fa-regular-400.eot
new file mode 100644
index 00000000000..d62be2fad88
Binary files /dev/null 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
new file mode 100644
index 00000000000..43b1a9ae49d
Binary files /dev/null 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
new file mode 100644
index 00000000000..c77baa8d46a
Binary files /dev/null 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
new file mode 100644
index 00000000000..77c1786227f
Binary files /dev/null and b/html/font-awesome/webfonts/fa-solid-900.woff differ
diff --git a/icons/effects/hit_blips.dmi b/icons/effects/hit_blips.dmi
deleted file mode 100644
index 49bb109799b..00000000000
Binary files a/icons/effects/hit_blips.dmi and /dev/null differ
diff --git a/icons/mob/human_races/r_def_zombie.dmi b/icons/mob/human_races/r_def_zombie.dmi
deleted file mode 100644
index 007eb3f31f3..00000000000
Binary files a/icons/mob/human_races/r_def_zombie.dmi and /dev/null differ
diff --git a/icons/mob/human_races/r_zombie.dmi b/icons/mob/human_races/r_zombie.dmi
deleted file mode 100644
index 007eb3f31f3..00000000000
Binary files a/icons/mob/human_races/r_zombie.dmi and /dev/null differ
diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi
index eb59b0288b3..dcbce35fb27 100644
Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ
diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi
index 4b74a2891e3..961cffec7f6 100644
Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ
diff --git a/icons/mob/ties.dmi b/icons/mob/ties.dmi
index bbb986c088e..e399e5e3a8b 100644
Binary files a/icons/mob/ties.dmi and b/icons/mob/ties.dmi differ
diff --git a/icons/obj/clothing/ties.dmi b/icons/obj/clothing/ties.dmi
index 4799e9ad963..7675ec0d51c 100644
Binary files a/icons/obj/clothing/ties.dmi and b/icons/obj/clothing/ties.dmi differ
diff --git a/icons/obj/clothing/ties_overlay.dmi b/icons/obj/clothing/ties_overlay.dmi
index 975059bfeeb..fd3b6023185 100644
Binary files a/icons/obj/clothing/ties_overlay.dmi and b/icons/obj/clothing/ties_overlay.dmi differ
diff --git a/nano/templates/ame.tmpl b/nano/templates/ame.tmpl
deleted file mode 100644
index 4a86139eda1..00000000000
--- a/nano/templates/ame.tmpl
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
Status:
- {{if data.active}}
-
Injecting
- {{else}}
-
Standby
- {{/if}}
- {{:helper.link("Toggle Status", "gear", {"togglestatus":1},null)}}
-
-{{if !data.fueljar}}
- No fuel jar detected.
-{{else}}
-
-
-
Fuel:
-
{{:data.fueljar.fuel}}/{{:data.fueljar.fuel_max}}
- {{:helper.link("Eject", "eject", {"ejectjar":1},null,'floatRight')}}
-
-
-
Injecting:
-
- {{:helper.link(data.fueljar.injecting+" units", 'arrows-v', {'set_strength': 1})}}
-
-
-
-{{/if}}
-
-
-
Stability:
-
- {{:helper.displayBar(data.stability,0,100,"good")}}
- {{:data.stability}}%
-
-
-
-
Shielding Count:
-
{{:data.linked_shields}}
-
-
-
Cores:
-
{{:data.linked_cores}}
- {{:helper.link("Refresh Parts",null,{"refreshicons" : 1},(data.active?"disabled":null),'floatRight')}}
-
-
-
Current Efficiency:
-
{{:data.efficiency}}
-
-
-
Average Efficiency:
-
{{:data.stability}}
-
-
-
Last Produced:
-
{{:data.stored_power}}
-
-
\ No newline at end of file
diff --git a/nano/templates/botany_editor.tmpl b/nano/templates/botany_editor.tmpl
deleted file mode 100644
index 96cabc4341c..00000000000
--- a/nano/templates/botany_editor.tmpl
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-{{if data.activity}}
- Scanning...
-{{else}}
- Buffered Genetic Data
- {{if data.disk}}
-
-
- Source:
-
-
- {{:data.sourceName}}
-
-
- Gene decay:
-
-
- {{if data.degradation <= 100}}
- {{:data.degradation}}%
- {{else}}
- FURTHER AMENDMENTS NONVIABLE
- {{/if}}
-
-
- Locus:
-
-
- {{:data.locus}}
-
- {{:helper.link('Eject Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}}
-
- {{else}}
- No disk loaded.
- {{/if}}
- Loaded Material
- {{if data.loaded}}
-
-
- Target:
-
-
- {{:data.loaded}}
-
- {{if data.degradation <= 100}}
- {{:helper.link('Apply Gene Mods', 'gear', {'apply_gene' : 1}, null)}}
- {{/if}}
- {{:helper.link('Eject Target', 'arrow-circle-right', {'eject_packet' : 1}, null)}}
-
- {{else}}
- No target seed packet loaded
- {{/if}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/botany_isolator.tmpl b/nano/templates/botany_isolator.tmpl
deleted file mode 100644
index a5efbc4a212..00000000000
--- a/nano/templates/botany_isolator.tmpl
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-{{if data.activity}}
- Scanning...
-{{else}}
- Buffered Genetic Data
- {{if data.hasGenetics}}
-
-
- Source:
-
-
- {{:data.sourceName}}
-
-
- Gene decay:
-
-
- {{:data.degradation}}%
-
-
- {{if data.disk}}
- {{for data.geneMasks}}
-
-
- {{:value}}
-
-
- {{:helper.link('Extract', 'arrow-circle-down', {'get_gene' : value}, null)}}
-
-
- {{empty}}
- Data error. Genetic record corrupt.
- {{/for}}
-
-
- {{:helper.link('Eject Loaded Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}}
- {{:helper.link('Clear Genetic Buffer', 'gear', {'clear_buffer' : 1}, null)}}
-
- {{else}}
- No disk inserted.
- {{/if}}
- {{else}}
- No data buffered.
- {{if data.disk}}
-
-
- {{:helper.link('Eject Loaded Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}}
-
- {{else}}
- No disk inserted.
- {{/if}}
- {{/if}}
- Loaded Material
- {{if data.loaded}}
-
-
- Packet loaded:
-
-
- {{:data.loaded}}
-
-
- {{:helper.link('Process Genome', 'gear', {'scan_genome' : 1}, null)}}{{:helper.link('Eject Packet', 'arrow-circle-right', {'eject_packet' : 1}, null)}}
-
-
- {{else}}
- No seeds loaded.
- {{/if}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/code_mirror.tmpl b/nano/templates/code_mirror.tmpl
deleted file mode 100644
index dce1bf6f43b..00000000000
--- a/nano/templates/code_mirror.tmpl
+++ /dev/null
@@ -1,14 +0,0 @@
-HELLO
-
-
diff --git a/nano/templates/crew_monitor.tmpl b/nano/templates/crew_monitor.tmpl
deleted file mode 100644
index 15ad2168808..00000000000
--- a/nano/templates/crew_monitor.tmpl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-{{:helper.link('Show Tracker Map', 'thumb-tack', {'showMap' : 1})}}
-
- {{for data.crewmembers}}
- {{if value.sensor_type == 1}}
- | {{:value.name}} ({{:value.assignment}}) | {{:value.dead ? "Deceased" : "Living"}} | Not Available | {{if data.isAI}}{{:helper.link('Track', null, {}, 'disabled')}} | {{/if}}
- {{else value.sensor_type == 2}}
- | {{:value.name}} ({{:value.assignment}}) | {{:value.dead ? "Deceased" : "Living"}} ({{:value.oxy}}/{{:value.tox}}/{{:value.fire}}/{{:value.brute}}) | Not Available | {{if data.isAI}}{{:helper.link('Track', null, {}, 'disabled')}} | {{/if}}
- {{else value.sensor_type == 3}}
- | {{:value.name}} ({{:value.assignment}}) | {{:value.dead ? "Deceased" : "Living"}} ({{:value.oxy}}/{{:value.tox}}/{{:value.fire}}/{{:value.brute}}) | {{:value.area}}({{:value.x}}, {{:value.y}}) | {{if data.isAI}}{{:helper.link('Track', null, {'track' : value.ref})}} | {{/if}}
- {{/if}}
- {{/for}}
-
\ No newline at end of file
diff --git a/nano/templates/crew_monitor_map_content.tmpl b/nano/templates/crew_monitor_map_content.tmpl
deleted file mode 100644
index c2daaf874bb..00000000000
--- a/nano/templates/crew_monitor_map_content.tmpl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-{{for data.crewmembers}}
- {{if value.sensor_type == 3}}
-
-
- {{:value.name}} ({{:value.assignment}}) ({{:value.dead ? "Deceased" : "Living"}}) ({{:value.oxy}}/{{:value.tox}}/{{:value.fire}}/{{:value.brute}}) ({{:value.area}}: {{:value.x}}, {{:value.y}})
-
-
- {{/if}}
-{{/for}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/crew_monitor_map_header.tmpl b/nano/templates/crew_monitor_map_header.tmpl
deleted file mode 100644
index c753dbce929..00000000000
--- a/nano/templates/crew_monitor_map_header.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-
-{{:helper.link('Show Detail List', 'file-text', {'showMap' : 0})}}
-
-
Zoom Level:
-
x1.0
-
x1.5
-
x2.0
-
x2.5
-
\ 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 fb248c0d6ca..00000000000
--- a/nano/templates/disease_splicer.tmpl
+++ /dev/null
@@ -1,108 +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, 'exchange', { 'grab' : value.reference })}}
- {{/for}}
-
-
- {{:helper.link('Species', 'exchange', { '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', 'floppy-o', { 'disk' : 1 }, (data.buffer || data.species_buffer) ? null : 'disabled')}}
- {{:helper.link('Splice Onto Dish', 'pencil', { 'splice' : 1 }, ((data.buffer || data.species_buffer) && !data.info) ? null : 'disabled')}}
-{{/if}}
diff --git a/nano/templates/dish_incubator.tmpl b/nano/templates/dish_incubator.tmpl
deleted file mode 100644
index c8f9d51014b..00000000000
--- 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-off', { 'power' : 1 }, !data.dish_inserted ? 'disabled' : data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', { 'power' : 1 }, data.on ? null : 'selected')}}
-
-
-
- {{:helper.link('Add Radiation', 'exclamation-circle', {'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', 'arrow-circle-down', { '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/disposal_bin.tmpl b/nano/templates/disposal_bin.tmpl
deleted file mode 100644
index d211b3fbb17..00000000000
--- a/nano/templates/disposal_bin.tmpl
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
- Pressure:
-
-
- {{:helper.smoothRound(data.pressure)}}%
-
-
-Controls
-
-
- Disposal Handle:
-
-
- {{if data.isAI}}
- {{:helper.link('Engaged', 'gear', {'handle' : 1}, 'disabled')}}
- {{:helper.link('Disengaged', 'gear', {'handle' : 0}, 'disabled')}}
- {{else}}
- {{:helper.link('Engaged', 'gear', {'handle' : 1}, data.flushing ? 'selected' : '')}}
- {{:helper.link('Disengaged', 'gear', {'handle' : 0}, !data.flushing ? 'selected' : '')}}
- {{/if}}
-
-
-
- Pump:
-
-
- {{:helper.link('On', 'gear', {'pump' : 1}, data.mode > 0 ? 'selected' : '')}}
- {{:helper.link('Off', 'gear', {'pump' : 0}, data.mode <= 0 ? 'selected' : '')}}
-
-
-
-
- Pump Status:
-
-
- {{:data.pumpstatus}}
-
-
-
-
- Contents:
-
-
- {{:helper.link('Eject', 'eject', {'eject' : 1}, data.isAI ? 'disabled' : '')}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/docking_airlock_console.tmpl b/nano/templates/docking_airlock_console.tmpl
deleted file mode 100644
index 91a9c81cf47..00000000000
--- a/nano/templates/docking_airlock_console.tmpl
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
- Docking Port Status:
-
- {{if data.docking_status == "docked"}}
-
- {{if !data.override_enabled}}
- DOCKED
- {{else}}
- DOCKED-OVERRIDE ENABLED
- {{/if}}
-
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
-
-
- {{else data.docking_status == "docking"}}
-
- {{if !data.override_enabled}}
- DOCKING
- {{else}}
- DOCKING-OVERRIDE ENABLED
- {{/if}}
-
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
-
-
- {{else data.docking_status == "undocking"}}
-
- {{if !data.override_enabled}}
- UNDOCKING
- {{else}}
- UNDOCKING-OVERRIDE ENABLED
- {{/if}}
-
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
-
-
- {{else data.docking_status == "undocked"}}
-
- {{if !data.override_enabled}}
- NOT IN USE
- {{else}}
- OVERRIDE ENABLED
- {{/if}}
-
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
-
-
- {{else}}
-
ERROR
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
- {{/if}}
-
-
-
-
-
- Chamber Pressure:
-
-
- {{:helper.displayBar(data.chamber_pressure, 0, 200, (data.chamber_pressure < 80 || data.chamber_pressure > 120) ? 'bad' : (data.chamber_pressure < 95 || data.chamber_pressure > 110) ? 'average' : 'good')}}
-
- {{:helper.smoothRound(data.chamber_pressure)}} kPa
-
-
-
-
-
-
-
- {{:helper.link('Cycle to Exterior', 'arrow-circle-o-left', {'command' : 'cycle_ext'}, (data.processing || data.airlock_disabled) ? 'disabled' : null)}}
- {{:helper.link('Cycle to Interior', 'arrow-circle-right', {'command' : 'cycle_int'}, (data.processing || data.airlock_disabled) ? 'disabled' : null)}}
-
-
- {{if data.airlock_disabled}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_ext'}, 'disabled', null)}}
- {{:helper.link('Force interior door', 'exclamation-triangle', {'command' : 'force_int'}, 'disabled', null)}}
- {{else}}
- {{if data.interior_status.state == "open"}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_ext'}, null, 'redButton')}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_ext'}, null, data.processing ? 'yellowButton' : null)}}
- {{/if}}
- {{if data.exterior_status.state == "open"}}
- {{:helper.link('Force interior door', 'exclamation-triangle', {'command' : 'force_int'}, null, 'redButton')}}
- {{else}}
- {{:helper.link('Force interior door', 'exclamation-triangle', {'command' : 'force_int'}, null, data.processing ? 'yellowButton' : null)}}
- {{/if}}
- {{/if}}
-
-
-
- {{:helper.link('Abort', 'ban', {'command' : 'abort'}, (data.processing && !data.airlock_disabled) ? null : 'disabled', (data.processing && !data.airlock_disabled) ? 'redButton' : null)}}
-
-
\ No newline at end of file
diff --git a/nano/templates/engines_control.tmpl b/nano/templates/engines_control.tmpl
deleted file mode 100644
index ae44ff572c0..00000000000
--- a/nano/templates/engines_control.tmpl
+++ /dev/null
@@ -1,84 +0,0 @@
-
- {{:helper.link('Overall status', 'sticky-note', {'state' :'status'}, null, state == 'status' ? 'selected' : null)}}
- {{:helper.link('Details', 'sticky-note', {'state' : 'engines'}, null, state == 'engines' ? 'selected' : null)}}
-
-
-{{if data.state == "engines"}}
- {{if data.engines_info}}
- {{for data.engines_info}}
-
-
- Engine #{{:(index + 1)}}:
-
-
- {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power-off', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? 'selected' : null)}}
-
-
-
-
-
- Type:
-
-
- {{:value.eng_type}}
-
-
-
-
- Status:
-
-
- {{:value.eng_on ? 'Online' : 'Offline'}}
- {{:value.eng_status}}
-
-
-
-
- Current thrust:
-
-
- {{:value.eng_thrust}}
-
-
-
-
- Thrust limit:
-
-
- {{:helper.link('', 'plus-circle', { 'limit' : 0.1, 'engine' : value.eng_reference }, null, null)}}
- {{:helper.link(value.eng_thrust_limiter+'%', null, { 'set_limit' : 1, 'engine' : value.eng_reference }, null, null)}}
- {{:helper.link('', 'minus-circle', { 'limit' : -0.1, 'engine' : value.eng_reference }, null, null)}}
-
-
-
- {{/for}}
- {{/if}}
-{{/if}}
-{{if data.state == "status"}}
- {{if data.engines_info}}
- {{for data.engines_info}}
-
-
-
- Engine #{{:(index + 1)}}:
-
-
- {{:helper.link(value.eng_on ? 'Shutdown' : 'Power up', 'power-off', { 'toggle' : 1, 'engine' : value.eng_reference }, null, value.eng_on ? 'selected' : null)}}
-
-
-
-
- Thrust:
-
- Thrust limit:
-
-
- {{:value.eng_thrust}}
-
- {{:value.eng_thrust_limiter}}%
-
-
-
- {{/for}}
- {{/if}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/escape_pod_berth_console.tmpl b/nano/templates/escape_pod_berth_console.tmpl
deleted file mode 100644
index 7bccb3205c4..00000000000
--- a/nano/templates/escape_pod_berth_console.tmpl
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
- Escape Pod Status:
-
-
- {{if data.docking_status == "docked"}}
- {{if data.armed}}
- ARMED
- {{else}}
- SYSTEMS OK
- {{/if}}
- {{else data.docking_status == "undocking"}}
- EJECTING-STAND CLEAR!
- {{else data.docking_status == "undocked"}}
- POD EJECTED
- {{else data.docking_status == "docking"}}
- INITIALIZING...
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- {{if data.armed}}
- {{if data.docking_status == "docked"}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', data.override_enabled ? 'redButton' : null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : 'yellowButton')}}
- {{/if}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, 'disabled', null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, 'disabled', null)}}
- {{/if}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/escape_pod_console.tmpl b/nano/templates/escape_pod_console.tmpl
deleted file mode 100644
index a99cb1e6810..00000000000
--- a/nano/templates/escape_pod_console.tmpl
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
- Escape Pod Status:
-
-
- {{if data.docking_status == "docked"}}
- {{if data.is_armed}}
- ARMED
- {{else}}
- SYSTEMS OK
- {{/if}}
- {{else data.docking_status == "undocking"}}
- EJECTING
- {{else data.docking_status == "undocked"}}
- POD EJECTED
- {{else data.docking_status == "docking"}}
- DOCKING
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- Docking Hatch:
-
-
- {{if data.docking_status == "docked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed"}}
- CLOSED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "docking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- {{if data.docking_status == "docked"}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', data.override_enabled ? 'redButton' : null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : 'yellowButton')}}
- {{/if}}
-
-
-
-
-
-
- {{:helper.link('ARM', 'exclamation-triangle', {'command' : 'manual_arm'}, data.is_armed ? 'disabled' : null, data.is_armed ? 'redButton' : 'yellowButton')}}
- {{:helper.link('MANUAL EJECT', 'exclamation-triangle', {'command' : 'force_launch'}, data.can_force ? null : 'disabled', data.can_force ? 'yellowButton' : null)}}
-
-
-
diff --git a/nano/templates/escape_shuttle_control_console.tmpl b/nano/templates/escape_shuttle_control_console.tmpl
deleted file mode 100644
index eab831464a8..00000000000
--- a/nano/templates/escape_shuttle_control_console.tmpl
+++ /dev/null
@@ -1,84 +0,0 @@
-Shuttle Status
-
-
- {{:data.shuttle_status}}
-
-
-
-
-
- Bluespace Drive:
-
-
- {{if data.shuttle_state == "idle"}}
- IDLE
- {{else data.shuttle_state == "warmup"}}
- SPINNING UP
- {{else data.shuttle_state == "in_transit"}}
- ENGAGED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{if data.has_docking}}
-
-
-
- Docking Status:
-
-
- {{if data.docking_status == "docked"}}
- DOCKED
- {{else data.docking_status == "docking"}}
- {{if !data.docking_override}}
- DOCKING
- {{else}}
- DOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if !data.docking_override}}
- UNDOCKING
- {{else}}
- UNDOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocked"}}
- UNDOCKED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{/if}}
-Shuttle Authorization
-
-
- {{if data.has_auth}}
- Access Granted. Shuttle controls unlocked.
- {{else}}
- Additional authorization required.
- {{/if}}
-
-
-
- {{for data.auth_list}}
- {{if value.auth_hash}}
- {{:helper.link(value.auth_name, 'eject', {'removeid' : value.auth_hash}, null, 'itemContentWide')}}
- {{else}}
- {{:helper.link("", 'eject', {'scanid' : 1}, null, 'itemContentWide')}}
- {{/if}}
- {{/for}}
-
-
-Shuttle Control
-
-
-
- {{:helper.link('Launch Shuttle', 'arrow-circle-right', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}}
-{{:helper.link('Cancel Launch', 'ban', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}}
- {{:helper.link('Force Launch', 'exclamation-triangle', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/geoscanner.tmpl b/nano/templates/geoscanner.tmpl
deleted file mode 100644
index 4204d6466a8..00000000000
--- a/nano/templates/geoscanner.tmpl
+++ /dev/null
@@ -1,185 +0,0 @@
-
-
-Machine Status
-
-
- {{:helper.link(data.scanning ? 'Halt Scan' : 'Begin Scan', 'signal', {'scanItem' : 1}, null)}}
-
-
- {{:helper.link('Eject item', 'eject', {'ejectItem' : 1}, (data.scanned_item && !data.scanning) ? null : 'disabled')}}
-
-
-
-
-
-
Item:
-
- {{if data.scanned_item}}
- {{:data.scanned_item}}
- {{else}}
- No item inserted
- {{/if}}
-
-
-
-
Heuristic analysis:
-
- {{if data.scanned_item_desc}}
- {{:data.scanned_item_desc}}
- {{/if}}
-
-
-
-
-Scanner
-
-
Scan progress:
-
- {{:helper.displayBar(data.scan_progress, 0, 100, 'good')}}
- {{:helper.smoothRound(data.scan_progress)}} %
-
-
- {{if data.scan_progress >= 100}}
- Scan completed successfully.
- {{/if}}
-
-
-
-
Vacuum seal integrity:
-
- {{:helper.displayBar(data.scanner_seal_integrity, 0, 100, ((data.scanner_seal_integrity < 66) ? ((data.scanner_seal_integrity < 33) ? 'bad' : 'average') : 'good'))}}
- {{:helper.smoothRound(data.scanner_seal_integrity)}} %
-
-
- {{if data.scanner_seal_integrity < 25}}
- Warning! Vacuum seal breach will result in scan failure!
- {{/if}}
-
-
-
-MASER
-
-
MASER Efficiency:
-
- {{:helper.displayBar(data.maser_efficiency, 1, 100, ((data.maser_efficiency < 66) ? ((data.maser_efficiency) < 33 ? 'bad' : 'average') : 'good'))}}
- {{:data.maser_efficiency}} %
-
-
- {{if data.maser_efficiency < 50}}
- Match wavelengths to progress the scan.
- {{/if}}
-
-
-
-
Optimal Wavelength:
-
- {{:helper.displayBar(data.optimal_wavelength, 1, 10000, 'good')}}
- {{:data.optimal_wavelength}} MHz
-
-
-
-
Current Wavelength:
-
- {{:helper.displayBar(data.maser_wavelength, 1, 10000, 'good')}}
- {{:data.maser_wavelength}} MHz
-
-
- {{:helper.link('-2 KHz', null, {'maserWavelength' : -2}, null)}}
- {{:helper.link('-1 KHz', null, {'maserWavelength' : -1}, null)}}
- {{:helper.link('-0.5 KHz', null, {'maserWavelength' : -0.5}, null)}}
-
-
- {{:helper.link('+0.5 KHz', null, {'maserWavelength' : 0.5}, null)}}
- {{:helper.link('+1 KHz', null, {'maserWavelength' : 1}, null)}}
- {{:helper.link('+2 KHz', null, {'maserWavelength' : 2}, null)}}
-
-
-
-Environment / Internal
-
-
Centrifuge speed:
-
- {{:helper.displayBar(data.scanner_rpm, 0, 1000, 'good')}}
- {{:data.scanner_rpm}} RPM
-
-
-
-
Internal temperature:
-
- {{:helper.displayBar(data.scanner_temperature, 0, 1273, (data.scanner_temperature > 250 ? (data.scanner_temperature > 1000 ? 'bad' : 'average') : 'good'))}}
- {{:helper.smoothRound(data.scanner_temperature)}} K
-
-
- {{if data.scanner_temperature > 1000}}
- Warning! Exceeding 1200K will result in scan failure!
- {{/if}}
-
-
-
-Radiation
-
-
Ambient radiation:
-
- {{:helper.displayBar(data.radiation, 0, 100, ((data.radiation > 15) ? ((data.radiation > 65) ? 'bad' : 'average') : 'good'))}}
- {{:helper.smoothRound(data.radiation)}} mSv
-
-
- {{:helper.link(data.rad_shield_on ? 'Disable Radiation Shielding' : 'Enable Radiation Shielding', 'exclamation-circle', {'toggle_rad_shield' : 1}, null)}}
- {{if data.rad_shield_on}}
- Shield blocking scanner.
- {{/if}}
-
-
-
-Cooling
-
-
Coolant remaining:
-
- {{:helper.displayBar(data.unused_coolant_per, 0, 100, ((data.unused_coolant_per < 66) ? ((data.unused_coolant_per < 33) ? 'bad' : 'average') : 'good'))}}
- {{:helper.smoothRound(data.unused_coolant_abs)}} u
-
-
- {{if data.unused_coolant_per < 20}}
- Warning! Coolant stocks low!
- {{/if}}
-
-
-
-
Coolant flow rate:
-
- {{:helper.displayBar(data.coolant_usage_rate, 0, 10, 'good')}}
- {{:helper.smoothRound(data.coolant_usage_rate)}} u/s
-
-
- {{:helper.link('Min u/s', null, {'coolantRate' : -10}, null)}}
- {{:helper.link('-3 u/s', null, {'coolantRate' : -3}, null)}}
- {{:helper.link('-1 u/s', null, {'coolantRate' : -1}, null)}}
-
-
- {{:helper.link('+1 u/s', null, {'coolantRate' : 1}, null)}}
- {{:helper.link('+3 u/s', null, {'coolantRate' : 3}, null)}}
- {{:helper.link('Max u/s', null, {'coolantRate' : 10}, null)}}
-
-
-
-
Coolant purity:
-
- {{:helper.displayBar(data.coolant_purity, 0, 100, ((data.coolant_purity < 66) ? ((data.coolant_purity < 33) ? 'bad' : 'average') : 'good'))}}
- {{:data.coolant_purity}} %
-
-
- {{if data.coolant_purity < 0.5}}
- Warning! Check coolant for contaminants!
- {{/if}}
-
-
-
-Latest Results
-
-
- {{:data.last_scan_data}}
-
-
diff --git a/nano/templates/hardsuit.tmpl b/nano/templates/hardsuit.tmpl
deleted file mode 100644
index 92ab1c0fadf..00000000000
--- a/nano/templates/hardsuit.tmpl
+++ /dev/null
@@ -1,259 +0,0 @@
-
-
-
-
-{{if data.interfacelock || data.malf > 0}}
- -- HARDSUIT INTERFACE OFFLINE --
-{{else}}
- {{if data.aicontrol && data.ai != 1}}
- -- HARDSUIT CONTROL OVERRIDDEN BY AI --
- {{else}}
-
-
-
-
-
-
- Suit status:
-
-
- {{if data.sealing == 1}}
-
PROCESSING
- {{else}}
- {{if data.seals == 1}}
-
INACTIVE
- {{else}}
-
ACTIVE
- {{/if}}
- {{/if}}
-
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_seals' : 1}, null)}}
-
-
-
-
- Cover status:
-
-
- {{if data.emagged || !data.securitycheck}}
-
ERROR - MAINTENANCE LOCK CONTROL OFFLINE
- {{else}}
- {{if data.coverlock}}
-
LOCKED
- {{else}}
-
UNLOCKED
- {{/if}}
- {{/if}}
-
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_suit_lock' : 1}, null)}}
-
-
-
-
-
- Hardware
- Suit pieces
-
-
-
-
- Helmet:
-
-
- {{:helper.capitalizeFirstLetter(data.helmet)}}
-
- {{if data.sealing != 1}}
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_piece' : 'helmet'}, null)}}
-
- {{/if}}
-
-
-
- Gauntlets:
-
-
- {{:helper.capitalizeFirstLetter(data.gauntlets)}}
-
- {{if data.sealing != 1}}
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_piece' : 'gauntlets'}, null)}}
-
- {{/if}}
-
-
-
- Boots:
-
-
- {{:helper.capitalizeFirstLetter(data.boots)}}
-
- {{if data.sealing != 1}}
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_piece' : 'boots'}, null)}}
-
- {{/if}}
-
-
-
- Chestpiece:
-
-
- {{:helper.capitalizeFirstLetter(data.chest)}}
-
- {{if data.sealing != 1}}
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_piece' : 'chest'}, null)}}
-
- {{/if}}
-
-
-
- System modules
- {{if data.seals == 1 || data.sealing == 1}}
- HARDSUIT SYSTEMS OFFLINE
- {{else}}
- Selected primary system:
- {{if data.primarysystem}}
- {{:helper.capitalizeFirstLetter(data.primarysystem)}}
- {{else}}
- None
- {{/if}}
-
- {{if data.modules}}
-
- {{for data.modules}}
-
-
-
-
{{:helper.capitalizeFirstLetter(value.name)}}
- {{if value.damage > 0}}
-
- {{if value.damage == 1}}
- (
damaged
)
- {{else}}
- (
destroyed
)
- {{/if}}
-
- {{/if}}
-
-
- Engage: {{:value.engagecost}}
- Activate: {{:value.activecost}}
- Passive: {{:value.passivecost}}
-
-
- {{:value.desc}}
-
-
- {{if value.can_use == 1}}
-
- {{:helper.link(value.engagestring, 'arrow-circle-down', {'interact_module' : value.index, 'module_mode' : 'engage'}, null)}}
-
- {{/if}}
- {{if value.can_select == 1}}
-
- {{if value.name == data.primarysystem}}
-
SELECTED
- {{else}}
- {{:helper.link('Select', 'arrow-circle-down', {'interact_module' : value.index, 'module_mode' : 'select'}, null)}}
- {{/if}}
-
- {{/if}}
- {{if value.can_toggle == 1}}
-
- {{if value.is_active == 1}}
- {{:helper.link(value.deactivatestring, 'arrow-circle-down', {'interact_module' : value.index, 'module_mode' : 'deactivate'}, null)}}
- {{else}}
- {{:helper.link(value.activatestring, 'arrow-circle-down', {'interact_module' : value.index, 'module_mode' : 'activate'}, null)}}
- {{/if}}
-
- {{/if}}
-
-
-
-
- {{if value.charges}}
-
Stored charges
-
-
Selected:
{{:helper.capitalizeFirstLetter(value.chargetype)}}
-
- {{for value.charges :itemValue:itemIndex}}
-
- {{:helper.link(helper.capitalizeFirstLetter(itemValue.caption), null, {'interact_module' : value.index, 'module_mode' : 'select_charge_type', 'charge_type' : itemValue.index}, null)}}
-
- {{/for}}
- {{/if}}
-
-
-
- {{/for}}
-
- {{else}}
- None.
- {{/if}}
- {{/if}}
- {{/if}}
-{{/if}}
\ No newline at end of file
diff --git a/nano/templates/helm.tmpl b/nano/templates/helm.tmpl
deleted file mode 100644
index 615d4ab46d0..00000000000
--- a/nano/templates/helm.tmpl
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
Sector information
-
- {{:data.sector}}
-
- Coordinates: {{:data.s_x}} : {{:data.s_y}}
-
- Additional information: {{:data.s_x}} : {{:data.s_y}}
- {{:data.sector_info}}
-
-
-
-
-
Flight data
-
-
-
- Speed:
-
-
- {{:data.speed}}
-
-
-
-
- Acceleration:
-
-
- {{:data.accel}}
-
-
-
-
- Heading:
-
-
- {{:data.heading}}
-
-
-
-
-
-
-Manual control
-
-
-
-
- {{:helper.link('', 'caret-left fa-rotate-45', { 'move' : 9 }, null, null)}}
- {{:helper.link('', 'caret-up', { 'move' : 1 }, null, null)}}
- {{:helper.link('', 'caret-up fa-rotate-45', { 'move' : 5 }, null, null)}}
-
-
- {{:helper.link('', 'caret-left', { 'move' : 8 }, null, null)}}
- {{:helper.link('', 'circle-close', { 'brake' : 1 }, null, null)}}
- {{:helper.link('', 'caret-right', { 'move' : 4 }, null, null)}}
-
-
- {{:helper.link('', 'caret-down fa-rotate-45', { 'move' : 10 }, null, null)}}
- {{:helper.link('', 'caret-down', { 'move' : 2 }, null, null)}}
- {{:helper.link('', 'caret-right fa-rotate-45', { 'move' : 6 }, null, null)}}
-
-
-
-
-
- Direct control
- {{:helper.link(data.manual_control ? 'Engaged' : 'Disengaged', 'random', { 'manual' : 1 }, data.manual_control ? 'selected' : null, null)}}
-
-
-
-
-
-
-
-
Autopilot
-
-
- {{:helper.link(data.autopilot ? 'Engaged' : 'Disengaged', 'gear', { 'apilot' : 1 }, data.dest ? null : 'disabled', data.autopilot ? 'selected' : null)}}
-
-
-
-
- Target coordinates
-
-
- {{if data.dest}}
- {{:helper.link(data.d_x, null, { 'setx' : 1 }, null, null)}} {{:helper.link(data.d_y, null, { 'sety' : 1 }, null, null)}}
- {{else}}
- {{:helper.link('None', null, { 'sety' : 1, 'setx' : 1 }, null, null)}}
- {{/if}}
-
-
-
-Navigation data
-
- {{:helper.link('Save current position', 'floppy-o', { 'add' : 'current' }, null)}}
- {{:helper.link('Add new entry', 'file', { 'add' : 'new' }, null)}}
-
-
-
- {{if data.locations}}
- {{for data.locations}}
-
- {{:value.name}}:
- {{:value.x}} : {{:value.y}}
-
-
- {{:helper.link('Plot course', 'arrow-right', { 'x' : value.x, 'y' : value.y }, null, null)}}
- {{:helper.link('Remove entry', 'close', { 'remove' : value.reference }, null, null)}}
-
- {{/for}}
- {{/if}}
-
diff --git a/nano/templates/isolation_centrifuge.tmpl b/nano/templates/isolation_centrifuge.tmpl
deleted file mode 100644
index 4ff0272df02..00000000000
--- 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/multi_docking_console.tmpl b/nano/templates/multi_docking_console.tmpl
deleted file mode 100644
index 9515799644e..00000000000
--- a/nano/templates/multi_docking_console.tmpl
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
- Docking Port Status:
-
-
- {{if data.docking_status == "docked"}}
- DOCKED
- {{else data.docking_status == "docking"}}
- DOCKING
- {{else data.docking_status == "undocking"}}
- UNDOCKING
- {{else data.docking_status == "undocked"}}
- NOT IN USE
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-{{for data.airlocks}}
-
-
-
- {{:value.name}}
-
-
- {{if value.override_enabled}}
- OVERRIDE ENABLED
- {{else}}
- STATUS OK
- {{/if}}
-
-
-
-{{/for}}
\ No newline at end of file
diff --git a/nano/templates/pathogenic_isolator.tmpl b/nano/templates/pathogenic_isolator.tmpl
deleted file mode 100644
index 119d5b6b7fe..00000000000
--- 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', 'sticky-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}}
-
-
{{:data.name}}
- {{:helper.link('Details', 'arrow-circle-down', { '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/shuttle_control_console.tmpl b/nano/templates/shuttle_control_console.tmpl
deleted file mode 100644
index eb7ce9c0ad8..00000000000
--- a/nano/templates/shuttle_control_console.tmpl
+++ /dev/null
@@ -1,66 +0,0 @@
-Shuttle Status
-
-
- {{:data.shuttle_status}}
-
-
-
-
-
- Bluespace Drive:
-
-
- {{if data.shuttle_state == "idle"}}
- IDLE
- {{else data.shuttle_state == "warmup"}}
- SPINNING UP
- {{else data.shuttle_state == "in_transit"}}
- ENGAGED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{if data.has_docking}}
-
-
-
- Docking Status:
-
-
- {{if data.docking_status == "docked"}}
- DOCKED
- {{else data.docking_status == "docking"}}
- {{if !data.docking_override}}
- DOCKING
- {{else}}
- DOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if !data.docking_override}}
- UNDOCKING
- {{else}}
- UNDOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocked"}}
- UNDOCKED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-{{/if}}
-
-Shuttle Control
-
-
-
- {{:helper.link('Launch Shuttle', 'arrow-circle-right', {'move' : '1'}, data.can_launch? null : 'disabled' , null)}}
- {{:helper.link('Cancel Launch', 'ban', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}}
- {{:helper.link('Force Launch', 'exclamation-triangle', {'force' : '1'}, data.can_force? null : 'disabled' , data.can_force ? 'redButton' : null)}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/shuttle_control_console_exploration.tmpl b/nano/templates/shuttle_control_console_exploration.tmpl
deleted file mode 100644
index 2e470a00978..00000000000
--- a/nano/templates/shuttle_control_console_exploration.tmpl
+++ /dev/null
@@ -1,73 +0,0 @@
-Shuttle Status
-
-
- {{:data.shuttle_status}}
-
-
-
-
-
- Drive:
-
-
- {{if data.shuttle_state == "idle"}}
- IDLE
- {{else data.shuttle_state == "warmup"}}
- SPINNING UP
- {{else data.shuttle_state == "in_transit"}}
- ENGAGED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{if data.has_docking}}
-
-
-
- Docking Status:
-
-
- {{if data.docking_status == "docked"}}
- DOCKED
- {{else data.docking_status == "docking"}}
- {{if !data.docking_override}}
- DOCKING
- {{else}}
- DOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if !data.docking_override}}
- UNDOCKING
- {{else}}
- UNDOCKING-MANUAL
- {{/if}}
- {{else data.docking_status == "undocked"}}
- UNDOCKED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{/if}}
-
-
- Current Destination:
-
-
{{:data.destination_name}}
-
- {{:helper.link('Choose Destination', 'arrow-down', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}}
-
-
-Shuttle Control
-
-
-
- {{:helper.link('Launch Shuttle', 'arrow-circle-right', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}}
- {{:helper.link('Cancel Launch', 'ban', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}}
- {{:helper.link('Force Launch', 'exclamation-triangle', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/shuttle_control_multi_console.tmpl b/nano/templates/shuttle_control_multi_console.tmpl
deleted file mode 100644
index 2d7d2fbb622..00000000000
--- a/nano/templates/shuttle_control_multi_console.tmpl
+++ /dev/null
@@ -1,58 +0,0 @@
-{{if data.is_syndicate}}
- {{:helper.syndicateMode()}}
-{{/if}}
-Ship Status
-
-
- {{:data.shuttle_status}}
-
-
-
-
-
- Bluespace Drive:
-
-
- {{if data.shuttle_state == "idle"}}
- IDLE
- {{else data.shuttle_state == "charging"}}
- CHARGING
- {{else data.shuttle_state == "warmup"}}
- SPINNING UP
- {{else data.shuttle_state == "in_transit"}}
- ENGAGED
- {{else}}
- ERROR
- {{/if}}
-
-
-
-{{if data.can_cloak}}
-
-
-
- Cloak:
-
-
- {{if data.cloaked}}
- ENABLED
- {{else}}
- DISABLED: The station will be warned of our arrival.
- {{/if}}
-
-
-
-{{/if}}
-
-Ship Control
-
-
-
- {{:helper.link('Launch Ship', 'arrow-circle-right', {'move_multi' : '1'}, data.can_launch ? null : 'disabled' , null)}}
- {{:helper.link('Return to Base', 'arrow-circle-o-left', {'start' : '1'}, data.can_return_to_base ? null : 'disabled' , null)}}
- {{if data.can_cloak}}
- {{:helper.link(data.cloaked ? 'Disable Cloak' : 'Enable Cloak', 'exclamation-triangle', {'toggle_cloak' : '1'}, data.can_cloak ? null : 'disabled' , null)}}
- {{/if}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/simple_docking_console.tmpl b/nano/templates/simple_docking_console.tmpl
deleted file mode 100644
index c97e8a84924..00000000000
--- a/nano/templates/simple_docking_console.tmpl
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
- Docking Port Status:
-
-
- {{if data.docking_status == "docked"}}
- {{if !data.override_enabled}}
- DOCKED
- {{else}}
- DOCKED-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "docking"}}
- {{if !data.override_enabled}}
- DOCKING
- {{else}}
- DOCKING-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if !data.override_enabled}}
- UNDOCKING
- {{else}}
- UNDOCKING-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "undocked"}}
- {{if !data.override_enabled}}
- NOT IN USE
- {{else}}
- OVERRIDE ENABLED
- {{/if}}
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- Docking Hatch:
-
-
- {{if data.docking_status == "docked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed"}}
- CLOSED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "docking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- {{if data.docking_status == "docked"}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', data.override_enabled ? 'redButton' : null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : 'yellowButton')}}
- {{/if}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/simple_docking_console_pod.tmpl b/nano/templates/simple_docking_console_pod.tmpl
deleted file mode 100644
index b17a8a01028..00000000000
--- a/nano/templates/simple_docking_console_pod.tmpl
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
- Docking Port Status:
-
-
- {{if data.docking_status == "docked"}}
- {{if !data.override_enabled}}
- DOCKED
- {{else}}
- DOCKED-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "docking"}}
- {{if !data.override_enabled}}
- DOCKING
- {{else}}
- DOCKING-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if !data.override_enabled}}
- UNDOCKING
- {{else}}
- UNDOCKING-OVERRIDE ENABLED
- {{/if}}
- {{else data.docking_status == "undocked"}}
- {{if !data.override_enabled}}
- NOT IN USE
- {{else}}
- OVERRIDE ENABLED
- {{/if}}
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- Docking Hatch:
-
-
- {{if data.docking_status == "docked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed"}}
- CLOSED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "docking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocking"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else data.docking_status == "undocked"}}
- {{if data.door_state == "open"}}
- OPEN
- {{else data.door_state == "closed" && data.door_lock == "locked"}}
- SECURED
- {{else data.door_state == "closed" && data.door_lock == "unlocked"}}
- UNSECURED
- {{else}}
- ERROR
- {{/if}}
- {{else}}
- ERROR
- {{/if}}
-
-
-
-
-
-
- {{if data.docking_status == "docked"}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : null)}}
- {{:helper.link('MANUAL EJECT', 'exclamation-triangle', {'command' : 'toggle_override'}, 'disabled', null)}}
- {{else}}
- {{:helper.link('Force exterior door', 'exclamation-triangle', {'command' : 'force_door'}, data.override_enabled ? null : 'disabled', data.override_enabled ? 'redButton' : null)}}
- {{:helper.link('Override', 'exclamation-triangle', {'command' : 'toggle_override'}, null, data.override_enabled ? 'redButton' : 'yellowButton')}}
- {{:helper.link('MANUAL EJECT', 'exclamation-triangle', {'command' : 'toggle_override'}, data.can_force ? null : 'disabled', data.can_force ? 'redButton' : null)}}
- {{/if}}
-
-
-
\ No newline at end of file
diff --git a/nano/templates/vending.tmpl b/nano/templates/vending.tmpl
deleted file mode 100644
index 6ca87626494..00000000000
--- a/nano/templates/vending.tmpl
+++ /dev/null
@@ -1,85 +0,0 @@
-{{:~link('Eject Coin', 'eject', {'remove_coin' : 1}, coin ? null : 'disabled', 'floatRight')}}
-
- {{if !coin}}
-
No coin loaded.
- {{else}}
-
Coin Loaded.
- {{/if}}
-
-
- Select an Item:
-
-
-
-
-
-
-
-
- {{if products}}
- {{for productData}}
-
- {{if amount}}
- {{:~link('Vend', 'eject', {'vend': itself}, vendReady ? 'disabled' : null)}} {{:name}} - {{:amount}}-{{:price}}
- {{else}}
- {{:~link('Vend', 'eject', {'vend': itself}, 'disabled')}}{{:name}} - Sold Out.
- {{/if}}
-
- {{/for}}
- {{else}}
- No products loaded.
- {{/if}}
-
-
-
-{{if coin}}
-
-
- Premium Items:
-
-
-
-
-
- {{if products}}
- {{for premiumData}}
-
- {{if amount}}
- {{:~link('Vend', 'eject', {'vend': itself}, vendReady ? 'disabled' : null)}} {{:name}} - {{:amount}}-{{:price}}
- {{else}}
- {{:~link('Vend', 'eject', {'vend': itself}, 'disabled')}}{{:name}} - Sold Out.
- {{/if}}
-
- {{/for}}
- {{else}}
- No products loaded.
- {{/if}}
-
-
-
-{{/if}}
-{{if extendedInventory}}
-
- Contraband:
-
-
-
-
-
- {{if products}}
- {{for contrabandData}}
-
- {{if amount}}
- {{:~link('Vend', 'eject', {'vend': itself}, vendReady ? 'disabled' : null)}} {{:name}} - {{:amount}}-{{:price}}
- {{else}}
- {{:~link('Vend', 'eject', {'vend': itself}, 'disabled')}}{{:name}} - Sold Out.
- {{/if}}
-
- {{/for}}
- {{else}}
- No products loaded.
- {{/if}}
-
-
-
-{{/if}}
\ No newline at end of file
diff --git a/paradise.dme b/paradise.dme
index f7741955994..2ec1113630d 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -247,6 +247,7 @@
#include "code\controllers\subsystem\spacedrift.dm"
#include "code\controllers\subsystem\statistics.dm"
#include "code\controllers\subsystem\sun.dm"
+#include "code\controllers\subsystem\tgui.dm"
#include "code\controllers\subsystem\throwing.dm"
#include "code\controllers\subsystem\ticker.dm"
#include "code\controllers\subsystem\timer.dm"
@@ -1850,7 +1851,6 @@
#include "code\modules\mob\living\carbon\human\species\vox.dm"
#include "code\modules\mob\living\carbon\human\species\vulpkanin.dm"
#include "code\modules\mob\living\carbon\human\species\wryn.dm"
-#include "code\modules\mob\living\carbon\human\species\zombies.dm"
#include "code\modules\mob\living\silicon\death.dm"
#include "code\modules\mob\living\silicon\emote.dm"
#include "code\modules\mob\living\silicon\laws.dm"
@@ -2138,7 +2138,6 @@
#include "code\modules\nano\interaction\zlevel.dm"
#include "code\modules\nano\modules\alarm_monitor.dm"
#include "code\modules\nano\modules\atmos_control.dm"
-#include "code\modules\nano\modules\crew_monitor.dm"
#include "code\modules\nano\modules\ert_manager.dm"
#include "code\modules\nano\modules\human_appearance.dm"
#include "code\modules\nano\modules\law_manager.dm"
@@ -2473,6 +2472,26 @@
#include "code\modules\telesci\rcs.dm"
#include "code\modules\telesci\telepad.dm"
#include "code\modules\telesci\telesci_computer.dm"
+#include "code\modules\tgui\external.dm"
+#include "code\modules\tgui\states.dm"
+#include "code\modules\tgui\tgui.dm"
+#include "code\modules\tgui\modules\_base.dm"
+#include "code\modules\tgui\modules\crew_monitor.dm"
+#include "code\modules\tgui\states\admin.dm"
+#include "code\modules\tgui\states\always.dm"
+#include "code\modules\tgui\states\conscious.dm"
+#include "code\modules\tgui\states\contained.dm"
+#include "code\modules\tgui\states\deep_inventory.dm"
+#include "code\modules\tgui\states\default.dm"
+#include "code\modules\tgui\states\hands.dm"
+#include "code\modules\tgui\states\human_adjacent.dm"
+#include "code\modules\tgui\states\inventory.dm"
+#include "code\modules\tgui\states\not_incapacitated.dm"
+#include "code\modules\tgui\states\notcontained.dm"
+#include "code\modules\tgui\states\observer.dm"
+#include "code\modules\tgui\states\physical.dm"
+#include "code\modules\tgui\states\self.dm"
+#include "code\modules\tgui\states\zlevel.dm"
#include "code\modules\tooltip\tooltip.dm"
#include "code\modules\unit_tests\_unit_tests.dm"
#include "code\modules\vehicle\ambulance.dm"
@@ -2484,8 +2503,6 @@
#include "code\modules\vehicle\speedbike.dm"
#include "code\modules\vehicle\sportscar.dm"
#include "code\modules\vehicle\vehicle.dm"
-#include "code\modules\zombie\items.dm"
-#include "code\modules\zombie\organs.dm"
#include "goon\code\datums\browserOutput.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
diff --git a/sound/ambience/ambiatmos.ogg b/sound/ambience/ambiatmos.ogg
new file mode 100644
index 00000000000..c832c677d5e
Binary files /dev/null and b/sound/ambience/ambiatmos.ogg differ
diff --git a/sound/ambience/ambiatmos2.ogg b/sound/ambience/ambiatmos2.ogg
new file mode 100644
index 00000000000..9651049c25b
Binary files /dev/null and b/sound/ambience/ambiatmos2.ogg differ
diff --git a/sound/ambience/ambicave.ogg b/sound/ambience/ambicave.ogg
new file mode 100644
index 00000000000..d78dc46a4ce
Binary files /dev/null and b/sound/ambience/ambicave.ogg differ
diff --git a/sound/ambience/ambidet1.ogg b/sound/ambience/ambidet1.ogg
new file mode 100644
index 00000000000..2ffc3ba494c
Binary files /dev/null and b/sound/ambience/ambidet1.ogg differ
diff --git a/sound/ambience/ambidet2.ogg b/sound/ambience/ambidet2.ogg
new file mode 100644
index 00000000000..7d5aae34d0b
Binary files /dev/null and b/sound/ambience/ambidet2.ogg differ
diff --git a/sound/ambience/ambifailure.ogg b/sound/ambience/ambifailure.ogg
new file mode 100644
index 00000000000..4dcf451bca6
Binary files /dev/null and b/sound/ambience/ambifailure.ogg differ
diff --git a/sound/ambience/ambigen15.ogg b/sound/ambience/ambigen15.ogg
new file mode 100644
index 00000000000..63afd437a65
Binary files /dev/null and b/sound/ambience/ambigen15.ogg differ
diff --git a/sound/ambience/ambiholy.ogg b/sound/ambience/ambiholy.ogg
new file mode 100644
index 00000000000..cd53dc2789c
Binary files /dev/null and b/sound/ambience/ambiholy.ogg differ
diff --git a/sound/ambience/ambiholy2.ogg b/sound/ambience/ambiholy2.ogg
new file mode 100644
index 00000000000..4532dd0a815
Binary files /dev/null and b/sound/ambience/ambiholy2.ogg differ
diff --git a/sound/ambience/ambiholy3.ogg b/sound/ambience/ambiholy3.ogg
new file mode 100644
index 00000000000..93f7a897e1c
Binary files /dev/null and b/sound/ambience/ambiholy3.ogg differ
diff --git a/sound/ambience/ambilava.ogg b/sound/ambience/ambilava1.ogg
similarity index 100%
rename from sound/ambience/ambilava.ogg
rename to sound/ambience/ambilava1.ogg
diff --git a/sound/ambience/ambilava2.ogg b/sound/ambience/ambilava2.ogg
new file mode 100644
index 00000000000..12e496670d0
Binary files /dev/null and b/sound/ambience/ambilava2.ogg differ
diff --git a/sound/ambience/ambilava3.ogg b/sound/ambience/ambilava3.ogg
new file mode 100644
index 00000000000..8913ad4a0ea
Binary files /dev/null and b/sound/ambience/ambilava3.ogg differ
diff --git a/sound/ambience/ambimaint.ogg b/sound/ambience/ambimaint.ogg
new file mode 100644
index 00000000000..592850a75c7
Binary files /dev/null and b/sound/ambience/ambimaint.ogg differ
diff --git a/sound/ambience/ambimystery.ogg b/sound/ambience/ambimystery.ogg
new file mode 100644
index 00000000000..b6e65c0bf62
Binary files /dev/null and b/sound/ambience/ambimystery.ogg differ
diff --git a/sound/ambience/ambinice.ogg b/sound/ambience/ambinice.ogg
new file mode 100644
index 00000000000..6ce351521bc
Binary files /dev/null and b/sound/ambience/ambinice.ogg differ
diff --git a/sound/ambience/ambiodd.ogg b/sound/ambience/ambiodd.ogg
new file mode 100644
index 00000000000..dde64a9858f
Binary files /dev/null and b/sound/ambience/ambiodd.ogg differ
diff --git a/sound/ambience/ambiruin.ogg b/sound/ambience/ambiruin.ogg
new file mode 100644
index 00000000000..ff4cef4a415
Binary files /dev/null and b/sound/ambience/ambiruin.ogg differ
diff --git a/sound/ambience/ambiruin2.ogg b/sound/ambience/ambiruin2.ogg
new file mode 100644
index 00000000000..2dc408e2b69
Binary files /dev/null and b/sound/ambience/ambiruin2.ogg differ
diff --git a/sound/ambience/ambiruin3.ogg b/sound/ambience/ambiruin3.ogg
new file mode 100644
index 00000000000..7a97ff9c60c
Binary files /dev/null and b/sound/ambience/ambiruin3.ogg differ
diff --git a/sound/ambience/ambiruin4.ogg b/sound/ambience/ambiruin4.ogg
new file mode 100644
index 00000000000..ad56a915f92
Binary files /dev/null and b/sound/ambience/ambiruin4.ogg differ
diff --git a/sound/ambience/ambiruin5.ogg b/sound/ambience/ambiruin5.ogg
new file mode 100644
index 00000000000..2073b5a2770
Binary files /dev/null and b/sound/ambience/ambiruin5.ogg differ
diff --git a/sound/ambience/ambiruin6.ogg b/sound/ambience/ambiruin6.ogg
new file mode 100644
index 00000000000..4b6c79a72d5
Binary files /dev/null and b/sound/ambience/ambiruin6.ogg differ
diff --git a/sound/ambience/ambiruin7.ogg b/sound/ambience/ambiruin7.ogg
new file mode 100644
index 00000000000..ed88fcd52d4
Binary files /dev/null and b/sound/ambience/ambiruin7.ogg differ
diff --git a/sound/ambience/ambispace2.ogg b/sound/ambience/ambispace2.ogg
new file mode 100644
index 00000000000..44b4c62c36e
Binary files /dev/null and b/sound/ambience/ambispace2.ogg differ
diff --git a/sound/ambience/ambitech.ogg b/sound/ambience/ambitech.ogg
new file mode 100644
index 00000000000..5f21514e5c4
Binary files /dev/null and b/sound/ambience/ambitech.ogg differ
diff --git a/sound/ambience/ambitech2.ogg b/sound/ambience/ambitech2.ogg
new file mode 100644
index 00000000000..bd6428bff3e
Binary files /dev/null and b/sound/ambience/ambitech2.ogg differ
diff --git a/sound/ambience/ambitech3.ogg b/sound/ambience/ambitech3.ogg
new file mode 100644
index 00000000000..effd23b1326
Binary files /dev/null and b/sound/ambience/ambitech3.ogg differ
diff --git a/sound/ambience/ambivapor1.ogg b/sound/ambience/ambivapor1.ogg
new file mode 100644
index 00000000000..4374f46710d
Binary files /dev/null and b/sound/ambience/ambivapor1.ogg differ
diff --git a/sound/ambience/aurora_caelus_short.ogg b/sound/ambience/aurora_caelus_short.ogg
new file mode 100644
index 00000000000..f6d1acaa2fe
Binary files /dev/null and b/sound/ambience/aurora_caelus_short.ogg differ
diff --git a/sound/ambience/servicebell.ogg b/sound/ambience/servicebell.ogg
new file mode 100644
index 00000000000..f55c7d5a759
Binary files /dev/null and b/sound/ambience/servicebell.ogg differ
diff --git a/sound/ambience/shipambience.ogg b/sound/ambience/shipambience.ogg
index 3c315961f71..51e090b02c8 100644
Binary files a/sound/ambience/shipambience.ogg and b/sound/ambience/shipambience.ogg differ
diff --git a/sound/music/clown.ogg b/sound/music/clown.ogg
new file mode 100644
index 00000000000..3214da56fa2
Binary files /dev/null and b/sound/music/clown.ogg differ
diff --git a/sound/voice/lowHiss1.ogg b/sound/voice/lowHiss1.ogg
new file mode 100644
index 00000000000..1373f010f84
Binary files /dev/null and b/sound/voice/lowHiss1.ogg differ
diff --git a/sound/voice/lowHiss2.ogg b/sound/voice/lowHiss2.ogg
new file mode 100644
index 00000000000..90a91db5dda
Binary files /dev/null and b/sound/voice/lowHiss2.ogg differ
diff --git a/sound/voice/lowHiss3.ogg b/sound/voice/lowHiss3.ogg
new file mode 100644
index 00000000000..ab1f2d5bdb7
Binary files /dev/null and b/sound/voice/lowHiss3.ogg differ
diff --git a/sound/voice/lowHiss4.ogg b/sound/voice/lowHiss4.ogg
new file mode 100644
index 00000000000..a5c95b02977
Binary files /dev/null and b/sound/voice/lowHiss4.ogg differ
diff --git a/tgui/.editorconfig b/tgui/.editorconfig
new file mode 100644
index 00000000000..33092d4928a
--- /dev/null
+++ b/tgui/.editorconfig
@@ -0,0 +1,13 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+max_line_length = 80
diff --git a/tgui/.eslintignore b/tgui/.eslintignore
new file mode 100644
index 00000000000..010416b9e88
--- /dev/null
+++ b/tgui/.eslintignore
@@ -0,0 +1,6 @@
+/**/node_modules
+/**/*.bundle.*
+/**/*.chunk.*
+/**/*.hot-update.*
+/packages/inferno/**
+/packages/tgui/public/shim-*.js
diff --git a/tgui/.eslintrc-harder.yml b/tgui/.eslintrc-harder.yml
new file mode 100644
index 00000000000..eb06f5b33db
--- /dev/null
+++ b/tgui/.eslintrc-harder.yml
@@ -0,0 +1,14 @@
+rules:
+ ## Enforce a maximum cyclomatic complexity allowed in a program
+ complexity: [error, { max: 25 }]
+ ## Enforce consistent brace style for blocks
+ brace-style: [error, stroustrup, { allowSingleLine: false }]
+ ## Enforce the consistent use of either backticks, double, or single quotes
+ quotes: [error, single, {
+ avoidEscape: true,
+ allowTemplateLiterals: true,
+ }]
+ react/jsx-closing-bracket-location: [error, {
+ selfClosing: after-props,
+ nonEmpty: after-props,
+ }]
diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml
new file mode 100644
index 00000000000..67e74085c7e
--- /dev/null
+++ b/tgui/.eslintrc.yml
@@ -0,0 +1,755 @@
+parser: babel-eslint
+parserOptions:
+ ecmaVersion: 2019
+ sourceType: module
+ ecmaFeatures:
+ jsx: true
+env:
+ es6: true
+ browser: true
+ node: true
+plugins:
+ - react
+settings:
+ react:
+ version: '16.10'
+rules:
+
+ ## Possible Errors
+ ## ----------------------------------------
+
+ ## Enforce “for” loop update clause moving the counter in the right
+ ## direction.
+ # for-direction: error
+ ## Enforce return statements in getters
+ # getter-return: error
+ ## Disallow using an async function as a Promise executor
+ no-async-promise-executor: error
+ ## Disallow await inside of loops
+ # no-await-in-loop: error
+ ## Disallow comparing against -0
+ # no-compare-neg-zero: error
+ ## Disallow assignment operators in conditional expressions
+ no-cond-assign: error
+ ## Disallow the use of console
+ # no-console: error
+ ## Disallow constant expressions in conditions
+ # no-constant-condition: error
+ ## Disallow control characters in regular expressions
+ # no-control-regex: error
+ ## Disallow the use of debugger
+ no-debugger: error
+ ## Disallow duplicate arguments in function definitions
+ no-dupe-args: error
+ ## Disallow duplicate keys in object literals
+ no-dupe-keys: error
+ ## Disallow duplicate case labels
+ no-duplicate-case: error
+ ## Disallow empty block statements
+ # no-empty: error
+ ## Disallow empty character classes in regular expressions
+ no-empty-character-class: error
+ ## Disallow reassigning exceptions in catch clauses
+ no-ex-assign: error
+ ## Disallow unnecessary boolean casts
+ no-extra-boolean-cast: error
+ ## Disallow unnecessary parentheses
+ # no-extra-parens: warn
+ ## Disallow unnecessary semicolons
+ no-extra-semi: error
+ ## Disallow reassigning function declarations
+ no-func-assign: error
+ ## Disallow assigning to imported bindings
+ no-import-assign: error
+ ## Disallow variable or function declarations in nested blocks
+ no-inner-declarations: error
+ ## Disallow invalid regular expression strings in RegExp constructors
+ no-invalid-regexp: error
+ ## Disallow irregular whitespace
+ no-irregular-whitespace: error
+ ## Disallow characters which are made with multiple code points in character
+ ## class syntax
+ no-misleading-character-class: error
+ ## Disallow calling global object properties as functions
+ no-obj-calls: error
+ ## Disallow calling some Object.prototype methods directly on objects
+ no-prototype-builtins: error
+ ## Disallow multiple spaces in regular expressions
+ no-regex-spaces: error
+ ## Disallow sparse arrays
+ no-sparse-arrays: error
+ ## Disallow template literal placeholder syntax in regular strings
+ no-template-curly-in-string: error
+ ## Disallow confusing multiline expressions
+ no-unexpected-multiline: error
+ ## Disallow unreachable code after return, throw, continue, and break
+ ## statements
+ # no-unreachable: warn
+ ## Disallow control flow statements in finally blocks
+ no-unsafe-finally: error
+ ## Disallow negating the left operand of relational operators
+ no-unsafe-negation: error
+ ## Disallow assignments that can lead to race conditions due to usage of
+ ## await or yield
+ # require-atomic-updates: error
+ ## Require calls to isNaN() when checking for NaN
+ use-isnan: error
+ ## Enforce comparing typeof expressions against valid strings
+ valid-typeof: error
+
+ ## Best practices
+ ## ----------------------------------------
+ ## Enforce getter and setter pairs in objects and classes
+ # accessor-pairs: error
+ ## Enforce return statements in callbacks of array methods
+ # array-callback-return: error
+ ## Enforce the use of variables within the scope they are defined
+ # block-scoped-var: error
+ ## Enforce that class methods utilize this
+ # class-methods-use-this: error
+ ## Enforce a maximum cyclomatic complexity allowed in a program
+ complexity: [error, { max: 50 }]
+ ## Require return statements to either always or never specify values
+ # consistent-return: error
+ ## Enforce consistent brace style for all control statements
+ curly: [error, all]
+ ## Require default cases in switch statements
+ # default-case: error
+ ## Enforce default parameters to be last
+ # default-param-last: error
+ ## Enforce consistent newlines before and after dots
+ dot-location: [error, property]
+ ## Enforce dot notation whenever possible
+ # dot-notation: error
+ ## Require the use of === and !==
+ eqeqeq: [error, always]
+ ## Require for-in loops to include an if statement
+ # guard-for-in: error
+ ## Enforce a maximum number of classes per file
+ # max-classes-per-file: error
+ ## Disallow the use of alert, confirm, and prompt
+ # no-alert: error
+ ## Disallow the use of arguments.caller or arguments.callee
+ # no-caller: error
+ ## Disallow lexical declarations in case clauses
+ no-case-declarations: error
+ ## Disallow division operators explicitly at the beginning of regular
+ ## expressions
+ # no-div-regex: error
+ ## Disallow else blocks after return statements in if statements
+ # no-else-return: error
+ ## Disallow empty functions
+ # no-empty-function: error
+ ## Disallow empty destructuring patterns
+ no-empty-pattern: error
+ ## Disallow null comparisons without type-checking operators
+ # no-eq-null: error
+ ## Disallow the use of eval()
+ # no-eval: error
+ ## Disallow extending native types
+ # no-extend-native: error
+ ## Disallow unnecessary calls to .bind()
+ # no-extra-bind: error
+ ## Disallow unnecessary labels
+ # no-extra-label: error
+ ## Disallow fallthrough of case statements
+ no-fallthrough: error
+ ## Disallow leading or trailing decimal points in numeric literals
+ # no-floating-decimal: error
+ ## Disallow assignments to native objects or read-only global variables
+ no-global-assign: error
+ ## Disallow shorthand type conversions
+ # no-implicit-coercion: error
+ ## Disallow variable and function declarations in the global scope
+ # no-implicit-globals: error
+ ## Disallow the use of eval()-like methods
+ # no-implied-eval: error
+ ## Disallow this keywords outside of classes or class-like objects
+ # no-invalid-this: error
+ ## Disallow the use of the __iterator__ property
+ # no-iterator: error
+ ## Disallow labeled statements
+ # no-labels: error
+ ## Disallow unnecessary nested blocks
+ # no-lone-blocks: error
+ ## Disallow function declarations that contain unsafe references inside
+ ## loop statements
+ # no-loop-func: error
+ ## Disallow magic numbers
+ # no-magic-numbers: error
+ ## Disallow multiple spaces
+ no-multi-spaces: warn
+ ## Disallow multiline strings
+ # no-multi-str: error
+ ## Disallow new operators outside of assignments or comparisons
+ # no-new: error
+ ## Disallow new operators with the Function object
+ # no-new-func: error
+ ## Disallow new operators with the String, Number, and Boolean objects
+ # no-new-wrappers: error
+ ## Disallow octal literals
+ no-octal: error
+ ## Disallow octal escape sequences in string literals
+ no-octal-escape: error
+ ## Disallow reassigning function parameters
+ # no-param-reassign: error
+ ## Disallow the use of the __proto__ property
+ # no-proto: error
+ ## Disallow variable redeclaration
+ no-redeclare: error
+ ## Disallow certain properties on certain objects
+ # no-restricted-properties: error
+ ## Disallow assignment operators in return statements
+ no-return-assign: error
+ ## Disallow unnecessary return await
+ # no-return-await: error
+ ## Disallow javascript: urls
+ # no-script-url: error
+ ## Disallow assignments where both sides are exactly the same
+ no-self-assign: error
+ ## Disallow comparisons where both sides are exactly the same
+ # no-self-compare: error
+ ## Disallow comma operators
+ no-sequences: error
+ ## Disallow throwing literals as exceptions
+ # no-throw-literal: error
+ ## Disallow unmodified loop conditions
+ # no-unmodified-loop-condition: error
+ ## Disallow unused expressions
+ # no-unused-expressions: error
+ ## Disallow unused labels
+ no-unused-labels: warn
+ ## Disallow unnecessary calls to .call() and .apply()
+ # no-useless-call: error
+ ## Disallow unnecessary catch clauses
+ # no-useless-catch: error
+ ## Disallow unnecessary concatenation of literals or template literals
+ # no-useless-concat: error
+ ## Disallow unnecessary escape characters
+ no-useless-escape: warn
+ ## Disallow redundant return statements
+ # no-useless-return: error
+ ## Disallow void operators
+ # no-void: error
+ ## Disallow specified warning terms in comments
+ # no-warning-comments: error
+ ## Disallow with statements
+ no-with: error
+ ## Enforce using named capture group in regular expression
+ # prefer-named-capture-group: error
+ ## Require using Error objects as Promise rejection reasons
+ # prefer-promise-reject-errors: error
+ ## Disallow use of the RegExp constructor in favor of regular expression
+ ## literals
+ # prefer-regex-literals: error
+ ## Enforce the consistent use of the radix argument when using parseInt()
+ radix: error
+ ## Disallow async functions which have no await expression
+ # require-await: error
+ ## Enforce the use of u flag on RegExp
+ # require-unicode-regexp: error
+ ## Require var declarations be placed at the top of their containing scope
+ # vars-on-top: error
+ ## Require parentheses around immediate function invocations
+ # wrap-iife: error
+ ## Require or disallow “Yoda” conditions
+ # yoda: error
+
+ ## Strict mode
+ ## ----------------------------------------
+ ## Require or disallow strict mode directives
+ strict: error
+
+ ## Variables
+ ## ----------------------------------------
+ ## Require or disallow initialization in variable declarations
+ # init-declarations: error
+ ## Disallow deleting variables
+ no-delete-var: error
+ ## Disallow labels that share a name with a variable
+ # no-label-var: error
+ ## Disallow specified global variables
+ # no-restricted-globals: error
+ ## Disallow variable declarations from shadowing variables declared in
+ ## the outer scope
+ # no-shadow: error
+ ## Disallow identifiers from shadowing restricted names
+ no-shadow-restricted-names: error
+ ## Disallow the use of undeclared variables unless mentioned
+ ## in /*global*/ comments
+ no-undef: error
+ ## Disallow initializing variables to undefined
+ no-undef-init: error
+ ## Disallow the use of undefined as an identifier
+ # no-undefined: error
+ ## Disallow unused variables
+ # no-unused-vars: error
+ ## Disallow the use of variables before they are defined
+ # no-use-before-define: error
+
+ ## Code style
+ ## ----------------------------------------
+ ## Enforce linebreaks after opening and before closing array brackets
+ array-bracket-newline: [error, consistent]
+ ## Enforce consistent spacing inside array brackets
+ array-bracket-spacing: [error, never]
+ ## Enforce line breaks after each array element
+ # array-element-newline: error
+ ## Disallow or enforce spaces inside of blocks after opening block and
+ ## before closing block
+ block-spacing: [error, always]
+ ## Enforce consistent brace style for blocks
+ # brace-style: [error, stroustrup, { allowSingleLine: false }]
+ ## Enforce camelcase naming convention
+ # camelcase: error
+ ## Enforce or disallow capitalization of the first letter of a comment
+ # capitalized-comments: error
+ ## Require or disallow trailing commas
+ comma-dangle: [error, {
+ arrays: always-multiline,
+ objects: always-multiline,
+ imports: always-multiline,
+ exports: always-multiline,
+ functions: only-multiline, ## Optional on functions
+ }]
+ ## Enforce consistent spacing before and after commas
+ comma-spacing: [error, { before: false, after: true }]
+ ## Enforce consistent comma style
+ comma-style: [error, last]
+ ## Enforce consistent spacing inside computed property brackets
+ computed-property-spacing: [error, never]
+ ## Enforce consistent naming when capturing the current execution context
+ # consistent-this: error
+ ## Require or disallow newline at the end of files
+ # eol-last: error
+ ## Require or disallow spacing between function identifiers and their
+ ## invocations
+ func-call-spacing: [error, never]
+ ## Require function names to match the name of the variable or property
+ ## to which they are assigned
+ # func-name-matching: error
+ ## Require or disallow named function expressions
+ # func-names: error
+ ## Enforce the consistent use of either function declarations or expressions
+ func-style: [error, expression]
+ ## Enforce line breaks between arguments of a function call
+ # function-call-argument-newline: error
+ ## Enforce consistent line breaks inside function parentheses
+ ## NOTE: This rule does not honor a newline on opening paren.
+ # function-paren-newline: [error, never]
+ ## Disallow specified identifiers
+ # id-blacklist: error
+ ## Enforce minimum and maximum identifier lengths
+ # id-length: error
+ ## Require identifiers to match a specified regular expression
+ # id-match: error
+ ## Enforce the location of arrow function bodies
+ # implicit-arrow-linebreak: error
+ ## Enforce consistent indentation
+ indent: [error, 2, { SwitchCase: 1 }]
+ ## Enforce the consistent use of either double or single quotes in JSX
+ ## attributes
+ jsx-quotes: [error, prefer-double]
+ ## Enforce consistent spacing between keys and values in object literal
+ ## properties
+ key-spacing: [error, { beforeColon: false, afterColon: true }]
+ ## Enforce consistent spacing before and after keywords
+ keyword-spacing: [error, { before: true, after: true }]
+ ## Enforce position of line comments
+ # line-comment-position: error
+ ## Enforce consistent linebreak style
+ # linebreak-style: error
+ ## Require empty lines around comments
+ # lines-around-comment: error
+ ## Require or disallow an empty line between class members
+ # lines-between-class-members: error
+ ## Enforce a maximum depth that blocks can be nested
+ # max-depth: error
+ ## Enforce a maximum line length
+ max-len: [error, {
+ code: 80,
+ ## Ignore imports
+ ignorePattern: '^(import\s.+\sfrom\s|.*require\()',
+ ignoreUrls: true,
+ ignoreRegExpLiterals: true,
+ }]
+ ## Enforce a maximum number of lines per file
+ # max-lines: error
+ ## Enforce a maximum number of line of code in a function
+ # max-lines-per-function: error
+ ## Enforce a maximum depth that callbacks can be nested
+ # max-nested-callbacks: error
+ ## Enforce a maximum number of parameters in function definitions
+ # max-params: error
+ ## Enforce a maximum number of statements allowed in function blocks
+ # max-statements: error
+ ## Enforce a maximum number of statements allowed per line
+ # max-statements-per-line: error
+ ## Enforce a particular style for multiline comments
+ # multiline-comment-style: error
+ ## Enforce newlines between operands of ternary expressions
+ multiline-ternary: [error, always-multiline]
+ ## Require constructor names to begin with a capital letter
+ # new-cap: error
+ ## Enforce or disallow parentheses when invoking a constructor with no
+ ## arguments
+ # new-parens: error
+ ## Require a newline after each call in a method chain
+ # newline-per-chained-call: error
+ ## Disallow Array constructors
+ # no-array-constructor: error
+ ## Disallow bitwise operators
+ # no-bitwise: error
+ ## Disallow continue statements
+ # no-continue: error
+ ## Disallow inline comments after code
+ # no-inline-comments: error
+ ## Disallow if statements as the only statement in else blocks
+ # no-lonely-if: error
+ ## Disallow mixed binary operators
+ # no-mixed-operators: error
+ ## Disallow mixed spaces and tabs for indentation
+ no-mixed-spaces-and-tabs: error
+ ## Disallow use of chained assignment expressions
+ # no-multi-assign: error
+ ## Disallow multiple empty lines
+ # no-multiple-empty-lines: error
+ ## Disallow negated conditions
+ # no-negated-condition: error
+ ## Disallow nested ternary expressions
+ # no-nested-ternary: error
+ ## Disallow Object constructors
+ # no-new-object: error
+ ## Disallow the unary operators ++ and --
+ # no-plusplus: error
+ ## Disallow specified syntax
+ # no-restricted-syntax: error
+ ## Disallow all tabs
+ # no-tabs: error
+ ## Disallow ternary operators
+ # no-ternary: error
+ ## Disallow trailing whitespace at the end of lines
+ # no-trailing-spaces: error
+ ## Disallow dangling underscores in identifiers
+ # no-underscore-dangle: error
+ ## Disallow ternary operators when simpler alternatives exist
+ # no-unneeded-ternary: error
+ ## Disallow whitespace before properties
+ no-whitespace-before-property: error
+ ## Enforce the location of single-line statements
+ # nonblock-statement-body-position: error
+ ## Enforce consistent line breaks inside braces
+ # object-curly-newline: [error, { multiline: true }]
+ ## Enforce consistent spacing inside braces
+ object-curly-spacing: [error, always]
+ ## Enforce placing object properties on separate lines
+ # object-property-newline: error
+ ## Enforce variables to be declared either together or separately in
+ ## functions
+ # one-var: error
+ ## Require or disallow newlines around variable declarations
+ # one-var-declaration-per-line: error
+ ## Require or disallow assignment operator shorthand where possible
+ # operator-assignment: error
+ ## Enforce consistent linebreak style for operators
+ operator-linebreak: [error, before]
+ ## Require or disallow padding within blocks
+ # padded-blocks: error
+ ## Require or disallow padding lines between statements
+ # padding-line-between-statements: error
+ ## Disallow using Object.assign with an object literal as the first
+ ## argument and prefer the use of object spread instead.
+ # prefer-object-spread: error
+ ## Require quotes around object literal property names
+ # quote-props: error
+ ## Enforce the consistent use of either backticks, double, or single quotes
+ # quotes: [error, single]
+ ## Require or disallow semicolons instead of ASI
+ semi: error
+ ## Enforce consistent spacing before and after semicolons
+ semi-spacing: [error, { before: false, after: true }]
+ ## Enforce location of semicolons
+ semi-style: [error, last]
+ ## Require object keys to be sorted
+ # sort-keys: error
+ ## Require variables within the same declaration block to be sorted
+ # sort-vars: error
+ ## Enforce consistent spacing before blocks
+ space-before-blocks: [error, always]
+ ## Enforce consistent spacing before function definition opening parenthesis
+ space-before-function-paren: [error, {
+ anonymous: always,
+ named: never,
+ asyncArrow: always,
+ }]
+ ## Enforce consistent spacing inside parentheses
+ space-in-parens: [error, never]
+ ## Require spacing around infix operators
+ # space-infix-ops: error
+ ## Enforce consistent spacing before or after unary operators
+ # space-unary-ops: error
+ ## Enforce consistent spacing after the // or /* in a comment
+ spaced-comment: [error, always]
+ ## Enforce spacing around colons of switch statements
+ switch-colon-spacing: [error, { before: false, after: true }]
+ ## Require or disallow spacing between template tags and their literals
+ template-tag-spacing: [error, never]
+ ## Require or disallow Unicode byte order mark (BOM)
+ # unicode-bom: [error, never]
+ ## Require parenthesis around regex literals
+ # wrap-regex: error
+
+ ## ES6
+ ## ----------------------------------------
+ ## Require braces around arrow function bodies
+ # arrow-body-style: error
+ ## Require parentheses around arrow function arguments
+ arrow-parens: [error, as-needed]
+ ## Enforce consistent spacing before and after the arrow in arrow functions
+ arrow-spacing: [error, { before: true, after: true }]
+ ## Require super() calls in constructors
+ # constructor-super: error
+ ## Enforce consistent spacing around * operators in generator functions
+ generator-star-spacing: [error, { before: false, after: true }]
+ ## Disallow reassigning class members
+ no-class-assign: error
+ ## Disallow arrow functions where they could be confused with comparisons
+ # no-confusing-arrow: error
+ ## Disallow reassigning const variables
+ no-const-assign: error
+ ## Disallow duplicate class members
+ no-dupe-class-members: error
+ ## Disallow duplicate module imports
+ # no-duplicate-imports: error
+ ## Disallow new operators with the Symbol object
+ no-new-symbol: error
+ ## Disallow specified modules when loaded by import
+ # no-restricted-imports: error
+ ## Disallow this/super before calling super() in constructors
+ no-this-before-super: error
+ ## Disallow unnecessary computed property keys in object literals
+ # no-useless-computed-key: error
+ ## Disallow unnecessary constructors
+ # no-useless-constructor: error
+ ## Disallow renaming import, export, and destructured assignments to the
+ ## same name
+ # no-useless-rename: error
+ ## Require let or const instead of var
+ no-var: error
+ ## Require or disallow method and property shorthand syntax for object
+ ## literals
+ # object-shorthand: error
+ ## Require using arrow functions for callbacks
+ prefer-arrow-callback: error
+ ## Require const declarations for variables that are never reassigned after
+ ## declared
+ # prefer-const: error
+ ## Require destructuring from arrays and/or objects
+ # prefer-destructuring: error
+ ## Disallow parseInt() and Number.parseInt() in favor of binary, octal, and
+ ## hexadecimal literals
+ # prefer-numeric-literals: error
+ ## Require rest parameters instead of arguments
+ # prefer-rest-params: error
+ ## Require spread operators instead of .apply()
+ # prefer-spread: error
+ ## Require template literals instead of string concatenation
+ # prefer-template: error
+ ## Require generator functions to contain yield
+ # require-yield: error
+ ## Enforce spacing between rest and spread operators and their expressions
+ # rest-spread-spacing: error
+ ## Enforce sorted import declarations within modules
+ # sort-imports: error
+ ## Require symbol descriptions
+ # symbol-description: error
+ ## Require or disallow spacing around embedded expressions of template
+ ## strings
+ # template-curly-spacing: error
+ ## Require or disallow spacing around the * in yield* expressions
+ yield-star-spacing: [error, { before: false, after: true }]
+
+ ## React
+ ## ----------------------------------------
+ ## Enforces consistent naming for boolean props
+ react/boolean-prop-naming: error
+ ## Forbid "button" element without an explicit "type" attribute
+ react/button-has-type: error
+ ## Prevent extraneous defaultProps on components
+ react/default-props-match-prop-types: error
+ ## Rule enforces consistent usage of destructuring assignment in component
+ # react/destructuring-assignment: [error, always, { ignoreClassFields: true }]
+ ## Prevent missing displayName in a React component definition
+ react/display-name: error
+ ## Forbid certain props on Components
+ # react/forbid-component-props: error
+ ## Forbid certain props on DOM Nodes
+ # react/forbid-dom-props: error
+ ## Forbid certain elements
+ # react/forbid-elements: error
+ ## Forbid certain propTypes
+ # react/forbid-prop-types: error
+ ## Forbid foreign propTypes
+ # react/forbid-foreign-prop-types: error
+ ## Prevent using this.state inside this.setState
+ react/no-access-state-in-setstate: error
+ ## Prevent using Array index in key props
+ # react/no-array-index-key: error
+ ## Prevent passing children as props
+ react/no-children-prop: error
+ ## Prevent usage of dangerous JSX properties
+ react/no-danger: error
+ ## Prevent problem with children and props.dangerouslySetInnerHTML
+ react/no-danger-with-children: error
+ ## Prevent usage of deprecated methods, including component lifecycle
+ ## methods
+ react/no-deprecated: error
+ ## Prevent usage of setState in componentDidMount
+ react/no-did-mount-set-state: error
+ ## Prevent usage of setState in componentDidUpdate
+ react/no-did-update-set-state: error
+ ## Prevent direct mutation of this.state
+ react/no-direct-mutation-state: error
+ ## Prevent usage of findDOMNode
+ react/no-find-dom-node: error
+ ## Prevent usage of isMounted
+ react/no-is-mounted: error
+ ## Prevent multiple component definition per file
+ # react/no-multi-comp: error
+ ## Prevent usage of shouldComponentUpdate when extending React.PureComponent
+ react/no-redundant-should-component-update: error
+ ## Prevent usage of the return value of React.render
+ react/no-render-return-value: error
+ ## Prevent usage of setState
+ # react/no-set-state: error
+ ## Prevent common casing typos
+ react/no-typos: error
+ ## Prevent using string references in ref attribute.
+ react/no-string-refs: error
+ ## Prevent using this in stateless functional components
+ react/no-this-in-sfc: error
+ ## Prevent invalid characters from appearing in markup
+ react/no-unescaped-entities: error
+ ## Prevent usage of unknown DOM property (fixable)
+ # react/no-unknown-property: error
+ ## Prevent usage of unsafe lifecycle methods
+ react/no-unsafe: error
+ ## Prevent definitions of unused prop types
+ react/no-unused-prop-types: error
+ ## Prevent definitions of unused state properties
+ react/no-unused-state: error
+ ## Prevent usage of setState in componentWillUpdate
+ react/no-will-update-set-state: error
+ ## Enforce ES5 or ES6 class for React Components
+ react/prefer-es6-class: error
+ ## Enforce that props are read-only
+ react/prefer-read-only-props: error
+ ## Enforce stateless React Components to be written as a pure function
+ react/prefer-stateless-function: error
+ ## Prevent missing props validation in a React component definition
+ # react/prop-types: error
+ ## Prevent missing React when using JSX
+ # react/react-in-jsx-scope: error
+ ## Enforce a defaultProps definition for every prop that is not a required
+ ## prop
+ # react/require-default-props: error
+ ## Enforce React components to have a shouldComponentUpdate method
+ # react/require-optimization: error
+ ## Enforce ES5 or ES6 class for returning value in render function
+ react/require-render-return: error
+ ## Prevent extra closing tags for components without children (fixable)
+ react/self-closing-comp: error
+ ## Enforce component methods order (fixable)
+ # react/sort-comp: error
+ ## Enforce propTypes declarations alphabetical sorting
+ # react/sort-prop-types: error
+ ## Enforce the state initialization style to be either in a constructor or
+ ## with a class property
+ react/state-in-constructor: error
+ ## Enforces where React component static properties should be positioned.
+ # react/static-property-placement: error
+ ## Enforce style prop value being an object
+ react/style-prop-object: error
+ ## Prevent void DOM elements (e.g.
,
) from receiving children
+ react/void-dom-elements-no-children: error
+
+ ## JSX-specific rules
+ ## ----------------------------------------
+ ## Enforce boolean attributes notation in JSX (fixable)
+ react/jsx-boolean-value: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions.
+ # react/jsx-child-element-spacing: error
+ ## Validate closing bracket location in JSX (fixable)
+ react/jsx-closing-bracket-location: [error, {
+ ## NOTE: Not really sure about enforcing this one
+ selfClosing: false,
+ nonEmpty: after-props,
+ }]
+ ## Validate closing tag location in JSX (fixable)
+ react/jsx-closing-tag-location: error
+ ## Enforce or disallow newlines inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-newline: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-spacing: error
+ ## Enforce or disallow spaces around equal signs in JSX attributes (fixable)
+ react/jsx-equals-spacing: error
+ ## Restrict file extensions that may contain JSX
+ # react/jsx-filename-extension: error
+ ## Enforce position of the first prop in JSX (fixable)
+ # react/jsx-first-prop-new-line: error
+ ## Enforce event handler naming conventions in JSX
+ react/jsx-handler-names: error
+ ## Validate JSX indentation (fixable)
+ react/jsx-indent: [error, 2, {
+ checkAttributes: true,
+ }]
+ ## Validate props indentation in JSX (fixable)
+ react/jsx-indent-props: [error, 2]
+ ## Validate JSX has key prop when in array or iterator
+ react/jsx-key: error
+ ## Validate JSX maximum depth
+ react/jsx-max-depth: [error, { max: 10 }] ## Generous
+ ## Limit maximum of props on a single line in JSX (fixable)
+ # react/jsx-max-props-per-line: error
+ ## Prevent usage of .bind() and arrow functions in JSX props
+ # react/jsx-no-bind: error
+ ## Prevent comments from being inserted as text nodes
+ react/jsx-no-comment-textnodes: error
+ ## Prevent duplicate props in JSX
+ react/jsx-no-duplicate-props: error
+ ## Prevent usage of unwrapped JSX strings
+ # react/jsx-no-literals: error
+ ## Prevent usage of unsafe target='_blank'
+ react/jsx-no-target-blank: error
+ ## Disallow undeclared variables in JSX
+ react/jsx-no-undef: error
+ ## Disallow unnecessary fragments (fixable)
+ react/jsx-no-useless-fragment: error
+ ## Limit to one expression per line in JSX
+ # react/jsx-one-expression-per-line: error
+ ## Enforce curly braces or disallow unnecessary curly braces in JSX
+ # react/jsx-curly-brace-presence: error
+ ## Enforce shorthand or standard form for React fragments
+ react/jsx-fragments: error
+ ## Enforce PascalCase for user-defined JSX components
+ react/jsx-pascal-case: error
+ ## Disallow multiple spaces between inline JSX props (fixable)
+ react/jsx-props-no-multi-spaces: error
+ ## Disallow JSX props spreading
+ # react/jsx-props-no-spreading: error
+ ## Enforce default props alphabetical sorting
+ # react/jsx-sort-default-props: error
+ ## Enforce props alphabetical sorting (fixable)
+ # react/jsx-sort-props: error
+ ## Validate whitespace in and around the JSX opening and closing brackets
+ ## (fixable)
+ react/jsx-tag-spacing: error
+ ## Prevent React to be incorrectly marked as unused
+ react/jsx-uses-react: error
+ ## Prevent variables used in JSX to be incorrectly marked as unused
+ react/jsx-uses-vars: error
+ ## Prevent missing parentheses around multilines JSX (fixable)
+ react/jsx-wrap-multilines: error
diff --git a/tgui/.gitattributes b/tgui/.gitattributes
new file mode 100644
index 00000000000..0016cc3bf67
--- /dev/null
+++ b/tgui/.gitattributes
@@ -0,0 +1,10 @@
+* text=auto
+
+## Enforce text mode and LF line breaks
+*.js text eol=lf
+*.css text eol=lf
+*.html text eol=lf
+*.json text eol=lf
+
+## Treat bundles as binary and ignore them during conflicts
+*.bundle.* binary merge=tgui-merge-bundle
diff --git a/tgui/.gitignore b/tgui/.gitignore
new file mode 100644
index 00000000000..416ca3768da
--- /dev/null
+++ b/tgui/.gitignore
@@ -0,0 +1,7 @@
+node_modules
+*.log
+package-lock.json
+
+/packages/tgui/public/.tmp/**/*
+/packages/tgui/public/**/*.hot-update.*
+/packages/tgui/public/**/*.map
diff --git a/tgui/README.md b/tgui/README.md
new file mode 100644
index 00000000000..5ddeb18fdda
--- /dev/null
+++ b/tgui/README.md
@@ -0,0 +1,187 @@
+# tgui
+
+## Introduction
+
+tgui is a robust user interface framework of /tg/station.
+
+tgui is very different from most UIs you will encounter in BYOND programming.
+It is heavily reliant on Javascript and web technologies as opposed to DM.
+If you are familiar with NanoUI (a library which can be found on almost
+every other SS13 codebase), tgui should be fairly easy to pick up.
+
+## Learn tgui
+
+People come to tgui from different backgrounds and with different
+learning styles. Whether you prefer a more theoretical or a practical
+approach, we hope you’ll find this section helpful.
+
+### Practical Tutorial
+
+If you are completely new to frontend and prefer to **learn by doing**,
+start with our [practical tutorial](docs/tutorial-and-examples.md).
+
+### Guides
+
+This project uses **Inferno** - a very fast UI rendering engine with a similar
+API to React. Take your time to read these guides:
+
+- [React guide](https://reactjs.org/docs/hello-world.html)
+- [Inferno documentation](https://infernojs.org/docs/guides/components) -
+highlights differences with React.
+
+If you were already familiar with an older, Ractive-based tgui, and want
+to translate concepts between old and new tgui, read this
+[interface conversion guide](docs/converting-old-tgui-interfaces.md).
+
+## Pre-requisites
+
+You will need these programs to start developing in tgui:
+
+- [Node v12.13+](https://nodejs.org/en/download/)
+- [Yarn v1.19+](https://yarnpkg.com/en/docs/install)
+- [MSys2](https://www.msys2.org/) (optional)
+
+> MSys2 closely replicates a unix-like environment which is necessary for
+> the `bin/tgui` script to run. It comes with a robust "mintty" terminal
+> emulator which is better than any standard Windows shell, it supports
+> "git" out of the box (almost like Git for Windows, but better), has
+> a "pacman" package manager, and you can install a text editor like "vim"
+> for a full boomer experience.
+
+## Usage
+
+**For MSys2, Git Bash, WSL, Linux or macOS users:**
+
+First and foremost, change your directory to `tgui`.
+
+Run `bin/tgui --install-git-hooks` (optional) to install merge drivers
+which will assist you in conflict resolution when rebasing your branches.
+
+Run one of the following:
+
+- `bin/tgui` - build the project in production mode.
+- `bin/tgui --dev` - launch a development server.
+ - tgui development server provides you with incremental compilation,
+ hot module replacement and logging facilities in all running instances
+ of tgui. In short, this means that you will instantly see changes in the
+ game as you code it. Very useful, highly recommended.
+ - In order to use it, you should start the game server first, connect to it
+ and wait until the world has been properly loaded and you are no longer
+ in the lobby. Start tgui dev server. You'll know that it's hooked correctly
+ if data gets dumped to the log when tgui windows are opened.
+- `bin/tgui --dev --reload` - reload byond cache once.
+- `bin/tgui --dev --debug` - run server with debug logging enabled.
+- `bin/tgui --dev --no-hot` - disable hot module replacement (helps when
+doing development on IE8).
+- `bin/tgui --lint` - show problems with the code.
+- `bin/tgui --lint --fix` - auto-fix problems with the code.
+- `bin/tgui --analyze` - run a bundle analyzer.
+- `bin/tgui --clean` - clean up project repo.
+- `bin/tgui [webpack options]` - build the project with custom webpack
+options.
+
+**For everyone else:**
+
+If you haven't opened the console already, you can do that by holding
+Shift and right clicking on the `tgui` folder, then pressing
+either `Open command window here` or `Open PowerShell window here`.
+
+Run `yarn install` to install npm dependencies, then one of the following:
+
+- `yarn run build` - build the project in production mode.
+- `yarn run watch` - launch a development server.
+- `yarn run lint` - show problems with the code.
+- `yarn run lint --fix` - auto-fix problems with the code.
+- `yarn run analyze` - run a bundle analyzer.
+
+We also got some batch files in store, for those who don't like fiddling
+with the console:
+
+- `bin/tgui-build.bat` - build the project in production mode.
+- `bin/tgui-dev-server.bat` - launch a development server.
+
+> Remember to always run a full build before submitting a PR. It creates
+> a compressed javascript bundle which is then referenced from DM code.
+> We prefer to keep it version controlled, so that people could build the
+> game just by using Dream Maker.
+
+## Troubleshooting
+
+**Development server doesn't find my BYOND cache!**
+
+This happens if your Documents folder in Windows has a custom location, for
+example in `E:\Libraries\Documents`. Development server has no knowledge
+of these non-standard locations, therefore you have to run the dev server
+with an additional environmental variable, with a full path to BYOND cache.
+
+```
+export BYOND_CACHE="E:/Libraries/Documents/BYOND/cache"
+bin/tgui --dev
+```
+
+Note that in Windows, you have to go through Advanced System Settings,
+System Properties and then open Environment Variables window to do the
+same thing. You may need to reboot after this.
+
+## Developer Tools
+
+When developing with `tgui-dev-server`, you will have access to certain
+development only features.
+
+**Debug Logs.**
+When running server via `bin/tgui --dev --debug`, server will print debug
+logs and time spent on rendering. Use this information to optimize your
+code, and try to keep re-renders below 16ms.
+
+**Kitchen Sink.**
+Press `Ctrl+Alt+=` to open the KitchenSink interface. This interface is a
+playground to test various tgui components.
+
+**Layout Debugger.**
+Press `Ctrl+Alt+-` to toggle the *layout debugger*. It will show outlines of
+all tgui elements, which makes it easy to understand how everything comes
+together, and can reveal certain layout bugs which are not normally visible.
+
+## Project Structure
+
+- `/packages` - Each folder here represents a self-contained Node module.
+- `/packages/common` - Helper functions
+- `/packages/tgui/index.js` - Application entry point.
+- `/packages/tgui/components` - Basic UI building blocks.
+- `/packages/tgui/interfaces` - Actual in-game interfaces.
+Interface takes data via the `state` prop and outputs an html-like stucture,
+which you can build using existing UI components.
+- `/packages/tgui/layouts` - Root level UI components, that affect the final
+look and feel of the browser window. They usually hold various window
+elements, like the titlebar and resize handlers, and control the UI theme.
+- `/packages/tgui/routes.js` - This is where tgui decides which interface to
+pull and render.
+- `/packages/tgui/layout.js` - A root-level component, holding the
+window elements, like the titlebar, buttons, resize handlers. Calls
+`routes.js` to decide which component to render.
+- `/packages/tgui/styles/main.scss` - CSS entry point.
+- `/packages/tgui/styles/functions.scss` - Useful SASS functions.
+Stuff like `lighten`, `darken`, `luminance` are defined here.
+- `/packages/tgui/styles/atomic` - Atomic CSS classes.
+These are very simple, tiny, reusable CSS classes which you can use and
+combine to change appearance of your elements. Keep them small.
+- `/packages/tgui/styles/components` - CSS classes which are used
+in UI components. These stylesheets closely follow the
+[BEM](https://en.bem.info/methodology/) methodology.
+- `/packages/tgui/styles/interfaces` - Custom stylesheets for your interfaces.
+Add stylesheets here if you really need a fine control over your UI styles.
+- `/packages/tgui/styles/layouts` - Layout-related styles.
+- `/packages/tgui/styles/themes` - Contains all the various themes you can
+use in tgui. Each theme must be registered in `webpack.config.js` file.
+
+## Component Reference
+
+See: [Component Reference](docs/component-reference.md).
+
+## License
+
+All code is licensed with the parent license of *tgstation*, **AGPL-3.0**.
+
+See the main [README](../README.md) for more details.
+
+The Authors retain all copyright to their respective work here submitted.
diff --git a/tgui/bin/tgui b/tgui/bin/tgui
new file mode 100755
index 00000000000..eb1f200b31e
--- /dev/null
+++ b/tgui/bin/tgui
@@ -0,0 +1,173 @@
+#!/bin/bash
+set -e
+shopt -s globstar
+shopt -s expand_aliases
+
+## Initial set-up
+## --------------------------------------------------------
+
+## Returns an absolute path to file
+alias tgui-realpath="readlink -f"
+
+## Fallbacks for GNU readlink
+## Detecting GNU coreutils http://stackoverflow.com/a/8748344/319952
+if ! readlink --version >/dev/null 2>&1; then
+ if hash greadlink 2>/dev/null; then
+ alias tgui-realpath="greadlink -f"
+ else
+ alias tgui-realpath="perl -MCwd -le 'print Cwd::abs_path(shift)'"
+ fi
+fi
+
+## Find a canonical path to project root
+base_dir="$(dirname "$(tgui-realpath "${0}")")/.."
+base_dir="$(tgui-realpath "${base_dir}")"
+
+## Add locally installed node programs to path
+PATH="${PATH}:node_modules/.bin"
+
+
+## Functions
+## --------------------------------------------------------
+
+## Installs node modules
+task-install() {
+ cd "${base_dir}"
+ yarn install
+}
+
+## Runs webpack
+task-webpack() {
+ cd "${base_dir}/packages/tgui"
+ webpack "${@}"
+}
+
+## Runs a development server
+task-dev-server() {
+ cd "${base_dir}/packages/tgui-dev-server"
+ exec node --experimental-modules index.js "${@}"
+}
+
+## Run a linter through all packages
+task-eslint() {
+ cd "${base_dir}"
+ eslint ./packages "${@}"
+}
+
+## Mr. Proper
+task-clean() {
+ cd "${base_dir}"
+ rm -rf packages/tgui/public/.tmp
+ rm -rf **/node_modules
+ rm -f **/package-lock.json
+}
+
+## Validates current build against the build stored in git
+task-validate-build() {
+ cd "${base_dir}"
+ local diff
+ diff="$(git diff packages/tgui/public/tgui.bundle.*)"
+ if [[ -n ${diff} ]]; then
+ echo "Error: our build differs from the build committed into git."
+ echo "Please rebuild tgui."
+ exit 1
+ fi
+ echo "tgui: build is ok"
+}
+
+## Installs merge drivers and git hooks
+task-install-git-hooks() {
+ cd "${base_dir}"
+ local git_root
+ local git_base_dir
+ git_root="$(git rev-parse --show-toplevel)"
+ git_base_dir="${base_dir/${git_root}/.}"
+ git config --replace-all merge.tgui-merge-bundle.driver \
+ "${git_base_dir}/bin/tgui --merge=bundle %O %A %B %L"
+ echo "tgui: Merge drivers have been successfully installed!"
+}
+
+## Bundle merge driver
+task-merge-bundle() {
+ local file_ancestor="${1}"
+ local file_current="${2}"
+ local file_other="${3}"
+ local conflict_marker_size="${4}"
+ echo "tgui: Discarding a local tgui build"
+ ## Do nothing (file_current will be merged and is what we want to keep).
+ exit 0
+}
+
+
+## Main
+## --------------------------------------------------------
+
+if [[ ${1} == "--merge"* ]]; then
+ if [[ ${1} == "--merge=bundle" ]]; then
+ shift 1
+ task-merge-bundle "${@}"
+ fi
+ echo "Unknown merge strategy: ${1}"
+ exit 1
+fi
+
+if [[ ${1} == "--install-git-hooks" ]]; then
+ shift 1
+ task-install-git-hooks
+ exit 0
+fi
+
+## Continuous integration scenario
+if [[ ${1} == "--ci" ]]; then
+ task-clean
+ task-install
+ task-eslint
+ task-webpack --mode=production
+ task-validate-build
+ exit 0
+fi
+
+if [[ ${1} == "--clean" ]]; then
+ task-clean
+ exit 0
+fi
+
+if [[ ${1} == "--dev" ]]; then
+ shift
+ task-install
+ task-dev-server "${@}"
+ exit 0
+fi
+
+if [[ ${1} == '--lint' ]]; then
+ shift 1
+ task-install
+ task-eslint "${@}"
+ exit 0
+fi
+
+if [[ ${1} == '--lint-harder' ]]; then
+ shift 1
+ task-install
+ task-eslint -c .eslintrc-harder.yml "${@}"
+ exit 0
+fi
+
+## Analyze the bundle
+if [[ ${1} == '--analyze' ]]; then
+ task-install
+ task-webpack --mode=production --analyze
+ exit 0
+fi
+
+## Make a production webpack build
+if [[ -z ${1} ]]; then
+ task-install
+ task-eslint
+ task-webpack --mode=production
+ exit 0
+fi
+
+## Run webpack with custom flags
+task-install
+task-webpack "${@}"
diff --git a/tgui/bin/tgui-build.bat b/tgui/bin/tgui-build.bat
new file mode 100644
index 00000000000..89e1aca9152
--- /dev/null
+++ b/tgui/bin/tgui-build.bat
@@ -0,0 +1,5 @@
+@echo off
+cd "%~dp0\.."
+call yarn install
+call yarn run build
+timeout /t 9
diff --git a/tgui/bin/tgui-dev-server.bat b/tgui/bin/tgui-dev-server.bat
new file mode 100644
index 00000000000..1b5bdcfb1db
--- /dev/null
+++ b/tgui/bin/tgui-dev-server.bat
@@ -0,0 +1,4 @@
+@echo off
+cd "%~dp0\.."
+call yarn install
+call yarn run watch
diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md
new file mode 100644
index 00000000000..b853fee4de3
--- /dev/null
+++ b/tgui/docs/component-reference.md
@@ -0,0 +1,1000 @@
+# Component Reference
+
+> Notice: This documentation might be out of date, so always check the source
+> code to see the most up-to-date information.
+
+
+
+- [General Concepts](#general-concepts)
+- [`tgui/components`](#tguicomponents)
+ - [`AnimatedNumber`](#animatednumber)
+ - [`BlockQuote`](#blockquote)
+ - [`Box`](#box)
+ - [`Button`](#button)
+ - [`Button.Checkbox`](#buttoncheckbox)
+ - [`Button.Confirm`](#buttonconfirm)
+ - [`Button.Input`](#buttoninput)
+ - [`ByondUi`](#byondui)
+ - [`Collapsible`](#collapsible)
+ - [`ColorBox`](#colorbox)
+ - [`Dimmer`](#dimmer)
+ - [`Divider`](#divider)
+ - [`Dropdown`](#dropdown)
+ - [`Flex`](#flex)
+ - [`Flex.Item`](#flexitem)
+ - [`Grid`](#grid)
+ - [`Grid.Column`](#gridcolumn)
+ - [`Icon`](#icon)
+ - [`Input`](#input)
+ - [`Knob`](#knob)
+ - [`LabeledControls`](#labeledcontrols)
+ - [`LabeledControls.Item`](#labeledcontrolsitem)
+ - [`LabeledList`](#labeledlist)
+ - [`LabeledList.Item`](#labeledlistitem)
+ - [`LabeledList.Divider`](#labeledlistdivider)
+ - [`Modal`](#modal)
+ - [`NoticeBox`](#noticebox)
+ - [`NumberInput`](#numberinput)
+ - [`ProgressBar`](#progressbar)
+ - [`Section`](#section)
+ - [`Slider`](#slider)
+ - [`Table`](#table)
+ - [`Table.Row`](#tablerow)
+ - [`Table.Cell`](#tablecell)
+ - [`Tabs`](#tabs)
+ - [`Tabs.Tab`](#tabstab)
+ - [`Tooltip`](#tooltip)
+- [`tgui/layouts`](#tguilayouts)
+ - [`Window`](#window)
+ - [`Window.Content`](#windowcontent)
+
+## General Concepts
+
+These are the components which you can use for interface construction.
+If you have trouble finding the exact prop you need on a component,
+please note, that most of these components inherit from other basic
+components, such as [Box](#box). This component in particular provides a lot
+of styling options for all components, e.g. `color` and `opacity`, thus
+it is used a lot in this framework.
+
+**Event handlers.**
+Event handlers are callbacks that you can attack to various element to
+listen for browser events. Inferno supports camelcase (`onClick`) and
+lowercase (`onclick`) event names.
+
+- Camel case names are what's called *synthetic* events, and are the
+**preferred way** of handling events in React, for efficiency and
+performance reasons. Please read
+[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling)
+to understand what this is about.
+- Lower case names are native browser events and should be used sparingly,
+for example when you need an explicit IE8 support. **DO NOT** use
+lowercase event handlers unless you really know what you are doing.
+- [Button](#button) component does not support the lowercase `onclick` event.
+Use the camel case `onClick` instead.
+
+## `tgui/components`
+
+### `AnimatedNumber`
+
+This component provides animations for numeric values.
+
+**Props:**
+
+- `value: number` - Value to animate.
+- `initial: number` - Initial value to use in animation when element
+first appears. If you set initial to `0` for example, number will always
+animate starting from `0`, and if omitted, it will not play an initial
+animation.
+- `format: value => value` - Output formatter.
+ - Example: `value => Math.round(value)`.
+- `children: (formattedValue, rawValue) => any` - Pull the animated number to
+animate more complex things deeper in the DOM tree.
+ - Example: `(_, value) => `
+
+### `BlockQuote`
+
+Just a block quote, just like this example in markdown:
+
+> Here's an example of a block quote.
+
+**Props:**
+
+- See inherited props: [Box](#box)
+
+### `Box`
+
+The Box component serves as a wrapper component for most of the CSS utility
+needs. It creates a new DOM element, a `