diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 22ac1eeeda2..3890e02e0c9 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -10196,8 +10196,14 @@
},
/obj/structure/table/reinforced,
/obj/item/clothing/gloves/color/orange,
-/obj/item/weapon/reagent_containers/spray/cleaner,
-/obj/item/weapon/reagent_containers/spray/cleaner,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_y = 2
+ },
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_y = 2
+ },
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
tag = "icon-intact (SOUTHEAST)";
icon_state = "intact";
@@ -32365,7 +32371,16 @@
},
/obj/item/clothing/gloves/color/latex,
/obj/item/device/healthanalyzer,
-/obj/item/weapon/reagent_containers/spray,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_x = -3;
+ pixel_y = 2
+ },
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = 0;
+ pixel_x = 5;
+ pixel_y = -1
+ },
/turf/open/floor/plasteel/whitered/side{
dir = 8
},
@@ -34092,9 +34107,7 @@
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"bjL" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable/white{
@@ -34112,9 +34125,7 @@
/turf/open/floor/plasteel{
tag = "icon-plasteel_warn_side (EAST)"
},
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"bjM" = (
/obj/structure/cable/white{
tag = "icon-0-4";
@@ -34882,9 +34893,7 @@
dir = 8
},
/turf/open/floor/plasteel,
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"blg" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable/white{
@@ -34899,9 +34908,7 @@
icon_state = "brown";
dir = 8
},
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"blh" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/dirt,
@@ -34916,9 +34923,7 @@
},
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"bli" = (
/turf/open/floor/plating,
/area/quartermaster/miningdock{
@@ -34929,9 +34934,7 @@
/obj/effect/decal/cleanable/dirt,
/obj/structure/reagent_dispensers/watertank,
/turf/open/floor/plating,
-/area/quartermaster/miningdock{
- name = "\improper Mining Office"
- })
+/area/maintenance/starboard/fore_starboard_maintenance)
"blk" = (
/obj/structure/cable/white,
/obj/structure/grille,
@@ -46640,7 +46643,7 @@
dir = 8
},
/turf/open/floor/plating,
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bER" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable/white{
@@ -46650,7 +46653,7 @@
/turf/open/floor/plasteel/neutral/side{
dir = 1
},
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bES" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable/white{
@@ -46658,7 +46661,7 @@
icon_state = "4-8"
},
/turf/open/floor/plating,
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bET" = (
/obj/structure/cable/white{
tag = "icon-2-8";
@@ -46670,7 +46673,7 @@
},
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bEU" = (
/obj/structure/cable/white{
tag = "icon-4-8";
@@ -46690,7 +46693,7 @@
initial_gas_mix = "o2=22;n2=82;TEMP=293.15";
tag = "icon-neutral (NORTHEAST)"
},
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bEW" = (
/obj/structure/table/wood,
/obj/item/weapon/clipboard,
@@ -47697,7 +47700,7 @@
tag = "icon-plating_warn_side (EAST)";
icon_state = "plating_warn_side"
},
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bGN" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable/white{
@@ -47705,7 +47708,7 @@
icon_state = "1-4"
},
/turf/open/floor/plating,
-/area/hallway/primary/central)
+/area/maintenance/starboard)
"bGO" = (
/obj/machinery/door/airlock/maintenance_hatch{
name = "Detective's Office Maintenance";
@@ -83406,7 +83409,11 @@
/obj/item/clothing/neck/stethoscope,
/obj/item/clothing/glasses/hud/health,
/obj/item/clothing/glasses/hud/health,
-/obj/item/weapon/reagent_containers/spray/cleaner,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_x = -3;
+ pixel_y = 2
+ },
/obj/machinery/status_display{
pixel_x = 32
},
@@ -87260,7 +87267,11 @@
"cXU" = (
/obj/structure/table/wood,
/obj/item/weapon/folder/white,
-/obj/item/weapon/reagent_containers/spray/cleaner,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_x = -3;
+ pixel_y = 2
+ },
/turf/open/floor/plasteel/neutral/corner{
dir = 8
},
@@ -88553,7 +88564,11 @@
/obj/structure/table/reinforced,
/obj/machinery/door/firedoor,
/obj/item/weapon/folder/white,
-/obj/item/weapon/reagent_containers/spray/cleaner,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_x = -3;
+ pixel_y = 2
+ },
/obj/effect/turf_decal/delivery,
/turf/open/floor/plasteel,
/area/medical/medbay{
@@ -112854,7 +112869,11 @@
/obj/item/weapon/book/manual/wiki/infections,
/obj/item/weapon/reagent_containers/syringe/antiviral,
/obj/item/weapon/reagent_containers/dropper,
-/obj/item/weapon/reagent_containers/spray/cleaner,
+/obj/item/weapon/reagent_containers/spray/cleaner{
+ pixel_x = -3;
+ pixel_x = -3;
+ pixel_y = 2
+ },
/obj/effect/turf_decal/stripes/line{
dir = 4
},
@@ -118566,6 +118585,14 @@
tag = "icon-plasteel_warn_side (WEST)"
},
/area/medical/virology)
+"edh" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/open/floor/plasteel/brown{
+ tag = "icon-brown (NORTH)";
+ icon_state = "brown";
+ dir = 1
+ },
+/area/maintenance/starboard/fore_starboard_maintenance)
(1,1,1) = {"
aaa
@@ -162620,9 +162647,9 @@ bcU
bes
bgg
bid
-aXb
+aeX
blf
-aXb
+aeX
boZ
boZ
brQ
@@ -162632,9 +162659,9 @@ bvW
bxR
boZ
boZ
-boZ
+cmX
bEQ
-boZ
+cmX
bIG
bIG
bIG
@@ -162879,7 +162906,7 @@ bgh
bie
bjK
blg
-aXb
+aeX
aaa
aaf
aaa
@@ -162889,9 +162916,9 @@ bvX
aaa
aaf
aaa
-boZ
+cmX
bER
-boZ
+cmX
bIH
bKL
bML
@@ -163136,7 +163163,7 @@ bgi
bif
bjL
blh
-aXb
+aeX
aaa
bqw
bqw
@@ -163146,9 +163173,9 @@ bqw
bqw
bqw
aaa
-boZ
+cmX
bES
-boZ
+cmX
bII
bKM
bMM
@@ -163391,9 +163418,9 @@ bcW
bev
bgj
big
-aXb
-bbO
-aXb
+aeX
+afn
+aeX
aaf
bqw
brR
@@ -163403,7 +163430,7 @@ btj
bxS
bqw
aaf
-boZ
+cmX
bET
bGM
bIJ
@@ -163648,9 +163675,9 @@ bcX
bew
bgk
aXb
-aXb
-bli
-aXb
+aeX
+amC
+aeX
aaa
bqw
brS
@@ -163660,9 +163687,9 @@ bti
bxT
bqw
aaa
-boZ
+cmX
bER
-boZ
+cmX
bIK
bKO
bMO
@@ -163905,9 +163932,9 @@ bcX
bex
bgj
bih
-aXb
-bar
-aXb
+aeX
+edh
+aeX
aaa
bqw
brT
@@ -163917,9 +163944,9 @@ bvY
bxU
bqw
aaa
-boZ
-bEU
-boZ
+cmX
+coB
+cmX
bIL
bKP
bMP
@@ -164162,9 +164189,9 @@ bcY
bbF
bgl
bii
-aXb
+aeX
blj
-aXb
+aeX
aaa
bqw
brU
@@ -164174,9 +164201,9 @@ bti
bxV
bqw
aaa
-boZ
-bEU
-boZ
+cmX
+coB
+cmX
bIG
bIG
bIG
@@ -164419,9 +164446,9 @@ bbF
bbF
bgm
bij
-aXb
-aXb
-aXb
+aeX
+aeX
+aeX
aaf
bqw
brV
@@ -164431,7 +164458,7 @@ btj
brR
bqw
aaf
-boZ
+cmX
bEV
bGN
bIM
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 72b06ea7c67..7e7abb3c14d 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -46647,10 +46647,7 @@
/turf/open/floor/plasteel,
/area/atmos)
"bAL" = (
-/obj/machinery/atmospherics/components/unary/thermomachine/heater{
- dir = 2;
- on = 1
- },
+/obj/machinery/atmospherics/components/unary/thermomachine/freezer,
/turf/open/floor/plasteel,
/area/atmos)
"bAM" = (
@@ -47562,11 +47559,11 @@
/turf/open/floor/plasteel,
/area/atmos)
"bCt" = (
+/obj/machinery/meter,
/obj/machinery/atmospherics/pipe/manifold/yellow/visible{
color = "purple";
dir = 1
},
-/obj/machinery/meter,
/turf/open/floor/plasteel,
/area/atmos)
"bCu" = (
@@ -94442,6 +94439,13 @@
/obj/machinery/holopad,
/turf/open/floor/plasteel/cafeteria,
/area/crew_quarters/kitchen)
+"ddF" = (
+/obj/machinery/atmospherics/pipe/simple/yellow/visible{
+ color = "purple";
+ dir = 5
+ },
+/turf/open/floor/plasteel,
+/area/atmos)
(1,1,1) = {"
aaa
@@ -137490,7 +137494,7 @@ bvv
bxi
bzc
bAL
-bCs
+ddF
bDX
bFR
bHx
diff --git a/code/__DEFINES/clockcult.dm b/code/__DEFINES/clockcult.dm
index 8b140cb8b7c..b3b491de4fa 100644
--- a/code/__DEFINES/clockcult.dm
+++ b/code/__DEFINES/clockcult.dm
@@ -93,3 +93,5 @@ var/global/list/all_scripture = list() //a list containing scripture instances;
#define SIGIL_ACCESS_RANGE 2 //range at which transmission sigils can access power
#define PROSELYTIZER_REPAIR_PER_TICK 4 //how much a proselytizer repairs each tick, and also how many deciseconds each tick is
+
+#define OCULAR_WARDEN_EXCLUSION_RANGE 3 //the range at which ocular wardens cannot be placed near other ocular wardens
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index fa040bf866b..8ec5bc755ad 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -19,6 +19,9 @@
#define WIRE_LAYER 2.4
#define WIRE_TERMINAL_LAYER 2.45
#define LOW_OBJ_LAYER 2.5
+#define LOW_SIGIL_LAYER 2.52
+#define SIGIL_LAYER 2.54
+#define HIGH_SIGIL_LAYER 2.56
#define BELOW_OPEN_DOOR_LAYER 2.6
#define OPEN_DOOR_LAYER 2.7
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index d4acb7f4b49..535e7a1010a 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -346,7 +346,7 @@ var/global/list/ghost_others_options = list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
//debug printing macros
#define debug_world(msg) if (Debug2) world << "DEBUG: [msg]"
#define debug_admins(msg) if (Debug2) admins << "DEBUG: [msg]"
-#define debug_world_log(msg) if (Debug2) world.log << "DEBUG: [msg]"
+#define debug_world_log(msg) if (Debug2) log_world("DEBUG: [msg]")
#define COORD(A) "([A.x],[A.y],[A.z])"
#define INCREMENT_TALLY(L, stat) if(L[stat]){L[stat]++}else{L[stat] = 1}
@@ -409,3 +409,6 @@ var/global/list/ghost_others_options = list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define TURF_DECAL_PAINT "paint"
#define TURF_DECAL_DAMAGE "damage"
#define TURF_DECAL_DIRT "dirt"
+
+//Error handler defines
+#define ERROR_USEFUL_LEN 2
\ No newline at end of file
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 9b89a666c88..fe5852c3b3e 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -1,16 +1,18 @@
//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].")
/proc/warning(msg)
- world.log << "## WARNING: [msg]"
+ msg = "## WARNING: [msg]"
+ log_world(msg)
//not an error or a warning, but worth to mention on the world log, just in case.
#define NOTICE(MSG) notice(MSG)
/proc/notice(msg)
- world.log << "## NOTICE: [msg]"
+ msg = "## NOTICE: [msg]"
+ log_world(msg)
//print a testing-mode debug message to world.log and world
#ifdef TESTING
-#define testing(msg) world.log << "## TESTING: [msg]"; world << "## TESTING: [msg]"
+#define testing(msg) log_world("## TESTING: [msg]"); world << "## TESTING: [msg]"
#else
#define testing(msg)
#endif
@@ -79,4 +81,31 @@
/proc/log_chat(text)
if (config.log_pda)
- diary << "\[[time_stamp()]]CHAT: [text]"
\ No newline at end of file
+ diary << "\[[time_stamp()]]CHAT: [text]"
+
+//This replaces world.log so it displays both in DD and the file
+/proc/log_world(text)
+ if(config && config.log_runtimes)
+ world.log = runtime_diary
+ world.log << text
+ world.log = null
+ world.log << text
+
+// Helper procs for building detailed log lines
+
+/proc/datum_info_line(datum/D)
+ if(!istype(D))
+ return
+ if(!istype(D, /mob))
+ return "[D] ([D.type])"
+ var/mob/M = D
+ return "[M] ([M.ckey]) ([M.type])"
+
+/proc/atom_loc_line(atom/A)
+ if(!istype(A))
+ return
+ var/turf/T = get_turf(A)
+ if(istype(T))
+ return "[A.loc] [COORD(T)] ([A.loc.type])"
+ else if(A.loc)
+ return "[A.loc] (0, 0, 0) ([A.loc.type])"
\ No newline at end of file
diff --git a/code/__HELPERS/bandetect.dm b/code/__HELPERS/bandetect.dm
index 61698c54d8a..f7887d32615 100644
--- a/code/__HELPERS/bandetect.dm
+++ b/code/__HELPERS/bandetect.dm
@@ -5,7 +5,7 @@
var/DBQuery/query = dbcon.NewQuery("SELECT DATEDIFF(Now(),'[y]-[m]-[d]')")
if(!query.Execute())
- world.log << "SQL ERROR doing datediff. Error : \[[query.ErrorMsg()]\]\n"
+ log_world("SQL ERROR doing datediff. Error : \[[query.ErrorMsg()]\]\n")
return FALSE
if(query.NextRow())
@@ -24,7 +24,7 @@
/client/proc/findJoinDate()
var/http[] = world.Export("http://byond.com/members/[src.ckey]?format=text")
if(!http)
- world.log << "Failed to connect to byond age check for [src.ckey]"
+ log_world("Failed to connect to byond age check for [src.ckey]")
return FALSE
var/F = file2text(http["CONTENT"])
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 6603a191db1..7a8103f8120 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -420,10 +420,12 @@
return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
-/proc/showCandidatePollWindow(mob/dead/observer/G, poll_time, Question, list/candidates, ignore_category, time_passed)
+/proc/showCandidatePollWindow(mob/dead/observer/G, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
set waitfor = 0
G << 'sound/misc/notice2.ogg' //Alerting them to their consideration
+ if(flashwindow)
+ window_flash(G.client)
switch(ignore_category ? askuser(G,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(G,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time))
if(1)
G << "Choice registered: Yes."
@@ -441,7 +443,7 @@
poll_ignore[ignore_category] += G.ckey
G << "Choice registered: Never for this round."
-/proc/pollCandidates(var/Question, var/jobbanType, var/datum/game_mode/gametypeCheck, var/be_special_flag = 0, var/poll_time = 300, var/ignore_category = null)
+/proc/pollCandidates(var/Question, var/jobbanType, var/datum/game_mode/gametypeCheck, var/be_special_flag = 0, var/poll_time = 300, var/ignore_category = null, flashwindow = TRUE)
var/list/mob/dead/observer/candidates = list()
var/time_passed = world.time
if (!Question)
@@ -460,7 +462,7 @@
if(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate"))
continue
- showCandidatePollWindow(G, poll_time, Question, candidates, ignore_category, time_passed)
+ showCandidatePollWindow(G, poll_time, Question, candidates, ignore_category, time_passed, flashwindow)
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off during the wait period.
@@ -507,7 +509,11 @@
if(M && !isnewplayer(M))
M << thing
-/proc/window_flash(var/client_or_usr)
- if (!client_or_usr)
+/proc/window_flash(client/C, ignorepref = FALSE)
+ if(ismob(C))
+ var/mob/M = C
+ if(M.client)
+ C = M.client
+ if(!C || (!C.prefs.windowflashing && !ignorepref))
return
- winset(client_or_usr, "mainwindow", "flash=5")
+ winset(C, "mainwindow", "flash=5")
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index 22ff1c2bef9..5e65dfd5251 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -1,6 +1,6 @@
//Returns the world time in english
/proc/worldtime2text()
- return gameTimestamp("hh:mm")
+ return gameTimestamp("hh:mm:ss")
/proc/time_stamp(format = "hh:mm:ss")
return time2text(world.timeofday, format)
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 81a49a7719b..5a3919ad148 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -1,4 +1,5 @@
var/diary = null
+var/runtime_diary = null
var/diaryofmeanpeople = null
var/href_logfile = null
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 0a2dd748dc0..380f017666c 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -42,6 +42,7 @@
var/log_hrefs = 0 // log all links clicked in-game. Could be used for debugging and tracking down exploits
var/log_twitter = 0 // log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
var/log_world_topic = 0 // log all world.Topic() calls
+ var/log_runtimes = FALSE // log runtimes into a file
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/allow_vote_restart = 0 // allow votes to restart
@@ -241,6 +242,11 @@
var/minutetopiclimit
var/secondtopiclimit
+ var/error_cooldown = 600 // The "cooldown" time for each occurrence of a unique error
+ var/error_limit = 50 // How many occurrences before the next will silence them
+ var/error_silence_time = 6000 // How long a unique error will be silenced for
+ var/error_msg_delay = 50 // How long to wait between messaging admins about occurrences of a unique error
+
/datum/configuration/New()
gamemode_cache = typecacheof(/datum/game_mode,TRUE)
for(var/T in gamemode_cache)
@@ -459,10 +465,11 @@
if("aggressive_changelog")
config.aggressive_changelog = 1
if("log_runtimes")
+ log_runtimes = TRUE
var/newlog = file("data/logs/runtimes/runtime-[time2text(world.realtime, "YYYY-MM-DD")].log")
- if (world.log != newlog)
+ if(runtime_diary != newlog)
world.log << "Now logging runtimes to data/logs/runtimes/runtime-[time2text(world.realtime, "YYYY-MM-DD")].log"
- world.log = newlog
+ runtime_diary = newlog
if("autoconvert_notes")
config.autoconvert_notes = 1
if("allow_webclient")
@@ -495,6 +502,14 @@
config.minutetopiclimit = text2num(value)
if("second_topic_limit")
config.secondtopiclimit = text2num(value)
+ if("error_cooldown")
+ error_cooldown = text2num(value)
+ if("error_limit")
+ error_limit = text2num(value)
+ if("error_silence_time")
+ error_silence_time = text2num(value)
+ if("error_msg_delay")
+ error_msg_delay = text2num(value)
else
diary << "Unknown setting in configuration: '[name]'"
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index dd88760e5fe..f17997074d2 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -104,7 +104,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
msg += "\t [varname] = [D]([D.type])\n"
else
msg += "\t [varname] = [varval]\n"
- world.log << msg
+ log_world(msg)
if (istype(Master.subsystems))
subsystems = Master.subsystems
StartProcessing(10)
@@ -139,7 +139,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
world << "Initializations complete!"
- world.log << "Initializations complete."
+ log_world("Initializations complete.")
// Sort subsystems by display setting for easy access.
sortTim(subsystems, /proc/cmp_subsystem_display)
@@ -267,7 +267,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
subsystems_to_check = tickersubsystems
if (CheckQueue(subsystems_to_check) <= 0)
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
- world.log << "MC: SoftReset() failed, crashing"
+ log_world("MC: SoftReset() failed, crashing")
return
if (!error_level)
iteration++
@@ -279,7 +279,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
if (queue_head)
if (RunQueue() <= 0)
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
- world.log << "MC: SoftReset() failed, crashing"
+ log_world("MC: SoftReset() failed, crashing")
return
if (!error_level)
iteration++
@@ -451,9 +451,9 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
// called if any mc's queue procs runtime or exit improperly.
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/normal_SS, list/lobby_SS)
. = 0
- world.log << "MC: SoftReset called, resetting MC queue state."
+ log_world("MC: SoftReset called, resetting MC queue state.")
if (!istype(subsystems) || !istype(ticker_SS) || !istype(normal_SS) || !istype(lobby_SS))
- world.log << "MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[normal_SS]' '[lobby_SS]' Crashing!"
+ log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[normal_SS]' '[lobby_SS]' Crashing!")
return
var/subsystemstocheck = subsystems + ticker_SS + normal_SS + lobby_SS
@@ -465,26 +465,26 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
ticker_SS -= list(SS)
normal_SS -= list(SS)
lobby_SS -= list(SS)
- world.log << "MC: SoftReset: Found bad entry in subsystem list, '[SS]'"
+ log_world("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
continue
if (SS.queue_next && !istype(SS.queue_next))
- world.log << "MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'"
+ log_world("MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
SS.queue_next = null
if (SS.queue_prev && !istype(SS.queue_prev))
- world.log << "MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'"
+ log_world("MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
SS.queue_prev = null
SS.queued_priority = 0
SS.queued_time = 0
SS.state = SS_IDLE
if (queue_head && !istype(queue_head))
- world.log << "MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'"
+ log_world("MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
queue_head = null
if (queue_tail && !istype(queue_tail))
- world.log << "MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'"
+ log_world("MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
queue_tail = null
queue_priority_count = 0
queue_priority_count_bg = 0
- world.log << "MC: SoftReset: Finished."
+ log_world("MC: SoftReset: Finished.")
. = 1
@@ -493,6 +493,6 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
if(!statclick)
statclick = new/obj/effect/statclick/debug("Initializing...", src)
-
- stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (TickDrift:[round(Master.tickdrift)]) (Iteration:[Master.iteration])"))
+ stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
+ stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index f616218bd23..5a63283012e 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -157,7 +157,7 @@
var/time = (world.timeofday - start_timeofday) / 10
var/msg = "Initialized [name] subsystem within [time] seconds!"
world << "[msg]"
- world.log << msg
+ log_world(msg)
return time
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index 65dff21ef9c..1c75307ac2c 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -228,7 +228,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
/datum/var/gc_destroyed //Time when this object was destroyed.
#ifdef TESTING
-/datum/var/running_find_references
+/datum/var/running_find_references
/datum/var/last_find_references = 0
/datum/verb/find_refs()
@@ -337,7 +337,7 @@ var/datum/subsystem/garbage_collector/SSgarbage
else
DoSearchVar(variable, "[Xname]: [varname]")
#endif
- else if(islist(X))
+ else if(islist(X))
if(src in X)
testing("Found [src.type] \ref[src] in list [Xname].")
#ifdef GC_FAILURE_HARD_LOOKUP
@@ -351,7 +351,6 @@ var/datum/subsystem/garbage_collector/SSgarbage
//update this list using tools/DMTreeToGlobalsList
/datum/proc/find_references_in_globals()
SearchVar(last_irc_status)
- SearchVar(inerror)
SearchVar(failed_db_connections)
SearchVar(nextmap)
SearchVar(mapchanging)
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index 802eb0e5162..5c706e62e66 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -374,10 +374,10 @@ var/datum/subsystem/job/SSjob
S = sloc
break
if(!S) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
- world.log << "Couldn't find a round start spawn point for [rank]"
+ log_world("Couldn't find a round start spawn point for [rank]")
S = pick(latejoin)
if(!S) //final attempt, lets find some area in the arrivals shuttle to spawn them in to.
- world.log << "Couldn't find a round start latejoin spawn point."
+ log_world("Couldn't find a round start latejoin spawn point.")
for(var/turf/T in get_area_turfs(/area/shuttle/arrival))
if(!T.density)
var/clear = 1
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index 7065fff925f..f15f04f15be 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -116,4 +116,4 @@ var/datum/subsystem/lighting/SSlighting
varval1 = "/list([length(varval1)])"
varval2 = "/list([length(varval2)])"
msg += "\t [varname] = [varval1] -> [varval2]\n"
- world.log << msg
+ log_world(msg)
diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm
index 6170185cede..24ae4a4d0c6 100644
--- a/code/controllers/subsystem/minimap.dm
+++ b/code/controllers/subsystem/minimap.dm
@@ -45,7 +45,7 @@ var/datum/subsystem/minimap/SSminimap
for(var/z in z_levels)
if(!fexists(file(map_path(z,backup)))) //Let's make sure we have a file for this map
if(backup)
- world.log << "Failed to find backup file for map [MAP_NAME] on zlevel [z]."
+ log_world("Failed to find backup file for map [MAP_NAME] on zlevel [z].")
return FALSE
return TRUE
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index bb3f399bb37..03b49d3340d 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -75,7 +75,7 @@ var/datum/subsystem/ticker/ticker
world << "Please set up your character and select \"Ready\". The game will start in [config.lobby_countdown] seconds."
current_state = GAME_STATE_PREGAME
for(var/client/C in clients)
- window_flash(C) //let them know lobby has opened up.
+ window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
if(GAME_STATE_PREGAME)
//lobby stats for statpanels
@@ -556,7 +556,7 @@ var/datum/subsystem/ticker/ticker
for(var/path in SSgarbage.sleptDestroy)
dellog += "Path : [path] \n"
dellog += "Sleeps : [SSgarbage.sleptDestroy[path]] \n"
- world.log << dellog
+ log_world(dellog)
CHECK_TICK
diff --git a/code/controllers/subsystem/time_tracking.dm b/code/controllers/subsystem/time_tracking.dm
new file mode 100644
index 00000000000..df87d9d125e
--- /dev/null
+++ b/code/controllers/subsystem/time_tracking.dm
@@ -0,0 +1,43 @@
+var/datum/subsystem/time_track/SStime_track
+
+/datum/subsystem/time_track
+ name = "Time Tracking"
+ wait = 600
+ flags = SS_NO_INIT|SS_FIRE_IN_LOBBY
+
+ var/time_dilation_current = 0
+
+ var/time_dilation_avg_fast = 0
+ var/time_dilation_avg = 0
+ var/time_dilation_avg_slow = 0
+
+ var/first_run = TRUE
+
+ var/last_tick_realtime = 0
+ var/last_tick_byond_time = 0
+ var/last_tick_tickcount = 0
+
+/datum/subsystem/time_track/New()
+ NEW_SS_GLOBAL(SStime_track)
+
+/datum/subsystem/time_track/fire()
+
+ var/current_realtime = REALTIMEOFDAY
+ var/current_byondtime = world.time
+ var/current_tickcount = world.time/world.tick_lag
+
+ if (!first_run)
+ var/tick_drift = max(0, (((current_realtime - last_tick_realtime) - (current_byondtime - last_tick_byond_time)) / world.tick_lag))
+
+ time_dilation_current = tick_drift / (current_tickcount - last_tick_tickcount) * 100
+
+ time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current)
+ time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast)
+ time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg)
+ else
+ first_run = FALSE
+ last_tick_realtime = current_realtime
+ last_tick_byond_time = current_byondtime
+ last_tick_tickcount = current_tickcount
+
+
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index d178e444274..667e68db4bc 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -627,7 +627,7 @@
if (prompt != "Yes")
return
L.Cut(index, index+1)
- world.log << "### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]"
+ log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]")
log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]")
message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]")
@@ -646,7 +646,7 @@
return
uniqueList_inplace(L)
- world.log << "### ListVarEdit by [src]: /list contents: CLEAR DUPES"
+ log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES")
log_admin("[key_name(src)] modified list's contents: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES")
@@ -657,7 +657,7 @@
return
listclearnulls(L)
- world.log << "### ListVarEdit by [src]: /list contents: CLEAR NULLS"
+ log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS")
log_admin("[key_name(src)] modified list's contents: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS")
@@ -671,7 +671,7 @@
return
L.len = value["value"]
- world.log << "### ListVarEdit by [src]: /list len: [L.len]"
+ log_world("### ListVarEdit by [src]: /list len: [L.len]")
log_admin("[key_name(src)] modified list's len: [L.len]")
message_admins("[key_name_admin(src)] modified list's len: [L.len]")
@@ -682,7 +682,7 @@
return
shuffle_inplace(L)
- world.log << "### ListVarEdit by [src]: /list contents: SHUFFLE"
+ log_world("### ListVarEdit by [src]: /list contents: SHUFFLE")
log_admin("[key_name(src)] modified list's contents: SHUFFLE")
message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE")
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 0569a5c30f2..c131187648d 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -19,17 +19,17 @@ var/global/datum/getrev/revdata = new()
parentcommit = head_log.group[1]
date = unix2date(text2num(head_log.group[2]))
commit = head_log.group[4]
- world.log << "Running /tg/ revision:"
- world.log << "[date]"
+ log_world("Running /tg/ revision:")
+ log_world("[date]")
if(testmerge.len)
- world.log << commit
+ log_world(commit)
for(var/line in testmerge)
if(line)
- world.log << "Test merge active of PR #[line]"
- world.log << "Based off master commit [parentcommit]"
+ log_world("Test merge active of PR #[line]")
+ log_world("Based off master commit [parentcommit]")
else
- world.log << parentcommit
- world.log << "Current map - [MAP_NAME]" //can't think of anywhere better to put it
+ log_world(parentcommit)
+ log_world("Current map - [MAP_NAME]") //can't think of anywhere better to put it
/client/verb/showrevinfo()
set category = "OOC"
@@ -75,7 +75,7 @@ var/global/datum/getrev/revdata = new()
if(config.probabilities[ctag] > 0)
var/percentage = round(config.probabilities[ctag] / prob_sum * 100, 0.1)
src << "[ctag] [percentage]%"
-
+
src <<"All Game Mode Odds:"
var/sum = 0
for(var/ctag in config.probabilities)
diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm
index 1912be6a279..9bdf5c34eaa 100644
--- a/code/datums/helper_datums/map_template.dm
+++ b/code/datums/helper_datums/map_template.dm
@@ -75,7 +75,7 @@
. = file(mappath)
if(!.)
- world.log << "The file of [src] appears to be empty/non-existent."
+ log_world("The file of [src] appears to be empty/non-existent.")
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
var/turf/placement = T
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index 931eb89320f..8eef4736e65 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -4,10 +4,10 @@
/area/CATEGORY/OR/DESCRIPTOR/NAME (you can make as many subdivisions as you want)
name = "NICE NAME" (not required but makes things really nice)
- icon = "ICON FILENAME" (defaults to areas.dmi)
+ icon = 'ICON FILENAME' (defaults to 'icons/turf/areas.dmi')
icon_state = "NAME OF ICON" (defaults to "unknown" (blank))
requires_power = 0 (defaults to 1)
- music = "music/music.ogg" (defaults to "music/music.ogg")
+ music = null (defaults to nothing, look in sound/ambience for music)
NOTE: there are two lists of areas in the end of this file: centcom and station itself. Please maintain these lists valid. --rastaf0
diff --git a/code/game/area/areas/away_content.dm b/code/game/area/areas/away_content.dm
index 6c80a47299e..abe44af5fee 100644
--- a/code/game/area/areas/away_content.dm
+++ b/code/game/area/areas/away_content.dm
@@ -126,32 +126,32 @@
/area/awaymission/centcomAway/general
name = "XCC-P5831"
- music = "music/ambigen3.ogg"
+ music = 'sound/ambience/ambigen3.ogg'
/area/awaymission/centcomAway/maint
name = "XCC-P5831 Maintenance"
icon_state = "away1"
- music = "music/ambisin1.ogg"
+ music = 'sound/ambience/ambisin1.ogg'
/area/awaymission/centcomAway/thunderdome
name = "XCC-P5831 Thunderdome"
icon_state = "away2"
- music = "music/ambisin2.ogg"
+ music = 'sound/ambience/ambisin2.ogg'
/area/awaymission/centcomAway/cafe
name = "XCC-P5831 Kitchen Arena"
icon_state = "away3"
- music = "music/ambisin3.ogg"
+ music = 'sound/ambience/ambisin3.ogg'
/area/awaymission/centcomAway/courtroom
name = "XCC-P5831 Courtroom"
icon_state = "away4"
- music = "music/ambisin4.ogg"
+ music = 'sound/ambience/ambisin4.ogg'
/area/awaymission/centcomAway/hangar
name = "XCC-P5831 Hangars"
icon_state = "away4"
- music = "music/ambigen5.ogg"
+ music = 'sound/ambience/ambigen5.ogg'
/*Cabin areas*/
diff --git a/code/game/area/areas/misc.dm b/code/game/area/areas/misc.dm
index 6a4951d4826..6ca11df629f 100644
--- a/code/game/area/areas/misc.dm
+++ b/code/game/area/areas/misc.dm
@@ -5,7 +5,6 @@
//icon = "ICON FILENAME"
//icon_state = "NAME OF ICON"
requires_power = 0
- music = "music/music.ogg"
/area/hell/trial1
name = "Hell Trial1"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 1b7b966d94e..03d6e9412d7 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -141,7 +141,6 @@
if(destination)
if(pulledby)
pulledby.stop_pulling()
-
var/atom/oldloc = loc
var/same_loc = oldloc == destination
var/area/old_area = get_area(oldloc)
@@ -159,10 +158,10 @@
if(destarea && old_area != destarea)
destarea.Entered(src, oldloc)
- for(var/atom/movable/AM in destination)
- if(AM == src)
- continue
- AM.Crossed(src)
+ for(var/atom/movable/AM in destination)
+ if(AM == src)
+ continue
+ AM.Crossed(src)
Moved(oldloc, 0)
return 1
diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
index 42c4a418c70..4add155d627 100644
--- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
+++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
@@ -51,8 +51,10 @@
name = "dull sigil"
desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground."
icon = 'icons/effects/clockwork_effects.dmi'
- clockwork_desc = "A sigil that will stun the first non-servant to cross it. Nar-Sie's dogs will be knocked down."
+ clockwork_desc = "A sigil that will stun the next non-Servant to cross it."
icon_state = "sigildull"
+ layer = HIGH_SIGIL_LAYER
+ alpha = 60
color = "#FAE48C"
sigil_name = "Sigil of Transgression"
@@ -78,8 +80,9 @@
desc = "A luminous golden sigil. Something about it really bothers you."
clockwork_desc = "A sigil that will enslave the first person to cross it, provided they remain on it for seven seconds."
icon_state = "sigilsubmission"
- color = "#FAE48C"
+ layer = LOW_SIGIL_LAYER
alpha = 125
+ color = "#FAE48C"
stat_affected = UNCONSCIOUS
resist_string = "glows faintly yellow"
var/convert_time = 70
@@ -148,8 +151,8 @@
clockwork_desc = "A sigil that will enslave any person who crosses it, provided they remain on it for seven seconds. \n\
It can convert a mindshielded target once before disppearing, but can convert any number of non-implanted targets."
icon_state = "sigiltransgression"
- color = "#A97F1B"
alpha = 200
+ color = "#A97F1B"
glow_light = 4 //bright light
glow_falloff = 3
delete_on_finish = FALSE
@@ -172,8 +175,8 @@
desc = "A glowing orange sigil. The air around it feels staticky."
clockwork_desc = "A sigil that will serve as a battery for clockwork structures."
icon_state = "sigiltransmission"
- color = "#EC8A2D"
alpha = 50
+ color = "#EC8A2D"
resist_string = "glows faintly"
sigil_name = "Sigil of Transmission"
affects_servants = TRUE
@@ -189,7 +192,11 @@
/obj/effect/clockwork/sigil/transmission/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
- user << "It is storing [ratvar_awakens ? "INFINITY":"[power_charge]"]W of power."
+ var/structure_number = 0
+ for(var/obj/structure/destructible/clockwork/powered/P in range(SIGIL_ACCESS_RANGE, src))
+ structure_number++
+ user << "It is storing [ratvar_awakens ? "INFINITY":"[power_charge]"]W of power, \
+ and [structure_number] Clockwork Structure[structure_number == 1 ? "":"s"] [structure_number == 1 ? "is":"are"] in range."
if(iscyborg(user))
user << "You can recharge from the [sigil_name] by crossing it."
@@ -264,8 +271,9 @@
desc = "A faint blue sigil. Looking at it makes you feel protected."
clockwork_desc = "A sigil that will drain non-Servants that remain on it. Servants that remain on it will be healed if it has any vitality drained."
icon_state = "sigilvitality"
- color = "#123456"
+ layer = SIGIL_LAYER
alpha = 75
+ color = "#123456"
affects_servants = TRUE
stat_affected = DEAD
resist_string = "glows shimmering yellow"
diff --git a/code/game/gamemodes/clock_cult/clock_effects/general_markers.dm b/code/game/gamemodes/clock_cult/clock_effects/general_markers.dm
index d9a85bd0332..6eb176ad48a 100644
--- a/code/game/gamemodes/clock_cult/clock_effects/general_markers.dm
+++ b/code/game/gamemodes/clock_cult/clock_effects/general_markers.dm
@@ -27,7 +27,7 @@
name = "Nezbere, the Brass Eidolon"
desc = "A towering colossus clad in nigh-impenetrable brass armor. Its gaze is stern yet benevolent, even upon you."
clockwork_desc = "One of Ratvar's four generals. Nezbere is responsible for the design, testing, and creation of everything in Ratvar's domain, and his loyalty to Ratvar knows no bounds. \
- It is said that Ratvar once asked him to destroy the plans for a weapon Nezbere had made that could have harmed him and Nezbere responded by not only destroying the plans, \
+ It is said that Ratvar once asked him to destroy the plans for a weapon Nezbere had made that could have harmed him, and Nezbere responded by not only destroying the plans, \
but by taking his own life so that the device could never be replicated. Nezbere's zealotry is unmatched."
icon = 'icons/effects/340x428.dmi'
icon_state = "nezbere"
@@ -47,7 +47,7 @@
/obj/effect/clockwork/general_marker/nzcrentr
name = "Nzcrentr, the Eternal Thunderbolt"
- desc = "A terrifying spiked construct crackling with limitless energy."
+ desc = "A terrifying spiked construct crackling with perpetual lightning."
clockwork_desc = "One of Ratvar's four generals. Before becoming one of Ratvar's generals, Nzcrentr sook out any and all sentient life to slaughter it for sport. \
Nzcrentr was coerced by Ratvar into entering a shell constructed by Nezbere, ostensibly made to grant Nzcrentr more power. In reality, the shell was made to trap and control it. \
Nzcrentr now serves loyally, though even one of Nezbere's finest creations was not enough to totally eliminate its will."
diff --git a/code/game/gamemodes/clock_cult/clock_helpers/clock_powerdrain.dm b/code/game/gamemodes/clock_cult/clock_helpers/clock_powerdrain.dm
index bbd6a503fd2..1452eaa13c0 100644
--- a/code/game/gamemodes/clock_cult/clock_helpers/clock_powerdrain.dm
+++ b/code/game/gamemodes/clock_cult/clock_helpers/clock_powerdrain.dm
@@ -44,12 +44,14 @@
if((!clockcult_user || !is_servant_of_ratvar(src)) && cell && cell.charge)
. = min(cell.charge, 250)
cell.use(.)
- src << "ERROR: Power loss detected!"
+ if(prob(20))
+ src << "ERROR: Power loss detected!"
spark_system.start()
/obj/mecha/power_drain(clockcult_user)
if((!clockcult_user || !occupant || occupant && !is_servant_of_ratvar(occupant)) && cell && cell.charge)
. = min(cell.charge, 250)
cell.use(.)
- occupant_message("Power loss detected!")
+ if(prob(20))
+ occupant_message("Power loss detected!")
spark_system.start()
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
index 96417c80b9e..e8b47ded9bb 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm
@@ -176,13 +176,13 @@
/datum/clockwork_scripture/create_object/sigil_of_transmission
descname = "Structure Battery"
name = "Sigil of Transmission"
- desc = "Scribes a sigil beneath the invoker which stores power to power clockwork structures."
+ desc = "Places a sigil that stores energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
channel_time = 70
consumed_components = list(VANGUARD_COGWHEEL = 1, GEIS_CAPACITOR = 1, HIEROPHANT_ANSIBLE = 2)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transmission
- creator_message = "A sigil silently appears below you. It will automatically power clockwork structures adjecent to it."
+ creator_message = "A sigil silently appears below you. It will automatically power clockwork structures near it."
usage_tip = "Cyborgs can charge from this sigil by remaining over it for 5 seconds."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
@@ -194,7 +194,7 @@
//Interdiction Lens: Creates a powerful totem that disables radios and cameras and drains power into nearby sigils of transmission.
/datum/clockwork_scripture/create_object/interdiction_lens
- descname = "Structure, Disables Machinery"
+ descname = "Structure, Area Sabotage, Power Generator"
name = "Interdiction Lens"
desc = "Creates a clockwork totem that sabotages nearby machinery and funnels drained power into nearby Sigils of Transmission or the area's APC."
invocations = list("May this totem...", "...shroud the false suns!")
@@ -216,14 +216,14 @@
//Mending Motor: Creates a prism that will quickly heal mechanical servants/clockwork structures and consume power or replicant alloy.
/datum/clockwork_scripture/create_object/mending_motor
- descname = "Structure, Repairs Other Structures"
+ descname = "Powered Structure, Repairs Other Structures"
name = "Mending Motor"
- desc = "Creates a mechanized prism that will rapidly repair damage to clockwork creatures, converted cyborgs, and clockwork structures. Requires power to function."
+ desc = "Creates a mechanized prism that will rapidly repair damaged clockwork constructs, converted cyborgs, and clockwork structures."
invocations = list("May this prism...", "...mend our dents and scratches!")
channel_time = 80
consumed_components = list(VANGUARD_COGWHEEL = 3, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 1)
object_path = /obj/structure/destructible/clockwork/powered/mending_motor
- creator_message = "You form a mending motor, which will consume power to mend constructs and structures."
+ creator_message = "You form a mending motor, which will rapidly repair damaged clockwork constructs, converted cyborgs, and clockwork structures."
observer_message = "An onyx prism forms in midair and sprouts tendrils to support itself!"
invokers_required = 2
multiple_invokers_used = TRUE
@@ -238,9 +238,9 @@
//Mania Motor: Creates a malevolent transmitter that will broadcast the whispers of Sevtug into the minds of nearby nonservants, causing a variety of mental effects at a power cost.
/datum/clockwork_scripture/create_object/mania_motor
- descname = "Structure, Area Denial"
+ descname = "Powered Structure, Area Denial"
name = "Mania Motor"
- desc = "Creates a mania motor which will cause brain damage and hallucinations in nearby non-servant humans. It will also try to convert humans directly adjecent to the motor."
+ desc = "Creates a mania motor which will cause brain damage and hallucinations in nearby non-Servant humans. It will also try to convert humans directly adjecent to the motor."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
channel_time = 80
consumed_components = list(GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 1)
@@ -249,7 +249,7 @@
observer_message = "A two-pronged machine rises from the ground!"
invokers_required = 2
multiple_invokers_used = TRUE
- usage_tip = "Eligible human servants next to the motor will be converted at an additional power cost. It will also cure hallucinations and brain damage in nearby servants."
+ usage_tip = "Eligible non-Servant humans next to the motor will be converted at an additional power cost. It will also cure hallucinations and brain damage in nearby Servants."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = GEIS_CAPACITOR
@@ -260,9 +260,10 @@
//Tinkerer's Daemon: Creates an efficient machine that rapidly produces components at a power cost.
/datum/clockwork_scripture/create_object/tinkerers_daemon
- descname = "Structure, Component Generator"
+ descname = "Powered Structure, Component Generator"
name = "Tinkerer's Daemon"
- desc = "Creates a tinkerer's daemon which can rapidly collect components. It will only function if it has sufficient power, is outnumbered by servants by a ratio of 5:1, and there is at least one existing cache."
+ desc = "Creates a tinkerer's daemon which can rapidly collect components. It will only function if it has sufficient power, is outnumbered by Servants by a ratio of 5:1, \
+ and there is at least one existing cache."
invocations = list("May this generator...", "...collect Engine parts that yet hold greatness!")
channel_time = 80
consumed_components = list(BELLIGERENT_EYE = 1, GEIS_CAPACITOR = 1, REPLICANT_ALLOY = 3)
@@ -294,9 +295,9 @@
//Clockwork Obelisk: Creates a powerful obelisk that can be used to broadcast messages or open a gateway to any servant or clockwork obelisk at a power cost.
/datum/clockwork_scripture/create_object/clockwork_obelisk
- descname = "Structure, Teleportation Hub"
+ descname = "Powered Structure, Teleportation Hub"
name = "Clockwork Obelisk"
- desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living servant or clockwork obelisk."
+ desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
channel_time = 80
consumed_components = list(BELLIGERENT_EYE = 1, VANGUARD_COGWHEEL = 1, HIEROPHANT_ANSIBLE = 3)
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
index 0b2c9f9e97e..206514b8013 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_drivers.dm
@@ -253,14 +253,15 @@
/datum/clockwork_scripture/create_object/tinkerers_cache
descname = "Necessary Structure, Shares Components"
name = "Tinkerer's Cache"
- desc = "Forms a cache that can store an infinite amount of components. All caches are linked and will provide components to slabs."
+ desc = "Forms a cache that can store an infinite amount of components. All caches are linked and will provide components to slabs. \
+ Striking a cache with a slab will transfer that slab's components to the global cache."
invocations = list("Constructing...", "...a cache!")
channel_time = 50
consumed_components = list(BELLIGERENT_EYE = 0, VANGUARD_COGWHEEL = 0, GEIS_CAPACITOR = 0, REPLICANT_ALLOY = 1, HIEROPHANT_ANSIBLE = 0)
object_path = /obj/structure/destructible/clockwork/cache
creator_message = "You form a tinkerer's cache, which is capable of storing components, which will automatically be used by slabs."
observer_message = "A hollow brass spire rises and begins to blaze!"
- usage_tip = "Slabs will draw components from the global cache after the slab's own repositories, making caches very efficient."
+ usage_tip = "Slabs will draw components from the global cache after the slab's own repositories, making caches extremely useful."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = REPLICANT_ALLOY
@@ -299,17 +300,17 @@
/datum/clockwork_scripture/create_object/sigil_of_transgression
descname = "Trap, Stunning"
name = "Sigil of Transgression"
- desc = "Wards a tile with a sigil. The next person to cross the sigil will be smitten and unable to move. Nar-Sian cultists are stunned altogether."
- invocations = list("Divinity, dazzle...", "...those who tresspass here!")
+ desc = "Wards a tile with a sigil, which will stun the next non-Servant to cross it."
+ invocations = list("Divinity, smite...", "...those who tresspass here!")
channel_time = 50
consumed_components = list(HIEROPHANT_ANSIBLE = 1)
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/transgression
- creator_message = "A sigil silently appears below you. The next non-servant to cross it will be immobilized."
+ creator_message = "A sigil silently appears below you. The next non-Servant to cross it will be stunned."
usage_tip = "The sigil, while fairly powerful in its stun, does not induce muteness in its victim."
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 10
quickbind = TRUE
- quickbind_desc = "Creates a Sigil of Transgression, which will stun the first non-Servant to cross it."
+ quickbind_desc = "Creates a Sigil of Transgression, which will stun the next non-Servant to cross it."
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
index bd00ee6f70a..47271c06a51 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_revenant.dm
@@ -6,11 +6,11 @@
/datum/clockwork_scripture/invoke_inathneq
descname = "Area Invulnerability"
name = "Invoke Inath-neq, the Resonant Cogwheel"
- desc = "Taps the limitless power of Inath-neq, one of Ratvar's four generals. The benevolence of Inath-Neq will grant complete invulnerability to all servants in range for fifteen seconds."
+ desc = "Taps the limitless power of Inath-neq, one of Ratvar's four generals. The benevolence of Inath-Neq will grant complete invulnerability to all Servants in range for fifteen seconds."
invocations = list("I call upon you, Vanguard!!", "Let the Resonant Cogs turn once more!!", "Grant me and my allies the strength to vanquish our foes!!")
channel_time = 100
consumed_components = list(VANGUARD_COGWHEEL = 6, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 3)
- usage_tip = "Those affected by this scripture are only weak to things that outright destroy bodies, such as bombs or the singularity."
+ usage_tip = "Servants affected by this scripture are only weak to things that outright destroy bodies, such as bombs or the singularity."
tier = SCRIPTURE_REVENANT
primary_component = VANGUARD_COGWHEEL
sort_priority = 2
@@ -124,7 +124,7 @@
return FALSE
if(!slab.no_cost && ratvar_awakens)
invoker << "\"[text2ratvar("Our master is here already. You do not require my help, friend.")]\"\n\
- Nezbere will not grant his power while Ratvar's dwarfs his own!"
+ There is no need for Nezbere's assistance while Ratvar is risen!"
return FALSE
return TRUE
@@ -165,8 +165,8 @@
/datum/clockwork_scripture/invoke_nzcrentr
descname = "Area Lightning Blast"
name = "Invoke Nzcrentr, the Eternal Thunderbolt"
- desc = "Taps the limitless power of Nzcrentr, one of Ratvar's four generals. The immense energy Nzcrentr wields will allow you to imbue a tiny fraction of it into your body. After several \
- seconds, anyone near you will be struck by a devastating lightning bolt."
+ desc = "Taps the limitless power of Nzcrentr, one of Ratvar's four generals. Nzcrentr will grant you a tiny fraction of its boundless power. After several seconds, all non-Servants near you \
+ will be struck by devastating lightning bolts."
invocations = list("I call upon you, Amperage!!", "Let your energy flow through me!!", "Let your boundless power shatter stars!!")
channel_time = 100
consumed_components = list(BELLIGERENT_EYE = 3, GEIS_CAPACITOR = 3, REPLICANT_ALLOY = 3, HIEROPHANT_ANSIBLE = 6)
@@ -187,7 +187,7 @@
clockwork_generals_invoked["nzcrentr"] = world.time + CLOCKWORK_GENERAL_COOLDOWN
hierophant_message("[text2ratvar("Amperage: \"[invoker.real_name] has called forth my power. Hope [invoker.p_they()] [invoker.p_do()] not shatter under it!")]\"", FALSE, invoker)
invoker.visible_message("[invoker] begins to radiate a blinding light!", \
- "\"[text2ratvar("The boss says it's okay to do this. Don't blame me if you die from it.")]\"\n \
+ "\"[text2ratvar("The boss says it's okay to do this. Don't blame me if you die from it.")]\"\n\
You feel limitless power surging through you!")
playsound(invoker, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
sleep(2)
@@ -197,7 +197,7 @@
sleep(88)
if(invoker)
invoker.visible_message("Massive bolts of energy emerge from across [invoker]'s body!", \
- "\"[text2ratvar("I told you you wouldn't be able to handle it.")]\"\n \
+ "\"[text2ratvar("I told you you wouldn't be able to handle it.")]\"\n\
TOO... MUCH! CAN'T... TAKE IT!")
playsound(invoker, 'sound/magic/lightningbolt.ogg', 100, 0)
if(invoker.stat == CONSCIOUS)
diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
index 754abeefa93..051b24d6340 100644
--- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
+++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_scripts.dm
@@ -6,12 +6,12 @@
/datum/clockwork_scripture/create_object/ocular_warden
descname = "Structure, Turret"
name = "Ocular Warden"
- desc = "Forms an automatic short-range turret that deals low sustained damage to the unenlightened in its range."
+ desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians...", "...of the Engine...", "...defend us!")
channel_time = 120
consumed_components = list(BELLIGERENT_EYE = 1, REPLICANT_ALLOY = 1)
object_path = /obj/structure/destructible/clockwork/ocular_warden
- creator_message = "You form an ocular warden, which will focus its searing gaze upon nearby unenlightened."
+ creator_message = "You form an ocular warden, which will automatically attack nearby unrestrained non-Servants that can see it."
observer_message = "A brass eye takes shape and slowly rises into the air, its red iris glaring!"
usage_tip = "Although powerful, the warden is very fragile and should optimally be placed behind barricades."
tier = SCRIPTURE_SCRIPT
@@ -23,7 +23,7 @@
quickbind_desc = "Creates an Ocular Warden, which will automatically attack nearby unrestrained non-Servants that can see it."
/datum/clockwork_scripture/create_object/ocular_warden/check_special_requirements()
- for(var/obj/structure/destructible/clockwork/ocular_warden/W in range(3, invoker))
+ for(var/obj/structure/destructible/clockwork/ocular_warden/W in range(OCULAR_WARDEN_EXCLUSION_RANGE, invoker))
invoker << "You sense another ocular warden too near this location. Placing another this close would cause them to fight." //fluff message
return FALSE
return ..()
@@ -52,7 +52,7 @@
/datum/clockwork_scripture/create_object/vitality_matrix
descname = "Trap, Damage to Healing"
name = "Vitality Matrix"
- desc = "Scribes a sigil beneath the invoker which drains life from any living non-Servants that cross it. Servants that cross it, however, will be healed based on how much Vitality all \
+ desc = "Places a sigil that drains life from any living non-Servants that cross it. Servants that cross it, however, will be healed based on how much Vitality all \
Matrices have drained from non-Servants. Dead Servants can be revived by this sigil if there is vitality equal to the target Servant's non-oxygen damage."
invocations = list("Divinity...", "...steal their life...", "...for these shells!")
channel_time = 60
diff --git a/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm b/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
index 106c494707d..b483d5a043e 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/mania_motor.dm
@@ -8,8 +8,8 @@
inactive_icon = "mania_motor_inactive"
unanchored_icon = "mania_motor_unwrenched"
construction_value = 20
- max_integrity = 80
- obj_integrity = 80
+ max_integrity = 100
+ obj_integrity = 100
break_message = "The antenna break off, leaving a pile of shards!"
debris = list(/obj/item/clockwork/alloy_shards/large = 2, \
/obj/item/clockwork/alloy_shards/small = 2, \
@@ -62,30 +62,7 @@
var/turf/T = get_turf(src)
var/hum = get_sfx('sound/effects/screech.ogg') //like playsound, same sound for everyone affected
var/efficiency = get_efficiency_mod()
- for(var/mob/living/carbon/human/H in view(1, src))
- if(is_servant_of_ratvar(H) || H.null_rod_check() || H.stat == DEAD)
- continue
- if(!H.Adjacent(src))
- H << "\"[text2ratvar(pick(close_messages))]\""
- continue
- if(try_use_power(convert_cost))
- H.playsound_local(T, hum, 80, 1)
- if(!H.stat)
- if(H.getBrainLoss() < 100)
- H.adjustBrainLoss(30 * efficiency)
- H.visible_message("[H] reaches out and touches [src].", "You touch [src] involuntarily.")
- else
- H.Paralyse(3)
- else if(is_eligible_servant(H))
- H << "\"[text2ratvar("You are mine and his, now.")]\""
- add_servant_of_ratvar(H)
- H.Paralyse(5)
- else
- H << "\"[text2ratvar(pick(convert_messages))]\""
- else
- visible_message("[src]'s antennae fizzle quietly.")
- playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
- for(var/mob/living/carbon/human/H in range(10, src))
+ for(var/mob/living/carbon/human/H in viewers(7, src))
if(is_servant_of_ratvar(H)) //heals servants of braindamage, hallucination, druggy, dizziness, and confusion
var/brainloss = H.getBrainLoss()
if(brainloss)
@@ -98,14 +75,34 @@
H.dizziness = 0
if(H.confused)
H.confused = 0
- else if(!H.null_rod_check() && H.stat == CONSCIOUS)
+ else if(!H.null_rod_check() && H.stat != DEAD)
var/distance = 0 + get_dist(T, get_turf(H))
var/falloff_distance = min((110) - distance * 10, 80)
var/sound_distance = falloff_distance * 0.5
var/targetbrainloss = H.getBrainLoss()
- if(distance >= 4 && prob(falloff_distance * 0.5))
+ if(distance > 3 && prob(falloff_distance * 0.5))
H << "\"[text2ratvar(pick(mania_messages))]\""
- H.playsound_local(T, hum, sound_distance, 1)
+ if(distance <= 1)
+ if(!H.Adjacent(src))
+ H << "\"[text2ratvar(pick(close_messages))]\""
+ H.playsound_local(T, hum, sound_distance, 1)
+ else if(!try_use_power(convert_cost))
+ visible_message("[src]'s antennae fizzle quietly.")
+ playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
+ else
+ H.playsound_local(T, hum, 80, 1)
+ if(!H.stat)
+ if(H.getBrainLoss() < 100)
+ H.adjustBrainLoss(20 * efficiency)
+ H.visible_message("[H] reaches out and touches [src].", "You touch [src] involuntarily.")
+ else
+ H.Paralyse(3)
+ else if(is_eligible_servant(H))
+ H << "\"[text2ratvar("You are mine and his, now.")]\""
+ add_servant_of_ratvar(H)
+ H.Paralyse(5)
+ else
+ H.playsound_local(T, hum, sound_distance, 1)
switch(distance)
if(0 to 3)
if(prob(falloff_distance * 0.5))
@@ -113,31 +110,31 @@
H << "\"[text2ratvar(pick(mania_messages))]\""
else
H << "\"[text2ratvar(pick(compel_messages))]\""
- if(targetbrainloss <= 50)
- H.adjustBrainLoss((50 * efficiency) - targetbrainloss) //got too close had brain eaten
- H.adjust_drugginess(Clamp(7 * efficiency, 0, 100 - H.druggy))
- H.hallucination = min(H.hallucination + (7 * efficiency), 100)
- H.dizziness = min(H.dizziness + (3 * efficiency), 45)
- H.confused = min(H.confused + (3 * efficiency), 45)
- if(4 to 5)
- if(targetbrainloss <= 50)
- H.adjustBrainLoss(1 * efficiency)
- H.adjust_drugginess(Clamp(5 * efficiency, 0, 80 - H.druggy))
- H.hallucination = min(H.hallucination + (5 * efficiency), 80)
- H.dizziness = min(H.dizziness + (2 * efficiency), 30)
- H.confused = min(H.confused + (2 * efficiency), 30)
- if(6 to 7)
- if(targetbrainloss <= 30)
- H.adjustBrainLoss(1 * efficiency)
- H.adjust_drugginess(Clamp(2 * efficiency, 0, 60 - H.druggy))
- H.hallucination = min(H.hallucination + (2 * efficiency), 60)
- H.dizziness = min(H.dizziness + (2 * efficiency), 15)
- H.confused = min(H.confused + (2 * efficiency), 15)
- if(8 to 9)
+ if(targetbrainloss <= 40)
+ H.adjustBrainLoss(3 * efficiency)
+ H.adjust_drugginess(Clamp(7 * efficiency, 0, 50 - H.druggy))
+ H.hallucination = min(H.hallucination + (7 * efficiency), 50)
+ H.dizziness = min(H.dizziness + (3 * efficiency), 20)
+ H.confused = min(H.confused + (3 * efficiency), 20)
+ if(3 to 5)
+ if(targetbrainloss <= 20)
+ H.adjustBrainLoss(2 * efficiency)
+ H.adjust_drugginess(Clamp(5 * efficiency, 0, 25 - H.druggy))
+ H.hallucination = min(H.hallucination + (5 * efficiency), 25)
+ H.dizziness = min(H.dizziness + (2 * efficiency), 10)
+ H.confused = min(H.confused + (2 * efficiency), 10)
+ if(5 to 6)
if(targetbrainloss <= 10)
H.adjustBrainLoss(1 * efficiency)
- H.adjust_drugginess(Clamp(2 * efficiency, 0, 40 - H.druggy))
- H.hallucination = min(H.hallucination + (2 * efficiency), 40)
- if(10 to INFINITY)
H.adjust_drugginess(Clamp(2 * efficiency, 0, 20 - H.druggy))
H.hallucination = min(H.hallucination + (2 * efficiency), 20)
+ H.dizziness = min(H.dizziness + (2 * efficiency), 5)
+ H.confused = min(H.confused + (2 * efficiency), 5)
+ if(6 to 7)
+ if(targetbrainloss <= 5)
+ H.adjustBrainLoss(1 * efficiency)
+ H.adjust_drugginess(Clamp(2 * efficiency, 0, 10 - H.druggy))
+ H.hallucination = min(H.hallucination + (2 * efficiency), 10)
+ if(7 to INFINITY)
+ H.adjust_drugginess(Clamp(2 * efficiency, 0, 5 - H.druggy))
+ H.hallucination = min(H.hallucination + (2 * efficiency), 5)
diff --git a/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm b/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm
index f2768588c68..79115225b49 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm
@@ -2,7 +2,7 @@
/obj/structure/destructible/clockwork/ocular_warden
name = "ocular warden"
desc = "A large brass eye with tendrils trailing below it and a wide red iris."
- clockwork_desc = "A fragile turret that will deal sustained damage to any non-faithful it sees."
+ clockwork_desc = "A fragile turret which will automatically attack nearby unrestrained non-Servants that can see it."
icon_state = "ocular_warden"
unanchored_icon = "ocular_warden_unwrenched"
obj_integrity = 25
@@ -39,7 +39,7 @@
user << "[src] is too damaged to unsecure!"
return FAILED_UNFASTEN
else
- for(var/obj/structure/destructible/clockwork/ocular_warden/W in orange(3, src))
+ for(var/obj/structure/destructible/clockwork/ocular_warden/W in orange(OCULAR_WARDEN_EXCLUSION_RANGE, src))
if(!silent)
user << "You sense another ocular warden too near this location. Activating this one this close would cause them to fight."
return FAILED_UNFASTEN
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 3bb7e24d781..ca15b73251f 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -286,7 +286,7 @@
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
SSshuttle.emergency.setTimer(timer)
user << "You shatter the orb! A dark essence spirals into the air, then disappears."
- playsound(user.loc, "sound/effects/Glassbr1.ogg", 50, 1)
+ playsound(user.loc, 'sound/effects/Glassbr1.ogg', 50, 1)
qdel(src)
sleep(20)
var/global/list/curses
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index c006aee6354..d6abcaceff5 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -507,7 +507,7 @@
feedback_add_details("wizard_spell_learned",log_name)
rightandwrong(0, user, 25)
active = 1
- playsound(get_turf(user),"sound/magic/CastSummon.ogg",50,1)
+ playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
user << "You have cast summon guns!"
return 1
@@ -525,7 +525,7 @@
feedback_add_details("wizard_spell_learned",log_name)
rightandwrong(1, user, 25)
active = 1
- playsound(get_turf(user),"sound/magic/CastSummon.ogg",50,1)
+ playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
user << "You have cast summon magic!"
return 1
@@ -545,7 +545,7 @@
feedback_add_details("wizard_spell_learned",log_name)
summonevents()
times++
- playsound(get_turf(user),"sound/magic/CastSummon.ogg",50,1)
+ playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1)
user << "You have cast summon events."
return 1
diff --git a/code/game/machinery/computer/gulag_teleporter.dm b/code/game/machinery/computer/gulag_teleporter.dm
index 59a445c8cb4..d11193759e8 100644
--- a/code/game/machinery/computer/gulag_teleporter.dm
+++ b/code/game/machinery/computer/gulag_teleporter.dm
@@ -144,7 +144,7 @@
/obj/machinery/computer/gulag_teleporter_computer/proc/teleport(mob/user)
log_game("[user]([user.ckey] teleported [prisoner]([prisoner.ckey]) to the Labor Camp ([beacon.x], [beacon.y], [beacon.z]) for [id.goal] points.")
teleporter.handle_prisoner(id, temporary_record)
- playsound(loc, "sound/weapons/emitter.ogg", 50, 1)
+ playsound(loc, 'sound/weapons/emitter.ogg', 50, 1)
prisoner.forceMove(get_turf(beacon))
prisoner.Weaken(2) // small travel dizziness
prisoner << "The teleportation makes you a little dizzy."
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 07109a45c22..01aeae48dff 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -245,7 +245,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/silenced
name = "\improper S.H.H. \"Quietus\" Carbine"
desc = "A weapon for combat exosuits. A mime invention, field tests have shown that targets cannot even scream before going down."
- fire_sound = "sound/weapons/Gunshot_silenced.ogg"
+ fire_sound = 'sound/weapons/Gunshot_silenced.ogg'
icon_state = "mecha_mime"
equip_cooldown = 30
projectile = /obj/item/projectile/bullet/mime
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index af9cd5c03e9..ab32002543e 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -104,7 +104,7 @@ var/explosionid = 1
for(var/obj/structure/blob/B in T)
cached_exp_block[T] += B.explosion_block
CHECK_TICK
-
+
//flash mobs
if(flash_range)
for(var/mob/living/L in viewers(flash_range, epicenter))
@@ -179,7 +179,7 @@ var/explosionid = 1
var/took = (world.timeofday-start)/10
//You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
if(Debug2)
- world.log << "## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds."
+ log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.")
//Machines which report explosions.
for(var/array in doppler_arrays)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 73a75a4f7cd..3a2ed505cb1 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -766,12 +766,8 @@
else if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!remove_item_from_storage(M))
- M.temporarilyRemoveItemFromInventory(src, TRUE)
- if(!M.put_in_hand(src, H.held_index))
- qdel(src)
- return
- usr << "You pick up the deck."
+ if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
+ usr << "You pick up the deck."
else
usr << "You can't reach it from here!"
diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm
index baff623a2d8..386c1f930d6 100644
--- a/code/game/objects/items/weapons/RPD.dm
+++ b/code/game/objects/items/weapons/RPD.dm
@@ -571,7 +571,7 @@ var/global/list/RPD_recipes=list(
new /obj/item/pipe_meter(A)
if(DISPOSALS_MODE) //Making disposals pipes
- if(!is_anchored_dense_turf(A))
+ if(is_anchored_dense_turf(A))
user << "The [src]'s error light flickers; there's something in the way!"
return
user << "You start building a disposals pipe..."
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index d13d469812f..965c9fe1463 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -93,13 +93,9 @@
/obj/item/weapon/defibrillator/MouseDrop(obj/over_object)
if(ismob(src.loc))
var/mob/M = src.loc
- if(istype(over_object, /obj/screen/inventory/hand))
+ if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!M.temporarilyRemoveItemFromInventory(src))
- return
- if(!M.put_in_hand(src, H.held_index))
- qdel(src) //wewie
-
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
/obj/item/weapon/defibrillator/attackby(obj/item/weapon/W, mob/user, params)
if(W == paddles)
diff --git a/code/game/objects/items/weapons/sharpener.dm b/code/game/objects/items/weapons/sharpener.dm
index 12eb65f3c68..d0e302e6f4a 100644
--- a/code/game/objects/items/weapons/sharpener.dm
+++ b/code/game/objects/items/weapons/sharpener.dm
@@ -20,6 +20,9 @@
if(requires_sharpness && !I.sharpness)
user << "You can only sharpen items that are already sharp, such as knives."
return
+ if(istype(I, /obj/item/weapon/melee/energy))
+ user << "You don't think \the [I] will be the thing getting modified if you use it on \the [src]."
+ return
if(istype(I, /obj/item/weapon/twohanded))//some twohanded items should still be sharpenable, but handle force differently. therefore i need this stuff
var/obj/item/weapon/twohanded/TH = I
if(TH.force_wielded >= max)
diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm
index ebdf0bc3d48..e82aaca5281 100644
--- a/code/game/objects/items/weapons/storage/firstaid.dm
+++ b/code/game/objects/items/weapons/storage/firstaid.dm
@@ -60,10 +60,10 @@
..()
if(empty) return
icon_state = pick("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3")
- for(var/i in 1 to 3)
+ for(var/i in 1 to 4)
new /obj/item/weapon/reagent_containers/syringe/charcoal(src)
- for(var/i in 1 to 3)
- new /obj/item/weapon/reagent_containers/pill/charcoal(src)
+ for(var/i in 1 to 2)
+ new /obj/item/weapon/storage/pill_bottle/charcoal(src)
new /obj/item/device/healthanalyzer(src)
return
@@ -138,21 +138,14 @@
var/mob/M = usr
if(!istype(over_object, /obj/screen) || !Adjacent(M))
return ..()
- if(!M.restrained() && !M.stat && istype(over_object, /obj/screen/inventory/hand))
+ if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!M.temporarilyRemoveItemFromInventory(src))
- return
- if(!M.put_in_hand(src,H.held_index))
- qdel(src)
- return
- src.add_fingerprint(usr)
- return
+ if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
+ add_fingerprint(usr)
if(over_object == usr && in_range(src, usr) || usr.contents.Find(src))
if(usr.s_active)
usr.s_active.close(usr)
src.show_to(usr)
- return
- return
/obj/item/weapon/storage/box/silver_sulf
name = "box of silver sulfadiazine patches"
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 30dca0f3ee6..513a8fee8d3 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -45,7 +45,7 @@
show_to(M)
return
- if(!M.restrained() && !M.stat)
+ if(!M.incapacitated())
if(!istype(over_object, /obj/screen))
return content_can_dump(over_object, M)
@@ -54,14 +54,9 @@
playsound(loc, "rustle", 50, 1, -5)
-
if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!M.temporarilyRemoveItemFromInventory(src))
- return
- if(!M.put_in_hand(src,H.held_index))
- qdel(src)
- return
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
add_fingerprint(usr)
diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm
index f6900215f43..dca3668758d 100644
--- a/code/game/objects/items/weapons/tanks/watertank.dm
+++ b/code/game/objects/items/weapons/tanks/watertank.dm
@@ -85,10 +85,7 @@
var/mob/M = src.loc
if(istype(M) && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!M.temporarilyRemoveItemFromInventory(src))
- return
- M.put_in_hand(src, H.held_index)
-
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
/obj/item/weapon/watertank/attackby(obj/item/W, mob/user, params)
if(W == noz)
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index c2467177804..02e215380b8 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -297,7 +297,7 @@
return (BRUTELOSS)
/obj/item/weapon/wirecutters/power/attack_self(mob/user)
- playsound(get_turf(user),"sound/items/change_jaws.ogg",50,1)
+ playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
var/obj/item/weapon/crowbar/power/pryjaws = new /obj/item/weapon/crowbar/power
user << "You attach the pry jaws to [src]."
qdel(src)
@@ -735,7 +735,7 @@
return (BRUTELOSS)
/obj/item/weapon/crowbar/power/attack_self(mob/user)
- playsound(get_turf(user),"sound/items/change_jaws.ogg",50,1)
+ playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
var/obj/item/weapon/wirecutters/power/cutjaws = new /obj/item/weapon/wirecutters/power
user << "You attach the cutting jaws to [src]."
qdel(src)
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index 0b0869222f9..582e71e3e62 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -496,7 +496,7 @@
/obj/item/weapon/twohanded/required/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance, damage, attack_type)
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("Ranged attacks just make [owner] angrier!")
- playsound(src, pick("sound/weapons/bulletflyby.ogg","sound/weapons/bulletflyby2.ogg","sound/weapons/bulletflyby3.ogg"), 75, 1)
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
return 1
return 0
@@ -613,7 +613,7 @@
if(prob(final_block_chance))
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("[owner] deflects [attack_text] with [src]!")
- playsound(src, pick("sound/weapons/bulletflyby.ogg","sound/weapons/bulletflyby2.ogg","sound/weapons/bulletflyby3.ogg"), 75, 1)
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
return 1
else
owner.visible_message("[owner] parries [attack_text] with [src]!")
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 4d4c51ee9bf..081379a826a 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -388,7 +388,7 @@ var/highlander_claymores = 0
throw_speed = 0
sharpness = IS_SHARP
attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
- hitsound = "sound/weapons/chainsawhit.ogg"
+ hitsound = 'sound/weapons/chainsawhit.ogg'
/obj/item/weapon/mounted_chainsaw/dropped()
..()
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 8c724c77c77..ce81c45c150 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -73,7 +73,7 @@
if(alert)
var/area/alarmed = get_area(src)
alarmed.burglaralert(src)
- playsound(src, "sound/effects/alert.ogg", 50, 1)
+ playsound(src, 'sound/effects/alert.ogg', 50, 1)
/*
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 9ce203ce7da..571711e47e7 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -57,7 +57,7 @@
..()
var/area/A = get_area(src)
if(A)
- notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK)
+ notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
//Timeless prisons: Spawns in Wish Granter prisons in lavaland. Ghosts become age-old users of the Wish Granter and are advised to seek repentance for their past.
/obj/effect/mob_spawn/human/exile
@@ -113,7 +113,7 @@
mob_species = species
var/area/A = get_area(src)
if(A)
- notify_ghosts("\A [initial(species.id)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK)
+ notify_ghosts("\A [initial(species.id)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
if(has_owner && creator)
flavour_text = "You are a golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 477cd6bb750..054489a3573 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -81,7 +81,7 @@
return
if(user.pulling != L)
return
- playsound(src.loc, "sound/effects/splat.ogg", 25, 1)
+ playsound(src.loc, 'sound/effects/splat.ogg', 25, 1)
L.visible_message("[user] slams [L] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.")
L.loc = src.loc
L.emote("scream")
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 4968942aed8..725a3332112 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -62,7 +62,7 @@
var/ckeytext = ckey(key)
if(!establish_db_connection())
- world.log << "Ban database connection failure. Key [ckeytext] not checked"
+ log_world("Ban database connection failure. Key [ckeytext] not checked")
diary << "Ban database connection failure. Key [ckeytext] not checked"
return
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index 20ebfe234f7..4032554165a 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -129,7 +129,7 @@ var/list/admin_ranks = list() //list of all admin_rank datums
else
establish_db_connection()
if(!dbcon.IsConnected())
- world.log << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
+ log_world("Failed to connect to database in load_admin_ranks(). Reverting to legacy system.")
diary << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admin_ranks()
@@ -204,7 +204,7 @@ var/list/admin_ranks = list() //list of all admin_rank datums
else
establish_db_connection()
if(!dbcon.IsConnected())
- world.log << "Failed to connect to database in load_admins(). Reverting to legacy system."
+ log_world("Failed to connect to database in load_admins(). Reverting to legacy system.")
diary << "Failed to connect to database in load_admins(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admins()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 88ed6297f1a..25e19b1c126 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -156,7 +156,8 @@ var/list/admin_verbs_debug = list(
/client/proc/map_template_upload,
/client/proc/jump_to_ruin,
/client/proc/clear_dynamic_transit,
- /client/proc/toggle_medal_disable
+ /client/proc/toggle_medal_disable,
+ /client/proc/view_runtimes
)
var/list/admin_verbs_possess = list(
/proc/possess,
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 3ce2157aa55..7d3dad07644 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2246,3 +2246,14 @@
message_admins("[key_name(usr)] created \"[G.name]\" station goal.")
ticker.mode.station_goals += G
modify_goals()
+
+ else if(href_list["viewruntime"])
+ var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"])
+ if(!istype(error_viewer))
+ usr << "That runtime viewer no longer exists."
+ return
+
+ if(href_list["viewruntime_backto"])
+ error_viewer.show_to(owner, locate(href_list["viewruntime_backto"]), href_list["viewruntime_linear"])
+ else
+ error_viewer.show_to(owner, null, href_list["viewruntime_linear"])
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index ee34b21e797..84b50f6dc88 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -115,7 +115,7 @@
for(var/client/X in admins)
if(X.prefs.toggles & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
- window_flash(X)
+ window_flash(X, ignorepref = TRUE)
X << msg
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 5367ed782d3..3d1466766ce 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -180,7 +180,7 @@
for(var/client/X in admins)
X << "PM: [key_name(src, X, 0)]->IRC: \blue [keywordparsedmsg]" //inform X
else
- window_flash(C)
+ window_flash(C, ignorepref = TRUE)
log_admin("PM: [key_name(src)]->[key_name(C)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in admins)
@@ -214,7 +214,7 @@
C << "-- Administrator private message --"
C << "Admin PM from-[adminname]: [msg]"
C << "Click on the administrator's name to reply."
- window_flash(C)
+ window_flash(C, ignorepref = TRUE)
//always play non-admin recipients the adminhelp sound
C << 'sound/effects/adminhelp.ogg'
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 1e5e4a29647..44f4baaa5fe 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -761,4 +761,14 @@ var/list/TYPES_SHORTCUTS = list(
message_admins("[key_name_admin(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.")
feedback_add_details("admin_verb","TMH") // If...
- log_admin("[key_name(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.")
\ No newline at end of file
+ log_admin("[key_name(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.")
+
+/client/proc/view_runtimes()
+ set category = "Debug"
+ set name = "View Runtimes"
+ set desc = "Open the runtime Viewer"
+
+ if(!holder)
+ return
+
+ error_cache.show_to(src)
\ No newline at end of file
diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm
index 7ac21e52477..1d70cc1f107 100644
--- a/code/modules/admin/verbs/massmodvar.dm
+++ b/code/modules/admin/verbs/massmodvar.dm
@@ -193,7 +193,7 @@
if (rejected)
src << "[rejected] out of [count] objects rejected your edit"
- world.log << "### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])"
+ log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])")
log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)")
message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)")
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm
index 7bd80561c2c..f2e6b5fb0f2 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/verbs/modifyvariables.dm
@@ -337,7 +337,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (O.vv_edit_var(objectvar, L) == FALSE)
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]"
+ log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
@@ -380,7 +380,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (!O.vv_edit_var(objectvar, L))
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS"
+ log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS")
return
@@ -390,7 +390,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (!O.vv_edit_var(objectvar, L))
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES"
+ log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES")
return
@@ -400,7 +400,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (!O.vv_edit_var(objectvar, L))
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE"
+ log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE")
return
@@ -477,7 +477,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (O.vv_edit_var(objectvar, L))
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]"
+ log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
return
@@ -496,7 +496,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (O.vv_edit_var(objectvar, L) == FALSE)
src << "Your edit was rejected by the object."
return
- world.log << "### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]"
+ log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]")
@@ -605,6 +605,6 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width
if (O.vv_edit_var(variable, var_new) == FALSE)
src << "Your edit was rejected by the object."
return
- world.log << "### VarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]"
+ log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[O.vars[variable]]")]")
log_admin("[key_name(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
message_admins("[key_name_admin(src)] modified [original_name]'s [variable] to [O.vars[variable]]")
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 96f3bddba2d..f5ce8bb9b88 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -93,7 +93,7 @@
M.temporarilyRemoveItemFromInventory(src, TRUE) //Remove the tank from your character,in case you were holding it
if(!M.put_in_hands(R)) //Equips the bomb if possible, or puts it on the floor.
- forceMove(M.loc)
+ forceMove(get_turf(M))
R.bombassembly = S //Tell the bomb about its assembly part
S.master = R //Tell the assembly about its new owner
diff --git a/code/modules/awaymissions/maploader/swapmaps.dm b/code/modules/awaymissions/maploader/swapmaps.dm
index 06f19423290..64fc383e001 100644
--- a/code/modules/awaymissions/maploader/swapmaps.dm
+++ b/code/modules/awaymissions/maploader/swapmaps.dm
@@ -586,7 +586,7 @@ var/swapmaps_byname
else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt"))
text=1
else
- world.log << "SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found."
+ log_world("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.")
return
if(text)
S=new
@@ -613,7 +613,7 @@ var/swapmaps_byname
else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt"))
text=1
else
- world.log << "SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found."
+ log_world("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.")
return
if(text)
S=new
@@ -631,9 +631,11 @@ var/swapmaps_byname
/proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2)
if(!corner1 || !corner2)
- world.log << "SwapMaps error in SwapMaps_SaveChunk():"
- if(!corner1) world.log << " corner1 turf is null"
- if(!corner2) world.log << " corner2 turf is null"
+ log_world("SwapMaps error in SwapMaps_SaveChunk():")
+ if(!corner1)
+ log_world(" corner1 turf is null")
+ if(!corner2)
+ log_world(" corner2 turf is null")
return
var/swapmap/M=new
M.id=chunk_id
@@ -660,7 +662,7 @@ var/swapmaps_byname
else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt"))
text=1
else
- world.log << "SwapMaps error in SwapMaps_GetSize(): map_[id] file not found."
+ log_world("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.")
return
if(text)
S=new
diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm
index 16698df4684..208829e7b3f 100644
--- a/code/modules/awaymissions/zlevel.dm
+++ b/code/modules/awaymissions/zlevel.dm
@@ -18,7 +18,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away
maploader.load_map(file)
smooth_zlevel(world.maxz)
SortAreas()
- world.log << "loaded [file] as z-level [world.maxz]"
+ log_world("loaded [file] as z-level [world.maxz]")
/proc/reset_gateway_spawns(reset = FALSE)
for(var/obj/machinery/gateway/G in world)
@@ -92,7 +92,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away
if(ruins && ruins.len)
ruin = ruins[pick(ruins)]
else
- world.log << "Ruin loader had no ruins to pick from with [budget] left to spend."
+ log_world("Ruin loader had no ruins to pick from with [budget] left to spend.")
break
// Can we afford it
if(ruin.cost > budget)
@@ -119,7 +119,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away
if(!valid)
continue
- world.log << "Ruin \"[ruin.name]\" placed at ([T.x], [T.y], [T.z])"
+ log_world("Ruin \"[ruin.name]\" placed at ([T.x], [T.y], [T.z])")
var/obj/effect/ruin_loader/R = new /obj/effect/ruin_loader(T)
R.Load(ruins,ruin)
@@ -129,7 +129,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away
break
if(!overall_sanity)
- world.log << "Ruin loader gave up with [budget] left to spend."
+ log_world("Ruin loader gave up with [budget] left to spend.")
/obj/effect/ruin_loader
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 7898597a187..5f9377a2b19 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -31,6 +31,7 @@ var/list/preferences_datums = list()
var/hotkeys = FALSE
var/tgui_fancy = TRUE
var/tgui_lock = TRUE
+ var/windowflashing = TRUE
var/toggles = TOGGLES_DEFAULT
var/chat_toggles = TOGGLES_DEFAULT_CHAT
var/ghost_form = "ghost"
@@ -101,7 +102,6 @@ var/list/preferences_datums = list()
var/uplink_spawn_loc = UPLINK_PDA
-
/datum/preferences/New(client/C)
parent = C
custom_names["ai"] = pick(ai_names)
@@ -362,6 +362,7 @@ var/list/preferences_datums = list()
dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
"
dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
"
dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
"
+ dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
"
dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
"
dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
"
dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
"
@@ -1156,7 +1157,8 @@ var/list/preferences_datums = list()
tgui_fancy = !tgui_fancy
if("tgui_lock")
tgui_lock = !tgui_lock
-
+ if("winflash")
+ windowflashing = !windowflashing
if("hear_adminhelps")
toggles ^= SOUND_ADMINHELP
if("announce_login")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 7765fa48c2f..245814b60c5 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -155,6 +155,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["hotkeys"] >> hotkeys
S["tgui_fancy"] >> tgui_fancy
S["tgui_lock"] >> tgui_lock
+ S["windowflash"] >> windowflashing
if(islist(S["be_special"]))
S["be_special"] >> be_special
@@ -189,6 +190,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
+ windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing))
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
clientfps = sanitize_integer(clientfps, 0, 1000, 0)
@@ -217,6 +219,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["hotkeys"] << hotkeys
S["tgui_fancy"] << tgui_fancy
S["tgui_lock"] << tgui_lock
+ S["windowflash"] << windowflashing
S["be_special"] << be_special
S["default_slot"] << default_slot
S["toggles"] << toggles
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index fa252f4c066..052ef5ac029 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -46,14 +46,10 @@
if(istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech
return
- if(!M.restrained() && !M.stat && loc == M && istype(over_object, /obj/screen/inventory/hand))
+ if(!M.incapacitated() && loc == M && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
- if(!M.temporarilyRemoveItemFromInventory(src))
- return
- if(!M.put_in_hand(src, H.held_index))
- qdel(src)
- return
- add_fingerprint(usr)
+ if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
+ add_fingerprint(usr)
/obj/item/clothing/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(pockets)
diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm
index 64cc93517dd..8dcfa75407c 100644
--- a/code/modules/clothing/shoes/bananashoes.dm
+++ b/code/modules/clothing/shoes/bananashoes.dm
@@ -15,7 +15,7 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/step_action()
if(on)
if(footstep > 1)//honks when its on
- playsound(src, "sound/items/bikehorn.ogg", 75, 1)
+ playsound(src, 'sound/items/bikehorn.ogg', 75, 1)
footstep = 0
else
footstep++
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
new file mode 100644
index 00000000000..7c88c604b61
--- /dev/null
+++ b/code/modules/error_handler/error_handler.dm
@@ -0,0 +1,114 @@
+var/global/list/error_last_seen = list()
+var/global/list/error_cooldown = list() /* Error_cooldown items will either be positive(cooldown time) or negative(silenced error)
+ If negative, starts at -1, and goes down by 1 each time that error gets skipped*/
+var/global/total_runtimes = 0
+var/global/total_runtimes_skipped = 0
+
+#ifdef DEBUG
+/world/Error(exception/E, datum/e_src)
+ if(!istype(E)) //Something threw an unusual exception
+ log_world("\[[time_stamp()]] Uncaught exception: [E]")
+ return ..()
+ if(!error_last_seen) // A runtime is occurring too early in start-up initialization
+ return ..()
+
+ total_runtimes++
+
+ var/erroruid = "[E.file][E.line]"
+ var/last_seen = error_last_seen[erroruid]
+ var/cooldown = error_cooldown[erroruid] || 0
+
+ if(last_seen == null)
+ error_last_seen[erroruid] = world.time
+ last_seen = world.time
+
+ if(cooldown < 0)
+ error_cooldown[erroruid]-- //Used to keep track of skip count for this error
+ total_runtimes_skipped++
+ return //Error is currently silenced, skip handling it
+ //Handle cooldowns and silencing spammy errors
+ var/silencing = FALSE
+
+ // We can runtime before config is initialized because BYOND initialize objs/map before a bunch of other stuff happens.
+ // This is a bunch of workaround code for that. Hooray!
+
+ var/configured_error_cooldown = initial(config.error_cooldown)
+ var/configured_error_limit = initial(config.error_limit)
+ var/configured_error_silence_time = initial(config.error_silence_time)
+ if(config)
+ configured_error_cooldown = config.error_cooldown
+ configured_error_limit = config.error_limit
+ configured_error_silence_time = config.error_silence_time
+
+
+ //Each occurence of an unique error adds to its cooldown time...
+ cooldown = max(0, cooldown - (world.time - last_seen)) + configured_error_cooldown
+ // ... which is used to silence an error if it occurs too often, too fast
+ if(cooldown > configured_error_cooldown * configured_error_limit)
+ cooldown = -1
+ silencing = TRUE
+ spawn(0)
+ usr = null
+ sleep(configured_error_silence_time)
+ var/skipcount = abs(error_cooldown[erroruid]) - 1
+ error_cooldown[erroruid] = 0
+ if(skipcount > 0)
+ world.log << "\[[time_stamp()]] Skipped [skipcount] runtimes in [E.file],[E.line]."
+ error_cache.log_error(E, skip_count = skipcount)
+
+ error_last_seen[erroruid] = world.time
+ error_cooldown[erroruid] = cooldown
+
+ var/list/usrinfo = null
+ var/locinfo
+ if(istype(usr))
+ usrinfo = list(" usr: [datum_info_line(usr)]")
+ locinfo = atom_loc_line(usr)
+ if(locinfo)
+ usrinfo += " usr.loc: [locinfo]"
+ // The proceeding mess will almost definitely break if error messages are ever changed
+ var/list/splitlines = splittext(E.desc, "\n")
+ var/list/desclines = list()
+ if(LAZYLEN(splitlines) > ERROR_USEFUL_LEN) // If there aren't at least three lines, there's no info
+ for(var/line in splitlines)
+ if(LAZYLEN(line) < 3 || findtext(line, "source file:") || findtext(line, "usr.loc:"))
+ continue
+ if(findtext(line, "usr:"))
+ if(usrinfo)
+ desclines.Add(usrinfo)
+ usrinfo = null
+ continue // Our usr info is better, replace it
+
+ if(copytext(line, 1, 3) != " ")
+ desclines += (" " + line) // Pad any unpadded lines, so they look pretty
+ else
+ desclines += line
+ if(usrinfo) //If this info isn't null, it hasn't been added yet
+ desclines.Add(usrinfo)
+ if(silencing)
+ desclines += " (This error will now be silenced for [configured_error_silence_time / 600] minutes)"
+ if(error_cache)
+ error_cache.log_error(E, desclines)
+
+ world.log << "\[[time_stamp()]] Runtime in [E.file],[E.line]: [E]"
+ for(var/line in desclines)
+ world.log << line
+
+/* This logs the runtime in the old format */
+
+ E.name = "\n\[[time2text(world.timeofday,"hh:mm:ss")]\][E.name]"
+
+ //Original
+ //
+ var/list/split = splittext(E.desc, "\n")
+ for (var/i in 1 to split.len)
+ if (split[i] != "")
+ split[i] = "\[[time2text(world.timeofday,"hh:mm:ss")]\][split[i]]"
+ E.desc = jointext(split, "\n")
+ if(config && config.log_runtimes)
+ world.log = runtime_diary
+ ..(E)
+
+ world.log = null
+
+#endif
\ No newline at end of file
diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm
new file mode 100644
index 00000000000..11d7532023b
--- /dev/null
+++ b/code/modules/error_handler/error_viewer.dm
@@ -0,0 +1,194 @@
+// Error viewing datums, responsible for storing error info, notifying admins
+// when errors occur, and showing them to admins on demand.
+
+// There are 3 different types used here:
+//
+// - error_cache keeps track of all error sources, as well as all individually
+// logged errors. Only one instance of this datum should ever exist, and it's
+// right here:
+
+#ifdef DEBUG
+/var/datum/error_viewer/error_cache/error_cache = new()
+#else
+// If debugging is disabled, there's nothing useful to log, so don't bother.
+/var/datum/error_viewer/error_cache/error_cache = null
+#endif
+
+// - error_source datums exist for each line (of code) that generates an error,
+// and keep track of all errors generated by that line.
+//
+// - error_entry datums exist for each logged error, and keep track of all
+// relevant info about that error.
+
+// Common vars and procs are kept at the error_viewer level
+/datum/error_viewer
+ var/name = ""
+
+/datum/error_viewer/proc/browse_to(client/user, html)
+ var/datum/browser/browser = new(user.mob, "error_viewer", null, 600, 400)
+ browser.set_content(html)
+ browser.add_head_content({"
+
+ "})
+ browser.open()
+
+/datum/error_viewer/proc/build_header(datum/error_viewer/back_to, linear)
+ // Common starter HTML for show_to
+
+ . = ""
+
+ if (istype(back_to))
+ . += back_to.make_link("<<<", null, linear)
+
+ . += "[make_link("Refresh")]
"
+
+/datum/error_viewer/proc/show_to(user, datum/error_viewer/back_to, linear)
+ // Specific to each child type
+ return
+
+/datum/error_viewer/proc/make_link(linktext, datum/error_viewer/back_to, linear)
+ var/back_to_param = ""
+ if (!linktext)
+ linktext = name
+
+ if (istype(back_to))
+ back_to_param = ";viewruntime_backto=\ref[back_to]"
+
+ if (linear)
+ back_to_param += ";viewruntime_linear=1"
+
+ return "[linktext]"
+
+/datum/error_viewer/error_cache
+ var/list/errors = list()
+ var/list/error_sources = list()
+ var/list/errors_silenced = list()
+
+/datum/error_viewer/error_cache/show_to(user, datum/error_viewer/back_to, linear)
+ var/html = build_header()
+ html += "[global.total_runtimes] runtimes, [global.total_runtimes_skipped] skipped
"
+ if (!linear)
+ html += "organized | [make_link("linear", null, 1)]