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/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/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/_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/_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/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/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..dadc8bc6e31 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() 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/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 8c47022974e..8cbee6cf6e6 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -41,7 +41,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station 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" @@ -88,39 +88,33 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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 @@ -134,7 +128,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/escape_pod2 name = "\improper Escape Pod Two" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod2/station @@ -148,7 +141,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/escape_pod3 name = "\improper Escape Pod Three" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod3/station @@ -162,7 +154,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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 @@ -176,7 +167,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/mining name = "\improper Mining Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/transport @@ -217,7 +207,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/siberia name = "\improper Labor Camp Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/specops @@ -300,7 +289,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/research name = "\improper Research Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/research/station @@ -469,6 +457,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station requires_power = FALSE dynamic_lighting = DYNAMIC_LIGHTING_FORCED nad_allowed = TRUE + ambientsounds = HIGHSEC_SOUNDS /area/syndicate_mothership/control name = "\improper Syndicate Control Room" @@ -490,6 +479,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station requires_power = FALSE valid_territory = FALSE dynamic_lighting = DYNAMIC_LIGHTING_FORCED + ambientsounds = MINING_SOUNDS /area/asteroid/cave // -- TLE name = "\improper Asteroid - Underground" @@ -604,7 +594,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //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 @@ -774,12 +764,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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" @@ -940,10 +929,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station 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" @@ -1078,7 +1069,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //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" @@ -1132,6 +1123,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station requires_power = FALSE valid_territory = FALSE dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT + ambientsounds = ENGINEERING_SOUNDS /area/solar/auxport name = "\improper Fore Port Solar Array" @@ -1198,18 +1190,17 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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" @@ -1221,42 +1212,39 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //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" @@ -1313,7 +1301,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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" @@ -1361,6 +1349,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //Security +/area/security + ambientsounds = HIGHSEC_SOUNDS + /area/security/main name = "\improper Security Office" icon_state = "securityoffice" @@ -1493,6 +1484,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /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" @@ -1841,6 +1833,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/construction name = "\improper Construction Area" icon_state = "yellow" + ambientsounds = ENGINEERING_SOUNDS /area/mining_construction name = "Auxillary Base Construction" @@ -1933,6 +1926,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/ai_monitored/storage/eva name = "EVA Storage" icon_state = "eva" + ambientsounds = HIGHSEC_SOUNDS /area/ai_monitored/storage/secure name = "Secure Storage" @@ -1943,7 +1937,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station 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" @@ -2005,7 +1999,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station // 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" @@ -2053,6 +2048,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Strange Location" icon_state = "away" report_alerts = FALSE + ambientsounds = AWAY_MISSION_SOUNDS /area/awaymission/example name = "\improper Strange Station" @@ -2067,7 +2063,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station 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/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..27097d64913 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 @@ -604,7 +605,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..c6f6218e969 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -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/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/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/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 96ee03283d3..8f8e7fdca9a 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,19 +62,34 @@ if(light_power && light_range) update_light() - if (opacity) + if(opacity) has_opaque_atom = 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) + return INITIALIZE_HINT_NORMAL + +/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) @@ -196,10 +219,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 @@ -212,7 +235,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) @@ -511,12 +533,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/world.dm b/code/game/world.dm index 7375ac1b5bc..a44495ef982 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -426,10 +426,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/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/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/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 673b6a14b9a..63fd1fa26b5 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/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/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/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/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..fd33d963566 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -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/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 21695e25e78..2114299f939 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -352,21 +352,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 +428,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/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/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/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/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/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 6f40dfe2a3f..7ebddfeec12 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -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..d6a86d3e3cd 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -20,8 +20,8 @@ AA.viewers -= src viewing_alternate_appearances = null logs.Cut() - ..() - return QDEL_HINT_HARDDEL + LAssailant = null + return ..() /mob/Initialize() GLOB.mob_list += src @@ -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) @@ -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..64444864804 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 diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index df2a39c0930..63175a68024 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. 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/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/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/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/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/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

