diff --git a/.travis.yml b/.travis.yml index 58cfbe98583..e4443d14ee0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,9 @@ jobs: - tools/travis/install_dreamchecker.sh script: - find . -name "*.php" -print0 | xargs -0 -n1 php -l - - find . -name "*.json" -not -path "./nano/node_modules/*" -print0 | xargs -0 python3 ./tools/travis/json_verifier.py + - find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/travis/json_verifier.py - tools/travis/build_nanoui.sh + - tools/travis/build_tgui.sh - tools/travis/check_grep.sh - python3 tools/travis/check_line_endings.py - ~/dreamchecker diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9ab4061ce6a..1c3b2d12286 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "gbasood.byond-dm-language-support", "platymuus.dm-langclient", - "EditorConfig.EditorConfig" + "EditorConfig.EditorConfig", + "dbaeumer.vscode-eslint" ] } diff --git a/_build_dependencies.sh b/_build_dependencies.sh index a6ce9f31cb5..1c164d3035d 100644 --- a/_build_dependencies.sh +++ b/_build_dependencies.sh @@ -1,8 +1,8 @@ # This file has all the information on what versions of libraries are thrown into the code # For dreamchecker export SPACEMANDMM_TAG=suite-1.4 -# For NanoUI -export NODE_VERSION=9 +# For NanoUI + TGUI +export NODE_VERSION=12 # For the scripts in tools export PHP_VERSION=5.6 # Byond Major diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm index 54ad9d08447..93815774e61 100644 --- a/_maps/map_files/Delta/delta.dmm +++ b/_maps/map_files/Delta/delta.dmm @@ -92226,6 +92226,9 @@ pressure_checks = 1 }, /obj/effect/decal/warning_stripes/yellow/hollow, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutralfull" @@ -94897,6 +94900,9 @@ on = 1 }, /obj/effect/decal/warning_stripes/yellow/hollow, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutralfull" diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm index 0c387859c7a..f7eda3fe8ee 100644 --- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm +++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm @@ -42197,9 +42197,6 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/hologram/holopad, -/obj/effect/landmark/start{ - name = "Cyborg" - }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -43998,6 +43995,9 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -44010,6 +44010,9 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -44025,6 +44028,9 @@ d2 = 8; icon_state = "4-8" }, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -45008,6 +45014,9 @@ icon_state = "1-4" }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 081839d2033..f3bc26c9600 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -91252,6 +91252,9 @@ initialize_directions = 11; level = 1 }, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "grimy" }, @@ -91732,6 +91735,9 @@ icon_state = "1-2"; pixel_y = 0 }, +/obj/effect/landmark/start{ + name = "Cyborg" + }, /turf/simulated/floor/plasteel{ icon_state = "grimy" }, @@ -96174,6 +96180,24 @@ }, /turf/simulated/floor/bluegrid, /area/tcommsat/chamber) +"rWq" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/effect/landmark/start{ + name = "Cyborg" + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "dark"; + tag = "icon-vault (NORTHEAST)" + }, +/area/turret_protected/aisat_interior) "rYq" = ( /obj/machinery/door/airlock/centcom{ id_tag = "adminshuttle"; @@ -133007,7 +133031,7 @@ diC diE diM djD -djJ +rWq dka djJ dkf diff --git a/_maps/map_files/generic/Lavaland.dmm b/_maps/map_files/generic/Lavaland.dmm index a3d4b951265..e44b9f52458 100644 --- a/_maps/map_files/generic/Lavaland.dmm +++ b/_maps/map_files/generic/Lavaland.dmm @@ -924,6 +924,7 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/production) "cn" = ( @@ -1008,6 +1009,11 @@ /obj/effect/turf_decal/tile/brown{ dir = 2 }, +/obj/machinery/camera{ + c_tag = "Shuttle Docking Foyer North"; + dir = 8; + network = list("Mining Outpost") + }, /turf/simulated/floor/plasteel, /area/mine/production) "cx" = ( @@ -1428,11 +1434,6 @@ /obj/machinery/light{ dir = 4 }, -/obj/machinery/camera{ - c_tag = "Shuttle Docking Foyer"; - dir = 8; - network = list("Mining Outpost") - }, /obj/machinery/newscaster{ pixel_x = 30; pixel_y = 1 @@ -1443,6 +1444,11 @@ /obj/effect/turf_decal/tile/brown{ dir = 2 }, +/obj/machinery/camera{ + c_tag = "Shuttle Docking Foyer South"; + dir = 8; + network = list("Mining Outpost") + }, /turf/simulated/floor/plasteel, /area/mine/production) "dw" = ( @@ -1897,6 +1903,7 @@ /obj/structure/cable{ icon_state = "1-4" }, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/living_quarters) "eu" = ( @@ -2019,6 +2026,7 @@ /obj/structure/cable{ icon_state = "2-8" }, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/production) "eD" = ( @@ -2396,6 +2404,7 @@ /obj/effect/turf_decal/tile/purple{ dir = 1 }, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/living_quarters) "fv" = ( @@ -2794,6 +2803,7 @@ dir = 1 }, /obj/effect/turf_decal/tile/bar, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/living_quarters) "gj" = ( @@ -3056,6 +3066,7 @@ dir = 6 }, /obj/effect/baseturf_helper/lava_land/surface, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel/dark, /area/mine/maintenance) "gJ" = ( @@ -4119,6 +4130,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, +/obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel, /area/mine/production) "BD" = ( @@ -4266,6 +4278,10 @@ }, /turf/simulated/floor/plasteel, /area/mine/production) +"Ww" = ( +/obj/machinery/hologram/holopad, +/turf/simulated/floor/plasteel, +/area/mine/eva) "WO" = ( /obj/effect/spawner/window, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ @@ -20693,7 +20709,7 @@ ab bg bs bY -bY +Ww bU cp bf diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 002b3ecc691..8dd95119aff 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -59,7 +59,7 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) for(var/mob/living/L in src) //ventcrawling is serious business L.remove_ventcrawl() L.forceMove(get_turf(src)) - QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else + QDEL_NULL(pipe_image) //we have to qdel it, or it might keep a ref somewhere else return ..() // Icons/overlays/underlays diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm index 6ec505d9f9a..30c56f93cc8 100644 --- a/code/LINDA/LINDA_fire.dm +++ b/code/LINDA/LINDA_fire.dm @@ -176,7 +176,7 @@ if(istype(loc, /turf/simulated)) var/turf/simulated/T = loc - if(T.to_be_destroyed) + if(T.to_be_destroyed && !T.changing_turf) var/chance_of_deletion if(T.heat_capacity) //beware of division by zero chance_of_deletion = T.max_fire_temperature_sustained / T.heat_capacity * 8 //there is no problem with prob(23456), min() was redundant --rastaf0 diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index c39226f37e6..acfa1983f72 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -74,8 +74,8 @@ air.temperature = temperature /turf/simulated/Destroy() - visibilityChanged() QDEL_NULL(active_hotspot) + QDEL_NULL(wet_overlay) return ..() /turf/simulated/assume_air(datum/gas_mixture/giver) diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index 58571e8b1ca..5fd85513501 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -145,7 +145,6 @@ #define RESISTCOLD "resist_cold" #define NO_EXAMINE "no_examine" #define CAN_WINGDINGS "can_wingdings" -#define NOZOMBIE "no_zombie" #define NO_GERMS "no_germs" #define NO_DECAY "no_decay" #define PIERCEIMMUNE "pierce_immunity" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index b52d08c1d23..2b8c515e81a 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -3,7 +3,6 @@ // Mobs #define ismegafauna(A) istype(A, /mob/living/simple_animal/hostile/megafauna) -#define iszombie(A) (is_species(A, /datum/species/zombie)) //Simple animals #define isshade(A) (istype(A, /mob/living/simple_animal/shade)) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index e1e15a926d6..719274857ec 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -196,7 +196,7 @@ #define isslimeperson(A) (is_species(A, /datum/species/slime)) #define isgrey(A) (is_species(A, /datum/species/grey)) #define isdiona(A) (is_species(A, /datum/species/diona)) -#define ismachine(A) (is_species(A, /datum/species/machine)) +#define ismachineperson(A) (is_species(A, /datum/species/machine)) #define isdrask(A) (is_species(A, /datum/species/drask)) #define isanimal(A) (istype((A), /mob/living/simple_animal)) diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index b0274e9d802..c15dfd78b0f 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -15,3 +15,57 @@ #define SOUND_MINIMUM_PRESSURE 10 #define FALLOFF_SOUNDS 0.5 + +//Ambience types + +#define GENERIC_SOUNDS list('sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg',\ + 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg',\ + 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen7.ogg',\ + 'sound/ambience/ambigen8.ogg', 'sound/ambience/ambigen9.ogg',\ + 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambigen11.ogg',\ + 'sound/ambience/ambigen12.ogg', 'sound/ambience/ambigen14.ogg', 'sound/ambience/ambigen15.ogg') + +#define HOLY_SOUNDS list('sound/ambience/ambicha1.ogg', 'sound/ambience/ambicha2.ogg', 'sound/ambience/ambicha3.ogg',\ + 'sound/ambience/ambicha4.ogg', 'sound/ambience/ambiholy.ogg', 'sound/ambience/ambiholy2.ogg',\ + 'sound/ambience/ambiholy3.ogg') + +#define HIGHSEC_SOUNDS list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg') + +#define RUINS_SOUNDS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\ + 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\ + 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\ + 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambitech3.ogg',\ + 'sound/ambience/ambimystery.ogg', 'sound/ambience/ambimaint1.ogg') + +#define ENGINEERING_SOUNDS list('sound/ambience/ambisin1.ogg', 'sound/ambience/ambisin2.ogg', 'sound/ambience/ambisin3.ogg', 'sound/ambience/ambisin4.ogg',\ + 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg') + +#define MINING_SOUNDS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\ + 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\ + 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\ + 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg',\ + 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava2.ogg', 'sound/ambience/ambilava3.ogg') + +#define MEDICAL_SOUNDS list('sound/ambience/ambinice.ogg') + +#define SPOOKY_SOUNDS list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/ambience/ambiruin7.ogg','sound/ambience/ambiruin6.ogg',\ + 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambimystery.ogg') + +#define SPACE_SOUNDS list('sound/ambience/ambispace.ogg', 'sound/ambience/ambispace2.ogg', 'sound/music/title2.ogg', 'sound/ambience/ambiatmos.ogg') + +#define MAINTENANCE_SOUNDS list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg',\ + 'sound/ambience/ambimaint5.ogg', 'sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg', 'sound/ambience/ambitech2.ogg' ) + +#define AWAY_MISSION_SOUNDS list('sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiruin.ogg',\ + 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\ + 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\ + 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint.ogg',\ + 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambiodd.ogg') + + + +#define CREEPY_SOUNDS list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\ + 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ + 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ + 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ + 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm index b0a74b6c702..c9763e4836c 100644 --- a/code/__DEFINES/stat.dm +++ b/code/__DEFINES/stat.dm @@ -3,7 +3,7 @@ #define UNCONSCIOUS 1 #define DEAD 2 -// NanoUI flags +// NanoUI & TGUI flags #define STATUS_INTERACTIVE 2 // GREEN Visability #define STATUS_UPDATE 1 // ORANGE Visability #define STATUS_DISABLED 0 // RED Visability diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index f5d582f5e42..a3154834fe4 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -143,6 +143,9 @@ GLOBAL_VAR_INIT(log_end, (world.system_type == UNIX ? ascii2text(13) : "")) /proc/log_runtime_summary(text) WRITE_LOG(GLOB.runtime_summary_log, "[text]") +/proc/log_tgui(text) + WRITE_LOG(GLOB.tgui_log, "[text]") + /** * Standardized method for tracking startup times. */ diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index b519130bb4f..bd077204f00 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -344,7 +344,7 @@ for(var/i = 1; i <= GLOB.player_list.len; i++) var/mob/M = GLOB.player_list[i] if(M && M.client) - if(istype(M, /mob/new_player)) // exclude people in the lobby + if(isnewplayer(M)) // exclude people in the lobby continue else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) var/mob/dead/observer/O = M diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index 549f5c71016..b66b1a11b90 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -16,8 +16,6 @@ GLOBAL_VAR_INIT(next_unique_datum_id, 1) -// /client/var/tmp/unique_datum_id = null - /datum/proc/UID() if(!unique_datum_id) var/tag_backup = tag @@ -37,8 +35,6 @@ GLOBAL_VAR_INIT(next_unique_datum_id, 1) var/datum/D = locate(copytext(uid, 1, splitat)) - // We might locate a client instead of a datum, but just using : is easier - // than actually checking and typecasting - if(D && D:unique_datum_id == uid) + if(D && D.unique_datum_id == uid) return D return null diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 75d9f5c79f4..b3b44335a6a 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -329,8 +329,9 @@ Turf and target are seperate in case you want to teleport some distance from a t /proc/select_active_ai_with_fewest_borgs() var/mob/living/silicon/ai/selected var/list/active = active_ais() - for(var/mob/living/silicon/ai/A in active) - if(!selected || (selected.connected_robots > A.connected_robots)) + for(var/thing in active) + var/mob/living/silicon/ai/A = thing + if(!selected || (length(selected.connected_robots) > length(A.connected_robots))) selected = A return selected diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 931a0e394e8..21af95cab48 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -16,7 +16,6 @@ GLOBAL_LIST_INIT(prisoncomputer_list, list()) GLOBAL_LIST_INIT(celltimers_list, list()) // list of all cell timers GLOBAL_LIST_INIT(cell_logs, list()) GLOBAL_LIST_INIT(navigation_computers, list()) -GLOBAL_LIST_INIT(zombie_infection_list, list()) GLOBAL_LIST_INIT(all_areas, list()) GLOBAL_LIST_INIT(machines, list()) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 0ea513708a1..f5f71e65c57 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -15,6 +15,8 @@ GLOBAL_VAR(world_asset_log) GLOBAL_PROTECT(world_asset_log) GLOBAL_VAR(runtime_summary_log) GLOBAL_PROTECT(runtime_summary_log) +GLOBAL_VAR(tgui_log) +GLOBAL_PROTECT(tgui_log) GLOBAL_LIST_EMPTY(jobMax) GLOBAL_PROTECT(jobMax) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 74591eb8070..c541e40768a 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -4,7 +4,7 @@ GLOBAL_DATUM(slmaster, /obj/effect/overlay) GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second) -// Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. +// Announcer intercom, because too much stuff creates an intercom for one message then qdel()s it. GLOBAL_DATUM_INIT(global_announcer, /obj/item/radio/intercom, create_global_announcer()) GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_command_announcer()) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 6f38814758a..f88244c0cd8 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -75,8 +75,8 @@ if(hitsound) playsound(loc, hitsound, get_clamped_volume(), 1, -1) - user.lastattacked = M - M.lastattacker = user + M.lastattacker = user.real_name + M.lastattackerckey = user.ckey user.do_attack_animation(M) . = M.attacked_by(src, user, def_zone) diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 80a9651f6ea..3452400edbb 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -63,7 +63,7 @@ if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the health_scan var. return if(user.client && user.health_scan) - if(issilicon(src) || ismachine(src)) + if(issilicon(src) || ismachineperson(src)) robot_healthscan(user, src) else if(ishuman(src)) healthscan(user, src, 1, TRUE) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index d348747fb80..083b347e52c 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -36,7 +36,7 @@ var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) var/vote_no_dead = 0 // dead people can't vote (tbi) // var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in + var/del_new_on_log = 1 // qdel's new players if they log before they spawn in var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard var/traitor_scaling = 0 //if amount of traitors scales based on amount of players var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm index 037b9a2bd0a..951a7fb1786 100644 --- a/code/controllers/subsystem/afk.dm +++ b/code/controllers/subsystem/afk.dm @@ -49,7 +49,7 @@ SUBSYSTEM_DEF(afk) if(mins_afk >= config.auto_cryo_afk && A.can_get_auto_cryod) if(A.fast_despawn) toRemove += H.ckey - warn(H, "You are have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.") + warn(H, "You have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.") log_afk_action(H, mins_afk, T, "despawned", "AFK in a fast despawn area") force_cryo_human(H) else @@ -67,7 +67,7 @@ SUBSYSTEM_DEF(afk) else if(afk_players[H.ckey] != AFK_ADMINS_WARNED && mins_afk >= config.auto_despawn_afk) log_afk_action(H, mins_afk, T, "despawned") - warn(H, "You are have been despawned after being AFK for [mins_afk] minutes.") + warn(H, "You have been despawned after being AFK for [mins_afk] minutes.") toRemove += H.ckey force_cryo_human(H) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index cbf17d05fbf..619508544a6 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -7,7 +7,7 @@ SUBSYSTEM_DEF(garbage) init_order = INIT_ORDER_GARBAGE offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended." - var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level + var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level //Stat tracking var/delslasttick = 0 // number of del()'s we've done this tick diff --git a/code/controllers/subsystem/nano_mob_hunter.dm b/code/controllers/subsystem/nano_mob_hunter.dm index 9094bf9fe2b..19b57f59a70 100644 --- a/code/controllers/subsystem/nano_mob_hunter.dm +++ b/code/controllers/subsystem/nano_mob_hunter.dm @@ -103,12 +103,12 @@ SUBSYSTEM_DEF(mob_hunt) return if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready) battle_turn = pick("Red", "Blue") - red_terminal.audible_message("Battle starting!", null, 5) - blue_terminal.audible_message("Battle starting!", null, 5) + red_terminal.atom_say("Battle starting!") + blue_terminal.atom_say("Battle starting!") if(battle_turn == "Red") - red_terminal.audible_message("Red Player's Turn!", null, 5) + red_terminal.atom_say("Red Player's Turn!") else if(battle_turn == "Blue") - blue_terminal.audible_message("Blue Player's Turn!", null, 5) + blue_terminal.atom_say("Blue Player's Turn!") /datum/controller/subsystem/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type) if(!team || !raw_damage) @@ -135,11 +135,11 @@ SUBSYSTEM_DEF(mob_hunt) winner_terminal.ready = 0 loser_terminal.ready = 0 if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle - winner_terminal.audible_message("Your rival surrendered!", null, 2) + winner_terminal.atom_say("Your rival surrendered!") else var/progress_message = winner_terminal.mob_info.gain_exp() - winner_terminal.audible_message("[winner_terminal.team] Player wins!", null, 5) - winner_terminal.audible_message(progress_message, null, 2) + winner_terminal.atom_say("[winner_terminal.team] Player wins!") + winner_terminal.atom_say(progress_message) /datum/controller/subsystem/mob_hunt/proc/end_turn() red_terminal.updateUsrDialog() @@ -148,7 +148,7 @@ SUBSYSTEM_DEF(mob_hunt) return if(battle_turn == "Red") battle_turn = "Blue" - blue_terminal.audible_message("Blue's turn.", null, 5) + blue_terminal.atom_say("Blue's turn.") else if(battle_turn == "Blue") battle_turn = "Red" - blue_terminal.audible_message("Red's turn.", null, 5) + blue_terminal.atom_say("Red's turn.") diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm new file mode 100644 index 00000000000..c4240021137 --- /dev/null +++ b/code/controllers/subsystem/tgui.dm @@ -0,0 +1,286 @@ + /** + * tgui subsystem + * + * Contains all tgui state and subsystem code. + **/ + + +SUBSYSTEM_DEF(tgui) + name = "TGUI" + wait = 9 + flags = SS_NO_INIT + priority = FIRE_PRIORITY_NANOUI // Yes I am aware that this is TGUI and I used the nanoUI fire priority. Dont @ me. + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + offline_implications = "All TGUIs will no longer process. Shuttle call recommended." + + var/list/currentrun = list() + var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key. + var/list/processing_uis = list() // A list of processing UIs, ungrouped. + var/basehtml // The HTML base used for all UIs. + +/datum/controller/subsystem/tgui/PreInit() + basehtml = file2text('tgui/packages/tgui/public/tgui.html') + +/datum/controller/subsystem/tgui/Shutdown() + close_all_uis() + +/datum/controller/subsystem/tgui/stat_entry() + ..("P:[processing_uis.len]") + +/datum/controller/subsystem/tgui/fire(resumed = 0) + if (!resumed) + src.currentrun = processing_uis.Copy() + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(currentrun.len) + var/datum/tgui/ui = currentrun[currentrun.len] + currentrun.len-- + if(ui && ui.user && ui.src_object) + ui.process() + else + processing_uis.Remove(ui) + if (MC_TICK_CHECK) + return + + /** + * public + * + * Get a open UI given a user, src_object, and ui_key and try to update it with data. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * required ui_key string The ui_key of the UI. + * optional ui datum/tgui The UI to be updated, if it exists. + * optional force_open bool If the UI should be re-opened instead of updated. + * + * return datum/tgui The found UI. + **/ +/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE) + if(isnull(ui)) // No UI was passed, so look for one. + ui = get_open_ui(user, src_object, ui_key) + + if(!isnull(ui)) + var/data = src_object.tgui_data(user) // Get data from the src_object. + if(!force_open) // UI is already open; update it. + ui.push_data(data) + else // Re-open it anyways. + ui.reinitialize(null, data) + return ui // We found the UI, return it. + else + return null // We couldn't find a UI. + + /** + * private + * + * Get a open UI given a user, src_object, and ui_key. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * required ui_key string The ui_key of the UI. + * + * return datum/tgui The found UI. + **/ +/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key) + var/src_object_key = "[src_object.UID()]" + if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return null // No UIs open. + else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) + return null // No UIs open for this object. + + for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object. + if(ui.user == user) // Make sure we have the right user + return ui + + return null // Couldn't find a UI! + + /** + * private + * + * Update all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs updated. + **/ +/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object) + var/src_object_key = "[src_object.UID()]" + if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return 0 // Couldn't find any UIs for this object. + + var/update_count = 0 + for(var/ui_key in open_uis[src_object_key]) + for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid. + ui.process(force = 1) // Update the UI. + update_count++ // Count each UI we update. + return update_count + + /** + * private + * + * Close all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs closed. + **/ +/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object) + var/src_object_key = "[src_object.UID()]" + if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return 0 // Couldn't find any UIs for this object. + + var/close_count = 0 + for(var/ui_key in open_uis[src_object_key]) + for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid. + ui.close() // Close the UI. + close_count++ // Count each UI we close. + return close_count + + /** + * private + * + * Close *ALL* UIs + * + * return int The number of UIs closed. + **/ +/datum/controller/subsystem/tgui/proc/close_all_uis() + var/close_count = 0 + for(var/src_object_key in open_uis) + for(var/ui_key in open_uis[src_object_key]) + for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid. + ui.close() // Close the UI. + close_count++ // Count each UI we close. + return close_count + + /** + * private + * + * Update all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only update UIs belonging this src_object. + * optional ui_key string If provided, only update UIs with this UI key. + * + * return int The number of UIs updated. + **/ +/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null) + if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0) + return 0 // Couldn't find any UIs for this user. + + var/update_count = 0 + for(var/datum/tgui/ui in user.open_tguis) + if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) + ui.process(force = 1) // Update the UI. + update_count++ // Count each UI we upadte. + return update_count + + /** + * private + * + * Close all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only close UIs belonging this src_object. + * optional ui_key string If provided, only close UIs with this UI key. + * + * return int The number of UIs closed. + **/ +/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null) + if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0) + return 0 // Couldn't find any UIs for this user. + + var/close_count = 0 + for(var/datum/tgui/ui in user.open_tguis) + if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) + ui.close() // Close the UI. + close_count++ // Count each UI we close. + return close_count + + /** + * private + * + * Add a UI to the list of open UIs. + * + * required ui datum/tgui The UI to be added. + **/ +/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui) + var/src_object_key = "[ui.src_object.UID()]" + if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object. + else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) + open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key. + + // Append the UI to all the lists. + ui.user.open_tguis |= ui + var/list/uis = open_uis[src_object_key][ui.ui_key] + uis |= ui + processing_uis |= ui + + /** + * private + * + * Remove a UI from the list of open UIs. + * + * required ui datum/tgui The UI to be removed. + * + * return bool If the UI was removed or not. + **/ +/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui) + var/src_object_key = "[ui.src_object.UID()]" + if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return FALSE // It wasn't open. + else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) + return FALSE // It wasn't open. + + processing_uis.Remove(ui) // Remove it from the list of processing UIs. + if(ui.user) // If the user exists, remove it from them too. + ui.user.open_tguis.Remove(ui) + var/Ukey = ui.ui_key + var/list/uis = open_uis[src_object_key][Ukey] // Remove it from the list of open UIs. + uis.Remove(ui) + if(!uis.len) + var/list/uiobj = open_uis[src_object_key] + uiobj.Remove(Ukey) + if(!uiobj.len) + open_uis.Remove(src_object_key) + + return TRUE // Let the caller know we did it. + + /** + * private + * + * Handle client logout, by closing all their UIs. + * + * required user mob The mob which logged out. + * + * return int The number of UIs closed. + **/ +/datum/controller/subsystem/tgui/proc/on_logout(mob/user) + return close_user_uis(user) + + /** + * private + * + * Handle clients switching mobs, by transferring their UIs. + * + * required user source The client's original mob. + * required user target The client's new mob. + * + * return bool If the UIs were transferred. + **/ +/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target) + if(!source || isnull(source.open_tguis) || !istype(source.open_tguis, /list) || open_uis.len == 0) + return FALSE // The old mob had no open UIs. + + if(isnull(target.open_tguis) || !istype(target.open_tguis, /list)) + target.open_tguis = list() // Create a list for the new mob if needed. + + for(var/datum/tgui/ui in source.open_tguis) + ui.user = target // Inform the UIs of their new owner. + target.open_tguis.Add(ui) // Transfer all the UIs. + + source.open_tguis.Cut() // Clear the old list. + return TRUE // Let the caller know we did it. diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 475bdaccb3c..6f8dd85b79b 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -407,7 +407,7 @@ SUBSYSTEM_DEF(ticker) EquipCustomItems(player) if(captainless) for(var/mob/M in GLOB.player_list) - if(!istype(M,/mob/new_player)) + if(!isnewplayer(M)) to_chat(M, "Captainship not forced on anyone.") /datum/controller/subsystem/ticker/proc/send_tip_of_the_round() diff --git a/code/datums/components/ducttape.dm b/code/datums/components/ducttape.dm index 98512589a46..49931db8edc 100644 --- a/code/datums/components/ducttape.dm +++ b/code/datums/components/ducttape.dm @@ -37,10 +37,10 @@ I.anchored = initial(I.anchored) for(var/datum/action/item_action/remove_tape/RT in I.actions) RT.Remove(user) - RT.Destroy() + qdel(RT) I.overlays.Cut(tape_overlay) user.transfer_fingerprints_to(I) - Destroy() + qdel(src) /datum/component/ducttape/proc/afterattack(obj/item/I, atom/target, mob/user, proximity, params) if(!proximity) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index b2b09003934..e8d0b881ccb 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -819,6 +819,59 @@ message_admins("[key_name(usr)] has made [A] process normally") return TRUE + else if(href_list["modifyarmor"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + var/obj/A = locateUID(href_list["modifyarmor"]) + if(!istype(A)) + return + A.var_edited = TRUE + var/list/armorlist = A.armor.getList() + var/list/displaylist + + var/result + do + displaylist = list() + for(var/key in armorlist) + displaylist += "[key] = [armorlist[key]]" + result = input(usr, "Select an armor type to modify..", "Modify armor") as null|anything in displaylist + "(ADD ALL)" + "(SET ALL)" + "(DONE)" + + if(result == "(DONE)") + break + else if(result == "(ADD ALL)" || result == "(SET ALL)") + var/new_amount = input(usr, result == "(ADD ALL)" ? "Enter armor to add to all types:" : "Enter new armor value for all types:", "Modify all types") as num|null + if(isnull(new_amount)) + continue + var/proper_amount = text2num(new_amount) + if(isnull(proper_amount)) + continue + for(var/key in armorlist) + armorlist[key] = (result == "(ADD ALL)" ? armorlist[key] : 0) + proper_amount + else if(result) + var/list/fields = splittext(result, " = ") + if(length(fields) != 2) + continue + var/type = fields[1] + if(isnull(armorlist[type])) + continue + var/new_amount = input(usr, "Enter new armor value for [type]:", "Modify [type]") as num|null + if(isnull(new_amount)) + continue + var/proper_amount = text2num(new_amount) + if(isnull(proper_amount)) + continue + armorlist[type] = proper_amount + while(result) + + if(!result || !A) + return TRUE + + A.armor = A.armor.setRating(armorlist["melee"], armorlist["bullet"], armorlist["laser"], armorlist["energy"], armorlist["bomb"], armorlist["bio"], armorlist["rad"], armorlist["fire"], armorlist["acid"], armorlist["magic"]) + + log_admin("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]") + message_admins("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]") + return TRUE + else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */ if(!check_rights(R_DEBUG|R_ADMIN)) return diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm index 3afb48ce534..e8486a8f408 100644 --- a/code/datums/diseases/critical.dm +++ b/code/datums/diseases/critical.dm @@ -159,7 +159,7 @@ var/mob/living/carbon/human/H = affected_mob if(NO_HUNGER in H.dna.species.species_traits) return TRUE - if(ismachine(H)) + if(ismachineperson(H)) return TRUE return ..() diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 7d3a73abf24..54635f01b0a 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -39,7 +39,6 @@ var/role_alt_title var/datum/job/assigned_job - var/list/kills = list() var/list/datum/objective/objectives = list() var/list/datum/objective/special_verbs = list() var/list/targets = list() @@ -90,6 +89,9 @@ if(antag_datum.delete_on_mind_deletion) qdel(i) antag_datums = null + current = null + original = null + soulOwner = null return ..() /datum/mind/proc/transfer_to(mob/living/new_character) diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm index 2a8e1d94526..a0c41b57001 100644 --- a/code/datums/spells/summonitem.dm +++ b/code/datums/spells/summonitem.dm @@ -75,8 +75,6 @@ B.transfer_identity(C) C.death() add_attack_logs(target, C, "Magically debrained INTENT: [uppertext(target.a_intent)]")*/ - if(C.stomach_contents && (item_to_retrieve in C.stomach_contents)) - C.stomach_contents -= item_to_retrieve for(var/X in C.bodyparts) var/obj/item/organ/external/part = X if(item_to_retrieve in part.embedded_objects) diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index a0c9a19e4b0..8dbdfd95420 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -188,15 +188,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/fake_revolver cost = 1 job = list("Clown") -/* -/datum/uplink_item/stealthy_weapons/romerol_kit - name = "Romerol" - reference = "ROM" - desc = "A highly experimental bioterror agent which creates dormant nodules to be etched into the grey matter of the brain. On death, these nodules take control of the dead body, causing limited revivification, along with slurred speech, aggression, and the ability to infect others with this agent." - item = /obj/item/storage/box/syndie_kit/romerol - cost = 25 - cant_discount = TRUE -*/ //mime /datum/uplink_item/jobspecific/caneshotgun name = "Cane Shotgun and Assassination Shells" diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index e3d90b8d075..dcde08d7ec1 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -166,9 +166,9 @@ //raises them if they are down (only if power's on) if(!A.locked) if(A.lock()) - A.audible_message("You hear a click from the bottom of the door.", null, 1) + A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1) else if(A.unlock()) - A.audible_message("You hear a click from the bottom of the door.", null, 1) + A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1) if(AIRLOCK_WIRE_BACKUP_POWER1) //two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 094e4f9ca2b..8f3eeeaa6fa 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -29,8 +29,9 @@ var/laname var/lakey if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) + laname = sanitizeSQL(H.lastattacker) + if(H.lastattackerckey) + lakey = sanitizeSQL(H.lastattackerckey) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" // to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") @@ -64,8 +65,9 @@ var/laname var/lakey if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) + laname = sanitizeSQL(H.lastattacker) + if(H.lastattackerckey) + lakey = sanitizeSQL(H.lastattackerckey) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" // to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index a57f48ffc11..9070d1409f6 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) power_environ = FALSE valid_territory = FALSE outdoors = TRUE - ambientsounds = list('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/traitor.ogg') + ambientsounds = SPACE_SOUNDS /area/space/nearstation icon_state = "space_near" @@ -117,39 +117,33 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/escape name = "\improper Emergency Shuttle" - music = "music/escape.ogg" icon_state = "shuttle2" nad_allowed = TRUE /area/shuttle/pod_1 name = "\improper Escape Pod One" - music = "music/escape.ogg" icon_state = "shuttle" nad_allowed = TRUE /area/shuttle/pod_2 name = "\improper Escape Pod Two" - music = "music/escape.ogg" icon_state = "shuttle" nad_allowed = TRUE /area/shuttle/pod_3 name = "\improper Escape Pod Three" - music = "music/escape.ogg" icon_state = "shuttle" nad_allowed = TRUE parallax_movedir = EAST /area/shuttle/pod_4 name = "\improper Escape Pod Four" - music = "music/escape.ogg" icon_state = "shuttle" nad_allowed = TRUE parallax_movedir = EAST /area/shuttle/escape_pod1 name = "\improper Escape Pod One" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod1/station @@ -163,7 +157,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/escape_pod2 name = "\improper Escape Pod Two" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod2/station @@ -177,7 +170,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/escape_pod3 name = "\improper Escape Pod Three" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod3/station @@ -191,7 +183,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/escape_pod5 //Pod 4 was lost to meteors name = "\improper Escape Pod Five" - music = "music/escape.ogg" nad_allowed = TRUE /area/shuttle/escape_pod5/station @@ -205,7 +196,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/mining name = "\improper Mining Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/transport @@ -246,7 +236,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/siberia name = "\improper Labor Camp Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/specops @@ -329,7 +318,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/shuttle/research name = "\improper Research Shuttle" - music = "music/escape.ogg" icon_state = "shuttle" /area/shuttle/research/station @@ -498,6 +486,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) requires_power = FALSE dynamic_lighting = DYNAMIC_LIGHTING_FORCED nad_allowed = TRUE + ambientsounds = HIGHSEC_SOUNDS /area/syndicate_mothership/control name = "\improper Syndicate Control Room" @@ -519,6 +508,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) requires_power = FALSE valid_territory = FALSE dynamic_lighting = DYNAMIC_LIGHTING_FORCED + ambientsounds = MINING_SOUNDS /area/asteroid/cave // -- TLE name = "\improper Asteroid - Underground" @@ -633,7 +623,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) //Maintenance /area/maintenance - ambientsounds = list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg', 'sound/ambience/ambimaint5.ogg') + ambientsounds = MAINTENANCE_SOUNDS valid_territory = FALSE /area/maintenance/atmos_control @@ -803,12 +793,11 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/bridge name = "\improper Bridge" icon_state = "bridge" - music = "signal" + ambientsounds = list('sound/ambience/signal.ogg') /area/bridge/meeting_room name = "\improper Heads of Staff Meeting Room" icon_state = "meeting" - music = null /area/crew_quarters/captain name = "\improper Captain's Office" @@ -969,10 +958,12 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) name = "\improper Abandoned Library" icon_state = "library" +/area/chapel + icon_state = "chapel" + ambientsounds = HOLY_SOUNDS + /area/chapel/main name = "\improper Chapel" - icon_state = "chapel" - ambientsounds = list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg') /area/chapel/office name = "\improper Chapel Office" @@ -1107,7 +1098,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) //Engineering /area/engine - ambientsounds = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg') + ambientsounds = ENGINEERING_SOUNDS /area/engine/engine_smes name = "\improper Engineering SMES" @@ -1161,6 +1152,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) requires_power = FALSE valid_territory = FALSE dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT + ambientsounds = ENGINEERING_SOUNDS /area/solar/auxport name = "\improper Fore Port Solar Array" @@ -1227,18 +1219,17 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/teleporter name = "\improper Teleporter" icon_state = "teleporter" - music = "signal" + ambientsounds = ENGINEERING_SOUNDS /area/gateway name = "\improper Gateway" icon_state = "teleporter" - music = "signal" + ambientsounds = ENGINEERING_SOUNDS /area/AIsattele name = "\improper Abandoned Teleporter" icon_state = "teleporter" - music = "signal" - ambientsounds = list('sound/ambience/ambimalf.ogg') + ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/signal.ogg') /area/toxins/explab name = "\improper E.X.P.E.R.I-MENTOR Lab" @@ -1250,42 +1241,39 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) //MedBay +/area/medical + ambientsounds = MEDICAL_SOUNDS + /area/medical/medbay name = "\improper Medbay" icon_state = "medbay" - music = 'sound/ambience/signal.ogg' //Medbay is a large area, these additional areas help level out APC load. /area/medical/medbay2 name = "\improper Medbay" icon_state = "medbay2" - music = 'sound/ambience/signal.ogg' /area/medical/medbay3 name = "\improper Medbay" icon_state = "medbay3" - music = 'sound/ambience/signal.ogg' /area/medical/biostorage name = "\improper Medical Storage" icon_state = "medbaysecstorage" - music = 'sound/ambience/signal.ogg' /area/medical/reception name = "\improper Medbay Reception" icon_state = "medbay" - music = 'sound/ambience/signal.ogg' /area/medical/psych name = "\improper Psych Room" icon_state = "medbaypsych" - music = 'sound/ambience/signal.ogg' + ambientsounds = list('sound/ambience/aurora_caelus_short.ogg') /area/medical/medbreak name = "\improper Break Room" icon_state = "medbaybreak" - music = 'sound/ambience/signal.ogg' /area/medical/patients_rooms name = "\improper Patient's Rooms" @@ -1342,7 +1330,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/medical/morgue name = "\improper Morgue" icon_state = "morgue" - ambientsounds = list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg') + ambientsounds = SPOOKY_SOUNDS /area/medical/chemistry name = "\improper Chemistry" @@ -1390,6 +1378,9 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) //Security +/area/security + ambientsounds = HIGHSEC_SOUNDS + /area/security/main name = "\improper Security Office" icon_state = "securityoffice" @@ -1522,6 +1513,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/security/detectives_office name = "\improper Detective's Office" icon_state = "detective" + ambientsounds = list('sound/ambience/ambidet1.ogg', 'sound/ambience/ambidet2.ogg') /area/security/range name = "\improper Firing Range" @@ -1870,6 +1862,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/construction name = "\improper Construction Area" icon_state = "yellow" + ambientsounds = ENGINEERING_SOUNDS /area/mining_construction name = "Auxillary Base Construction" @@ -1962,6 +1955,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) /area/ai_monitored/storage/eva name = "EVA Storage" icon_state = "eva" + ambientsounds = HIGHSEC_SOUNDS /area/ai_monitored/storage/secure name = "Secure Storage" @@ -1972,7 +1966,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) icon_state = "storage" /area/turret_protected/ - ambientsounds = list('sound/ambience/ambimalf.ogg') + ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg') /area/turret_protected/ai_upload name = "\improper AI Upload Chamber" @@ -2034,7 +2028,8 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) // Telecommunications Satellite /area/tcommsat - ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg') + ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambitech.ogg',\ + 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg', 'sound/ambience/ambimystery.ogg') /area/tcommsat/chamber name = "\improper Telecoms Central Compartment" @@ -2082,6 +2077,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) name = "\improper Strange Location" icon_state = "away" report_alerts = FALSE + ambientsounds = AWAY_MISSION_SOUNDS /area/awaymission/example name = "\improper Strange Station" @@ -2096,7 +2092,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs) icon_state = "beach" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED requires_power = FALSE - ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg') + ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambinice.ogg') /area/awaymission/undersea name = "Undersea" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 6ad45532641..da80a6e775c 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -26,7 +26,6 @@ var/power_equip = TRUE var/power_light = TRUE var/power_environ = TRUE - var/music = null var/used_equip = FALSE var/used_light = FALSE var/used_environ = FALSE @@ -56,12 +55,7 @@ var/global/global_uid = 0 var/uid - var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ - 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ - 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ - 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ - 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ - 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') + var/list/ambientsounds = GENERIC_SOUNDS var/fast_despawn = FALSE var/can_get_auto_cryod = TRUE @@ -379,20 +373,24 @@ // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys - L.client.ambience_playing = 1 - L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) + L.client.ambience_playing = TRUE + SEND_SOUND(L, sound('sound/ambience/shipambience.ogg', repeat = TRUE, wait = FALSE, volume = 35, channel = CHANNEL_BUZZ)) else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) - L.client.ambience_playing = 0 + L.client.ambience_playing = FALSE if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) var/sound = pick(ambientsounds) if(!L.client.played) - L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) - L.client.played = 1 - spawn(600) //ewww - this is very very bad - if(L && L.client) - L.client.played = 0 + SEND_SOUND(L, sound(sound, repeat = FALSE, wait = FALSE, volume = 25, channel = CHANNEL_AMBIENCE)) + L.client.played = TRUE + addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600) + +/** + * Reset the played var to false on the client + */ +/client/proc/ResetAmbiencePlayed() + played = FALSE /area/proc/gravitychange(var/gravitystate = 0, var/area/A) A.has_gravity = gravitystate diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm index 0b07fffda5e..b621bade493 100644 --- a/code/game/area/areas/mining.dm +++ b/code/game/area/areas/mining.dm @@ -7,7 +7,6 @@ /area/mine/explored name = "Mine" icon_state = "explored" - music = null always_unpowered = TRUE requires_power = TRUE poweralm = FALSE @@ -15,7 +14,7 @@ power_equip = FALSE power_light = FALSE outdoors = TRUE - ambientsounds = list('sound/ambience/ambimine.ogg') + ambientsounds = MINING_SOUNDS flags = NONE /area/mine/dangerous/explored/golem @@ -24,7 +23,6 @@ /area/mine/unexplored name = "Mine" icon_state = "unexplored" - music = null always_unpowered = TRUE requires_power = TRUE poweralm = FALSE @@ -32,7 +30,7 @@ power_equip = FALSE power_light = FALSE outdoors = TRUE - ambientsounds = list('sound/ambience/ambimine.ogg') + ambientsounds = MINING_SOUNDS flags = NONE /area/mine/lobby @@ -80,6 +78,7 @@ /area/mine/laborcamp/security name = "Labor Camp Security" icon_state = "security" + ambientsounds = HIGHSEC_SOUNDS /area/mine/podbay name = "Mining Podbay" @@ -95,26 +94,24 @@ /area/lavaland/surface name = "Lavaland" icon_state = "explored" - music = null always_unpowered = TRUE poweralm = FALSE power_environ = FALSE power_equip = FALSE power_light = FALSE requires_power = TRUE - ambientsounds = list('sound/ambience/ambilava.ogg') + ambientsounds = MINING_SOUNDS /area/lavaland/underground name = "Lavaland Caves" icon_state = "unexplored" - music = null always_unpowered = TRUE requires_power = TRUE poweralm = FALSE power_environ = FALSE power_equip = FALSE power_light = FALSE - ambientsounds = list('sound/ambience/ambilava.ogg') + ambientsounds = MINING_SOUNDS /area/lavaland/surface/outdoors name = "Lavaland Wastes" diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm index 5baf1fe315e..53544d8643b 100644 --- a/code/game/area/areas/ruins/lavaland.dm +++ b/code/game/area/areas/ruins/lavaland.dm @@ -5,6 +5,7 @@ /area/ruin/powered/clownplanet icon_state = "dk_yellow" + ambientsounds = list('sound/music/clown.ogg') /area/ruin/powered/animal_hospital icon_state = "dk_yellow" @@ -38,7 +39,7 @@ /area/ruin/unpowered/syndicate_lava_base name = "Secret Base" icon_state = "dk_yellow" - ambientsounds = list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg') + ambientsounds = HIGHSEC_SOUNDS report_alerts = FALSE hide_attacklogs = TRUE diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5d3c6d688ac..776a2bccd12 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -14,6 +14,7 @@ var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. var/simulated = TRUE //filter for actions - used by lighting overlays var/atom_say_verb = "says" + var/bubble_icon = "default" ///what icon the mob uses for speechbubbles var/dont_save = FALSE // For atoms that are temporary by necessity - like lighting overlays ///Chemistry. @@ -721,17 +722,38 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) var/mob/M = loc M.update_inv_shoes() -/mob/living/carbon/human/clean_blood() - if(gloves) +/mob/living/carbon/human/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE) + if(w_uniform && !(wear_suit && wear_suit.flags_inv & HIDEJUMPSUIT)) + if(w_uniform.clean_blood()) + update_inv_w_uniform() + if(gloves && !(wear_suit && wear_suit.flags_inv & HIDEGLOVES)) if(gloves.clean_blood()) - clean_blood() - update_inv_gloves() - gloves.germ_level = 0 - else - ..() // Clear the Blood_DNA list - if(bloody_hands) - bloody_hands = 0 update_inv_gloves() + gloves.germ_level = 0 + clean_hands = FALSE + if(shoes && !(wear_suit && wear_suit.flags_inv & HIDESHOES)) + if(shoes.clean_blood()) + update_inv_shoes() + clean_feet = FALSE + if(s_store && !(wear_suit && wear_suit.flags_inv & HIDESUITSTORAGE)) + if(s_store.clean_blood()) + update_inv_s_store() + if(lip_style && !(head && head.flags_inv & HIDEMASK)) + lip_style = null + update_body() + if(glasses && !(wear_mask && wear_mask.flags_inv & HIDEEYES)) + if(glasses.clean_blood()) + update_inv_glasses() + if(l_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS)) + if(l_ear.clean_blood()) + update_inv_ears() + if(r_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS)) + if(r_ear.clean_blood()) + update_inv_ears() + if(belt) + if(belt.clean_blood()) + update_inv_belt() + ..(clean_hands, clean_mask, clean_feet) update_icons() //apply the now updated overlays to the mob /atom/proc/add_vomit_floor(toxvomit = FALSE, green = FALSE) @@ -819,7 +841,16 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/proc/atom_say(message) if(!message) return - audible_message("[src] [atom_say_verb], \"[message]\"") + var/list/speech_bubble_hearers = list() + for(var/mob/M in get_mobs_in_view(7, src)) + M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) + if(M.client) + speech_bubble_hearers += M.client + + if(length(speech_bubble_hearers)) + var/image/I = image('icons/mob/talk.dmi', src, "[bubble_icon][say_test(message)]", FLY_LAYER) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30) /atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index aed4a4c5c2d..ab5ff19462a 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -50,6 +50,7 @@ loc.handle_atom_del(src) for(var/atom/movable/AM in contents) qdel(AM) + LAZYCLEARLIST(client_mobs_in_contents) loc = null if(pulledby) pulledby.stop_pulling() diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index a11e5d922ba..641167c768c 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -46,7 +46,6 @@ GLOBAL_LIST_EMPTY(blob_nodes) var/datum/mind/blob = pick(possible_blobs) infected_crew += blob blob.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blob) blob.restricted_roles = restricted_jobs log_game("[key_name(blob)] has been selected as a Blob") possible_blobs -= blob @@ -152,6 +151,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) for(var/datum/mind/blob in infected_crew) greet_blob(blob) + update_blob_icons_added(blob) if(SSshuttle) SSshuttle.emergencyNoEscape = 1 diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index 38da76a348b..46b1972e565 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -8,6 +8,7 @@ icon = 'icons/mob/blob.dmi' pass_flags = PASSBLOB faction = list(ROLE_BLOB) + bubble_icon = "blob" atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 maxbodytemp = 360 diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index 672ab037cc5..be794b9092b 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -89,41 +89,40 @@ ..() -/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) +/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay) if(overmind_get_delay > world.time && !override_delay) return - overmind_get_delay = world.time + 3000 // 5 minutes + overmind_get_delay = world.time + 5 MINUTES if(overmind) qdel(overmind) + INVOKE_ASYNC(src, .proc/get_new_overmind, new_overmind) + +/obj/structure/blob/core/proc/get_new_overmind(client/new_overmind) var/mob/C = null var/list/candidates = list() - - spawn() - if(!new_overmind) - if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) - else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) - - if(candidates.len) - C = pick(candidates) + if(!new_overmind) + if(is_offspring) + candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) else - C = new_overmind + candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) - if(C) - var/mob/camera/blob/B = new(src.loc) - B.key = C.key - B.blob_core = src - src.overmind = B - color = overmind.blob_reagent_datum.color - if(B.mind && !B.mind.special_role) - B.mind.make_Overmind() - spawn(0) - if(is_offspring) - B.is_offspring = TRUE + if(length(candidates)) + C = pick(candidates) + else + C = new_overmind + + if(C && !QDELETED(src)) + var/mob/camera/blob/B = new(loc) + B.key = C.key + B.blob_core = src + overmind = B + color = overmind.blob_reagent_datum.color + if(B.mind && !B.mind.special_role) + B.mind.make_Overmind() + B.is_offspring = is_offspring /obj/structure/blob/core/proc/lateblobtimer() addtimer(CALLBACK(src, .proc/lateblobcheck), 50) diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index 94bde26674b..fbfb75b8b6a 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -36,11 +36,9 @@ to_chat(user, "We haven't prepared our sting yet!") if(!iscarbon(target)) return - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(H.isSynthetic()) - to_chat(user, "This won't work on synthetics.") - return + if(ismachineperson(target)) + to_chat(user, "This won't work on synthetics.") + return if(!isturf(user.loc)) return if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0)) diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index d3e8f7d6bbd..02cd7b6db65 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -808,6 +808,7 @@ GLOBAL_LIST_EMPTY(teleport_runes) allow_excess_invokers = 1 icon_state = "5" invoke_damage = 5 + var/summoning = FALSE var/summontime = 0 /obj/effect/rune/summon/invoke(var/list/invokers) @@ -819,6 +820,11 @@ GLOBAL_LIST_EMPTY(teleport_runes) var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of [SSticker.cultdat.entity_title3]") as null|anything in cultists if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) return + if(summoning) + to_chat(user, "You are already summoning a target!") + fail_invoke() + return + if(!cultist_to_summon) to_chat(user, "You require a summoning target!") fail_invoke() @@ -841,18 +847,20 @@ GLOBAL_LIST_EMPTY(teleport_runes) new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning log_game("Summon Cultist rune failed - holywater in target") return - + summoning = TRUE ..() if(hard_summon) summontime = 20 - if(do_after(user, summontime, target = loc)) + if(do_after(user, summontime, target = src)) + summoning = FALSE // Here incase the proc stops after the qdel cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \ "Overwhelming vertigo consumes you as you are hurled through the air!") visible_message("A foggy shape materializes atop [src] and solidifies into [cultist_to_summon]!") cultist_to_summon.forceMove(get_turf(src)) qdel(src) + summoning = FALSE //Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby /obj/effect/rune/blood_boil diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index c7ca587b34f..a0519fcbe56 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -480,8 +480,8 @@ Congratulations! You are now trained for invasive xenobiology research!"} H.update_inv_r_hand() /obj/item/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user) - user.lastattacked = L - L.lastattacker = user + L.lastattacker = user.real_name + L.lastattackerckey = user.ckey L.Stun(7) L.Weaken(7) diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index 059eed3541c..8da5b4468d7 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -61,6 +61,7 @@ icon = 'icons/mob/swarmer.dmi' desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely." speak_emote = list("tones") + bubble_icon = "swarmer" health = 40 maxHealth = 40 status_flags = CANPUSH @@ -362,6 +363,10 @@ to_chat(S, "This cryopod control computer should be preserved, it contains useful items and information about the inhabitants. Aborting.") return FALSE +/obj/structure/spacepoddoor/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) + to_chat(S, "Disrupting this energy field would overload us. Aborting.") + return FALSE + /turf/simulated/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) var/isonshuttle = istype(loc, /area/shuttle) for(var/turf/T in range(1, src)) @@ -604,7 +609,7 @@ if(!istype(L, /mob/living/simple_animal/hostile/swarmer)) playsound(loc,'sound/effects/snap.ogg',50, 1, -1) L.electrocute_act(0, src, 1, TRUE, TRUE) - if(isrobot(L) || L.isSynthetic()) + if(isrobot(L) || ismachineperson(L)) L.Weaken(5) qdel(src) ..() diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index 5f9256e18b4..f647ae5a6d1 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -3,6 +3,7 @@ real_name = "Guardian Spirit" desc = "A mysterious being that stands by it's charge, ever vigilant." speak_emote = list("intones") + bubble_icon = "guardian" response_help = "passes through" response_disarm = "flails at" response_harm = "punches" diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 0da5d8a3a0a..0b46264cb33 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/assassinate/check_completion() if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) + if(target.current.stat == DEAD) return 1 if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite return 1 @@ -111,7 +111,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/maroon/check_completion() if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) + if(target.current.stat == DEAD) return 1 if(!target.current.ckey) return 1 @@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(!target) //If it's a free objective. return 1 if(target.current) - if(target.current.stat == DEAD || iszombie(target)) + if(target.current.stat == DEAD) return 0 if(issilicon(target.current)) return 0 @@ -262,7 +262,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) return 0 if(isbrain(owner.current)) return 0 - if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) + if(!owner.current || owner.current.stat == DEAD) return 0 if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith return 1 @@ -317,7 +317,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) explanation_text = "Die a glorious death." /datum/objective/die/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) return 1 if(issilicon(owner.current) && owner.current != owner.original) return 1 @@ -573,9 +573,10 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) // /vg/; Vox Inviolate for humans :V /datum/objective/minimize_casualties explanation_text = "Minimise casualties." + /datum/objective/minimize_casualties/check_completion() - if(owner.kills.len>5) return 0 - return 1 + return TRUE + //Vox heist objectives. @@ -781,16 +782,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) explanation_text = "Follow the Inviolate. Minimise death and loss of resources." /datum/objective/heist/inviolate_death/check_completion() - var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. - var/vox_total_kills = 0 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills - - if(vox_total_kills > vox_allowed_kills) return 0 - return 1 - + return TRUE // Traders // These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index 2e43096ce8e..b46bc3c6c51 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -194,8 +194,8 @@ Made by Xhuis return 1 var/mob/living/M = thrall_mind.current if(issilicon(M)) - M.audible_message("[M] lets out a short blip.", \ - "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...") + M.audible_message("[M] lets out a short blip.") + to_chat(M, "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...") else M.visible_message("[M] looks like [M.p_their()] mind is [M.p_their()] own again!", \ "A piercing white light floods your eyes. Your mind is your own again! Though you try, you cannot remember anything about the shadowlings or your time \ @@ -244,8 +244,8 @@ Made by Xhuis ling_mind.RemoveSpell(S) var/mob/living/M = ling_mind.current if(issilicon(M)) - M.audible_message("[M] lets out a short blip.", \ - "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...") + M.audible_message("[M] lets out a short blip.") + to_chat(M, "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...") else M.visible_message("[M] screams and contorts!", \ "THE LIGHT-- YOUR MIND-- BURNS--") diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 205fd63d9c6..09b228bdd0c 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -470,7 +470,7 @@ GLOBAL_LIST_EMPTY(multiverse) M.equip_to_slot_or_del(sword, slot_r_hand) if("cyborg") - if(!ismachine(M)) + if(!ismachineperson(M)) for(var/obj/item/organ/O in M.bodyparts) O.robotize(make_tough = 1) M.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/eyepatch(M), slot_glasses) diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index b6321765861..c510518716a 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -24,8 +24,8 @@ title = "Cyborg" flag = JOB_CYBORG department_flag = JOBCAT_ENGSEC - total_positions = 1 - spawn_positions = 1 + total_positions = 2 + spawn_positions = 2 supervisors = "your laws and the AI" //Nodrak department_head = list("AI") selection_color = "#ddffdd" diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index c56d27bb408..1d627f131b2 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -223,6 +223,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca glasses = /obj/item/clothing/glasses/hud/security/sunglasses id = /obj/item/card/id/nanotrasen l_pocket = /obj/item/flash + r_pocket = /obj/item/clothing/accessory/lawyers_badge pda = /obj/item/pda/heads/magistrate backpack_contents = list( /obj/item/melee/classic_baton/telescopic = 1 @@ -262,9 +263,12 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca glasses = /obj/item/clothing/glasses/hud/security/sunglasses/read_only id = /obj/item/card/id/security l_pocket = /obj/item/laser_pointer - r_pocket = /obj/item/flash + r_pocket = /obj/item/clothing/accessory/lawyers_badge l_hand = /obj/item/storage/briefcase pda = /obj/item/pda/lawyer + backpack_contents = list( + /obj/item/flash = 1 + ) implants = list(/obj/item/implant/mindshield) satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index f2b8cf31671..09328865b59 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -282,14 +282,14 @@ if(visualsOnly) return - if(ismachine(H)) + if(ismachineperson(H)) var/obj/item/organ/internal/cyberimp/brain/clown_voice/implant = new implant.insert(H) H.dna.SetSEState(GLOB.clumsyblock, TRUE) genemutcheck(H, GLOB.clumsyblock, null, MUTCHK_FORCED) H.dna.default_blocks.Add(GLOB.clumsyblock) - if(!ismachine(H)) + if(!ismachineperson(H)) H.dna.SetSEState(GLOB.comicblock, TRUE) genemutcheck(H, GLOB.comicblock, null, MUTCHK_FORCED) H.dna.default_blocks.Add(GLOB.comicblock) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 6174f4a497d..9fda2cfe45c 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -322,16 +322,18 @@ apply_mode() -/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment, var/datum/tlv/cur_tlv, var/turf/simulated/location) +/obj/machinery/alarm/proc/handle_heating_cooling(datum/gas_mixture/environment, datum/tlv/cur_tlv, turf/simulated/location) cur_tlv = TLV["temperature"] //Handle temperature adjustment here. if(environment.temperature < target_temperature - 2 || environment.temperature > target_temperature + 2 || regulating_temperature) //If it goes too far, we should adjust ourselves back before stopping. if(!cur_tlv.get_danger_level(target_temperature)) + var/datum/gas_mixture/gas = location.remove_air(0.25 * environment.total_moles()) + if(!gas) + return if(!regulating_temperature) regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click and a faint electronic hum.") if(target_temperature > MAX_TEMPERATURE) target_temperature = MAX_TEMPERATURE @@ -339,9 +341,8 @@ if(target_temperature < MIN_TEMPERATURE) target_temperature = MIN_TEMPERATURE - var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) var/heat_capacity = gas.heat_capacity() - var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) + var/energy_used = max(abs(heat_capacity * (gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) //Use power. Assuming that each power unit represents 1000 watts.... use_power(energy_used/1000, ENVIRON) @@ -349,16 +350,15 @@ //We need to cool ourselves. if(heat_capacity) if(environment.temperature > target_temperature) - gas.temperature -= energy_used/heat_capacity + gas.temperature -= energy_used / heat_capacity else - gas.temperature += energy_used/heat_capacity + gas.temperature += energy_used / heat_capacity environment.merge(gas) if(abs(environment.temperature - target_temperature) <= 0.5) regulating_temperature = 0 - visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.") + visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click as a faint electronic humming stops.") /obj/machinery/alarm/update_icon() if(wiresexposed) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index a832e2b5d16..131956ca3c1 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -8,7 +8,7 @@ active_power_usage = 500 light_color = LIGHT_COLOR_DARKBLUE circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/tgui_module/crew_monitor/crew_monitor /obj/machinery/computer/crew/New() crew_monitor = new(src) @@ -20,17 +20,15 @@ /obj/machinery/computer/crew/attack_ai(mob/user) attack_hand(user) - ui_interact(user) - /obj/machinery/computer/crew/attack_hand(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.ui_interact(user, ui_key, ui, force_open) +/obj/machinery/computer/crew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + crew_monitor.tgui_interact(user, ui_key, ui, force_open) /obj/machinery/computer/crew/interact(mob/user) - crew_monitor.ui_interact(user) + crew_monitor.tgui_interact(user) diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 05198a7a55f..a4b9510945a 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -89,12 +89,12 @@ GLOBAL_LIST_EMPTY(doppler_arrays) /obj/machinery/doppler_array/proc/print_explosive_logs(mob/user) if(!logged_explosions.len) - atom_say("No logs currently stored in internal database.") + atom_say("No logs currently stored in internal database.") return if(active_timers) to_chat(user, "[src] is already printing something, please wait.") return - atom_say("Printing explosive log. Standby...") + atom_say("Printing explosive log. Standby...") addtimer(CALLBACK(src, .proc/print), 50) /obj/machinery/doppler_array/proc/print() diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index a17f9dfb9bd..b6da46a67c8 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -177,8 +177,8 @@ F.broken = 0 F.times_used = 0 F.icon_state = "flash" - if(istype(O,/obj/item/gun/energy/disabler/cyborg)) - var/obj/item/gun/energy/disabler/cyborg/D = O + if(istype(O,/obj/item/gun/energy)) + var/obj/item/gun/energy/D = O if(D.cell.charge < D.cell.maxcharge) var/obj/item/ammo_casing/energy/E = D.ammo_type[D.select] D.cell.give(E.e_cost) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 6529dd4b73d..81379df97c4 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -225,7 +225,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles) message_log += "Message sent to [recipient] at [station_time_timestamp()]
[message]" Radio.autosay("Alert; a new requests console message received for [recipient] from [department]", null, "[radiochannel]") else - audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) + atom_say("No server detected!") //Handle screen switching if(href_list["setScreen"]) @@ -256,7 +256,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles) else if(world.time < print_cooldown) error_message = "Please allow the printer time to prepare the next shipping label." if(error_message) - audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: [error_message]'"),,4) + atom_say("[error_message]") return print_label(ship_tag_name, ship_tag_index) shipping_log += "Shipping Label printed for [ship_tag_name]
[msgVerified]" diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm index 7e468a548bf..dc933099faa 100644 --- a/code/game/machinery/tcomms/_base.dm +++ b/code/game/machinery/tcomms/_base.dm @@ -306,7 +306,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines) if(is_admin(R) && !R.get_preference(CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. continue - if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes. + if(isnewplayer(R)) // we don't want new players to hear messages. rare but generates runtimes. continue // --- Can understand the speech --- diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 7dac9de2b9a..9acdcba48de 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -166,10 +166,6 @@ var/mob/M = A if(istype(firer, /mob)) add_attack_logs(firer, M, "Mecha-shot with [src]") - if(!iscarbon(firer)) - M.LAssailant = null - else - M.LAssailant = firer else add_attack_logs(null, M, "Mecha-shot with [src]") if(life <= 0) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index 04322c63093..896ce886887 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -5,8 +5,8 @@ /turf/simulated/floor/mech_bay_recharge_floor/airless icon_state = "recharge_floor_asteroid" - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB /obj/machinery/mech_bay_recharge_port diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 4c912cf8e55..90d466baf3e 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -11,6 +11,7 @@ force = 5 max_integrity = 300 //max_integrity is base health armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) + bubble_icon = "machine" var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) @@ -1446,8 +1447,10 @@ diag_hud_set_mechtracking() -/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) +/obj/mecha/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) + var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30) /obj/mecha/update_remote_sight(mob/living/user) if(occupant_sight_flags) diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index ec717b3db32..4978a4ee314 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -6,7 +6,7 @@ var/lootdoubles = 1 //if the same item can be spawned twice var/list/loot //a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect) -/obj/effect/spawner/lootdrop/Initialize(mapload) +/obj/effect/spawner/lootdrop/New() ..() if(loot && loot.len) for(var/i = lootcount, i > 0, i--) @@ -17,7 +17,7 @@ if(lootspawn) new lootspawn(loc) - return INITIALIZE_HINT_QDEL + qdel(src) /obj/effect/spawner/lootdrop/armory_contraband name = "armory contraband gun spawner" diff --git a/code/game/objects/effects/spawners/vaultspawner.dm b/code/game/objects/effects/spawners/vaultspawner.dm index 0d882cc9bd2..a78c6111eb7 100644 --- a/code/game/objects/effects/spawners/vaultspawner.dm +++ b/code/game/objects/effects/spawners/vaultspawner.dm @@ -21,6 +21,7 @@ if(i == lowBoundX || i == hiBoundX || j == lowBoundY || j == hiBoundY) new /turf/simulated/wall/vault(locate(i,j,z),type) else - new /turf/simulated/floor/vault(locate(i,j,z),type) + var/turf/T = new /turf/simulated/floor/vault(locate(i, j, z)) + T.icon_state = "[type]vault" qdel(src) diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index 77aef9bb71d..5246652aa0e 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -56,7 +56,7 @@ desc = "Huh." allow_wrap = FALSE -/obj/item/storage/pill_bottle/random_drug_bottle/New() - ..() +/obj/item/storage/pill_bottle/random_drug_bottle/Initialize(mapload) + . = ..() for(var/i in 1 to 5) new /obj/item/reagent_containers/food/pill/random_drugs(src) diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 97c5817f05b..95b36194f56 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -241,6 +241,7 @@ user.base_icon = disguise user.icon_state = disguise user.cham_proj = src + user.bubble_icon = "robot" active = TRUE user.update_icons() @@ -249,6 +250,7 @@ S = user user.base_icon = initial(user.base_icon) user.icon_state = initial(user.icon_state) + user.bubble_icon = "syndibot" active = FALSE user.update_icons() diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 58848f3f7e4..8bfbf22f05d 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -113,7 +113,6 @@ /obj/item/flash/attack(mob/living/M, mob/user) if(!try_use_flash(user)) return 0 - if(iscarbon(M)) flash_carbon(M, user, 5, 1) if(overcharged) @@ -121,22 +120,12 @@ M.IgniteMob() burn_out() return 1 - else if(issilicon(M)) - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.module) // Perhaps they didn't choose a module yet - for(var/obj/item/borg/combat/shield/S in R.module.modules) - if(R.activated(S)) - add_attack_logs(user, M, "Flashed with [src]") - user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") - return 1 add_attack_logs(user, M, "Flashed with [src]") if(M.flash_eyes(affect_silicon = 1)) M.Weaken(rand(5,10)) user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") return 1 - user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") diff --git a/code/game/objects/items/devices/pizza_bomb.dm b/code/game/objects/items/devices/pizza_bomb.dm index dd824332ad3..e5c237938d6 100644 --- a/code/game/objects/items/devices/pizza_bomb.dm +++ b/code/game/objects/items/devices/pizza_bomb.dm @@ -52,7 +52,7 @@ if(disarmed) visible_message("[bicon(src)] Sparks briefly jump out of the [correct_wire] wire on \the [src], but it's disarmed!") return - src.audible_message("[bicon(src)] [src] beeps, \"Enjoy the pizza!\"") + atom_say("Enjoy the pizza!") src.visible_message("\The [src] violently explodes!") explosion(src.loc,1,2,4,flame_range = 2) //Identical to a minibomb qdel(src) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 97aa706060a..009ec01ef43 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -623,6 +623,9 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( ..() set_frequency(ERT_FREQ) +/obj/item/radio/borg/ert/specops + keyslot = new /obj/item/encryptionkey/centcom + /obj/item/radio/borg/attackby(obj/item/W as obj, mob/user as mob, params) if(istype(W, /obj/item/encryptionkey/)) user.set_machine(src) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 0d8873a6377..f20a2cc5875 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -141,7 +141,7 @@ REAGENT SCANNER // Used by the PDA medical scanner too /proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) - if(!ishuman(M) || M.isSynthetic()) + if(!ishuman(M) || ismachineperson(M)) //these sensors are designed for organic life to_chat(user, "Analyzing Results for ERROR:\n\t Overall Status: ERROR") to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute") diff --git a/code/game/objects/items/devices/sensor_device.dm b/code/game/objects/items/devices/sensor_device.dm index 8c69402ed70..b83128cf4a5 100644 --- a/code/game/objects/items/devices/sensor_device.dm +++ b/code/game/objects/items/devices/sensor_device.dm @@ -6,7 +6,7 @@ w_class = WEIGHT_CLASS_SMALL slot_flags = SLOT_BELT origin_tech = "programming=3;materials=3;magnets=3" - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/tgui_module/crew_monitor/crew_monitor /obj/item/sensor_device/New() ..() @@ -17,7 +17,7 @@ return ..() /obj/item/sensor_device/attack_self(mob/user as mob) - ui_interact(user) + tgui_interact(user) -/obj/item/sensor_device/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.ui_interact(user, ui_key, ui, force_open) +/obj/item/sensor_device/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + crew_monitor.tgui_interact(user, ui_key, ui, force_open) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 43178a85ef8..8b82341a06c 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -9,23 +9,23 @@ origin_tech = "programming=2" var/locked = 0 var/installed = 0 - var/require_module = 0 + var/require_module = FALSE var/module_type = null /obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R) if(R.stat == DEAD) to_chat(usr, "[src] will not function on a deceased cyborg.") - return 1 + return TRUE if(module_type && !istype(R.module, module_type)) to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") to_chat(usr, "There's no mounting point for the module!") - return 1 + return TRUE /obj/item/borg/upgrade/reset name = "cyborg module reset board" desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the cyborg." icon_state = "cyborg_upgrade1" - require_module = 1 + require_module = TRUE /obj/item/borg/upgrade/reset/action(mob/living/silicon/robot/R) if(..()) @@ -55,7 +55,7 @@ R.custom_name = heldname R.real_name = heldname - return 1 + return TRUE /obj/item/borg/upgrade/restart name = "cyborg emergency reboot module" @@ -77,14 +77,14 @@ GLOB.alive_mob_list += R R.notify_ai(1) - return 1 + return TRUE /obj/item/borg/upgrade/vtec name = "robotic VTEC Module" desc = "Used to kick in a robot's VTEC systems, increasing their speed." icon_state = "cyborg_upgrade2" - require_module = 1 + require_module = TRUE origin_tech = "engineering=4;materials=5;programming=4" /obj/item/borg/upgrade/vtec/action(var/mob/living/silicon/robot/R) @@ -97,14 +97,14 @@ R.speed = -1 // Gotta go fast. - return 1 + return TRUE /obj/item/borg/upgrade/disablercooler name = "cyborg rapid disabler cooling module" desc = "Used to cool a mounted disabler, increasing the potential current in it and thus its recharge rate." icon_state = "cyborg_upgrade3" origin_tech = "engineering=4;powerstorage=4;combat=4" - require_module = 1 + require_module = TRUE module_type = /obj/item/robot_module/security /obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R) @@ -122,7 +122,7 @@ T.charge_delay = max(2 , T.charge_delay - 4) - return 1 + return TRUE /obj/item/borg/upgrade/thrusters name = "ion thruster upgrade" @@ -139,14 +139,14 @@ return R.ionpulse = 1 - return 1 + return TRUE /obj/item/borg/upgrade/ddrill name = "mining cyborg diamond drill" desc = "A diamond drill replacement for the mining module's standard drill." icon_state = "cyborg_upgrade3" origin_tech = "engineering=4;materials=5" - require_module = 1 + require_module = TRUE module_type = /obj/item/robot_module/miner /obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R) @@ -161,14 +161,14 @@ R.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(R.module) R.module.rebuild() - return 1 + return TRUE /obj/item/borg/upgrade/soh name = "mining cyborg satchel of holding" desc = "A satchel of holding replacement for mining cyborg's ore satchel module." icon_state = "cyborg_upgrade3" origin_tech = "engineering=4;materials=4;bluespace=4" - require_module = 1 + require_module = TRUE module_type = /obj/item/robot_module/miner /obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R) @@ -181,35 +181,46 @@ R.module.modules += new /obj/item/storage/bag/ore/holding(R.module) R.module.rebuild() - return 1 + return TRUE /obj/item/borg/upgrade/syndicate - name = "illegal equipment module" + name = "safety override module" desc = "Unlocks the hidden, deadlier functions of a cyborg. Also prevents emag subversion." icon_state = "cyborg_upgrade3" origin_tech = "combat=4;syndicate=1" - require_module = 1 + require_module = TRUE /obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/R) if(..()) return - if(R.emagged) return - if(R.weapons_unlock) to_chat(R, "Internal diagnostic error: incompatible upgrade module detected."); return - R.emagged = 1 + return TRUE - return 1 +/obj/item/borg/upgrade/lavaproof + name = "mining cyborg lavaproof chassis" + desc = "An upgrade kit to apply specialized coolant systems and insulation layers to a mining cyborg's chassis, enabling them to withstand exposure to molten rock." + icon_state = "ash_plating" + resistance_flags = LAVA_PROOF | FIRE_PROOF + require_module = TRUE + module_type = /obj/item/robot_module/miner + +/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R) + if(..()) + return + if(istype(R)) + R.weather_immunities += "lava" + return TRUE /obj/item/borg/upgrade/selfrepair name = "self-repair module" desc = "This module will repair the cyborg over time." icon_state = "cyborg_upgrade5" - require_module = 1 + require_module = TRUE var/repair_amount = -1 var/repair_tick = 1 var/msg_cooldown = 0 @@ -230,7 +241,7 @@ icon_state = "selfrepair_off" var/datum/action/A = new /datum/action/item_action/toggle(src) A.Grant(R) - return 1 + return TRUE /obj/item/borg/upgrade/selfrepair/Destroy() cyborg = null diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm index 708070e1478..ff469ba3c59 100644 --- a/code/game/objects/items/stacks/nanopaste.dm +++ b/code/game/objects/items/stacks/nanopaste.dm @@ -57,7 +57,7 @@ E.heal_damage(0, remheal, 0, 1) //Healing Burn remheal = nremheal user.visible_message("\The [user] applies some nanite paste at \the [M]'s [E.name] with \the [src].") - if(H.bleed_rate && H.isSynthetic()) + if(H.bleed_rate && ismachineperson(H)) H.bleed_rate = 0 else to_chat(user, "Nothing to fix here.") diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 14ccc103ab1..ab592cc0402 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -204,7 +204,7 @@ if(amount < 1) // Just in case a stack's amount ends up fractional somehow var/oldsrc = src - src = null //dont kill proc after del() + src = null //dont kill proc after qdel() usr.unEquip(oldsrc, 1) qdel(oldsrc) if(istype(O, /obj/item)) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 40d659ccf6b..66f35a169f9 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1408,6 +1408,7 @@ obj/item/toy/cards/deck/syndicate/black name = "xenomorph action figure" desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." w_class = WEIGHT_CLASS_SMALL + bubble_icon = "alien" var/cooldown = 0 /obj/item/toy/toy_xeno/attack_self(mob/user) @@ -1416,7 +1417,7 @@ obj/item/toy/cards/deck/syndicate/black user.visible_message("[user] pulls back the string on [src].") icon_state = "[initial(icon_state)]_used" sleep(5) - audible_message("[bicon(src)] Hiss!") + atom_say("Hiss!") var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg') playsound(get_turf(src), pick(possible_sounds), 50, 1) spawn(45) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index d6397025424..f8f9a48102a 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -477,9 +477,7 @@ GLOBAL_LIST_INIT(rcd_door_types, list( return R.cell.charge >= (amount * use_multiplier) /obj/item/rcd/proc/detonate_pulse() - audible_message("[src] begins to vibrate and \ - buzz loudly!","[src] begins \ - vibrating violently!") + audible_message("[src] begins to vibrate and buzz loudly!", "[src] begins vibrating violently!") // 5 seconds to get rid of it addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50) diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index 8f11f65fe71..45047e97156 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -147,10 +147,6 @@ to_chat(user, "You inject yourself with [src].") add_attack_logs(user, M, attack_log, ATKLOG_ALL) - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user inject(M, user) used = TRUE diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index f965907c5ba..6cf011bc2c3 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -121,19 +121,6 @@ desc = "A sleek, sturdy box" icon_state = "box_of_doom" -/obj/item/storage/box/syndie_kit/romerol - name = "Romerol Kit" - desc = "A box containing a deadly virus capable of reanimating dead as zombies." - max_w_class = WEIGHT_CLASS_NORMAL - can_hold = list(/obj/item/reagent_containers/glass/bottle/romerol,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/dropper) - -/obj/item/storage/box/syndie_kit/romerol/New() - ..() - new /obj/item/reagent_containers/glass/bottle/romerol(src) - new /obj/item/reagent_containers/syringe(src) - new /obj/item/reagent_containers/dropper(src) - return - /obj/item/storage/box/syndie_kit/space name = "Boxed Space Suit and Helmet" can_hold = list(/obj/item/clothing/suit/space/syndicate/black/red, /obj/item/clothing/head/helmet/space/syndicate/black/red, /obj/item/tank/emergency_oxygen/syndi, /obj/item/clothing/mask/gas/syndicate) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 6782057d928..a39050cba47 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -189,8 +189,8 @@ L.apply_effect(STUTTER, stunforce) if(user) - user.lastattacked = L - L.lastattacker = user + L.lastattacker = user.real_name + L.lastattackerckey = user.ckey L.visible_message("[user] has stunned [L] with [src]!", \ "[user] has stunned you with [src]!") add_attack_logs(user, L, "stunned") diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 43244c470ff..d40c628ed90 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -347,6 +347,7 @@ a { .["Make speed process"] = "?_src_=vars;makespeedy=[UID()]" else .["Make normal process"] = "?_src_=vars;makenormalspeed=[UID()]" + .["Modify armor values"] = "?_src_=vars;modifyarmor=[UID()]" /obj/proc/check_uplink_validity() return TRUE diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 448fd6feb34..0da39d2255d 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -115,6 +115,8 @@ new /obj/item/clothing/shoes/black(src) new /obj/item/clothing/glasses/sunglasses/big(src) new /obj/item/clothing/glasses/sunglasses/big(src) + new /obj/item/clothing/accessory/lawyers_badge(src) + new /obj/item/clothing/accessory/lawyers_badge(src) //Paramedic diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 8576afe14b8..be004a82912 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -466,3 +466,4 @@ new /obj/item/gavelhammer(src) new /obj/item/clothing/head/justice_wig(src) new /obj/item/clothing/accessory/medal/legal(src) + new /obj/item/clothing/accessory/lawyers_badge(src) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 87107f97abe..1a5b275bc01 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -403,75 +403,7 @@ L.ExtinguishMob() L.adjust_fire_stacks(-20) //Douse ourselves with water to avoid fire more easily to_chat(L, "You've been drenched in water!") - if(iscarbon(O)) - var/mob/living/carbon/M = O - if(M.r_hand) - M.r_hand.clean_blood() - if(M.l_hand) - M.l_hand.clean_blood() - if(M.back) - if(M.back.clean_blood()) - M.update_inv_back() - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/washgloves = 1 - var/washshoes = 1 - var/washmask = 1 - var/washears = 1 - var/washglasses = 1 - - if(H.wear_suit) - washgloves = !(H.wear_suit.flags_inv & HIDEGLOVES) - washshoes = !(H.wear_suit.flags_inv & HIDESHOES) - - if(H.head) - washmask = !(H.head.flags_inv & HIDEMASK) - washglasses = !(H.head.flags_inv & HIDEEYES) - washears = !(H.head.flags_inv & HIDEEARS) - - if(H.wear_mask) - if(washears) - washears = !(H.wear_mask.flags_inv & HIDEEARS) - if(washglasses) - washglasses = !(H.wear_mask.flags_inv & HIDEEYES) - - if(H.head) - if(H.head.clean_blood()) - H.update_inv_head() - if(H.wear_suit) - if(H.wear_suit.clean_blood()) - H.update_inv_wear_suit() - else if(H.w_uniform) - if(H.w_uniform.clean_blood()) - H.update_inv_w_uniform() - if(H.gloves && washgloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves() - if(H.shoes && washshoes) - if(H.shoes.clean_blood()) - H.update_inv_shoes() - if(H.wear_mask && washmask) - if(H.wear_mask.clean_blood()) - H.update_inv_wear_mask() - if(H.glasses && washglasses) - if(H.glasses.clean_blood()) - H.update_inv_glasses() - if(H.l_ear && washears) - if(H.l_ear.clean_blood()) - H.update_inv_ears() - if(H.r_ear && washears) - if(H.r_ear.clean_blood()) - H.update_inv_ears() - if(H.belt) - if(H.belt.clean_blood()) - H.update_inv_belt() - else - if(M.wear_mask) //if the mob is not human, it cleans the mask without asking for bitflags - if(M.wear_mask.clean_blood()) - M.update_inv_wear_mask() - - else - O.clean_blood() + L.clean_blood() if(isturf(loc)) var/turf/tile = loc diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 19717991593..6bc1c4ba142 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -8,12 +8,6 @@ nitrogen = MOLES_N2STANDARD var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - var/dirtoverlay = null - -/turf/simulated/New() - ..() - levelupdate() - visibilityChanged() /turf/simulated/proc/break_tile() return diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 55b4f336917..614b8232b0b 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -31,8 +31,8 @@ GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","dama var/list/burnt_states = list("floorscorched1", "floorscorched2") var/list/prying_tool_list = list(TOOL_CROWBAR) //What tool/s can we use to pry up the tile? -/turf/simulated/floor/New() - ..() +/turf/simulated/floor/Initialize(mapload) + . = ..() if(icon_state in GLOB.icons_to_ignore_at_floor_init) //so damaged/burned tiles or plating icons aren't saved as the default icon_regular_floor = "floor" else diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index f8dc414ef85..bfbfa70331e 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -49,9 +49,9 @@ floor_tile = /obj/item/stack/tile/grass broken_states = list("sand") -/turf/simulated/floor/grass/Initialize() +/turf/simulated/floor/grass/Initialize(mapload) + . = ..() update_icon() - ..() /turf/simulated/floor/grass/update_icon() icon_state = "grass[pick("1","2","3","4")]" @@ -80,10 +80,9 @@ ) -/turf/simulated/floor/carpet/New() - ..() - if(broken || burnt) - make_plating() +/turf/simulated/floor/carpet/Initialize(mapload) + . = ..() + update_icon() /turf/simulated/floor/carpet/update_icon() if(!..()) @@ -119,18 +118,18 @@ broken_states = list("damaged") plane = PLANE_SPACE -/turf/simulated/floor/fakespace/New() - ..() - icon_state = "[rand(0,25)]" - -/turf/simulated/floor/carpet/arcade - icon = 'icons/goonstation/turf/floor.dmi' - icon_state = "arcade" - floor_tile = /obj/item/stack/tile/arcade_carpet - smooth = SMOOTH_FALSE +/turf/simulated/floor/fakespace/Initialize(mapload) + . = ..() + icon_state = SPACE_ICON_STATE /turf/simulated/floor/fakespace/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = 'icons/turf/space.dmi' underlay_appearance.icon_state = SPACE_ICON_STATE underlay_appearance.plane = PLANE_SPACE return TRUE + +/turf/simulated/floor/carpet/arcade + icon = 'icons/goonstation/turf/floor.dmi' + icon_state = "arcade" + floor_tile = /obj/item/stack/tile/arcade_carpet + smooth = SMOOTH_FALSE diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm index b32580b9bab..14a08639132 100644 --- a/code/game/turfs/simulated/floor/lava.dm +++ b/code/game/turfs/simulated/floor/lava.dm @@ -8,9 +8,6 @@ light_power = 0.75 light_color = LIGHT_COLOR_LAVA -/turf/simulated/floor/plating/lava/New() - ..() - /turf/simulated/floor/plating/lava/ex_act() return diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm index 2c24a422343..8276c0de33f 100644 --- a/code/game/turfs/simulated/floor/light_floor.dm +++ b/code/game/turfs/simulated/floor/light_floor.dm @@ -20,8 +20,8 @@ var/state = LIGHTFLOOR_ON var/can_modify_colour = TRUE -/turf/simulated/floor/light/New() - ..() +/turf/simulated/floor/light/Initialize(mapload) + . = ..() update_icon() /turf/simulated/floor/light/update_icon() diff --git a/code/game/turfs/simulated/floor/mineral.dm b/code/game/turfs/simulated/floor/mineral.dm index ffb1cce0b5c..dd975dc80d9 100644 --- a/code/game/turfs/simulated/floor/mineral.dm +++ b/code/game/turfs/simulated/floor/mineral.dm @@ -14,8 +14,8 @@ icon_state = "" var/list/icons = list() -/turf/simulated/floor/mineral/New() - ..() +/turf/simulated/floor/mineral/Initialize(mapload) + . = ..() broken_states = list("[initial(icon_state)]_dam") /turf/simulated/floor/mineral/update_icon() @@ -97,24 +97,24 @@ broken_states = list("titanium_dam1","titanium_dam2","titanium_dam3","titanium_dam4","titanium_dam5") /turf/simulated/floor/mineral/titanium/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB /turf/simulated/floor/mineral/titanium/blue icon_state = "titanium_blue" /turf/simulated/floor/mineral/titanium/blue/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB /turf/simulated/floor/mineral/titanium/yellow icon_state = "titanium_yellow" /turf/simulated/floor/mineral/titanium/yellow/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB /turf/simulated/floor/mineral/titanium/purple @@ -122,8 +122,8 @@ floor_tile = /obj/item/stack/tile/mineral/titanium/purple /turf/simulated/floor/mineral/titanium/purple/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB //PLASTITANIUM (syndieshuttle) @@ -137,8 +137,8 @@ icon_state = "plastitanium_red" /turf/simulated/floor/mineral/plastitanium/red/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB /turf/simulated/floor/mineral/plastitanium/red/brig @@ -179,8 +179,8 @@ spam_flag = world.time + 10 /turf/simulated/floor/mineral/bananium/airless - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB @@ -254,8 +254,8 @@ floor_tile = /obj/item/stack/tile/mineral/abductor icons = list("alienpod1", "alienpod2", "alienpod3", "alienpod4", "alienpod5", "alienpod6", "alienpod7", "alienpod8", "alienpod9") -/turf/simulated/floor/mineral/abductor/New() - ..() +/turf/simulated/floor/mineral/abductor/Initialize(mapload) + . = ..() icon_state = "alienpod[rand(1,9)]" /turf/simulated/floor/mineral/abductor/break_tile() diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 95d5f816aa4..83835cd947f 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -3,19 +3,11 @@ icon_state = "rockvault" smooth = SMOOTH_FALSE -/turf/simulated/floor/vault/New(location, vtype) - ..() - icon_state = "[vtype]vault" - /turf/simulated/wall/vault icon = 'icons/turf/walls.dmi' icon_state = "rockvault" smooth = SMOOTH_FALSE -/turf/simulated/wall/vault/New(location, vtype) - ..() - icon_state = "[vtype]vault" - /turf/simulated/floor/bluegrid icon = 'icons/turf/floors.dmi' icon_state = "bcircuit" @@ -35,12 +27,12 @@ /turf/simulated/floor/greengrid/airless icon_state = "gcircuit" name = "airless floor" - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB -/turf/simulated/floor/greengrid/airless/New() - ..() +/turf/simulated/floor/greengrid/airless/Initialize(mapload) + . = ..() name = "floor" /turf/simulated/floor/redgrid @@ -78,8 +70,8 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/obj/machinery/poolcontroller/linkedcontroller = null -/turf/simulated/floor/beach/water/New() - ..() +/turf/simulated/floor/beach/water/Initialize(mapload) + . = ..() var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = "water5", layer = ABOVE_MOB_LAYER) overlay_image.plane = GAME_PLANE overlays += overlay_image diff --git a/code/game/turfs/simulated/floor/plasteel_floor.dm b/code/game/turfs/simulated/floor/plasteel_floor.dm index ee19b4eeeb5..116d57dc1ef 100644 --- a/code/game/turfs/simulated/floor/plasteel_floor.dm +++ b/code/game/turfs/simulated/floor/plasteel_floor.dm @@ -12,12 +12,12 @@ /turf/simulated/floor/plasteel/airless name = "airless floor" - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB -/turf/simulated/floor/plasteel/airless/New() - ..() +/turf/simulated/floor/plasteel/airless/Initialize(mapload) + . = ..() name = "floor" /turf/simulated/floor/plasteel/airless/indestructible // For bomb testing range diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index c692135dc44..a6dbe0de281 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -14,17 +14,17 @@ "xeno" = list('sound/effects/footstep/plating_xeno.ogg') ) -/turf/simulated/floor/plating/New() - ..() +/turf/simulated/floor/plating/Initialize(mapload) + . = ..() icon_plating = icon_state update_icon() -/turf/simulated/floor/plating/damaged/New() - ..() +/turf/simulated/floor/plating/damaged/Initialize(mapload) + . = ..() break_tile() -/turf/simulated/floor/plating/burnt/New() - ..() +/turf/simulated/floor/plating/burnt/Initialize(mapload) + . = ..() burn_tile() /turf/simulated/floor/plating/update_icon() @@ -113,12 +113,12 @@ /turf/simulated/floor/plating/airless icon_state = "plating" name = "airless plating" - oxygen = 0.01 - nitrogen = 0.01 + oxygen = 0 + nitrogen = 0 temperature = TCMB -/turf/simulated/floor/plating/airless/New() - ..() +/turf/simulated/floor/plating/airless/Initialize(mapload) + . = ..() name = "plating" /turf/simulated/floor/engine @@ -190,8 +190,8 @@ name = "engraved floor" icon_state = "cult" -/turf/simulated/floor/engine/cult/New() - ..() +/turf/simulated/floor/engine/cult/Initialize(mapload) + . = ..() if(SSticker.mode)//only do this if the round is going..otherwise..fucking asteroid.. icon_state = SSticker.cultdat.cult_floor_icon_state @@ -275,8 +275,8 @@ icon = 'icons/turf/floors/ironsand.dmi' icon_state = "ironsand1" -/turf/simulated/floor/plating/ironsand/New() - ..() +/turf/simulated/floor/plating/ironsand/Initialize(mapload) + . = ..() icon_state = "ironsand[rand(1,15)]" /turf/simulated/floor/plating/ironsand/remove_plating() @@ -359,8 +359,8 @@ name = "alien floor" icon_state = "alienpod1" -/turf/simulated/floor/plating/abductor/New() - ..() +/turf/simulated/floor/plating/abductor/Initialize(mapload) + . = ..() icon_state = "alienpod[rand(1,9)]" /turf/simulated/floor/plating/ice diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index f34465fd60f..56908a7ecae 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -9,8 +9,8 @@ sheet_amount = 1 girder_type = /obj/structure/girder/cult -/turf/simulated/wall/cult/New() - ..() +/turf/simulated/wall/cult/Initialize(mapload) + . = ..() if(SSticker.mode)//game hasn't started offically don't do shit.. new /obj/effect/temp_visual/cult/turf(src) icon_state = SSticker.cultdat.cult_wall_icon_state @@ -72,10 +72,7 @@ realappearance.linked = src /turf/simulated/wall/clockwork/Destroy() - if(realappearance) - qdel(realappearance) - realappearance = null - + QDEL_NULL(realappearance) return ..() /turf/simulated/wall/clockwork/ReplaceWithLattice() diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index f78f520cfd3..1a8ccdae550 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -21,6 +21,11 @@ /turf/space/Initialize(mapload) if(!istype(src, /turf/space/transit)) icon_state = SPACE_ICON_STATE + vis_contents.Cut() //removes inherited overlays + + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE var/area/A = loc if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) @@ -34,12 +39,6 @@ return INITIALIZE_HINT_NORMAL -/turf/space/Destroy(force) - if(force) - . = ..() - else - return QDEL_HINT_LETMELIVE - /turf/space/BeforeChange() ..() var/datum/space_level/S = GLOB.space_manager.get_zlev(z) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 2a14230d467..a96cbae1269 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -125,9 +125,9 @@ /turf/space/transit/attackby() return -/turf/space/transit/New() +/turf/space/transit/Initialize(mapload) + . = ..() update_icon() - ..() /turf/space/transit/proc/update_icon() var/p = 9 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index d1541dcc4fa..e174634123d 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -31,21 +31,29 @@ var/image/obscured //camerachunks + var/changing_turf = FALSE + var/list/blueprint_data //for the station blueprints, images of objects eg: pipes var/list/footstep_sounds var/shoe_running_volume = 50 var/shoe_walking_volume = 20 -/turf/New() - ..() +/turf/Initialize(mapload) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + + // by default, vis_contents is inherited from the turf that was here before + vis_contents.Cut() + + levelupdate() + if(smooth) + queue_smooth(src) + visibilityChanged() + for(var/atom/movable/AM in src) Entered(AM) - if(smooth && SSticker && SSticker.current_state == GAME_STATE_PLAYING) - queue_smooth(src) - -/turf/Initialize(mapload) - . = ..() var/area/A = loc if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) @@ -54,9 +62,11 @@ if(light_power && light_range) update_light() - if (opacity) + if(opacity) has_opaque_atom = TRUE + return INITIALIZE_HINT_NORMAL + /hook/startup/proc/smooth_world() var/watch = start_watch() log_startup_progress("Smoothing atoms...") @@ -70,16 +80,29 @@ log_startup_progress(" Smoothed atoms in [stop_watch(watch)]s.") return TRUE -/turf/Destroy() -// Adds the adjacent turfs to the current atmos processing - if(SSair) - for(var/direction in GLOB.cardinal) - if(atmos_adjacent_turfs & direction) - var/turf/simulated/T = get_step(src, direction) - if(istype(T)) - SSair.add_to_active(T) +/turf/Destroy(force) + . = QDEL_HINT_IWILLGC + if(!changing_turf) + stack_trace("Incorrect turf deletion") + changing_turf = FALSE + if(force) + ..() + //this will completely wipe turf state + var/turf/B = new world.turf(src) + for(var/A in B.contents) + qdel(A) + return + // Adds the adjacent turfs to the current atmos processing + for(var/direction in GLOB.cardinal) + if(atmos_adjacent_turfs & direction) + var/turf/simulated/T = get_step(src, direction) + if(istype(T)) + SSair.add_to_active(T) + SSair.remove_from_active(src) + visibilityChanged() + QDEL_LIST(blueprint_data) + initialized = FALSE ..() - return QDEL_HINT_HARDDEL_NOW /turf/attack_hand(mob/user as mob) user.Move_Pulled(src) @@ -209,10 +232,10 @@ var/old_corners = corners BeforeChange() - if(SSair) - SSair.remove_from_active(src) var/old_baseturf = baseturf + changing_turf = TRUE + qdel(src) //Just get the side effects and call Destroy var/turf/W = new path(src) W.baseturf = old_baseturf @@ -225,7 +248,6 @@ if(SSlighting.initialized) recalc_atom_opacity() lighting_object = old_lighting_object - affecting_lights = old_affecting_lights corners = old_corners if(old_opacity != opacity || dynamic_lighting != old_dynamic_lighting) @@ -524,12 +546,9 @@ I.appearance = AM.appearance I.appearance_flags = RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM I.loc = src - I.dir = AM.dir + I.setDir(AM.dir) I.alpha = 128 - - if(!blueprint_data) - blueprint_data = list() - blueprint_data += I + LAZYADD(blueprint_data, I) /turf/proc/add_blueprints_preround(atom/movable/AM) if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 688dc778ce3..02af9173e2b 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -40,6 +40,6 @@ nitrogen = 0 temperature = TCMB -/turf/unsimulated/floor/plating/airless/New() - ..() +/turf/unsimulated/floor/plating/airless/Initialize(mapload) + . = ..() name = "plating" diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index f6a3a62c324..a7c73971e99 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -3,9 +3,9 @@ icon = 'icons/misc/beach.dmi' var/water_overlay_image = null mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/turf/unsimulated/beach/New() - ..() + +/turf/unsimulated/beach/Initialize(mapload) + . = ..() if(water_overlay_image) var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) overlay_image.plane = GAME_PLANE @@ -15,10 +15,10 @@ name = "Sand" icon_state = "desert" mouse_opacity = MOUSE_OPACITY_ICON - -/turf/unsimulated/beach/sand/New() //adds some aesthetic randomness to the beach sand + +/turf/unsimulated/beach/sand/Initialize(mapload) + . = ..() //adds some aesthetic randomness to the beach sand icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") - ..() /turf/unsimulated/beach/sand/dense //for boundary "walls" density = 1 @@ -42,7 +42,7 @@ /turf/unsimulated/beach/water/Entered(atom/movable/AM, atom/OldLoc) . = ..() if(!linkedcontroller) - return + return if(ismob(AM)) linkedcontroller.mobinpool += AM @@ -79,8 +79,8 @@ /turf/unsimulated/beach/water/edge_drop) var/obj/effect/beach_drop_overlay/water_overlay -/turf/unsimulated/beach/water/drop/New() - ..() +/turf/unsimulated/beach/water/drop/Initialize(mapload) + . = ..() water_overlay = new(src) /turf/unsimulated/beach/water/drop/Destroy() diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm index 28f4e359223..750ebe015ae 100644 --- a/code/game/turfs/unsimulated/floor.dm +++ b/code/game/turfs/unsimulated/floor.dm @@ -7,8 +7,8 @@ name = "grass patch" icon_state = "grass1" -/turf/unsimulated/floor/grass/New() - ..() +/turf/unsimulated/floor/grass/Initialize(mapload) + . = ..() icon_state = "grass[rand(1,4)]" /turf/unsimulated/floor/snow @@ -20,8 +20,8 @@ name = "alien floor" icon_state = "alienpod1" -/turf/unsimulated/floor/abductor/New() - ..() +/turf/unsimulated/floor/abductor/Initialize(mapload) + . = ..() icon_state = "alienpod[rand(1,9)]" /turf/unsimulated/floor/vox diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 2683e236269..95b11353b86 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -27,7 +27,7 @@ entry += " - Observing" else entry += " - DEAD" - else if(istype(C.mob, /mob/new_player)) + else if(isnewplayer(C.mob)) entry += " - New Player" else entry += " - DEAD" @@ -90,7 +90,7 @@ if(isobserver(C.mob)) msg += " - Observing" - else if(istype(C.mob,/mob/new_player)) + else if(isnewplayer(C.mob)) msg += " - Lobby" else msg += " - Playing" @@ -106,7 +106,7 @@ if(isobserver(C.mob)) modmsg += " - Observing" - else if(istype(C.mob,/mob/new_player)) + else if(isnewplayer(C.mob)) modmsg += " - Lobby" else modmsg += " - Playing" diff --git a/code/game/world.dm b/code/game/world.dm index 4b1ccc641cf..355c6280aaf 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -232,7 +232,7 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) if(!C) return "No client with that name on server" - del(C) + qdel(C) return "Kick Successful" @@ -425,10 +425,12 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0) GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log" GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log" GLOB.world_asset_log = "[GLOB.log_directory]/asset.log" + GLOB.tgui_log = "[GLOB.log_directory]/tgui.log" start_log(GLOB.world_game_log) start_log(GLOB.world_href_log) start_log(GLOB.world_runtime_log) start_log(GLOB.world_qdel_log) + start_log(GLOB.tgui_log) // This log follows a special format and this path should NOT be used for anything else GLOB.runtime_summary_log = "data/logs/runtime_summary.log" diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 21b59586bfd..25693b58f47 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -146,7 +146,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = if(kickbannedckey) if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey) - del(banned_mob.client) + qdel(banned_mob.client) if(isjobban) jobban_client_fullban(ckey, job) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index df156c5b0df..1eed8083a97 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -72,7 +72,7 @@ GLOBAL_VAR_INIT(nologevent, 0) body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" - if(istype(M, /mob/new_player)) + if(isnewplayer(M)) body += " Hasn't Entered Game " else body += " \[Heal\] " @@ -147,7 +147,7 @@ GLOBAL_VAR_INIT(nologevent, 0) body += {" | Cryo "} if(M.client) - if(!istype(M, /mob/new_player)) + if(!isnewplayer(M)) body += "

