diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index 7d37863a3de..06d1bce4373 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -19,7 +19,7 @@
var/hibernate = 0 //Do we even process?
var/scrubbing = 1 //0 = siphoning, 1 = scrubbing
- var/list/scrubbing_gas = list("carbon_dioxide")
+ var/list/scrubbing_gas = list("carbon_dioxide", "phoron")
var/panic = 0 //is this scrubber panicked?
diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm
index 56c95535fec..f311ea4aa2c 100644
--- a/code/__defines/lighting.dm
+++ b/code/__defines/lighting.dm
@@ -4,3 +4,48 @@
for(type in view(range, dview_mob))
#define END_FOR_DVIEW dview_mob.loc = null
+
+#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square
+#define LIGHTING_LAMBERTIAN 0 // use lambertian shading for light sources
+#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
+
+#define LIGHTING_LAYER 10 // drawing layer for lighting overlays
+#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
+#define LIGHTING_ICON_STATE_DARK "soft_dark" // Change between "soft_dark" and "dark" to swap soft darkvision
+
+#define LIGHTING_ROUND_VALUE (1 / 64) // Value used to round lumcounts, values smaller than 1/69 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY.
+
+#define LIGHTING_SOFT_THRESHOLD 0.05 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. This also should be the transparancy of the "soft_dark" icon state.
+
+// If I were you I'd leave this alone.
+#define LIGHTING_BASE_MATRIX \
+ list \
+ ( \
+ LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
+ LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
+ LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
+ LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
+ 0, 0, 0, 1 \
+ ) \
+
+// Helpers so we can (more easily) control the colour matrices.
+#define CL_MATRIX_RR 1
+#define CL_MATRIX_RG 2
+#define CL_MATRIX_RB 3
+#define CL_MATRIX_RA 4
+#define CL_MATRIX_GR 5
+#define CL_MATRIX_GG 6
+#define CL_MATRIX_GB 7
+#define CL_MATRIX_GA 8
+#define CL_MATRIX_BR 9
+#define CL_MATRIX_BG 10
+#define CL_MATRIX_BB 11
+#define CL_MATRIX_BA 12
+#define CL_MATRIX_AR 13
+#define CL_MATRIX_AG 14
+#define CL_MATRIX_AB 15
+#define CL_MATRIX_AA 16
+#define CL_MATRIX_CR 17
+#define CL_MATRIX_CG 18
+#define CL_MATRIX_CB 19
+#define CL_MATRIX_CA 20
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index 6f8bec025b1..cae68c3a232 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -3,6 +3,10 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
#define CELLRATE 0.002 // Multiplier for watts per tick <> cell storage (e.g., 0.02 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
// It's a conversion constant. power_used*CELLRATE = charge_provided, or charge_used/CELLRATE = power_provided
+#define KILOWATTS *1000
+#define MEGAWATTS *1000000
+#define GIGAWATTS *1000000000
+
// Doors!
#define DOOR_CRUSH_DAMAGE 20
#define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien.
diff --git a/code/__defines/process_scheduler.dm b/code/__defines/process_scheduler.dm
index d9c8f106efc..f1e30c23ed3 100644
--- a/code/__defines/process_scheduler.dm
+++ b/code/__defines/process_scheduler.dm
@@ -7,13 +7,12 @@
#define PROCESS_STATUS_HUNG 6
// Process time thresholds
-#define PROCESS_DEFAULT_HANG_WARNING_TIME 300 // 30 seconds
-#define PROCESS_DEFAULT_HANG_ALERT_TIME 600 // 60 seconds
-#define PROCESS_DEFAULT_HANG_RESTART_TIME 900 // 90 seconds
-#define PROCESS_DEFAULT_SCHEDULE_INTERVAL 50 // 50 ticks
-#define PROCESS_DEFAULT_SLEEP_INTERVAL 8 // 1/8th of a tick
+#define PROCESS_DEFAULT_HANG_WARNING_TIME 300 // 30 seconds
+#define PROCESS_DEFAULT_HANG_ALERT_TIME 600 // 60 seconds
+#define PROCESS_DEFAULT_HANG_RESTART_TIME 900 // 90 seconds
+#define PROCESS_DEFAULT_SCHEDULE_INTERVAL 50 // 50 ticks
+#define PROCESS_DEFAULT_SLEEP_INTERVAL 20 // 20% of a tick
+#define PROCESS_DEFAULT_DEFER_USAGE 90 // 90% of a tick
-// SCHECK macros
-// This references src directly to work around a weird bug with try/catch
-#define SCHECK_EVERY(this_many_calls) if(++src.calls_since_last_scheck >= this_many_calls) sleepCheck()
-#define SCHECK sleepCheck()
+// Sleep check macro
+#define SCHECK if(world.tick_usage >= next_sleep_usage) defer()
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index d244916ec21..6fa9d4ec0c7 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -952,7 +952,7 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0)
// var/area/AR = X.loc
-// if(AR.lighting_use_dynamic)
+// if(AR.dynamic_lighting)
// X.opacity = !X.opacity
// X.sd_SetOpacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn
@@ -1299,3 +1299,16 @@ var/mob/dview/dview_mob = new
tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1))))
return locate(tX, tY, tZ)
+// Displays something as commonly used (non-submultiples) SI units.
+/proc/format_SI(var/number, var/symbol)
+ switch(round(abs(number)))
+ if(0 to 1000-1)
+ return "[number] [symbol]"
+ if(1e3 to 1e6-1)
+ return "[round(number / 1000, 0.1)] k[symbol]" // kilo
+ if(1e6 to 1e9-1)
+ return "[round(number / 1e6, 0.1)] M[symbol]" // mega
+ if(1e9 to 1e12-1) // Probably not needed but why not be complete?
+ return "[round(number / 1e9, 0.1)] G[symbol]" // giga
+ if(1e12 to 1e15-1)
+ return "[round(number / 1e12, 0.1)] T[symbol]" // tera
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 3f6758c6ad2..e55323fb3a2 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -162,6 +162,12 @@
update_icon()
return 1
+/obj/screen/zone_sel/proc/set_selected_zone(bodypart)
+ var/old_selecting = selecting
+ selecting = bodypart
+ if(old_selecting != selecting)
+ update_icon()
+
/obj/screen/zone_sel/update_icon()
overlays.Cut()
overlays += image('icons/mob/zone_sel.dmi', "[selecting]")
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index 2d5b8b1b7dc..323c5f76639 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -48,7 +48,10 @@
// This controls how often the process will yield (call sleep(0)) while it is running.
// Every concurrent process should sleep periodically while running in order to allow other
// processes to execute concurrently.
- var/tmp/sleep_interval
+ var/tmp/sleep_interval = PROCESS_DEFAULT_SLEEP_INTERVAL
+
+ // Defer usage; the tick usage at which this process will defer until the next tick
+ var/tmp/defer_usage = PROCESS_DEFAULT_DEFER_USAGE
// hang_warning_time - this is the time (in 1/10 seconds) after which the server will begin to show "maybe hung" in the context window
var/tmp/hang_warning_time = PROCESS_DEFAULT_HANG_WARNING_TIME
@@ -59,19 +62,13 @@
// hang_restart_time - After this much time(in 1/10 seconds), the server will automatically kill and restart the process.
var/tmp/hang_restart_time = PROCESS_DEFAULT_HANG_RESTART_TIME
- // How many times in the current run has the process deferred work till the next tick?
- var/tmp/cpu_defer_count = 0
-
- // How many SCHECKs have been skipped (to limit btime calls)
- var/tmp/calls_since_last_scheck = 0
+ // Number of deciseconds to delay before starting the process
+ var/start_delay = 0
/**
* recordkeeping vars
*/
- // Records the time (1/10s timeoftick) at which the process last finished sleeping
- var/tmp/last_slept = 0
-
// Records the time (1/10s timeofgame) at which the process last began running
var/tmp/run_start = 0
@@ -85,11 +82,29 @@
var/tmp/last_object
+ // How many times in the current run has the process deferred work till the next tick?
+ var/tmp/cpu_defer_count = 0
+
// Counts the number of times an exception has occurred; gets reset after 10
var/tmp/list/exceptions = list()
- // Number of deciseconds to delay before starting the process
- var/start_delay = 0
+ // The next tick_usage the process will sleep at
+ var/tmp/next_sleep_usage
+
+ // Last run duration, in seconds
+ var/tmp/last_run_time = 0
+
+ // Last 20 run durations
+ var/tmp/list/last_twenty_run_times = list()
+
+ // Highest run duration, in seconds
+ var/tmp/highest_run_time = 0
+
+ // Tick usage at start of current run (updates upon deferring)
+ var/tmp/tick_usage_start
+
+ // Accumulated tick usage from before each deferral
+ var/tmp/tick_usage_accumulated = 0
/datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
..()
@@ -97,9 +112,6 @@
previousStatus = "idle"
idle()
name = "process"
- schedule_interval = 50
- sleep_interval = world.tick_lag / PROCESS_DEFAULT_SLEEP_INTERVAL
- last_slept = 0
run_start = 0
ticks = 0
last_task = 0
@@ -112,6 +124,10 @@
// Initialize defer count
cpu_defer_count = 0
+ // Prepare usage tracking (defer() updates these)
+ tick_usage_start = world.tick_usage
+ tick_usage_accumulated = 0
+
running()
main.processStarted(src)
@@ -119,11 +135,23 @@
/datum/controller/process/proc/finished()
ticks++
+ recordRunTime()
idle()
main.processFinished(src)
onFinish()
+/datum/controller/process/proc/recordRunTime()
+ // Convert from tick usage (100/tick) to seconds of CPU time used
+ var/total_usage = (tick_usage_accumulated + (world.tick_usage - tick_usage_start)) / 1000 * world.tick_lag
+
+ last_run_time = total_usage
+ if(total_usage > highest_run_time)
+ highest_run_time = total_usage
+ if(last_twenty_run_times.len == 20)
+ last_twenty_run_times.Cut(1, 2)
+ last_twenty_run_times += total_usage
+
/datum/controller/process/proc/doWork()
/datum/controller/process/proc/setup()
@@ -183,10 +211,9 @@
// This should del
del(src)
-// Do not call this directly - use SHECK or SCHECK_EVERY
-/datum/controller/process/proc/sleepCheck(var/tickId = 0)
- calls_since_last_scheck = 0
- if (killed)
+// Do not call this directly - use SHECK
+/datum/controller/process/proc/defer()
+ if(killed)
// The kill proc is the only place where killed is set.
// The kill proc should have deleted this datum, and all sleeping procs that are
// owned by it.
@@ -196,15 +223,14 @@
handleHung()
CRASH("Process [name] hung and was restarted.")
- if (main.getCurrentTickElapsedTime() > main.timeAllowance)
+ tick_usage_accumulated += (world.tick_usage - tick_usage_start)
+ if(world.tick_usage < defer_usage)
+ sleep(0)
+ else
sleep(world.tick_lag)
cpu_defer_count++
- last_slept = 0
- else
- if (TimeOfTick > last_slept + sleep_interval)
- // If we haven't slept in sleep_interval deciseconds, sleep to allow other work to proceed.
- sleep(0)
- last_slept = TimeOfTick
+ tick_usage_start = world.tick_usage
+ next_sleep_usage = min(world.tick_usage + sleep_interval, defer_usage)
/datum/controller/process/proc/update()
// Clear delta
@@ -231,14 +257,14 @@
return
/datum/controller/process/proc/getContext()
- return "
| [name] | [main.averageRunTime(src)] | [main.last_run_time[src]] | [main.highest_run_time[src]] | [ticks] |
\n"
+ return "| [name] | [getAverageRunTime()] | [last_run_time] | [highest_run_time] | [ticks] |
\n"
/datum/controller/process/proc/getContextData()
return list(
"name" = name,
- "averageRunTime" = main.averageRunTime(src),
- "lastRunTime" = main.last_run_time[src],
- "highestRunTime" = main.highest_run_time[src],
+ "averageRunTime" = getAverageRunTime(),
+ "lastRunTime" = last_run_time,
+ "highestRunTime" = highest_run_time,
"ticks" = ticks,
"schedule" = schedule_interval,
"status" = getStatusText(),
@@ -286,7 +312,6 @@
name = target.name
schedule_interval = target.schedule_interval
sleep_interval = target.sleep_interval
- last_slept = 0
run_start = 0
times_killed = target.times_killed
ticks = target.ticks
@@ -309,21 +334,31 @@
disabled = 0
/datum/controller/process/proc/getAverageRunTime()
- return main.averageRunTime(src)
+ var/t = 0
+ var/c = 0
+ for(var/time in last_twenty_run_times)
+ t += time
+ c++
+
+ if(c > 0)
+ return t / c
+ return c
+
/datum/controller/process/proc/getLastRunTime()
- return main.getProcessLastRunTime(src)
+ return last_run_time
/datum/controller/process/proc/getHighestRunTime()
- return main.getProcessHighestRunTime(src)
+ return highest_run_time
/datum/controller/process/proc/getTicks()
return ticks
/datum/controller/process/proc/statProcess()
- var/averageRunTime = round(getAverageRunTime(), 0.1)/10
- var/lastRunTime = round(getLastRunTime(), 0.1)/10
- var/highestRunTime = round(getHighestRunTime(), 0.1)/10
- stat("[name]", "T#[getTicks()] | AR [averageRunTime] | LR [lastRunTime] | HR [highestRunTime] | D [cpu_defer_count]")
+ var/averageRunTime = round(getAverageRunTime(), 0.001)
+ var/lastRunTime = round(last_run_time, 0.001)
+ var/highestRunTime = round(highest_run_time, 0.001)
+ var/deferTime = round(cpu_defer_count / 10 * world.tick_lag, 0.01)
+ stat("[name]", "T#[getTicks()] | AR [averageRunTime] | LR [lastRunTime] | HR [highestRunTime] | D [deferTime]")
/datum/controller/process/proc/catchException(var/exception/e, var/thrower)
if(istype(e)) // Real runtimes go to the real error handler
diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm
index dc412ee92ad..86dcdac2874 100644
--- a/code/controllers/ProcessScheduler/core/processScheduler.dm
+++ b/code/controllers/ProcessScheduler/core/processScheduler.dm
@@ -20,18 +20,6 @@ var/global/datum/controller/processScheduler/processScheduler
// Process last queued times (world time)
var/tmp/datum/controller/process/list/last_queued = new
- // Process last start times (real time)
- var/tmp/datum/controller/process/list/last_start = new
-
- // Process last run durations
- var/tmp/datum/controller/process/list/last_run_time = new
-
- // Per process list of the last 20 durations
- var/tmp/datum/controller/process/list/last_twenty_run_times = new
-
- // Process highest run time
- var/tmp/datum/controller/process/list/highest_run_time = new
-
// How long to sleep between runs (set to tick_lag in New)
var/tmp/scheduler_sleep_interval
@@ -41,22 +29,12 @@ var/global/datum/controller/processScheduler/processScheduler
// Setup for these processes will be deferred until all the other processes are set up.
var/tmp/list/deferredSetupList = new
- var/tmp/currentTick = 0
-
- var/tmp/timeAllowance = 0
-
- var/tmp/cpuAverage = 0
-
- var/tmp/timeAllowanceMax = 0
-
/datum/controller/processScheduler/New()
..()
// When the process scheduler is first new'd, tick_lag may be wrong, so these
// get re-initialized when the process scheduler is started.
// (These are kept here for any processes that decide to process before round start)
scheduler_sleep_interval = world.tick_lag
- timeAllowance = world.tick_lag * 0.5
- timeAllowanceMax = world.tick_lag
/**
* deferSetupFor
@@ -88,20 +66,12 @@ var/global/datum/controller/processScheduler/processScheduler
isRunning = 1
// tick_lag will have been set by now, so re-initialize these
scheduler_sleep_interval = world.tick_lag
- timeAllowance = world.tick_lag * 0.5
- timeAllowanceMax = world.tick_lag
updateStartDelays()
spawn(0)
process()
/datum/controller/processScheduler/proc/process()
- updateCurrentTickData()
-
- for(var/i=world.tick_lag,i highest_run_time[process])
- highest_run_time[process] = time
-
- var/list/lastTwenty = last_twenty_run_times[process]
- if (lastTwenty.len == 20)
- lastTwenty.Cut(1, 2)
- lastTwenty.len++
- lastTwenty[lastTwenty.len] = time
-
-/**
- * averageRunTime
- * returns the average run time (over the last 20) of the process
- */
-/datum/controller/processScheduler/proc/averageRunTime(var/datum/controller/process/process)
- var/lastTwenty = last_twenty_run_times[process]
-
- var/t = 0
- var/c = 0
- for(var/time in lastTwenty)
- t += time
- c++
-
- if(c > 0)
- return t / c
- return c
-
-/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process)
- return last_run_time[process]
-
-/datum/controller/processScheduler/proc/getProcessHighestRunTime(var/datum/controller/process/process)
- return highest_run_time[process]
/datum/controller/processScheduler/proc/getStatusData()
var/list/data = new
@@ -338,34 +221,12 @@ var/global/datum/controller/processScheduler/processScheduler
var/datum/controller/process/process = nameToProcessMap[processName]
process.disable()
-/datum/controller/processScheduler/proc/getCurrentTickElapsedTime()
- if (world.time > currentTick)
- updateCurrentTickData()
- return 0
- else
- return TimeOfTick
-
-/datum/controller/processScheduler/proc/updateCurrentTickData()
- if (world.time > currentTick)
- // New tick!
- currentTick = world.time
- updateTimeAllowance()
- cpuAverage = (world.cpu + cpuAverage + cpuAverage) / 3
-
-/datum/controller/processScheduler/proc/updateTimeAllowance()
- // Time allowance goes down linearly with world.cpu.
- var/tmp/error = cpuAverage - 100
- var/tmp/timeAllowanceDelta = SIMPLE_SIGN(error) * -0.5 * world.tick_lag * max(0, 0.001 * abs(error))
-
- //timeAllowance = world.tick_lag * min(1, 0.5 * ((200/max(1,cpuAverage)) - 1))
- timeAllowance = min(timeAllowanceMax, max(0, timeAllowance + timeAllowanceDelta))
/datum/controller/processScheduler/proc/statProcesses()
if(!isRunning)
stat("Processes", "Scheduler not running")
return
stat("Processes", "[processes.len] (R [running.len] / Q [queued.len] / I [idle.len])")
- stat(null, "[round(cpuAverage, 0.1)] CPU, [round(timeAllowance, 0.1)/10] TA")
for(var/datum/controller/process/p in processes)
p.statProcess()
diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm
new file mode 100644
index 00000000000..d46cab1d19f
--- /dev/null
+++ b/code/controllers/Processes/lighting.dm
@@ -0,0 +1,98 @@
+/var/lighting_overlays_initialised = FALSE
+
+/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
+/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
+/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
+
+/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
+/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
+/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
+
+
+/datum/controller/process/lighting
+ // Queues of update counts, waiting to be rolled into stats lists
+ var/list/stats_queues = list(
+ "Source" = list(), "Corner" = list(), "Overlay" = list())
+ // Stats lists
+ var/list/stats_lists = list(
+ "Source" = list(), "Corner" = list(), "Overlay" = list())
+ var/update_stats_every = (1 SECONDS)
+ var/next_stats_update = 0
+ var/stat_updates_to_keep = 5
+
+/datum/controller/process/lighting/setup()
+ name = "lighting"
+
+ schedule_interval = 0 // run as fast as you possibly can
+ sleep_interval = 10 // Yield every 10% of a tick
+ defer_usage = 80 // Defer at 80% of a tick
+ create_all_lighting_overlays()
+ lighting_overlays_initialised = TRUE
+
+ // Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
+ spawn(300)
+ doWork(1)
+
+/datum/controller/process/lighting/doWork(roundstart)
+
+ lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
+ lighting_update_lights = list()
+ for(var/datum/light_source/L in lighting_update_lights_old)
+
+ if(L.check() || L.destroyed || L.force_update)
+ L.remove_lum()
+ if(!L.destroyed)
+ L.apply_lum()
+
+ else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
+ L.smart_vis_update()
+
+ L.vis_update = FALSE
+ L.force_update = FALSE
+ L.needs_update = FALSE
+
+ SCHECK
+
+ lighting_update_corners_old = lighting_update_corners //Same as above.
+ lighting_update_corners = list()
+ for(var/A in lighting_update_corners_old)
+ var/datum/lighting_corner/C = A
+
+ C.update_overlays()
+
+ C.needs_update = FALSE
+
+ SCHECK
+
+ lighting_update_overlays_old = lighting_update_overlays //Same as above.
+ lighting_update_overlays = list()
+
+ for(var/A in lighting_update_overlays_old)
+ var/atom/movable/lighting_overlay/O = A
+ O.update_overlay()
+ O.needs_update = 0
+ SCHECK
+
+ stats_queues["Source"] += lighting_update_lights_old.len
+ stats_queues["Corner"] += lighting_update_corners_old.len
+ stats_queues["Overlay"] += lighting_update_overlays_old.len
+
+ if(next_stats_update <= world.time)
+ next_stats_update = world.time + update_stats_every
+ for(var/stat_name in stats_queues)
+ var/stat_sum = 0
+ var/list/stats_queue = stats_queues[stat_name]
+ for(var/count in stats_queue)
+ stat_sum += count
+ stats_queue.Cut()
+
+ var/list/stats_list = stats_lists[stat_name]
+ stats_list.Insert(1, stat_sum)
+ if(stats_list.len > stat_updates_to_keep)
+ stats_list.Cut(stats_list.len)
+
+/datum/controller/process/lighting/statProcess()
+ ..()
+ stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
+ for(var/stat_type in stats_lists)
+ stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
diff --git a/code/controllers/Processes/planet.dm b/code/controllers/Processes/planet.dm
index 31406e92a09..b3b556d7bd1 100644
--- a/code/controllers/Processes/planet.dm
+++ b/code/controllers/Processes/planet.dm
@@ -1,11 +1,16 @@
+var/datum/controller/process/planet/planet_controller = null
+
/datum/controller/process/planet
var/list/planets = list()
/datum/controller/process/planet/setup()
name = "planet"
+ planet_controller = src
schedule_interval = 600 // every minute
- planet_sif = new()
- planets.Add(planet_sif)
+ var/list/planet_datums = typesof(/datum/planet) - /datum/planet
+ for(var/P in planet_datums)
+ var/datum/planet/NP = new P()
+ planets.Add(NP)
/datum/controller/process/planet/doWork()
for(var/datum/planet/P in planets)
diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm
index fdbe55faedd..276249bd558 100644
--- a/code/controllers/Processes/scheduler.dm
+++ b/code/controllers/Processes/scheduler.dm
@@ -25,6 +25,21 @@
catchException(e, last_object)
SCHECK
+// We've been restarted, probably due to having a massive list of tasks.
+// Lets copy over the task list as safely as we can and try to chug thru it...
+// Note: We won't be informed about tasks being destroyed, but this is the best we can do.
+/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target)
+ scheduled_tasks = list()
+ for(var/st in target.scheduled_tasks)
+ if(!deleted(st) && istype(st, /datum/scheduled_task))
+ schedule(st)
+ scheduler = src
+
+// We are being killed. Least we can do is deregister all those events we registered
+/datum/controller/process/scheduler/onKill()
+ for(var/st in scheduled_tasks)
+ destroyed_event.unregister(st, src)
+
/datum/controller/process/scheduler/statProcess()
..()
stat(null, "[scheduled_tasks.len] task\s")
@@ -130,4 +145,4 @@
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
- scheduler.schedule(st)
\ No newline at end of file
+ scheduler.schedule(st)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index c1a0f83a872..6039dd66123 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -94,6 +94,7 @@ var/list/gamemode_cache = list()
var/allow_extra_antags = 0
var/guests_allowed = 1
var/debugparanoid = 0
+ var/panic_bunker = 0
var/serverurl
var/server
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 0c8d6ee052a..01e95e5716a 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -100,7 +100,7 @@ var/list/ghostteleportlocs = list()
icon_state = "space"
requires_power = 1
always_unpowered = 1
- lighting_use_dynamic = 1
+ dynamic_lighting = 1
power_light = 0
power_equip = 0
power_environ = 0
@@ -293,7 +293,7 @@ area/space/atmosalert()
/area/shuttle/mining
name = "\improper Mining Elevator"
music = "music/escape.ogg"
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/mining/station
@@ -392,7 +392,7 @@ area/space/atmosalert()
/area/shuttle/research
name = "\improper Research Elevator"
music = "music/escape.ogg"
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/research/station
@@ -418,7 +418,7 @@ area/space/atmosalert()
name = "\improper CentCom"
icon_state = "centcom"
requires_power = 0
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/centcom/control
name = "\improper CentCom Control"
@@ -491,7 +491,7 @@ area/space/atmosalert()
name = "\improper Mercenary Base"
icon_state = "syndie-ship"
requires_power = 0
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/syndicate_mothership/control
name = "\improper Mercenary Control Room"
@@ -543,7 +543,7 @@ area/space/atmosalert()
name = "\improper Thunderdome"
icon_state = "thunder"
requires_power = 0
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
sound_env = ARENA
/area/tdome/tdome1
@@ -624,7 +624,7 @@ area/space/atmosalert()
name = "\improper Wizard's Den"
icon_state = "yellow"
requires_power = 0
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/skipjack_station
name = "\improper Skipjack"
@@ -1485,7 +1485,7 @@ area/space/atmosalert()
/area/holodeck
name = "\improper Holodeck"
icon_state = "Holodeck"
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
sound_env = LARGE_ENCLOSED
/area/holodeck/alphadeck
@@ -1638,7 +1638,7 @@ area/space/atmosalert()
/area/solar
requires_power = 1
always_unpowered = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
base_turf = /turf/space
auxport
@@ -2336,7 +2336,7 @@ area/space/atmosalert()
/area/shuttle/constructionsite
name = "\improper Construction Site Shuttle"
icon_state = "yellow"
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/constructionsite/station
@@ -2493,25 +2493,25 @@ area/space/atmosalert()
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/AIsatextFS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/AIsatextAS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/AIsatextAP
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
/area/NewAIMain
name = "\improper AI Main New"
@@ -2685,7 +2685,7 @@ area/space/atmosalert()
name = "Beach"
icon_state = "null"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
requires_power = 0
ambience = list()
var/sound/mysound = null
@@ -2807,7 +2807,7 @@ var/list/the_station_areas = list (
name = "Keelin's private beach"
icon_state = "null"
luminosity = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
requires_power = 0
var/sound/mysound = null
/*
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index e118e6fa4d5..f1520509902 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -18,7 +18,7 @@
power_equip = 0
power_environ = 0
- if(lighting_use_dynamic)
+ if(dynamic_lighting)
luminosity = 0
else
luminosity = 1
diff --git a/code/game/area/asteroid_areas.dm b/code/game/area/asteroid_areas.dm
index 68f9807eadc..66a7e86913a 100644
--- a/code/game/area/asteroid_areas.dm
+++ b/code/game/area/asteroid_areas.dm
@@ -126,7 +126,7 @@
/area/outpost/engineering/solarsoutside
requires_power = 1
always_unpowered = 1
- lighting_use_dynamic = 0
+ dynamic_lighting = 0
aft
name = "\improper Engineering Outpost Solar Array"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index b76620fa95f..b511c9befd2 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -40,7 +40,13 @@
reagents = null
for(var/atom/movable/AM in contents)
qdel(AM)
+ var/turf/un_opaque
+ if(opacity && isturf(loc))
+ un_opaque = loc
+
loc = null
+ if(un_opaque)
+ un_opaque.recalc_atom_opacity()
if (pulledby)
if (pulledby.pulling == src)
pulledby.pulling = null
diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm
index 417fb339e0a..10cb5f9fbbe 100644
--- a/code/game/gamemodes/changeling/powers/absorb.dm
+++ b/code/game/gamemodes/changeling/powers/absorb.dm
@@ -61,7 +61,7 @@
T:UpdateDamageIcon()
feedback_add_details("changeling_powers","A[stage]")
- if(!do_mob(src, T, 150))
+ if(!do_mob(src, T, 150) || G.state != GRAB_KILL)
src << "Our absorption of [T] has been interrupted!"
changeling.isabsorbing = 0
return
diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm
index 055cf3e28ba..660434d185a 100644
--- a/code/game/gamemodes/changeling/powers/panacea.dm
+++ b/code/game/gamemodes/changeling/powers/panacea.dm
@@ -28,6 +28,7 @@
C.reagents.clear_reagents()
C.ingested.clear_reagents()
+
var/heal_amount = 5
if(src.mind.changeling.recursive_enhancement)
heal_amount = heal_amount * 2
@@ -38,5 +39,17 @@
C.adjustToxLoss(-heal_amount)
sleep(10)
+ for(var/obj/item/organ/external/E in C.organs)
+ var/obj/item/organ/external/G = E
+ if(G.germ_level)
+ var/germ_heal = heal_amount * 100
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
+ for(var/obj/item/organ/internal/I in C.internal_organs)
+ var/obj/item/organ/internal/G = I
+ if(G.germ_level)
+ var/germ_heal = heal_amount * 100
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
feedback_add_details("changeling_powers","AP")
return 1
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm
index 496af98ef8e..75c97d5ceea 100644
--- a/code/game/gamemodes/cult/hell_universe.dm
+++ b/code/game/gamemodes/cult/hell_universe.dm
@@ -62,7 +62,7 @@ In short:
/datum/universal_state/hell/OverlayAndAmbientSet()
spawn(0)
- for(var/atom/movable/lighting_overlay/L in world)
+ for(var/datum/lighting_corner/L in world)
L.update_lumcount(1, 0, 0)
for(var/turf/space/T in turfs)
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
index 11c61cfed63..6f549d15f4e 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
@@ -93,7 +93,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked
/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet()
spawn(0)
- for(var/atom/movable/lighting_overlay/L in world)
+ for(var/datum/lighting_corner/L in world)
if(L.z in using_map.admin_levels)
L.update_lumcount(1,1,1)
else
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index bd056f7c69e..ad8b89ec804 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -80,6 +80,10 @@
var/turf/T = locate(endx, endy, Z)
return T
+// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'.
+/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor)
+ return TRUE
+
///////////////////////
//The meteor effect
//////////////////////
@@ -101,6 +105,11 @@
var/meteordrop = /obj/item/weapon/ore/iron
var/dropamt = 2
+ // How much damage it does to walls, using take_damage().
+ // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through.
+ // Multiply this and the hits var to get a rough idea of how penetrating a meteor is.
+ var/wall_power = 100
+
/obj/effect/meteor/New()
..()
z_original = z
@@ -132,8 +141,11 @@
/obj/effect/meteor/Bump(atom/A)
if(A)
- ram_turf(get_turf(A))
- get_hit()
+ if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield.
+ ram_turf(get_turf(A))
+ get_hit()
+ else
+ die(0)
/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
return istype(mover, /obj/effect/meteor) ? 1 : ..()
@@ -146,7 +158,11 @@
//then, ram the turf if it still exists
if(T)
- T.ex_act(hitpwr)
+ if(istype(T, /turf/simulated/wall))
+ var/turf/simulated/wall/W = T
+ W.take_damage(wall_power) // Stronger walls can halt asteroids.
+ else
+ T.ex_act(hitpwr) // Floors and other things lack fancy health.
//process getting 'hit' by colliding with a dense object
@@ -154,9 +170,12 @@
/obj/effect/meteor/proc/get_hit()
hits--
if(hits <= 0)
- make_debris()
- meteor_effect()
- qdel(src)
+ die(1)
+
+/obj/effect/meteor/proc/die(var/explode = 1)
+ make_debris()
+ meteor_effect(explode)
+ qdel(src)
/obj/effect/meteor/ex_act()
return
@@ -172,21 +191,24 @@
var/obj/item/O = new meteordrop(get_turf(src))
O.throw_at(dest, 5, 10)
-/obj/effect/meteor/proc/meteor_effect()
+/obj/effect/meteor/proc/shake_players()
+ for(var/mob/M in player_list)
+ var/turf/T = get_turf(M)
+ if(!T || T.z != src.z)
+ continue
+ var/dist = get_dist(M.loc, src.loc)
+ shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
+
+/obj/effect/meteor/proc/meteor_effect(var/explode)
if(heavy)
- for(var/mob/M in player_list)
- var/turf/T = get_turf(M)
- if(!T || T.z != src.z)
- continue
- var/dist = get_dist(M.loc, src.loc)
- shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
+ shake_players()
///////////////////////
//Meteor types
///////////////////////
-//Dust
+// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot.
/obj/effect/meteor/dust
name = "space dust"
icon_state = "dust"
@@ -194,63 +216,74 @@
hits = 1
hitpwr = 3
meteordrop = /obj/item/weapon/ore/glass
+ wall_power = 50
-//Medium-sized
+// Medium-sized meteors aren't very special and can be stopped easily by r-walls.
/obj/effect/meteor/medium
name = "meteor"
dropamt = 3
+ wall_power = 200
-/obj/effect/meteor/medium/meteor_effect()
+/obj/effect/meteor/medium/meteor_effect(var/explode)
..()
- explosion(src.loc, 0, 1, 2, 3, 0)
+ if(explode)
+ explosion(src.loc, 0, 1, 2, 3, 0)
-//Large-sized
+// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter.
/obj/effect/meteor/big
name = "large meteor"
icon_state = "large"
- hits = 6
+ hits = 8
heavy = 1
dropamt = 4
+ wall_power = 400
-/obj/effect/meteor/big/meteor_effect()
+/obj/effect/meteor/big/meteor_effect(var/explode)
..()
- explosion(src.loc, 1, 2, 3, 4, 0)
+ if(explode)
+ explosion(src.loc, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 6, flash_range = 12, adminlog = 0)
-//Flaming meteor
+// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end.
/obj/effect/meteor/flaming
name = "flaming meteor"
icon_state = "flaming"
hits = 5
heavy = 1
meteordrop = /obj/item/weapon/ore/phoron
+ wall_power = 100
-/obj/effect/meteor/flaming/meteor_effect()
+/obj/effect/meteor/flaming/meteor_effect(var/explode)
..()
- explosion(src.loc, 1, 2, 3, 4, 0, 0, 5)
+ if(explode)
+ explosion(src.loc, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 8, flash_range = 16, adminlog = 0)
-//Radiation meteor
+// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding.
/obj/effect/meteor/irradiated
name = "glowing meteor"
icon_state = "glowing"
heavy = 1
meteordrop = /obj/item/weapon/ore/uranium
+ wall_power = 75
-/obj/effect/meteor/irradiated/meteor_effect()
+/obj/effect/meteor/irradiated/meteor_effect(var/explode)
..()
- explosion(src.loc, 0, 0, 4, 3, 0)
+ if(explode)
+ explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
- for(var/mob/living/L in view(5, src))
+ for(var/mob/living/L in view(10, src))
L.apply_effect(40, IRRADIATE)
+// This meteor fries toasters.
/obj/effect/meteor/emp
name = "conducting meteor"
icon_state = "glowing_blue"
desc = "Hide your floppies!"
meteordrop = /obj/item/weapon/ore/osmium
dropamt = 3
+ wall_power = 80
-/obj/effect/meteor/emp/meteor_effect()
+/obj/effect/meteor/emp/meteor_effect(var/explode)
..()
// Best case scenario: Comparable to a low-yield EMP grenade.
// Worst case scenario: Comparable to a standard yield EMP grenade.
@@ -265,10 +298,12 @@
hitpwr = 1
heavy = 1
meteordrop = /obj/item/weapon/ore/phoron
+ wall_power = 150
-/obj/effect/meteor/tunguska/meteor_effect()
+/obj/effect/meteor/tunguska/meteor_effect(var/explode)
..()
- explosion(src.loc, 5, 10, 15, 20, 0)
+ if(explode)
+ explosion(src.loc, 5, 10, 15, 20, 0)
/obj/effect/meteor/tunguska/Bump()
..()
diff --git a/code/game/gamemodes/technomancer/spells/insert/purify.dm b/code/game/gamemodes/technomancer/spells/insert/purify.dm
index b1e369f6e89..6ba36d44c66 100644
--- a/code/game/gamemodes/technomancer/spells/insert/purify.dm
+++ b/code/game/gamemodes/technomancer/spells/insert/purify.dm
@@ -32,5 +32,18 @@
H.adjustToxLoss(-heal_power / 5)
H.adjustCloneLoss(-heal_power / 5)
H.radiation = max(host.radiation - ( (heal_power * 2) / 5), 0)
+
+ for(var/obj/item/organ/external/E in H.organs)
+ var/obj/item/organ/external/G = E
+ if(G.germ_level)
+ var/germ_heal = heal_power * 10
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
+ for(var/obj/item/organ/internal/I in H.internal_organs)
+ var/obj/item/organ/internal/G = I
+ if(G.germ_level)
+ var/germ_heal = heal_power * 10
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
sleep(1 SECOND)
on_expire()
diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
index 5bb032b9c70..c6e5a2804db 100644
--- a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
+++ b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
@@ -83,11 +83,8 @@
continue
if(!true_sight)
- var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(L)
- var/light_amount = 1 // Unsimulated tiles are pretend-lit, so we need to be pretend too if that somehow happens.
- if(light)
- light_amount = (light.lum_r + light.lum_g + light.lum_b) / 3
-
+ var/turf/T = get_turf(L)
+ var/light_amount = T.get_lumcount()
if(light_amount <= 0.5)
continue // Too dark to see.
diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm
index 3a57b35f0a6..1e4398546c5 100644
--- a/code/game/jobs/job/civilian.dm
+++ b/code/game/jobs/job/civilian.dm
@@ -26,7 +26,12 @@
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/bartender(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/device/pda/bar(H), slot_belt)
- return 1
+ if(has_alt_title(H, alt_title,"Bartender"))
+ if(H.backbag == 1)
+ H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H), slot_l_hand)
+ else
+ H.equip_to_slot_or_del(new /obj/item/weapon/permit/gun/bar(H.back), slot_in_backpack)
+ return 1
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 9af5de87d4f..d4f5dac431b 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -93,7 +93,7 @@
req_access = list(access_rd, access_atmospherics, access_engine_equip)
TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa
TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa
- TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa
+ TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa
TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa
TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */
TLV["temperature"] = list(20, 40, 140, 160) // K
@@ -121,7 +121,7 @@
// breathable air according to human/Life()
TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa
TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa
- TLV["phoron"] = list(-1.0, -1.0, 0.2, 0.5) // Partial pressure, kpa
+ TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa
TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa
TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */
TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K
@@ -758,6 +758,7 @@
user << "You [ locked ? "lock" : "unlock"] the Air Alarm interface."
else
user << "Access denied."
+ return
return ..()
/obj/machinery/alarm/power_change()
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 97dd9f5fe05..6aeecfc8e5d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -944,7 +944,7 @@ About the new airlock wires panel:
healthcheck()
/obj/effect/energy_field/airlock_crush(var/crush_damage)
- Stress(crush_damage)
+ adjust_strength(crush_damage)
/obj/structure/closet/airlock_crush(var/crush_damage)
..()
diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm
index 3fa93e7e467..3f077116e93 100644
--- a/code/game/objects/effects/chem/chemsmoke.dm
+++ b/code/game/objects/effects/chem/chemsmoke.dm
@@ -162,9 +162,9 @@
smoke.pixel_x = -32 + rand(-8, 8)
smoke.pixel_y = -32 + rand(-8, 8)
walk_to(smoke, T)
- smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then
+ smoke.set_opacity(1) //switching opacity on after the smoke has spawned, and then
sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner
- smoke.opacity = 0 // lighting and view range updates
+ smoke.set_opacity(0) // lighting and view range updates
fadeOut(smoke)
qdel(src)
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 3560d4e37fb..674e746c28b 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -66,7 +66,19 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/examine(mob/user)
if(..(user, 1))
- user << "The time [stationtime2text()] is displayed in the corner of the screen."
+ to_chat(user, "The time [stationtime2text()] is displayed in the corner of the screen.")
+
+
+/obj/item/device/pda/AltClick()
+ if(issilicon(usr))
+ return
+
+ if ( can_use(usr) )
+ if(id)
+ remove_id()
+ else
+ to_chat(usr, "This PDA does not have an ID in it.")
+
/obj/item/device/pda/medical
default_cartridge = /obj/item/weapon/cartridge/medical
@@ -247,7 +259,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
set name = "Send Message"
set src in usr
if(usr.stat == 2)
- usr << "You can't send PDA messages because you are dead!"
+ to_chat(usr, "You can't send PDA messages because you are dead!")
return
var/list/plist = available_pdas()
if (plist)
@@ -262,28 +274,27 @@ var/global/list/obj/item/device/pda/PDAs = list()
set name = "Toggle Sender/Receiver"
set src in usr
if(usr.stat == 2)
- usr << "You can't do that because you are dead!"
+ to_chat(usr, "You can't send PDA messages because you are dead!")
return
toff = !toff
- usr << "PDA sender/receiver toggled [(toff ? "Off" : "On")]!"
+ to_chat(usr, "PDA sender/receiver toggled [(toff ? "Off" : "On")]!")
/obj/item/device/pda/ai/verb/cmd_toggle_pda_silent()
set category = "AI IM"
set name = "Toggle Ringer"
set src in usr
if(usr.stat == 2)
- usr << "You can't do that because you are dead!"
+ to_chat(usr, "You can't send PDA messages because you are dead!")
return
message_silent=!message_silent
- usr << "PDA ringer toggled [(message_silent ? "Off" : "On")]!"
-
+ to_chat(usr, "PDA ringer toggled [(message_silent ? "Off" : "On")]!")
/obj/item/device/pda/ai/verb/cmd_show_message_log()
set category = "AI IM"
set name = "Show Message Log"
set src in usr
if(usr.stat == 2)
- usr << "You can't do that because you are dead!"
+ to_chat(usr, "You can't send PDA messages because you are dead!")
return
var/HTML = "AI PDA Message Log"
for(var/index in tnote)
@@ -809,7 +820,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (in_range(src, U) && loc == U)
if (t)
if(src.hidden_uplink && hidden_uplink.check_trigger(U, lowertext(t), lowertext(lock_code)))
- U << "The PDA softly beeps."
+ to_chat(U, "The PDA softly beeps.")
ui.close()
else
t = sanitize(t, 20)
@@ -856,7 +867,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
U.show_message("Virus sent!", 1)
P.honkamt = (rand(15,20))
else
- U << "PDA not found."
+ to_chat(U, "PDA not found.")
else
ui.close()
return 0
@@ -872,7 +883,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
P.ttone = "silence"
P.newstone = "silence"
else
- U << "PDA not found."
+ to_chat(U, "PDA not found.")
else
ui.close()
return 0
@@ -928,9 +939,10 @@ var/global/list/obj/item/device/pda/PDAs = list()
message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1)
detonate_act(P)
else
- U << "No charges left."
+ to_chat(U, "No charges left.")
+
else
- U << "PDA not found."
+ to_chat(U, "PDA not found.")
else
U.unset_machine()
ui.close()
@@ -1048,7 +1060,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (ismob(loc))
var/mob/M = loc
M.put_in_hands(id)
- usr << "You remove the ID from the [name]."
+ to_chat(usr, "You remove the ID from the [name].")
else
id.loc = get_turf(src)
id = null
@@ -1080,11 +1092,11 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable
if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level?
- U << "ERROR: Cannot reach recipient."
+ to_chat(U, "ERROR: Cannot reach recipient.")
return
var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]")
if (send_result)
- U << "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'."
+ to_chat(U, "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'.")
return
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]")))
@@ -1113,7 +1125,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
P.new_message_from_pda(src, t)
nanomanager.update_user_uis(U, src) // Update the sending user's PDA UI so that they can see the new message
else
- U << "ERROR: Messaging server is not responding."
+ to_chat(U, "ERROR: Messaging server is not responding.")
/obj/item/device/pda/proc/new_info(var/beep_silent, var/message_tone, var/reception_message)
if (!beep_silent)
@@ -1176,9 +1188,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(can_use(usr))
mode = 0
nanomanager.update_uis(src)
- usr << "You press the reset button on \the [src]."
+ to_chat(usr, "You press the reset button on \the [src].")
else
- usr << "You cannot do this while restrained."
+ to_chat(usr, "You cannot do this while restrained.")
/obj/item/device/pda/verb/verb_remove_id()
set category = "Object"
@@ -1192,9 +1204,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(id)
remove_id()
else
- usr << "This PDA does not have an ID in it."
+ to_chat(usr, "This PDA does not have an ID in it.")
else
- usr << "You cannot do this while restrained."
+ to_chat(usr, "You cannot do this while restrained.")
/obj/item/device/pda/verb/verb_remove_pen()
@@ -1212,13 +1224,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/mob/M = loc
if(M.get_active_hand() == null)
M.put_in_hands(O)
- usr << "You remove \the [O] from \the [src]."
+ to_chat(usr, "You remove \the [O] from \the [src].")
return
O.loc = get_turf(src)
else
- usr << "This PDA does not have a pen in it."
+ to_chat(usr, "This PDA does not have a pen in it.")
else
- usr << "You cannot do this while restrained."
+ to_chat(usr, "You cannot do this while restrained.")
/obj/item/device/pda/verb/verb_remove_cartridge()
set category = "Object"
@@ -1241,9 +1253,9 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (cartridge.radio)
cartridge.radio.hostpda = null
cartridge = null
- usr << "You remove \the [cartridge] from the [name]."
+ to_chat(usr, "You remove \the [cartridge] from the [name].")
else
- usr << "You cannot do this while restrained."
+ to_chat(usr, "You cannot do this while restrained.")
/obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use.
if(choice == 1)
@@ -1273,7 +1285,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
cartridge = C
user.drop_item()
cartridge.loc = src
- user << "You insert [cartridge] into [src]."
+ to_chat(usr, "You insert [cartridge] into [src].")
nanomanager.update_uis(src) // update all UIs attached to src
if(cartridge.radio)
cartridge.radio.hostpda = src
@@ -1281,19 +1293,19 @@ var/global/list/obj/item/device/pda/PDAs = list()
else if(istype(C, /obj/item/weapon/card/id))
var/obj/item/weapon/card/id/idcard = C
if(!idcard.registered_name)
- user << "\The [src] rejects the ID."
+ to_chat(user, "\The [src] rejects the ID.")
return
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
ownrank = idcard.rank
name = "PDA-[owner] ([ownjob])"
- user << "Card scanned."
+ to_chat(user, "Card scanned.")
else
//Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand.
if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) )
if(id_check(user, 2))
- user << "You put the ID into \the [src]'s slot."
+ to_chat(user, "You put the ID into \the [src]'s slot.")
updateSelfDialog()//Update self dialog on success.
return //Return in case of failed check or when successful.
updateSelfDialog()//For the non-input related code.
@@ -1301,16 +1313,16 @@ var/global/list/obj/item/device/pda/PDAs = list()
user.drop_item()
C.loc = src
pai = C
- user << "You slot \the [C] into [src]."
+ to_chat(user, "You slot \the [C] into \the [src].")
nanomanager.update_uis(src) // update all UIs attached to src
else if(istype(C, /obj/item/weapon/pen))
var/obj/item/weapon/pen/O = locate() in src
if(O)
- user << "There is already a pen in \the [src]."
+ to_chat(user, "There is already a pen in \the [src].")
else
user.drop_item()
C.loc = src
- user << "You slide \the [C] into \the [src]."
+ to_chat(user, "You slot \the [C] into \the [src].")
return
/obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob)
@@ -1346,18 +1358,18 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(2)
if (!istype(C:dna, /datum/dna))
- user << "No fingerprints found on [C]"
+ to_chat(user, "No fingerprints found on [C]")
else
- user << text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]")
+ to_chat(user, text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]"))
if ( !(C:blood_DNA) )
- user << "No blood found on [C]"
+ to_chat(user, "No blood found on [C]")
if(C:blood_DNA)
qdel(C:blood_DNA)
else
- user << "Blood found on [C]. Analysing..."
+ to_chat(user, "Blood found on [C]. Analysing...")
spawn(15)
for(var/blood in C:blood_DNA)
- user << "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]"
+ to_chat(user, "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]")
if(4)
for (var/mob/O in viewers(C, null))
@@ -1379,13 +1391,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(!isnull(A.reagents))
if(A.reagents.reagent_list.len > 0)
var/reagents_length = A.reagents.reagent_list.len
- user << "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found."
+ to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.")
for (var/re in A.reagents.reagent_list)
- user << " [re]"
+ to_chat(user," [re]")
else
- user << "No active chemical agents found in [A]."
+ to_chat(user,"No active chemical agents found in [A].")
else
- user << "No significant chemical agents found in [A]."
+ to_chat(user,"No significantchemical agents found in [A].")
if(5)
analyze_gases(A, user)
@@ -1437,8 +1449,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
// feature to the PDA, which would better convey the availability of the feature, but this will work for now.
// Inform the user
- user << "Paper scanned and OCRed to notekeeper." //concept of scanning paper copyright brainoblivion 2009
-
+ to_chat(user,"Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009
/obj/item/device/pda/proc/explode() //This needs tuning. //Sure did.
@@ -1469,7 +1480,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/list/namecounts = list()
if (toff)
- usr << "Turn on your receiver in order to send messages."
+ to_chat(usr, "Turn on your receiver in order to send messages.")
return
for (var/obj/item/device/pda/P in PDAs)
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index a3c5d33d672..1190d7f8120 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -42,7 +42,8 @@
/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user)
if(istype(user, /mob/living/silicon/robot))
- return 1
+ if(user.Adjacent(target))
+ return 1
else
for(var/obj/item/weapon/grab/G in target.grabbed_by)
if(G.loc == user && G.state >= GRAB_AGGRESSIVE)
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index c89f41056fb..6ea812ad3a7 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -1113,10 +1113,10 @@
"}
/obj/item/weapon/book/manual/evaguide
- name = "EVA Gear and You: Not Spending All Day Inside"
+ name = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition"
icon_state = "evabook"
author = "Maria Crash, Senior Atmospherics Technician"
- title = "EVA Gear and You: Not Spending All Day Inside"
+ title = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition"
dat = {"