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 = {"