" body += "Transformation:" body += "
" @@ -1004,13 +1004,13 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space /proc/kick_clients_in_lobby(message, kick_only_afk = 0) var/list/kicked_client_names = list() for(var/client/C in GLOB.clients) - if(istype(C.mob, /mob/new_player)) + if(isnewplayer(C.mob)) if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk continue if(message) to_chat(C, message) kicked_client_names.Add("[C.ckey]") - del(C) + qdel(C) return kicked_client_names //returns 1 to let the dragdrop code know we are trapping this event diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4e3d2d65f30..80a60957c75 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -337,7 +337,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( ghost.reenter_corpse() log_admin("[key_name(usr)] re-entered their body") feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - else if(istype(mob,/mob/new_player)) + else if(isnewplayer(mob)) to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.") else //ghostize @@ -542,7 +542,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban") log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban") to_chat(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.") - del(C) + qdel(C) else message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index efb5ac32f97..e89b829f421 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -268,7 +268,7 @@ else M_job = "Living" - else if(istype(M,/mob/new_player)) + else if(isnewplayer(M)) M_job = "New player" else if(isobserver(M)) @@ -356,7 +356,7 @@ dat += "[M.real_name]" else if(istype(M, /mob/living/silicon/pai)) dat += "pAI" - else if(istype(M, /mob/new_player)) + else if(isnewplayer(M)) dat += "New Player" else if(isobserver(M)) dat += "Ghost" diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index 2db6e09b354..a438a43eaec 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -187,12 +187,12 @@ log_game("SQL ERROR obtaining ckey from notes table. Error : \[[err]\]\n") return to_chat(usr, "Started regex note search for [search]. Please wait for results...") - message_admins("[usr] has started a note search with regex [search]. CPU usage may be higher.") + message_admins("[usr.ckey] has started a note search with the following regex: [search] | CPU usage may be higher.") while(query_list_notes.NextRow()) index_ckey = query_list_notes.item[1] output += "[index_ckey]
" CHECK_TICK - message_admins("The note search started by [usr] has complete. CPU should return to normal.") + message_admins("The note search started by [usr.ckey] has complete. CPU should return to normal.") else output += "
\[Add Note\]
" output += ruler diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1a040d46e90..ac8adc5fdf0 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -909,7 +909,7 @@ log_admin("[key_name(usr)] booted [key_name(M)].") message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) //M.client = null - del(M.client) + qdel(M.client) //Player Notes else if(href_list["addnote"]) @@ -1011,8 +1011,7 @@ log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - del(M.client) - //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. + qdel(M.client) if("No") var/reason = input(usr,"Please state the reason","Reason") as message|null if(!reason) @@ -1032,8 +1031,7 @@ feedback_inc("ban_perma",1) DB_ban_record(BANTYPE_PERMA, M, -1, reason) - del(M.client) - //qdel(M) + qdel(M.client) if("Cancel") return @@ -1507,7 +1505,7 @@ if(!check_rights(R_SPAWN)) return var/mob/M = locateUID(href_list["makeanimal"]) - if(istype(M, /mob/new_player)) + if(isnewplayer(M)) to_chat(usr, "This cannot be used on instances of type /mob/new_player") return if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 164d499e04a..dba9a5c26aa 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -19,7 +19,7 @@ var/list/client/targets[0] for(var/client/T) if(T.mob) - if(istype(T.mob, /mob/new_player)) + if(isnewplayer(T.mob)) targets["(New Player) - [T]"] = T else if(istype(T.mob, /mob/dead/observer)) targets["[T.mob.name](Ghost) - [T]"] = T @@ -42,7 +42,7 @@ var/list/client/targets[0] for(var/client/T) if(T.mob) - if(istype(T.mob, /mob/new_player)) + if(isnewplayer(T.mob)) targets["[T] - (New Player)"] = T else if(istype(T.mob, /mob/dead/observer)) targets["[T] - [T.mob.name](Ghost)"] = T diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 329efb8e067..496292c919b 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -251,7 +251,7 @@ GLOBAL_PROTECT(AdminProcCaller) alert("That mob doesn't seem to exist, close the panel and try again.") return - if(istype(M, /mob/new_player)) + if(isnewplayer(M)) alert("The mob must not be a new_player.") return @@ -382,7 +382,7 @@ GLOBAL_PROTECT(AdminProcCaller) if(!check_rights(R_DEBUG)) return - //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all + //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with qdel-all var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") if(confirm != "Yes") return diff --git a/code/modules/admin/verbs/onlyoneteam.dm b/code/modules/admin/verbs/onlyoneteam.dm index 9575cc5b340..974b65e05d2 100644 --- a/code/modules/admin/verbs/onlyoneteam.dm +++ b/code/modules/admin/verbs/onlyoneteam.dm @@ -73,9 +73,11 @@ ..() if((ishuman(hit_atom))) var/mob/living/carbon/human/H = hit_atom - if(H.r_hand == src) return - if(H.l_hand == src) return - var/mob/A = H.LAssailant + if(H.r_hand == src) + return + if(H.l_hand == src) + return + var/mob/A = thrownby if((H in GLOB.team_alpha) && (A in GLOB.team_alpha)) to_chat(A, "He's on your team!") return @@ -89,4 +91,3 @@ playsound(src, 'sound/items/dodgeball.ogg', 50, 1) visible_message("[H] HAS BEEN ELIMINATED!") H.melt() - return diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm index 94337a5cfac..cb144c99f01 100644 --- a/code/modules/arcade/mob_hunt/battle_computer.dm +++ b/code/modules/arcade/mob_hunt/battle_computer.dm @@ -80,7 +80,7 @@ /obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0) if(!override) if(ready && SSmob_hunt.battle_turn != team) - audible_message("You can't recall on your rival's turn!", null, 2) + atom_say("You can't recall on your rival's turn!") return card.mob_data = mob_info mob_info = null @@ -202,7 +202,7 @@ start_battle() else if(option == 2) ready = 0 - audible_message("[team] Player cancels their battle challenge.", null, 5) + atom_say("[team] Player cancels their battle challenge.") updateUsrDialog() @@ -246,7 +246,7 @@ var/message = "[mob_info.mob_name] attacks!" if(mob_info.nickname) message = "[mob_info.nickname] attacks!" - audible_message(message, null, 5) + atom_say(message) SSmob_hunt.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type()) /obj/machinery/computer/mob_battle_terminal/proc/start_battle() @@ -255,7 +255,7 @@ if(!card) //don't do anything if there isn't a card inserted return ready = 1 - audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5) + atom_say("[team] Player is ready for battle! Waiting for rival...") SSmob_hunt.start_check() /obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type) @@ -268,7 +268,7 @@ SSmob_hunt.end_turn() /obj/machinery/computer/mob_battle_terminal/proc/surrender() - audible_message("[team] Player surrenders the battle!", null, 5) + atom_say("[team] Player surrenders the battle!") SSmob_hunt.end_battle(team, 1) ////////////////////////////// diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 89eda41be7c..d1077bc81e1 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -55,7 +55,7 @@ var/datum/data/pda/app/mob_hunter_game/client = P.current_app var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee if(!client.connected) //must be connected to attempt captures - P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4) + P.atom_say("No server connection. Capture aborted.") return if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check @@ -79,7 +79,7 @@ return else //deal with the new hunter by either running away or getting caught clients_encountered += client - var/message = "[bicon(P)] " + var/message = null var/effective_run_chance = mob_info.run_chance + total_catch_mod if((effective_run_chance > 0) && prob(effective_run_chance)) message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!" @@ -91,7 +91,7 @@ else message += "Capture error! Try again." clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob - P.audible_message(message, null, 4) + P.atom_say(message) /obj/effect/nanomob/proc/despawn() if(SSmob_hunt) diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index a83df5501c9..a0a7c43a7a9 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -55,7 +55,7 @@ health_scan = M.health if(health_scan <= alarm_health) pulse() - audible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + audible_message("[bicon(src)] *beep* *beep*") toggle_scan() return return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 400b50614a6..9de2f32ed6f 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -129,7 +129,7 @@ return FALSE cooldown = 2 pulse(FALSE) - audible_message("[bicon(src)] *beep* *beep*", null, 3) + audible_message("[bicon(src)] *beep* *beep*", hearing_distance = 3) if(first) qdel(first) addtimer(CALLBACK(src, .proc/process_cooldown), 10) diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm index bd9890aa43a..92fa90891e0 100644 --- a/code/modules/awaymissions/mission_code/centcomAway.dm +++ b/code/modules/awaymissions/mission_code/centcomAway.dm @@ -7,32 +7,32 @@ /area/awaymission/centcomAway/general name = "XCC-P5831" - music = "music/ambigen3.ogg" + ambientsounds = list('sound/ambience/ambigen3.ogg') /area/awaymission/centcomAway/maint name = "XCC-P5831 Maintenance" icon_state = "away1" - music = "music/ambisin1.ogg" + ambientsounds = list('sound/ambience/ambisin1.ogg') /area/awaymission/centcomAway/thunderdome name = "XCC-P5831 Thunderdome" icon_state = "away2" - music = "music/ambisin2.ogg" + ambientsounds = list('sound/ambience/ambisin2.ogg') /area/awaymission/centcomAway/cafe name = "XCC-P5831 Kitchen Arena" icon_state = "away3" - music = "music/ambisin3.ogg" + ambientsounds = list('sound/ambience/ambisin3.ogg') /area/awaymission/centcomAway/courtroom name = "XCC-P5831 Courtroom" icon_state = "away4" - music = "music/ambisin4.ogg" + ambientsounds = list('sound/ambience/ambisin4.ogg') /area/awaymission/centcomAway/hangar name = "XCC-P5831 Hangars" icon_state = "away4" - music = "music/ambigen5.ogg" + ambientsounds = list('sound/ambience/ambigen5.ogg') //centcomAway items diff --git a/code/modules/awaymissions/mission_code/ruins/oldstation.dm b/code/modules/awaymissions/mission_code/ruins/oldstation.dm index 4d3c54f8f4b..9489ddec45b 100644 --- a/code/modules/awaymissions/mission_code/ruins/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ruins/oldstation.dm @@ -321,6 +321,7 @@ name = "Beta Station Atmospherics" icon_state = "red" has_gravity = FALSE + ambientsounds = ENGINEERING_SOUNDS /area/ruin/space/ancientstation/betanorth name = "Beta Station North Corridor" @@ -333,6 +334,7 @@ /area/ruin/space/ancientstation/engi name = "Charlie Station Engineering" icon_state = "engine" + ambientsounds = ENGINEERING_SOUNDS /area/ruin/space/ancientstation/comm name = "Charlie Station Command" diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index e1e68bd0617..8ca8bf0fbb1 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -177,6 +177,12 @@ GLOBAL_LIST_EMPTY(asset_datums) //DEFINITIONS FOR ASSET DATUMS START HERE. +/datum/asset/simple/tgui + assets = list( + "tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js', + "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css' +) + /datum/asset/simple/paper assets = list( "large_stamp-clown.png" = 'icons/paper_icons/large_stamp-clown.png', @@ -344,3 +350,15 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/mob_hunt/send(client) send_asset_list(client, assets, verify) + +// Fontawesome +/datum/asset/simple/fontawesome + verify = FALSE + assets = list( + "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', + "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', + "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', + "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', + "font-awesome.css" = 'html/font-awesome/css/all.min.css', + "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' + ) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 3cd6458afdf..9a3a1215df3 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -59,10 +59,6 @@ var/karma = 0 var/karma_spent = 0 var/karma_tab = 0 - ///////////////////////////////////////////// - // /vg/: MEDIAAAAAAAA - // Set on login. - var/datum/media_manager/media = null var/topic_debugging = 0 //if set to true, allows client to see nanoUI errors -- yes i realize this is messy but it'll make live testing infinitely easier @@ -85,9 +81,6 @@ // If set to true, this client can interact with atoms such as buttons and doors on top of regular machinery interaction var/advanced_admin_interaction = FALSE - // Has the client been varedited by an admin? [Inherits from datum now] - // var/var_edited = FALSE - var/client_keysend_amount = 0 var/next_keysend_reset = 0 var/next_keysend_trip_reset = 0 diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 561b0b48149..c4604fcf9ab 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -432,7 +432,13 @@ ////////////// //DISCONNECT// ////////////// + /client/Del() + if(!gc_destroyed) + Destroy() //Clean up signals and timers. + return ..() + +/client/Destroy() if(holder) holder.owner = null GLOB.admins -= src @@ -442,7 +448,8 @@ movingmob.client_mobs_in_contents -= mob UNSETEMPTY(movingmob.client_mobs_in_contents) Master.UpdateTickRate() - return ..() + ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening + return QDEL_HINT_HARDDEL_NOW /client/proc/donator_check() @@ -556,7 +563,7 @@ if(GLOB.panic_bunker_enabled) var/threshold = config.panic_bunker_threshold src << "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later." - del(src) + qdel(src) return // Dont insert or they can just go in again var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index d34584ff425..7ac87e5d5bc 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -113,7 +113,7 @@ /obj/item/clothing/head/soft/sec/corp name = "corporate security cap" - desc = "It's baseball hat in corpotate colours." + desc = "It's a baseball hat in corporate colours." icon_state = "corpsoft" item_color = "corp" diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 673b6a14b9a..76bcd87921d 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -318,6 +318,36 @@ if(isliving(user)) user.visible_message("[user] invades [M]'s personal space, thrusting [src] into [M.p_their()] face insistently.","You invade [M]'s personal space, thrusting [src] into [M.p_their()] face insistently. You are the law.") +////////////// +//OBJECTION!// +////////////// + +/obj/item/clothing/accessory/lawyers_badge + name = "attorney's badge" + desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet." + icon_state = "lawyerbadge" + item_state = "lawyerbadge" + item_color = "lawyerbadge" + var/cached_bubble_icon = null + +/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user) + if(prob(1)) + user.say("The testimony contradicts the evidence!") + user.visible_message("[user] shows [user.p_their()] attorney's badge.", "You show your attorney's badge.") + +/obj/item/clothing/accessory/lawyers_badge/on_attached(obj/item/clothing/under/S, mob/user) + ..() + if(has_suit && ismob(has_suit.loc)) + var/mob/M = has_suit.loc + cached_bubble_icon = M.bubble_icon + M.bubble_icon = "lawyer" + +/obj/item/clothing/accessory/lawyers_badge/on_removed(mob/user) + if(has_suit && ismob(has_suit.loc)) + var/mob/M = has_suit.loc + M.bubble_icon = cached_bubble_icon + ..() + /////////// //SCARVES// /////////// diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index f8d2029651d..1a5da52ffce 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -48,7 +48,7 @@ var/mob/living/carbon/human/target = M - if(ismachine(target)) + if(ismachineperson(target)) to_chat(user, "[target] has no skin, how do you expect to tattoo [target.p_them()]?") return diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 563ef98697d..a11ef107a8f 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -16,7 +16,7 @@ density = 1 anchored = 1 var/strength = 2 //ex_act severity number - var/life = 2 //how many things we hit before del(src) + var/life = 2 //how many things we hit before qdel(src) var/atom/goal = null /obj/effect/space_dust/weak diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index d6395bf3d43..4515a62d86b 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -115,7 +115,7 @@ //The reagents in the bottle splash all over the target, thanks for the idea Nodrak SplashReagents(target) - //Finally, smash the bottle. This kills (del) the bottle. + //Finally, smash the bottle. This kills (qdel) the bottle. smash(target, user) /obj/item/reagent_containers/food/drinks/bottle/proc/SplashReagents(mob/M) diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index 7d17ac07720..db078675db1 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -278,11 +278,6 @@ if(!UserOverride) add_attack_logs(user, occupant, "Gibbed in [src]", !!occupant.ckey ? ATKLOG_FEW : ATKLOG_ALL) - if(!iscarbon(user)) - occupant.LAssailant = null - else - occupant.LAssailant = user - else //this looks ugly but it's better than a copy-pasted startgibbing proc override occupant.create_attack_log("Was gibbed by an autogibber (\the [src])") add_attack_logs(src, occupant, "gibbed") diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index adb43034bc2..a3b522187e2 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -184,3 +184,4 @@ D.consume(src) else return ..() + diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 5bc63d4bace..1be8f11527d 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -978,7 +978,7 @@ /obj/machinery/hydroponics/proc/spawnplant() // why would you put strange reagent in a hydro tray you monster I bet you also feed them blood var/list/livingplants = list(/mob/living/simple_animal/hostile/tree, /mob/living/simple_animal/hostile/killertomato) var/chosen = pick(livingplants) - var/mob/living/simple_animal/hostile/C = new chosen + var/mob/living/simple_animal/hostile/C = new chosen(get_turf(src)) C.faction = list("plants") /obj/machinery/hydroponics/proc/become_self_sufficient() // Ambrosia Gaia effect diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm index 5200c4bb00b..f9aae71b2a5 100644 --- a/code/modules/library/computers/checkout.dm +++ b/code/modules/library/computers/checkout.dm @@ -207,7 +207,7 @@ var/obj/item/barcodescanner/scanner = W scanner.computer = src to_chat(user, "[scanner]'s associated machine has been set to [src].") - audible_message("[src] lets out a low, short blip.", 2) + audible_message("[src] lets out a low, short blip.", hearing_distance = 2) return 1 else return ..() @@ -413,7 +413,7 @@ return if(bibledelay) - audible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") + visible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") else bibledelay = 1 spawn(60) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 7b0b782bdca..9398bd9683c 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -220,13 +220,14 @@ var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) for(T in view(CEILING(light_range, 1), source_turf)) - if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources) || T.has_opaque_atom) + if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources)) continue - if(!T.lighting_corners_initialised) - T.generate_missing_corners() - for(thing in T.corners) - C = thing - corners[C] = 0 + if(!T.has_opaque_atom) + if(!T.lighting_corners_initialised) + T.generate_missing_corners() + for(thing in T.corners) + C = thing + corners[C] = 0 turfs += T source_turf.luminosity = oldlum diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 5f18a27adc9..99e0af0ca6a 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -135,7 +135,7 @@ if(message) for(var/mob/M in GLOB.player_list) - if(istype(M, /mob/new_player)) + if(isnewplayer(M)) continue if(check_rights(R_ADMIN|R_MOD, 0, M) && M.get_preference(CHAT_DEAD)) // Show the emote to admins/mods diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 92d936227cc..a46268daf50 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -767,17 +767,6 @@ desc = "Bark bark bark." key = "vu" -/datum/language/zombie - name = "Zombie" - desc = "BRAAAAAAINS!" - speech_verb = "moans" - whisper_verb = "mutters" - exclaim_verb = "wails" - colour = "zombie" - key = "zom" - flags = RESTRICTED - syllables = list("BRAAAAAAAAAAAAAAAAINS", "BRAAINS", "BRAINS") - /mob/proc/grant_all_babel_languages() for(var/la in GLOB.all_languages) var/datum/language/new_language = GLOB.all_languages[la] diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index b12f62623e8..f6354b61940 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -2,6 +2,7 @@ name = "alien" voice_name = "alien" speak_emote = list("hisses") + bubble_icon = "alien" icon = 'icons/mob/alien.dmi' gender = NEUTER dna = null diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm index fec3012529e..58bdb3a9ecc 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -136,14 +136,11 @@ Doesn't work on other aliens/AI.*/ set category = "Alien" if(powerc()) - if(stomach_contents.len) + if(LAZYLEN(stomach_contents)) for(var/mob/M in src) - if(M in stomach_contents) - stomach_contents.Remove(M) - M.forceMove(loc) - //Paralyse(10) - src.visible_message("[src] hurls out the contents of [p_their()] stomach!") - return + LAZYREMOVE(stomach_contents, M) + M.forceMove(drop_location()) + visible_message("[src] hurls out the contents of [p_their()] stomach!") /mob/living/carbon/proc/getPlasma() var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel) diff --git a/code/modules/mob/living/carbon/alien/humanoid/empress.dm b/code/modules/mob/living/carbon/alien/humanoid/empress.dm index 63514686f3d..a28b3f33a53 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/empress.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/empress.dm @@ -6,6 +6,7 @@ icon_state = "alienq_s" status_flags = CANPARALYSE mob_size = MOB_SIZE_LARGE + bubble_icon = "alienroyal" large = 1 ventcrawler = 0 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 3aef7135679..98585262518 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -22,8 +22,7 @@ return ..() /mob/living/carbon/handle_atom_del(atom/A) - if(A in processing_patches) - processing_patches -= A + LAZYREMOVE(processing_patches, A) return ..() /mob/living/carbon/blob_act(obj/structure/blob/B) @@ -51,41 +50,40 @@ /mob/living/carbon/var/last_stomach_attack //defining this here because no one would look in carbon_defines for it -/mob/living/carbon/relaymove(var/mob/user, direction) - if(user in src.stomach_contents) - if(last_stomach_attack + STOMACH_ATTACK_DELAY > world.time) return +/mob/living/carbon/relaymove(mob/user, direction) + if(LAZYLEN(stomach_contents)) + if(user in stomach_contents) + if(last_stomach_attack + STOMACH_ATTACK_DELAY > world.time) + return - last_stomach_attack = world.time - for(var/mob/M in hearers(4, src)) - if(M.client) - M.show_message(text("You hear something rumbling inside [src]'s stomach..."), 2) - - var/obj/item/I = user.get_active_hand() - if(I && I.force) - var/d = rand(round(I.force / 4), I.force) - - if(istype(src, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - var/obj/item/organ/external/organ = H.get_organ("chest") - if(istype(organ)) - if(organ.receive_damage(d, 0)) - H.UpdateDamageIcon() - - H.updatehealth("stomach attack") - - else - src.take_organ_damage(d) - - for(var/mob/M in viewers(user, null)) + last_stomach_attack = world.time + for(var/mob/M in hearers(4, src)) if(M.client) - M.show_message(text("[user] attacks [src]'s stomach wall with the [I.name]!"), 2) - playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1) + M.show_message(text("You hear something rumbling inside [src]'s stomach..."), 2) - if(prob(src.getBruteLoss() - 50)) - for(var/atom/movable/A in stomach_contents) - A.forceMove(drop_location()) - stomach_contents.Remove(A) - src.gib() + var/obj/item/I = user.get_active_hand() + if(I && I.force) + var/d = rand(round(I.force / 4), I.force) + + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + var/obj/item/organ/external/organ = H.get_organ("chest") + if(istype(organ)) + if(organ.receive_damage(d, 0)) + H.UpdateDamageIcon() + + H.updatehealth("stomach attack") + + else + take_organ_damage(d) + + for(var/mob/M in viewers(user, null)) + if(M.client) + M.show_message(text("[user] attacks [src]'s stomach wall with the [I.name]!"), 2) + playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1) + + if(prob(getBruteLoss() - 50)) + gib() #undef STOMACH_ATTACK_DELAY @@ -140,9 +138,8 @@ I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5) for(var/mob/M in src) - if(M in src.stomach_contents) - src.stomach_contents.Remove(M) - M.forceMove(get_turf(src)) + LAZYREMOVE(stomach_contents, M) + M.forceMove(drop_location()) visible_message("[M] bursts out of [src]!") /mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) @@ -1069,10 +1066,6 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven /mob/living/carbon/proc/forceFedAttackLog(var/obj/item/reagent_containers/food/toEat, mob/user) add_attack_logs(user, src, "Fed [toEat]. Reagents: [toEat.reagents.log_list(toEat)]", toEat.reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) - if(!iscarbon(user)) - LAssailant = null - else - LAssailant = user /*TO DO - If/when stomach organs are introduced, override this at the human level sending the item to the stomach @@ -1205,3 +1198,18 @@ so that different stomachs can handle things in different ways VB*/ I.acid_level = 0 //washes off the acid on our clothes I.extinguish() //extinguishes our clothes ..() + +/mob/living/carbon/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE) + if(head) + if(head.clean_blood()) + update_inv_head() + if(head.flags_inv & HIDEMASK) + clean_mask = FALSE + if(wear_suit) + if(wear_suit.clean_blood()) + update_inv_wear_suit() + if(wear_suit.flags_inv & HIDESHOES) + clean_feet = FALSE + if(wear_suit.flags_inv & HIDEGLOVES) + clean_hands = FALSE + ..(clean_hands, clean_mask, clean_feet) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 97ecf4dd08b..667fd86204e 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,11 +1,10 @@ /mob/living/carbon gender = MALE pressure_resistance = 15 - var/list/stomach_contents = list() - var/list/processing_patches = list() + var/list/stomach_contents + var/list/processing_patches var/list/internal_organs = list() var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access. - var/antibodies = 0 var/life_tick = 0 // The amount of life ticks that have processed on this mob. diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 6b7a0a2b15d..d213ee7206f 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -6,7 +6,7 @@ canmove = 0 icon = null invisibility = 101 - if(!isSynthetic()) + if(!ismachineperson(src)) animation = new(loc) animation.icon_state = "blank" animation.icon = 'icons/mob/mob.dmi' @@ -32,12 +32,11 @@ E.droplimb(DROPLIMB_SHARP) for(var/mob/M in src) - if(M in stomach_contents) - stomach_contents.Remove(M) - M.forceMove(get_turf(src)) + LAZYREMOVE(stomach_contents, M) + M.forceMove(drop_location()) visible_message("[M] bursts out of [src]!") - if(!isSynthetic()) + if(!ismachineperson(src)) flick("gibbed-h", animation) hgibs(loc, dna) else @@ -106,11 +105,6 @@ //Handle species-specific deaths. dna.species.handle_death(gibbed, src) - if(ishuman(LAssailant)) - var/mob/living/carbon/human/H=LAssailant - if(H.mind) - H.mind.kills += "[key_name(src)]" - if(SSticker && SSticker.mode) // log_world("k") sql_report_death(src) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index fed50d442c9..10c2ac8f119 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -34,7 +34,7 @@ //Cooldown-inducing emotes if("ping", "pings", "buzz", "buzzes", "beep", "beeps", "yes", "no", "buzz2") var/found_machine_head = FALSE - if(ismachine(src)) //Only Machines can beep, ping, and buzz, yes, no, and make a silly sad trombone noise. + if(ismachineperson(src)) //Only Machines can beep, ping, and buzz, yes, no, and make a silly sad trombone noise. on_CD = handle_emote_CD() //proc located in code\modules\mob\emote.dm found_machine_head = TRUE else @@ -947,7 +947,7 @@ if("Skrell") emotelist += "\nSkrell specific emotes :- warble(s)" - if(ismachine(src)) + if(ismachineperson(src)) emotelist += "\nMachine specific emotes :- beep(s)-(none)/mob, buzz(es)-none/mob, no-(none)/mob, ping(s)-(none)/mob, yes-(none)/mob, buzz2-(none)/mob" else var/obj/item/organ/external/head/H = get_organ("head") // If you have a robotic head, you can make beep-boop noises diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index b67e01924c8..31825ea3765 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -208,7 +208,7 @@ if(!E) wound_flavor_text["[organ_tag]"] = "[p_they(TRUE)] [p_are()] missing [p_their()] [organ_descriptor].\n" else - if(!isSynthetic()) + if(!ismachineperson(src)) if(E.is_robotic()) wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a robotic [E.name]!\n" @@ -252,7 +252,7 @@ var/temp = getBruteLoss() //no need to calculate each of these twice if(temp) - var/brute_message = !isSynthetic() ? "bruising" : "denting" + var/brute_message = !ismachineperson(src) ? "bruising" : "denting" if(temp < 30) msg += "[p_they(TRUE)] [p_have()] minor [brute_message ].\n" else @@ -301,13 +301,13 @@ else if(nutrition >= NUTRITION_LEVEL_FAT) msg += "[p_they(TRUE)] [p_are()] quite chubby.\n" - if(!isSynthetic() && blood_volume < BLOOD_VOLUME_SAFE) + if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE) msg += "[p_they(TRUE)] [p_have()] pale skin.\n" if(bleedsuppress) msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n" else if(bleed_rate) - var/bleed_message = !isSynthetic() ? "bleeding" : "leaking" + var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking" msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n" if(reagents.has_reagent("teslium")) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 9df498328fa..16f1738d2f6 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -18,7 +18,7 @@ dna = new /datum/dna(null) // Species name is handled by set_species() - ..() + . = ..() set_species(new_species, 1, delay_icon_update = 1, skip_same_check = TRUE) @@ -68,55 +68,55 @@ status_flags = GODMODE|CANPUSH /mob/living/carbon/human/skrell/Initialize(mapload) - ..(mapload, /datum/species/skrell) + . = ..(mapload, /datum/species/skrell) /mob/living/carbon/human/tajaran/Initialize(mapload) - ..(mapload, /datum/species/tajaran) + . = ..(mapload, /datum/species/tajaran) /mob/living/carbon/human/vulpkanin/Initialize(mapload) - ..(mapload, /datum/species/vulpkanin) + . = ..(mapload, /datum/species/vulpkanin) /mob/living/carbon/human/unathi/Initialize(mapload) - ..(mapload, /datum/species/unathi) + . = ..(mapload, /datum/species/unathi) /mob/living/carbon/human/vox/Initialize(mapload) - ..(mapload, /datum/species/vox) + . = ..(mapload, /datum/species/vox) /mob/living/carbon/human/voxarmalis/Initialize(mapload) - ..(mapload, /datum/species/vox/armalis) + . = ..(mapload, /datum/species/vox/armalis) /mob/living/carbon/human/skeleton/Initialize(mapload) - ..(mapload, /datum/species/skeleton) + . = ..(mapload, /datum/species/skeleton) /mob/living/carbon/human/kidan/Initialize(mapload) - ..(mapload, /datum/species/kidan) + . = ..(mapload, /datum/species/kidan) /mob/living/carbon/human/plasma/Initialize(mapload) - ..(mapload, /datum/species/plasmaman) + . = ..(mapload, /datum/species/plasmaman) /mob/living/carbon/human/slime/Initialize(mapload) - ..(mapload, /datum/species/slime) + . = ..(mapload, /datum/species/slime) /mob/living/carbon/human/grey/Initialize(mapload) - ..(mapload, /datum/species/grey) + . = ..(mapload, /datum/species/grey) /mob/living/carbon/human/abductor/Initialize(mapload) - ..(mapload, /datum/species/abductor) + . = ..(mapload, /datum/species/abductor) /mob/living/carbon/human/diona/Initialize(mapload) - ..(mapload, /datum/species/diona) + . = ..(mapload, /datum/species/diona) /mob/living/carbon/human/pod_diona/Initialize(mapload) - ..(mapload, /datum/species/diona/pod) + . = ..(mapload, /datum/species/diona/pod) /mob/living/carbon/human/machine/Initialize(mapload) - ..(mapload, /datum/species/machine) + . = ..(mapload, /datum/species/machine) /mob/living/carbon/human/machine/created name = "Integrated Robotic Chassis" /mob/living/carbon/human/machine/created/Initialize(mapload) - ..() + . = ..() rename_character(null, "Integrated Robotic Chassis ([rand(1, 9999)])") update_dna() for(var/obj/item/organ/external/E in bodyparts) @@ -129,34 +129,34 @@ death() /mob/living/carbon/human/shadow/Initialize(mapload) - ..(mapload, /datum/species/shadow) + . = ..(mapload, /datum/species/shadow) /mob/living/carbon/human/golem/Initialize(mapload) - ..(mapload, /datum/species/golem) + . = ..(mapload, /datum/species/golem) /mob/living/carbon/human/wryn/Initialize(mapload) - ..(mapload, /datum/species/wryn) + . = ..(mapload, /datum/species/wryn) /mob/living/carbon/human/nucleation/Initialize(mapload) - ..(mapload, /datum/species/nucleation) + . = ..(mapload, /datum/species/nucleation) /mob/living/carbon/human/drask/Initialize(mapload) - ..(mapload, /datum/species/drask) + . = ..(mapload, /datum/species/drask) /mob/living/carbon/human/monkey/Initialize(mapload) - ..(mapload, /datum/species/monkey) + . = ..(mapload, /datum/species/monkey) /mob/living/carbon/human/farwa/Initialize(mapload) - ..(mapload, /datum/species/monkey/tajaran) + . = ..(mapload, /datum/species/monkey/tajaran) /mob/living/carbon/human/wolpin/Initialize(mapload) - ..(mapload, /datum/species/monkey/vulpkanin) + . = ..(mapload, /datum/species/monkey/vulpkanin) /mob/living/carbon/human/neara/Initialize(mapload) - ..(mapload, /datum/species/monkey/skrell) + . = ..(mapload, /datum/species/monkey/skrell) /mob/living/carbon/human/stok/Initialize(mapload) - ..(mapload, /datum/species/monkey/unathi) + . = ..(mapload, /datum/species/monkey/unathi) /mob/living/carbon/human/Stat() ..() @@ -1162,18 +1162,6 @@ custom_pain("You feel a stabbing pain in your chest!") L.damage = L.min_bruised_damage -//returns 1 if made bloody, returns 0 otherwise - -/mob/living/carbon/human/clean_blood(var/clean_feet) - .=..() - if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len) - feet_blood_color = null - qdel(feet_blood_DNA) - bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - blood_state = BLOOD_STATE_NOT_BLOODY - update_inv_shoes() - return 1 - /mob/living/carbon/human/cuff_resist(obj/item/I) if(HULK in mutations) say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) @@ -1701,14 +1689,14 @@ Eyes need to have significantly high darksight to shine unless the mob has the X /mob/living/carbon/human/forceFed(var/obj/item/reagent_containers/food/toEat, mob/user, fullness) if(!check_has_mouth()) - if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachine(src))))) + if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachineperson(src))))) to_chat(user, "Where do you intend to put \the [toEat]? \The [src] doesn't have a mouth!") return 0 return ..() /mob/living/carbon/human/selfDrink(var/obj/item/reagent_containers/food/drinks/toDrink) if(!check_has_mouth()) - if(!ismachine(src)) + if(!ismachineperson(src)) to_chat(src, "Where do you intend to put \the [src]? You don't have a mouth!") return 0 else diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index a849235af64..21a3e8bf274 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -84,7 +84,7 @@ emp_act E.heal_damage(rembrute,0,0,1) rembrute = nrembrute user.visible_message("[user] patches some dents on [src]'s [E.name] with [I].") - if(bleed_rate && isSynthetic()) + if(bleed_rate && ismachineperson(src)) bleed_rate = 0 user.visible_message("[user] patches some leaks on [src] with [I].") if(IgniteMob()) @@ -387,11 +387,6 @@ emp_act to_chat(user, "You hack off a chunk of meat from [name]") if(!meatleft) add_attack_logs(user, src, "Chopped up into meat") - if(!iscarbon(user)) - LAssailant = null - else - LAssailant = user - qdel(src) var/obj/item/organ/external/affecting = get_organ(ran_zone(user.zone_selected)) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 60b6da987dd..3d365afa940 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -37,7 +37,6 @@ GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new()) var/obj/item/s_store = null var/icon/stand_icon = null - var/icon/lying_icon = null var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 5030c5c4170..90653a6750b 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -635,7 +635,7 @@ else overeatduration -= 2 - if(!ismachine(src) && nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) //Gosh damn snowflakey IPCs + if(!ismachineperson(src) && nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) //Gosh damn snowflakey IPCs var/datum/disease/D = new /datum/disease/critical/hypoglycemia ForceContractDisease(D) @@ -697,7 +697,7 @@ alcohol_strength /= sober_str var/obj/item/organ/internal/liver/L - if(!isSynthetic()) + if(!ismachineperson(src)) L = get_int_organ(/obj/item/organ/internal/liver) if(L) alcohol_strength *= L.alcohol_intensity @@ -719,7 +719,7 @@ AdjustConfused(3 / sober_str) if(alcohol_strength >= blur_start) //blurry eyes EyeBlurry(10 / sober_str) - if(!isSynthetic()) //stuff only for non-synthetics + if(!ismachineperson(src)) //stuff only for non-synthetics if(alcohol_strength >= vomit_start) //vomiting if(prob(8)) fakevomit() diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm index 41c108dadb9..9dcf83f2e9a 100644 --- a/code/modules/mob/living/carbon/human/npcs.dm +++ b/code/modules/mob/living/carbon/human/npcs.dm @@ -7,7 +7,7 @@ species_exception = list(/datum/species/monkey) /mob/living/carbon/human/monkey/punpun/Initialize(mapload) - ..() + . = ..() name = "Pun Pun" real_name = name equip_to_slot(new /obj/item/clothing/under/punpun(src), slot_w_uniform) diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 21695e25e78..f65cefff49d 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -53,7 +53,6 @@ var/stun_mod = 1 // If a species is more/less impacated by stuns/weakens/paralysis var/speed_mod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster var/blood_damage_type = OXY //What type of damage does this species take if it's low on blood? - var/obj/item/mutanthands var/total_health = 100 var/punchdamagelow = 0 //lowest possible punch damage var/punchdamagehigh = 9 //highest possible punch damage @@ -352,21 +351,6 @@ if(organ.receive_damage(damage, 0, sharp, used_weapon)) H.UpdateDamageIcon() - if(H.LAssailant && ishuman(H.LAssailant)) //superheros still get the comical hit markers - var/mob/living/carbon/human/A = H.LAssailant - if(A.mind && (A.mind in (SSticker.mode.superheroes) || SSticker.mode.supervillains || SSticker.mode.greyshirts)) - var/list/attack_bubble_recipients = list() - var/mob/living/user - for(var/mob/O in viewers(user, src)) - if(O.client && O.has_vision(information_only=TRUE)) - attack_bubble_recipients.Add(O.client) - spawn(0) - var/image/dmgIcon = image('icons/effects/hit_blips.dmi', src, "dmg[rand(1,2)]",MOB_LAYER+1) - dmgIcon.pixel_x = (!H.lying) ? rand(-3,3) : rand(-11,12) - dmgIcon.pixel_y = (!H.lying) ? rand(-11,9) : rand(-10,1) - flick_overlay(dmgIcon, attack_bubble_recipients, 9) - - if(BURN) H.damageoverlaytemp = 20 damage = damage * burn_mod @@ -443,6 +427,9 @@ else target.LAssailant = user + target.lastattacker = user.real_name + target.lastattackerckey = user.ckey + var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh) damage += attack.damage if(!damage) diff --git a/code/modules/mob/living/carbon/human/species/zombies.dm b/code/modules/mob/living/carbon/human/species/zombies.dm deleted file mode 100644 index 1f1c4d979b7..00000000000 --- a/code/modules/mob/living/carbon/human/species/zombies.dm +++ /dev/null @@ -1,102 +0,0 @@ -#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin - -/datum/species/zombie - // 1spooky - name = "High-Functioning Zombie" - name_plural = "High-Functioning Zombies" - icobase = 'icons/mob/human_races/r_zombie.dmi' - deform = 'icons/mob/human_races/r_def_zombie.dmi' - dies_at_threshold = TRUE - language = "Zombie" - species_traits = list(NO_BLOOD, NOZOMBIE, NOTRANSSTING, NO_BREATHE, RADIMMUNE, NO_SCAN) - var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg') - warning_low_pressure = -1 - hazard_low_pressure = -1 - hazard_high_pressure = 999999999 - warning_high_pressure = 999999999 - cold_level_1 = -1 - cold_level_2 = -1 - cold_level_3 = -1 - tox_mod = 0 - flesh_color = "#00FF00" // for green examine text - bodyflags = HAS_SKIN_COLOR - dietflags = DIET_CARN - has_organ = list( - "heart" = /obj/item/organ/internal/heart, - "lungs" = /obj/item/organ/internal/lungs, - "liver" = /obj/item/organ/internal/liver, - "kidneys" = /obj/item/organ/internal/kidneys, - "brain" = /obj/item/organ/internal/brain, - "appendix" = /obj/item/organ/internal/appendix, - "eyes" = /obj/item/organ/internal/eyes, - "ears" = /obj/item/organ/internal/ears) - - -/datum/species/zombie/infectious - name = "Infectious Zombie" - mutanthands = /obj/item/zombie_hand - icobase = 'icons/mob/human_races/r_zombie.dmi' - deform = 'icons/mob/human_races/r_def_zombie.dmi' - brute_mod = 0.8 // 120 damage to KO a zombie, which kills it - burn_mod = 0.8 - clone_mod = 0.8 - brain_mod = 0.8 - stamina_mod = 0.8 - speed_mod = 1.6 - default_language = "Zombie" - var/heal_rate = 1 - var/regen_cooldown = 0 - -/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H, amount) - . = min(20, amount) - -/datum/species/zombie/infectious/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null) - . = ..() - if(damage) - regen_cooldown = world.time + REGENERATION_DELAY - -/datum/species/zombie/infectious/handle_life(mob/living/carbon/human/H) - . = ..() - H.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW - - //Zombies never actually die, they just fall down until they regenerate enough to rise back up. - //They must be restrained, beheaded or gibbed to stop being a threat. - if(regen_cooldown < world.time) - var/heal_amt = heal_rate - if(H.InCritical()) - heal_amt *= 2 - H.heal_overall_damage(heal_amt,heal_amt) - H.adjustToxLoss(-heal_amt) - if(!H.InCritical() && prob(4)) - playsound(H, pick(spooks), 50, TRUE, 10) - -//Congrats you somehow died so hard you stopped being a zombie -/datum/species/zombie/infectious/handle_death(gibbed, mob/living/carbon/C) - . = ..() - var/obj/item/organ/internal/zombie_infection/infection - infection = C.get_organ_slot("zombie_infection") - if(infection) - qdel(infection) - -/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) - . = ..() - // Deal with the source of this zombie corruption - // Infection organ needs to be handled separately from mutant_organs - // because it persists through species transitions - if(mutanthands) - H.drop_l_hand() - H.drop_r_hand() - H.put_in_hands(new mutanthands()) - H.put_in_hands(new mutanthands()) - var/obj/item/organ/internal/zombie_infection/infection - infection = H.get_organ_slot("zombie_infection") - if(!infection) - infection = new() - infection.insert(H) - -/datum/species/zombie/infectious/on_species_loss(mob/living/carbon/human/C, datum/species/old_species) - QDEL_NULL(C.r_hand) - QDEL_NULL(C.l_hand) - return ..() - -#undef REGENERATION_DELAY diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 2cce48d51c2..81907ba516f 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -376,7 +376,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) //base icons var/icon/hair_standing = new /icon('icons/mob/human_face.dmi',"bald_s") - if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(isSynthetic()))) + if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(ismachineperson(src)))) var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] if(hair_style && hair_style.species_allowed) if((head_organ.dna.species.name in hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index a2df4b30e9e..c11eaca5293 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -11,6 +11,10 @@ if(stat != DEAD) handle_organs() + //stuff in the stomach + if(LAZYLEN(stomach_contents)) + handle_stomach(times_fired) + . = ..() if(QDELETED(src)) @@ -254,14 +258,15 @@ if(times_fired % 20==2) //dry off a bit once every 20 ticks or so wetlevel = max(wetlevel - 1,0) -/mob/living/carbon/handle_stomach(times_fired) - for(var/mob/living/M in stomach_contents) +/mob/living/carbon/proc/handle_stomach(times_fired) + for(var/thing in stomach_contents) + var/mob/living/M = thing if(M.loc != src) - stomach_contents.Remove(M) + LAZYREMOVE(stomach_contents, M) continue if(stat != DEAD) if(M.stat == DEAD) - stomach_contents.Remove(M) + LAZYREMOVE(stomach_contents, M) qdel(M) continue if(times_fired % 3 == 1) @@ -476,7 +481,7 @@ P.reagents.remove_any(applied_amount * 0.5) else if(!P.reagents || P.reagents.total_volume <= 0) - processing_patches -= P + LAZYREMOVE(processing_patches, P) qdel(P) /mob/living/carbon/proc/handle_germs() diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 6d020ccdad1..3062ef461f9 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -58,9 +58,6 @@ handle_fire() - //stuff in the stomach - handle_stomach(times_fired) - update_gravity(mob_has_gravity()) if(pulling) @@ -117,9 +114,6 @@ /mob/living/proc/handle_environment(datum/gas_mixture/environment) return -/mob/living/proc/handle_stomach(times_fired) - return - /mob/living/proc/update_pulling() if(incapacitated()) stop_pulling() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 9f768ffc76d..f3d23ca3f51 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -682,9 +682,6 @@ *///////////////////// /mob/living/proc/resist_grab() var/resisting = 0 - for(var/obj/O in requests) - qdel(O) - resisting++ for(var/X in grabbed_by) var/obj/item/grab/G = X resisting++ diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index fb633f116bd..630b2223321 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -61,7 +61,7 @@ proc/get_radio_key_from_channel(var/channel) return default_language /mob/living/proc/handle_speech_problems(list/message_pieces, var/verb) - var/robot = isSynthetic() + var/robot = ismachineperson(src) for(var/datum/multilingual_say_piece/S in message_pieces) if(S.speaking && S.speaking.flags & NO_STUTTER) continue @@ -269,12 +269,12 @@ proc/get_radio_key_from_channel(var/channel) M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency) if(M.client) speech_bubble_recipients.Add(M.client) - spawn(0) - if(loc && !isturf(loc)) - var/atom/A = loc //Non-turf, let it handle the speech bubble - A.speech_bubble("hR[speech_bubble_test]", A, speech_bubble_recipients) - else //Turf, leave speech bubbles to the mob - speech_bubble("h[speech_bubble_test]", src, speech_bubble_recipients) + + if(loc && !isturf(loc)) + var/atom/A = loc //Non-turf, let it handle the speech bubble + A.speech_bubble("[A.bubble_icon][speech_bubble_test]", A, speech_bubble_recipients) + else //Turf, leave speech bubbles to the mob + speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients) for(var/obj/O in listening_obj) spawn(0) @@ -462,10 +462,7 @@ proc/get_radio_key_from_channel(var/channel) if(M.client) speech_bubble_recipients.Add(M.client) - spawn(0) - var/image/I = image('icons/mob/talk.dmi', src, "h[speech_bubble_test]", MOB_LAYER + 1) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - flick_overlay(I, speech_bubble_recipients, 30) + speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients) if(watching.len) var/rendered = "[name] [not_heard]." @@ -474,7 +471,7 @@ proc/get_radio_key_from_channel(var/channel) return 1 -/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, MOB_LAYER + 1) +/mob/living/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) + var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER) I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - flick_overlay(I, bubble_recipients, 30) + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 85c4ceac427..1c232f9f43c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -243,7 +243,8 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( /mob/living/silicon/ai/proc/show_borg_info() stat(null, text("Connected cyborgs: [connected_robots.len]")) - for(var/mob/living/silicon/robot/R in connected_robots) + for(var/thing in connected_robots) + var/mob/living/silicon/robot/R = thing var/robot_status = "Nominal" if(R.stat || !R.client) robot_status = "OFFLINE" @@ -251,8 +252,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( robot_status = "DEPOWERED" // Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies! var/area/A = get_area(R) + var/area_name = A ? sanitize(A.name) : "Unknown" stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \ - Module: [R.designation] | Loc: [sanitize(A.name)] | Status: [robot_status]")) + Module: [R.designation] | Loc: [area_name] | Status: [robot_status]")) /mob/living/silicon/ai/rename_character(oldname, newname) if(!..(oldname, newname)) diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 26b5683d1a3..99d135df93b 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -253,7 +253,7 @@ proc/robot_healthscan(mob/user, mob/living/M) to_chat(user, "You can't analyze non-robotic things!") return - + switch(scan_type) if("robot") var/BU = M.getFireLoss() > 50 ? "[M.getFireLoss()]" : M.getFireLoss() @@ -269,7 +269,7 @@ proc/robot_healthscan(mob/user, mob/living/M) to_chat(user, "Localized Damage:") if(!LAZYLEN(damaged) && !LAZYLEN(missing)) to_chat(user, "\t Components are OK.") - else + else if(LAZYLEN(damaged)) for(var/datum/robot_component/org in damaged) user.show_message(text("\t []: [][] - [] - [] - []", \ @@ -282,7 +282,7 @@ proc/robot_healthscan(mob/user, mob/living/M) if(LAZYLEN(missing)) for(var/datum/robot_component/org in missing) user.show_message("\t [capitalize(org.name)]: MISSING") - + if(H.emagged && prob(5)) to_chat(user, "\t ERROR: INTERNAL SYSTEMS COMPROMISED") @@ -313,7 +313,7 @@ proc/robot_healthscan(mob/user, mob/living/M) if(!organ_found) to_chat(user, "No prosthetics located.") - if(H.isSynthetic()) + if(ismachineperson(H)) to_chat(user, "Internal Fluid Level:[H.blood_volume]/[H.max_blood]") if(H.bleed_rate) to_chat(user, "Warning:External component leak detected!") diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 39bedc78c77..f932f4270ba 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -6,6 +6,7 @@ icon_state = "repairbot" maxHealth = 35 health = 35 + bubble_icon = "machine" universal_speak = 0 universal_understand = 1 gender = NEUTER diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 2eb1e4669ab..d7f42a9ceb5 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( icon_state = "robot" maxHealth = 100 health = 100 + bubble_icon = "robot" universal_understand = 1 deathgasp_on_death = TRUE @@ -16,8 +17,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/custom_name = "" var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best -//Hud stuff - + //Hud stuff var/obj/screen/inv1 = null var/obj/screen/inv2 = null var/obj/screen/inv3 = null @@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not var/obj/screen/robot_modules_background -//3 Modules can be activated at any one time. + //3 Modules can be activated at any one time. var/obj/item/robot_module/module = null var/module_active = null var/module_state_1 = null @@ -57,6 +57,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/is_emaggable = TRUE var/eye_protection = 0 var/ear_protection = 0 + var/damage_protection = 0 + var/emp_protection = FALSE + var/xeno_disarm_chance = 85 var/list/force_modules = list() var/allow_rename = TRUE @@ -86,7 +89,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/tracking_entities = 0 //The number of known entities currently accessing the internal camera var/braintype = "Cyborg" var/base_icon = "" - var/crisis = 0 var/modules_break = TRUE var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. @@ -97,6 +99,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + var/default_cell_type = /obj/item/stock_parts/cell/high var/magpulse = 0 var/ionpulse = 0 // Jetpack-like effect. var/ionpulse_on = 0 // Jetpack-like effect. @@ -132,7 +135,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( init() - if(!scrambledcodes && !camera) + if(!camera && (!scrambledcodes || designation == "ERT")) camera = new /obj/machinery/camera(src) camera.c_tag = real_name camera.network = list("SS13","Robots") @@ -144,7 +147,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( mmi.icon_state = "boris" if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly - cell = new /obj/item/stock_parts/cell/high(src) + cell = new default_cell_type(src) initialize_components() //if(!unfinished) @@ -288,12 +291,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/pick_module() if(module) return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") + var/list/modules = list("Generalist", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") if(islist(force_modules) && force_modules.len) modules = force_modules.Copy() - if(GLOB.security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) - to_chat(src, "Crisis mode active. The combat module is now available.") - modules += "Combat" if(mmi != null && mmi.alien) modules = list("Hunter") modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules @@ -306,9 +306,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return switch(modtype) - if("Standard") + if("Generalist") module = new /obj/item/robot_module/standard(src) - module.channels = list("Service" = 1) + module.channels = list("Engineering" = 1, "Medical" = 1, "Security" = 1, "Service" = 1) module_sprites["Basic"] = "robot_old" module_sprites["Android"] = "droid" module_sprites["Default"] = "Standard" @@ -337,6 +337,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( module_sprites["Standard"] = "Standard-Mine" module_sprites["Noble-DIG"] = "Noble-DIG" module_sprites["Cricket"] = "Cricket-MINE" + module_sprites["Lavaland"] = "lavaland" if("Medical") module = new /obj/item/robot_module/medical(src) @@ -353,6 +354,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( status_flags &= ~CANPUSH if("Security") + if(!weapons_unlock) + var/count_secborgs = 0 + for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list) + if(R && R.stat != DEAD && R.module && istype(R.module, /obj/item/robot_module/security)) + count_secborgs++ + var/max_secborgs = 2 + if(GLOB.security_level == SEC_LEVEL_GREEN) + max_secborgs = 1 + if(count_secborgs >= max_secborgs) + to_chat(src, "There are too many Security cyborgs active. Please choose another module.") + return module = new /obj/item/robot_module/security(src) module.channels = list("Security" = 1) module_sprites["Basic"] = "secborg" @@ -387,10 +399,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( module_sprites["Noble-CLN"] = "Noble-CLN" module_sprites["Cricket"] = "Cricket-JANI" - if("Combat") - module = new /obj/item/robot_module/combat(src) + if("Destroyer") // Rolling Borg + module = new /obj/item/robot_module/destroyer(src) module.channels = list("Security" = 1) icon_state = "droidcombat" + status_flags &= ~CANPUSH + + if("Combat") // Gamma ERT + module = new /obj/item/robot_module/combat(src) + icon_state = "ertgamma" + status_flags &= ~CANPUSH if("Hunter") module = new /obj/item/robot_module/alien/hunter(src) @@ -442,6 +460,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ionpulse = FALSE magpulse = FALSE add_language("Robot Talk", 1) + if("lava" in weather_immunities) // Remove the lava-immunity effect given by a printable upgrade + weather_immunities -= "lava" status_flags |= CANPUSH @@ -916,7 +936,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return 0 /mob/living/silicon/robot/update_icons() - overlays.Cut() if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. if(custom_panel in custom_eye_names) @@ -925,36 +944,24 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( overlays += "eyes-[icon_state]" else overlays -= "eyes" - if(opened) var/panelprefix = "ov" if(custom_sprite) //Custom borgs also have custom panels, heh panelprefix = "[ckey]" - if(custom_panel in custom_panel_names) //For default borgs with different panels panelprefix = custom_panel - if(wiresexposed) overlays += "[panelprefix]-openpanel +w" else if(cell) overlays += "[panelprefix]-openpanel +c" else overlays += "[panelprefix]-openpanel -c" - - var/combat = list("Combat") - if(modtype in combat) - if(base_icon == "") - base_icon = icon_state - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - icon_state = "[base_icon]-roll" - else - icon_state = base_icon - if(module) - for(var/obj/item/borg/combat/shield/S in module.modules) - if(activated(S)) - overlays += "[base_icon]-shield" + borg_icons() update_fire() +/mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it + return + /mob/living/silicon/robot/proc/installed_modules() if(weapon_lock) to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") @@ -1309,6 +1316,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ..() update_module_icon() +/mob/living/silicon/robot/emp_act(severity) + if(emp_protection) + return + ..() + switch(severity) + if(1) + disable_component("comms", 160) + if(2) + disable_component("comms", 60) + /mob/living/silicon/robot/deathsquad base_icon = "nano_bloodhound" icon_state = "nano_bloodhound" @@ -1321,44 +1338,29 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( pdahide = 1 eye_protection = 2 // Immunity to flashes and the visual part of flashbangs ear_protection = 1 // Immunity to the audio part of flashbangs + damage_protection = 10 // Reduce all incoming damage by this number + xeno_disarm_chance = 20 allow_rename = FALSE modtype = "Commando" faction = list("nanotrasen") is_emaggable = FALSE - -/mob/living/silicon/robot/deathsquad/New(loc) - ..() - cell = new /obj/item/stock_parts/cell/hyper(src) + default_cell_type = /obj/item/stock_parts/cell/bluespace /mob/living/silicon/robot/deathsquad/init() laws = new /datum/ai_laws/deathsquad module = new /obj/item/robot_module/deathsquad(src) - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) radio = new /obj/item/radio/borg/deathsquad(src) radio.recalculateChannels() - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) -/mob/living/silicon/robot/combat - base_icon = "droidcombat" - icon_state = "droidcombat" - modtype = "Combat" - designation = "Combat" +/mob/living/silicon/robot/deathsquad/bullet_act(var/obj/item/projectile/P) + if(istype(P) && P.is_reflectable && P.starting) + visible_message("The [P.name] gets reflected by [src]!", "The [P.name] gets reflected by [src]!") + P.reflect_back(src) + return -1 + return ..(P) -/mob/living/silicon/robot/combat/init() - ..() - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - status_flags &= ~CANPUSH - - radio.config(module.channels) - notify_ai(2) /mob/living/silicon/robot/ert designation = "ERT" @@ -1366,11 +1368,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( scrambledcodes = 1 req_one_access = list(ACCESS_CENT_SPECOPS) ionpulse = 1 - force_modules = list("Engineering", "Medical", "Security") static_radio_channels = 1 allow_rename = FALSE weapons_unlock = TRUE + default_cell_type = /obj/item/stock_parts/cell/super + var/eprefix = "Amber" /mob/living/silicon/robot/ert/init() @@ -1379,11 +1382,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( radio.recalculateChannels() aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) -/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) +/mob/living/silicon/robot/ert/New(loc) ..(loc) - cell = new /obj/item/stock_parts/cell/hyper(src) var/rnum = rand(1,1000) - var/borgname = "ERT [rnum]" + var/borgname = "[eprefix] ERT [rnum]" name = borgname custom_name = borgname real_name = name @@ -1392,22 +1394,63 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( mind.original = src mind.assigned_role = SPECIAL_ROLE_ERT mind.special_role = SPECIAL_ROLE_ERT - if(cyborg_unlock) - crisis = 1 if(!(mind in SSticker.minds)) SSticker.minds += mind SSticker.mode.ert += mind -/mob/living/silicon/robot/ert/gamma - crisis = 1 -/mob/living/silicon/robot/emp_act(severity) +/mob/living/silicon/robot/ert/red + eprefix = "Red" + default_cell_type = /obj/item/stock_parts/cell/hyper + +/mob/living/silicon/robot/ert/gamma + default_cell_type = /obj/item/stock_parts/cell/bluespace + force_modules = list("Combat", "Engineering", "Medical") + damage_protection = 5 // Reduce all incoming damage by this number + eprefix = "Gamma" + magpulse = 1 + xeno_disarm_chance = 40 + + +/mob/living/silicon/robot/destroyer + // admin-only borg, the seraph / special ops officer of borgs + base_icon = "droidcombat" + icon_state = "droidcombat" + modtype = "Destroyer" + designation = "Destroyer" + lawupdate = 0 + scrambledcodes = 1 + req_one_access = list(ACCESS_CENT_SPECOPS) + ionpulse = 1 + magpulse = 1 + pdahide = 1 + eye_protection = 2 // Immunity to flashes and the visual part of flashbangs + ear_protection = 1 // Immunity to the audio part of flashbangs + emp_protection = TRUE // Immunity to EMP, due to heavy shielding + damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg. + xeno_disarm_chance = 10 + default_cell_type = /obj/item/stock_parts/cell/bluespace + +/mob/living/silicon/robot/destroyer/init() ..() - switch(severity) - if(1) - disable_component("comms", 160) - if(2) - disable_component("comms", 60) + module = new /obj/item/robot_module/destroyer(src) + module.add_languages(src) + module.add_subsystems_and_actions(src) + status_flags &= ~CANPUSH + if(radio) + qdel(radio) + radio = new /obj/item/radio/borg/ert/specops(src) + radio.recalculateChannels() + +/mob/living/silicon/robot/destroyer/borg_icons() + if(base_icon == "") + base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility)) + icon_state = "[base_icon]-roll" + else + icon_state = base_icon + overlays += "[base_icon]-shield" + /mob/living/silicon/robot/extinguish_light() update_headlamp(1, 150) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index f4290d764d6..b7b81ed40b7 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -76,29 +76,6 @@ if(!LAZYLEN(components)) return - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - var/datum/robot_component/armour/A = get_armour() if(A) A.take_damage(brute, burn, sharp, updating_health) @@ -127,32 +104,15 @@ updatehealth("heal overall damage") /mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) - if(status_flags & GODMODE) return //godmode + if(status_flags & GODMODE) + return + + if(damage_protection) + brute = clamp(brute - damage_protection, 0, brute) + burn = clamp(burn - damage_protection, 0, burn) + var/list/datum/robot_component/parts = get_damageable_components() - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - var/datum/robot_component/armour/A = get_armour() if(A) A.take_damage(brute, burn, sharp) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 9b4f1089f46..8445a399582 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -2,7 +2,7 @@ if(M.a_intent == INTENT_DISARM) if(!lying) M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - if(prob(85)) + if(prob(xeno_disarm_chance)) Stun(7) step(src, get_dir(M,src)) spawn(5) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 92115986b2e..b1abb49d2ef 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -73,24 +73,7 @@ /obj/item/borg var/powerneeded // Percentage of power remaining required to run item -/obj/item/borg/combat/shield - name = "personal shielding" - desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge." - icon = 'icons/obj/decals.dmi' - icon_state = "shock" - powerneeded = 25 - var/shield_level = 0.5 //Percentage of damage absorbed by the shield. - -/obj/item/borg/combat/shield/verb/set_shield_level() - set name = "Set shield level" - set category = "Object" - set src in range(0) - - var/N = input("How much damage should the shield absorb?") in list("5","10","25","50","75","100") - if(N) - shield_level = text2num(N)/100 - -/obj/item/borg/combat/mobility +/obj/item/borg/destroyer/mobility name = "mobility module" desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/decals.dmi' diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 3260775c674..8e8c67693c5 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -118,20 +118,48 @@ return /obj/item/robot_module/standard - name = "standard robot module" + // if station is fine, assist with constructing station goal room, cleaning, and repairing cables chewed by rats + // if medical crisis, assist by providing basic healthcare, retrieving corpses, and monitoring crew lifesigns + // if eng crisis, assist by helping repair hull breaches + // if sec crisis, assist by opening doors for sec and providing backup zipties on patrols + name = "generalist robot module" module_type = "Standard" + subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor, /mob/living/silicon/proc/subsystem_crew_monitor) + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) /obj/item/robot_module/standard/New() ..() - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/wrench/cyborg(src) + // sec + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + // janitorial + modules += new /obj/item/soap/nanotrasen(src) + modules += new /obj/item/lightreplacer/cyborg(src) + // eng modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/extinguisher(src) // for firefighting, and propulsion in space + modules += new /obj/item/weldingtool/largetank/cyborg(src) + // mining + modules += new /obj/item/pickaxe(src) + modules += new /obj/item/t_scanner/adv_mining_scanner(src) + modules += new /obj/item/storage/bag/ore/cyborg(src) + // med modules += new /obj/item/healthanalyzer(src) + modules += new /obj/item/reagent_containers/borghypo/basic(src) + modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way emag = new /obj/item/melee/energy/sword/cyborg(src) - + for(var/G in stacktypes) + var/obj/item/stack/sheet/M = new G(src) + M.amount = stacktypes[G] + modules += M fix_modules() + /obj/item/robot_module/medical name = "medical robot module" module_type = "Medical" @@ -259,6 +287,7 @@ modules += new /obj/item/mop/advanced/cyborg(src) modules += new /obj/item/lightreplacer/cyborg(src) modules += new /obj/item/holosign_creator(src) + modules += new /obj/item/extinguisher/mini(src) emag = new /obj/item/reagent_containers/spray(src) emag.reagents.add_reagent("lube", 250) @@ -483,25 +512,44 @@ fix_modules() -/obj/item/robot_module/combat - name = "combat robot module" +/obj/item/robot_module/destroyer + name = "destroyer robot module" module_type = "Malf" module_actions = list( /datum/action/innate/robot_sight/thermal, ) +/obj/item/robot_module/destroyer/New() + ..() + + modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // See comments on /robot_module/combat below + modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves + modules += new /obj/item/borg/destroyer/mobility(src) + emag = null + fix_modules() + + +/obj/item/robot_module/combat + name = "combat robot module" + module_type = "Malf" + module_actions = list() + /obj/item/robot_module/combat/New() ..() + modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot. + // Borg gets 40 shots of this weapon. Gamma Sec ERT gets 10. + // So, borg has way more burst damage, but also takes way longer to recharge / get back in the fight once depleted. Has to find a borg recharger and sit in it for ages. + // Organic gamma sec ERT carries alternate weapons, including a box of flashbangs, and can load up on a huge number of guns from science. Borg cannot do either. + // Overall, gamma borg has higher skill floor but lower skill ceiling. + modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/gun/energy/gun/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) - modules += new /obj/item/borg/combat/shield(src) - modules += new /obj/item/borg/combat/mobility(src) - modules += new /obj/item/wrench/cyborg(src) - emag = new /obj/item/gun/energy/lasercannon/cyborg(src) - + modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves + emag = null fix_modules() + /obj/item/robot_module/alien/hunter name = "alien hunter module" module_type = "Standard" diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 4b645b02849..1d3e1cade6d 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -9,7 +9,7 @@ /mob/living/silicon/robot/movement_delay() . = ..() . += speed - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility)) . -= 3 . += config.robot_delay diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index f5ac3e5532b..98afb8285d1 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -5,6 +5,7 @@ scrambledcodes = 1 pdahide = 1 faction = list("syndicate") + bubble_icon = "syndibot" designation = "Syndicate Assault" modtype = "Syndicate" req_access = list(ACCESS_SYNDICATE) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 50af43f454d..c923e0912b7 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -2,7 +2,9 @@ gender = NEUTER robot_talk_understand = 1 voice_name = "synthesized voice" + bubble_icon = "machine" has_unlimited_silicon_privilege = 1 + weather_immunities = list("ash") var/syndicate = 0 var/const/MAIN_CHANNEL = "Main Frequency" var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm index 5f00beff1e4..d18d41a40cf 100644 --- a/code/modules/mob/living/silicon/subsystems.dm +++ b/code/modules/mob/living/silicon/subsystems.dm @@ -2,7 +2,7 @@ var/register_alarms = 1 var/datum/nano_module/alarm_monitor/all/alarm_monitor var/datum/nano_module/atmos_control/atmos_control - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/tgui_module/crew_monitor/crew_monitor var/datum/nano_module/law_manager/law_manager var/datum/nano_module/power_monitor/silicon/power_monitor @@ -71,8 +71,7 @@ /mob/living/silicon/proc/subsystem_crew_monitor() set category = "Subsystems" set name = "Crew Monitor" - - crew_monitor.ui_interact(usr, state = GLOB.self_state) + crew_monitor.tgui_interact(usr, state = GLOB.tgui_self_state) /**************** * Law Manager * diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 962b4722f39..cb45fd98e8b 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -18,7 +18,7 @@ speak_emote = list("states") friendly = "boops" - + bubble_icon = "machine" faction = list("neutral", "silicon") var/obj/machinery/bot_core/bot_core = null @@ -63,6 +63,7 @@ var/new_destination // pending new destination (waiting for beacon response) var/destination // destination description tag var/next_destination // the next destination in the patrol route + var/ignorelistcleanuptimer = 1 // This ticks up every automated action, at 300 we clean the ignore list var/robot_arm = /obj/item/robot_parts/r_arm var/blockcount = 0 //number of times retried a blocked path @@ -254,6 +255,15 @@ /mob/living/simple_animal/bot/handle_automated_action() diag_hud_set_botmode() + if(ignorelistcleanuptimer % 300 == 0) // Every 300 actions, clean up the ignore list from old junk + for(var/uid in ignore_list) + var/atom/referredatom = locateUID(uid) + if(!referredatom || QDELETED(referredatom)) + ignore_list -= uid + ignorelistcleanuptimer = 1 + else + ignorelistcleanuptimer++ + if(!on) return @@ -439,14 +449,15 @@ Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1) The proc would return a human next to the bot to be set to the patient var. Pass the desired type path itself, declaring a temporary var beforehand is not required. */ -/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE) +/mob/living/simple_animal/bot/proc/scan(atom/scan_type, atom/old_target, scan_range = DEFAULT_SCAN_RANGE) var/final_result for(var/scan in shuffle(view(scan_range, src))) //Search for something in range! - if(!istype(scan, scan_type)) //Check that the thing we found is the type we want! + var/atom/A = scan + if(!istype(A, scan_type)) //Check that the thing we found is the type we want! continue //If not, keep searching! - if( (scan in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness + if((A.UID() in ignore_list) || (A == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness continue - var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected. + var/scan_result = process_scan(A) //Some bots may require additional processing when a result is selected. if(scan_result) final_result = scan_result else @@ -454,17 +465,16 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r return final_result //When the scan finds a target, run bot specific processing to select it for the next step. Empty by default. -/mob/living/simple_animal/bot/proc/process_scan(scan_target) +/mob/living/simple_animal/bot/proc/process_scan(atom/scan_target) return scan_target -/mob/living/simple_animal/bot/proc/add_to_ignore(subject) +/mob/living/simple_animal/bot/proc/add_to_ignore(atom/A) if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot. - ignore_list |= subject - else if(ignore_list.len >= subject) //If the list is full, insert newest, delete oldest. - ignore_list -= ignore_list[1] - ignore_list |= subject - + ignore_list |= A.UID() + else //If the list is full, insert newest, delete oldest. + ignore_list.Cut(1, 2) + ignore_list |= A.UID() /* Movement proc for stepping a bot through a path generated through A-star. Pass a positive integer as an argument to override a bot's default speed. diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index f9747238138..4cb4a532d6b 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -50,7 +50,7 @@ /mob/living/simple_animal/bot/cleanbot/bot_reset() ..() - ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable. + ignore_list.Cut() //Allows the bot to clean targets it previously ignored due to being unreachable. target = null oldloc = null @@ -106,7 +106,7 @@ audible_message("[src] makes an excited beeping booping sound!") if(!target) //Search for cleanables it can see. - target = scan(/obj/effect/decal/cleanable/) + target = scan(/obj/effect/decal/cleanable) if(!target && auto_patrol) //Search for cleanables it can see. if(mode == BOT_IDLE || mode == BOT_START_PATROL) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 5b0ea28a72a..df96cae3b6b 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -56,7 +56,7 @@ ..() target = null oldloc = null - ignore_list = list() + ignore_list.Cut() nagged = 0 anchored = FALSE update_icon() @@ -270,7 +270,7 @@ return 1 //Floorbots, having several functions, need sort out special conditions here. -/mob/living/simple_animal/bot/floorbot/process_scan(scan_target) +/mob/living/simple_animal/bot/floorbot/process_scan(atom/scan_target) var/result var/turf/simulated/floor/F switch(process_type) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index d9097a82d2e..62c133fffd7 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -33,7 +33,7 @@ /obj/machinery/bot_core/honkbot req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) -/mob/living/simple_animal/bot/honkbot/Initialize() +/mob/living/simple_animal/bot/honkbot/Initialize(mapload) . = ..() update_icon() auto_patrol = TRUE diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 728b050dd54..f1278918898 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -323,13 +323,13 @@ /mob/living/simple_animal/bot/mulebot/proc/buzz(type) switch(type) if(SIGH) - audible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + audible_message("[src] makes a sighing buzz.") playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) if(ANNOYED) - audible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") + audible_message("[src] makes an annoyed buzzing sound.") playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) if(DELIGHT) - audible_message("[src] makes a delighted ping!", "You hear a ping.") + audible_message("[src] makes a delighted ping!") playsound(loc, 'sound/machines/ping.ogg', 50, 0) @@ -601,7 +601,7 @@ /mob/living/simple_animal/bot/mulebot/proc/at_target() if(!reached_target) radio_channel = "Supply" //Supply channel - audible_message("[src] makes a chiming sound!", "You hear a chime.") + audible_message("[src] makes a chiming sound!") playsound(loc, 'sound/machines/chime.ogg', 50, 0) reached_target = 1 diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index 10460d11e65..8d0ad5d0e2b 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -103,9 +103,9 @@ name = "Space Wizard Corpse" outfit = /datum/outfit/wizardcorpse -/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() +/obj/effect/mob_spawn/human/corpse/clownoff/Initialize(mapload) mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" - ..() + . = ..() /datum/outfit/wizardcorpse name = "Space Wizard Corpse" diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index 65b0c492fe6..7c7b4dd6a2f 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -20,6 +20,7 @@ melee_damage_upper = 25 attacktext = "slashes" speak_emote = list("hisses") + bubble_icon = "alien" a_intent = INTENT_HARM attack_sound = 'sound/weapons/bladeslice.ogg' atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -127,6 +128,7 @@ icon_state = "queen_s" icon_living = "queen_s" icon_dead = "queen_dead" + bubble_icon = "alienroyal" move_to_delay = 4 maxHealth = 400 health = 400 diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index ced4a61d8c5..08eb7128055 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -25,7 +25,7 @@ var/list/human_overlays = list() /mob/living/simple_animal/hostile/headcrab/Life(seconds, times_fired) - if(..()) + if(..() && !stat) if(!is_zombie && isturf(src.loc)) for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD)) @@ -35,7 +35,7 @@ if(cycles >= 4) for(var/mob/living/simple_animal/K in oview(src, 1)) //Only for corpse right next to/on same tile if(K.stat == DEAD || (!K.check_death_method() && K.health <= HEALTH_THRESHOLD_DEAD)) - visible_message("[src] consumes [target] whole!") + visible_message("[src] consumes [K] whole!") if(health < maxHealth) health += 10 qdel(K) diff --git a/code/modules/mob/living/simple_animal/hostile/headslug.dm b/code/modules/mob/living/simple_animal/hostile/headslug.dm index 801514a3256..6800598fc7f 100644 --- a/code/modules/mob/living/simple_animal/hostile/headslug.dm +++ b/code/modules/mob/living/simple_animal/hostile/headslug.dm @@ -72,7 +72,7 @@ /obj/item/organ/internal/body_egg/changeling_egg/proc/Pop() var/mob/living/carbon/human/monkey/M = new(owner) - owner.stomach_contents += M + LAZYADD(owner.stomach_contents, M) for(var/obj/item/organ/internal/I in src) I.insert(M, 1) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index d2a97949c8e..c6f3ac845e3 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -25,6 +25,7 @@ gold_core_spawnable = HOSTILE_SPAWN loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) deathmessage = "blows apart!" + bubble_icon = "machine" del_on_death = 1 /mob/living/simple_animal/hostile/hivebot/range diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 6dca834169b..50531f976e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -534,7 +534,7 @@ obj/effect/temp_visual/fireball duration = 9 pixel_z = 270 -/obj/effect/temp_visual/fireball/Initialize() +/obj/effect/temp_visual/fireball/Initialize(mapload) . = ..() animate(src, pixel_z = 0, time = duration) diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 848a285abf4..fbf65ccfa9b 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -48,8 +48,8 @@ var/attempt_open = 0 // Pickup loot -/mob/living/simple_animal/hostile/mimic/crate/Initialize() - ..() +/mob/living/simple_animal/hostile/mimic/crate/Initialize(mapload) + . = ..() for(var/obj/item/I in loc) I.loc = src diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index e8ce4027b4e..89164167005 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -31,7 +31,7 @@ deathmessage = "wails as its form turns into a pulpy mush." death_sound = 'sound/voice/hiss6.ogg' -/mob/living/simple_animal/hostile/netherworld/migo/Initialize() +/mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload) . = ..() migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers @@ -82,14 +82,14 @@ /obj/structure/spawner/nether/examine(mob/user) . = ..() - if(isskeleton(user) || iszombie(user)) + if(isskeleton(user)) . += "A direct link to another dimension full of creatures very happy to see you. You can see your house from here!" else . += "A direct link to another dimension full of creatures not very happy to see you. Entering the link would be a very bad idea." /obj/structure/spawner/nether/attack_hand(mob/user) . = ..() - if(isskeleton(user) || iszombie(user)) + if(isskeleton(user)) to_chat(user, "You don't feel like going home yet...") else user.visible_message("[user] is violently pulled into the link!", \ diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 6f40dfe2a3f..decfc7146cf 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -260,7 +260,7 @@ alert_on_shield_breach = TRUE /mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) - ..() + . = ..() if(prob(50)) // 50% chance of switching to extremely dangerous ranged variant melee_damage_lower = 10 @@ -363,6 +363,7 @@ minbodytemp = 0 mob_size = MOB_SIZE_TINY flying = 1 + bubble_icon = "syndibot" gold_core_spawnable = HOSTILE_SPAWN del_on_death = 1 deathmessage = "is smashed into pieces!" diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 8c7d14b23f4..15f483bef76 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -17,6 +17,7 @@ response_harm = "stomps on" emote_see = list("jiggles", "bounces in place") speak_emote = list("blorbles") + bubble_icon = "slime" atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 8bdb0d89332..91bf5f0ab9c 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,5 +1,6 @@ /mob/Logout() SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs + SStgui.on_logout(src) // Cleanup any TGUIs the user has open unset_machine() GLOB.player_list -= src log_access_out(src) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d7f05bd943d..c98ca77a594 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -20,10 +20,10 @@ AA.viewers -= src viewing_alternate_appearances = null logs.Cut() - ..() - return QDEL_HINT_HARDDEL + LAssailant = null + return ..() -/mob/Initialize() +/mob/Initialize(mapload) GLOB.mob_list += src if(stat == DEAD) GLOB.dead_mob_list += src @@ -31,7 +31,7 @@ GLOB.alive_mob_list += src set_focus(src) prepare_huds() - ..() + . = ..() /atom/proc/prepare_huds() hud_list = list() @@ -127,14 +127,12 @@ // self_message (optional) is what the src mob hears. // deaf_message (optional) is what deaf people will see. // hearing_distance (optional) is the range, how many tiles away the message can be heard. -/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message) +/mob/audible_message(message, deaf_message, hearing_distance) var/range = 7 if(hearing_distance) range = hearing_distance var/msg = message for(var/mob/M in get_mobs_in_view(range, src)) - if(self_message && M == src) - msg = self_message M.show_message(msg, 2, deaf_message, 1) // based on say code @@ -156,12 +154,12 @@ // message is the message output to anyone who can hear. // deaf_message (optional) is what deaf people will see. // hearing_distance (optional) is the range, how many tiles away the message can be heard. -/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance) +/atom/proc/audible_message(message, deaf_message, hearing_distance) var/range = 7 if(hearing_distance) range = hearing_distance for(var/mob/M in get_mobs_in_view(range, src)) - M.show_message( message, 2, deaf_message, 1) + M.show_message(message, 2, deaf_message, 1) /mob/proc/findname(msg) for(var/mob/M in GLOB.mob_list) @@ -754,7 +752,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ if(client.holder && (client.holder.rights & R_ADMIN)) is_admin = 1 - else if(stat != DEAD || istype(src, /mob/new_player)) + else if(stat != DEAD || isnewplayer(src)) to_chat(usr, "You must be observing to use this!") return @@ -1390,3 +1388,29 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ ///Force set the mob nutrition /mob/proc/set_nutrition(change) nutrition = max(0, change) + +/mob/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE) + . = ..() + if(bloody_hands && clean_hands) + bloody_hands = 0 + update_inv_gloves() + if(l_hand) + if(l_hand.clean_blood()) + update_inv_l_hand() + if(r_hand) + if(r_hand.clean_blood()) + update_inv_r_hand() + if(back) + if(back.clean_blood()) + update_inv_back() + if(wear_mask && clean_mask) + if(wear_mask.clean_blood()) + update_inv_wear_mask() + if(clean_feet) + feet_blood_color = null + qdel(feet_blood_DNA) + bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + blood_state = BLOOD_STATE_NOT_BLOODY + update_inv_shoes() + update_icons() //apply the now updated overlays to the mob + diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index dc77610b97e..1477be47d89 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -31,8 +31,8 @@ var/use_me = 1 //Allows all mobs to use the me verb by default, will have to manually specify they cannot var/damageoverlaytemp = 0 var/computer_id = null - var/lastattacker = null - var/lastattacked = null + var/lastattacker = null // real name of the person doing the attacking + var/lastattackerckey = null // their ckey var/list/attack_log_old = list( ) var/list/debug_log = null @@ -50,7 +50,6 @@ var/med_record = "" var/sec_record = "" var/gen_record = "" - var/bhunger = 0 //Carbon var/lying = 0 var/lying_prev = 0 var/lastpuke = 0 @@ -61,8 +60,7 @@ var/emote_type = 1 // Define emote default type, 1 for seen emotes, 2 for heard emotes var/name_archive //For admin things like possession - var/timeofdeath = 0.0//Living - + var/timeofdeath = 0 //Living var/bodytemperature = 310.055 //98.7 F var/flying = 0 @@ -71,22 +69,22 @@ var/hunger_drain = HUNGER_FACTOR // how quickly the mob gets hungry; largely utilized by species. var/overeatduration = 0 // How long this guy is overeating //Carbon - var/intent = null//Living + var/intent = null //Living var/shakecamera = 0 - var/a_intent = INTENT_HELP//Living - var/m_intent = MOVE_INTENT_RUN//Living + var/a_intent = INTENT_HELP //Living + var/m_intent = MOVE_INTENT_RUN //Living var/lastKnownIP = null /// movable atoms buckled to this mob - var/atom/movable/buckled = null//Living + var/atom/movable/buckled = null //Living /// movable atom we are buckled to var/atom/movable/buckling - var/obj/item/l_hand = null//Living - var/obj/item/r_hand = null//Living - var/obj/item/back = null//Human/Monkey - var/obj/item/tank/internal = null//Human/Monkey - var/obj/item/storage/s_active = null//Carbon - var/obj/item/clothing/mask/wear_mask = null//Carbon + var/obj/item/l_hand = null //Living + var/obj/item/r_hand = null //Living + var/obj/item/back = null //Human + var/obj/item/tank/internal = null //Human + var/obj/item/storage/s_active = null //Carbon + var/obj/item/clothing/mask/wear_mask = null //Carbon var/datum/hud/hud_used = null @@ -95,7 +93,6 @@ var/research_scanner = 0 //For research scanner equipped mobs. Enable to show research data when examining. var/list/grabbed_by = list() - var/list/requests = list() var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE var/list/mapobjs = list() @@ -103,9 +100,9 @@ var/emote_cd = 0 // Used to supress emote spamming. 1 if on CD, 2 if disabled by admin (manually set), else 0 - var/job = null//Living + var/job = null //Living - var/datum/dna/dna = null//Carbon + var/datum/dna/dna = null //Carbon var/radiation = 0 //Carbon var/list/mutations = list() //Carbon -- Doohl @@ -179,8 +176,6 @@ var/turf/listed_turf = null //the current turf being examined in the stat panel var/list/shouldnt_see = list() //list of objects that this mob shouldn't see in the stat panel. this silliness is needed because of AI alt+click and cult blood runes - var/kills = 0 - var/stance_damage = 0 //Whether this mob's ability to stand has been affected var/list/active_genes = list() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 640ffb09d4f..37e116b4376 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -408,7 +408,7 @@ add_attack_logs(attacker, affecting, "Devoured") affecting.forceMove(user) - attacker.stomach_contents.Add(affecting) + LAZYADD(attacker.stomach_contents, affecting) qdel(src) /obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab @@ -434,7 +434,7 @@ if(affecting) if(!affecting.buckled) affecting.pixel_x = 0 - affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing + affecting.pixel_y = 0 //used to be an animate, not quick enough for qdel'ing affecting.layer = initial(affecting.layer) affecting.grabbed_by -= src affecting = null diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index df2a39c0930..5fb5bcb06c8 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -12,14 +12,6 @@ return 1 return 0 -/mob/proc/isSynthetic() - return 0 - -/mob/living/carbon/human/isSynthetic() - if(ismachine(src)) - return TRUE - return FALSE - /mob/proc/get_screen_colour() /mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. @@ -467,7 +459,7 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM name = realname for(var/mob/M in GLOB.player_list) - if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD)) + if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD)) var/follow var/lname if(subject) diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index c3ec68c9793..1e704ea3449 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -4,7 +4,7 @@ //Note that this proc does NOT do MMI related stuff! /mob/proc/change_mob_type(var/new_type = null, var/turf/location = null, var/new_name = null as text, var/delete_old_mob = 0 as num, var/forcekey = 0) - if(istype(src,/mob/new_player)) + if(isnewplayer(src)) to_chat(usr, "cannot convert players who have not entered yet.") return diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index af3d439df59..f53daba6626 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -12,8 +12,13 @@ stat = 2 canmove = 0 -/mob/new_player/New() +/mob/new_player/Initialize(mapload) + SHOULD_CALL_PARENT(FALSE) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE GLOB.mob_list += src + return INITIALIZE_HINT_NORMAL /mob/new_player/verb/new_player_panel() set src = usr @@ -205,6 +210,7 @@ if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! observer.key = key + QDEL_NULL(mind) GLOB.respawnable_list += observer qdel(src) return 1 diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm index 476861861a3..d4834eccc80 100644 --- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm @@ -48,7 +48,7 @@ var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations // Restrict some styles to specific species - var/list/species_allowed = list("Human", "Slime People", "Infectious Zombie", "High-Functioning Zombie") + var/list/species_allowed = list("Human", "Slime People") var/list/sprite_sheets = list() //For accessories common across species but need to use 'fitted' sprites (like underwear). e.g. list("Vox" = 'icons/mob/species/vox/iconfile.dmi') var/list/models_allowed = list() //Specifies which, if any, hairstyles or markings can be accessed by which prosthetics. Should equal the manufacturing company name in robolimbs.dm. var/list/heads_allowed = null //Specifies which, if any, alt heads a head marking, hairstyle or facial hair style is compatible with. diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 81d22e40a0b..0417e28f546 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -114,7 +114,7 @@ return get_turf(src) -/mob/proc/say_test(var/text) +/proc/say_test(text) var/ending = copytext(text, length(text)) if(ending == "?") return "1" diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 4dfbddc2d84..a2ca226147c 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -5,31 +5,32 @@ mob/var/typing mob/var/last_typed mob/var/last_typed_time -GLOBAL_DATUM(typing_indicator, /image) +GLOBAL_LIST_EMPTY(typing_indicator) -/mob/proc/set_typing_indicator(var/state) +/mob/proc/set_typing_indicator(state) - if(!GLOB.typing_indicator) - GLOB.typing_indicator = image('icons/mob/talk.dmi', null, "typing", MOB_LAYER + 1) - GLOB.typing_indicator.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + if(!GLOB.typing_indicator[bubble_icon]) + GLOB.typing_indicator[bubble_icon] = image('icons/mob/talk.dmi', null, "[bubble_icon]typing", FLY_LAYER) + var/image/I = GLOB.typing_indicator[bubble_icon] + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA if(ishuman(src)) var/mob/living/carbon/human/H = src if((MUTE in H.mutations) || H.silent) - overlays -= GLOB.typing_indicator + overlays -= GLOB.typing_indicator[bubble_icon] return if(client) if((client.prefs.toggles & SHOW_TYPING) || stat != CONSCIOUS || is_muzzled()) - overlays -= GLOB.typing_indicator + overlays -= GLOB.typing_indicator[bubble_icon] else if(state) if(!typing) - overlays += GLOB.typing_indicator + overlays += GLOB.typing_indicator[bubble_icon] typing = 1 else if(typing) - overlays -= GLOB.typing_indicator + overlays -= GLOB.typing_indicator[bubble_icon] typing = 0 return state diff --git a/code/modules/nano/modules/crew_monitor.dm b/code/modules/nano/modules/crew_monitor.dm deleted file mode 100644 index 4ed7fc03de1..00000000000 --- a/code/modules/nano/modules/crew_monitor.dm +++ /dev/null @@ -1,41 +0,0 @@ -/datum/nano_module/crew_monitor - name = "Crew monitor" - -/datum/nano_module/crew_monitor/Topic(href, href_list) - if(..()) - return 1 - var/turf/T = get_turf(nano_host()) - if(!T || !is_level_reachable(T.z)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 0 - if(href_list["track"]) - if(isAI(usr)) - var/mob/living/silicon/ai/AI = usr - var/mob/living/carbon/human/H = locate(href_list["track"]) in GLOB.mob_list - if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) - AI.ai_actual_track(H) - return 1 - -/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "crew_monitor_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") - - ui.open() - - // should make the UI auto-update; doesn't seem to? - ui.set_auto_update(1) - -/datum/nano_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/turf/T = get_turf(nano_host()) - - data["isAI"] = isAI(user) - data["crewmembers"] = GLOB.crew_repository.health_data(T) - - return data diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 5ca53396253..d5cbbdc0ad6 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -340,7 +340,7 @@ return 0 else playsound(loc, 'sound/machines/ping.ogg', 50, 0) - atom_say("Attention: Posterior Placed on Printing Plaque!") + atom_say("Attention: Posterior Placed on Printing Plaque!") return 1 /obj/machinery/photocopier/emag_act(user as mob) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 5934dfa4f7d..0cb571d576c 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -562,13 +562,13 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor talk_into(M, msg) for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) + T.atom_say(msg) /obj/item/videocam/hear_message(mob/M as mob, msg) if(camera && on) for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) + T.atom_say(msg) ///hauntings, like hallucinations but more spooky diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index c48ebdd4e9e..c72aa6d0d41 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -47,7 +47,7 @@ SSmob_hunt.connected_clients += src connected = 1 if(pda) - pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2) + pda.atom_say("Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!") return 1 /datum/data/pda/app/mob_hunter_game/proc/get_player() @@ -67,7 +67,7 @@ connected = 0 //show a disconnect message if we were disconnected involuntarily (reason argument provided) if(pda && reason) - pda.audible_message("[bicon(pda)] Disconnected from server. Reason: [reason].", null, 2) + pda.atom_say("Disconnected from server. Reason: [reason].") /datum/data/pda/app/mob_hunter_game/program_process() if(!SSmob_hunt || !connected) diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index e0eb509aa9d..bcadfc62e08 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -12,7 +12,7 @@ select_name = "kill" /obj/item/ammo_casing/energy/laser/cyborg //to balance cyborg energy cost seperately - e_cost = 250 + e_cost = 250 /obj/item/ammo_casing/energy/lasergun projectile_type = /obj/item/projectile/beam/laser @@ -75,6 +75,10 @@ e_cost = 125 select_name = "precise" +/obj/item/ammo_casing/energy/immolator/strong/cyborg + // Used by gamma ERT borgs + e_cost = 1000 // 5x that of the standard laser, for 2.25x the damage (if 1/1 shots hit) plus ignite. Not energy-efficient, but can be used for sniping. + /obj/item/ammo_casing/energy/immolator/scatter projectile_type = /obj/item/projectile/beam/immolator/weak e_cost = 125 @@ -82,6 +86,10 @@ variance = 25 select_name = "scatter" +/obj/item/ammo_casing/energy/immolator/scatter/cyborg + // Used by gamma ERT borgs + e_cost = 1000 // 5x that of the standard laser, for 7.5x the damage (if 6/6 shots hit) plus ignite. Efficient only if you hit with at least 4/6 of the shots. + /obj/item/ammo_casing/energy/electrode projectile_type = /obj/item/projectile/energy/electrode select_name = "stun" diff --git a/code/modules/projectiles/guns/dartgun.dm b/code/modules/projectiles/guns/dartgun.dm index 0a50b844f4a..8c46e542b4a 100644 --- a/code/modules/projectiles/guns/dartgun.dm +++ b/code/modules/projectiles/guns/dartgun.dm @@ -175,11 +175,6 @@ for(var/datum/reagent/A in D.reagents.reagent_list) R += A.id + " (" R += num2text(A.volume) + ")," - if(istype(M, /mob)) - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user add_attack_logs(user, M, "Shot with dartgun containing [R]") diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index ecf5eeed77e..19316cb9540 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -253,7 +253,7 @@ icon = 'icons/obj/objects.dmi' icon_state = "modkit" origin_tech = "programming=2;materials=2;magnets=4" - require_module = 1 + require_module = TRUE module_type = /obj/item/robot_module/miner usesound = 'sound/items/screwdriver.ogg' var/denied_type = null diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 9d4cfaee3d4..3e9c882458e 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -139,6 +139,12 @@ var/append = shot.select_name overlays += image(icon = icon, icon_state = "multilensimmolator-[append]") + +/obj/item/gun/energy/immolator/multi/cyborg + name = "cyborg immolator cannon" + ammo_type = list(/obj/item/ammo_casing/energy/immolator/scatter/cyborg, /obj/item/ammo_casing/energy/immolator/strong/cyborg) // scatter is default, because it is more useful + + ////////Laser Tag//////////////////// /obj/item/gun/energy/laser/tag diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index dc70eda9cec..4505d984f84 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -28,7 +28,7 @@ /obj/item/projectile/magic/death/on_hit(mob/living/carbon/C) . = ..() if(isliving(C)) - if(ismachine(C)) //speshul snowfleks deserv speshul treetment + if(ismachineperson(C)) //speshul snowfleks deserv speshul treetment C.adjustFireLoss(6969) //remember - slimes love fire else C.death() diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index dec256ac455..c6ff39aaead 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -226,37 +226,6 @@ if(B && islist(B.data) && !data) data = B.data.Copy() -/datum/reagent/romerol - name = "romerol" - // the REAL zombie powder - id = "romerol" - description = "Romerol is a highly experimental bioterror agent \ - which causes dormant nodules to be etched into the grey matter of \ - the subject. These nodules only become active upon death of the \ - host, upon which, the secondary structures activate and take control \ - of the host body." - color = "#123524" // RGB (18, 53, 36) - metabolization_rate = INFINITY - can_synth = FALSE - taste_description = "CAAAARL" - -/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method = REAGENT_TOUCH, volume) - if(!istype(H)) - return - // Silently add the zombie infection organ to be activated upon death - if(!H.get_organ_slot("zombie_infection")) - var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new() - ZI.insert(H) - ..() - -/datum/reagent/romerol/on_mob_life(mob/living/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(!H.get_organ_slot("zombie_infection")) - var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new() - ZI.insert(H) - return ..() - /datum/reagent/uranium name ="Uranium" id = "uranium" diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm index f975cac7293..186315ecc56 100644 --- a/code/modules/reagents/chemistry/reagents/water.dm +++ b/code/modules/reagents/chemistry/reagents/water.dm @@ -83,37 +83,7 @@ M.adjustToxLoss(rand(5, 10)) /datum/reagent/space_cleaner/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.lip_style) - H.lip_style = null - H.update_body() - if(C.r_hand) - C.r_hand.clean_blood() - if(C.l_hand) - C.l_hand.clean_blood() - if(C.wear_mask) - if(C.wear_mask.clean_blood()) - C.update_inv_wear_mask() - if(ishuman(M)) - var/mob/living/carbon/human/H = C - if(H.head) - if(H.head.clean_blood()) - H.update_inv_head() - if(H.wear_suit) - if(H.wear_suit.clean_blood()) - H.update_inv_wear_suit() - else if(H.w_uniform) - if(H.w_uniform.clean_blood()) - H.update_inv_w_uniform() - if(H.shoes) - if(H.shoes.clean_blood()) - H.update_inv_shoes() - M.clean_blood() - ..() - + M.clean_blood() /datum/reagent/blood data = list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=null,"blood_colour"="#A10808","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null, "dna" = null) diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 1fa4930b0f6..8338253fc6c 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -47,7 +47,7 @@ /obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg charge_tick++ - if(charge_tick < recharge_time) + if(charge_tick < recharge_time) return FALSE charge_tick = 0 @@ -97,9 +97,7 @@ var/contained = injected.name var/trans = R.trans_to(M, amount_per_transfer_from_this) add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null) - M.LAssailant = user to_chat(user, "[trans] units injected. [R.total_volume] units remaining.") - return /obj/item/reagent_containers/borghypo/attack_self(mob/user) playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode @@ -126,4 +124,9 @@ if(empty) . += "It is currently empty. Allow some time for the internal syntheszier to produce more." +/obj/item/reagent_containers/borghypo/basic + name = "Basic Medical Hypospray" + desc = "A very basic medical hypospray, capable of providing simple medical treatment in emergencies." + reagent_ids = list("salglu_solution", "epinephrine") + #undef BORGHYPO_REFILL_VALUE diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 61552eae789..6c836988e8b 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -12,11 +12,6 @@ container_type = OPENCONTAINER volume = 30 -/obj/item/reagent_containers/glass/bottle/romerol - name = "romerol bottle" - desc = "A small bottle of Romerol. The REAL zombie powder." - list_reagents = list("romerol" = 30) - /obj/item/reagent_containers/glass/bottle/on_reagent_change() update_icon() diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 6f32411a261..1e42899111b 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -43,10 +43,6 @@ M.visible_message("[user] splashes the contents of [src] onto [M]!", \ "[user] splashes the contents of [src] onto [M]!") add_attack_logs(user, M, "Splashed with [name] containing [contained]", !!M.ckey ? null : ATKLOG_ALL) - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user reagents.reaction(M, REAGENT_TOUCH) reagents.clear_reagents() diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 582e157004c..17112901b29 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -20,7 +20,7 @@ if(M.eat(src, user)) user.drop_item() forceMove(M) - M.processing_patches += src + LAZYADD(M.processing_patches, src) return TRUE return FALSE diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 2aec9412def..13d88100042 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -207,7 +207,10 @@ else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) msg = "[user.name] stuffs [target.name] into the [src]!" to_chat(user, "You stuff [target.name] into the [src]!") - + if(!iscarbon(user)) + target.LAssailant = null + else + target.LAssailant = user add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) else return @@ -237,10 +240,10 @@ // ai as human but can't flush /obj/machinery/disposal/attack_ai(mob/user as mob) src.add_hiddenprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/disposal/attack_ghost(mob/user as mob) - ui_interact(user) + tgui_interact(user) // human interact with machine /obj/machinery/disposal/attack_hand(mob/user as mob) @@ -256,76 +259,68 @@ // Clumsy folks can only flush it. if(user.IsAdvancedToolUser()) - ui_interact(user) + tgui_interact(user) else flush = !flush update() return -/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/disposal/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250) + ui = new(user, src, ui_key, "DisposalBin", name, 300, 250, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/pressure = clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) - var/pressure_round = round(pressure,1) +/obj/machinery/disposal/tgui_data(mob/user) + var/list/data = list() data["isAI"] = isAI(user) data["flushing"] = flush data["mode"] = mode - if(mode <= 0) - data["pumpstatus"] = "N/A" - else if(mode == 1) - data["pumpstatus"] = "Pressurizing" - else if(mode == 2) - data["pumpstatus"] = "Ready" - else - data["pumpstatus"] = "Idle" - data["pressure"] = pressure_round + data["pressure"] = round(clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100),1) return data -/obj/machinery/disposal/Topic(href, href_list) +/obj/machinery/disposal/tgui_act(action, params) + if(..()) + return if(usr.loc == src) to_chat(usr, "You cannot reach the controls from inside.") return - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + if(mode==-1 && action != "eject") // If the mode is -1, only allow ejection to_chat(usr, "The disposal units power is disabled.") return - if(..()) - return - if(stat & BROKEN) return src.add_fingerprint(usr) - if(usr.stat || usr.restrained() || src.flushing) + if(src.flushing) return if(istype(src.loc, /turf)) - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 + if(action == "pumpOn") + mode = 1 + update() + if(action == "pumpOff") + mode = 0 update() - if(!isAI(usr)) - if(href_list["handle"]) - flush = text2num(href_list["handle"]) + if(!issilicon(usr)) + if(action == "engageHandle") + flush = 1 + update() + if(action == "disengageHandle") + flush = 0 update() - if(href_list["eject"]) + if(action == "eject") eject() - return + + return TRUE // eject the contents of the disposal unit /obj/machinery/disposal/proc/eject() diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 5b5dba286fd..76eabe46761 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1081,8 +1081,17 @@ construction_time = 120 category = list("Cyborg Upgrade Modules") +/datum/design/borg_upgrade_lavaproof + name = "Cyborg Upgrade (Lavaproof Chassis)" + id = "borg_upgrade_lavaproof" + build_type = MECHFAB + build_path = /obj/item/borg/upgrade/lavaproof + materials = list(MAT_METAL = 10000, MAT_PLASMA = 4000, MAT_TITANIUM = 5000) + construction_time = 120 + category = list("Cyborg Upgrade Modules") + /datum/design/borg_syndicate_module - name = "Cyborg Upgrade (Illegal Modules)" + name = "Cyborg Upgrade (Safety Override)" id = "borg_syndicate_module" build_type = MECHFAB req_tech = list("combat" = 4, "syndicate" = 2) diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index df3a7945e92..91306d910f1 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -103,12 +103,12 @@ GLOBAL_LIST_EMPTY(message_servers) if(2) if(!Console.silent) playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) + Console.atom_say("PRIORITY Alert in [sender]") Console.message_log += "High Priority message from [sender]
[authmsg]" else if(!Console.silent) playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) + Console.atom_say("Message from [sender]") Console.message_log += "Message from [sender]
[authmsg]" Console.set_light(2) diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm index 6155c1de6b9..a25be3814a7 100644 --- a/code/modules/response_team/ert.dm +++ b/code/modules/response_team/ert.dm @@ -135,8 +135,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) /client/proc/create_response_team(new_gender, role, turf/spawn_location) if(role == "Cyborg") - var/cyborg_unlock = GLOB.active_team.getCyborgUnlock() - var/mob/living/silicon/robot/ert/R = new /mob/living/silicon/robot/ert(spawn_location, cyborg_unlock) + var/mob/living/silicon/robot/ert/R = new GLOB.active_team.borg_path(spawn_location) return R var/mob/living/carbon/human/M = new(null) @@ -208,7 +207,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) var/security_outfit var/janitor_outfit var/paranormal_outfit - var/cyborg_unlock = 0 + var/borg_path = /mob/living/silicon/robot/ert /datum/response_team/proc/setSlots(com=1, sec=3, med=3, eng=3, jan=0, par=0, cyb=0) slots["Commander"] = com @@ -223,9 +222,6 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) slots[role]-- count++ -/datum/response_team/proc/getCyborgUnlock() - return cyborg_unlock - /datum/response_team/proc/get_slot_list() RETURN_TYPE(/list) var/list/slots_available = list() @@ -285,6 +281,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) command_outfit = /datum/outfit/job/centcom/response_team/commander/red janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/red paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/red + borg_path = /mob/living/silicon/robot/ert/red /datum/response_team/red/announce_team() GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code RED Emergency Response Team. Standby.", "ERT En-Route") @@ -298,7 +295,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) command_outfit = /datum/outfit/job/centcom/response_team/commander/gamma janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/gamma paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/gamma - cyborg_unlock = 1 + borg_path = /mob/living/silicon/robot/ert/gamma /datum/response_team/gamma/announce_team() GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code GAMMA elite Emergency Response Team. Standby.", "ERT En-Route") diff --git a/code/modules/ruins/ruin_areas.dm b/code/modules/ruins/ruin_areas.dm index 754f8d44f29..cf3b08aa0f2 100644 --- a/code/modules/ruins/ruin_areas.dm +++ b/code/modules/ruins/ruin_areas.dm @@ -5,8 +5,8 @@ icon_state = "away" has_gravity = TRUE there_can_be_many = TRUE - ambientsounds = list('sound/ambience/ambimine.ogg') dynamic_lighting = DYNAMIC_LIGHTING_FORCED + ambientsounds = RUINS_SOUNDS /area/ruin/unpowered always_unpowered = FALSE diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm index 0f07107a00b..00c575e53b0 100644 --- a/code/modules/security_levels/security levels.dm +++ b/code/modules/security_levels/security levels.dm @@ -31,15 +31,6 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur // Mark down this time to prevent shuttle cheese SSshuttle.emergency_sec_level_time = world.time - // Reset gamma borgs if the new security level is lower than Gamma. - if(level < SEC_LEVEL_GAMMA) - for(var/M in GLOB.silicon_mob_list) - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(istype(R.module, /obj/item/robot_module/combat) && !R.crisis) - R.reset_module() - to_chat(R, "Crisis mode deactivated. The combat module is no longer available and your module has been reset.") - switch(level) if(SEC_LEVEL_GREEN) GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.") diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index f82598b1e14..de08d5cdf82 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -178,8 +178,6 @@ continue if(player.stat == DEAD) // Corpses continue - if(iszombie(player)) // Walking corpses - continue if(issilicon(player)) //Borgs are technically dead anyways continue if(isanimal(player)) //Poly does not own the shuttle diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 2e3c89017ef..56e7f8f96ba 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -973,7 +973,7 @@ var/obj/I = pick(true_contents) if(user.put_in_any_hand_if_possible(I)) src.contents -= I - to_chat(user, "You find a [I] [pick("under the seat", "under the console", "in the mainenance access")]!") + to_chat(user, "You find a [I] [pick("under the seat", "under the console", "in the maintenance access")]!") else to_chat(user, "You think you saw something shiny, but you can't reach it!") else diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index db1ad04aef3..c43c3f373d5 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -32,7 +32,7 @@ if(ishuman(target)) var/mob/living/carbon/human/H = target var/obj/item/organ/external/affected = H.get_organ(user.zone_selected) - if(ismachine(target)) + if(ismachineperson(target)) // RIP bi-centennial man return 0 if(!affected) diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index e01c75dcbd0..b0403d8ae49 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -54,7 +54,7 @@ var/obj/item/organ/external/BP = X var/brutedamage = BP.brute_dam - if(BP.is_robotic() && !isSynthetic()) + if(BP.is_robotic() && !ismachineperson(src)) continue //We want an accurate reading of .len diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm index c2df537e771..2a3b94ca249 100644 --- a/code/modules/surgery/organs/organ_icon.dm +++ b/code/modules/surgery/organs/organ_icon.dm @@ -138,7 +138,7 @@ GLOBAL_LIST_EMPTY(limb_icon_cache) add_overlay(facial_s) if(h_style) - if(!owner.isSynthetic() || (owner.isSynthetic() && ((owner.head && (owner.head.flags & BLOCKHEADHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHEADHAIR))))) + if(!ismachineperson(owner) || (ismachineperson(owner) && ((owner.head && (owner.head.flags & BLOCKHEADHAIR)) || (owner.wear_mask && (owner.wear_mask.flags & BLOCKHEADHAIR))))) var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style] if(hair_style && ((dna.species.name in hair_style.species_allowed) || (dna.species.bodyflags & ALL_RPARTS))) var/icon/hair_s = new /icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm new file mode 100644 index 00000000000..840f0cd267c --- /dev/null +++ b/code/modules/tgui/external.dm @@ -0,0 +1,155 @@ +/** + * tgui external + * + * Contains all external tgui declarations. + */ + +/** + * public + * + * Used to open and update UIs. + * If this proc is not implemented properly, the UI will not update correctly. + * + * required user mob The mob who opened/is using the UI. + * optional ui_key string The ui_key of the UI. + * optional ui datum/tgui The UI to be updated, if it exists. + * optional force_open bool If the UI should be re-opened instead of updated. + * optional master_ui datum/tgui The parent UI. + * optional state datum/ui_state The state used to determine status. + */ + +/datum/proc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + return FALSE // Not implemented. + +/** + * public + * + * Data to be sent to the UI. + * This must be implemented for a UI to work. + * + * required user mob The mob interacting with the UI. + * + * return list Data to be sent to the UI. + */ +/datum/proc/tgui_data(mob/user) + return list() // Not implemented. + +/** + * public + * + * Static Data to be sent to the UI. + * Static data differs from normal data in that it's large data that should be sent infrequently + * This is implemented optionally for heavy uis that would be sending a lot of redundant data + * frequently. + * Gets squished into one object on the frontend side, but the static part is cached. + * + * required user mob The mob interacting with the UI. + * + * return list Statuic Data to be sent to the UI. + */ +/datum/proc/tgui_static_data(mob/user) + return list() + +/** + * public + * + * Forces an update on static data. Should be done manually whenever something happens to change static data. + * + * required user the mob currently interacting with the ui + * optional ui ui to be updated + * optional ui_key ui key of ui to be updated + */ +/datum/proc/update_tgui_static_data(mob/user, datum/tgui/ui, ui_key = "main") + ui = SStgui.try_update_ui(user, src, ui_key, ui) + // If there was no ui to update, there's no static data to update either. + if(!ui) + return + ui.push_data(null, tgui_static_data(), TRUE) + +/** + * public + * + * Called on a UI when the UI receieves a href. + * Think of this as Topic(). + * + * required action string The action/button that has been invoked by the user. + * required params list A list of parameters attached to the button. + * + * return bool If the UI should be updated or not. + */ +/datum/proc/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + // If UI is not interactive or usr calling Topic is not the UI user, bail. + if(!ui || ui.status != STATUS_INTERACTIVE) + return TRUE + +/** + * public + * + * Called on an object when a tgui object is being created, allowing you to + * customise the html + * For example: inserting a custom stylesheet that you need in the head + * + * For this purpose, some tags are available in the html, to be parsed out + ^ with replacetext + * (customheadhtml) - Additions to the head tag + * + * required html the html base text + */ +/datum/proc/tgui_base_html(html) + return html + +/** + * private + * + * The UI's host object (usually src_object). + * This allows modules/datums to have the UI attached to them, + * and be a part of another object. + */ +/datum/proc/tgui_host(mob/user) + return src // Default src. + +/** + * global + * + * Associative list of JSON-encoded shared states that were set by + * tgui clients. + */ + +/datum/var/list/tgui_shared_states + +/** + * global + * + * Used to track UIs for a mob. + */ +/mob/var/list/open_tguis = list() +/** + * public + * + * Called on a UI's object when the UI is closed, not to be confused with + * client/verb/uiclose(), which closes the ui window + */ +/datum/proc/tgui_close(mob/user) + +/** + * verb + * + * Called by UIs when they are closed. + * Must be a verb so winset() can call it. + * + * required uiref ref The UI that was closed. + */ +/client/verb/tguiclose(uid as text) + // Name the verb, and hide it from the user panel. + set name = "uiclose" + set hidden = TRUE + + // Get the UI based on the ref. + var/datum/tgui/ui = locateUID(uid) + + // If we found the UI, close it. + if(istype(ui)) + ui.close() + // Unset machine just to be sure. + if(src && src.mob) + src.mob.unset_machine() diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm new file mode 100644 index 00000000000..36be6295710 --- /dev/null +++ b/code/modules/tgui/modules/_base.dm @@ -0,0 +1,21 @@ +/* +TGUI MODULES + +This allows for datum-based TGUIs that can be hooked into objects. +This is useful for things such as the power monitor, which needs to exist on a physical console in the world, but also as a virtual device the AI can use + +Code is pretty much ripped verbatim from nano modules, but with un-needed stuff removed +*/ +/datum/tgui_module + var/name + var/datum/host + +/datum/tgui_module/New(var/host) + src.host = host + +/datum/tgui_module/tgui_host() + return host ? host : src + +/datum/tgui_module/tgui_close(mob/user) + if(host) + host.tgui_close(user) diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm new file mode 100644 index 00000000000..783498542e4 --- /dev/null +++ b/code/modules/tgui/modules/crew_monitor.dm @@ -0,0 +1,39 @@ +/datum/tgui_module/crew_monitor + name = "Crew monitor" + +/datum/tgui_module/crew_monitor/tgui_act(action, params) + if(..()) + return TRUE + + var/turf/T = get_turf(tgui_host()) + if(!T || !is_level_reachable(T.z)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return FALSE + + switch(action) + if("track") + if(isAI(usr)) + var/mob/living/silicon/ai/AI = usr + var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.mob_list + if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) + AI.ai_actual_track(H) + return TRUE + + +/datum/tgui_module/crew_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // The 557 may seem random, but its the perfectsize for margins on the nanomap + ui = new(user, src, ui_key, "CrewMonitor", name, 1400, 557, master_ui, state) + ui.autoupdate = TRUE + ui.open() + + +/datum/tgui_module/crew_monitor/tgui_data(mob/user) + var/data[0] + var/turf/T = get_turf(tgui_host()) + + data["isAI"] = isAI(user) + data["crewmembers"] = GLOB.crew_repository.health_data(T) + + return data diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm new file mode 100644 index 00000000000..81152426d06 --- /dev/null +++ b/code/modules/tgui/states.dm @@ -0,0 +1,117 @@ +/** + * tgui states + * + * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks. + */ + +/** + * public + * + * Checks the UI state for a mob. + * + * required user mob The mob who opened/is using the UI. + * required state datum/ui_state The state to check. + * + * return UI_state The state of the UI. + */ +/datum/proc/tgui_status(mob/user, datum/tgui_state/state) + var/src_object = tgui_host(user) + . = STATUS_CLOSE + if(!state) + return + + if(isobserver(user)) + // If they turn on ghost AI control, admins can always interact. + if(user.client.advanced_admin_interaction) + . = max(., STATUS_INTERACTIVE) + + // Regular ghosts can always at least view if in range. + var/clientviewlist = getviewsize(user.client.view) + if(get_dist(src_object, user) < max(clientviewlist[1],clientviewlist[2])) + . = max(., STATUS_UPDATE) + + // Check if the state allows interaction + var/result = state.can_use_topic(src_object, user) + . = max(., result) + +/** + * private + * + * Checks if a user can use src_object's UI, and returns the state. + * Can call a mob proc, which allows overrides for each mob. + * + * required src_object datum The object/datum which owns the UI. + * required user mob The mob who opened/is using the UI. + * + * return UI_state The state of the UI. + */ +/datum/tgui_state/proc/can_use_topic(src_object, mob/user) + return STATUS_CLOSE // Don't allow interaction by default. + +/** + * public + * + * Standard interaction/sanity checks. Different mob types may have overrides. + * + * return UI_state The state of the UI. + */ +/mob/proc/shared_tgui_interaction(src_object) + if(!client) // Close UIs if mindless. + return STATUS_CLOSE + else if(stat) // Disable UIs if unconcious. + return STATUS_DISABLED + else if(incapacitated()) // Update UIs if incapicitated but concious. + return STATUS_UPDATE + return STATUS_INTERACTIVE + +/mob/living/silicon/ai/shared_tgui_interaction(src_object) + if(lacks_power()) // Disable UIs if the AI is unpowered. + return STATUS_DISABLED + return ..() + +/mob/living/silicon/robot/shared_tgui_interaction(src_object) + if(!cell || cell.charge <= 0 || lockcharge) // Disable UIs if the Borg is unpowered or locked. + return STATUS_DISABLED + return ..() + +/** + * public + * + * Check the distance for a living mob. + * Really only used for checks outside the context of a mob. + * Otherwise, use shared_living_ui_distance(). + * + * required src_object The object which owns the UI. + * required user mob The mob who opened/is using the UI. + * + * return UI_state The state of the UI. + */ +/atom/proc/contents_tgui_distance(src_object, mob/living/user) + return user.shared_living_tgui_distance(src_object) // Just call this mob's check. + +/** + * public + * + * Distance versus interaction check. + * + * required src_object atom/movable The object which owns the UI. + * + * return UI_state The state of the UI. + */ +/mob/living/proc/shared_living_tgui_distance(atom/movable/src_object, viewcheck = TRUE) + if(viewcheck && !(src_object in view(src))) // If the object is obscured, close it. + return STATUS_CLOSE + + var/dist = get_dist(src_object, src) + if(dist <= 1) // Open and interact if 1-0 tiles away. + return STATUS_INTERACTIVE + else if(dist <= 2) // View only if 2-3 tiles away. + return STATUS_UPDATE + else if(dist <= 5) // Disable if 5 tiles away. + return STATUS_DISABLED + return STATUS_CLOSE // Otherwise, we got nothing. + +/mob/living/carbon/human/shared_living_tgui_distance(atom/movable/src_object) + if((TK in mutations) && (get_dist(src, src_object) <= 2)) + return STATUS_INTERACTIVE + return ..() diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm new file mode 100644 index 00000000000..6d1c680927a --- /dev/null +++ b/code/modules/tgui/states/admin.dm @@ -0,0 +1,12 @@ + /** + * tgui state: admin_state + * + * Checks that the user is an admin, end-of-story. + **/ + +GLOBAL_DATUM_INIT(tgui_admin_state, /datum/tgui_state/admin_state, new) + +/datum/tgui_state/admin_state/can_use_topic(src_object, mob/user) + if(check_rights_for(user.client, R_ADMIN)) + return STATUS_INTERACTIVE + return STATUS_CLOSE diff --git a/code/modules/tgui/states/always.dm b/code/modules/tgui/states/always.dm new file mode 100644 index 00000000000..30915785442 --- /dev/null +++ b/code/modules/tgui/states/always.dm @@ -0,0 +1,11 @@ + + /** + * tgui state: always_state + * + * Always grants the user UI_INTERACTIVE. Period. + **/ + +GLOBAL_DATUM_INIT(tgui_always_state, /datum/tgui_state/always_state, new) + +/datum/tgui_state/always_state/can_use_topic(src_object, mob/user) + return STATUS_INTERACTIVE diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm new file mode 100644 index 00000000000..6bc0c7ec031 --- /dev/null +++ b/code/modules/tgui/states/conscious.dm @@ -0,0 +1,12 @@ + /** + * tgui state: conscious_state + * + * Only checks if the user is conscious. + **/ + +GLOBAL_DATUM_INIT(tgui_conscious_state, /datum/tgui_state/conscious_state, new) + +/datum/tgui_state/conscious_state/can_use_topic(src_object, mob/user) + if(user.stat == CONSCIOUS) + return STATUS_INTERACTIVE + return STATUS_CLOSE diff --git a/code/modules/tgui/states/contained.dm b/code/modules/tgui/states/contained.dm new file mode 100644 index 00000000000..c2fbd0b6b06 --- /dev/null +++ b/code/modules/tgui/states/contained.dm @@ -0,0 +1,12 @@ + /** + * tgui state: contained_state + * + * Checks that the user is inside the src_object. + **/ + +GLOBAL_DATUM_INIT(tgui_contained_state, /datum/tgui_state/contained_state, new) + +/datum/tgui_state/contained_state/can_use_topic(atom/src_object, mob/user) + if(!src_object.contains(user)) + return STATUS_CLOSE + return user.shared_tgui_interaction(src_object) diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm new file mode 100644 index 00000000000..137f262a0ea --- /dev/null +++ b/code/modules/tgui/states/deep_inventory.dm @@ -0,0 +1,12 @@ + /** + * tgui state: deep_inventory_state + * + * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory. + **/ + +GLOBAL_DATUM_INIT(tgui_deep_inventory_state, /datum/tgui_state/deep_inventory_state, new) + +/datum/tgui_state/deep_inventory_state/can_use_topic(src_object, mob/user) + if(!user.contains(src_object)) + return STATUS_CLOSE + return user.shared_tgui_interaction(src_object) diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm new file mode 100644 index 00000000000..4da729aa7e5 --- /dev/null +++ b/code/modules/tgui/states/default.dm @@ -0,0 +1,63 @@ + /** + * tgui state: default_state + * + * Checks a number of things -- mostly physical distance for humans and view for robots. + **/ + +GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new) + +/datum/tgui_state/default/can_use_topic(src_object, mob/user) + return user.default_can_use_tgui_topic(src_object) // Call the individual mob-overridden procs. + +/mob/proc/default_can_use_tgui_topic(src_object) + return STATUS_CLOSE // Don't allow interaction by default. + +/mob/living/default_can_use_tgui_topic(src_object) + . = shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE && loc) + . = min(., loc.contents_tgui_distance(src_object, src)) // Check the distance... + if(. == STATUS_INTERACTIVE) // Non-human living mobs can only look, not touch. + return STATUS_UPDATE + +/mob/living/carbon/human/default_can_use_tgui_topic(src_object) + . = shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE) + . = min(., shared_living_tgui_distance(src_object)) // Check the distance... + +/mob/living/silicon/robot/default_can_use_tgui_topic(src_object) + . = shared_tgui_interaction(src_object) + if(. <= STATUS_DISABLED) + return + + // Robots can interact with anything they can see. + var/list/clientviewlist = getviewsize(client.view) + if((src_object in view(src)) && (get_dist(src, src_object) <= min(clientviewlist[1],clientviewlist[2]))) + return STATUS_INTERACTIVE + return STATUS_DISABLED // Otherwise they can keep the UI open. + +/mob/living/silicon/ai/default_can_use_tgui_topic(src_object) + . = shared_tgui_interaction(src_object) + if(. < STATUS_INTERACTIVE) + return + + // The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled. + if(!control_disabled && can_see(src_object)) + return STATUS_INTERACTIVE + return STATUS_CLOSE + +/mob/living/simple_animal/default_can_use_tgui_topic(src_object) + . = shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE) + . = min(., shared_living_tgui_distance(src_object)) //simple animals can only use things they're near. + +/mob/living/silicon/pai/default_can_use_tgui_topic(src_object) + // pAIs can only use themselves and the owner's radio. + if((src_object == src || src_object == radio) && !stat) + return STATUS_INTERACTIVE + else + return ..() + +/mob/dead/observer/default_can_use_tgui_topic() + if(can_admin_interact()) + return STATUS_INTERACTIVE // Admins are more equal + return STATUS_UPDATE // Ghosts can view updates diff --git a/code/modules/tgui/states/hands.dm b/code/modules/tgui/states/hands.dm new file mode 100644 index 00000000000..6dc1e2fc1a4 --- /dev/null +++ b/code/modules/tgui/states/hands.dm @@ -0,0 +1,25 @@ + /** + * tgui state: hands_state + * + * Checks that the src_object is in the user's hands. + **/ + +GLOBAL_DATUM_INIT(tgui_hands_state, /datum/tgui_state/hands_state, new) + +/datum/tgui_state/hands_state/can_use_topic(src_object, mob/user) + . = user.shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE) + return min(., user.hands_can_use_tgui_topic(src_object)) + +/mob/proc/hands_can_use_tgui_topic(src_object) + return STATUS_CLOSE + +/mob/living/hands_can_use_tgui_topic(src_object) + if(is_in_active_hand(src_object) || is_in_inactive_hand(src_object)) + return STATUS_INTERACTIVE + return STATUS_CLOSE + +/mob/living/silicon/robot/hands_can_use_tgui_topic(src_object) + if(activated(src_object)) + return STATUS_INTERACTIVE + return STATUS_CLOSE diff --git a/code/modules/tgui/states/human_adjacent.dm b/code/modules/tgui/states/human_adjacent.dm new file mode 100644 index 00000000000..8164d5f9cee --- /dev/null +++ b/code/modules/tgui/states/human_adjacent.dm @@ -0,0 +1,17 @@ + + /** + * tgui state: human_adjacent_state + * + * In addition to default checks, only allows interaction for a + * human adjacent user. + **/ + +GLOBAL_DATUM_INIT(tgui_human_adjacent_state, /datum/tgui_state/human_adjacent_state, new) + +/datum/tgui_state/human_adjacent_state/can_use_topic(src_object, mob/user) + . = user.default_can_use_tgui_topic(src_object) + + var/dist = get_dist(src_object, user) + if((dist > 1) || (!ishuman(user))) + // Can't be used unless adjacent and human, even with TK + . = min(., STATUS_UPDATE) diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm new file mode 100644 index 00000000000..92274cc1f2e --- /dev/null +++ b/code/modules/tgui/states/inventory.dm @@ -0,0 +1,12 @@ + /** + * tgui state: inventory_state + * + * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory. + **/ + +GLOBAL_DATUM_INIT(tgui_inventory_state, /datum/tgui_state/inventory_state, new) + +/datum/tgui_state/inventory_state/can_use_topic(src_object, mob/user) + if(!(src_object in user)) + return STATUS_CLOSE + return user.shared_tgui_interaction(src_object) diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm new file mode 100644 index 00000000000..1185b516478 --- /dev/null +++ b/code/modules/tgui/states/not_incapacitated.dm @@ -0,0 +1,29 @@ + /** + * tgui state: not_incapacitated_state + * + * Checks that the user isn't incapacitated + **/ + +GLOBAL_DATUM_INIT(tgui_not_incapacitated_state, /datum/tgui_state/not_incapacitated_state, new) + + /** + * tgui state: not_incapacitated_turf_state + * + * Checks that the user isn't incapacitated and that their loc is a turf + **/ + +GLOBAL_DATUM_INIT(tgui_not_incapacitated_turf_state, /datum/tgui_state/not_incapacitated_state, new(no_turfs = TRUE)) + +/datum/tgui_state/not_incapacitated_state + var/turf_check = FALSE + +/datum/tgui_state/not_incapacitated_state/New(loc, no_turfs = FALSE) + ..() + turf_check = no_turfs + +/datum/tgui_state/not_incapacitated_state/can_use_topic(src_object, mob/user) + if(user.stat) + return STATUS_CLOSE + if(user.incapacitated() || (turf_check && !isturf(user.loc))) + return STATUS_DISABLED + return STATUS_INTERACTIVE diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm new file mode 100644 index 00000000000..01811d427f2 --- /dev/null +++ b/code/modules/tgui/states/notcontained.dm @@ -0,0 +1,26 @@ + /** + * tgui state: notcontained_state + * + * Checks that the user is not inside src_object, and then makes the default checks. + **/ + +GLOBAL_DATUM_INIT(tgui_notcontained_state, /datum/tgui_state/notcontained_state, new) + +/datum/tgui_state/notcontained_state/can_use_topic(atom/src_object, mob/user) + . = user.shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE) + return min(., user.notcontained_can_use_tgui_topic(src_object)) + +/mob/proc/notcontained_can_use_tgui_topic(src_object) + return STATUS_CLOSE + +/mob/living/notcontained_can_use_tgui_topic(atom/src_object) + if(src_object.contains(src)) + return STATUS_CLOSE // Close if we're inside it. + return default_can_use_tgui_topic(src_object) + +/mob/living/silicon/notcontained_can_use_tgui_topic(src_object) + return default_can_use_tgui_topic(src_object) // Silicons use default bevhavior. + +/mob/living/simple_animal/drone/notcontained_can_use_tgui_topic(src_object) + return default_can_use_tgui_topic(src_object) // Drones use default bevhavior. diff --git a/code/modules/tgui/states/observer.dm b/code/modules/tgui/states/observer.dm new file mode 100644 index 00000000000..bf98f444654 --- /dev/null +++ b/code/modules/tgui/states/observer.dm @@ -0,0 +1,15 @@ + /** + * tgui state: observer_state + * + * Checks that the user is an observer/ghost. + **/ + +GLOBAL_DATUM_INIT(tgui_observer_state, /datum/tgui_state/observer_state, new) + +/datum/tgui_state/observer_state/can_use_topic(src_object, mob/user) + if(isobserver(user)) + return STATUS_INTERACTIVE + if(check_rights(R_ADMIN, 0, src)) + return STATUS_INTERACTIVE + return STATUS_CLOSE + diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm new file mode 100644 index 00000000000..a57a321e958 --- /dev/null +++ b/code/modules/tgui/states/physical.dm @@ -0,0 +1,24 @@ + /** + * tgui state: physical_state + * + * Short-circuits the default state to only check physical distance. + **/ + +GLOBAL_DATUM_INIT(tgui_physical_state, /datum/tgui_state/physical, new) + +/datum/tgui_state/physical/can_use_topic(src_object, mob/user) + . = user.shared_tgui_interaction(src_object) + if(. > STATUS_CLOSE) + return min(., user.physical_can_use_tgui_topic(src_object)) + +/mob/proc/physical_can_use_tgui_topic(src_object) + return STATUS_CLOSE + +/mob/living/physical_can_use_tgui_topic(src_object) + return shared_living_tgui_distance(src_object) + +/mob/living/silicon/physical_can_use_tgui_topic(src_object) + return max(STATUS_UPDATE, shared_living_tgui_distance(src_object)) // Silicons can always see. + +/mob/living/silicon/ai/physical_can_use_tgui_topic(src_object) + return STATUS_UPDATE // AIs are not physical. diff --git a/code/modules/tgui/states/self.dm b/code/modules/tgui/states/self.dm new file mode 100644 index 00000000000..109fd6ae440 --- /dev/null +++ b/code/modules/tgui/states/self.dm @@ -0,0 +1,12 @@ + /** + * tgui state: self_state + * + * Only checks that the user and src_object are the same. + **/ + +GLOBAL_DATUM_INIT(tgui_self_state, /datum/tgui_state/self_state, new) + +/datum/tgui_state/self_state/can_use_topic(src_object, mob/user) + if(src_object != user) + return STATUS_CLOSE + return user.shared_tgui_interaction(src_object) diff --git a/code/modules/tgui/states/zlevel.dm b/code/modules/tgui/states/zlevel.dm new file mode 100644 index 00000000000..a589a4b64d7 --- /dev/null +++ b/code/modules/tgui/states/zlevel.dm @@ -0,0 +1,14 @@ + /** + * tgui state: z_state + * + * Only checks that the Z-level of the user and src_object are the same. + **/ + +GLOBAL_DATUM_INIT(tgui_z_state, /datum/tgui_state/z_state, new) + +/datum/tgui_state/z_state/can_use_topic(src_object, mob/user) + var/turf/turf_obj = get_turf(src_object) + var/turf/turf_usr = get_turf(user) + if(turf_obj && turf_usr && turf_obj.z == turf_usr.z) + return STATUS_INTERACTIVE + return STATUS_CLOSE diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm new file mode 100644 index 00000000000..5157d0c9fed --- /dev/null +++ b/code/modules/tgui/tgui.dm @@ -0,0 +1,363 @@ +/** + * tgui + * + * /tg/station user interface library + */ + +/** + * tgui datum (represents a UI). + */ +/datum/tgui + /// The mob who opened/is using the UI. + var/mob/user + /// The object which owns the UI. + var/datum/src_object + /// The title of te UI. + var/title + /// The ui_key of the UI. This allows multiple UIs for one src_object. + var/ui_key + /// The window_id for browse() and onclose(). + var/window_id + /// The window width. + var/width = 0 + /// The window height + var/height = 0 + /// The interface (template) to be used for this UI. + var/interface + /// Update the UI every MC tick. + var/autoupdate = TRUE + /// If the UI has been initialized yet. + var/initialized = FALSE + /// The data (and datastructure) used to initialize the UI. + var/list/initial_data + /// The static data used to initialize the UI. + var/list/initial_static_data + /// Holder for the json string, that is sent during the initial update + var/_initial_update + /// The status/visibility of the UI. + var/status = STATUS_INTERACTIVE + /// Topic state used to determine status/interactability. + var/datum/tgui_state/state = null + /// The parent UI. + var/datum/tgui/master_ui + /// Children of this UI. + var/list/datum/tgui/children = list() + +/** + * public + * + * Create a new UI. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object or datum which owns the UI. + * required ui_key string The ui_key of the UI. + * required interface string The interface used to render the UI. + * optional title string The title of the UI. + * optional width int The window width. + * optional height int The window height. + * optional master_ui datum/tgui The parent UI. + * optional state datum/ui_state The state used to determine status. + * + * return datum/tgui The requested UI. + */ +/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + src.user = user + src.src_object = src_object + src.ui_key = ui_key + src.window_id = "[src_object.UID()]-[ui_key]" + src.interface = interface + + if(title) + src.title = sanitize(title) + if(width) + src.width = width + if(height) + src.height = height + + src.master_ui = master_ui + if(master_ui) + master_ui.children += src + src.state = state + + var/datum/asset/tgui_assets = get_asset_datum(/datum/asset/simple/tgui) + var/datum/asset/fa = get_asset_datum(/datum/asset/simple/fontawesome) + tgui_assets.send(user) + fa.send(user) + +/** + * public + * + * Open this UI (and initialize it with data). + */ +/datum/tgui/proc/open() + if(!user.client) + return // Bail if there is no client. + + update_status(push = FALSE) // Update the window status. + if(status < STATUS_UPDATE) + return // Bail if we're not supposed to open. + + + + // Build window options + var/window_options = "can_minimize=0;auto_format=0;" + // If we have a width and height, use them. + if(width && height) + window_options += "size=[width]x[height];" + + // Remove titlebar and resize handles for a fancy window + if(user.client.prefs.nanoui_fancy) + window_options += "titlebar=0;can_resize=0;" + else + window_options += "titlebar=1;can_resize=1;" + + // Generate page html + var/html + html = SStgui.basehtml + // Allow the src object to override the html if needed + html = src_object.tgui_base_html(html) + // Replace template tokens with important UI data + html = replacetextEx(html, "\[tgui:ref]", "[src.UID()]") + + // Open the window. + user << browse(html, "window=[window_id];[window_options]") + + // Instruct the client to signal UI when the window is closed. + // NOTE: Intentional \ref usage; tgui datums can't/shouldn't + // be tagged, so this is an effective unwrap + winset(user, window_id, "on-close=\"uiclose [UID()]\"") + + // Pre-fetch initial state while browser is still loading in + // another thread + if(!initial_data) + initial_data = src_object.tgui_data(user) + if(!initial_static_data) + initial_static_data = src_object.tgui_static_data(user) + _initial_update = url_encode(get_json(initial_data, initial_static_data)) + + SStgui.on_open(src) + +/** + * public + * + * Reinitialize the UI. + * (Possibly with a new interface and/or data). + * + * optional template string The name of the new interface. + * optional data list The new initial data. + */ +/datum/tgui/proc/reinitialize(interface, list/data, list/static_data) + if(interface) + src.interface = interface + if(data) + initial_data = data + if(static_data) + initial_static_data = static_data + open() + +/** + * public + * + * Close the UI, and all its children. + */ +/datum/tgui/proc/close() + user << browse(null, "window=[window_id]") // Close the window. + src_object.tgui_close(user) + SStgui.on_close(src) + for(var/datum/tgui/child in children) // Loop through and close all children. + child.close() + children.Cut() + state = null + master_ui = null + qdel(src) + +/** + * public + * + * Enable/disable auto-updating of the UI. + * + * required state bool Enable/disable auto-updating. + */ +/datum/tgui/proc/set_autoupdate(state = TRUE) + autoupdate = state + +/** + * private + * + * Package the data to send to the UI, as JSON. + * This includes the UI data and config_data. + * + * return string The packaged JSON. + */ +/datum/tgui/proc/get_json(list/data, list/static_data) + var/list/json_data = list() + + json_data["config"] = list( + "title" = title, + "status" = status, + "interface" = interface, + "fancy" = user.client.prefs.nanoui_fancy, + "observer" = isobserver(user), + "window" = window_id, + "map" = (GLOB.using_map && GLOB.using_map.name) ? GLOB.using_map.name : "Unknown", + + "ref" = "[src.UID()]" + ) + + if(!isnull(data)) + json_data["data"] = data + if(!isnull(static_data)) + json_data["static_data"] = static_data + + // Send shared states + if(src_object.tgui_shared_states) + json_data["shared"] = src_object.tgui_shared_states + + // Generate the JSON. + var/json = json_encode(json_data) + // Strip #255/improper. + json = replacetext(json, "\proper", "") + json = replacetext(json, "\improper", "") + return json + +/** + * private + * + * Handle clicks from the UI. + * Call the src_object's ui_act() if status is UI_INTERACTIVE. + * If the src_object's ui_act() returns 1, update all UIs attacked to it. + */ +/datum/tgui/Topic(href, href_list) + if(user != usr) + return // Something is not right here. + + var/action = href_list["action"] + var/params = href_list; params -= "action" + + switch(action) + if("tgui:initialize") + user << output(_initial_update, "[window_id].browser:update") + initialized = TRUE + if("tgui:setSharedState") + // Update the window state. + update_status(push = FALSE) + // Bail if UI is not interactive or usr calling Topic + // is not the UI user. + if(status != STATUS_INTERACTIVE) + return + var/key = params["key"] + var/value = params["value"] + if(!src_object.tgui_shared_states) + src_object.tgui_shared_states = list() + src_object.tgui_shared_states[key] = value + SStgui.update_uis(src_object) + if("tgui:setFancy") + var/value = text2num(params["value"]) + user.client.prefs.nanoui_fancy = value + if("tgui:log") + // Force window to show frills on fatal errors + if(params["fatal"]) + winset(user, window_id, "titlebar=1;can-resize=1;size=600x600") + log_message(params["log"]) + if("tgui:link") + user << link(params["url"]) + + else + // Update the window state. + update_status(push = FALSE) + // Call tgui_act() on the src_object. + if(src_object.tgui_act(action, params, src, state)) + // Update if the object requested it. + SStgui.update_uis(src_object) + +/** + * private + * + * Update the UI. + * Only updates the data if update is true, otherwise only updates the status. + * + * optional force bool If the UI should be forced to update. + */ +/datum/tgui/process(force = FALSE) + var/datum/host = src_object.tgui_host(user) + if(!src_object || !host || !user) // If the object or user died (or something else), abort. + close() + return + + if(status && (force || autoupdate)) + update() // Update the UI if the status and update settings allow it. + else + update_status(push = TRUE) // Otherwise only update status. + +/** + * private + * + * Push data to an already open UI. + * + * required data list The data to send. + * optional force bool If the update should be sent regardless of state. + */ +/datum/tgui/proc/push_data(data, static_data, force = FALSE) + // Update the window state. + update_status(push = FALSE) + // Cannot update UI if it is not set up yet. + if(!initialized) + return + // Cannot update UI, we have no visibility. + if(status <= STATUS_DISABLED && !force) + return + // Send the new JSON to the update() Javascript function. + user << output( + url_encode(get_json(data, static_data)), + "[window_id].browser:update") + +/** + * private + * + * Updates the UI by interacting with the src_object again, which will hopefully + * call try_ui_update on it. + * + * optional force_open bool If force_open should be passed to ui_interact. + */ +/datum/tgui/proc/update(force_open = FALSE) + src_object.tgui_interact(user, ui_key, src, force_open, master_ui, state) + +/** + * private + * + * Update the status/visibility of the UI for its user. + * + * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). + */ +/datum/tgui/proc/update_status(push = FALSE) + var/status = src_object.tgui_status(user, state) + if(master_ui) + status = min(status, master_ui.status) + + set_status(status, push) + if(status == STATUS_CLOSE) + close() + +/** + * private + * + * Set the status/visibility of the UI. + * + * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE). + * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). + */ +/datum/tgui/proc/set_status(status, push = FALSE) + // Only update if status has changed. + if(src.status != status) + if(src.status == STATUS_DISABLED) + src.status = status + if(push) + update() + else + src.status = status + // Update if the UI just because disabled, or a push is requested. + if(status == STATUS_DISABLED || push) + push_data(null, force = TRUE) + +/datum/tgui/proc/log_message(message) + log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]") diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm deleted file mode 100644 index 0f0f85b088b..00000000000 --- a/code/modules/zombie/items.dm +++ /dev/null @@ -1,69 +0,0 @@ -/obj/item/zombie_hand - name = "zombie claw" - desc = "A zombie's claw is its primary tool, capable of infecting \ - unconscious or dead humans, butchering all other living things to \ - sustain the zombie, forcing open airlock doors and opening \ - child-safe caps on bottles." - flags = NODROP|ABSTRACT|DROPDEL - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - icon = 'icons/effects/blood.dmi' - icon_state = "bloodhand_left" - var/icon_left = "bloodhand_left" - var/icon_right = "bloodhand_right" - hitsound = 'sound/hallucinations/growl1.ogg' - force = 21 // Just enough to break airlocks with melee attacks - damtype = "brute" - -/obj/item/zombie_hand/equipped(mob/user, slot) - . = ..() - switch(slot) - // Yes, these intentionally don't match - if(slot_l_hand) - icon_state = icon_right - if(slot_r_hand) - icon_state = icon_left - -/obj/item/zombie_hand/afterattack(atom/target, mob/user, proximity_flag) - . = ..() - if(!proximity_flag) - return - - else if(isliving(target)) - if(ishuman(target)) - try_to_zombie_infect(target) - else - check_feast(target, user) - -/proc/try_to_zombie_infect(mob/living/carbon/human/target) - CHECK_DNA_AND_SPECIES(target) - - if(NOZOMBIE in target.dna.species.species_traits) - // cannot infect any NOZOMBIE subspecies (such as high functioning - // zombies) - return - - var/obj/item/organ/internal/zombie_infection/infection - infection = target.get_organ_slot("zombie_infection") - if(!infection) - infection = new() - infection.insert(target) - -/obj/item/zombie_hand/proc/check_feast(mob/living/target, mob/living/user) - if(target.stat == DEAD) - var/hp_gained = target.maxHealth - target.gib() - user.adjustBruteLoss(-hp_gained, FALSE) - user.adjustToxLoss(-hp_gained, FALSE) - user.adjustFireLoss(-hp_gained, FALSE) - user.adjustCloneLoss(-hp_gained, FALSE) - user.adjustBrainLoss(-hp_gained, FALSE) // Zom Bee gibbers "BRAAAAISNSs!1!" - user.updatehealth() - -/obj/item/zombie_hand/suicide_act(mob/living/carbon/human/user) - user.visible_message("[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!") - if(ishuman(user)) - var/mob/living/carbon/human/L = user - var/obj/item/organ/external/O = L.get_organ("head") - if(O) - O.droplimb() - return (BRUTELOSS) diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm deleted file mode 100644 index 6ea40c90e96..00000000000 --- a/code/modules/zombie/organs.dm +++ /dev/null @@ -1,99 +0,0 @@ -/obj/item/organ/internal/zombie_infection - name = "festering ooze" - desc = "A black web of pus and viscera." - parent_organ = "head" - slot = "zombie_infection" - icon_state = "blacktumor" - var/causes_damage = TRUE - var/datum/species/old_species = /datum/species/human - var/living_transformation_time = 30 - var/converts_living = FALSE - - var/revive_time_min = 450 - var/revive_time_max = 700 - var/timer_id - -/obj/item/organ/internal/zombie_infection/New(mob/living/carbon/holder) - ..() - GLOB.zombie_infection_list += src - -/obj/item/organ/internal/zombie_infection/Destroy() - GLOB.zombie_infection_list -= src - . = ..() - -/obj/item/organ/internal/zombie_infection/insert(mob/living/carbon/human/M, special = 0) - ..() - START_PROCESSING(SSobj, src) - -/obj/item/organ/internal/zombie_infection/remove(mob/living/carbon/human/M, special = 0) - STOP_PROCESSING(SSobj, src) - if(iszombie(M) && old_species) - M.set_species(old_species, retain_damage = TRUE) - if(timer_id) - deltimer(timer_id) - . = ..() - -/obj/item/organ/internal/zombie_infection/on_find(mob/living/finder) - to_chat(finder, "Inside the head is a disgusting black \ - web of pus and viscera, bound tightly around the brain like some \ - biological harness.") - -/obj/item/organ/internal/zombie_infection/process() - if(!owner) - return - if(!(src in owner.internal_organs)) - remove(owner) - if(causes_damage && !iszombie(owner) && owner.stat != DEAD) - owner.adjustToxLoss(1) - if (prob(10)) - to_chat(owner, "You feel sick...") - if(timer_id) - return - if(owner.suiciding) - return - if(owner.stat != DEAD && !converts_living) - return - if(!owner.get_int_organ(/obj/item/organ/internal/brain)) - return - if(!iszombie(owner)) - to_chat(owner, "You can feel your heart stopping, but something isn't right... \ - life has not abandoned your broken form. You can only feel a deep and immutable hunger that \ - not even death can stop, you will rise again!") - var/revive_time = rand(revive_time_min, revive_time_max) - var/flags = TIMER_STOPPABLE - timer_id = addtimer(CALLBACK(src, .proc/zombify), revive_time, flags) - -/obj/item/organ/internal/zombie_infection/proc/zombify() - timer_id = null - - if(!converts_living && owner.stat != DEAD) - return - - if(!iszombie(owner)) - old_species = owner.dna.species.type - owner.set_species(/datum/species/zombie/infectious) - for(var/datum/disease/critical/crit in owner.viruses) // cure any new crit viruses - crit.cure(0) - - var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS) - //Fully heal the zombie's damage the first time they rise - owner.setToxLoss(0) - owner.setOxyLoss(0) - owner.setBrainLoss(0) - owner.setCloneLoss(0) - owner.SetLoseBreath(0) - owner.heal_overall_damage(INFINITY, INFINITY, TRUE, TRUE, FALSE) - owner.setStaminaLoss(0) - - if(!owner.update_revive()) - return - - owner.grab_ghost() - owner.visible_message("[owner] suddenly convulses, as [owner.p_they()][stand_up ? " stagger to [owner.p_their()] feet and" : ""] gain a ravenous hunger in [owner.p_their()] eyes!", "You HUNGER!") - playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, TRUE) - owner.do_jitter_animation(living_transformation_time) - owner.Stun(living_transformation_time * 0.05) - to_chat(owner, "You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.") - -/obj/item/organ/internal/zombie_infection/nodamage - causes_damage = FALSE diff --git a/html/font-awesome/README.MD b/html/font-awesome/README.MD new file mode 100644 index 00000000000..7d693c36f03 --- /dev/null +++ b/html/font-awesome/README.MD @@ -0,0 +1,6 @@ +Due to the fact browse_rsc can't create subdirectories, every time you update font-awesome you'll need to change relative webfont references in all.min.css +eg ../webfonts/fa-regular-400.ttf => fa-regular-400.ttf (or whatever you call it in asset datum) + +Second change is ripping out file types other than woff and eot(ie8) from the css + +Finally, removing brand related css. \ No newline at end of file diff --git a/html/font-awesome/css/all.min.css b/html/font-awesome/css/all.min.css new file mode 100644 index 00000000000..e7cdfffe751 --- /dev/null +++ b/html/font-awesome/css/all.min.css @@ -0,0 +1,5 @@ +/*! + * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} \ No newline at end of file diff --git a/html/font-awesome/css/v4-shims.min.css b/html/font-awesome/css/v4-shims.min.css new file mode 100644 index 00000000000..5f3fdc598c8 --- /dev/null +++ b/html/font-awesome/css/v4-shims.min.css @@ -0,0 +1,5 @@ +/*! + * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa.fa-glass:before{content:"\f000"}.fa.fa-meetup{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-star-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-file-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-pencil:before{content:"\f303"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-share-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart:before{content:"\f080"}.fa.fa-bar-chart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart-o:before{content:"\f080"}.fa.fa-facebook-square,.fa.fa-twitter-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-lemon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-scissors:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-google-plus,.fa.fa-google-plus-square,.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f3fd"}.fa.fa-comment-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard,.fa.fa-paste{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paste:before{content:"\f328"}.fa.fa-lightbulb-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f381"}.fa.fa-cloud-upload:before{content:"\f382"}.fa.fa-bell-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f089"}.fa.fa-star-half-empty{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f089"}.fa.fa-star-half-full{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f089"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before{content:"\f127"}.fa.fa-shield:before{content:"\f3ed"}.fa.fa-calendar-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ticket:before{content:"\f3ff"}.fa.fa-minus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\f155"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\f156"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f15e"}.fa.fa-sort-amount-asc:before{content:"\f160"}.fa.fa-sort-amount-desc:before{content:"\f161"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f163"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube,.fa.fa-youtube-play,.fa.fa-youtube-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\f195"}.fa.fa-plus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-google,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle,.fa.fa-yahoo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-spoon:before{content:"\f2e5"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-cab:before{content:"\f1ba"}.fa.fa-envelope-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-deviantart,.fa.fa-soundcloud{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-life-bouy,.fa.fa-life-ring{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-bouy:before{content:"\f1cd"}.fa.fa-life-buoy{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-buoy:before{content:"\f1cd"}.fa.fa-life-saver{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-saver:before{content:"\f1cd"}.fa.fa-support{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git,.fa.fa-git-square,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-sliders:before{content:"\f1de"}.fa.fa-futbol-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-angellist,.fa.fa-ioxhost,.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-meanpath{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-meanpath:before{content:"\f2b4"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before{content:"\f224"}.fa.fa-facebook-official{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-clone,.fa.fa-hourglass-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-chrome,.fa.fa-creative-commons,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-internet-explorer,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square,.fa.fa-opera,.fa.fa-safari,.fa.fa-tripadvisor,.fa.fa-wikipedia-w{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-snapchat,.fa.fa-snapchat-ghost,.fa.fa-snapchat-square,.fa.fa-themeisle,.fa.fa-viadeo,.fa.fa-viadeo-square,.fa.fa-yoast{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-spotify,.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 5 Brands";font-weight:400} \ No newline at end of file diff --git a/html/font-awesome/webfonts/fa-regular-400.eot b/html/font-awesome/webfonts/fa-regular-400.eot new file mode 100644 index 00000000000..d62be2fad88 Binary files /dev/null and b/html/font-awesome/webfonts/fa-regular-400.eot differ diff --git a/html/font-awesome/webfonts/fa-regular-400.woff b/html/font-awesome/webfonts/fa-regular-400.woff new file mode 100644 index 00000000000..43b1a9ae49d Binary files /dev/null and b/html/font-awesome/webfonts/fa-regular-400.woff differ diff --git a/html/font-awesome/webfonts/fa-solid-900.eot b/html/font-awesome/webfonts/fa-solid-900.eot new file mode 100644 index 00000000000..c77baa8d46a Binary files /dev/null and b/html/font-awesome/webfonts/fa-solid-900.eot differ diff --git a/html/font-awesome/webfonts/fa-solid-900.woff b/html/font-awesome/webfonts/fa-solid-900.woff new file mode 100644 index 00000000000..77c1786227f Binary files /dev/null and b/html/font-awesome/webfonts/fa-solid-900.woff differ diff --git a/icons/effects/hit_blips.dmi b/icons/effects/hit_blips.dmi deleted file mode 100644 index 49bb109799b..00000000000 Binary files a/icons/effects/hit_blips.dmi and /dev/null differ diff --git a/icons/mob/human_races/r_def_zombie.dmi b/icons/mob/human_races/r_def_zombie.dmi deleted file mode 100644 index 007eb3f31f3..00000000000 Binary files a/icons/mob/human_races/r_def_zombie.dmi and /dev/null differ diff --git a/icons/mob/human_races/r_zombie.dmi b/icons/mob/human_races/r_zombie.dmi deleted file mode 100644 index 007eb3f31f3..00000000000 Binary files a/icons/mob/human_races/r_zombie.dmi and /dev/null differ diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi index eb59b0288b3..dcbce35fb27 100644 Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi index 4b74a2891e3..961cffec7f6 100644 Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ diff --git a/icons/mob/ties.dmi b/icons/mob/ties.dmi index bbb986c088e..e399e5e3a8b 100644 Binary files a/icons/mob/ties.dmi and b/icons/mob/ties.dmi differ diff --git a/icons/obj/clothing/ties.dmi b/icons/obj/clothing/ties.dmi index 4799e9ad963..7675ec0d51c 100644 Binary files a/icons/obj/clothing/ties.dmi and b/icons/obj/clothing/ties.dmi differ diff --git a/icons/obj/clothing/ties_overlay.dmi b/icons/obj/clothing/ties_overlay.dmi index 975059bfeeb..fd3b6023185 100644 Binary files a/icons/obj/clothing/ties_overlay.dmi and b/icons/obj/clothing/ties_overlay.dmi differ diff --git a/nano/templates/ame.tmpl b/nano/templates/ame.tmpl deleted file mode 100644 index 4a86139eda1..00000000000 --- a/nano/templates/ame.tmpl +++ /dev/null @@ -1,56 +0,0 @@ -
-
Status:
- {{if data.active}} -
Injecting
- {{else}} -
Standby
- {{/if}} -  {{:helper.link("Toggle Status", "gear", {"togglestatus":1},null)}} -
-{{if !data.fueljar}} -
No fuel jar detected.
-{{else}} -
-
-
Fuel:
-
{{:data.fueljar.fuel}}/{{:data.fueljar.fuel_max}}
- {{:helper.link("Eject", "eject", {"ejectjar":1},null,'floatRight')}} -
-
-
Injecting:
-
- {{:helper.link(data.fueljar.injecting+" units", 'arrows-v', {'set_strength': 1})}} -
-
-
-{{/if}} -
-
-
Stability:
-
- {{:helper.displayBar(data.stability,0,100,"good")}} - {{:data.stability}}% -
-
-
-
Shielding Count:
-
{{:data.linked_shields}}
-
-
-
Cores:
-
{{:data.linked_cores}}
- {{:helper.link("Refresh Parts",null,{"refreshicons" : 1},(data.active?"disabled":null),'floatRight')}} -
-
-
Current Efficiency:
-
{{:data.efficiency}}
-
-
-
Average Efficiency:
-
{{:data.stability}}
-
-
-
Last Produced:
-
{{:data.stored_power}}
-
-
\ No newline at end of file diff --git a/nano/templates/botany_editor.tmpl b/nano/templates/botany_editor.tmpl deleted file mode 100644 index 96cabc4341c..00000000000 --- a/nano/templates/botany_editor.tmpl +++ /dev/null @@ -1,56 +0,0 @@ - - -{{if data.activity}} - Scanning... -{{else}} -

