Merge branch 'master' of https://github.com/PolarisSS13/Polaris into scratch_damage

This commit is contained in:
Anewbe
2017-04-17 16:49:10 -05:00
130 changed files with 2114 additions and 1098 deletions
@@ -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?
+45
View File
@@ -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
+4
View File
@@ -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.
+8 -9
View File
@@ -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()
+14 -1
View File
@@ -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
+6
View File
@@ -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]")
@@ -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 "<tr><td>[name]</td><td>[main.averageRunTime(src)]</td><td>[main.last_run_time[src]]</td><td>[main.highest_run_time[src]]</td><td>[ticks]</td></tr>\n"
return "<tr><td>[name]</td><td>[getAverageRunTime()]</td><td>[last_run_time]</td><td>[highest_run_time]</td><td>[ticks]</td></tr>\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
@@ -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<world.tick_lag*50,i+=world.tick_lag)
spawn(i) updateCurrentTickData()
while(isRunning)
// Hopefully spawning this for 50 ticks in the future will make it the first thing in the queue.
spawn(world.tick_lag*50) updateCurrentTickData()
checkRunningProcesses()
queueProcesses()
runQueuedProcesses()
@@ -148,20 +118,6 @@ var/global/datum/controller/processScheduler/processScheduler
process.idle()
idle.Add(process)
// init recordkeeping vars
last_start.Add(process)
last_start[process] = 0
last_run_time.Add(process)
last_run_time[process] = 0
last_twenty_run_times.Add(process)
last_twenty_run_times[process] = list()
highest_run_time.Add(process)
highest_run_time[process] = 0
// init starts and stops record starts
recordStart(process, 0)
recordEnd(process, 0)
// Set up process
process.setup()
@@ -178,24 +134,9 @@ var/global/datum/controller/processScheduler/processScheduler
queued.Remove(oldProcess)
idle.Add(newProcess)
last_start.Remove(oldProcess)
last_start.Add(newProcess)
last_start[newProcess] = 0
last_run_time.Add(newProcess)
last_run_time[newProcess] = last_run_time[oldProcess]
last_run_time.Remove(oldProcess)
last_twenty_run_times.Add(newProcess)
last_twenty_run_times[newProcess] = last_twenty_run_times[oldProcess]
last_twenty_run_times.Remove(oldProcess)
highest_run_time.Add(newProcess)
highest_run_time[newProcess] = highest_run_time[oldProcess]
highest_run_time.Remove(oldProcess)
recordStart(newProcess, 0)
recordEnd(newProcess, 0)
newProcess.last_run_time = oldProcess.last_run_time
newProcess.last_twenty_run_times = oldProcess.last_twenty_run_times
newProcess.highest_run_time = oldProcess.highest_run_time
nameToProcessMap[newProcess.name] = newProcess
@@ -210,11 +151,10 @@ var/global/datum/controller/processScheduler/processScheduler
/datum/controller/processScheduler/proc/processStarted(var/datum/controller/process/process)
setRunningProcessState(process)
recordStart(process)
last_queued[process] = world.time
/datum/controller/processScheduler/proc/processFinished(var/datum/controller/process/process)
setIdleProcessState(process)
recordEnd(process)
/datum/controller/processScheduler/proc/setIdleProcessState(var/datum/controller/process/process)
if (process in running)
@@ -243,63 +183,6 @@ var/global/datum/controller/processScheduler/processScheduler
if (!(process in running))
running += process
/datum/controller/processScheduler/proc/recordStart(var/datum/controller/process/process, var/time = null)
if (isnull(time))
time = TimeOfGame
last_queued[process] = world.time
last_start[process] = time
else
last_queued[process] = (time == 0 ? 0 : world.time)
last_start[process] = time
/datum/controller/processScheduler/proc/recordEnd(var/datum/controller/process/process, var/time = null)
if (isnull(time))
time = TimeOfGame
var/lastRunTime = time - last_start[process]
if(lastRunTime < 0)
lastRunTime = 0
recordRunTime(process, lastRunTime)
/**
* recordRunTime
* Records a run time for a process
*/
/datum/controller/processScheduler/proc/recordRunTime(var/datum/controller/process/process, time)
last_run_time[process] = time
if(time > 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()
+98
View File
@@ -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], " | ")]")
+7 -2
View File
@@ -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)
+16 -1
View File
@@ -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)
scheduler.schedule(st)
+1
View File
@@ -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
+16 -16
View File
@@ -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
/*
+1 -1
View File
@@ -18,7 +18,7 @@
power_equip = 0
power_environ = 0
if(lighting_use_dynamic)
if(dynamic_lighting)
luminosity = 0
else
luminosity = 1
+1 -1
View File
@@ -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"
+6
View File
@@ -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
@@ -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 << "<span class='warning'>Our absorption of [T] has been interrupted!</span>"
changeling.isabsorbing = 0
return
@@ -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
+1 -1
View File
@@ -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)
@@ -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
+66 -31
View File
@@ -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()
..()
@@ -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()
@@ -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.
+6 -1
View File
@@ -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
+3 -2
View File
@@ -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 << "<span class='notice'>You [ locked ? "lock" : "unlock"] the Air Alarm interface.</span>"
else
user << "<span class='warning'>Access denied.</span>"
return
return ..()
/obj/machinery/alarm/power_change()
+1 -1
View File
@@ -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)
..()
+2 -2
View File
@@ -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)
+56 -45
View File
@@ -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, "<span class='notice'>This PDA does not have an ID in it.</span>")
/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 << "<span class='notice'>PDA sender/receiver toggled [(toff ? "Off" : "On")]!</span>"
to_chat(usr, "<span class='notice'>PDA sender/receiver toggled [(toff ? "Off" : "On")]!</span>")
/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 << "<span class='notice'>PDA ringer toggled [(message_silent ? "Off" : "On")]!</span>"
to_chat(usr, "<span class='notice'>PDA ringer toggled [(message_silent ? "Off" : "On")]!</span>")
/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 = "<html><head><title>AI PDA Message Log</title></head><body>"
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("<span class='notice'>Virus sent!</span>", 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 << "<span class='notice'>You remove the ID from the [name].</span>"
to_chat(usr, "<span class='notice'>You remove the ID from the [name].</span>")
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 << "<span class='notice'>ERROR: Messaging server is not responding.</span>"
to_chat(U, "<span class='notice'>ERROR: Messaging server is not responding.</span>")
/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 << "<span class='notice'>You press the reset button on \the [src].</span>"
to_chat(usr, "<span class='notice'>You press the reset button on \the [src].</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/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 << "<span class='notice'>This PDA does not have an ID in it.</span>"
to_chat(usr, "<span class='notice'>This PDA does not have an ID in it.</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/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 << "<span class='notice'>You remove \the [O] from \the [src].</span>"
to_chat(usr, "<span class='notice'>You remove \the [O] from \the [src].</span>")
return
O.loc = get_turf(src)
else
usr << "<span class='notice'>This PDA does not have a pen in it.</span>"
to_chat(usr, "<span class='notice'>This PDA does not have a pen in it.</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/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 << "<span class='notice'>You remove \the [cartridge] from the [name].</span>"
to_chat(usr, "<span class='notice'>You remove \the [cartridge] from the [name].</span>")
else
usr << "<span class='notice'>You cannot do this while restrained.</span>"
to_chat(usr, "<span class='notice'>You cannot do this while restrained.</span>")
/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 << "<span class='notice'>You insert [cartridge] into [src].</span>"
to_chat(usr, "<span class='notice'>You insert [cartridge] into [src].</span>")
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 << "<span class='notice'>\The [src] rejects the ID.</span>"
to_chat(user, "<span class='notice'>\The [src] rejects the ID.</span>")
return
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
ownrank = idcard.rank
name = "PDA-[owner] ([ownjob])"
user << "<span class='notice'>Card scanned.</span>"
to_chat(user, "<span class='notice'>Card scanned.</span>")
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 << "<span class='notice'>You put the ID into \the [src]'s slot.</span>"
to_chat(user, "<span class='notice'>You put the ID into \the [src]'s slot.</span>")
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 << "<span class='notice'>You slot \the [C] into [src].</span>"
to_chat(user, "<span class='notice'>You slot \the [C] into \the [src].</span>")
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 << "<span class='notice'>There is already a pen in \the [src].</span>"
to_chat(user, "<span class='notice'>There is already a pen in \the [src].</span>")
else
user.drop_item()
C.loc = src
user << "<span class='notice'>You slide \the [C] into \the [src].</span>"
to_chat(user, "<span class='notice'>You slot \the [C] into \the [src].</span>")
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 << "<span class='notice'>No fingerprints found on [C]</span>"
to_chat(user, "<span class='notice'>No fingerprints found on [C]</span>")
else
user << text("<span class='notice'>\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]</span>")
to_chat(user, text("<span class='notice'>\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]</span>"))
if ( !(C:blood_DNA) )
user << "<span class='notice'>No blood found on [C]</span>"
to_chat(user, "<span class='notice'>No blood found on [C]</span>")
if(C:blood_DNA)
qdel(C:blood_DNA)
else
user << "<span class='notice'>Blood found on [C]. Analysing...</span>"
to_chat(user, "<span class='notice'>Blood found on [C]. Analysing...</span>")
spawn(15)
for(var/blood in C:blood_DNA)
user << "<span class='notice'>Blood type: [C:blood_DNA[blood]]\nDNA: [blood]</span>"
to_chat(user, "<span class='notice'>Blood type: [C:blood_DNA[blood]]\nDNA: [blood]</span>")
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 << "<span class='notice'>[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.</span>"
to_chat(user, "<span class='notice'>[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.</span>")
for (var/re in A.reagents.reagent_list)
user << "<span class='notice'> [re]</span>"
to_chat(user,"<span class='notice'> [re]</span>")
else
user << "<span class='notice'>No active chemical agents found in [A].</span>"
to_chat(user,"<span class='notice'>No active chemical agents found in [A].</span>")
else
user << "<span class='notice'>No significant chemical agents found in [A].</span>"
to_chat(user,"<span class='notice'>No significantchemical agents found in [A].</span>")
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 << "<span class='notice'>Paper scanned and OCRed to notekeeper.</span>" //concept of scanning paper copyright brainoblivion 2009
to_chat(user,"<span class='notice'>Paper scanned and OCRed to notekeeper.</span>") //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)
+2 -1
View File
@@ -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)
+56 -18
View File
@@ -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 = {"<html>
<head>
<style>
@@ -1130,27 +1130,32 @@
</head>
<body>
<h1><a name="Foreword">EVA Gear and You: Not Spending All Day Inside</a></h1>
<h1><a name="Foreword">EVA Gear and You: Not Spending All Day Inside, 2nd Edition</a></h1>
<I>Or: How not to suffocate because there's a hole in your shoes</I><BR>
<h2><a name="Contents">Contents</a></h2>
<ol>
<li><a href="#Foreword">A foreword on using EVA gear</a></li>
<li><a href="#Civilian">Donning a Civilian Suit</a></li>
<li><a href="#Hardsuit">Putting on a Hardsuit</a></li>
<li><a href="#Softsuits">Use of Softsuits</a></li>
<li><a href="#Voidsuits">Putting on a Voidsuit</a></li>
<li><a href="#Hardsuits">Operation of Hardsuits</a></li>
<li><a href="#Equipment">Cyclers and Other Modification Equipment</a></li>
<li><a href="#Advice">Miscellaneous Advice</a></li>
<li><a href="#Final">Final Checks</a></li>
</ol>
<br>
EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training,
but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into two sections:
Civilian suits and hardsuits.<BR><BR>
but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into three main sections:
softsuits, voidsuits, and hardsuits. General advice and instructions for modification are present as well.<BR><BR>
<h2><a name="Civilian">Civilian Suits</a></h2>
One important point for synthetics and people using full-body prosthetics: You obviously don't need oxygen, but you do run the risk of overheating in vacuum.
Rather than oxygen, use a suit cooling unit. Many emergency equipment stores don't hold them, unfortunately, but dedicated EVA stores will. Be aware of your heat tolerances.<BR><BR>
<h2><a name="Softsuits">Softsuits and Emergency Equipment</a></h2>
<I>The bulkiest things this side of Alpha Centauri</I><BR>
These suits are the grey ones that are stored in EVA. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiation or physical impact.
As Medical, Engineering, Security, and Mining all have hardsuits of their own, these don't see much use, but knowing how to put them on is quite useful anyways.<BR><BR>
These suits are the both grey ones that are stored in EVA and orange emergency suits in emergency lockers. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiation or physical impact.
As Medical, Engineering, Security, and Mining all have voidsuits of their own, these don't see much use outside of emergencies. In an emergency, knowing how to put one on can save your life.<BR><BR>
First, take the suit. It should be in three pieces: A top, a bottom, and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however,
put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit,
@@ -1160,35 +1165,68 @@
There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit,
but it is recommended to have a full-sized tank on your back for EVA.<BR><BR>
Important note: When using these, especially in emergencies, be aware of your surroundings! These suits can tear or breach more easily than any other type, especially in an environment with broken glass and metal everywhere.
If your suit is breached, you will be in deep trouble. Pressure issues can inhibit breathing even with internals.<BR><BR>
These suits tend to be wearable by most species. They're large and flexible. They might be pretty uncomfortable for some, though, so keep that in mind.<BR><BR>
<h2><a name="Hardsuit">Hardsuits</a></h2>
<h2><a name="Voidsuits">Voidsuits</a></h2>
<I>Heavy, uncomfortable, still the best option.</I><BR>
These suits come in Engineering, Mining, and the Armory. There's also a couple Medical Hardsuits in EVA. These provide a lot more protection than the standard suits.<BR><BR>
These suits come in many specialized varieties. The most common are engineering, atmospherics, security, medical, and mining varieties.
These provide a lot more protection than the standard suits, and depending on the specialization, can offer different protections.
For example, security suits have armor plating, engineering suits have radiation protection, and atmospherics suits are rated for extremely high temperatures.<BR><BR>
Similarly to the other suits, these are split into three parts. Fastening the pant and top are mostly the same as the other spacesuits, with the exception that these are a bit heavier,
Similarly to the softsuits, these are split into three parts. Fastening the pant and top are mostly the same as the softsuits, with the exception that these are a bit heavier,
though not as bulky. The helmet goes on differently, with the air tube feeding into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise,
and then is screwed in for one and a quarter full rotations clockwise, leaving the faceplate directly in front of you. There is a small button on the right side of the helmet that activates the helmet light.
and then is turned to face the front and sealed. There is a small button on the right side of the helmet that activates the helmet light.
The tanks that fasten onto the side slot are emergency tanks, as well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.<BR><BR>
These suits generally only fit one species. NanoTrasen's are usually human-fitting by default, but there's equipment that can make modifications to the hardsuits to fit them to other species.<BR><BR>
Later-model voidsuits can have magboots and helmets installed into the suit and deployed when needed. Check the operator's manual for individual suits to see how the helmets are installed.
If a helmet is installed, you can skip it while putting the suit on, obviously. When deployed, it will deploy from the back of your neck, covering the head and sealing at the front.<BR><BR>
<h2><a name="Hardsuits">Hardsuits/Rigs</a></h2>
<I>The fancy stuff.</I><BR>
Proper hardsuits are the most complex sort of EVA equipment available, and blur the line between spacesuits and smaller exosuits. They're sometimes known as 'rigs' or 'powered armor'.
These are the suits with the widest variety of uses, owing to the wide variety of equipment that can be installed on them. Like voidsuits, they come in different, specialized varieties, each one offering different protections and different equipment.
Equipment that can be installed includes weapons, power tools, mining equipment, medical equipment, AI assistants, and more.<BR><BR>
Putting these on is relatively simple. They come as compact packs, worn like a backpack and secured with a harness. Activating them, though, is a more complex process. The suit deploys from the module similarly to helmets deploying from voidsuits.
After it covers the whole body, the suit can be started. The startup sequence takes some time. The suit will automatically fit itself to your body, sealing each section individually - boots, gloves, pants, torso, and helmet - then connecting them.<BR><BR>
Operating a hardsuit is a much more complicated proposal than operating other EVA equipment. While putting them on is relatively simple, and operating basic functions like oxygen and magboots is the same as other suits, the rest is far more complex.
Consult the operator's manual for invidual pieces of equipment that you plan to use. Use of these for heavy work is only reccomended for people who have specialized training and extensive EVA experience.
The potential of a suit breach is always there, and the use of powered equipment raises it significantly.<BR><BR>
<h2><a name="Advice">Miscellaneous Advice</a></h2>
<I>Pro tip: Safety first.</I><BR>
There's a lot of general advice that can be helpful for people who haven't taken a long-form instruction course. Much of this is going to be fairly obvious safety advice, but it's never bad to remind yourself of that.<BR><BR>
<ul>
<li>Magboots are important. They can be the difference between keeping your footing and needing a rescue team. A tie-off or a jetpack can substitute if necessary.</li>
<li>Be aware of breach hazards, especially in softsuits. Loss of suit pressure can be a fatal disaster.</li>
<li>Keep an eye on your internals. Having to make two trips outside is better than running out of air.</li>
<li>Similarly, keep an eye on the battery status of cooling units and other equipment.</li>
<li>In vacuum, sound doesn't carry. Use a radio or sign language for communication.</li><BR><BR>
</ul>
<h2><a name="Equipment">Modification Equipment</a></h2>
<I>How to actually make hardsuits fit you.</I><BR>
<I>How to actually make voidsuits fit you.</I><BR>
There's a variety of equipment that can modify hardsuits to fit species that can't fit into them, making life quite a bit easier.<BR><BR>
The first piece of equipment is a suit cycler. This is a large machine resembling the storage pods that are in place in some places. These are machines that will automatically tailor a suit to certain specifications.
The largest uses of them are for their cleaning functions and their ability to tailor suits for a species. Do not enter them physically. You will die from any of the functions being activated, and it will be painful.
The largest uses of them are for their cleaning functions and their ability to tailor suits for a species. Do not enter them physically. You will die from any of the functions being activated, and it will hurt the whole time you're dying.
These machines can both tailor a suit between species, and between types. This means you can convert engineering hardsuits to atmospherics, or the other way. This is useful. Use it if you can.<BR><BR>
There's also modification kits that let you modify suits yourself. These are extremely difficult to use unless you understand the actual construction of the suit. I do not reccomend using them unless no other option is available.
There's also modification kits that let you modify suits yourself. These are extremely difficult to use unless you understand the actual construction of the suit. I do not reccomend using them unless no other option is available.<BR><BR>
<h2><a name="Final">Final Checks</a></h2>
<ul>
<li>Are all seals fastened correctly?</li>
<li>If you have modified it manually, is absolutely everything sealed perfectly?</li>
<li>Do you either have shoes on under the suit, or magnetic boots on over it?</li>
<li>Do you have a mask on and internals on the suit or your back?</li>
<li>Do you have internals connected and activated?</li>
<li>Do you have a way to communicate with the station in case something goes wrong?</li>
<li>Do you have a second person watching if this is a training session?</li><BR>
</ul>
@@ -9,6 +9,7 @@
default_material = "wood"
force_divisor = 1.1 // 22 when wielded with weight 20 (steel)
unwielded_force_divisor = 0.7 // 15 when unwielded based on above.
dulled_divisor = 0.75 // A "dull" bat is still gonna hurt
slot_flags = SLOT_BACK
//Predefined materials go here.
@@ -94,7 +94,6 @@
icon_state = "tacknife"
item_state = "knife"
applies_material_colour = 0
unbreakable = 1
/obj/item/weapon/material/kitchen/utensil/knife/attack(target as mob, mob/living/user as mob)
if ((CLUMSY in user.mutations) && prob(50))
@@ -117,6 +116,7 @@
attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked")
default_material = "wood"
force_divisor = 0.7 // 10 when wielded with weight 15 (wood)
dulled_divisor = 0.75 // Still a club
thrown_force_divisor = 1 // as above
/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob)
@@ -33,7 +33,6 @@
name = "switchblade"
desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster."
icon_state = "switchblade"
unbreakable = 1
/obj/item/weapon/material/butterfly/boxcutter
name = "box cutter"
@@ -67,7 +66,6 @@
matter = list(DEFAULT_WALL_MATERIAL = 12000)
origin_tech = "materials=1"
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
unbreakable = 1
/obj/item/weapon/material/knife/suicide_act(mob/user)
viewers(user) << pick("<span class='danger'>\The [user] is slitting \his wrists with \the [src]! It looks like \he's trying to commit suicide.</span>", \
@@ -16,9 +16,12 @@
)
var/applies_material_colour = 1
var/unbreakable
var/unbreakable = 0 //Doesn't lose health
var/fragile = 0 //Shatters when it dies
var/dulled = 0 //Has gone dull
var/force_divisor = 0.5
var/thrown_force_divisor = 0.5
var/dulled_divisor = 0.5 //Just drops the damage by half
var/default_material = DEFAULT_WALL_MATERIAL
var/material/material
var/drops_debris = 1
@@ -47,6 +50,8 @@
else
force = material.get_blunt_damage()
force = round(force*force_divisor)
if(dulled)
force = round(force*dulled_divisor)
throwforce = round(material.get_blunt_damage()*thrown_force_divisor)
//spawn(1)
// world << "[src] has force [force] and throwforce [throwforce] when made from default material [material.name]"
@@ -77,9 +82,18 @@
health--
check_health()
/obj/item/weapon/material/attackby(obj/item/weapon/W, mob/user as mob)
if(istype(W, /obj/item/weapon/whetstone))
var/obj/item/weapon/whetstone/whet = W
repair(whet.repair_amount, whet.repair_time, user)
..()
/obj/item/weapon/material/proc/check_health(var/consumed)
if(health<=0)
shatter(consumed)
if(fragile)
shatter(consumed)
else if(!dulled)
dull()
/obj/item/weapon/material/proc/shatter(var/consumed)
var/turf/T = get_turf(src)
@@ -90,6 +104,34 @@
playsound(src, "shatter", 70, 1)
if(!consumed && drops_debris) material.place_shard(T)
qdel(src)
/obj/item/weapon/material/proc/dull()
var/turf/T = get_turf(src)
T.visible_message("<span class='danger'>\The [src] goes dull!</span>")
playsound(src, "shatter", 70, 1)
dulled = 1
if(is_sharp() || has_edge())
sharp = 0
edge = 0
/obj/item/weapon/material/proc/repair(var/repair_amount, var/repair_time, mob/living/user)
if(!fragile)
if(health < initial(health))
user.visible_message("[user] begins repairing \the [src].", "You begin repairing \the [src].")
if(do_after(user, repair_time))
user.visible_message("[user] has finished repairing \the [src]", "You finish repairing \the [src].")
health = min(health + repair_amount, initial(health))
dulled = 0
sharp = initial(sharp)
edge = initial(edge)
else
to_chat(user, "<span class='notice'>[src] doesn't need repairs.</span>")
else
to_chat(user, "<span class='warning'>You can't repair \the [src].</span>")
return
/*
Commenting this out pending rebalancing of radiation based on small objects.
/obj/item/weapon/material/process()
@@ -15,6 +15,7 @@
gender = PLURAL
w_class = ITEMSIZE_SMALL
force_divisor = 0.63
dulled_divisor = 0.75 //It's a heavy bit of metal
attack_verb = list("punched", "beaten", "struck")
applies_material_colour = 0
@@ -82,6 +83,7 @@
icon_state = "hoe"
force_divisor = 0.25 // 5 with weight 20 (steel)
thrown_force_divisor = 0.25 // as above
dulled_divisor = 0.75 //Still metal on a long pole
w_class = ITEMSIZE_SMALL
attack_verb = list("slashed", "sliced", "cut", "clawed")
@@ -85,6 +85,7 @@
desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
unwielded_force_divisor = 0.25
force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor
dulled_divisor = 0.75 //Still metal on a stick
sharp = 1
edge = 1
w_class = ITEMSIZE_LARGE
@@ -140,4 +141,5 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
default_material = "glass"
applies_material_colour = 0
applies_material_colour = 0
fragile = 1 //It's a haphazard thing of glass, wire, and steel
@@ -0,0 +1,11 @@
//This is in the material folder because it's used by them...
//Actual name may need to change
//All of the important code is in material_weapons.dm
/obj/item/weapon/whetstone
name = "whetstone"
desc = "A simple, fine grit stone, useful for sharpening dull edges and polishing out dents."
icon_state = "whetstone"
force = 3
w_class = ITEMSIZE_SMALL
var/repair_amount = 5
var/repair_time = 40
@@ -12,6 +12,7 @@
if(isliving(user))
if(!owner)
set_name(user.name)
to_chat(user, "[src] registers your name.")
else
to_chat(user, "[src] already has an owner!")
@@ -23,7 +23,9 @@
/obj/item/weapon/photo,
/obj/item/weapon/reagent_containers/dropper,
/obj/item/weapon/screwdriver,
/obj/item/weapon/stamp)
/obj/item/weapon/stamp,
/obj/item/weapon/permit
)
slot_flags = SLOT_ID
var/obj/item/weapon/card/id/front_id = null
+1 -1
View File
@@ -136,7 +136,7 @@
/obj/effect/energy_net/user_unbuckle_mob(mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
visible_message("<span class='danger'>[user] begins to tear at \the [src]!</span>")
if(do_after(usr, escape_time, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY)))
if(do_after(usr, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY)))
if(!buckled_mob)
return
visible_message("<span class='danger'>[user] manages to tear \the [src] apart!</span>")
+1 -1
View File
@@ -27,7 +27,7 @@
..()
/obj/structure/curtain/proc/toggle()
opacity = !opacity
set_opacity(!opacity)
if(opacity)
icon_state = "closed"
layer = SHOWER_CLOSED_LAYER
+4 -4
View File
@@ -32,9 +32,9 @@
name = "[material.display_name] door"
color = material.icon_colour
if(material.opacity < 0.5)
opacity = 0
set_opacity(0)
else
opacity = 1
set_opacity(1)
if(material.products_need_process())
processing_objects |= src
update_nearby_tiles(need_rebuild=1)
@@ -99,7 +99,7 @@
flick("[material.door_icon_base]opening",src)
sleep(10)
density = 0
opacity = 0
set_opacity(0)
state = 1
update_icon()
isSwitchingStates = 0
@@ -111,7 +111,7 @@
flick("[material.door_icon_base]closing",src)
sleep(10)
density = 1
opacity = 1
set_opacity(1)
state = 0
update_icon()
isSwitchingStates = 0
@@ -22,6 +22,9 @@
/obj/effect/wingrille_spawn/attack_generic()
activate()
/obj/effect/wingrille_spawn/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
return FALSE
/obj/effect/wingrille_spawn/initialize()
..()
if(!win_path)
+2 -2
View File
@@ -11,7 +11,7 @@
update_icon()
set_light(0)
src.blocks_air = 0
src.opacity = 0
set_opacity(0)
for(var/turf/simulated/turf in loc)
air_master.mark_for_update(turf)
else
@@ -21,7 +21,7 @@
update_icon()
set_light(1)
src.blocks_air = 1
src.opacity = 1
set_opacity(1)
for(var/turf/simulated/turf in loc)
air_master.mark_for_update(turf)
-1
View File
@@ -21,7 +21,6 @@
var/icon_old = null
var/pathweight = 1 // How much does it cost to pathfind over this turf?
var/blessed = 0 // Has the turf been blessed?
var/dynamic_lighting = 1 // Does the turf use dynamic lighting?
var/list/decals
+15 -13
View File
@@ -30,9 +30,10 @@
var/obj/fire/old_fire = fire
var/old_opacity = opacity
var/old_dynamic_lighting = dynamic_lighting
var/list/old_affecting_lights = affecting_lights
var/old_affecting_lights = affecting_lights
var/old_lighting_overlay = lighting_overlay
var/old_weather_overlay = weather_overlay
var/old_corners = corners
//world << "Replacing [src.type] with [N]"
@@ -94,15 +95,16 @@
W.post_change()
. = W
lighting_overlay = old_lighting_overlay
affecting_lights = old_affecting_lights
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting) || force_lighting_update)
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(dynamic_lighting)
lighting_build_overlays()
else
lighting_clear_overlays()
else
if(lighting_overlay)
lighting_overlay.update_overlay()
recalc_atom_opacity()
if(lighting_overlays_initialised)
lighting_overlay = old_lighting_overlay
affecting_lights = old_affecting_lights
corners = old_corners
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting))
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(dynamic_lighting)
lighting_build_overlay()
else
lighting_clear_overlay()
+2 -1
View File
@@ -162,7 +162,8 @@ var/list/admin_verbs_server = list(
/client/proc/check_customitem_activity,
/client/proc/nanomapgen_DumpImage,
/client/proc/modify_server_news,
/client/proc/recipe_dump
/client/proc/recipe_dump,
/client/proc/panicbunker
)
var/list/admin_verbs_debug = list(
/client/proc/getruntimelog, //allows us to access runtime logs to somebody,
+2 -2
View File
@@ -977,7 +977,7 @@
if(!check_rights(R_DEBUG))
return
var/datum/planet/planet = input(usr, "Which planet do you want to modify the weather on?", "Change Weather") in list(planet_sif)
var/datum/planet/planet = input(usr, "Which planet do you want to modify the weather on?", "Change Weather") in planet_controller.planets
var/datum/weather/new_weather = input(usr, "What weather do you want to change to?", "Change Weather") as null|anything in planet.weather_holder.allowed_weather_types
if(new_weather)
planet.weather_holder.change_weather(new_weather)
@@ -993,7 +993,7 @@
if(!check_rights(R_DEBUG))
return
var/datum/planet/planet = input(usr, "Which planet do you want to modify time on?", "Change Time") in list(planet_sif)
var/datum/planet/planet = input(usr, "Which planet do you want to modify time on?", "Change Time") in planet_controller.planets
var/datum/time/current_time_datum = planet.current_time
var/new_hour = input(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) as null|num
+15
View File
@@ -0,0 +1,15 @@
/client/proc/panicbunker()
set category = "Server"
set name = "Toggle Panic Bunker"
if (!config.sql_enabled)
to_chat(usr, "<span class='adminnotice'>The Database is not enabled!</span>")
return
config.panic_bunker = (!config.panic_bunker)
log_admin("[key_name(usr)] has toggled the Panic Bunker, it is now [(config.panic_bunker?"on":"off")]")
message_admins("[key_name_admin(usr)] has toggled the Panic Bunker, it is now [(config.panic_bunker?"enabled":"disabled")].")
if (config.panic_bunker && (!dbcon || !dbcon.IsConnected()))
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
feedback_add_details("admin_verb","PANIC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+9 -6
View File
@@ -461,14 +461,17 @@ Traitors and the like can also be revived with the previous role mostly intact.
//Write the appearance and whatnot out to the character
picked_client.prefs.copy_to(new_character)
if(new_character.dna)
new_character.dna.ResetUIFrom(new_character)
new_character.sync_organ_dna()
if(inhabit)
new_character.key = player_key
//Were they any particular special role? If so, copy.
var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role)
if(antag_data)
antag_data.add_antagonist(new_character.mind)
antag_data.place_mob(new_character)
//Were they any particular special role? If so, copy.
if(new_character.mind)
var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role)
if(antag_data)
antag_data.add_antagonist(new_character.mind)
antag_data.place_mob(new_character)
//If desired, apply equipment.
if(equipment)
+8 -1
View File
@@ -251,6 +251,14 @@
var/sql_computerid = sql_sanitize_text(src.computer_id)
var/sql_admin_rank = sql_sanitize_text(admin_rank)
//Panic bunker code
if (isnum(player_age) && player_age == 0) //first connection
if (config.panic_bunker && !holder && !deadmin_holder)
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "Sorry but the server is currently not accepting connections from never before seen players.")
qdel(src)
return 0
if(sql_id)
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
@@ -266,7 +274,6 @@
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `erro_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');")
query_accesslog.Execute()
#undef TOPIC_SPAM_DELAY
#undef UPLOAD_LIMIT
#undef MIN_CLIENT_VERSION
+2
View File
@@ -8,6 +8,8 @@
flags_inv = 0
siemens_coefficient = 0.9
action_button_name = "Toggle Head-light"
w_class = ITEMSIZE_NORMAL
ear_protection = 1
/obj/item/clothing/head/hardhat/orange
icon_state = "hardhat0_orange"
+4 -4
View File
@@ -2,7 +2,7 @@
/obj/item/clothing/head/helmet/space/skrell
name = "Skrellian helmet"
desc = "Smoothly contoured and polished to a shine. Still looks like a fishbowl."
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50)
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
species_restricted = list("Skrell","Human")
@@ -15,7 +15,7 @@
/obj/item/clothing/suit/space/skrell
name = "Skrellian voidsuit"
desc = "Seems like a wetsuit with reinforced plating seamlessly attached to it. Very chic."
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
armor = list(melee = 20, bullet = 20, laser = 20, energy = 50, bomb = 50, bio = 100, rad = 50)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
@@ -35,14 +35,14 @@
allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank)
phoronproof = 1
slowdown = 2
armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40,energy = 15, bomb = 30, bio = 100, rad = 50)
siemens_coefficient = 0.2
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
species_restricted = list("Vox")
/obj/item/clothing/head/helmet/space/vox
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
siemens_coefficient = 0.2
item_flags = STOPPRESSUREDAMAGE | THICKMATERIAL | AIRTIGHT | PHORONGUARD
flags_inv = 0
@@ -149,17 +149,19 @@
/obj/item/rig_module/chem_dispenser/ninja
interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible."
//just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face.
//Want more? Go refill. Gives the ninja another reason to have to show their face.
charges = list(
list("tricordrazine", "tricordrazine", 0, 20),
list("tramadol", "tramadol", 0, 20),
list("dexalin plus", "dexalinp", 0, 20),
list("antibiotics", "spaceacillin", 0, 20),
list("antitoxins", "anti_toxin", 0, 20),
list("nutrients", "glucose", 0, 80),
list("clotting agent", "myelamine", 0, 80),
list("hyronalin", "hyronalin", 0, 20),
list("radium", "radium", 0, 20)
list("tricordrazine", "tricordrazine", 0, 30),
list("tramadol", "tramadol", 0, 30),
list("dexalin plus", "dexalinp", 0, 30),
list("antibiotics", "spaceacillin", 0, 30),
list("antitoxins", "anti_toxin", 0, 60),
list("nutrients", "glucose", 0, 80),
list("bicaridine", "bicaridine", 0, 30),
list("clotting agent", "myelamine", 0, 30),
list("peridaxon", "peridaxon", 0, 30),
list("hyronalin", "hyronalin", 0, 30),
list("radium", "radium", 0, 30)
)
/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user)
@@ -45,7 +45,7 @@
desc = "This metal box writhes and squirms as if it were alive..."
suit_type = "alien"
icon_state = "vox_rig"
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
item_flags = THICKMATERIAL
siemens_coefficient = 0.2
phoronproof = 1
@@ -88,7 +88,7 @@
/obj/item/weapon/rig/vox/carapace
name = "dense alien control module"
suit_type = "dense alien"
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 30, rad = 30)
armor = list(melee = 60, bullet = 50, laser = 40, energy = 15, bomb = 30, bio = 100, rad = 50)
emp_protection = 40 //change this to 30 if too high.
phoronproof = 1
@@ -109,7 +109,7 @@
name = "sinister alien control module"
suit_type = "sinister alien"
icon_state = "voxstealth_rig"
armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 100)
armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 30, bio = 100, rad = 50)
emp_protection = 40 //change this to 30 if too high.
phoronproof = 1
@@ -93,7 +93,7 @@
/obj/item/rig_module/vision,
/obj/item/rig_module/voice,
/obj/item/rig_module/fabricator/energy_net,
/obj/item/rig_module/chem_dispenser,
/obj/item/rig_module/chem_dispenser/ninja,
/obj/item/rig_module/grenade_launcher,
/obj/item/rig_module/ai_container,
/obj/item/rig_module/power_sink,
+1 -5
View File
@@ -296,11 +296,7 @@
// Handle light requirements.
if(!light_supplied)
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in current_turf
if(L)
light_supplied = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
else
light_supplied = 5
light_supplied = current_turf.get_lumcount() * 5
if(light_supplied)
if(abs(light_supplied - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE))
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
@@ -192,7 +192,7 @@
if(growth>2 && growth == max_growth)
layer = 5
opacity = 1
set_opacity(1)
if(!isnull(seed.chems["woodpulp"]))
density = 1
else
+1 -6
View File
@@ -625,12 +625,7 @@
if(closed_system && mechanical)
light_string = "that the internal lights are set to [tray_light] lumens"
else
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
var/light_available
if(L)
light_available = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
else
light_available = 5
var/light_available = T.get_lumcount() * 5
light_string = "a light level of [light_available] lumens"
usr << "The tray's sensor suite is reporting [light_string] and a temperature of [environment.temperature]K at [environment.return_pressure()] kPa in the [environment_type] environment"
@@ -21,11 +21,8 @@
var/max_power = 1
/obj/item/integrated_circuit/passive/power/solar_cell/make_energy()
var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(src)
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(src)
var/light_amount = T ? T.get_lumcount() : 0
var/adjusted_power = max(max_power * light_amount, 0)
adjusted_power = round(adjusted_power, 0.1)
if(adjusted_power)
+4 -4
View File
@@ -14,9 +14,9 @@ Changes from tg DAL:
- light_range; range in tiles of the light, used for calculating falloff,
- light_power; multiplier for the brightness of lights,
- light_color; hex string representing the RGB colour of the light.
- SetLuminosity() is now set_light() and takes the three variables above.
- setLuminousity() is now set_light() and takes the three variables above.
- Variables can be left as null to not update them.
- SetOpacity() is now set_opacity().
- set_opacity() is now set_opacity().
- Areas have luminosity set to 1 permanently, no hard-lighting.
- Objects inside other objects can have lights and they properly affect the turf. (flashlights)
- area/master and area/list/related have been eviscerated since subareas aren't needed.
@@ -51,7 +51,7 @@ turf: (lighting_turf.dm)
- proc/lighting_clear_overlays():
- Delete (manual GC) all light overlays on this turf, used when changing turf to space
- proc/lighting_build_overlays():
- Create lighting overlays for this turf. Called by ChangeTurf in case the turf is being changed to use dynamic lighting.
- Create lighting overlays for this turf
atom/movable/lighting_overlay: (lighting_overlay.dm)
@@ -64,4 +64,4 @@ atom/movable/lighting_overlay: (lighting_overlay.dm)
- Change the lumcount vars and queue the overlay for update
- proc/update_overlay()
- Called by the lighting process to update the color of the overlay
*/
*/
-9
View File
@@ -1,9 +0,0 @@
#define LIGHTING_INTERVAL 5 // frequency, in 1/10ths of a second, of the lighting process
#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square
#define LIGHTING_LAMBERTIAN 1 // 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_ROUND_VALUE (1 / 128) //Value used to round lumcounts, values smaller than 1/255 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_LAYER 10 // drawing layer for lighting overlays
#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
-300
View File
@@ -1,300 +0,0 @@
//So much copypasta in this file, supposedly in the name of performance. If you change anything make sure to consider other places where the code may have been copied.
/datum/light_source
var/atom/top_atom
var/atom/source_atom
var/turf/source_turf
var/light_power
var/light_range
var/light_color // string, decomposed by parse_light_color()
var/lum_r
var/lum_g
var/lum_b
//hold onto the actual applied lum values so we can undo them when the lighting changes
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/effect_str
var/list/effect_turf
var/applied
var/vis_update //Whetever we should smartly recalculate visibility. and then only update tiles that became (in) visible to us
var/needs_update
var/destroyed
var/force_update
/datum/light_source/New(atom/owner, atom/top)
source_atom = owner
if(!source_atom.light_sources) source_atom.light_sources = list()
source_atom.light_sources += src
top_atom = top
if(top_atom != source_atom)
if(!top.light_sources) top.light_sources = list()
top_atom.light_sources += src
source_turf = top_atom
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
effect_str = list()
effect_turf = list()
update()
return ..()
/datum/light_source/proc/destroy()
destroyed = 1
force_update()
if(source_atom && source_atom.light_sources) source_atom.light_sources -= src
if(top_atom && top_atom.light_sources) top_atom.light_sources -= src
/datum/light_source/proc/update(atom/new_top_atom)
if(new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom) top_atom.light_sources -= src
top_atom = new_top_atom
if(top_atom != source_atom)
if(!top_atom.light_sources) top_atom.light_sources = list()
top_atom.light_sources += src
if(!needs_update) //Incase we're already updating either way.
lighting_update_lights += src
needs_update = 1
/datum/light_source/proc/force_update()
force_update = 1
if(!needs_update) //Incase we're already updating either way.
needs_update = 1
lighting_update_lights += src
/datum/light_source/proc/vis_update()
if(!needs_update)
needs_update = 1
lighting_update_lights += src
vis_update = 1
/datum/light_source/proc/check()
if(!source_atom || !light_range || !light_power)
destroy()
return 1
if(!top_atom)
top_atom = source_atom
. = 1
if(istype(top_atom, /turf))
if(source_turf != top_atom)
source_turf = top_atom
. = 1
else if(top_atom.loc != source_turf)
source_turf = top_atom.loc
. = 1
if(source_atom.light_power != light_power)
light_power = source_atom.light_power
. = 1
if(source_atom.light_range != light_range)
light_range = source_atom.light_range
. = 1
if(light_range && light_power && !applied)
. = 1
if(source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
. = 1
/datum/light_source/proc/parse_light_color()
if(light_color)
lum_r = GetRedPart(light_color) / 255
lum_g = GetGreenPart(light_color) / 255
lum_b = GetBluePart(light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
#if LIGHTING_FALLOFF == 1 //circular
#define LUM_DISTANCE(swapvar, O, T) swapvar = (O.x - T.x)**2 + (O.y - T.y)**2 + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(sqrt(swapvar) / max(1,light_range))) * (1 / sqrt(swapvar + 1)))
#else
#define LUM_ATTENUATION(swapvar) swapvar = 1 - CLAMP01(sqrt(swapvar) / max(1,light_range))
#endif
#elif LIGHTING_FALLOFF == 2 //square
#define LUM_DISTANCE(swapvar, O, T) swapvar = abs(O.x - T.x) + abs(O.y - T.y) + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(swapvar / max(1,light_range))) * (1 / sqrt(swapvar**2 + 1)))
#else
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01(swapvar / max(1,light_range))
#endif
#endif
#define LUM_FALLOFF(swapvar, O, T) \
LUM_DISTANCE(swapvar, O, T); \
LUM_ATTENUATION(swapvar);
/datum/light_source/proc/apply_lum()
applied = 1
//Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
if(istype(source_turf))
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
if(T.lighting_overlay)
var/strength
LUM_FALLOFF(strength, T, source_turf)
strength *= light_power
if(!strength) //Don't add turfs that aren't affected to the affected turfs.
continue
strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
effect_str += strength
T.lighting_overlay.update_lumcount(
applied_lum_r * strength,
applied_lum_g * strength,
applied_lum_b * strength
)
else
effect_str += 0
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
effect_turf += T
END_FOR_DVIEW
/datum/light_source/proc/remove_lum()
applied = 0
var/i = 1
for(var/turf/T in effect_turf)
if(T.affecting_lights)
T.affecting_lights -= src
if(T.lighting_overlay)
var/str = effect_str[i]
T.lighting_overlay.update_lumcount(
-str * applied_lum_r,
-str * applied_lum_g,
-str * applied_lum_b
)
i++
effect_str.Cut()
effect_turf.Cut()
//Smartly updates the lighting, only removes lum from and adds lum to turfs that actually got changed.
//This is for lights that need to reconsider due to nearby opacity changes.
//Stupid dumb copy pasta because BYOND and speed.
/datum/light_source/proc/smart_vis_update()
var/list/view[0]
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
view += T //Filter out turfs.
END_FOR_DVIEW
//This is the part where we calculate new turfs (if any)
var/list/new_turfs = view - effect_turf //This will result with all the tiles that are added.
for(var/turf/T in new_turfs)
if(T.lighting_overlay)
LUM_FALLOFF(., T, source_turf)
. *= light_power
if(!.) //Don't add turfs that aren't affected to the affected turfs.
continue
. = round(., LIGHTING_ROUND_VALUE)
effect_str += .
T.lighting_overlay.update_lumcount(
applied_lum_r * .,
applied_lum_g * .,
applied_lum_b * .
)
else
effect_str += 0
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
effect_turf += T
var/list/old_turfs = effect_turf - view
for(var/turf/T in old_turfs)
//Insert not-so-huge copy paste from remove_lum().
var/idx = effect_turf.Find(T) //Get the index, luckily Find() is cheap in small lists like this. (with small I mean under a couple thousand len)
if(T.affecting_lights)
T.affecting_lights -= src
if(T.lighting_overlay)
var/str = effect_str[idx]
T.lighting_overlay.update_lumcount(-str * applied_lum_r, -str * applied_lum_g, -str * applied_lum_b)
effect_turf.Cut(idx, idx + 1)
effect_str.Cut(idx, idx + 1)
//Whoop yet not another copy pasta because speed ~~~~BYOND.
//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example).
//Assumes the turf is visible and such.
//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen!
/datum/light_source/proc/calc_turf(var/turf/T)
var/idx = effect_turf.Find(T)
if(!idx)
return //WHY.
if(T.lighting_overlay)
#if LIGHTING_FALLOFF == 1 // circular
. = (T.lighting_overlay.x - source_turf.x)**2 + (T.lighting_overlay.y - source_turf.y)**2 + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
. = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1))))
#else
. = 1 - CLAMP01(sqrt(.) / light_range)
#endif
#elif LIGHTING_FALLOFF == 2 // square
. = abs(T.lighting_overlay.x - source_turf.x) + abs(T.lighting_overlay.y - source_turf.y) + LIGHTING_HEIGHT
#if LIGHTING_LAMBERTIAN == 1
. = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + )))
#else
. = 1 - CLAMP01(. / light_range)
#endif
#endif
. *= light_power
. = round(., LIGHTING_ROUND_VALUE)
effect_str[idx] = .
//Since the applied_lum values are what are (later) removed by remove_lum.
//Anything we apply to the lighting overlays HAS to match what remove_lum uses.
T.lighting_overlay.update_lumcount(
applied_lum_r * .,
applied_lum_g * .,
applied_lum_b * .
)
#undef LUM_FALLOFF
#undef LUM_DISTANCE
#undef LUM_ATTENUATION
+9
View File
@@ -0,0 +1,9 @@
/area
luminosity = TRUE
var/dynamic_lighting = TRUE
/area/New()
. = ..()
if(dynamic_lighting)
luminosity = FALSE
+38 -11
View File
@@ -1,12 +1,14 @@
/atom
var/light_power = 1 // intensity of the light
var/light_range = 0 // range in tiles of the light
var/light_color // RGB string representing the colour of the light
var/light_color // Hexadecimal RGB string representing the colour of the light
var/datum/light_source/light
var/list/light_sources
/atom/proc/set_light(l_range, l_power, l_color)
// Nonsensical value for l_color default, so we can detect if it gets set to null.
#define NONSENSICAL_VALUE -99999
/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE)
. = 0 //make it less costly if nothing's changed
if(l_power != null && l_power != light_power)
@@ -15,16 +17,17 @@
if(l_range != null && l_range != light_range)
light_range = l_range
. = 1
if(l_color != null && l_color != light_color)
if(l_color != NONSENSICAL_VALUE && l_color != light_color)
light_color = l_color
. = 1
if(.) update_light()
/atom/proc/copy_light(atom/A)
set_light(A.light_range, A.light_power, A.light_color)
#undef NONSENSICAL_VALUE
/atom/proc/update_light()
set waitfor = FALSE
if(!light_power || !light_range)
if(light)
light.destroy()
@@ -42,9 +45,14 @@
/atom/New()
. = ..()
if(light_power && light_range)
update_light()
if(opacity && isturf(loc))
var/turf/T = loc
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways.
/atom/Destroy()
if(light)
light.destroy()
@@ -57,19 +65,38 @@
T.reconsider_lights()
return ..()
/atom/Entered(atom/movable/obj, atom/prev_loc)
/atom/movable/Move()
var/turf/old_loc = loc
. = ..()
if(obj && prev_loc != src)
for(var/datum/light_source/L in obj.light_sources)
if(loc != old_loc)
for(var/datum/light_source/L in light_sources)
L.source_atom.update_light()
var/turf/new_loc = loc
if(istype(old_loc) && opacity)
old_loc.reconsider_lights()
if(istype(new_loc) && opacity)
new_loc.reconsider_lights()
/atom/proc/set_opacity(new_opacity)
var/old_opacity = opacity
if(new_opacity == opacity)
return
opacity = new_opacity
var/turf/T = loc
if(old_opacity != new_opacity && istype(T))
var/turf/T = isturf(src) ? src : loc
if(!isturf(T))
return
if(new_opacity == TRUE)
T.has_opaque_atom = TRUE
T.reconsider_lights()
else
var/old_has_opaque_atom = T.has_opaque_atom
T.recalc_atom_opacity()
if(old_has_opaque_atom != T.has_opaque_atom)
T.reconsider_lights()
/obj/item/equipped()
. = ..()
+133
View File
@@ -0,0 +1,133 @@
/var/total_lighting_corners = 0
/var/datum/lighting_corner/dummy/dummy_lighting_corner = new
// Because we can control each corner of every lighting overlay.
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
/var/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)
/datum/lighting_corner
var/list/turf/masters = list()
var/list/datum/light_source/affecting = list() // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.
var/x = 0
var/y = 0
var/z = 0
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
var/needs_update = FALSE
var/cache_r = LIGHTING_SOFT_THRESHOLD
var/cache_g = LIGHTING_SOFT_THRESHOLD
var/cache_b = LIGHTING_SOFT_THRESHOLD
var/cache_mx = 0
var/update_gen = 0
/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
. = ..()
total_lighting_corners++
masters[new_turf] = turn(diagonal, 180)
z = new_turf.z
var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5)
y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5)
// My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
// So we'll have this hardcode instead.
var/turf/T
var/i
// Diagonal one is easy.
T = get_step(new_turf, diagonal)
if (T) // In case we're on the map's border.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = diagonal
i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
// Now the horizontal one.
T = get_step(new_turf, horizontal)
if (T) // Ditto.
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
// And finally the vertical one.
T = get_step(new_turf, vertical)
if (T)
if (!T.corners)
T.corners = list(null, null, null, null)
masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
update_active()
/datum/lighting_corner/proc/update_active()
active = FALSE
for (var/turf/T in masters)
if (T.lighting_overlay)
active = TRUE
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b)
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if (!needs_update)
needs_update = TRUE
lighting_update_corners += src
/datum/lighting_corner/proc/update_overlays()
// Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually.
var/lum_r = src.lum_r
var/lum_g = src.lum_g
var/lum_b = src.lum_b
var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1.
. = 1 // factor
if (mx > 1)
. = 1 / mx
#if LIGHTING_SOFT_THRESHOLD != 0
else if (mx < LIGHTING_SOFT_THRESHOLD)
. = 0 // 0 means soft lighting.
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
#else
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE)
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE)
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE)
#endif
cache_mx = round(mx, LIGHTING_ROUND_VALUE)
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_overlay)
if (!T.lighting_overlay.needs_update)
T.lighting_overlay.needs_update = TRUE
lighting_update_overlays += T.lighting_overlay
/datum/lighting_corner/dummy/New()
return
+93 -70
View File
@@ -1,102 +1,125 @@
/var/total_lighting_overlays = 0
/atom/movable/lighting_overlay
name = ""
mouse_opacity = 0
simulated = 0
anchored = 1
icon = LIGHTING_ICON
icon_state = "light1"
layer = LIGHTING_LAYER
invisibility = INVISIBILITY_LIGHTING
color = "#000000"
color = LIGHTING_BASE_MATRIX
icon_state = "light1"
auto_init = 0 // doesn't need special init
blend_mode = BLEND_MULTIPLY
var/lum_r
var/lum_g
var/lum_b
var/lum_r = 0
var/lum_g = 0
var/lum_b = 0
var/needs_update
var/needs_update = FALSE
/atom/movable/lighting_overlay/New()
/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE)
. = ..()
verbs.Cut()
total_lighting_overlays++
var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays in things that aren't turfs.
var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays outside of turfs.
T.lighting_overlay = src
T.luminosity = 0
/atom/movable/lighting_overlay/proc/update_lumcount(delta_r, delta_g, delta_b)
if(!delta_r && !delta_g && !delta_b) //Nothing is being changed all together.
if(no_update)
return
var/should_update = 0
if(!needs_update) //If this isn't true, we're already updating anyways.
if(max(lum_r, lum_g, lum_b) < 1) //Any change that could happen WILL change appearance.
should_update = 1
else if(max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) < 1) //The change would bring us under 1 max lum, again, guaranteed to change appearance.
should_update = 1
else //We need to make sure that the colour ratios won't change in this code block.
var/mx1 = max(lum_r, lum_g, lum_b)
var/mx2 = max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b)
if(lum_r / mx1 != (lum_r + delta_r) / mx2 || lum_g / mx1 != (lum_g + delta_g) / mx2 || lum_b / mx1 != (lum_b + delta_b) / mx2) //Stuff would change.
should_update = 1
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
if(!needs_update && should_update)
needs_update = 1
lighting_update_overlays += src
update_overlay()
/atom/movable/lighting_overlay/proc/update_overlay()
set waitfor = FALSE
var/turf/T = loc
if(istype(T)) //Incase we're not on a turf, pool ourselves, something happened.
if(lum_r == lum_g && lum_r == lum_b) //greyscale
blend_mode = BLEND_OVERLAY
if(lum_r <= 0)
T.luminosity = 0
color = "#000000"
alpha = 255
else
T.luminosity = 1
color = "#000000"
alpha = (1 - min(lum_r, 1)) * 255
if(!istype(T))
if(loc)
log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!")
else
alpha = 255
var/mx = max(lum_r, lum_g, lum_b)
. = 1 // factor
if(mx > 1)
. = 1/mx
blend_mode = BLEND_MULTIPLY
color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .)
if(color != "#000000")
T.luminosity = 1
else //No light, set the turf's luminosity to 0 to remove it from view()
T.luminosity = 0
else
warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : ""]) in update_overlay() and got pooled!")
log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
qdel(src)
return
/atom/movable/lighting_overlay/ResetVars()
loc = null
// To the future coder who sees this and thinks
// "Why didn't he just use a loop?"
// Well my man, it's because the loop performed like shit.
// And there's no way to improve it because
// without a loop you can make the list all at once which is the fastest you're gonna get.
// Oh it's also shorter line wise.
// Including with these comments.
lum_r = 0
lum_g = 0
lum_b = 0
// See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
// No I seriously cannot think of a more efficient method, fuck off Comic.
var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner
var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner
var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner
var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner
color = "#000000"
var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
needs_update = 0
var/rr = cr.cache_r
var/rg = cr.cache_g
var/rb = cr.cache_b
var/gr = cg.cache_r
var/gg = cg.cache_g
var/gb = cg.cache_b
var/br = cb.cache_r
var/bg = cb.cache_g
var/bb = cb.cache_b
var/ar = ca.cache_r
var/ag = ca.cache_g
var/ab = ca.cache_b
#if LIGHTING_SOFT_THRESHOLD != 0
var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD
#else
// Because of floating points, it won't even be a flat 0.
// This number is mostly arbitrary.
var/set_luminosity = max > 1e-6
#endif
if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8))
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
icon_state = "transparent"
color = null
else if(!set_luminosity)
icon_state = LIGHTING_ICON_STATE_DARK
color = null
else
icon_state = null
color = list(
rr, rg, rb, 00,
gr, gg, gb, 00,
br, bg, bb, 00,
ar, ag, ab, 00,
00, 00, 00, 01
)
luminosity = set_luminosity
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_overlay/ex_act()
return
/atom/movable/lighting_overlay/singularity_act()
return
/atom/movable/lighting_overlay/singularity_pull()
return
/atom/movable/lighting_overlay/Destroy()
lighting_update_overlays -= src
total_lighting_overlays--
global.lighting_update_overlays -= src
global.lighting_update_overlays_old -= src
var/turf/T = loc
if(istype(T))
T.lighting_overlay = null
..()
T.luminosity = 1
return ..()
-35
View File
@@ -1,35 +0,0 @@
/datum/controller/process/lighting/setup()
name = "lighting"
schedule_interval = LIGHTING_INTERVAL
create_lighting_overlays()
/datum/controller/process/lighting/doWork()
var/list/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 = null //Nulling it first because of http://www.byond.com/forum/?post=1854520
lighting_update_lights = list()
for(var/datum/light_source/L in lighting_update_lights_old)
if(L.destroyed || L.check() || 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 = 0
L.force_update = 0
L.needs_update = 0
SCHECK
var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = null //Same as above
lighting_update_overlays = list()
for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old)
O.update_overlay()
O.needs_update = 0
SCHECK
+16
View File
@@ -0,0 +1,16 @@
/proc/create_all_lighting_overlays()
for(var/zlevel = 1 to world.maxz)
create_lighting_overlays_zlevel(zlevel)
/proc/create_lighting_overlays_zlevel(var/zlevel)
ASSERT(zlevel)
for(var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
if(!T.dynamic_lighting)
continue
var/area/A = T.loc
if(!A.dynamic_lighting)
continue
new /atom/movable/lighting_overlay(T, TRUE)
+290
View File
@@ -0,0 +1,290 @@
/var/total_lighting_sources = 0
// This is where the fun begins.
// These are the main datums that emit light.
/datum/light_source
var/atom/top_atom // The atom we're emitting light from(for example a mob if we're from a flashlight that's being held).
var/atom/source_atom // The atom that we belong to.
var/turf/source_turf // The turf under the above.
var/light_power // Intensity of the emitter light.
var/light_range // The range of the emitted light.
var/light_color // The colour of the light, string, decomposed by parse_light_color()
// Variables for keeping track of the colour.
var/lum_r
var/lum_g
var/lum_b
// The lumcount values used to apply the light.
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
var/list/turf/affecting_turfs
var/applied = FALSE // Whether we have applied our light yet or not.
var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became(in)visible to us.
var/needs_update // Whether we are queued for an update.
var/destroyed // Whether we are destroyed and need to stop emitting light.
var/force_update
/datum/light_source/New(var/atom/owner, var/atom/top)
total_lighting_sources++
source_atom = owner // Set our new owner.
if(!source_atom.light_sources)
source_atom.light_sources = list()
source_atom.light_sources += src // Add us to the lights of our owner.
top_atom = top
if(top_atom != source_atom)
if(!top.light_sources)
top.light_sources = list()
top_atom.light_sources += src
source_turf = top_atom
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
effect_str = list()
affecting_turfs = list()
update()
return ..()
// Kill ourselves.
/datum/light_source/proc/destroy()
total_lighting_sources--
destroyed = TRUE
force_update()
if(source_atom)
if(!source_atom.light_sources)
log_runtime(EXCEPTION("Atom [source_atom] was a light source, but lacked a light source list!\n"), source_atom)
else
source_atom.light_sources -= src
if(top_atom)
top_atom.light_sources -= src
// Call it dirty, I don't care.
// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates.
// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a() macro that has no arguments :|.
#define effect_update(BYOND) \
if(!needs_update) \
{ \
lighting_update_lights += src; \
needs_update = TRUE; \
}
// This proc will cause the light source to update the top atom, and add itself to the update queue.
/datum/light_source/proc/update(var/atom/new_top_atom)
// This top atom is different.
if(new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom.
top_atom.light_sources -= src
top_atom = new_top_atom
if(top_atom != source_atom)
if(!top_atom.light_sources)
top_atom.light_sources = list()
top_atom.light_sources += src // Add ourselves to the light sources of our new top atom.
effect_update(null)
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
force_update = 1
effect_update(null)
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
/datum/light_source/proc/vis_update()
vis_update = 1
effect_update(null)
// Will check if we actually need to update, and update any variables that may need to be updated.
/datum/light_source/proc/check()
if(!source_atom || !light_range || !light_power)
destroy()
return 1
if(!top_atom)
top_atom = source_atom
. = 1
if(isturf(top_atom))
if(source_turf != top_atom)
source_turf = top_atom
. = 1
else if(top_atom.loc != source_turf)
source_turf = top_atom.loc
. = 1
if(source_atom.light_power != light_power)
light_power = source_atom.light_power
. = 1
if(source_atom.light_range != light_range)
light_range = source_atom.light_range
. = 1
if(light_range && light_power && !applied)
. = 1
if(source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
. = 1
// Decompile the hexadecimal colour into lumcounts of each perspective.
/datum/light_source/proc/parse_light_color()
if(light_color)
lum_r = GetRedPart (light_color) / 255
lum_g = GetGreenPart(light_color) / 255
lum_b = GetBluePart (light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
// Macro that applies light to a new corner.
// It is a macro in the interest of speed, yet not having to copy paste it.
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
// As such this all gets counted as a single line.
// The braces and semicolons are there to be able to do this on a single line.
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, source_turf); \
\
. *= light_power; \
\
effect_str[C] = .; \
\
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// I don't need to explain what this does, do I?
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// This is the define used to calculate falloff.
#define LUM_FALLOFF(C, T)(1 - CLAMP01(sqrt((C.x - T.x) ** 2 +(C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
/datum/light_source/proc/apply_lum()
var/static/update_gen = 1
applied = 1
// Keep track of the last applied lum values so that the lighting can be reversed
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
for(var/datum/lighting_corner/C in T.get_corners())
if(C.update_gen == update_gen)
continue
C.update_gen = update_gen
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
if(!T.affecting_lights)
T.affecting_lights = list()
T.affecting_lights += src
affecting_turfs += T
update_gen++
/datum/light_source/proc/remove_lum()
applied = FALSE
for(var/turf/T in affecting_turfs)
if(!T.affecting_lights)
T.affecting_lights = list()
else
T.affecting_lights -= src
affecting_turfs.Cut()
for(var/datum/lighting_corner/C in effect_str)
REMOVE_CORNER(C)
C.affecting -= src
effect_str.Cut()
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
if(effect_str.Find(C)) // Already have one.
REMOVE_CORNER(C)
APPLY_CORNER(C)
/datum/light_source/proc/smart_vis_update()
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
if(!T.lighting_corners_initialised)
T.generate_missing_corners()
corners |= T.get_corners()
turfs += T
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
affecting_turfs += L
for(var/turf/T in L)
if(!T.affecting_lights)
T.affecting_lights = list(src)
else
T.affecting_lights += src
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
affecting_turfs -= L
for(var/turf/T in L)
T.affecting_lights -= src
for(var/datum/lighting_corner/C in corners - effect_str) // New corners
C.affecting += src
if(!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for(var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
REMOVE_CORNER(C)
C.affecting -= src
effect_str -= C
#undef effect_update
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
-25
View File
@@ -1,25 +0,0 @@
/var/list/lighting_update_lights = list()
/var/list/lighting_update_overlays = list()
/area/var/lighting_use_dynamic = 1
// duplicates lots of code, but this proc needs to be as fast as possible.
/proc/create_lighting_overlays(zlevel = 0)
var/area/A
if(zlevel == 0) // populate all zlevels
for(var/turf/T in world)
if(T.dynamic_lighting)
A = T.loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
T.lighting_overlay = O
else
for(var/x = 1; x <= world.maxx; x++)
for(var/y = 1; y <= world.maxy; y++)
var/turf/T = locate(x, y, zlevel)
if(T.dynamic_lighting)
A = T.loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
T.lighting_overlay = O
+85 -15
View File
@@ -1,32 +1,102 @@
/turf
var/list/affecting_lights
var/atom/movable/lighting_overlay/lighting_overlay
var/dynamic_lighting = TRUE // Does the turf use dynamic lighting?
luminosity = 1
var/tmp/lighting_corners_initialised = FALSE
var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf.
var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay.
var/tmp/list/datum/lighting_corner/corners
var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
/turf/New()
. = ..()
if(opacity)
has_opaque_atom = TRUE
// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
for(var/datum/light_source/L in affecting_lights)
L.vis_update()
/turf/proc/lighting_clear_overlays()
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay)
/turf/proc/lighting_build_overlays()
for(var/datum/lighting_corner/C in corners)
C.update_active()
// Builds a lighting overlay for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay()
if(lighting_overlay)
return
var/area/A = loc
if(A.dynamic_lighting)
if(!lighting_corners_initialised)
generate_missing_corners()
new /atom/movable/lighting_overlay(src)
for(var/datum/lighting_corner/C in corners)
if(!C.active) // We would activate the corner, calculate the lighting for it.
for(var/L in C.affecting)
var/datum/light_source/S = L
S.recalc_corner(C)
C.active = TRUE
// Used to get a scaled lumcount.
/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
if(!lighting_overlay)
var/area/A = loc
if(A.lighting_use_dynamic)
var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, src)
lighting_overlay = O
return 1
//Make the light sources recalculate us so the lighting overlay updates immediately
for(var/datum/light_source/L in affecting_lights)
L.calc_turf(src)
var/totallums = 0
for(var/datum/lighting_corner/L in corners)
totallums += max(L.lum_r, L.lum_g, L.lum_b)
/turf/Entered(atom/movable/obj)
totallums /= 4 // 4 corners, max channel selected, return the average
totallums =(totallums - minlum) /(maxlum - minlum)
return CLAMP01(totallums)
// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
/turf/proc/recalc_atom_opacity()
has_opaque_atom = FALSE
for(var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
if(A.opacity)
has_opaque_atom = TRUE
// If an opaque movable atom moves around we need to potentially update visibility.
/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc)
. = ..()
if(obj && obj.opacity)
if(Obj && Obj.opacity)
has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
/turf/Exited(atom/movable/obj)
/turf/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
if(obj && obj.opacity)
if(Obj && Obj.opacity)
recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
/turf/proc/get_corners()
if(has_opaque_atom)
return null // Since this proc gets used in a for loop, null won't be looped though.
return corners
/turf/proc/generate_missing_corners()
lighting_corners_initialised = TRUE
if(!corners)
corners = list(null, null, null, null)
for(var/i = 1 to 4)
if(corners[i]) // Already have a corner on this direction.
continue
corners[i] = new /datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i])
+2 -4
View File
@@ -1,9 +1,7 @@
#undef LIGHTING_INTERVAL
#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_RESOLUTION
#undef LIGHTING_LAYER
#undef LIGHTING_ICON
#undef LIGHTING_BASE_MATRIX
@@ -96,6 +96,7 @@
recipes += new/datum/stack_recipe("knife grip", /obj/item/weapon/material/butterflyhandle, 4, time = 20, one_per_turf = 0, on_floor = 1, supplied_material = "[name]")
recipes += new/datum/stack_recipe("dark floor tile", /obj/item/stack/tile/floor_dark, 1, 4, 20)
recipes += new/datum/stack_recipe("roller bed", /obj/item/roller, 5, time = 30, on_floor = 1)
recipes += new/datum/stack_recipe("whetstone", /obj/item/weapon/whetstone, 2, time = 10)
/material/sandstone/generate_recipes()
..()
+1
View File
@@ -81,6 +81,7 @@ var/list/mining_overlay_cache = list()
/turf/simulated/mineral/proc/update_general()
update_icon(1)
recalc_atom_opacity()
if(ticker && ticker.current_state == GAME_STATE_PLAYING)
reconsider_lights()
if(air_master)
@@ -4,12 +4,7 @@
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = 2 * (min(5,L.lum_r + L.lum_g + L.lum_b) - 2.5) //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 5
light_amount = T.get_lumcount() * 5
nutrition += light_amount
@@ -695,7 +695,7 @@
if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses))
if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/sechud/aviator))
var/obj/item/clothing/glasses/sunglasses/sechud/aviator/S = src.glasses
if(!S.active)
if(!S.on)
number += 1
else if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/medhud/aviator))
number += 0
@@ -235,8 +235,6 @@
nutrition -= 200
// Theoretically the only internal organ a slime will have
// is the slime core. but we might as well be thorough.
for(var/obj/item/organ/I in internal_organs)
if(I.damage > 0)
I.damage = 0
@@ -245,6 +243,7 @@
// Replace completely missing limbs.
for(var/limb_type in src.species.has_limbs)
var/obj/item/organ/external/E = src.organs_by_name[limb_type]
E.disfigured = 0
if(E && (E.is_stump() || (E.status & (ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_MUTATED))))
E.removed()
qdel(E)
+5 -14
View File
@@ -817,11 +817,7 @@
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(loc)) //else, there's considered to be no light
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = min(10,L.lum_r + L.lum_g + L.lum_b) - 2 //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 10
light_amount = T.get_lumcount() * 10
nutrition += light_amount
traumatic_shock -= light_amount
@@ -840,11 +836,7 @@
var/light_amount = 0
if(isturf(loc))
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L)
light_amount = L.lum_r + L.lum_g + L.lum_b //hardcapped so it's not abused by having a ton of flashlights
else
light_amount = 10
light_amount = T.get_lumcount() * 10
if(light_amount > species.light_dam) //if there's enough light, start dying
take_overall_damage(1,1)
else //heal in the dark
@@ -921,9 +913,9 @@
//Brain damage from Oxyloss
if(should_have_organ("brain"))
var/brainOxPercent = 0.02 //Default2% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage
var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage
if(CE_STABLE in chem_effects)
brainOxPercent = 0.01 //Halved in effect
brainOxPercent = 0.008 //Halved in effect
if(oxyloss >= 20 && prob(5))
adjustBrainLoss(brainOxPercent * oxyloss)
@@ -1358,8 +1350,7 @@
//0.1% chance of playing a scary sound to someone who's in complete darkness
if(isturf(loc) && rand(1,1000) == 1)
var/turf/T = loc
var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
if(L && L.lum_r + L.lum_g + L.lum_b == 0)
if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD)
playsound_local(src,pick(scarySounds),50, 1, -1)
/mob/living/carbon/human/handle_stomach()
@@ -230,7 +230,7 @@
if("halt")
if (istype(module,/obj/item/weapon/robot_module/robot/security))
message = "<B>[src]</B>'s speakers skreech, \"Halt! Security!\"."
message = "<B>'s</B> speakers skreech, \"Halt! Security!\"."
playsound(src.loc, 'sound/voice/halt.ogg', 50, 0)
m_type = 2
+44
View File
@@ -1017,3 +1017,47 @@ mob/proc/yank_out_object()
/mob/proc/is_muzzled()
return 0
/client/proc/check_has_body_select()
return mob && mob.hud_used && istype(mob.zone_sel, /obj/screen/zone_sel)
/client/verb/body_toggle_head()
set name = "body-toggle-head"
set hidden = 1
toggle_zone_sel(list(BP_HEAD, O_EYES, O_MOUTH))
/client/verb/body_r_arm()
set name = "body-r-arm"
set hidden = 1
toggle_zone_sel(list(BP_R_ARM,BP_R_HAND))
/client/verb/body_l_arm()
set name = "body-l-arm"
set hidden = 1
toggle_zone_sel(list(BP_L_ARM,BP_L_HAND))
/client/verb/body_chest()
set name = "body-chest"
set hidden = 1
toggle_zone_sel(list(BP_TORSO))
/client/verb/body_groin()
set name = "body-groin"
set hidden = 1
toggle_zone_sel(list(BP_GROIN))
/client/verb/body_r_leg()
set name = "body-r-leg"
set hidden = 1
toggle_zone_sel(list(BP_R_LEG,BP_R_FOOT))
/client/verb/body_l_leg()
set name = "body-l-leg"
set hidden = 1
toggle_zone_sel(list(BP_L_LEG,BP_L_FOOT))
/client/proc/toggle_zone_sel(list/zones)
if(!check_has_body_select())
return
var/obj/screen/zone_sel/selector = mob.zone_sel
selector.set_selected_zone(next_in_list(mob.zone_sel.selecting,zones))
+10 -1
View File
@@ -78,6 +78,7 @@
if(state >= GRAB_AGGRESSIVE)
animate(affecting, pixel_x = 0, pixel_y = 0, 4, 1)
return affecting
return null
@@ -365,12 +366,20 @@
break_strength++
break_chance_table = list(3, 18, 45, 100)
if(GRAB_KILL)
grab_name = "stranglehold"
break_chance_table = list(5, 20, 40, 80, 100)
//It's easier to break out of a grab by a smaller mob
break_strength += max(size_difference(affecting, assailant), 0)
var/break_chance = break_chance_table[Clamp(break_strength, 1, break_chance_table.len)]
if(prob(break_chance))
if(grab_name)
if(state == GRAB_KILL)
reset_kill_state()
return
else if(grab_name)
affecting.visible_message("<span class='warning'>[affecting] has broken free of [assailant]'s [grab_name]!</span>")
qdel(src)
@@ -388,7 +388,7 @@
balding
name = "Balding Hair"
icon_state = "hair_e"
gender = MALE // turnoff!
gender = MALE
familyman
name = "The Family Man"
@@ -529,9 +529,19 @@
icon_state = "hair_coffeehouse"
gender = MALE
undercut
undercut1
name = "Undercut"
icon_state = "hair_undercut"
icon_state = "hair_undercut1"
gender = MALE
undercut2
name = "Undercut Swept Right"
icon_state = "hair_undercut2"
gender = MALE
undercut3
name = "Undercut Swept Left"
icon_state = "hair_undercut3"
gender = MALE
partfade
@@ -551,7 +561,7 @@
/datum/sprite_accessory/facial_hair
icon = 'icons/mob/Human_face.dmi'
gender = MALE // barf (unless you're a dorf, dorfs dig chix /w beards :P)
gender = MALE
shaved
name = "Shaved"
@@ -1080,11 +1090,13 @@
name = "Color Patches"
icon_state = "patches"
body_parts = list(BP_L_FOOT,BP_R_FOOT,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_L_HAND,BP_R_HAND,BP_TORSO,BP_GROIN)
species_allowed = list("Tajara")
patchesface
name = "Color Patches (Face)"
icon_state = "patchesface"
body_parts = list(BP_HEAD)
species_allowed = list("Tajara")
bands
name = "Color Bands"
+1
View File
@@ -203,6 +203,7 @@ var/list/organ_cache = list()
/obj/item/organ/proc/rejuvenate(var/ignore_prosthetic_prefs)
damage = 0
status = 0
germ_level = 0
if(!ignore_prosthetic_prefs && owner && owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name)
var/status = owner.client.prefs.organ_data[organ_tag]
if(status == "assisted")
+1 -1
View File
@@ -492,7 +492,7 @@ This function completely restores a damaged organ to perfect condition.
//Burn damage can cause fluid loss due to blistering and cook-off
if((damage > 5 || damage + burn_dam >= 15) && type == BURN && (robotic < ORGAN_ROBOT))
var/fluid_loss = (damage/(owner.maxHealth - config.health_threshold_dead)) * owner.species.blood_volume*(1 - BLOOD_VOLUME_SURVIVE/100)
var/fluid_loss = 0.75 * (damage/(owner.maxHealth - config.health_threshold_dead)) * owner.species.blood_volume*(1 - BLOOD_VOLUME_SURVIVE/100)
owner.remove_blood(fluid_loss)
// first check whether we can widen an existing wound
+9 -4
View File
@@ -36,7 +36,12 @@
if(weather_holder)
weather_holder.process()
// Returns the time datum of Sif.
/proc/get_sif_time()
if(planet_sif)
return planet_sif.current_time
/datum/planet/proc/update_sun_deferred(var/new_range, var/new_brightness, var/new_color)
set background = 1
set waitfor = 0
var/i = 0
for(var/turf/simulated/floor/T in outdoor_turfs)
T.set_light(new_range, new_brightness, new_color)
i++
if(i % 30 == 0)
sleep(1)
+9 -9
View File
@@ -13,6 +13,7 @@ var/datum/planet/sif/planet_sif = null
/datum/planet/sif/New()
..()
planet_sif = src
weather_holder = new /datum/weather_holder/sif(src) // Cold weather is also nice.
// This code is horrible.
@@ -95,12 +96,11 @@ var/datum/planet/sif/planet_sif = null
spawn(1)
update_sun_deferred(2, new_brightness, new_color)
/datum/planet/proc/update_sun_deferred(var/new_range, var/new_brightness, var/new_color)
set background = 1
set waitfor = 0
var/i = 0
for(var/turf/simulated/floor/T in outdoor_turfs)
T.set_light(new_range, new_brightness, new_color)
i++
if(i % 30 == 0)
sleep(1)
// We're gonna pretend there are 32 hours in a Sif day instead of 32.64 for the purposes of not losing sanity. We lose 38m 24s but the alternative is a path to madness.
/datum/time/sif
seconds_in_day = 60 * 60 * 32 * 10 // 115,200 seconds. If we did 32.64 hours/day it would be around 117,504 seconds instead.
// Returns the time datum of Sif.
/proc/get_sif_time()
if(planet_sif)
return planet_sif.current_time
-4
View File
@@ -70,7 +70,3 @@
answer = replacetext(answer, "mm", minute_text)
answer = replacetext(answer, "ss", second_text)
return answer
// We're gonna pretend there are 32 hours in a Sif day instead of 32.64 for the purposes of not losing sanity. We lose 38m 24s but the alternative is a path to madness.
/datum/time/sif
seconds_in_day = 60 * 60 * 32 * 10 // 115,200 seconds. If we did 32.64 hours/day it would be around 117,504 seconds instead.
@@ -163,6 +163,7 @@ obj/aiming_overlay/proc/update_aiming_deferred()
owner.visible_message("<span class='danger'>\The [owner] turns \the [thing] on \the [target]!</span>")
else
owner.visible_message("<span class='danger'>\The [owner] aims \the [thing] at \the [target]!</span>")
log_and_message_admins("aimed \a [thing] at [key_name(target)].")
if(owner.client)
owner.client.add_gun_icons()
@@ -70,11 +70,11 @@
// Create a decorative ramp bottom and flatten out our current ramp.
density = 0
opacity = 0
set_opacity(0)
icon_state = "ramptop"
var/obj/structure/droppod_door/door_bottom = new(T)
door_bottom.deployed = 1
door_bottom.density = 0
door_bottom.opacity = 0
door_bottom.set_opacity(0)
door_bottom.dir = src.dir
door_bottom.icon_state = "rampbottom"
@@ -94,7 +94,7 @@
M.adjustToxLoss(-4 * removed)
/datum/reagent/carthatoline
name = "carthatoline"
name = "Carthatoline"
id = "carthatoline"
description = "Carthatoline is strong evacuant used to treat severe poisoning."
reagent_state = LIQUID
@@ -134,7 +134,7 @@
if(rag)
var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay)
underlays += underlay_image
copy_light(rag)
set_light(rag.light_range, rag.light_power, rag.light_color)
else
set_light(0)
+6 -6
View File
@@ -54,8 +54,8 @@
playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
check_failure()
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
..()
@@ -63,8 +63,8 @@
health -= Proj.get_structure_damage()
..()
check_failure()
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
/obj/machinery/shield/ex_act(severity)
switch(severity)
@@ -113,8 +113,8 @@
check_failure()
//The shield becomes dense to absorb the blow.. purely asthetic.
opacity = 1
spawn(20) if(src) opacity = 0
set_opacity(1)
spawn(20) if(!deleted(src)) set_opacity(0)
..()
return
+113 -33
View File
@@ -1,61 +1,100 @@
//---------- actual energy field
// Each field object has a strength var (mensured in "Renwicks").
// Melee weapons do 5% of their normal (force var) damage, so a harmbaton would do 0.75 Renwick.
// Projectiles do 5% of their structural damage, so a normal laser would do 2 Renwick damage.
// For meteors, one Renwick is about equal to one layer of r-wall.
// Meteors will be completely halted by the shield if the shield survives the impact.
// Explosions do 4 Renwick of damage per severity, at a max of 12.
/obj/effect/energy_field
name = "energy field"
name = "energy shield field"
desc = "Impenetrable field of energy, capable of blocking anything as long as it's active."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "shieldsparkles"
icon_state = "shield"
alpha = 100
anchored = 1
layer = 4.1 //just above mobs
density = 0
invisibility = 101
var/strength = 0
var/obj/machinery/shield_gen/my_gen = null
var/strength = 0 // in Renwicks
var/ticks_recovering = 10
var/max_strength = 10
/obj/effect/energy_field/New()
..()
/obj/effect/energy_field/New(var/newloc, var/new_gen)
..(newloc)
my_gen = new_gen
update_nearby_tiles()
/obj/effect/energy_field/Destroy()
update_nearby_tiles()
my_gen.field.Remove(src)
my_gen = null
var/turf/current_loc = get_turf(src)
spawn(1) // Updates neightbors after we're gone.
for(var/direction in cardinal)
var/turf/T = get_step(current_loc, direction)
if(T)
for(var/obj/effect/energy_field/F in T)
F.update_icon()
..()
/obj/effect/energy_field/ex_act(var/severity)
Stress(0.5 + severity)
adjust_strength(-(4 - severity) * 4)
/obj/effect/energy_field/bullet_act(var/obj/item/projectile/Proj)
Stress(Proj.get_structure_damage() / 10)
adjust_strength(-Proj.get_structure_damage() / 10)
/obj/effect/energy_field/proc/Stress(var/severity)
strength -= severity
/obj/effect/energy_field/attackby(obj/item/W, mob/user)
if(W.force)
adjust_strength(-W.force / 20)
user.do_attack_animation(src)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
..()
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
ticks_recovering = min(ticks_recovering + 2, 10)
if(strength < 1)
invisibility = 101
density = 0
ticks_recovering = 10
strength = 0
else if(strength >= 1)
invisibility = 0
density = 1
/obj/effect/energy_field/attack_hand(var/mob/living/user)
impact_effect(3) // Harmless, but still produces the 'impact' effect.
..()
/obj/effect/energy_field/proc/Strengthen(var/severity)
strength += severity
if (strength < 0)
strength = 0
/obj/effect/energy_field/Bumped(atom/A)
..(A)
impact_effect(2)
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
/obj/effect/energy_field/handle_meteor_impact(var/obj/effect/meteor/meteor)
var/penetrated = TRUE
adjust_strength(-max((meteor.wall_power * meteor.hits) / 800, 0)) // One renwick (strength var) equals one r-wall for the purposes of meteor-stopping.
sleep(1)
if(density) // Check if we're still up.
penetrated = FALSE
explosion(meteor.loc, 0, 0, 0, 0, 0, 0, 0) // For the sound effect.
// Returning FALSE will kill the meteor.
return penetrated // If the shield's still around, the meteor was successfully stopped, otherwise keep going and plow into the station.
/obj/effect/energy_field/proc/adjust_strength(amount, impact = 1)
var/old_density = density
if(strength >= 1)
invisibility = 0
density = 1
else if(strength < 1)
invisibility = 101
density = 0
if (density != old_density)
strength = between(0, strength + amount, max_strength)
//maptext = "[round(strength, 0.1)]/[max_strength]"
//if we take too much damage, drop out - the generator will bring us back up if we have enough power
if(amount < 0) // Taking damage.
if(impact)
impact_effect(round(abs(amount * 2)))
ticks_recovering = min(ticks_recovering + 2, 10)
if(strength < 1) // We broke
density = 0
ticks_recovering = 10
strength = 0
else if(amount > 0) // Healing damage.
if(strength >= 1)
density = 1
if(density != old_density)
update_icon()
update_nearby_tiles()
/obj/effect/energy_field/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
@@ -66,3 +105,44 @@
//return (!density || !height || air_group)
return !density
/obj/effect/energy_field/update_icon(var/update_neightbors = 0)
overlays.Cut()
var/list/adjacent_shields_dir = list()
for(var/direction in cardinal)
var/turf/T = get_step(src, direction)
if(T) // Incase we somehow stepped off the map.
for(var/obj/effect/energy_field/F in T)
if(update_neightbors)
F.update_icon(0)
adjacent_shields_dir |= direction
break
// Icon_state and Glow
if(density)
icon_state = "shield"
set_light(3, 3, "#66FFFF")
else
icon_state = "shield_broken"
set_light(3, 5, "#FF9900")
// Edge overlays
for(var/found_dir in adjacent_shields_dir)
overlays += image(src.icon, src, icon_state = "shield_edge", dir = found_dir)
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields.
/obj/effect/energy_field/proc/impact_effect(var/i, var/list/affected_shields = list())
i = between(1, i, 10)
alpha = 200
animate(src, alpha = initial(alpha), time = 1 SECOND)
affected_shields |= src
i--
if(i)
spawn(2)
for(var/direction in cardinal)
var/turf/T = get_step(src, direction)
if(T) // Incase we somehow stepped off the map.
for(var/obj/effect/energy_field/F in T)
if(!(F in affected_shields))
F.impact_effect(i, affected_shields) // Spread the effect to them.
@@ -0,0 +1,49 @@
/obj/item/weapon/shield_diffuser
name = "portable shield diffuser"
desc = "A small handheld device designed to disrupt energy barriers"
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "hdiffuser_off"
var/obj/item/weapon/cell/device/cell
var/enabled = 0
/obj/item/weapon/shield_diffuser/update_icon()
if(enabled)
icon_state = "hdiffuser_on"
else
icon_state = "hdiffuser_off"
/obj/item/weapon/shield_diffuser/New()
cell = new(src)
..()
/obj/item/weapon/shield_diffuser/Destroy()
qdel(cell)
cell = null
if(enabled)
processing_objects.Remove(src)
. = ..()
/obj/item/weapon/shield_diffuser/process()
if(!enabled)
return
for(var/direction in cardinal)
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
for(var/obj/effect/energy_field/S in shielded_tile)
if(istype(S) && cell.checked_use(10 KILOWATTS * CELLRATE))
qdel(S)
/obj/item/weapon/shield_diffuser/attack_self()
enabled = !enabled
update_icon()
if(enabled)
processing_objects.Add(src)
else
processing_objects.Remove(src)
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
/obj/item/weapon/shield_diffuser/examine()
. = ..()
to_chat(usr, "The charge meter reads [cell ? cell.percent() : 0]%")
to_chat(usr, "It is [enabled ? "enabled" : "disabled"].")
+7 -15
View File
@@ -19,14 +19,6 @@
var/charge_rate = 100000 //100 kW
var/obj/machinery/shield_gen/owned_gen
/obj/machinery/shield_capacitor/New()
spawn(10)
for(var/obj/machinery/shield_gen/possible_gen in range(1, src))
if(get_dir(src, possible_gen) == src.dir)
possible_gen.owned_capacitor = src
break
..()
/obj/machinery/shield_capacitor/emag_act(var/remaining_charges, var/mob/user)
if(prob(75))
src.locked = !src.locked
@@ -54,13 +46,13 @@
if(anchored)
spawn(0)
for(var/obj/machinery/shield_gen/gen in range(1, src))
if(get_dir(src, gen) == src.dir && !gen.owned_capacitor)
if(get_dir(src, gen) == src.dir)
owned_gen = gen
owned_gen.owned_capacitor = src
owned_gen.capacitors |= src
owned_gen.updateDialog()
else
if(owned_gen && owned_gen.owned_capacitor == src)
owned_gen.owned_capacitor = null
if(owned_gen && src in owned_gen.capacitors)
owned_gen.capacitors -= src
owned_gen = null
else
..()
@@ -82,16 +74,16 @@
else
t += "This capacitor is: [active ? "<font color=green>Online</font>" : "<font color=red>Offline</font>" ] <a href='?src=\ref[src];toggle=1'>[active ? "\[Deactivate\]" : "\[Activate\]"]</a><br>"
t += "Capacitor Status: [time_since_fail > 2 ? "<font color=green>OK.</font>" : "<font color=red>Discharging!</font>"]<br>"
t += "Stored Energy: [round(stored_charge/1000, 0.1)] kJ ([100 * round(stored_charge/max_charge, 0.1)]%)<br>"
t += "Stored Energy: [format_SI(stored_charge, "J")] ([100 * round(stored_charge/max_charge, 0.01)]%)<br>"
t += "Charge Rate: \
<a href='?src=\ref[src];charge_rate=-100000'>\[----\]</a> \
<a href='?src=\ref[src];charge_rate=-10000'>\[---\]</a> \
<a href='?src=\ref[src];charge_rate=-1000'>\[--\]</a> \
<a href='?src=\ref[src];charge_rate=-100'>\[-\]</a>[charge_rate] W \
<a href='?src=\ref[src];charge_rate=-100'>\[-\]</a>[format_SI(charge_rate, "W")]\
<a href='?src=\ref[src];charge_rate=100'>\[+\]</a> \
<a href='?src=\ref[src];charge_rate=1000'>\[++\]</a> \
<a href='?src=\ref[src];charge_rate=10000'>\[+++\]</a> \
<a href='?src=\ref[src];charge_rate=100000'>\[+++\]</a><br>"
<a href='?src=\ref[src];charge_rate=100000'>\[++++\]</a><br>"
t += "<hr>"
t += "<A href='?src=\ref[src]'>Refresh</A> "
t += "<A href='?src=\ref[src];close=1'>Close</A><BR>"
+74
View File
@@ -0,0 +1,74 @@
/obj/machinery/shield_diffuser
name = "shield diffuser"
desc = "A small underfloor device specifically designed to disrupt energy barriers."
description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access."
icon = 'icons/obj/machines/shielding.dmi'
icon_state = "fdiffuser_on"
use_power = 2
idle_power_usage = 100
active_power_usage = 2000
anchored = 1
density = 0
level = 1
var/alarm = 0
var/enabled = 1
/obj/machinery/shield_diffuser/New()
..()
var/turf/T = get_turf(src)
hide(!T.is_plating())
//If underfloor, hide the cable
/obj/machinery/shield_diffuser/hide(var/i)
if(istype(loc, /turf))
invisibility = i ? 101 : 0
update_icon()
/obj/machinery/shield_diffuser/hides_under_flooring()
return 1
/obj/machinery/shield_diffuser/process()
if(alarm)
alarm--
if(!alarm)
update_icon()
return
if(!enabled)
return
for(var/direction in cardinal)
var/turf/simulated/shielded_tile = get_step(get_turf(src), direction)
for(var/obj/effect/energy_field/S in shielded_tile)
qdel(S)
/obj/machinery/shield_diffuser/update_icon()
if(alarm)
icon_state = "fdiffuser_emergency"
return
if((stat & (NOPOWER | BROKEN)) || !enabled)
icon_state = "fdiffuser_off"
else
icon_state = "fdiffuser_on"
/obj/machinery/shield_diffuser/attack_hand()
if(alarm)
to_chat(usr, "You press an override button on \the [src], re-enabling it.")
alarm = 0
update_icon()
return
enabled = !enabled
use_power = enabled + 1
update_icon()
to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].")
/obj/machinery/shield_diffuser/proc/meteor_alarm(var/duration)
if(!duration)
return
alarm = round(max(alarm, duration))
update_icon()
/obj/machinery/shield_diffuser/examine(var/mob/user)
. = ..()
to_chat(user, "It is [enabled ? "enabled" : "disabled"].")
if(alarm)
to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.")
+91 -35
View File
@@ -1,10 +1,3 @@
//renwicks: fictional unit to describe shield strength
//a small meteor hit will deduct 1 renwick of strength from that shield tile
//light explosion range will do 1 renwick's damage
//medium explosion range will do 2 renwick's damage
//heavy explosion range will do 3 renwick's damage
//explosion damage is cumulative. if a tile is in range of light, medium and heavy damage, it will take a hit from all three
/obj/machinery/shield_gen
name = "bubble shield generator"
desc = "Machine that generates an impenetrable field of energy when activated."
@@ -13,7 +6,7 @@
var/active = 0
var/field_radius = 3
var/max_field_radius = 100
var/list/field
var/list/field = list()
density = 1
var/locked = 0
var/average_field_strength = 0
@@ -23,20 +16,25 @@
var/min_dissipation = 0.01 //will dissipate by at least this rate in renwicks per field tile (otherwise field would never dissipate completely as dissipation is a percentage)
var/powered = 0
var/check_powered = 1
var/obj/machinery/shield_capacitor/owned_capacitor
var/list/capacitors = list()
var/target_field_strength = 10
var/max_field_strength = 10
var/time_since_fail = 100
var/energy_conversion_rate = 0.0002 //how many renwicks per watt?
var/z_range = 0 // How far 'up and or down' to extend the shield to, in z-levels. Only works on MultiZ supported z-levels.
use_power = 0 //doesn't use APC power
/obj/machinery/shield_gen/New()
spawn(10)
for(var/obj/machinery/shield_capacitor/possible_cap in range(1, src))
if(get_dir(possible_cap, src) == possible_cap.dir)
owned_capacitor = possible_cap
break
field = new/list()
spawn(1 SECOND)
if(anchored)
for(var/obj/machinery/shield_capacitor/cap in range(1, src))
if(!cap.anchored)
continue
if(cap.owned_gen)
continue
if(get_dir(cap, src) == cap.dir)
capacitors |= cap
cap.owned_gen = src
..()
/obj/machinery/shield_gen/Destroy()
@@ -44,7 +42,7 @@
field.Remove(D)
D.loc = null
..()
/obj/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user)
if(prob(75))
src.locked = !src.locked
@@ -76,14 +74,14 @@
if(cap.owned_gen)
continue
if(get_dir(cap, src) == cap.dir && src.anchored)
owned_capacitor = cap
owned_capacitor.owned_gen = src
// owned_capacitor = cap
capacitors |= cap
cap.owned_gen = src
updateDialog()
break
// break
else
if(owned_capacitor && owned_capacitor.owned_gen == src)
owned_capacitor.owned_gen = null
owned_capacitor = null
for(var/obj/machinery/shield_capacitor/capacitor in capacitors)
capacitor.owned_gen = null
else
..()
@@ -105,7 +103,13 @@
if(locked)
t += "<i>Swipe your ID card to begin.</i>"
else
t += "[owned_capacitor ? "<font color=green>Charge capacitor connected.</font>" : "<font color=red>Unable to locate charge capacitor!</font>"]<br>"
t += "[capacitors.len ? "<font color=green>Charge capacitor(s) connected.</font>" : "<font color=red>Unable to locate charge capacitor!</font>"]<br>"
var/i = 0
for(var/obj/machinery/shield_capacitor/capacitor in capacitors)
i++
t += "Capacitor #[i]: [capacitor.active ? "<font color=green>Online.</font>" : "<font color=red>Offline.</font>"] \
Charge: [round(capacitor.stored_charge/1000, 0.1)] kJ ([100 * round(capacitor.stored_charge/capacitor.max_charge, 0.01)]%) \
Status: [capacitor.time_since_fail > 2 ? "<font color=green>OK.</font>" : "<font color=red>Discharging!</font>"]<br>"
t += "This generator is: [active ? "<font color=green>Online</font>" : "<font color=red>Offline</font>" ] <a href='?src=\ref[src];toggle=1'>[active ? "\[Deactivate\]" : "\[Activate\]"]</a><br>"
t += "Field Status: [time_since_fail > 2 ? "<font color=green>Stable</font>" : "<font color=red>Unstable</font>"]<br>"
t += "Coverage Radius (restart required): \
@@ -116,12 +120,17 @@
<a href='?src=\ref[src];change_radius=1'>+</a> \
<a href='?src=\ref[src];change_radius=5'>++</a> \
<a href='?src=\ref[src];change_radius=50'>+++</a><br>"
if(HasAbove(src.z) || HasBelow(src.z)) // Won't show up on maps lacking MultiZ support.
t += "Vertical Shielding (restart required): \
<a href='?src=\ref[src];z_range=-1'>-</a> \
[z_range] Vertical Range \
<a href='?src=\ref[src];z_range=1'>+</a><br>"
t += "Overall Field Strength: [round(average_field_strength, 0.01)] Renwick ([target_field_strength ? round(100 * average_field_strength / target_field_strength, 0.1) : "NA"]%)<br>"
t += "Upkeep Power: [round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate)] W<br>"
t += "Upkeep Power: [format_SI(round(field.len * max(average_field_strength * dissipation_rate, min_dissipation) / energy_conversion_rate), "W")]<br>"
t += "Charge Rate: <a href='?src=\ref[src];strengthen_rate=-0.1'>--</a> \
[strengthen_rate] Renwick/s \
<a href='?src=\ref[src];strengthen_rate=0.1'>++</a><br>"
t += "Shield Generation Power: [round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate)] W<br>"
t += "Shield Generation Power: [format_SI(round(field.len * min(strengthen_rate, target_field_strength - average_field_strength) / energy_conversion_rate), "W")]<br>"
t += "Maximum Field Strength: \
<a href='?src=\ref[src];target_field_strength=-10'>\[min\]</a> \
<a href='?src=\ref[src];target_field_strength=-5'>--</a> \
@@ -148,13 +157,28 @@
var/renwick_upkeep_per_field = max(average_field_strength * dissipation_rate, min_dissipation)
//figure out how much energy we need to draw from the capacitor
if(active && owned_capacitor && owned_capacitor.active)
if(active && capacitors.len)
// Get a list of active capacitors to drain from.
var/list/active_capacitors = list()
for(var/obj/machinery/shield_capacitor/capacitor in capacitors) // Some capacitors might be off. Exclude them.
if(capacitor.active && capacitor.stored_charge > 0)
active_capacitors |= capacitor
var/target_renwick_increase = min(target_field_strength - average_field_strength, strengthen_rate) + renwick_upkeep_per_field //per field tile
var/required_energy = field.len * target_renwick_increase / energy_conversion_rate
var/assumed_charge = min(owned_capacitor.stored_charge, required_energy)
// Gets the charge for all capacitors
var/sum_charge = 0
for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors)
sum_charge += capacitor.stored_charge
var/assumed_charge = min(sum_charge, required_energy)
total_renwick_increase = assumed_charge * energy_conversion_rate
owned_capacitor.stored_charge -= assumed_charge
for(var/obj/machinery/shield_capacitor/capacitor in active_capacitors)
capacitor.stored_charge -= max(assumed_charge / active_capacitors.len, 0) // Drain from all active capacitors evenly.
else
renwick_upkeep_per_field = max(renwick_upkeep_per_field, 0.5)
@@ -162,12 +186,13 @@
average_field_strength = 0 //recalculate the average field strength
for(var/obj/effect/energy_field/E in field)
E.max_strength = target_field_strength
var/amount_to_strengthen = renwick_increase_per_field - renwick_upkeep_per_field
if(E.ticks_recovering > 0 && amount_to_strengthen > 0)
E.Strengthen( min(amount_to_strengthen / 10, 0.1) )
E.adjust_strength( min(amount_to_strengthen / 10, 0.1), 0 )
E.ticks_recovering -= 1
else
E.Strengthen(amount_to_strengthen)
E.adjust_strength(amount_to_strengthen, 0)
average_field_strength += E.strength
@@ -194,6 +219,8 @@
strengthen_rate = between(0, strengthen_rate + text2num(href_list["strengthen_rate"]), max_strengthen_rate)
else if( href_list["target_field_strength"] )
target_field_strength = between(1, target_field_strength + text2num(href_list["target_field_strength"]), max_field_strength)
else if( href_list["z_range"] )
z_range = between(0, z_range + text2num(href_list["z_range"]), 10) // Max is extending ten z-levels up and down. Probably too big of a number but it shouldn't matter.
updateDialog()
@@ -213,16 +240,19 @@
if(T in covered_turfs)
covered_turfs.Remove(T)
for(var/turf/O in covered_turfs)
var/obj/effect/energy_field/E = new(O)
var/obj/effect/energy_field/E = new(O, src)
field.Add(E)
covered_turfs = null
for(var/mob/M in view(5,src))
M << "\icon[src] You hear heavy droning start up."
for(var/obj/effect/energy_field/E in field) // Update the icons here to ensure all the shields have been made already.
E.update_icon()
else
for(var/obj/effect/energy_field/D in field)
field.Remove(D)
D.loc = null
//D.loc = null
qdel(D)
for(var/mob/M in view(5,src))
M << "\icon[src] You hear heavy droning fade out."
@@ -236,12 +266,38 @@
else
icon_state = "generator0"
//TODO MAKE THIS MULTIZ COMPATIBLE
//grab the border tiles in a circle around this machine
/obj/machinery/shield_gen/proc/get_shielded_turfs()
var/list/out = list()
var/turf/gen_turf = get_turf(src)
var/turf/T = get_turf(src)
if (!T)
return
out += get_shielded_turfs_on_z_level(T)
if(z_range)
var/i = z_range
while(HasAbove(T.z) && i)
T = GetAbove(T)
i--
if(istype(T))
out += get_shielded_turfs_on_z_level(T)
T = get_turf(src)
i = z_range
while(HasBelow(T.z) && i)
T = GetBelow(T)
i--
if(istype(T))
out += get_shielded_turfs_on_z_level(T)
return out
/obj/machinery/shield_gen/proc/get_shielded_turfs_on_z_level(var/turf/gen_turf)
var/list/out = list()
if (!gen_turf)
return
@@ -260,4 +316,4 @@
T = locate(gen_turf.x + field_radius, gen_turf.y + y_offset, gen_turf.z)
if (T) out += T
return out
return out
@@ -7,15 +7,13 @@
/obj/machinery/shield_gen/external/New()
..()
//NOT MULTIZ COMPATIBLE
//Search for space turfs within range that are adjacent to a simulated turf.
/obj/machinery/shield_gen/external/get_shielded_turfs()
/obj/machinery/shield_gen/external/get_shielded_turfs_on_z_level(var/turf/gen_turf)
var/list/out = list()
var/turf/gen_turf = get_turf(src)
if (!gen_turf)
return
var/turf/T
for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++)
for (var/y_offset = -field_radius; y_offset <= field_radius; y_offset++)
@@ -24,4 +22,4 @@
//check neighbors of T
if (locate(/turf/simulated/) in orange(1, T))
out += T
return out
return out
+2 -1
View File
@@ -12,7 +12,8 @@
return 1
if(locate(/obj/structure/table/bench) in get_turf(mover))
return 0
if(locate(/obj/structure/table) in get_turf(mover))
var/obj/structure/table/table = locate(/obj/structure/table) in get_turf(mover)
if(table && !table.flipped)
return 1
return 0

Some files were not shown because too many files have changed in this diff Show More