diff --git a/btime.dll b/btime.dll
new file mode 100644
index 0000000000..af6c82a998
Binary files /dev/null and b/btime.dll differ
diff --git a/btime.so b/btime.so
new file mode 100644
index 0000000000..edb3cc3113
Binary files /dev/null and b/btime.so differ
diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm
index 1905c35e4c..11777e51d2 100644
--- a/code/ATMOSPHERICS/components/unary/cold_sink.dm
+++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm
@@ -24,9 +24,6 @@
/obj/machinery/atmospherics/unary/freezer/New()
..()
initialize_directions = dir
-
-/obj/machinery/atmospherics/unary/freezer/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm
index 302beb6baf..8ee9600606 100644
--- a/code/ATMOSPHERICS/components/unary/heat_source.dm
+++ b/code/ATMOSPHERICS/components/unary/heat_source.dm
@@ -24,9 +24,6 @@
/obj/machinery/atmospherics/unary/heater/New()
..()
initialize_directions = dir
-
-/obj/machinery/atmospherics/unary/heater/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
diff --git a/code/__defines/btime.dm b/code/__defines/btime.dm
new file mode 100644
index 0000000000..d4cefc3524
--- /dev/null
+++ b/code/__defines/btime.dm
@@ -0,0 +1,18 @@
+// Comment this out if the external btime library is unavailable
+#define PRECISE_TIMER_AVAILABLE
+
+#ifdef PRECISE_TIMER_AVAILABLE
+var/global/__btime__libName = "btime.[world.system_type==MS_WINDOWS?"dll":"so"]"
+#define TimeOfHour (__extern__timeofhour)
+#define __extern__timeofhour text2num(call(__btime__libName, "gettime")())
+/hook/startup/proc/checkbtime()
+ try
+ // This will always return 1 unless the btime library cannot be accessed
+ if(TimeOfHour || 1) return 1
+ catch(var/exception/e)
+ log_to_dd("PRECISE_TIMER_AVAILABLE is defined in btime.dm, but calling the btime library failed: [e]")
+ log_to_dd("This is a fatal error. The world will now shut down.")
+ del(world)
+#else
+#define TimeOfHour (world.timeofday % 36000)
+#endif
\ No newline at end of file
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 4a84b66083..0a06743993 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -23,7 +23,7 @@
// Some arbitrary defines to be used by self-pruning global lists. (see master_controller)
#define PROCESS_KILL 26 // Used to trigger removal from a processing list.
-#define MAX_GEAR_COST 10 // Used in chargen for accessory loadout limit.
+#define MAX_GEAR_COST 15 // Used in chargen for accessory loadout limit.
// For secHUDs and medHUDs and variants. The number is the location of the image on the list hud_list of humans.
#define HEALTH_HUD 1 // A simple line rounding the mob's number health.
diff --git a/code/controllers/ProcessScheduler/core/_define.dm b/code/__defines/process_scheduler.dm
similarity index 66%
rename from code/controllers/ProcessScheduler/core/_define.dm
rename to code/__defines/process_scheduler.dm
index f0165c9870..76449b9a4b 100644
--- a/code/controllers/ProcessScheduler/core/_define.dm
+++ b/code/__defines/process_scheduler.dm
@@ -11,7 +11,10 @@
#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 2 // 2 ticks
+#define PROCESS_DEFAULT_SLEEP_INTERVAL 8 // 2 ticks
#define PROCESS_DEFAULT_CPU_THRESHOLD 90 // 90%
-//#define UPDATE_QUEUE_DEBUG
\ No newline at end of file
+// 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 SCHECK_EVERY(50)
\ No newline at end of file
diff --git a/code/_helpers/datum_pool.dm b/code/_helpers/datum_pool.dm
index 5c13c0d8ba..b5bcea123f 100644
--- a/code/_helpers/datum_pool.dm
+++ b/code/_helpers/datum_pool.dm
@@ -74,7 +74,6 @@ var/global/list/GlobalPool = list()
D.Destroy()
D.ResetVars()
- D.disposed = 1 //Set to stop processing while pooled
/proc/IsPooled(var/datum/D)
if(isnull(GlobalPool[D.type]))
@@ -86,7 +85,6 @@ var/global/list/GlobalPool = list()
New(arglist(args))
else
New(args)
- disposed = null
/atom/movable/Prepare(args)
var/list/args_list = args
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index 3fb0306901..d4e6710b35 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -602,13 +602,13 @@ proc/dd_sortedTextList(list/incoming)
/datum/alarm/dd_SortValue()
return "[sanitize_old(last_name)]"
-/proc/subtypes(prototype)
+/proc/subtypesof(prototype)
return (typesof(prototype) - prototype)
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
if(!istype(L)) L = list()
- for(var/path in subtypes(prototype))
+ for(var/path in subtypesof(prototype))
L += new path()
return L
diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm
index 133707b794..35618c9b66 100644
--- a/code/_helpers/logging.dm
+++ b/code/_helpers/logging.dm
@@ -25,7 +25,6 @@
if (config.log_admin)
diary << "\[[time_stamp()]]ADMIN: [text][log_end]"
-
/proc/log_debug(text)
if (config.log_debug)
diary << "\[[time_stamp()]]DEBUG: [text][log_end]"
@@ -34,7 +33,6 @@
if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs))
C << "DEBUG: [text]"
-
/proc/log_game(text)
if (config.log_game)
diary << "\[[time_stamp()]]GAME: [text][log_end]"
@@ -79,6 +77,11 @@
if (config.log_pda)
diary << "\[[time_stamp()]]PDA: [text][log_end]"
+/proc/log_to_dd(text)
+ world.log << text //this comes before the config check because it can't possibly runtime
+ if(config.log_world_output)
+ diary << "\[[time_stamp()]]DD_OUTPUT: [text][log_end]"
+
/proc/log_misc(text)
diary << "\[[time_stamp()]]MISC: [text][log_end]"
diff --git a/code/controllers/ProcessScheduler/ProcessScheduler.dme b/code/controllers/ProcessScheduler/ProcessScheduler.dme
deleted file mode 100644
index bf17734cc2..0000000000
--- a/code/controllers/ProcessScheduler/ProcessScheduler.dme
+++ /dev/null
@@ -1,32 +0,0 @@
-// DM Environment file for ProcessScheduler.dme.
-// All manual changes should be made outside the BEGIN_ and END_ blocks.
-// New source code should be placed in .dm files: choose File/New --> Code File.
-
-// BEGIN_INTERNALS
-// END_INTERNALS
-
-// BEGIN_FILE_DIR
-#define FILE_DIR .
-// END_FILE_DIR
-
-// BEGIN_PREFERENCES
-// END_PREFERENCES
-
-// BEGIN_INCLUDE
-#include "core\_define.dm"
-#include "core\_stubs.dm"
-#include "core\process.dm"
-#include "core\processScheduler.dm"
-#include "core\updateQueue.dm"
-#include "core\updateQueueWorker.dm"
-#include "test\processSchedulerView.dm"
-#include "test\testDyingUpdateQueueProcess.dm"
-#include "test\testHarness.dm"
-#include "test\testHungProcess.dm"
-#include "test\testNiceProcess.dm"
-#include "test\testSlowProcess.dm"
-#include "test\testUpdateQueue.dm"
-#include "test\testUpdateQueueProcess.dm"
-#include "test\testZombieProcess.dm"
-// END_INCLUDE
-
diff --git a/code/controllers/ProcessScheduler/core/_stubs.dm b/code/controllers/ProcessScheduler/core/_stubs.dm
index 326fd29ac2..3615c12f19 100644
--- a/code/controllers/ProcessScheduler/core/_stubs.dm
+++ b/code/controllers/ProcessScheduler/core/_stubs.dm
@@ -4,15 +4,7 @@
* This file contains constructs that the process scheduler expects to exist
* in a standard ss13 fork.
*/
-/*
-/**
- * message_admins
- *
- * sends a message to admins
- */
-/proc/message_admins(msg)
- world << msg
-*/
+
/**
* logTheThing
*
@@ -25,14 +17,3 @@
world << "Diary: \[[diaryType]:[type]] [text]"
else
world << "Log: \[[type]] [text]"
-
-/**
- * var/disposed
- *
- * In goonstation, disposed is set to 1 after an object enters the delete queue
- * or the object is placed in an object pool (effectively out-of-play so to speak)
- */
-/datum/var/disposed
-// Garbage collection (controller).
-/datum/var/gcDestroyed
-/datum/var/timeDestroyed
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index 1f27f4c1de..b7767c367f 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -48,7 +48,7 @@
// 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 = PROCESS_DEFAULT_SLEEP_INTERVAL
+ var/tmp/sleep_interval
// 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,20 +59,20 @@
// 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
- // cpu_threshold - if world.cpu >= cpu_threshold, scheck() will call sleep(1) to defer further work until the next tick. This keeps a process from driving a tick into overtime (causing perceptible lag)
- var/tmp/cpu_threshold = PROCESS_DEFAULT_CPU_THRESHOLD
-
// 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
+
/**
* recordkeeping vars
*/
- // Records the time (server ticks) at which the process last finished sleeping
+ // Records the time (1/10s timeofday) at which the process last finished sleeping
var/tmp/last_slept = 0
- // Records the time (s-ticks) at which the process last began running
+ // Records the time (1/10s timeofday) at which the process last began running
var/tmp/run_start = 0
// Records the number of times this process has been killed and restarted
@@ -85,26 +85,33 @@
var/tmp/last_object
-datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
+ // 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
+
+/datum/controller/process/New(var/datum/controller/processScheduler/scheduler)
..()
main = scheduler
previousStatus = "idle"
idle()
name = "process"
schedule_interval = 50
- sleep_interval = 2
+ sleep_interval = world.tick_lag / PROCESS_DEFAULT_SLEEP_INTERVAL
last_slept = 0
run_start = 0
ticks = 0
last_task = 0
last_object = null
-datum/controller/process/proc/started()
+/datum/controller/process/proc/started()
+ var/timeofhour = TimeOfHour
// Initialize last_slept so we can know when to sleep
- last_slept = world.timeofday
+ last_slept = timeofhour
// Initialize run_start so we can detect hung processes.
- run_start = world.timeofday
+ run_start = timeofhour
// Initialize defer count
cpu_defer_count = 0
@@ -114,65 +121,65 @@ datum/controller/process/proc/started()
onStart()
-datum/controller/process/proc/finished()
+/datum/controller/process/proc/finished()
ticks++
idle()
main.processFinished(src)
onFinish()
-datum/controller/process/proc/doWork()
+/datum/controller/process/proc/doWork()
-datum/controller/process/proc/setup()
+/datum/controller/process/proc/setup()
-datum/controller/process/proc/process()
+/datum/controller/process/proc/process()
started()
doWork()
finished()
-datum/controller/process/proc/running()
+/datum/controller/process/proc/running()
idle = 0
queued = 0
running = 1
hung = 0
setStatus(PROCESS_STATUS_RUNNING)
-datum/controller/process/proc/idle()
+/datum/controller/process/proc/idle()
queued = 0
running = 0
idle = 1
hung = 0
setStatus(PROCESS_STATUS_IDLE)
-datum/controller/process/proc/queued()
+/datum/controller/process/proc/queued()
idle = 0
running = 0
queued = 1
hung = 0
setStatus(PROCESS_STATUS_QUEUED)
-datum/controller/process/proc/hung()
+/datum/controller/process/proc/hung()
hung = 1
setStatus(PROCESS_STATUS_HUNG)
-datum/controller/process/proc/handleHung()
+/datum/controller/process/proc/handleHung()
+ var/timeofhour = TimeOfHour
var/datum/lastObj = last_object
var/lastObjType = "null"
if(istype(lastObj))
lastObjType = lastObj.type
- // If world.timeofday has rolled over, then we need to adjust.
- if (world.timeofday < run_start)
- run_start -= 864000
-
- var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(world.timeofday - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]"
+ // If timeofhour has rolled over, then we need to adjust.
+ if (timeofhour < run_start)
+ run_start -= 36000
+ var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(timeofhour - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]"
logTheThing("debug", null, null, msg)
logTheThing("diary", null, null, msg, "debug")
message_admins(msg)
main.restartProcess(src.name)
-datum/controller/process/proc/kill()
+/datum/controller/process/proc/kill()
if (!killed)
var/msg = "[name] process was killed at tick #[ticks]."
logTheThing("debug", null, null, msg)
@@ -182,59 +189,68 @@ datum/controller/process/proc/kill()
// Allow inheritors to clean up if needed
onKill()
- killed = TRUE
+ // This should del
+ del(src)
- del(src) // This should del
-
-datum/controller/process/proc/scheck(var/tickId = 0)
+// 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)
// 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.
CRASH("A killed process is still running somehow...")
+ if (hung)
+ // This will only really help if the doWork proc ends up in an infinite loop.
+ handleHung()
+ CRASH("Process [name] hung and was restarted.")
- // For each tick the process defers, it increments the cpu_defer_count so we don't
- // defer indefinitely
- if (world.cpu >= cpu_threshold + cpu_defer_count * 10)
- sleep(1)
+ if (main.getCurrentTickElapsedTime() > main.timeAllowance)
+ sleep(world.tick_lag)
cpu_defer_count++
- last_slept = world.timeofday
+ last_slept = TimeOfHour
else
- // If world.timeofday has rolled over, then we need to adjust.
- if (world.timeofday < last_slept)
- last_slept -= 864000
+ var/timeofhour = TimeOfHour
+ // If timeofhour has rolled over, then we need to adjust.
+ if (timeofhour < last_slept)
+ last_slept -= 36000
- if (world.timeofday > last_slept + sleep_interval)
- // If we haven't slept in sleep_interval ticks, sleep to allow other work to proceed.
+ if (timeofhour > last_slept + sleep_interval)
+ // If we haven't slept in sleep_interval deciseconds, sleep to allow other work to proceed.
sleep(0)
- last_slept = world.timeofday
+ last_slept = TimeOfHour
-datum/controller/process/proc/update()
+/datum/controller/process/proc/update()
// Clear delta
if(previousStatus != status)
setStatus(status)
var/elapsedTime = getElapsedTime()
- if (elapsedTime > hang_restart_time)
+ if (hung)
+ handleHung()
+ return
+ else if (elapsedTime > hang_restart_time)
hung()
else if (elapsedTime > hang_alert_time)
setStatus(PROCESS_STATUS_PROBABLY_HUNG)
else if (elapsedTime > hang_warning_time)
setStatus(PROCESS_STATUS_MAYBE_HUNG)
-datum/controller/process/proc/getElapsedTime()
- if (world.timeofday < run_start)
- return world.timeofday - (run_start - 864000)
- return world.timeofday - run_start
-datum/controller/process/proc/tickDetail()
+/datum/controller/process/proc/getElapsedTime()
+ var/timeofhour = TimeOfHour
+ if (timeofhour < run_start)
+ return timeofhour - (run_start - 36000)
+ return timeofhour - run_start
+
+/datum/controller/process/proc/tickDetail()
return
-datum/controller/process/proc/getContext()
+/datum/controller/process/proc/getContext()
return "
[name]
[main.averageRunTime(src)]
[main.last_run_time[src]]
[main.highest_run_time[src]]
[ticks]
\n"
-datum/controller/process/proc/getContextData()
+/datum/controller/process/proc/getContextData()
return list(
"name" = name,
"averageRunTime" = main.averageRunTime(src),
@@ -246,10 +262,10 @@ datum/controller/process/proc/getContextData()
"disabled" = disabled
)
-datum/controller/process/proc/getStatus()
+/datum/controller/process/proc/getStatus()
return status
-datum/controller/process/proc/getStatusText(var/s = 0)
+/datum/controller/process/proc/getStatusText(var/s = 0)
if(!s)
s = status
switch(s)
@@ -268,21 +284,21 @@ datum/controller/process/proc/getStatusText(var/s = 0)
else
return "UNKNOWN"
-datum/controller/process/proc/getPreviousStatus()
+/datum/controller/process/proc/getPreviousStatus()
return previousStatus
-datum/controller/process/proc/getPreviousStatusText()
+/datum/controller/process/proc/getPreviousStatusText()
return getStatusText(previousStatus)
-datum/controller/process/proc/setStatus(var/newStatus)
+/datum/controller/process/proc/setStatus(var/newStatus)
previousStatus = status
status = newStatus
-datum/controller/process/proc/setLastTask(var/task, var/object)
+/datum/controller/process/proc/setLastTask(var/task, var/object)
last_task = task
last_object = object
-datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target)
+/datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target)
main = target.main
name = target.name
schedule_interval = target.schedule_interval
@@ -295,28 +311,62 @@ datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target
last_object = target.last_object
copyStateFrom(target)
-datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
+/datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
-datum/controller/process/proc/onKill()
+/datum/controller/process/proc/onKill()
-datum/controller/process/proc/onStart()
+/datum/controller/process/proc/onStart()
-datum/controller/process/proc/onFinish()
+/datum/controller/process/proc/onFinish()
-datum/controller/process/proc/disable()
+/datum/controller/process/proc/disable()
disabled = 1
-datum/controller/process/proc/enable()
+/datum/controller/process/proc/enable()
disabled = 0
+/datum/controller/process/proc/getAverageRunTime()
+ return main.averageRunTime(src)
/datum/controller/process/proc/getLastRunTime()
return main.getProcessLastRunTime(src)
+/datum/controller/process/proc/getHighestRunTime()
+ return main.getProcessHighestRunTime(src)
+
/datum/controller/process/proc/getTicks()
return ticks
-/datum/controller/process/proc/getStatName()
- return name
+/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]")
-/datum/controller/process/proc/getTickTime()
- return "#[getTicks()]\t- [getLastRunTime()]"
+/datum/controller/process/proc/catchException(var/exception/e, var/thrower)
+ var/etext = "[e]"
+ var/eid = "[e]" // Exception ID, for tracking repeated exceptions
+ var/ptext = "" // "processing..." text, for what was being processed (if known)
+ if(istype(e))
+ etext += " in [e.file], line [e.line]"
+ eid = "[e.file]:[e.line]"
+ if(eid in exceptions)
+ if(exceptions[eid]++ >= 10)
+ return
+ else
+ exceptions[eid] = 1
+ if(istype(thrower, /datum))
+ var/datum/D = thrower
+ ptext = " processing [D.type]"
+ if(istype(thrower, /atom))
+ var/atom/A = thrower
+ ptext += " ([A]) ([A.x],[A.y],[A.z])"
+ log_to_dd("\[[time_stamp()]\] Process [name] caught exception[ptext]: [etext]")
+ if(exceptions[eid] >= 10)
+ log_to_dd("This exception will now be ignored for ten minutes.")
+ spawn(6000)
+ exceptions[eid] = 0
+
+/datum/controller/process/proc/catchBadType(var/datum/caught)
+ if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed))
+ return // Only bother with types we can identify and that don't belong
+ catchException("Type [caught.type] does not belong in process' queue")
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm
index 1be2404593..bde79fba07 100644
--- a/code/controllers/ProcessScheduler/core/processScheduler.dm
+++ b/code/controllers/ProcessScheduler/core/processScheduler.dm
@@ -17,7 +17,10 @@ var/global/datum/controller/processScheduler/processScheduler
// Process name -> process object map
var/tmp/datum/controller/process/list/nameToProcessMap = new
- // Process last start times
+ // 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
@@ -29,8 +32,8 @@ var/global/datum/controller/processScheduler/processScheduler
// Process highest run time
var/tmp/datum/controller/process/list/highest_run_time = new
- // Sleep 1 tick -- This may be too aggressive.
- var/tmp/scheduler_sleep_interval = 1
+ // How long to sleep between runs (set to tick_lag in New)
+ var/tmp/scheduler_sleep_interval
// Controls whether the scheduler is running or not
var/tmp/isRunning = 0
@@ -38,6 +41,25 @@ 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/currentTickStart = 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
* @param path processPath
@@ -57,7 +79,7 @@ var/global/datum/controller/processScheduler/processScheduler
var/process
// Add all the processes we can find, except for the ticker
- for (process in typesof(/datum/controller/process) - /datum/controller/process)
+ for (process in subtypesof(/datum/controller/process))
if (!(process in deferredSetupList))
addProcess(new process(src))
@@ -66,11 +88,22 @@ var/global/datum/controller/processScheduler/processScheduler
/datum/controller/processScheduler/proc/start()
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 last_start[p] + p.schedule_interval)
+ if (world.time >= last_queued[p] + p.schedule_interval)
setQueuedProcessState(p)
/datum/controller/processScheduler/proc/runQueuedProcesses()
@@ -176,6 +201,10 @@ var/global/datum/controller/processScheduler/processScheduler
nameToProcessMap[newProcess.name] = newProcess
+/datum/controller/processScheduler/proc/updateStartDelays()
+ for(var/datum/controller/process/p in processes)
+ if(p.start_delay)
+ last_queued[p] = world.time - p.start_delay
/datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process)
spawn(0)
@@ -197,8 +226,6 @@ var/global/datum/controller/processScheduler/processScheduler
if (!(process in idle))
idle += process
- process.idle()
-
/datum/controller/processScheduler/proc/setQueuedProcessState(var/datum/controller/process/process)
if (process in running)
running -= process
@@ -218,21 +245,22 @@ var/global/datum/controller/processScheduler/processScheduler
if (!(process in running))
running += process
- process.running()
-
/datum/controller/processScheduler/proc/recordStart(var/datum/controller/process/process, var/time = null)
if (isnull(time))
- time = world.timeofday
-
- last_start[process] = time
+ time = TimeOfHour
+ 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 = world.timeofday
+ time = TimeOfHour
// If world.timeofday has rolled over, then we need to adjust.
if (time < last_start[process])
- last_start[process] -= 864000
+ last_start[process] -= 36000
var/lastRunTime = time - last_start[process]
@@ -273,6 +301,12 @@ var/global/datum/controller/processScheduler/processScheduler
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
@@ -310,11 +344,39 @@ var/global/datum/controller/processScheduler/processScheduler
var/datum/controller/process/process = nameToProcessMap[processName]
process.disable()
-/datum/controller/processScheduler/proc/getProcess(var/name)
- return nameToProcessMap[name]
+/datum/controller/processScheduler/proc/getCurrentTickElapsedTime()
+ if (world.time > currentTick)
+ updateCurrentTickData()
+ return 0
+ else
+ return TimeOfHour - currentTickStart
-/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process)
- return last_run_time[process]
+/datum/controller/processScheduler/proc/updateCurrentTickData()
+ if (world.time > currentTick)
+ // New tick!
+ currentTick = world.time
+ currentTickStart = TimeOfHour
+ updateTimeAllowance()
+ cpuAverage = (world.cpu + cpuAverage + cpuAverage) / 3
-/datum/controller/processScheduler/proc/getIsRunning()
- return isRunning
+/datum/controller/processScheduler/proc/updateTimeAllowance()
+ // Time allowance goes down linearly with world.cpu.
+ var/tmp/error = cpuAverage - 100
+ var/tmp/timeAllowanceDelta = 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/sign(var/x)
+ if (x == 0)
+ return 1
+ return x / abs(x)
+
+/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()
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/core/updateQueue.dm b/code/controllers/ProcessScheduler/core/updateQueue.dm
deleted file mode 100644
index 118b6692b5..0000000000
--- a/code/controllers/ProcessScheduler/core/updateQueue.dm
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * updateQueue.dm
- */
-
-#ifdef UPDATE_QUEUE_DEBUG
-#define uq_dbg(text) world << text
-#else
-#define uq_dbg(text)
-#endif
-/datum/updateQueue
- var/tmp/list/objects
- var/tmp/previousStart
- var/tmp/procName
- var/tmp/list/arguments
- var/tmp/datum/updateQueueWorker/currentWorker
- var/tmp/workerTimeout
- var/tmp/adjustedWorkerTimeout
- var/tmp/currentKillCount
- var/tmp/totalKillCount
-
-/datum/updateQueue/New(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0)
- ..()
-
- uq_dbg("Update queue created.")
-
- // Init proc allows for recycling the worker.
- init(objects = objects, procName = procName, arguments = arguments, workerTimeout = workerTimeout, inplace = inplace)
-
-/**
- * init
- * @param list objects objects to update
- * @param text procName the proc to call on each item in the object list
- * @param list arguments optional arguments to pass to the update proc
- * @param number workerTimeout number of ticks to wait for an update to
- finish before forking a new update worker
- * @param bool inplace whether the updateQueue should make a copy of objects.
- the internal list will be modified, so it is usually
- a good idea to leave this alone. Default behavior is to
- copy.
- */
-/datum/updateQueue/proc/init(list/objects = list(), procName = "update", list/arguments = list(), workerTimeout = 2, inplace = 0)
- uq_dbg("Update queue initialization started.")
-
- if (!inplace)
- // Make an internal copy of the list so we're not modifying the original.
- initList(objects)
- else
- src.objects = objects
-
- // Init vars
- src.procName = procName
- src.arguments = arguments
- src.workerTimeout = workerTimeout
-
- adjustedWorkerTimeout = workerTimeout
- currentKillCount = 0
- totalKillCount = 0
-
- uq_dbg("Update queue initialization finished. procName = '[procName]'")
-
-/datum/updateQueue/proc/initList(list/toCopy)
- /**
- * We will copy the list in reverse order, as our doWork proc
- * will access them by popping an element off the end of the list.
- * This ends up being quite a lot faster than taking elements off
- * the head of the list.
- */
- objects = new
-
- uq_dbg("Copying [toCopy.len] items for processing.")
-
- for(var/i=toCopy.len,i>0,)
- objects.len++
- objects[objects.len] = toCopy[i--]
-
-/datum/updateQueue/proc/Run()
- uq_dbg("Starting run...")
-
- startWorker()
- while (istype(currentWorker) && !currentWorker.finished)
- sleep(2)
- checkWorker()
-
- uq_dbg("UpdateQueue completed run.")
-
-/datum/updateQueue/proc/checkWorker()
- if(istype(currentWorker))
- // If world.timeofday has rolled over, then we need to adjust.
- if(world.timeofday < currentWorker.lastStart)
- currentWorker.lastStart -= 864000
-
- if(world.timeofday - currentWorker.lastStart > adjustedWorkerTimeout)
- // This worker is a bit slow, let's spawn a new one and kill the old one.
- uq_dbg("Current worker is lagging... starting a new one.")
- killWorker()
- startWorker()
- else // No worker!
- uq_dbg("update queue ended up without a worker... starting a new one...")
- startWorker()
-
-/datum/updateQueue/proc/startWorker()
- // only run the worker if we have objects to work on
- if(objects.len)
- uq_dbg("Starting worker process.")
-
- // No need to create a fresh worker if we already have one...
- if (istype(currentWorker))
- currentWorker.init(objects, procName, arguments)
- else
- currentWorker = new(objects, procName, arguments)
- currentWorker.start()
- else
- uq_dbg("Queue is empty. No worker was started.")
- currentWorker = null
-
-/datum/updateQueue/proc/killWorker()
- // Kill the worker
- currentWorker.kill()
- currentWorker = null
- // After we kill a worker, yield so that if the worker's been tying up the cpu, other stuff can immediately resume
- sleep(-1)
- currentKillCount++
- totalKillCount++
- if (currentKillCount >= 3)
- uq_dbg("[currentKillCount] workers have been killed with a timeout of [adjustedWorkerTimeout]. Increasing worker timeout to compensate.")
- adjustedWorkerTimeout++
- currentKillCount = 0
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/core/updateQueueWorker.dm b/code/controllers/ProcessScheduler/core/updateQueueWorker.dm
deleted file mode 100644
index 66f66bbcc0..0000000000
--- a/code/controllers/ProcessScheduler/core/updateQueueWorker.dm
+++ /dev/null
@@ -1,83 +0,0 @@
-datum/updateQueueWorker
- var/tmp/list/objects
- var/tmp/killed
- var/tmp/finished
- var/tmp/procName
- var/tmp/list/arguments
- var/tmp/lastStart
- var/tmp/cpuThreshold
-
-datum/updateQueueWorker/New(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90)
- ..()
- uq_dbg("updateQueueWorker created.")
-
- init(objects, procName, arguments, cpuThreshold)
-
-datum/updateQueueWorker/proc/init(var/list/objects, var/procName, var/list/arguments, var/cpuThreshold = 90)
- src.objects = objects
- src.procName = procName
- src.arguments = arguments
- src.cpuThreshold = cpuThreshold
-
- killed = 0
- finished = 0
-
-datum/updateQueueWorker/proc/doWork()
- // If there's nothing left to execute or we were killed, mark finished and return.
- if (!objects || !objects.len) return finished()
-
- lastStart = world.timeofday // Absolute number of ticks since the world started up
-
- var/datum/object = objects[objects.len] // Pull out the object
- objects.len-- // Remove the object from the list
-
- if (istype(object) && !isturf(object) && !object.disposed && isnull(object.gcDestroyed)) // We only work with real objects
- call(object, procName)(arglist(arguments))
-
- // If there's nothing left to execute
- // or we were killed while running the above code, mark finished and return.
- if (!objects || !objects.len) return finished()
-
- if (world.cpu > cpuThreshold)
- // We don't want to force a tick into overtime!
- // If the tick is about to go overtime, spawn the next update to go
- // in the next tick.
- uq_dbg("tick went into overtime with world.cpu = [world.cpu], deferred next update to next tick [1+(world.time / world.tick_lag)]")
-
- spawn(1)
- doWork()
- else
- spawn(0) // Execute anonymous function immediately as if we were in a while loop...
- doWork()
-
-datum/updateQueueWorker/proc/finished()
- uq_dbg("updateQueueWorker finished.")
- /**
- * If the worker was killed while it was working on something, it
- * should delete itself when it finally finishes working on it.
- * Meanwhile, the updateQueue will have proceeded on with the rest of
- * the queue. This will also terminate the spawned function that was
- * created in the kill() proc.
- */
- if(killed)
- del(src)
-
- finished = 1
-
-datum/updateQueueWorker/proc/kill()
- uq_dbg("updateQueueWorker killed.")
- killed = 1
- objects = null
-
- /**
- * If the worker is not done in 30 seconds after it's killed,
- * we'll forcibly delete it, causing the anonymous function it was
- * running to be terminated. Hasta la vista, baby.
- */
- spawn(300)
- del(src)
-
-datum/updateQueueWorker/proc/start()
- uq_dbg("updateQueueWorker started.")
- spawn(0)
- doWork()
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/processSchedulerView.dm b/code/controllers/ProcessScheduler/test/processSchedulerView.dm
deleted file mode 100644
index ae78b3f015..0000000000
--- a/code/controllers/ProcessScheduler/test/processSchedulerView.dm
+++ /dev/null
@@ -1,94 +0,0 @@
-/datum/processSchedulerView
-
-/datum/processSchedulerView/Topic(href, href_list)
- if (!href_list["action"])
- return
-
- switch (href_list["action"])
- if ("kill")
- var/toKill = href_list["name"]
- processScheduler.killProcess(toKill)
- refreshProcessTable()
- if ("enable")
- var/toEnable = href_list["name"]
- processScheduler.enableProcess(toEnable)
- refreshProcessTable()
- if ("disable")
- var/toDisable = href_list["name"]
- processScheduler.disableProcess(toDisable)
- refreshProcessTable()
- if ("refresh")
- refreshProcessTable()
-
-/datum/processSchedulerView/proc/refreshProcessTable()
- windowCall("handleRefresh", getProcessTable())
-
-/datum/processSchedulerView/proc/windowCall(var/function, var/data = null)
- usr << output(data, "processSchedulerContext.browser:[function]")
-
-/datum/processSchedulerView/proc/getProcessTable()
- var/text = "
Name
Avg(s)
Last(s)
Highest(s)
Tickcount
Tickrate
State
Action
"
- // and the context of each
- for (var/list/data in processScheduler.getStatusData())
- text += "
"
- text += "
[data["name"]]
"
- text += "
[num2text(data["averageRunTime"]/10,3)]
"
- text += "
[num2text(data["lastRunTime"]/10,3)]
"
- text += "
[num2text(data["highestRunTime"]/10,3)]
"
- text += "
[num2text(data["ticks"],4)]
"
- text += "
[data["schedule"]]
"
- text += "
[data["status"]]
"
- text += "
"
- if (data["disabled"])
- text += ""
- else
- text += ""
- text += "
"
- text += "
"
-
- text += "
"
- return text
-
-/**
- * getContext
- * Outputs an interface showing stats for all processes.
- */
-/datum/processSchedulerView/proc/getContext()
- bootstrap_browse()
- usr << browse('processScheduler.js', "file=processScheduler.js;display=0")
-
- var/text = {"
- Process Scheduler Detail
-
- [bootstrap_includes()]
-
-
-
-
Process Scheduler
-
-
-
-
-
The process scheduler controls [processScheduler.getProcessCount()] loops.
"}
-
- text += "
"
- text += getProcessTable()
- text += "
"
-
- usr << browse(text, "window=processSchedulerContext;size=800x600")
-
-/datum/processSchedulerView/proc/bootstrap_browse()
- usr << browse('bower_components/jquery/dist/jquery.min.js', "file=jquery.min.js;display=0")
- usr << browse('bower_components/bootstrap2.3.2/bootstrap/js/bootstrap.min.js', "file=bootstrap.min.js;display=0")
- usr << browse('bower_components/bootstrap2.3.2/bootstrap/css/bootstrap.min.css', "file=bootstrap.min.css;display=0")
- usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings-white.png', "file=glyphicons-halflings-white.png;display=0")
- usr << browse('bower_components/bootstrap2.3.2/bootstrap/img/glyphicons-halflings.png', "file=glyphicons-halflings.png;display=0")
- usr << browse('bower_components/json2/json2.js', "file=json2.js;display=0")
-
-/datum/processSchedulerView/proc/bootstrap_includes()
- return {"
-
-
-
-
- "}
diff --git a/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm b/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm
deleted file mode 100644
index d08ec46c7d..0000000000
--- a/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * testDyingUpdateQueueProcess
- * This process is an example of a process using an updateQueue.
- * The datums updated by this process behave badly and block the update loop
- * by sleeping. If you #define UPDATE_QUEUE_DEBUG, you will see the updateQueue
- * killing off its worker processes and spawning new ones to work around slow
- * updates. This means that if you have a code path that sleeps for a long time
- * in mob.Life once in a blue moon, the mob update loop will not hang.
- */
-/datum/slowTestDatum/proc/wackyUpdateProcessName()
- sleep(rand(0,20)) // Randomly REALLY slow :|
-
-/datum/controller/process/testDyingUpdateQueueProcess
- var/tmp/datum/updateQueue/updateQueueInstance
- var/tmp/list/testDatums = list()
-
-/datum/controller/process/testDyingUpdateQueueProcess/setup()
- name = "Dying UpdateQueue Process"
- schedule_interval = 30 // every 3 seconds
- updateQueueInstance = new
- for(var/i = 1, i < 30, i++)
- testDatums.Add(new /datum/slowTestDatum)
-
-/datum/controller/process/testDyingUpdateQueueProcess/doWork()
- updateQueueInstance.init(testDatums, "wackyUpdateProcessName")
- updateQueueInstance.Run()
-
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/testHarness.dm b/code/controllers/ProcessScheduler/test/testHarness.dm
deleted file mode 100644
index 2b5f1dff81..0000000000
--- a/code/controllers/ProcessScheduler/test/testHarness.dm
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- These are simple defaults for your project.
- */
-#define DEBUG
-
-var/global/datum/processSchedulerView/processSchedulerView
-
-world
- loop_checks = 0
- New()
- ..()
- processScheduler = new
- processSchedulerView = new
-
-mob
- step_size = 8
-
- New()
- ..()
-
-
- verb
- startProcessScheduler()
- set name = "Start Process Scheduler"
- processScheduler.setup()
- processScheduler.start()
-
- getProcessSchedulerContext()
- set name = "Get Process Scheduler Status Panel"
- processSchedulerView.getContext()
-
- runUpdateQueueTests()
- set name = "Run Update Queue Testsuite"
- var/datum/updateQueueTests/t = new
- t.runTests()
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/testHungProcess.dm b/code/controllers/ProcessScheduler/test/testHungProcess.dm
deleted file mode 100644
index ced05dd4d7..0000000000
--- a/code/controllers/ProcessScheduler/test/testHungProcess.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * testHungProcess
- * This process is an example of a simple update loop process that hangs.
- */
-
-/datum/controller/process/testHungProcess/setup()
- name = "Hung Process"
- schedule_interval = 30 // every 3 seconds
-
-/datum/controller/process/testHungProcess/doWork()
- sleep(1000) // FUCK
- // scheck is also responsible for handling hung processes. If a process
- // hangs, and later resumes, but has already been killed by the scheduler,
- // scheck will force the process to bail out.
- scheck()
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/testNiceProcess.dm b/code/controllers/ProcessScheduler/test/testNiceProcess.dm
deleted file mode 100644
index aa921bc62f..0000000000
--- a/code/controllers/ProcessScheduler/test/testNiceProcess.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * testNiceProcess
- * This process is an example of a simple update loop process that is
- * relatively fast.
- */
-
-/datum/controller/process/testNiceProcess/setup()
- name = "Nice Process"
- schedule_interval = 10 // every second
-
-/datum/controller/process/testNiceProcess/doWork()
- sleep(rand(1,5)) // Just to pretend we're doing something
-
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/testSlowProcess.dm b/code/controllers/ProcessScheduler/test/testSlowProcess.dm
deleted file mode 100644
index b7c9e6e21e..0000000000
--- a/code/controllers/ProcessScheduler/test/testSlowProcess.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * testSlowProcess
- * This process is an example of a simple update loop process that is slow.
- * The update loop here sleeps inside to provide an example, but if you had
- * a computationally intensive loop process that is simply slow, you can use
- * scheck() inside the loop to force it to yield periodically according to
- * the sleep_interval var. By default, scheck will cause a loop to sleep every
- * 2 ticks.
- */
-
-/datum/controller/process/testSlowProcess/setup()
- name = "Slow Process"
- schedule_interval = 30 // every 3 seconds
-
-/datum/controller/process/testSlowProcess/doWork()
- // set background = 1 will cause loop constructs to sleep periodically,
- // whenever the BYOND scheduler deems it productive to do so.
- // This behavior is not always sufficient, nor is it always consistent.
- // Rather than leaving it up to the BYOND scheduler, we can control it
- // ourselves and leave nothing to the black box.
- set background = 1
-
- for(var/i=1,i<30,i++)
- // Just to pretend we're doing something here
- sleep(rand(3, 5))
-
- // Forces this loop to yield(sleep) periodically.
- scheck()
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/testUpdateQueue.dm b/code/controllers/ProcessScheduler/test/testUpdateQueue.dm
deleted file mode 100644
index 07b64e927f..0000000000
--- a/code/controllers/ProcessScheduler/test/testUpdateQueue.dm
+++ /dev/null
@@ -1,209 +0,0 @@
-var/global/list/updateQueueTestCount = list()
-
-/datum/updateQueueTests
- var/start
- proc
- runTests()
- world << "Running 9 tests..."
- testUpdateQueuePerformance()
- sleep(1)
- testInplace()
- sleep(1)
- testInplaceUpdateQueuePerformance()
- sleep(1)
- testUpdateQueueReinit()
- sleep(1)
- testCrashingQueue()
- sleep(1)
- testEmptyQueue()
- sleep(1)
- testManySlowItemsInQueue()
- sleep(1)
- testVariableWorkerTimeout()
- sleep(1)
- testReallySlowItemInQueue()
- sleep(1)
- world << "Finished!"
-
- beginTiming()
- start = world.time
-
- endTiming(text)
- var/time = (world.time - start) / world.tick_lag
- world << {"Performance - [text] - [time] ticks"}
-
- getCount()
- return updateQueueTestCount[updateQueueTestCount.len]
-
- incrementTestCount()
- updateQueueTestCount.len++
- updateQueueTestCount[updateQueueTestCount.len] = 0
-
- assertCountEquals(count, text)
- assertThat(getCount() == count, text)
-
- assertCountLessThan(count, text)
- assertThat(getCount() < count, text)
-
- assertCountGreaterThan(count, text)
- assertThat(getCount() > count, text)
-
- assertThat(condition, text)
- if (condition)
- world << {"PASS: [text]"}
- else
- world << {"FAIL: [text]"}
-
- testUpdateQueuePerformance()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=100000,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
-
- var/datum/updateQueue/uq = new(objs)
-
- beginTiming()
- uq.Run()
- endTiming("updating 100000 simple objects")
-
- assertCountEquals(100000, "test that update queue updates all objects expected")
- del(objs)
- del(uq)
-
- testUpdateQueueReinit()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=100,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
-
- var/datum/updateQueue/uq = new(objs)
- uq.Run()
- objs = new
-
- for(var/i=1,i<=100,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
- uq.init(objs)
- uq.Run()
- assertCountEquals(200, "test that update queue reinitializes properly and updates all objects as expected.")
- del(objs)
- del(uq)
-
- testInplace()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=100,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
- var/datum/updateQueue/uq = new(objects = objs, inplace = 1)
- uq.Run()
- assertThat(objs.len == 0, "test that update queue inplace option really works inplace")
- assertCountEquals(100, "test that inplace update queue updates the right number of objects")
- del(objs)
- del(uq)
-
- testInplaceUpdateQueuePerformance()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=100000,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
-
- var/datum/updateQueue/uq = new(objs)
-
- beginTiming()
- uq.Run()
- endTiming("updating 100000 simple objects in place")
- del(objs)
- del(uq)
-
- testCrashingQueue()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=10,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
- objs.Add(new /datum/uqTestDatum/crasher(updateQueueTestCount.len))
- for(var/i=1,i<=10,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
-
- var/datum/updateQueue/uq = new(objs)
- uq.Run()
- assertCountEquals(20, "test that update queue handles crashed update procs OK")
- del(objs)
- del(uq)
-
- testEmptyQueue()
- incrementTestCount()
- var/list/objs = new
- var/datum/updateQueue/uq = new(objs)
- uq.Run()
- assertCountEquals(0, "test that update queue doesn't barf on empty lists")
- del(objs)
- del(uq)
-
- testManySlowItemsInQueue()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=30,i++)
- objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
- var/datum/updateQueue/uq = new(objs)
- uq.Run()
- assertCountEquals(30, "test that update queue slows down execution if too many objects are slow to update")
- del(objs)
- del(uq)
-
- testVariableWorkerTimeout()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=20,i++)
- objs.Add(new /datum/uqTestDatum/slow(updateQueueTestCount.len))
- var/datum/updateQueue/uq = new(objs, workerTimeout=6)
- uq.Run()
- assertCountEquals(20, "test that variable worker timeout works properly")
- del(objs)
- del(uq)
-
- testReallySlowItemInQueue()
- incrementTestCount()
- var/list/objs = new
- for(var/i=1,i<=10,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
- objs.Add(new /datum/uqTestDatum/reallySlow(updateQueueTestCount.len))
- for(var/i=1,i<=10,i++)
- objs.Add(new /datum/uqTestDatum/fast(updateQueueTestCount.len))
- var/datum/updateQueue/uq = new(objs)
- uq.Run()
- assertCountEquals(20, "test that update queue skips objects that are too slow to update")
- del(objs)
- del(uq)
-
-
-
-datum/uqTestDatum
- var/testNum
- New(testNum)
- ..()
- src.testNum = testNum
- proc/update()
- updateQueueTestCount[testNum]++
- proc/lag(cycles)
- set background = 1
- for(var/i=0,i time_to_kill)
- break
- var/atom/A = locate(refID)
- if(A && A.gcDestroyed == GCd_at_time)
- searching += A
- if(searching.len >= checkRemain)
- break
-
- for(var/atom/A in searching)
- testing("GC: Searching references for [A] | [A.type]")
- if(A.loc != null)
- testing("GC: [A] | [A.type] is located in [A.loc] instead of null")
- if(A.contents.len)
- testing("GC: [A] | [A.type] has contents: [jointext(A.contents)]")
- if(searching.len)
- for(var/atom/D in world)
- LookForRefs(D, searching)
- for(var/datum/D)
- LookForRefs(D, searching)
- #endif
while(destroyed.len && --checkRemain >= 0)
- if(dels >= maxDels)
+ if(remaining_force_dels <= 0)
#ifdef GC_DEBUG
testing("GC: Reached max force dels per tick [dels] vs [maxDels]")
#endif
@@ -88,13 +70,22 @@ world/loop_checks = 0
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
logging["[A.type]"]++
del(A)
- ++dels
- ++hard_dels
- #ifdef GC_DEBUG
+
+ hard_dels++
+ remaining_force_dels--
else
+ #ifdef GC_DEBUG
testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
- #endif
+ #endif
+ soft_dels++
+ tick_dels++
+ total_dels++
destroyed.Cut(1, 2)
+ SCHECK
+
+#undef GC_FORCE_DEL_PER_TICK
+#undef GC_COLLECTION_TIMEOUT
+#undef GC_COLLECTIONS_PER_TICK
#ifdef GC_FINDREF
/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ)
@@ -132,8 +123,11 @@ world/loop_checks = 0
destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
destroyed["\ref[A]"] = world.time
-/datum/controller/process/garbage_collector/getStatName()
- return ..()+"([garbage_collector.destroyed.len]/[garbage_collector.dels]/[garbage_collector.hard_dels])"
+/datum/controller/process/garbage_collector/statProcess()
+ ..()
+ stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued")
+ stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run")
+
// Tests if an atom has been deleted.
/proc/deleted(atom/A)
@@ -149,7 +143,7 @@ world/loop_checks = 0
crash_with("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
del(A)
if(garbage_collector)
- garbage_collector.dels++
+ garbage_collector.total_dels++
garbage_collector.hard_dels++
else if(isnull(A.gcDestroyed))
// Let our friend know they're about to get collected
@@ -263,4 +257,4 @@ world/loop_checks = 0
#ifdef GC_FINDREF
#undef GC_FINDREF
-#endif
+#endif
\ No newline at end of file
diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/Processes/inactivity.dm
index e61c2d4dc1..26cc136ccc 100644
--- a/code/controllers/Processes/inactivity.dm
+++ b/code/controllers/Processes/inactivity.dm
@@ -4,10 +4,11 @@
/datum/controller/process/inactivity/doWork()
if(config.kick_inactive)
- for(var/client/C in clients)
+ for(last_object in clients)
+ var/client/C = last_object
if(!C.holder && C.is_afk(config.kick_inactive MINUTES))
if(!istype(C.mob, /mob/observer/dead))
log_access("AFK: [key_name(C)]")
C << "You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected."
del(C) // Don't qdel, cannot override finalize_qdel behaviour for clients.
- scheck()
+ SCHECK
diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm
index 7959ee4fa7..405615dc1f 100644
--- a/code/controllers/Processes/machinery.dm
+++ b/code/controllers/Processes/machinery.dm
@@ -3,6 +3,7 @@
/datum/controller/process/machinery/setup()
name = "machinery"
schedule_interval = 20 // every 2 seconds
+ start_delay = 12
/datum/controller/process/machinery/doWork()
internal_sort()
@@ -17,12 +18,9 @@
machines = dd_sortedObjectList(machines)
/datum/controller/process/machinery/proc/internal_process_machinery()
- for(var/obj/machinery/M in machines)
+ for(last_object in machines)
+ var/obj/machinery/M = last_object
if(M && !M.gcDestroyed)
- #ifdef PROFILE_MACHINES
- var/time_start = world.timeofday
- #endif
-
if(M.process() == PROCESS_KILL)
//M.inMachineList = 0 We don't use this debugging function
machines.Remove(M)
@@ -31,41 +29,39 @@
if(M && M.use_power)
M.auto_use_power()
- #ifdef PROFILE_MACHINES
- var/time_end = world.timeofday
-
- if(!(M.type in machine_profiling))
- machine_profiling[M.type] = 0
-
- machine_profiling[M.type] += (time_end - time_start)
- #endif
-
- scheck()
+ SCHECK
/datum/controller/process/machinery/proc/internal_process_power()
- for(var/datum/powernet/powerNetwork in powernets)
- if(istype(powerNetwork) && !powerNetwork.disposed)
+ for(last_object in powernets)
+ var/datum/powernet/powerNetwork = last_object
+ if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed))
powerNetwork.reset()
- scheck()
+ SCHECK
continue
powernets.Remove(powerNetwork)
/datum/controller/process/machinery/proc/internal_process_power_drain()
// Currently only used by powersinks. These items get priority processed before machinery
- for(var/obj/item/I in processing_power_items)
+ for(last_object in processing_power_items)
+ var/obj/item/I = last_object
if(!I.pwr_drain()) // 0 = Process Kill, remove from processing list.
processing_power_items.Remove(I)
- scheck()
+ SCHECK
/datum/controller/process/machinery/proc/internal_process_pipenets()
- for(var/datum/pipe_network/pipeNetwork in pipe_networks)
- if(istype(pipeNetwork) && !pipeNetwork.disposed)
+ for(last_object in pipe_networks)
+ var/datum/pipe_network/pipeNetwork = last_object
+ if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
pipeNetwork.process()
- scheck()
+ SCHECK
continue
pipe_networks.Remove(pipeNetwork)
-/datum/controller/process/machinery/getStatName()
- return ..()+"(MCH:[machines.len] PWR:[powernets.len] PIP:[pipe_networks.len])"
+/datum/controller/process/machinery/statProcess()
+ ..()
+ stat(null, "[machines.len] machines")
+ stat(null, "[powernets.len] powernets")
+ stat(null, "[pipe_networks.len] pipenets")
+ stat(null, "[processing_power_items.len] power item\s")
\ No newline at end of file
diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm
index 39d4844a02..b44842036d 100644
--- a/code/controllers/Processes/mob.dm
+++ b/code/controllers/Processes/mob.dm
@@ -4,20 +4,26 @@
/datum/controller/process/mob/setup()
name = "mob"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
+ start_delay = 16
/datum/controller/process/mob/started()
..()
- if(!updateQueueInstance)
- if(!mob_list)
- mob_list = list()
- else if(mob_list.len)
- updateQueueInstance = new
+ if(!mob_list)
+ mob_list = list()
/datum/controller/process/mob/doWork()
- if(updateQueueInstance)
- updateQueueInstance.init(mob_list, "Life")
- updateQueueInstance.Run()
+ for(last_object in mob_list)
+ var/mob/M = last_object
+ if(isnull(M.gcDestroyed))
+ try
+ M.Life()
+ catch(var/exception/e)
+ catchException(e, M)
+ SCHECK
+ else
+ catchBadType(M)
+ mob_list -= M
-/datum/controller/process/mob/getStatName()
- return ..()+"([mob_list.len])"
+/datum/controller/process/mob/statProcess()
+ ..()
+ stat(null, "[mob_list.len] mobs")
\ No newline at end of file
diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm
index 654b1621be..49b9048c07 100644
--- a/code/controllers/Processes/nanoui.dm
+++ b/code/controllers/Processes/nanoui.dm
@@ -1,14 +1,19 @@
-/datum/controller/process/nanoui
- var/tmp/datum/updateQueue/updateQueueInstance
-
/datum/controller/process/nanoui/setup()
name = "nanoui"
- schedule_interval = 10 // every 1 second
- updateQueueInstance = new
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/nanoui/statProcess()
+ ..()
+ stat(null, "[nanomanager.processing_uis.len] UIs")
/datum/controller/process/nanoui/doWork()
- updateQueueInstance.init(nanomanager.processing_uis, "process")
- updateQueueInstance.Run()
-
-/datum/controller/process/nanoui/getStatName()
- return ..()+"([nanomanager.processing_uis.len])"
+ for(last_object in nanomanager.processing_uis)
+ var/datum/nanoui/NUI = last_object
+ if(istype(NUI) && isnull(NUI.gcDestroyed))
+ try
+ NUI.process()
+ catch(var/exception/e)
+ catchException(e, NUI)
+ else
+ catchBadType(NUI)
+ nanomanager.processing_uis -= NUI
\ No newline at end of file
diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm
index 37766cf92d..bd50edd111 100644
--- a/code/controllers/Processes/obj.dm
+++ b/code/controllers/Processes/obj.dm
@@ -1,24 +1,26 @@
-var/global/list/object_profiling = list()
-/datum/controller/process/obj
- var/tmp/datum/updateQueue/updateQueueInstance
-
/datum/controller/process/obj/setup()
name = "obj"
schedule_interval = 20 // every 2 seconds
- updateQueueInstance = new
+ start_delay = 8
/datum/controller/process/obj/started()
..()
- if(!updateQueueInstance)
- if(!processing_objects)
- processing_objects = list()
- else if(processing_objects.len)
- updateQueueInstance = new
+ if(!processing_objects)
+ processing_objects = list()
/datum/controller/process/obj/doWork()
- if(updateQueueInstance)
- updateQueueInstance.init(processing_objects, "process")
- updateQueueInstance.Run()
+ for(last_object in processing_objects)
+ var/datum/O = last_object
+ if(isnull(O.gcDestroyed))
+ try
+ O:process()
+ catch(var/exception/e)
+ catchException(e, O)
+ SCHECK
+ else
+ catchBadType(O)
+ processing_objects -= O
-/datum/controller/process/obj/getStatName()
- return ..()+"([processing_objects.len])"
+/datum/controller/process/obj/statProcess()
+ ..()
+ stat(null, "[processing_objects.len] objects")
\ No newline at end of file
diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm
new file mode 100644
index 0000000000..fdbe55faed
--- /dev/null
+++ b/code/controllers/Processes/scheduler.dm
@@ -0,0 +1,133 @@
+/var/datum/controller/process/scheduler/scheduler
+
+/************
+* Scheduler *
+************/
+/datum/controller/process/scheduler
+ var/list/scheduled_tasks
+
+/datum/controller/process/scheduler/setup()
+ name = "scheduler"
+ schedule_interval = 3 SECONDS
+ scheduled_tasks = list()
+ scheduler = src
+
+/datum/controller/process/scheduler/doWork()
+ for(last_object in scheduled_tasks)
+ var/datum/scheduled_task/scheduled_task = last_object
+ try
+ if(world.time > scheduled_task.trigger_time)
+ unschedule(scheduled_task)
+ scheduled_task.pre_process()
+ scheduled_task.process()
+ scheduled_task.post_process()
+ catch(var/exception/e)
+ catchException(e, last_object)
+ SCHECK
+
+/datum/controller/process/scheduler/statProcess()
+ ..()
+ stat(null, "[scheduled_tasks.len] task\s")
+
+/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
+ scheduled_tasks += st
+ destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
+
+/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
+ if(st in scheduled_tasks)
+ scheduled_tasks -= st
+ destroyed_event.unregister(st, src)
+
+/**********
+* Helpers *
+**********/
+/proc/schedule_task_in(var/in_time, var/procedure, var/list/arguments = list())
+ return schedule_task(world.time + in_time, procedure, arguments)
+
+/proc/schedule_task_with_source_in(var/in_time, var/source, var/procedure, var/list/arguments = list())
+ return schedule_task_with_source(world.time + in_time, source, procedure, arguments)
+
+/proc/schedule_task(var/trigger_time, var/procedure, var/list/arguments)
+ var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/destroy_scheduled_task, list())
+ scheduler.schedule(st)
+ return st
+
+/proc/schedule_task_with_source(var/trigger_time, var/source, var/procedure, var/list/arguments)
+ var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/destroy_scheduled_task, list())
+ scheduler.schedule(st)
+ return st
+
+/proc/schedule_repeating_task(var/trigger_time, var/repeat_interval, var/procedure, var/list/arguments)
+ var/datum/scheduled_task/st = new/datum/scheduled_task(trigger_time, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
+ scheduler.schedule(st)
+ return st
+
+/proc/schedule_repeating_task_with_source(var/trigger_time, var/repeat_interval, var/source, var/procedure, var/list/arguments)
+ var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
+ scheduler.schedule(st)
+ return st
+
+/*************
+* Task Datum *
+*************/
+/datum/scheduled_task
+ var/trigger_time
+ var/procedure
+ var/list/arguments
+ var/task_after_process
+ var/list/task_after_process_args
+
+/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
+ ..()
+ src.trigger_time = trigger_time
+ src.procedure = procedure
+ src.arguments = arguments ? arguments : list()
+ src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
+ src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
+ task_after_process_args += src
+
+/datum/scheduled_task/Destroy()
+ procedure = null
+ arguments.Cut()
+ task_after_process = null
+ task_after_process_args.Cut()
+ return ..()
+
+/datum/scheduled_task/proc/pre_process()
+ task_triggered_event.raise_event(list(src))
+
+/datum/scheduled_task/proc/process()
+ if(procedure)
+ call(procedure)(arglist(arguments))
+
+/datum/scheduled_task/proc/post_process()
+ call(task_after_process)(arglist(task_after_process_args))
+
+// Resets the trigger time, has no effect if the task has already triggered
+/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
+ src.trigger_time = world.time + trigger_in
+
+/datum/scheduled_task/source
+ var/datum/source
+
+/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
+ src.source = source
+ destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
+ ..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
+
+/datum/scheduled_task/source/Destroy()
+ source = null
+ return ..()
+
+/datum/scheduled_task/source/process()
+ call(source, procedure)(arglist(arguments))
+
+/datum/scheduled_task/source/proc/source_destroyed()
+ qdel(src)
+
+/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
+ qdel(st)
+
+/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
+ st.trigger_time = world.time + trigger_delay
+ scheduler.schedule(st)
\ No newline at end of file
diff --git a/code/controllers/Processes/turf.dm b/code/controllers/Processes/turf.dm
index 2ac33f48ba..bfced8f93b 100644
--- a/code/controllers/Processes/turf.dm
+++ b/code/controllers/Processes/turf.dm
@@ -5,10 +5,12 @@ var/global/list/turf/processing_turfs = list()
schedule_interval = 20 // every 2 seconds
/datum/controller/process/turf/doWork()
- for(var/turf/T in processing_turfs)
+ for(last_object in processing_turfs)
+ var/turf/T = last_object
if(T.process() == PROCESS_KILL)
processing_turfs.Remove(T)
- scheck()
+ SCHECK
-/datum/controller/process/turf/getStatName()
- return ..()+"([processing_turfs.len])"
+/datum/controller/process/turf/statProcess()
+ ..()
+ stat(null, "[processing_turfs.len] turf\s")
\ No newline at end of file
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index d456c52e71..b249db9a47 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -21,6 +21,7 @@ var/list/gamemode_cache = list()
var/log_pda = 0 // log pda messages
var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits
var/log_runtime = 0 // logs world.log to a file
+ var/log_world_output = 0 // log world.log << messages
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/allow_vote_restart = 0 // allow votes to restart
@@ -327,6 +328,9 @@ var/list/gamemode_cache = list()
if ("log_pda")
config.log_pda = 1
+ if ("log_world_output")
+ config.log_world_output = 1
+
if ("log_hrefs")
config.log_hrefs = 1
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 5263e1af79..682b0fb3d0 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -47,7 +47,8 @@ datum/controller/game_controller/proc/setup_objects()
admin_notice("Initializing objects", R_DEBUG)
sleep(-1)
for(var/atom/movable/object in world)
- object.initialize()
+ if(isnull(object.gcDestroyed))
+ object.initialize()
admin_notice("Initializing areas", R_DEBUG)
sleep(-1)
diff --git a/code/controllers/observer_listener/datum/observer.dm b/code/controllers/observer_listener/datum/observer.dm
deleted file mode 100644
index 61f6fbf180..0000000000
--- a/code/controllers/observer_listener/datum/observer.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-#define OBSERVER_EVENT_DESTROY "OnDestroy"
-
-/datum
- var/list/observer_events
-
-/datum/Destroy()
- for(var/list/listeners in observer_events)
- listeners.Cut()
-
- return ..()
-
-/datum/proc/register(var/event, var/procOwner, var/proc_call)
- var/list/listeners = get_listener_list_from_event(event)
- listeners[procOwner] = proc_call
-
-/datum/proc/unregister(var/event, var/procOwner)
- var/list/listeners = get_listener_list_from_event(event)
- listeners -= procOwner
-
-/datum/proc/get_listener_list_from_event(var/observer_event)
- if(!observer_events) observer_events = list()
- var/list/listeners = observer_events[observer_event]
- if(!listeners)
- listeners = list()
- observer_events[observer_event] = listeners
- return listeners
-*/
diff --git a/code/controllers/subsystem/alarms.dm b/code/controllers/subsystem/alarms.dm
deleted file mode 100644
index b05be7ccbf..0000000000
--- a/code/controllers/subsystem/alarms.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-// We manually initialize the alarm handlers instead of looping over all existing types
-// to make it possible to write: camera.triggerAlarm() rather than alarm_manager.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof.
-/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
-/var/global/datum/alarm_handler/camera/camera_alarm = new()
-/var/global/datum/alarm_handler/fire/fire_alarm = new()
-/var/global/datum/alarm_handler/motion/motion_alarm = new()
-/var/global/datum/alarm_handler/power/power_alarm = new()
-
-/datum/subsystem/alarm
- name = "Alarm"
- var/list/datum/alarm/all_handlers
-
-/datum/subsystem/alarm/New()
- all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
-
-/datum/subsystem/alarm/fire()
- for(var/datum/alarm_handler/AH in all_handlers)
- AH.process()
-
-/datum/subsystem/alarm/proc/active_alarms()
- var/list/all_alarms = new
- for(var/datum/alarm_handler/AH in all_handlers)
- var/list/alarms = AH.alarms
- all_alarms += alarms
-
- return all_alarms
-
-/datum/subsystem/alarm/proc/number_of_active_alarms()
- var/list/alarms = active_alarms()
- return alarms.len
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 34082d12ba..aef0fd0ac2 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -28,7 +28,6 @@ var/global/datum/getrev/revdata = new()
world.log << branch
world.log << date
world.log << revision
- return
client/verb/showrevinfo()
set category = "OOC"
@@ -43,4 +42,3 @@ client/verb/showrevinfo()
src << revdata.revision
else
src << "Revision unknown"
- return
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 824b52a2fe..11d622b255 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -71,6 +71,7 @@
/datum/mind/New(var/key)
src.key = key
+ ..()
/datum/mind/proc/transfer_to(mob/living/new_character)
if(!istype(new_character))
diff --git a/code/datums/observation/_debug.dm b/code/datums/observation/_debug.dm
new file mode 100644
index 0000000000..2f882929cf
--- /dev/null
+++ b/code/datums/observation/_debug.dm
@@ -0,0 +1,11 @@
+/****************
+* Debug Support *
+****************/
+var/datum/all_observable_events/all_observable_events = new()
+
+/datum/all_observable_events
+ var/list/events
+
+/datum/all_observable_events/New()
+ events = list()
+ ..()
diff --git a/code/datums/observation/_defines.dm b/code/datums/observation/_defines.dm
new file mode 100644
index 0000000000..902d655870
--- /dev/null
+++ b/code/datums/observation/_defines.dm
@@ -0,0 +1 @@
+#define CANCEL_MOVE_EVENT -55
diff --git a/code/datums/observation/destroyed.dm b/code/datums/observation/destroyed.dm
new file mode 100644
index 0000000000..6a65300a25
--- /dev/null
+++ b/code/datums/observation/destroyed.dm
@@ -0,0 +1,15 @@
+// Observer Pattern Implementation: Destroyed
+// Registration type: /datum
+//
+// Raised when: A /datum instance is destroyed.
+//
+// Arguments that the called proc should expect:
+// /datum/destroyed_instance: The instance that was destroyed.
+var/decl/observ/destroyed/destroyed_event = new()
+
+/decl/observ/destroyed
+ name = "Destroyed"
+
+/datum/Destroy()
+ destroyed_event.raise_event(src)
+ . = ..()
diff --git a/code/datums/observation/dir_set.dm b/code/datums/observation/dir_set.dm
new file mode 100644
index 0000000000..1f4c8e251e
--- /dev/null
+++ b/code/datums/observation/dir_set.dm
@@ -0,0 +1,35 @@
+// Observer Pattern Implementation: Direction Set
+// Registration type: /atom
+//
+// Raised when: An /atom changes dir using the set_dir() proc.
+//
+// Arguments that the called proc should expect:
+// /atom/dir_changer: The instance that changed direction
+// /old_dir: The dir before the change.
+// /new_dir: The dir after the change.
+
+var/decl/observ/dir_set/dir_set_event = new()
+
+/decl/observ/dir_set
+ name = "Direction Set"
+ expected_type = /atom
+
+/decl/observ/dir_set/register(var/atom/dir_changer, var/datum/listener, var/proc_call)
+ . = ..()
+
+ // Listen to the parent if possible.
+ if(. && istype(dir_changer.loc, /atom/movable)) // We don't care about registering to turfs.
+ register(dir_changer.loc, dir_changer, /atom/proc/recursive_dir_set)
+
+/*********************
+* Direction Handling *
+*********************/
+
+/atom/movable/Entered(var/atom/movable/am, atom/old_loc)
+ . = ..()
+ if(. != CANCEL_MOVE_EVENT && dir_set_event.has_listeners(am))
+ dir_set_event.register(src, am, /atom/proc/recursive_dir_set)
+
+/atom/movable/Exited(var/atom/movable/am, atom/old_loc)
+ . = ..()
+ dir_set_event.unregister(src, am, /atom/proc/recursive_dir_set)
diff --git a/code/datums/observation/equipped.dm b/code/datums/observation/equipped.dm
new file mode 100644
index 0000000000..de07a74355
--- /dev/null
+++ b/code/datums/observation/equipped.dm
@@ -0,0 +1,38 @@
+// Observer Pattern Implementation: Equipped
+// Registration type: /mob
+//
+// Raised when: A mob equips an item.
+//
+// Arguments that the called proc should expect:
+// /mob/equipper: The mob that equipped the item.
+// /obj/item/item: The equipped item.
+// slot: The slot equipped to.
+var/decl/observ/mob_equipped/mob_equipped_event = new()
+
+/decl/observ/mob_equipped
+ name = "Mob Equipped"
+ expected_type = /mob
+
+// Observer Pattern Implementation: Equipped
+// Registration type: /obj/item
+//
+// Raised when: A mob equips an item.
+//
+// Arguments that the called proc should expect:
+// /obj/item/item: The equipped item.
+// /mob/equipper: The mob that equipped the item.
+// slot: The slot equipped to.
+var/decl/observ/item_equipped/item_equipped_event = new()
+
+/decl/observ/item_equipped
+ name = "Item Equipped"
+ expected_type = /obj/item
+
+/********************
+* Equipped Handling *
+********************/
+
+/obj/item/equipped(var/mob/user, var/slot)
+ . = ..()
+ mob_equipped_event.raise_event(user, src, slot)
+ item_equipped_event.raise_event(src, user, slot)
diff --git a/code/datums/observation/helpers.dm b/code/datums/observation/helpers.dm
new file mode 100644
index 0000000000..79ee8eb08d
--- /dev/null
+++ b/code/datums/observation/helpers.dm
@@ -0,0 +1,18 @@
+/atom/movable/proc/recursive_move(var/atom/movable/am, var/old_loc, var/new_loc)
+ moved_event.raise_event(src, old_loc, new_loc)
+
+/atom/movable/proc/move_to_destination(var/atom/movable/am, var/old_loc, var/new_loc)
+ var/turf/T = get_turf(new_loc)
+ if(T && T != loc)
+ forceMove(T)
+
+/atom/proc/recursive_dir_set(var/atom/a, var/old_dir, var/new_dir)
+ set_dir(new_dir)
+
+/proc/register_all_movement(var/event_source, var/listener)
+ moved_event.register(event_source, listener, /atom/movable/proc/recursive_move)
+ dir_set_event.register(event_source, listener, /atom/proc/recursive_dir_set)
+
+/proc/unregister_all_movement(var/event_source, var/listener)
+ moved_event.unregister(event_source, listener, /atom/movable/proc/recursive_move)
+ dir_set_event.unregister(event_source, listener, /atom/proc/recursive_dir_set)
diff --git a/code/datums/observation/logged_in.dm b/code/datums/observation/logged_in.dm
new file mode 100644
index 0000000000..311ff8acb6
--- /dev/null
+++ b/code/datums/observation/logged_in.dm
@@ -0,0 +1,21 @@
+// Observer Pattern Implementation: Logged in
+// Registration type: /mob
+//
+// Raised when: A mob logs in (not client)
+//
+// Arguments that the called proc should expect:
+// /mob/joiner: The mob that has logged in
+
+var/decl/observ/logged_in/logged_in_event = new()
+
+/decl/observ/logged_in
+ name = "Logged In"
+ expected_type = /mob
+
+/*****************
+* Login Handling *
+*****************/
+
+/mob/Login()
+ ..()
+ logged_in_event.raise_event(src)
diff --git a/code/datums/observation/moved.dm b/code/datums/observation/moved.dm
new file mode 100644
index 0000000000..86a6b793ac
--- /dev/null
+++ b/code/datums/observation/moved.dm
@@ -0,0 +1,52 @@
+// Observer Pattern Implementation: Moved
+// Registration type: /atom/movable
+//
+// Raised when: An /atom/movable instance has moved using Move() or forceMove().
+//
+// Arguments that the called proc should expect:
+// /atom/movable/moving_instance: The instance that moved
+// /atom/old_loc: The loc before the move.
+// /atom/new_loc: The loc after the move.
+
+var/decl/observ/moved/moved_event = new()
+
+/decl/observ/moved
+ name = "Moved"
+ expected_type = /atom/movable
+
+/decl/observ/moved/register(var/atom/movable/mover, var/datum/listener, var/proc_call)
+ . = ..()
+
+ // Listen to the parent if possible.
+ if(. && istype(mover.loc, expected_type))
+ register(mover.loc, mover, /atom/movable/proc/recursive_move)
+
+/********************
+* Movement Handling *
+********************/
+
+/atom/Entered(var/atom/movable/am, var/atom/old_loc)
+ . = ..()
+ moved_event.raise_event(am, old_loc, am.loc)
+
+/atom/movable/Entered(var/atom/movable/am, atom/old_loc)
+ . = ..()
+ if(moved_event.has_listeners(am))
+ moved_event.register(src, am, /atom/movable/proc/recursive_move)
+
+/atom/movable/Exited(var/atom/movable/am, atom/old_loc)
+ . = ..()
+ moved_event.unregister(src, am, /atom/movable/proc/recursive_move)
+
+// Entered() typically lifts the moved event, but in the case of null-space we'll have to handle it.
+/atom/movable/Move()
+ var/old_loc = loc
+ . = ..()
+ if(. && !loc)
+ moved_event.raise_event(src, old_loc, null)
+
+/atom/movable/forceMove(atom/destination)
+ var/old_loc = loc
+ . = ..()
+ if(. && !loc)
+ moved_event.raise_event(src, old_loc, null)
diff --git a/code/datums/observation/observation.dm b/code/datums/observation/observation.dm
new file mode 100644
index 0000000000..32a96871e6
--- /dev/null
+++ b/code/datums/observation/observation.dm
@@ -0,0 +1,238 @@
+//
+// Observer Pattern Implementation
+//
+// Implements a basic observer pattern with the following main procs:
+//
+// /decl/observ/proc/is_listening(var/event_source, var/datum/listener, var/proc_call)
+// event_source: The instance which is generating events.
+// listener: The instance which may be listening to events by event_source
+// proc_call: Optional. The specific proc to call when the event is raised.
+//
+// Returns true if listener is listening for events by event_source, and proc_call supplied is either null or one of the proc that will be called when an event is raised.
+//
+// /decl/observ/proc/has_listeners(var/event_source)
+// event_source: The instance which is generating events.
+//
+// Returns true if the given event_source has any listeners at all, globally or to specific event sources.
+//
+// /decl/observ/proc/register(var/event_source, var/datum/listener, var/proc_call)
+// event_source: The instance you wish to receive events from.
+// listener: The instance/owner of the proc to call when an event is raised by the event_source.
+// proc_call: The proc to call when an event is raised.
+//
+// It is possible to register the same listener to the same event_source multiple times as long as it is using different proc_calls.
+// Registering again using the same event_source, listener, and proc_call that has been registered previously will have no additional effect.
+// I.e.: The proc_call will still only be called once per raised event. That particular proc_call will only have to be unregistered once.
+//
+// When proc_call is called the first argument is always the source of the event (event_source).
+// Additional arguments may or may not be supplied, see individual event definition files (destroyed.dm, moved.dm, etc.) for details.
+//
+// The instance making the register() call is also responsible for calling unregister(), see below for additonal details, including when event_source is destroyed.
+// This can be handled by listening to the event_source's destroyed event, unregistering in the listener's Destroy() proc, etc.
+//
+// /decl/observ/proc/unregister(var/event_source, var/datum/listener, var/proc_call)
+// event_source: The instance you wish to stop receiving events from.
+// listener: The instance which will no longer receive the events.
+// proc_call: Optional: The proc_call to unregister.
+//
+// Unregisters the listener from the event_source.
+// If a proc_call has been supplied only that particular proc_call will be unregistered. If the proc_call isn't currently registered there will be no effect.
+// If no proc_call has been supplied, the listener will have all registrations made to the given event_source undone.
+//
+// /decl/observ/proc/register_global(var/datum/listener, var/proc_call)
+// listener: The instance/owner of the proc to call when an event is raised by any and all sources.
+// proc_call: The proc to call when an event is raised.
+//
+// Works very much the same as register(), only the listener/proc_call will receive all relevant events from all event sources.
+// Global registrations can overlap with registrations made to specific event sources and these will not affect each other.
+//
+// /decl/observ/proc/unregister_global(var/datum/listener, var/proc_call)
+// listener: The instance/owner of the proc which will no longer receive the events.
+// proc_call: Optional: The proc_call to unregister.
+//
+// Works very much the same as unregister(), only it undoes global registrations instead.
+//
+// /decl/observ/proc/raise_event(src, ...)
+// Should never be called unless implementing a new event type.
+// The first argument shall always be the event_source belonging to the event. Beyond that there are no restrictions.
+
+/decl/observ
+ var/name = "Unnamed Event" // The name of this event, used mainly for debug/VV purposes. The list of event managers can be reached through the "Debug Controller" verb, selecting the "Observation" entry.
+ var/expected_type = /datum // The expected event source for this event. register() will CRASH() if it receives an unexpected type.
+ var/list/event_sources = list() // Associative list of event sources, each with their own associative list. This associative list contains an instance/list of procs to call when the event is raised.
+ var/list/global_listeners = list() // Associative list of instances that listen to all events of this type (as opposed to events belonging to a specific source) and the proc to call.
+
+/decl/observ/New()
+ all_observable_events.events += src
+ . = ..()
+
+/decl/observ/proc/is_listening(var/event_source, var/datum/listener, var/proc_call)
+ // Return whether there are global listeners unless the event source is given.
+ if (!event_source)
+ return !!global_listeners.len
+
+ // Return whether anything is listening to a source, if no listener is given.
+ if (!listener)
+ return global_listeners.len || (event_source in event_sources)
+
+ // Return false if nothing is associated with that source.
+ if (!(event_source in event_sources))
+ return FALSE
+
+ // Get and check the listeners for the reuqested event.
+ var/listeners = event_sources[event_source]
+ if (!(listener in listeners))
+ return FALSE
+
+ // Return true unless a specific callback needs checked.
+ if (!proc_call)
+ return TRUE
+
+ // Check if the specific callback exists.
+ var/list/callback = listeners[listener]
+ if (!callback)
+ return FALSE
+
+ return (proc_call in callback)
+
+/decl/observ/proc/has_listeners(var/event_source)
+ return is_listening(event_source)
+
+/decl/observ/proc/register(var/datum/event_source, var/datum/listener, var/proc_call)
+ // Sanity checking.
+ if (!(event_source && listener && proc_call))
+ return FALSE
+ if (istype(event_source, /decl/observ))
+ return FALSE
+
+ // Crash if the event source is the wrong type.
+ if (!istype(event_source, expected_type))
+ CRASH("Unexpected type. Expected [expected_type], was [event_source.type]")
+
+ // Setup the listeners for this source if needed.
+ var/list/listeners = event_sources[event_source]
+ if (!listeners)
+ listeners = list()
+ event_sources[event_source] = listeners
+
+ // Make sure the callbacks are a list.
+ var/list/callbacks = listeners[listener]
+ if (!callbacks)
+ callbacks = list()
+ listeners[listener] = callbacks
+
+ // If the proc_call is already registered skip
+ if(proc_call in callbacks)
+ return FALSE
+
+ // Add the callback, and return true.
+ callbacks += proc_call
+ return TRUE
+
+/decl/observ/proc/unregister(var/event_source, var/datum/listener, var/proc_call)
+ // Sanity.
+ if (!(event_source && listener && (event_source in event_sources)))
+ return FALSE
+
+ // Return false if nothing is listening for this event.
+ var/list/listeners = event_sources[event_source]
+ if (!listeners)
+ return FALSE
+
+ // Remove all callbacks if no specific one is given.
+ if (!proc_call)
+ if(listeners.Remove(listener))
+ // Perform some cleanup and return true.
+ if (!listeners.len)
+ event_sources -= event_source
+ return TRUE
+ return FALSE
+
+ // See if the listener is registered.
+ var/list/callbacks = listeners[listener]
+ if (!callbacks)
+ return FALSE
+
+ // See if the callback exists.
+ if(!callbacks.Remove(proc_call))
+ return FALSE
+
+ if (!callbacks.len)
+ listeners -= listener
+ if (!listeners.len)
+ event_sources -= event_source
+ return TRUE
+
+/decl/observ/proc/register_global(var/datum/listener, var/proc_call)
+ // Sanity.
+ if (!(listener && proc_call))
+ return FALSE
+
+ // Make sure the callbacks are setup.
+ var/list/callbacks = global_listeners[listener]
+ if (!callbacks)
+ callbacks = list()
+ global_listeners[listener] = callbacks
+
+ // Add the callback and return true.
+ callbacks |= proc_call
+ return TRUE
+
+/decl/observ/proc/unregister_global(var/datum/listener, var/proc_call)
+ // Return false unless the listener is set as a global listener.
+ if (!(listener && (listener in global_listeners)))
+ return FALSE
+
+ // Remove all callbacks if no specific one is given.
+ if (!proc_call)
+ global_listeners -= listener
+ return TRUE
+
+ // See if the listener is registered.
+ var/list/callbacks = global_listeners[listener]
+ if (!callbacks)
+ return FALSE
+
+ // See if the callback exists.
+ if(!callbacks.Remove(proc_call))
+ return FALSE
+
+ if (!callbacks.len)
+ global_listeners -= listener
+ return TRUE
+
+/decl/observ/proc/raise_event()
+ // Sanity
+ if (!args.len)
+ return FALSE
+
+ // Call the global listeners.
+ for (var/datum/listener in global_listeners)
+ var/list/callbacks = global_listeners[listener]
+ for (var/proc_call in callbacks)
+
+ // If the callback crashes, record the error and remove it.
+ try
+ call(listener, proc_call)(arglist(args))
+ catch (var/exception/e)
+ error("[e.name] - [e.file] - [e.line]")
+ error(e.desc)
+ unregister_global(listener, proc_call)
+
+ // Call the listeners for this specific event source, if they exist.
+ var/source = args[1]
+ if (source in event_sources)
+ var/list/listeners = event_sources[source]
+ for (var/datum/listener in listeners)
+ var/list/callbacks = listeners[listener]
+ for (var/proc_call in callbacks)
+
+ // If the callback crashes, record the error and remove it.
+ try
+ call(listener, proc_call)(arglist(args))
+ catch (var/exception/e)
+ error("[e.name] - [e.file] - [e.line]")
+ error(e.desc)
+ unregister(source, listener, proc_call)
+
+ return TRUE
diff --git a/code/datums/observation/task_triggered.dm b/code/datums/observation/task_triggered.dm
new file mode 100644
index 0000000000..0c04ef2ead
--- /dev/null
+++ b/code/datums/observation/task_triggered.dm
@@ -0,0 +1,13 @@
+//
+// Observer Pattern Implementation: Scheduled task triggered
+// Registration type: /datum/scheduled_task
+//
+// Raised when: When a scheduled task reaches its trigger time.
+//
+// Arguments that the called proc should expect:
+// /datum/scheduled_task/task: The task that reached its trigger time.
+var/decl/observ/task_triggered/task_triggered_event = new()
+
+/decl/observ/task_triggered
+ name = "Task Triggered"
+ expected_type = /datum/scheduled_task
diff --git a/code/datums/observation/unequipped.dm b/code/datums/observation/unequipped.dm
new file mode 100644
index 0000000000..3287c0a3b5
--- /dev/null
+++ b/code/datums/observation/unequipped.dm
@@ -0,0 +1,38 @@
+// Observer Pattern Implementation: Unequipped (dropped)
+// Registration type: /mob
+//
+// Raised when: A mob unequips/drops an item.
+//
+// Arguments that the called proc should expect:
+// /mob/equipped: The mob that unequipped/dropped the item.
+// /obj/item/item: The unequipped item.
+
+var/decl/observ/mob_unequipped/mob_unequipped_event = new()
+
+/decl/observ/mob_unequipped
+ name = "Mob Unequipped"
+ expected_type = /mob
+
+// Observer Pattern Implementation: Unequipped (dropped)
+// Registration type: /obj/item
+//
+// Raised when: A mob unequips/drops an item.
+//
+// Arguments that the called proc should expect:
+// /obj/item/item: The unequipped item.
+// /mob/equipped: The mob that unequipped/dropped the item.
+
+var/decl/observ/item_unequipped/item_unequipped_event = new()
+
+/decl/observ/item_unequipped
+ name = "Item Unequipped"
+ expected_type = /obj/item
+
+/**********************
+* Unequipped Handling *
+**********************/
+
+/obj/item/dropped(var/mob/user)
+ ..()
+ mob_unequipped_event.raise_event(user, src)
+ item_unequipped_event.raise_event(src, user)
diff --git a/code/datums/observation/~cleanup.dm b/code/datums/observation/~cleanup.dm
new file mode 100644
index 0000000000..eb6ccceef8
--- /dev/null
+++ b/code/datums/observation/~cleanup.dm
@@ -0,0 +1,70 @@
+var/list/global_listen_count = list()
+var/list/event_sources_count = list()
+var/list/event_listen_count = list()
+
+/decl/observ/destroyed/raise_event()
+ . = ..()
+ if(!.)
+ return
+ var/source = args[1]
+
+ if(global_listen_count[source])
+ cleanup_global_listener(source, global_listen_count[source])
+ if(event_sources_count[source])
+ cleanup_source_listeners(source, event_sources_count[source])
+ if(event_listen_count[source])
+ cleanup_event_listener(source, event_listen_count[source])
+
+
+/decl/observ/register(var/datum/event_source, var/datum/listener, var/proc_call)
+ . = ..()
+ if(.)
+ event_sources_count[event_source] += 1
+ event_listen_count[listener] += 1
+
+/decl/observ/unregister(var/datum/event_source, var/datum/listener, var/proc_call)
+ . = ..()
+ if(.)
+ event_sources_count[event_source] -= 1
+ event_listen_count[listener] -= 1
+
+/decl/observ/register_global(var/datum/listener, var/proc_call)
+ . = ..()
+ if(.)
+ global_listen_count[listener] += 1
+
+/decl/observ/unregister_global(var/datum/listener, var/proc_call)
+ . = ..()
+ if(.)
+ global_listen_count[listener] -= 1
+
+/decl/observ/destroyed/proc/cleanup_global_listener(listener, listen_count)
+ global_listen_count -= listener
+ for(var/entry in all_observable_events.events)
+ var/decl/observ/event = entry
+ if(event.unregister_global(listener))
+ log_debug("[event] - [listener] was deleted while still registered to global events.")
+ if(!(--listen_count))
+ return
+
+/decl/observ/destroyed/proc/cleanup_source_listeners(event_source, source_listener_count)
+ event_sources_count -= event_source
+ for(var/entry in all_observable_events.events)
+ var/decl/observ/event = entry
+ var/proc_owners = event.event_sources[event_source]
+ if(proc_owners)
+ for(var/proc_owner in proc_owners)
+ if(event.unregister(event_source, proc_owner))
+ log_debug("[event] - [event_source] was deleted while still being listened to by [proc_owner].")
+ if(!(--source_listener_count))
+ return
+
+/decl/observ/destroyed/proc/cleanup_event_listener(listener, listener_count)
+ event_listen_count -= listener
+ for(var/entry in all_observable_events.events)
+ var/decl/observ/event = entry
+ for(var/event_source in event.event_sources)
+ if(event.unregister(event_source, listener))
+ log_debug("[event] - [listener] was deleted while still listening to [event_source].")
+ if(!(--listener_count))
+ return
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 11c1fa87fc..8c81c98db6 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -1834,6 +1834,35 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
access = access_security
group = "Security"
+/datum/supply_packs/bolt_rifles_competitive
+ name = "Competitive shooting crate"
+ contains = list(
+ /obj/item/device/assembly/timer,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle/practice = 2,
+ /obj/item/ammo_magazine/clip/a762/practice = 4,
+ /obj/item/target = 2,
+ /obj/item/target/alien = 2,
+ /obj/item/target/syndicate = 2
+ )
+ cost = 40
+ containertype = /obj/structure/closet/crate/secure/weapon
+ containername = "Weapons crate"
+ access = access_security
+ group = "Security"
+
+/datum/supply_packs/bolt_rifles_mosin
+ name = "Surplus militia rifles"
+ contains = list(
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin = 3,
+ /obj/item/ammo_magazine/clip/a762 = 6
+ )
+ cost = 50
+ hidden = 1
+ containertype = /obj/structure/closet/crate/secure/weapon
+ containername = "Weapons crate"
+ access = access_security
+ group = "Security"
+
/datum/supply_packs/medicalextragear
name = "Medical surplus equipment"
contains = list(
@@ -2108,4 +2137,4 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
cost = 10
containertype = "/obj/structure/closet/crate"
containername = "Chaplain equipment crate"
- group = "Miscellaneous"
\ No newline at end of file
+ group = "Miscellaneous"
diff --git a/code/datums/uplink/ammunition.dm b/code/datums/uplink/ammunition.dm
index b45cf3be3c..8308cfc319 100644
--- a/code/datums/uplink/ammunition.dm
+++ b/code/datums/uplink/ammunition.dm
@@ -6,62 +6,98 @@
category = /datum/uplink_category/ammunition
/datum/uplink_item/item/ammo/a357
- name = ".357"
+ name = ".357 Speedloader"
path = /obj/item/ammo_magazine/a357
/datum/uplink_item/item/ammo/mc9mm
- name = "9mm"
+ name = "Pistol Magazine (9mm)"
path = /obj/item/ammo_magazine/mc9mm
/datum/uplink_item/item/ammo/c45m
- name = ".45"
+ name = "Pistol Magazine (.45)"
path = /obj/item/ammo_magazine/c45m
+/datum/uplink_item/item/ammo/tommymag
+ name = "Tommygun Magazine (.45)"
+ path = /obj/item/ammo_magazine/tommymag
+
+/datum/uplink_item/item/ammo/tommydrum
+ name = "Tommygun Drum Magazine (.45)"
+ path = /obj/item/ammo_magazine/tommydrum
+ item_cost = 4 // Buy 40 bullets, get 10 free!
+
/datum/uplink_item/item/ammo/darts
name = "Darts"
path = /obj/item/ammo_magazine/chemdart
/datum/uplink_item/item/ammo/sniperammo
- name = "14.5mm"
+ name = "Anti-Materiel Rifle ammo box (14.5mm)"
path = /obj/item/weapon/storage/box/sniperammo
/datum/uplink_item/item/ammo/a556
- name = "5.56mm"
+ name = "10rnd Rifle Magazine (5.56mm)"
path = /obj/item/ammo_magazine/a556
/datum/uplink_item/item/ammo/a556/ap
- name = "5.56mm AP"
+ name = "10rnd Rifle Magazine (5.56mm AP)"
path = /obj/item/ammo_magazine/a556/ap
+/datum/uplink_item/item/ammo/a556m
+ name = "20rnd Rifle Magazine (5.56mm)"
+ path = /obj/item/ammo_magazine/a556m
+ item_cost = 4
+
+/datum/uplink_item/item/ammo/a556m/ap
+ name = "20rnd Rifle Magazine (5.56mm AP)"
+ path = /obj/item/ammo_magazine/a556m/ap
+ item_cost = 4
+
+/datum/uplink_item/item/ammo/c762
+ name = "20rnd Rifle Magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/c762
+
+/datum/uplink_item/item/ammo/c762/ap
+ name = "20rnd Rifle Magazine (7.62mm AP)"
+ path = /obj/item/ammo_magazine/c762/ap
+
+/datum/uplink_item/item/ammo/s762
+ name = "10rnd Rifle Magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/s762
+ item_cost = 1 // Half the capacity.
+
+/datum/uplink_item/item/ammo/s762/ap
+ name = "10rnd Rifle Magazine (7.62mm AP)"
+ path = /obj/item/ammo_magazine/s762/ap
+
/datum/uplink_item/item/ammo/a10mm
- name = "10mm"
+ name = "SMG Magazine (10mm)"
path = /obj/item/ammo_magazine/a10mm
/datum/uplink_item/item/ammo/a762
- name = "7.62mm"
+ name = "Machinegun Magazine (7.62mm)"
path = /obj/item/ammo_magazine/a762
/datum/uplink_item/item/ammo/a762/ap
- name = "7.62mm AP"
+ name = "Machinegun Magazine (7.62mm AP)"
path = /obj/item/ammo_magazine/a762/ap
/datum/uplink_item/item/ammo/g12
- name = "12 gauge"
+ name = "12g Auto-Shotgun Magazine (Slug)"
path = /obj/item/ammo_magazine/g12
/datum/uplink_item/item/ammo/g12/beanbag
- name = "12 gauge beanbag"
+ name = "12g Auto-Shotgun Magazine (Beanbag)"
path = /obj/item/ammo_magazine/g12/beanbag
item_cost = 1 // Discount due to it being LTL.
/datum/uplink_item/item/ammo/g12/pellet
- name = "12 gauge pellet"
+ name = "12g Auto-Shotgun Magazine (Pellet)"
path = /obj/item/ammo_magazine/g12/pellet
/datum/uplink_item/item/ammo/g12/stun
- name = "12 gauge stun"
+ name = "12g Auto-Shotgun Magazine (Stun)"
path = /obj/item/weapon/storage/box/stunshells
/datum/uplink_item/item/ammo/g12/flash
- name = "12 gauge flash"
+ name = "12g Auto-Shotgun Magazine (Flash)"
path = /obj/item/weapon/storage/box/flashshells
\ No newline at end of file
diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm
index 4c9ac49ee1..c0e8d3aaca 100644
--- a/code/datums/uplink/visible_weapons.dm
+++ b/code/datums/uplink/visible_weapons.dm
@@ -34,22 +34,37 @@
item_cost = 6
path = /obj/item/weapon/gun/projectile/revolver
+/datum/uplink_item/item/visible_weapons/Derringer
+ name = ".357 Derringer Pistol"
+ item_cost = 5
+ path = /obj/item/weapon/gun/projectile/derringer
+
/datum/uplink_item/item/visible_weapons/heavysniper
- name = "Anti-materiel Rifle"
+ name = "Anti-Materiel Rifle (14.5mm)"
item_cost = DEFAULT_TELECRYSTAL_AMOUNT
path = /obj/item/weapon/gun/projectile/heavysniper
+/datum/uplink_item/item/visible_weapons/tommygun
+ name = "Tommygun (.45)" // We're keeping this because it's CLASSY. -Spades
+ item_cost = 7
+ path = /obj/item/weapon/gun/projectile/automatic/tommygun
+
//These are for traitors (or other antags, perhaps) to have the option of purchasing some merc gear.
/datum/uplink_item/item/visible_weapons/submachinegun
- name = "Submachine Gun"
+ name = "Submachine Gun (10mm)"
item_cost = 6
path = /obj/item/weapon/gun/projectile/automatic/c20r
/datum/uplink_item/item/visible_weapons/assaultrifle
- name = "Assault Rifle"
+ name = "Assault Rifle (7.62mm)"
item_cost = 7
path = /obj/item/weapon/gun/projectile/automatic/sts35
+/datum/uplink_item/item/visible_weapons/bullpuprifle
+ name = "Assault Rifle (5.56mm)"
+ item_cost = 7
+ path = /obj/item/weapon/gun/projectile/automatic/carbine
+
/datum/uplink_item/item/visible_weapons/combatshotgun
name = "Combat Shotgun"
item_cost = 7
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 39c91afbed..77779bafcf 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -44,6 +44,7 @@
/obj/item/weapon/soap/deluxe/New()
desc = "A deluxe Waffle Co. brand bar of soap. Smells of [pick("lavender", "vanilla", "strawberry", "chocolate" ,"space")]."
+ ..()
/obj/item/weapon/soap/syndie
desc = "An untrustworthy bar of soap. Smells of fear."
@@ -398,9 +399,11 @@
icon = 'icons/obj/stock_parts.dmi'
w_class = 2.0
var/rating = 1
- New()
- src.pixel_x = rand(-5.0, 5)
- src.pixel_y = rand(-5.0, 5)
+
+/obj/item/weapon/stock_parts/New()
+ src.pixel_x = rand(-5.0, 5)
+ src.pixel_y = rand(-5.0, 5)
+ ..()
//Rank 1
diff --git a/code/game/antagonist/outsider/commando.dm b/code/game/antagonist/outsider/commando.dm
index 135a586b2e..d7acf05273 100644
--- a/code/game/antagonist/outsider/commando.dm
+++ b/code/game/antagonist/outsider/commando.dm
@@ -27,7 +27,7 @@ var/datum/antagonist/deathsquad/mercenary/commandos
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(player), slot_glasses)
player.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(player), slot_wear_mask)
player.equip_to_slot_or_del(new /obj/item/weapon/storage/box(player), slot_in_backpack)
- player.equip_to_slot_or_del(new /obj/item/ammo_magazine/c45(player), slot_in_backpack)
+ player.equip_to_slot_or_del(new /obj/item/ammo_magazine/clip/c45(player), slot_in_backpack)
player.equip_to_slot_or_del(new /obj/item/weapon/rig/merc(player), slot_back)
player.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/pulse_rifle(player), slot_r_hand)
diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm
index 08a6e8644b..b420b3d59e 100644
--- a/code/game/antagonist/outsider/raider.dm
+++ b/code/game/antagonist/outsider/raider.dm
@@ -80,16 +80,22 @@ var/datum/antagonist/raider/raiders
/obj/item/weapon/gun/projectile/automatic/c20r,
/obj/item/weapon/gun/projectile/automatic/wt550,
/obj/item/weapon/gun/projectile/automatic/sts35,
+ /obj/item/weapon/gun/projectile/automatic/carbine,
+ /obj/item/weapon/gun/projectile/automatic/tommygun,
/obj/item/weapon/gun/projectile/silenced,
/obj/item/weapon/gun/projectile/shotgun/pump,
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/pellet,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel/sawn,
/obj/item/weapon/gun/projectile/colt/detective,
/obj/item/weapon/gun/projectile/pistol,
/obj/item/weapon/gun/projectile/revolver,
- /obj/item/weapon/gun/projectile/pirate
+ /obj/item/weapon/gun/projectile/pirate,
+ /obj/item/weapon/gun/projectile/revolver/judge,
+ list(/obj/item/weapon/gun/projectile/luger,/obj/item/weapon/gun/projectile/luger/brown),
+ list(/obj/item/weapon/gun/projectile/deagle, /obj/item/weapon/gun/projectile/deagle/gold, /obj/item/weapon/gun/projectile/deagle/camo)
)
var/list/raider_holster = list(
diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm
index efcdbad236..1e6913a374 100644
--- a/code/game/antagonist/station/renegade.dm
+++ b/code/game/antagonist/station/renegade.dm
@@ -37,19 +37,25 @@ var/datum/antagonist/renegade/renegades
/obj/item/weapon/gun/projectile/automatic/mini_uzi,
/obj/item/weapon/gun/projectile/automatic/c20r,
/obj/item/weapon/gun/projectile/automatic/sts35,
+ /obj/item/weapon/gun/projectile/automatic/carbine,
/obj/item/weapon/gun/projectile/automatic/wt550,
/obj/item/weapon/gun/projectile/automatic/z8,
+ /obj/item/weapon/gun/projectile/automatic/tommygun,
/obj/item/weapon/gun/projectile/colt/detective,
/obj/item/weapon/gun/projectile/sec/wood,
/obj/item/weapon/gun/projectile/silenced,
/obj/item/weapon/gun/projectile/pistol,
/obj/item/weapon/gun/projectile/revolver,
+ /obj/item/weapon/gun/projectile/derringer,
/obj/item/weapon/gun/projectile/shotgun/pump,
+ /obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin,
/obj/item/weapon/gun/projectile/shotgun/pump/combat,
/obj/item/weapon/gun/projectile/shotgun/doublebarrel,
+ /obj/item/weapon/gun/projectile/revolver/judge,
list(/obj/item/weapon/gun/projectile/shotgun/doublebarrel/pellet, /obj/item/weapon/gun/projectile/shotgun/doublebarrel/sawn),
list(/obj/item/weapon/gun/projectile/deagle, /obj/item/weapon/gun/projectile/deagle/gold, /obj/item/weapon/gun/projectile/deagle/camo),
- list(/obj/item/weapon/gun/projectile/revolver/detective, /obj/item/weapon/gun/projectile/revolver/deckard)
+ list(/obj/item/weapon/gun/projectile/revolver/detective, /obj/item/weapon/gun/projectile/revolver/deckard),
+ list(/obj/item/weapon/gun/projectile/luger,/obj/item/weapon/gun/projectile/luger/brown)
)
/datum/antagonist/renegade/New()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 7639084918..e45be4bb6a 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -46,7 +46,8 @@
pulledby = null
/atom/movable/proc/initialize()
- return
+ if(!isnull(gcDestroyed))
+ crash_with("GC: -- [type] had initialize() called after qdel() --")
/atom/movable/Bump(var/atom/A, yes)
if(src.throwing)
@@ -211,7 +212,7 @@
/atom/movable/overlay/New()
for(var/x in src.verbs)
src.verbs -= x
- return
+ ..()
/atom/movable/overlay/attackby(a, b)
if (src.master)
diff --git a/code/game/gamemodes/events/dust.dm b/code/game/gamemodes/events/dust.dm
index 492b932e46..bb8e34364c 100644
--- a/code/game/gamemodes/events/dust.dm
+++ b/code/game/gamemodes/events/dust.dm
@@ -53,6 +53,7 @@ The "dust" will damage the hull of the station causin minor hull breaches.
New()
+ ..()
var/startx = 0
var/starty = 0
var/endy = 0
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 6b373953b1..f5dd1efe5f 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -12,6 +12,7 @@ datum/objective
all_objectives |= src
if(text)
explanation_text = text
+ ..()
Destroy()
all_objectives -= src
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 877dfa1065..c0ef428f99 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -92,16 +92,6 @@
active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors.
/obj/machinery/sleeper/New()
- ..()
- spawn(5)
- //src.machine = locate(/obj/machinery/mineral/processing_unit, get_step(src, machinedir))
- var/obj/machinery/sleep_console/C = locate(/obj/machinery/sleep_console) in range(2,src)
- if(C)
- C.connected = src
- return
- return
-
-/obj/machinery/sleeper/map/New()
..()
beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
circuit = new circuit(src)
@@ -114,6 +104,14 @@
component_parts += new /obj/item/weapon/reagent_containers/syringe(src)
component_parts += new /obj/item/weapon/reagent_containers/syringe(src)
component_parts += new /obj/item/stack/material/glass/reinforced(src, 2)
+
+ spawn(5)
+ //src.machine = locate(/obj/machinery/mineral/processing_unit, get_step(src, machinedir))
+ var/obj/machinery/sleep_console/C = locate(/obj/machinery/sleep_console) in range(2,src)
+ if(C)
+ C.connected = src
+ return
+
RefreshParts()
/obj/machinery/sleeper/initialize()
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 3d5806eeab..eab6da38ca 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -16,15 +16,6 @@
active_power_usage = 10000 //10 kW. It's a big all-body scanner.
/obj/machinery/bodyscanner/New()
- ..()
- spawn( 5 )
- var/obj/machinery/body_scanconsole/C = locate(/obj/machinery/body_scanconsole) in range(2,src)
- if(C)
- C.connected = src
- return
- return
-
-/obj/machinery/bodyscanner/map/New()
..()
circuit = new circuit(src)
component_parts = list()
@@ -32,6 +23,13 @@
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
component_parts += new /obj/item/stack/material/glass/reinforced(src, 2)
+
+ spawn( 5 )
+ var/obj/machinery/body_scanconsole/C = locate(/obj/machinery/body_scanconsole) in range(2,src)
+ if(C)
+ C.connected = src
+ return
+
RefreshParts()
/obj/machinery/bodyscanner/relaymove(mob/user as mob)
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 5d80030337..5901148603 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -26,10 +26,6 @@
/obj/machinery/autolathe/New()
..()
wires = new(src)
-
-/obj/machinery/autolathe/map/New()
- ..()
- //Create parts for lathe.
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
@@ -150,6 +146,22 @@
if(is_robot_module(O))
return 0
+ if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/a357) || istype(O,/obj/item/ammo_magazine/c38)) // Prevents ammo recycling exploit with speedloaders.
+ user << "\The [O] is too hazardous to recycle with the autolathe!"
+ return
+ /* ToDo: Make this actually check for ammo and change the value of the magazine if it's empty. -Spades
+ var/obj/item/ammo_magazine/speedloader = O
+ if(speedloader.stored_ammo)
+ user << "\The [speedloader] is too hazardous to put back into the autolathe while there's ammunition inside of it!"
+ return
+ else
+ speedloader.matter = list(DEFAULT_WALL_MATERIAL = 75) // It's just a hunk of scrap metal now.
+ if(istype(O,/obj/item/ammo_magazine)) // This was just for immersion consistency with above.
+ var/obj/item/ammo_magazine/mag = O
+ if(mag.stored_ammo)
+ user << "\The [mag] is too hazardous to put back into the autolathe while there's ammunition inside of it!"
+ return*/
+
//Resources are being loaded.
var/obj/item/eating = O
if(!eating.matter)
diff --git a/code/game/machinery/autolathe_datums.dm b/code/game/machinery/autolathe_datums.dm
index bedf855ec8..e88af6b521 100644
--- a/code/game/machinery/autolathe_datums.dm
+++ b/code/game/machinery/autolathe_datums.dm
@@ -330,36 +330,415 @@
path = /obj/item/weapon/syringe_cartridge
category = "Arms and Ammunition"
+////////////////
+/*Ammo casings*/
+////////////////
+
/datum/autolathe/recipe/shotgun_blanks
- name = "ammunition (shotgun, blank)"
+ name = "ammunition (12g, blank)"
path = /obj/item/ammo_casing/shotgun/blank
category = "Arms and Ammunition"
/datum/autolathe/recipe/shotgun_beanbag
- name = "ammunition (shotgun, beanbag)"
+ name = "ammunition (12g, beanbag)"
path = /obj/item/ammo_casing/shotgun/beanbag
category = "Arms and Ammunition"
/datum/autolathe/recipe/shotgun_flash
- name = "ammunition (shotgun, flash)"
+ name = "ammunition (12g, flash)"
path = /obj/item/ammo_casing/shotgun/flash
category = "Arms and Ammunition"
-/datum/autolathe/recipe/magazine_rubber
- name = "ammunition (.45, rubber)"
+/datum/autolathe/recipe/shotgun
+ name = "ammunition (12g, slug)"
+ path = /obj/item/ammo_casing/shotgun
+ hidden = 1
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/shotgun_pellet
+ name = "ammunition (12g, pellet)"
+ path = /obj/item/ammo_casing/shotgun/pellet
+ hidden = 1
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/stunshell
+ name = "ammunition (stun cartridge, shotgun)"
+ path = /obj/item/ammo_casing/shotgun/stunshell
+ hidden = 1
+ category = "Arms and Ammunition"
+
+//////////////////
+/*Ammo magazines*/
+//////////////////
+
+/////// 5mm
+/*
+/datum/autolathe/recipe/pistol_5mm
+ name = "pistol magazine (5mm)"
+ path = /obj/item/ammo_magazine/c5mm
+ category = "Arms and Ammunition"
+ hidden = 1
+*/
+
+/////// .45
+/datum/autolathe/recipe/pistol_45
+ name = "pistol magazine (.45)"
+ path = /obj/item/ammo_magazine/c45m
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_45p
+ name = "pistol magazine (.45 practice)"
+ path = /obj/item/ammo_magazine/c45m/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_45r
+ name = "pistol magazine (.45 rubber)"
path = /obj/item/ammo_magazine/c45m/rubber
category = "Arms and Ammunition"
-/datum/autolathe/recipe/magazine_flash
- name = "ammunition (.45, flash)"
+/datum/autolathe/recipe/pistol_45f
+ name = "pistol magazine (.45 flash)"
path = /obj/item/ammo_magazine/c45m/flash
category = "Arms and Ammunition"
-/datum/autolathe/recipe/magazine_smg_rubber
- name = "ammunition (9mm rubber top mounted)"
+/datum/autolathe/recipe/pistol_45uzi
+ name = "uzi magazine (.45)"
+ path = /obj/item/ammo_magazine/c45uzi
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/tommymag
+ name = "Tommygun magazine (.45)"
+ path = /obj/item/ammo_magazine/tommymag
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/tommydrum
+ name = "Tommygun drum magazine (.45)"
+ path = /obj/item/ammo_magazine/tommydrum
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/////// 9mm
+
+/obj/item/ammo_magazine/mc9mm/flash
+ ammo_type = /obj/item/ammo_casing/c9mmf
+
+/obj/item/ammo_magazine/mc9mm/rubber
+ name = "magazine (9mm rubber)"
+ ammo_type = /obj/item/ammo_casing/c9mmr
+
+/obj/item/ammo_magazine/mc9mm/practice
+ name = "magazine (9mm practice)"
+ ammo_type = /obj/item/ammo_casing/c9mmp
+
+/datum/autolathe/recipe/pistol_9mm
+ name = "pistol magazine (9mm)"
+ path = /obj/item/ammo_magazine/mc9mm
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_9mmr
+ name = "pistol magazine (9mm rubber)"
+ path = /obj/item/ammo_magazine/mc9mm/rubber
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_9mmp
+ name = "pistol magazine (9mm practice)"
+ path = /obj/item/ammo_magazine/mc9mm/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_9mmf
+ name = "pistol magazine (9mm flash)"
+ path = /obj/item/ammo_magazine/mc9mm/flash
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/smg_9mm
+ name = "top-mounted SMG magazine (9mm)"
+ path = /obj/item/ammo_magazine/mc9mmt
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/smg_9mmr
+ name = "top-mounted SMG magazine (9mm rubber)"
path = /obj/item/ammo_magazine/mc9mmt/rubber
category = "Arms and Ammunition"
+/datum/autolathe/recipe/smg_9mmp
+ name = "top-mounted SMG magazine (9mm practice)"
+ path = /obj/item/ammo_magazine/mc9mmt/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/smg_9mmf
+ name = "top-mounted SMG magazine (9mm flash)"
+ path = /obj/item/ammo_magazine/mc9mmt/flash
+ category = "Arms and Ammunition"
+
+/////// 10mm
+/datum/autolathe/recipe/smg_10mm
+ name = "SMG magazine (10mm)"
+ path = /obj/item/ammo_magazine/a10mm
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_50
+ name = "pistol magazine (.50AE)"
+ path = /obj/item/ammo_magazine/a50
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/////// 5.56mm
+/datum/autolathe/recipe/rifle_556
+ name = "10rnd rifle magazine (5.56mm)"
+ path = /obj/item/ammo_magazine/a556
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_556p
+ name = "10rnd rifle magazine (5.56mm practice)"
+ path = /obj/item/ammo_magazine/a556/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/rifle_556m
+ name = "20rnd rifle magazine (5.56mm)"
+ path = /obj/item/ammo_magazine/a556m
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_556mp
+ name = "20rnd rifle magazine (5.56mm practice)"
+ path = /obj/item/ammo_magazine/a556m/practice
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/////// 7.62
+/datum/autolathe/recipe/rifle_small_762
+ name = "10rnd rifle magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/s762
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_762
+ name = "20rnd rifle magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/c762
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/machinegun_762
+ name = "machinegun box magazine (7.62)"
+ path = /obj/item/ammo_magazine/a762
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/shotgun_magazine
+ name = "24rnd shotgun magazine (12g)"
+ path = /obj/item/ammo_magazine/g12
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/* Commented out until autolathe stuff is decided/fixed. Will probably remove these entirely. -Spades
+// These should always be /empty! The idea is to fill them up manually with ammo clips.
+
+/datum/autolathe/recipe/pistol_5mm
+ name = "pistol magazine (5mm)"
+ path = /obj/item/ammo_magazine/c5mm/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/smg_5mm
+ name = "top-mounted SMG magazine (5mm)"
+ path = /obj/item/ammo_magazine/c5mmt/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_45
+ name = "pistol magazine (.45)"
+ path = /obj/item/ammo_magazine/c45m/empty
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_45uzi
+ name = "uzi magazine (.45)"
+ path = /obj/item/ammo_magazine/c45uzi/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/tommymag
+ name = "Tommygun magazine (.45)"
+ path = /obj/item/ammo_magazine/tommymag/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/tommydrum
+ name = "Tommygun drum magazine (.45)"
+ path = /obj/item/ammo_magazine/tommydrum/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_9mm
+ name = "pistol magazine (9mm)"
+ path = /obj/item/ammo_magazine/mc9mm/empty
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/smg_9mm
+ name = "top-mounted SMG magazine (9mm)"
+ path = /obj/item/ammo_magazine/mc9mmt/empty
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/smg_10mm
+ name = "SMG magazine (10mm)"
+ path = /obj/item/ammo_magazine/a10mm/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_50
+ name = "pistol magazine (.50AE)"
+ path = /obj/item/ammo_magazine/a50/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_556
+ name = "10rnd rifle magazine (5.56mm)"
+ path = /obj/item/ammo_magazine/a556/empty
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/rifle_556m
+ name = "20rnd rifle magazine (5.56mm)"
+ path = /obj/item/ammo_magazine/a556m/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_SVD
+ name = "10rnd rifle magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/SVD/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_762
+ name = "20rnd rifle magazine (7.62mm)"
+ path = /obj/item/ammo_magazine/c762/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/machinegun_762
+ name = "machinegun box magazine (7.62)"
+ path = /obj/item/ammo_magazine/a762/empty
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/shotgun_magazine
+ name = "24rnd shotgun magazine (12g)"
+ path = /obj/item/ammo_magazine/g12/empty
+ category = "Arms and Ammunition"
+ hidden = 1*/
+
+///////////////////////////////
+/*Ammo clips and Speedloaders*/
+///////////////////////////////
+
+/datum/autolathe/recipe/speedloader_357
+ name = "speedloader (.357)"
+ path = /obj/item/ammo_magazine/a357
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/speedloader_38
+ name = "speedloader (.38)"
+ path = /obj/item/ammo_magazine/c38
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/speedloader_38r
+ name = "speedloader (.38 rubber)"
+ path = /obj/item/ammo_magazine/c38/rubber
+ category = "Arms and Ammunition"
+
+// Commented out until metal exploits with autolathe is fixed.
+/*/datum/autolathe/recipe/pistol_clip_45
+ name = "ammo clip (.45)"
+ path = /obj/item/ammo_magazine/clip/c45
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_clip_45r
+ name = "ammo clip (.45 rubber)"
+ path = /obj/item/ammo_magazine/clip/c45/rubber
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_45f
+ name = "ammo clip (.45 flash)"
+ path = /obj/item/ammo_magazine/clip/c45/flash
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_45p
+ name = "ammo clip (.45 practice)"
+ path = /obj/item/ammo_magazine/clip/c45/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_9mm
+ name = "ammo clip (9mm)"
+ path = /obj/item/ammo_magazine/clip/c9mm
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_clip_9mmr
+ name = "ammo clip (9mm rubber)"
+ path = /obj/item/ammo_magazine/clip/c9mm/rubber
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_9mmp
+ name = "ammo clip (9mm practice)"
+ path = /obj/item/ammo_magazine/clip/c9mm/practice
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_9mmf
+ name = "ammo clip (9mm flash)"
+ path = /obj/item/ammo_magazine/clip/c9mm/flash
+ category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/pistol_clip_5mm
+ name = "ammo clip (5mm)"
+ path = /obj/item/ammo_magazine/clip/c5mm
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_clip_10mm
+ name = "ammo clip (10mm)"
+ path = /obj/item/ammo_magazine/clip/a10mm
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/pistol_clip_50
+ name = "ammo clip (.50AE)"
+ path = /obj/item/ammo_magazine/clip/a50
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_clip_556
+ name = "ammo clip (5.56mm)"
+ path = /obj/item/ammo_magazine/clip/a556
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_clip_556_practice
+ name = "ammo clip (5.56mm practice)"
+ path = /obj/item/ammo_magazine/clip/a556/practice
+ category = "Arms and Ammunition"
+*/
+
+/datum/autolathe/recipe/rifle_clip_762
+ name = "ammo clip (7.62mm)"
+ path = /obj/item/ammo_magazine/clip/a762
+ category = "Arms and Ammunition"
+ hidden = 1
+
+/datum/autolathe/recipe/rifle_clip_762_practice
+ name = "ammo clip (7.62mm practice)"
+ path = /obj/item/ammo_magazine/clip/a762/practice
+ category = "Arms and Ammunition"
+
+//////////////
+
/datum/autolathe/recipe/consolescreen
name = "console screen"
path = /obj/item/weapon/stock_parts/console_screen
@@ -427,78 +806,6 @@
hidden = 1
category = "Arms and Ammunition"
-/datum/autolathe/recipe/magazine_revolver_1
- name = "ammunition (.357)"
- path = /obj/item/ammo_magazine/a357
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_revolver_2
- name = "ammunition (.45)"
- path = /obj/item/ammo_magazine/c45m
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_stetchkin
- name = "ammunition (9mm)"
- path = /obj/item/ammo_magazine/mc9mm
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_stetchkin_flash
- name = "ammunition (9mm, flash)"
- path = /obj/item/ammo_magazine/mc9mm/flash
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_c20r
- name = "ammunition (10mm)"
- path = /obj/item/ammo_magazine/a10mm
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_arifle
- name = "ammunition (7.62mm)"
- path = /obj/item/ammo_magazine/c762
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_smg
- name = "ammunition (9mm top mounted)"
- path = /obj/item/ammo_magazine/mc9mmt
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/magazine_carbine
- name = "ammunition (5.56mm)"
- path = /obj/item/ammo_magazine/a556
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/shotgun
- name = "ammunition (slug, shotgun)"
- path = /obj/item/ammo_casing/shotgun
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/shotgun_pellet
- name = "ammunition (shell, shotgun)"
- path = /obj/item/ammo_casing/shotgun/pellet
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/tacknife
- name = "tactical knife"
- path = /obj/item/weapon/material/hatchet/tacknife
- hidden = 1
- category = "Arms and Ammunition"
-
-/datum/autolathe/recipe/stunshell
- name = "ammunition (stun cartridge, shotgun)"
- path = /obj/item/ammo_casing/shotgun/stunshell
- hidden = 1
- category = "Arms and Ammunition"
-
/datum/autolathe/recipe/rcd
name = "rapid construction device"
path = /obj/item/weapon/rcd
@@ -534,3 +841,9 @@
path = /obj/item/weapon/material/knuckledusters
hidden = 1
category = "Arms and Ammunition"
+
+/datum/autolathe/recipe/tacknife
+ name = "tactical knife"
+ path = /obj/item/weapon/material/hatchet/tacknife
+ hidden = 1
+ category = "Arms and Ammunition"
\ No newline at end of file
diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm
index 8122d49b33..c6b6fabe1b 100644
--- a/code/game/machinery/biogenerator.dm
+++ b/code/game/machinery/biogenerator.dm
@@ -22,8 +22,6 @@
reagents = R
R.my_atom = src
-/obj/machinery/biogenerator/map/New()
- ..()
beaker = new /obj/item/weapon/reagent_containers/glass/bottle(src)
circuit = new circuit(src)
component_parts = list()
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index eed2d4d031..19d187ade4 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -45,7 +45,7 @@
var/eject_wait = 0 //Don't eject them as soon as they are created fuckkk
var/biomass = CLONE_BIOMASS * 3
-/obj/machinery/clonepod/map/New()
+/obj/machinery/clonepod/New()
..()
circuit = new circuit(src)
component_parts = list()
@@ -424,6 +424,7 @@
read_only = 1
New()
+ ..()
initializeDisk()
buf.types=DNA2_BUF_SE
var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4)
diff --git a/code/game/machinery/computer/RCON_Console.dm b/code/game/machinery/computer/RCON_Console.dm
index 0aeb3eab80..f3d8036786 100644
--- a/code/game/machinery/computer/RCON_Console.dm
+++ b/code/game/machinery/computer/RCON_Console.dm
@@ -8,7 +8,7 @@
name = "\improper RCON console"
desc = "Console used to remotely control machinery on the station."
icon_keyboard = "power_key"
- icon_screen = "power_screen"
+ icon_screen = "power:0"
light_color = "#a97faa"
circuit = /obj/item/weapon/circuitboard/rcon_console
req_one_access = list(access_engine)
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index f4b06aff1c..eef148a830 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -14,10 +14,10 @@ var/global/list/minor_air_alarms = list()
/obj/machinery/computer/atmos_alert/New()
..()
- atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/update_icon)
+ atmosphere_alarm.register_alarm(src, /obj/machinery/computer/station_alert/update_icon)
/obj/machinery/computer/atmos_alert/Destroy()
- atmosphere_alarm.unregister(src)
+ atmosphere_alarm.unregister_alarm(src)
..()
/obj/machinery/computer/atmos_alert/attack_hand(mob/user)
diff --git a/code/game/machinery/computer/camera_circuit.dm b/code/game/machinery/computer/camera_circuit.dm
index 4680bb57d1..8db87c36ae 100644
--- a/code/game/machinery/computer/camera_circuit.dm
+++ b/code/game/machinery/computer/camera_circuit.dm
@@ -17,6 +17,7 @@
possibleNets["Cargo"] = access_qm
possibleNets["Research"] = access_rd
possibleNets["Medbay"] = access_cmo
+ ..()
proc/updateBuildPath()
build_path = null
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index 22c97a955a..d1c66b500d 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -17,13 +17,13 @@
monitor_type = /datum/nano_module/alarm_monitor/all
circuit = /obj/item/weapon/circuitboard/stationalert_all
-/obj/machinery/computer/station_alert/New()
- ..()
+/obj/machinery/computer/station_alert/initialize()
alarm_monitor = new monitor_type(src)
- alarm_monitor.register(src, /obj/machinery/computer/station_alert/update_icon)
+ alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon)
+ ..()
/obj/machinery/computer/station_alert/Destroy()
- alarm_monitor.unregister(src)
+ alarm_monitor.unregister_alarm(src)
qdel(alarm_monitor)
..()
@@ -46,7 +46,7 @@
/obj/machinery/computer/station_alert/update_icon()
if(!(stat & (BROKEN|NOPOWER)))
- var/list/alarms = alarm_monitor.major_alarms()
+ var/list/alarms = alarm_monitor ? alarm_monitor.major_alarms() : list()
if(alarms.len)
icon_screen = "alert:2"
else
diff --git a/code/game/machinery/computer3/file.dm b/code/game/machinery/computer3/file.dm
index b1fbe0ddbe..be4285b3e1 100644
--- a/code/game/machinery/computer3/file.dm
+++ b/code/game/machinery/computer3/file.dm
@@ -100,6 +100,7 @@
if(content)
if(file_increment > 1)
volume = round(file_increment * length(content))
+ ..()
/*
A generic file that contains text
diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm
index 831edadcbf..1be387f35f 100644
--- a/code/game/machinery/kitchen/microwave.dm
+++ b/code/game/machinery/kitchen/microwave.dm
@@ -30,14 +30,11 @@
reagents = new/datum/reagents(100)
reagents.my_atom = src
-/obj/machinery/microwave/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
component_parts += new /obj/item/weapon/stock_parts/motor(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
- RefreshParts()
if (!available_recipes)
available_recipes = new
@@ -58,6 +55,8 @@
acceptable_items |= /obj/item/weapon/holder
acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown
+ RefreshParts()
+
/*******************
* Item Adding
********************/
diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index 05de6b9ac6..a483232526 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -1,3 +1,63 @@
+/*
+* Datum that holds the instances and information about the items in the smartfridge.
+*/
+/datum/data/stored_item
+ var/item_name = "name" //Name of the item(s) displayed
+ var/item_path = null
+ var/amount = 0
+ var/list/instances //What items are actually stored
+ var/fridge //The attatched fridge
+
+/datum/data/stored_item/New(var/fridge, var/path, var/name = null, var/amount = 0)
+ ..()
+
+ src.item_path = path
+
+ if(!name)
+ var/atom/tmp = path
+ src.item_name = initial(tmp.name)
+ else
+ src.item_name = name
+
+ src.amount = amount
+ src.fridge = fridge
+
+/datum/data/stored_item/Destroy()
+ fridge = null
+ if(instances)
+ for(var/product in instances)
+ qdel(product)
+ instances.Cut()
+ . = ..()
+
+/datum/data/stored_item/proc/get_amount()
+ return instances ? instances.len : amount
+
+/datum/data/stored_item/proc/get_product(var/product_location)
+ if(!get_amount() || !product_location)
+ return
+ init_products()
+
+ var/atom/movable/product = instances[instances.len] // Remove the last added product
+ instances -= product
+ product.forceMove(product_location)
+
+/datum/data/stored_item/proc/add_product(var/atom/movable/product)
+ if(product.type != item_path)
+ return 0
+ init_products()
+ product.forceMove(fridge)
+ instances += product
+
+/datum/data/stored_item/proc/init_products()
+ if(instances)
+ return
+ instances = list()
+ for(var/i = 1 to amount)
+ var/new_product = new item_path(fridge)
+ instances += new_product
+
+
/* SmartFridge. Much todo
*/
/obj/machinery/smartfridge
@@ -15,7 +75,8 @@
var/icon_on = "smartfridge"
var/icon_off = "smartfridge-off"
var/icon_panel = "smartfridge-panel"
- var/item_quants = list()
+ var/list/item_records = list()
+ var/datum/data/stored_item/currently_vending = null //What we're putting out of the machine.
var/seconds_electrified = 0;
var/shoot_inventory = 0
var/locked = 0
@@ -68,11 +129,14 @@
/obj/machinery/smartfridge/secure/extract/New()
..()
+ var/datum/data/stored_item/I = new(src, /obj/item/xenoproduct/slime/core)
+ item_records.Add(I)
for(var/i=1 to 5)
var/obj/item/xenoproduct/slime/core/C = new(src)
C.traits = new()
C.nameVar = "grey"
- item_quants[C.name]++
+ I.add_product(C)
+
/obj/machinery/smartfridge/secure/medbay
name = "\improper Refrigerated Medicine Storage"
@@ -162,20 +226,19 @@
overlays += "drying_rack_drying"
/obj/machinery/smartfridge/drying_rack/proc/dry()
- for(var/obj/item/weapon/reagent_containers/food/snacks/S in contents)
- if(S.dry) continue
- if(S.dried_type == S.type)
- S.dry = 1
- item_quants[S.name]--
- S.name = "dried [S.name]"
- S.color = "#AAAAAA"
- S.loc = loc
- else
- var/D = S.dried_type
- new D(loc)
- item_quants[S.name]--
- qdel(S)
- return
+ for(var/datum/data/stored_item/I in item_records)
+ for(var/obj/item/weapon/reagent_containers/food/snacks/S in I.instances)
+ if(S.dry) continue
+ if(S.dried_type == S.type)
+ S.dry = 1
+ S.name = "dried [S.name]"
+ S.color = "#AAAAAA"
+ I.get_product(loc)
+ else
+ var/D = S.dried_type
+ new D(loc)
+ qdel(S)
+ return
return
/obj/machinery/smartfridge/process()
@@ -222,35 +285,39 @@
return
if(accept_check(O))
- if(contents.len >= max_n_of_items)
- user << "\The [src] is full."
- return 1
- else
- user.remove_from_mob(O)
- O.loc = src
- if(item_quants[O.name])
- item_quants[O.name]++
- else
- item_quants[O.name] = 1
- user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
+ user.remove_from_mob(O)
+ var/hasRecord = FALSE //Check to see if this passes or not.
+ for(var/datum/data/stored_item/I in item_records)
+ if(istype(O, I.item_path))
+ stock(O, I)
+ qdel(O)
+ return
+ if(!hasRecord)
+ var/datum/data/stored_item/item = new/datum/data/stored_item(src, O.type)
+ stock(O, item)
+ item_records.Add(item)
+
+ user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
- nanomanager.update_uis(src)
+ nanomanager.update_uis(src)
else if(istype(O, /obj/item/weapon/storage/bag))
var/obj/item/weapon/storage/bag/P = O
var/plants_loaded = 0
for(var/obj/G in P.contents)
if(accept_check(G))
- if(contents.len >= max_n_of_items)
- user << "\The [src] is full."
- return 1
- else
- P.remove_from_storage(G,src)
- if(item_quants[G.name])
- item_quants[G.name]++
- else
- item_quants[G.name] = 1
- plants_loaded++
+ plants_loaded++
+ var/hasRecord = FALSE //Check to see if this passes or not.
+ for(var/datum/data/stored_item/I in item_records)
+ if(istype(G, I.item_path))
+ if(G.name == I.item_name)
+ stock(G, I)
+ hasRecord = TRUE
+ if(!hasRecord)
+ var/datum/data/stored_item/item = new/datum/data/stored_item(src, G.type, G.name)
+ stock(G, item)
+ item_records.Add(item)
+
if(plants_loaded)
user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].")
@@ -269,6 +336,14 @@
locked = -1
user << "You short out the product lock on [src]."
return 1
+
+/obj/machinery/smartfridge/proc/stock(obj/item/O, var/datum/data/stored_item/I)
+ I.add_product(O)
+ nanomanager.update_uis(src)
+
+/obj/machinery/smartfridge/proc/vend(datum/data/stored_item/I)
+ I.get_product(get_turf(src))
+ nanomanager.update_uis(src)
/obj/machinery/smartfridge/attack_ai(mob/user as mob)
attack_hand(user)
@@ -294,11 +369,11 @@
data["secure"] = is_secure
var/list/items[0]
- for (var/i=1 to length(item_quants))
- var/K = item_quants[i]
- var/count = item_quants[K]
+ for (var/i=1 to length(item_records))
+ var/datum/data/stored_item/I = item_records[i]
+ var/count = I.get_amount()
if(count > 0)
- items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count)))
+ items.Add(list(list("display_name" = html_encode(capitalize(I.item_name)), "vend" = i, "quantity" = count)))
if(items.len > 0)
data["contents"] = items
@@ -325,20 +400,15 @@
if(href_list["vend"])
var/index = text2num(href_list["vend"])
var/amount = text2num(href_list["amount"])
- var/K = item_quants[index]
- var/count = item_quants[K]
+ var/datum/data/stored_item/I = item_records[index]
+ var/count = I.get_amount()
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
if(count > 0)
- item_quants[K] = max(count - amount, 0)
-
- var/i = amount
- for(var/obj/O in contents)
- if(O.name == K)
- O.loc = loc
- i--
- if(i <= 0)
- return 1
+ if((count - amount) < 0)
+ amount = count
+ for(var/i = 1 to amount)
+ vend(I)
return 1
return 0
@@ -349,17 +419,12 @@
if(!target)
return 0
- for (var/O in item_quants)
- if(item_quants[O] <= 0) //Try to use a record that actually has something to dump.
+ for(var/datum/data/stored_item/I in src.item_records)
+ throw_item = I.get_product(loc)
+ if (!throw_item)
continue
-
- item_quants[O]--
- for(var/obj/T in contents)
- if(T.name == O)
- T.loc = src.loc
- throw_item = T
- break
break
+
if(!throw_item)
return 0
spawn(0)
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 3681d1e798..30a9b8548b 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -136,7 +136,7 @@ Class Procs:
if(contents) // The same for contents.
for(var/atom/A in contents)
qdel(A)
- ..()
+ return ..()
/obj/machinery/process()//If you dont use process or power why are you here
if(!(use_power || idle_power_usage || active_power_usage))
diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm
index f94427bf3a..f563d9ef39 100644
--- a/code/game/machinery/mass_driver.dm
+++ b/code/game/machinery/mass_driver.dm
@@ -16,7 +16,7 @@
var/id = 1.0
var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess.
-/obj/machinery/mass_driver/map/New()
+/obj/machinery/mass_driver/New()
..()
circuit = new circuit(src)
component_parts = list()
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 71d63afc0a..55c2e10eaa 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -16,7 +16,7 @@ obj/machinery/recharger
var/portable = 1
circuit = /obj/item/weapon/circuitboard/recharger
-obj/machinery/recharger/map/New()
+obj/machinery/recharger/New()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
@@ -172,12 +172,3 @@ obj/machinery/recharger/wallcharger
portable = 0
circuit = /obj/item/weapon/circuitboard/recharger/wrecharger
frame_type = "wrecharger"
-
-obj/machinery/recharger/wallcharger/map/New()
- circuit = new circuit(src)
- component_parts = list()
- component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
- component_parts += new /obj/item/stack/cable_coil(src, 5)
- RefreshParts()
- ..()
- return
\ No newline at end of file
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 204f48e0d6..b5f7e029a5 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -22,7 +22,7 @@
var/weld_power_use = 2300 // power used per point of brute damage repaired. 2.3 kW ~ about the same power usage of a handheld arc welder
var/wire_power_use = 500 // power used per point of burn damage repaired.
-/obj/machinery/recharge_station/map/New()
+/obj/machinery/recharge_station/New()
..()
circuit = new circuit(src)
component_parts = list()
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index 4dad236b73..6d12dae5dd 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -179,8 +179,6 @@
underlays.Cut()
underlays += image('icons/obj/stationobjs.dmi', icon_state = "tele-wires")
-/obj/machinery/teleport/hub/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
@@ -331,8 +329,6 @@
overlays.Cut()
overlays += image('icons/obj/stationobjs.dmi', icon_state = "controller-wires")
-/obj/machinery/teleport/station/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 4babe5cd1b..ad8976e121 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -21,7 +21,7 @@
var/obj/crayon
var/list/washing = list()
-/obj/machinery/washing_machine/map/New()
+/obj/machinery/washing_machine/New()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/motor(src)
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 4721e84440..4edaae81aa 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -48,7 +48,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target)
var/obj/item/projectile/P = A
- P.launch(target)
+ P.launch(target)
/obj/item/mecha_parts/mecha_equipment/weapon/energy
name = "general energy weapon"
@@ -95,7 +95,7 @@
energy_drain = 120
origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 6, TECH_POWER = 4)
projectile = /obj/item/projectile/beam/pulse/heavy
- fire_sound = 'sound/weapons/marauder.ogg'
+ fire_sound = 'sound/weapons/gauss_shoot.ogg'
/obj/item/projectile/beam/pulse/heavy
name = "heavy pulse laser"
@@ -205,7 +205,7 @@
icon_state = "mecha_uac2"
equip_cooldown = 10
projectile = /obj/item/projectile/bullet/pistol/medium
- fire_sound = 'sound/weapons/Gunshot.ogg'
+ fire_sound = 'sound/weapons/machinegun.ogg'
projectiles = 300
projectiles_per_shot = 3
deviation = 0.3
@@ -218,7 +218,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/Fire(atom/movable/AM, atom/target, turf/aimloc)
AM.throw_at(target,missile_range, missile_speed, chassis)
-
+
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flare
name = "\improper BNI Flare Launcher"
icon_state = "mecha_flaregun"
@@ -231,7 +231,7 @@
missile_speed = 1
missile_range = 15
required_type = /obj/mecha //Why restrict it to just mining or combat mechs?
-
+
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flare/Fire(atom/movable/AM, atom/target, turf/aimloc)
var/obj/item/device/flashlight/flare/fired = AM
fired.ignite()
@@ -241,7 +241,7 @@
name = "\improper SRM-8 missile rack"
icon_state = "mecha_missilerack"
projectile = /obj/item/missile
- fire_sound = 'sound/effects/bang.ogg'
+ fire_sound = 'sound/weapons/rpg.ogg'
projectiles = 8
projectile_energy_cost = 1000
equip_cooldown = 60
diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm
index 561a5bb3d8..e24a520396 100644
--- a/code/game/mecha/mech_prosthetics.dm
+++ b/code/game/mecha/mech_prosthetics.dm
@@ -1,6 +1,6 @@
/obj/machinery/pros_fabricator
icon = 'icons/obj/robotics.dmi'
- icon_state = "pros-idle"
+ icon_state = "fab-idle"
name = "Prosthetics Fabricator"
desc = "A machine used for construction of prosthetics."
density = 1
@@ -59,11 +59,11 @@
/obj/machinery/pros_fabricator/update_icon()
overlays.Cut()
if(panel_open)
- icon_state = "pros-o"
+ icon_state = "fab-o"
else
- icon_state = "pros-idle"
+ icon_state = "fab-idle"
if(busy)
- overlays += "pros-active"
+ overlays += "fab-active"
/obj/machinery/pros_fabricator/dismantle()
for(var/f in materials)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 02d85234ed..e900ba7373 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -29,12 +29,16 @@
/obj/proc/buckle_mob(mob/living/M)
if(!can_buckle || !istype(M) || (M.loc != loc) || M.buckled || M.pinned.len || (buckle_require_restraints && !M.restrained()))
return 0
+ if(buckled_mob) //Handles trying to buckle yourself to the chair when someone is on it
+ M << "\The [src] already has someone buckled to it."
+ return 0
M.buckled = src
M.facing_dir = null
M.set_dir(buckle_dir ? buckle_dir : dir)
M.update_canmove()
buckled_mob = M
+
post_buckle_mob(M)
return 1
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 01fc592690..d7e530b761 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -107,9 +107,10 @@ steam.start() -- spawns the effect
var/turf/T = src.loc
if (istype(T, /turf))
T.hotspot_expose(1000,100)
- spawn (20)
- qdel(src)
- return
+
+/obj/effect/effect/sparks/initialize()
+ ..()
+ schedule_task_in(5 SECONDS, /proc/qdel, list(src))
/obj/effect/effect/sparks/Destroy()
var/turf/T = src.loc
@@ -185,7 +186,6 @@ steam.start() -- spawns the effect
..()
spawn (time_to_live)
qdel(src)
- return
/obj/effect/effect/smoke/Crossed(mob/living/carbon/M as mob )
..()
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 15cb670a8c..d08d45d5ce 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -5,6 +5,7 @@
anchored = 1.0
unacidable = 1
simulated = 0
+ var/delete_me = 0
/obj/effect/landmark/New()
..()
@@ -14,33 +15,35 @@
switch(name) //some of these are probably obsolete
if("monkey")
monkeystart += loc
- qdel(src)
+ delete_me = 1
return
if("start")
newplayer_start += loc
- qdel(src)
+ delete_me = 1
+ return
if("JoinLate")
latejoin += loc
- qdel(src)
+ delete_me = 1
+ return
if("JoinLateGateway")
latejoin_gateway += loc
- qdel(src)
+ delete_me = 1
return
if("JoinLateElevator")
latejoin_elevator += loc
- qdel(src)
+ delete_me = 1
return
if("JoinLateCryo")
latejoin_cryo += loc
- qdel(src)
+ delete_me = 1
return
if("JoinLateCyborg")
latejoin_cyborg += loc
- qdel(src)
+ delete_me = 1
return
if("prisonwarp")
prisonwarp += loc
- qdel(src)
+ delete_me = 1
return
if("Holding Facility")
holdingfacility += loc
@@ -54,28 +57,36 @@
tdomeobserve += loc
if("prisonsecuritywarp")
prisonsecuritywarp += loc
- qdel(src)
+ delete_me = 1
return
if("blobstart")
blobstart += loc
- qdel(src)
+ delete_me = 1
return
if("xeno_spawn")
xeno_spawn += loc
- qdel(src)
+ delete_me = 1
return
if("endgame_exit")
endgame_safespawns += loc
- qdel(src)
+ delete_me = 1
return
if("bluespacerift")
endgame_exits += loc
- qdel(src)
+ delete_me = 1
return
landmarks_list += src
return 1
+/obj/effect/landmark/proc/delete()
+ delete_me = 1
+
+/obj/effect/landmark/initialize()
+ ..()
+ if(delete_me)
+ qdel(src)
+
/obj/effect/landmark/Destroy()
landmarks_list -= src
return ..()
@@ -99,14 +110,14 @@
var/list/options = typesof(/obj/effect/landmark/costume)
var/PICK= options[rand(1,options.len)]
new PICK(src.loc)
- qdel(src)
+ delete_me = 1
//SUBCLASSES. Spawn a bunch of items and disappear likewise
/obj/effect/landmark/costume/chicken/New()
new /obj/item/clothing/suit/chickensuit(src.loc)
new /obj/item/clothing/head/chicken(src.loc)
new /obj/item/weapon/reagent_containers/food/snacks/egg(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/gladiator/New()
new /obj/item/clothing/under/gladiator(src.loc)
@@ -118,32 +129,32 @@
new /obj/item/clothing/head/flatcap(src.loc)
new /obj/item/clothing/suit/storage/toggle/labcoat/mad(src.loc)
new /obj/item/clothing/glasses/gglasses(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/elpresidente/New()
new /obj/item/clothing/under/gimmick/rank/captain/suit(src.loc)
new /obj/item/clothing/head/flatcap(src.loc)
new /obj/item/clothing/mask/smokable/cigarette/cigar/havana(src.loc)
new /obj/item/clothing/shoes/jackboots(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/nyangirl/New()
new /obj/item/clothing/under/schoolgirl(src.loc)
new /obj/item/clothing/head/kitty(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/maid/New()
new /obj/item/clothing/under/skirt(src.loc)
var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears )
new CHOICE(src.loc)
new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/butler/New()
new /obj/item/clothing/suit/wcoat(src.loc)
new /obj/item/clothing/under/suit_jacket(src.loc)
new /obj/item/clothing/head/that(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/scratch/New()
new /obj/item/clothing/gloves/white(src.loc)
@@ -151,12 +162,12 @@
new /obj/item/clothing/under/scratch(src.loc)
if (prob(30))
new /obj/item/clothing/head/cueball(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/highlander/New()
new /obj/item/clothing/under/kilt(src.loc)
new /obj/item/clothing/head/beret(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/prig/New()
new /obj/item/clothing/suit/wcoat(src.loc)
@@ -167,24 +178,24 @@
new /obj/item/weapon/cane(src.loc)
new /obj/item/clothing/under/sl_suit(src.loc)
new /obj/item/clothing/mask/fakemoustache(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/plaguedoctor/New()
new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc)
new /obj/item/clothing/head/plaguedoctorhat(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/nightowl/New()
new /obj/item/clothing/under/owl(src.loc)
new /obj/item/clothing/mask/gas/owl_mask(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/waiter/New()
new /obj/item/clothing/under/waiter(src.loc)
var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears)
new CHOICE(src.loc)
new /obj/item/clothing/suit/apron(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/pirate/New()
new /obj/item/clothing/under/pirate(src.loc)
@@ -192,18 +203,18 @@
var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana )
new CHOICE(src.loc)
new /obj/item/clothing/glasses/eyepatch(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/commie/New()
new /obj/item/clothing/under/soviet(src.loc)
new /obj/item/clothing/head/ushanka(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/imperium_monk/New()
new /obj/item/clothing/suit/imperium_monk(src.loc)
if (prob(25))
new /obj/item/clothing/mask/gas/cyborg(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/holiday_priest/New()
new /obj/item/clothing/suit/holidaypriest(src.loc)
@@ -212,26 +223,26 @@
/obj/effect/landmark/costume/marisawizard/fake/New()
new /obj/item/clothing/head/wizard/marisa/fake(src.loc)
new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/cutewitch/New()
new /obj/item/clothing/under/sundress(src.loc)
new /obj/item/clothing/head/witchwig(src.loc)
new /obj/item/weapon/staff/broom(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/fakewizard/New()
new /obj/item/clothing/suit/wizrobe/fake(src.loc)
new /obj/item/clothing/head/wizard/fake(src.loc)
new /obj/item/weapon/staff/(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/sexyclown/New()
new /obj/item/clothing/mask/gas/sexyclown(src.loc)
new /obj/item/clothing/under/sexyclown(src.loc)
- qdel(src)
+ delete_me = 1
/obj/effect/landmark/costume/sexymime/New()
new /obj/item/clothing/mask/gas/sexymime(src.loc)
new /obj/item/clothing/under/sexymime(src.loc)
- qdel(src)
\ No newline at end of file
+ delete_me = 1
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index cc52bf7bbf..4fc07a2a8a 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -52,3 +52,48 @@
..()
pixel_x += rand(-10, 10)
pixel_y += rand(-10, 10)
+
+/obj/effect/overlay/snow
+ name = "snow"
+ icon = 'icons/turf/overlays.dmi'
+ icon_state = "snow"
+ anchored = 1
+
+/obj/effect/overlay/snow/floor
+ icon_state = "snowfloor"
+ layer = 2.01 //Just above floor
+
+/obj/effect/overlay/snow/floor/edges
+ icon_state = "snow_edges"
+
+/obj/effect/overlay/snow/floor/surround
+ icon_state = "snow_surround"
+
+/obj/effect/overlay/snow/airlock
+ icon_state = "snowairlock"
+ layer = 3.2 //Just above airlocks
+
+/obj/effect/overlay/snow/floor/north
+ icon_state = "snowfloor_n"
+
+/obj/effect/overlay/snow/floor/south
+ icon_state = "snowfloor_s"
+
+/obj/effect/overlay/snow/floor/east
+ icon_state = "snowfloor_e"
+
+/obj/effect/overlay/snow/floor/west
+ icon_state = "snowfloor_w"
+
+/obj/effect/overlay/snow/wall/north
+ icon_state = "snowwall_n"
+ layer = 5 //Same as lights so humans can stand under it
+
+/obj/effect/overlay/snow/wall/south
+ icon_state = "snowwall_s"
+
+/obj/effect/overlay/snow/wall/east
+ icon_state = "snowwall_e"
+
+/obj/effect/overlay/snow/wall/west
+ icon_state = "snowwall_w"
\ No newline at end of file
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 9a54fa1829..9b5dffdd37 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -12,9 +12,6 @@
var/syndie = 0
var/list/channels = list()
-
-/obj/item/device/encryptionkey/New()
-
/obj/item/device/encryptionkey/attackby(obj/item/weapon/W as obj, mob/user as mob)
/obj/item/device/encryptionkey/syndicate
diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm
index e2ec23be7c..2a85fbdfc9 100644
--- a/code/game/objects/items/weapons/extinguisher.dm
+++ b/code/game/objects/items/weapons/extinguisher.dm
@@ -37,6 +37,7 @@
/obj/item/weapon/extinguisher/New()
create_reagents(max_water)
reagents.add_reagent("water", max_water)
+ ..()
/obj/item/weapon/extinguisher/examine(mob/user)
if(..(user, 0))
diff --git a/code/game/objects/items/weapons/improvised_components.dm b/code/game/objects/items/weapons/improvised_components.dm
index af4e528b70..0fd7d95e8c 100644
--- a/code/game/objects/items/weapons/improvised_components.dm
+++ b/code/game/objects/items/weapons/improvised_components.dm
@@ -54,7 +54,7 @@
/obj/item/weapon/material/wirerod/attackby(var/obj/item/I, mob/user as mob)
..()
var/obj/item/finished
- if(istype(I, /obj/item/weapon/material/shard))
+ if(istype(I, /obj/item/weapon/material/shard) || istype(I, /obj/item/weapon/material/butterflyblade))
var/obj/item/weapon/material/tmp_shard = I
finished = new /obj/item/weapon/material/twohanded/spear(get_turf(user), tmp_shard.material.name)
user << "You fasten \the [I] to the top of the rod with the cable."
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index f8b21af190..9f6992d404 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -225,7 +225,7 @@
/obj/item/weapon/storage/box/emps/New()
..()
for(var/i = 1 to 5)
- new /obj/item/weapon/grenade/empgrenade(src)
+ new /obj/item/weapon/grenade/empgrenade(src)
/obj/item/weapon/storage/box/empslite
name = "box of low yield emp grenades"
@@ -275,7 +275,7 @@
/obj/item/weapon/storage/box/metalfoam/New()
..()
for(var/i = 1 to 7)
- new /obj/item/weapon/grenade/chem_grenade/metalfoam
+ new /obj/item/weapon/grenade/chem_grenade/metalfoam(src)
/obj/item/weapon/storage/box/trackimp
name = "boxed tracking implant kit"
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index b39fc0dd27..b7868cf7e0 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -79,7 +79,7 @@
if (prob(75))
src.pixel_y = rand(0, 16)
- return
+ ..()
/obj/item/weapon/screwdriver/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
if(!istype(M) || user.a_intent == "help")
@@ -114,6 +114,7 @@
if(prob(50))
icon_state = "cutters-y"
item_state = "cutters_yellow"
+ ..()
/obj/item/weapon/wirecutters/attack(mob/living/carbon/C as mob, mob/user as mob)
if(user.a_intent == I_HELP && (C.handcuffed) && (istype(C.handcuffed, /obj/item/weapon/handcuffs/cable)))
@@ -162,12 +163,12 @@
reagents = R
R.my_atom = src
R.add_reagent("fuel", max_fuel)
- return
+ ..()
/obj/item/weapon/weldingtool/Destroy()
if(welding)
processing_objects -= src
- ..()
+ return ..()
/obj/item/weapon/weldingtool/examine(mob/user)
if(..(user, 0))
diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm
index 1127bf7510..fc6615fcf0 100644
--- a/code/game/objects/random/random.dm
+++ b/code/game/objects/random/random.dm
@@ -11,8 +11,10 @@
..()
if (!prob(spawn_nothing_percentage))
spawn_item()
- qdel(src)
+/obj/random/initialize()
+ ..()
+ qdel(src)
// this function should return a specific item to spawn
/obj/random/proc/item_to_spawn()
diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm
index 31f8115b91..714be281b8 100644
--- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm
@@ -22,7 +22,7 @@
/obj/structure/closet/emcloset/New()
..()
- switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10, "nothing" = 0, "delete" = 0)))
+ switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10)))
if ("small")
new /obj/item/weapon/tank/emergency_oxygen(src)
new /obj/item/weapon/tank/emergency_oxygen(src)
@@ -51,17 +51,6 @@
new /obj/item/clothing/suit/space/emergency(src)
new /obj/item/clothing/head/helmet/space/emergency(src)
new /obj/item/clothing/head/helmet/space/emergency(src)
- if ("nothing")
- // doot
-
- // teehee - Ah, tg coders...
- if ("delete")
- qdel(src)
-
- //If you want to re-add fire, just add "fire" = 15 to the pick list.
- /*if ("fire")
- new /obj/structure/closet/firecloset(src.loc)
- qdel(src)*/
/obj/structure/closet/emcloset/legacy/New()
..()
diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm
index b9c9a1b797..d8de10bf18 100644
--- a/code/game/objects/structures/crates_lockers/largecrate.dm
+++ b/code/game/objects/structures/crates_lockers/largecrate.dm
@@ -5,6 +5,13 @@
icon_state = "densecrate"
density = 1
+/obj/structure/largecrate/initialize()
+ ..()
+ for(var/obj/I in src.loc)
+ if(I.density || I.anchored || I == src || !I.simulated)
+ continue
+ I.forceMove(src)
+
/obj/structure/largecrate/attack_hand(mob/user as mob)
user << "You need a crowbar to pry this open!"
return
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 2b8396c554..eff0de4b48 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -9,7 +9,7 @@
layer = 2.3 //under pipes
// flags = CONDUCT
-/obj/structure/lattice/New() //turf/simulated/floor/asteroid
+/obj/structure/lattice/initialize()
..()
if(!(istype(src.loc, /turf/space) || istype(src.loc, /turf/simulated/open) || istype(src.loc, /turf/simulated/mineral)))
diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm
index 1624a4bbde..c76ca1802d 100644
--- a/code/game/objects/structures/mop_bucket.dm
+++ b/code/game/objects/structures/mop_bucket.dm
@@ -12,7 +12,7 @@
/obj/structure/mopbucket/New()
create_reagents(100)
-
+ ..()
/obj/structure/mopbucket/examine(mob/user)
if(..(user, 1))
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index 511709200c..73ceddb70f 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -129,6 +129,9 @@
else if(istype(W, /obj/item/weapon/grab))
var/obj/item/weapon/grab/G = W
var/mob/living/affecting = G.affecting
+ if(buckled_mob) //Handles trying to buckle someone else to a chair when someone else is on it
+ user << "\The [src] already has someone buckled to it."
+ return
user.visible_message("[user] attempts to buckle [affecting] into \the [src]!")
if(do_after(user, 20))
affecting.loc = loc
@@ -181,6 +184,9 @@
icon_state = "doublebed"
base_icon = "doublebed"
+/obj/structure/bed/double/padded/New(var/newloc)
+ ..(newloc,"wood","cotton")
+
/obj/structure/bed/double/post_buckle_mob(mob/living/M as mob)
if(M == buckled_mob)
M.pixel_y = 13
diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm
index 163867d1fa..927facec8d 100644
--- a/code/game/turfs/flooring/flooring.dm
+++ b/code/game/turfs/flooring/flooring.dm
@@ -56,6 +56,26 @@ var/list/flooring_types
flags = TURF_HAS_EDGES | TURF_REMOVE_SHOVEL
build_type = null
+/decl/flooring/snow
+ name = "snow"
+ desc = "A layer of many tiny bits of frozen water. It's hard to tell how deep it is."
+ icon = 'icons/turf/snow.dmi'
+ icon_base = "snow"
+ flags = TURF_HAS_EDGES
+
+/decl/flooring/snow/gravsnow
+ name = "snow"
+ icon_base = "gravsnow"
+
+/decl/flooring/snow/plating
+ name = "snowy plating"
+ desc = "Steel plating coated with a light layer of snow."
+ icon_base = "snowyplating"
+ flags = null
+
+/decl/flooring/snow/plating/drift
+ icon_base = "snowyplayingdrift"
+
/decl/flooring/carpet
name = "carpet"
desc = "Imported and comfy."
diff --git a/code/game/turfs/flooring/flooring_premade.dm b/code/game/turfs/flooring/flooring_premade.dm
index 73bb5f71f8..86ec1b2891 100644
--- a/code/game/turfs/flooring/flooring_premade.dm
+++ b/code/game/turfs/flooring/flooring_premade.dm
@@ -45,6 +45,27 @@
icon_state = "reinforced"
initial_flooring = /decl/flooring/reinforced
+/turf/simulated/floor/snow
+ name = "snow"
+ icon = 'icons/turf/snow.dmi'
+ icon_state = "snow"
+ initial_flooring = /decl/flooring/snow
+
+/turf/simulated/floor/snow/gravsnow
+ name = "snow"
+ icon_state = "gravsnow"
+ initial_flooring = /decl/flooring/snow/gravsnow
+
+/turf/simulated/floor/snow/plating
+ name = "snowy playing"
+ icon_state = "snowyplating"
+ initial_flooring = /decl/flooring/snow/plating
+
+/turf/simulated/floor/snow/plating/drift
+ name = "snowy plating"
+ icon_state = "snowyplayingdrift"
+ initial_flooring = /decl/flooring/snow/plating/drift
+
/turf/simulated/floor/reinforced/airless
oxygen = 0
nitrogen = 0
@@ -195,7 +216,6 @@
/turf/simulated/floor/airless/lava
/turf/simulated/floor/light
-/turf/simulated/floor/snow
/*
/turf/simulated/floor/beach
/turf/simulated/floor/beach/sand
@@ -204,5 +224,5 @@
/turf/simulated/floor/beach/water
/turf/simulated/floor/beach/water/ocean
*/
-/turf/simulated/floor/plating/snow
/turf/simulated/floor/airless/ceiling
+/turf/simulated/floor/plating
\ No newline at end of file
diff --git a/code/game/turfs/initialization/maintenance.dm b/code/game/turfs/initialization/maintenance.dm
index 5a9574810b..16b1d03d21 100644
--- a/code/game/turfs/initialization/maintenance.dm
+++ b/code/game/turfs/initialization/maintenance.dm
@@ -26,7 +26,7 @@ var/global/list/random_junk
if(prob(25))
return /obj/effect/decal/cleanable/generic
if(!random_junk)
- random_junk = subtypes(/obj/item/trash)
+ random_junk = subtypesof(/obj/item/trash)
random_junk += typesof(/obj/item/weapon/cigbutt)
random_junk += /obj/effect/decal/cleanable/spiderling_remains
random_junk += /obj/effect/decal/remains/mouse
diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm
index e5341c7719..0d3e43bbfa 100644
--- a/code/game/turfs/simulated/floor_types.dm
+++ b/code/game/turfs/simulated/floor_types.dm
@@ -40,3 +40,5 @@
name = "skipjack floor"
oxygen = 0
nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD
+
+
diff --git a/code/global.dm b/code/global.dm
index c7e5fe5340..e2df3f0661 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -118,7 +118,6 @@ var/join_motd = null
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
var/datum/event_manager/event_manager = new() // Event Manager, the manager for events.
-var/datum/subsystem/alarm/alarm_manager = new() // Alarm Manager, the manager for alarms.
var/list/awaydestinations = list() // Away missions. A list of landmarks that the warpgate can take you to.
diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm
index 47b7f7b571..afa45d7649 100644
--- a/code/modules/alarm/alarm_handler.dm
+++ b/code/modules/alarm/alarm_handler.dm
@@ -86,10 +86,10 @@
/turf/get_alarm_origin()
return get_area(src)
-/datum/alarm_handler/proc/register(var/object, var/procName)
+/datum/alarm_handler/proc/register_alarm(var/object, var/procName)
listeners[object] = procName
-/datum/alarm_handler/proc/unregister(var/object)
+/datum/alarm_handler/proc/unregister_alarm(var/object)
listeners -= object
/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm
index d7b4bcddc5..6a0c0705ae 100644
--- a/code/modules/client/preference_setup/general/01_basic.dm
+++ b/code/modules/client/preference_setup/general/01_basic.dm
@@ -107,7 +107,7 @@ datum/preferences/proc/set_biological_gender(var/gender)
else if(href_list["metadata"])
var/new_metadata = sanitize(input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , pref.metadata) as message|null) as message|null
if(new_metadata && CanUseTopic(user))
- pref.metadata = sanitize(new_metadata)
+ pref.metadata = new_metadata
return TOPIC_REFRESH
return ..()
diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index e0e0356a6c..d446a8f950 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -5,7 +5,7 @@ var/list/_client_preferences_by_type
/proc/get_client_preferences()
if(!_client_preferences)
_client_preferences = list()
- for(var/ct in subtypes(/datum/client_preference))
+ for(var/ct in subtypesof(/datum/client_preference))
var/datum/client_preference/client_type = ct
if(initial(client_type.description))
_client_preferences += new client_type()
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 87b6b7540d..6b2e8e6e4f 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -216,7 +216,7 @@
body_parts_covered = HANDS
slot_flags = SLOT_GLOVES
attack_verb = list("challenged")
- species_restricted = list("exclude","Unathi","Tajara")
+ species_restricted = null
sprite_sheets = list(
"Teshari" = 'icons/mob/species/seromi/gloves.dmi',
)
@@ -238,7 +238,7 @@
/obj/item/clothing/gloves/proc/Touch(var/atom/A, var/proximity)
return 0 // return 1 to cancel attack_hand()
-/obj/item/clothing/gloves/attackby(obj/item/weapon/W, mob/user)
+/*/obj/item/clothing/gloves/attackby(obj/item/weapon/W, mob/user)
if(istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/weapon/scalpel))
if (clipped)
user << "The [src] have already been clipped!"
@@ -255,7 +255,7 @@
species_restricted -= "Unathi"
species_restricted -= "Tajara"
return
-
+*/
///////////////////////////////////////////////////////////////////////
//Head
/obj/item/clothing/head
@@ -406,7 +406,7 @@
slowdown = SHOES_SLOWDOWN
force = 2
var/overshoes = 0
- species_restricted = list("exclude","Teshari", "Unathi","Tajara")
+ species_restricted = list("exclude","Teshari")
sprite_sheets = list(
"Teshari" = 'icons/mob/species/seromi/shoes.dmi',
)
diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm
index b4359dcb41..9fc49b1bf7 100644
--- a/code/modules/detectivework/microscope/dnascanner.dm
+++ b/code/modules/detectivework/microscope/dnascanner.dm
@@ -16,7 +16,7 @@
var/last_process_worldtime = 0
var/report_num = 0
-/obj/machinery/dnaforensics/map/New()
+/obj/machinery/dnaforensics/New()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm
index 1c8361dbaf..055e366337 100644
--- a/code/modules/lighting/lighting_process.dm
+++ b/code/modules/lighting/lighting_process.dm
@@ -22,7 +22,7 @@
L.force_update = 0
L.needs_update = 0
- scheck()
+ SCHECK
var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = null //Same as above
@@ -32,4 +32,4 @@
O.update_overlay()
O.needs_update = 0
- scheck()
+ SCHECK
diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm
index ca6898cb16..eeb7546211 100644
--- a/code/modules/mining/drilling/drill.dm
+++ b/code/modules/mining/drilling/drill.dm
@@ -39,7 +39,7 @@
var/need_update_field = 0
var/need_player_check = 0
-/obj/machinery/mining/drill/map/New()
+/obj/machinery/mining/drill/New()
..()
circuit = new circuit(src)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index aaaad1557c..96b34c0949 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -56,8 +56,8 @@
/mob/living/carbon/human/Stat()
..()
if(statpanel("Status"))
- stat(null, "Intent: [a_intent]")
- stat(null, "Move Mode: [m_intent]")
+ stat("Intent:", "[a_intent]")
+ stat("Move Mode:", "[m_intent]")
if(emergency_shuttle)
var/eta_status = emergency_shuttle.get_status_panel_eta()
if(eta_status)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index d670e17ee3..5a13c7bb79 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -40,7 +40,7 @@
/mob/living/silicon/Destroy()
silicon_mob_list -= src
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
- AH.unregister(src)
+ AH.unregister_alarm(src)
..()
/mob/living/silicon/proc/init_id()
diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm
index fa1363203b..1dae0026aa 100644
--- a/code/modules/mob/living/silicon/subystems.dm
+++ b/code/modules/mob/living/silicon/subystems.dm
@@ -39,7 +39,7 @@
return
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
- AH.register(src, /mob/living/silicon/proc/receive_alarm)
+ AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm)
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
/********************
diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm
index af42c6ad2e..b065a78ae9 100644
--- a/code/modules/mob/living/simple_animal/hostile/creature.dm
+++ b/code/modules/mob/living/simple_animal/hostile/creature.dm
@@ -6,15 +6,16 @@
icon_state = "otherthing"
icon_living = "otherthing"
icon_dead = "otherthing-dead"
- health = 40
maxHealth = 40
+ health = 40
+
+ harm_intent_damage = 8
melee_damage_lower = 5
melee_damage_upper = 5
attacktext = "chomped"
attack_sound = 'sound/weapons/bite.ogg'
faction = "creature"
speed = 8
- harm_intent_damage = 10
/mob/living/simple_animal/hostile/creature/cult
faction = "cult"
@@ -37,3 +38,35 @@
/mob/living/simple_animal/hostile/creature/cult/Life()
..()
check_horde()
+
+
+/mob/living/simple_animal/hostile/creature/strong
+ maxHealth = 160
+ health = 160
+
+ harm_intent_damage = 5
+ melee_damage_lower = 8
+ melee_damage_upper = 25
+
+
+/mob/living/simple_animal/hostile/creature/strong/cult
+ faction = "cult"
+
+ min_oxy = 0
+ max_oxy = 0
+ min_tox = 0
+ max_tox = 0
+ min_co2 = 0
+ max_co2 = 0
+ min_n2 = 0
+ max_n2 = 0
+ minbodytemp = 0
+
+ supernatural = 1
+
+/mob/living/simple_animal/hostile/creature/cult/cultify()
+ return
+
+/mob/living/simple_animal/hostile/creature/cult/Life()
+ ..()
+ check_horde()
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index 7b96db183a..dd20145b38 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -58,3 +58,26 @@
/mob/living/simple_animal/hostile/faithless/cult/Life()
..()
check_horde()
+
+
+
+
+/mob/living/simple_animal/hostile/faithless/strong
+ maxHealth = 100
+ health = 100
+
+ harm_intent_damage = 5
+ melee_damage_lower = 7
+ melee_damage_upper = 20
+
+
+/mob/living/simple_animal/hostile/faithless/strong/cult
+ faction = "cult"
+ supernatural = 1
+
+/mob/living/simple_animal/hostile/faithless/cult/cultify()
+ return
+
+/mob/living/simple_animal/hostile/faithless/cult/Life()
+ ..()
+ check_horde()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 262aad8e1a..713d33458c 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -19,7 +19,7 @@
meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat
response_help = "pets"
response_disarm = "gently pushes aside"
- response_harm = "pokes"
+ response_harm = "punches"
stop_automated_movement_when_pulled = 0
maxHealth = 200
health = 200
diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm
index da2599aeb7..ee99318c15 100644
--- a/code/modules/mob/living/simple_animal/hostile/russian.dm
+++ b/code/modules/mob/living/simple_animal/hostile/russian.dm
@@ -42,7 +42,7 @@
ranged = 1
projectiletype = /obj/item/projectile/bullet
projectilesound = 'sound/weapons/Gunshot.ogg'
- casingtype = /obj/item/ammo_casing/a357
+ casingtype = /obj/item/ammo_casing/spent
/mob/living/simple_animal/hostile/russian/death()
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 609b77ae56..694e95b434 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -107,7 +107,7 @@
rapid = 1
icon_state = "syndicateranged"
icon_living = "syndicateranged"
- casingtype = /obj/item/ammo_casing/a10mm
+ casingtype = /obj/item/ammo_casing/spent
projectilesound = 'sound/weapons/Gunshot_light.ogg'
projectiletype = /obj/item/projectile/bullet/pistol/medium
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 2b2317524d..8e80f31d0b 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -662,15 +662,13 @@
if(client.holder)
if(statpanel("Status"))
- stat("Location:","([x], [y], [z])")
- if(statpanel("Processes"))
+ stat("Location:", "([x], [y], [z]) [loc]")
stat("CPU:","[world.cpu]")
stat("Instances:","[world.contents.len]")
- if(processScheduler && processScheduler.getIsRunning())
- for(var/datum/controller/process/P in processScheduler.processes)
- stat(P.getStatName(), P.getTickTime())
- else
- stat("processScheduler is not running.")
+
+ if(statpanel("Processes"))
+ if(processScheduler)
+ processScheduler.statProcesses()
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm
index 29ae55b04a..f3fca46d43 100644
--- a/code/modules/nano/modules/alarm_monitor.dm
+++ b/code/modules/nano/modules/alarm_monitor.dm
@@ -15,13 +15,13 @@
..()
alarm_handlers = list(camera_alarm, motion_alarm)
-/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
+/datum/nano_module/alarm_monitor/proc/register_alarm(var/object, var/procName)
for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.register(object, procName)
+ AH.register_alarm(object, procName)
-/datum/nano_module/alarm_monitor/proc/unregister(var/object)
+/datum/nano_module/alarm_monitor/proc/unregister_alarm(var/object)
for(var/datum/alarm_handler/AH in alarm_handlers)
- AH.unregister(object)
+ AH.unregister_alarm(object)
/datum/nano_module/alarm_monitor/proc/all_alarms()
var/list/all_alarms = new()
diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm
index a0f515ac13..b99833670d 100644
--- a/code/modules/paperwork/faxmachine.dm
+++ b/code/modules/paperwork/faxmachine.dm
@@ -14,6 +14,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
use_power = 1
idle_power_usage = 30
active_power_usage = 200
+ circuit = /obj/item/weapon/circuitboard/fax
var/obj/item/weapon/card/id/scan = null // identification
var/authenticated = 0
@@ -22,21 +23,11 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins
var/destination = null // the department we're sending to
/obj/machinery/photocopier/faxmachine/New()
- ..()
allfaxes += src
if(!destination) destination = "[boss_name]"
if( !(("[department]" in alldepartments) || ("[department]" in admin_departments)) )
alldepartments |= department
-
-/obj/machinery/photocopier/faxmachine/map/New()
..()
- circuit = new circuit(src)
- component_parts = list()
- component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
- component_parts += new /obj/item/weapon/stock_parts/motor(src)
- component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
- component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
- RefreshParts()
/obj/machinery/photocopier/faxmachine/attack_hand(mob/user as mob)
user.set_machine(src)
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index dcee1fb3fa..d122d42c2b 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -15,7 +15,7 @@
var/toner = 30 //how much toner is left! woooooo~
var/maxcopies = 10 //how many copies can be copied at once- idea shamelessly stolen from bs12's copier!
-/obj/machinery/photocopier/map/New()
+/obj/machinery/photocopier/New()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
diff --git a/code/modules/power/sensors/sensor_monitoring.dm b/code/modules/power/sensors/sensor_monitoring.dm
index c75b628528..597ced19db 100644
--- a/code/modules/power/sensors/sensor_monitoring.dm
+++ b/code/modules/power/sensors/sensor_monitoring.dm
@@ -8,7 +8,7 @@
desc = "Computer designed to remotely monitor power levels around the station"
icon = 'icons/obj/computer.dmi'
icon_keyboard = "power_key"
- icon_screen = "power"
+ icon_screen = "power:0"
light_color = "#ffcc33"
//computer stuff
@@ -30,16 +30,12 @@
// Updates icon of this computer according to current status.
/obj/machinery/computer/power_monitor/update_icon()
- if(stat & BROKEN)
- icon_state = "powerb"
- return
- if(stat & NOPOWER)
- icon_state = "power0"
- return
- if(alerting)
- icon_state = "power_alert"
- return
- icon_state = "power"
+ if(!(stat & (NOPOWER|BROKEN)))
+ if(alerting)
+ icon_screen = "power:1"
+ else
+ icon_screen = "power:0"
+ ..()
// On creation automatically connects to active sensors. This is delayed to ensure sensors already exist.
/obj/machinery/computer/power_monitor/New()
diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm
index 219ebff141..59428ceb94 100644
--- a/code/modules/projectiles/ammunition.dm
+++ b/code/modules/projectiles/ammunition.dm
@@ -12,7 +12,7 @@
var/caliber = "" //Which kind of guns it can be loaded into
var/projectile_type //The bullet type to create when New() is called
var/obj/item/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed?
- var/spent_icon = null
+// var/spent_icon = null
/obj/item/ammo_casing/New()
..()
@@ -46,8 +46,10 @@
BB.name = "[initial(BB.name)] (\"[label_text]\")"
/obj/item/ammo_casing/update_icon()
- if(spent_icon && !BB)
- icon_state = spent_icon
+/* if(spent_icon && !BB)
+ icon_state = spent_icon*/
+ if(!BB) // This is really just a much better way of doing this.
+ icon_state = "[initial(icon_state)]-spent"
/obj/item/ammo_casing/examine(mob/user)
..()
@@ -88,6 +90,7 @@
var/list/ammo_states = list() //values
/obj/item/ammo_magazine/New()
+ ..()
if(multiple_sprites)
initialize_magazine_icondata(src)
@@ -112,6 +115,24 @@
C.loc = src
stored_ammo.Insert(1, C) //add to the head of the list
update_icon()
+ if(istype(W, /obj/item/ammo_magazine/clip))
+ var/obj/item/ammo_magazine/clip/L = W
+ if(L.caliber != caliber)
+ user << "The ammo in [L] does not fit into [src]."
+ return
+ if(!L.stored_ammo.len)
+ user << "There's no more ammo [L]!"
+ return
+ if(stored_ammo.len >= max_ammo)
+ user << "[src] is full!"
+ return
+ var/obj/item/ammo_casing/AC = L.stored_ammo[1] //select the next casing.
+ L.stored_ammo -= AC //Remove this casing from loaded list of the clip.
+ AC.loc = src
+ stored_ammo.Insert(1, AC) //add it to the head of our magazine's list
+ L.update_icon()
+ playsound(user.loc, 'sound/weapons/flipblade.ogg', 50, 1)
+ update_icon()
/obj/item/ammo_magazine/attack_self(mob/user)
if(!stored_ammo.len)
diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm
index a2b1dd7272..001753e81a 100644
--- a/code/modules/projectiles/ammunition/boxes.dm
+++ b/code/modules/projectiles/ammunition/boxes.dm
@@ -1,17 +1,20 @@
+///////// .357 /////////
+
/obj/item/ammo_magazine/a357
- //name = "ammo box (.357)"
- //desc = "A box of .357 ammo"
- //icon_state = "357"
- name = "speed loader (.357)"
- icon_state = "T38"
+ name = "speedloader (.357)"
+ desc = "A speedloader for .357 revolvers."
+ icon_state = "38"
caliber = "357"
ammo_type = /obj/item/ammo_casing/a357
matter = list(DEFAULT_WALL_MATERIAL = 1260)
- max_ammo = 7
+ max_ammo = 6
multiple_sprites = 1
+///////// .38 /////////
+
/obj/item/ammo_magazine/c38
- name = "speed loader (.38)"
+ name = "speedloader (.38)"
+ desc = "A speedloader for .38 revolvers."
icon_state = "38"
caliber = "38"
matter = list(DEFAULT_WALL_MATERIAL = 360)
@@ -20,11 +23,13 @@
multiple_sprites = 1
/obj/item/ammo_magazine/c38/rubber
- name = "speed loader (.38 rubber)"
+ name = "speedloader (.38 rubber)"
ammo_type = /obj/item/ammo_casing/c38r
+///////// .45 /////////
+
/obj/item/ammo_magazine/c45m
- name = "magazine (.45)"
+ name = "pistol magazine (.45)"
icon_state = "45"
mag_type = MAGAZINE
ammo_type = /obj/item/ammo_casing/c45
@@ -46,17 +51,79 @@
/obj/item/ammo_magazine/c45m/flash
name = "magazine (.45 flash)"
- ammo_type = "/obj/item/ammo_casing/c45f"
+ ammo_type = /obj/item/ammo_casing/c45f
+
+/obj/item/ammo_magazine/c45uzi
+ name = "stick magazine (.45)"
+ icon_state = "uzi45"
+ mag_type = MAGAZINE
+ ammo_type = /obj/item/ammo_casing/c45
+ matter = list(DEFAULT_WALL_MATERIAL = 1200)
+ caliber = ".45"
+ max_ammo = 16
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/c45uzi/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/tommymag
+ name = "tommygun magazine (.45)"
+ icon_state = "tommy-mag"
+ mag_type = MAGAZINE
+ ammo_type = /obj/item/ammo_casing/c45
+ matter = list(DEFAULT_WALL_MATERIAL = 1500)
+ caliber = ".45"
+ max_ammo = 20
+
+/obj/item/ammo_magazine/tommymag/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/tommydrum
+ name = "tommygun drum magazine (.45)"
+ icon_state = "tommy-drum"
+ w_class = 3 // Bulky ammo doesn't fit in your pockets!
+ mag_type = MAGAZINE
+ ammo_type = /obj/item/ammo_casing/c45
+ matter = list(DEFAULT_WALL_MATERIAL = 3750)
+ caliber = ".45"
+ max_ammo = 50
+
+/obj/item/ammo_magazine/tommydrum/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/clip/c45
+ name = "ammo clip (.45)"
+ icon_state = "clip_pistol"
+ desc = "A stripper clip for reloading .45 rounds into magazines."
+ caliber = ".45"
+ ammo_type = /obj/item/ammo_casing/c45
+ matter = list(DEFAULT_WALL_MATERIAL = 675) // metal costs very roughly based around one .45 casing = 75 metal
+ max_ammo = 9
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/clip/c45/rubber
+ name = "ammo clip (.45 rubber)"
+ ammo_type = /obj/item/ammo_casing/c45r
+
+/obj/item/ammo_magazine/clip/c45/practice
+ name = "ammo clip (.45 practice)"
+ ammo_type = /obj/item/ammo_casing/c45p
+
+/obj/item/ammo_magazine/clip/c45/flash
+ name = "ammo clip (.45 flash)"
+ ammo_type = /obj/item/ammo_casing/c45f
+
+///////// 9mm /////////
/obj/item/ammo_magazine/mc9mm
name = "magazine (9mm)"
icon_state = "9x19p"
origin_tech = list(TECH_COMBAT = 2)
mag_type = MAGAZINE
- matter = list(DEFAULT_WALL_MATERIAL = 600)
+ matter = list(DEFAULT_WALL_MATERIAL = 480)
caliber = "9mm"
ammo_type = /obj/item/ammo_casing/c9mm
- max_ammo = 10
+ max_ammo = 8
multiple_sprites = 1
/obj/item/ammo_magazine/mc9mm/empty
@@ -65,17 +132,13 @@
/obj/item/ammo_magazine/mc9mm/flash
ammo_type = /obj/item/ammo_casing/c9mmf
-/obj/item/ammo_magazine/c9mm
- name = "ammunition Box (9mm)"
- icon_state = "9mm"
- origin_tech = list(TECH_COMBAT = 2)
- matter = list(DEFAULT_WALL_MATERIAL = 1800)
- caliber = "9mm"
- ammo_type = /obj/item/ammo_casing/c9mm
- max_ammo = 30
+/obj/item/ammo_magazine/mc9mm/rubber
+ name = "magazine (9mm rubber)"
+ ammo_type = /obj/item/ammo_casing/c9mmr
-/obj/item/ammo_magazine/c9mm/empty
- initial_ammo = 0
+/obj/item/ammo_magazine/mc9mm/practice
+ name = "magazine (9mm practice)"
+ ammo_type = /obj/item/ammo_casing/c9mmp
/obj/item/ammo_magazine/mc9mmt
name = "top mounted magazine (9mm)"
@@ -94,22 +157,85 @@
name = "top mounted magazine (9mm rubber)"
ammo_type = /obj/item/ammo_casing/c9mmr
+/obj/item/ammo_magazine/mc9mmt/flash
+ name = "top mounted magazine (9mm flash)"
+ ammo_type = /obj/item/ammo_casing/c9mmf
+
/obj/item/ammo_magazine/mc9mmt/practice
name = "top mounted magazine (9mm practice)"
ammo_type = /obj/item/ammo_casing/c9mmp
-/obj/item/ammo_magazine/c45
- name = "ammunition Box (.45)"
+/obj/item/ammo_magazine/p90
+ name = "high capacity top mounted magazine (9mm armor-piercing)"
+ icon_state = "p90"
+ mag_type = MAGAZINE
+ ammo_type = /obj/item/ammo_casing/c9mm/ap
+ matter = list(DEFAULT_WALL_MATERIAL = 3000)
+ caliber = "9mm"
+ max_ammo = 50
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/p90/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/clip/c9mm
+ name = "ammo clip (9mm)"
+ icon_state = "clip_pistol"
+ desc = "A stripper clip for reloading 9mm rounds into magazines."
+ caliber = "9mm"
+ ammo_type = /obj/item/ammo_casing/c9mm
+ matter = list(DEFAULT_WALL_MATERIAL = 540) // metal costs are very roughly based around one 9mm casing = 60 metal
+ max_ammo = 9
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/clip/c9mm/rubber
+ name = "ammo clip (.45 rubber)"
+ ammo_type = /obj/item/ammo_casing/c9mmr
+
+/obj/item/ammo_magazine/clip/c9mm/practice
+ name = "ammo clip (.45 practice)"
+ ammo_type = /obj/item/ammo_casing/c9mmp
+
+/obj/item/ammo_magazine/clip/c9mm/flash
+ name = "ammo clip (.45 flash)"
+ ammo_type = /obj/item/ammo_casing/c9mmf
+
+/obj/item/ammo_magazine/c9mm // Made by RnD for Prototype SMG and should probably be removed because why does it require DIAMONDS to make bullets?
+ name = "ammunition Box (9mm)"
icon_state = "9mm"
origin_tech = list(TECH_COMBAT = 2)
- caliber = ".45"
- matter = list(DEFAULT_WALL_MATERIAL = 2250)
- ammo_type = /obj/item/ammo_casing/c45
+ matter = list(DEFAULT_WALL_MATERIAL = 1800)
+ caliber = "9mm"
+ ammo_type = /obj/item/ammo_casing/c9mm
max_ammo = 30
/obj/item/ammo_magazine/c9mm/empty
initial_ammo = 0
+///////// 5mm /////////
+/*
+/obj/item/ammo_magazine/c5mm
+ name = "magazine (5mm)"
+ icon_state = "fiveseven"
+ mag_type = MAGAZINE
+ ammo_type = /obj/item/ammo_casing/c5mm
+ matter = list(DEFAULT_WALL_MATERIAL = 1200)
+ caliber = "5mm"
+ max_ammo = 20
+ //multiple_sprites = 1
+
+/obj/item/ammo_magazine/clip/c5mm
+ name = "ammo clip (5mm)"
+ icon_state = "clip_pistol"
+ desc = "A stripper clip for reloading 5mm rounds into magazines."
+ caliber = "5mm"
+ ammo_type = /obj/item/ammo_casing/c5mm
+ matter = list(DEFAULT_WALL_MATERIAL = 540) // metal costs are very roughly based around one 5mm casing = 60 metal
+ max_ammo = 9
+ multiple_sprites = 1
+*/
+///////// 10mm /////////
+
/obj/item/ammo_magazine/a10mm
name = "magazine (10mm)"
icon_state = "12mm"
@@ -124,6 +250,18 @@
/obj/item/ammo_magazine/a10mm/empty
initial_ammo = 0
+/obj/item/ammo_magazine/clip/a10mm
+ name = "ammo clip (10mm)"
+ icon_state = "clip_pistol"
+ desc = "A stripper clip for reloading 5mm rounds into magazines."
+ caliber = "10mm"
+ ammo_type = /obj/item/ammo_casing/a10mm
+ matter = list(DEFAULT_WALL_MATERIAL = 675) // metal costs are very roughly based around one 10mm casing = 75 metal
+ max_ammo = 9
+ multiple_sprites = 1
+
+///////// 5.56mm /////////
+
/obj/item/ammo_magazine/a556
name = "magazine (5.56mm)"
icon_state = "5.56"
@@ -146,8 +284,49 @@
name = "magazine (5.56mm armor-piercing)"
ammo_type = /obj/item/ammo_casing/a556/ap
+/obj/item/ammo_magazine/a556m
+ name = "20rnd magazine (5.56mm)"
+ icon_state = "5.56mid"
+ origin_tech = list(TECH_COMBAT = 2)
+ mag_type = MAGAZINE
+ caliber = "a556"
+ matter = list(DEFAULT_WALL_MATERIAL = 3600)
+ ammo_type = /obj/item/ammo_casing/a556
+ max_ammo = 20
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/a556m/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/a556m/ap
+ name = "20rnd magazine (5.56mm armor-piercing)"
+ ammo_type = /obj/item/ammo_casing/a556/ap
+
+/obj/item/ammo_magazine/a556m/practice
+ name = "20rnd magazine (5.56mm practice)"
+ ammo_type = /obj/item/ammo_casing/a556p
+
+/obj/item/ammo_magazine/clip/a556
+ name = "ammo clip (5.56mm)"
+ icon_state = "clip_rifle"
+ caliber = "a556"
+ ammo_type = /obj/item/ammo_casing/a556
+ matter = list(DEFAULT_WALL_MATERIAL = 450) // metal costs are very roughly based around one 10mm casing = 180 metal
+ max_ammo = 5
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/clip/a556/ap
+ name = "rifle clip (5.56mm armor-piercing)"
+ ammo_type = /obj/item/ammo_casing/a556/ap
+
+/obj/item/ammo_magazine/clip/a556/practice
+ name = "rifle clip (5.56mm practice)"
+ ammo_type = /obj/item/ammo_casing/a556
+
+///////// .50 AE /////////
+
/obj/item/ammo_magazine/a50
- name = "magazine (.50)"
+ name = "magazine (.50 AE)"
icon_state = "50ae"
origin_tech = list(TECH_COMBAT = 2)
mag_type = MAGAZINE
@@ -160,17 +339,17 @@
/obj/item/ammo_magazine/a50/empty
initial_ammo = 0
-/obj/item/ammo_magazine/a75
- name = "ammo magazine (20mm)"
- icon_state = "75"
- mag_type = MAGAZINE
- caliber = "75"
- ammo_type = /obj/item/ammo_casing/a75
+/obj/item/ammo_magazine/clip/a50
+ name = "ammo clip (.50 AE)"
+ icon_state = "clip_pistol"
+ desc = "A stripper clip for reloading .50 Action Express rounds into magazines."
+ caliber = ".50"
+ ammo_type = /obj/item/ammo_casing/a50
+ matter = list(DEFAULT_WALL_MATERIAL = 1620) // metal costs are very roughly based around one .50 casing = 180 metal
+ max_ammo = 9
multiple_sprites = 1
- max_ammo = 4
-/obj/item/ammo_magazine/a75/empty
- initial_ammo = 0
+///////// 7.62mm /////////
/obj/item/ammo_magazine/a762
name = "magazine box (7.62mm)"
@@ -178,8 +357,9 @@
origin_tech = list(TECH_COMBAT = 2)
mag_type = MAGAZINE
caliber = "a762"
- matter = list(DEFAULT_WALL_MATERIAL = 4500)
+ matter = list(DEFAULT_WALL_MATERIAL = 10000)
ammo_type = /obj/item/ammo_casing/a762
+ w_class = 3 // This should NOT fit in your pocket!!
max_ammo = 50
multiple_sprites = 1
@@ -195,7 +375,7 @@
icon_state = "c762"
mag_type = MAGAZINE
caliber = "a762"
- matter = list(DEFAULT_WALL_MATERIAL = 1800)
+ matter = list(DEFAULT_WALL_MATERIAL = 4000)
ammo_type = /obj/item/ammo_casing/a762
max_ammo = 20
multiple_sprites = 1
@@ -204,19 +384,48 @@
name = "magazine (7.62mm armor-piercing)"
ammo_type = /obj/item/ammo_casing/a762/ap
-/obj/item/ammo_magazine/caps
- name = "speed loader (caps)"
- icon_state = "T38"
- caliber = "caps"
- color = "#FF0000"
- ammo_type = /obj/item/ammo_casing/cap
- matter = list(DEFAULT_WALL_MATERIAL = 600)
- max_ammo = 7
+/obj/item/ammo_magazine/c762/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/s762 // 's' for small!
+ name = "magazine (7.62mm)"
+ icon_state = "SVD"
+ mag_type = MAGAZINE
+ caliber = "a762"
+ matter = list(DEFAULT_WALL_MATERIAL = 2000)
+ ammo_type = /obj/item/ammo_casing/a762
+ max_ammo = 10
multiple_sprites = 1
+/obj/item/ammo_magazine/s762/empty
+ initial_ammo = 0
+
+/obj/item/ammo_magazine/s762/ap
+ name = "magazine (7.62mm armor-piercing)"
+ ammo_type = /obj/item/ammo_casing/a762/ap
+
+/obj/item/ammo_magazine/clip/a762
+ name = "ammo clip (7.62mm)"
+ icon_state = "clip_rifle"
+ caliber = "a762"
+ ammo_type = /obj/item/ammo_casing/a762
+ matter = list(DEFAULT_WALL_MATERIAL = 1000) // metal costs are very roughly based around one 7.62 casing = 200 metal
+ max_ammo = 5
+ multiple_sprites = 1
+
+/obj/item/ammo_magazine/clip/a762/ap
+ name = "rifle clip (7.62mm armor-piercing)"
+ ammo_type = /obj/item/ammo_casing/a762/ap
+
+/obj/item/ammo_magazine/clip/a762/practice
+ name = "rifle clip (7.62mm practice)"
+ ammo_type = /obj/item/ammo_casing/a762p
+
+///////// 12g /////////
+
/obj/item/ammo_magazine/g12
name = "magazine (12 gauge)"
- icon_state = "g12"
+ icon_state = "12g"
mag_type = MAGAZINE
caliber = "shotgun"
matter = list(DEFAULT_WALL_MATERIAL = 2200)
@@ -237,4 +446,30 @@
ammo_type = /obj/item/ammo_casing/shotgun/flash
/obj/item/ammo_magazine/g12/empty
- initial_ammo = 0
\ No newline at end of file
+ initial_ammo = 0
+
+///////// .75 Gyrojet /////////
+
+/obj/item/ammo_magazine/a75
+ name = "ammo magazine (.75 Gyrojet)"
+ icon_state = "75"
+ mag_type = MAGAZINE
+ caliber = "75"
+ ammo_type = /obj/item/ammo_casing/a75
+ multiple_sprites = 1
+ max_ammo = 4
+
+/obj/item/ammo_magazine/a75/empty
+ initial_ammo = 0
+
+///////// Misc. /////////
+
+/obj/item/ammo_magazine/caps
+ name = "speedloader (caps)"
+ icon_state = "T38"
+ caliber = "caps"
+ color = "#FF0000"
+ ammo_type = /obj/item/ammo_casing/cap
+ matter = list(DEFAULT_WALL_MATERIAL = 600)
+ max_ammo = 7
+ multiple_sprites = 1
\ No newline at end of file
diff --git a/code/modules/projectiles/ammunition/bullets.dm b/code/modules/projectiles/ammunition/bullets.dm
index 6050fb2c65..d10280854f 100644
--- a/code/modules/projectiles/ammunition/bullets.dm
+++ b/code/modules/projectiles/ammunition/bullets.dm
@@ -9,7 +9,7 @@
projectile_type = /obj/item/projectile/bullet/pistol/strong
/obj/item/ammo_casing/a75
- desc = "A 20mm bullet casing."
+ desc = "A .75 gyrojet rocket sheathe."
caliber = "75"
projectile_type = /obj/item/projectile/bullet/gyro
@@ -21,6 +21,7 @@
/obj/item/ammo_casing/c38r
desc = "A .38 rubber bullet casing."
caliber = "38"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/pistol/rubber
/obj/item/ammo_casing/c9mm
@@ -28,21 +29,34 @@
caliber = "9mm"
projectile_type = /obj/item/projectile/bullet/pistol
+/obj/item/ammo_casing/c9mm/ap
+ desc = "A 9mm armor-piercing bullet casing."
+ projectile_type = /obj/item/projectile/bullet/pistol/ap
+
/obj/item/ammo_casing/c9mmf
desc = "A 9mm flash shell casing."
caliber = "9mm"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/energy/flash
/obj/item/ammo_casing/c9mmr
desc = "A 9mm rubber bullet casing."
caliber = "9mm"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/pistol/rubber
/obj/item/ammo_casing/c9mmp
desc = "A 9mm practice bullet casing."
caliber = "9mm"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/pistol/practice
+/*
+/obj/item/ammo_casing/c5mm
+ desc = "A 5mm bullet casing."
+ caliber = "5mm"
+ projectile_type = /obj/item/projectile/bullet/pistol/ap
+*/
/obj/item/ammo_casing/c45
desc = "A .45 bullet casing."
@@ -52,16 +66,19 @@
/obj/item/ammo_casing/c45p
desc = "A .45 practice bullet casing."
caliber = ".45"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/pistol/practice
/obj/item/ammo_casing/c45r
desc = "A .45 rubber bullet casing."
caliber = ".45"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/pistol/rubber
/obj/item/ammo_casing/c45f
desc = "A .45 flash shell casing."
caliber = ".45"
+ icon_state = "r-casing"
projectile_type = /obj/item/projectile/energy/flash
/obj/item/ammo_casing/a10mm
@@ -112,7 +129,6 @@
name = "stun shell"
desc = "A 12 gauge taser cartridge."
icon_state = "stunshell"
- spent_icon = "stunshell-spent"
projectile_type = /obj/item/projectile/energy/electrode/stunshot
matter = list(DEFAULT_WALL_MATERIAL = 360, "glass" = 720)
@@ -131,18 +147,27 @@
/obj/item/ammo_casing/a762
desc = "A 7.62mm bullet casing."
caliber = "a762"
+ icon_state = "rifle-casing"
projectile_type = /obj/item/projectile/bullet/rifle/a762
/obj/item/ammo_casing/a762/ap
desc = "A 7.62mm armor-piercing bullet casing."
- caliber = "a762"
projectile_type = /obj/item/projectile/bullet/rifle/a762/ap
+/obj/item/ammo_casing/a762p
+ desc = "A 7.62mm practice bullet casing."
+ caliber = "a762"
+ icon_state = "rifle-casing" // Need to make an icon for these
+ projectile_type = /obj/item/projectile/bullet/rifle/practice
+
+/obj/item/ammo_casing/a762/blank
+ desc = "A blank 7.62mm bullet casing."
+ projectile_type = /obj/item/projectile/bullet/blank
+ matter = list(DEFAULT_WALL_MATERIAL = 90)
+
/obj/item/ammo_casing/a145
- name = "shell casing"
desc = "A 14.5mm shell."
icon_state = "lcasing"
- spent_icon = "lcasing-spent"
caliber = "14.5mm"
projectile_type = /obj/item/projectile/bullet/rifle/a145
matter = list(DEFAULT_WALL_MATERIAL = 1250)
@@ -150,17 +175,18 @@
/obj/item/ammo_casing/a556
desc = "A 5.56mm bullet casing."
caliber = "a556"
+ icon_state = "rifle-casing"
projectile_type = /obj/item/projectile/bullet/rifle/a556
/obj/item/ammo_casing/a556/ap
desc = "A 5.56mm armor-piercing bullet casing."
- caliber = "a556"
projectile_type = /obj/item/projectile/bullet/rifle/a556/ap
/obj/item/ammo_casing/a556p
desc = "A 5.56mm practice bullet casing."
caliber = "a556"
- projectile_type = /obj/item/projectile/bullet/rifle/a556/practice
+ icon_state = "rifle-casing" // Need to make an icon for these
+ projectile_type = /obj/item/projectile/bullet/rifle/practice
/obj/item/ammo_casing/rocket
name = "rocket shell"
@@ -173,5 +199,11 @@
name = "cap"
desc = "A cap for children toys."
caliber = "caps"
+ icon_state = "r-casing"
color = "#FF0000"
projectile_type = /obj/item/projectile/bullet/pistol/cap
+
+/obj/item/ammo_casing/spent // For simple hostile mobs only, so they don't cough up usable bullets when firing. This is for literally nothing else.
+ icon_state = "s-casing-spent"
+ BB = null
+ projectile_type = null
\ No newline at end of file
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 92066ae899..2db78ccd86 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -82,6 +82,13 @@
var/tmp/told_cant_shoot = 0 //So that it doesn't spam them with the fact they cannot hit them.
var/tmp/lock_time = -100
+ var/dna_lock = 0 //whether or not the gun is locked to dna
+ var/list/stored_dna = list() //list of the dna stored in the gun, used to allow users to use it or not
+ var/safety_level = 0 //either 0 or 1, at 0 the game buzzes and tells the user they can't use it, at 1 it self destructs after 10 seconds
+ var/controller_dna = null //The dna of the person who is the primary controller of the gun
+ var/controller_lock = 0 //whether or not the gun is locked by the primar controller, 0 or 1, at 1 it is locked and does not allow
+ var/exploding = 0
+
/obj/item/weapon/gun/New()
..()
for(var/i in 1 to firemodes.len)
@@ -90,6 +97,11 @@
if(isnull(scoped_accuracy))
scoped_accuracy = accuracy
+ if(!dna_lock)
+ verbs -= /obj/item/weapon/gun/verb/remove_dna
+ verbs -= /obj/item/weapon/gun/verb/give_dna
+ verbs -= /obj/item/weapon/gun/verb/allow_dna
+
/obj/item/weapon/gun/update_held_icon()
if(requires_two_hands)
var/mob/living/M = loc
@@ -114,6 +126,20 @@
return 0
var/mob/living/M = user
+ if(dna_lock && stored_dna)
+ if(!authorized_user(user))
+ if(safety_level == 0)
+ M << "\The [src] buzzes in dissapoint and displays an invalid DNA symbol."
+ return 0
+ if(!exploding)
+ if(safety_level == 1)
+ M << "\The [src] hisses in dissapointment."
+ visible_message("\The [src] announces, \"Self-destruct occurring in ten seconds.\"", "\The [src] announces, \"Self-destruct occurring in ten seconds.\"")
+ sleep(100)
+ explosion(src, -1, 0, 2, 3, 0)
+ exploding = 1
+ qdel(src)
+ return 0
if(HULK in M.mutations)
M << "Your fingers are much too large for the trigger guard!"
return 0
@@ -426,4 +452,78 @@
return new_mode
/obj/item/weapon/gun/attack_self(mob/user)
- switch_firemodes(user)
+ switch_firemodes(user)
+
+/obj/item/weapon/gun/proc/get_dna(mob/user)
+ var/mob/living/M = user
+ if(!controller_lock)
+
+ if(!stored_dna && !(M.dna in stored_dna))
+ M << "\The [src] buzzes and displays a symbol showing the gun already contains your DNA."
+ return 0
+ else
+ stored_dna += M.dna
+ M << "\The [src] pings and a needle flicks out from the grip, taking a DNA sample from you."
+ if(!controller_dna)
+ controller_dna = M.dna
+ M << "\The [src] processes the dna sample and pings, acknowledging you as the primary controller."
+ return 1
+ else
+ M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA samples at this time."
+ return 0
+
+/obj/item/weapon/gun/verb/give_dna()
+ set name = "Give DNA"
+ set category = "Object"
+ set src in usr
+ get_dna(usr)
+
+/obj/item/weapon/gun/proc/clear_dna(mob/user)
+ var/mob/living/M = user
+ if(!controller_lock)
+ if(!authorized_user(M))
+ M << "\The [src] buzzes and displays an invalid user symbol."
+ return 0
+ else
+ stored_dna -= user.dna
+ M << "\The [src] beeps and clears the DNA it has stored."
+ if(M.dna == controller_dna)
+ controller_dna = null
+ M << "\The [src] beeps and removes you as the primary controller."
+ if(controller_lock)
+ controller_lock = 0
+ return 1
+ else
+ M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA modifcation at this time."
+ return 0
+
+/obj/item/weapon/gun/verb/remove_dna()
+ set name = "Remove DNA"
+ set category = "Object"
+ set src in usr
+ clear_dna(usr)
+
+/obj/item/weapon/gun/proc/toggledna(mob/user)
+ var/mob/living/M = user
+ if(authorized_user(M) && user.dna == controller_dna)
+ if(!controller_lock)
+ controller_lock = 1
+ M << "\The [src] beeps and displays a locked symbol, informing you it will no longer allow DNA samples."
+ else
+ controller_lock = 0
+ M << "\The [src] beeps and displays an unlocked symbol, informing you it will now allow DNA samples."
+ else
+ M << "\The [src] buzzes and displays an invalid user symbol."
+
+/obj/item/weapon/gun/verb/allow_dna()
+ set name = "Toggle DNA Samples Allowance"
+ set category = "Object"
+ set src in usr
+ toggledna(usr)
+
+/obj/item/weapon/gun/proc/authorized_user(mob/user)
+ if(!stored_dna || !stored_dna.len)
+ return 1
+ if(!(user.dna in stored_dna))
+ return 0
+ return 1
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 8cd8f72a36..9d0ba4e736 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -90,7 +90,7 @@ obj/item/weapon/gun/energy/retro
standard photonic beams, resulting in an effective 'anti-armor' energy weapon."
icon_state = "xray"
item_state = "xray"
- fire_sound = 'sound/weapons/laser3.ogg'
+ fire_sound = 'sound/weapons/eluger.ogg'
origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2)
projectile_type = /obj/item/projectile/beam/xray
charge_cost = 100
@@ -101,8 +101,8 @@ obj/item/weapon/gun/energy/retro
desc = "The HI DMR 9E is an older design of Hesphaistos Industries. A designated marksman rifle capable of shooting powerful \
ionized beams, this is a weapon to kill from a distance."
icon_state = "sniper"
- item_state = "laser"
- fire_sound = 'sound/weapons/marauder.ogg'
+ item_state = "laser" // placeholder
+ fire_sound = 'sound/weapons/gauss_shoot.ogg'
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 5, TECH_POWER = 4)
projectile_type = /obj/item/projectile/beam/sniper
slot_flags = SLOT_BACK
diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm
index 5e1612a50b..2352cdce28 100644
--- a/code/modules/projectiles/guns/energy/nuclear.dm
+++ b/code/modules/projectiles/guns/energy/nuclear.dm
@@ -23,12 +23,12 @@
/obj/item/weapon/gun/energy/gun/burst
- name = "fm-2t"
+ name = "burst laser"
desc = "The FM-2t is a versatile energy based small arm, capable of switching between stun or kill with a three round burst option for both settings."
icon_state = "fm-2tstun100"
item_state = null //so the human update icon uses the icon_state instead.
fire_sound = 'sound/weapons/Taser.ogg'
- max_shots = 12
+ max_shots = 21 //7 trigger pulls
projectile_type = /obj/item/projectile/beam/stun/weak
origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 2, TECH_ILLEGAL = 3)
@@ -38,10 +38,10 @@
one_handed_penalty = 2
firemodes = list(
- list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun/weak, modifystate="fm-2tstun", fire_sound='sound/weapons/Taser.ogg'),
- list(mode_name="stun 3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), projectile_type=/obj/item/projectile/beam/stun, modifystate="fm-2tstun", fire_sound='sound/weapons/Taser.ogg'),
- list(mode_name="lethal", projectile_type=/obj/item/projectile/beam/weaklaser, modifystate="fm-2tkill", fire_sound='sound/weapons/Laser.ogg'),
- list(mode_name="lethal 3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), projectile_type=/obj/item/projectile/beam, modifystate="fm-2tkill", fire_sound='sound/weapons/Laser.ogg'),
+ list(mode_name="stun", burst=1, projectile_type=/obj/item/projectile/beam/stun/weak, modifystate="fm-2tstun", fire_sound='sound/weapons/Taser.ogg'),
+ list(mode_name="stun burst", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), projectile_type=/obj/item/projectile/beam/stun/weak, modifystate="fm-2tstun", fire_sound='sound/weapons/Taser.ogg'),
+ list(mode_name="lethal", burst=1, projectile_type=/obj/item/projectile/beam/weaklaser, modifystate="fm-2tkill", fire_sound='sound/weapons/Laser.ogg'),
+ list(mode_name="lethal burst", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), projectile_type=/obj/item/projectile/beam/weaklaser, modifystate="fm-2tkill", fire_sound='sound/weapons/Laser.ogg'),
)
/obj/item/weapon/gun/energy/gun/nuclear
diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm
index ab134d3406..3074638bce 100644
--- a/code/modules/projectiles/guns/energy/pulse.dm
+++ b/code/modules/projectiles/guns/energy/pulse.dm
@@ -7,13 +7,14 @@
force = 10
fire_sound='sound/weapons/Laser.ogg'
projectile_type = /obj/item/projectile/beam
+ charge_cost=100
+ max_shots = 20 // This is cut in half by "DESTROY" mode.
sel_mode = 2
- max_shots = 10
-
+
firemodes = list(
- list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun, fire_sound='sound/weapons/Taser.ogg', fire_delay=null, charge_cost=null),
- list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, fire_sound='sound/weapons/Laser.ogg', fire_delay=null, charge_cost=null),
- list(mode_name="DESTROY", projectile_type=/obj/item/projectile/beam/pulse, fire_sound='sound/weapons/pulse.ogg', fire_delay=null, charge_cost=100),
+ list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun, fire_sound='sound/weapons/Taser.ogg', fire_delay=null, charge_cost=100),
+ list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, fire_sound='sound/weapons/Laser.ogg', fire_delay=null, charge_cost=100),
+ list(mode_name="DESTROY", projectile_type=/obj/item/projectile/beam/pulse, fire_sound='sound/weapons/gauss_shoot.ogg', fire_delay=null, charge_cost=200),
)
/obj/item/weapon/gun/energy/pulse_rifle/mounted
@@ -25,7 +26,7 @@
desc = "A heavy-duty, pulse-based energy weapon. Because of its complexity and cost, it is rarely seen in use except by specialists."
cell_type = /obj/item/weapon/cell/super
fire_delay = 25
- fire_sound='sound/weapons/pulse.ogg'
+ fire_sound='sound/weapons/gauss_shoot.ogg'
projectile_type=/obj/item/projectile/beam/pulse
charge_cost=400
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index fab9a08b7a..ef3a74666e 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -19,7 +19,7 @@
/obj/item/weapon/gun/energy/ionrifle/update_icon()
..()
if(power_supply.charge < charge_cost)
- item_state = "ionrifle-empty"
+ item_state = "ionrifle0"
else
item_state = initial(item_state)
diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm
index 547dc702cf..107330c13d 100644
--- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm
@@ -60,6 +60,7 @@
grenades.len--
user.put_in_hands(G)
user.visible_message("[user] removes \a [G] from [src].", "You remove \a [G] from [src].")
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
else
user << "[src] is empty."
@@ -117,6 +118,7 @@
if(chambered)
user.put_in_hands(chambered)
user.visible_message("[user] removes \a [chambered] from [src].", "You remove \a [chambered] from [src].")
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
chambered = null
else
user << "[src] is empty."
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm
index b20e9521e6..5463fe26f0 100644
--- a/code/modules/projectiles/guns/launcher/pneumatic.dm
+++ b/code/modules/projectiles/guns/launcher/pneumatic.dm
@@ -54,6 +54,7 @@
item_storage.remove_from_storage(removing, src.loc)
user.put_in_hands(removing)
user << "You remove [removing] from the hopper."
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
else
user << "There is nothing to remove in \the [src]."
diff --git a/code/modules/projectiles/guns/launcher/rocket.dm b/code/modules/projectiles/guns/launcher/rocket.dm
index 12338ee5c3..300121aef7 100644
--- a/code/modules/projectiles/guns/launcher/rocket.dm
+++ b/code/modules/projectiles/guns/launcher/rocket.dm
@@ -10,8 +10,8 @@
flags = CONDUCT
slot_flags = 0
origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 5)
- fire_sound = 'sound/effects/bang.ogg'
-
+ fire_sound = 'sound/weapons/rpg.ogg'
+
release_force = 15
throw_distance = 30
var/max_rockets = 1
diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm
index 07cb48d784..3cbbd40f34 100644
--- a/code/modules/projectiles/guns/launcher/syringe_gun.dm
+++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm
@@ -31,6 +31,7 @@
/obj/item/weapon/syringe_cartridge/attack_self(mob/user)
if(syringe)
user << "You remove [syringe] from [src]."
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
user.put_in_hands(syringe)
syringe = null
sharp = initial(sharp)
@@ -114,6 +115,7 @@
darts -= C
user.put_in_hands(C)
user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src].")
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
else
..()
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index d032cb3848..bb5168169e 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -24,6 +24,7 @@
//For MAGAZINE guns
var/magazine_type = null //the type of magazine that the gun comes preloaded with
var/obj/item/ammo_magazine/ammo_magazine = null //stored magazine
+ var/allowed_magazines //determines list of which magazines will fit in the gun
var/auto_eject = 0 //if the magazine should automatically eject itself when empty.
var/auto_eject_sound = null
//TODO generalize ammo icon states for guns
@@ -96,9 +97,9 @@
/obj/item/weapon/gun/projectile/proc/load_ammo(var/obj/item/A, mob/user)
if(istype(A, /obj/item/ammo_magazine))
var/obj/item/ammo_magazine/AM = A
- if(!(load_method & AM.mag_type) || caliber != AM.caliber)
- return //incompatible
-
+ if(!(load_method & AM.mag_type) || caliber != AM.caliber || allowed_magazines && !is_type_in_list(A, allowed_magazines))
+ user << "[AM] won't load into [src]!"
+ return
switch(AM.mag_type)
if(MAGAZINE)
if(ammo_magazine)
@@ -167,6 +168,7 @@
loaded.len--
user.put_in_hands(C)
user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src].")
+ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
else
user << "[src] is empty."
update_icon()
diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm
index 975f48ae7b..2ed89336a9 100644
--- a/code/modules/projectiles/guns/projectile/automatic.dm
+++ b/code/modules/projectiles/guns/projectile/automatic.dm
@@ -17,21 +17,10 @@
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
- list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
// list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1,-2,-2), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2)),
)
-/obj/item/weapon/gun/projectile/automatic/mini_uzi
- name = "\improper Uzi"
- desc = "The UZI is a lightweight, fast firing gun. For when you want someone dead. Uses .45 rounds."
- icon_state = "mini-uzi"
- w_class = 3
- load_method = SPEEDLOADER //yup. until someone sprites a magazine for it.
- max_shells = 15
- caliber = ".45"
- origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2, TECH_ILLEGAL = 8)
- ammo_type = /obj/item/ammo_casing/c45
-
/obj/item/weapon/gun/projectile/automatic/c20r
name = "submachine gun"
desc = "The C-20r is a lightweight and rapid firing SMG, for when you REALLY need someone dead. Uses 10mm rounds. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp."
@@ -45,6 +34,7 @@
fire_sound = 'sound/weapons/Gunshot_light.ogg'
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/a10mm
+ allowed_magazines = list(/obj/item/ammo_magazine/a10mm)
auto_eject = 1
auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg'
@@ -71,23 +61,27 @@
slot_flags = SLOT_BACK
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/c762
+ allowed_magazines = list(/obj/item/ammo_magazine/c762, /obj/item/ammo_magazine/s762)
one_handed_penalty = 4
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
- list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, burst_accuracy=list(0,-1,-2), dispersion=list(0.0, 0.6, 0.6)),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, burst_accuracy=list(0,-1,-2), dispersion=list(0.0, 0.6, 0.6))
// list(mode_name="short bursts", burst=5, fire_delay=null, move_delay=6, burst_accuracy=list(0,-1,-2,-2,-3), dispersion=list(0.6, 1.0, 1.0, 1.0, 1.2)),
)
/obj/item/weapon/gun/projectile/automatic/sts35/update_icon(var/ignore_inhands)
..()
- icon_state = (ammo_magazine)? "arifle" : "arifle-empty"
+ if(istype(ammo_magazine,/obj/item/ammo_magazine/s762))
+ icon_state = "arifle-small"
+ else
+ icon_state = (ammo_magazine)? "arifle" : "arifle-empty"
if(!ignore_inhands) update_held_icon()
/obj/item/weapon/gun/projectile/automatic/wt550
name = "machine pistol"
- desc = "The W-T 550 Saber is a cheap self-defense weapon, mass-produced by Ward-Takahashi for paramilitary and private use. Uses 9mm rounds."
+ desc = "The WT550 Saber is a cheap self-defense weapon mass-produced by Ward-Takahashi for paramilitary and private use. Uses 9mm rounds."
icon_state = "wt550"
item_state = "wt550"
w_class = 3
@@ -98,6 +92,7 @@
fire_sound = 'sound/weapons/Gunshot_light.ogg'
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/mc9mmt/rubber
+ allowed_magazines = list(/obj/item/ammo_magazine/mc9mmt)
/obj/item/weapon/gun/projectile/automatic/wt550/update_icon()
..()
@@ -108,19 +103,20 @@
return
/obj/item/weapon/gun/projectile/automatic/z8
- name = "bullpup assault rifle"
- desc = "The Z8 Bulldog is an older model bullpup carbine, made by the now defunct Zendai Foundries. Uses armor piercing 5.56mm rounds. Makes you feel like a space marine when you hold it."
- icon_state = "carbine"
+ name = "designated marksman rifle"
+ desc = "The Z8 Bulldog is an older model designated marksman rifle, made by the now defunct Zendai Foundries. Makes you feel like a space marine when you hold it, even though it can only hold 10 round magazines. Uses 5.56mm rounds and has an under barrel grenade launcher."
+ icon_state = "carbine" // This isn't a carbine. :T
item_state = "z8carbine"
w_class = 4
force = 10
caliber = "a556"
origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 3)
- ammo_type = "/obj/item/ammo_casing/a556"
+ ammo_type = "/obj/item/ammo_casing/a556" // Is this really needed anymore?
fire_sound = 'sound/weapons/Gunshot.ogg'
slot_flags = SLOT_BACK
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/a556
+ allowed_magazines = list(/obj/item/ammo_magazine/a556)
auto_eject = 1
auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg'
@@ -180,7 +176,7 @@
name = "light machine gun"
desc = "A rather traditionally made L6 SAW with a pleasantly lacquered wooden pistol grip. Has 'Aussec Armoury- 2531' engraved on the reciever"
icon_state = "l6closed100"
- item_state = "l6closedmag"
+ item_state = "l6closed"
w_class = 4
force = 10
slot_flags = 0
@@ -188,17 +184,18 @@
caliber = "a762"
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 1, TECH_ILLEGAL = 2)
slot_flags = SLOT_BACK
- ammo_type = "/obj/item/ammo_casing/a762"
- fire_sound = 'sound/weapons/Gunshot_light.ogg'
+ ammo_type = "/obj/item/ammo_casing/a762" // Is this really needed anymore?
+ fire_sound = 'sound/weapons/machinegun.ogg'
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/a762
+ allowed_magazines = list(/obj/item/ammo_magazine/a762, /obj/item/ammo_magazine/c762)
one_handed_penalty = 6
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
- list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
- list(mode_name="short bursts", burst=5, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2)),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0)),
+ list(mode_name="short bursts", burst=5, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2))
)
var/cover_open = 0
@@ -213,6 +210,7 @@
cover_open = !cover_open
user << "You [cover_open ? "open" : "close"] [src]'s cover."
update_icon()
+ update_held_icon()
/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user as mob)
if(cover_open)
@@ -227,7 +225,13 @@
return ..() //once open, behave like normal
/obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon()
- icon_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? round(ammo_magazine.stored_ammo.len, 25) : "-empty"]"
+ if(istype(ammo_magazine,/obj/item/ammo_magazine/c762))
+ icon_state = "l6[cover_open ? "open" : "closed"]mag"
+ item_state = icon_state
+ else
+ icon_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? round(ammo_magazine.stored_ammo.len, 25) : "-empty"]"
+ item_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? "" : "-empty"]"
+ update_held_icon()
/obj/item/weapon/gun/projectile/automatic/l6_saw/load_ammo(var/obj/item/A, mob/user)
if(!cover_open)
@@ -242,23 +246,25 @@
..()
/obj/item/weapon/gun/projectile/automatic/as24
- name = "\improper AS-24 automatic shotgun"
- desc = "A durable, rugged looking automatic weapon of a make popular on the frontier worlds. Uses 12 gauge shells. It is unmarked."
+ name = "automatic shotgun"
+ desc = "The AS-24 is a durable, rugged looking automatic weapon of a make popular on the frontier worlds. Uses 12 gauge shells. It is unmarked."
icon_state = "ashot"
item_state = null
w_class = 4
force = 10
caliber = "shotgun"
+ fire_sound = 'sound/weapons/shotgun.ogg'
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 1, TECH_ILLEGAL = 4)
slot_flags = SLOT_BACK
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/g12
+ allowed_magazines = list(/obj/item/ammo_magazine/g12)
one_handed_penalty = 4
firemodes = list(
list(mode_name="semiauto", burst=1, fire_delay=0),
- list(mode_name="3-round bursts", burst=3, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.0, 0.6, 0.6)),
+ list(mode_name="3-round bursts", burst=3, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.0, 0.6, 0.6))
// list(mode_name="6-round bursts", burst=6, move_delay=6, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2, 1.2)),
)
@@ -270,5 +276,99 @@
icon_state = "ashot"
return
+/obj/item/weapon/gun/projectile/automatic/mini_uzi
+ name = "\improper Uzi"
+ desc = "A lightweight, compact, fast firing gun, for when you want someone really dead. Uses .45 rounds."
+ icon_state = "mini-uzi"
+ w_class = 3
+ load_method = MAGAZINE
+ caliber = ".45"
+ origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2, TECH_ILLEGAL = 5)
+ magazine_type = /obj/item/ammo_magazine/c45uzi
+ allowed_magazines = list(/obj/item/ammo_magazine/c45uzi)
+ firemodes = list(
+ list(mode_name="semiauto", burst=1, fire_delay=0),
+ list(mode_name="3-round bursts", burst=3, burst_delay=1, fire_delay=4, move_delay=4, burst_accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0))
+ )
+/obj/item/weapon/gun/projectile/automatic/mini_uzi/update_icon()
+ ..()
+ if(ammo_magazine)
+ icon_state = "mini-uzi"
+ else
+ icon_state = "mini-uzi-empty"
+
+/obj/item/weapon/gun/projectile/automatic/p90
+ name = "personal defense weapon"
+ desc = "The H90K is a compact, high capacity submachine gun produced by Hephaistos Industries. Despite its fierce reputation, it still manages to feel like a toy. Uses 9mm rounds."
+ icon_state = "p90smg"
+ item_state = "p90"
+ w_class = 3
+ caliber = "9mm"
+ origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2)
+ slot_flags = SLOT_BELT // ToDo: Belt sprite.
+ fire_sound = 'sound/weapons/Gunshot_light.ogg'
+ load_method = MAGAZINE
+ magazine_type = /obj/item/ammo_magazine/p90
+ allowed_magazines = list(/obj/item/ammo_magazine/p90, /obj/item/ammo_magazine/mc9mmt) // ToDo: New sprite for the different mag.
+
+ firemodes = list(
+ list(mode_name="semiauto", burst=1, fire_delay=0),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
+ )
+
+/obj/item/weapon/gun/projectile/automatic/p90/update_icon()
+ icon_state = "p90smg-[ammo_magazine ? round(ammo_magazine.stored_ammo.len, 6) : "empty"]"
+
+/obj/item/weapon/gun/projectile/automatic/tommygun
+ name = "\improper Tommygun"
+ desc = "This weapon was made famous by gangsters in the 20th century. Cybersun Industries is currently reproducing these for a target market of historic gun collectors and classy criminals. Uses .45 rounds."
+ icon_state = "tommygun"
+ w_class = 3
+ caliber = ".45"
+ origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2, TECH_ILLEGAL = 5)
+ slot_flags = SLOT_BELT // ToDo: Belt sprite.
+ load_method = MAGAZINE
+ magazine_type = /obj/item/ammo_magazine/tommymag
+ allowed_magazines = list(/obj/item/ammo_magazine/tommymag, /obj/item/ammo_magazine/tommydrum)
+
+ firemodes = list(
+ list(mode_name="semiauto", burst=1, fire_delay=0),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0))
+ )
+
+/obj/item/weapon/gun/projectile/automatic/tommygun/update_icon()
+ ..()
+ icon_state = (ammo_magazine)? "tommygun" : "tommygun-empty"
+// update_held_icon()
+
+/obj/item/weapon/gun/projectile/automatic/carbine
+ name = "assault carbine"
+ desc = "The bullpup configured GP3000 is a lightweight, compact, military-grade assault rifle produced by Gurov Projectile Weapons LLC. It is sold almost exclusively to standing armies. The serial number on this one has been scratched off. Uses 5.56mm rounds."
+ icon_state = "bullpupm"
+ item_state = "bullpup"
+ w_class = 4
+ force = 10
+ caliber = "a556"
+ origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 1, TECH_ILLEGAL = 4)
+ slot_flags = SLOT_BACK
+ load_method = MAGAZINE
+ magazine_type = /obj/item/ammo_magazine/a556m
+ allowed_magazines = list(/obj/item/ammo_magazine/a556, /obj/item/ammo_magazine/a556m)
+
+ one_handed_penalty = 4
+
+ firemodes = list(
+ list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null),
+ list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, burst_accuracy=list(0,-1,-2), dispersion=list(0.0, 0.6, 0.6))
+ )
+
+/obj/item/weapon/gun/projectile/automatic/carbine/update_icon(var/ignore_inhands)
+ ..()
+ if(istype(ammo_magazine,/obj/item/ammo_magazine/a556m))
+ icon_state = "bullpupm"
+ else
+ icon_state = (ammo_magazine)? "bullpup" : "bullpup-empty"
+ item_state = (ammo_magazine)? "bullpup" : "bullpup-empty"
+ if(!ignore_inhands) update_held_icon()
diff --git a/code/modules/projectiles/guns/projectile/boltaction.dm b/code/modules/projectiles/guns/projectile/boltaction.dm
new file mode 100644
index 0000000000..28da9997d2
--- /dev/null
+++ b/code/modules/projectiles/guns/projectile/boltaction.dm
@@ -0,0 +1,52 @@
+// For all intents and purposes, these work exactly the same as pump shotguns. It's unnecessary to make their own procs for them.
+
+/obj/item/weapon/gun/projectile/shotgun/pump/rifle
+ name = "bolt action rifle"
+ desc = "A reproduction of an almost ancient weapon design from the early 20th century. It's still popular among hunters and collectors due to its reliability. Uses 7.62mm rounds."
+ item_state = "boltaction"
+ icon_state = "boltaction"
+ fire_sound = 'sound/weapons/rifleshot.ogg'
+ max_shells = 5
+ caliber = "a762"
+ origin_tech = list(TECH_COMBAT = 1)// Old as shit rifle doesn't have very good tech.
+ ammo_type = /obj/item/ammo_casing/a762
+ load_method = SINGLE_CASING|SPEEDLOADER
+ action_sound = 'sound/weapons/riflebolt.ogg'
+
+/obj/item/weapon/gun/projectile/shotgun/pump/rifle/practice // For target practice
+ desc = "A bolt-action rifle with a lightweight synthetic wood stock, designed for competitive shooting. Comes shipped with practice rounds pre-loaded into the gun. Popular among professional marksmen. Uses 7.62mm rounds."
+ ammo_type = /obj/item/ammo_casing/a762p
+
+/obj/item/weapon/gun/projectile/shotgun/pump/rifle/ceremonial
+ name = "ceremonial bolt-action rifle"
+ desc = "A bolt-action rifle with a heavy, high-quality wood stock that has a beautiful finish. Clearly not intended to be used in combat. Uses 7.62mm rounds."
+ ammo_type = /obj/item/ammo_casing/a762/blank
+
+/obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin
+ name = "\improper Mosin Nagant"
+ desc = "Despite its age, the Mosin Nagant continues to be a favorite weapon among colonists, conscripts, and militias across the cosmos. Most today are built by Chen-Iltchenko Firearms, but it's hard to say who built this particular gun, considering the design has been ripped off by just about every arms manufacturer in the galaxy. Uses 7.62mm rounds."
+ icon_state = "mosin"
+ item_state = "mosin"
+
+// Stole hacky terrible code from doublebarrel shotgun. -Spades
+/obj/item/weapon/gun/projectile/shotgun/pump/rifle/mosin/attackby(var/obj/item/A as obj, mob/user as mob)
+ if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/pickaxe/plasmacutter) && w_class != 3)
+ user << "You begin to shorten the barrel and stock of \the [src]."
+ if(loaded.len)
+ afterattack(user, user) //will this work? //it will. we call it twice, for twice the FUN
+ playsound(user, fire_sound, 50, 1)
+ user.visible_message("[src] goes off!", "The rifle goes off in your face!")
+ return
+ if(do_after(user, 30))
+ icon_state = "obrez"
+ w_class = 3
+ recoil = 2 // Owch
+ accuracy = -1 // You know damn well why.
+ item_state = "gun"
+ slot_flags &= ~SLOT_BACK //you can't sling it on your back
+ slot_flags |= (SLOT_BELT|SLOT_HOLSTER) //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) - or in a holster, why not.
+ name = "\improper Obrez"
+ desc = "The firepower of a Mosin, now the size of a pistol, with an effective combat range of about three feet. Uses 7.62mm rounds."
+ user << "You shorten the barrel and stock of \the [src]!"
+ else
+ ..()
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm
index 747a8294b8..79ca041ed5 100644
--- a/code/modules/projectiles/guns/projectile/dartgun.dm
+++ b/code/modules/projectiles/guns/projectile/dartgun.dm
@@ -54,6 +54,7 @@
silenced = 1
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/chemdart
+ allowed_magazines = list(/obj/item/ammo_magazine/chemdart)
auto_eject = 0
var/list/beakers = list() //All containers inside the gun.
diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm
index c3a48cd374..9caf66af4b 100644
--- a/code/modules/projectiles/guns/projectile/pistol.dm
+++ b/code/modules/projectiles/guns/projectile/pistol.dm
@@ -3,10 +3,11 @@
name = ".45 pistol"
desc = "A cheap Martian knock-off of a Colt M1911. Uses .45 rounds."
magazine_type = /obj/item/ammo_magazine/c45m
+ allowed_magazines = list(/obj/item/ammo_magazine/c45m)
icon_state = "colt"
caliber = ".45"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
- fire_sound = 'sound/weapons/Gunshot_light.ogg'
+ fire_sound = 'sound/weapons/semiauto.ogg'
load_method = MAGAZINE
/obj/item/weapon/gun/projectile/colt/detective
@@ -54,6 +55,7 @@
options["NT Mk. 58"] = "secguncomp"
options["NT Mk. 58 Custom"] = "secgundark"
options["Colt M1911"] = "colt"
+ options["FiveSeven"] = "fnseven"
options["USP"] = "usp"
options["H&K VP"] = "VP78"
options["P08 Luger"] = "p08"
@@ -72,7 +74,7 @@
magazine_type = /obj/item/ammo_magazine/c45m/rubber
caliber = ".45"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
- fire_sound = 'sound/weapons/Gunshot_light.ogg'
+ fire_sound = 'sound/weapons/semiauto.ogg'
load_method = MAGAZINE
/obj/item/weapon/gun/projectile/sec/update_icon()
@@ -108,42 +110,62 @@
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ILLEGAL = 8)
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/c45m
+ allowed_magazines = list(/obj/item/ammo_magazine/c45m)
/obj/item/weapon/gun/projectile/deagle
name = "desert eagle"
- desc = "A robust handgun that uses .50 AE ammo"
+ desc = "A robust handgun that uses .50 AE rounds."
icon_state = "deagle"
item_state = "deagle"
force = 14.0
caliber = ".50"
load_method = MAGAZINE
+ fire_sound = 'sound/weapons/deagle.ogg'
magazine_type = /obj/item/ammo_magazine/a50
- auto_eject = 1
+ allowed_magazines = list(/obj/item/ammo_magazine/a50)
/obj/item/weapon/gun/projectile/deagle/gold
- desc = "A gold plated gun folded over a million times by superior martian gunsmiths. Uses .50 AE ammo."
+ desc = "A gold plated gun folded over a million times by superior martian gunsmiths. Uses .50 AE rounds."
icon_state = "deagleg"
item_state = "deagleg"
/obj/item/weapon/gun/projectile/deagle/camo
- desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo."
+ desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE rounds."
icon_state = "deaglecamo"
item_state = "deagleg"
- auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg'
+/*
+/obj/item/weapon/gun/projectile/fiveseven
+ name = "\improper WT-AP57"
+ desc = "This tacticool pistol made by Ward-Takahashi trades stopping power for armor piercing and a high capacity. Uses 5mm rounds."
+ icon_state = "fnseven"
+ origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2)
+ caliber = "5mm"
+ load_method = MAGAZINE
+ fire_sound = 'sound/weapons/semiauto.ogg'
+ magazine_type = /obj/item/ammo_magazine/c5mm
+ allowed_magazines = list(/obj/item/ammo_magazine/c5mm)
+/obj/item/weapon/gun/projectile/fiveseven/update_icon()
+ ..()
+ if(ammo_magazine)
+ icon_state = "fnseven"
+ else
+ icon_state = "fnseven-empty"
+*/
-/obj/item/weapon/gun/projectile/gyropistol
+/obj/item/weapon/gun/projectile/gyropistol // Does this even appear anywhere outside of admin abuse?
name = "gyrojet pistol"
- desc = "A bulky pistol designed to fire self propelled rounds"
+ desc = "Speak softly, and carry a big gun. Fires rare .75 caliber self-propelled exploding bolts--because fuck you and everything around you."
icon_state = "gyropistol"
max_shells = 8
caliber = "75"
- fire_sound = 'sound/effects/Explosion1.ogg'
+ fire_sound = 'sound/weapons/rpg.ogg'
origin_tech = list(TECH_COMBAT = 3)
ammo_type = "/obj/item/ammo_casing/a75"
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/a75
+ allowed_magazines = list(/obj/item/ammo_magazine/a75)
auto_eject = 1
auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg'
@@ -163,9 +185,10 @@
caliber = "9mm"
silenced = 0
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ILLEGAL = 2)
- fire_sound = 'sound/weapons/Gunshot_light.ogg'
+ fire_sound = 'sound/weapons/semiauto.ogg'
load_method = MAGAZINE
magazine_type = /obj/item/ammo_magazine/mc9mm
+ allowed_magazines = list(/obj/item/ammo_magazine/mc9mm)
/obj/item/weapon/gun/projectile/pistol/flash
name = "holdout signal pistol"
@@ -246,3 +269,36 @@
var/obj/item/ammo_casing/ammo = ammo_type
caliber = initial(ammo.caliber)
..()
+
+/obj/item/weapon/gun/projectile/derringer
+ name = "derringer"
+ desc = "It's not size of your gun that matters, just the size of your load. Uses .357 rounds." //OHHH MYYY~
+ icon_state = "derringer"
+ item_state = "concealed"
+ w_class = 2
+ origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ILLEGAL = 3)
+ handle_casings = CYCLE_CASINGS //player has to take the old casing out manually before reloading
+ load_method = SINGLE_CASING
+ max_shells = 2
+ ammo_type = /obj/item/ammo_casing/a357
+
+/obj/item/weapon/gun/projectile/luger
+ name = "\improper P08 Luger"
+ desc = "Not some cheap Scheisse .45 caliber Martian knockoff! This Luger is an authentic reproduction by RauMauser. Accuracy, easy handling, and its signature appearance make it popular among historic gun collectors. Uses 9mm rounds."
+ icon_state = "p08"
+ origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 2)
+ caliber = "9mm"
+ load_method = MAGAZINE
+ fire_sound = 'sound/weapons/semiauto.ogg'
+ magazine_type = /obj/item/ammo_magazine/mc9mm
+ allowed_magazines = list(/obj/item/ammo_magazine/mc9mm)
+
+/obj/item/weapon/gun/projectile/luger/update_icon()
+ ..()
+ if(ammo_magazine)
+ icon_state = "[initial(icon_state)]"
+ else
+ icon_state = "[initial(icon_state)]-e"
+
+/obj/item/weapon/gun/projectile/luger/brown
+ icon_state = "p08b"
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm
index d1e561a509..7de8e7b877 100644
--- a/code/modules/projectiles/guns/projectile/revolver.dm
+++ b/code/modules/projectiles/guns/projectile/revolver.dm
@@ -1,12 +1,12 @@
/obj/item/weapon/gun/projectile/revolver
name = "revolver"
- desc = "The Lumoco Arms HE Colt is a choice revolver for when you absolutely, positively need to put a hole in the other guy. Uses .357 ammo."
+ desc = "The Lumoco Arms HE Colt is a choice revolver for when you absolutely, positively need to put a hole in the other guy. Uses .357 rounds."
icon_state = "revolver"
item_state = "revolver"
caliber = "357"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
handle_casings = CYCLE_CASINGS
- max_shells = 7
+ max_shells = 6
ammo_type = /obj/item/ammo_casing/a357
var/chamber_offset = 0 //how many empty chambers in the cylinder until you hit a round
@@ -35,6 +35,7 @@
/obj/item/weapon/gun/projectile/revolver/mateba
name = "mateba"
+ desc = "This unique looking handgun is named after an Italian company famous for the manufacture of these revolvers, and pasta kneading machines. Uses .357 rounds." // Yes I'm serious. -Spades
icon_state = "mateba"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
@@ -42,7 +43,6 @@
name = "revolver"
desc = "A cheap Martian knock-off of a Smith & Wesson Model 10. Uses .38-Special rounds."
icon_state = "detective"
- max_shells = 6
caliber = "38"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
fire_sound = 'sound/weapons/Gunshot_light.ogg'
@@ -68,10 +68,9 @@
// Blade Runner pistol.
/obj/item/weapon/gun/projectile/revolver/deckard
- name = "Deckard .44"
+ name = "Deckard .38"
desc = "A custom-built revolver, based off the semi-popular Detective Special model."
icon_state = "deckard-empty"
- max_shells = 6
caliber = "38"
origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
fire_sound = 'sound/weapons/Gunshot_light.ogg'
@@ -100,3 +99,15 @@
max_shells = 7
ammo_type = /obj/item/ammo_casing/cap
+/obj/item/weapon/gun/projectile/revolver/judge
+ name = "\"The Judge\""
+ desc = "A revolving hand-shotgun by Cybersun Industries that packs the power of a 12 guage in the palm of your hand (if you don't break your wrist). Uses 12 shotgun rounds."
+ icon_state = "judge"
+ caliber = "shotgun"
+ origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ILLEGAL = 4)
+ max_shells = 5
+ fire_sound = 'sound/weapons/shotgun.ogg'
+ recoil = 2 // ow my fucking hand
+ accuracy = -1 // smooth bore + short barrel = shit accuracy
+ ammo_type = /obj/item/ammo_casing/shotgun
+ // ToDo: Remove accuracy debuf in exchange for slightly injuring your hand every time you fire it.
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index d4da287a71..025aac20d9 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -13,7 +13,9 @@
load_method = SINGLE_CASING
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
handle_casings = HOLD_CASINGS
+ fire_sound = 'sound/weapons/shotgun.ogg'
var/recentpump = 0 // to prevent spammage
+ var/action_sound = 'sound/weapons/shotgunpump.ogg'
/obj/item/weapon/gun/projectile/shotgun/pump/consume_next_projectile()
if(chambered)
@@ -26,7 +28,7 @@
recentpump = world.time
/obj/item/weapon/gun/projectile/shotgun/pump/proc/pump(mob/M as mob)
- playsound(M, 'sound/weapons/shotgunpump.ogg', 60, 1)
+ playsound(M, action_sound, 60, 1)
if(chambered)//We have a shell in the chamber
chambered.loc = get_turf(src)//Eject casing
@@ -65,7 +67,7 @@
caliber = "shotgun"
origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 1)
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
-
+
burst_delay = 0
firemodes = list(
list(mode_name="fire one barrel at a time", burst=1),
diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm
index e03ff73988..d69a97f2f4 100644
--- a/code/modules/projectiles/guns/projectile/sniper.dm
+++ b/code/modules/projectiles/guns/projectile/sniper.dm
@@ -1,15 +1,17 @@
+////////////// PTR-7 Anti-Materiel Rifle //////////////
+
/obj/item/weapon/gun/projectile/heavysniper
name = "anti-materiel rifle"
desc = "A portable anti-armour rifle fitted with a scope, the HI PTR-7 Rifle was originally designed to used against armoured exosuits. It is capable of punching through windows and non-reinforced walls with ease. Fires armor piercing 14.5mm shells."
icon_state = "heavysniper"
- item_state = "l6closednomag" //placeholder
+ item_state = "l6closed-empty" // placeholder
w_class = 5 // So it can't fit in a backpack.
force = 10
slot_flags = SLOT_BACK
origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 2, TECH_ILLEGAL = 8)
caliber = "14.5mm"
- recoil = 2 //extra kickback
- //fire_sound = 'sound/weapons/sniper.ogg'
+ recoil = 3 //extra kickback
+ fire_sound = 'sound/weapons/sniper.ogg' // extra boom
handle_casings = HOLD_CASINGS
load_method = SINGLE_CASING
max_shells = 1
@@ -65,3 +67,41 @@
toggle_scope(2.0)
+////////////// Dragunov Sniper Rifle //////////////
+
+/* // Commented out until it's not worthless. Also might be nice to have a new icon that looks more sci-fi Dragunov-ish.
+/obj/item/weapon/gun/projectile/SVD
+ name = "\improper Dragunov"
+ desc = "The SVD, also known as the Dragunov, was mass produced with an Optical Sniper Sight so simple that even Ivan can figure out how it works. Too bad for you that it's written in Russian. Uses 7.62mm rounds."
+ icon_state = "SVD"
+ item_state = "SVD"
+ w_class = 5 // So it can't fit in a backpack.
+ force = 10
+ slot_flags = SLOT_BACK // Needs a sprite.
+ origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 2, TECH_ILLEGAL = 8)
+ recoil = 2 //extra kickback
+ caliber = "a762"
+ load_method = MAGAZINE
+ accuracy = -3 //shooting at the hip
+ scoped_accuracy = 0
+// requires_two_hands = 1
+ one_handed_penalty = 4 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand.
+ fire_sound = 'sound/weapons/SVD_shot.ogg'
+ magazine_type = /obj/item/ammo_magazine/SVD
+ allowed_magazines = list(/obj/item/ammo_magazine/SVD, /obj/item/ammo_magazine/c762)
+
+/obj/item/weapon/gun/projectile/SVD/update_icon()
+ ..()
+// if(istype(ammo_magazine,/obj/item/ammo_magazine/c762)
+// icon_state = "SVD-bigmag" //No icon for this exists yet.
+ if(ammo_magazine)
+ icon_state = "SVD"
+ else
+ icon_state = "SVD-empty"
+
+/obj/item/weapon/gun/projectile/SVD/verb/scope()
+ set category = "Object"
+ set name = "Use Scope"
+ set popup_menu = 1
+
+ toggle_scope(2.0)*/
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index ca2ab63eef..8bbd31aed0 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -42,7 +42,7 @@
damage *= 0.7 //squishy mobs absorb KE
return 1
- var/chance = 0
+ var/chance = damage
if(istype(A, /turf/simulated/wall))
var/turf/simulated/wall/W = A
chance = round(damage/W.material.integrity*180)
@@ -52,8 +52,6 @@
if(D.glass) chance *= 2
else if(istype(A, /obj/structure/girder))
chance = 100
- else if(istype(A, /obj/machinery) || istype(A, /obj/structure))
- chance = damage
if(prob(chance))
if(A.opacity)
@@ -128,6 +126,10 @@
/obj/item/projectile/bullet/pistol
damage = 20
+/obj/item/projectile/bullet/pistol/ap
+ damage = 20
+ armor_penetration = 30
+
/obj/item/projectile/bullet/pistol/medium
damage = 25
@@ -227,8 +229,9 @@
/obj/item/projectile/bullet/pistol/practice
damage = 5
-/obj/item/projectile/bullet/rifle/a556/practice
+/obj/item/projectile/bullet/rifle/practice
damage = 5
+ penetrating = 0
/obj/item/projectile/bullet/shotgun/practice
name = "practice"
diff --git a/code/modules/reagents/reagent_containers/food/lunch.dm b/code/modules/reagents/reagent_containers/food/lunch.dm
index edb4e3b890..794d7f69dd 100644
--- a/code/modules/reagents/reagent_containers/food/lunch.dm
+++ b/code/modules/reagents/reagent_containers/food/lunch.dm
@@ -107,7 +107,7 @@ var/list/lunchables_ethanol_reagents_ = list(/datum/reagent/ethanol/acid_spit,
/proc/init_lunchable_reagent_list(var/list/banned_reagents, var/reagent_types)
. = list()
- for(var/reagent_type in subtypes(reagent_types))
+ for(var/reagent_type in subtypesof(reagent_types))
if(reagent_type in banned_reagents)
continue
var/datum/reagent/reagent = reagent_type
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index d813e71329..6296a8b951 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -38,8 +38,6 @@
operating = 1
setmove()
-/obj/machinery/conveyor/map/New()
- ..()
circuit = new circuit(src)
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/gear(src)
diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm
index 1a5b1b3066..e444bcb8c1 100644
--- a/code/modules/research/circuitprinter.dm
+++ b/code/modules/research/circuitprinter.dm
@@ -22,7 +22,7 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid).
idle_power_usage = 30
active_power_usage = 2500
-/obj/machinery/r_n_d/circuit_imprinter/map/New()
+/obj/machinery/r_n_d/circuit_imprinter/New()
..()
circuit = new circuit()
component_parts = list()
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index 4738d89f95..c0ecdbfc3d 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -206,6 +206,60 @@ other types of metals and chemistry for reagents).
build_path = /obj/item/weapon/storage/part_replacer
sort_string = "CBAAA"
+/datum/design/item/powercell
+ build_type = PROTOLATHE | MECHFAB
+
+/datum/design/item/powercell/AssembleDesignName()
+ name = "Power cell model ([item_name])"
+
+/datum/design/item/powercell/AssembleDesignDesc()
+ if(build_path)
+ var/obj/item/weapon/cell/C = build_path
+ desc = "Allows the construction of power cells that can hold [initial(C.maxcharge)] units of energy."
+
+/datum/design/item/powercell/Fabricate()
+ var/obj/item/weapon/cell/C = ..()
+ C.charge = 0 //shouldn't produce power out of thin air.
+ return C
+
+/datum/design/item/powercell/basic
+ name = "basic"
+ build_type = PROTOLATHE | MECHFAB
+ id = "basic_cell"
+ req_tech = list(TECH_POWER = 1)
+ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 50)
+ build_path = /obj/item/weapon/cell
+ category = "Misc"
+ sort_string = "DAAAA"
+
+/datum/design/item/powercell/high
+ name = "high-capacity"
+ build_type = PROTOLATHE | MECHFAB
+ id = "high_cell"
+ req_tech = list(TECH_POWER = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 60)
+ build_path = /obj/item/weapon/cell/high
+ category = "Misc"
+ sort_string = "DAAAB"
+
+/datum/design/item/powercell/super
+ name = "super-capacity"
+ id = "super_cell"
+ req_tech = list(TECH_POWER = 3, TECH_MATERIAL = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 70)
+ build_path = /obj/item/weapon/cell/super
+ category = "Misc"
+ sort_string = "DAAAC"
+
+/datum/design/item/powercell/hyper
+ name = "hyper-capacity"
+ id = "hyper_cell"
+ req_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4)
+ materials = list(DEFAULT_WALL_MATERIAL = 400, "gold" = 150, "silver" = 150, "glass" = 70)
+ build_path = /obj/item/weapon/cell/hyper
+ category = "Misc"
+ sort_string = "DAAAD"
+
/datum/design/item/hud
materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50)
@@ -673,17 +727,13 @@ CIRCUITS BELOW
/datum/design/circuit/AssembleDesignName()
..()
if(build_path)
- var/obj/item/weapon/circuitboard/C = new build_path()
- if(C && istype(C))
- if(C.board_type == "machine")
- name = "Machine circuit design ([item_name])"
- qdel(C)
- return
- else if(C.board_type == "computer")
- name = "Computer circuit design ([item_name])"
- qdel(C)
- return
- name = "Circuit design ([item_name])"
+ var/obj/item/weapon/circuitboard/C = build_path
+ if(initial(C.board_type) == "machine")
+ name = "Machine circuit design ([item_name])"
+ else if(initial(C.board_type) == "computer")
+ name = "Computer circuit design ([item_name])"
+ else
+ name = "Circuit design ([item_name])"
/datum/design/circuit/AssembleDesignDesc()
if(!desc)
diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm
index 98923bc89d..686a273023 100644
--- a/code/modules/research/destructive_analyzer.dm
+++ b/code/modules/research/destructive_analyzer.dm
@@ -16,7 +16,7 @@ Note: Must be placed within 3 tiles of the R&D Console
idle_power_usage = 30
active_power_usage = 2500
-/obj/machinery/r_n_d/destructive_analyzer/map/New()
+/obj/machinery/r_n_d/destructive_analyzer/New()
..()
circuit = new circuit()
component_parts = list()
diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm
index c12434536b..8c0670e706 100644
--- a/code/modules/research/prosfab_designs.dm
+++ b/code/modules/research/prosfab_designs.dm
@@ -50,7 +50,7 @@
/datum/design/item/prosfab/pros/torso
time = 35
materials = list(DEFAULT_WALL_MATERIAL = 60000, "glass" = 10000, "plasteel" = 2000)
- req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3)
+// req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3) //Saving the values just in case
var/gender = MALE
/datum/design/item/prosfab/pros/torso/male
@@ -73,7 +73,7 @@
build_path = /obj/item/organ/external/head
time = 30
materials = list(DEFAULT_WALL_MATERIAL = 25000, "glass" = 5000, "plasteel" = 1000)
- req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3)
+// req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3) //Saving the values just in case
/datum/design/item/prosfab/pros/l_arm
name = "Prosthetic left arm"
@@ -137,7 +137,7 @@
build_path = /obj/item/organ/internal/cell
time = 15
materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 4000, "plasteel" = 2000)
- req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
+// req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
/datum/design/item/prosfab/pros/eyes
name = "Prosthetic eyes"
@@ -145,7 +145,7 @@
build_path = /obj/item/organ/internal/eyes/robot
time = 15
materials = list(DEFAULT_WALL_MATERIAL = 7500, "glass" = 7500)
- req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
+// req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
//////////////////// Cyborg Parts ////////////////////
/datum/design/item/prosfab/cyborg
diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm
index f13f6b6e66..46b688929d 100644
--- a/code/modules/research/protolathe.dm
+++ b/code/modules/research/protolathe.dm
@@ -17,7 +17,7 @@
materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "gold" = 0, "silver" = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0)
-/obj/machinery/r_n_d/protolathe/map/New()
+/obj/machinery/r_n_d/protolathe/New()
..()
circuit = new circuit()
component_parts = list()
@@ -76,15 +76,8 @@
speed = T / 2
/obj/machinery/r_n_d/protolathe/dismantle()
- for(var/obj/I in component_parts)
- if(istype(I, /obj/item/weapon/reagent_containers/glass/beaker))
- reagents.trans_to_obj(I, reagents.total_volume)
for(var/f in materials)
- if(materials[f] >= SHEET_MATERIAL_AMOUNT)
- var/path = getMaterialType(f)
- if(path)
- var/obj/item/stack/S = new f(loc)
- S.amount = round(materials[f] / SHEET_MATERIAL_AMOUNT)
+ eject_materials(f, -1)
..()
/obj/machinery/r_n_d/protolathe/update_icon()
@@ -203,3 +196,36 @@
if(new_item.matter && new_item.matter.len > 0)
for(var/i in new_item.matter)
new_item.matter[i] = new_item.matter[i] * mat_efficiency
+
+/obj/machinery/r_n_d/protolathe/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
+ var/recursive = amount == -1 ? 1 : 0
+ material = lowertext(material)
+ var/mattype
+ switch(material)
+ if(DEFAULT_WALL_MATERIAL)
+ mattype = /obj/item/stack/material/steel
+ if("glass")
+ mattype = /obj/item/stack/material/glass
+ if("gold")
+ mattype = /obj/item/stack/material/gold
+ if("silver")
+ mattype = /obj/item/stack/material/silver
+ if("diamond")
+ mattype = /obj/item/stack/material/diamond
+ if("phoron")
+ mattype = /obj/item/stack/material/phoron
+ if("uranium")
+ mattype = /obj/item/stack/material/uranium
+ else
+ return
+ var/obj/item/stack/material/S = new mattype(loc)
+ if(amount <= 0)
+ amount = S.max_amount
+ var/ejected = min(round(materials[material] / S.perunit), amount)
+ S.amount = min(ejected, amount)
+ if(S.amount <= 0)
+ qdel(S)
+ return
+ materials[material] -= ejected * S.perunit
+ if(recursive && materials[material] >= S.perunit)
+ eject_materials(material, -1)
\ No newline at end of file
diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm
index fc5ca4bcbf..41c6f6978c 100644
--- a/code/modules/research/research.dm
+++ b/code/modules/research/research.dm
@@ -124,7 +124,7 @@ research holder datum.
// A simple helper proc to find the name of a tech with a given ID.
/proc/CallTechName(var/ID)
- for(var/T in subtypes(/datum/tech))
+ for(var/T in subtypesof(/datum/tech))
var/datum/tech/check_tech = T
if(initial(check_tech.id) == ID)
return initial(check_tech.name)
diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm
index 81b298e43d..bf4cab0697 100644
--- a/code/modules/research/server.dm
+++ b/code/modules/research/server.dm
@@ -17,16 +17,13 @@
/obj/machinery/r_n_d/server/New()
..()
- initialize();
-
-/obj/machinery/r_n_d/server/map/New()
circuit = new circuit()
component_parts = list()
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
component_parts += new /obj/item/stack/cable_coil(src)
component_parts += new /obj/item/stack/cable_coil(src)
RefreshParts()
- ..()
+ initialize();
/obj/machinery/r_n_d/server/Destroy()
griefProtection()
@@ -127,15 +124,6 @@
name = "Central R&D Database"
server_id = -1
-/obj/machinery/r_n_d/server/centcom/map/New()
- circuit = new circuit()
- component_parts = list()
- component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- RefreshParts()
- ..()
-
/obj/machinery/r_n_d/server/centcom/initialize()
..()
var/list/no_id_servers = list()
@@ -319,26 +307,8 @@
id_with_download_string = "1;2"
server_id = 2
-/obj/machinery/r_n_d/server/robotics/map/New()
- circuit = new circuit()
- component_parts = list()
- component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- RefreshParts()
- ..()
-
/obj/machinery/r_n_d/server/core
name = "Core R&D Server"
id_with_upload_string = "1"
id_with_download_string = "1"
- server_id = 1
-
-/obj/machinery/r_n_d/server/core/map/New()
- circuit = new circuit()
- component_parts = list()
- component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- component_parts += new /obj/item/stack/cable_coil(src)
- RefreshParts()
- ..()
\ No newline at end of file
+ server_id = 1
\ No newline at end of file
diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm
index 1c6a9ca06d..12b9e26b48 100644
--- a/code/modules/tables/tables.dm
+++ b/code/modules/tables/tables.dm
@@ -51,8 +51,9 @@
visible_message("\The [src] breaks down!")
return break_to_parts() // if we break and form shards, return them to the caller to do !FUN! things with
-/obj/structure/table/New()
+/obj/structure/table/initialize()
..()
+
// One table per turf.
for(var/obj/structure/table/T in loc)
if(T != src)
@@ -61,8 +62,6 @@
break_to_parts(full_return = 1)
return
-/obj/structure/table/initialize()
- ..()
// reset color/alpha, since they're set for nice map previews
color = "#ffffff"
alpha = 255
diff --git a/code/modules/xenobio2/_xeno_setup.dm b/code/modules/xenobio2/_xeno_setup.dm
index a08cb54681..e77230b3e5 100644
--- a/code/modules/xenobio2/_xeno_setup.dm
+++ b/code/modules/xenobio2/_xeno_setup.dm
@@ -93,6 +93,12 @@ var/global/list/xenoChemList = list("mutationtoxin",
var/val = traits["[trait]"]
return val
+/datum/xeno/traits/proc/copy_traits(var/datum/xeno/traits/target)
+ target.traits = traits
+ target.chemlist = chemlist
+ target.chems = chems
+ target.source = source
+
/datum/xeno/traits/New()
..()
set_trait(TRAIT_XENO_COLOR, "#CACACA")
diff --git a/code/modules/xenobio2/machinery/core_extractor.dm b/code/modules/xenobio2/machinery/core_extractor.dm
index 421c8ed22f..e01bc6f852 100644
--- a/code/modules/xenobio2/machinery/core_extractor.dm
+++ b/code/modules/xenobio2/machinery/core_extractor.dm
@@ -110,7 +110,8 @@
icon_state = "scanner_1old"
for(var/i=1 to occupant.cores)
var/obj/item/xenoproduct/slime/core/C = new(src)
- C.traits = occupant.traitdat
+ C.traits = new()
+ occupant.traitdat.copy_traits(C.traits)
C.nameVar = occupant.nameVar
diff --git a/code/modules/xenobio2/mob/slime/slime procs.dm b/code/modules/xenobio2/mob/slime/slime procs.dm
index 56ad5d6050..adc8f4e5ba 100644
--- a/code/modules/xenobio2/mob/slime/slime procs.dm
+++ b/code/modules/xenobio2/mob/slime/slime procs.dm
@@ -13,11 +13,10 @@ Slime specific procs go here.
traitdat.traits[TRAIT_XENO_HUNGER] = rand(1, 20)
traitdat.traits[TRAIT_XENO_STARVEDAMAGE] = rand(1, 4)
traitdat.traits[TRAIT_XENO_EATS] = prob(95) //Odds are, that thing'll need to eat.
- traitdat.traits[TRAIT_XENO_CHROMATIC] = prob(5)
- if(traitdat.traits[TRAIT_XENO_CHROMATIC])
+ traitdat.traits[TRAIT_XENO_HOSTILE] = prob(60) //Let's increase this probabilty.
+ if(prob(10))
+ traitdat.traits[TRAIT_XENO_CHROMATIC] = 1
traitdat.traits[TRAIT_XENO_HOSTILE] = 0
- else
- traitdat.traits[TRAIT_XENO_HOSTILE] = prob(30)
traitdat.traits[TRAIT_XENO_GLOW_STRENGTH] = round(rand(1,3))
traitdat.traits[TRAIT_XENO_GLOW_RANGE] = round(rand(1,3))
traitdat.traits[TRAIT_XENO_STRENGTH] = round(rand(4,9))
diff --git a/code/modules/xenobio2/mob/xeno procs.dm b/code/modules/xenobio2/mob/xeno procs.dm
index e20373c21c..94da479f5a 100644
--- a/code/modules/xenobio2/mob/xeno procs.dm
+++ b/code/modules/xenobio2/mob/xeno procs.dm
@@ -29,10 +29,8 @@ Procs for targeting
set_light(traitdat.traits[TRAIT_XENO_GLOW_RANGE], traitdat.traits[TRAIT_XENO_GLOW_STRENGTH], traitdat.traits[TRAIT_XENO_BIO_COLOR])
else
set_light(0, 0, "#000000") //Should kill any light that shouldn't be there.
- if(chromatic)
- hostile = 0 //No. No laser-reflecting hostile creatures. Bad.
- else
- hostile = traitdat.traits[TRAIT_XENO_HOSTILE]
+
+ hostile = traitdat.traits[TRAIT_XENO_HOSTILE]
speed = traitdat.traits[TRAIT_XENO_SPEED]
diff --git a/code/modules/xenobio2/tools/xeno_trait_scanner.dm b/code/modules/xenobio2/tools/xeno_trait_scanner.dm
index 8063b6071b..565db47364 100644
--- a/code/modules/xenobio2/tools/xeno_trait_scanner.dm
+++ b/code/modules/xenobio2/tools/xeno_trait_scanner.dm
@@ -111,7 +111,7 @@
dat += "It bears no characters indicating resilience to damage. "
if(growth_max)
- if(growth_level < 25)
+ if(growth_level < 35)
dat += "It appears to be far to growing up. "
else if(growth_level > 40)
dat += "It appears to be close to growing up. "
diff --git a/code/world.dm b/code/world.dm
index 66edcd00d2..a6d27a8850 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -73,7 +73,7 @@ var/global/datum/global_init/init = new ()
// Set up roundstart seed list.
plant_controller = new()
-
+
// Set up roundstart gene masking
xenobio_controller = new()
diff --git a/config/example/config.txt b/config/example/config.txt
index ec93429827..ae99ec3d19 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -58,6 +58,9 @@ LOG_ATTACK
## log pda messages
LOG_PDA
+## log world.log messages
+# LOG_WORLD_OUTPUT
+
## log all Topic() calls (for use by coders in tracking down Topic issues)
# LOG_HREFS
diff --git a/html/changelog.html b/html/changelog.html
index e5cdf2a7a2..e0ee2e5c88 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -53,6 +53,38 @@
-->
+
07 June 2016
+
Datraen updated:
+
+
Adds a trait tool for Xenobio2. It was supposed to be in the initial implementation, but fell through the cracks, sorry!
+
Fixed the xenobio smartfridge not displaying it's starting core amount.
+
Fixed an inverted check with slimes.
+
+
EmperorJon updated:
+
+
Increased loadout points from 10 back to 15. We've added so many things like lunchboxes and communicators that everyone just takes by default, so a few more points will be nice.
+
+
JerTheAce updated:
+
+
Adds a variety of new guns. Some of these guns and and their ammunition are purchasable through antag uplink, cargo, or can be spawned during Renegade or Heist modes.
+
Adds ammunition clips for reloading magazines and some guns (such as bolt-actions).
+
All magazines and ammunitions can now be produced with the autolathe.
+
New bullpup SMG is available in armory area on CentCom level.
+
Revolvers now use 6 shots. Finally.
+
Most guns that didn't use magazines before for some reason (such as the Uzi) now use magazines.
+
All casings now update icons if they have been spent.
+
Corrects Pulse Rifle charge cost consistency.
+
Corrects and improves a number of icons for guns, including a couple of energy guns, and adds fallback icons so they do not turn invisible when held or strapped to your back.
+
Made the icons for some guns look differently depending on what magazine is loaded into them.
+
Made antag uplink menu for guns and ammo more informative.
+
Machinegun magazines no longer fit in pockets.
+
Made names and descriptions for all projectile guns and ammo consistent while removing grammar and spelling errors.
+
Autolathe no longer exploitable for ammo by repeatedly recycling empty magazines.
+
Guns now check a list of compatible magazines.
+
Ranged mobs no longer drop usable .357 casings.
+
Added a variety of new gun sound effects, and swapped out the old default gun sound.