mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-29 15:08:34 +01:00
Merge branch 'master' of https://github.com/PolarisSS13/Polaris into scratch_damage
This commit is contained in:
@@ -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?
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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], " | ")]")
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
/*
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
power_equip = 0
|
||||
power_environ = 0
|
||||
|
||||
if(lighting_use_dynamic)
|
||||
if(dynamic_lighting)
|
||||
luminosity = 0
|
||||
else
|
||||
luminosity = 1
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
..()
|
||||
|
||||
/obj/structure/curtain/proc/toggle()
|
||||
opacity = !opacity
|
||||
set_opacity(!opacity)
|
||||
if(opacity)
|
||||
icon_state = "closed"
|
||||
layer = SHOWER_CLOSED_LAYER
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
/area
|
||||
luminosity = TRUE
|
||||
var/dynamic_lighting = TRUE
|
||||
|
||||
/area/New()
|
||||
. = ..()
|
||||
|
||||
if(dynamic_lighting)
|
||||
luminosity = FALSE
|
||||
@@ -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()
|
||||
. = ..()
|
||||
|
||||
@@ -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
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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])
|
||||
|
||||
@@ -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()
|
||||
..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"].")
|
||||
@@ -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>"
|
||||
|
||||
@@ -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.")
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user