- -
- - - -
- - {{:helper.cMirror("fSubmit")}} -
- -
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}} - {{if data.isAI}}{{/if}} - {{else value.sensor_type == 2}} - {{if data.isAI}}{{/if}} - {{else value.sensor_type == 3}} - {{if data.isAI}}{{/if}} - {{/if}} - {{/for}} -
{{:value.name}} ({{:value.assignment}}){{:value.dead ? "Deceased" : "Living"}}Not Available{{:helper.link('Track', null, {}, 'disabled')}}
{{:value.name}} ({{:value.assignment}}){{:value.dead ? "Deceased" : "Living"}} ({{:value.oxy}}/{{:value.tox}}/{{:value.fire}}/{{:value.brute}})Not Available{{:helper.link('Track', null, {}, 'disabled')}}
{{:value.name}} ({{:value.assignment}}){{:value.dead ? "Deceased" : "Living"}} ({{:value.oxy}}/{{:value.tox}}/{{:value.fire}}/{{:value.brute}}){{:value.area}}({{:value.x}}, {{:value.y}}){{:helper.link('Track', null, {'track' : value.ref})}}
\ 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}} -
- -
- {{/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:  - - - - -
\ 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}} -
-
-
- Power supply -
-
- {{:helper.displayBar(data.chargestatus, 0, 50, (data.chargestatus >= 35) ? 'good' : (data.chargestatus >= 15) ? 'average' : 'bad')}} {{:helper.smoothRound(data.charge)}}/{{:data.maxcharge}} -
-
-
-
-
- AI control: -
-
- {{if data.aioverride}} -
ENABLED
- {{else}} -
DISABLED
- {{/if}} -
-
- {{:helper.link('Toggle', 'arrow-circle-down', {'toggle_ai_control' : 1}, null)}} -
-
-
-
- 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 238c90c1e96..887bdec895a 100644 --- a/paradise.dme +++ b/paradise.dme @@ -244,6 +244,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" @@ -2134,7 +2135,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" @@ -2469,6 +2469,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" 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 `
` by default that can be changed +with the `as` property. Let's say you want to use a `` instead: + +```jsx + + +
+ {buttons && ( +
+ {buttons} +
+ )} +
+ {open && ( + + {children} + + )} + + ); + } +} diff --git a/tgui/packages/tgui/components/ColorBox.js b/tgui/packages/tgui/components/ColorBox.js new file mode 100644 index 00000000000..f37c4e057e4 --- /dev/null +++ b/tgui/packages/tgui/components/ColorBox.js @@ -0,0 +1,28 @@ +import { classes, pureComponentHooks } from 'common/react'; +import { computeBoxClassName, computeBoxProps } from './Box'; + +export const ColorBox = props => { + const { + content, + children, + className, + color, + backgroundColor, + ...rest + } = props; + rest.color = content ? null : 'transparent'; + rest.backgroundColor = color || backgroundColor; + return ( +
+ {content || '.'} +
+ ); +}; + +ColorBox.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/Dimmer.js b/tgui/packages/tgui/components/Dimmer.js new file mode 100644 index 00000000000..f38d0a026a6 --- /dev/null +++ b/tgui/packages/tgui/components/Dimmer.js @@ -0,0 +1,18 @@ +import { classes } from 'common/react'; +import { Box } from './Box'; + +export const Dimmer = props => { + const { className, children, ...rest } = props; + return ( + +
+ {children} +
+
+ ); +}; diff --git a/tgui/packages/tgui/components/Divider.js b/tgui/packages/tgui/components/Divider.js new file mode 100644 index 00000000000..5c807c290b8 --- /dev/null +++ b/tgui/packages/tgui/components/Divider.js @@ -0,0 +1,18 @@ +import { classes } from 'common/react'; + +export const Divider = props => { + const { + vertical, + hidden, + } = props; + return ( +