Buffered Genetic Data

- {{if data.disk}} -
-
- Source: -
-
- {{:data.sourceName}} -
-
- Gene decay: -
-
- {{if data.degradation <= 100}} - {{:data.degradation}}% - {{else}} - FURTHER AMENDMENTS NONVIABLE - {{/if}} -
-
- Locus: -
-
- {{:data.locus}} -
- {{:helper.link('Eject Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}} -
- {{else}} -
No disk loaded.
- {{/if}} -

Loaded Material

- {{if data.loaded}} -
-
- Target: -
-
- {{:data.loaded}} -
- {{if data.degradation <= 100}} - {{:helper.link('Apply Gene Mods', 'gear', {'apply_gene' : 1}, null)}} - {{/if}} - {{:helper.link('Eject Target', 'arrow-circle-right', {'eject_packet' : 1}, null)}} -
- {{else}} -
No target seed packet loaded
- {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/botany_isolator.tmpl b/nano/templates/botany_isolator.tmpl deleted file mode 100644 index a5efbc4a212..00000000000 --- a/nano/templates/botany_isolator.tmpl +++ /dev/null @@ -1,73 +0,0 @@ - - -{{if data.activity}} - Scanning... -{{else}} -

Buffered Genetic Data

- {{if data.hasGenetics}} -
-
- Source: -
-
- {{:data.sourceName}} -
-
- Gene decay: -
-
- {{:data.degradation}}% -
-
- {{if data.disk}} - {{for data.geneMasks}} -
-
- {{:value}} -
-
- {{:helper.link('Extract', 'arrow-circle-down', {'get_gene' : value}, null)}} -
-
- {{empty}} -
Data error. Genetic record corrupt.
- {{/for}} -
-
- {{:helper.link('Eject Loaded Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}} - {{:helper.link('Clear Genetic Buffer', 'gear', {'clear_buffer' : 1}, null)}} -
- {{else}} -
No disk inserted.
- {{/if}} - {{else}} -
No data buffered.
- {{if data.disk}} -
-
- {{:helper.link('Eject Loaded Disk', 'arrow-circle-right', {'eject_disk' : 1}, null)}} -
- {{else}} -
No disk inserted.
- {{/if}} - {{/if}} -

Loaded Material

- {{if data.loaded}} -
-
- Packet loaded: -
-
- {{:data.loaded}} -
-
- {{:helper.link('Process Genome', 'gear', {'scan_genome' : 1}, null)}}{{:helper.link('Eject Packet', 'arrow-circle-right', {'eject_packet' : 1}, null)}} -
-
- {{else}} -
No seeds loaded.
- {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/code_mirror.tmpl b/nano/templates/code_mirror.tmpl deleted file mode 100644 index dce1bf6f43b..00000000000 --- a/nano/templates/code_mirror.tmpl +++ /dev/null @@ -1,14 +0,0 @@ -

HELLO

- -
- - - -
- - {{: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 f7741955994..2ec1113630d 100644 --- a/paradise.dme +++ b/paradise.dme @@ -247,6 +247,7 @@ #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\statistics.dm" #include "code\controllers\subsystem\sun.dm" +#include "code\controllers\subsystem\tgui.dm" #include "code\controllers\subsystem\throwing.dm" #include "code\controllers\subsystem\ticker.dm" #include "code\controllers\subsystem\timer.dm" @@ -1850,7 +1851,6 @@ #include "code\modules\mob\living\carbon\human\species\vox.dm" #include "code\modules\mob\living\carbon\human\species\vulpkanin.dm" #include "code\modules\mob\living\carbon\human\species\wryn.dm" -#include "code\modules\mob\living\carbon\human\species\zombies.dm" #include "code\modules\mob\living\silicon\death.dm" #include "code\modules\mob\living\silicon\emote.dm" #include "code\modules\mob\living\silicon\laws.dm" @@ -2138,7 +2138,6 @@ #include "code\modules\nano\interaction\zlevel.dm" #include "code\modules\nano\modules\alarm_monitor.dm" #include "code\modules\nano\modules\atmos_control.dm" -#include "code\modules\nano\modules\crew_monitor.dm" #include "code\modules\nano\modules\ert_manager.dm" #include "code\modules\nano\modules\human_appearance.dm" #include "code\modules\nano\modules\law_manager.dm" @@ -2473,6 +2472,26 @@ #include "code\modules\telesci\rcs.dm" #include "code\modules\telesci\telepad.dm" #include "code\modules\telesci\telesci_computer.dm" +#include "code\modules\tgui\external.dm" +#include "code\modules\tgui\states.dm" +#include "code\modules\tgui\tgui.dm" +#include "code\modules\tgui\modules\_base.dm" +#include "code\modules\tgui\modules\crew_monitor.dm" +#include "code\modules\tgui\states\admin.dm" +#include "code\modules\tgui\states\always.dm" +#include "code\modules\tgui\states\conscious.dm" +#include "code\modules\tgui\states\contained.dm" +#include "code\modules\tgui\states\deep_inventory.dm" +#include "code\modules\tgui\states\default.dm" +#include "code\modules\tgui\states\hands.dm" +#include "code\modules\tgui\states\human_adjacent.dm" +#include "code\modules\tgui\states\inventory.dm" +#include "code\modules\tgui\states\not_incapacitated.dm" +#include "code\modules\tgui\states\notcontained.dm" +#include "code\modules\tgui\states\observer.dm" +#include "code\modules\tgui\states\physical.dm" +#include "code\modules\tgui\states\self.dm" +#include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tooltip\tooltip.dm" #include "code\modules\unit_tests\_unit_tests.dm" #include "code\modules\vehicle\ambulance.dm" @@ -2484,8 +2503,6 @@ #include "code\modules\vehicle\speedbike.dm" #include "code\modules\vehicle\sportscar.dm" #include "code\modules\vehicle\vehicle.dm" -#include "code\modules\zombie\items.dm" -#include "code\modules\zombie\organs.dm" #include "goon\code\datums\browserOutput.dm" #include "interface\interface.dm" #include "interface\skin.dmf" diff --git a/sound/ambience/ambiatmos.ogg b/sound/ambience/ambiatmos.ogg new file mode 100644 index 00000000000..c832c677d5e Binary files /dev/null and b/sound/ambience/ambiatmos.ogg differ diff --git a/sound/ambience/ambiatmos2.ogg b/sound/ambience/ambiatmos2.ogg new file mode 100644 index 00000000000..9651049c25b Binary files /dev/null and b/sound/ambience/ambiatmos2.ogg differ diff --git a/sound/ambience/ambicave.ogg b/sound/ambience/ambicave.ogg new file mode 100644 index 00000000000..d78dc46a4ce Binary files /dev/null and b/sound/ambience/ambicave.ogg differ diff --git a/sound/ambience/ambidet1.ogg b/sound/ambience/ambidet1.ogg new file mode 100644 index 00000000000..2ffc3ba494c Binary files /dev/null and b/sound/ambience/ambidet1.ogg differ diff --git a/sound/ambience/ambidet2.ogg b/sound/ambience/ambidet2.ogg new file mode 100644 index 00000000000..7d5aae34d0b Binary files /dev/null and b/sound/ambience/ambidet2.ogg differ diff --git a/sound/ambience/ambifailure.ogg b/sound/ambience/ambifailure.ogg new file mode 100644 index 00000000000..4dcf451bca6 Binary files /dev/null and b/sound/ambience/ambifailure.ogg differ diff --git a/sound/ambience/ambigen15.ogg b/sound/ambience/ambigen15.ogg new file mode 100644 index 00000000000..63afd437a65 Binary files /dev/null and b/sound/ambience/ambigen15.ogg differ diff --git a/sound/ambience/ambiholy.ogg b/sound/ambience/ambiholy.ogg new file mode 100644 index 00000000000..cd53dc2789c Binary files /dev/null and b/sound/ambience/ambiholy.ogg differ diff --git a/sound/ambience/ambiholy2.ogg b/sound/ambience/ambiholy2.ogg new file mode 100644 index 00000000000..4532dd0a815 Binary files /dev/null and b/sound/ambience/ambiholy2.ogg differ diff --git a/sound/ambience/ambiholy3.ogg b/sound/ambience/ambiholy3.ogg new file mode 100644 index 00000000000..93f7a897e1c Binary files /dev/null and b/sound/ambience/ambiholy3.ogg differ diff --git a/sound/ambience/ambilava.ogg b/sound/ambience/ambilava1.ogg similarity index 100% rename from sound/ambience/ambilava.ogg rename to sound/ambience/ambilava1.ogg diff --git a/sound/ambience/ambilava2.ogg b/sound/ambience/ambilava2.ogg new file mode 100644 index 00000000000..12e496670d0 Binary files /dev/null and b/sound/ambience/ambilava2.ogg differ diff --git a/sound/ambience/ambilava3.ogg b/sound/ambience/ambilava3.ogg new file mode 100644 index 00000000000..8913ad4a0ea Binary files /dev/null and b/sound/ambience/ambilava3.ogg differ diff --git a/sound/ambience/ambimaint.ogg b/sound/ambience/ambimaint.ogg new file mode 100644 index 00000000000..592850a75c7 Binary files /dev/null and b/sound/ambience/ambimaint.ogg differ diff --git a/sound/ambience/ambimystery.ogg b/sound/ambience/ambimystery.ogg new file mode 100644 index 00000000000..b6e65c0bf62 Binary files /dev/null and b/sound/ambience/ambimystery.ogg differ diff --git a/sound/ambience/ambinice.ogg b/sound/ambience/ambinice.ogg new file mode 100644 index 00000000000..6ce351521bc Binary files /dev/null and b/sound/ambience/ambinice.ogg differ diff --git a/sound/ambience/ambiodd.ogg b/sound/ambience/ambiodd.ogg new file mode 100644 index 00000000000..dde64a9858f Binary files /dev/null and b/sound/ambience/ambiodd.ogg differ diff --git a/sound/ambience/ambiruin.ogg b/sound/ambience/ambiruin.ogg new file mode 100644 index 00000000000..ff4cef4a415 Binary files /dev/null and b/sound/ambience/ambiruin.ogg differ diff --git a/sound/ambience/ambiruin2.ogg b/sound/ambience/ambiruin2.ogg new file mode 100644 index 00000000000..2dc408e2b69 Binary files /dev/null and b/sound/ambience/ambiruin2.ogg differ diff --git a/sound/ambience/ambiruin3.ogg b/sound/ambience/ambiruin3.ogg new file mode 100644 index 00000000000..7a97ff9c60c Binary files /dev/null and b/sound/ambience/ambiruin3.ogg differ diff --git a/sound/ambience/ambiruin4.ogg b/sound/ambience/ambiruin4.ogg new file mode 100644 index 00000000000..ad56a915f92 Binary files /dev/null and b/sound/ambience/ambiruin4.ogg differ diff --git a/sound/ambience/ambiruin5.ogg b/sound/ambience/ambiruin5.ogg new file mode 100644 index 00000000000..2073b5a2770 Binary files /dev/null and b/sound/ambience/ambiruin5.ogg differ diff --git a/sound/ambience/ambiruin6.ogg b/sound/ambience/ambiruin6.ogg new file mode 100644 index 00000000000..4b6c79a72d5 Binary files /dev/null and b/sound/ambience/ambiruin6.ogg differ diff --git a/sound/ambience/ambiruin7.ogg b/sound/ambience/ambiruin7.ogg new file mode 100644 index 00000000000..ed88fcd52d4 Binary files /dev/null and b/sound/ambience/ambiruin7.ogg differ diff --git a/sound/ambience/ambispace2.ogg b/sound/ambience/ambispace2.ogg new file mode 100644 index 00000000000..44b4c62c36e Binary files /dev/null and b/sound/ambience/ambispace2.ogg differ diff --git a/sound/ambience/ambitech.ogg b/sound/ambience/ambitech.ogg new file mode 100644 index 00000000000..5f21514e5c4 Binary files /dev/null and b/sound/ambience/ambitech.ogg differ diff --git a/sound/ambience/ambitech2.ogg b/sound/ambience/ambitech2.ogg new file mode 100644 index 00000000000..bd6428bff3e Binary files /dev/null and b/sound/ambience/ambitech2.ogg differ diff --git a/sound/ambience/ambitech3.ogg b/sound/ambience/ambitech3.ogg new file mode 100644 index 00000000000..effd23b1326 Binary files /dev/null and b/sound/ambience/ambitech3.ogg differ diff --git a/sound/ambience/ambivapor1.ogg b/sound/ambience/ambivapor1.ogg new file mode 100644 index 00000000000..4374f46710d Binary files /dev/null and b/sound/ambience/ambivapor1.ogg differ diff --git a/sound/ambience/aurora_caelus_short.ogg b/sound/ambience/aurora_caelus_short.ogg new file mode 100644 index 00000000000..f6d1acaa2fe Binary files /dev/null and b/sound/ambience/aurora_caelus_short.ogg differ diff --git a/sound/ambience/servicebell.ogg b/sound/ambience/servicebell.ogg new file mode 100644 index 00000000000..f55c7d5a759 Binary files /dev/null and b/sound/ambience/servicebell.ogg differ diff --git a/sound/ambience/shipambience.ogg b/sound/ambience/shipambience.ogg index 3c315961f71..51e090b02c8 100644 Binary files a/sound/ambience/shipambience.ogg and b/sound/ambience/shipambience.ogg differ diff --git a/sound/music/clown.ogg b/sound/music/clown.ogg new file mode 100644 index 00000000000..3214da56fa2 Binary files /dev/null and b/sound/music/clown.ogg differ diff --git a/sound/voice/lowHiss1.ogg b/sound/voice/lowHiss1.ogg new file mode 100644 index 00000000000..1373f010f84 Binary files /dev/null and b/sound/voice/lowHiss1.ogg differ diff --git a/sound/voice/lowHiss2.ogg b/sound/voice/lowHiss2.ogg new file mode 100644 index 00000000000..90a91db5dda Binary files /dev/null and b/sound/voice/lowHiss2.ogg differ diff --git a/sound/voice/lowHiss3.ogg b/sound/voice/lowHiss3.ogg new file mode 100644 index 00000000000..ab1f2d5bdb7 Binary files /dev/null and b/sound/voice/lowHiss3.ogg differ diff --git a/sound/voice/lowHiss4.ogg b/sound/voice/lowHiss4.ogg new file mode 100644 index 00000000000..a5c95b02977 Binary files /dev/null and b/sound/voice/lowHiss4.ogg differ diff --git a/tgui/.editorconfig b/tgui/.editorconfig new file mode 100644 index 00000000000..33092d4928a --- /dev/null +++ b/tgui/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +max_line_length = 80 diff --git a/tgui/.eslintignore b/tgui/.eslintignore new file mode 100644 index 00000000000..010416b9e88 --- /dev/null +++ b/tgui/.eslintignore @@ -0,0 +1,6 @@ +/**/node_modules +/**/*.bundle.* +/**/*.chunk.* +/**/*.hot-update.* +/packages/inferno/** +/packages/tgui/public/shim-*.js diff --git a/tgui/.eslintrc-harder.yml b/tgui/.eslintrc-harder.yml new file mode 100644 index 00000000000..eb06f5b33db --- /dev/null +++ b/tgui/.eslintrc-harder.yml @@ -0,0 +1,14 @@ +rules: + ## Enforce a maximum cyclomatic complexity allowed in a program + complexity: [error, { max: 25 }] + ## Enforce consistent brace style for blocks + brace-style: [error, stroustrup, { allowSingleLine: false }] + ## Enforce the consistent use of either backticks, double, or single quotes + quotes: [error, single, { + avoidEscape: true, + allowTemplateLiterals: true, + }] + react/jsx-closing-bracket-location: [error, { + selfClosing: after-props, + nonEmpty: after-props, + }] diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml new file mode 100644 index 00000000000..67e74085c7e --- /dev/null +++ b/tgui/.eslintrc.yml @@ -0,0 +1,755 @@ +parser: babel-eslint +parserOptions: + ecmaVersion: 2019 + sourceType: module + ecmaFeatures: + jsx: true +env: + es6: true + browser: true + node: true +plugins: + - react +settings: + react: + version: '16.10' +rules: + + ## Possible Errors + ## ---------------------------------------- + + ## Enforce “for” loop update clause moving the counter in the right + ## direction. + # for-direction: error + ## Enforce return statements in getters + # getter-return: error + ## Disallow using an async function as a Promise executor + no-async-promise-executor: error + ## Disallow await inside of loops + # no-await-in-loop: error + ## Disallow comparing against -0 + # no-compare-neg-zero: error + ## Disallow assignment operators in conditional expressions + no-cond-assign: error + ## Disallow the use of console + # no-console: error + ## Disallow constant expressions in conditions + # no-constant-condition: error + ## Disallow control characters in regular expressions + # no-control-regex: error + ## Disallow the use of debugger + no-debugger: error + ## Disallow duplicate arguments in function definitions + no-dupe-args: error + ## Disallow duplicate keys in object literals + no-dupe-keys: error + ## Disallow duplicate case labels + no-duplicate-case: error + ## Disallow empty block statements + # no-empty: error + ## Disallow empty character classes in regular expressions + no-empty-character-class: error + ## Disallow reassigning exceptions in catch clauses + no-ex-assign: error + ## Disallow unnecessary boolean casts + no-extra-boolean-cast: error + ## Disallow unnecessary parentheses + # no-extra-parens: warn + ## Disallow unnecessary semicolons + no-extra-semi: error + ## Disallow reassigning function declarations + no-func-assign: error + ## Disallow assigning to imported bindings + no-import-assign: error + ## Disallow variable or function declarations in nested blocks + no-inner-declarations: error + ## Disallow invalid regular expression strings in RegExp constructors + no-invalid-regexp: error + ## Disallow irregular whitespace + no-irregular-whitespace: error + ## Disallow characters which are made with multiple code points in character + ## class syntax + no-misleading-character-class: error + ## Disallow calling global object properties as functions + no-obj-calls: error + ## Disallow calling some Object.prototype methods directly on objects + no-prototype-builtins: error + ## Disallow multiple spaces in regular expressions + no-regex-spaces: error + ## Disallow sparse arrays + no-sparse-arrays: error + ## Disallow template literal placeholder syntax in regular strings + no-template-curly-in-string: error + ## Disallow confusing multiline expressions + no-unexpected-multiline: error + ## Disallow unreachable code after return, throw, continue, and break + ## statements + # no-unreachable: warn + ## Disallow control flow statements in finally blocks + no-unsafe-finally: error + ## Disallow negating the left operand of relational operators + no-unsafe-negation: error + ## Disallow assignments that can lead to race conditions due to usage of + ## await or yield + # require-atomic-updates: error + ## Require calls to isNaN() when checking for NaN + use-isnan: error + ## Enforce comparing typeof expressions against valid strings + valid-typeof: error + + ## Best practices + ## ---------------------------------------- + ## Enforce getter and setter pairs in objects and classes + # accessor-pairs: error + ## Enforce return statements in callbacks of array methods + # array-callback-return: error + ## Enforce the use of variables within the scope they are defined + # block-scoped-var: error + ## Enforce that class methods utilize this + # class-methods-use-this: error + ## Enforce a maximum cyclomatic complexity allowed in a program + complexity: [error, { max: 50 }] + ## Require return statements to either always or never specify values + # consistent-return: error + ## Enforce consistent brace style for all control statements + curly: [error, all] + ## Require default cases in switch statements + # default-case: error + ## Enforce default parameters to be last + # default-param-last: error + ## Enforce consistent newlines before and after dots + dot-location: [error, property] + ## Enforce dot notation whenever possible + # dot-notation: error + ## Require the use of === and !== + eqeqeq: [error, always] + ## Require for-in loops to include an if statement + # guard-for-in: error + ## Enforce a maximum number of classes per file + # max-classes-per-file: error + ## Disallow the use of alert, confirm, and prompt + # no-alert: error + ## Disallow the use of arguments.caller or arguments.callee + # no-caller: error + ## Disallow lexical declarations in case clauses + no-case-declarations: error + ## Disallow division operators explicitly at the beginning of regular + ## expressions + # no-div-regex: error + ## Disallow else blocks after return statements in if statements + # no-else-return: error + ## Disallow empty functions + # no-empty-function: error + ## Disallow empty destructuring patterns + no-empty-pattern: error + ## Disallow null comparisons without type-checking operators + # no-eq-null: error + ## Disallow the use of eval() + # no-eval: error + ## Disallow extending native types + # no-extend-native: error + ## Disallow unnecessary calls to .bind() + # no-extra-bind: error + ## Disallow unnecessary labels + # no-extra-label: error + ## Disallow fallthrough of case statements + no-fallthrough: error + ## Disallow leading or trailing decimal points in numeric literals + # no-floating-decimal: error + ## Disallow assignments to native objects or read-only global variables + no-global-assign: error + ## Disallow shorthand type conversions + # no-implicit-coercion: error + ## Disallow variable and function declarations in the global scope + # no-implicit-globals: error + ## Disallow the use of eval()-like methods + # no-implied-eval: error + ## Disallow this keywords outside of classes or class-like objects + # no-invalid-this: error + ## Disallow the use of the __iterator__ property + # no-iterator: error + ## Disallow labeled statements + # no-labels: error + ## Disallow unnecessary nested blocks + # no-lone-blocks: error + ## Disallow function declarations that contain unsafe references inside + ## loop statements + # no-loop-func: error + ## Disallow magic numbers + # no-magic-numbers: error + ## Disallow multiple spaces + no-multi-spaces: warn + ## Disallow multiline strings + # no-multi-str: error + ## Disallow new operators outside of assignments or comparisons + # no-new: error + ## Disallow new operators with the Function object + # no-new-func: error + ## Disallow new operators with the String, Number, and Boolean objects + # no-new-wrappers: error + ## Disallow octal literals + no-octal: error + ## Disallow octal escape sequences in string literals + no-octal-escape: error + ## Disallow reassigning function parameters + # no-param-reassign: error + ## Disallow the use of the __proto__ property + # no-proto: error + ## Disallow variable redeclaration + no-redeclare: error + ## Disallow certain properties on certain objects + # no-restricted-properties: error + ## Disallow assignment operators in return statements + no-return-assign: error + ## Disallow unnecessary return await + # no-return-await: error + ## Disallow javascript: urls + # no-script-url: error + ## Disallow assignments where both sides are exactly the same + no-self-assign: error + ## Disallow comparisons where both sides are exactly the same + # no-self-compare: error + ## Disallow comma operators + no-sequences: error + ## Disallow throwing literals as exceptions + # no-throw-literal: error + ## Disallow unmodified loop conditions + # no-unmodified-loop-condition: error + ## Disallow unused expressions + # no-unused-expressions: error + ## Disallow unused labels + no-unused-labels: warn + ## Disallow unnecessary calls to .call() and .apply() + # no-useless-call: error + ## Disallow unnecessary catch clauses + # no-useless-catch: error + ## Disallow unnecessary concatenation of literals or template literals + # no-useless-concat: error + ## Disallow unnecessary escape characters + no-useless-escape: warn + ## Disallow redundant return statements + # no-useless-return: error + ## Disallow void operators + # no-void: error + ## Disallow specified warning terms in comments + # no-warning-comments: error + ## Disallow with statements + no-with: error + ## Enforce using named capture group in regular expression + # prefer-named-capture-group: error + ## Require using Error objects as Promise rejection reasons + # prefer-promise-reject-errors: error + ## Disallow use of the RegExp constructor in favor of regular expression + ## literals + # prefer-regex-literals: error + ## Enforce the consistent use of the radix argument when using parseInt() + radix: error + ## Disallow async functions which have no await expression + # require-await: error + ## Enforce the use of u flag on RegExp + # require-unicode-regexp: error + ## Require var declarations be placed at the top of their containing scope + # vars-on-top: error + ## Require parentheses around immediate function invocations + # wrap-iife: error + ## Require or disallow “Yoda” conditions + # yoda: error + + ## Strict mode + ## ---------------------------------------- + ## Require or disallow strict mode directives + strict: error + + ## Variables + ## ---------------------------------------- + ## Require or disallow initialization in variable declarations + # init-declarations: error + ## Disallow deleting variables + no-delete-var: error + ## Disallow labels that share a name with a variable + # no-label-var: error + ## Disallow specified global variables + # no-restricted-globals: error + ## Disallow variable declarations from shadowing variables declared in + ## the outer scope + # no-shadow: error + ## Disallow identifiers from shadowing restricted names + no-shadow-restricted-names: error + ## Disallow the use of undeclared variables unless mentioned + ## in /*global*/ comments + no-undef: error + ## Disallow initializing variables to undefined + no-undef-init: error + ## Disallow the use of undefined as an identifier + # no-undefined: error + ## Disallow unused variables + # no-unused-vars: error + ## Disallow the use of variables before they are defined + # no-use-before-define: error + + ## Code style + ## ---------------------------------------- + ## Enforce linebreaks after opening and before closing array brackets + array-bracket-newline: [error, consistent] + ## Enforce consistent spacing inside array brackets + array-bracket-spacing: [error, never] + ## Enforce line breaks after each array element + # array-element-newline: error + ## Disallow or enforce spaces inside of blocks after opening block and + ## before closing block + block-spacing: [error, always] + ## Enforce consistent brace style for blocks + # brace-style: [error, stroustrup, { allowSingleLine: false }] + ## Enforce camelcase naming convention + # camelcase: error + ## Enforce or disallow capitalization of the first letter of a comment + # capitalized-comments: error + ## Require or disallow trailing commas + comma-dangle: [error, { + arrays: always-multiline, + objects: always-multiline, + imports: always-multiline, + exports: always-multiline, + functions: only-multiline, ## Optional on functions + }] + ## Enforce consistent spacing before and after commas + comma-spacing: [error, { before: false, after: true }] + ## Enforce consistent comma style + comma-style: [error, last] + ## Enforce consistent spacing inside computed property brackets + computed-property-spacing: [error, never] + ## Enforce consistent naming when capturing the current execution context + # consistent-this: error + ## Require or disallow newline at the end of files + # eol-last: error + ## Require or disallow spacing between function identifiers and their + ## invocations + func-call-spacing: [error, never] + ## Require function names to match the name of the variable or property + ## to which they are assigned + # func-name-matching: error + ## Require or disallow named function expressions + # func-names: error + ## Enforce the consistent use of either function declarations or expressions + func-style: [error, expression] + ## Enforce line breaks between arguments of a function call + # function-call-argument-newline: error + ## Enforce consistent line breaks inside function parentheses + ## NOTE: This rule does not honor a newline on opening paren. + # function-paren-newline: [error, never] + ## Disallow specified identifiers + # id-blacklist: error + ## Enforce minimum and maximum identifier lengths + # id-length: error + ## Require identifiers to match a specified regular expression + # id-match: error + ## Enforce the location of arrow function bodies + # implicit-arrow-linebreak: error + ## Enforce consistent indentation + indent: [error, 2, { SwitchCase: 1 }] + ## Enforce the consistent use of either double or single quotes in JSX + ## attributes + jsx-quotes: [error, prefer-double] + ## Enforce consistent spacing between keys and values in object literal + ## properties + key-spacing: [error, { beforeColon: false, afterColon: true }] + ## Enforce consistent spacing before and after keywords + keyword-spacing: [error, { before: true, after: true }] + ## Enforce position of line comments + # line-comment-position: error + ## Enforce consistent linebreak style + # linebreak-style: error + ## Require empty lines around comments + # lines-around-comment: error + ## Require or disallow an empty line between class members + # lines-between-class-members: error + ## Enforce a maximum depth that blocks can be nested + # max-depth: error + ## Enforce a maximum line length + max-len: [error, { + code: 80, + ## Ignore imports + ignorePattern: '^(import\s.+\sfrom\s|.*require\()', + ignoreUrls: true, + ignoreRegExpLiterals: true, + }] + ## Enforce a maximum number of lines per file + # max-lines: error + ## Enforce a maximum number of line of code in a function + # max-lines-per-function: error + ## Enforce a maximum depth that callbacks can be nested + # max-nested-callbacks: error + ## Enforce a maximum number of parameters in function definitions + # max-params: error + ## Enforce a maximum number of statements allowed in function blocks + # max-statements: error + ## Enforce a maximum number of statements allowed per line + # max-statements-per-line: error + ## Enforce a particular style for multiline comments + # multiline-comment-style: error + ## Enforce newlines between operands of ternary expressions + multiline-ternary: [error, always-multiline] + ## Require constructor names to begin with a capital letter + # new-cap: error + ## Enforce or disallow parentheses when invoking a constructor with no + ## arguments + # new-parens: error + ## Require a newline after each call in a method chain + # newline-per-chained-call: error + ## Disallow Array constructors + # no-array-constructor: error + ## Disallow bitwise operators + # no-bitwise: error + ## Disallow continue statements + # no-continue: error + ## Disallow inline comments after code + # no-inline-comments: error + ## Disallow if statements as the only statement in else blocks + # no-lonely-if: error + ## Disallow mixed binary operators + # no-mixed-operators: error + ## Disallow mixed spaces and tabs for indentation + no-mixed-spaces-and-tabs: error + ## Disallow use of chained assignment expressions + # no-multi-assign: error + ## Disallow multiple empty lines + # no-multiple-empty-lines: error + ## Disallow negated conditions + # no-negated-condition: error + ## Disallow nested ternary expressions + # no-nested-ternary: error + ## Disallow Object constructors + # no-new-object: error + ## Disallow the unary operators ++ and -- + # no-plusplus: error + ## Disallow specified syntax + # no-restricted-syntax: error + ## Disallow all tabs + # no-tabs: error + ## Disallow ternary operators + # no-ternary: error + ## Disallow trailing whitespace at the end of lines + # no-trailing-spaces: error + ## Disallow dangling underscores in identifiers + # no-underscore-dangle: error + ## Disallow ternary operators when simpler alternatives exist + # no-unneeded-ternary: error + ## Disallow whitespace before properties + no-whitespace-before-property: error + ## Enforce the location of single-line statements + # nonblock-statement-body-position: error + ## Enforce consistent line breaks inside braces + # object-curly-newline: [error, { multiline: true }] + ## Enforce consistent spacing inside braces + object-curly-spacing: [error, always] + ## Enforce placing object properties on separate lines + # object-property-newline: error + ## Enforce variables to be declared either together or separately in + ## functions + # one-var: error + ## Require or disallow newlines around variable declarations + # one-var-declaration-per-line: error + ## Require or disallow assignment operator shorthand where possible + # operator-assignment: error + ## Enforce consistent linebreak style for operators + operator-linebreak: [error, before] + ## Require or disallow padding within blocks + # padded-blocks: error + ## Require or disallow padding lines between statements + # padding-line-between-statements: error + ## Disallow using Object.assign with an object literal as the first + ## argument and prefer the use of object spread instead. + # prefer-object-spread: error + ## Require quotes around object literal property names + # quote-props: error + ## Enforce the consistent use of either backticks, double, or single quotes + # quotes: [error, single] + ## Require or disallow semicolons instead of ASI + semi: error + ## Enforce consistent spacing before and after semicolons + semi-spacing: [error, { before: false, after: true }] + ## Enforce location of semicolons + semi-style: [error, last] + ## Require object keys to be sorted + # sort-keys: error + ## Require variables within the same declaration block to be sorted + # sort-vars: error + ## Enforce consistent spacing before blocks + space-before-blocks: [error, always] + ## Enforce consistent spacing before function definition opening parenthesis + space-before-function-paren: [error, { + anonymous: always, + named: never, + asyncArrow: always, + }] + ## Enforce consistent spacing inside parentheses + space-in-parens: [error, never] + ## Require spacing around infix operators + # space-infix-ops: error + ## Enforce consistent spacing before or after unary operators + # space-unary-ops: error + ## Enforce consistent spacing after the // or /* in a comment + spaced-comment: [error, always] + ## Enforce spacing around colons of switch statements + switch-colon-spacing: [error, { before: false, after: true }] + ## Require or disallow spacing between template tags and their literals + template-tag-spacing: [error, never] + ## Require or disallow Unicode byte order mark (BOM) + # unicode-bom: [error, never] + ## Require parenthesis around regex literals + # wrap-regex: error + + ## ES6 + ## ---------------------------------------- + ## Require braces around arrow function bodies + # arrow-body-style: error + ## Require parentheses around arrow function arguments + arrow-parens: [error, as-needed] + ## Enforce consistent spacing before and after the arrow in arrow functions + arrow-spacing: [error, { before: true, after: true }] + ## Require super() calls in constructors + # constructor-super: error + ## Enforce consistent spacing around * operators in generator functions + generator-star-spacing: [error, { before: false, after: true }] + ## Disallow reassigning class members + no-class-assign: error + ## Disallow arrow functions where they could be confused with comparisons + # no-confusing-arrow: error + ## Disallow reassigning const variables + no-const-assign: error + ## Disallow duplicate class members + no-dupe-class-members: error + ## Disallow duplicate module imports + # no-duplicate-imports: error + ## Disallow new operators with the Symbol object + no-new-symbol: error + ## Disallow specified modules when loaded by import + # no-restricted-imports: error + ## Disallow this/super before calling super() in constructors + no-this-before-super: error + ## Disallow unnecessary computed property keys in object literals + # no-useless-computed-key: error + ## Disallow unnecessary constructors + # no-useless-constructor: error + ## Disallow renaming import, export, and destructured assignments to the + ## same name + # no-useless-rename: error + ## Require let or const instead of var + no-var: error + ## Require or disallow method and property shorthand syntax for object + ## literals + # object-shorthand: error + ## Require using arrow functions for callbacks + prefer-arrow-callback: error + ## Require const declarations for variables that are never reassigned after + ## declared + # prefer-const: error + ## Require destructuring from arrays and/or objects + # prefer-destructuring: error + ## Disallow parseInt() and Number.parseInt() in favor of binary, octal, and + ## hexadecimal literals + # prefer-numeric-literals: error + ## Require rest parameters instead of arguments + # prefer-rest-params: error + ## Require spread operators instead of .apply() + # prefer-spread: error + ## Require template literals instead of string concatenation + # prefer-template: error + ## Require generator functions to contain yield + # require-yield: error + ## Enforce spacing between rest and spread operators and their expressions + # rest-spread-spacing: error + ## Enforce sorted import declarations within modules + # sort-imports: error + ## Require symbol descriptions + # symbol-description: error + ## Require or disallow spacing around embedded expressions of template + ## strings + # template-curly-spacing: error + ## Require or disallow spacing around the * in yield* expressions + yield-star-spacing: [error, { before: false, after: true }] + + ## React + ## ---------------------------------------- + ## Enforces consistent naming for boolean props + react/boolean-prop-naming: error + ## Forbid "button" element without an explicit "type" attribute + react/button-has-type: error + ## Prevent extraneous defaultProps on components + react/default-props-match-prop-types: error + ## Rule enforces consistent usage of destructuring assignment in component + # react/destructuring-assignment: [error, always, { ignoreClassFields: true }] + ## Prevent missing displayName in a React component definition + react/display-name: error + ## Forbid certain props on Components + # react/forbid-component-props: error + ## Forbid certain props on DOM Nodes + # react/forbid-dom-props: error + ## Forbid certain elements + # react/forbid-elements: error + ## Forbid certain propTypes + # react/forbid-prop-types: error + ## Forbid foreign propTypes + # react/forbid-foreign-prop-types: error + ## Prevent using this.state inside this.setState + react/no-access-state-in-setstate: error + ## Prevent using Array index in key props + # react/no-array-index-key: error + ## Prevent passing children as props + react/no-children-prop: error + ## Prevent usage of dangerous JSX properties + react/no-danger: error + ## Prevent problem with children and props.dangerouslySetInnerHTML + react/no-danger-with-children: error + ## Prevent usage of deprecated methods, including component lifecycle + ## methods + react/no-deprecated: error + ## Prevent usage of setState in componentDidMount + react/no-did-mount-set-state: error + ## Prevent usage of setState in componentDidUpdate + react/no-did-update-set-state: error + ## Prevent direct mutation of this.state + react/no-direct-mutation-state: error + ## Prevent usage of findDOMNode + react/no-find-dom-node: error + ## Prevent usage of isMounted + react/no-is-mounted: error + ## Prevent multiple component definition per file + # react/no-multi-comp: error + ## Prevent usage of shouldComponentUpdate when extending React.PureComponent + react/no-redundant-should-component-update: error + ## Prevent usage of the return value of React.render + react/no-render-return-value: error + ## Prevent usage of setState + # react/no-set-state: error + ## Prevent common casing typos + react/no-typos: error + ## Prevent using string references in ref attribute. + react/no-string-refs: error + ## Prevent using this in stateless functional components + react/no-this-in-sfc: error + ## Prevent invalid characters from appearing in markup + react/no-unescaped-entities: error + ## Prevent usage of unknown DOM property (fixable) + # react/no-unknown-property: error + ## Prevent usage of unsafe lifecycle methods + react/no-unsafe: error + ## Prevent definitions of unused prop types + react/no-unused-prop-types: error + ## Prevent definitions of unused state properties + react/no-unused-state: error + ## Prevent usage of setState in componentWillUpdate + react/no-will-update-set-state: error + ## Enforce ES5 or ES6 class for React Components + react/prefer-es6-class: error + ## Enforce that props are read-only + react/prefer-read-only-props: error + ## Enforce stateless React Components to be written as a pure function + react/prefer-stateless-function: error + ## Prevent missing props validation in a React component definition + # react/prop-types: error + ## Prevent missing React when using JSX + # react/react-in-jsx-scope: error + ## Enforce a defaultProps definition for every prop that is not a required + ## prop + # react/require-default-props: error + ## Enforce React components to have a shouldComponentUpdate method + # react/require-optimization: error + ## Enforce ES5 or ES6 class for returning value in render function + react/require-render-return: error + ## Prevent extra closing tags for components without children (fixable) + react/self-closing-comp: error + ## Enforce component methods order (fixable) + # react/sort-comp: error + ## Enforce propTypes declarations alphabetical sorting + # react/sort-prop-types: error + ## Enforce the state initialization style to be either in a constructor or + ## with a class property + react/state-in-constructor: error + ## Enforces where React component static properties should be positioned. + # react/static-property-placement: error + ## Enforce style prop value being an object + react/style-prop-object: error + ## Prevent void DOM elements (e.g. ,
) from receiving children + react/void-dom-elements-no-children: error + + ## JSX-specific rules + ## ---------------------------------------- + ## Enforce boolean attributes notation in JSX (fixable) + react/jsx-boolean-value: error + ## Enforce or disallow spaces inside of curly braces in JSX attributes and + ## expressions. + # react/jsx-child-element-spacing: error + ## Validate closing bracket location in JSX (fixable) + react/jsx-closing-bracket-location: [error, { + ## NOTE: Not really sure about enforcing this one + selfClosing: false, + nonEmpty: after-props, + }] + ## Validate closing tag location in JSX (fixable) + react/jsx-closing-tag-location: error + ## Enforce or disallow newlines inside of curly braces in JSX attributes and + ## expressions (fixable) + react/jsx-curly-newline: error + ## Enforce or disallow spaces inside of curly braces in JSX attributes and + ## expressions (fixable) + react/jsx-curly-spacing: error + ## Enforce or disallow spaces around equal signs in JSX attributes (fixable) + react/jsx-equals-spacing: error + ## Restrict file extensions that may contain JSX + # react/jsx-filename-extension: error + ## Enforce position of the first prop in JSX (fixable) + # react/jsx-first-prop-new-line: error + ## Enforce event handler naming conventions in JSX + react/jsx-handler-names: error + ## Validate JSX indentation (fixable) + react/jsx-indent: [error, 2, { + checkAttributes: true, + }] + ## Validate props indentation in JSX (fixable) + react/jsx-indent-props: [error, 2] + ## Validate JSX has key prop when in array or iterator + react/jsx-key: error + ## Validate JSX maximum depth + react/jsx-max-depth: [error, { max: 10 }] ## Generous + ## Limit maximum of props on a single line in JSX (fixable) + # react/jsx-max-props-per-line: error + ## Prevent usage of .bind() and arrow functions in JSX props + # react/jsx-no-bind: error + ## Prevent comments from being inserted as text nodes + react/jsx-no-comment-textnodes: error + ## Prevent duplicate props in JSX + react/jsx-no-duplicate-props: error + ## Prevent usage of unwrapped JSX strings + # react/jsx-no-literals: error + ## Prevent usage of unsafe target='_blank' + react/jsx-no-target-blank: error + ## Disallow undeclared variables in JSX + react/jsx-no-undef: error + ## Disallow unnecessary fragments (fixable) + react/jsx-no-useless-fragment: error + ## Limit to one expression per line in JSX + # react/jsx-one-expression-per-line: error + ## Enforce curly braces or disallow unnecessary curly braces in JSX + # react/jsx-curly-brace-presence: error + ## Enforce shorthand or standard form for React fragments + react/jsx-fragments: error + ## Enforce PascalCase for user-defined JSX components + react/jsx-pascal-case: error + ## Disallow multiple spaces between inline JSX props (fixable) + react/jsx-props-no-multi-spaces: error + ## Disallow JSX props spreading + # react/jsx-props-no-spreading: error + ## Enforce default props alphabetical sorting + # react/jsx-sort-default-props: error + ## Enforce props alphabetical sorting (fixable) + # react/jsx-sort-props: error + ## Validate whitespace in and around the JSX opening and closing brackets + ## (fixable) + react/jsx-tag-spacing: error + ## Prevent React to be incorrectly marked as unused + react/jsx-uses-react: error + ## Prevent variables used in JSX to be incorrectly marked as unused + react/jsx-uses-vars: error + ## Prevent missing parentheses around multilines JSX (fixable) + react/jsx-wrap-multilines: error diff --git a/tgui/.gitattributes b/tgui/.gitattributes new file mode 100644 index 00000000000..0016cc3bf67 --- /dev/null +++ b/tgui/.gitattributes @@ -0,0 +1,10 @@ +* text=auto + +## Enforce text mode and LF line breaks +*.js text eol=lf +*.css text eol=lf +*.html text eol=lf +*.json text eol=lf + +## Treat bundles as binary and ignore them during conflicts +*.bundle.* binary merge=tgui-merge-bundle diff --git a/tgui/.gitignore b/tgui/.gitignore new file mode 100644 index 00000000000..416ca3768da --- /dev/null +++ b/tgui/.gitignore @@ -0,0 +1,7 @@ +node_modules +*.log +package-lock.json + +/packages/tgui/public/.tmp/**/* +/packages/tgui/public/**/*.hot-update.* +/packages/tgui/public/**/*.map diff --git a/tgui/README.md b/tgui/README.md new file mode 100644 index 00000000000..5ddeb18fdda --- /dev/null +++ b/tgui/README.md @@ -0,0 +1,187 @@ +# tgui + +## Introduction + +tgui is a robust user interface framework of /tg/station. + +tgui is very different from most UIs you will encounter in BYOND programming. +It is heavily reliant on Javascript and web technologies as opposed to DM. +If you are familiar with NanoUI (a library which can be found on almost +every other SS13 codebase), tgui should be fairly easy to pick up. + +## Learn tgui + +People come to tgui from different backgrounds and with different +learning styles. Whether you prefer a more theoretical or a practical +approach, we hope you’ll find this section helpful. + +### Practical Tutorial + +If you are completely new to frontend and prefer to **learn by doing**, +start with our [practical tutorial](docs/tutorial-and-examples.md). + +### Guides + +This project uses **Inferno** - a very fast UI rendering engine with a similar +API to React. Take your time to read these guides: + +- [React guide](https://reactjs.org/docs/hello-world.html) +- [Inferno documentation](https://infernojs.org/docs/guides/components) - +highlights differences with React. + +If you were already familiar with an older, Ractive-based tgui, and want +to translate concepts between old and new tgui, read this +[interface conversion guide](docs/converting-old-tgui-interfaces.md). + +## Pre-requisites + +You will need these programs to start developing in tgui: + +- [Node v12.13+](https://nodejs.org/en/download/) +- [Yarn v1.19+](https://yarnpkg.com/en/docs/install) +- [MSys2](https://www.msys2.org/) (optional) + +> MSys2 closely replicates a unix-like environment which is necessary for +> the `bin/tgui` script to run. It comes with a robust "mintty" terminal +> emulator which is better than any standard Windows shell, it supports +> "git" out of the box (almost like Git for Windows, but better), has +> a "pacman" package manager, and you can install a text editor like "vim" +> for a full boomer experience. + +## Usage + +**For MSys2, Git Bash, WSL, Linux or macOS users:** + +First and foremost, change your directory to `tgui`. + +Run `bin/tgui --install-git-hooks` (optional) to install merge drivers +which will assist you in conflict resolution when rebasing your branches. + +Run one of the following: + +- `bin/tgui` - build the project in production mode. +- `bin/tgui --dev` - launch a development server. + - tgui development server provides you with incremental compilation, + hot module replacement and logging facilities in all running instances + of tgui. In short, this means that you will instantly see changes in the + game as you code it. Very useful, highly recommended. + - In order to use it, you should start the game server first, connect to it + and wait until the world has been properly loaded and you are no longer + in the lobby. Start tgui dev server. You'll know that it's hooked correctly + if data gets dumped to the log when tgui windows are opened. +- `bin/tgui --dev --reload` - reload byond cache once. +- `bin/tgui --dev --debug` - run server with debug logging enabled. +- `bin/tgui --dev --no-hot` - disable hot module replacement (helps when +doing development on IE8). +- `bin/tgui --lint` - show problems with the code. +- `bin/tgui --lint --fix` - auto-fix problems with the code. +- `bin/tgui --analyze` - run a bundle analyzer. +- `bin/tgui --clean` - clean up project repo. +- `bin/tgui [webpack options]` - build the project with custom webpack +options. + +**For everyone else:** + +If you haven't opened the console already, you can do that by holding +Shift and right clicking on the `tgui` folder, then pressing +either `Open command window here` or `Open PowerShell window here`. + +Run `yarn install` to install npm dependencies, then one of the following: + +- `yarn run build` - build the project in production mode. +- `yarn run watch` - launch a development server. +- `yarn run lint` - show problems with the code. +- `yarn run lint --fix` - auto-fix problems with the code. +- `yarn run analyze` - run a bundle analyzer. + +We also got some batch files in store, for those who don't like fiddling +with the console: + +- `bin/tgui-build.bat` - build the project in production mode. +- `bin/tgui-dev-server.bat` - launch a development server. + +> Remember to always run a full build before submitting a PR. It creates +> a compressed javascript bundle which is then referenced from DM code. +> We prefer to keep it version controlled, so that people could build the +> game just by using Dream Maker. + +## Troubleshooting + +**Development server doesn't find my BYOND cache!** + +This happens if your Documents folder in Windows has a custom location, for +example in `E:\Libraries\Documents`. Development server has no knowledge +of these non-standard locations, therefore you have to run the dev server +with an additional environmental variable, with a full path to BYOND cache. + +``` +export BYOND_CACHE="E:/Libraries/Documents/BYOND/cache" +bin/tgui --dev +``` + +Note that in Windows, you have to go through Advanced System Settings, +System Properties and then open Environment Variables window to do the +same thing. You may need to reboot after this. + +## Developer Tools + +When developing with `tgui-dev-server`, you will have access to certain +development only features. + +**Debug Logs.** +When running server via `bin/tgui --dev --debug`, server will print debug +logs and time spent on rendering. Use this information to optimize your +code, and try to keep re-renders below 16ms. + +**Kitchen Sink.** +Press `Ctrl+Alt+=` to open the KitchenSink interface. This interface is a +playground to test various tgui components. + +**Layout Debugger.** +Press `Ctrl+Alt+-` to toggle the *layout debugger*. It will show outlines of +all tgui elements, which makes it easy to understand how everything comes +together, and can reveal certain layout bugs which are not normally visible. + +## Project Structure + +- `/packages` - Each folder here represents a self-contained Node module. +- `/packages/common` - Helper functions +- `/packages/tgui/index.js` - Application entry point. +- `/packages/tgui/components` - Basic UI building blocks. +- `/packages/tgui/interfaces` - Actual in-game interfaces. +Interface takes data via the `state` prop and outputs an html-like stucture, +which you can build using existing UI components. +- `/packages/tgui/layouts` - Root level UI components, that affect the final +look and feel of the browser window. They usually hold various window +elements, like the titlebar and resize handlers, and control the UI theme. +- `/packages/tgui/routes.js` - This is where tgui decides which interface to +pull and render. +- `/packages/tgui/layout.js` - A root-level component, holding the +window elements, like the titlebar, buttons, resize handlers. Calls +`routes.js` to decide which component to render. +- `/packages/tgui/styles/main.scss` - CSS entry point. +- `/packages/tgui/styles/functions.scss` - Useful SASS functions. +Stuff like `lighten`, `darken`, `luminance` are defined here. +- `/packages/tgui/styles/atomic` - Atomic CSS classes. +These are very simple, tiny, reusable CSS classes which you can use and +combine to change appearance of your elements. Keep them small. +- `/packages/tgui/styles/components` - CSS classes which are used +in UI components. These stylesheets closely follow the +[BEM](https://en.bem.info/methodology/) methodology. +- `/packages/tgui/styles/interfaces` - Custom stylesheets for your interfaces. +Add stylesheets here if you really need a fine control over your UI styles. +- `/packages/tgui/styles/layouts` - Layout-related styles. +- `/packages/tgui/styles/themes` - Contains all the various themes you can +use in tgui. Each theme must be registered in `webpack.config.js` file. + +## Component Reference + +See: [Component Reference](docs/component-reference.md). + +## License + +All code is licensed with the parent license of *tgstation*, **AGPL-3.0**. + +See the main [README](../README.md) for more details. + +The Authors retain all copyright to their respective work here submitted. diff --git a/tgui/bin/tgui b/tgui/bin/tgui new file mode 100755 index 00000000000..eb1f200b31e --- /dev/null +++ b/tgui/bin/tgui @@ -0,0 +1,173 @@ +#!/bin/bash +set -e +shopt -s globstar +shopt -s expand_aliases + +## Initial set-up +## -------------------------------------------------------- + +## Returns an absolute path to file +alias tgui-realpath="readlink -f" + +## Fallbacks for GNU readlink +## Detecting GNU coreutils http://stackoverflow.com/a/8748344/319952 +if ! readlink --version >/dev/null 2>&1; then + if hash greadlink 2>/dev/null; then + alias tgui-realpath="greadlink -f" + else + alias tgui-realpath="perl -MCwd -le 'print Cwd::abs_path(shift)'" + fi +fi + +## Find a canonical path to project root +base_dir="$(dirname "$(tgui-realpath "${0}")")/.." +base_dir="$(tgui-realpath "${base_dir}")" + +## Add locally installed node programs to path +PATH="${PATH}:node_modules/.bin" + + +## Functions +## -------------------------------------------------------- + +## Installs node modules +task-install() { + cd "${base_dir}" + yarn install +} + +## Runs webpack +task-webpack() { + cd "${base_dir}/packages/tgui" + webpack "${@}" +} + +## Runs a development server +task-dev-server() { + cd "${base_dir}/packages/tgui-dev-server" + exec node --experimental-modules index.js "${@}" +} + +## Run a linter through all packages +task-eslint() { + cd "${base_dir}" + eslint ./packages "${@}" +} + +## Mr. Proper +task-clean() { + cd "${base_dir}" + rm -rf packages/tgui/public/.tmp + rm -rf **/node_modules + rm -f **/package-lock.json +} + +## Validates current build against the build stored in git +task-validate-build() { + cd "${base_dir}" + local diff + diff="$(git diff packages/tgui/public/tgui.bundle.*)" + if [[ -n ${diff} ]]; then + echo "Error: our build differs from the build committed into git." + echo "Please rebuild tgui." + exit 1 + fi + echo "tgui: build is ok" +} + +## Installs merge drivers and git hooks +task-install-git-hooks() { + cd "${base_dir}" + local git_root + local git_base_dir + git_root="$(git rev-parse --show-toplevel)" + git_base_dir="${base_dir/${git_root}/.}" + git config --replace-all merge.tgui-merge-bundle.driver \ + "${git_base_dir}/bin/tgui --merge=bundle %O %A %B %L" + echo "tgui: Merge drivers have been successfully installed!" +} + +## Bundle merge driver +task-merge-bundle() { + local file_ancestor="${1}" + local file_current="${2}" + local file_other="${3}" + local conflict_marker_size="${4}" + echo "tgui: Discarding a local tgui build" + ## Do nothing (file_current will be merged and is what we want to keep). + exit 0 +} + + +## Main +## -------------------------------------------------------- + +if [[ ${1} == "--merge"* ]]; then + if [[ ${1} == "--merge=bundle" ]]; then + shift 1 + task-merge-bundle "${@}" + fi + echo "Unknown merge strategy: ${1}" + exit 1 +fi + +if [[ ${1} == "--install-git-hooks" ]]; then + shift 1 + task-install-git-hooks + exit 0 +fi + +## Continuous integration scenario +if [[ ${1} == "--ci" ]]; then + task-clean + task-install + task-eslint + task-webpack --mode=production + task-validate-build + exit 0 +fi + +if [[ ${1} == "--clean" ]]; then + task-clean + exit 0 +fi + +if [[ ${1} == "--dev" ]]; then + shift + task-install + task-dev-server "${@}" + exit 0 +fi + +if [[ ${1} == '--lint' ]]; then + shift 1 + task-install + task-eslint "${@}" + exit 0 +fi + +if [[ ${1} == '--lint-harder' ]]; then + shift 1 + task-install + task-eslint -c .eslintrc-harder.yml "${@}" + exit 0 +fi + +## Analyze the bundle +if [[ ${1} == '--analyze' ]]; then + task-install + task-webpack --mode=production --analyze + exit 0 +fi + +## Make a production webpack build +if [[ -z ${1} ]]; then + task-install + task-eslint + task-webpack --mode=production + exit 0 +fi + +## Run webpack with custom flags +task-install +task-webpack "${@}" diff --git a/tgui/bin/tgui-build.bat b/tgui/bin/tgui-build.bat new file mode 100644 index 00000000000..89e1aca9152 --- /dev/null +++ b/tgui/bin/tgui-build.bat @@ -0,0 +1,5 @@ +@echo off +cd "%~dp0\.." +call yarn install +call yarn run build +timeout /t 9 diff --git a/tgui/bin/tgui-dev-server.bat b/tgui/bin/tgui-dev-server.bat new file mode 100644 index 00000000000..1b5bdcfb1db --- /dev/null +++ b/tgui/bin/tgui-dev-server.bat @@ -0,0 +1,4 @@ +@echo off +cd "%~dp0\.." +call yarn install +call yarn run watch diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md new file mode 100644 index 00000000000..b853fee4de3 --- /dev/null +++ b/tgui/docs/component-reference.md @@ -0,0 +1,1000 @@ +# Component Reference + +> Notice: This documentation might be out of date, so always check the source +> code to see the most up-to-date information. + + + +- [General Concepts](#general-concepts) +- [`tgui/components`](#tguicomponents) + - [`AnimatedNumber`](#animatednumber) + - [`BlockQuote`](#blockquote) + - [`Box`](#box) + - [`Button`](#button) + - [`Button.Checkbox`](#buttoncheckbox) + - [`Button.Confirm`](#buttonconfirm) + - [`Button.Input`](#buttoninput) + - [`ByondUi`](#byondui) + - [`Collapsible`](#collapsible) + - [`ColorBox`](#colorbox) + - [`Dimmer`](#dimmer) + - [`Divider`](#divider) + - [`Dropdown`](#dropdown) + - [`Flex`](#flex) + - [`Flex.Item`](#flexitem) + - [`Grid`](#grid) + - [`Grid.Column`](#gridcolumn) + - [`Icon`](#icon) + - [`Input`](#input) + - [`Knob`](#knob) + - [`LabeledControls`](#labeledcontrols) + - [`LabeledControls.Item`](#labeledcontrolsitem) + - [`LabeledList`](#labeledlist) + - [`LabeledList.Item`](#labeledlistitem) + - [`LabeledList.Divider`](#labeledlistdivider) + - [`Modal`](#modal) + - [`NoticeBox`](#noticebox) + - [`NumberInput`](#numberinput) + - [`ProgressBar`](#progressbar) + - [`Section`](#section) + - [`Slider`](#slider) + - [`Table`](#table) + - [`Table.Row`](#tablerow) + - [`Table.Cell`](#tablecell) + - [`Tabs`](#tabs) + - [`Tabs.Tab`](#tabstab) + - [`Tooltip`](#tooltip) +- [`tgui/layouts`](#tguilayouts) + - [`Window`](#window) + - [`Window.Content`](#windowcontent) + +## General Concepts + +These are the components which you can use for interface construction. +If you have trouble finding the exact prop you need on a component, +please note, that most of these components inherit from other basic +components, such as [Box](#box). This component in particular provides a lot +of styling options for all components, e.g. `color` and `opacity`, thus +it is used a lot in this framework. + +**Event handlers.** +Event handlers are callbacks that you can attack to various element to +listen for browser events. Inferno supports camelcase (`onClick`) and +lowercase (`onclick`) event names. + +- Camel case names are what's called *synthetic* events, and are the +**preferred way** of handling events in React, for efficiency and +performance reasons. Please read +[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) +to understand what this is about. +- Lower case names are native browser events and should be used sparingly, +for example when you need an explicit IE8 support. **DO NOT** use +lowercase event handlers unless you really know what you are doing. +- [Button](#button) component does not support the lowercase `onclick` event. +Use the camel case `onClick` instead. + +## `tgui/components` + +### `AnimatedNumber` + +This component provides animations for numeric values. + +**Props:** + +- `value: number` - Value to animate. +- `initial: number` - Initial value to use in animation when element +first appears. If you set initial to `0` for example, number will always +animate starting from `0`, and if omitted, it will not play an initial +animation. +- `format: value => value` - Output formatter. + - Example: `value => Math.round(value)`. +- `children: (formattedValue, rawValue) => any` - Pull the animated number to +animate more complex things deeper in the DOM tree. + - Example: `(_, value) => ` + +### `BlockQuote` + +Just a block quote, just like this example in markdown: + +> Here's an example of a block quote. + +**Props:** + +- See inherited props: [Box](#box) + +### `Box` + +The Box component serves as a wrapper component for most of the CSS utility +needs. It creates a new DOM element, a `
` 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 ( +