\n"
+
+datum/controller/process/proc/getContextData()
+ return list(
+ "name" = name,
+ "averageRunTime" = main.averageRunTime(src),
+ "lastRunTime" = main.last_run_time[src],
+ "highestRunTime" = main.highest_run_time[src],
+ "ticks" = ticks,
+ "schedule" = schedule_interval,
+ "status" = getStatusText(),
+ "disabled" = disabled
+ )
+
+datum/controller/process/proc/getStatus()
+ return status
+
+datum/controller/process/proc/getStatusText(var/s = 0)
+ if(!s)
+ s = status
+ switch(s)
+ if(PROCESS_STATUS_IDLE)
+ return "idle"
+ if(PROCESS_STATUS_QUEUED)
+ return "queued"
+ if(PROCESS_STATUS_RUNNING)
+ return "running"
+ if(PROCESS_STATUS_MAYBE_HUNG)
+ return "maybe hung"
+ if(PROCESS_STATUS_PROBABLY_HUNG)
+ return "probably hung"
+ if(PROCESS_STATUS_HUNG)
+ return "HUNG"
+ else
+ return "UNKNOWN"
+
+datum/controller/process/proc/getPreviousStatus()
+ return previousStatus
+
+datum/controller/process/proc/getPreviousStatusText()
+ return getStatusText(previousStatus)
+
+datum/controller/process/proc/setStatus(var/newStatus)
+ previousStatus = status
+ status = newStatus
+
+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)
+ main = target.main
+ name = target.name
+ schedule_interval = target.schedule_interval
+ sleep_interval = target.sleep_interval
+ last_slept = 0
+ run_start = 0
+ times_killed = target.times_killed
+ ticks = target.ticks
+ last_task = target.last_task
+ last_object = target.last_object
+ copyStateFrom(target)
+
+datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target)
+
+datum/controller/process/proc/onKill()
+
+datum/controller/process/proc/onStart()
+
+datum/controller/process/proc/onFinish()
+
+datum/controller/process/proc/disable()
+ disabled = 1
+
+datum/controller/process/proc/enable()
+ disabled = 0
+
+/datum/controller/process/proc/getLastRunTime()
+ return main.getProcessLastRunTime(src)
+
+/datum/controller/process/proc/getTicks()
+ return ticks
diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm
new file mode 100644
index 00000000000..1be24045938
--- /dev/null
+++ b/code/controllers/ProcessScheduler/core/processScheduler.dm
@@ -0,0 +1,320 @@
+// Singleton instance of game_controller_new, setup in world.New()
+var/global/datum/controller/processScheduler/processScheduler
+
+/datum/controller/processScheduler
+ // Processes known by the scheduler
+ var/tmp/datum/controller/process/list/processes = new
+
+ // Processes that are currently running
+ var/tmp/datum/controller/process/list/running = new
+
+ // Processes that are idle
+ var/tmp/datum/controller/process/list/idle = new
+
+ // Processes that are queued to run
+ var/tmp/datum/controller/process/list/queued = new
+
+ // Process name -> process object map
+ var/tmp/datum/controller/process/list/nameToProcessMap = new
+
+ // Process last start times
+ var/tmp/datum/controller/process/list/last_start = new
+
+ // Process last run durations
+ var/tmp/datum/controller/process/list/last_run_time = new
+
+ // Per process list of the last 20 durations
+ var/tmp/datum/controller/process/list/last_twenty_run_times = new
+
+ // Process highest run time
+ var/tmp/datum/controller/process/list/highest_run_time = new
+
+ // Sleep 1 tick -- This may be too aggressive.
+ var/tmp/scheduler_sleep_interval = 1
+
+ // Controls whether the scheduler is running or not
+ var/tmp/isRunning = 0
+
+ // Setup for these processes will be deferred until all the other processes are set up.
+ var/tmp/list/deferredSetupList = new
+
+/**
+ * deferSetupFor
+ * @param path processPath
+ * If a process needs to be initialized after everything else, add it to
+ * the deferred setup list. On goonstation, only the ticker needs to have
+ * this treatment.
+ */
+/datum/controller/processScheduler/proc/deferSetupFor(var/processPath)
+ if (!(processPath in deferredSetupList))
+ deferredSetupList += processPath
+
+/datum/controller/processScheduler/proc/setup()
+ // There can be only one
+ if(processScheduler && (processScheduler != src))
+ del(src)
+ return 0
+
+ var/process
+ // Add all the processes we can find, except for the ticker
+ for (process in typesof(/datum/controller/process) - /datum/controller/process)
+ if (!(process in deferredSetupList))
+ addProcess(new process(src))
+
+ for (process in deferredSetupList)
+ addProcess(new process(src))
+
+/datum/controller/processScheduler/proc/start()
+ isRunning = 1
+ spawn(0)
+ process()
+
+/datum/controller/processScheduler/proc/process()
+ while(isRunning)
+ checkRunningProcesses()
+ queueProcesses()
+ runQueuedProcesses()
+ sleep(scheduler_sleep_interval)
+
+/datum/controller/processScheduler/proc/stop()
+ isRunning = 0
+
+/datum/controller/processScheduler/proc/checkRunningProcesses()
+ for(var/datum/controller/process/p in running)
+ p.update()
+
+ if (isnull(p)) // Process was killed
+ continue
+
+ var/status = p.getStatus()
+ var/previousStatus = p.getPreviousStatus()
+
+ // Check status changes
+ if(status != previousStatus)
+ //Status changed.
+
+ switch(status)
+ if(PROCESS_STATUS_MAYBE_HUNG)
+ message_admins("Process '[p.name]' is [p.getStatusText(status)].")
+ if(PROCESS_STATUS_PROBABLY_HUNG)
+ message_admins("Process '[p.name]' is [p.getStatusText(status)].")
+ if(PROCESS_STATUS_HUNG)
+ message_admins("Process '[p.name]' is [p.getStatusText(status)].")
+ p.handleHung()
+
+/datum/controller/processScheduler/proc/queueProcesses()
+ for(var/datum/controller/process/p in processes)
+ // Don't double-queue, don't queue running processes
+ if (p.disabled || p.running || p.queued || !p.idle)
+ continue
+
+ // If world.timeofday has rolled over, then we need to adjust.
+ if (world.timeofday < last_start[p])
+ last_start[p] -= 864000
+
+ // If the process should be running by now, go ahead and queue it
+ if (world.timeofday > last_start[p] + p.schedule_interval)
+ setQueuedProcessState(p)
+
+/datum/controller/processScheduler/proc/runQueuedProcesses()
+ for(var/datum/controller/process/p in queued)
+ runProcess(p)
+
+/datum/controller/processScheduler/proc/addProcess(var/datum/controller/process/process)
+ processes.Add(process)
+ process.idle()
+ idle.Add(process)
+
+ // init recordkeeping vars
+ last_start.Add(process)
+ last_start[process] = 0
+ last_run_time.Add(process)
+ last_run_time[process] = 0
+ last_twenty_run_times.Add(process)
+ last_twenty_run_times[process] = list()
+ highest_run_time.Add(process)
+ highest_run_time[process] = 0
+
+ // init starts and stops record starts
+ recordStart(process, 0)
+ recordEnd(process, 0)
+
+ // Set up process
+ process.setup()
+
+ // Save process in the name -> process map
+ nameToProcessMap[process.name] = process
+
+/datum/controller/processScheduler/proc/replaceProcess(var/datum/controller/process/oldProcess, var/datum/controller/process/newProcess)
+ processes.Remove(oldProcess)
+ processes.Add(newProcess)
+
+ newProcess.idle()
+ idle.Remove(oldProcess)
+ running.Remove(oldProcess)
+ queued.Remove(oldProcess)
+ idle.Add(newProcess)
+
+ last_start.Remove(oldProcess)
+ last_start.Add(newProcess)
+ last_start[newProcess] = 0
+
+ last_run_time.Add(newProcess)
+ last_run_time[newProcess] = last_run_time[oldProcess]
+ last_run_time.Remove(oldProcess)
+
+ last_twenty_run_times.Add(newProcess)
+ last_twenty_run_times[newProcess] = last_twenty_run_times[oldProcess]
+ last_twenty_run_times.Remove(oldProcess)
+
+ highest_run_time.Add(newProcess)
+ highest_run_time[newProcess] = highest_run_time[oldProcess]
+ highest_run_time.Remove(oldProcess)
+
+ recordStart(newProcess, 0)
+ recordEnd(newProcess, 0)
+
+ nameToProcessMap[newProcess.name] = newProcess
+
+
+/datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process)
+ spawn(0)
+ process.process()
+
+/datum/controller/processScheduler/proc/processStarted(var/datum/controller/process/process)
+ setRunningProcessState(process)
+ recordStart(process)
+
+/datum/controller/processScheduler/proc/processFinished(var/datum/controller/process/process)
+ setIdleProcessState(process)
+ recordEnd(process)
+
+/datum/controller/processScheduler/proc/setIdleProcessState(var/datum/controller/process/process)
+ if (process in running)
+ running -= process
+ if (process in queued)
+ queued -= process
+ if (!(process in idle))
+ idle += process
+
+ process.idle()
+
+/datum/controller/processScheduler/proc/setQueuedProcessState(var/datum/controller/process/process)
+ if (process in running)
+ running -= process
+ if (process in idle)
+ idle -= process
+ if (!(process in queued))
+ queued += process
+
+ // The other state transitions are handled internally by the process.
+ process.queued()
+
+/datum/controller/processScheduler/proc/setRunningProcessState(var/datum/controller/process/process)
+ if (process in queued)
+ queued -= process
+ if (process in idle)
+ idle -= process
+ 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
+
+/datum/controller/processScheduler/proc/recordEnd(var/datum/controller/process/process, var/time = null)
+ if (isnull(time))
+ time = world.timeofday
+
+ // If world.timeofday has rolled over, then we need to adjust.
+ if (time < last_start[process])
+ last_start[process] -= 864000
+
+ var/lastRunTime = time - last_start[process]
+
+ if(lastRunTime < 0)
+ lastRunTime = 0
+
+ recordRunTime(process, lastRunTime)
+
+/**
+ * recordRunTime
+ * Records a run time for a process
+ */
+/datum/controller/processScheduler/proc/recordRunTime(var/datum/controller/process/process, time)
+ last_run_time[process] = time
+ if(time > highest_run_time[process])
+ highest_run_time[process] = time
+
+ var/list/lastTwenty = last_twenty_run_times[process]
+ if (lastTwenty.len == 20)
+ lastTwenty.Cut(1, 2)
+ lastTwenty.len++
+ lastTwenty[lastTwenty.len] = time
+
+/**
+ * averageRunTime
+ * returns the average run time (over the last 20) of the process
+ */
+/datum/controller/processScheduler/proc/averageRunTime(var/datum/controller/process/process)
+ var/lastTwenty = last_twenty_run_times[process]
+
+ var/t = 0
+ var/c = 0
+ for(var/time in lastTwenty)
+ t += time
+ c++
+
+ if(c > 0)
+ return t / c
+ return c
+
+/datum/controller/processScheduler/proc/getStatusData()
+ var/list/data = new
+
+ for (var/datum/controller/process/p in processes)
+ data.len++
+ data[data.len] = p.getContextData()
+
+ return data
+
+/datum/controller/processScheduler/proc/getProcessCount()
+ return processes.len
+
+/datum/controller/processScheduler/proc/hasProcess(var/processName as text)
+ if (nameToProcessMap[processName])
+ return 1
+
+/datum/controller/processScheduler/proc/killProcess(var/processName as text)
+ restartProcess(processName)
+
+/datum/controller/processScheduler/proc/restartProcess(var/processName as text)
+ if (hasProcess(processName))
+ var/datum/controller/process/oldInstance = nameToProcessMap[processName]
+ var/datum/controller/process/newInstance = new oldInstance.type(src)
+ newInstance._copyStateFrom(oldInstance)
+ replaceProcess(oldInstance, newInstance)
+ oldInstance.kill()
+
+/datum/controller/processScheduler/proc/enableProcess(var/processName as text)
+ if (hasProcess(processName))
+ var/datum/controller/process/process = nameToProcessMap[processName]
+ process.enable()
+
+/datum/controller/processScheduler/proc/disableProcess(var/processName as text)
+ if (hasProcess(processName))
+ var/datum/controller/process/process = nameToProcessMap[processName]
+ process.disable()
+
+/datum/controller/processScheduler/proc/getProcess(var/name)
+ return nameToProcessMap[name]
+
+/datum/controller/processScheduler/proc/getProcessLastRunTime(var/datum/controller/process/process)
+ return last_run_time[process]
+
+/datum/controller/processScheduler/proc/getIsRunning()
+ return isRunning
diff --git a/code/controllers/ProcessScheduler/core/updateQueue.dm b/code/controllers/ProcessScheduler/core/updateQueue.dm
new file mode 100644
index 00000000000..118b6692b5a
--- /dev/null
+++ b/code/controllers/ProcessScheduler/core/updateQueue.dm
@@ -0,0 +1,127 @@
+/**
+ * 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
new file mode 100644
index 00000000000..66f66bbcc01
--- /dev/null
+++ b/code/controllers/ProcessScheduler/core/updateQueueWorker.dm
@@ -0,0 +1,83 @@
+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/package.json b/code/controllers/ProcessScheduler/package.json
new file mode 100644
index 00000000000..f699553fc61
--- /dev/null
+++ b/code/controllers/ProcessScheduler/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "ProcessScheduler",
+ "version": "1.0.0",
+ "description": "BYOND SS13 Process Scheduler",
+ "main": "processScheduler.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/goonstation/ProcessScheduler.git"
+ },
+ "keywords": [
+ "byond",
+ "ss13",
+ "process",
+ "scheduler"
+ ],
+ "author": "Volundr",
+ "license": "CC-BY-NC",
+ "bugs": {
+ "url": "https://github.com/goonstation/ProcessScheduler/issues"
+ },
+ "homepage": "https://github.com/goonstation/ProcessScheduler",
+ "dependencies": {
+ "bower": "*"
+ }
+}
diff --git a/code/controllers/ProcessScheduler/test/processScheduler.js b/code/controllers/ProcessScheduler/test/processScheduler.js
new file mode 100644
index 00000000000..0a4f111355d
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/processScheduler.js
@@ -0,0 +1,56 @@
+(function ($) {
+ function setRef(theRef) {
+ ref = theRef;
+ }
+
+ function jax(action, data) {
+ if (typeof data === 'undefined')
+ data = {};
+ var params = [];
+ for (var k in data) {
+ if (data.hasOwnProperty(k)) {
+ params.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k]));
+ }
+ }
+ var newLoc = '?src=' + ref + ';action=' + action + ';' + params.join(';');
+ window.location = newLoc;
+ }
+
+ function requestRefresh(e) {
+ jax("refresh", null);
+ }
+
+ function handleRefresh(processTable) {
+ $('#processTable').html(processTable);
+ initProcessTableButtons();
+ }
+
+ function requestKill(e) {
+ var button = $(e.currentTarget);
+ jax("kill", {name: button.data("process-name")});
+ }
+
+ function requestEnable(e) {
+ var button = $(e.currentTarget);
+ jax("enable", {name: button.data("process-name")});
+ }
+
+ function requestDisable(e) {
+ var button = $(e.currentTarget);
+ jax("disable", {name: button.data("process-name")});
+ }
+
+ function initProcessTableButtons() {
+ $(".kill-btn").on("click", requestKill);
+ $(".enable-btn").on("click", requestEnable);
+ $(".disable-btn").on("click", requestDisable);
+ }
+
+ window.setRef = setRef;
+ window.handleRefresh = handleRefresh;
+
+ $(function() {
+ initProcessTableButtons();
+ $('#btn-refresh').on("click", requestRefresh);
+ });
+}(jQuery));
\ No newline at end of file
diff --git a/code/controllers/ProcessScheduler/test/processSchedulerView.dm b/code/controllers/ProcessScheduler/test/processSchedulerView.dm
new file mode 100644
index 00000000000..ae78b3f0154
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/processSchedulerView.dm
@@ -0,0 +1,94 @@
+/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
new file mode 100644
index 00000000000..d08ec46c7da
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testDyingUpdateQueueProcess.dm
@@ -0,0 +1,27 @@
+/**
+ * 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
new file mode 100644
index 00000000000..2b5f1dff813
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testHarness.dm
@@ -0,0 +1,35 @@
+/*
+ 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
new file mode 100644
index 00000000000..ced05dd4d70
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testHungProcess.dm
@@ -0,0 +1,15 @@
+/**
+ * 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
new file mode 100644
index 00000000000..aa921bc62fa
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testNiceProcess.dm
@@ -0,0 +1,13 @@
+/**
+ * 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
new file mode 100644
index 00000000000..b7c9e6e21e8
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testSlowProcess.dm
@@ -0,0 +1,28 @@
+/**
+ * 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
new file mode 100644
index 00000000000..07b64e927f3
--- /dev/null
+++ b/code/controllers/ProcessScheduler/test/testUpdateQueue.dm
@@ -0,0 +1,209 @@
+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 5)
+ world << "RUNTIMES IN ATMOS TICKER. Killing air simulation!"
+ world.log << "### ZAS SHUTDOWN"
+
+ message_admins("ZASALERT: Shutting down! status: [air_master.tick_progress]")
+ log_admin("ZASALERT: Shutting down! status: [air_master.tick_progress]")
+
+ air_processing_killed = TRUE
+ air_master.failed_ticks = 0
diff --git a/code/controllers/Processes/bot.dm b/code/controllers/Processes/bot.dm
new file mode 100644
index 00000000000..b93324d0901
--- /dev/null
+++ b/code/controllers/Processes/bot.dm
@@ -0,0 +1,20 @@
+/datum/controller/process/bot
+ var/tmp/datum/updateQueue/updateQueueInstance
+
+/datum/controller/process/bot/setup()
+ name = "bot"
+ schedule_interval = 20 // every 2 seconds
+ updateQueueInstance = new
+
+/datum/controller/process/bot/started()
+ ..()
+ if(!updateQueueInstance)
+ if(!aibots)
+ aibots = list()
+ else if(aibots.len)
+ updateQueueInstance = new
+
+/datum/controller/process/bot/doWork()
+ if(updateQueueInstance)
+ updateQueueInstance.init(aibots, "bot_process")
+ updateQueueInstance.Run()
diff --git a/code/controllers/Processes/disease.dm b/code/controllers/Processes/disease.dm
new file mode 100644
index 00000000000..a8d840097ec
--- /dev/null
+++ b/code/controllers/Processes/disease.dm
@@ -0,0 +1,11 @@
+/datum/controller/process/disease
+ var/tmp/datum/updateQueue/updateQueueInstance
+
+/datum/controller/process/disease/setup()
+ name = "disease"
+ schedule_interval = 20 // every 2 seconds
+ updateQueueInstance = new
+
+/datum/controller/process/disease/doWork()
+ updateQueueInstance.init(active_diseases, "process")
+ updateQueueInstance.Run()
diff --git a/code/controllers/Processes/emergencyShuttle.dm b/code/controllers/Processes/emergencyShuttle.dm
new file mode 100644
index 00000000000..e7289311b95
--- /dev/null
+++ b/code/controllers/Processes/emergencyShuttle.dm
@@ -0,0 +1,9 @@
+/datum/controller/process/emergencyShuttle/setup()
+ name = "emergency shuttle"
+ schedule_interval = 20 // every 2 seconds
+
+ if(!emergency_shuttle)
+ emergency_shuttle = new
+
+/datum/controller/process/emergencyShuttle/doWork()
+ emergency_shuttle.process()
diff --git a/code/controllers/Processes/event.dm b/code/controllers/Processes/event.dm
new file mode 100644
index 00000000000..9948ef18ea6
--- /dev/null
+++ b/code/controllers/Processes/event.dm
@@ -0,0 +1,6 @@
+/datum/controller/process/event/setup()
+ name = "event"
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/event/doWork()
+ event_manager.process()
\ No newline at end of file
diff --git a/code/controllers/Processes/garbage.dm b/code/controllers/Processes/garbage.dm
new file mode 100644
index 00000000000..f589c641f5b
--- /dev/null
+++ b/code/controllers/Processes/garbage.dm
@@ -0,0 +1,10 @@
+/datum/controller/process/garbage/setup()
+ name = "garbage"
+ schedule_interval = 20 // every 2 seconds
+
+ if(!garbageCollector)
+ garbageCollector = new
+
+/datum/controller/process/garbage/doWork()
+ garbageCollector.process()
+ scheck()
\ No newline at end of file
diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/Processes/inactivity.dm
new file mode 100644
index 00000000000..e4cc04e4871
--- /dev/null
+++ b/code/controllers/Processes/inactivity.dm
@@ -0,0 +1,16 @@
+/*/datum/controller/process/inactivity/setup()
+ name = "inactivity"
+ schedule_interval = INACTIVITY_KICK
+
+/datum/controller/process/inactivity/doWork()
+ if(config.kick_inactive)
+ for(var/client/C in clients)
+ if(C.is_afk(INACTIVITY_KICK))
+ if(!istype(C.mob, /mob/dead))
+ log_access("AFK: [key_name(C)]")
+ C << "You have been inactive for more than 10 minutes and have been disconnected."
+ del(C)
+
+ scheck()
+
+#undef INACTIVITY_KICK*/
diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm
new file mode 100644
index 00000000000..bbf9ecd2f99
--- /dev/null
+++ b/code/controllers/Processes/lighting.dm
@@ -0,0 +1,26 @@
+/datum/controller/process/lighting/setup()
+ name = "lighting"
+ schedule_interval = 5 // every .5 second
+ lighting_controller.Initialize()
+
+/datum/controller/process/lighting/doWork()
+ lighting_controller.lights_workload_max = \
+ max(lighting_controller.lights_workload_max, lighting_controller.lights.len)
+
+ for(var/datum/light_source/L in lighting_controller.lights)
+ if(L && L.check())
+ lighting_controller.lights.Remove(L)
+
+ scheck()
+
+ lighting_controller.changed_turfs_workload_max = \
+ max(lighting_controller.changed_turfs_workload_max, lighting_controller.changed_turfs.len)
+
+ for(var/turf/T in lighting_controller.changed_turfs)
+ if(T && T.lighting_changed)
+ T.shift_to_subarea()
+
+ scheck()
+
+ if(lighting_controller.changed_turfs && lighting_controller.changed_turfs.len)
+ lighting_controller.changed_turfs.len = 0 // reset the changed list
diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm
new file mode 100644
index 00000000000..52274a519af
--- /dev/null
+++ b/code/controllers/Processes/machinery.dm
@@ -0,0 +1,34 @@
+/datum/controller/process/machinery/setup()
+ name = "machinery"
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/machinery/doWork()
+ //#ifdef PROFILE_MACHINES
+ //machine_profiling.len = 0
+ //#endif
+
+ for(var/obj/machinery/M in machines)
+ 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)
+ continue
+
+ 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()
+
diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm
new file mode 100644
index 00000000000..b3765b0cf9c
--- /dev/null
+++ b/code/controllers/Processes/mob.dm
@@ -0,0 +1,20 @@
+/datum/controller/process/mob
+ var/tmp/datum/updateQueue/updateQueueInstance
+
+/datum/controller/process/mob/setup()
+ name = "mob"
+ schedule_interval = 20 // every 2 seconds
+ updateQueueInstance = new
+
+/datum/controller/process/mob/started()
+ ..()
+ if(!updateQueueInstance)
+ if(!mob_list)
+ mob_list = list()
+ else if(mob_list.len)
+ updateQueueInstance = new
+
+/datum/controller/process/mob/doWork()
+ if(updateQueueInstance)
+ updateQueueInstance.init(mob_list, "Life")
+ updateQueueInstance.Run()
diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm
new file mode 100644
index 00000000000..c8396bcab87
--- /dev/null
+++ b/code/controllers/Processes/nanoui.dm
@@ -0,0 +1,11 @@
+/datum/controller/process/nanoui
+ var/tmp/datum/updateQueue/updateQueueInstance
+
+/datum/controller/process/nanoui/setup()
+ name = "nanoui"
+ schedule_interval = 20 // every 2 seconds
+ updateQueueInstance = new
+
+/datum/controller/process/nanoui/doWork()
+ updateQueueInstance.init(nanomanager.processing_uis, "process")
+ updateQueueInstance.Run()
diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm
new file mode 100644
index 00000000000..15ad98dd3d6
--- /dev/null
+++ b/code/controllers/Processes/obj.dm
@@ -0,0 +1,21 @@
+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
+
+/datum/controller/process/obj/started()
+ ..()
+ if(!updateQueueInstance)
+ if(!processing_objects)
+ processing_objects = list()
+ else if(processing_objects.len)
+ updateQueueInstance = new
+
+/datum/controller/process/obj/doWork()
+ if(updateQueueInstance)
+ updateQueueInstance.init(processing_objects, "process")
+ updateQueueInstance.Run()
diff --git a/code/controllers/Processes/pipenet.dm b/code/controllers/Processes/pipenet.dm
new file mode 100644
index 00000000000..56a068f54ca
--- /dev/null
+++ b/code/controllers/Processes/pipenet.dm
@@ -0,0 +1,12 @@
+/datum/controller/process/pipenet/setup()
+ name = "pipenet"
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/pipenet/doWork()
+ for(var/datum/pipe_network/pipeNetwork in pipe_networks)
+ if(istype(pipeNetwork) && !pipeNetwork.disposed)
+ pipeNetwork.process()
+ scheck()
+ continue
+
+ pipe_networks.Remove(pipeNetwork)
diff --git a/code/controllers/Processes/power_machinery.dm b/code/controllers/Processes/power_machinery.dm
new file mode 100644
index 00000000000..ce17c0583a6
--- /dev/null
+++ b/code/controllers/Processes/power_machinery.dm
@@ -0,0 +1,45 @@
+var/global/list/power_machinery_profiling = list()
+
+/datum/controller/process/power_machinery
+ var/tmp/datum/updateQueue/updateQueueInstance
+
+/datum/controller/process/power_machinery/setup()
+ name = "pow_machine"
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/power_machinery/doWork()
+ for(var/i = 1 to power_machines.len)
+ if(i > power_machines.len)
+ break
+ var/obj/machinery/M = power_machines[i]
+ if(istype(M) && !M.gcDestroyed)
+ #ifdef PROFILE_MACHINES
+ var/time_start = world.timeofday
+ #endif
+
+ if(M.process() == PROCESS_KILL)
+ M.inMachineList = 0
+ power_machines.Remove(M)
+ continue
+
+ if(M && M.use_power)
+ M.auto_use_power()
+ if(istype(M))
+ #ifdef PROFILE_MACHINES
+ var/time_end = world.timeofday
+
+ if(!(M.type in power_machinery_profiling))
+ power_machinery_profiling[M.type] = 0
+
+ power_machinery_profiling[M.type] += (time_end - time_start)
+ #endif
+ else
+ if(!power_machines.Remove(M))
+ power_machines.Cut(i,i+1)
+ else
+ if(M)
+ M.inMachineList = 0
+ if(!power_machines.Remove(M))
+ power_machines.Cut(i,i+1)
+
+ scheck()
diff --git a/code/controllers/Processes/powernet.dm b/code/controllers/Processes/powernet.dm
new file mode 100644
index 00000000000..1edf194915a
--- /dev/null
+++ b/code/controllers/Processes/powernet.dm
@@ -0,0 +1,12 @@
+/datum/controller/process/powernet/setup()
+ name = "powernet"
+ schedule_interval = 20 // every 2 seconds
+
+/datum/controller/process/powernet/doWork()
+ for(var/datum/powernet/powerNetwork in powernets)
+ if(istype(powerNetwork) && !powerNetwork.disposed)
+ powerNetwork.reset()
+ scheck()
+ continue
+
+ powernets.Remove(powerNetwork)
diff --git a/code/controllers/Processes/shuttle.dm b/code/controllers/Processes/shuttle.dm
new file mode 100644
index 00000000000..a10586d76d9
--- /dev/null
+++ b/code/controllers/Processes/shuttle.dm
@@ -0,0 +1,9 @@
+/datum/controller/process/Shuttle/setup()
+ name = "shuttle controller"
+ schedule_interval = 20 // every 2 seconds
+
+ if(!shuttle_controller)
+ shuttle_controller = new
+
+/datum/controller/process/Shuttle/doWork()
+ shuttle_controller.process()
diff --git a/code/controllers/Processes/sun.dm b/code/controllers/Processes/sun.dm
new file mode 100644
index 00000000000..f09806cef53
--- /dev/null
+++ b/code/controllers/Processes/sun.dm
@@ -0,0 +1,7 @@
+/datum/controller/process/sun/setup()
+ name = "sun"
+ schedule_interval = 20 // every second
+ sun = new
+
+/datum/controller/process/sun/doWork()
+ sun.calc_position()
diff --git a/code/controllers/Processes/supply.dm b/code/controllers/Processes/supply.dm
new file mode 100644
index 00000000000..891a511ec6d
--- /dev/null
+++ b/code/controllers/Processes/supply.dm
@@ -0,0 +1,6 @@
+/datum/controller/process/supply/setup()
+ name = "supply controller"
+ schedule_interval = 300 // every 30 seconds
+
+/datum/controller/process/supply/doWork()
+ supply_controller.process()
\ No newline at end of file
diff --git a/code/controllers/Processes/ticker.dm b/code/controllers/Processes/ticker.dm
new file mode 100644
index 00000000000..b7c45ba91a4
--- /dev/null
+++ b/code/controllers/Processes/ticker.dm
@@ -0,0 +1,35 @@
+var/global/datum/controller/process/ticker/tickerProcess
+
+/datum/controller/process/ticker
+ var/lastTickerTimeDuration
+ var/lastTickerTime
+
+/datum/controller/process/ticker/setup()
+ name = "ticker"
+ schedule_interval = 20 // every 2 seconds
+
+ lastTickerTime = world.timeofday
+
+ if(!ticker)
+ ticker = new
+
+ tickerProcess = src
+
+ spawn(0)
+ if(ticker)
+ ticker.pregame()
+
+/datum/controller/process/ticker/doWork()
+ var/currentTime = world.timeofday
+
+ if(currentTime < lastTickerTime) // check for midnight rollover
+ lastTickerTimeDuration = (currentTime - (lastTickerTime - TICKS_IN_DAY)) / TICKS_IN_SECOND
+ else
+ lastTickerTimeDuration = (currentTime - lastTickerTime) / TICKS_IN_SECOND
+
+ lastTickerTime = currentTime
+
+ ticker.process()
+
+/datum/controller/process/ticker/proc/getLastTickerTimeDuration()
+ return lastTickerTimeDuration
diff --git a/code/controllers/Processes/vote.dm b/code/controllers/Processes/vote.dm
new file mode 100644
index 00000000000..5df5ce69792
--- /dev/null
+++ b/code/controllers/Processes/vote.dm
@@ -0,0 +1,6 @@
+/datum/controller/process/vote/setup()
+ name = "vote"
+ schedule_interval = 10 // every second
+
+/datum/controller/process/vote/doWork()
+ vote.process()
diff --git a/code/controllers/_DynamicAreaLighting_TG.dm b/code/controllers/_DynamicAreaLighting_TG.dm
index 8168ef50aad..7ed1a047a0d 100644
--- a/code/controllers/_DynamicAreaLighting_TG.dm
+++ b/code/controllers/_DynamicAreaLighting_TG.dm
@@ -94,7 +94,7 @@ datum/light_source
if(owner.loc && owner.luminosity > 0)
readrgb(owner.l_color)
effect = list()
- for(var/turf/T in view(owner.get_light_range(),owner))
+ for(var/turf/T in view(owner.get_light_range(),get_turf(owner)))
var/delta_lumen = lum(T)
if(delta_lumen > 0)
effect[T] = delta_lumen
@@ -160,14 +160,6 @@ atom/movable/New()
trueLuminosity = luminosity * luminosity
light = new(src)
-//Objects with opacity will trigger nearby lights to update at next lighting process.
-atom/movable/Destroy()
- if(opacity)
- if(isturf(loc))
- if(loc:lighting_lumcount > 1)
- UpdateAffectingLights()
-
- ..()
//Sets our luminosity.
//If we have no light it will create one.
diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm
index b3a5a9f58d1..6fc3faab894 100644
--- a/code/controllers/communications.dm
+++ b/code/controllers/communications.dm
@@ -40,7 +40,7 @@
obj/proc/receive_signal(datum/signal/signal, var/receive_method as num, var/receive_param)
Handler from received signals. By default does nothing. Define your own for your object.
- Avoid of sending signals directly from this proc, use spawn(-1). Do not use sleep() here please.
+ Avoid of sending signals directly from this proc, use spawn(-1). DO NOT use sleep() here or call procs that sleep please. If you must, use spawn()
parameters:
signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
@@ -61,33 +61,6 @@
*/
-/* the radio controller is a confusing piece of shit and didnt work
- so i made radios not use the radio controller.
-*/
-var/list/all_radios = list()
-
-/proc/add_radio(var/obj/item/radio, freq)
- if(!freq || !radio)
- return
- if(!all_radios["[freq]"])
- all_radios["[freq]"] = list(radio)
- return freq
-
- all_radios["[freq]"] |= radio
- return freq
-
-/proc/remove_radio(var/obj/item/radio, freq)
- if(!freq || !radio)
- return
- if(!all_radios["[freq]"])
- return
-
- all_radios["[freq]"] -= radio
-
-/proc/remove_radio_all(var/obj/item/radio)
- for(var/freq in all_radios)
- all_radios["[freq]"] -= radio
-
/*
Frequency range: 1200 to 1600
Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency, even during mapmaking)
@@ -99,10 +72,10 @@ Radio:
1355 - Medical
1357 - Engineering
1359 - Security
-1441 - death squad
+1341 - death squad
1443 - Confession Intercom
1347 - Cargo techs
-1349 - Service
+1349 - Service people
Devices:
1451 - tracking implant
@@ -112,7 +85,7 @@ On the map:
1311 for prison shuttle console (in fact, it is not used)
1435 for status displays
1437 for atmospherics/fire alerts
-1439 for engine components
+1438 for engine components
1439 for air pumps, air scrubbers, atmo control
1441 for atmospherics - supply tanks
1443 for atmospherics - distribution loop/mixed air tank
@@ -124,30 +97,14 @@ On the map:
1455 for AI access
*/
-var/list/radiochannels = list(
- "Common" = 1459,
- "Science" = 1351,
- "Command" = 1353,
- "Medical" = 1355,
- "Engineering" = 1357,
- "Security" = 1359,
- "Response Team" = 1443,
- "Deathsquad" = 1441,
- "Syndicate" = 1213,
- "Supply" = 1347,
- "Service" = 1349,
- "AI Private" = 1447
-)
-//depenging helpers
-var/list/DEPT_FREQS = list(1351, 1355, 1357, 1359, 1213, 1443, 1441, 1347, 1349)
-
-// central command channels, i.e deathsquid & response teams
-var/list/CENT_FREQS = list(1441, 1443)
-
-var/const/COMM_FREQ = 1353 //command, colored gold in chat window
+var/const/COMM_FREQ = 1353
var/const/SYND_FREQ = 1213
+var/const/ERT_FREQ = 1345
+var/const/DTH_FREQ = 1341
+var/const/AI_FREQ = 1447
// department channels
+var/const/PUB_FREQ = 1459
var/const/SEC_FREQ = 1359
var/const/ENG_FREQ = 1357
var/const/SCI_FREQ = 1351
@@ -155,25 +112,85 @@ var/const/MED_FREQ = 1355
var/const/SUP_FREQ = 1347
var/const/SRV_FREQ = 1349
-// other channels
-var/const/AIPRIV_FREQ = 1447
+var/list/radiochannels = list(
+ "Common" = PUB_FREQ,
+ "Science" = SCI_FREQ,
+ "Command" = COMM_FREQ,
+ "Medical" = MED_FREQ,
+ "Engineering" = ENG_FREQ,
+ "Security" = SEC_FREQ,
+ "Response Team" = ERT_FREQ,
+ "Special Ops" = DTH_FREQ,
+ "Syndicate" = SYND_FREQ,
+ "Supply" = SUP_FREQ,
+ "Service" = SRV_FREQ,
+ "AI Private" = AI_FREQ
+)
+
+// central command channels, i.e deathsquid & response teams
+var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ)
+
+// Antag channels, i.e. Syndicate
+var/list/ANTAG_FREQS = list(SYND_FREQ)
+
+//depenging helpers
+var/list/DEPT_FREQS = list(SCI_FREQ, MED_FREQ, ENG_FREQ, SEC_FREQ, SUP_FREQ, SRV_FREQ, ERT_FREQ, SYND_FREQ, DTH_FREQ)
#define TRANSMISSION_WIRE 0
#define TRANSMISSION_RADIO 1
+/proc/frequency_span_class(var/frequency)
+ // Antags!
+ if (frequency in ANTAG_FREQS)
+ return "syndradio"
+ // centcomm channels (deathsquid and ert)
+ else if(frequency in CENT_FREQS)
+ return "centradio"
+ // command channel
+ else if(frequency == COMM_FREQ)
+ return "comradio"
+ // AI private channel
+ else if(frequency == AI_FREQ)
+ return "airadio"
+ // department radio formatting (poorly optimized, ugh)
+ else if(frequency == SEC_FREQ)
+ return "secradio"
+ else if (frequency == ENG_FREQ)
+ return "engradio"
+ else if(frequency == SCI_FREQ)
+ return "sciradio"
+ else if(frequency == MED_FREQ)
+ return "medradio"
+ else if(frequency == SUP_FREQ) // cargo
+ return "supradio"
+ else if(frequency == SRV_FREQ) // service
+ return "srvradio"
+ // If all else fails and it's a dept_freq, color me purple!
+ else if(frequency in DEPT_FREQS)
+ return "deptradio"
+
+ return "radio"
+
/* filters */
-var/const/RADIO_TO_AIRALARM = "1"
-var/const/RADIO_FROM_AIRALARM = "2"
-var/const/RADIO_CHAT = "3"
-var/const/RADIO_ATMOSIA = "4"
-var/const/RADIO_NAVBEACONS = "5"
-var/const/RADIO_AIRLOCK = "6"
-var/const/RADIO_SECBOT = "7"
-var/const/RADIO_MULEBOT = "8"
-var/const/RADIO_MAGNETS = "9"
+//When devices register with the radio controller, they might register under a certain filter.
+//Other devices can then choose to send signals to only those devices that belong to a particular filter.
+//This is done for performance, so we don't send signals to lots of machines unnecessarily.
+
+//This filter is special because devices belonging to default also recieve signals sent to any other filter.
+var/const/RADIO_DEFAULT = "radio_default"
+
+var/const/RADIO_TO_AIRALARM = "radio_airalarm" //air alarms
+var/const/RADIO_FROM_AIRALARM = "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms
+var/const/RADIO_CHAT = "radio_telecoms"
+var/const/RADIO_ATMOSIA = "radio_atmos"
+var/const/RADIO_NAVBEACONS = "radio_navbeacon"
+var/const/RADIO_AIRLOCK = "radio_airlock"
+var/const/RADIO_SECBOT = "radio_secbot"
+var/const/RADIO_MULEBOT = "radio_mulebot"
var/const/RADIO_CLEANBOT = "10"
var/const/RADIO_FLOORBOT = "11"
var/const/RADIO_MEDBOT = "12"
+var/const/RADIO_MAGNETS = "radio_magnet"
var/global/datum/controller/radio/radio_controller
@@ -181,164 +198,138 @@ var/global/datum/controller/radio/radio_controller
radio_controller = new /datum/controller/radio()
return 1
-datum/controller/radio
+//callback used by objects to react to incoming radio signals
+/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
+ return null
+
+//The global radio controller
+/datum/controller/radio
var/list/datum/radio_frequency/frequencies = list()
- proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
- var/f_text = num2text(new_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
+/datum/controller/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
+ var/f_text = num2text(new_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
- if(!frequency)
- frequency = new
- frequency.frequency = new_frequency
- frequencies[f_text] = frequency
+ if(!frequency)
+ frequency = new
+ frequency.frequency = new_frequency
+ frequencies[f_text] = frequency
- frequency.add_listener(device, filter)
- return frequency
+ frequency.add_listener(device, filter)
+ return frequency
- proc/remove_object(obj/device, old_frequency)
- var/f_text = num2text(old_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
+/datum/controller/radio/proc/remove_object(obj/device, old_frequency)
+ var/f_text = num2text(old_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
- if(frequency)
- frequency.remove_listener(device)
+ if(frequency)
+ frequency.remove_listener(device)
- if(frequency.devices.len == 0)
- del(frequency)
- frequencies -= f_text
+ if(frequency.devices.len == 0)
+ del(frequency)
+ frequencies -= f_text
- return 1
+ return 1
- proc/return_frequency(var/new_frequency as num)
- var/f_text = num2text(new_frequency)
- var/datum/radio_frequency/frequency = frequencies[f_text]
+/datum/controller/radio/proc/return_frequency(var/new_frequency as num)
+ var/f_text = num2text(new_frequency)
+ var/datum/radio_frequency/frequency = frequencies[f_text]
- if(!frequency)
- frequency = new
- frequency.frequency = new_frequency
- frequencies[f_text] = frequency
+ if(!frequency)
+ frequency = new
+ frequency.frequency = new_frequency
+ frequencies[f_text] = frequency
- return frequency
+ return frequency
-datum/radio_frequency
+/datum/radio_frequency
var/frequency as num
var/list/list/obj/devices = list()
- proc
- post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null)
- //log_admin("DEBUG \[[world.timeofday]\]: post_signal {source=\"[source]\", [signal.debug_print()], filter=[filter]}")
-// var/N_f=0
-// var/N_nf=0
-// var/Nt=0
- var/turf/start_point
- if(range)
- start_point = get_turf(source)
- if(!start_point)
- del(signal)
- return 0
- if (filter) //here goes some copypasta. It is for optimisation. -rastaf0
- for(var/obj/device in devices[filter])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
- for(var/obj/device in devices["_default"])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
-// N_f++
- else
- for (var/next_filter in devices)
-// var/list/obj/DDD = devices[next_filter]
-// Nt+=DDD.len
- for(var/obj/device in devices[next_filter])
- if(device == source)
- continue
- if(range)
- var/turf/end_point = get_turf(device)
- if(!end_point)
- continue
- //if(max(abs(start_point.x-end_point.x), abs(start_point.y-end_point.y)) <= range)
- if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
- continue
- device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
-// N_nf++
+/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null)
+ var/turf/start_point
+ if(range)
+ start_point = get_turf(source)
+ if(!start_point)
+ del(signal)
+ return 0
+ if (filter)
+ send_to_filter(source, signal, filter, start_point, range)
+ send_to_filter(source, signal, RADIO_DEFAULT, start_point, range)
+ else
+ //Broadcast the signal to everyone!
+ for (var/next_filter in devices)
+ send_to_filter(source, signal, next_filter, start_point, range)
-// log_admin("DEBUG: post_signal(source=[source] ([source.x], [source.y], [source.z]),filter=[filter]) frequency=[frequency], N_f=[N_f], N_nf=[N_nf]")
+//Sends a signal to all machines belonging to a given filter. Should be called by post_signal()
+/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null)
+ if (range && !start_point)
+ return
+ for(var/obj/device in devices[filter])
+ if(device == source)
+ continue
+ if(range)
+ var/turf/end_point = get_turf(device)
+ if(!end_point)
+ continue
+ if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range)
+ continue
-// del(signal)
+ device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
- add_listener(obj/device as obj, var/filter as text|null)
- if (!filter)
- filter = "_default"
- //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]")
- var/list/obj/devices_line = devices[filter]
- if (!devices_line)
- devices_line = new
- devices[filter] = devices_line
- devices_line+=device
+/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null)
+ if (!filter)
+ filter = RADIO_DEFAULT
+ //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]")
+ var/list/obj/devices_line = devices[filter]
+ if (!devices_line)
+ devices_line = new
+ devices[filter] = devices_line
+ devices_line+=device
// var/list/obj/devices_line___ = devices[filter_str]
// var/l = devices_line___.len
- //log_admin("DEBUG: devices_line.len=[devices_line.len]")
- //log_admin("DEBUG: devices(filter_str).len=[l]")
+ //log_admin("DEBUG: devices_line.len=[devices_line.len]")
+ //log_admin("DEBUG: devices(filter_str).len=[l]")
- remove_listener(obj/device)
- for (var/devices_filter in devices)
- var/list/devices_line = devices[devices_filter]
- devices_line-=device
- while (null in devices_line)
- devices_line -= null
- if (devices_line.len==0)
- devices -= devices_filter
- del(devices_line)
+/datum/radio_frequency/proc/remove_listener(obj/device)
+ for (var/devices_filter in devices)
+ var/list/devices_line = devices[devices_filter]
+ devices_line-=device
+ while (null in devices_line)
+ devices_line -= null
+ if (devices_line.len==0)
+ devices -= devices_filter
+ del(devices_line)
-
-obj/proc
- receive_signal(datum/signal/signal, receive_method, receive_param)
- return null
-
-datum/signal
+/datum/signal
var/obj/source
- var/transmission_method = 0
+ var/transmission_method = 0 //unused at the moment
//0 = wire
//1 = radio transmission
//2 = subspace transmission
- var/data = list()
+ var/list/data = list()
var/encryption
var/frequency = 0
- proc/copy_from(datum/signal/model)
- source = model.source
- transmission_method = model.transmission_method
- data = model.data
- encryption = model.encryption
- frequency = model.frequency
+/datum/signal/proc/copy_from(datum/signal/model)
+ source = model.source
+ transmission_method = model.transmission_method
+ data = model.data
+ encryption = model.encryption
+ frequency = model.frequency
- proc/debug_print()
- if (source)
- . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
- else
- . = "signal = {source = '[source]' ()\n"
- for (var/i in data)
- . += "data\[\"[i]\"\] = \"[data[i]]\"\n"
- if(islist(data[i]))
- var/list/L = data[i]
- for(var/t in L)
- . += "data\[\"[i]\"\] list has: [t]"
+/datum/signal/proc/debug_print()
+ if (source)
+ . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
+ else
+ . = "signal = {source = '[source]' ()\n"
+ for (var/i in data)
+ . += "data\[\"[i]\"\] = \"[data[i]]\"\n"
+ if(islist(data[i]))
+ var/list/L = data[i]
+ for(var/t in L)
+ . += "data\[\"[i]\"\] list has: [t]"
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 5806da2e51a..95ecef88978 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -134,7 +134,12 @@
var/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix
var/default_laws = 0 //Controls what laws the AI spawns with.
-
+
+ var/list/station_levels = list(1) // Defines which Z-levels the station exists on.
+ var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle
+ var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect
+ var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach
+
var/const/minutes_to_ticks = 60 * 10
// Event settings
var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours
@@ -163,7 +168,7 @@
src.probabilities[M.config_tag] = M.probability
if (M.votable)
src.votable_modes += M.config_tag
- del(M)
+ qdel(M)
src.votable_modes += "secret"
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
@@ -204,9 +209,9 @@
if ("use_age_restriction_for_jobs")
config.use_age_restriction_for_jobs = 1
-
+
if ("use_age_restriction_for_antags")
- config.use_age_restriction_for_antags = 1
+ config.use_age_restriction_for_antags = 1
if ("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
@@ -457,7 +462,19 @@
if("max_maint_drones")
config.max_maint_drones = text2num(value)
-
+
+ if("station_levels")
+ config.station_levels = text2numlist(value, ";")
+
+ if("admin_levels")
+ config.admin_levels = text2numlist(value, ";")
+
+ if("contact_levels")
+ config.contact_levels = text2numlist(value, ";")
+
+ if("player_levels")
+ config.player_levels = text2numlist(value, ";")
+
if("expected_round_length")
config.expected_round_length = MinutesToTicks(text2num(value))
diff --git a/code/controllers/emergency_shuttle_controller.dm b/code/controllers/emergency_shuttle_controller.dm
index 55b325eba3c..83a9d379434 100644
--- a/code/controllers/emergency_shuttle_controller.dm
+++ b/code/controllers/emergency_shuttle_controller.dm
@@ -18,7 +18,10 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
var/deny_shuttle = 0 //allows admins to prevent the shuttle from being called
var/departed = 0 //if the shuttle has left the station at least once
-
+
+ var/datum/announcement/priority/emergency_shuttle_docked = new(0, new_sound = sound('sound/AI/shuttledock.ogg'))
+ var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
+ var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
/datum/emergency_shuttle_controller/proc/process()
if (wait_for_launch)
@@ -29,7 +32,7 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
if (!shuttle.location) //leaving from the station
if(is_stranded())
- captain_announce("Hostile environment detected. Departure has been postponed indefinitely pending conflict resolution.")
+ priority_announcement.Announce("Hostile environment detected. Departure has been postponed indefinitely pending conflict resolution.")
wait_for_launch = 0
return
//launch the pods!
@@ -49,10 +52,9 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
set_launch_countdown(SHUTTLE_LEAVETIME) //get ready to return
if (evac)
- captain_announce("The Emergency Shuttle has docked with the station. You have approximately [round(estimate_launch_time()/60,1)] minutes to board the Emergency Shuttle.")
- world << sound('sound/AI/shuttledock.ogg')
+ emergency_shuttle_docked.Announce("The Emergency Shuttle has docked with the station. You have approximately [round(estimate_launch_time()/60,1)] minutes to board the Emergency Shuttle.")
else
- captain_announce("The scheduled Crew Transfer Shuttle has docked with the station. It will depart in approximately [round(emergency_shuttle.estimate_launch_time()/60,1)] minutes.")
+ priority_announcement.Announce("The scheduled Crew Transfer Shuttle has docked with the station. It will depart in approximately [round(emergency_shuttle.estimate_launch_time()/60,1)] minutes.")
//arm the escape pods
if (evac)
@@ -81,8 +83,7 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
shuttle.move_time = SHUTTLE_TRANSIT_DURATION
evac = 1
- captain_announce("An emergency evacuation shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.")
- world << sound('sound/AI/shuttlecalled.ogg')
+ emergency_shuttle_called.Announce("An emergency evacuation shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.")
for(var/area/A in world)
if(istype(A, /area/hallway))
A.readyalert()
@@ -101,7 +102,7 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
//reset the shuttle transit time if we need to
shuttle.move_time = SHUTTLE_TRANSIT_DURATION
- captain_announce("A crew transfer has been scheduled. The shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.")
+ priority_announcement.Announce("A crew transfer has been scheduled. The shuttle has been called. It will arrive in approximately [round(estimate_arrival_time()/60)] minutes.")
//recalls the shuttle
/datum/emergency_shuttle_controller/proc/recall()
@@ -111,15 +112,14 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
shuttle.cancel_launch(src)
if (evac)
- captain_announce("The emergency shuttle has been recalled.")
- world << sound('sound/AI/shuttlerecalled.ogg')
+ emergency_shuttle_recalled.Announce("The emergency shuttle has been recalled.")
for(var/area/A in world)
if(istype(A, /area/hallway))
A.readyreset()
evac = 0
else
- captain_announce("The scheduled crew transfer has been cancelled.")
+ priority_announcement.Announce("The scheduled crew transfer has been cancelled.")
/datum/emergency_shuttle_controller/proc/can_call()
if (deny_shuttle)
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index ff46b6b10b6..a2a6274a8ab 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -1,68 +1,78 @@
-var/datum/controller/failsafe/Failsafe
+var/global/datum/controller/failsafe/failsafe
-/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
- var/processing = 0
- var/processing_interval = 100 //poke the MC every 10 seconds
+/datum/controller/failsafe // This thing pretty much just keeps poking the controllers.
+ processing_interval = 100 // Poke the controllers every 10 seconds.
- var/MC_iteration = 0
- var/MC_defcon = 0 //alert level. For every poke that fails this is raised by 1. When it reaches 5 the MC is replaced with a new one. (effectively killing any master_controller.process() and starting a new one)
+ /*
+ * Controller alert level.
+ * For every poke that fails this is raised by 1.
+ * When it reaches 5 the MC is replaced with a new one
+ * (effectively killing any controller process() and starting a new one).
+ */
- var/lighting_iteration = 0
- var/lighting_defcon = 0 //alert level for lighting controller.
+ // master
+ var/masterControllerIteration = 0
+ var/masterControllerAlertLevel = 0
+
+ // lighting
+ var/lightingControllerIteration = 0
+ var/lightingControllerAlertLevel = 0
/datum/controller/failsafe/New()
- //There can be only one failsafe. Out with the old in with the new (that way we can restart the Failsafe by spawning a new one)
- if(Failsafe != src)
- if(istype(Failsafe))
- del(Failsafe)
- Failsafe = src
- Failsafe.process()
+ . = ..()
+ // There can be only one failsafe. Out with the old in with the new (that way we can restart the Failsafe by spawning a new one).
+ if (failsafe != src)
+ if (istype(failsafe))
+ recover()
+ qdel(failsafe)
+
+ failsafe = src
+
+ failsafe.process()
/datum/controller/failsafe/proc/process()
processing = 1
+
spawn(0)
- //set background = 1
- while(1) //more efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop
- if(!master_controller) new /datum/controller/game_controller() //replace the missing master_controller! This should never happen.
- if(!lighting_controller) new /datum/controller/lighting() //replace the missing lighting_controller
+ set background = BACKGROUND_ENABLED
+
+ while(1) // More efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop.
+ iteration++
if(processing)
- if(master_controller.processing) //only poke if these overrides aren't in effect
- if(MC_iteration == controller_iteration) //master_controller hasn't finished processing in the defined interval
- switch(MC_defcon)
+ if(master_controller.processing) // Only poke if these overrides aren't in effect
+ if(masterControllerIteration == master_controller.iteration) // Master controller hasn't finished processing in the defined interval.
+ switch(masterControllerAlertLevel)
if(0 to 3)
- MC_defcon++
+ masterControllerAlertLevel++
if(4)
- admins << "Warning. The Master Controller has not fired in the last [MC_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks."
- MC_defcon = 5
+ admins << "Warning. The master Controller has not fired in the last [masterControllerAlertLevel * processing_interval] ticks. Automatic restart in [processing_interval] ticks."
+ masterControllerAlertLevel = 5
if(5)
- admins << "Warning. The Master Controller has still not fired within the last [MC_defcon*processing_interval] ticks. Killing and restarting..."
- new /datum/controller/game_controller() //replace the old master_controller (hence killing the old one's process)
- master_controller.process() //Start it rolling again
- MC_defcon = 0
+ admins << "Warning. The master Controller has still not fired within the last [masterControllerAlertLevel * processing_interval] ticks. Killing and restarting..."
+ new /datum/controller/game_controller() // Replace the old master controller (hence killing the old one's process).
+ master_controller.process() // Start it rolling again.
+ masterControllerAlertLevel = 0
else
- MC_defcon = 0
- MC_iteration = controller_iteration
+ masterControllerAlertLevel = 0
+ masterControllerIteration = master_controller.iteration
if(lighting_controller.processing)
- if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
- switch(lighting_defcon)
+ if(lightingControllerIteration == lighting_controller.iteration) // Lighting controller hasn't finished processing in the defined interval.
+ switch(lightingControllerAlertLevel)
if(0 to 3)
- lighting_defcon++
+ lightingControllerAlertLevel++
if(4)
- admins << "Warning. The Lighting Controller has not fired in the last [lighting_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks."
- lighting_defcon = 5
+ admins << "Warning. The lighting_controller controller has not fired in the last [lightingControllerAlertLevel * processing_interval] ticks. Automatic restart in [processing_interval] ticks."
+ lightingControllerAlertLevel = 5
if(5)
- admins << "Warning. The Lighting Controller has still not fired within the last [lighting_defcon*processing_interval] ticks. Killing and restarting..."
- new /datum/controller/lighting() //replace the old lighting_controller (hence killing the old one's process)
- lighting_controller.process() //Start it rolling again
- lighting_defcon = 0
+ admins << "Warning. The lighting_controller controller has still not fired within the last [lightingControllerAlertLevel * processing_interval] ticks. Killing and restarting..."
+ new /datum/controller/lighting() // Replace the old lighting_controller (hence killing the old one's process).
+ lighting_controller.process() // Start it rolling again.
+ lightingControllerAlertLevel = 0
else
- lighting_defcon = 0
- lighting_iteration = lighting_controller.iteration
- else
- MC_defcon = 0
- lighting_defcon = 0
+ lightingControllerAlertLevel = 0
+ lightingControllerIteration = lighting_controller.iteration
- sleep(processing_interval)
\ No newline at end of file
+ sleep(processing_interval)
diff --git a/code/controllers/garbage.dm b/code/controllers/garbage.dm
index 412a4bdef10..3c96126c0f1 100644
--- a/code/controllers/garbage.dm
+++ b/code/controllers/garbage.dm
@@ -1,124 +1,186 @@
+#define GC_COLLECTIONS_PER_TICK 300 // Was 100.
+#define GC_COLLECTION_TIMEOUT (30 SECONDS)
+#define GC_FORCE_DEL_PER_TICK 60
+//#define GC_DEBUG
-#define GC_COLLECTIONS_PER_TICK 250 // Was 100
-#define GC_COLLECTION_TIMEOUT 100 // 10s
-var/global/datum/controller/garbage_collector/garbage
-var/global/list/uncollectable_vars=list(
- "alpha",
- "bestF",
- "bounds",
- "bound_height",
- "bound_width",
- "ckey",
- "color",
- "contents",
- "gender",
- "group",
- "key",
- //"loc",
- "locs",
- "luminosity",
- "parent",
- "parent_type",
- "step_size",
- "glide_size",
- "gc_destroyed",
- "step_x",
- "step_y",
- "step_z",
- "tag",
- "thermal_conductivity",
- "type",
- "vars",
- "verbs",
- "x",
- "y",
- "z",
-)
-/datum/controller/garbage_collector
- var/list/queue=list()
- var/list/destroyed=list()
- var/waiting=0
- var/del_everything=1
- var/turf/trashbin=null
+var/list/gc_hard_del_types = new
+var/datum/garbage_collector/garbageCollector
- New()
- trashbin=locate(0,0,CENTCOMM_Z)
+/client/proc/gc_dump_hdl()
+ set name = "(GC) Hard Del List"
+ set desc = "List types that are hard del()'d by the GC."
+ set category = "Debug"
- proc/AddTrash(var/atom/movable/A)
- if(!A)
- return
- if(del_everything)
- del(A)
- return
- A.loc=trashbin
- queue.Add(A)
- waiting++
+ for(var/A in gc_hard_del_types)
+ usr << "[A] = [gc_hard_del_types[A]]"
- proc/Pop()
- var/atom/movable/A = queue[1]
- if(!A)
- if(isnull(A))
- var/loopcheck = 0
- while(queue.Remove(null))
- loopcheck++
- if(loopcheck > 50)
- break
- return
- if(del_everything)
- del(A)
- return
- if(!istype(A,/atom/movable))
- testing("GC given a [A.type].")
- del(A)
- return
- for(var/vname in A.vars)
- if(!issaved(A.vars[vname]))
- continue
- if(vname in uncollectable_vars)
- continue
- //testing("Unsetting [vname] in [A.type]!")
- A.vars[vname]=null
- A.loc=null
- destroyed.Add("\ref[A]")
- queue.Remove(A)
+/datum/garbage_collector
+ var/list/queue = new
+ var/del_everything = 0
- proc/process()
- for(var/i=0;i= world.time - GC_COLLECTION_TIMEOUT)
- // Something's still referring to the qdel'd object. Kill it.
- del(A)
- destroyed.Remove(refID)
+ // To let them know how hardworking am I :^).
+ var/dels_count = 0
+ var/hard_dels = 0
+ var/soft_dels = 0
-/**
-* NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
-* OTHER TYPES CANNOT BE QDEL'D BECAUSE THEIR LOC IS LOCKED OR THEY DON'T HAVE ONE.
-*/
-/proc/qdel(var/atom/movable/A)
- if(!A) return
- if(!istype(A))
- warning("qdel() passed object of type [A.type]. qdel() can only handle /atom/movable types.")
- del(A)
+/datum/garbage_collector/proc/addTrash(const/atom/movable/AM)
+ if(!istype(AM))
return
- if(!garbage)
- del(A)
+
+ if(del_everything)
+ del(AM)
+ hard_dels++
+ dels_count++
return
- // Let our friend know they're about to get fucked up.
- A.Destroy()
- garbage.AddTrash(A)
+
+ queue["\ref[AM]"] = world.timeofday
+
+/datum/garbage_collector/proc/process()
+ var/remainingCollectionPerTick = GC_COLLECTIONS_PER_TICK
+ var/remainingForceDelPerTick = GC_FORCE_DEL_PER_TICK
+ var/collectionTimeScope = world.timeofday - GC_COLLECTION_TIMEOUT
+ if(narsie_cometh) return //don't even fucking bother, its over.
+ while(queue.len && --remainingCollectionPerTick >= 0)
+ var/refID = queue[1]
+ var/destroyedAtTime = queue[refID]
+
+ if(destroyedAtTime > collectionTimeScope)
+ break
+
+ var/atom/movable/AM = locate(refID)
+ if(AM) // Something's still referring to the qdel'd object. del it.
+ if(isnull(AM.gcDestroyed))
+ queue -= refID
+ continue
+ if(remainingForceDelPerTick <= 0)
+ break
+
+ #ifdef GC_DEBUG
+ WARNING("gc process force delete [AM.type]")
+ #endif
+
+ AM.hard_deleted = 1
+ if(!AM.type in gc_hard_del_types)
+ gc_hard_del_types += AM.type
+ del AM
+
+ hard_dels++
+ remainingForceDelPerTick--
+
+#ifdef GC_DEBUG
+#undef GC_DEBUG
+#endif
+
+#undef GC_FORCE_DEL_PER_TICK
+#undef GC_COLLECTION_TIMEOUT
+#undef GC_COLLECTIONS_PER_TICK
+
+/datum/garbage_collector/proc/dequeue(id)
+ if (queue)
+ queue -= id
+
+ dels_count++
+
+/*
+ * NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
+ * OTHER TYPES CANNOT BE QDEL'D BECAUSE THEIR LOC IS LOCKED OR THEY DON'T HAVE ONE.
+ */
+/proc/qdel(const/atom/movable/AM, ignore_pooling = 0)
+ if(isnull(AM))
+ return
+
+ if(isnull(garbageCollector))
+ del(AM)
+ return
+
+ if(!istype(AM))
+ WARNING("qdel() passed object of type [AM.type]. qdel() can only handle /atom/movable types.")
+ if(!AM.type in gc_hard_del_types)
+ gc_hard_del_types += AM.type
+ del(AM)
+ garbageCollector.hard_dels++
+ garbageCollector.dels_count++
+ return
+
+ //We are object pooling this.
+ if(("[AM.type]" in masterPool) && !ignore_pooling)
+ returnToPool(AM)
+ return
+
+ if(isnull(AM.gcDestroyed))
+ // Let our friend know they're about to get fucked up.
+ AM.Destroy()
+
+ garbageCollector.addTrash(AM)
+
+/datum/controller
+ var/processing = 0
+ var/iteration = 0
+ var/processing_interval = 0
+
+/datum/controller/proc/recover() // If we are replacing an existing controller (due to a crash) we attempt to preserve as much as we can.
+
+/*
+ * Like Del(), but for qdel.
+ * Called BEFORE qdel moves shit.
+ */
+/datum/proc/Destroy()
+ del(src)
/client/proc/qdel_toggle()
set name = "Toggle qdel Behavior"
set desc = "Toggle qdel usage between normal and force del()."
set category = "Debug"
- garbage.del_everything = !garbage.del_everything
- world << "GC: qdel turned [garbage.del_everything?"off":"on"]."
- log_admin("[key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].")
- message_admins("\blue [key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].", 1)
\ No newline at end of file
+ garbageCollector.del_everything = !garbageCollector.del_everything
+ world << "GC: qdel turned [garbageCollector.del_everything ? "off" : "on"]."
+ log_admin("[key_name(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].")
+ message_admins("\blue [key_name(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].", 1)
+
+/*/client/var/running_find_references
+
+/atom/verb/find_references()
+ set category = "Debug"
+ set name = "Find References"
+ set background = 1
+ set src in world
+
+ if(!usr || !usr.client)
+ return
+
+ if(usr.client.running_find_references)
+ testing("CANCELLED search for references to a [usr.client.running_find_references].")
+ usr.client.running_find_references = null
+ return
+
+ if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
+ return
+ qdel(src)
+ // Remove this object from the list of things to be auto-deleted.
+ if(garbageCollector)
+ garbageCollector.queue -= "\ref[src]"
+
+ usr.client.running_find_references = type
+ testing("Beginning search for references to a [type].")
+ var/list/things = list()
+ for(var/client/thing)
+ things += thing
+ for(var/datum/thing)
+ things += thing
+ for(var/atom/thing)
+ things += thing
+ for(var/event/thing)
+ things += thing
+ testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
+ for(var/datum/thing in things)
+ if(!usr.client.running_find_references) return
+ for(var/varname in thing.vars)
+ var/variable = thing.vars[varname]
+ if(variable == src)
+ testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
+ else if(islist(variable))
+ if(src in variable)
+ testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
+ testing("Completed search for references to a [type].")
+ usr.client.running_find_references = null
+*/
\ No newline at end of file
diff --git a/code/controllers/lighting_controller.dm b/code/controllers/lighting_controller.dm
index c118fa43abb..a7decbc1185 100644
--- a/code/controllers/lighting_controller.dm
+++ b/code/controllers/lighting_controller.dm
@@ -1,10 +1,8 @@
var/datum/controller/lighting/lighting_controller = new ()
datum/controller/lighting
- var/processing = 0
- var/processing_interval = 5 //setting this too low will probably kill the server. Don't be silly with it!
+ processing_interval = 5 //setting this too low will probably kill the server. Don't be silly with it!
var/process_cost = 0
- var/iteration = 0
var/lighting_states = 7
@@ -21,7 +19,7 @@ datum/controller/lighting/New()
if(lighting_controller != src)
if(istype(lighting_controller,/datum/controller/lighting))
Recover() //if we are replacing an existing lighting_controller (due to a crash) we attempt to preserve as much as we can
- del(lighting_controller)
+ qdel(lighting_controller)
lighting_controller = src
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 3aacb25c3fc..963e01b7260 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -5,14 +5,12 @@
var/global/datum/controller/game_controller/master_controller //Set in world.New()
var/global/controller_iteration = 0
-var/global/last_tick_timeofday = world.timeofday
var/global/last_tick_duration = 0
var/global/air_processing_killed = 0
var/global/pipe_processing_killed = 0
datum/controller/game_controller
- var/processing = 0
var/breather_ticks = 2 //a somewhat crude attempt to iron over the 'bumps' caused by high-cpu use by letting the MC have a breather for this many ticks after every loop
var/minimum_ticks = 20 //The minimum length of time between MC ticks
@@ -29,7 +27,7 @@ datum/controller/game_controller
var/events_cost = 0
var/puddles_cost
var/ticker_cost = 0
- var/gc_cost = 0
+ var/garbageCollectorCost = 0
var/total_cost = 0
var/last_thing_processed
@@ -37,13 +35,14 @@ datum/controller/game_controller
var/list/shuttle_list // For debugging and VV
var/datum/ore_distribution/asteroid_ore_map // For debugging and VV.
+ var/global/datum/garbage_collector/garbageCollector
datum/controller/game_controller/New()
//There can be only one master_controller. Out with the old and in with the new.
if(master_controller != src)
if(istype(master_controller))
Recover()
- del(master_controller)
+ qdel(master_controller)
master_controller = src
if(!job_master)
@@ -54,8 +53,8 @@ datum/controller/game_controller/New()
if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase()
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
- if(!emergency_shuttle) emergency_shuttle = new /datum/emergency_shuttle_controller()
- if(!shuttle_controller) shuttle_controller = new /datum/shuttle_controller()
+ //if(!emergency_shuttle) emergency_shuttle = new /datum/emergency_shuttle_controller() MOVED TO SCHEDULER
+ //if(!shuttle_controller) shuttle_controller = new /datum/shuttle_controller()
datum/controller/game_controller/proc/setup()
world.tick_lag = config.Ticklag
@@ -63,15 +62,15 @@ datum/controller/game_controller/proc/setup()
spawn(20)
createRandomZlevel()
+ /* MOVED TO SCHEDULER
if(!air_master)
air_master = new /datum/controller/air_system()
air_master.Setup()
if(!ticker)
ticker = new /datum/controller/gameticker()
+ */
- if(!garbage)
- garbage = new /datum/controller/garbage_collector()
color_windows_init()
setup_objects()
@@ -82,15 +81,17 @@ datum/controller/game_controller/proc/setup()
for(var/i=0, i= R.reqs[A])
- continue main_loop
- return 0
- for(var/A in R.chem_catalists)
- if(holder_contents[A] < R.chem_catalists[A])
- return 0
- return 1
-
-/datum/crafting_holder/proc/check_holder()
- var/list/holder_contents = list()
- for(var/obj/I in holder.loc)
- if(istype(I, /obj/item/stack))
- var/obj/item/stack/S = I
- holder_contents[I.type] += S.amount
- else
- if(istype(I, /obj/item/weapon/reagent_containers))
- for(var/datum/reagent/R in I.reagents.reagent_list)
- holder_contents[R.type] += R.volume
-
- holder_contents[I.type] += 1
-
- return holder_contents
-
-/datum/crafting_holder/proc/check_tools(mob/user, datum/crafting_recipe/R, list/holder_contents)
- if(!R.tools.len)
- return 1
- var/list/possible_tools = list()
- for(var/obj/item/I in user.contents)
- if(istype(I, /obj/item/weapon/storage))
- for(var/obj/item/SI in I.contents)
- possible_tools += SI.type
- else
- possible_tools += I.type
- possible_tools += holder_contents
- var/i = R.tools.len
- var/I
- for(var/A in R.tools)
- I = possible_tools.Find(A)
- if(I)
- possible_tools.Cut(I, I+1)
- i--
- else
- break
- return !i
-
-/datum/crafting_holder/proc/construct_item(mob/user, datum/crafting_recipe/R)
- var/list/holder_contents = check_holder()
- if(check_contents(R, holder_contents) && check_tools(user, R, holder_contents))
- if(do_after(user, R.time))
- if(!check_contents(R, holder_contents) || !check_tools(user, R, holder_contents))
- return 0
- var/list/parts = del_reqs(R, holder_contents)
- var/atom/movable/I = new R.result_path
- for(var/A in parts)
- if(istype(A, /obj/item))
- var/atom/movable/B = A
- B.loc = I
- else
- if(!I.reagents)
- I.reagents = new /datum/reagents()
- I.reagents.reagent_list.Add(A)
- I.CheckParts()
- I.loc = holder.loc
- return 1
- return 0
-
-/datum/crafting_holder/proc/del_reqs(datum/crafting_recipe/R, list/holder_contents)
- var/list/Deletion = list()
- var/amt
- for(var/A in R.reqs)
- amt = R.reqs[A]
- if(ispath(A, /obj/item/stack))
- var/obj/item/stack/S
- stack_loop:
- for(var/B in holder_contents)
- if(ispath(B, A))
- while(amt > 0)
- S = locate(B) in holder.loc
- if(S.amount >= amt)
- S.use(amt)
- break stack_loop
- else
- amt -= S.amount
- qdel(S)
- else if(ispath(A, /obj/item))
- var/obj/item/I
- item_loop:
- for(var/B in holder_contents)
- if(ispath(B, A))
- while(amt > 0)
- I = locate(B) in holder.loc
- Deletion.Add(I)
- amt--
- break item_loop
- else
- var/datum/reagent/RG = new A
- reagent_loop:
- for(var/B in holder_contents)
- if(ispath(B, /obj/item/weapon/reagent_containers))
- var/obj/item/RC = locate(B) in holder.loc
- if(RC.reagents.has_reagent(RG.id, amt))
- RC.reagents.remove_reagent(RG.id, amt)
- RG.volume = amt
- Deletion.Add(RG)
- break reagent_loop
- else if(RC.reagents.has_reagent(RG.id))
- Deletion.Add(RG)
- RG.volume += RC.reagents.get_reagent_amount(RG.id)
- amt -= RC.reagents.get_reagent_amount(RG.id)
- RC.reagents.del_reagent(RG.id)
-
- for(var/A in R.parts)
- for(var/B in Deletion)
- if(!istype(B, A))
- Deletion.Remove(B)
- qdel(B)
- return Deletion
-
-/datum/crafting_holder/proc/interact(mob/user)
- var/list/holder_contents = check_holder()
- if(!holder_contents.len)
- return
- var/dat = "
Construction menu
"
- dat += "
"
- if(busy)
- dat += "Construction inprogress...
"
- else
- for(var/A in recipes)
- var/datum/crafting_recipe/R = recipes[A]
- if(check_contents(R, holder_contents))
- dat += "[R.name] "
- dat += ""
-
- var/datum/browser/popup = new(user, "craft", "Craft", 300, 300)
- popup.set_content(dat)
- popup.open()
- return
-
-/datum/crafting_holder/Topic(href, href_list)
- if(usr.stat || !holder.Adjacent(usr) || usr.lying)
- return
- if(href_list["make"])
- if(busy)
- return
- busy = 1
- interact(usr)
- var/datum/crafting_recipe/TR = locate(href_list["make"])
- if(construct_item(usr, TR))
- usr << "[TR.name] constructed."
- else
- usr << "Construction failed."
- busy = 0
- interact(usr)
diff --git a/code/datums/crafting/recipes.dm b/code/datums/crafting/recipes.dm
deleted file mode 100644
index 6dda4d79622..00000000000
--- a/code/datums/crafting/recipes.dm
+++ /dev/null
@@ -1,137 +0,0 @@
-//Basic crafting components -- Using cheap easily found items to create components that can be used to make more complex objects
-
-/datum/crafting_recipe/table/igniter
- name = "Igniter"
- result_path = /obj/item/device/assembly/igniter
- reqs = list(/obj/item/weapon/lighter = 1,
- /obj/item/stack/cable_coil = 1)
- time = 20
-
-/datum/crafting_recipe/table/voice
- name = "Voice analyzer"
- result_path = /obj/item/device/assembly/voice
- reqs = list(/obj/item/device/taperecorder = 1,
- /obj/item/stack/cable_coil = 1)
- time = 20
-
-/datum/crafting_recipe/table/infra
- name = "Infrared Emitter"
- result_path = /obj/item/device/assembly/infra
- reqs = list(/obj/item/device/laser_pointer = 1,
- /obj/item/stack/cable_coil = 1)
- time = 20
-
-//Medium crafting
-
-/datum/crafting_recipe/table/IED
- name = "IED"
- result_path = /obj/item/weapon/grenade/iedcasing
- reqs = list(/obj/item/stack/cable_coil = 1,
- /obj/item/device/assembly/igniter = 1,
- /obj/item/weapon/reagent_containers/food/drinks/cans = 1,
- /datum/reagent/fuel = 10)
- time = 80
-
-/datum/crafting_recipe/table/stunprod
- name = "Stunprod"
- result_path = /obj/item/weapon/melee/baton/cattleprod
- reqs = list(/obj/item/weapon/handcuffs/cable = 1,
- /obj/item/stack/rods = 1,
- /obj/item/weapon/wirecutters = 1,
- /obj/item/weapon/cell = 1)
- time = 80
- parts = list(/obj/item/weapon/cell = 1)
-
-/datum/crafting_recipe/table/flamethrower
- name = "Flamethrower"
- result_path = /obj/item/weapon/flamethrower
- reqs = list(/obj/item/weapon/weldingtool = 1,
- /obj/item/device/assembly/igniter = 1,
- /obj/item/stack/rods = 2)
- tools = list(/obj/item/weapon/screwdriver)
- time = 20
-
-/datum/crafting_recipe/table/meteorshot
- name = "Meteorshot Shell"
- result_path = /obj/item/ammo_casing/shotgun/meteorshot
- reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1,
- /obj/item/weapon/rcd_ammo = 1,
- /obj/item/weapon/stock_parts/manipulator = 2)
- tools = list(/obj/item/weapon/screwdriver)
- time = 5
-
-/datum/crafting_recipe/table/pulseslug
- name = "Pulse Slug Shell"
- result_path = /obj/item/ammo_casing/shotgun/pulseslug
- reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1,
- /obj/item/weapon/stock_parts/capacitor/adv = 2,
- /obj/item/weapon/stock_parts/micro_laser/ultra = 1)
- tools = list(/obj/item/weapon/screwdriver)
- time = 5
-
-/datum/crafting_recipe/table/dragonsbreath
- name = "Dragonsbreath Shell"
- result_path = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
- reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1,
- /datum/reagent/phosphorus = 5,)
- tools = list(/obj/item/weapon/screwdriver)
- time = 5
-
-//Advanced crafting
-
-/datum/crafting_recipe/table/ed209
- name = "ED209"
- result_path = /obj/machinery/bot/ed209
- reqs = list(/obj/item/robot_parts/robot_suit = 1,
- /obj/item/clothing/head/helmet = 1,
- /obj/item/clothing/suit/armor/vest = 1,
- /obj/item/robot_parts/l_leg = 1,
- /obj/item/robot_parts/r_leg = 1,
- /obj/item/stack/sheet/metal = 5,
- /obj/item/stack/cable_coil = 5,
- /obj/item/weapon/gun/energy/advtaser = 1,
- /obj/item/weapon/cell = 1,
- /obj/item/device/assembly/prox_sensor = 1,
- /obj/item/robot_parts/r_arm = 1)
- tools = list(/obj/item/weapon/weldingtool, /obj/item/weapon/screwdriver)
- time = 120
-
-/datum/crafting_recipe/table/secbot
- name = "Secbot"
- result_path = /obj/machinery/bot/secbot
- reqs = list(/obj/item/device/assembly/signaler = 1,
- /obj/item/clothing/head/helmet = 1,
- /obj/item/weapon/melee/baton = 1,
- /obj/item/device/assembly/prox_sensor = 1,
- /obj/item/robot_parts/r_arm = 1)
- tools = list(/obj/item/weapon/weldingtool)
- time = 120
-
-/datum/crafting_recipe/table/cleanbot
- name = "Cleanbot"
- result_path = /obj/machinery/bot/cleanbot
- reqs = list(/obj/item/weapon/reagent_containers/glass/bucket = 1,
- /obj/item/device/assembly/prox_sensor = 1,
- /obj/item/robot_parts/r_arm = 1)
- time = 80
-
-/datum/crafting_recipe/table/floorbot
- name = "Floorbot"
- result_path = /obj/machinery/bot/floorbot
- reqs = list(/obj/item/weapon/storage/toolbox/mechanical = 1,
- /obj/item/stack/tile/plasteel = 1,
- /obj/item/device/assembly/prox_sensor = 1,
- /obj/item/robot_parts/r_arm = 1)
- time = 80
-
-/datum/crafting_recipe/table/medbot
- name = "Medbot"
- result_path = /obj/machinery/bot/medbot
- reqs = list(/obj/item/device/healthanalyzer = 1,
- /obj/item/weapon/storage/firstaid = 1,
- /obj/item/device/assembly/prox_sensor = 1,
- /obj/item/robot_parts/r_arm = 1)
- time = 80
-
-
-/////////////////////////////////////////////////////////
\ No newline at end of file
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index d202f29d8f9..f3062199ec7 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -441,7 +441,7 @@ client
usr << "This can only be used on instances of type /mob"
return
- var/new_name = copytext(sanitize(input(usr,"What would you like to name this mob?","Input a name",M.real_name) as text|null),1,MAX_NAME_LEN)
+ var/new_name = sanitize(copytext(input(usr,"What would you like to name this mob?","Input a name",M.real_name) as text|null,1,MAX_NAME_LEN))
if( !new_name || !M ) return
message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].")
@@ -627,7 +627,7 @@ client
for(var/obj/Obj in world)
if(Obj.type == O_type)
i++
- del(Obj)
+ qdel(Obj)
if(!i)
usr << "No objects of this type exist"
return
@@ -638,7 +638,7 @@ client
for(var/obj/Obj in world)
if(istype(Obj,O_type))
i++
- del(Obj)
+ qdel(Obj)
if(!i)
usr << "No objects of this type exist"
return
diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm
index ffede1e6957..0eb4d73dfde 100644
--- a/code/datums/helper_datums/construction_datum.dm
+++ b/code/datums/helper_datums/construction_datum.dm
@@ -231,7 +231,7 @@
user.visible_message(fixText(state["vis_msg"],user),fixText(state["self_msg"],user))
if("delete" in state)
- del(used_atom)
+ qdel(used_atom)
else if("spawn" in state)
var/spawntype=state["spawn"]
var/atom/A = new spawntype(holder.loc)
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index a4cde9f99e0..70024bf68d6 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -49,7 +49,7 @@
//must succeed in most cases
proc/setTeleatom(atom/movable/ateleatom)
if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon))
- del(ateleatom)
+ qdel(ateleatom)
return 0
if(istype(ateleatom))
teleatom = ateleatom
@@ -190,7 +190,7 @@
teleatom.visible_message("\red The [teleatom] bounces off of the portal!")
return 0
- if(destination.z == 2) //centcomm z-level
+ if((destination.z in config.admin_levels)) //centcomm z-level
if(istype(teleatom, /obj/mecha))
var/obj/mecha/MM = teleatom
MM.occupant << "\red The mech would not survive the jump to a location so far away!"
@@ -200,6 +200,6 @@
return 0
- if(destination.z > 7) //Away mission z-levels
+ if(!(destination.z in config.player_levels)) //Away mission z-levels
return 0
return 1
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 7bcb54660e8..905d47d36b8 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -70,11 +70,6 @@ datum/mind
if(!istype(new_character))
world.log << "## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn"
if(current) //remove ourself from our old body's mind variable
- if(changeling)
- current.remove_changeling_powers()
- current.verbs -= /datum/changeling/proc/EvolutionMenu
- if(vampire)
- current.remove_vampire_powers()
current.mind = null
if(new_character.mind) //remove any mind currently in our new body's mind variable
@@ -85,10 +80,6 @@ datum/mind
current = new_character //link ourself to our new body
new_character.mind = src //and link our new body to ourself
- if(changeling)
- new_character.make_changeling()
- if(vampire)
- new_character.make_vampire()
if(active)
new_character.key = key //now transfer the key to link the client to our new body
@@ -455,7 +446,7 @@ datum/mind
assigned_role = new_role
else if (href_list["memory_edit"])
- var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN)
+ var/new_memo = sanitize(copytext(input("Write new memory", "Memory", memory) as null|message,1,MAX_MESSAGE_LEN))
if (isnull(new_memo)) return
memory = new_memo
@@ -475,7 +466,7 @@ datum/mind
if(!def_value)//If it's a custom objective, it will be an empty string.
def_value = "custom"
- var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "harm", "speciesist", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "custom")
+ var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "harm", "speciesist", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom")
if (!new_obj_type) return
var/datum/objective/new_objective = null
@@ -588,8 +579,25 @@ datum/mind
new_objective.owner = src
new_objective.target_amount = target_number
+ if("identity theft")
+ var/list/possible_targets = list("Free objective")
+ for(var/datum/mind/possible_target in ticker.minds)
+ if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human))
+ possible_targets += possible_target.current
+
+ var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets
+ if (!new_target)
+ return
+ var/datum/mind/targ = new_target
+ if(!istype(targ))
+ log_debug("Invalid target for identity theft objective, cancelling")
+ return
+ new_objective = new /datum/objective/escape/escape_with_identity
+ new_objective.owner = src
+ new_objective.target = new_target
+ new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing their identification card."
if ("custom")
- var/expl = copytext(sanitize(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null),1,MAX_MESSAGE_LEN)
+ var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN))
if (!expl) return
new_objective = new /datum/objective
new_objective.owner = src
@@ -727,7 +735,7 @@ datum/mind
var/obj/item/device/flash/flash = locate() in L
if (!flash)
usr << "\red Deleting flash failed!"
- del(flash)
+ qdel(flash)
if("repairflash")
var/list/L = current.get_contents()
@@ -740,7 +748,7 @@ datum/mind
if("reequip")
var/list/L = current.get_contents()
var/obj/item/device/flash/flash = locate() in L
- del(flash)
+ qdel(flash)
take_uplink()
var/fail = 0
fail |= !ticker.mode.equip_traitor(current, 1)
@@ -843,7 +851,6 @@ datum/mind
ticker.mode.changelings -= src
special_role = null
current.remove_changeling_powers()
- current.verbs -= /datum/changeling/proc/EvolutionMenu
if(changeling) del(changeling)
current << "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!"
log_admin("[key_name_admin(usr)] has de-changeling'ed [current].")
@@ -920,17 +927,17 @@ datum/mind
if("lair")
current.loc = get_turf(locate("landmark*Syndicate-Spawn"))
if("dressup")
- del(H.belt)
- del(H.back)
- del(H.l_ear)
- del(H.r_ear)
- del(H.gloves)
- del(H.head)
- del(H.shoes)
- del(H.wear_id)
- del(H.wear_pda)
- del(H.wear_suit)
- del(H.w_uniform)
+ qdel(H.belt)
+ qdel(H.back)
+ qdel(H.l_ear)
+ qdel(H.r_ear)
+ qdel(H.gloves)
+ qdel(H.head)
+ qdel(H.shoes)
+ qdel(H.wear_id)
+ qdel(H.wear_pda)
+ qdel(H.wear_suit)
+ qdel(H.w_uniform)
if (!ticker.mode.equip_syndicate(current))
usr << "\red Equipping a syndicate failed!"
@@ -1075,7 +1082,7 @@ datum/mind
switch(href_list["common"])
if("undress")
for(var/obj/item/W in current)
- current.drop_from_inventory(W)
+ current.unEquip(W, 1)
if("takeuplink")
take_uplink()
memory = null//Remove any memory they may have had.
@@ -1143,7 +1150,7 @@ datum/mind
proc/take_uplink()
var/obj/item/device/uplink/hidden/H = find_syndicate_uplink()
if(H)
- del(H)
+ qdel(H)
proc/make_AI_Malf()
@@ -1184,17 +1191,17 @@ datum/mind
current.loc = get_turf(locate("landmark*Syndicate-Spawn"))
var/mob/living/carbon/human/H = current
- del(H.belt)
- del(H.back)
- del(H.l_ear)
- del(H.r_ear)
- del(H.gloves)
- del(H.head)
- del(H.shoes)
- del(H.wear_id)
- del(H.wear_pda)
- del(H.wear_suit)
- del(H.w_uniform)
+ qdel(H.belt)
+ qdel(H.back)
+ qdel(H.l_ear)
+ qdel(H.r_ear)
+ qdel(H.gloves)
+ qdel(H.head)
+ qdel(H.shoes)
+ qdel(H.wear_id)
+ qdel(H.wear_pda)
+ qdel(H.wear_suit)
+ qdel(H.w_uniform)
ticker.mode.equip_syndicate(current)
@@ -1283,7 +1290,7 @@ datum/mind
var/list/L = current.get_contents()
var/obj/item/device/flash/flash = locate() in L
- del(flash)
+ qdel(flash)
take_uplink()
var/fail = 0
// fail |= !ticker.mode.equip_traitor(current, 1)
diff --git a/code/datums/periodic_news.dm b/code/datums/periodic_news.dm
index 976ef536cb4..ffa52bffdc6 100644
--- a/code/datums/periodic_news.dm
+++ b/code/datums/periodic_news.dm
@@ -8,6 +8,7 @@
author = "Nanotrasen Editor"
channel_name = "Tau Ceti Daily"
can_be_redacted = 0
+ message_type = "Story"
revolution_inciting_event
@@ -129,12 +130,6 @@ proc/check_for_newscaster_updates(type)
proc/announce_newscaster_news(datum/news_announcement/news)
- var/datum/feed_message/newMsg = new /datum/feed_message
- newMsg.author = news.author
- newMsg.is_admin_message = !news.can_be_redacted
-
- newMsg.body = news.message
-
var/datum/feed_channel/sendto
for(var/datum/feed_channel/FC in news_network.network_channels)
if(FC.channel_name == news.channel_name)
@@ -148,6 +143,12 @@ proc/announce_newscaster_news(datum/news_announcement/news)
sendto.locked = 1
sendto.is_admin_channel = 1
news_network.network_channels += sendto
+
+ var/datum/feed_message/newMsg = new /datum/feed_message
+ newMsg.author = news.author ? news.author : sendto.author
+ newMsg.is_admin_message = !news.can_be_redacted
+ newMsg.body = news.message
+ newMsg.message_type = news.message_type
sendto.messages += newMsg
diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm
index d2c747dd5b8..4f2750c0c06 100644
--- a/code/datums/recipe.dm
+++ b/code/datums/recipe.dm
@@ -79,7 +79,7 @@
var/obj/result_obj = new result(container)
for (var/obj/O in (container.contents-result_obj))
O.reagents.trans_to(result_obj, O.reagents.total_volume)
- del(O)
+ qdel(O)
container.reagents.clear_reagents()
return result_obj
@@ -91,7 +91,7 @@
O.reagents.del_reagent("nutriment")
O.reagents.update_total()
O.reagents.trans_to(result_obj, O.reagents.total_volume)
- del(O)
+ qdel(O)
container.reagents.clear_reagents()
return result_obj
diff --git a/code/datums/spell.dm b/code/datums/spell.dm
index d6c2350510d..e03f172f2a2 100644
--- a/code/datums/spell.dm
+++ b/code/datums/spell.dm
@@ -48,7 +48,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
var/icon_power_button
var/power_button_name
-/obj/effect/proc_holder/spell/wizard/proc/cast_check(skipcharge = 0, mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
+/obj/effect/proc_holder/spell/wizard/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
if(!(src in user.spell_list))
user << "You shouldn't have this spell! Something's wrong."
@@ -60,7 +60,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
caster.reset_view(0)
return 0
- if(user.z == 2 && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
+ if((user.z in config.admin_levels) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
return 0
if(!skipcharge)
@@ -75,27 +75,27 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
return 0
if(!ghost)
- if(usr.stat && !stat_allowed)
- usr << "Not when you're incapacitated."
+ if(user.stat && !stat_allowed)
+ user << "Not when you're incapacitated."
return 0
- if(ishuman(usr) || ismonkey(usr))
- if(istype(usr.wear_mask, /obj/item/clothing/mask/muzzle))
- usr << "Mmmf mrrfff!"
+ if(ishuman(user) || ismonkey(user))
+ if(user.is_muzzled())
+ user << "Mmmf mrrfff!"
return 0
var/obj/effect/proc_holder/spell/wizard/noclothes/spell = locate() in user.spell_list
if(clothes_req && !(spell && istype(spell)))//clothes check
- if(!istype(usr, /mob/living/carbon/human))
- usr << "You aren't a human, Why are you trying to cast a human spell, silly non-human? Casting human spells is for humans."
+ if(!istype(user, /mob/living/carbon/human))
+ user << "You aren't a human, Why are you trying to cast a human spell, silly non-human? Casting human spells is for humans."
return 0
- if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(user:wear_suit, /obj/item/clothing/suit/space/rig/wizard))
- usr << "I don't feel strong enough without my robe."
+ if(!istype(user:wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(user:wear_suit, /obj/item/clothing/suit/space/rig/wizard))
+ user << "I don't feel strong enough without my robe."
return 0
- if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal))
- usr << "I don't feel strong enough without my sandals."
+ if(!istype(user:shoes, /obj/item/clothing/shoes/sandal))
+ user << "I don't feel strong enough without my sandals."
return 0
- if(!istype(usr:head, /obj/item/clothing/head/wizard) && !istype(usr:head, /obj/item/clothing/head/helmet/space/rig/wizard))
- usr << "I don't feel strong enough without my hat."
+ if(!istype(user:head, /obj/item/clothing/head/wizard) && !istype(user:head, /obj/item/clothing/head/helmet/space/rig/wizard))
+ user << "I don't feel strong enough without my hat."
return 0
if(!skipcharge)
diff --git a/code/datums/spells/charge.dm b/code/datums/spells/charge.dm
index b4603f5e6b1..cabde8c7181 100644
--- a/code/datums/spells/charge.dm
+++ b/code/datums/spells/charge.dm
@@ -53,8 +53,8 @@
W.icon_state = initial(W.icon_state)
charged_item = I
break
- else if(istype(item, /obj/item/weapon/cell/))
- var/obj/item/weapon/cell/C = item
+ else if(istype(item, /obj/item/weapon/stock_parts/cell/))
+ var/obj/item/weapon/stock_parts/cell/C = item
if(prob(80))
C.maxcharge -= 200
if(C.maxcharge <= 1) //Div by 0 protection
@@ -66,8 +66,8 @@
else if(item.contents)
var/obj/I = null
for(I in item.contents)
- if(istype(I, /obj/item/weapon/cell/))
- var/obj/item/weapon/cell/C = I
+ if(istype(I, /obj/item/weapon/stock_parts/cell/))
+ var/obj/item/weapon/stock_parts/cell/C = I
if(prob(80))
C.maxcharge -= 200
if(C.maxcharge <= 1) //Div by 0 protection
diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm
index 45be50f0b75..f9d327a5723 100644
--- a/code/datums/spells/genetic.dm
+++ b/code/datums/spells/genetic.dm
@@ -20,8 +20,8 @@
for(var/mob/living/target in targets)
for(var/x in mutations)
target.mutations.Add(x)
- if(x == M_HULK && ishuman(target))
- target:hulk_time=world.time + duration
+ /* if(x == HULK && ishuman(target))
+ target:hulk_time=world.time + duration */
target.disabilities |= disabilities
target.update_mutations() //update target's mutation overlays
spawn(duration)
diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm
index b04969d2d98..5f45dc41aaa 100644
--- a/code/datums/spells/horsemask.dm
+++ b/code/datums/spells/horsemask.dm
@@ -26,7 +26,7 @@
if(!target)
return
- if(target.type in compatible_mobs || ishuman(target))
+ if((target.type in compatible_mobs) || ishuman(target))
user << "It'd be stupid to curse [target] with a horse's head!"
return
@@ -35,12 +35,13 @@
return
var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead
- magichead.canremove = 0 //curses!
+ magichead.flags |= NODROP //curses!
magichead.flags_inv = null //so you can still see their face
magichead.voicechange = 1 //NEEEEIIGHH
target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \
"Your face burns up, and shortly after the fire you realise you have the face of a horse!")
- target.drop_from_inventory(target.wear_mask)
+ if(!target.unEquip(target.wear_mask))
+ del target.wear_mask
target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1)
flick("e_flash", target.flash)
diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm
index cca82c3c0c0..05b835605d4 100644
--- a/code/datums/spells/summonitem.dm
+++ b/code/datums/spells/summonitem.dm
@@ -59,7 +59,7 @@
item_to_retrive = null
break
- M.u_equip(item_to_retrive)
+ M.unEquip(item_to_retrive)
if(ishuman(M)) //Edge case housekeeping
var/mob/living/carbon/human/C = M
diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm
index 5672052e0a2..ae507556181 100644
--- a/code/datums/spells/wizard.dm
+++ b/code/datums/spells/wizard.dm
@@ -48,7 +48,7 @@
include_user = 1
centcom_cancast = 0
- mutations = list(M_LASER, M_HULK)
+ mutations = list(LASER, HULK)
duration = 300
cooldown_min = 300 //25 deciseconds reduction per rank
diff --git a/code/datums/sun.dm b/code/datums/sun.dm
index 1379a7b9d87..eafbcd1ee0c 100644
--- a/code/datums/sun.dm
+++ b/code/datums/sun.dm
@@ -1,5 +1,7 @@
#define SOLAR_UPDATE_TIME 600 //duration between two updates of the whole sun/solars positions
+var/global/datum/sun/sun
+
/datum/sun
var/angle
var/dx
@@ -17,10 +19,11 @@
solar_next_update = world.time // init the timer
angle = rand (0,360) // the station position to the sun is randomised at round start
+/* HANDLED IN PROCESS SCHEDULER
/hook/startup/proc/createSun()
sun = new /datum/sun()
return 1
-
+*/
// calculate the sun's position given the time of day
// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute.
// a full rotation thus take a game hour in that case
@@ -50,54 +53,9 @@
dx = s/abs(s)
dy = c / abs(s)
-
- for(var/obj/machinery/power/M in solars_list)
-
- if(!M.powernet)
- solars_list.Remove(M)
+ //now tell the solar control computers to update their status and linked devices
+ for(var/obj/machinery/power/solar_control/SC in solars_list)
+ if(!SC.powernet)
+ solars_list.Remove(SC)
continue
-
- // Solar Tracker
- if(istype(M, /obj/machinery/power/tracker))
- var/obj/machinery/power/tracker/T = M
- T.set_angle(angle)
-
- // Solar Control
- else if(istype(M, /obj/machinery/power/solar_control))
- var/obj/machinery/power/solar_control/C = M
- if(C.track == 1) //if manual tracking...
- C.tracker_update() //...update the position (not passing an angle, it is handled internally for manual tracking)
-
- // Solar Panel
- else if(istype(M, /obj/machinery/power/solar))
- var/obj/machinery/power/solar/S = M
- if(S.control)
- occlusion(S)
-
-
-// for a solar panel, trace towards sun to see if we're in shadow
-/datum/sun/proc/occlusion(var/obj/machinery/power/solar/S)
-
- var/ax = S.x // start at the solar panel
- var/ay = S.y
- var/turf/T = null
-
- for(var/i = 1 to 20) // 20 steps is enough
- ax += dx // do step
- ay += dy
-
- T = locate( round(ax,0.5),round(ay,0.5),S.z)
-
- if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge
- break
-
- if(T.density) // if we hit a solid turf, panel is obscured
- S.obscured = 1
- return
-
- S.obscured = 0 // if hit the edge or stepped 20 times, not obscured
- S.update_solar_exposure()
-
-
-
-
+ SC.update()
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
old mode 100755
new mode 100644
index de49b586def..8497680bd2d
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -1,1204 +1,1297 @@
-//SUPPLY PACKS
-//NOTE: only secure crate types use the access var (and are lockable)
-//NOTE: hidden packs only show up when the computer has been hacked.
-//ANOTER NOTE: Contraband is obtainable through modified supplycomp circuitboards.
-//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle.
-//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points.
-
-var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Medical / Science","Hydroponics","Organic")
-
-/datum/supply_packs
- var/name = null
- var/list/contains = list()
- var/manifest = ""
- var/amount = null
- var/cost = null
- var/containertype = null
- var/containername = null
- var/access = null
- var/hidden = 0
- var/contraband = 0
- var/group = "Operations"
-
-/datum/supply_packs/New()
- manifest += "
"
- for(var/path in contains)
- if(!path) continue
- var/atom/movable/AM = new path()
- manifest += "
[AM.name]
"
- AM.loc = null //just to make sure they're deleted by the garbage collector
- manifest += "
"
+
+////// Use the sections to keep things tidy please /Malkevin
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Emergency ///////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/emergency // Section header - use these to set default supply group and crate type for sections
+ name = "HEADER" // Use "HEADER" to denote section headers, this is needed for the supply computers to filter them
+ containertype = /obj/structure/closet/crate/internals
+ group = supply_emergency
+
+
+/datum/supply_packs/emergency/evac
+ name = "Emergency equipment"
+ contains = list(/obj/machinery/bot/floorbot,
+ /obj/machinery/bot/floorbot,
+ /obj/machinery/bot/medbot,
+ /obj/machinery/bot/medbot,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas)
+ cost = 35
+ containertype = /obj/structure/closet/crate/internals
+ containername = "emergency crate"
+ group = supply_emergency
+
+/datum/supply_packs/emergency/internals
+ name = "Internals Crate"
+ contains = list(/obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air,
+ /obj/item/weapon/tank/air)
+ cost = 10
+ containername = "internals crate"
+
+/datum/supply_packs/emergency/firefighting
+ name = "Firefighting Crate"
+ contains = list(/obj/item/clothing/suit/fire/firefighter,
+ /obj/item/clothing/suit/fire/firefighter,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/device/flashlight,
+ /obj/item/device/flashlight,
+ /obj/item/weapon/tank/oxygen/red,
+ /obj/item/weapon/tank/oxygen/red,
+ /obj/item/weapon/extinguisher,
+ /obj/item/weapon/extinguisher,
+ /obj/item/clothing/head/hardhat/red,
+ /obj/item/clothing/head/hardhat/red)
+ cost = 10
+ containertype = /obj/structure/closet/crate
+ containername = "firefighting crate"
+
+/datum/supply_packs/emergency/atmostank
+ name = "Firefighting Watertank"
+ contains = list(/obj/item/weapon/watertank/atmos)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "firefighting watertank crate"
+ access = access_atmospherics
+
+/datum/supply_packs/emergency/weedcontrol
+ name = "Weed Control Crate"
+ contains = list(/obj/item/weapon/scythe,
+ /obj/item/clothing/mask/gas,
+ /obj/item/weapon/grenade/chem_grenade/antiweed,
+ /obj/item/weapon/grenade/chem_grenade/antiweed)
+ cost = 15
+ containertype = /obj/structure/closet/crate/secure/hydrosec
+ containername = "weed control crate"
+ access = access_hydroponics
+
+/datum/supply_packs/emergency/specialops
+ name = "Special Ops supplies"
+ contains = list(/obj/item/weapon/storage/box/emps,
+ /obj/item/weapon/grenade/smokebomb,
+ /obj/item/weapon/grenade/smokebomb,
+ /obj/item/weapon/grenade/smokebomb,
+ /obj/item/weapon/pen/sleepy,
+ /obj/item/weapon/grenade/chem_grenade/incendiary)
+ cost = 20
+ containertype = /obj/structure/closet/crate
+ containername = "special ops crate"
+ hidden = 1
+
+/datum/supply_packs/emergency/syndicate
+ name = "ERROR_NULL_ENTRY"
+ contains = list(/obj/item/weapon/storage/box/syndicate)
+ cost = 140
+ containertype = /obj/structure/closet/crate
+ containername = "crate"
+ hidden = 1
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Security ////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/security
+ name = "HEADER"
+ containertype = /obj/structure/closet/crate/secure/gear
+ access = access_security
+ group = supply_security
+
+
+/datum/supply_packs/security/supplies
+ name = "Security Supplies Crate"
+ contains = list(/obj/item/weapon/storage/box/flashbangs,
+ /obj/item/weapon/storage/box/teargas,
+ /obj/item/weapon/storage/box/flashes,
+ /obj/item/weapon/storage/box/handcuffs)
+ cost = 10
+ containername = "security supply crate"
+
+////// Armor: Basic
+
+/datum/supply_packs/security/helmets
+ name = "Helmets Crate"
+ contains = list(/obj/item/clothing/head/helmet,
+ /obj/item/clothing/head/helmet,
+ /obj/item/clothing/head/helmet)
+ cost = 10
+ containername = "helmet crate"
+
+/datum/supply_packs/security/armor
+ name = "Armor Crate"
+ contains = list(/obj/item/clothing/suit/armor/vest,
+ /obj/item/clothing/suit/armor/vest,
+ /obj/item/clothing/suit/armor/vest)
+ cost = 10
+ containername = "armor crate"
+
+////// Weapons: Basic
+
+/datum/supply_packs/security/baton
+ name = "Stun Batons Crate"
+ contains = list(/obj/item/weapon/melee/baton/loaded,
+ /obj/item/weapon/melee/baton/loaded,
+ /obj/item/weapon/melee/baton/loaded)
+ cost = 10
+ containername = "stun baton crate"
+
+/datum/supply_packs/security/laser
+ name = "Lasers Crate"
+ contains = list(/obj/item/weapon/gun/energy/laser,
+ /obj/item/weapon/gun/energy/laser,
+ /obj/item/weapon/gun/energy/laser)
+ cost = 15
+ containername = "laser crate"
+
+/datum/supply_packs/security/taser
+ name = "Stun Guns Crate"
+ contains = list(/obj/item/weapon/gun/energy/advtaser,
+ /obj/item/weapon/gun/energy/advtaser,
+ /obj/item/weapon/gun/energy/advtaser)
+ cost = 15
+ containername = "stun gun crate"
+
+/datum/supply_packs/security/disabler
+ name = "Disabler Crate"
+ contains = list(/obj/item/weapon/gun/energy/disabler,
+ /obj/item/weapon/gun/energy/disabler,
+ /obj/item/weapon/gun/energy/disabler)
+ cost = 10
+ containername = "disabler crate"
+
+///// Armory stuff
+
+/datum/supply_packs/security/armory
+ name = "HEADER"
+ containertype = /obj/structure/closet/crate/secure/weapon
+ access = access_armory
+
+///// Armor: Specialist
+
+/datum/supply_packs/security/armory/riothelmets
+ name = "Riot Helmets Crate"
+ contains = list(/obj/item/clothing/head/helmet/riot,
+ /obj/item/clothing/head/helmet/riot,
+ /obj/item/clothing/head/helmet/riot)
+ cost = 15
+ containername = "riot helmets crate"
+
+/datum/supply_packs/security/armory/riotarmor
+ name = "Riot Armor Crate"
+ contains = list(/obj/item/clothing/suit/armor/riot,
+ /obj/item/clothing/suit/armor/riot,
+ /obj/item/clothing/suit/armor/riot)
+ cost = 15
+ containername = "riot armor crate"
+
+/datum/supply_packs/security/armory/riotshields
+ name = "Riot Shields Crate"
+ contains = list(/obj/item/weapon/shield/riot,
+ /obj/item/weapon/shield/riot,
+ /obj/item/weapon/shield/riot)
+ cost = 20
+ containername = "riot shields crate"
+
+/datum/supply_packs/security/bullethelmets
+ name = "Bulletproof Helmets Crate"
+ contains = list(/obj/item/clothing/head/helmet/alt,
+ /obj/item/clothing/head/helmet/alt,
+ /obj/item/clothing/head/helmet/alt)
+ cost = 10
+ containername = "bulletproof helmet crate"
+
+/datum/supply_packs/security/armory/bulletarmor
+ name = "Bulletproof Armor Crate"
+ contains = list(/obj/item/clothing/suit/armor/bulletproof,
+ /obj/item/clothing/suit/armor/bulletproof,
+ /obj/item/clothing/suit/armor/bulletproof)
+ cost = 15
+ containername = "tactical armor crate"
+
+/datum/supply_packs/security/armory/laserarmor
+ name = "Ablative Armor Crate"
+ contains = list(/obj/item/clothing/suit/armor/laserproof,
+ /obj/item/clothing/suit/armor/laserproof) // Only two vests to keep costs down for balance
+ cost = 20
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "ablative armor crate"
+
+/////// Weapons: Specialist
+
+/datum/supply_packs/security/armory/ballistic
+ name = "Combat Shotguns Crate"
+ contains = list(/obj/item/weapon/gun/projectile/shotgun/combat,
+ /obj/item/weapon/gun/projectile/shotgun/combat,
+ /obj/item/weapon/gun/projectile/shotgun/combat,
+ /obj/item/weapon/storage/belt/bandolier,
+ /obj/item/weapon/storage/belt/bandolier,
+ /obj/item/weapon/storage/belt/bandolier)
+ cost = 20
+ containername = "combat shotgun crate"
+
+/datum/supply_packs/security/armory/expenergy
+ name = "Energy Guns Crate"
+ contains = list(/obj/item/weapon/gun/energy/gun,
+ /obj/item/weapon/gun/energy/gun) // Only two guns to keep costs down
+ cost = 25
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "energy gun crate"
+
+/datum/supply_packs/security/armory/eweapons
+ name = "Incendiary Weapons Crate"
+ contains = list(/obj/item/weapon/flamethrower/full,
+ /obj/item/weapon/tank/plasma,
+ /obj/item/weapon/tank/plasma,
+ /obj/item/weapon/tank/plasma,
+ /obj/item/weapon/grenade/chem_grenade/incendiary,
+ /obj/item/weapon/grenade/chem_grenade/incendiary,
+ /obj/item/weapon/grenade/chem_grenade/incendiary)
+ cost = 15 // its a fecking flamethrower and some plasma, why the shit did this cost so much before!?
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "incendiary weapons crate"
+ access = access_heads
+
+/////// Implants & etc
+
+/datum/supply_packs/security/armory/loyalty
+ name = "Loyalty Implants Crate"
+ contains = list (/obj/item/weapon/storage/lockbox/loyalty)
+ cost = 40
+ containername = "loyalty implant crate"
+
+/datum/supply_packs/security/armory/trackingimp
+ name = "Tracking Implants Crate"
+ contains = list (/obj/item/weapon/storage/box/trackimp)
+ cost = 20
+ containername = "tracking implant crate"
+
+/datum/supply_packs/security/armory/chemimp
+ name = "Chemical Implants Crate"
+ contains = list (/obj/item/weapon/storage/box/chemimp)
+ cost = 20
+ containername = "chemical implant crate"
+
+/datum/supply_packs/security/armory/exileimp
+ name = "Exile Implants Crate"
+ contains = list (/obj/item/weapon/storage/box/exileimp)
+ cost = 30
+ containername = "exile implant crate"
+
+/datum/supply_packs/security/securitybarriers
+ name = "Security Barriers Crate"
+ contains = list(/obj/machinery/deployable/barrier,
+ /obj/machinery/deployable/barrier,
+ /obj/machinery/deployable/barrier,
+ /obj/machinery/deployable/barrier)
+ cost = 20
+ containername = "security barriers crate"
+
+/datum/supply_packs/security/securityclothes
+ name = "Security Clothing Crate"
+ contains = list(/obj/item/clothing/under/rank/security/corp,
+ /obj/item/clothing/under/rank/security/corp,
+ /obj/item/clothing/head/soft/sec/corp,
+ /obj/item/clothing/head/soft/sec/corp,
+ /obj/item/clothing/under/rank/warden/corp,
+ /obj/item/clothing/head/beret/sec/warden,
+ /obj/item/clothing/under/rank/head_of_security/corp,
+ /obj/item/clothing/head/HoS/beret)
+ cost = 30
+ containername = "security clothing crate"
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Engineering /////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/engineering
+ name = "HEADER"
+ group = supply_engineer
+
+
+/datum/supply_packs/engineering/fueltank
+ name = "Fuel Tank Crate"
+ contains = list(/obj/structure/reagent_dispensers/fueltank)
+ cost = 8
+ containertype = /obj/structure/largecrate
+ containername = "fuel tank crate"
+
+/datum/supply_packs/engineering/tools //the most robust crate
+ name = "Toolbox Crate"
+ contains = list(/obj/item/weapon/storage/toolbox/electrical,
+ /obj/item/weapon/storage/toolbox/electrical,
+ /obj/item/weapon/storage/toolbox/mechanical,
+ /obj/item/weapon/storage/toolbox/electrical,
+ /obj/item/weapon/storage/toolbox/mechanical,
+ /obj/item/weapon/storage/toolbox/mechanical)
+ cost = 10
+ containername = "electrical maintenance crate"
+
+/datum/supply_packs/engineering/powergamermitts
+ name = "Insulated Gloves Crate"
+ contains = list(/obj/item/clothing/gloves/yellow,
+ /obj/item/clothing/gloves/yellow,
+ /obj/item/clothing/gloves/yellow)
+ cost = 20 //Made of pure-grade bullshittinium
+ containername = "insulated gloves crate"
+
+/datum/supply_packs/engineering/power
+ name = "Powercell Crate"
+ contains = list(/obj/item/weapon/stock_parts/cell/high, //Changed to an extra high powercell because normal cells are useless
+ /obj/item/weapon/stock_parts/cell/high,
+ /obj/item/weapon/stock_parts/cell/high)
+ cost = 10
+ containername = "electrical maintenance crate"
+
+/datum/supply_packs/engineering/engiequipment
+ name = "Engineering Gear Crate"
+ contains = list(/obj/item/weapon/storage/belt/utility,
+ /obj/item/weapon/storage/belt/utility,
+ /obj/item/weapon/storage/belt/utility,
+ /obj/item/clothing/suit/storage/hazardvest,
+ /obj/item/clothing/suit/storage/hazardvest,
+ /obj/item/clothing/suit/storage/hazardvest,
+ /obj/item/clothing/head/welding,
+ /obj/item/clothing/head/welding,
+ /obj/item/clothing/head/welding,
+ /obj/item/clothing/head/hardhat,
+ /obj/item/clothing/head/hardhat,
+ /obj/item/clothing/head/hardhat)
+ cost = 10
+ containername = "engineering gear crate"
+
+/datum/supply_packs/engineering/solar
+ name = "Solar Pack Crate"
+ contains = list(/obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly,
+ /obj/item/solar_assembly, // 21 Solar Assemblies. 1 Extra for the controller
+ /obj/item/weapon/circuitboard/solar_control,
+ /obj/item/weapon/tracker_electronics,
+ /obj/item/weapon/paper/solar)
+ cost = 20
+ containername = "solar pack crate"
+
+/datum/supply_packs/engineering/engine
+ name = "Emitter Crate"
+ contains = list(/obj/machinery/power/emitter,
+ /obj/machinery/power/emitter)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "emitter crate"
+ access = access_ce
+
+/datum/supply_packs/engineering/engine/field_gen
+ name = "Field Generator Crate"
+ contains = list(/obj/machinery/field_generator,
+ /obj/machinery/field_generator)
+ cost = 10
+ containername = "field generator crate"
+
+/datum/supply_packs/engineering/engine/sing_gen
+ name = "Singularity Generator Crate"
+ contains = list(/obj/machinery/the_singularitygen)
+ cost = 10
+ containername = "singularity generator crate"
+
+/datum/supply_packs/engineering/engine/collector
+ name = "Collector Crate"
+ contains = list(/obj/machinery/power/rad_collector,
+ /obj/machinery/power/rad_collector,
+ /obj/machinery/power/rad_collector)
+ cost = 10
+ containername = "collector crate"
+
+/datum/supply_packs/engineering/engine/PA
+ name = "Particle Accelerator Crate"
+ contains = list(/obj/structure/particle_accelerator/fuel_chamber,
+ /obj/machinery/particle_accelerator/control_box,
+ /obj/structure/particle_accelerator/particle_emitter/center,
+ /obj/structure/particle_accelerator/particle_emitter/left,
+ /obj/structure/particle_accelerator/particle_emitter/right,
+ /obj/structure/particle_accelerator/power_box,
+ /obj/structure/particle_accelerator/end_cap)
+ cost = 25
+ containername = "particle accelerator crate"
+
+/datum/supply_packs/engineering/engine/spacesuit
+ name = "Space Suit Crate"
+ contains = list(/obj/item/clothing/suit/space,
+ /obj/item/clothing/head/helmet/space,
+ /obj/item/clothing/mask/breath,)
+ cost = 80
+ containertype = /obj/structure/closet/crate/secure
+ containername = "space suit crate"
+ access = access_eva
+
+/datum/supply_packs/engineering/inflatable
+ name = "Inflatable barriers"
+ contains = list(/obj/item/weapon/storage/briefcase/inflatable,
+ /obj/item/weapon/storage/briefcase/inflatable,
+ /obj/item/weapon/storage/briefcase/inflatable)
+ cost = 20
+ containername = "Inflatable Barrier Crate"
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Medical /////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/medical
+ name = "HEADER"
+ containertype = /obj/structure/closet/crate/medical
+ group = supply_medical
+
+
+/datum/supply_packs/medical/supplies
+ name = "Medical Supplies Crate"
+ contains = list(/obj/item/weapon/reagent_containers/glass/bottle/antitoxin,
+ /obj/item/weapon/reagent_containers/glass/bottle/antitoxin,
+ /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline,
+ /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline,
+ /obj/item/weapon/reagent_containers/glass/bottle/stoxin,
+ /obj/item/weapon/reagent_containers/glass/bottle/stoxin,
+ /obj/item/weapon/reagent_containers/glass/bottle/toxin,
+ /obj/item/weapon/reagent_containers/glass/bottle/toxin,
+ /obj/item/weapon/reagent_containers/glass/beaker/large,
+ /obj/item/weapon/reagent_containers/glass/beaker/large,
+ /obj/item/stack/medical/bruise_pack,
+ /obj/item/weapon/storage/box/beakers,
+ /obj/item/weapon/storage/box/syringes,
+ /obj/item/weapon/storage/box/bodybags)
+ cost = 20
+ containertype = /obj/structure/closet/crate/medical
+ containername = "medical supplies crate"
+
+/datum/supply_packs/medical/firstaid
+ name = "First Aid Kits Crate"
+ contains = list(/obj/item/weapon/storage/firstaid/regular,
+ /obj/item/weapon/storage/firstaid/regular,
+ /obj/item/weapon/storage/firstaid/regular,
+ /obj/item/weapon/storage/firstaid/regular)
+ cost = 10
+ containername = "first aid kits crate"
+
+/datum/supply_packs/medical/firstaidadv
+ name = "Advaced First Aid Kits Crate"
+ contains = list(/obj/item/weapon/storage/firstaid/adv,
+ /obj/item/weapon/storage/firstaid/adv,
+ /obj/item/weapon/storage/firstaid/adv,
+ /obj/item/weapon/storage/firstaid/adv)
+ cost = 10
+ containername = "advaced first aid kits crate"
+
+/datum/supply_packs/medical/firstaidburns
+ name = "Burns Treatment Kits Crate"
+ contains = list(/obj/item/weapon/storage/firstaid/fire,
+ /obj/item/weapon/storage/firstaid/fire,
+ /obj/item/weapon/storage/firstaid/fire)
+ cost = 10
+ containername = "fire first aid kits crate"
+
+/datum/supply_packs/medical/firstaidtoxins
+ name = "Toxin Treatment Kits Crate"
+ contains = list(/obj/item/weapon/storage/firstaid/toxin,
+ /obj/item/weapon/storage/firstaid/toxin,
+ /obj/item/weapon/storage/firstaid/toxin)
+ cost = 10
+ containername = "toxin first aid kits crate"
+
+/datum/supply_packs/medical/firstaidoxygen
+ name = "Oxygen Deprivation Kits Crate"
+ contains = list(/obj/item/weapon/storage/firstaid/o2,
+ /obj/item/weapon/storage/firstaid/o2,
+ /obj/item/weapon/storage/firstaid/o2)
+ cost = 10
+ containername = "oxygen deprivation kits crate"
+
+
+/datum/supply_packs/medical/virus
+ name = "Virus Crate"
+ contains = list(/obj/item/weapon/virusdish/random,
+ /obj/item/weapon/virusdish/random,
+ /obj/item/weapon/virusdish/random,
+ /obj/item/weapon/virusdish/random)
+ cost = 25
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "virus crate"
+ access = access_cmo
+
+
+/datum/supply_packs/medical/bloodpacks
+ name = "Blood Pack Variety Crate"
+ contains = list(/obj/item/weapon/reagent_containers/blood/empty,
+ /obj/item/weapon/reagent_containers/blood/empty,
+ /obj/item/weapon/reagent_containers/blood/APlus,
+ /obj/item/weapon/reagent_containers/blood/AMinus,
+ /obj/item/weapon/reagent_containers/blood/BPlus,
+ /obj/item/weapon/reagent_containers/blood/BMinus,
+ /obj/item/weapon/reagent_containers/blood/OPlus,
+ /obj/item/weapon/reagent_containers/blood/OMinus)
+ cost = 35
+ containertype = /obj/structure/closet/crate/freezer
+ containername = "blood pack crate"
+
+/datum/supply_packs/medical/iv_drip
+ name = "IV Drip Crate"
+ contains = list(/obj/machinery/iv_drip)
+ cost = 30
+ containertype = /obj/structure/closet/crate/secure
+ containername = "iv drip crate"
+ access = access_cmo
+
+/datum/supply_packs/medical/surgery
+ name = "Surgery crate"
+ contains = list(/obj/item/weapon/cautery,
+ /obj/item/weapon/surgicaldrill,
+ /obj/item/clothing/mask/breath/medical,
+ /obj/item/weapon/tank/anesthetic,
+ /obj/item/weapon/FixOVein,
+ /obj/item/weapon/hemostat,
+ /obj/item/weapon/scalpel,
+ /obj/item/weapon/bonegel,
+ /obj/item/weapon/retractor,
+ /obj/item/weapon/bonesetter,
+ /obj/item/weapon/circular_saw)
+ cost = 25
+ containertype = /obj/structure/closet/crate/secure
+ containername = "Surgery crate"
+ access = access_medical
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Science /////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/science
+ name = "HEADER"
+ group = supply_science
+
+
+/datum/supply_packs/science/robotics
+ name = "Robotics Assembly Crate"
+ contains = list(/obj/item/device/assembly/prox_sensor,
+ /obj/item/device/assembly/prox_sensor,
+ /obj/item/device/assembly/prox_sensor,
+ /obj/item/weapon/storage/toolbox/electrical,
+ /obj/item/weapon/storage/box/flashes,
+ /obj/item/weapon/stock_parts/cell/high,
+ /obj/item/weapon/stock_parts/cell/high)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "robotics assembly crate"
+ access = access_robotics
+
+/datum/supply_packs/science/robotics/mecha_ripley
+ name = "Circuit Crate (\"Ripley\" APLU)"
+ contains = list(/obj/item/weapon/book/manual/ripley_build_and_repair,
+ /obj/item/weapon/circuitboard/mecha/ripley/main, //TEMPORARY due to lack of circuitboard printer
+ /obj/item/weapon/circuitboard/mecha/ripley/peripherals) //TEMPORARY due to lack of circuitboard printer
+ cost = 30
+ containertype = /obj/structure/closet/crate/secure
+ containername = "\improper APLU \"Ripley\" circuit crate"
+
+/datum/supply_packs/science/robotics/mecha_odysseus
+ name = "Circuit Crate (\"Odysseus\")"
+ contains = list(/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, //TEMPORARY due to lack of circuitboard printer
+ /obj/item/weapon/circuitboard/mecha/odysseus/main) //TEMPORARY due to lack of circuitboard printer
+ cost = 25
+ containertype = /obj/structure/closet/crate/secure
+ containername = "\improper \"Odysseus\" circuit crate"
+
+/datum/supply_packs/science/plasma
+ name = "Plasma Assembly Crate"
+ contains = list(/obj/item/weapon/tank/plasma,
+ /obj/item/weapon/tank/plasma,
+ /obj/item/weapon/tank/plasma,
+ /obj/item/device/assembly/igniter,
+ /obj/item/device/assembly/igniter,
+ /obj/item/device/assembly/igniter,
+ /obj/item/device/assembly/prox_sensor,
+ /obj/item/device/assembly/prox_sensor,
+ /obj/item/device/assembly/prox_sensor,
+ /obj/item/device/assembly/timer,
+ /obj/item/device/assembly/timer,
+ /obj/item/device/assembly/timer)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "plasma assembly crate"
+ access = access_tox_storage
+ group = supply_science
+
+/datum/supply_packs/science/shieldwalls
+ name = "Shield Generators"
+ contains = list(/obj/machinery/shieldwallgen,
+ /obj/machinery/shieldwallgen,
+ /obj/machinery/shieldwallgen,
+ /obj/machinery/shieldwallgen)
+ cost = 20
+ containertype = /obj/structure/closet/crate/secure
+ containername = "shield generators crate"
+ access = access_teleporter
+
+
+/datum/supply_packs/science/transfer_valves
+ name = "Tank Transfer Valves"
+ contains = list(/obj/item/device/transfer_valve,
+ /obj/item/device/transfer_valve)
+ cost = 60
+ containertype = /obj/structure/closet/crate/secure
+ containername = "transfer valves crate"
+ access = access_rd
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Organic /////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/organic
+ name = "HEADER"
+ group = supply_organic
+ containertype = /obj/structure/closet/crate/freezer
+
+
+/datum/supply_packs/organic/food
+ name = "Food Crate"
+ contains = list(/obj/item/weapon/reagent_containers/food/snacks/flour,
+ /obj/item/weapon/reagent_containers/food/snacks/flour,
+ /obj/item/weapon/reagent_containers/food/snacks/flour,
+ /obj/item/weapon/reagent_containers/food/snacks/flour,
+ /obj/item/weapon/reagent_containers/food/drinks/milk,
+ /obj/item/weapon/reagent_containers/food/drinks/soymilk,
+ /obj/item/weapon/storage/fancy/egg_box,
+ /obj/item/weapon/reagent_containers/food/condiment/enzyme,
+ /obj/item/weapon/reagent_containers/food/condiment/sugar,
+ /obj/item/weapon/reagent_containers/food/snacks/meat/monkey,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana)
+ cost = 10
+ containername = "food crate"
+
+/datum/supply_packs/organic/pizza
+ name = "Pizza Crate"
+ contains = list(/obj/item/pizzabox/margherita,
+ /obj/item/pizzabox/mushroom,
+ /obj/item/pizzabox/meat,
+ /obj/item/pizzabox/vegetable)
+ cost = 60
+ containername = "Pizza crate"
+
+/datum/supply_packs/organic/monkey
+ name = "Monkey Crate"
+ contains = list (/obj/item/weapon/storage/box/monkeycubes)
+ cost = 20
+ containername = "monkey crate"
+
+/datum/supply_packs/organic/farwa
+ name = "Farwa crate"
+ contains = list (/obj/item/weapon/storage/box/farwacubes)
+ cost = 30
+ containername = "farwa crate"
+
+/datum/supply_packs/organic/skrell
+ name = "Neaera crate"
+ contains = list (/obj/item/weapon/storage/box/neaeracubes)
+ cost = 30
+ containername = "neaera crate"
+
+/datum/supply_packs/organic/stok
+ name = "Stok crate"
+ contains = list (/obj/item/weapon/storage/box/stokcubes)
+ cost = 30
+ containername = "stok crate"
+
+/datum/supply_packs/organic/party
+ name = "Party equipment"
+ contains = list(/obj/item/weapon/storage/box/drinkingglasses,
+ /obj/item/weapon/reagent_containers/food/drinks/shaker,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/patron,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/goldschlager,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/ale,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/ale,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/beer,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/beer,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/beer,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/beer)
+ cost = 20
+ containername = "party equipment"
+
+//////// livestock
+/datum/supply_packs/organic/cow
+ name = "Cow Crate"
+ cost = 30
+ containertype = /obj/structure/closet/critter/cow
+ containername = "cow crate"
+
+/datum/supply_packs/organic/goat
+ name = "Goat Crate"
+ cost = 25
+ containertype = /obj/structure/closet/critter/goat
+ containername = "goat crate"
+
+/datum/supply_packs/organic/chicken
+ name = "Chicken Crate"
+ cost = 20
+ containertype = /obj/structure/closet/critter/chick
+ containername = "chicken crate"
+
+/datum/supply_packs/organic/corgi
+ name = "Corgi Crate"
+ cost = 50
+ containertype = /obj/structure/closet/critter/corgi
+ containername = "corgi crate"
+
+/datum/supply_packs/organic/cat
+ name = "Cat Crate"
+ cost = 50 //Cats are worth as much as corgis.
+ containertype = /obj/structure/closet/critter/cat
+ containername = "cat crate"
+
+/datum/supply_packs/organic/pug
+ name = "Pug Crate"
+ cost = 50
+ containertype = /obj/structure/closet/critter/pug
+ containername = "pug crate"
+
+/datum/supply_packs/organic/fox
+ name = "Fox Crate"
+ cost = 55 //Foxes are cool.
+ containertype = /obj/structure/closet/critter/fox
+ containername = "fox crate"
+
+/datum/supply_packs/organic/butterfly
+ name = "Butterflies Crate"
+ cost = 50
+ containertype = /obj/structure/closet/critter/butterfly
+ containername = "butterflies crate"
+ contraband = 1
+
+////// hippy gear
+
+/datum/supply_packs/organic/hydroponics // -- Skie
+ name = "Hydroponics Supply Crate"
+ contains = list(/obj/item/weapon/reagent_containers/spray/plantbgone,
+ /obj/item/weapon/reagent_containers/spray/plantbgone,
+ /obj/item/weapon/reagent_containers/glass/bottle/ammonia,
+ /obj/item/weapon/reagent_containers/glass/bottle/ammonia,
+ /obj/item/weapon/hatchet,
+ /obj/item/weapon/minihoe,
+ /obj/item/device/analyzer/plant_analyzer,
+ /obj/item/clothing/gloves/botanic_leather,
+ /obj/item/clothing/suit/apron) // Updated with new things
+ cost = 15
+ containertype = /obj/structure/closet/crate/hydroponics
+ containername = "hydroponics crate"
+
+/datum/supply_packs/misc/hydroponics/hydrotank
+ name = "Hydroponics Watertank Backpack Crate"
+ contains = list(/obj/item/weapon/watertank)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "hydroponics watertank crate"
+ access = access_hydroponics
+
+/datum/supply_packs/organic/hydroponics/seeds
+ name = "Seeds Crate"
+ contains = list(/obj/item/seeds/chiliseed,
+ /obj/item/seeds/berryseed,
+ /obj/item/seeds/cornseed,
+ /obj/item/seeds/eggplantseed,
+ /obj/item/seeds/tomatoseed,
+ /obj/item/seeds/soyaseed,
+ /obj/item/seeds/wheatseed,
+ /obj/item/seeds/carrotseed,
+ /obj/item/seeds/sunflowerseed,
+ /obj/item/seeds/chantermycelium,
+ /obj/item/seeds/potatoseed,
+ /obj/item/seeds/sugarcaneseed)
+ cost = 10
+ containername = "seeds crate"
+
+/datum/supply_packs/organic/hydroponics/exoticseeds
+ name = "Exotic Seeds Crate"
+ contains = list(/obj/item/seeds/nettleseed,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/plumpmycelium,
+ /obj/item/seeds/libertymycelium,
+ /obj/item/seeds/amanitamycelium,
+ /obj/item/seeds/reishimycelium,
+ /obj/item/seeds/bananaseed,
+ /obj/item/seeds/eggyseed)
+ cost = 15
+ containername = "exotic seeds crate"
+
+/datum/supply_packs/organic/bee_keeper
+ name = "Beekeeping Crate"
+ contains = list(/obj/item/beezeez,
+ /obj/item/beezeez,
+ /obj/item/weapon/bee_net,
+ /obj/item/apiary,
+ /obj/item/queen_bee,
+ /obj/item/queen_bee,
+ /obj/item/queen_bee)
+ cost = 20
+ containertype = /obj/structure/closet/crate/hydroponics
+ containername = "Beekeeping crate"
+ access = access_hydroponics
+
+/datum/supply_packs/organic/vending
+ name = "Bartending Supply Crate"
+ contains = list(/obj/item/weapon/vending_refill/boozeomat,
+ /obj/item/weapon/vending_refill/boozeomat,
+ /obj/item/weapon/vending_refill/boozeomat,
+ /obj/item/weapon/vending_refill/coffee,
+ /obj/item/weapon/vending_refill/coffee,
+ /obj/item/weapon/vending_refill/coffee)
+ cost = 20
+ containername = "bartending supply crate"
+
+/datum/supply_packs/organic/foodcart
+ name = "Food Cart crate"
+ contains = list(/obj/structure/foodcart)
+ cost = 10
+ containertype = /obj/structure/largecrate
+ containername = "food cart crate"
+
+/datum/supply_packs/organic/vending/snack
+ name = "Snack Supply Crate"
+ contains = list(/obj/item/weapon/vending_refill/snack,
+ /obj/item/weapon/vending_refill/snack,
+ /obj/item/weapon/vending_refill/snack)
+ cost = 15
+ containername = "snacks supply crate"
+
+/datum/supply_packs/organic/vending/cola
+ name = "Softdrinks Supply Crate"
+ contains = list(/obj/item/weapon/vending_refill/cola,
+ /obj/item/weapon/vending_refill/cola,
+ /obj/item/weapon/vending_refill/cola)
+ cost = 15
+ containername = "softdrinks supply crate"
+
+/datum/supply_packs/organic/vending/cigarette
+ name = "Cigarette Supply Crate"
+ contains = list(/obj/item/weapon/vending_refill/cigarette,
+ /obj/item/weapon/vending_refill/cigarette,
+ /obj/item/weapon/vending_refill/cigarette)
+ cost = 15
+ containername = "cigarette supply crate"
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Materials ///////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/materials
+ name = "HEADER"
+ group = supply_materials
+
+
+/datum/supply_packs/materials/metal50
+ name = "50 Metal Sheets"
+ contains = list(/obj/item/stack/sheet/metal)
+ amount = 50
+ cost = 10
+ containername = "metal sheets crate"
+
+/datum/supply_packs/materials/plasteel20
+ name = "20 Plasteel Sheets"
+ contains = list(/obj/item/stack/sheet/plasteel)
+ amount = 20
+ cost = 30
+ containername = "plasteel sheets crate"
+
+/datum/supply_packs/materials/plasteel50
+ name = "50 Plasteel Sheets"
+ contains = list(/obj/item/stack/sheet/plasteel)
+ amount = 50
+ cost = 50
+ containername = "plasteel sheets crate"
+
+/datum/supply_packs/materials/glass50
+ name = "50 Glass Sheets"
+ contains = list(/obj/item/stack/sheet/glass)
+ amount = 50
+ cost = 10
+ containername = "glass sheets crate"
+
+/datum/supply_packs/materials/cardboard50
+ name = "50 Cardboard Sheets"
+ contains = list(/obj/item/stack/sheet/cardboard)
+ amount = 50
+ cost = 10
+ containername = "cardboard sheets crate"
+
+/datum/supply_packs/materials/sandstone30
+ name = "30 Sandstone Blocks"
+ contains = list(/obj/item/stack/sheet/mineral/sandstone)
+ amount = 30
+ cost = 20
+ containername = "sandstone blocks crate"
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Miscellaneous ///////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_packs/misc
+ name = "HEADER"
+ group = supply_misc
+
+/datum/supply_packs/misc/mule
+ name = "MULEbot Crate"
+ contains = list(/obj/machinery/bot/mulebot)
+ cost = 20
+ containertype = /obj/structure/largecrate/mule
+ containername = "\improper MULEbot Crate"
+
+/datum/supply_packs/misc/watertank
+ name = "Water Tank Crate"
+ contains = list(/obj/structure/reagent_dispensers/watertank)
+ cost = 8
+ containertype = /obj/structure/largecrate
+ containername = "water tank crate"
+
+/datum/supply_packs/misc/lasertag
+ name = "Laser Tag Crate"
+ contains = list(/obj/item/weapon/gun/energy/laser/redtag,
+ /obj/item/weapon/gun/energy/laser/redtag,
+ /obj/item/weapon/gun/energy/laser/redtag,
+ /obj/item/weapon/gun/energy/laser/bluetag,
+ /obj/item/weapon/gun/energy/laser/bluetag,
+ /obj/item/weapon/gun/energy/laser/bluetag,
+ /obj/item/clothing/suit/redtag,
+ /obj/item/clothing/suit/redtag,
+ /obj/item/clothing/suit/redtag,
+ /obj/item/clothing/suit/bluetag,
+ /obj/item/clothing/suit/bluetag,
+ /obj/item/clothing/suit/bluetag,
+ /obj/item/clothing/head/helmet/redtaghelm,
+ /obj/item/clothing/head/helmet/bluetaghelm)
+ cost = 15
+ containername = "laser tag crate"
+
+/datum/supply_packs/misc/religious_supplies
+ name = "Religious Supplies Crate"
+ contains = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/holywater,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater,
+ /obj/item/weapon/storage/bible/booze,
+ /obj/item/weapon/storage/bible/booze,
+ /obj/item/clothing/suit/chaplain_hoodie,
+ /obj/item/clothing/head/chaplain_hood,
+ /obj/item/clothing/suit/chaplain_hoodie,
+ /obj/item/clothing/head/chaplain_hood)
+ cost = 40
+ containername = "religious supplies crate"
+
+
+///////////// Paper Work
+
+/datum/supply_packs/misc/paper
+ name = "Bureaucracy Crate"
+ contains = list(/obj/structure/filingcabinet/chestdrawer,
+ /obj/item/device/camera_film,
+ /obj/item/weapon/hand_labeler,
+ /obj/item/weapon/paper_bin,
+ /obj/item/weapon/pen,
+ /obj/item/weapon/pen/blue,
+ /obj/item/weapon/pen/red,
+ /obj/item/weapon/folder/blue,
+ /obj/item/weapon/folder/red,
+ /obj/item/weapon/folder/yellow,
+ /obj/item/weapon/clipboard,
+ /obj/item/weapon/clipboard)
+ cost = 15
+ containername = "bureaucracy crate"
+
+/datum/supply_packs/misc/toner
+ name = "Toner Cartridges crate"
+ contains = list(/obj/item/device/toner,
+ /obj/item/device/toner,
+ /obj/item/device/toner,
+ /obj/item/device/toner,
+ /obj/item/device/toner,
+ /obj/item/device/toner)
+ cost = 10
+ containername = "toner cartridges crate"
+
+/datum/supply_packs/misc/artscrafts
+ name = "Arts and Crafts supplies"
+ contains = list(/obj/item/weapon/storage/fancy/crayons,
+ /obj/item/device/camera,
+ /obj/item/device/camera_film,
+ /obj/item/device/camera_film,
+ /obj/item/weapon/storage/photo_album,
+ /obj/item/stack/packageWrap,
+ /obj/item/weapon/reagent_containers/glass/paint/red,
+ /obj/item/weapon/reagent_containers/glass/paint/green,
+ /obj/item/weapon/reagent_containers/glass/paint/blue,
+ /obj/item/weapon/reagent_containers/glass/paint/yellow,
+ /obj/item/weapon/reagent_containers/glass/paint/violet,
+ /obj/item/weapon/reagent_containers/glass/paint/black,
+ /obj/item/weapon/reagent_containers/glass/paint/white,
+ /obj/item/weapon/reagent_containers/glass/paint/remover,
+ /obj/item/weapon/contraband/poster,
+ /obj/item/stack/wrapping_paper,
+ /obj/item/stack/wrapping_paper,
+ /obj/item/stack/wrapping_paper)
+ cost = 10
+ containername = "Arts and Crafts crate"
+
+///////////// Janitor Supplies
+
+/datum/supply_packs/misc/janitor
+ name = "Janitorial Supplies Crate"
+ contains = list(/obj/item/weapon/reagent_containers/glass/bucket,
+ /obj/item/weapon/reagent_containers/glass/bucket,
+ /obj/item/weapon/reagent_containers/glass/bucket,
+ /obj/item/weapon/mop,
+ /obj/item/weapon/caution,
+ /obj/item/weapon/caution,
+ /obj/item/weapon/caution,
+ /obj/item/weapon/storage/bag/trash,
+ /obj/item/weapon/reagent_containers/spray/cleaner,
+ /obj/item/weapon/reagent_containers/glass/rag,
+ /obj/item/weapon/grenade/chem_grenade/cleaner,
+ /obj/item/weapon/grenade/chem_grenade/cleaner,
+ /obj/item/weapon/grenade/chem_grenade/cleaner)
+ cost = 10
+ containername = "janitorial supplies crate"
+
+/datum/supply_packs/misc/janitor/janicart
+ name = "Janitorial Cart and Galoshes Crate"
+ contains = list(/obj/structure/janitorialcart,
+ /obj/item/clothing/shoes/galoshes)
+ cost = 10
+ containertype = /obj/structure/largecrate
+ containername = "janitorial cart crate"
+
+/datum/supply_packs/misc/janitor/janitank
+ name = "Janitor Watertank Backpack"
+ contains = list(/obj/item/weapon/watertank/janitor)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "janitor watertank crate"
+ access = access_janitor
+
+/datum/supply_packs/misc/janitor/lightbulbs
+ name = "Replacement Lights"
+ contains = list(/obj/item/weapon/storage/box/lights/mixed,
+ /obj/item/weapon/storage/box/lights/mixed,
+ /obj/item/weapon/storage/box/lights/mixed)
+ cost = 10
+ containername = "replacement lights"
+
+///////////// Costumes
+
+/datum/supply_packs/misc/costume
+ name = "Standard Costume Crate"
+ contains = list(/obj/item/weapon/storage/backpack/clown,
+ /obj/item/clothing/shoes/clown_shoes,
+ /obj/item/clothing/mask/gas/clown_hat,
+ /obj/item/clothing/under/rank/clown,
+ /obj/item/weapon/bikehorn,
+ /obj/item/weapon/storage/backpack/mime,
+ /obj/item/clothing/under/mime,
+ /obj/item/clothing/shoes/black,
+ /obj/item/clothing/gloves/color/white,
+ /obj/item/clothing/mask/gas/mime,
+ /obj/item/clothing/head/beret,
+ /obj/item/clothing/suit/suspenders,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/bottleofnothing)
+ cost = 10
+ containertype = /obj/structure/closet/crate/secure
+ containername = "standard costumes"
+ access = access_theatre
+
+/datum/supply_packs/misc/wizard
+ name = "Wizard Costume Crate"
+ contains = list(/obj/item/weapon/staff,
+ /obj/item/clothing/suit/wizrobe/fake,
+ /obj/item/clothing/shoes/sandal,
+ /obj/item/clothing/head/wizard/fake)
+ cost = 20
+ containername = "wizard costume crate"
+
+/datum/supply_packs/misc/mafia
+ name = "Mafia Supply crate"
+ contains = list(/obj/item/clothing/suit/browntrenchcoat =1,/obj/item/clothing/suit/blacktrenchcoat =1,/obj/item/clothing/head/fedora/whitefedora =1,
+ /obj/item/clothing/head/fedora/brownfedora =1,/obj/item/clothing/head/fedora =1,/obj/item/clothing/under/flappers =1,/obj/item/clothing/under/mafia =1,/obj/item/clothing/under/mafia/vest =1,/obj/item/clothing/under/mafia/white =1,
+ /obj/item/clothing/under/mafia/sue =1,/obj/item/clothing/under/mafia/tan =1, /obj/item/toy/crossbow/tommygun =2)
+ cost = 15
+ containername = "mafia supply crate"
+
+/datum/supply_packs/misc/randomised
+ var/num_contained = 3 //number of items picked to be contained in a randomised crate
+ contains = list(/obj/item/clothing/head/collectable/chef,
+ /obj/item/clothing/head/collectable/paper,
+ /obj/item/clothing/head/collectable/tophat,
+ /obj/item/clothing/head/collectable/captain,
+ /obj/item/clothing/head/collectable/beret,
+ /obj/item/clothing/head/collectable/welding,
+ /obj/item/clothing/head/collectable/flatcap,
+ /obj/item/clothing/head/collectable/pirate,
+ /obj/item/clothing/head/collectable/kitty,
+ /obj/item/clothing/head/collectable/rabbitears,
+ /obj/item/clothing/head/collectable/wizard,
+ /obj/item/clothing/head/collectable/hardhat,
+ /obj/item/clothing/head/collectable/HoS,
+ /obj/item/clothing/head/collectable/thunderdome,
+ /obj/item/clothing/head/collectable/swat,
+ /obj/item/clothing/head/collectable/slime,
+ /obj/item/clothing/head/collectable/police,
+ /obj/item/clothing/head/collectable/slime,
+ /obj/item/clothing/head/collectable/xenom,
+ /obj/item/clothing/head/collectable/petehat)
+ name = "Collectable hat crate!"
+ cost = 200
+ containername = "collectable hats crate! Brought to you by Bass.inc!"
+
+/datum/supply_packs/misc/randomised/New()
+ manifest += "Contains any [num_contained] of:"
+ ..()
+
+
+/datum/supply_packs/misc/randomised/contraband
+ num_contained = 5
+ contains = list(/obj/item/weapon/storage/pill_bottle/zoom,
+ /obj/item/weapon/storage/pill_bottle/happy,
+ /obj/item/weapon/storage/pill_bottle/random_drug_bottle,
+ /obj/item/weapon/contraband/poster,
+ /obj/item/weapon/storage/fancy/cigarettes/dromedaryco,
+ /obj/item/weapon/storage/fancy/cigarettes/cigpack_shadyjims)
+ name = "Contraband Crate"
+ cost = 30
+ containername = "crate" //let's keep it subtle, eh?
+ contraband = 1
+
+/datum/supply_packs/misc/autodrobe
+ name = "Autodrobe Supply Crate"
+ contains = list(/obj/item/weapon/vending_refill/autodrobe,
+ /obj/item/weapon/vending_refill/autodrobe)
+ cost = 15
+ containername = "autodrobe supply crate"
+
+/datum/supply_packs/misc/formalwear //This is a very classy crate.
+ name = "Formal-wear Crate"
+ contains = list(/obj/item/clothing/under/blacktango,
+ /obj/item/clothing/under/assistantformal,
+ /obj/item/clothing/under/assistantformal,
+ /obj/item/clothing/under/lawyer/bluesuit,
+ /obj/item/clothing/suit/storage/lawyer/bluejacket,
+ /obj/item/clothing/under/lawyer/purpsuit,
+ /obj/item/clothing/suit/storage/lawyer/purpjacket,
+ /obj/item/clothing/under/lawyer/black,
+ /obj/item/clothing/suit/storage/lawyer/blackjacket,
+ /obj/item/clothing/accessory/waistcoat,
+ /obj/item/clothing/accessory/blue,
+ /obj/item/clothing/accessory/red,
+ /obj/item/clothing/accessory/black,
+ /obj/item/clothing/head/bowlerhat,
+ /obj/item/clothing/head/fedora,
+ /obj/item/clothing/head/flatcap,
+ /obj/item/clothing/head/beret,
+ /obj/item/clothing/head/that,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/under/suit_jacket/charcoal,
+ /obj/item/clothing/under/suit_jacket/navy,
+ /obj/item/clothing/under/suit_jacket/burgundy,
+ /obj/item/clothing/under/suit_jacket/checkered,
+ /obj/item/clothing/under/suit_jacket/tan,
+ /obj/item/weapon/lipstick/random)
+ cost = 30 //Lots of very expensive items. You gotta pay up to look good!
+ containername = "formal-wear crate"
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index 8d864128905..1aae642647b 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -323,18 +323,18 @@ var/list/uplink_items = list()
cost = 18
gamemodes = list("nuclear emergency")
-/datum/uplink_item/ammo/bullstun
- name = "Drum Magazine - 12g Stun Slug"
- desc = "An additional 8-round stun slug magazine for use in the Bulldog shotgun. Saying that they're non-lethal would be lying."
+/datum/uplink_item/ammo/bullbuck
+ name = "Drum Magazine - 12g buckshot"
+ desc = "An additional 8-round buckshot magazine for use in the Bulldog shotgun. Front towards enemy."
item = /obj/item/ammo_box/magazine/m12g
cost = 2
gamemodes = list("nuclear emergency")
-/datum/uplink_item/ammo/bullbuck
- name = "Drum Magazine - 12g Buckshot"
- desc = "An alternative 8-round buckshot magazine for use in the Bulldog shotgun. Front towards enemy."
- item = /obj/item/ammo_box/magazine/m12g/buckshot
- cost = 2
+/datum/uplink_item/ammo/bullstun
+ name = "Drum Magazine - 12g Stun Slug"
+ desc = "An alternative 8-round stun slug magazine for use in the Bulldog shotgun. Saying that they're completely non-lethal would be lying."
+ item = /obj/item/ammo_box/magazine/m12g/stun
+ cost = 3
gamemodes = list("nuclear emergency")
/datum/uplink_item/ammo/bulldragon
@@ -371,10 +371,11 @@ var/list/uplink_items = list()
/datum/uplink_item/stealthy_weapons
category = "Stealthy and Inconspicuous Weapons"
-/datum/uplink_item/stealthy_weapons/para_pen
- name = "Paralysis Pen"
- desc = "A syringe disguised as a functional pen, filled with a neuromuscular-blocking drug that renders a target mute on injection that will eventually cause them to pass out. The pen holds one dose of paralyzing agent,though it can be refilled."
- item = /obj/item/weapon/pen/paralysis
+/datum/uplink_item/stealthy_weapons/sleepy_pen
+ name = "Sleepy Pen"
+ desc = "A syringe disguised as a functional pen, filled with a potent mix of drugs, including a strong anaesthetic and a chemical that is capable of blocking the movement of the vocal chords. \
+ The pen holds one dose of the mixture. The pen can be refilled."
+ item = /obj/item/weapon/pen/sleepy
cost = 8
excludefrom = list("nuclear emergency")
@@ -440,6 +441,12 @@ var/list/uplink_items = list()
item = /obj/item/device/chameleon
cost = 7
+/datum/uplink_item/stealthy_tools/camera_bug
+ name = "Camera Bug"
+ desc = "Enables you to bug cameras to view them remotely. Adding particular items to it alters its functions."
+ item = /obj/item/device/camera_bug
+ cost = 2
+
/datum/uplink_item/stealthy_tools/dnascrambler
name = "DNA Scrambler"
desc = "A syringe with one injection that randomizes appearance and name upon use. A cheaper but less versatile alternative to an agent card and voice changer."
@@ -480,7 +487,7 @@ var/list/uplink_items = list()
/datum/uplink_item/device_tools/medkit
- name = "Syndicate Medical Supply Kit"
+ name = "Syndicate Combat Medic Kit"
desc = "The syndicate medkit is a suspicious black and red. Included is a combat stimulant injector for rapid healing, a medical hud for quick identification of injured comrades, \
and other medical supplies helpful for a medical field operative."
item = /obj/item/weapon/storage/firstaid/tactical
@@ -505,20 +512,6 @@ var/list/uplink_items = list()
item = /obj/item/clothing/glasses/thermal/syndi
cost = 6
-/*
-/datum/uplink_item/device_tools/surveillance
- name = "Camera Surveillance Kit"
- desc = "This kit contains 5 Camera bugs and one mobile receiver. Attach camera bugs to a camera to enable remote viewing."
- item = /obj/item/weapon/storage/box/syndie_kit/surveillance
- cost = 5
-
-/datum/uplink_item/device_tools/camerabugs
- name = "Camera Bugs"
- desc = "This is a Camera bug resupply giving you 5 more camera bugs."
- item = /obj/item/weapon/storage/box/surveillance
- cost = 4
-*/ //commented out until porting over TG's camera bug
-
/datum/uplink_item/device_tools/binary
name = "Binary Translator Key"
desc = "A key, that when inserted into a radio headset, allows you to listen to and talk with artificial intelligences and cybernetic organisms in binary."
diff --git a/code/datums/visibility_networks/chunk.dm b/code/datums/visibility_networks/chunk.dm
deleted file mode 100644
index 4abc1340b0f..00000000000
--- a/code/datums/visibility_networks/chunk.dm
+++ /dev/null
@@ -1,179 +0,0 @@
-#define UPDATE_BUFFER 25 // 2.5 seconds
-
-// CAMERA CHUNK
-//
-// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
-// Allows the mob using this chunk to stream these chunks and know what it can and cannot see.
-
-/datum/visibility_chunk
- var/obscured_image = 'icons/effects/cameravis.dmi'
- var/obscured_sub = "black"
- var/list/obscuredTurfs = list()
- var/list/visibleTurfs = list()
- var/list/obscured = list()
- var/list/viewpoints = list()
- var/list/turfs = list()
- var/list/seenby = list()
- var/visible = 0
- var/changed = 0
- var/updating = 0
- var/x = 0
- var/y = 0
- var/z = 0
-
-/datum/visibility_chunk/proc/add(mob/new_mob)
-
- // if this thing doesn't use one of these visibility systems, kick it out
- if (!new_mob.visibility_interface)
- return
-
- // if the mob being added isn't a valid form of that mob, kick it out
- if (!new_mob.visibility_interface:canBeAddedToChunk(src))
- return
-
- // add this chunk to the list of visible chunks
- new_mob.visibility_interface:addChunk(src)
-
- visible++
- seenby += new_mob
- if(changed && !updating)
- update()
-
-/datum/visibility_chunk/proc/remove(mob/new_mob)
- // if this thing doesn't use one of these visibility systems, kick it out
- if (!new_mob.visibility_interface)
- return
-
- // if the mob being added isn't a valid form of that mob, kick it out
- if (!new_mob.visibility_interface:canBeAddedToChunk(src))
- return
-
- // remove the chunk
- new_mob.visibility_interface:removeChunk(src)
-
- // remove the mob from out lists
- seenby -= new_mob
- if(visible > 0)
- visible--
-
-/datum/visibility_chunk/proc/visibilityChanged(turf/loc)
- if(!visibleTurfs[loc])
- return
- hasChanged()
-
-/datum/visibility_chunk/proc/hasChanged(var/update_now = 0)
- if(visible || update_now)
- if(!updating)
- updating = 1
- spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once
- update()
- updating = 0
- else
- changed = 1
-
-
-/*
-This function needs to be overwritten to return True if the viewpoint object is valid, and false if it is not.
-*/
-/datum/visibility_chunk/proc/validViewpoint(var/viewpoint)
- return FALSE
-
-/*
-This function needs to be overwritten to return a list of visible turfs for that viewpoint
-*/
-/datum/visibility_chunk/proc/getVisibleTurfsForViewpoint(var/viewpoint)
- return list()
-
-// returns a list of turfs which can be seen in by the chunks viewpoints
-/datum/visibility_chunk/proc/getVisibleTurfs()
- var/list/newVisibleTurfs = list()
- for(var/viewpoint in viewpoints)
- if (validViewpoint(viewpoint))
- for (var/turf/t in getVisibleTurfsForViewpoint(viewpoint))
- newVisibleTurfs[t]=t
- return newVisibleTurfs
-
-/*
-This function needs to be overwritten to find nearby viewpoint objects to the chunk center.
-*/
-/datum/visibility_chunk/proc/findNearbyViewpoints()
- return FALSE
-
-/*
-This function can be overwritten to change or randomize the obscuring images
-*/
-/datum/visibility_chunk/proc/setObscuredImage(var/turf/target_turf)
- if(!target_turf.obscured)
- target_turf.obscured = image(obscured_image, target_turf, obscured_sub, 15)
-
-/datum/visibility_chunk/proc/update()
-
- set background = 1
-
- // get a list of all the turfs that our viewpoints can see
- var/list/newVisibleTurfs = getVisibleTurfs()
-
- // Removes turf that isn't in turfs.
- newVisibleTurfs &= turfs
-
- var/list/visAdded = newVisibleTurfs - visibleTurfs
- var/list/visRemoved = visibleTurfs - newVisibleTurfs
-
- visibleTurfs = newVisibleTurfs
- obscuredTurfs = turfs - newVisibleTurfs
-
- // update the visibility overlays
- for(var/turf in visAdded)
- var/turf/t = turf
- if(t.obscured)
- obscured -= t.obscured
- for(var/mob/current_mob in seenby)
- if (current_mob.visibility_interface)
- current_mob.visibility_interface:removeObscuredTurf(t)
-
- for(var/turf in visRemoved)
- var/turf/t = turf
- if(obscuredTurfs[t])
- setObscuredImage(t)
- obscured += t.obscured
- for(var/mob/current_mob in seenby)
- if (current_mob.visibility_interface)
- current_mob.visibility_interface:addObscuredTurf(t)
- else
- seenby -= current_mob
-
-
-// Create a new chunk, since the chunks are made as they are needed.
-/datum/visibility_chunk/New(loc, x, y, z)
-
- // 0xf = 15
- x &= ~0xf
- y &= ~0xf
-
- src.x = x
- src.y = y
- src.z = z
-
- for(var/turf/t in range(10, locate(x + 8, y + 8, z)))
- if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16)
- turfs[t] = t
-
- // locate all nearby viewpoints
- findNearbyViewpoints()
-
- // get the turfs that are visible to those viewpoints
- visibleTurfs = getVisibleTurfs()
-
- // Removes turf that isn't in turfs.
- visibleTurfs &= turfs
-
- // create the list of turfs we can't see
- obscuredTurfs = turfs - visibleTurfs
-
- // create the list of obscuring images to add to viewing clients
- for(var/turf in obscuredTurfs)
- var/turf/t = turf
- setObscuredImage(t)
- obscured += t.obscured
-
-#undef UPDATE_BUFFER
\ No newline at end of file
diff --git a/code/datums/visibility_networks/dictionary.dm b/code/datums/visibility_networks/dictionary.dm
deleted file mode 100644
index 5f57ddd7a17..00000000000
--- a/code/datums/visibility_networks/dictionary.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-var/datum/visibility_network/cameras/cameranet = new()
-var/datum/visibility_network/cult/cultNetwork = new()
-var/datum/visibility_network/list/visibility_networks = list("ALL_CAMERAS"=cameranet, "CULT" = cultNetwork)
-
-
-// used by turfs and objects to update all visibility networks
-/proc/updateVisibilityNetworks(atom/A, var/opacity_check = 1)
- var/datum/visibility_network/currentNetwork
- for (var/networkName in visibility_networks)
- currentNetwork = visibility_networks[networkName]
- currentNetwork.updateVisibility(A, opacity_check)
\ No newline at end of file
diff --git a/code/datums/visibility_networks/update_triggers.dm b/code/datums/visibility_networks/update_triggers.dm
deleted file mode 100644
index 97ed2db3a6c..00000000000
--- a/code/datums/visibility_networks/update_triggers.dm
+++ /dev/null
@@ -1,94 +0,0 @@
-//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update.
-
-// TURFS
-
-/turf
- var/image/obscured
-
-/turf/proc/visibilityChanged()
- if(ticker)
- updateVisibilityNetworks(src)
-
-/turf/simulated/Del()
- visibilityChanged()
- ..()
-
-/turf/simulated/New()
- ..()
- visibilityChanged()
-
-
-
-// STRUCTURES
-
-/obj/structure/Del()
- if(ticker)
- updateVisibilityNetworks(src)
- ..()
-
-/obj/structure/New()
- ..()
- if(ticker)
- updateVisibilityNetworks(src)
-
-// EFFECTS
-
-/obj/effect/Del()
- if(ticker)
- updateVisibilityNetworks(src)
- ..()
-
-/obj/effect/New()
- ..()
- if(ticker)
- updateVisibilityNetworks(src)
-
-
-// DOORS
-
-// Simply updates the visibility of the area when it opens/closes/destroyed.
-/obj/machinery/door/proc/update_nearby_tiles(need_rebuild)
-
- if(!glass)
- updateVisibilityNetworks(src,0)
-
- if(!air_master)
- return 0
-
- for(var/turf/simulated/turf in locs)
- update_heat_protection(turf)
- air_master.mark_for_update(turf)
-
- return 1
-
-
-
-#define UPDATE_VISIBILITY_NETWORK_BUFFER 30
-
-/mob
- var/datum/visibility_network/list/visibilityNetworks=list()
- var/updatingVisibilityNetworks=FALSE
-
-/mob/Move(n,direct)
- var/oldLoc = src.loc
- //. = ..()
- if(..(n,direct))
- if(src.visibilityNetworks.len)
- if(!src.updatingVisibilityNetworks)
- src.updatingVisibilityNetworks = 1
- spawn(UPDATE_VISIBILITY_NETWORK_BUFFER)
- if(oldLoc != src.loc)
- for (var/datum/visibility_network/currentNetwork in src.visibilityNetworks)
- currentNetwork.updateMob(src)
- src.updatingVisibilityNetworks = 0
- return .
-
-/mob/proc/addToVisibilityNetwork(var/datum/visibility_network/network)
- if(network)
- src.visibilityNetworks+=network
-
-/mob/proc/removeFromVisibilityNetwork(var/datum/visibility_network/network)
- if(network)
- src.visibilityNetworks|=network
-
-#undef UPDATE_VISIBILITY_NETWORK_BUFFER
\ No newline at end of file
diff --git a/code/datums/visibility_networks/visibility_interface.dm b/code/datums/visibility_networks/visibility_interface.dm
deleted file mode 100644
index 7d8efba41d3..00000000000
--- a/code/datums/visibility_networks/visibility_interface.dm
+++ /dev/null
@@ -1,46 +0,0 @@
-/datum/visibility_interface
- var/chunk_type = null
- var/mob/controller = null
- var/list/visible_chunks = list()
-
-
-/datum/visibility_interface/New(var/mob/controller)
- src.controller = controller
-
-
-/datum/visibility_interface/proc/validMob()
- return getClient()
-
-/datum/visibility_interface/proc/getClient()
- return controller.client
-
-/datum/visibility_interface/proc/canBeAddedToChunk(var/datum/visibility_chunk/test_chunk)
- return istype(test_chunk,chunk_type)
-
-
-/datum/visibility_interface/proc/addChunk(var/datum/visibility_chunk/test_chunk)
- visible_chunks+=test_chunk
- var/client/currentClient = getClient()
- if(currentClient)
- currentClient.images += test_chunk.obscured
-
-
-/datum/visibility_interface/proc/removeChunk(var/datum/visibility_chunk/test_chunk)
- visible_chunks-=test_chunk
- var/client/currentClient = getClient()
- if(currentClient)
- currentClient.images -= test_chunk.obscured
-
-
-/datum/visibility_interface/proc/removeObscuredTurf(var/turf/target_turf)
- if(validMob())
- var/client/currentClient = getClient()
- if(currentClient)
- currentClient.images -= target_turf.obscured
-
-
-/datum/visibility_interface/proc/addObscuredTurf(var/turf/target_turf)
- if(validMob())
- var/client/currentClient = getClient()
- if(currentClient)
- currentClient.images -= target_turf.obscured
\ No newline at end of file
diff --git a/code/datums/visibility_networks/visibility_network.dm b/code/datums/visibility_networks/visibility_network.dm
deleted file mode 100644
index f1bc24e771a..00000000000
--- a/code/datums/visibility_networks/visibility_network.dm
+++ /dev/null
@@ -1,144 +0,0 @@
-/datum/visibility_network
- var/list/viewpoints = list()
-
- // the type of chunk used by this network
- var/datum/visibility_chunk/ChunkType = /datum/visibility_chunk
-
- // The chunks of the map, mapping the areas that the viewpoints can see.
- var/list/chunks = list()
-
- var/ready = 0
-
-
-// Creates a chunk key string from x,y,z coordinates
-/datum/visibility_network/proc/createChunkKey(x,y,z)
- x &= ~0xf
- y &= ~0xf
- return "[x],[y],[z]"
-
-
-// Checks if a chunk has been Generated in x, y, z.
-/datum/visibility_network/proc/chunkGenerated(x, y, z)
- return (chunks[createChunkKey(x, y, z)])
-
-
-// Returns the chunk in the x, y, z.
-// If there is no chunk, it creates a new chunk and returns that.
-/datum/visibility_network/proc/getChunk(x, y, z)
- var/key = createChunkKey(x, y, z)
- if(!chunks[key])
- chunks[key] = new ChunkType(null, x, y, z)
- return chunks[key]
-
-
-/datum/visibility_network/proc/visibility(var/mob/targetMob)
-
- // if we've got not visibility interface on the mob, we canot do this
- if (!targetMob.visibility_interface)
- return
-
- // 0xf = 15
- var/x1 = max(0, targetMob.x - 16) & ~0xf
- var/y1 = max(0, targetMob.y - 16) & ~0xf
- var/x2 = min(world.maxx, targetMob.x + 16) & ~0xf
- var/y2 = min(world.maxy, targetMob.y + 16) & ~0xf
-
- var/list/visibleChunks = list()
-
- for(var/x = x1; x <= x2; x += 16)
- for(var/y = y1; y <= y2; y += 16)
- visibleChunks += getChunk(x, y, targetMob.z)
-
- var/list/remove = targetMob.visibility_interface:visible_chunks - visibleChunks
- var/list/add = visibleChunks - targetMob.visibility_interface:visible_chunks
-
- for(var/datum/visibility_chunk/chunk in remove)
- chunk.remove(targetMob)
-
- for(var/datum/visibility_chunk/chunk in add)
- chunk.add(targetMob)
-
-
-// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
-/datum/visibility_network/proc/updateVisibility(atom/A, var/opacity_check = 1)
- if(!ticker || (opacity_check && !A.opacity))
- return
- majorChunkChange(A, 2)
-
-
-/datum/visibility_network/proc/updateChunk(x, y, z)
- if(!chunkGenerated(x, y, z))
- return
- var/datum/visibility_chunk/chunk = getChunk(x, y, z)
- chunk.hasChanged()
-
-
-/datum/visibility_network/proc/validViewpoint(var/viewpoint)
- return FALSE
-
-
-/datum/visibility_network/proc/addViewpoint(var/viewpoint)
- if(validViewpoint(viewpoint))
- majorChunkChange(viewpoint, 1)
-
-
-/datum/visibility_network/proc/removeViewpoint(var/viewpoint)
- if(validViewpoint(viewpoint))
- majorChunkChange(viewpoint, 0)
-
-/datum/visibility_network/proc/getViewpointFromMob(var/mob/currentMob)
- return FALSE
-
-/datum/visibility_network/proc/updateMob(var/mob/currentMob)
- var/viewpoint = getViewpointFromMob(currentMob)
- if(viewpoint)
- updateViewpoint(viewpoint)
-
-
-/datum/visibility_network/proc/updateViewpoint(var/viewpoint)
- if(validViewpoint(viewpoint))
- majorChunkChange(viewpoint, 1)
-
-
-// Never access this proc directly!!!!
-// This will update the chunk and all the surrounding chunks.
-// It will also add the atom to the cameras list if you set the choice to 1.
-// Setting the choice to 0 will remove the viewpoint from the chunks.
-// If you want to update the chunks around an object, without adding/removing a viewpoint, use choice 2.
-/datum/visibility_network/proc/majorChunkChange(atom/c, var/choice)
- // 0xf = 15
- if(!c)
- return
-
- var/turf/T = get_turf(c)
- if(T)
- var/x1 = max(0, T.x - 8) & ~0xf
- var/y1 = max(0, T.y - 8) & ~0xf
- var/x2 = min(world.maxx, T.x + 8) & ~0xf
- var/y2 = min(world.maxy, T.y + 8) & ~0xf
-
- for(var/x = x1; x <= x2; x += 16)
- for(var/y = y1; y <= y2; y += 16)
- if(chunkGenerated(x, y, T.z))
- var/datum/visibility_chunk/chunk = getChunk(x, y, T.z)
- if(choice == 0)
- // Remove the viewpoint.
- chunk.viewpoints -= c
- else if(choice == 1)
- // You can't have the same viewpoint in the list twice.
- chunk.viewpoints |= c
- chunk.hasChanged()
-
-// checks if the network can see a particular atom
-/datum/visibility_network/proc/checkCanSee(var/atom/target)
- var/turf/position = get_turf(target)
- return checkTurfVis(position)
-
-/datum/visibility_network/proc/checkTurfVis(var/turf/position)
- var/datum/visibility_chunk/chunk = getChunk(position.x, position.y, position.z)
- if(chunk)
- if(chunk.changed)
- chunk.hasChanged(1) // Update now, no matter if it's visible or not.
- if(chunk.visibleTurfs[position])
- return 1
- return 0
\ No newline at end of file
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 3b6d009a5ff..211e82d4394 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -33,14 +33,16 @@ var/const/AIRLOCK_WIRE_LIGHT = 2048
/datum/wires/airlock/GetInteractWindow()
var/obj/machinery/door/airlock/A = holder
+ var/haspower = A.arePowerSystemsOn()
. += ..()
- . += text(" \n[] \n[] \n[] \n[] \n[] \n[] \n[]", (A.locked ? "The door bolts have fallen!" : "The door bolts look up."),
- (A.lights ? "The door bolt lights are on." : "The door bolt lights are off!"),
- ((A.arePowerSystemsOn() && !(A.stat & NOPOWER)) ? "The test light is on." : "The test light is off!"),
- (A.aiControlDisabled==0 ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."),
- (A.safe==0 ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."),
- (A.normalspeed==0 ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off."),
- (A.emergency==0 ? "The emergency lights are off." : "The emergency lights are on."))
+ . += text(" \n[] \n[] \n[] \n[] \n[] \n[] \n[]",
+ (A.locked ? "The door bolts have fallen!" : "The door bolts look up."),
+ ((A.lights && haspower) ? "The door bolt lights are on." : "The door bolt lights are off!"),
+ ((haspower) ? "The test light is on." : "The test light is off!"),
+ ((A.aiControlDisabled==0 && !A.emagged && haspower) ? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."),
+ ((A.safe==0 && haspower) ? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."),
+ ((A.normalspeed==0 && haspower) ? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off."),
+ ((A.emergency==0 && haspower) ? "The emergency lights are off." : "The emergency lights are on."))
/datum/wires/airlock/UpdateCut(var/index, var/mended)
@@ -125,7 +127,7 @@ var/const/AIRLOCK_WIRE_LIGHT = 2048
switch(index)
if(AIRLOCK_WIRE_IDSCAN)
//Sending a pulse through this disables emergency access and flashes the red light on the door (if the door has power).
- if((A.arePowerSystemsOn()) && (!(A.stat & NOPOWER)))
+ if((A.arePowerSystemsOn()) && A.density)
A.door_animate("deny")
if(A.emergency)
A.emergency = 0
diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm
index a8171270008..9da8890ac32 100644
--- a/code/datums/wires/radio.dm
+++ b/code/datums/wires/radio.dm
@@ -21,11 +21,24 @@ var/const/WIRE_TRANSMIT = 4
var/obj/item/device/radio/R = holder
switch(index)
if(WIRE_SIGNAL)
- R.listening = !R.listening
- R.broadcasting = R.listening
+ R.listening = !R.listening && !IsIndexCut(WIRE_RECEIVE)
+ R.broadcasting = R.listening && !IsIndexCut(WIRE_TRANSMIT)
if(WIRE_RECEIVE)
- R.listening = !R.listening
+ R.listening = !R.listening && !IsIndexCut(WIRE_SIGNAL)
if(WIRE_TRANSMIT)
- R.broadcasting = !R.broadcasting
\ No newline at end of file
+ R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL)
+
+/datum/wires/radio/UpdateCut(var/index, var/mended)
+ var/obj/item/device/radio/R = holder
+ switch(index)
+ if(WIRE_SIGNAL)
+ R.listening = mended && !IsIndexCut(WIRE_RECEIVE)
+ R.broadcasting = mended && !IsIndexCut(WIRE_TRANSMIT)
+
+ if(WIRE_RECEIVE)
+ R.listening = mended && !IsIndexCut(WIRE_SIGNAL)
+
+ if(WIRE_TRANSMIT)
+ R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL)
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index c0c76cd89ae..574c7961e09 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -1,3 +1,5 @@
+#define CAT_HIDDEN 2 // Also in code/game/machinery/vending.dm
+
/datum/wires/vending
holder_type = /obj/machinery/vending
wire_count = 4
@@ -17,17 +19,12 @@ var/const/VENDING_WIRE_IDSCAN = 8
return 1
return 0
-/datum/wires/vending/Interact(var/mob/living/user)
- if(CanUse(user))
- var/obj/machinery/vending/V = holder
- V.attack_hand(user)
-
/datum/wires/vending/GetInteractWindow()
var/obj/machinery/vending/V = holder
. += ..()
. += " The orange light is [V.seconds_electrified ? "on" : "off"]. "
. += "The red light is [V.shoot_inventory ? "off" : "blinking"]. "
- . += "The green light is [V.extended_inventory ? "on" : "off"]. "
+ . += "The green light is [(V.categories & CAT_HIDDEN) ? "on" : "off"]. "
. += "A [V.scan_id ? "purple" : "yellow"] light is on. "
/datum/wires/vending/UpdatePulsed(var/index)
@@ -36,7 +33,7 @@ var/const/VENDING_WIRE_IDSCAN = 8
if(VENDING_WIRE_THROW)
V.shoot_inventory = !V.shoot_inventory
if(VENDING_WIRE_CONTRABAND)
- V.extended_inventory = !V.extended_inventory
+ V.categories ^= CAT_HIDDEN
if(VENDING_WIRE_ELECTRIFY)
V.seconds_electrified = 30
if(VENDING_WIRE_IDSCAN)
@@ -48,7 +45,7 @@ var/const/VENDING_WIRE_IDSCAN = 8
if(VENDING_WIRE_THROW)
V.shoot_inventory = !mended
if(VENDING_WIRE_CONTRABAND)
- V.extended_inventory = 0
+ V.categories &= ~CAT_HIDDEN
if(VENDING_WIRE_ELECTRIFY)
if(mended)
V.seconds_electrified = 0
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index c3d07013d14..04c3541dc7e 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -72,8 +72,11 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown",
html = GetInteractWindow()
if(html)
user.set_machine(holder)
- //user << browse(html, "window=wires;size=[window_x]x[window_y]")
- //onclose(user, "wires")
+ else
+ user.unset_machine()
+ // No content means no window.
+ user << browse(null, "window=wires")
+ return
var/datum/browser/popup = new(user, "wires", holder.name, window_x, window_y)
popup.set_content(html)
popup.set_title_image(user.browse_rsc_icon(holder.icon, holder.icon_state))
diff --git a/code/defines/obj.dm b/code/defines/obj.dm
index aff00b487eb..5eb6c3b54fc 100644
--- a/code/defines/obj.dm
+++ b/code/defines/obj.dm
@@ -4,7 +4,7 @@
anchored = 1
density = 1
- attackby(obj/item/weapon/W as obj, mob/user as mob)
+ attackby(obj/item/weapon/W as obj, mob/user as mob, params)
return attack_hand(user)
attack_hand(mob/user as mob)
@@ -19,7 +19,7 @@
anchored = 1
density = 0
- attackby(obj/item/weapon/W as obj, mob/user as mob)
+ attackby(obj/item/weapon/W as obj, mob/user as mob, params)
return attack_hand(user)
@@ -380,7 +380,7 @@ var/global/list/PDA_Manifest = list()
throwforce = 0.0
throw_speed = 1
throw_range = 20
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
/obj/effect/stop
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index e8fba96a0d0..c00ba0f9061 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -3,7 +3,7 @@
desc = "Should anything ever go wrong..."
icon = 'icons/obj/items.dmi'
icon_state = "red_phone"
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
force = 3.0
throwforce = 2.0
throw_speed = 1
@@ -22,7 +22,6 @@
anchored = 0.0
var/matter = 0
var/mode = 1
- flags = TABLEPASS
w_class = 3.0
/obj/item/weapon/bananapeel
@@ -108,7 +107,7 @@
icon = 'icons/obj/weapons.dmi'
icon_state = "cane"
item_state = "stick"
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
force = 5.0
throwforce = 7.0
w_class = 2.0
@@ -160,7 +159,7 @@
gender = PLURAL
icon = 'icons/obj/items.dmi'
icon_state = "handcuff"
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
throwforce = 0
w_class = 3.0
origin_tech = "materials=1"
@@ -235,7 +234,7 @@
if(H)
user << "You use [src] to destroy [H]."
signs -= H
- del(H)
+ qdel(H)
else
if(signs.len < max_signs)
H = new(get_turf(target))
@@ -251,7 +250,7 @@
if(signs.len)
var/list/L = signs.Copy()
for(var/sign in L)
- del(sign)
+ qdel(sign)
signs -= sign
user << "You clear all active holograms."
@@ -272,7 +271,6 @@
throw_speed = 1
throw_range = 5
w_class = 2.0
- flags = FPRINT | TABLEPASS
attack_verb = list("warned", "cautioned", "smashed")
proximity_sign
@@ -315,7 +313,7 @@
if(ishuman(C))
dead_legs(C)
if(src)
- del(src)
+ qdel(src)
proc/dead_legs(mob/living/carbon/human/H as mob)
var/datum/organ/external/l = H.get_organ("l_leg")
@@ -335,7 +333,7 @@
desc = "Parts of a rack."
icon = 'icons/obj/items.dmi'
icon_state = "rack_parts"
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
m_amt = 3750
/*/obj/item/weapon/syndicate_uplink
@@ -349,7 +347,7 @@
var/traitor_frequency = 0.0
var/mob/currentUser = null
var/obj/item/device/radio/origradio = null
- flags = FPRINT | TABLEPASS | CONDUCT | ONBELT
+ flags = CONDUCT | ONBELT
w_class = 2.0
item_state = "radio"
throw_speed = 4
@@ -367,7 +365,7 @@
var/selfdestruct = 0.0
var/traitor_frequency = 0.0
var/obj/item/device/radio/origradio = null
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
slot_flags = SLOT_BELT
item_state = "radio"
throwforce = 5
@@ -387,7 +385,7 @@
throw_speed = 1
throw_range = 5
w_class = 2.0
- flags = FPRINT | TABLEPASS | NOSHIELD
+ flags = NOSHIELD
attack_verb = list("bludgeoned", "whacked", "disciplined")
/obj/item/weapon/staff/broom
@@ -407,7 +405,7 @@
throw_speed = 1
throw_range = 5
w_class = 2.0
- flags = FPRINT | TABLEPASS | NOSHIELD
+ flags = NOSHIELD
/obj/item/weapon/table_parts
name = "table parts"
@@ -416,7 +414,7 @@
icon = 'icons/obj/items.dmi'
icon_state = "table_parts"
m_amt = 3750
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
attack_verb = list("slammed", "bashed", "battered", "bludgeoned", "thrashed", "whacked")
/obj/item/weapon/table_parts/reinforced
@@ -425,7 +423,7 @@
icon = 'icons/obj/items.dmi'
icon_state = "reinf_tableparts"
m_amt = 7500
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
/obj/item/weapon/table_parts/wood
name = "wooden table parts"
@@ -453,7 +451,7 @@
icon_state = "std_module"
w_class = 2.0
item_state = "electronic"
- flags = FPRINT|TABLEPASS|CONDUCT
+ flags = CONDUCT
var/mtype = 1 // 1=electronic 2=hardware
/obj/item/weapon/module/card_reader
@@ -481,48 +479,12 @@
icon_state = "power_mod"
desc = "Charging circuits for power cells."
-
-/obj/item/device/camera_bug
- name = "camera bug"
- desc = "Tiny electronic device meant to bug cameras for viewing later."
- icon = 'icons/obj/device.dmi'
- icon_state = "implant_evil"
- w_class = 1.0
- item_state = ""
- throw_speed = 4
- throw_range = 20
-
-/obj/item/weapon/camera_bug/attack_self(mob/usr as mob)
- var/list/cameras = new/list()
- for (var/obj/machinery/camera/C in cameranet.viewpoints)
- if (C.bugged && C.status)
- cameras.Add(C)
- if (length(cameras) == 0)
- usr << "\red No bugged functioning cameras found."
- return
-
- var/list/friendly_cameras = new/list()
-
- for (var/obj/machinery/camera/C in cameras)
- friendly_cameras.Add(C.c_tag)
-
- var/target = input("Select the camera to observe", null) as null|anything in friendly_cameras
- if (!target)
- return
- for (var/obj/machinery/camera/C in cameras)
- if (C.c_tag == target)
- target = C
- break
- if (usr.stat == 2) return
-
- usr.client.eye = target
-
/obj/item/weapon/hatchet
name = "hatchet"
desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood."
icon = 'icons/obj/weapons.dmi'
icon_state = "hatchet"
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
force = 12.0
sharp = 1
edge = 1
@@ -553,7 +515,7 @@
throw_speed = 2
throw_range = 3
w_class = 4.0
- flags = FPRINT | TABLEPASS | NOSHIELD
+ flags = NOSHIELD
slot_flags = SLOT_BACK
origin_tech = "materials=2;combat=2"
attack_verb = list("chopped", "sliced", "cut", "reaped")
@@ -564,8 +526,8 @@
if(istype(A, /obj/effect/plantsegment))
for(var/obj/effect/plantsegment/B in orange(A,1))
if(prob(80))
- del B
- del A
+ qdel(B)
+ qdel(A)
/*
/obj/item/weapon/cigarpacket
@@ -577,7 +539,7 @@
w_class = 1
throwforce = 2
var/cigarcount = 6
- flags = ONBELT | TABLEPASS */
+ flags = ONBELT */
/obj/item/weapon/pai_cable
desc = "A flexible coated cable with a universal jack on one end."
@@ -596,7 +558,7 @@
item_state = "RPED"
icon_override = 'icons/mob/in-hand/tools.dmi'
w_class = 5
- can_hold = list("/obj/item/weapon/stock_parts","/obj/item/weapon/cell")
+ can_hold = list("/obj/item/weapon/stock_parts")
storage_slots = 50
use_to_pickup = 1
allow_quick_gather = 1
@@ -672,6 +634,7 @@
/obj/item/weapon/stock_parts/capacitor/adv
name = "advanced capacitor"
desc = "An advanced capacitor used in the construction of a variety of devices."
+ icon_state = "adv_capacitor"
origin_tech = "powerstorage=3"
rating = 2
m_amt = 50
@@ -680,7 +643,7 @@
/obj/item/weapon/stock_parts/scanning_module/adv
name = "advanced scanning module"
desc = "A compact, high resolution scanning module used in the construction of certain devices."
- icon_state = "scan_module"
+ icon_state = "adv_scan_module"
origin_tech = "magnets=3"
rating = 2
m_amt = 50
@@ -716,6 +679,7 @@
/obj/item/weapon/stock_parts/capacitor/super
name = "super capacitor"
desc = "A super-high capacity capacitor used in the construction of a variety of devices."
+ icon_state = "super_capacitor"
origin_tech = "powerstorage=5;materials=4"
rating = 3
m_amt = 50
@@ -724,6 +688,7 @@
/obj/item/weapon/stock_parts/scanning_module/phasic
name = "phasic scanning module"
desc = "A compact, high resolution phasic scanning module used in the construction of certain devices."
+ icon_state = "super_scan_module"
origin_tech = "magnets=5"
rating = 3
m_amt = 50
@@ -832,7 +797,6 @@
icon = 'icons/obj/lightning.dmi'
icon_state = "lightning"
desc = "test lightning"
- flags = USEDELAY
New()
icon = midicon
diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm
new file mode 100644
index 00000000000..7cb7799a372
--- /dev/null
+++ b/code/defines/procs/announce.dm
@@ -0,0 +1,121 @@
+/var/datum/announcement/priority/priority_announcement = new(do_log = 0)
+/var/datum/announcement/priority/command/command_announcement = new(do_log = 0, do_newscast = 1)
+
+/datum/announcement
+ var/title = "Attention"
+ var/announcer = ""
+ var/log = 0
+ var/sound
+ var/newscast = 0
+ var/channel_name = "Station Announcements"
+ var/announcement_type = "Announcement"
+ var/disable_newscasts = 1 // Bay also adds announcements to their newscaster system - set this to 0 to also use that system
+
+/datum/announcement/New(var/do_log = 0, var/new_sound = null, var/do_newscast = 0)
+ sound = new_sound
+ log = do_log
+ newscast = do_newscast
+
+/datum/announcement/priority/New(var/do_log = 1, var/new_sound = sound('sound/misc/notice2.ogg'), var/do_newscast = 0)
+ ..(do_log, new_sound, do_newscast)
+ title = "Priority Announcement"
+ announcement_type = "Priority Announcement"
+
+/datum/announcement/priority/command/New(var/do_log = 1, var/new_sound = sound('sound/misc/notice2.ogg'), var/do_newscast = 0)
+ ..(do_log, new_sound, do_newscast)
+ title = "[command_name()] Update"
+ announcement_type = "[command_name()] Update"
+
+/datum/announcement/priority/security/New(var/do_log = 1, var/new_sound = sound('sound/misc/notice2.ogg'), var/do_newscast = 0)
+ ..(do_log, new_sound, do_newscast)
+ title = "Security Announcement"
+ announcement_type = "Security Announcement"
+
+/datum/announcement/proc/Announce(var/message as text, var/new_title = "", var/new_sound = null, var/do_newscast = newscast)
+ if(!message)
+ return
+ var/tmp/message_title = new_title ? new_title : title
+ var/tmp/message_sound = new_sound ? sound(new_sound) : sound
+
+ message = trim_strip_html_properly(message)
+ message_title = html_encode(message_title)
+
+ Message(message, message_title)
+ if(do_newscast)
+ NewsCast(message, message_title)
+ Sound(message_sound)
+ Log(message, message_title)
+
+datum/announcement/proc/Message(message as text, message_title as text)
+ for(var/mob/M in player_list)
+ if(!istype(M,/mob/new_player) && !isdeaf(M))
+ M << "
[title]
"
+ M << "[message]"
+ if (announcer)
+ M << " -[html_encode(announcer)]"
+
+datum/announcement/minor/Message(message as text, message_title as text)
+ world << "[message]"
+
+datum/announcement/priority/Message(message as text, message_title as text)
+ world << "
[message_title]
"
+ world << "[message]"
+ if(announcer)
+ world << " -[html_encode(announcer)]"
+ world << " "
+
+datum/announcement/priority/command/Message(message as text, message_title as text)
+ var/command
+ command += "
[command_name()] Update
"
+ if (message_title)
+ command += "
[message_title]
"
+
+ command += " [message] "
+ command += " "
+ for(var/mob/M in player_list)
+ if(!istype(M,/mob/new_player) && !isdeaf(M))
+ M << command
+
+datum/announcement/priority/security/Message(message as text, message_title as text)
+ world << "[message_title]"
+ world << "[message]"
+
+datum/announcement/proc/NewsCast(message as text, message_title as text)
+ if(disable_newscasts)
+ return
+ if(!newscast)
+ return
+
+ var/datum/news_announcement/news = new
+ news.channel_name = channel_name
+ news.author = announcer
+ news.message = message
+ news.message_type = announcement_type
+ news.can_be_redacted = 0
+ announce_newscaster_news(news)
+
+datum/announcement/proc/PlaySound(var/message_sound)
+ if(!message_sound)
+ return
+ for(var/mob/M in player_list)
+ if(!istype(M,/mob/new_player) && !isdeaf(M))
+ M << message_sound
+
+datum/announcement/proc/Sound(var/message_sound)
+ PlaySound(message_sound)
+
+datum/announcement/priority/Sound(var/message_sound)
+ if(sound)
+ world << sound
+
+datum/announcement/priority/command/Sound(var/message_sound)
+ PlaySound(message_sound)
+
+datum/announcement/proc/Log(message as text, message_title as text)
+ if(log)
+ log_say("[key_name(usr)] has made \a [announcement_type]: [message_title] - [message] - [announcer]")
+ message_admins("[key_name_admin(usr)] has made \a [announcement_type].", 1)
+
+/proc/GetNameAndAssignmentFromId(var/obj/item/weapon/card/id/I)
+ // Format currently matches that of newscaster feeds: Registered Name (Assigned Rank)
+ return I.assignment ? "[I.registered_name] ([I.assignment])" : I.registered_name
diff --git a/code/defines/procs/captain_announce.dm b/code/defines/procs/captain_announce.dm
deleted file mode 100644
index 9b91705ea56..00000000000
--- a/code/defines/procs/captain_announce.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-/proc/captain_announce(var/text)
- world << "
"
-
- command += " [html_encode(text)] "
- command += " "
- for(var/mob/M in player_list)
- if(!istype(M,/mob/new_player))
- M << command
diff --git a/code/defines/procs/radio.dm b/code/defines/procs/radio.dm
new file mode 100644
index 00000000000..1e0e045928b
--- /dev/null
+++ b/code/defines/procs/radio.dm
@@ -0,0 +1,81 @@
+#define TELECOMMS_RECEPTION_NONE 0
+#define TELECOMMS_RECEPTION_SENDER 1
+#define TELECOMMS_RECEPTION_RECEIVER 2
+#define TELECOMMS_RECEPTION_BOTH 3
+
+/proc/get_frequency_name(var/display_freq)
+ var/freq_text
+
+ // the name of the channel
+ if(display_freq in ANTAG_FREQS)
+ freq_text = "#unkn"
+ else
+ for(var/channel in radiochannels)
+ if(radiochannels[channel] == display_freq)
+ freq_text = channel
+ break
+
+ // --- If the frequency has not been assigned a name, just use the frequency as the name ---
+ if(!freq_text)
+ freq_text = format_frequency(display_freq)
+
+ return freq_text
+
+/datum/reception
+ var/obj/machinery/message_server/message_server = null
+ var/telecomms_reception = TELECOMMS_RECEPTION_NONE
+ var/message = ""
+
+/datum/receptions
+ var/obj/machinery/message_server/message_server = null
+ var/sender_reception = TELECOMMS_RECEPTION_NONE
+ var/list/receiver_reception = new
+
+/proc/get_message_server()
+ if(message_servers)
+ for (var/obj/machinery/message_server/MS in message_servers)
+ if(MS.active)
+ return MS
+ return null
+
+/proc/check_signal(var/datum/signal/signal)
+ return signal && signal.data["done"]
+
+/proc/get_sender_reception(var/atom/sender, var/datum/signal/signal)
+ return check_signal(signal) ? TELECOMMS_RECEPTION_SENDER : TELECOMMS_RECEPTION_NONE
+
+/proc/get_receiver_reception(var/receiver, var/datum/signal/signal)
+ if(receiver && check_signal(signal))
+ var/turf/pos = get_turf(receiver)
+ if(pos && (pos.z in signal.data["level"]))
+ return TELECOMMS_RECEPTION_RECEIVER
+ return TELECOMMS_RECEPTION_NONE
+
+/proc/get_reception(var/atom/sender, var/receiver, var/message = "", var/do_sleep = 1)
+ var/datum/reception/reception = new
+
+ // check if telecomms I/O route 1459 is stable
+ reception.message_server = get_message_server()
+
+ var/datum/signal/signal = sender.telecomms_process(do_sleep) // Be aware that this proc calls sleep, to simulate transmition delays
+ reception.telecomms_reception |= get_sender_reception(sender, signal)
+ reception.telecomms_reception |= get_receiver_reception(receiver, signal)
+ reception.message = signal && signal.data["compression"] > 0 ? Gibberish(message, signal.data["compression"] + 50) : message
+
+ return reception
+
+/proc/get_receptions(var/atom/sender, var/list/atom/receivers, var/do_sleep = 1)
+ var/datum/receptions/receptions = new
+ receptions.message_server = get_message_server()
+
+ var/datum/signal/signal
+ if(sender)
+ signal = sender.telecomms_process(do_sleep)
+ receptions.sender_reception = get_sender_reception(sender, signal)
+
+ for(var/atom/receiver in receivers)
+ if(!signal)
+ signal = receiver.telecomms_process()
+ receptions.receiver_reception[receiver] = get_receiver_reception(receiver, signal)
+
+ return receptions
diff --git a/code/defines/procs/records.dm b/code/defines/procs/records.dm
new file mode 100644
index 00000000000..4e9fbcc9afc
--- /dev/null
+++ b/code/defines/procs/records.dm
@@ -0,0 +1,48 @@
+/proc/CreateGeneralRecord()
+ var/mob/living/carbon/human/dummy = new()
+ dummy.mind = new()
+ var/icon/front = new(get_id_photo(dummy), dir = SOUTH)
+ var/icon/side = new(get_id_photo(dummy), dir = WEST)
+ var/datum/data/record/G = new /datum/data/record()
+ G.fields["name"] = "New Record"
+ G.fields["id"] = text("[]", add_zero(num2hex(rand(1, 1.6777215E7)), 6))
+ G.fields["rank"] = "Unassigned"
+ G.fields["real_rank"] = "Unassigned"
+ G.fields["sex"] = "Male"
+ G.fields["age"] = "Unknown"
+ G.fields["fingerprint"] = "Unknown"
+ G.fields["p_stat"] = "Active"
+ G.fields["m_stat"] = "Stable"
+ G.fields["species"] = "Human"
+ G.fields["home_system"] = "Unknown"
+ G.fields["citizenship"] = "Unknown"
+ G.fields["faction"] = "Unknown"
+ G.fields["religion"] = "Unknown"
+ G.fields["photo_front"] = front
+ G.fields["photo_side"] = side
+ data_core.general += G
+
+ del(dummy)
+ return G
+
+/proc/CreateSecurityRecord(var/name as text, var/id as text)
+ var/datum/data/record/R = new /datum/data/record()
+ R.fields["name"] = name
+ R.fields["id"] = id
+ R.name = text("Security Record #[id]")
+ R.fields["criminal"] = "None"
+ R.fields["mi_crim"] = "None"
+ R.fields["mi_crim_d"] = "No minor crime convictions."
+ R.fields["ma_crim"] = "None"
+ R.fields["ma_crim_d"] = "No major crime convictions."
+ R.fields["notes"] = "No notes."
+ data_core.security += R
+ return R
+
+/proc/find_security_record(field, value)
+ return find_record(field, value, data_core.security)
+
+/proc/find_record(field, value, list/L)
+ for(var/datum/data/record/R in L)
+ if(R.fields[field] == value)
+ return R
diff --git a/code/game/area/Dynamic areas.dm b/code/game/area/Dynamic areas.dm
index 9d6010f29b4..9198a990ed5 100644
--- a/code/game/area/Dynamic areas.dm
+++ b/code/game/area/Dynamic areas.dm
@@ -24,15 +24,18 @@
match_tag = "arrivals"
match_width = 5
match_height = 4
+ requires_power = 0
/area/dynamic/source/lobby_russian
name = "\improper Russian Lounge"
match_tag = "arrivals"
match_width = 5
match_height = 4
+ requires_power = 0
/area/dynamic/source/lobby_disco
name = "\improper Disco Lounge"
match_tag = "arrivals"
match_width = 5
- match_height = 4
\ No newline at end of file
+ match_height = 4
+ requires_power = 0
\ No newline at end of file
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index e703f7ba3b2..7d5547f3c45 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -22,6 +22,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/poweralm = 1
var/party = null
var/radalert = 0
+ var/report_alerts = 1 // Should atmos alerts notify the AI/computers
level = null
name = "Space"
icon = 'icons/turf/areas.dmi'
@@ -69,7 +70,7 @@ var/list/teleportlocs = list()
var/list/turfs = get_area_turfs(AR.type)
if(turfs.len)
var/turf/picked = pick(turfs)
- if (picked.z == 1)
+ if ((picked.z in config.station_levels))
teleportlocs += AR.name
teleportlocs[AR.name] = AR
@@ -82,13 +83,13 @@ var/list/ghostteleportlocs = list()
/hook/startup/proc/setupGhostTeleportLocs()
for(var/area/AR in world)
if(ghostteleportlocs.Find(AR.name)) continue
- if(istype(AR, /area/turret_protected/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome))
+ if(istype(AR, /area/tdome))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
var/list/turfs = get_area_turfs(AR.type)
if(turfs.len)
var/turf/picked = pick(turfs)
- if (picked.z == 1 || picked.z == 5 || picked.z == 3)
+ if ((picked.z in config.player_levels))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
@@ -1983,6 +1984,11 @@ area/security/podbay
//Traitor Station
+/area/traitor
+ name = "\improper Syndicate Base"
+ icon_state = "syndie_hall"
+ report_alerts = 0
+
/area/traitor/rnd
name = "\improper Syndicate Research and Development"
icon_state = "syndie_rnd"
@@ -2262,6 +2268,7 @@ area/security/podbay
/area/awaycontent
name = "space"
+ report_alerts = 0
/area/awaycontent/a1
icon_state = "awaycontent1"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 3c04e9cf951..c24ea331bcd 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -52,25 +52,32 @@
InitializeLighting()
-/area/proc/poweralert(var/state, var/obj/source as obj)
+/area/proc/poweralert(var/state, var/obj/source as obj)
if (state != poweralm)
poweralm = state
if(istype(source)) //Only report power alarms on the z-level where the source is located.
var/list/cameras = list()
for (var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
+ if(!report_alerts)
+ break
cameras += C
if(state == 1)
+
C.network.Remove("Power Alarms")
else
C.network.Add("Power Alarms")
for (var/mob/living/silicon/aiPlayer in player_list)
+ if(!report_alerts)
+ break
if(aiPlayer.z == source.z)
if (state == 1)
aiPlayer.cancelAlarm("Power", src, source)
else
aiPlayer.triggerAlarm("Power", src, cameras, source)
for(var/obj/machinery/computer/station_alert/a in machines)
+ if(!report_alerts)
+ break
if(a.z == source.z)
if(state == 1)
a.cancelAlarm("Power", src, source)
@@ -107,11 +114,17 @@
for(var/area/RA in related)
//updateicon()
for(var/obj/machinery/camera/C in RA)
+ if(!report_alerts)
+ break
cameras += C
C.network.Add("Atmosphere Alarms")
for(var/mob/living/silicon/aiPlayer in player_list)
+ if(!report_alerts)
+ break
aiPlayer.triggerAlarm("Atmosphere", src, cameras, src)
for(var/obj/machinery/computer/station_alert/a in machines)
+ if(!report_alerts)
+ break
a.triggerAlarm("Atmosphere", src, cameras, src)
air_doors_activated=1
CloseFirelocks()
@@ -119,10 +132,16 @@
else if (atmosalm == 2)
for(var/area/RA in related)
for(var/obj/machinery/camera/C in RA)
+ if(!report_alerts)
+ break
C.network.Remove("Atmosphere Alarms")
for(var/mob/living/silicon/aiPlayer in player_list)
+ if(!report_alerts)
+ break
aiPlayer.cancelAlarm("Atmosphere", src, src)
for(var/obj/machinery/computer/station_alert/a in machines)
+ if(!report_alerts)
+ break
a.cancelAlarm("Atmosphere", src, src)
air_doors_activated=0
OpenFirelocks()
@@ -162,11 +181,17 @@
var/list/cameras = list()
for(var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
+ if(!report_alerts)
+ continue
cameras.Add(C)
C.network.Add("Fire Alarms")
for (var/mob/living/silicon/ai/aiPlayer in player_list)
+ if(!report_alerts)
+ continue
aiPlayer.triggerAlarm("Fire", src, cameras, src)
for (var/obj/machinery/computer/station_alert/a in machines)
+ if(!report_alerts)
+ continue
a.triggerAlarm("Fire", src, cameras, src)
/area/proc/firereset()
@@ -176,10 +201,16 @@
updateicon()
for(var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
+ if(!report_alerts)
+ continue
C.network.Remove("Fire Alarms")
for (var/mob/living/silicon/ai/aiPlayer in player_list)
+ if(!report_alerts)
+ continue
aiPlayer.cancelAlarm("Fire", src, src)
for (var/obj/machinery/computer/station_alert/a in machines)
+ if(!report_alerts)
+ continue
a.cancelAlarm("Fire", src, src)
OpenFirelocks()
@@ -230,12 +261,11 @@
return
/area/proc/updateicon()
- if ((fire || eject || party || radalert) && ((!requires_power)?(!requires_power):power_environ))//If it doesn't require power, can still activate this proc.
- // Highest priority at the top.
- if(radalert && !fire)
- icon_state = "radiation"
- blend_mode = BLEND_MULTIPLY
- else if(fire && !radalert && !eject && !party)
+ if(radalert) // always show the radiation alert, regardless of power
+ icon_state = "radiation"
+ blend_mode = BLEND_MULTIPLY
+ else if ((fire || eject || party) && ((!requires_power)?(!requires_power):power_environ))//If it doesn't require power, can still activate this proc.
+ if(fire && !radalert && !eject && !party)
icon_state = "red"
blend_mode = BLEND_MULTIPLY
/*else if(atmosalm && !fire && !eject && !party)
@@ -355,7 +385,7 @@
thunk(L)
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
- if(L && L.client && (L.client.prefs.toggles & SOUND_AMBIENCE))
+ if(L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE))
if(!L.client.ambience_playing)
L.client.ambience_playing = 1
L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 6dec5fed5c8..61b63bb6a9d 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1,7 +1,10 @@
+var/global/list/del_profiling = list()
+var/global/list/gdel_profiling = list()
+var/global/list/ghdel_profiling = list()
/atom
layer = 2
var/level = 2
- var/flags = FPRINT
+ var/flags = 0
var/list/fingerprints
var/list/fingerprintshidden
var/fingerprintslast = null
@@ -9,7 +12,6 @@
var/last_bumped = 0
var/pass_flags = 0
var/throwpass = 0
- var/datum/crafting_holder/craft_holder = null
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
///Chemistry.
@@ -25,16 +27,18 @@
// Garbage collection
var/gc_destroyed=null
-/atom/Del()
- // Pass to Destroy().
- if(!gc_destroyed)
- Destroy()
- ..()
-/atom/proc/Destroy()
- gc_destroyed=world.time
+/atom/Destroy()
+ SetOpacity(0)
+ if(reagents)
+ reagents.Destroy()
+ reagents = null
+
+ // Idea by ChuckTheSheep to make the object even more unreferencable.
+ invisibility = 101
+
/atom/proc/CheckParts()
return
@@ -242,6 +246,8 @@ its easier to just keep the beam vertical.
/atom/proc/blob_act()
return
+/atom/proc/emag_act()
+ return
/atom/proc/hitby(atom/movable/AM as mob|obj)
if (density)
@@ -294,7 +300,7 @@ its easier to just keep the beam vertical.
add_fibers(M)
//He has no prints!
- if (M_FINGERPRINTS in M.mutations)
+ if (FINGERPRINTS in M.mutations)
if(fingerprintslast != M.key)
fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]"
fingerprintslast = M.key
@@ -378,8 +384,6 @@ its easier to just keep the beam vertical.
M.dna = new /datum/dna(null)
M.dna.real_name = M.real_name
M.check_dna()
- if (!( src.flags ) & FPRINT)
- return 0
if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
blood_DNA = list()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index b80fe3249f6..fdb556a6b64 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -15,13 +15,67 @@
var/mob/pulledby = null
var/area/areaMaster
-
+ var/hard_deleted = 0
/atom/movable/New()
. = ..()
areaMaster = get_area_master(src)
+/atom/movable/Destroy()
+ if(opacity)
+ if(isturf(loc))
+ if(loc:lighting_lumcount > 1)
+ UpdateAffectingLights()
+ gcDestroyed = "Bye, world!"
+ tag = null
+ loc = null
+/*
+ if(istype(beams) && beams.len)
+ for(var/obj/effect/beam/B in beams)
+ if(B && B.target == src)
+ B.target = null
+ if(B.master && B.master.target == src)
+ B.master.target = null
+ beams.len = 0
+*/
+ ..()
+
+/proc/delete_profile(var/type, code = 0)
+ if(!ticker || !ticker.current_state < 3) return
+ switch(code)
+ if(0)
+ if (!("[type]" in del_profiling))
+ del_profiling["[type]"] = 0
+
+ del_profiling["[type]"] += 1
+ if(1)
+ if (!("[type]" in ghdel_profiling))
+ ghdel_profiling["[type]"] = 0
+
+ ghdel_profiling["[type]"] += 1
+ if(2)
+ if (!("[type]" in gdel_profiling))
+ gdel_profiling["[type]"] = 0
+
+ gdel_profiling["[type]"] += 1
+ if(garbageCollector)
+ garbageCollector.soft_dels++
+
+/atom/movable/Del()
+ if (gcDestroyed)
+ garbageCollector.dequeue("\ref[src]")
+
+ if (hard_deleted)
+ delete_profile("[type]", 1)
+ else
+ delete_profile("[type]", 2)
+ else // direct del calls or nulled explicitly.
+ delete_profile("[type]", 0)
+ Destroy()
+
+ ..()
+
// Used in shuttle movement and AI eye stuff.
// Primarily used to notify objects being moved by a shuttle/bluespace fuckup.
/atom/movable/proc/setLoc(var/T, var/teleported=0)
@@ -99,17 +153,14 @@
src.throw_impact(A,speed)
/atom/movable/proc/throw_at(atom/target, range, speed, thrower)
- if(!target || !src) return 0
+ if(!target || !src || (flags & NODROP))
+ return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
src.throwing = 1
src.thrower = thrower
src.throw_source = get_turf(src) //store the origin turf
- if(usr)
- if(M_HULK in usr.mutations)
- src.throwing = 2 // really strong throw!
-
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
@@ -206,9 +257,9 @@
verbs.Cut()
return
-/atom/movable/overlay/attackby(a, b)
+/atom/movable/overlay/attackby(a, b, c)
if (src.master)
- return src.master.attackby(a, b)
+ return src.master.attackby(a, b, c)
return
/atom/movable/overlay/attack_paw(a, b, c)
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index d4b1a0d44a6..e028702a44e 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -163,7 +163,7 @@ var/global/list/bad_blocks[0]
// Set a DNA UI block's raw value.
/datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0)
if (block<=0) return
- ASSERT(value>=0)
+ ASSERT(value>0)
ASSERT(value<=4095)
UI[block]=value
dirtyUI=1
@@ -178,7 +178,10 @@ var/global/list/bad_blocks[0]
// Set a DNA UI block's value, given a value and a max possible value.
// Used in hair and facial styles (value being the index and maxvalue being the len of the hairstyle list)
/datum/dna/proc/SetUIValueRange(var/block,var/value,var/maxvalue,var/defer=0)
- if (block<=0) return
+ if (block<=0)
+ return
+ if(value == 0)
+ value = 1
ASSERT(maxvalue<=4095)
var/range = (4095 / maxvalue)
if(value)
diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm
index 6f0e3412f19..545e65b691d 100644
--- a/code/game/dna/dna2_domutcheck.dm
+++ b/code/game/dna/dna2_domutcheck.dm
@@ -5,6 +5,7 @@
// flags: See below, bitfield.
#define MUTCHK_FORCED 1
/proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0)
+
for(var/datum/dna/gene/gene in dna_genes)
if(!M || !M.dna)
return
@@ -75,6 +76,14 @@
var/gene_active = (gene.flags & GENE_ALWAYS_ACTIVATE)
if(!gene_active)
gene_active = M.dna.GetSEState(gene.block)
+
+ var/defaultgenes // Do not mutate inherent species abilities
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ defaultgenes = H.species.default_genes
+
+ if((gene in defaultgenes) && gene_active)
+ return
// Prior state
var/gene_prior_status = (gene.type in M.active_genes)
diff --git a/code/game/dna/dna_misc.dm b/code/game/dna/dna_misc.dm
index 13ddefd5514..6b237f5a00e 100644
--- a/code/game/dna/dna_misc.dm
+++ b/code/game/dna/dna_misc.dm
@@ -410,7 +410,7 @@
for(var/obj/item/W in (H.contents-implants))
if (W==H.w_uniform) // will be teared
continue
- H.drop_from_inventory(W)
+ H.unEquip(W)
M.monkeyizing = 1
M.canmove = 0
M.icon = null
@@ -483,7 +483,7 @@
W.loc = null
if(!connected)
for(var/obj/item/W in (Mo.contents-implants))
- Mo.drop_from_inventory(W)
+ Mo.unEquip(W)
M.monkeyizing = 1
M.canmove = 0
M.icon = null
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 5360f08814e..29b1cd1b3d9 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -45,6 +45,7 @@
use_power = 1
idle_power_usage = 50
active_power_usage = 300
+ interact_offline = 1
var/locked = 0
var/mob/living/carbon/occupant = null
var/obj/item/weapon/reagent_containers/glass/beaker = null
@@ -64,7 +65,7 @@
component_parts += new /obj/item/stack/cable_coil(src, 1)
component_parts += new /obj/item/stack/cable_coil(src, 1)
RefreshParts()
-
+
/obj/machinery/dna_scannernew/upgraded/New()
..()
component_parts = list()
@@ -190,7 +191,7 @@
if(user.pulling == L)
user.pulling = null
-/obj/machinery/dna_scannernew/attackby(var/obj/item/weapon/item as obj, var/mob/user as mob)
+/obj/machinery/dna_scannernew/attackby(var/obj/item/weapon/item as obj, var/mob/user as mob, params)
if(istype(item, /obj/item/weapon/screwdriver))
if(occupant)
user << "The maintenance panel is locked."
@@ -232,7 +233,7 @@
return
put_in(G.affecting)
src.add_fingerprint(user)
- del(G)
+ qdel(G)
return
/obj/machinery/dna_scannernew/proc/put_in(var/mob/M)
@@ -308,7 +309,7 @@
if(prob(75))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
- del(src)
+ qdel(src)
/obj/machinery/computer/scan_consolenew
name = "DNA Modifier Access Console"
@@ -337,7 +338,7 @@
active_power_usage = 400
var/waiting_for_user_input=0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
-/obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob, params)
if (istype(I, /obj/item/weapon/disk/data)) //INSERT SOME diskS
if (!src.disk)
user.drop_item()
@@ -366,7 +367,7 @@
/obj/machinery/computer/scan_consolenew/blob_act()
if(prob(75))
- del(src)
+ qdel(src)
/obj/machinery/computer/scan_consolenew/power_change()
if(stat & BROKEN)
@@ -503,7 +504,7 @@
occupantData["name"] = connected.occupant.name
occupantData["stat"] = connected.occupant.stat
occupantData["isViableSubject"] = 1
- if ((M_NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !src.connected.occupant.dna)
+ if ((NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !src.connected.occupant.dna)
occupantData["isViableSubject"] = 0
occupantData["health"] = connected.occupant.health
occupantData["maxHealth"] = connected.occupant.maxHealth
@@ -842,7 +843,7 @@
return 1
if (bufferOption == "transfer")
- if (!src.connected.occupant || (M_NOCLONE in src.connected.occupant.mutations && connected.scan_level < 3) || !src.connected.occupant.dna)
+ if (!src.connected.occupant || (NOCLONE in src.connected.occupant.mutations && connected.scan_level < 3) || !src.connected.occupant.dna)
return
irradiating = 2
diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm
index eaf4500359e..0431d666c53 100644
--- a/code/game/dna/genes/disabilities.dm
+++ b/code/game/dna/genes/disabilities.dm
@@ -55,7 +55,7 @@
name="Hallucinate"
activation_message="Your mind says 'Hello'."
deactivation_message ="Sanity returns. Or does it?"
- mutation=M_HALLUCINATE
+ mutation=HALLUCINATE
New()
block=HALLUCINATIONBLOCK
@@ -82,7 +82,7 @@
name="Clumsiness"
activation_message="You feel lightheaded."
deactivation_message ="You regain some control of your movements"
- mutation=M_CLUMSY
+ mutation=CLUMSY
New()
block=CLUMSYBLOCK
@@ -148,4 +148,14 @@
block=LISPBLOCK
OnSay(var/mob/M, var/message)
- return replacetext(message,"s","th")
\ No newline at end of file
+ return replacetext(message,"s","th")
+
+/datum/dna/gene/disability/comic
+ name = "Comic"
+ desc = "This will only bring death and destruction."
+ activation_message = "Uh oh!"
+ deactivation_message = "Well thank god that's over with."
+ mutation=COMIC
+
+ New()
+ block = COMICBLOCK
diff --git a/code/game/dna/genes/goon_disabilities.dm b/code/game/dna/genes/goon_disabilities.dm
index c0973e2206d..4f8af67358b 100644
--- a/code/game/dna/genes/goon_disabilities.dm
+++ b/code/game/dna/genes/goon_disabilities.dm
@@ -12,6 +12,7 @@
desc = "Completely shuts down the speech center of the subject's brain."
activation_message = "You feel unable to express yourself at all."
deactivation_message = "You feel able to speak freely again."
+ sdisability = 1
New()
..()
@@ -57,7 +58,7 @@
activation_message = "You feel blubbery and lethargic!"
deactivation_message = "You feel fit!"
- mutation = M_OBESITY
+ mutation = OBESITY
New()
..()
@@ -219,7 +220,7 @@
deactivation_message = "Your stomach stops acting up. Phew!"
instability=2
- mutation = M_TOXIC_FARTS
+ mutation = TOXIC_FARTS
New()
..()
@@ -237,7 +238,7 @@
activation_message = "You feel buff!"
deactivation_message = "You feel wimpy and weak."
- mutation = M_STRONG
+ mutation = STRONG
New()
..()
@@ -335,7 +336,7 @@
if(L)
usr.attack_log += text("\[[time_stamp()]\] [usr.real_name] ([usr.ckey]) cast the spell [name] on [L.real_name] ([L.ckey]).")
- msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast the spell [name] on [L.real_name] ([L.ckey]) ()")
+ msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast the spell [name] on [L.real_name] ([L.ckey]) (JMP)")
L.adjust_fire_stacks(0.5)
L.visible_message("\red [L.name] suddenly bursts into flames!")
diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm
index e421a9594f8..8f57ce1e6cd 100644
--- a/code/game/dna/genes/goon_powers.dm
+++ b/code/game/dna/genes/goon_powers.dm
@@ -6,7 +6,7 @@
activation_messages=list("You feel unusually sober.")
deactivation_messages = list("You feel like you could use a stiff drink.")
- mutation=M_SOBER
+ mutation=SOBER
New()
block=SOBERBLOCK
@@ -19,7 +19,7 @@
deactivation_messages = list("You feel oddly exposed.")
instability=2
- mutation=M_PSY_RESIST
+ mutation=PSY_RESIST
New()
block=PSYRESISTBLOCK
@@ -47,6 +47,7 @@
desc = "Enables the subject to bend low levels of light around themselves, creating a cloaking effect."
activation_messages = list("You begin to fade into the shadows.")
deactivation_messages = list("You become fully visible.")
+ activation_prob=10
New()
block=SHADOWBLOCK
@@ -66,6 +67,7 @@
desc = "The subject becomes able to subtly alter light patterns to become invisible, as long as they remain still."
activation_messages = list("You feel one with your surroundings.")
deactivation_messages = list("You feel oddly exposed.")
+ activation_prob=10
New()
block=CHAMELEONBLOCK
@@ -149,7 +151,7 @@
usr << "\red This will only work on normal organic beings."
return
- if (M_RESIST_COLD in C.mutations)
+ if (RESIST_COLD in C.mutations)
C.visible_message("\red A cloud of fine ice crystals engulfs [C.name], but disappears almost instantly!")
return
var/handle_suit = 0
@@ -162,12 +164,12 @@
H.visible_message("\red [usr] sprays a cloud of fine ice crystals, engulfing [H]!",
"[usr] sprays a cloud of fine ice crystals over your [H.head]'s visor.")
log_admin("[ckey(usr.key)] has used cryokinesis on [ckey(C.key)], internals yes, suit yes")
- msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), ()")
+ msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)")
else
H.visible_message("\red [usr] sprays a cloud of fine ice crystals engulfing, [H]!",
"[usr] sprays a cloud of fine ice crystals cover your [H.head]'s visor and make it into your air vents!.")
log_admin("[usr.real_name] ([ckey(usr.key)]) has used cryokinesis on [C.real_name] ([ckey(C.key)]), ()")
- msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), ()")
+ msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)")
H.bodytemperature = max(0, H.bodytemperature - 50)
H.adjustFireLoss(5)
if(!handle_suit)
@@ -177,7 +179,7 @@
C.visible_message("\red [usr] sprays a cloud of fine ice crystals, engulfing [C]!")
log_admin("[ckey(usr.key)] has used cryokinesis on [ckey(C.key)], internals no, suit no")
- msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), ()")
+ msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)")
//playsound(usr.loc, 'bamf.ogg', 50, 0)
@@ -316,7 +318,7 @@
else
usr.visible_message("\red [usr] eats \the [the_item].")
playsound(usr.loc, 'sound/items/eatfood.ogg', 50, 0)
- del(the_item)
+ qdel(the_item)
doHeal(usr)
return
@@ -392,7 +394,7 @@
else usr.pixel_y -= 8
sleep(1)
- if (M_FAT in usr.mutations && prob(66))
+ if (FAT in usr.mutations && prob(66))
usr.visible_message("\red [usr.name] crashes due to their heavy weight!")
//playsound(usr.loc, 'zhit.wav', 50, 1)
usr.weakened += 10
@@ -481,7 +483,7 @@
activation_messages = list("You suddenly notice more about others than you did before.")
deactivation_messages = list("You no longer feel able to sense intentions.")
instability=1
- mutation=M_EMPATH
+ mutation=EMPATH
New()
..()
@@ -514,7 +516,7 @@
usr << "\red You may only use this on other organic beings."
return
- if (M_PSY_RESIST in M.mutations)
+ if (PSY_RESIST in M.mutations)
usr << "\red You can't see into [M.name]'s mind at all!"
return
@@ -578,7 +580,7 @@
usr << "\blue Numbers: You sense the number[numbers.len>1?"s":""] [english_list(numbers)] [numbers.len>1?"are":"is"] important to [M.name]."
usr << "\blue Thoughts: [M.name] is currently [thoughts]."
- if (M_EMPATH in M.mutations)
+ if (EMPATH in M.mutations)
M << "\red You sense [usr.name] reading your mind."
else if (prob(5) || M.mind.assigned_role=="Chaplain")
M << "\red You sense someone intruding upon your thoughts..."
@@ -594,7 +596,7 @@
deactivation_messages = list("You no longer feel gassy. What a relief!")
instability=1
- mutation = M_SUPER_FART
+ mutation = SUPER_FART
New()
..()
diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm
index e1faba73a75..03ca9880c2c 100644
--- a/code/game/dna/genes/monkey.dm
+++ b/code/game/dna/genes/monkey.dm
@@ -22,7 +22,7 @@
for(var/obj/item/W in (H.contents-implants))
if (W==H.w_uniform) // will be teared
continue
- H.drop_from_inventory(W)
+ H.unEquip(W)
M.monkeyizing = 1
M.canmove = 0
M.icon = null
@@ -33,7 +33,7 @@
animation.master = src
flick("h2monkey", animation)
sleep(48)
- del(animation)
+ qdel(animation)
var/mob/living/carbon/monkey/O = null
@@ -56,7 +56,7 @@
for(var/obj/T in (M.contents-implants))
- del(T)
+ qdel(T)
O.loc = M.loc
@@ -78,7 +78,7 @@
I.loc = O
I.implanted = O
// O.update_icon = 1 //queue a full icon update at next life() call
- del(M)
+ qdel(M)
return
/datum/dna/gene/monkey/deactivate(var/mob/living/M, var/connected, var/flags)
@@ -93,7 +93,7 @@
W.loc = null
if(!connected)
for(var/obj/item/W in (Mo.contents-implants))
- Mo.drop_from_inventory(W)
+ Mo.unEquip(W)
M.monkeyizing = 1
M.canmove = 0
M.icon = null
diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm
index 81d20456736..d74379c3d8b 100644
--- a/code/game/dna/genes/powers.dm
+++ b/code/game/dna/genes/powers.dm
@@ -5,8 +5,9 @@
/datum/dna/gene/basic/nobreath
name="No Breathing"
activation_messages=list("You feel no need to breathe.")
- mutation=M_NO_BREATH
+ mutation=NO_BREATH
instability=2
+ activation_prob=10
New()
block=NOBREATHBLOCK
@@ -15,7 +16,7 @@
/datum/dna/gene/basic/regenerate
name="Regenerate"
activation_messages=list("You feel better.")
- mutation=M_REGEN
+ mutation=REGEN
instability=2
New()
@@ -24,7 +25,7 @@
/datum/dna/gene/basic/increaserun
name="Super Speed"
activation_messages=list("Your leg muscles pulsate.")
- mutation=M_RUN
+ mutation=RUN
instability=1
New()
@@ -34,7 +35,7 @@
/datum/dna/gene/basic/heat_resist
name="Heat Resistance"
activation_messages=list("Your skin is icy to the touch.")
- mutation=M_RESIST_HEAT
+ mutation=RESIST_HEAT
instability=2
New()
@@ -45,7 +46,7 @@
return !(/datum/dna/gene/basic/cold_resist in M.active_genes)
// Probability check
var/_prob = 15
- if(M_RESIST_COLD in M.mutations)
+ if(RESIST_COLD in M.mutations)
_prob=5
if(probinj(_prob,(flags&MUTCHK_FORCED)))
return 1
@@ -56,7 +57,7 @@
/datum/dna/gene/basic/cold_resist
name="Cold Resistance"
activation_messages=list("Your body is filled with warmth.")
- mutation=M_RESIST_COLD
+ mutation=RESIST_COLD
instability=2
New()
@@ -67,7 +68,7 @@
return !(/datum/dna/gene/basic/heat_resist in M.active_genes)
// Probability check
var/_prob=30
- if(M_RESIST_HEAT in M.mutations)
+ if(RESIST_HEAT in M.mutations)
_prob=5
if(probinj(_prob,(flags&MUTCHK_FORCED)))
return 1
@@ -78,7 +79,7 @@
/datum/dna/gene/basic/noprints
name="No Prints"
activation_messages=list("Your fingers feel numb.")
- mutation=M_FINGERPRINTS
+ mutation=FINGERPRINTS
instability=1
New()
@@ -87,7 +88,7 @@
/datum/dna/gene/basic/noshock
name="Shock Immunity"
activation_messages=list("Your skin feels strange.")
- mutation=M_NO_SHOCK
+ mutation=NO_SHOCK
instability=2
New()
@@ -96,7 +97,7 @@
/datum/dna/gene/basic/midget
name="Midget"
activation_messages=list("Your skin feels rubbery.")
- mutation=M_DWARF
+ mutation=DWARF
instability=1
New()
@@ -104,7 +105,7 @@
can_activate(var/mob/M,var/flags)
// Can't be big and small.
- if(M_HULK in M.mutations)
+ if(HULK in M.mutations)
return 0
return ..(M,flags)
@@ -112,10 +113,57 @@
..(M,connected,flags)
M.pass_flags |= 1
+
+// OLD HULK BEHAVIOR
+/datum/dna/gene/basic/hulk
+ name="Hulk"
+ activation_messages=list("Your muscles hurt.")
+ mutation=HULK
+ activation_prob=5
+
+ New()
+ block=HULKBLOCK
+
+ can_activate(var/mob/M,var/flags)
+ // Can't be big AND small.
+ if(DWARF in M.mutations)
+ return 0
+ return ..(M,flags)
+
+ activate(var/mob/M, var/connected, var/flags)
+ ..()
+ var/status = CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH
+ M.status_flags &= ~status
+
+ deactivate(var/mob/M, var/connected, var/flags)
+ ..()
+ M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH
+
+ OnDrawUnderlays(var/mob/M,var/g,var/fat)
+ if(HULK in M.mutations)
+ if(fat)
+ return "hulk_[fat]_s"
+ else
+ return "hulk_[g]_s"
+ return 0
+
+ OnMobLife(var/mob/living/carbon/human/M)
+ if(!istype(M)) return
+ if ((HULK in M.mutations) && M.health <= 25)
+ M.mutations.Remove(HULK)
+ M.dna.SetSEState(HULKBLOCK,0)
+ M.update_mutations() //update our mutation overlays
+ M.update_body()
+ M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH //temporary fix until the problem can be solved.
+ M << "You suddenly feel very weak."
+ M.Weaken(3)
+ M.emote("collapse")
+
/datum/dna/gene/basic/xray
name="X-Ray Vision"
activation_messages=list("The walls suddenly disappear.")
- mutation=M_XRAY
+ mutation=XRAY
+ activation_prob=10
instability=2
New()
@@ -124,8 +172,8 @@
/datum/dna/gene/basic/tk
name="Telekenesis"
activation_messages=list("You feel smarter.")
- mutation=M_TK
- activation_prob=15
+ mutation=TK
+ activation_prob=10
instability=5
New()
diff --git a/code/game/dna/genes/vg_disabilities.dm b/code/game/dna/genes/vg_disabilities.dm
index ad6c103f429..6cb2c6f6ce8 100644
--- a/code/game/dna/genes/vg_disabilities.dm
+++ b/code/game/dna/genes/vg_disabilities.dm
@@ -17,7 +17,7 @@
message = replacetext(message,"!","!!")
return uppertext(message)
-
+/* BROKEN WITH NEW SAYCODE
/datum/dna/gene/disability/speech/whisper
name = "Quiet"
desc = "Damages the subjects vocal cords"
@@ -30,13 +30,13 @@
can_activate(var/mob/M,var/flags)
// No loud whispering.
- if(M_LOUD in M.mutations)
+ if(LOUD in M.mutations)
return 0
return ..(M,flags)
OnSay(var/mob/M, var/message)
M.whisper(message)
-
+*/
/datum/dna/gene/disability/dizzy
name = "Dizzy"
@@ -51,5 +51,5 @@
OnMobLife(var/mob/living/carbon/human/M)
if(!istype(M)) return
- if(M_DIZZY in M.mutations)
+ if(DIZZY in M.mutations)
M.Dizzy(300)
diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm
index b4bdee1b464..f0a5d1dbd18 100644
--- a/code/game/dna/genes/vg_powers.dm
+++ b/code/game/dna/genes/vg_powers.dm
@@ -2,7 +2,7 @@
This is /vg/'s nerf for hulk. Feel free to steal it.
Obviously, requires DNA2.
-*/
+
// When hulk was first applied (world.time).
/mob/living/carbon/human/var/hulk_time=0
@@ -26,12 +26,12 @@ Obviously, requires DNA2.
can_activate(var/mob/M,var/flags)
// Can't be big AND small.
- if(M_DWARF in M.mutations)
+ if(DWARF in M.mutations)
return 0
return ..(M,flags)
OnDrawUnderlays(var/mob/M,var/g,var/fat)
- if(M_HULK in M.mutations)
+ if(HULK in M.mutations)
if(fat)
return "hulk_[fat]_s"
else
@@ -40,11 +40,11 @@ Obviously, requires DNA2.
OnMobLife(var/mob/living/carbon/human/M)
if(!istype(M)) return
- if(M_HULK in M.mutations)
+ if(HULK in M.mutations)
var/timeleft=M.hulk_time - world.time
if(M.health <= 25 || timeleft <= 0)
M.hulk_time=0 // Just to be sure.
- M.mutations.Remove(M_HULK)
+ M.mutations.Remove(HULK)
//M.dna.SetSEState(HULKBLOCK,0)
M.update_mutations() //update our mutation overlays
M.update_body()
@@ -78,13 +78,13 @@ Obviously, requires DNA2.
return
var/mob/living/carbon/human/M=usr
M.hulk_time = world.time + HULK_DURATION
- M.mutations.Add(M_HULK)
+ M.mutations.Add(HULK)
M.update_mutations() //update our mutation overlays
M.update_body()
//M.say(pick("",";")+pick("HULK MAD","YOU MADE HULK ANGRY")) // Just a note to security.
message_admins("[key_name(usr)] has hulked out! ([formatJumpTo(usr)])")
return
-
+*/
///////////////////Vanilla Morph////////////////////////////////////
@@ -98,7 +98,7 @@ Obviously, requires DNA2.
deactivation_messages = list("You body feels normal.")
- mutation=M_MORPH
+ mutation=MORPH
instability=2
New()
@@ -197,7 +197,7 @@ Obviously, requires DNA2.
/datum/dna/gene/basic/grant_spell/remotetalk
name="Telepathy"
activation_messages=list("You expand your mind outwards.")
- mutation=M_REMOTE_TALK
+ mutation=REMOTE_TALK
instability=1
spelltype =/obj/effect/proc_holder/spell/wizard/targeted/remotetalk
@@ -222,19 +222,19 @@ Obviously, requires DNA2.
/obj/effect/proc_holder/spell/wizard/targeted/remotetalk/choose_targets(mob/user = usr)
var/list/targets = new /list()
var/list/validtargets = new /list()
- for(var/mob/M in living_mob_list)
+ for(var/mob/M in living_mob_list)
if(M && M.mind)
var/special_role = M.mind.special_role
if (special_role == "Wizard" || special_role == "Ninja" || special_role == "Syndicate" || special_role == "Syndicate Commando" || special_role == "Vox Raider" || special_role == "Alien")
continue
-
+
validtargets += M
-
+
if(!validtargets.len || validtargets.len == 1)
usr << "There are no valid targets!"
start_recharge()
return
-
+
targets += input("Choose the target to talk to.", "Targeting") as mob in validtargets
perform(targets)
@@ -244,7 +244,7 @@ Obviously, requires DNA2.
var/say = strip_html(input("What do you wish to say"))
for(var/mob/living/target in targets)
- if(M_REMOTE_TALK in target.mutations)
+ if(REMOTE_TALK in target.mutations)
target.show_message("\blue You hear [usr.real_name]'s voice: [say]")
else
target.show_message("\blue You hear a voice that seems to echo around the room: [say]")
@@ -257,7 +257,7 @@ Obviously, requires DNA2.
/datum/dna/gene/basic/grant_spell/remoteview
name="Remote Viewing"
activation_messages=list("Your mind expands.")
- mutation=M_REMOTE_VIEW
+ mutation=REMOTE_VIEW
instability=3
spelltype =/obj/effect/proc_holder/spell/wizard/targeted/remoteview
@@ -280,10 +280,10 @@ Obviously, requires DNA2.
icon_power_button = "genetic_view"
/obj/effect/proc_holder/spell/wizard/targeted/remoteview/choose_targets(mob/user = usr)
- var/list/targets = living_mob_list
+ var/list/targets = living_mob_list
var/list/remoteviewers = new /list()
for(var/mob/M in targets)
- if(M_REMOTE_VIEW in M.mutations)
+ if(REMOTE_VIEW in M.mutations)
remoteviewers += M
if(!remoteviewers.len || remoteviewers.len == 1)
usr << "No valid targets with remote view were found!"
@@ -316,7 +316,7 @@ Obviously, requires DNA2.
for(var/mob/living/L in targets)
if(ishuman(L))
var/mob/living/carbon/human/H = L
- if(M_PSY_RESIST in H.mutations)
+ if(PSY_RESIST in H.mutations)
continue
target = L
diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm
index 46482dc9ff6..5eb2ad4ecd6 100644
--- a/code/game/gamemodes/antag_spawner.dm
+++ b/code/game/gamemodes/antag_spawner.dm
@@ -26,7 +26,7 @@
if(!checking)
checking = 1
user << "The device is now checking for possible candidates."
- get_candidate_answer(user, get_candidates(BE_OPERATIVE,,"operative","Syndicate"))
+ get_candidate_answer(user, get_candidates(BE_OPERATIVE))
else
user << "The device is already checking for possible candidates."
return
diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm
index ccbff9b326d..886ab3b5863 100644
--- a/code/game/gamemodes/blob/blob.dm
+++ b/code/game/gamemodes/blob/blob.dm
@@ -58,7 +58,7 @@ var/list/blob_nodes = list()
/datum/game_mode/blob/proc/get_nuke_code()
var/nukecode = "ERROR"
for(var/obj/machinery/nuclearbomb/bomb in world)
- if(bomb && bomb.r_code && bomb.z == 1)
+ if(bomb && bomb.r_code && (bomb.z in config.station_levels))
nukecode = bomb.r_code
return nukecode
@@ -92,7 +92,7 @@ var/list/blob_nodes = list()
if(directory[ckey(blob.key)])
blob_client = directory[ckey(blob.key)]
location = get_turf(C)
- if(location.z != 1 || istype(location, /turf/space))
+ if(!(location.z in config.station_levels) || istype(location, /turf/space))
location = null
C.gib()
@@ -175,21 +175,17 @@ var/list/blob_nodes = list()
return
if (1)
- command_alert("Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.", "Biohazard Alert")
- for(var/mob/M in player_list)
- if(!istype(M,/mob/new_player))
- M << sound('sound/AI/blob_confirmed.ogg')
+ command_announcement.Announce("Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.", "Biohazard Alert", new_sound = 'sound/AI/blob_confirmed.ogg')
return
if (2)
- command_alert("The biohazard has grown out of control and will soon reach critical mass. Activate the nuclear failsafe to maintain quarantine. The Nuclear Authentication Code is [get_nuke_code()] ", "Biohazard Alert")
+ command_announcement.Announce("The biohazard has grown out of control and will soon reach critical mass. Activate the nuclear failsafe to maintain quarantine. The Nuclear Authentication Code is [get_nuke_code()] ", "Biohazard Alert", new_sound = 'sound/effects/siren.ogg')
set_security_level("gamma")
var/obj/machinery/door/airlock/vault/V = locate(/obj/machinery/door/airlock/vault) in world
- if(V && V.z == 1)
+ if(V && (V.z in config.station_levels))
V.locked = 0
V.update_icon()
send_intercept(2)
- spawn(10) world << sound('sound/effects/siren.ogg')
return
return
diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm
index 868cd62a5f6..1c3e57d2e00 100644
--- a/code/game/gamemodes/blob/blob_finish.dm
+++ b/code/game/gamemodes/blob/blob_finish.dm
@@ -62,7 +62,7 @@ datum/game_mode/proc/auto_declare_completion_blob()
if (istype(T, /turf/space))
numSpace += 1
else if(istype(T, /turf))
- if (M.z!=1)
+ if (!(M.z in config.station_levels))
numOffStation += 1
else
numAlive += 1
diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm
index 4b8dd6bf31d..20feb9044cb 100644
--- a/code/game/gamemodes/blob/blob_report.dm
+++ b/code/game/gamemodes/blob/blob_report.dm
@@ -61,7 +61,7 @@
proc/count()
for(var/turf/T in world)
- if(T.z != 1)
+ if(!(T.z in config.station_levels))
continue
if(istype(T,/turf/simulated/floor))
@@ -83,7 +83,7 @@
src.r_wall += 1
for(var/obj/O in world)
- if(O.z != 1)
+ if(!(O.z in config.station_levels))
continue
if(istype(O, /obj/structure/window))
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index fb5fd71928b..2dd69860b14 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -72,7 +72,7 @@
var/list/candidates = list()
if(!new_overmind)
- candidates = get_candidates(BE_BLOB,,"blob","Syndicate")
+ candidates = get_candidates(BE_BLOB)
if(candidates.len)
C = pick(candidates)
else
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index 8316c570f9a..37b70de409a 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -14,13 +14,26 @@
var/obj/effect/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 100
+ var/ghostimage = null
/mob/camera/blob/New()
var/new_name = "[initial(name)] ([rand(1, 999)])"
name = new_name
real_name = new_name
+
+ ghostimage = image(src.icon,src,src.icon_state)
+ ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness
+ updateallghostimages()
+
..()
+/mob/camera/blob/Destroy()
+ if (ghostimage)
+ ghost_darkness_images -= ghostimage
+ qdel(ghostimage)
+ ghostimage = null;
+ updateallghostimages()
+
/mob/camera/blob/Login()
..()
sync_mind()
@@ -65,13 +78,13 @@
/mob/camera/blob/proc/blob_talk(message)
log_say("[key_name(src)] : [message]")
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+ message = trim(sanitize(copytext(message, 1, MAX_MESSAGE_LEN)))
if (!message)
return
- var/message_a = say_quote(message)
- var/rendered = "Blob Telepathy, [name][message_a]"
+ var/verb = "states,"
+ var/rendered = "Blob Telepathy, [name][verb] \"[message]\""
for (var/mob/camera/blob/S in world)
if(istype(S))
@@ -79,7 +92,7 @@
for (var/mob/M in dead_mob_list)
if(!istype(M,/mob/new_player) && !istype(M,/mob/living/carbon/brain)) //No meta-evesdropping
- rendered = "Blob Telepathy, [name](Follow)[message_a]"
+ rendered = "Blob Telepathy, [name] (follow) [verb] \"[message]\""
M.show_message(rendered, 2)
/mob/camera/blob/emote(var/act,var/m_type=1,var/message = null)
diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm
index 9fdcc7ce7ba..7c336b0acab 100644
--- a/code/game/gamemodes/blob/theblob.dm
+++ b/code/game/gamemodes/blob/theblob.dm
@@ -129,7 +129,9 @@
return 0
- attackby(var/obj/item/weapon/W, var/mob/user)
+ attackby(var/obj/item/weapon/W, var/mob/living/user, params)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
playsound(get_turf(src), 'sound/effects/attackblob.ogg', 50, 1)
src.visible_message("\red The [src.name] has been attacked with \the [W][(user ? " by [user]." : ".")]")
var/damage = 0
@@ -144,6 +146,19 @@
health -= damage
update_icon()
return
+
+ attack_animal(mob/living/simple_animal/M as mob)
+ M.changeNext_move(CLICK_CD_MELEE)
+ M.do_attack_animation(src)
+ playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
+ src.visible_message("The [src.name] has been attacked by \the [M]!")
+ var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
+ if(!damage) // Avoid divide by zero errors
+ return
+ damage /= max(src.brute_resist, 1)
+ health -= damage
+ update_icon()
+ return
proc/change_to(var/type)
if(!ispath(type))
diff --git a/code/game/gamemodes/borer/borer.dm b/code/game/gamemodes/borer/borer.dm
index 7ec3801be02..f5948f3a683 100644
--- a/code/game/gamemodes/borer/borer.dm
+++ b/code/game/gamemodes/borer/borer.dm
@@ -39,7 +39,7 @@
return 0 // not enough candidates for borer
for(var/obj/machinery/atmospherics/unary/vent_pump/v in world)
- if(!v.welded && v.z == STATION_Z) // No more spawning in atmos. Assuming the mappers did their jobs, anyway.
+ if(!v.welded && (v.z in config.station_levels))
found_vents.Add(v)
// for each 2 possible borers, add one borer and one host
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 27bae141374..8edd5e0c664 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -9,7 +9,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
config_tag = "changeling"
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent")
- protected_species = list("Machine")
+ protected_species = list("Machine", "Slime People")
required_players = 2
required_players_secret = 10
required_enemies = 1
@@ -90,31 +90,55 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
var/datum/objective/absorb/absorb_objective = new
absorb_objective.owner = changeling
- absorb_objective.gen_amount_goal(2, 3)
+ absorb_objective.gen_amount_goal(6, 8)
changeling.objectives += absorb_objective
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = changeling
- kill_objective.find_target()
- changeling.objectives += kill_objective
+ if(prob(60))
+ var/datum/objective/steal/steal_objective = new
+ steal_objective.owner = changeling
+ steal_objective.find_target()
+ changeling.objectives += steal_objective
+ else
+ var/datum/objective/debrain/debrain_objective = new
+ debrain_objective.owner = changeling
+ debrain_objective.find_target()
+ changeling.objectives += debrain_objective
- var/datum/objective/steal/steal_objective = new
- steal_objective.owner = changeling
- steal_objective.find_target()
- changeling.objectives += steal_objective
+ var/list/active_ais = active_ais()
+ if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags.
+ var/datum/objective/destroy/destroy_objective = new
+ destroy_objective.owner = changeling
+ destroy_objective.find_target()
+ changeling.objectives += destroy_objective
+ else
+ var/datum/objective/assassinate/kill_objective = new
+ kill_objective.owner = changeling
+ kill_objective.find_target()
+ changeling.objectives += kill_objective
+ if (!(locate(/datum/objective/escape) in changeling.objectives))
+ var/datum/objective/escape/escape_with_identity/identity_theft = new
+ identity_theft.owner = changeling
+ identity_theft.target = kill_objective.target
+ if(identity_theft.target && identity_theft.target.current)
+ identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this.
+ var/mob/living/carbon/human/H = identity_theft.target.current
+ if(H.species && H.species.flags && H.species.flags & NO_SCAN) // For species that can't be absorbed - should default to an escape objective instead
+ return
+ else
+ identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing their identification card."
+ changeling.objectives += identity_theft
- switch(rand(1,100))
- if(1 to 80)
- if (!(locate(/datum/objective/escape) in changeling.objectives))
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = changeling
- changeling.objectives += escape_objective
+ if (!(locate(/datum/objective/escape) in changeling.objectives))
+ if(prob(70))
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = changeling
+ changeling.objectives += escape_objective
else
- if (!(locate(/datum/objective/survive) in changeling.objectives))
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = changeling
- changeling.objectives += survive_objective
+ var/datum/objective/escape/escape_with_identity/identity_theft = new
+ identity_theft.owner = changeling
+ identity_theft.find_target()
+ changeling.objectives += identity_theft
return
/datum/game_mode/proc/greet_changeling(var/datum/mind/changeling, var/you_are=1)
@@ -126,7 +150,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
if (changeling.current.mind)
if (changeling.current.mind.assigned_role == "Clown")
changeling.current << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
- changeling.current.mutations.Remove(M_CLUMSY)
+ changeling.current.mutations.Remove(CLUMSY)
var/obj_count = 1
for(var/datum/objective/objective in changeling.objectives)
@@ -162,7 +186,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
/datum/game_mode/proc/auto_declare_completion_changeling()
if(changelings.len)
- var/text = "The changelings were:"
+ var/text = "The changelings were:"
for(var/datum/mind/changeling in changelings)
var/changelingwin = 1
@@ -181,7 +205,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
text += " Changeling ID: [changeling.changeling.changelingID]."
- text += " Genomes Absorbed: [changeling.changeling.absorbedcount]"
+ text += " Genomes Extracted: [changeling.changeling.absorbedcount]"
if(changeling.objectives.len)
var/count = 1
@@ -209,19 +233,25 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
var/list/absorbed_dna = list()
- var/list/absorbed_species = list()
var/list/absorbed_languages = list()
- var/absorbedcount = 0
+ var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full.
+ var/dna_max = 4 //How many extra DNA strands the changeling can store for transformation.
+ var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human
var/chem_charges = 20
var/chem_recharge_rate = 0.5
+ var/chem_recharge_slowdown = 0
var/chem_storage = 50
- var/sting_range = 1
+ var/sting_range = 2
var/changelingID = "Changeling"
var/geneticdamage = 0
var/isabsorbing = 0
- var/geneticpoints = 5
+ var/geneticpoints = 10
var/purchasedpowers = list()
var/mimicing = ""
+ var/canrespec = 0
+ var/changeling_speak = 0
+ var/datum/dna/chosen_dna
+ var/obj/effect/proc_holder/changeling/sting/chosen_sting
/datum/changeling/New(var/gender=FEMALE)
..()
@@ -234,16 +264,52 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
changelingID = "[honorific] [changelingID]"
else
changelingID = "[honorific] [rand(1,999)]"
+ absorbed_dna.len = dna_max
/datum/changeling/proc/regenerate()
- chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage)
+ chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage)
geneticdamage = max(0, geneticdamage-1)
/datum/changeling/proc/GetDNA(var/dna_owner)
- var/datum/dna/chosen_dna
- for(var/datum/dna/DNA in absorbed_dna)
+ for(var/datum/dna/DNA in (absorbed_dna + protected_dna))
if(dna_owner == DNA.real_name)
- chosen_dna = DNA
- break
- return chosen_dna
+ return DNA
+
+/datum/changeling/proc/has_dna(var/datum/dna/tDNA)
+ for(var/datum/dna/D in (absorbed_dna + protected_dna))
+ if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species == D.species)
+ return 1
+ return 0
+
+/datum/changeling/proc/can_absorb_dna(var/mob/living/carbon/user, var/mob/living/carbon/target)
+ if(absorbed_dna[1] == user.dna)//If our current DNA is the stalest, we gotta ditch it.
+ user << "We have reached our capacity to store genetic information! We must transform before absorbing more."
+ return
+
+ if(!target || !target.dna)
+ user << "This creature does not have any DNA."
+ return
+
+ var/mob/living/carbon/human/T = target
+ if(!istype(T))
+ user << "[T] is not compatible with our biology."
+ return
+
+ if((NOCLONE || SKELETON || HUSK) in T.mutations)
+ user << "DNA of [target] is ruined beyond usability!"
+ return
+
+ if(T.species.flags & IS_SYNTHETIC)
+ user << "This creature does not have DNA!"
+ return
+
+ if(T.species.flags & NO_SCAN)
+ user << "We do not know how to parse this creature's DNA!"
+ return
+
+ if(has_dna(target.dna))
+ user << "We already have this DNA in storage!"
+
+ return 1
+
diff --git a/code/game/gamemodes/changeling/changeling_power.dm b/code/game/gamemodes/changeling/changeling_power.dm
new file mode 100644
index 00000000000..36df179ae32
--- /dev/null
+++ b/code/game/gamemodes/changeling/changeling_power.dm
@@ -0,0 +1,79 @@
+/*
+ * Don't use the apostrophe in name or desc. Causes script errors.
+ * TODO: combine atleast some of the functionality with /proc_holder/spell
+ */
+
+/obj/effect/proc_holder/changeling
+ panel = "Changeling"
+ name = "Prototype Sting"
+ desc = "" // Fluff
+ var/helptext = "" // Details
+ var/chemical_cost = 0 // negative chemical cost is for passive abilities (chemical glands)
+ var/dna_cost = -1 //cost of the sting in dna points. 0 = auto-purchase, -1 = cannot be purchased
+ var/req_dna = 0 //amount of dna needed to use this ability. Changelings always have atleast 1
+ var/req_human = 0 //if you need to be human to use this ability
+ var/req_stat = CONSCIOUS // CONSCIOUS, UNCONSCIOUS or DEAD
+ var/genetic_damage = 0 // genetic damage caused by using the sting. Nothing to do with cloneloss.
+ var/max_genetic_damage = 100 // hard counter for spamming abilities. Not used/balanced much yet.
+
+/obj/effect/proc_holder/changeling/proc/on_purchase(var/mob/user)
+ return
+
+/obj/effect/proc_holder/changeling/Click()
+ var/mob/user = usr
+ if(!user || !user.mind || !user.mind.changeling)
+ return
+ try_to_sting(user)
+
+/obj/effect/proc_holder/changeling/proc/try_to_sting(var/mob/user, var/mob/target)
+ if(!user.mind || !user.mind.changeling)
+ return
+ if(!can_sting(user, target))
+ return
+ var/datum/changeling/c = user.mind.changeling
+ if(sting_action(user, target))
+ sting_feedback(user, target)
+ take_chemical_cost(c)
+
+/obj/effect/proc_holder/changeling/proc/sting_action(var/mob/user, var/mob/target)
+ return 0
+
+/obj/effect/proc_holder/changeling/proc/sting_feedback(var/mob/user, var/mob/target)
+ return 0
+
+/obj/effect/proc_holder/changeling/proc/take_chemical_cost(var/datum/changeling/changeling)
+ changeling.chem_charges -= chemical_cost
+ changeling.geneticdamage += genetic_damage
+
+//Fairly important to remember to return 1 on success >.<
+/obj/effect/proc_holder/changeling/proc/can_sting(var/mob/user, var/mob/target)
+ if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards
+ return 0
+ if(req_human && !ishuman(user))
+ user << "We cannot do that in this form!"
+ return 0
+ var/datum/changeling/c = user.mind.changeling
+ if(c.chem_chargesWe require at least [chemical_cost] unit\s of chemicals to do that!
"
+ return 0
+ if(c.absorbedcountWe require at least [req_dna] sample\s of compatible DNA."
+ return 0
+ if(req_stat < user.stat)
+ user << "We are incapacitated."
+ return 0
+ if((user.status_flags & FAKEDEATH) && name!="Regenerate")
+ user << "We are incapacitated."
+ return 0
+ if(c.geneticdamage > max_genetic_damage)
+ user << "Our genomes are still reassembling. We need time to recover first."
+ return 0
+ return 1
+
+//used in /mob/Stat()
+/obj/effect/proc_holder/changeling/proc/can_be_used_by(var/mob/user)
+ if(!ishuman(user) && !ismonkey(user))
+ return 0
+ if(req_human && !ishuman(user))
+ return 0
+ return 1
diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm
deleted file mode 100644
index c6df778144d..00000000000
--- a/code/game/gamemodes/changeling/changeling_powers.dm
+++ /dev/null
@@ -1,944 +0,0 @@
-//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human
-/mob/proc/make_changeling()
-
- if(!mind) return
- if(!mind.changeling) mind.changeling = new /datum/changeling(gender)
- if(!ishuman(src) && !ismonkey(src)) return
- verbs += /datum/changeling/proc/EvolutionMenu
-
- var/lesser_form = !ishuman(src)
-
- if(!powerinstances.len)
- for(var/P in powers)
- powerinstances += new P()
-
- // Code to auto-purchase free powers.
- for(var/datum/power/changeling/P in powerinstances)
- if(!P.genomecost) // Is it free?
- if(!(P in mind.changeling.purchasedpowers)) // Do we not have it already?
- mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this.
-
- for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
- if(P.isVerb)
- if(lesser_form && !P.allowduringlesserform) continue
- if(!(P in src.verbs))
- src.verbs += P.verbpath
-
- mind.changeling.absorbed_dna |= dna
-
- var/mob/living/carbon/human/H = src
- if(istype(H))
- mind.changeling.absorbed_species += H.species.name
-
- for(var/language in languages)
- if(!(language in mind.changeling.absorbed_languages))
- mind.changeling.absorbed_languages += language
-
- return 1
-
-//removes our changeling verbs
-/mob/proc/remove_changeling_powers()
- if(!mind || !mind.changeling) return
- for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
- if(P.isVerb)
- verbs -= P.verbpath
-
-
-//Helper proc. Does all the checks and stuff for us to avoid copypasta
-/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0)
-
- if(!src.mind) return
- if(!iscarbon(src)) return
-
- var/datum/changeling/changeling = src.mind.changeling
- if(!changeling)
- world.log << "[src] has the changeling_transform() verb but is not a changeling."
- return
-
- if(src.stat > max_stat)
- src << "We are incapacitated."
- return
-
- if(changeling.absorbed_dna.len < required_dna)
- src << "We require at least [required_dna] samples of compatible DNA."
- return
-
- if(changeling.chem_charges < required_chems)
- src << "We require at least [required_chems] units of chemicals to do that!"
- return
-
- if(changeling.geneticdamage > max_genetic_damage)
- src << "Our geneomes are still reassembling. We need time to recover first."
- return
-
- return changeling
-
-
-//Used to dump the languages from the changeling datum into the actual mob.
-/mob/proc/changeling_update_languages(var/updated_languages)
-
- languages = list()
- for(var/language in updated_languages)
- languages += language
-
- return
-
-//Used to switch species based on the changeling datum.
-/mob/proc/changeling_change_species()
-
- set category = "Changeling"
- set name = "Change Species (5)"
-
- var/mob/living/carbon/human/H = src
- if(!istype(H))
- src << "We may only use this power while in humanoid form."
- return
-
- var/datum/changeling/changeling = changeling_power(5,1,0)
- if(!changeling) return
-
- if(changeling.absorbed_species.len < 2)
- src << "We do not know of any other species genomes to use."
- return
-
- var/S = input("Select the target species: ", "Target Species", null) as null|anything in changeling.absorbed_species
- if(!S) return
-
- domutcheck(src, null)
-
- changeling.chem_charges -= 5
- changeling.geneticdamage = 30
-
- src.visible_message("[src] transforms!")
-
- src.verbs -= /mob/proc/changeling_change_species
- H.set_species(S,null,1) //Until someone moves body colour into DNA, they're going to have to use the default.
-
- spawn(10)
- src.verbs += /mob/proc/changeling_change_species
- src.regenerate_icons()
-
- changeling_update_languages(changeling.absorbed_languages)
- feedback_add_details("changeling_powers","TR")
-
- return 1
-
-//Absorbs the victim's DNA making them uncloneable. Requires a strong grip on the victim.
-//Doesn't cost anything as it's the most basic ability.
-/mob/proc/changeling_absorb_dna()
- set category = "Changeling"
- set name = "Absorb DNA"
-
- var/datum/changeling/changeling = changeling_power(0,0,100)
- if(!changeling) return
-
- var/obj/item/weapon/grab/G = src.get_active_hand()
- if(!istype(G))
- src << "We must be grabbing a creature in our active hand to absorb them."
- return
-
- var/mob/living/carbon/human/T = G.affecting
- if(!istype(T))
- src << "[T] is not compatible with our biology."
- return
-
- if(T.species.flags & NO_SCAN)
- src << "We do not know how to parse this creature's DNA!"
- return
-
- if((M_NOCLONE || SKELETON) in T.mutations)
- src << "This creature's DNA is ruined beyond useability!"
- return
-
- if(!G.state == GRAB_KILL)
- src << "We must have a tighter grip to absorb this creature."
- return
-
- if(changeling.isabsorbing)
- src << "We are already absorbing!"
- return
-
- changeling.isabsorbing = 1
- for(var/stage = 1, stage<=3, stage++)
- switch(stage)
- if(1)
- src << "This creature is compatible. We must hold still..."
- if(2)
- src << "We extend a proboscis."
- src.visible_message("[src] extends a proboscis!")
- if(3)
- src << "We stab [T] with the proboscis."
- src.visible_message("[src] stabs [T] with the proboscis!")
- T << "You feel a sharp stabbing pain!"
- var/datum/organ/external/affecting = T.get_organ(src.zone_sel.selecting)
- if(affecting.take_damage(39,0,1,0,"large organic needle"))
- T:UpdateDamageIcon()
- continue
-
- feedback_add_details("changeling_powers","A[stage]")
- if(!do_mob(src, T, 150))
- src << "Our absorption of [T] has been interrupted!"
- changeling.isabsorbing = 0
- return
-
- src << "We have absorbed [T]!"
- src.visible_message("[src] sucks the fluids from [T]!")
- T << "You have been absorbed by the changeling!"
-
- T.dna.real_name = T.real_name //Set this again, just to be sure that it's properly set.
- changeling.absorbed_dna |= T.dna
- if(src.nutrition < 400) src.nutrition = min((src.nutrition + T.nutrition), 400)
-
- changeling.chem_charges += 10
- changeling.geneticpoints += 2
-
- //Steal all of their languages!
- for(var/language in T.languages)
- if(!(language in changeling.absorbed_languages))
- changeling.absorbed_languages += language
-
- changeling_update_languages(changeling.absorbed_languages)
-
- //Steal their species!
- if(T.species && !(T.species.name in changeling.absorbed_species))
- changeling.absorbed_species += T.species.name
-
- if(T.mind && T.mind.changeling)
- if(T.mind.changeling.absorbed_dna)
- for(var/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
- if(dna_data in changeling.absorbed_dna)
- continue
- changeling.absorbed_dna += dna_data
- changeling.absorbedcount++
- T.mind.changeling.absorbed_dna.len = 1
-
- if(T.mind.changeling.purchasedpowers)
- for(var/datum/power/changeling/Tp in T.mind.changeling.purchasedpowers)
- if(Tp in changeling.purchasedpowers)
- continue
- else
- changeling.purchasedpowers += Tp
-
- if(!Tp.isVerb)
- call(Tp.verbpath)()
- else
- changeling.purchasedpowers += Tp
-
- if(!Tp.isVerb)
- call(Tp.verbpath)()
- else
- src.make_changeling()
-
- changeling.chem_charges += T.mind.changeling.chem_charges
- changeling.geneticpoints += T.mind.changeling.geneticpoints
- T.mind.changeling.chem_charges = 0
- T.mind.changeling.geneticpoints = 0
- T.mind.changeling.absorbedcount = 0
-
- changeling.absorbedcount++
- changeling.isabsorbing = 0
-
- T.death(0)
- T.Drain()
- return 1
-
-
-//Change our DNA to that of somebody we've absorbed.
-/mob/proc/changeling_transform()
- set category = "Changeling"
- set name = "Transform (5)"
-
- var/datum/changeling/changeling = changeling_power(5,1,0)
- if(!changeling) return
-
- var/list/names = list()
- for(var/datum/dna/DNA in changeling.absorbed_dna)
- names += "[DNA.real_name]"
-
- var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- changeling.chem_charges -= 5
- src.visible_message("[src] transforms!")
- changeling.geneticdamage = 30
- src.dna = chosen_dna.Clone()
- src.real_name = chosen_dna.real_name
- src.flavor_text = ""
- if(ishuman(src))
- var/mob/living/carbon/human/H = src
- H.set_species()
- src.UpdateAppearance()
- domutcheck(src, null)
-
- src.verbs -= /mob/proc/changeling_transform
- spawn(10) src.verbs += /mob/proc/changeling_transform
-
- feedback_add_details("changeling_powers","TR")
- return 1
-
-
-//Transform into a monkey.
-/mob/proc/changeling_lesser_form()
- set category = "Changeling"
- set name = "Lesser Form (1)"
-
- var/datum/changeling/changeling = changeling_power(1,0,0)
- if(!changeling) return
-
- if(src.has_brain_worms())
- src << "We cannot perform this ability at the present time!"
- return
-
- var/mob/living/carbon/C = src
- changeling.chem_charges--
- C.remove_changeling_powers()
- C.visible_message("[C] transforms!")
- changeling.geneticdamage = 30
- C << "Our genes cry out!"
-
- //TODO replace with monkeyize proc
- var/list/implants = list() //Try to preserve implants.
- for(var/obj/item/weapon/implant/W in C)
- implants += W
-
- C.monkeyizing = 1
- C.canmove = 0
- C.icon = null
- C.overlays.Cut()
- C.invisibility = 101
-
- var/atom/movable/overlay/animation = new /atom/movable/overlay( C.loc )
- animation.icon_state = "blank"
- animation.icon = 'icons/mob/mob.dmi'
- animation.master = src
- flick("h2monkey", animation)
- sleep(48)
- del(animation)
-
- var/mob/living/carbon/monkey/O = new /mob/living/carbon/monkey(src)
- O.dna = C.dna.Clone()
- C.dna = null
-
- for(var/obj/item/W in C)
- C.drop_from_inventory(W)
- for(var/obj/T in C)
- del(T)
-
- O.loc = C.loc
- O.name = "monkey ([copytext(md5(C.real_name), 2, 6)])"
- O.setToxLoss(C.getToxLoss())
- O.adjustBruteLoss(C.getBruteLoss())
- O.setOxyLoss(C.getOxyLoss())
- O.adjustFireLoss(C.getFireLoss())
- O.stat = C.stat
- O.a_intent = "harm"
- for(var/obj/item/weapon/implant/I in implants)
- I.loc = O
- I.implanted = O
-
- C.mind.transfer_to(O)
-
- O.make_changeling(1)
- O.verbs += /mob/proc/changeling_lesser_transform
- O.changeling_update_languages(changeling.absorbed_languages)
-
- feedback_add_details("changeling_powers","LF")
- del(C)
- return 1
-
-
-//Transform into a human
-/mob/proc/changeling_lesser_transform()
- set category = "Changeling"
- set name = "Transform (1)"
-
- var/datum/changeling/changeling = changeling_power(1,1,0)
- if(!changeling) return
-
- var/list/names = list()
- for(var/datum/dna/DNA in changeling.absorbed_dna)
- names += "[DNA.real_name]"
-
- var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- var/mob/living/carbon/C = src
-
- changeling.chem_charges--
- C.remove_changeling_powers()
- C.visible_message("[C] transforms!")
- C.dna = chosen_dna.Clone()
-
- var/list/implants = list()
- for (var/obj/item/weapon/implant/I in C) //Still preserving implants
- implants += I
-
- C.monkeyizing = 1
- C.canmove = 0
- C.icon = null
- C.overlays.Cut()
- C.invisibility = 101
- var/atom/movable/overlay/animation = new /atom/movable/overlay( C.loc )
- animation.icon_state = "blank"
- animation.icon = 'icons/mob/mob.dmi'
- animation.master = src
- flick("monkey2h", animation)
- sleep(48)
- del(animation)
-
- for(var/obj/item/W in src)
- C.u_equip(W)
- if (C.client)
- C.client.screen -= W
- if (W)
- W.loc = C.loc
- W.dropped(C)
- W.layer = initial(W.layer)
-
- var/mob/living/carbon/human/O = new /mob/living/carbon/human( src, delay_ready_dna=1 )
- if (C.dna.GetUIState(DNA_UI_GENDER))
- O.gender = FEMALE
- else
- O.gender = MALE
- O.dna = C.dna.Clone()
- C.dna = null
- O.real_name = chosen_dna.real_name
- O.set_species()
- for(var/obj/T in C)
- del(T)
-
- O.loc = C.loc
-
- O.UpdateAppearance()
- domutcheck(O, null)
- O.setToxLoss(C.getToxLoss())
- O.adjustBruteLoss(C.getBruteLoss())
- O.setOxyLoss(C.getOxyLoss())
- O.adjustFireLoss(C.getFireLoss())
- O.stat = C.stat
- for (var/obj/item/weapon/implant/I in implants)
- I.loc = O
- I.implanted = O
-
- C.mind.transfer_to(O)
- O.make_changeling()
- O.changeling_update_languages(changeling.absorbed_languages)
-
- feedback_add_details("changeling_powers","LFT")
- del(C)
- return 1
-
-
-//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
-/*
-/mob/verb/honk()
- set name = "OH HOLY FUCK"
- set category = "Debug"
- var/yes = 0
- if(src in mob_list)
- yes = 1
- else
- var/mob/M = locate(src) in mob_list
- if(M == src)
- yes = 1
- usr << "[yes ? "\blue" : "\red"] You are [yes ? "" : "not "]in the mob list"
-*/
-/mob/proc/changeling_returntolife()
- set category = "Changeling"
- set name = "Return To Life (20)"
- var/datum/changeling/changeling = changeling_power(20,1,100,DEAD)
- if(!changeling) return
-
- var/mob/living/carbon/C = src
- if(changeling_power(20,1,100,DEAD))
- changeling.chem_charges -= 20
- dead_mob_list -= C
- living_mob_list |= C
- C.stat = CONSCIOUS
- C.tod = null
- C.setToxLoss(0)
- C.setOxyLoss(0)
- C.setCloneLoss(0)
- C.setBrainLoss(0)
- C.SetParalysis(0)
- C.SetStunned(0)
- C.SetWeakened(0)
- C.radiation = 0
- C.heal_overall_damage(C.getBruteLoss(), C.getFireLoss())
- C.reagents.clear_reagents()
- C.germ_level = 0
- C.next_pain_time = 0
- C.traumatic_shock = 0
- if(ishuman(C))
- var/mob/living/carbon/human/H = C
- H.vessel.reagent_list = list()
- H.vessel.add_reagent("blood",560)
- H.shock_stage = 0
- spawn(1)
- H.fixblood()
- for(var/organ_name in H.organs_by_name)
- var/datum/organ/external/O = H.organs_by_name[organ_name]
- for(var/obj/item/weapon/shard/shrapnel/s in O.implants)
- if(istype(s))
- O.implants -= s
- H.contents -= s
- del(s)
- O.amputated = 0
- O.brute_dam = 0
- O.burn_dam = 0
- O.damage_state = "00"
- O.germ_level = 0
- O.hidden = null
- O.number_wounds = 0
- O.open = 0
- O.perma_injury = 0
- O.stage = 0
- O.status = 0
- O.trace_chemicals = list()
- O.wounds = list()
- O.wound_update_accuracy = 1
- for(var/n in H.internal_organs_by_name)
- var/datum/organ/internal/IO = H.internal_organs_by_name[n]
- IO.damage = 0
- IO.trace_chemicals = list()
- H.updatehealth()
- C << "We have regenerated."
- C.visible_message("[src] appears to wake from the dead, having healed all wounds.")
- C.status_flags &= ~(FAKEDEATH)
- C.update_canmove()
- C.make_changeling()
- src.verbs -= /mob/proc/changeling_returntolife
- feedback_add_details("changeling_powers","RJ")
-
-/mob/proc/changeling_fakedeath()
- set category = "Changeling"
- set name = "Regenerative Stasis (20)"
-
- var/datum/changeling/changeling = changeling_power(20,1,100,DEAD)
- if(!changeling) return
-
- var/mob/living/carbon/C = src
- if(!C.stat && alert("Are we sure we wish to fake our death?",,"Yes","No") == "No")//Confirmation for living changelings if they want to fake their death
- return
- C << "We will attempt to regenerate our form."
-
- C.status_flags |= FAKEDEATH //play dead
- C.update_canmove()
- C.remove_changeling_powers()
-
- C.emote("deathgasp")
- C.tod = worldtime2text()
-
- spawn(rand(800,1200))
- src << "We are now ready to regenerate."
- src.verbs += /mob/proc/changeling_returntolife
- feedback_add_details("changeling_powers","FD")
- return 1
-
-
-//Boosts the range of your next sting attack by 1
-/mob/proc/changeling_boost_range()
- set category = "Changeling"
- set name = "Ranged Sting (10)"
- set desc="Your next sting ability can be used against targets 2 squares away."
-
- var/datum/changeling/changeling = changeling_power(10,0,100)
- if(!changeling) return 0
- changeling.chem_charges -= 10
- src << "Your throat adjusts to launch the sting."
- changeling.sting_range = 2
- src.verbs -= /mob/proc/changeling_boost_range
- spawn(5) src.verbs += /mob/proc/changeling_boost_range
- feedback_add_details("changeling_powers","RS")
- return 1
-
-
-//Recover from stuns.
-/mob/proc/changeling_unstun()
- set category = "Changeling"
- set name = "Epinephrine Sacs (45)"
- set desc = "Removes all stuns"
-
- var/datum/changeling/changeling = changeling_power(45,0,100,UNCONSCIOUS)
- if(!changeling) return 0
- changeling.chem_charges -= 45
-
- var/mob/living/carbon/human/C = src
- C.stat = 0
- C.SetParalysis(0)
- C.SetStunned(0)
- C.SetWeakened(0)
- C.adjustStaminaLoss(-75)
- C.reagents.add_reagent("synaptizine", 20)
- C.lying = 0
- C.update_canmove()
-
- src.verbs -= /mob/proc/changeling_unstun
- spawn(5) src.verbs += /mob/proc/changeling_unstun
- feedback_add_details("changeling_powers","UNS")
- return 1
-
-
-//Speeds up chemical regeneration
-/mob/proc/changeling_fastchemical()
- src.mind.changeling.chem_recharge_rate *= 2
- return 1
-
-//Increases macimum chemical storage
-/mob/proc/changeling_engorgedglands()
- src.mind.changeling.chem_storage += 25
- return 1
-
-
-//Prevents AIs tracking you but makes you easily detectable to the human-eye.
-/mob/proc/changeling_digitalcamo()
- set category = "Changeling"
- set name = "Toggle Digital Camoflague"
- set desc = "The AI can no longer track us, but we will look different if examined. Has a constant cost while active."
-
- var/datum/changeling/changeling = changeling_power()
- if(!changeling) return 0
-
- var/mob/living/carbon/human/C = src
- if(C.digitalcamo) C << "We return to normal."
- else C << "We distort our form to prevent AI-tracking."
- C.digitalcamo = !C.digitalcamo
-
- spawn(0)
- while(C && C.digitalcamo && C.mind && C.mind.changeling)
- C.mind.changeling.chem_charges = max(C.mind.changeling.chem_charges - 1, 0)
- sleep(40)
-
- src.verbs -= /mob/proc/changeling_digitalcamo
- spawn(5) src.verbs += /mob/proc/changeling_digitalcamo
- feedback_add_details("changeling_powers","CAM")
- return 1
-
-
-//Starts healing you every second for 10 seconds. Can be used whilst unconscious.
-/mob/proc/changeling_rapidregen()
- set category = "Changeling"
- set name = "Rapid Regeneration (30)"
- set desc = "Begins rapidly regenerating. Does not effect stuns or chemicals."
-
- var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS)
- if(!changeling) return 0
- src.mind.changeling.chem_charges -= 30
-
- var/mob/living/carbon/human/C = src
- if(ishuman(src))
- var/mob/living/carbon/human/H=src
- if(H.said_last_words)
- H.said_last_words=0
-
- spawn(0)
- for(var/i = 0, i<10,i++)
- if(C)
- C.adjustBruteLoss(-10)
- C.adjustToxLoss(-10)
- C.adjustOxyLoss(-10)
- C.adjustFireLoss(-10)
- sleep(10)
-
- src.verbs -= /mob/proc/changeling_rapidregen
- spawn(5) src.verbs += /mob/proc/changeling_rapidregen
- feedback_add_details("changeling_powers","RR")
- return 1
-
-// HIVE MIND UPLOAD/DOWNLOAD DNA
-
-var/list/datum/dna/hivemind_bank = list()
-
-/mob/proc/changeling_hiveupload()
- set category = "Changeling"
- set name = "Hive Channel (10)"
- set desc = "Allows you to channel DNA in the airwaves to allow other changelings to absorb it."
-
- var/datum/changeling/changeling = changeling_power(10,1)
- if(!changeling) return
-
- var/list/names = list()
- for(var/datum/dna/DNA in changeling.absorbed_dna)
- if(!(DNA in hivemind_bank))
- names += DNA.real_name
-
- if(names.len <= 0)
- src << "The airwaves already have all of our DNA."
- return
-
- var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- changeling.chem_charges -= 10
- hivemind_bank += chosen_dna
- src << "We channel the DNA of [S] to the air."
- feedback_add_details("changeling_powers","HU")
- return 1
-
-/mob/proc/changeling_hivedownload()
- set category = "Changeling"
- set name = "Hive Absorb (20)"
- set desc = "Allows you to absorb DNA that is being channelled in the airwaves."
-
- var/datum/changeling/changeling = changeling_power(20,1)
- if(!changeling) return
-
- var/list/names = list()
- for(var/datum/dna/DNA in hivemind_bank)
- if(!(DNA in changeling.absorbed_dna))
- names[DNA.real_name] = DNA
-
- if(names.len <= 0)
- src << "There's no new DNA to absorb from the air."
- return
-
- var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names
- if(!S) return
- var/datum/dna/chosen_dna = names[S]
- if(!chosen_dna)
- return
-
- changeling.chem_charges -= 20
- changeling.absorbed_dna += chosen_dna
- src << "We absorb the DNA of [S] from the air."
- feedback_add_details("changeling_powers","HD")
- return 1
-
-// Fake Voice
-
-/mob/proc/changeling_mimicvoice()
- set category = "Changeling"
- set name = "Mimic Voice"
- set desc = "Shape our vocal glands to form a voice of someone we choose. We cannot regenerate chemicals when mimicing."
-
-
- var/datum/changeling/changeling = changeling_power()
- if(!changeling) return
-
- if(changeling.mimicing)
- changeling.mimicing = ""
- src << "We return our vocal glands to their original location."
- return
-
- var/mimic_voice = stripped_input(usr, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN)
- if(!mimic_voice)
- return
-
- changeling.mimicing = mimic_voice
-
- src << "We shape our glands to take the voice of [mimic_voice], this will stop us from regenerating chemicals while active."
- src << "Use this power again to return to our original voice and reproduce chemicals again."
-
- feedback_add_details("changeling_powers","MV")
-
- spawn(0)
- while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing)
- src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0)
- sleep(40)
- if(src && src.mind && src.mind.changeling)
- src.mind.changeling.mimicing = ""
- //////////
- //STINGS// //They get a pretty header because there's just so fucking many of them ;_;
- //////////
-
-/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1)
- if(M.loc == src.loc) return 1 //target and source are in the same thing
- if(!isturf(src.loc) || !isturf(M.loc)) return 0 //One is inside, the other is outside something.
- if(AStar(src.loc, M.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance, sting_range)) //If a path exists, good!
- return 1
- return 0
-
-//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities)
-/mob/proc/changeling_sting(var/required_chems=0, var/verb_path)
- var/datum/changeling/changeling = changeling_power(required_chems)
- if(!changeling) return
-
- var/list/victims = list()
- for(var/mob/living/carbon/C in oview(changeling.sting_range))
- victims += C
- var/mob/living/carbon/T = input(src, "Who will we sting?") as null|anything in victims
-
- if(!T) return
- if(!(T in view(changeling.sting_range))) return
- if(!sting_can_reach(T, changeling.sting_range)) return
- if(!changeling_power(required_chems)) return
-
- if(ishuman(T))
- var/mob/living/carbon/human/H = T
- if(H.species.flags & IS_SYNTHETIC)
- src << "This won't work on synthetics."
- return
-
- changeling.chem_charges -= required_chems
- changeling.sting_range = 1
- src.verbs -= verb_path
- spawn(10) src.verbs += verb_path
-
- src << "We stealthily sting [T]."
- if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting
- T << "You feel a tiny prick."
- return
-
-
-/mob/proc/changeling_lsdsting()
- set category = "Changeling"
- set name = "Hallucination Sting (15)"
- set desc = "Causes terror in the target."
-
- var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting)
- if(!T) return 0
- spawn(rand(300,600))
- if(T) T.hallucination += 400
- feedback_add_details("changeling_powers","HS")
- return 1
-
-/mob/proc/changeling_silence_sting()
- set category = "Changeling"
- set name = "Silence sting (10)"
- set desc="Sting target"
-
- var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting)
- if(!T) return 0
- T.silent += 30
- feedback_add_details("changeling_powers","SS")
- return 1
-
-/mob/proc/changeling_blind_sting()
- set category = "Changeling"
- set name = "Blind sting (20)"
- set desc="Sting target"
-
- var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting)
- if(!T) return 0
- T << "Your eyes burn horrificly!"
- T.disabilities |= NEARSIGHTED
- spawn(300) T.disabilities &= ~NEARSIGHTED
- T.eye_blind = 10
- T.eye_blurry = 20
- feedback_add_details("changeling_powers","BS")
- return 1
-
-/mob/proc/changeling_deaf_sting()
- set category = "Changeling"
- set name = "Deaf sting (5)"
- set desc="Sting target:"
-
- var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting)
- if(!T) return 0
- T << "Your ears pop and begin ringing loudly!"
- T.sdisabilities |= DEAF
- spawn(300) T.sdisabilities &= ~DEAF
- feedback_add_details("changeling_powers","DS")
- return 1
-
-/mob/proc/changeling_paralysis_sting()
- set category = "Changeling"
- set name = "Paralysis sting (30)"
- set desc="Sting target"
-
- var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting)
- if(!T) return 0
- T << "Your muscles begin to painfully tighten."
- T.Weaken(20)
- feedback_add_details("changeling_powers","PS")
- return 1
-
-/mob/proc/changeling_transformation_sting()
- set category = "Changeling"
- set name = "Transformation sting (40)"
- set desc="Sting target"
-
- var/datum/changeling/changeling = changeling_power(40)
- if(!changeling) return 0
-
-
-
- var/list/names = list()
- for(var/datum/dna/DNA in changeling.absorbed_dna)
- names += "[DNA.real_name]"
-
- var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting)
- if(!T) return 0
- if((M_HUSK in T.mutations) || (!ishuman(T) && !ismonkey(T)))
- src << "Our sting appears ineffective against its DNA."
- return 0
- T.visible_message("[T] transforms!")
- T.dna = chosen_dna.Clone()
- T.real_name = chosen_dna.real_name
- T.UpdateAppearance()
- domutcheck(T, null)
- feedback_add_details("changeling_powers","TS")
- return 1
-
-/mob/proc/changeling_unfat_sting()
- set category = "Changeling"
- set name = "Unfat sting (5)"
- set desc = "Sting target"
-
- var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting)
- if(!T) return 0
- T << "You feel a small prick as stomach churns violently and you become to feel skinnier."
- T.overeatduration = 0
- T.nutrition -= 100
- feedback_add_details("changeling_powers","US")
- return 1
-
-/mob/proc/changeling_DEATHsting()
- set category = "Changeling"
- set name = "Death Sting (40)"
- set desc = "Causes spasms onto death."
-
- var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting)
- if(!T) return 0
- T << "You feel a small prick and your chest becomes tight."
- T.silent = 10
- T.Paralyse(10)
- T.Jitter(1000)
- if(T.reagents) T.reagents.add_reagent("lexorin", 40)
- feedback_add_details("changeling_powers","DTHS")
- return 1
-
-/mob/proc/changeling_extract_dna_sting()
- set category = "Changeling"
- set name = "Extract DNA Sting (40)"
- set desc="Stealthily sting a target to extract their DNA."
-
- var/datum/changeling/changeling = null
- if(src.mind && src.mind.changeling)
- changeling = src.mind.changeling
- if(!changeling)
- return 0
-
- var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting)
- if(!T) return 0
- if(T.species.flags & NO_SCAN) // Yeah, this needs the same protection, otherwise you can steal DNA from Slime People and then blood overdose when you turn into one.
- src << "We do not know how to parse this creature's DNA!"
- changeling.chem_charges += 40
- return 0
-
- T.dna.real_name = T.real_name
- changeling.absorbed_dna |= T.dna
- if(T.species && !(T.species.name in changeling.absorbed_species))
- changeling.absorbed_species += T.species.name
-
- feedback_add_details("changeling_powers","ED")
- return 1
diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm
new file mode 100644
index 00000000000..32ab8473d66
--- /dev/null
+++ b/code/game/gamemodes/changeling/evolution_menu.dm
@@ -0,0 +1,416 @@
+var/list/sting_paths
+// totally stolen from the new player panel. YAYY
+
+/obj/effect/proc_holder/changeling/evolution_menu
+ name = "-Evolution Menu-" //Dashes are so it's listed before all the other abilities.
+ desc = "Choose our method of subjugation."
+ dna_cost = 0
+
+/obj/effect/proc_holder/changeling/evolution_menu/Click()
+ if(!usr || !usr.mind || !usr.mind.changeling)
+ return
+ var/datum/changeling/changeling = usr.mind.changeling
+
+ if(!sting_paths)
+ sting_paths = init_subtypes(/obj/effect/proc_holder/changeling)
+
+ var/dat = create_menu(changeling)
+ usr << browse(dat, "window=powers;size=600x700")//900x480
+
+
+/obj/effect/proc_holder/changeling/evolution_menu/proc/create_menu(var/datum/changeling/changeling)
+ var/dat
+ dat +="
Changling Evolution Menu"
+
+ //javascript, the part that does most of the work~
+ dat += {"
+
+
+
+
+
+
+ "}
+
+ //body tag start + onload and onkeypress (onkeyup) javascript event calls
+ dat += ""
+
+ //title + search bar
+ dat += {"
+
+
+
+
+ Changeling Evolution Menu
+ Hover over a power to see more information
+ Current ability choices remaining: [changeling.geneticpoints]
+ By rendering a lifeform to a husk, we gain enough power to alter and adapt our evolutions.
+ (Readapt)
+
+
+
+
+
+ Search:
+
+
+
+
+ "}
+
+ //player table header
+ dat += {"
+
+
"}
+
+ var/i = 1
+ for(var/obj/effect/proc_holder/changeling/cling_power in sting_paths)
+
+ if(cling_power.dna_cost <= 0) //Let's skip the crap we start with. Keeps the evolution menu uncluttered.
+ continue
+
+ var/ownsthis = changeling.has_sting(cling_power)
+
+ var/color
+ if(ownsthis)
+ if(i%2 == 0)
+ color = "#d8ebd8"
+ else
+ color = "#c3dec3"
+ else
+ if(i%2 == 0)
+ color = "#f2f2f2"
+ else
+ color = "#e6e6e6"
+
+
+ dat += {"
+
+
+
+
+
+
+ "}
+ return dat
+
+
+/obj/effect/proc_holder/changeling/evolution_menu/Topic(href, href_list)
+ ..()
+ if(!(iscarbon(usr) && usr.mind && usr.mind.changeling))
+ return
+
+ if(href_list["P"])
+ usr.mind.changeling.purchasePower(usr, href_list["P"])
+ else if(href_list["readapt"])
+ usr.mind.changeling.lingRespec(usr)
+ var/dat = create_menu(usr.mind.changeling)
+ usr << browse(dat, "window=powers;size=600x700")
+/////
+
+/datum/changeling/proc/purchasePower(var/mob/living/carbon/user, var/sting_name)
+
+ var/obj/effect/proc_holder/changeling/thepower = null
+
+ if(!sting_paths)
+ sting_paths = init_subtypes(/obj/effect/proc_holder/changeling)
+ for(var/obj/effect/proc_holder/changeling/cling_sting in sting_paths)
+ if(cling_sting.name == sting_name)
+ thepower = cling_sting
+
+ if(thepower == null)
+ user << "This is awkward. Changeling power purchase failed, please report this bug to a coder!"
+ return
+
+ if(absorbedcount < thepower.req_dna)
+ user << "We lack the energy to evolve this ability!"
+ return
+
+ if(has_sting(thepower))
+ user << "We have already evolved this ability!"
+ return
+
+ if(thepower.dna_cost < 0)
+ user << "We cannot evolve this ability."
+ return
+
+ if(geneticpoints < thepower.dna_cost)
+ user << "We have reached our capacity for abilities."
+ return
+
+ if(user.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
+ user << "We lack the energy to evolve new abilities right now."
+ return
+
+ geneticpoints -= thepower.dna_cost
+ purchasedpowers += thepower
+ thepower.on_purchase(user)
+
+//Reselect powers
+/datum/changeling/proc/lingRespec(var/mob/user)
+ if(!ishuman(user))
+ user << "We can't remove our evolutions in this form!"
+ return
+ if(canrespec)
+ user << "We have removed our evolutions from this form, and are now ready to readapt."
+ user.remove_changeling_powers(1)
+ canrespec = 0
+ user.make_changeling()
+ return 1
+ else
+ user << "You lack the power to readapt your evolutions!"
+ return 0
+
+/mob/proc/make_changeling()
+ if(!mind)
+ return
+ if(!ishuman(src) && !ismonkey(src))
+ return
+ if(!mind.changeling)
+ mind.changeling = new /datum/changeling(gender)
+ if(!sting_paths)
+ sting_paths = init_subtypes(/obj/effect/proc_holder/changeling)
+ if(mind.changeling.purchasedpowers)
+ remove_changeling_powers(1)
+
+ add_language("Changeling")
+
+ for(var/language in languages)
+ mind.changeling.absorbed_languages |= language
+
+ // purchase free powers.
+ for(var/obj/effect/proc_holder/changeling/path in sting_paths)
+ //var/obj/effect/proc_holder/changeling/S = new path()
+ if(!path.dna_cost)
+ if(!mind.changeling.has_sting(path))
+ mind.changeling.purchasedpowers += path
+ path.on_purchase(src)
+
+ var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
+ mind.changeling.absorbed_dna |= C.dna
+ return 1
+
+//Used to dump the languages from the changeling datum into the actual mob.
+/mob/proc/changeling_update_languages(var/updated_languages)
+
+ for(var/datum/language/L in updated_languages)
+ add_language("L.name")
+
+ //This isn't strictly necessary but just to be safe...
+ add_language("Changeling")
+
+ return
+
+/datum/changeling/proc/reset()
+ chosen_sting = null
+ geneticpoints = initial(geneticpoints)
+ sting_range = initial(sting_range)
+ chem_storage = initial(chem_storage)
+ chem_recharge_rate = initial(chem_recharge_rate)
+ chem_charges = min(chem_charges, chem_storage)
+ chem_recharge_slowdown = initial(chem_recharge_slowdown)
+ mimicing = ""
+
+/mob/proc/remove_changeling_powers(var/keep_free_powers=0)
+ if(ishuman(src) || ismonkey(src))
+ if(mind && mind.changeling)
+ digitalcamo = 0
+ mind.changeling.changeling_speak = 0
+ mind.changeling.reset()
+ for(var/obj/effect/proc_holder/changeling/p in mind.changeling.purchasedpowers)
+ if(!(p.dna_cost == 0 && keep_free_powers))
+ mind.changeling.purchasedpowers -= p
+
+/datum/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
+ for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
+ if(power.name == P.name)
+ return 1
+ return 0
diff --git a/code/game/gamemodes/changeling/modularchangling.dm b/code/game/gamemodes/changeling/modularchangling.dm
deleted file mode 100644
index 68458760855..00000000000
--- a/code/game/gamemodes/changeling/modularchangling.dm
+++ /dev/null
@@ -1,506 +0,0 @@
-// READ: Don't use the apostrophe in name or desc. Causes script errors.
-
-var/list/powers = typesof(/datum/power/changeling) - /datum/power/changeling //needed for the badmin verb for now
-var/list/datum/power/changeling/powerinstances = list()
-
-/datum/power //Could be used by other antags too
- var/name = "Power"
- var/desc = "Placeholder"
- var/helptext = ""
- var/isVerb = 1 // Is it an active power, or passive?
- var/verbpath // Path to a verb that contains the effects.
-
-/datum/power/changeling
- var/allowduringlesserform = 0
- var/genomecost = 500000 // Cost for the changling to evolve this power.
-
-/datum/power/changeling/absorb_dna
- name = "Absorb DNA"
- desc = "Permits us to syphon the DNA from a human. They become one with us, and we become stronger."
- genomecost = 0
- verbpath = /mob/proc/changeling_absorb_dna
-
-/datum/power/changeling/transform
- name = "Transform"
- desc = "We take on the apperance and voice of one we have absorbed."
- genomecost = 0
- verbpath = /mob/proc/changeling_transform
-
-/datum/power/changeling/change_species
- name = "Change Species"
- desc = "We take on the apperance of a species that we have absorbed."
- genomecost = 0
- verbpath = /mob/proc/changeling_change_species
-
-/datum/power/changeling/fakedeath
- name = "Regenerative Stasis"
- desc = "We become weakened to a death-like state, where we will rise again from death."
- helptext = "Can be used before or after death. Duration varies greatly."
- genomecost = 0
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_fakedeath
-
-// Hivemind
-
-/datum/power/changeling/hive_upload
- name = "Hive Channel"
- desc = "We can channel a DNA into the airwaves, allowing our fellow changelings to absorb it and transform into it as if they acquired the DNA themselves."
- helptext = "Allows other changelings to absorb the DNA you channel from the airwaves. Will not help them towards their absorb objectives."
- genomecost = 0
- verbpath = /mob/proc/changeling_hiveupload
-
-/datum/power/changeling/hive_download
- name = "Hive Absorb"
- desc = "We can absorb a single DNA from the airwaves, allowing us to use more disguises with help from our fellow changelings."
- helptext = "Allows you to absorb a single DNA and use it. Does not count towards your absorb objective."
- genomecost = 0
- verbpath = /mob/proc/changeling_hivedownload
-
-/datum/power/changeling/lesser_form
- name = "Lesser Form"
- desc = "We debase ourselves and become lesser. We become a monkey."
- genomecost = 1
- verbpath = /mob/proc/changeling_lesser_form
-
-/datum/power/changeling/deaf_sting
- name = "Deaf Sting"
- desc = "We silently sting a human, completely deafening them for a short time."
- genomecost = 1
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_deaf_sting
-
-/datum/power/changeling/blind_sting
- name = "Blind Sting"
- desc = "We silently sting a human, completely blinding them for a short time."
- genomecost = 2
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_blind_sting
-
-/datum/power/changeling/silence_sting
- name = "Silence Sting"
- desc = "We silently sting a human, completely silencing them for a short time."
- helptext = "Does not provide a warning to a victim that they have been stung, until they try to speak and cannot."
- genomecost = 3
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_silence_sting
-
-/datum/power/changeling/mimicvoice
- name = "Mimic Voice"
- desc = "We shape our vocal glands to sound like a desired voice."
- helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this"
- genomecost = 3
- verbpath = /mob/proc/changeling_mimicvoice
-
-/datum/power/changeling/extractdna
- name = "Extract DNA"
- desc = "We stealthily sting a target and extract the DNA from them."
- helptext = "Will give you the DNA of your target, allowing you to transform into them. Does not count towards absorb objectives."
- genomecost = 4
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_extract_dna_sting
-
-/datum/power/changeling/transformation_sting
- name = "Transformation Sting"
- desc = "We silently sting a human, injecting a retrovirus that forces them to transform into another."
- helptext = "Does not provide a warning to others. The victim will transform much like a changeling would."
- genomecost = 3
- verbpath = /mob/proc/changeling_transformation_sting
-
-/datum/power/changeling/paralysis_sting
- name = "Paralysis Sting"
- desc = "We silently sting a human, paralyzing them for a short time."
- genomecost = 4
- verbpath = /mob/proc/changeling_paralysis_sting
-
-/datum/power/changeling/LSDSting
- name = "Hallucination Sting"
- desc = "We evolve the ability to sting a target with a powerful hallunicationary chemical."
- helptext = "The target does not notice they have been stung. The effect occurs after 30 to 60 seconds."
- genomecost = 3
- verbpath = /mob/proc/changeling_lsdsting
-
-/datum/power/changeling/DeathSting
- name = "Death Sting"
- desc = "We silently sting a human, filling him with potent chemicals. His rapid death is all but assured."
- genomecost = 10
- verbpath = /mob/proc/changeling_DEATHsting
-
-/datum/power/changeling/unfat_sting
- name = "Unfat Sting"
- desc = "We silently sting a human, forcing them to rapidly metobolize their fat."
- genomecost = 1
- verbpath = /mob/proc/changeling_unfat_sting
-
-/datum/power/changeling/boost_range
- name = "Boost Range"
- desc = "We evolve the ability to shoot our stingers at humans, with some preperation."
- genomecost = 2
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_boost_range
-
-/datum/power/changeling/Epinephrine
- name = "Epinephrine sacs"
- desc = "We evolve additional sacs of adrenaline throughout our body."
- helptext = "Gives the ability to instantly recover from stuns. High chemical cost."
- genomecost = 4
- verbpath = /mob/proc/changeling_unstun
-
-/datum/power/changeling/ChemicalSynth
- name = "Rapid Chemical-Synthesis"
- desc = "We evolve new pathways for producing our necessary chemicals, permitting us to naturally create them faster."
- helptext = "Doubles the rate at which we naturally recharge chemicals."
- genomecost = 4
- isVerb = 0
- verbpath = /mob/proc/changeling_fastchemical
-/*
-/datum/power/changeling/AdvChemicalSynth
- name = "Advanced Chemical-Synthesis"
- desc = "We evolve new pathways for producing our necessary chemicals, permitting us to naturally create them faster."
- helptext = "Doubles the rate at which we naturally recharge chemicals."
- genomecost = 8
- isVerb = 0
- verbpath = /mob/proc/changeling_fastchemical
-*/
-/datum/power/changeling/EngorgedGlands
- name = "Engorged Chemical Glands"
- desc = "Our chemical glands swell, permitting us to store more chemicals inside of them."
- helptext = "Allows us to store an extra 25 units of chemicals."
- genomecost = 4
- isVerb = 0
- verbpath = /mob/proc/changeling_engorgedglands
-
-/datum/power/changeling/DigitalCamoflague
- name = "Digital Camoflauge"
- desc = "We evolve the ability to distort our form and proprtions, defeating common altgorthms used to detect lifeforms on cameras."
- helptext = "We cannot be tracked by camera while using this skill. However, humans looking at us will find us.. uncanny. We must constantly expend chemicals to maintain our form like this."
- genomecost = 3
- allowduringlesserform = 1
- verbpath = /mob/proc/changeling_digitalcamo
-
-/datum/power/changeling/rapidregeneration
- name = "Rapid Regeneration"
- desc = "We evolve the ability to rapidly regenerate, negating the need for stasis."
- helptext = "Heals a moderate amount of damage every tick."
- genomecost = 8
- verbpath = /mob/proc/changeling_rapidregen
-
-
-
-// Modularchangling, totally stolen from the new player panel. YAYY
-/datum/changeling/proc/EvolutionMenu()//The new one
- set category = "Changeling"
- set desc = "Level up!"
-
- if(!usr || !usr.mind || !usr.mind.changeling) return
- src = usr.mind.changeling
-
- if(!powerinstances.len)
- for(var/P in powers)
- powerinstances += new P()
-
- var/dat = "Changling Evolution Menu"
-
- //javascript, the part that does most of the work~
- dat += {"
-
-
-
-
-
-
- "}
-
- //body tag start + onload and onkeypress (onkeyup) javascript event calls
- dat += ""
-
- //title + search bar
- dat += {"
-
-
-
-
- Changling Evolution Menu
- Hover over a power to see more information
- Current evolution points left to evolve with: [geneticpoints]
- Absorb genomes to acquire more evolution points
-
"
+ dat += "Metal amount: [src.m_amount] / [max_m_amount] cm3 "
+ dat += "Glass amount: [src.g_amount] / [max_g_amount] cm3"
- if(D.materials["$metal"] && (m_amount < (D.materials["$metal"] / coeff)))
- return 0
- if(D.materials["$glass"] && (g_amount < (D.materials["$glass"] / coeff)))
- return 0
- return 1
+ for(var/datum/design/D in matching_designs)
+ if(disabled || !can_build(D))
+ dat += "[D.name]"
+ else
+ dat += "[D.name]"
+
+ if(ispath(D.build_path, /obj/item/stack))
+ var/max_multiplier = min(50, D.materials["$metal"] ?round(m_amount/D.materials["$metal"]):INFINITY,D.materials["$glass"]?round(g_amount/D.materials["$glass"]):INFINITY)
+ if (max_multiplier>10 && !disabled)
+ dat += " x10"
+ if (max_multiplier>25 && !disabled)
+ dat += " x25"
+ if(max_multiplier > 0 && !disabled)
+ dat += " x[max_multiplier]"
+
+ dat += "[get_design_cost(D)] "
+
+ dat += "
"
+ dat += "
"
+ dat += get_queue()
+ dat += "
"
+ return dat
/obj/machinery/autolathe/proc/get_design_cost(var/datum/design/D)
- var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)
+ var/coeff = get_coeff(D)
var/dat
if(D.materials["$metal"])
dat += "[D.materials["$metal"] / coeff] metal "
@@ -327,7 +521,7 @@
if(hack)
for(var/datum/design/D in files.possible_designs)
- if((D.build_type & 4) && ("hacked" in D.category))
+ if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
files.known_designs += D
else
for(var/datum/design/D in files.known_designs)
diff --git a/code/game/machinery/bees_apiary.dm b/code/game/machinery/bees_apiary.dm
index d4e6aa6307e..9021c4ba84d 100644
--- a/code/game/machinery/bees_apiary.dm
+++ b/code/game/machinery/bees_apiary.dm
@@ -43,7 +43,7 @@
..()
return
-/obj/machinery/apiary/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/apiary/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item/queen_bee))
if(health > 0)
user << "\red There is already a queen in there."
diff --git a/code/game/machinery/bees_items.dm b/code/game/machinery/bees_items.dm
index 01ed5bfa407..01e5d96ac02 100644
--- a/code/game/machinery/bees_items.dm
+++ b/code/game/machinery/bees_items.dm
@@ -65,7 +65,6 @@
name = "bottle of BeezEez"
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle17"
- flags = FPRINT | TABLEPASS
New()
src.pixel_x = rand(-5.0, 5)
src.pixel_y = rand(-5.0, 5)
diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm
index f834719c9e0..bf53df74ecf 100644
--- a/code/game/machinery/biogenerator.dm
+++ b/code/game/machinery/biogenerator.dm
@@ -50,17 +50,17 @@
icon_state = "biogen-work"
return
-/obj/machinery/biogenerator/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/biogenerator/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item/weapon/reagent_containers/glass) && !panel_open)
if(beaker)
user << "A container is already loaded into the machine."
else
- user.before_take_item(O)
+ user.unEquip(O)
O.loc = src
beaker = O
user << "You add the container to the machine."
updateUsrDialog()
-
+
if(!processing)
if(default_deconstruction_screwdriver(user, "biogen-empty-o", "biogen-empty", O))
if(beaker)
@@ -72,7 +72,7 @@
if(exchange_parts(user, O))
return
- else if(istype(O, /obj/item/weapon/crowbar))
+ else if(istype(O, /obj/item/weapon/crowbar))
else if(panel_open)
user << "Close the maintenance panel first."
else if(processing)
@@ -105,13 +105,13 @@
if(i >= 10)
user << "The biogenerator is full! Activate it."
else
- user.before_take_item(O)
+ user.unEquip(O)
O.loc = src
user << "You put [O.name] in [src.name]"
- default_deconstruction_crowbar(O)
-
+ default_deconstruction_crowbar(O)
+
update_icon()
return
@@ -246,14 +246,14 @@
if(in_beaker)
if(check_container_volume(10)) return 0
else beaker.reagents.add_reagent("left4zed",10)
- else
+ else
new/obj/item/weapon/reagent_containers/glass/fertilizer/l4z(src.loc)
if("rh")
if (check_cost(25/efficiency)) return 0
if(in_beaker)
if(check_container_volume(10)) return 0
else beaker.reagents.add_reagent("robustharvest",10)
- else
+ else
new/obj/item/weapon/reagent_containers/glass/fertilizer/rh(src.loc)
if("wallet")
if (check_cost(100/efficiency)) return 0
@@ -298,7 +298,7 @@
/obj/machinery/biogenerator/Topic(href, href_list)
if(..() || panel_open)
- return
+ return 1
usr.set_machine(src)
@@ -324,7 +324,7 @@
else if(href_list["menu"])
menustat = "menu"
updateUsrDialog()
-
+
else if(href_list["inbeaker"])
in_beaker = !in_beaker
updateUsrDialog()
\ No newline at end of file
diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm
index 539dd775b0f..89281d06dcd 100644
--- a/code/game/machinery/bots/bots.dm
+++ b/code/game/machinery/bots/bots.dm
@@ -29,7 +29,6 @@
var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras.
var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it.
var/obj/item/device/radio/Radio //The bot's radio, for speaking to people.
- var/radio_frequency //The bot's default radio speaking freqency. Recommended to be on a department frequency.
var/radio_name = "Common"
//var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots
var/auto_patrol = 0// set to make bot automatically patrol
@@ -146,6 +145,8 @@
user << "[src] is in pristine condition."
/obj/machinery/bot/attack_alien(var/mob/living/carbon/alien/user as mob)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
health -= rand(15,30)*brute_dam_coeff
visible_message("[user] has slashed [src]!")
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
@@ -155,6 +156,7 @@
/obj/machinery/bot/attack_animal(var/mob/living/simple_animal/M as mob)
+ M.do_attack_animation(src)
if(M.melee_damage_upper == 0)
return
health -= M.melee_damage_upper
@@ -230,15 +232,13 @@
return 1 //Successful completion. Used to prevent child process() continuing if this one is ended early.
-/obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/screwdriver))
if(!locked)
open = !open
user << "Maintenance panel is now [open ? "opened" : "closed"]."
else
user << "Maintenance panel is locked."
- else if (istype(W, /obj/item/weapon/card/emag) && emagged < 2)
- Emag(user)
else
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm")
if(health >= maxhealth)
@@ -267,7 +267,10 @@
..()
healthcheck()
-
+/obj/machinery/bot/emag_act(user as mob)
+ if (emagged < 2)
+ Emag(user)
+
/obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj)
if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
health -= Proj.damage
@@ -338,11 +341,9 @@
/obj/machinery/bot/attack_ai(mob/user as mob)
attack_hand(user)
-/obj/machinery/bot/proc/speak(var/message, freq, var/freqname = null) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
+/obj/machinery/bot/proc/speak(var/message, var/freqname = null) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
if((!on) || (!message))
return
- if(freq)
- Radio.set_frequency(radio_frequency)
if(freqname)
Radio.autosay(message, src.name, freqname, list(src.z))
else
@@ -373,6 +374,8 @@ obj/machinery/bot/proc/scan(var/scan_type, var/old_target, var/scan_range)
final_result = scan_result
else
continue //The current element failed assessment, move on to the next.
+ else
+ continue
return final_result
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
@@ -583,7 +586,7 @@ obj/machinery/bot/proc/start_patrol()
new_destination = "__nearest__"
post_signal(beacon_freq, "findbeacon", "patrol")
awaiting_beacon = 1
- spawn(150)
+ spawn(200)
awaiting_beacon = 0
if(nearest_beacon)
set_destination(nearest_beacon)
@@ -653,7 +656,7 @@ obj/machinery/bot/proc/start_patrol()
botcard.access = user_access + prev_access //Adds the user's access, if any.
mode = BOT_SUMMON
calc_summon_path()
- speak("Responding.", radio_frequency, radio_name)
+ speak("Responding.", radio_name)
return
// receive response from beacon
@@ -740,7 +743,7 @@ obj/machinery/bot/proc/bot_summon()
check_bot_access()
path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid)
if(!path || tries >= 5) //Cannot reach target. Give up and announce the issue.
- speak("Summon command failed, destination unreachable.", radio_frequency, radio_name)
+ speak("Summon command failed, destination unreachable.", radio_name)
bot_reset()
/obj/machinery/bot/proc/summon_step()
diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm
index 5c4d3bf9200..7bd66b6838f 100644
--- a/code/game/machinery/bots/cleanbot.dm
+++ b/code/game/machinery/bots/cleanbot.dm
@@ -38,7 +38,6 @@
var/failed_steps
var/next_dest
var/next_dest_loc
- radio_frequency = SRV_FREQ //Service
radio_name = "Service"
bot_type = CLEAN_BOT
bot_type_name = "Cleanbot"
@@ -117,7 +116,7 @@ text("[on ? "On" : "Off"]"))
beacon_freq = freq
updateUsrDialog()
-/obj/machinery/bot/cleanbot/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/bot/cleanbot/attackby(obj/item/weapon/W, mob/user as mob, params)
if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if(allowed(usr) && !open && !emagged)
locked = !locked
@@ -263,7 +262,7 @@ text("[on ? "On" : "Off"]"))
qdel(src)
return
-/obj/item/weapon/bucket_sensor/attackby(var/obj/item/W, mob/user as mob)
+/obj/item/weapon/bucket_sensor/attackby(var/obj/item/W, mob/user as mob, params)
..()
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
user.drop_item()
@@ -272,7 +271,7 @@ text("[on ? "On" : "Off"]"))
var/obj/machinery/bot/cleanbot/A = new /obj/machinery/bot/cleanbot(T)
A.name = created_name
user << "You add the robot arm to the bucket and sensor assembly. Beep boop!"
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
else if (istype(W, /obj/item/weapon/pen))
diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm
index 6fb65f8a705..2c9055e29c4 100644
--- a/code/game/machinery/bots/ed209bot.dm
+++ b/code/game/machinery/bots/ed209bot.dm
@@ -33,7 +33,6 @@
var/arrest_type = 0 //If true, don't handcuff
var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type
var/shoot_sound = 'sound/weapons/Taser.ogg'
- radio_frequency = SEC_FREQ
radio_name = "Security"
bot_type = SEC_BOT
bot_type_name = "ED-209"
@@ -171,7 +170,7 @@ Auto Patrol[]"},
declare_arrests = !declare_arrests
updateUsrDialog()
-/obj/machinery/bot/ed209/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/bot/ed209/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if (allowed(user) && !open && !emagged)
locked = !locked
@@ -258,7 +257,7 @@ Auto Patrol[]"},
icon_state = "[lasercolor]ed209[on]"
var/mob/living/carbon/M = target
if(istype(M, /mob/living/carbon/human))
- if( M.stuttering < 5 && !(M_HULK in M.mutations) )
+ if( M.stuttering < 5 && !(HULK in M.mutations) )
M.stuttering = 5
M.Stun(5)
M.Weaken(5)
@@ -269,7 +268,7 @@ Auto Patrol[]"},
if(declare_arrests)
var/area/location = get_area(src)
- speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].",radio_frequency, radio_name)
+ speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_name)
target.visible_message("[target] has been stunned by [src]!",\
"[target] has been stunned by [src]!")
@@ -299,16 +298,15 @@ Auto Patrol[]"},
if(!arrest_type)
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
mode = BOT_ARREST
- playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- target.visible_message("[src] is trying to put handcuffs on [target]!",\
- "[src] is trying to put handcuffs on [target]!")
-
+ playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
+ target.visible_message("[src] is trying to put zipties on [target]!",\
+ "[src] is trying to put zipties on [target]!")
spawn(30)
if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!target.handcuffed)
- target.handcuffed = new /obj/item/weapon/handcuffs(target)
- target.update_inv_handcuffed(0) //update the handcuffs overlay
+ target.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(target)
+ target.update_inv_handcuffed(1) //update the handcuffs overlay
back_to_idle()
else
back_to_idle()
@@ -540,7 +538,7 @@ Auto Patrol[]"},
-/obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
if(istype(W, /obj/item/weapon/pen))
@@ -665,14 +663,14 @@ Auto Patrol[]"},
user << "Taser gun attached."
if(9)
- if(istype(W, /obj/item/weapon/cell))
+ if(istype(W, /obj/item/weapon/stock_parts/cell))
build_step++
user << "You complete the ED-209."
var/turf/T = get_turf(src)
new /obj/machinery/bot/ed209(T,created_name,lasercolor)
user.drop_item()
qdel(W)
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
diff --git a/code/game/machinery/bots/farmbot.dm b/code/game/machinery/bots/farmbot.dm
index f2981c70fe1..c8775c7f5ca 100644
--- a/code/game/machinery/bots/farmbot.dm
+++ b/code/game/machinery/bots/farmbot.dm
@@ -161,7 +161,7 @@
src.updateUsrDialog()
return
-/obj/machinery/bot/farmbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/bot/farmbot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if (src.allowed(user))
src.locked = !src.locked
@@ -531,7 +531,7 @@
new /obj/structure/reagent_dispensers/watertank(src)
-/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob)
+/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob, params)
if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
..()
@@ -544,30 +544,30 @@
A.loc = src.loc
user << "You add the robot arm to the [src]"
src.loc = A //Place the water tank into the assembly, it will be needed for the finished bot
- user.u_equip(S)
+ user.unEquip(S)
del(S)
-/obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (!src.build_step))
src.build_step++
user << "You add the plant analyzer to [src]!"
src.name = "farmbot assembly"
- user.u_equip(W)
+ user.unEquip(W)
del(W)
else if(( istype(W, /obj/item/weapon/reagent_containers/glass/bucket)) && (src.build_step == 1))
src.build_step++
user << "You add a bucket to [src]!"
src.name = "farmbot assembly with bucket"
- user.u_equip(W)
+ user.unEquip(W)
del(W)
else if(( istype(W, /obj/item/weapon/minihoe)) && (src.build_step == 2))
src.build_step++
user << "You add a minihoe to [src]!"
src.name = "farmbot assembly with bucket and minihoe"
- user.u_equip(W)
+ user.unEquip(W)
del(W)
else if((isprox(W)) && (src.build_step == 3))
@@ -579,13 +579,13 @@
S.tank = wTank
S.loc = get_turf(src)
S.name = src.created_name
- user.u_equip(W)
+ user.unEquip(W)
del(W)
del(src)
else if(istype(W, /obj/item/weapon/pen))
var/t = input(user, "Enter new robot name", src.name, src.created_name) as text
- t = copytext(sanitize(t), 1, MAX_NAME_LEN)
+ t = sanitize(copytext(t), 1, MAX_NAME_LEN)
if (!t)
return
if (!in_range(src, usr) && src.loc != usr)
diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm
index eede1892d76..a962d81d6e4 100644
--- a/code/game/machinery/bots/floorbot.dm
+++ b/code/game/machinery/bots/floorbot.dm
@@ -49,7 +49,6 @@
var/oldloc = null
req_one_access = list(access_construction, access_robotics)
var/targetdirection
- radio_frequency = ENG_FREQ //Engineering channel
radio_name = "Engineering"
bot_type = FLOOR_BOT
bot_type_name = "Floorbot"
@@ -134,7 +133,7 @@
return
-/obj/machinery/bot/floorbot/attackby(var/obj/item/W , mob/user as mob)
+/obj/machinery/bot/floorbot/attackby(var/obj/item/W , mob/user as mob, params)
if(istype(W, /obj/item/stack/tile/plasteel))
var/obj/item/stack/tile/plasteel/T = W
if(amount >= 50)
@@ -245,7 +244,7 @@
if(!target && replacetiles) //Finds a floor without a tile and gives it one.
process_type = REPLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile.
target = scan(/turf/simulated/floor, oldtarget)
-
+
if(!target && fixfloors) //Repairs damaged floors and tiles.
process_type = FIX_TILE
target = scan(/turf/simulated/floor, oldtarget)
@@ -288,6 +287,8 @@
target = null
mode = BOT_IDLE
return
+
+ ignore_list = list() // Reset the ignore list
if(loc == target || loc == target.loc)
if(istype(target, /obj/item/stack/tile/plasteel))
@@ -318,7 +319,7 @@
/obj/machinery/bot/floorbot/proc/nag() //Annoy everyone on the channel to refill us!
if(!nagged)
var/area/location = get_area(src)
- speak("Requesting refill at [location]!", radio_frequency, radio_name)
+ speak("Requesting refill at [location]!", radio_name)
nagged = 1
/obj/machinery/bot/floorbot/proc/is_hull_breach(var/turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas.
@@ -478,7 +479,7 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
return
-/obj/item/weapon/storage/toolbox/mechanical/attackby(var/obj/item/stack/tile/plasteel/T, mob/user as mob)
+/obj/item/weapon/storage/toolbox/mechanical/attackby(var/obj/item/stack/tile/plasteel/T, mob/user as mob, params)
if(!istype(T, /obj/item/stack/tile/plasteel))
..()
return
@@ -491,13 +492,13 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles
user.put_in_hands(B)
user << "You add the tiles into the empty toolbox. They protrude from the top."
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
else
user << "You need 10 floor tiles to start building a floorbot."
return
-/obj/item/weapon/toolbox_tiles/attackby(var/obj/item/W, mob/user as mob)
+/obj/item/weapon/toolbox_tiles/attackby(var/obj/item/W, mob/user as mob, params)
..()
if(isprox(W))
qdel(W)
@@ -505,7 +506,7 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
B.created_name = created_name
user.put_in_hands(B)
user << "You add the sensor to the toolbox and tiles!"
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
else if (istype(W, /obj/item/weapon/pen))
@@ -517,7 +518,7 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
created_name = t
-/obj/item/weapon/toolbox_tiles_sensor/attackby(var/obj/item/W, mob/user as mob)
+/obj/item/weapon/toolbox_tiles_sensor/attackby(var/obj/item/W, mob/user as mob, params)
..()
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
qdel(W)
@@ -525,7 +526,7 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
var/obj/machinery/bot/floorbot/A = new /obj/machinery/bot/floorbot(T)
A.name = created_name
user << "You add the robot arm to the odd looking toolbox assembly! Boop beep!"
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
else if (istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm
index c77a5477e13..078d9fff2b9 100644
--- a/code/game/machinery/bots/medbot.dm
+++ b/code/game/machinery/bots/medbot.dm
@@ -30,7 +30,6 @@
var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users.
var/declare_cooldown = 0 //Prevents spam of critical patient alerts.
var/stationary_mode = 0 //If enabled, the Medibot will not move automatically.
- radio_frequency = MED_FREQ //Medical frequency
radio_name = "Medical"
//Setting which reagents to use to treat what by default. By id.
var/treatment_brute = "tricordrazine"
@@ -221,7 +220,7 @@
updateUsrDialog()
return
-/obj/machinery/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if (allowed(user) && !open && !emagged)
locked = !locked
@@ -533,7 +532,7 @@
if((skin == "bezerk"))
return
var/area/location = get_area(src)
- speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!",radio_frequency, radio_name)
+ speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!", radio_name)
declare_cooldown = 1
spawn(200) //Twenty seconds
declare_cooldown = 0
@@ -542,7 +541,7 @@
* Medbot Assembly -- Can be made out of all three medkits.
*/
-/obj/item/weapon/storage/firstaid/attackby(var/obj/item/robot_parts/S, mob/user as mob)
+/obj/item/weapon/storage/firstaid/attackby(var/obj/item/robot_parts/S, mob/user as mob, params)
if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
..()
@@ -564,11 +563,11 @@
qdel(S)
user.put_in_hands(A)
user << "You add the robot arm to the first aid kit."
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
-/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
@@ -598,5 +597,5 @@
var/obj/machinery/bot/medbot/S = new /obj/machinery/bot/medbot(T)
S.skin = skin
S.name = created_name
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
\ No newline at end of file
diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm
index bd1ee21a88c..93342641732 100644
--- a/code/game/machinery/bots/mulebot.dm
+++ b/code/game/machinery/bots/mulebot.dm
@@ -43,7 +43,7 @@ var/global/mulebot_count = 0
var/auto_pickup = 1 // true if auto-pickup at beacon
var/report_delivery = 1 // true if bot will announce an arrival to a location.
- var/obj/item/weapon/cell/cell
+ var/obj/item/weapon/stock_parts/cell/cell
var/datum/wires/mulebot/wires = null
// the installed power cell
@@ -86,18 +86,13 @@ var/global/mulebot_count = 0
// screwdriver: open/close hatch
// cell: insert it
// other: chance to knock rider off bot
-/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user)
- if(istype(I,/obj/item/weapon/card/emag))
- locked = !locked
- user << "You [locked ? "lock" : "unlock"] the mulebot's controls!"
- flick("mulebot-emagged", src)
- playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)
- else if(istype(I, /obj/item/weapon/card/id) || istype(I, /obj/item/device/pda))
+/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user, params)
+ if(istype(I, /obj/item/weapon/card/id) || istype(I, /obj/item/device/pda))
if(toggle_lock(user))
user << "Controls [(locked ? "locked" : "unlocked")]."
updateUsrDialog()
- else if(istype(I,/obj/item/weapon/cell) && open && !cell)
- var/obj/item/weapon/cell/C = I
+ else if(istype(I,/obj/item/weapon/stock_parts/cell) && open && !cell)
+ var/obj/item/weapon/stock_parts/cell/C = I
user.drop_item()
C.loc = src
cell = C
@@ -139,7 +134,12 @@ var/global/mulebot_count = 0
..()
return
-
+/obj/machinery/bot/mulebot/emag_act(user as mob)
+ locked = !locked
+ user << "You [locked ? "lock" : "unlock"] the mulebot's controls!"
+ flick("mulebot-emagged", src)
+ playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)
+
/obj/machinery/bot/mulebot/ex_act(var/severity)
unload(0)
switch(severity)
@@ -238,7 +238,7 @@ var/global/mulebot_count = 0
//user << browse("M.U.L.E. Mk. III [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500")
//onclose(user, "mulebot")
- var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. V [suffix ? "([suffix])" : ""]", 350, 535)
+ var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. V [suffix ? "([suffix])" : ""]", 350, 620)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
@@ -286,7 +286,7 @@ var/global/mulebot_count = 0
if("cellinsert")
if(open && !cell)
- var/obj/item/weapon/cell/C = usr.get_active_hand()
+ var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand()
if(istype(C))
usr.drop_item()
cell = C
@@ -313,6 +313,8 @@ var/global/mulebot_count = 0
updateDialog()
if("destination")
+ if(!delivery_beacons.len)
+ post_signal(beacon_freq, "findbeacon", "delivery")
refresh=0
var/new_dest = input("Select M.U.L.E. Destination", "Mulebot [suffix ? "([suffix])" : ""]", destination) as null|anything in delivery_beacons
refresh=1
@@ -322,7 +324,7 @@ var/global/mulebot_count = 0
if("setid")
refresh=0
- var/new_id = copytext(sanitize(input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null),1,MAX_NAME_LEN)
+ var/new_id = sanitize(copytext(input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null,1,MAX_NAME_LEN))
refresh=1
if(new_id)
suffix = new_id
@@ -683,7 +685,6 @@ var/global/mulebot_count = 0
// called when bot reaches current target
/obj/machinery/bot/mulebot/proc/at_target()
if(!reached_target)
- radio_frequency = SUP_FREQ //Supply channel
radio_name = "Supply"
Radio.config(list("[radio_name]" = 0))
visible_message("[src] makes a chiming sound!", "You hear a chime.")
@@ -696,12 +697,11 @@ var/global/mulebot_count = 0
calling_ai << "\icon[src] [src] wirelessly plays a chiming sound!"
playsound(calling_ai, 'sound/machines/chime.ogg',40, 0)
calling_ai = null
- radio_frequency = AIPRIV_FREQ //Report on AI Private instead if the AI is controlling us.
radio_name = "AI Private"
Radio.config(list("[radio_name]" = 0))
if(load) // if loaded, unload at target
- speak("Destination [destination] reached. Unloading [load].", radio_frequency, radio_name)
+ speak("Destination [destination] reached. Unloading [load].", radio_name)
unload(loaddir)
else
// not loaded
@@ -717,7 +717,7 @@ var/global/mulebot_count = 0
if(AM)
load(AM)
if(report_delivery)
- speak("Now loading [load] at [get_area(src)].", radio_frequency, radio_name)
+ speak("Now loading [load] at [get_area(src)].", radio_name)
// whatever happened, check to see if we return home
if(auto_return && destination != home_destination)
diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm
index fecf5cdcb26..a790767c851 100644
--- a/code/game/machinery/bots/secbot.dm
+++ b/code/game/machinery/bots/secbot.dm
@@ -25,7 +25,6 @@
var/arrest_type = 0 //If true, don't handcuff
var/harmbaton = 0
var/base_icon = "secbot"
- radio_frequency = SEC_FREQ //Security channel
radio_name = "Security"
bot_type = SEC_BOT
bot_type_name = "Secbot"
@@ -50,9 +49,8 @@
/obj/machinery/bot/secbot/pingsky
name = "Officer Pingsky"
desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment."
- radio_frequency = AIPRIV_FREQ
radio_name = "AI Private"
-
+
/obj/machinery/bot/secbot/ofitser
name = "Prison Ofitser"
desc = "It's Prison Ofitser! Powered by the tears and sweat of prisoners."
@@ -177,7 +175,7 @@ Auto Patrol: []"},
updateUsrDialog()
-/obj/machinery/bot/secbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/bot/secbot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if(allowed(user) && !open && !emagged)
locked = !locked
@@ -243,7 +241,7 @@ Auto Patrol: []"},
icon_state = "[base_icon][on]"
var/mob/living/carbon/M = target
if(istype(M, /mob/living/carbon/human))
- if( M.stuttering < 5 && !(M_HULK in M.mutations) )
+ if( M.stuttering < 5 && !(HULK in M.mutations) )
M.stuttering = 5
if(harmbaton) // Bots with harmbaton enabled become shitcurity. - Dave
M.apply_damage(10)
@@ -256,7 +254,7 @@ Auto Patrol: []"},
if(declare_arrests)
var/area/location = get_area(src)
- speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].",radio_frequency, radio_name)
+ speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_name)
target.visible_message("[target] has been [harmbaton ? "beaten" : "stunned"] by [src]!",\
"[target] has been [harmbaton ? "beaten" : "stunned"] by [src]!")
@@ -286,15 +284,15 @@ Auto Patrol: []"},
if(!arrest_type)
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
mode = BOT_ARREST
- playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- target.visible_message("[src] is trying to put handcuffs on [target]!",\
- "[src] is trying to put handcuffs on [target]!")
- spawn(60)
+ playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
+ target.visible_message("[src] is trying to put zipties on [target]!",\
+ "[src] is trying to put zipties on [target]!")
+ spawn(30)
if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!target.handcuffed)
- target.handcuffed = new /obj/item/weapon/handcuffs(target)
- target.update_inv_handcuffed(0) //update the handcuffs overlay
+ target.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(target)
+ target.update_inv_handcuffed(1) //update the handcuffs overlay
playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0)
back_to_idle()
else
@@ -414,7 +412,7 @@ Auto Patrol: []"},
//Secbot Construction
-/obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob)
+/obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob, params)
..()
if(!issignaler(S))
..()
@@ -428,12 +426,12 @@ Auto Patrol: []"},
var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly
user.put_in_hands(A)
user << "You add the signaler to the helmet."
- user.before_take_item(src, 1)
+ user.unEquip(src, 1)
qdel(src)
else
return
-/obj/item/weapon/secbot_assembly/attackby(obj/item/I, mob/user)
+/obj/item/weapon/secbot_assembly/attackby(obj/item/I, mob/user, params)
..()
if(istype(I, /obj/item/weapon/weldingtool))
if(!build_step)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 977c1bd3d3e..e60921e700b 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -14,11 +14,10 @@
var/c_tag_order = 999
var/status = 1.0
anchored = 1.0
+ var/start_active = 0 //If it ignores the random chance to start broken on round start
var/invuln = null
- var/bugged = 0
+ var/obj/item/device/camera_bug/bug = null
var/obj/item/weapon/camera_assembly/assembly = null
- var/watcherslist = list()
- var/obj/item/device/camera_bug/hasbug = null
//OTHER
@@ -28,26 +27,40 @@
var/light_disabled = 0
var/alarm_on = 0
var/busy = 0
- var/indestructible = 0 // If set, prevents aliens from destroying it
+ var/emped = 0 //Number of consecutive EMP's on this camera
/obj/machinery/camera/New()
wires = new(src)
assembly = new(src)
assembly.state = 4
+ assembly.anchored = 1
+ assembly.update_icon()
+
/* // Use this to look for cameras that have the same c_tag.
- for(var/obj/machinery/camera/C in cameranet.viewpoints)
+ for(var/obj/machinery/camera/C in cameranet.cameras)
var/list/tempnetwork = C.network&src.network
if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]"
*/
- if(!src.network || src.network.len < 1)
- if(loc)
- error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]")
- else
- error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]")
- ASSERT(src.network)
- ASSERT(src.network.len > 0)
+ ..()
+
+/obj/machinery/camera/initialize()
+ if(z == 1 && prob(3) && !start_active)
+ deactivate()
+
+/obj/machinery/camera/Destroy()
+ deactivate(null, 0) //kick anyone viewing out
+ if(assembly)
+ qdel(assembly)
+ assembly = null
+ if(istype(bug))
+ bug.bugged_cameras -= src.c_tag
+ if(bug.current == src)
+ bug.current = null
+ bug = null
+ del(wires)
+ cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
..()
/obj/machinery/camera/emp_act(severity)
@@ -60,30 +73,35 @@
stat |= EMPED
SetLuminosity(0)
triggerCameraAlarm()
+ emped = emped+1 //Increase the number of consecutive EMP's
+ var/thisemp = emped //Take note of which EMP this proc is for
spawn(900)
- network = previous_network
- icon_state = initial(icon_state)
- stat &= ~EMPED
- cancelCameraAlarm()
- if(can_use())
- cameranet.addCamera(src)
+ if(loc) //qdel limbo
+ if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again
+ network = previous_network
+ icon_state = initial(icon_state)
+ stat &= ~EMPED
+ cancelCameraAlarm()
+ if(can_use())
+ cameranet.addCamera(src)
+ emped = 0 //Resets the consecutive EMP count
for(var/mob/O in mob_list)
- if(O.client && O.client.eye == src)
+ if (O.client && O.client.eye == src)
O.unset_machine()
O.reset_view(null)
O << "The screen bursts into static."
..()
-/obj/machinery/camera/ex_act(severity)
+/obj/machinery/camera/ex_act(severity, target)
if(src.invuln)
return
else
- ..(severity)
+ ..()
return
/obj/machinery/camera/blob_act()
- del(src)
+ qdel(src)
return
/obj/machinery/camera/proc/setViewRange(var/num = 7)
@@ -98,8 +116,7 @@
/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user as mob)
if(!istype(user))
return
- if(indestructible)
- return
+ user.do_attack_animation(src)
status = 0
visible_message("\The [user] slashes at [src]!")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
@@ -107,7 +124,9 @@
add_hiddenprint(user)
deactivate(user,0)
-/obj/machinery/camera/attackby(W as obj, mob/living/user as mob)
+/obj/machinery/camera/attackby(W as obj, mob/living/user as mob, params)
+ var/msg = "You attach [W] into the assembly inner circuits."
+ var/msg2 = "The camera already has that upgrade!"
// DECONSTRUCTION
if(istype(W, /obj/item/weapon/screwdriver))
@@ -123,11 +142,38 @@
else if(istype(W, /obj/item/weapon/weldingtool) && wires.CanDeconstruct())
if(weld(W, user))
- if(assembly)
- assembly.loc = src.loc
- assembly.state = 1
- del(src)
+ user << "You unweld the camera leaving it as just a frame screwed to the wall."
+ if(!assembly)
+ assembly = new()
+ assembly.loc = src.loc
+ assembly.state = 1
+ assembly.dir = src.dir
+ assembly.update_icon()
+ assembly = null
+ qdel(src)
+ return
+ else if(istype(W, /obj/item/device/analyzer) && panel_open) //XRay
+ if(!isXRay())
+ upgradeXRay()
+ qdel(W)
+ user << "[msg]"
+ else
+ user << "[msg2]"
+ else if(istype(W, /obj/item/stack/sheet/mineral/plasma) && panel_open)
+ if(!isEmpProof())
+ upgradeEmpProof()
+ user << "[msg]"
+ qdel(W)
+ else
+ user << "[msg2]"
+ else if(istype(W, /obj/item/device/assembly/prox_sensor) && panel_open)
+ if(!isMotion())
+ upgradeMotion()
+ user << "[msg]"
+ qdel(W)
+ else
+ user << "[msg2]"
// OTHER
else if ((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
@@ -145,37 +191,29 @@
P = W
itemname = P.name
info = P.notehtml
- U << "You hold \a [itemname] up to the camera ..."
- for(var/mob/living/silicon/ai/O in living_mob_list)
- if(!O.client) continue
- if(U.name == "Unknown") O << "[U] holds \a [itemname] up to one of your cameras ..."
- else O << "[U] holds \a [itemname] up to one of your cameras ..."
- O << browse(text("[][]", itemname, info), text("window=[]", itemname))
+ U << "You hold \the [itemname] up to the camera ..."
+ U.changeNext_move(CLICK_CD_MELEE)
for(var/mob/O in player_list)
- if(O.client && O.client.eye == src)
- O.unset_machine()
- O.reset_view(null)
- O << "[U] holds \a [itemname] up to the camera..."
- O << browse("[itemname][info]","window=[itemname]")
- else if (istype(W, /obj/item/device/camera_bug) && panel_open)
+ if(istype(O, /mob/living/silicon/ai))
+ var/mob/living/silicon/ai/AI = O
+ if(U.name == "Unknown") AI << "[U] holds \a [itemname] up to one of your cameras ..."
+ else AI << "[U] holds \a [itemname] up to one of your cameras ..."
+ AI.last_paper_seen = "[itemname][info]"
+ else if (O.client && O.client.eye == src)
+ O << "[U] holds \a [itemname] up to one of the cameras ..."
+ O << browse(text("[][]", itemname, info), text("window=[]", itemname))
+ else if (istype(W, /obj/item/device/camera_bug))
if (!src.can_use())
- user << "\blue Camera non-functional"
+ user << "Camera non-functional."
return
+ if(istype(src.bug))
+ user << "Camera bug removed."
+ src.bug.bugged_cameras -= src.c_tag
+ src.bug = null
else
- user << "\blue Camera bugged."
- user.drop_item(W)
- hasbug = W
- contents += W
- if(prob(15))
- spawn(30)
- if(src.can_use() && hasbug)
- desc += " The power light on the camera is blinking"
- triggerCameraAlarm()
- else if (iscrowbar(W) && panel_open && src.hasbug)
- user << "\blue You retrieve \the [hasbug]"
- user.put_in_hands(hasbug)
- hasbug = null
- deactivatebug(user)
+ user << "Camera bugged."
+ src.bug = W
+ src.bug.bugged_cameras[src.c_tag] = src
else if(istype(W, /obj/item/weapon/melee/energy/blade))//Putting it here last since it's a special case. I wonder if there is a better way to do these than type casting.
deactivate(user,2)//Here so that you can disconnect anyone viewing the camera, regardless if it's on or off.
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
@@ -183,49 +221,41 @@
spark_system.start()
playsound(loc, 'sound/weapons/blade1.ogg', 50, 1)
playsound(loc, "sparks", 50, 1)
- visible_message("\blue The camera has been sliced apart by [] with an energy blade!")
- del(src)
+ visible_message("[user] has sliced the camera apart with an energy blade!")
+ qdel(src)
else if(istype(W, /obj/item/device/laser_pointer))
var/obj/item/device/laser_pointer/L = W
L.laser_act(src, user)
else
..()
return
-/obj/machinery/camera/proc/deactivatebug(user as mob)
- for(var/mob/O in player_list)
- if(istype(O.machine, /obj/item/device/handtv))
- var/obj/item/device/handtv/S = O.machine
- if (S.current == src)
- O.unset_machine()
- O.reset_view(null)
- O << "The screen bursts into static."
/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1)
if(choice==1)
status = !( src.status )
if (!(src.status))
if(user)
- visible_message("\red [user] has deactivated [src]!")
+ visible_message("[user] deactivates [src]!")
add_hiddenprint(user)
else
- visible_message("\red \The [src] deactivates!")
+ visible_message("\The [src] deactivates!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = "[initial(icon_state)]1"
- add_hiddenprint(user)
+
else
if(user)
- visible_message("\red [user] has reactivated [src]!")
+ visible_message("[user] reactivates [src]!")
add_hiddenprint(user)
else
- visible_message("\red \the [src] reactivates!")
+ visible_message("\The [src] reactivates!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = initial(icon_state)
- add_hiddenprint(user)
+
// now disconnect anyone using the camera
//Apparently, this will disconnect anyone even if the camera was re-activated.
//I guess that doesn't matter since they can't use it anyway?
for(var/mob/O in player_list)
- if(O.client && O.client.eye == src)
+ if (O.client && O.client.eye == src)
O.unset_machine()
O.reset_view(null)
O << "The screen bursts into static."
@@ -239,7 +269,7 @@
/obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = 0
for(var/mob/living/silicon/S in mob_list)
- S.cancelAlarm("Camera", get_area(src), list(src), src)
+ S.cancelAlarm("Camera", get_area(src), src)
/obj/machinery/camera/proc/can_use()
if(!status)
@@ -301,7 +331,7 @@
return 0
// Do after stuff here
- user << "You start to weld the [src].."
+ user << "You start to weld [src]."
playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
WT.eyecheck(user)
busy = 1
diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm
index 9f6eb2210af..f91565511f5 100644
--- a/code/game/machinery/camera/camera_assembly.dm
+++ b/code/game/machinery/camera/camera_assembly.dm
@@ -1,16 +1,15 @@
/obj/item/weapon/camera_assembly
name = "camera assembly"
- desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
+ desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface."
icon = 'icons/obj/monitors.dmi'
icon_state = "cameracase"
w_class = 2
anchored = 0
-
- m_amt = 700
- g_amt = 300
+ m_amt = 400
+ g_amt = 250
// Motion, EMP-Proof, X-Ray
- var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/weapon/reagent_containers/food/snacks/grown/carrot)
+ var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/device/analyzer)
var/list/upgrades = list()
var/state = 0
var/busy = 0
@@ -22,7 +21,7 @@
4 = Screwdriver panel closed and is fully built (you cannot attach upgrades)
*/
-/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob)
+/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob, params)
switch(state)
@@ -59,8 +58,10 @@
if(iscoil(W))
var/obj/item/stack/cable_coil/C = W
if(C.use(2))
- user << "You add wires to the assembly."
+ user << "You add wires to the assembly."
state = 3
+ else
+ user << "You need 2 coils of wire to wire the assembly."
return
else if(iswelder(W))
@@ -87,7 +88,8 @@
usr << "No network found please hang up and try your call again."
return
- var/temptag = "[get_area(src)] ([rand(1, 999)])"
+ var/area/camera_area = get_area(src)
+ var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])"
input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag))
state = 4
@@ -98,7 +100,7 @@
C.auto_turn()
C.network = uniquelist(tempnetwork)
- tempnetwork = difflist(C.network,RESTRICTED_CAMERA_NETWORKS)
+ tempnetwork = difflist(C.network,restricted_camera_networks)
if(!tempnetwork.len)//Camera isn't on any open network - remove its chunk from AI visibility.
cameranet.removeCamera(C)
@@ -124,7 +126,7 @@
// Upgrades!
if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already.
- user << "You attach the [W] into the assembly inner circuits."
+ user << "You attach \the [W] into the assembly inner circuits."
upgrades += W
user.drop_item(W)
W.loc = src
@@ -169,4 +171,4 @@
return 0
return 1
busy = 0
- return 0
\ No newline at end of file
+ return 0
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index 588ab5005ce..0c6f7d95a7f 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -8,6 +8,8 @@
/obj/machinery/camera/process()
// motion camera event loop
+ if (stat & (EMPED|NOPOWER))
+ return
if(!isMotion())
. = PROCESS_KILL
return
@@ -40,16 +42,20 @@
cancelAlarm()
/obj/machinery/camera/proc/cancelAlarm()
+ if (!status || (stat & NOPOWER))
+ return 0
if (detectTime == -1)
for (var/mob/living/silicon/aiPlayer in player_list)
- if (status) aiPlayer.cancelAlarm("Motion", src.loc.loc)
+ aiPlayer.cancelAlarm("Motion", get_area(src), src)
detectTime = 0
return 1
/obj/machinery/camera/proc/triggerAlarm()
+ if (!status || (stat & NOPOWER))
+ return 0
if (!detectTime) return 0
for (var/mob/living/silicon/aiPlayer in player_list)
- if (status) aiPlayer.triggerAlarm("Motion", src.loc.loc, src)
+ aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
detectTime = -1
return 1
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 0ee0e622b8b..84ec6734e1b 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -57,7 +57,7 @@
return O
/obj/machinery/camera/proc/isXRay()
- var/O = locate(/obj/item/weapon/reagent_containers/food/snacks/grown/carrot) in assembly.upgrades
+ var/O = locate(/obj/item/device/analyzer) in assembly.upgrades
return O
/obj/machinery/camera/proc/isMotion()
@@ -68,10 +68,21 @@
/obj/machinery/camera/proc/upgradeEmpProof()
assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly))
+ setPowerUsage()
/obj/machinery/camera/proc/upgradeXRay()
- assembly.upgrades.Add(new /obj/item/weapon/reagent_containers/food/snacks/grown/carrot(assembly))
+ assembly.upgrades.Add(new /obj/item/device/analyzer(assembly))
+ setPowerUsage()
// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list.
/obj/machinery/camera/proc/upgradeMotion()
- assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
\ No newline at end of file
+ assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
+ setPowerUsage()
+
+/obj/machinery/camera/proc/setPowerUsage()
+ var/mult = 1
+ if (isXRay())
+ mult++
+ if (isMotion())
+ mult++
+ active_power_usage = mult*initial(active_power_usage)
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index 47b2edf9fac..dd7c175ce4d 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -1,16 +1,25 @@
+/mob/living/silicon/ai/var/max_locations = 10
+/mob/living/silicon/ai/var/stored_locations[0]
+
+/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf)
+ if(!T)
+ return 1
+ if((T.z in config.admin_levels))
+ return 1
+ if(T.z > 6)
+ return 1
+ return 0
+
/mob/living/silicon/ai/proc/get_camera_list()
+
if(src.stat == 2)
return
- var/list/L = list()
- for (var/obj/machinery/camera/C in cameranet.viewpoints)
- L.Add(C)
-
- camera_sort(L)
+ cameranet.process_sort()
var/list/T = list()
T["Cancel"] = "Cancel"
- for (var/obj/machinery/camera/C in L)
+ for (var/obj/machinery/camera/C in cameranet.cameras)
var/list/tempnetwork = C.network&src.network
if (tempnetwork.len)
T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C
@@ -19,20 +28,76 @@
track.cameras = T
return T
+
/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list())
+ set category = "AI Commands"
+ set name = "Show Camera List"
+
if(src.stat == 2)
src << "You can't list the cameras because you are dead!"
return
if (!camera || camera == "Cancel")
return 0
-
+
var/obj/machinery/camera/C = track.cameras[camera]
- track = null
src.eyeobj.setLoc(C)
return
+/mob/living/silicon/ai/proc/ai_store_location(loc as text)
+ set category = "AI Commands"
+ set name = "Store Camera Location"
+ set desc = "Stores your current camera location by the given name"
+
+ loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN))
+ if(!loc)
+ src << "\red Must supply a location name"
+ return
+
+ if(stored_locations.len >= max_locations)
+ src << "\red Cannot store additional locations. Remove one first"
+ return
+
+ if(loc in stored_locations)
+ src << "\red There is already a stored location by this name"
+ return
+
+ var/L = src.eyeobj.getLoc()
+ if (InvalidTurf(get_turf(L)))
+ src << "\red Unable to store this location"
+ return
+
+ stored_locations[loc] = L
+ src << "Location '[loc]' stored"
+
+/mob/living/silicon/ai/proc/sorted_stored_locations()
+ return sortList(stored_locations)
+
+/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations())
+ set category = "AI Commands"
+ set name = "Goto Camera Location"
+ set desc = "Returns to the selected camera location"
+
+ if (!(loc in stored_locations))
+ src << "\red Location [loc] not found"
+ return
+
+ var/L = stored_locations[loc]
+ src.eyeobj.setLoc(L)
+
+/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations())
+ set category = "AI Commands"
+ set name = "Delete Camera Location"
+ set desc = "Deletes the selected camera location"
+
+ if (!(loc in stored_locations))
+ src << "\red Location [loc] not found"
+ return
+
+ stored_locations.Remove(loc)
+ src << "Location [loc] removed"
+
// Used to allow the AI is write in mob names/camera name from the CMD line.
/datum/trackable
var/list/names = list()
@@ -50,12 +115,7 @@
for(var/mob/living/M in mob_list)
// Easy checks first.
// Don't detect mobs on Centcom. Since the wizard den is on Centcomm, we only need this.
- var/turf/T = get_turf(M)
- if(!T)
- continue
- if(T.z == 2)
- continue
- if(T.z > 6)
+ if(InvalidTurf(get_turf(M)))
continue
if(M == usr)
continue
@@ -72,11 +132,8 @@
//Cameras can't track people wearing an agent card or a ninja hood.
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
continue
- if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja))
- var/obj/item/clothing/head/helmet/space/space_ninja/hood = H.head
- if(!hood.canremove)
- continue
-
+ if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && (H.head.flags & NODROP))
+ continue
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
if(!near_camera(M))
continue
@@ -98,6 +155,10 @@
return targets
/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs())
+ set category = "AI Commands"
+ set name = "Track With Camera"
+ set desc = "Select who you would like to track."
+
if(src.stat == 2)
src << "You can't track with camera because you are dead!"
return
@@ -108,61 +169,18 @@
src.track = null
ai_actual_track(target)
-/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target as mob)
- if(!istype(target)) return
- spawn(0)
- if(istype(target, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = target
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
- src << "Unable to locate an airlock"
- return
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
- src << "Unable to locate an airlock"
- return
- if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && !H.head.canremove)
- src << "Unable to locate an airlock"
- return
- if(H.digitalcamo)
- src << "Unable to locate an airlock"
- return
- if (!near_camera(target))
- src << "Target is not near any active cameras."
- return
- var/obj/machinery/door/airlock/tobeopened
- var/dist = -1
- for(var/obj/machinery/door/airlock/D in range(3,target))
- if(!D.density) continue
- if(dist < 0)
- dist = get_dist(D, target)
- //world << dist
- tobeopened = D
- else
- if(dist > get_dist(D, target))
- dist = get_dist(D, target)
- //world << dist
- tobeopened = D
- //world << "found [tobeopened.name] closer"
- else
- //world << "[D.name] not close enough | [get_dist(D, target)] | [dist]"
- if(tobeopened)
- switch(alert(src, "Do you want to open \the [tobeopened] for [target]?","Doorknob_v2a.exe","Yes","No"))
- if("Yes")
- var/nhref = "src=\ref[tobeopened];aiEnable=7"
- tobeopened.Topic(nhref, params2list(nhref), tobeopened, 1)
- src << "\blue You've opened \the [tobeopened] for [target]."
- if("No")
- src << "\red You deny the request."
- else
- src << "\red You've failed to open an airlock for [target]"
+/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0)
+ if(!cameraFollow)
return
-/mob/living/silicon/ai/proc/ai_actual_track(atom/target as mob|obj)
+
+ src << "Follow camera mode [forced ? "terminated" : "ended"]."
+ cameraFollow = null
+
+/mob/living/silicon/ai/proc/ai_actual_track(atom/movable/target as mob|obj)
if(!istype(target)) return
var/mob/living/silicon/ai/U = usr
U.cameraFollow = target
- //U << text("Now tracking [] on camera.", target.name)
- //if (U.machine == null)
- // U.machine = U
U << "Now tracking [target.name] on camera."
spawn (0)
@@ -172,30 +190,26 @@
if (istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
- U << "Follow camera mode terminated."
- U.cameraFollow = null
+ U.ai_cancel_tracking(1)
return
-/* if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && !H.head.canremove)
- U << "Follow camera mode terminated."
- U.cameraFollow = null
- return*/
if(H.digitalcamo)
- U << "Follow camera mode terminated."
- U.cameraFollow = null
+ U.ai_cancel_tracking(1)
+ return
+ if(H.head && istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && (H.head.flags & NODROP))
+ U.ai_cancel_tracking(1)
return
if(istype(target.loc,/obj/effect/dummy))
- U << "Follow camera mode ended."
- U.cameraFollow = null
+ U.ai_cancel_tracking()
return
- if (!near_camera(target))
+ if (!trackable(target))
U << "Target is not near any active cameras."
sleep(100)
continue
if(U.eyeobj)
- U.eyeobj.setLoc(get_turf(target))
+ U.eyeobj.setLoc(get_turf(target), 0)
else
view_core()
return
@@ -212,6 +226,12 @@
return 0
return 1
+/proc/trackable(atom/movable/M)
+ var/turf/T = get_turf(M)
+ if(T && (T.z in config.contact_levels))
+ return 1
+
+ return near_camera(M)
/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob)
if (!istype(user))
@@ -223,19 +243,3 @@
/mob/living/silicon/ai/attack_ai(var/mob/user as mob)
ai_camera_list()
-
-/proc/camera_sort(list/L)
- var/obj/machinery/camera/a
- var/obj/machinery/camera/b
-
- for (var/i = L.len, i > 0, i--)
- for (var/j = 1 to i - 1)
- a = L[j]
- b = L[j + 1]
- if (a.c_tag_order != b.c_tag_order)
- if (a.c_tag_order > b.c_tag_order)
- L.Swap(j, j + 1)
- else
- if (sorttext(a.c_tag, b.c_tag) < 0)
- L.Swap(j, j + 1)
- return L
diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm
index 16c39585dfa..823671c2019 100644
--- a/code/game/machinery/cell_charger.dm
+++ b/code/game/machinery/cell_charger.dm
@@ -8,7 +8,7 @@
idle_power_usage = 5
active_power_usage = 60
power_channel = EQUIP
- var/obj/item/weapon/cell/charging = null
+ var/obj/item/weapon/stock_parts/cell/charging = null
var/chargelevel = -1
proc
updateicon()
@@ -34,11 +34,11 @@
if(charging)
usr << "Current charge: [charging.charge]"
- attackby(obj/item/weapon/W, mob/user)
+ attackby(obj/item/weapon/W, mob/user, params)
if(stat & BROKEN)
return
- if(istype(W, /obj/item/weapon/cell) && anchored)
+ if(istype(W, /obj/item/weapon/stock_parts/cell) && anchored)
if(charging)
user << "\red There is already a cell in the charger."
return
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index f7eb7803fd4..27e16c1be95 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -328,7 +328,7 @@
return
//Let's unlock this early I guess. Might be too early, needs tweaking.
-/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/screwdriver))
if(occupant || mess || locked)
user << "The maintenance panel is locked."
diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm
index cd78210b41f..a14da59c097 100644
--- a/code/game/machinery/computer/HolodeckControl.dm
+++ b/code/game/machinery/computer/HolodeckControl.dm
@@ -161,45 +161,17 @@
-/obj/machinery/computer/HolodeckControl/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob)
-//Warning, uncommenting this can have concequences. For example, deconstructing the computer may cause holographic eswords to never derez
-
-/* if(istype(D, /obj/item/weapon/screwdriver))
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
- if(do_after(user, 20))
- if (src.stat & BROKEN)
- user << "\blue The broken glass falls out."
- var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
- new /obj/item/weapon/shard( src.loc )
- var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
- for (var/obj/C in src)
- C.loc = src.loc
- A.circuit = M
- A.state = 3
- A.icon_state = "3"
- A.anchored = 1
- del(src)
- else
- user << "\blue You disconnect the monitor."
- var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
- var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
- for (var/obj/C in src)
- C.loc = src.loc
- A.circuit = M
- A.state = 4
- A.icon_state = "4"
- A.anchored = 1
- del(src)
-
-*/
- if(istype(D, /obj/item/weapon/card/emag) && !emagged)
+/obj/machinery/computer/HolodeckControl/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob, params)
+ return
+
+/obj/machinery/computer/HolodeckControl/emag_act(user as mob)
+ if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
user << "\blue You vastly increase projector power and override the safety and security protocols."
user << "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator."
log_game("[key_name(usr)] emagged the Holodeck Control Computer")
- src.updateUsrDialog()
- return
+ src.updateUsrDialog()
/obj/machinery/computer/HolodeckControl/New()
..()
@@ -269,8 +241,7 @@
if(isobj(obj))
var/mob/M = obj.loc
if(ismob(M))
- M.u_equip(obj)
- M.update_icons() //so their overlays update
+ M.unEquip(obj, 1) //Holoweapons should always drop.
if(!silent)
var/obj/oldobj = obj
@@ -394,7 +365,7 @@
var/turf/simulated/floor/FF = get_step(src,direction)
FF.update_icon() //so siding get updated properly
-/turf/simulated/floor/holofloor/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/turf/simulated/floor/holofloor/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
return
// HOLOFLOOR DOES NOT GIVE A FUCK
@@ -431,7 +402,7 @@
return // HOLOTABLE DOES NOT GIVE A FUCK
-/obj/structure/table/holotable/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/structure/table/holotable/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
var/obj/item/weapon/grab/G = W
if(G.state<2)
@@ -489,7 +460,7 @@
throw_range = 5
throwforce = 0
w_class = 2.0
- flags = FPRINT | TABLEPASS | NOSHIELD
+ flags = NOSHIELD
var/active = 0
/obj/item/weapon/holo/esword/green
@@ -551,7 +522,7 @@
density = 1
throwpass = 1
-/obj/structure/holohoop/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/structure/holohoop/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
var/obj/item/weapon/grab/G = W
if(G.state<2)
@@ -609,7 +580,7 @@
..()
-/obj/machinery/readybutton/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/readybutton/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
user << "The device is a solid button, there's nothing you can do with it!"
/obj/machinery/readybutton/attack_hand(mob/user as mob)
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index bfd979025be..9f73b206443 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -73,7 +73,7 @@
/obj/machinery/computer/operating/Topic(href, href_list)
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
return
diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm
index 99751ab3296..d408d934c25 100644
--- a/code/game/machinery/computer/ai_core.dm
+++ b/code/game/machinery/computer/ai_core.dm
@@ -10,7 +10,7 @@
var/obj/item/device/mmi/brain = null
-/obj/structure/AIcore/attackby(obj/item/P as obj, mob/user as mob)
+/obj/structure/AIcore/attackby(obj/item/P as obj, mob/user as mob, params)
switch(state)
if(0)
if(istype(P, /obj/item/weapon/wrench))
@@ -174,8 +174,12 @@
icon_state = "ai-empty"
anchored = 1
state = 20//So it doesn't interact based on the above. Not really necessary.
+
+/obj/structure/AIcore/deactivated/Destroy()
+ empty_playable_ai_cores -= src
+ ..()
-/obj/structure/AIcore/deactivated/attackby(var/obj/item/W, var/mob/user)
+/obj/structure/AIcore/deactivated/attackby(var/obj/item/W, var/mob/user, params)
if(istype(W, /obj/item/device/aicard))//Is it?
var/obj/item/device/aicard/card = W
card.transfer_ai("INACTIVE","AICARD",src,user)
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index 7a45f1a8114..51c8626c1d1 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -13,7 +13,7 @@
src.overlays += image('icons/obj/computer.dmi', "ai-fixer-empty")
-/obj/machinery/computer/aifixer/attackby(I as obj, user as mob)
+/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/device/aicard))
if(stat & (NOPOWER|BROKEN))
user << "This terminal isn't functioning right now, get it working!"
@@ -79,7 +79,7 @@
/obj/machinery/computer/aifixer/Topic(href, href_list)
if(..())
- return
+ return 1
if (href_list["fix"])
src.active = 1
src.overlays += image('icons/obj/computer.dmi', "ai-fixer-on")
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index a3402129d88..7831bfc8f87 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -31,6 +31,8 @@
/obj/item/toy/carpplushie = 2,
/obj/item/toy/minimeteor = 2,
/obj/item/toy/redbutton = 2,
+ /obj/item/toy/owl = 2,
+ /obj/item/toy/griffin = 2,
/obj/item/clothing/head/blob = 2,
/obj/item/weapon/id_decal/gold = 2,
/obj/item/weapon/id_decal/silver = 2,
@@ -144,7 +146,10 @@
/obj/machinery/computer/arcade/battle/Topic(href, href_list)
if(..())
- return
+ return 1
+
+ if(usr.machine != src)
+ return 0
if (!src.blocked && !src.gameover)
if (href_list["attack"])
@@ -269,8 +274,8 @@
return
-/obj/machinery/computer/arcade/battle/attackby(I as obj, user as mob)
- if(istype(I, /obj/item/weapon/card/emag) && !emagged)
+/obj/machinery/computer/arcade/battle/emag_act(user as mob)
+ if(!emagged)
temp = "If you die in the game, you die for real!"
player_hp = 30
player_mp = 10
@@ -284,14 +289,7 @@
enemy_name = "Cuban Pete"
name = "Outbomb Cuban Pete"
-
src.updateUsrDialog()
- else
-
- ..()
-
-
-
/obj/machinery/computer/arcade/orion_trail
name = "The Orion Trail"
@@ -405,7 +403,9 @@
/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list)
if(..())
- return
+ return 1
+ if(usr.machine != src)
+ return 0
if(href_list["close"])
usr.unset_machine()
usr << browse(null, "window=arcade")
@@ -566,4 +566,5 @@
/obj/machinery/computer/arcade/orion_trail/proc/win()
playing = 0
+ turns = 0
prizevend()
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 9a0ff286700..5e9bb34866b 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -23,8 +23,10 @@
var/zone = signal.data["zone"]
var/severity = signal.data["alert"]
+ var/hidden = signal.data["hidden"]
if(!zone || !severity) return
+ if(hidden) return
minor_alarms -= zone
priority_alarms -= zone
@@ -98,7 +100,7 @@
/obj/machinery/computer/atmos_alert/Topic(href, href_list)
if(..())
- return
+ return 1
if(href_list["priority_clear"])
var/removing_zone = href_list["priority_clear"]
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index df9a3cbef9e..bce0006f488 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -37,15 +37,16 @@
return ui_interact(user)
-/obj/machinery/computer/atmoscontrol/attackby(var/obj/item/I as obj, var/mob/user as mob)
- if(istype(I, /obj/item/weapon/card/emag) && !emagged)
- user.visible_message("\red \The [user] swipes \a [I] through \the [src], causing the screen to flash!",\
- "\red You swipe your [I] through \the [src], the screen flashing as you gain full control.",\
+/obj/machinery/computer/atmoscontrol/emag_act(user as mob)
+ if(!emagged)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+ H.visible_message("\red \The [user] swipes \a card through \the [src], causing the screen to flash!",\
+ "\red You swipe your card through \the [src], the screen flashing as you gain full control.",\
"You hear the swipe of a card through a reader, and an electronic warble.")
emagged = 1
overridden = 1
- return
- return ..()
/obj/machinery/computer/atmoscontrol/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
if(user.stat && !isobserver(user))
@@ -115,7 +116,7 @@
//a bunch of this is copied from atmos alarms
/obj/machinery/computer/atmoscontrol/Topic(href, href_list)
if(..())
- return
+ return 1
if(href_list["reset"])
current = null
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index eb8efc36264..23743ffc10c 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -233,7 +233,7 @@
board_type = "honkcomputer"
-/obj/item/weapon/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob)
+/obj/item/weapon/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I,/obj/item/device/multitool))
var/catastasis = src.contraband_enabled
var/opposite_catastasis
@@ -255,7 +255,7 @@
user << "DERP! BUG! Report this (And what you were doing to cause it) to Agouri"
return
-/obj/item/weapon/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob)
+/obj/item/weapon/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I,/obj/item/weapon/screwdriver))
user.visible_message("\blue \the [user] adjusts the jumper on the [src]'s access protocol pins.", "\blue You adjust the jumper on the access protocol pins.")
if(src.build_path == "/obj/machinery/computer/rdconsole/core")
@@ -268,7 +268,7 @@
user << "\blue Access protocols set to default."
return
-/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob)
+/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params)
switch(state)
if(0)
if(istype(P, /obj/item/weapon/wrench))
@@ -379,7 +379,7 @@
name = "Bananium Computer-frame"
icon = 'icons/obj/machines/HONKputer.dmi'
-/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob)
+/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params)
switch(state)
if(0)
if(istype(P, /obj/item/weapon/wrench))
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 7bc376580b8..8370dd40f3c 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -1,3 +1,7 @@
+/proc/invalidateCameraCache()
+ for(var/obj/machinery/computer/security/s in world)
+ s.camera_cache = null
+
/obj/machinery/computer/security
name = "Camera Monitor"
desc = "Used to access the various cameras networks on the station."
@@ -12,6 +16,7 @@
var/list/tempnets[0]
var/list/data[0]
var/list/access[0]
+ var/camera_cache = null
New() // Lists existing networks and their required access. Format: networks[] = list()
networks["SS13"] = list(access_hos,access_captain)
@@ -48,27 +53,23 @@
return 1
// Network configuration
- attackby(I as obj, user as mob)
+ attackby(I as obj, user as mob, params)
access = list()
- if(istype(I,/obj/item/weapon/card/emag)) // If hit by an emag.
- var/obj/item/weapon/card/emag/E = I
- if(!emagged)
- if(E.uses)
- E.uses--
- emagged = 1
- user << "\blue You have authorized full network access!"
- ui_interact(user)
- else
- ui_interact(user)
- else
- ui_interact(user)
- else if(istype(I,/obj/item/weapon/card/id)) // If hit by a regular ID card.
+ if(istype(I,/obj/item/weapon/card/id)) // If hit by a regular ID card.
var/obj/item/weapon/card/id/E = I
access = E.access
ui_interact(user)
else
..()
+ emag_act(user as mob)
+ if(!emagged)
+ emagged = 1
+ user << "\blue You have authorized full network access!"
+ ui_interact(user)
+ else
+ ui_interact(user)
+
ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(src.z > 6) return
if(stat & (NOPOWER|BROKEN)) return
@@ -76,14 +77,15 @@
var/data[0]
+
data["current"] = null
var/list/L = list()
- for (var/obj/machinery/camera/C in cameranet.viewpoints)
+ for (var/obj/machinery/camera/C in cameranet.cameras)
if(can_access_camera(C))
L.Add(C)
- camera_sort(L)
+ cameranet.process_sort()
var/cameras[0]
for(var/obj/machinery/camera/C in L)
@@ -106,7 +108,7 @@
if(emagged)
access = list(access_captain) // Assume captain level access when emagged
data["emagged"] = 1
- if(isAI(user) || isrobot (user))
+ if(isAI(user) || isrobot(user))
access = list(access_captain) // Assume captain level access when AI
// Loop through the ID's permission, and check which networks the ID has access to.
@@ -119,7 +121,7 @@
tempnets.Add(list(list("name" = l, "active" = 0)))
break
data["networks"] = tempnets
-
+
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800)
@@ -137,7 +139,7 @@
if(href_list["switchTo"])
if(src.z>6 || stat&(NOPOWER|BROKEN)) return
if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return
- var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.viewpoints
+ var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
if(!C) return
switch_to_camera(usr, C)
@@ -197,6 +199,9 @@
else
if(isAI(user))
var/mob/living/silicon/ai/A = user
+ // Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise.
+ if(!A.is_in_chassis())
+ return 0
A.eyeobj.setLoc(get_turf(C))
A.client.eye = A.eyeobj
else
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 6298f0d7d89..73931aa0183 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -55,7 +55,7 @@
usr << "There is nothing to remove from the console."
return
-/obj/machinery/computer/card/attackby(obj/item/weapon/card/id/id_card, mob/user)
+/obj/machinery/computer/card/attackby(obj/item/weapon/card/id/id_card, mob/user, params)
if(!istype(id_card))
return ..()
@@ -198,7 +198,7 @@
if (is_authenticated() && modify)
var/t1 = href_list["assign_target"]
if(t1 == "Custom")
- var/temp_t = copytext(sanitize(input("Enter a custom job assignment.","Assignment")),1,MAX_MESSAGE_LEN)
+ var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN))
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t && modify)
modify.assignment = temp_t
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index c8ae03e2c26..bf28014c6e9 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -73,7 +73,7 @@
return podf
-/obj/machinery/computer/cloning/attackby(obj/item/W as obj, mob/user as mob)
+/obj/machinery/computer/cloning/attackby(obj/item/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/disk/data)) //INSERT SOME DISKETTES
if (!src.diskette)
user.drop_item()
@@ -155,7 +155,7 @@
/obj/machinery/computer/cloning/Topic(href, href_list)
if(..())
- return
+ return 1
if(loading)
return
@@ -329,7 +329,7 @@
scantemp = "Error: Mental interface failure."
nanomanager.update_uis(src)
return
- if ((M_NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
+ if ((NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
scantemp = "Error: Mental interface failure."
nanomanager.update_uis(src)
return
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index e4ecdb12efa..e5aeabf72ae 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -55,14 +55,20 @@ var/shuttle_call/shuttle_calls[0]
var/stat_msg1
var/stat_msg2
var/display_type="blank"
+
+ var/datum/announcement/priority/crew_announcement = new
l_color = "#0000FF"
+
+/obj/machinery/computer/communications/New()
+ ..()
+ crew_announcement.newscast = 1
/obj/machinery/computer/communications/Topic(href, href_list)
if(..(href, href_list))
- return
+ return 1
- if (!(src.z in list(STATION_Z,CENTCOMM_Z)))
+ if ((!(src.z in config.station_levels) && !(src.z in config.admin_levels)))
usr << "\red Unable to establish a connection: \black You're too far away from the station!"
return
@@ -83,10 +89,12 @@ var/shuttle_call/shuttle_calls[0]
if (I && istype(I))
if(src.check_access(I))
authenticated = 1
- if(20 in I.access)
+ if(access_captain in I.access)
authenticated = 2
+ crew_announcement.announcer = GetNameAndAssignmentFromId(I)
if("logout")
authenticated = 0
+ crew_announcement.announcer = ""
setMenuState(usr,COMM_SCREEN_MAIN)
// ALART LAVUL
@@ -127,14 +135,14 @@ var/shuttle_call/shuttle_calls[0]
usr << "You need to swipe your ID."
if("announce")
- if(src.authenticated==2 && !issilicon(usr))
- if(message_cooldown) return
- var/input = stripped_input(usr, "Please choose a message to announce to the station crew.", "What?")
+ if(src.authenticated==2)
+ if(message_cooldown)
+ usr << "Please allow at least one minute to pass between announcements"
+ return
+ var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement")
if(!input || !(usr in view(1,src)))
return
- captain_announce(input)//This should really tell who is, IE HoP, CE, HoS, RD, Captain
- log_say("[key_name(usr)] has made a captain announcement: [input]")
- message_admins("[key_name_admin(usr)] has made a captain announcement.", 1)
+ crew_announcement.Announce(input)
message_cooldown = 1
spawn(600)//One minute cooldown
message_cooldown = 0
@@ -204,7 +212,7 @@ var/shuttle_call/shuttle_calls[0]
if(centcomm_message_cooldown)
usr << "Arrays recycling. Please stand by."
return
- var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")
+ var/input = input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")
if(!input || !(usr in view(1,src)))
return
Centcomm_announce(input, usr)
@@ -240,12 +248,11 @@ var/shuttle_call/shuttle_calls[0]
return 1
-/obj/machinery/computer/communications/attackby(var/obj/I as obj, var/mob/user as mob)
- if(istype(I,/obj/item/weapon/card/emag/))
+/obj/machinery/computer/communications/emag_act(user as mob)
+ if(!emagged)
src.emagged = 1
user << "You scramble the communication routing circuits!"
- ..()
-
+
/obj/machinery/computer/communications/attack_ai(var/mob/user as mob)
return src.attack_hand(user)
@@ -399,7 +406,7 @@ var/shuttle_call/shuttle_calls[0]
return
if(emergency_shuttle.going_to_centcom())
- user << "The shuttle may not be called while returning to CentCom."
+ user << "The shuttle may not be called while returning to Central Command."
return
if(emergency_shuttle.online())
@@ -409,11 +416,11 @@ var/shuttle_call/shuttle_calls[0]
// if force is 0, some things may stop the shuttle call
if(!force)
if(emergency_shuttle.deny_shuttle)
- user << "Centcom does not currently have a shuttle available in your sector. Please try again later."
+ user << "Central Command does not currently have a shuttle available in your sector. Please try again later."
return
if(sent_strike_team == 1)
- user << "Centcom will not allow the shuttle to be called. Consider all contracts terminated."
+ user << "Central Command will not allow the shuttle to be called. Consider all contracts terminated."
return
if(world.time < 54000) // 30 minute grace period to let the game get going
@@ -427,8 +434,6 @@ var/shuttle_call/shuttle_calls[0]
emergency_shuttle.call_transfer()
log_game("[key_name(user)] has called the shuttle.")
message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1)
- captain_announce("A crew transfer has been initiated. The shuttle has been called. It will arrive in [round(emergency_shuttle.estimate_arrival_time()/60)] minutes.")
-
return
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index 9dab90e6ed3..14011f549b5 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -102,8 +102,20 @@
stat |= BROKEN
update_icon()
+/obj/machinery/computer/proc/decode(text)
+ // Adds line breaks
+ text = replacetext(text, "\n", " ")
+ return text
+
+/obj/machinery/computer/attack_ghost(user as mob)
+ return src.attack_hand(user)
-/obj/machinery/computer/attackby(I as obj, user as mob)
+/obj/machinery/computer/attack_hand(user as mob)
+ /* Observers can view computers, but not actually use them via Topic*/
+ if(istype(user, /mob/dead/observer)) return 0
+ return ..()
+
+/obj/machinery/computer/attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/weapon/screwdriver) && circuit)
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -127,8 +139,30 @@
src.attack_hand(user)
return
+/obj/machinery/computer/attack_paw(mob/living/user)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
+ if(circuit)
+ if(prob(10))
+ user.visible_message("[user.name] smashes the [src.name] with its paws.",\
+ "You smash the [src.name] with your paws.",\
+ "You hear a smashing sound.")
+ set_broken()
+ return
+ user.visible_message("[user.name] smashes against the [src.name] with its paws.",\
+ "You smash against the [src.name] with your paws.",\
+ "You hear a clicking sound.")
-
-
-
-
+/obj/machinery/computer/attack_alien(mob/living/user)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
+ if(circuit)
+ if(prob(80))
+ user.visible_message("[user.name] smashes the [src.name] with its claws.",\
+ "You smash the [src.name] with your claws.",\
+ "You hear a smashing sound.")
+ set_broken()
+ return
+ user.visible_message("[user.name] smashes against the [src.name] with its claws.",\
+ "You smash against the [src.name] with your claws.",\
+ "You hear a clicking sound.")
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index a007c324ac4..084eb974688 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -1,18 +1,15 @@
/obj/machinery/computer/crew
- name = "Crew Monitoring Computer"
+ name = "crew monitoring computer"
desc = "Used to monitor active health sensors built into most of the crew's uniforms."
icon_state = "crew"
use_power = 1
idle_power_usage = 250
active_power_usage = 500
circuit = "/obj/item/weapon/circuitboard/crew"
- var/list/tracked = list( )
-
- l_color = "#0000FF"
-
+ var/obj/nano_module/crew_monitor/crew_monitor
/obj/machinery/computer/crew/New()
- tracked = list()
+ crew_monitor = new(src)
..()
@@ -27,6 +24,8 @@
return
ui_interact(user)
+/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ crew_monitor.ui_interact(user, ui_key, ui, force_open)
/obj/machinery/computer/crew/update_icon()
@@ -40,99 +39,5 @@
icon_state = initial(icon_state)
stat &= ~NOPOWER
-
-/obj/machinery/computer/crew/Topic(href, href_list)
- if(..()) return
- if (src.z > 6)
- usr << "\red Unable to establish a connection: \black You're too far away from the station!"
- return 0
- if( href_list["close"] )
- var/mob/user = usr
- var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main")
- usr.unset_machine()
- ui.close()
- return 0
- if(href_list["update"])
- src.updateDialog()
- return 1
-
/obj/machinery/computer/crew/interact(mob/user)
- ui_interact(user)
-
-/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(stat & (BROKEN|NOPOWER))
- return
- user.set_machine(src)
- src.scan()
-
- var/data[0]
- var/list/crewmembers = list()
-
- for(var/obj/item/clothing/under/C in src.tracked)
-
-
- var/turf/pos = get_turf(C)
-
- if((C) && (C.has_sensor) && (pos) && (pos.z == src.z) && C.sensor_mode)
- if(istype(C.loc, /mob/living/carbon/human))
-
- var/mob/living/carbon/human/H = C.loc
-
- var/list/crewmemberData = list()
-
- crewmemberData["sensor_type"] = C.sensor_mode
- crewmemberData["dead"] = H.stat > 1
- crewmemberData["oxy"] = round(H.getOxyLoss(), 1)
- crewmemberData["tox"] = round(H.getToxLoss(), 1)
- crewmemberData["fire"] = round(H.getFireLoss(), 1)
- crewmemberData["brute"] = round(H.getBruteLoss(), 1)
-
- crewmemberData["name"] = "Unknown"
- crewmemberData["rank"] = "Unknown"
- if(H.wear_id && istype(H.wear_id, /obj/item/weapon/card/id) )
- var/obj/item/weapon/card/id/I = H.wear_id
- crewmemberData["name"] = I.name
- crewmemberData["rank"] = I.rank
- else if(H.wear_id && istype(H.wear_id, /obj/item/device/pda) )
- var/obj/item/device/pda/P = H.wear_id
- crewmemberData["name"] = (P.id ? P.id.name : "Unknown")
- crewmemberData["rank"] = (P.id ? P.id.rank : "Unknown")
- var/area/A = get_area(H)
- crewmemberData["area"] = sanitize(A.name)
- crewmemberData["x"] = pos.x
- crewmemberData["y"] = pos.y
-
- // Works around list += list2 merging lists; it's not pretty but it works
- crewmembers += "temporary item"
- crewmembers[crewmembers.len] = crewmemberData
-
- crewmembers = sortByKey(crewmembers, "name")
-
- data["crewmembers"] = crewmembers
-
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800)
-
- // adding a template with the key "mapContent" enables the map ui functionality
- ui.add_template("mapContent", "crew_monitor_map_content.tmpl")
- // adding a template with the key "mapHeader" replaces the map header content
- ui.add_template("mapHeader", "crew_monitor_map_header.tmpl")
-
- // we want to show the map by default
- ui.set_show_map(1)
-
- ui.set_initial_data(data)
- ui.open()
-
- // should make the UI auto-update; doesn't seem to?
- ui.set_auto_update(1)
-
-
-/obj/machinery/computer/crew/proc/scan()
- for(var/mob/living/carbon/human/H in mob_list)
- if(istype(H.w_uniform, /obj/item/clothing/under))
- var/obj/item/clothing/under/C = H.w_uniform
- if (C.has_sensor)
- tracked |= C
- return 1
+ crew_monitor.ui_interact(user)
\ No newline at end of file
diff --git a/code/game/machinery/computer/hologram.dm b/code/game/machinery/computer/hologram.dm
index e57aa56fe78..ecb3ba58b3d 100644
--- a/code/game/machinery/computer/hologram.dm
+++ b/code/game/machinery/computer/hologram.dm
@@ -57,7 +57,7 @@
/obj/machinery/computer/hologram_comp/Topic(href, href_list)
if(..())
- return
+ return 1
if (in_range(src, usr))
flick("holo_console1", src)
if (href_list["power"])
diff --git a/code/game/machinery/computer/honkputer.dm b/code/game/machinery/computer/honkputer.dm
index a5efb1dd2a9..bd9d0344a2c 100644
--- a/code/game/machinery/computer/honkputer.dm
+++ b/code/game/machinery/computer/honkputer.dm
@@ -16,8 +16,8 @@
/obj/machinery/computer/HONKputer/Topic(href, href_list)
if(..())
- return
- if (src.z > 1)
+ return 1
+ if (!(src.z in config.station_levels))
usr << "\red Unable to establish a connection: \black You're too far away from the station!"
return
usr.set_machine(src)
@@ -57,12 +57,10 @@
src.updateUsrDialog()
-/obj/machinery/computer/HONKputer/attackby(var/obj/I as obj, var/mob/user as mob)
- if(istype(I,/obj/item/weapon/card/emag/))
+/obj/machinery/computer/HONKputer/emag_act(user as mob)
+ if(!emagged)
src.emagged = 1
user << "You scramble the login circuits, allowing anyone to use the console!"
- ..()
-
/obj/machinery/computer/HONKputer/attack_hand(var/mob/user as mob)
if(..())
@@ -92,7 +90,7 @@
onclose(user, "honkputer")
-/obj/machinery/computer/HONKputer/attackby(I as obj, user as mob)
+/obj/machinery/computer/HONKputer/attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/weapon/screwdriver) && circuit)
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm
index 47a0951b20e..4802e855af2 100644
--- a/code/game/machinery/computer/law.dm
+++ b/code/game/machinery/computer/law.dm
@@ -26,7 +26,7 @@
return
- attackby(obj/item/weapon/O as obj, mob/user as mob)
+ attackby(obj/item/weapon/O as obj, mob/user as mob, params)
if (user.z > 6)
user << "\red Unable to establish a connection: \black You're too far away from the station!"
return
@@ -53,7 +53,8 @@
usr << "[src.current.name] selected for law changes."
return
-
+ attack_ghost(user as mob)
+ return 1
/obj/machinery/computer/borgupload
name = "Cyborg Upload"
@@ -63,7 +64,7 @@
var/mob/living/silicon/robot/current = null
- attackby(obj/item/weapon/aiModule/module as obj, mob/user as mob)
+ attackby(obj/item/weapon/aiModule/module as obj, mob/user as mob, params)
if(istype(module, /obj/item/weapon/aiModule))
module.install(src)
else
@@ -85,3 +86,6 @@
else
usr << "[src.current.name] selected for law changes."
return
+
+ attack_ghost(user as mob)
+ return 1
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index 1d9cee6a55b..23a8c3ed16f 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -128,7 +128,7 @@
/obj/machinery/computer/med_data/Topic(href, href_list)
if(..())
- return
+ return 1
if (!( data_core.general.Find(src.active1) ))
src.active1 = null
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index 73c469d6db4..3cf15c5308a 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -36,29 +36,11 @@
l_color = "#50AB00"
-/obj/machinery/computer/message_monitor/attackby(obj/item/weapon/O as obj, mob/living/user as mob)
+/obj/machinery/computer/message_monitor/attackby(obj/item/weapon/O as obj, mob/living/user as mob, params)
if(stat & (NOPOWER|BROKEN))
return
if(!istype(user))
return
- if(istype(O,/obj/item/weapon/card/emag/))
- // Will create sparks and print out the console's password. You will then have to wait a while for the console to be back online.
- // It'll take more time if there's more characters in the password..
- if(!emag)
- if(!isnull(src.linkedServer))
- icon_state = hack_icon // An error screen I made in the computers.dmi
- emag = 1
- screen = 2
- spark_system.set_up(5, 0, src)
- src.spark_system.start()
- var/obj/item/weapon/paper/monitorkey/MK = new/obj/item/weapon/paper/monitorkey
- MK.loc = src.loc
- // Will help make emagging the console not so easy to get away with.
- MK.info += "
£%@%(*$%&(£&?*(%&£/{}"
- spawn(100*length(src.linkedServer.decryptkey)) UnmagConsole()
- message = rebootmsg
- else
- user << "A no server error appears on the screen."
if(isscrewdriver(O) && emag)
//Stops people from just unscrewing the monitor and putting it back to get the console working again.
user << "It is too hot to mess with!"
@@ -66,6 +48,25 @@
..()
return
+
+/obj/machinery/computer/message_monitor/emag_act(user as mob)
+ // Will create sparks and print out the console's password. You will then have to wait a while for the console to be back online.
+ // It'll take more time if there's more characters in the password..
+ if(!emag)
+ if(!isnull(src.linkedServer))
+ icon_state = hack_icon // An error screen I made in the computers.dmi
+ emag = 1
+ screen = 2
+ spark_system.set_up(5, 0, src)
+ src.spark_system.start()
+ var/obj/item/weapon/paper/monitorkey/MK = new/obj/item/weapon/paper/monitorkey
+ MK.loc = src.loc
+ // Will help make emagging the console not so easy to get away with.
+ MK.info += "
£%@%(*$%&(£&?*(%&£/{}"
+ spawn(100*length(src.linkedServer.decryptkey)) UnmagConsole()
+ message = rebootmsg
+ else
+ user << "A no server error appears on the screen."
/obj/machinery/computer/message_monitor/update_icon()
..()
@@ -274,7 +275,7 @@
/obj/machinery/computer/message_monitor/Topic(href, href_list)
if(..())
- return
+ return 1
if(stat & (NOPOWER|BROKEN))
return
if(!istype(usr, /mob/living))
@@ -421,7 +422,7 @@
//Enter message
if("Message")
custommessage = input(usr, "Please enter your message.") as text|null
- custommessage = copytext(sanitize(custommessage), 1, MAX_MESSAGE_LEN)
+ custommessage = sanitize(copytext(custommessage, 1, MAX_MESSAGE_LEN))
//Send message
if("Send")
diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm
index 2f408817802..69c68ecf8aa 100644
--- a/code/game/machinery/computer/pod.dm
+++ b/code/game/machinery/computer/pod.dm
@@ -50,7 +50,7 @@
return
/*
-/obj/machinery/computer/pod/attackby(I as obj, user as mob)
+/obj/machinery/computer/pod/attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/weapon/screwdriver))
playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -163,7 +163,7 @@
/obj/machinery/computer/pod/Topic(href, href_list)
if(..())
- return
+ return 1
if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
if(href_list["power"])
diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm
index 65eb7ce4bc3..06f8cfe5692 100644
--- a/code/game/machinery/computer/power.dm
+++ b/code/game/machinery/computer/power.dm
@@ -23,15 +23,14 @@
if(isturf(T))
attached = locate() in T
if(attached)
- powernet = attached.get_powernet()
-
-
+ powernet = attached.get_powernet()
+
/obj/machinery/power/monitor/attack_ai(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- interact(user)
+ interact(user)
/obj/machinery/power/monitor/attack_hand(mob/user)
add_fingerprint(user)
@@ -40,7 +39,7 @@
return
interact(user)
-/obj/machinery/power/monitor/attackby(I as obj, user as mob)
+/obj/machinery/power/monitor/attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/weapon/screwdriver))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -133,7 +132,8 @@
/obj/machinery/power/monitor/Topic(href, href_list)
- ..()
+ if(..())
+ return 1
if( href_list["close"] )
usr << browse(null, "window=powcomp")
usr.unset_machine()
diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm
index 11b71274ca6..7b4b37665b0 100644
--- a/code/game/machinery/computer/prisoner.dm
+++ b/code/game/machinery/computer/prisoner.dm
@@ -65,7 +65,7 @@
if(!T.implanted) continue
var/loc_display = "Unknown"
var/mob/living/carbon/M = T.imp_in
- if(M.z == 1 && !istype(M.loc, /turf/space))
+ if((M.z in config.station_levels) && !istype(M.loc, /turf/space))
var/turf/mob_loc = get_turf_loc(M)
loc_display = mob_loc.loc
if(T.malfunction)
@@ -129,7 +129,7 @@
usr << "Unauthorized Access."
else if(href_list["warn"])
- var/warning = copytext(sanitize(input(usr,"Message:","Enter your message here!","")),1,MAX_MESSAGE_LEN)
+ var/warning = sanitize(copytext(input(usr,"Message:","Enter your message here!",""),1,MAX_MESSAGE_LEN))
if(!warning) return
var/obj/item/weapon/implant/I = locate(href_list["warn"])
if((I)&&(I.imp_in))
diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm
index 9ce03c4630d..a5837969741 100644
--- a/code/game/machinery/computer/prisonshuttle.dm
+++ b/code/game/machinery/computer/prisonshuttle.dm
@@ -22,7 +22,7 @@ var/prison_shuttle_timeleft = 0
var/prison_break = 0
- attackby(I as obj, user as mob)
+ attackby(I as obj, user as mob, params)
return src.attack_hand(user)
@@ -34,7 +34,7 @@ var/prison_shuttle_timeleft = 0
return src.attack_hand(user)
- attackby(I as obj, user as mob)
+ attackby(I as obj, user as mob, params)
if(istype(I, /obj/item/weapon/screwdriver))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 35552231640..7692b31e3e4 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -106,7 +106,7 @@
/obj/machinery/computer/robotics/Topic(href, href_list)
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
@@ -123,7 +123,7 @@
if (istype(I))
if(src.check_access(I))
if (!status)
- message_admins("\blue [key_name_admin(usr)] has initiated the global cyborg killswitch!")
+ msg_admin_attack("\blue [key_name_admin(usr)] has initiated the global cyborg killswitch!")
log_game("\blue [key_name(usr)] has initiated the global cyborg killswitch!")
use_log += text("\[[time_stamp()]\] [usr.name] ([usr.ckey]) has initiated the global cyborg killswitch!")
src.status = 1
@@ -169,7 +169,7 @@
R.ResetSecurityCodes()
else
- message_admins("\blue [key_name_admin(usr)] detonated [R.name]!")
+ msg_admin_attack("\blue [key_name_admin(usr)] detonated [R.name]!")
log_game("\blue [key_name_admin(usr)] detonated [R.name]!")
use_log += text("\[[time_stamp()]\] [usr.name] ([usr.ckey]) detonated [R.name] ([R.ckey])!")
R.self_destruct()
@@ -183,7 +183,7 @@
var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
if(choice == "Confirm")
if(R && istype(R))
- message_admins("\blue [key_name_admin(usr)] [R.canmove ? "locked down" : "released"] [R.name]!")
+ msg_admin_attack("\blue [key_name_admin(usr)] [R.canmove ? "locked down" : "released"] [R.name]!")
log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [R.name]!")
R.canmove = !R.canmove
if (R.lockcharge)
@@ -208,7 +208,7 @@
var/choice = input("Are you certain you wish to hack [R.name]?") in list("Confirm", "Abort")
if(choice == "Confirm")
if(R && istype(R))
-// message_admins("\blue [key_name_admin(usr)] emagged [R.name] using robotic console!")
+ msg_admin_attack("\blue [key_name_admin(usr)] emagged [R.name] using robotic console!")
log_game("[key_name(usr)] emagged [R.name] using robotic console!")
R.emagged = 1
if(R.hud_used)
diff --git a/code/game/machinery/computer/salvage_ship.dm b/code/game/machinery/computer/salvage_ship.dm
index a54340da4de..5e0feda0c20 100644
--- a/code/game/machinery/computer/salvage_ship.dm
+++ b/code/game/machinery/computer/salvage_ship.dm
@@ -36,7 +36,7 @@
return 1
-/obj/machinery/computer/salvage_ship/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/computer/salvage_ship/attackby(obj/item/I as obj, mob/user as mob, params)
return attack_hand(user)
/obj/machinery/computer/salvage_ship/attack_ai(mob/user as mob)
@@ -75,6 +75,8 @@
/obj/machinery/computer/salvage_ship/Topic(href, href_list)
+ if(..())
+ return 1
if(!isliving(usr)) return
var/mob/living/user = usr
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index ce9b239da49..1094ede2762 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -25,7 +25,7 @@
l_color = "#B40000"
-/obj/machinery/computer/secure_data/attackby(obj/item/O as obj, user as mob)
+/obj/machinery/computer/secure_data/attackby(obj/item/O as obj, user as mob, params)
if(istype(O, /obj/item/weapon/card/id) && !scan)
usr.drop_item()
O.loc = src
@@ -208,7 +208,7 @@ I can't be bothered to look more of the actual code outside of switch but that p
What a mess.*/
/obj/machinery/computer/secure_data/Topic(href, href_list)
if(..())
- return
+ return 1
if (!( data_core.general.Find(active1) ))
active1 = null
if (!( data_core.security.Find(active2) ))
diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm
index 0815cf7932a..ee4a88fb89b 100644
--- a/code/game/machinery/computer/shuttle.dm
+++ b/code/game/machinery/computer/shuttle.dm
@@ -8,7 +8,7 @@
l_color = "#7BF9FF"
- attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob)
+ attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob, params)
if(stat & (BROKEN|NOPOWER)) return
if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return
if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
@@ -56,11 +56,13 @@
world << "\blue All authorizations to shortening time for shuttle launch have been revoked!"
src.authorized.len = 0
src.authorized = list( )
-
- else if (istype(W, /obj/item/weapon/card/emag) && !emagged)
+ return
+
+ emag_act(user as mob)
+ if (!emagged)
var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "Launch", "Cancel")
- if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W)
+ if(!emagged && !emergency_shuttle.location())
switch(choice)
if("Launch")
world << "\blue Alert: Shuttle launch time shortened to 10 seconds!"
@@ -68,4 +70,3 @@
emagged = 1
if("Cancel")
return
- return
diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm
index 3b6a530f490..9f1781519ae 100644
--- a/code/game/machinery/computer/skills.dm
+++ b/code/game/machinery/computer/skills.dm
@@ -22,7 +22,7 @@
var/order = 1 // -1 = Descending - 1 = Ascending
-/obj/machinery/computer/skills/attackby(obj/item/O as obj, user as mob)
+/obj/machinery/computer/skills/attackby(obj/item/O as obj, user as mob, params)
if(istype(O, /obj/item/weapon/card/id) && !scan)
usr.drop_item()
O.loc = src
@@ -151,7 +151,7 @@ I can't be bothered to look more of the actual code outside of switch but that p
What a mess.*/
/obj/machinery/computer/skills/Topic(href, href_list)
if(..())
- return
+ return 1
if (!( data_core.general.Find(active1) ))
active1 = null
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon)))
diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm
index bdfa06eeba4..68ded19320d 100644
--- a/code/game/machinery/computer/specops_shuttle.dm
+++ b/code/game/machinery/computer/specops_shuttle.dm
@@ -252,7 +252,7 @@ var/specops_shuttle_timeleft = 0
/obj/machinery/computer/specops_shuttle/attack_paw(var/mob/user as mob)
return attack_hand(user)
-/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob)
+/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params)
if(istype(I,/obj/item/weapon/card/emag))
user << "\blue The electronic systems in this console are far too advanced for your primitive hacking peripherals."
else
@@ -287,7 +287,7 @@ var/specops_shuttle_timeleft = 0
/obj/machinery/computer/specops_shuttle/Topic(href, href_list)
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.machine = src
diff --git a/code/game/machinery/computer/store.dm b/code/game/machinery/computer/store.dm
index 31981544e62..9ab45f15d94 100644
--- a/code/game/machinery/computer/store.dm
+++ b/code/game/machinery/computer/store.dm
@@ -128,7 +128,7 @@ td.cost.toomuch {
/obj/machinery/computer/merch/Topic(href, href_list)
if(..())
- return
+ return 1
//testing(href)
diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm
index a095dc495f0..288fa2dfdf6 100644
--- a/code/game/machinery/computer/syndicate_specops_shuttle.dm
+++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm
@@ -176,7 +176,7 @@ var/syndicate_elite_shuttle_timeleft = 0
if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0
else return 1
-/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob)
+/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params)
return attack_hand(user)
/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob)
@@ -186,7 +186,7 @@ var/syndicate_elite_shuttle_timeleft = 0
/obj/machinery/computer/syndicate_elite_shuttle/attack_paw(var/mob/user as mob)
return attack_hand(user)
-/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob)
+/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params)
if(istype(I,/obj/item/weapon/card/emag))
user << "\blue The electronic systems in this console are far too advanced for your primitive hacking peripherals."
else
@@ -220,7 +220,7 @@ var/syndicate_elite_shuttle_timeleft = 0
/obj/machinery/computer/syndicate_elite_shuttle/Topic(href, href_list)
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm
index ae076b34349..f65ad4aae49 100644
--- a/code/game/machinery/computer/telecrystalconsoles.dm
+++ b/code/game/machinery/computer/telecrystalconsoles.dm
@@ -29,7 +29,7 @@ var/list/possible_uplinker_IDs = list("Alfa","Bravo","Charlie","Delta","Echo","F
name = "[name] [rand(1,999)]"
-/obj/machinery/computer/telecrystals/uplinker/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/computer/telecrystals/uplinker/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item))
if(uplinkholder)
@@ -106,7 +106,7 @@ var/list/possible_uplinker_IDs = list("Alfa","Bravo","Charlie","Delta","Echo","F
/obj/machinery/computer/telecrystals/uplinker/Topic(href, href_list)
if(..())
- return
+ return 1
if(href_list["donate1"])
donateTC(1)
diff --git a/code/game/machinery/computer/xenos_shuttle.dm b/code/game/machinery/computer/xenos_shuttle.dm
index 1944274965a..343c2717b76 100644
--- a/code/game/machinery/computer/xenos_shuttle.dm
+++ b/code/game/machinery/computer/xenos_shuttle.dm
@@ -85,7 +85,7 @@
return 1
-/obj/machinery/computer/xenos_station/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/computer/xenos_station/attackby(obj/item/I as obj, mob/user as mob, params)
return attack_hand(user)
/obj/machinery/computer/xenos_station/attack_ai(mob/user as mob)
@@ -93,6 +93,9 @@
/obj/machinery/computer/xenos_station/attack_paw(mob/user as mob)
return attack_hand(user)
+
+/obj/machinery/computer/xenos_station/attack_alien(mob/user as mob)
+ return attack_hand(user)
/obj/machinery/computer/xenos_station/attack_hand(mob/user as mob)
if(!allowed(user))
@@ -123,6 +126,9 @@
/obj/machinery/computer/xenos_station/Topic(href, href_list)
+ if(..())
+ return 1
+
if(!isliving(usr)) return
var/mob/living/user = usr
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index c108e02822c..546b5956f0b 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -92,7 +92,7 @@
else
desc += "."
-/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P as obj, mob/user as mob)
+/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P as obj, mob/user as mob, params)
if(P.crit_fail)
user << "This part is faulty, you cannot add this to the machine!"
return
@@ -270,7 +270,7 @@ to destroy them and players will be able to make replacements.
/obj/machinery/vending/suitdispenser = "Suitlord 9000",
/obj/machinery/vending/shoedispenser = "Shoelord 9000")
-/obj/item/weapon/circuitboard/vendor/attackby(obj/item/I, mob/user)
+/obj/item/weapon/circuitboard/vendor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/screwdriver))
set_type(pick(names_paths), user)
@@ -289,7 +289,7 @@ to destroy them and players will be able to make replacements.
frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor."
req_components = list(
/obj/item/stack/cable_coil = 5,
- /obj/item/weapon/cell = 5,
+ /obj/item/weapon/stock_parts/cell = 5,
/obj/item/weapon/stock_parts/capacitor = 1)
@@ -306,7 +306,7 @@ to destroy them and players will be able to make replacements.
/obj/item/stack/cable_coil = 1,
/obj/item/weapon/stock_parts/console_screen = 1)
-/obj/item/weapon/circuitboard/thermomachine/attackby(obj/item/I, mob/user)
+/obj/item/weapon/circuitboard/thermomachine/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/screwdriver))
if(build_path == /obj/machinery/atmospherics/unary/cold_sink/freezer)
build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater
@@ -361,7 +361,7 @@ to destroy them and players will be able to make replacements.
/obj/item/weapon/stock_parts/capacitor = 1,
/obj/item/weapon/stock_parts/manipulator = 1,
/obj/item/weapon/stock_parts/console_screen = 1,
- /obj/item/weapon/cell = 1)
+ /obj/item/weapon/stock_parts/cell = 1)
/obj/item/weapon/circuitboard/destructive_analyzer
name = "Circuit board (Destructive Analyzer)"
@@ -583,7 +583,7 @@ obj/item/weapon/circuitboard/rdserver
frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator."
req_components = list(
/obj/item/weapon/stock_parts/capacitor = 2,
- /obj/item/weapon/cell = 1,
+ /obj/item/weapon/stock_parts/cell = 1,
/obj/item/weapon/stock_parts/manipulator = 1)
// Telecomms circuit boards:
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index dc738ad2139..0d92be24127 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -5,6 +5,7 @@
density = 1
anchored = 1.0
layer = 2.8
+ interact_offline = 1
var/on = 0
var/temperature_archived
@@ -77,7 +78,7 @@
return
if(user.restrained() || user.stat || user.weakened || user.stunned || user.paralysis || user.resting) //are you cuffed, dying, lying, stunned or other
return
- if(O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source
+ if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source
return
if(!ismob(O)) //humans only
return
@@ -125,7 +126,6 @@
if(!node)
return
if(!on)
- updateUsrDialog()
return
if(occupant)
@@ -140,7 +140,6 @@
if(abs(temperature_archived-air_contents.temperature) > 1)
network.update = 1
- updateUsrDialog()
return 1
@@ -220,12 +219,14 @@
for(var/datum/reagent/R in beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
+ data["autoeject"] = autoeject
+
// update the ui if it exists, returns null if no ui is passed/found
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
- ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 410)
+ ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 420)
// when the ui is first opened this is the data it will use
ui.set_initial_data(data)
// open the new ui window
@@ -267,7 +268,7 @@
add_fingerprint(usr)
return 1 // update UIs attached to this object
-/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob)
+/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob, params)
if(istype(G, /obj/item/weapon/reagent_containers/glass))
if(beaker)
user << "\red A beaker is already loaded into the machine."
@@ -303,7 +304,6 @@
var/mob/M = G:affecting
if(put_mob(M))
del(G)
- updateUsrDialog()
return
/obj/machinery/atmospherics/unary/cryo_cell/update_icon()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 91dbbae40b3..dec81660ef4 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -65,9 +65,8 @@
onclose(user, "cryopod_console")
/obj/machinery/computer/cryopod/Topic(href, href_list)
-
if(..())
- return
+ return 1
var/mob/user = usr
@@ -200,7 +199,7 @@
/obj/item/clothing/shoes/magboots,
/obj/item/blueprints,
/obj/item/clothing/head/helmet/space,
- /obj/item/weapon/tank
+ /obj/item/weapon/storage/internal
)
/obj/machinery/cryopod/right
@@ -284,7 +283,7 @@
/obj/machinery/cryopod/robot/despawn_occupant()
var/mob/living/silicon/robot/R = occupant
if(!istype(R)) return ..()
-
+
R.contents -= R.mmi
del(R.mmi)
for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc
@@ -300,7 +299,7 @@
/obj/machinery/cryopod/proc/despawn_occupant()
//Drop all items into the pod.
for(var/obj/item/W in occupant)
- occupant.drop_from_inventory(W)
+ occupant.unEquip(W)
W.loc = src
if(W.contents.len) //Make sure we catch anything not handled by del() on the items.
@@ -389,7 +388,7 @@
//Make an announcement and log the person entering storage.
control_computer.frozen_crew += "[occupant.real_name]"
-
+
var/ailist[] = list()
for (var/mob/living/silicon/ai/A in living_mob_list)
ailist += A
@@ -398,7 +397,7 @@
announcer.say(";[occupant.real_name] [on_store_message]")
else
announce.autosay("[occupant.real_name] [on_store_message]", "[on_store_name]")
-
+
visible_message("\The [src] hums and hisses as it moves [occupant.real_name] into storage.", 3)
// Delete the mob.
@@ -407,7 +406,7 @@
name = initial(name)
-/obj/machinery/cryopod/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob)
+/obj/machinery/cryopod/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob, params)
if(istype(G, /obj/item/weapon/grab))
@@ -461,7 +460,7 @@
//Despawning occurs when process() is called with an occupant without a client.
src.add_fingerprint(M)
-
+
/obj/machinery/cryopod/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
@@ -469,7 +468,7 @@
return
if(user.restrained() || user.stat || user.weakened || user.stunned || user.paralysis || user.resting) //are you cuffed, dying, lying, stunned or other
return
- if(O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source
+ if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source
return
if(!ismob(O)) //humans only
return
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index c00668a7276..8cde409e649 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -65,7 +65,7 @@ for reference:
var/health = 100.0
var/maxhealth = 100.0
- attackby(obj/item/W as obj, mob/user as mob)
+ attackby(obj/item/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/stack/sheet/wood))
if (src.health < src.maxhealth)
visible_message("\red [user] begins to repair the [src]!")
@@ -157,8 +157,8 @@ for reference:
src.icon_state = "barrier[src.locked]"
- attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/card/id/))
+ attackby(obj/item/weapon/W as obj, mob/user as mob, params)
+ if (istype(W, /obj/item/weapon/card/id))
if (src.allowed(user))
if (src.emagged < 2.0)
src.locked = !src.locked
@@ -177,24 +177,6 @@ for reference:
visible_message("\red BZZzZZzZZzZT")
return
return
- else if (istype(W, /obj/item/weapon/card/emag))
- if (src.emagged == 0)
- src.emagged = 1
- src.req_access = null
- user << "You break the ID authentication lock on the [src]."
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(2, 1, src)
- s.start()
- visible_message("\red BZZzZZzZZzZT")
- return
- else if (src.emagged == 1)
- src.emagged = 2
- user << "You short out the anchoring mechanism on the [src]."
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(2, 1, src)
- s.start()
- visible_message("\red BZZzZZzZZzZT")
- return
else if (istype(W, /obj/item/weapon/wrench))
if (src.health < src.maxhealth)
src.health = src.maxhealth
@@ -218,6 +200,23 @@ for reference:
if (src.health <= 0)
src.explode()
..()
+
+ emag_act(user as mob)
+ if (!emagged)
+ emagged = 1
+ req_access = null
+ user << "You break the ID authentication lock on the [src]."
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(2, 1, src)
+ s.start()
+ visible_message("\red BZZzZZzZZzZT")
+ else if (src.emagged == 1)
+ src.emagged = 2
+ user << "You short out the anchoring mechanism on the [src]."
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(2, 1, src)
+ s.start()
+ visible_message("\red BZZzZZzZZzZT")
ex_act(severity)
switch(severity)
diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm
index 2dcb0c0b2e0..50d6eb49a0f 100644
--- a/code/game/machinery/door_control.dm
+++ b/code/game/machinery/door_control.dm
@@ -40,7 +40,7 @@
/obj/machinery/door_control/attack_paw(mob/user as mob)
return src.attack_hand(user)
-/obj/machinery/door_control/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/door_control/attackby(obj/item/weapon/W, mob/user as mob, params)
/* For later implementation
if (istype(W, /obj/item/weapon/screwdriver))
{
@@ -57,11 +57,14 @@
*/
if(istype(W, /obj/item/device/detective_scanner))
return
- if(istype(W, /obj/item/weapon/card/emag))
+ return src.attack_hand(user)
+
+/obj/machinery/door_control/emag_act(user as mob)
+ if(!emagged)
+ emagged = 1
req_access = list()
req_one_access = list()
playsound(src.loc, "sparks", 100, 1)
- return src.attack_hand(user)
/obj/machinery/door_control/attack_hand(mob/user as mob)
src.add_fingerprint(usr)
@@ -139,7 +142,7 @@
/obj/machinery/driver_button/attack_paw(mob/user as mob)
return src.attack_hand(user)
-/obj/machinery/driver_button/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/driver_button/attackby(obj/item/weapon/W, mob/user as mob, params)
if(istype(W, /obj/item/device/detective_scanner))
return
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 9ec64ff61c5..1582c0cd6a7 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -258,6 +258,10 @@
icon = 'icons/obj/doors/Doorbananium.dmi'
mineral = "clown"
+/obj/machinery/door/airlock/mime
+ name = "Airlock"
+ icon = 'icons/obj/doors/Doorfreezer.dmi'
+
/obj/machinery/door/airlock/sandstone
name = "Sandstone Airlock"
icon = 'icons/obj/doors/Doorsand.dmi'
@@ -298,7 +302,7 @@
user << "You do not know how to operate this airlock's mechanism."
return
-/obj/machinery/door/airlock/alien/attackby(C as obj, mob/user as mob)
+/obj/machinery/door/airlock/alien/attackby(C as obj, mob/user as mob, params)
if(isalien(user) || isrobot(user) || isAI(user))
..(C, user)
else
@@ -358,7 +362,7 @@ About the new airlock wires panel:
return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss()));
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
- if (stat & NOPOWER)
+ if (stat & (NOPOWER|BROKEN))
return 0
return (src.secondsMainPowerLost==0 || src.secondsBackupPowerLost==0)
@@ -473,7 +477,8 @@ About the new airlock wires panel:
if("spark")
flick("door_spark", src)
if("deny")
- flick("door_deny", src)
+ if(density && src.arePowerSystemsOn())
+ flick("door_deny", src)
return
/obj/machinery/door/airlock/attack_ai(mob/user as mob)
@@ -695,16 +700,13 @@ About the new airlock wires panel:
//AI
//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 8 door safties, 9 door speed, 11 emergency access
//aiEnable - 1 idscan, 4 raise door bolts, 5 electrify door for 30 seconds, 6 electrify door indefinitely, 7 open door, 8 door safties, 9 door speed, 11 emergency access
- if(!nowindow)
- ..()
- if(usr.stat || usr.restrained()|| usr.small)
- return
- add_fingerprint(usr)
+ if(..())
+ return 1
if(href_list["close"])
usr << browse(null, "window=airlock")
if(usr.machine==src)
usr.unset_machine()
- return
+ return 1
if((in_range(src, usr) && istype(src.loc, /turf)) && src.p_open)
usr.set_machine(src)
@@ -753,7 +755,7 @@ About the new airlock wires panel:
if(istype(usr, /mob/living/silicon))
if (!check_synth_access(usr))
- return
+ return 1
//AI
//aiDisable - 1 idscan, 2 disrupt main power, 3 disrupt backup power, 4 drop door bolts, 5 un-electrify door, 7 close door, 8 door safties, 9 door speed
@@ -943,9 +945,9 @@ About the new airlock wires panel:
update_icon()
if(!nowindow)
updateUsrDialog()
- return
+ return 0
-/obj/machinery/door/airlock/attackby(C as obj, mob/user as mob)
+/obj/machinery/door/airlock/attackby(C as obj, mob/user as mob, params)
//world << text("airlock attackby src [] obj [] mob []", src, C, user)
if(!istype(usr, /mob/living/silicon))
if(src.isElectrified())
@@ -1031,26 +1033,26 @@ About the new airlock wires panel:
if(density)
if(beingcrowbarred == 0) //being fireaxe'd
var/obj/item/weapon/twohanded/fireaxe/F = C
- if(F:wielded)
+ if(F.wielded)
spawn(0) open(1)
else
- user << "\red You need to be wielding the Fire axe to do that."
+ user << "\red You need to be wielding \the [C] to do that."
else
spawn(0) open(1)
else
if(beingcrowbarred == 0)
var/obj/item/weapon/twohanded/fireaxe/F = C
- if(F:wielded)
+ if(F.wielded)
spawn(0) close(1)
else
- user << "\red You need to be wielding the Fire axe to do that."
+ user << "\red You need to be wielding \the [C] to do that."
else
spawn(0) close(1)
else
..()
return
-/obj/machinery/door/airlock/plasma/attackby(C as obj, mob/user as mob)
+/obj/machinery/door/airlock/plasma/attackby(C as obj, mob/user as mob, params)
if(C)
ignite(is_hot(C))
..()
@@ -1068,6 +1070,7 @@ About the new airlock wires panel:
playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1)
else if(istype(src, /obj/machinery/door/airlock/clown))
playsound(src.loc, 'sound/items/bikehorn.ogg', 30, 1)
+ else if(istype(src, /obj/machinery/door/airlock/mime))
else
playsound(src.loc, 'sound/machines/airlock.ogg', 30, 1)
if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density)
@@ -1104,6 +1107,7 @@ About the new airlock wires panel:
playsound(src.loc, 'sound/machines/windowdoor.ogg', 30, 1)
else if(istype(src, /obj/machinery/door/airlock/clown))
playsound(src.loc, 'sound/items/bikehorn.ogg', 30, 1)
+ else if(istype(src, /obj/machinery/door/airlock/mime))
else
playsound(get_turf(src), 'sound/machines/airlock.ogg', 30, 1)
@@ -1176,7 +1180,7 @@ About the new airlock wires panel:
return
-/obj/machinery/door/airlock/hatch/gamma/attackby(C as obj, mob/user as mob)
+/obj/machinery/door/airlock/hatch/gamma/attackby(C as obj, mob/user as mob, params)
//world << text("airlock attackby src [] obj [] mob []", src, C, user)
if(!istype(usr, /mob/living/silicon))
if(src.isElectrified())
@@ -1209,7 +1213,7 @@ About the new airlock wires panel:
return
-/obj/machinery/door/airlock/highsecurity/red/attackby(C as obj, mob/user as mob)
+/obj/machinery/door/airlock/highsecurity/red/attackby(C as obj, mob/user as mob, params)
//world << text("airlock attackby src [] obj [] mob []", src, C, user)
if(!istype(usr, /mob/living/silicon))
if(src.isElectrified())
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index 498d7449a36..fef0ef7d66b 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -8,11 +8,6 @@ obj/machinery/door/airlock
var/datum/radio_frequency/radio_connection
var/cur_command = null //the command the door is currently attempting to complete
-obj/machinery/door/airlock/proc/can_radio()
- if(!arePowerSystemsOn())
- return 0
- return 1
-
obj/machinery/door/airlock/process()
..()
if (arePowerSystemsOn())
@@ -21,14 +16,13 @@ obj/machinery/door/airlock/process()
obj/machinery/door/airlock/receive_signal(datum/signal/signal)
if (!arePowerSystemsOn()) return //no power
- if (!can_radio()) return //no radio
-
if(!signal || signal.encryption) return
if(id_tag != signal.data["tag"] || !signal.data["command"]) return
cur_command = signal.data["command"]
- execute_current_command()
+ spawn()
+ execute_current_command()
obj/machinery/door/airlock/proc/execute_current_command()
if(operating)
@@ -94,7 +88,7 @@ obj/machinery/door/airlock/proc/command_completed(var/command)
return 1 //Unknown command. Just assume it's completed.
-obj/machinery/door/airlock/proc/send_status()
+obj/machinery/door/airlock/proc/send_status(var/bumped = 0)
if(radio_connection)
var/datum/signal/signal = new
signal.transmission_method = 1 //radio signal
@@ -103,6 +97,9 @@ obj/machinery/door/airlock/proc/send_status()
signal.data["door_status"] = density?("closed"):("open")
signal.data["lock_status"] = locked?("locked"):("unlocked")
+
+ if (bumped)
+ signal.data["bumped_with_access"] = 1
radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
@@ -122,17 +119,7 @@ obj/machinery/door/airlock/Bumped(atom/AM)
if(istype(AM, /obj/mecha))
var/obj/mecha/mecha = AM
if(density && radio_connection && mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access)))
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.data["tag"] = id_tag
- signal.data["timestamp"] = world.time
-
- signal.data["door_status"] = density?("closed"):("open")
- signal.data["lock_status"] = locked?("locked"):("unlocked")
-
- signal.data["bumped_with_access"] = 1
-
- radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
+ send_status(1)
return
obj/machinery/door/airlock/proc/set_frequency(new_frequency)
@@ -258,6 +245,12 @@ obj/machinery/access_button/update_icon()
else
icon_state = "access_button_off"
+obj/machinery/access_button/attackby(obj/item/I as obj, mob/user as mob, params)
+ //Swiping ID on the access button
+ if (istype(I, /obj/item/weapon/card/id) || istype(I, /obj/item/device/pda))
+ attack_hand(user)
+ return
+ ..()
obj/machinery/access_button/attack_hand(mob/user)
add_fingerprint(usr)
@@ -296,4 +289,4 @@ obj/machinery/access_button/airlock_interior
obj/machinery/access_button/airlock_exterior
frequency = 1379
- command = "cycle_exterior"
+ command = "cycle_exterior"
\ No newline at end of file
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 88d72fbf8b9..30122e4d4b7 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -20,6 +20,7 @@
var/heat_proof = 0 // For glass airlocks/opacity firedoors
var/emergency = 0
var/air_properties_vary_with_direction = 0
+ var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
//Multi-tile doors
dir = EAST
@@ -96,7 +97,7 @@
/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group) return 0
+ if(air_group) return !block_air_zones
if(istype(mover) && mover.checkpass(PASSGLASS))
return !opacity
return !density
@@ -143,7 +144,7 @@
return
..()
-/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I, /obj/item/device/detective_scanner))
return
if(src.operating || isrobot(user)) return //borgs can't attack doors open because it conflicts with their AI-like interaction with them.
@@ -153,10 +154,7 @@
if(!src.requiresID())
user = null
if(src.density && (istype(I, /obj/item/weapon/card/emag)||istype(I, /obj/item/weapon/melee/energy/blade)))
- flick("door_spark", src)
- sleep(6)
- open()
- operating = -1
+ emag_act(user)
return 1
if(src.allowed(user) || src.emergency == 1)
if(src.density)
@@ -168,6 +166,13 @@
flick("door_deny", src)
return
+/obj/machinery/door/emag_act(user as mob)
+ if(density)
+ flick("door_spark", src)
+ sleep(6)
+ open()
+ operating = -1
+ return 1
/obj/machinery/door/blob_act()
if(prob(40))
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index a6609c03ec1..968f5b8575e 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -60,6 +60,10 @@
opacity = 0
density = 0
+ //These are frequenly used with windows, so make sure zones can pass.
+ //Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it.
+ block_air_zones = 0
+
var/blocked = 0
var/lockdown = 0 // When the door has detected a problem, it locks.
var/pdiff_alert = 0
@@ -106,7 +110,7 @@
/obj/machinery/door/firedoor/examine()
set src in view()
. = ..()
-
+
if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF)
usr << "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!"
@@ -169,7 +173,7 @@
/obj/machinery/door/firedoor/attack_hand(mob/user as mob)
return attackby(null, user)
-/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob)
+/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob, params)
add_fingerprint(user)
if(operating)
return//Already doing something.
@@ -287,7 +291,7 @@
/obj/machinery/door/firedoor/attack_ai(mob/user as mob)
if(operating)
- return //Already doing something.
+ return //Already doing something.
if(blocked)
user << "\red \The [src] is welded solid!"
@@ -295,11 +299,11 @@
var/area/A = get_area_master(src)
ASSERT(istype(A)) // This worries me.
- var/alarmed = A.air_doors_activated || A.fire
+ var/alarmed = A.air_doors_activated || A.fire
var/access_granted = 0
if(isAI(user) || isrobot(user))
- access_granted = 1
+ access_granted = 1
if(access_granted == 1)
user.visible_message("\blue \The [src] [density ? "open" : "close"]s for \the [user].",\
@@ -320,7 +324,7 @@
spawn(50)
if(alarmed)
nextstate = CLOSED
-
+
// CHECK PRESSURE
/obj/machinery/door/firedoor/process()
..()
diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm
index eeceaf2de18..2495dbfc4bc 100644
--- a/code/game/machinery/doors/poddoor.dm
+++ b/code/game/machinery/doors/poddoor.dm
@@ -25,7 +25,7 @@
else
return 0
-/obj/machinery/door/poddoor/attackby(obj/item/weapon/C as obj, mob/user as mob)
+/obj/machinery/door/poddoor/attackby(obj/item/weapon/C as obj, mob/user as mob, params)
src.add_fingerprint(user)
if (!( istype(C, /obj/item/weapon/crowbar) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1) ))
return
diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm
index 8f54535c8fe..d8f751ec89e 100644
--- a/code/game/machinery/doors/shutters.dm
+++ b/code/game/machinery/doors/shutters.dm
@@ -13,7 +13,7 @@
density = 0
opacity = 0
-/obj/machinery/door/poddoor/shutters/attackby(obj/item/weapon/C as obj, mob/user as mob)
+/obj/machinery/door/poddoor/shutters/attackby(obj/item/weapon/C as obj, mob/user as mob, params)
add_fingerprint(user)
if(!(istype(C, /obj/item/weapon/crowbar) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1) ))
return
diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm
index dce2ab37bda..8e03e874a71 100644
--- a/code/game/machinery/doors/unpowered.dm
+++ b/code/game/machinery/doors/unpowered.dm
@@ -10,7 +10,7 @@
return
- attackby(obj/item/I as obj, mob/user as mob)
+ attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I, /obj/item/weapon/card/emag)||istype(I, /obj/item/weapon/melee/energy/blade)) return
if(src.locked) return
..()
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 84563748126..a8ebd55930a 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -195,12 +195,15 @@
/obj/machinery/door/window/proc/attack_generic(mob/user as mob, damage = 0)
if(src.operating)
return
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
user.visible_message("[user] smashes against the [src.name].", \
"[user] smashes against the [src.name].")
take_damage(damage)
/obj/machinery/door/window/attack_alien(mob/living/user as mob)
+
if(islarva(user))
return
attack_generic(user, 25)
@@ -225,21 +228,13 @@
/obj/machinery/door/window/attack_hand(mob/user as mob)
return src.attackby(user, user)
-/obj/machinery/door/window/attackby(obj/item/weapon/I as obj, mob/living/user as mob)
-
- //If it's in the process of opening/closing, ignore the click
- if (src.operating)
- return
-
- add_fingerprint(user)
-
- //Emags and ninja swords? You may pass.
- if (src.density && (istype(I, /obj/item/weapon/card/emag)||istype(I, /obj/item/weapon/melee/energy/blade)))
+/obj/machinery/door/window/emag_act(user as mob, weapon as obj)
+ if(density)
src.operating = -1
flick("[src.base_state]spark", src)
sleep(6)
desc += " Its access panel is smoking slightly."
- if(istype(I, /obj/item/weapon/melee/energy/blade))
+ if(istype(weapon, /obj/item/weapon/melee/energy/blade))
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
spark_system.set_up(5, 0, src.loc)
spark_system.start()
@@ -252,6 +247,19 @@
open()
emagged = 1
return 1
+
+/obj/machinery/door/window/attackby(obj/item/weapon/I as obj, mob/living/user as mob, params)
+
+ //If it's in the process of opening/closing, ignore the click
+ if (src.operating)
+ return
+
+ add_fingerprint(user)
+
+ //Ninja swords? You may pass.
+ if (src.density && (istype(I, /obj/item/weapon/card/emag)||istype(I, /obj/item/weapon/melee/energy/blade)))
+ emag_act(user,I)
+ return 1
if(istype(I, /obj/item/weapon/screwdriver))
if(src.density || src.operating)
@@ -325,6 +333,8 @@
//If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway)
if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card) )
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
if( (I.flags&NOBLUDGEON) || !I.force )
return
var/aforce = I.force
diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm
index 9866dc54502..840ed1133c2 100644
--- a/code/game/machinery/doppler_array.dm
+++ b/code/game/machinery/doppler_array.dm
@@ -19,7 +19,7 @@ var/list/doppler_arrays = list()
/obj/machinery/doppler_array/process()
return PROCESS_KILL
-/obj/machinery/doppler_array/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/doppler_array/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item/weapon/wrench))
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
anchored = !anchored
diff --git a/code/game/machinery/drying_rack.dm b/code/game/machinery/drying_rack.dm
index d776fc3bef3..f8c76de25ff 100644
--- a/code/game/machinery/drying_rack.dm
+++ b/code/game/machinery/drying_rack.dm
@@ -31,11 +31,11 @@
-/obj/machinery/drying_rack/attackby(var/obj/item/W as obj, var/mob/user as mob)
+/obj/machinery/drying_rack/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
if(is_type_in_list(W,accepted))
if(!running)
if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/meat))
- user.u_equip(W)
+ user.unEquip(W)
del(W)
user << "You add the meat to the drying rack."
src.running = 1
@@ -48,7 +48,7 @@
src.running = 0
return
if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/grown/grapes))
- user.u_equip(W)
+ user.unEquip(W)
del(W)
user << "You add the grapes to the drying rack."
src.running = 1
@@ -61,7 +61,7 @@
src.running = 0
return
if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/grown/greengrapes))
- user.u_equip(W)
+ user.unEquip(W)
del(W)
user << "You add the green grapes to the drying rack."
src.running = 1
@@ -79,7 +79,7 @@
var/obj/item/weapon/reagent_containers/food/snacks/grown/B = W
B.reagents.trans_to(src, B.reagents.total_volume)
user << "You add the [W] to the drying rack."
- user.u_equip(W)
+ user.unEquip(W)
del(W)
src.running = 1
use_power = 2
diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm
index 4a2a9c5e56e..fb909dee555 100644
--- a/code/game/machinery/embedded_controller/airlock_controllers.dm
+++ b/code/game/machinery/embedded_controller/airlock_controllers.dm
@@ -1,13 +1,15 @@
//base type for controllers of two-door systems
/obj/machinery/embedded_controller/radio/airlock
// Setup parameters only
+ radio_filter = RADIO_AIRLOCK
var/tag_exterior_door
var/tag_interior_door
var/tag_airpump
var/tag_chamber_sensor
var/tag_exterior_sensor
var/tag_interior_sensor
- var/tag_mech_sensor
+ var/tag_airlock_mech_sensor
+ var/tag_shuttle_mech_sensor
var/tag_secure = 0
/obj/machinery/embedded_controller/radio/airlock/initialize()
diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm
index 90a2ca57206..8cb3abb9980 100644
--- a/code/game/machinery/embedded_controller/airlock_program.dm
+++ b/code/game/machinery/embedded_controller/airlock_program.dm
@@ -1,8 +1,9 @@
//Handles the control of airlocks
-#define STATE_WAIT 0
-#define STATE_DEPRESSURIZE 1
-#define STATE_PRESSURIZE 2
+#define STATE_IDLE 0
+#define STATE_PREPARE 1
+#define STATE_DEPRESSURIZE 2
+#define STATE_PRESSURIZE 3
#define TARGET_NONE 0
#define TARGET_INOPEN -1
@@ -16,9 +17,10 @@
var/tag_chamber_sensor
var/tag_exterior_sensor
var/tag_interior_sensor
- var/tag_mech_sensor
+ var/tag_airlock_mech_sensor
+ var/tag_shuttle_mech_sensor
- var/state = STATE_WAIT
+ var/state = STATE_IDLE
var/target_state = TARGET_NONE
/datum/computer/file/embedded_program/airlock/New(var/obj/machinery/embedded_controller/M)
@@ -42,7 +44,8 @@
tag_chamber_sensor = controller.tag_chamber_sensor? controller.tag_chamber_sensor : "[id_tag]_sensor"
tag_exterior_sensor = controller.tag_exterior_sensor
tag_interior_sensor = controller.tag_interior_sensor
- tag_mech_sensor = controller.tag_mech_sensor? controller.tag_mech_sensor : "[id_tag]_mech"
+ tag_airlock_mech_sensor = controller.tag_airlock_mech_sensor? controller.tag_airlock_mech_sensor : "[id_tag]_airlock_mech"
+ tag_shuttle_mech_sensor = controller.tag_shuttle_mech_sensor? controller.tag_shuttle_mech_sensor : "[id_tag]_shuttle_mech"
memory["secure"] = controller.tag_secure
spawn(10)
@@ -157,7 +160,7 @@
/datum/computer/file/embedded_program/airlock/process()
- if(!state)
+ if(!state) //Idle
if(target_state)
switch(target_state)
if(TARGET_INOPEN)
@@ -168,38 +171,45 @@
//lock down the airlock before activating pumps
close_doors()
- var/chamber_pressure = memory["chamber_sensor_pressure"]
- var/target_pressure = memory["target_pressure"]
-
- if(memory["purge"])
- target_pressure = 0
-
- if(chamber_pressure <= target_pressure)
- state = STATE_PRESSURIZE
- signalPump(tag_airpump, 1, 1, target_pressure) //send a signal to start pressurizing
-
- else if(chamber_pressure > target_pressure)
- state = STATE_DEPRESSURIZE
- signalPump(tag_airpump, 1, 0, target_pressure) //send a signal to start depressurizing
-
- //Check for vacuum - this is set after the pumps so the pumps are aiming for 0
- if(!memory["target_pressure"])
- memory["target_pressure"] = ONE_ATMOSPHERE * 0.05
+ state = STATE_PREPARE
else
//make sure to return to a sane idle state
if(memory["pump_status"] != "off") //send a signal to stop pumping
signalPump(tag_airpump, 0)
- //the airlock will not allow itself to continue to cycle when any of the doors are forced open.
- if (state && !check_doors_secured())
+ if ((state == STATE_PRESSURIZE || state == STATE_DEPRESSURIZE) && !check_doors_secured())
+ //the airlock will not allow itself to continue to cycle when any of the doors are forced open.
stop_cycling()
switch(state)
+ if(STATE_PREPARE)
+ if (check_doors_secured())
+ var/chamber_pressure = memory["chamber_sensor_pressure"]
+ var/target_pressure = memory["target_pressure"]
+
+ if(memory["purge"])
+ target_pressure = 0
+
+ if(memory["purge"])
+ target_pressure = 0
+
+ if(chamber_pressure <= target_pressure)
+ state = STATE_PRESSURIZE
+ signalPump(tag_airpump, 1, 1, target_pressure) //send a signal to start pressurizing
+
+ else if(chamber_pressure > target_pressure)
+ state = STATE_DEPRESSURIZE
+ signalPump(tag_airpump, 1, 0, target_pressure) //send a signal to start depressurizing
+
+ //Check for vacuum - this is set after the pumps so the pumps are aiming for 0
+ if(!memory["target_pressure"])
+ memory["target_pressure"] = ONE_ATMOSPHERE * 0.05
+
if(STATE_PRESSURIZE)
if(memory["chamber_sensor_pressure"] >= memory["target_pressure"] * 0.95)
cycleDoors(target_state)
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
if(memory["pump_status"] != "off")
@@ -216,7 +226,7 @@
else if(memory["chamber_sensor_pressure"] <= memory["target_pressure"] * 1.05)
cycleDoors(target_state)
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
//send a signal to stop pumping
@@ -231,11 +241,11 @@
//these are here so that other types don't have to make so many assuptions about our implementation
/datum/computer/file/embedded_program/airlock/proc/begin_cycle_in()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_INOPEN
/datum/computer/file/embedded_program/airlock/proc/begin_cycle_out()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_OUTOPEN
/datum/computer/file/embedded_program/airlock/proc/close_doors()
@@ -243,11 +253,11 @@
toggleDoor(memory["exterior_status"], tag_exterior_door, 1, "close")
/datum/computer/file/embedded_program/airlock/proc/stop_cycling()
- state = STATE_WAIT
+ state = STATE_IDLE
target_state = TARGET_NONE
/datum/computer/file/embedded_program/airlock/proc/done_cycling()
- return (state == STATE_WAIT && target_state == TARGET_NONE)
+ return (state == STATE_IDLE && target_state == TARGET_NONE)
//are the doors closed and locked?
/datum/computer/file/embedded_program/airlock/proc/check_exterior_door_secured()
@@ -265,7 +275,7 @@
var/datum/signal/signal = new
signal.data["tag"] = tag
signal.data["command"] = command
- post_signal(signal)
+ post_signal(signal, RADIO_AIRLOCK)
/datum/computer/file/embedded_program/airlock/proc/signalPump(var/tag, var/power, var/direction, var/pressure)
var/datum/signal/signal = new
@@ -295,11 +305,19 @@
signalDoor(tag_exterior_door, command)
signalDoor(tag_interior_door, command)
+datum/computer/file/embedded_program/airlock/proc/signal_mech_sensor(var/command, var/sensor)
+ var/datum/signal/signal = new
+ signal.data["tag"] = sensor
+ signal.data["command"] = command
+ post_signal(signal)
+
/datum/computer/file/embedded_program/airlock/proc/enable_mech_regulation()
- signalDoor(tag_mech_sensor, "enable")
+ signal_mech_sensor("enable", tag_shuttle_mech_sensor)
+ signal_mech_sensor("enable", tag_airlock_mech_sensor)
/datum/computer/file/embedded_program/airlock/proc/disable_mech_regulation()
- signalDoor(tag_mech_sensor, "disable")
+ signal_mech_sensor("disable", tag_shuttle_mech_sensor)
+ signal_mech_sensor("disable", tag_airlock_mech_sensor)
/*----------------------------------------------------------
toggleDoor()
@@ -355,7 +373,7 @@ send an additional command to open the door again.
signalDoor(doorTag, doorCommand)
-#undef STATE_WAIT
+#undef STATE_IDLE
#undef STATE_DEPRESSURIZE
#undef STATE_PRESSURIZE
diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm
index a9a8b40ed58..ec411753803 100644
--- a/code/game/machinery/embedded_controller/embedded_controller_base.dm
+++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm
@@ -3,6 +3,9 @@
name = "Embedded Controller"
anchored = 1
+
+ use_power = 1
+ idle_power_usage = 10
var/on = 1
@@ -43,8 +46,10 @@
density = 0
var/id_tag
+ //var/radio_power_use = 50 //power used to xmit signals
var/frequency = 1379
+ var/radio_filter = null
var/datum/radio_frequency/radio_connection
unacidable = 1
@@ -60,14 +65,15 @@
else
icon_state = "airlock_control_off"
-/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal)
+/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null)
signal.transmission_method = TRANSMISSION_RADIO
if(radio_connection)
- return radio_connection.post_signal(src, signal)
+ //use_power(radio_power_use) //neat idea, but causes way too much lag.
+ return radio_connection.post_signal(src, signal, filter)
else
del(signal)
/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency)
radio_controller.remove_object(src, frequency)
frequency = new_frequency
- radio_connection = radio_controller.add_object(src, frequency)
\ No newline at end of file
+ radio_connection = radio_controller.add_object(src, frequency, radio_filter)
\ No newline at end of file
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index d51f29655de..148e1b53322 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -38,7 +38,7 @@
// src.sd_SetLuminosity(0)
//Don't want to render prison breaks impossible
-/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/wirecutters))
add_fingerprint(user)
src.disable = !src.disable
@@ -107,7 +107,7 @@
if ((M.m_intent != "walk") && (src.anchored))
src.flash()
-/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/wrench))
add_fingerprint(user)
src.anchored = !src.anchored
@@ -126,7 +126,7 @@
/obj/machinery/flasher_button/attack_paw(mob/user as mob)
return src.attack_hand(user)
-/obj/machinery/flasher_button/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/flasher_button/attackby(obj/item/weapon/W, mob/user as mob, params)
return src.attack_hand(user)
/obj/machinery/flasher_button/attack_hand(mob/user as mob)
diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm
index b7203be245d..b6da9e11d95 100644
--- a/code/game/machinery/floodlight.dm
+++ b/code/game/machinery/floodlight.dm
@@ -4,9 +4,10 @@
name = "Emergency Floodlight"
icon = 'icons/obj/machines/floodlight.dmi'
icon_state = "flood00"
+ anchored = 0
density = 1
var/on = 0
- var/obj/item/weapon/cell/high/cell = null
+ var/obj/item/weapon/stock_parts/cell/high/cell = null
var/use = 5
var/unlocked = 0
var/open = 0
@@ -62,7 +63,22 @@
updateicon()
-/obj/machinery/floodlight/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/floodlight/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
+ if(istype(W, /obj/item/weapon/wrench))
+ if (!anchored && !isinspace())
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user.visible_message( \
+ "[user] tightens \the [src]'s casters.", \
+ " You have tightened \the [src]'s casters.", \
+ "You hear ratchet.")
+ anchored = 1
+ else if(anchored)
+ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
+ user.visible_message( \
+ "[user] loosens \the [src]'s casters.", \
+ " You have loosened \the [src]'s casters.", \
+ "You hear ratchet.")
+ anchored = 0
if (istype(W, /obj/item/weapon/screwdriver))
if (!open)
if(unlocked)
@@ -83,7 +99,7 @@
open = 1
user << "You remove the battery panel."
- if (istype(W, /obj/item/weapon/cell))
+ if (istype(W, /obj/item/weapon/stock_parts/cell))
if(open)
if(cell)
user << "There is a power cell already installed."
diff --git a/code/game/machinery/guestpass.dm b/code/game/machinery/guestpass.dm
index ee93ac74799..83cf1682bd8 100644
--- a/code/game/machinery/guestpass.dm
+++ b/code/game/machinery/guestpass.dm
@@ -54,7 +54,7 @@
var/list/internal_log = list()
var/mode = 0 // 0 - making pass, 1 - viewing logs
-/obj/machinery/computer/guestpass/attackby(obj/O, mob/user)
+/obj/machinery/computer/guestpass/attackby(obj/O, mob/user, params)
if(istype(O, /obj/item/weapon/card/id))
if(!giver)
user.drop_item()
@@ -105,7 +105,7 @@
/obj/machinery/computer/guestpass/Topic(href, href_list)
if(..())
- return
+ return 1
usr.set_machine(src)
if (href_list["mode"])
mode = text2num(href_list["mode"])
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 265145b4a04..a04ed3f2053 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -81,13 +81,20 @@ var/const/HOLOPAD_MODE = 0
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
-/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb)
+/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, datum/language/speaking)
if(M&&hologram&&master)//Master is mostly a safety in case lag hits or something.
- if(!master.say_understands(M))//The AI will be able to understand most mobs talking through the holopad.
- text = stars(text)
+ if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad.
+ if(speaking)
+ text = speaking.scramble(text)
+ else
+ text = stars(text)
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
- var/rendered = "Holopad received, [name_used] [verb], \"[text]\""
+ var/rendered
+ if(speaking)
+ rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]"
+ else
+ rendered = "Holopad received, [name_used] [verb], \"[text]\""
master.show_message(rendered, 2)
return
diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm
index 073973cdee7..cd1e5a85e62 100644
--- a/code/game/machinery/holosign.dm
+++ b/code/game/machinery/holosign.dm
@@ -50,7 +50,7 @@
obj/machinery/holosign_switch/attack_paw(mob/user as mob)
return src.attack_hand(user)
-/obj/machinery/holosign_switch/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/holosign_switch/attackby(obj/item/weapon/W, mob/user as mob, params)
if(istype(W, /obj/item/device/detective_scanner))
return
return src.attack_hand(user)
diff --git a/code/game/machinery/hydroponics.dm b/code/game/machinery/hydroponics.dm
index 58d52bc992b..ef9a7d29150 100644
--- a/code/game/machinery/hydroponics.dm
+++ b/code/game/machinery/hydroponics.dm
@@ -664,7 +664,7 @@
return
-/obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/portable_atmospherics/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(exchange_parts(user, O))
return
@@ -958,7 +958,7 @@
use_power = 0
draw_warnings = 0
-/obj/machinery/portable_atmospherics/hydroponics/soil/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/portable_atmospherics/hydroponics/soil/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item/weapon/shovel))
user << "You clear up [src]!"
del(src)
diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm
index 7aba3dc25f1..9577a539203 100755
--- a/code/game/machinery/igniter.dm
+++ b/code/game/machinery/igniter.dm
@@ -71,7 +71,7 @@
icon_state = "[base_state]-p"
// src.sd_SetLuminosity(0)
-/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/device/detective_scanner))
return
if (istype(W, /obj/item/weapon/screwdriver))
@@ -125,7 +125,7 @@
/obj/machinery/ignition_switch/attack_paw(mob/user as mob)
return src.attack_hand(user)
-/obj/machinery/ignition_switch/attackby(obj/item/weapon/W, mob/user as mob)
+/obj/machinery/ignition_switch/attackby(obj/item/weapon/W, mob/user as mob, params)
return src.attack_hand(user)
/obj/machinery/ignition_switch/attack_hand(mob/user as mob)
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 17127b65378..31b41770259 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -37,6 +37,13 @@
/obj/machinery/iv_drip/MouseDrop(over_object, src_location, over_location)
..()
+
+ if(!ishuman(usr) && !isrobot(usr))
+ return
+
+ var/turf/T = get_turf(src)
+ if(!usr in range(1, T))
+ return
if(attached)
visible_message("[src.attached] is detached from \the [src]")
@@ -50,7 +57,7 @@
src.update_icon()
-/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/reagent_containers))
if(!isnull(src.beaker))
user << "There is already a reagent container loaded!"
@@ -103,7 +110,7 @@
if(!istype(T)) return
if(!T.dna)
return
- if(M_NOCLONE in T.mutations)
+ if(NOCLONE in T.mutations)
return
if(T.species && T.species.flags & NO_BLOOD)
@@ -133,6 +140,7 @@
/obj/machinery/iv_drip/verb/toggle_mode()
set name = "Toggle Mode"
+ set category = "Object"
set src in view(1)
if(!istype(usr, /mob/living))
diff --git a/code/game/machinery/kitchen/gibber.dm b/code/game/machinery/kitchen/gibber.dm
index d5510dbbd26..8e657b6b53f 100644
--- a/code/game/machinery/kitchen/gibber.dm
+++ b/code/game/machinery/kitchen/gibber.dm
@@ -78,7 +78,7 @@
else
src.startgibbing(user)
-/obj/machinery/gibber/attackby(obj/item/weapon/grab/G as obj, mob/user as mob)
+/obj/machinery/gibber/attackby(obj/item/weapon/grab/G as obj, mob/user as mob, params)
if(src.occupant)
user << "\red The gibber is full, empty it first!"
return
diff --git a/code/game/machinery/kitchen/icecream_vat.dm b/code/game/machinery/kitchen/icecream_vat.dm
index ab39761b3da..711e2020361 100644
--- a/code/game/machinery/kitchen/icecream_vat.dm
+++ b/code/game/machinery/kitchen/icecream_vat.dm
@@ -89,7 +89,7 @@ var/list/ingredients_source = list(
user << browse(dat,"window=icecreamvat;size=600x400")
-/obj/machinery/icecream_vat/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/icecream_vat/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(istype(O, /obj/item/weapon/reagent_containers))
if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/icecream))
var/obj/item/weapon/reagent_containers/food/snacks/icecream/I = O
diff --git a/code/game/machinery/kitchen/juicer.dm b/code/game/machinery/kitchen/juicer.dm
index b237f8223f6..86d59679f76 100644
--- a/code/game/machinery/kitchen/juicer.dm
+++ b/code/game/machinery/kitchen/juicer.dm
@@ -31,13 +31,15 @@
return
-/obj/machinery/juicer/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/juicer/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if (istype(O,/obj/item/weapon/reagent_containers/glass) || \
istype(O,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass))
if (beaker)
return 1
else
- user.before_take_item(O)
+ if(!user.unEquip(O))
+ user << "\the [O] is stuck to your hand, you cannot put it in \the [src]"
+ return 0
O.loc = src
beaker = O
src.verbs += /obj/machinery/juicer/verb/detach
@@ -45,9 +47,11 @@
src.updateUsrDialog()
return 0
if (!is_type_in_list(O, allowed_items))
- user << "It looks as not containing any juice."
+ user << "It doesn't look like that contains any juice."
return 1
- user.before_take_item(O)
+ if(!user.unEquip(O))
+ user << "\the [O] is stuck to your hand, you cannot put it in \the [src]"
+ return 0
O.loc = src
src.updateUsrDialog()
return 0
diff --git a/code/game/machinery/kitchen/microwave.dm b/code/game/machinery/kitchen/microwave.dm
index 0268755bb1a..cd8e806fbb0 100644
--- a/code/game/machinery/kitchen/microwave.dm
+++ b/code/game/machinery/kitchen/microwave.dm
@@ -49,7 +49,7 @@
component_parts += new /obj/item/weapon/stock_parts/micro_laser(null)
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
component_parts += new /obj/item/stack/cable_coil(null, 2)
- RefreshParts()
+ RefreshParts()
/obj/machinery/microwave/upgraded/New()
@@ -59,19 +59,19 @@
component_parts += new /obj/item/weapon/stock_parts/micro_laser/ultra(null)
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
component_parts += new /obj/item/stack/cable_coil(null, 2)
- RefreshParts()
+ RefreshParts()
/obj/machinery/microwave/RefreshParts()
var/E
for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
E += M.rating
- efficiency = E
-
+ efficiency = E
+
/*******************
* Item Adding
********************/
-/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(operating)
return
if(!broken && dirty < 100)
@@ -87,11 +87,11 @@
return
else if(!anchored)
anchored = 1
- user << "The [src] is now secured."
+ user << "The [src] is now secured."
return
-
+
default_deconstruction_crowbar(O)
-
+
if(src.broken > 0)
if(src.broken == 2 && istype(O, /obj/item/weapon/screwdriver)) // If it's broken and they're using a screwdriver
user.visible_message( \
@@ -150,8 +150,10 @@
"\blue [user] has added one of [O] to \the [src].", \
"\blue You add one of [O] to \the [src].")
else
- // user.before_take_item(O) //This just causes problems so far as I can tell. -Pete
- user.drop_item()
+ // user.unEquip(O) //This just causes problems so far as I can tell. -Pete
+ if(!user.drop_item())
+ user << "\the [O] is stuck to your hand, you cannot put it in \the [src]"
+ return 0
O.loc = src
user.visible_message( \
"\blue [user] has added \the [O] to \the [src].", \
diff --git a/code/game/machinery/kitchen/monkeyrecycler.dm b/code/game/machinery/kitchen/monkeyrecycler.dm
index f4b373e8eab..9342e6530b3 100644
--- a/code/game/machinery/kitchen/monkeyrecycler.dm
+++ b/code/game/machinery/kitchen/monkeyrecycler.dm
@@ -12,7 +12,7 @@
var/grinded = 0
-/obj/machinery/monkey_recycler/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/monkey_recycler/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if (src.stat != 0) //NOPOWER etc
return
if (istype(O, /obj/item/weapon/grab))
diff --git a/code/game/machinery/kitchen/processor.dm b/code/game/machinery/kitchen/processor.dm
index aea4592faae..f466149d8d1 100644
--- a/code/game/machinery/kitchen/processor.dm
+++ b/code/game/machinery/kitchen/processor.dm
@@ -104,7 +104,7 @@
return P
return 0
-/obj/machinery/processor/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/processor/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(src.processing)
user << "\red The processor is in the process of processing."
return 1
diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index 7f8ba253582..397ee26b20a 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -75,7 +75,7 @@
* Item Adding
********************/
-/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if(!src.ispowered)
user << "\The [src] is unpowered and useless."
return
@@ -85,7 +85,9 @@
user << "\The [src] is full."
return 1
else
- user.before_take_item(O)
+ if(!user.unEquip(O))
+ usr << "\the [O] is stuck to your hand, you cannot put it in \the [src]"
+ return
O.loc = src
if(item_quants[O.name])
item_quants[O.name]++
diff --git a/code/game/machinery/laprecharger.dm b/code/game/machinery/laprecharger.dm
new file mode 100644
index 00000000000..cafd5e3d297
--- /dev/null
+++ b/code/game/machinery/laprecharger.dm
@@ -0,0 +1,101 @@
+//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
+
+/obj/machinery/laprecharger
+ name = "laptop recharger"
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "recharger0"
+ anchored = 1
+ use_power = 1
+ idle_power_usage = 40
+ active_power_usage = 2500
+ var/obj/item/charging = null
+ var/icon_state_charged = "recharger2"
+ var/icon_state_charging = "recharger1"
+ var/icon_state_idle = "recharger0"
+
+/obj/machinery/laprecharger/attackby(obj/item/weapon/G as obj, mob/user as mob, params)
+ if(istype(user,/mob/living/silicon))
+ return
+ if(istype(G, /obj/item/weapon/gun/energy) || istype(G, /obj/item/weapon/melee/baton))
+ user << "\red The laptop recharger blinks red as you try to insert the item!"
+ return
+ if(istype(G,/obj/item/device/laptop))
+ if(charging)
+ return
+
+
+ // Checks to make sure he's not in space doing it, and that the area got proper power.
+ var/area/a = get_area(src)
+ if(!isarea(a))
+ user << "\red The [name] blinks red as you try to insert the item!"
+ return
+ if(a.power_equip == 0)
+ user << "\red The [name] blinks red as you try to insert the item!"
+ return
+
+ if(istype(G, /obj/item/device/laptop))
+ var/obj/item/device/laptop/L = G
+ if(!L.stored_computer.battery)
+ user << "There's no battery in it!"
+ return
+ user.drop_item()
+ G.loc = src
+ charging = G
+ use_power = 2
+ update_icon()
+ else if(istype(G, /obj/item/weapon/wrench))
+ if(charging)
+ user << "\red Remove the laptop first!"
+ return
+ anchored = !anchored
+ user << "You [anchored ? "attached" : "detached"] the recharger."
+ playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
+
+/obj/machinery/laprecharger/attack_hand(mob/user as mob)
+ add_fingerprint(user)
+
+ if(charging)
+ charging.update_icon()
+ charging.loc = loc
+ charging = null
+ use_power = 1
+ update_icon()
+
+/obj/machinery/laprecharger/attack_paw(mob/user as mob)
+ return attack_hand(user)
+
+/obj/machinery/laprecharger/process()
+ if(stat & (NOPOWER|BROKEN) || !anchored)
+ return
+
+ if(charging)
+ if(istype(charging, /obj/item/device/laptop))
+ var/obj/item/device/laptop/L = charging
+ if(L.stored_computer.battery.charge < L.stored_computer.battery.maxcharge)
+ L.stored_computer.battery.give(1000)
+ icon_state = icon_state_charging
+ use_power(2500)
+ else
+ icon_state = icon_state_charged
+ return
+
+
+/obj/machinery/laprecharger/emp_act(severity)
+ if(stat & (NOPOWER|BROKEN) || !anchored)
+ ..(severity)
+ return
+
+/obj/machinery/laprecharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
+ if(charging)
+ icon_state = icon_state_charging
+ else
+ icon_state = icon_state_idle
+
+// Atlantis: No need for that copy-pasta code, just use var to store icon_states instead.
+obj/machinery/laprecharger/wallcharger
+ name = "wall laptop recharger"
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "wrecharger0"
+ icon_state_idle = "wrecharger0"
+ icon_state_charging = "wrecharger1"
+ icon_state_charged = "wrecharger2"
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 5e69e5701af..6a0bcbf496f 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -184,37 +184,21 @@ Class Procs:
use_power(active_power_usage,power_channel, 1)
return 1
-/obj/machinery/Topic(href, href_list)
+/obj/machinery/CanUseTopic(var/mob/user, var/be_close)
+ if(!interact_offline && (stat & (NOPOWER|BROKEN)))
+ return STATUS_CLOSE
+
+ return ..()
+
+/obj/machinery/CouldUseTopic(var/mob/user)
..()
- if(!interact_offline && stat & (NOPOWER|BROKEN))
- return 1
- if(usr.restrained() || usr.lying || usr.stat)
- return 1
- if ( ! (istype(usr, /mob/living/carbon/human) || \
- istype(usr, /mob/living/silicon) || \
- istype(usr, /mob/living/carbon/monkey) && ticker && ticker.mode.name == "monkey") )
- usr << "\red You don't have the dexterity to do this!"
- return 1
+ user.set_machine(src)
- var/norange = 0
- if(istype(usr, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = usr
- if(istype(H.l_hand, /obj/item/tk_grab))
- norange = 1
- else if(istype(H.r_hand, /obj/item/tk_grab))
- norange = 1
-
- if(!norange)
- if ((!in_range(src, usr) || !istype(src.loc, /turf)) && !istype(usr, /mob/living/silicon))
- return 1
-
- src.add_fingerprint(usr)
-
- var/area/A = get_area(src)
- A.powerupdate = 1
-
- return 0
+/obj/machinery/CouldNotUseTopic(var/mob/user)
+ usr.unset_machine()
+////////////////////////////////////////////////////////////////////////////////////////////
+
/obj/machinery/attack_ai(var/mob/user as mob)
if(isAI(user))
var/mob/living/silicon/ai/A = user
@@ -351,22 +335,6 @@ Class Procs:
user << "[A.name] replaced with [B.name]."
shouldplaysound = 1
break
- // Power cell snowflake
- for(var/obj/item/weapon/cell/A in component_parts)
- for(var/D in CB.req_components)
- if(ispath(A.type, D))
- P = D
- break
- for(var/obj/item/weapon/cell/B in W.contents)
- if(istype(B, P) && istype(A, P))
- if(B.rating > A.rating)
- W.remove_from_storage(B, src)
- W.handle_item_insertion(A, 1)
- component_parts -= A
- component_parts += B
- B.loc = null
- user << "[A.name] replaced with [B.name]."
- break
RefreshParts()
else
user << "Following parts detected in the machine:"
@@ -389,3 +357,51 @@ Class Procs:
I.loc = loc
del(src)
return 1
+
+/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp)
+ return 0
+
+/obj/machinery/proc/is_assess_emagged()
+ return emagged
+
+/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/auth_weapons, var/check_records, var/check_arrest)
+ var/threatcount = 0 //the integer returned
+
+ if(is_assess_emagged())
+ return 10 //if emagged, always return 10.
+
+ threatcount += on_assess_perp(perp)
+ if(threatcount >= 10)
+ return threatcount
+
+ //Agent cards lower threatlevel.
+ var/obj/item/weapon/card/id/id = GetIdCard(perp)
+ if(id && istype(id, /obj/item/weapon/card/id/syndicate))
+ threatcount -= 2
+
+ if(auth_weapons && !src.allowed(perp))
+ if(istype(perp.l_hand, /obj/item/weapon/gun) || istype(perp.l_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(perp.r_hand, /obj/item/weapon/gun) || istype(perp.r_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(perp.belt, /obj/item/weapon/gun) || istype(perp.belt, /obj/item/weapon/melee))
+ threatcount += 2
+
+ if(perp.species.name != "Human") //beepsky so racist.
+ threatcount += 2
+
+ if(check_records || check_arrest)
+ var/perpname = perp.name
+ if(id)
+ perpname = id.registered_name
+
+ var/datum/data/record/R = find_security_record("name", perpname)
+ if(check_records && !R)
+ threatcount += 4
+
+ if(check_arrest && R && (R.fields["criminal"] == "*Arrest*"))
+ threatcount += 4
+
+ return threatcount
diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm
index a2dd432d991..8569ede3fd7 100644
--- a/code/game/machinery/magnet.dm
+++ b/code/game/machinery/magnet.dm
@@ -323,7 +323,7 @@
if(speed <= 0)
speed = 1
if("setpath")
- var/newpath = copytext(sanitize(input(usr, "Please define a new path!",,path) as text|null),1,MAX_MESSAGE_LEN)
+ var/newpath = sanitize(copytext(input(usr, "Please define a new path!",,path) as text|null,1,MAX_MESSAGE_LEN))
if(newpath && newpath != "")
moving = 0 // stop moving
path = newpath
diff --git a/code/game/machinery/metaldetector.dm b/code/game/machinery/metaldetector.dm
index 3d7ccb4ab3b..95e76eaa2c5 100644
--- a/code/game/machinery/metaldetector.dm
+++ b/code/game/machinery/metaldetector.dm
@@ -26,12 +26,7 @@
return 0
return 1
-/obj/machinery/metaldetector/attackby(obj/item/W as obj, mob/user as mob)
- if(istype(W, /obj/item/weapon/card/emag))
- if(!src.emagged)
- src.emagged = 1
- user << "\blue You short out the circuitry."
- return
+/obj/machinery/metaldetector/attackby(obj/item/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/card))
for(var/ID in list(user.equipped(), user:wear_id, user:belt))
if(src.check_access(ID,list("20")))
@@ -46,6 +41,12 @@
else
user << "\red You lack access to the control panel!"
return
+
+/obj/machinery/metaldetector/emag_act(user as mob)
+ if(!emagged)
+ emagged = 1
+ user << "\blue You short out the circuitry."
+ return
/obj/machinery/metaldetector/Crossed(AM as mob|obj)
if(emagged)
diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm
index 59bf0f2ea17..a8f39a4597e 100644
--- a/code/game/machinery/navbeacon.dm
+++ b/code/game/machinery/navbeacon.dm
@@ -101,7 +101,7 @@
frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
-/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user)
+/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user, params)
var/turf/T = loc
if(T.intact)
return // prevent intraction when T-scanner revealed
@@ -193,7 +193,7 @@ Transponder Codes:
"}
updateDialog()
else if(href_list["locedit"])
- var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN)
+ var/newloc = sanitize(copytext(input("Enter New Location", "Navigation Beacon", location) as text|null,1,MAX_MESSAGE_LEN))
if(newloc)
location = newloc
updateDialog()
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index fcb2412c122..248908ed824 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -5,6 +5,7 @@
/datum/feed_message
var/author =""
var/body =""
+ var/message_type ="Story"
//var/parent_channel
var/backup_body =""
var/backup_author =""
@@ -39,6 +40,12 @@
src.backup_author = ""
src.censored = 0
src.is_admin_channel = 0
+
+/datum/feed_channel/proc/announce_news()
+ return "Breaking news from [channel_name]!"
+
+/datum/feed_channel/station/announce_news()
+ return "New Station Announcement Available"
/datum/feed_network
var/list/datum/feed_channel/network_channels = list()
@@ -213,7 +220,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
switch(screen)
if(0)
dat += {"Welcome to Newscasting Unit #[src.unit_no]. Interface & News networks Operational.
- Property of Nanotransen Inc"}
+ Property of Nanotrasen"}
if(news_network.wanted_issue)
dat+= "Read Wanted Issue"
dat+= {" Create Feed Channel
@@ -248,7 +255,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
dat+="No feed messages found in channel...
"
else
for(var/datum/feed_message/MESSAGE in CHANNEL.messages)
- dat+="-[MESSAGE.body] \[Story by [MESSAGE.author]\] "*/
+ dat+="-[MESSAGE.body] \[[MESSAGE.message_type] by [MESSAGE.author]\] "*/
dat+=" Refresh"
dat+=" Back"
@@ -334,7 +341,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
if(MESSAGE.img)
usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
dat+="
"
- dat+="\[Story by [MESSAGE.author]\] "
+ dat+="\[[MESSAGE.message_type] by [MESSAGE.author]\] "
dat+=" Refresh"
dat+=" Back"
if(10)
@@ -369,7 +376,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
dat+="No feed messages found in channel... "
else
for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages)
- dat+="-[MESSAGE.body] \[Story by [MESSAGE.author]\] "
+ dat+="-[MESSAGE.body] \[[MESSAGE.message_type] by [MESSAGE.author]\] "
dat+="[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")] "
dat+=" Back"
if(13)
@@ -383,7 +390,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
dat+="No feed messages found in channel... "
else
for(var/datum/feed_message/MESSAGE in src.viewing_channel.messages)
- dat+="-[MESSAGE.body] \[Story by [MESSAGE.author]\] "
+ dat+="-[MESSAGE.body] \[[MESSAGE.message_type] by [MESSAGE.author]\] "
dat+=" Back"
if(14)
@@ -460,7 +467,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
/obj/machinery/newscaster/Topic(href, href_list)
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
if(href_list["set_channel_name"])
@@ -516,7 +523,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
src.updateUsrDialog()
else if(href_list["set_new_message"])
- src.msg = strip_html(input(usr, "Write your Feed story", "Network Channel Handler", ""))
+ src.msg = strip_html(input(usr, "Write your feed story", "Network Channel Handler", ""))
while (findtext(src.msg," ") == 1)
src.msg = copytext(src.msg,2,lentext(src.msg)+1)
src.updateUsrDialog()
@@ -535,13 +542,15 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
if(photo)
newMsg.img = photo.img
feedback_inc("newscaster_stories",1)
+ var/announcement = ""
for(var/datum/feed_channel/FC in news_network.network_channels)
if(FC.channel_name == src.channel_name)
FC.messages += newMsg //Adding message to the network's appropriate feed_channel
+ announcement = FC.announce_news()
break
src.screen=4
for(var/obj/machinery/newscaster/NEWSCASTER in allCasters)
- NEWSCASTER.newsAlert(src.channel_name)
+ NEWSCASTER.newsAlert(announcement)
src.updateUsrDialog()
@@ -727,7 +736,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
src.updateUsrDialog()
-/obj/machinery/newscaster/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/newscaster/attackby(obj/item/I as obj, mob/living/user as mob, params)
if(istype(I, /obj/item/weapon/wrench))
user << "Now [anchored ? "un" : ""]securing [name]"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
@@ -929,13 +938,13 @@ obj/item/weapon/newspaper/Topic(href, href_list)
src.attack_self(src.loc)
-obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob)
+obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/pen))
if(src.scribble_page == src.curr_page)
user << "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?"
else
var/s = strip_html( input(user, "Write something", "Newspaper", "") )
- s = copytext(sanitize(s), 1, MAX_MESSAGE_LEN)
+ s = sanitize(copytext(s, 1, MAX_MESSAGE_LEN))
if (!s)
return
if (!in_range(src, usr) && src.loc != usr)
@@ -986,11 +995,11 @@ obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob)
///obj/machinery/newscaster/process() //Was thinking of doing the icon update through process, but multiple iterations per second does not
// return //bode well with a newscaster network of 10+ machines. Let's just return it, as it's added in the machines list.
-/obj/machinery/newscaster/proc/newsAlert(channel) //This isn't Agouri's work, for it is ugly and vile.
+/obj/machinery/newscaster/proc/newsAlert(var/news_call) //This isn't Agouri's work, for it is ugly and vile.
var/turf/T = get_turf(src) //Who the fuck uses spawn(600) anyway, jesus christ
- if(channel)
+ if(news_call)
for(var/mob/O in hearers(world.view-1, T))
- O.show_message("[src.name] beeps, \"Breaking news from [channel]!\"",2)
+ O.show_message("[src.name] beeps, \"[news_call]\"",2)
src.alert = 1
src.update_icon()
spawn(300)
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index c1cbc65c297..21e66517422 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -56,7 +56,6 @@ Buildable meters
icon = 'icons/obj/pipe-item.dmi'
icon_state = "simple"
item_state = "buildpipe"
- flags = TABLEPASS|FPRINT
w_class = 3
level = 2
@@ -389,7 +388,7 @@ Buildable meters
/obj/item/pipe/attack_self(mob/user as mob)
return rotate()
-/obj/item/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+/obj/item/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob, params)
..()
//*
if (!istype(W, /obj/item/weapon/wrench))
@@ -432,7 +431,7 @@ Buildable meters
if (P.node2)
P.node2.initialize()
P.node2.build_network()
-
+
if(PIPE_SUPPLY_STRAIGHT, PIPE_SUPPLY_BENT)
var/obj/machinery/atmospherics/pipe/simple/hidden/supply/P = new( src.loc )
P.color = color
@@ -546,7 +545,7 @@ Buildable meters
if (M.node3)
M.node3.initialize()
M.node3.build_network()
-
+
if(PIPE_SUPPLY_MANIFOLD) //manifold
var/obj/machinery/atmospherics/pipe/manifold/hidden/supply/M = new( src.loc )
M.color = color
@@ -618,7 +617,7 @@ Buildable meters
if (M.node4)
M.node4.initialize()
M.node4.build_network()
-
+
if(PIPE_SUPPLY_MANIFOLD4W) //4-way manifold
var/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply/M = new( src.loc )
M.color = color
@@ -904,7 +903,7 @@ Buildable meters
if(C.node)
C.node.initialize()
C.node.build_network()
-
+
if(PIPE_SUPPLY_CAP)
var/obj/machinery/atmospherics/pipe/cap/hidden/supply/C = new(src.loc)
C.dir = dir
@@ -1039,10 +1038,9 @@ Buildable meters
icon = 'icons/obj/pipe-item.dmi'
icon_state = "meter"
item_state = "buildpipe"
- flags = TABLEPASS|FPRINT
w_class = 4
-/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob, params)
..()
if (!istype(W, /obj/item/weapon/wrench))
diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm
index e52737efd8f..959df353b38 100644
--- a/code/game/machinery/pipe/pipe_dispenser.dm
+++ b/code/game/machinery/pipe/pipe_dispenser.dm
@@ -87,7 +87,7 @@
wait = 0
return
-/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob)
+/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
src.add_fingerprint(usr)
if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter))
usr << "\blue You put [W] back to [src]."
diff --git a/code/game/machinery/podmen.dm b/code/game/machinery/podmen.dm
index 4a698e2a4e2..58dc6dc7888 100644
--- a/code/game/machinery/podmen.dm
+++ b/code/game/machinery/podmen.dm
@@ -27,7 +27,7 @@ Growing it to term with nothing injected will grab a ghost from the observers. *
var/found_player = 0
var/beingharvested = 0
-/obj/item/seeds/replicapod/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/seeds/replicapod/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W,/obj/item/weapon/reagent_containers))
diff --git a/code/game/machinery/portable_tag_turret.dm b/code/game/machinery/portable_tag_turret.dm
new file mode 100644
index 00000000000..c6da942645a
--- /dev/null
+++ b/code/game/machinery/portable_tag_turret.dm
@@ -0,0 +1,124 @@
+#define TURRET_PRIORITY_TARGET 2
+#define TURRET_SECONDARY_TARGET 1
+#define TURRET_NOT_TARGET 0
+
+/obj/machinery/porta_turret/tag
+ // Reasonable defaults, in case someone manually spawns us
+ var/lasercolor = "r" //Something to do with lasertag turrets, blame Sieve for not adding a comment.
+ installation = /obj/item/weapon/gun/energy/laser/redtag
+
+/obj/machinery/porta_turret/tag/red
+
+/obj/machinery/porta_turret/tag/blue
+ lasercolor = "b"
+ installation = /obj/item/weapon/gun/energy/laser/bluetag
+
+/obj/machinery/porta_turret/tag/New()
+ ..()
+ icon_state = "[lasercolor]grey_target_prism"
+
+/obj/machinery/porta_turret/tag/weapon_setup(var/obj/item/weapon/gun/energy/E)
+ switch(E.type)
+ if(/obj/item/weapon/gun/energy/laser/bluetag)
+ eprojectile = /obj/item/weapon/gun/energy/laser/bluetag
+ lasercolor = "b"
+ req_access = list(access_maint_tunnels, access_theatre)
+ check_arrest = 0
+ check_records = 0
+ check_weapons = 1
+ check_access = 0
+ check_anomalies = 0
+ shot_delay = 30
+
+ if(/obj/item/weapon/gun/energy/laser/redtag)
+ eprojectile = /obj/item/weapon/gun/energy/laser/redtag
+ lasercolor = "r"
+ req_access = list(access_maint_tunnels, access_theatre)
+ check_arrest = 0
+ check_records = 0
+ check_weapons = 1
+ check_access = 0
+ check_anomalies = 0
+ shot_delay = 30
+ iconholder = 1
+
+/obj/machinery/porta_turret/tag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["access"] = !isLocked(user)
+ data["locked"] = locked
+ data["enabled"] = enabled
+ data["is_lethal"] = 0
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/porta_turret/tag/update_icon()
+ if(!anchored)
+ icon_state = "turretCover"
+ return
+ if(stat & BROKEN)
+ icon_state = "[lasercolor]destroyed_target_prism"
+ else
+ if(powered())
+ if(enabled)
+ if(iconholder)
+ //lasers have a orange icon
+ icon_state = "[lasercolor]orange_target_prism"
+ else
+ //almost everything has a blue icon
+ icon_state = "[lasercolor]target_prism"
+ else
+ icon_state = "[lasercolor]grey_target_prism"
+ else
+ icon_state = "[lasercolor]grey_target_prism"
+
+/obj/machinery/porta_turret/tag/bullet_act(obj/item/projectile/Proj)
+ ..()
+
+ if(lasercolor == "b" && disabled == 0)
+ if(istype(Proj, /obj/item/weapon/gun/energy/laser/redtag))
+ disabled = 1
+ del(Proj) // qdel
+ sleep(100)
+ disabled = 0
+ if(lasercolor == "r" && disabled == 0)
+ if(istype(Proj, /obj/item/weapon/gun/energy/laser/bluetag))
+ disabled = 1
+ del(Proj) // qdel
+ sleep(100)
+ disabled = 0
+
+/obj/machinery/porta_turret/tag/assess_living(var/mob/living/L)
+ if(!L)
+ return TURRET_NOT_TARGET
+
+ if(L.lying)
+ return TURRET_NOT_TARGET
+
+ var/target_suit
+ var/target_weapon
+ switch(lasercolor)
+ if("b")
+ target_suit = /obj/item/clothing/suit/redtag
+ target_weapon = /obj/item/weapon/gun/energy/laser/redtag
+ if("r")
+ target_suit = /obj/item/clothing/suit/bluetag
+ target_weapon = /obj/item/weapon/gun/energy/laser/bluetag
+
+
+ if(target_suit)//Lasertag turrets target the opposing team, how great is that? -Sieve
+ if((istype(L.r_hand, target_weapon)) || (istype(L.l_hand, target_weapon)))
+ return TURRET_PRIORITY_TARGET
+
+ if(istype(L, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = L
+ if(istype(H.wear_suit, target_suit))
+ return TURRET_PRIORITY_TARGET
+ if(istype(H.belt, target_weapon))
+ return TURRET_SECONDARY_TARGET
+
+ return TURRET_NOT_TARGET
\ No newline at end of file
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 84d6915556c..e7a064ff077 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -1,636 +1,589 @@
-/*
- Portable Turrets:
-
+/* Portable Turrets:
Constructed from metal, a gun of choice, and a prox sensor.
- Gun can be a taser or laser or energy gun.
-
This code is slightly more documented than normal, as requested by XSI on IRC.
-
*/
-
/obj/machinery/porta_turret
name = "turret"
icon = 'icons/obj/turrets.dmi'
icon_state = "grey_target_prism"
anchored = 1
layer = 3
- invisibility = INVISIBILITY_LEVEL_TWO // the turret is invisible if it's inside its cover
+ invisibility = INVISIBILITY_LEVEL_TWO //the turret is invisible if it's inside its cover
density = 1
- use_power = 1 // this turret uses and requires power
- idle_power_usage = 50 // when inactive, this turret takes up constant 50 Equipment power
- active_power_usage = 300// when active, this turret takes up constant 300 Equipment power
- req_access = list(access_security)
- power_channel = EQUIP // drains power from the EQUIPMENT channel
+ use_power = 1 //this turret uses and requires power
+ idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power
+ active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power
+ req_access = null
+ req_one_access = list(access_security, access_heads)
+ power_channel = EQUIP //drains power from the EQUIPMENT channel
- var/lasercolor = "" // Something to do with lasertag turrets, blame Sieve for not adding a comment.
- var/raised = 0 // if the turret cover is "open" and the turret is raised
- var/raising= 0 // if the turret is currently opening or closing its cover
- var/health = 80 // the turret's health
- var/locked = 1 // if the turret's behaviour control access is locked
+ var/raised = 0 //if the turret cover is "open" and the turret is raised
+ var/raising= 0 //if the turret is currently opening or closing its cover
+ var/health = 80 //the turret's health
+ var/locked = 1 //if the turret's behaviour control access is locked
+ var/controllock = 0 //if the turret responds to control panels
- var/installation // the type of weapon installed
- var/gun_charge = 0 // the charge of the gun inserted
+ var/installation = /obj/item/weapon/gun/energy/gun/turret //the type of weapon installed
+ var/gun_charge = 0 //the charge of the gun inserted
var/projectile = null //holder for bullettype
- var/eprojectile = null//holder for the shot when emagged
- var/reqpower = 0 //holder for power needed
- var/sound = null//So the taser can have sound
- var/iconholder = null//holder for the icon_state
- var/egun = null//holder to handle certain guns switching bullettypes
+ var/eprojectile = null //holder for the shot when emagged
+ var/reqpower = 500 //holder for power needed
+ var/iconholder = null //holder for the icon_state. 1 for orange sprite, null for blue.
+ var/egun = null //holder to handle certain guns switching bullettypes
- var/obj/machinery/porta_turret_cover/cover = null // the cover that is covering this turret
- var/last_fired = 0 // 1: if the turret is cooling down from a shot, 0: turret is ready to fire
- var/shot_delay = 15 // 1.5 seconds between each shot
+ var/obj/machinery/porta_turret_cover/cover = null //the cover that is covering this turret
+ var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire
+ var/shot_delay = 15 //1.5 seconds between each shot
- var/check_records = 1 // checks if it can use the security records
- var/criminals = 1 // checks if it can shoot people on arrest
- var/auth_weapons = 0 // checks if it can shoot people that have a weapon they aren't authorized to have
- var/stun_all = 0 // if this is active, the turret shoots everything that isn't security or head of staff
- var/check_anomalies = 1 // checks if it can shoot at unidentified lifeforms (ie xenos)
- var/ai = 0 // if active, will shoot at anything not an AI or cyborg
+ var/check_arrest = 1 //checks if the perp is set to arrest
+ var/check_records = 1 //checks if a security record exists at all
+ var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
+ var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
+ var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
+ var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
+ var/ailock = 0 // AI cannot use this
- var/attacked = 0 // if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
+ var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
- //var/emagged = 0 // 1: emagged, 0: not emagged
- var/on = 1 // determines if the turret is on
+ var/enabled = 1 //determines if the turret is on
+ var/lethal = 0 //whether in lethal or stun mode
var/disabled = 0
- var/datum/effect/effect/system/spark_spread/spark_system // the spark system, used for generating... sparks?
+ var/shot_sound //what sound should play when the turret fires
+ var/eshot_sound //what sound should play when the emagged turret fires
- New()
- ..()
- icon_state = "[lasercolor]grey_target_prism"
- // Sets up a spark system
- spark_system = new /datum/effect/effect/system/spark_spread
- spark_system.set_up(5, 0, src)
- spark_system.attach(src)
- sleep(10)
- if(!installation)// if for some reason the turret has no gun (ie, admin spawned) it resorts to basic taser shots
- projectile = /obj/item/projectile/energy/electrode//holder for the projectile, here it is being set
- eprojectile = /obj/item/projectile/beam//holder for the projectile when emagged, if it is different
- reqpower = 200
- sound = 1
+ var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks?
+
+ var/wrenching = 0
+ var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range
+
+/obj/machinery/porta_turret/stationary
+ lethal = 1
+ installation = /obj/item/weapon/gun/energy/laser
+
+/obj/machinery/porta_turret/New()
+ ..()
+ icon_state = "grey_target_prism"
+ //Sets up a spark system
+ spark_system = new /datum/effect/effect/system/spark_spread
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+
+ cover = new /obj/machinery/porta_turret_cover(loc)
+ cover.Parent_Turret = src
+ setup()
+
+/obj/machinery/porta_turret/proc/setup()
+ var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable
+ //var/obj/item/ammo_casing/shottype = E.projectile_type
+
+ projectile = initial(E.projectile_type)
+ eprojectile = projectile
+ shot_sound = initial(E.fire_sound)
+ eshot_sound = shot_sound
+
+ weapon_setup(installation)
+
+/obj/machinery/porta_turret/proc/weapon_setup(var/guntype)
+ switch(guntype)
+ if(/obj/item/weapon/gun/energy/laser/practice)
iconholder = 1
- else
- var/obj/item/weapon/gun/energy/E=new installation
- // All energy-based weapons are applicable
- switch(E.type)
- if(/obj/item/weapon/gun/energy/laser/bluetag)
- projectile = /obj/item/projectile/lasertag/blue
- eprojectile = /obj/item/projectile/lasertag/omni//This bolt will stun ERRYONE with a vest
- iconholder = null
- reqpower = 100
- lasercolor = "b"
- req_access = list(access_maint_tunnels)
- check_records = 0
- criminals = 0
- auth_weapons = 1
- stun_all = 0
- check_anomalies = 0
- shot_delay = 30
+ eprojectile = /obj/item/projectile/beam
- if(/obj/item/weapon/gun/energy/laser/redtag)
- projectile = /obj/item/projectile/lasertag/red
- eprojectile = /obj/item/projectile/lasertag/omni
- iconholder = null
- reqpower = 100
- lasercolor = "r"
- req_access = list(access_maint_tunnels)
- check_records = 0
- criminals = 0
- auth_weapons = 1
- stun_all = 0
- check_anomalies = 0
- shot_delay = 30
+// if(/obj/item/weapon/gun/energy/laser/practice/sc_laser)
+// iconholder = 1
+// eprojectile = /obj/item/projectile/beam
- if(/obj/item/weapon/gun/energy/laser/practice)
- projectile = /obj/item/projectile/practice
- eprojectile = /obj/item/projectile/beam
- iconholder = null
- reqpower = 100
+ if(/obj/item/weapon/gun/energy/laser/retro)
+ iconholder = 1
- if(/obj/item/weapon/gun/energy/pulse_rifle)
- projectile = /obj/item/projectile/beam/pulse
- eprojectile = projectile
- iconholder = null
- reqpower = 700
+// if(/obj/item/weapon/gun/energy/retro/sc_retro)
+// iconholder = 1
- if(/obj/item/weapon/gun/energy/staff)
- projectile = /obj/item/projectile/change
- eprojectile = projectile
- iconholder = 1
- reqpower = 700
+ if(/obj/item/weapon/gun/energy/laser/captain)
+ iconholder = 1
- if(/obj/item/weapon/gun/energy/ionrifle)
- projectile = /obj/item/projectile/ion
- eprojectile = projectile
- iconholder = 1
- reqpower = 700
+ if(/obj/item/weapon/gun/energy/lasercannon)
+ iconholder = 1
- if(/obj/item/weapon/gun/energy/advtaser)
- projectile = /obj/item/projectile/energy/electrode
- eprojectile = projectile
- iconholder = 1
- reqpower = 200
+ if(/obj/item/weapon/gun/energy/taser)
+ eprojectile = /obj/item/projectile/beam
+ eshot_sound = 'sound/weapons/Laser.ogg'
- if(/obj/item/weapon/gun/energy/stunrevolver)
- projectile = /obj/item/projectile/energy/electrode
- eprojectile = projectile
- iconholder = 1
- reqpower = 200
+ if(/obj/item/weapon/gun/energy/stunrevolver)
+ eprojectile = /obj/item/projectile/beam
+ eshot_sound = 'sound/weapons/Laser.ogg'
- if(/obj/item/weapon/gun/energy/lasercannon)
- projectile = /obj/item/projectile/beam/heavylaser
- eprojectile = projectile
- iconholder = null
- reqpower = 600
+ if(/obj/item/weapon/gun/energy/gun)
+ eprojectile = /obj/item/projectile/beam //If it has, going to kill mode
+ eshot_sound = 'sound/weapons/Laser.ogg'
+ egun = 1
- if(/obj/item/weapon/gun/energy/decloner)
- projectile = /obj/item/projectile/energy/declone
- eprojectile = projectile
- iconholder = null
- reqpower = 600
-
- if(/obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/large)
- projectile = /obj/item/projectile/energy/bolt/large
- eprojectile = projectile
- iconholder = null
- reqpower = 125
-
- if(/obj/item/weapon/gun/energy/kinetic_accelerator/crossbow)
- projectile = /obj/item/projectile/energy/bolt
- eprojectile = projectile
- iconholder = null
- reqpower = 50
-
- if(/obj/item/weapon/gun/energy/laser)
- projectile = /obj/item/projectile/beam
- eprojectile = projectile
- iconholder = null
- reqpower = 500
-
- else // Energy gun shots
- projectile = /obj/item/projectile/energy/electrode// if it hasn't been emagged, it uses normal taser shots
- eprojectile = /obj/item/projectile/beam//If it has, going to kill mode
- iconholder = 1
- egun = 1
- reqpower = 200
-
- Destroy()
- // deletes its own cover with it
- del(cover)
- ..()
-
-
-/obj/machinery/porta_turret/attack_ai(mob/user as mob)
- return attack_hand(user)
-
-/obj/machinery/porta_turret/attack_hand(mob/user as mob)
- . = ..()
- if (.)
- return
- var/dat
-
- // The browse() text, similar to ED-209s and beepskies.
- if(!(src.lasercolor))//Lasertag turrets have less options
- dat += text({"
-Automatic Portable Turret Installation
-Status: [] "},
-
-"[src.on ? "On" : "Off"]" )
-
-
- user << browse("Automatic Portable Turret Installation[dat]", "window=autosec")
- onclose(user, "autosec")
- return
-
-/obj/machinery/porta_turret/Topic(href, href_list)
- if (..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- if ((href_list["power"]) && (src.allowed(usr)))
- if(anchored) // you can't turn a turret on/off if it's not anchored/secured
- on = !on // toggle on/off
- else
- usr << "\red It has to be secured first!"
-
- updateUsrDialog()
- return
-
- switch(href_list["operation"])
- // toggles customizable behavioural protocols
-
- if ("authweapon")
- src.auth_weapons = !src.auth_weapons
- if ("checkrecords")
- src.check_records = !src.check_records
- if ("shootcrooks")
- src.criminals = !src.criminals
- if("shootall")
- stun_all = !stun_all
- updateUsrDialog()
-
-
-/obj/machinery/porta_turret/power_change()
+ if(/obj/item/weapon/gun/energy/gun/nuclear)
+ eprojectile = /obj/item/projectile/beam //If it has, going to kill mode
+ eshot_sound = 'sound/weapons/Laser.ogg'
+ egun = 1
+
+ if(/obj/item/weapon/gun/energy/gun/turret)
+ eprojectile = /obj/item/projectile/beam //If it has, going to copypaste mode
+ eshot_sound = 'sound/weapons/Laser.ogg'
+ egun = 1
+/obj/machinery/porta_turret/update_icon()
if(!anchored)
icon_state = "turretCover"
return
if(stat & BROKEN)
- icon_state = "[lasercolor]destroyed_target_prism"
+ icon_state = "destroyed_target_prism"
else
- if( powered() )
- if (on)
- if (installation == /obj/item/weapon/gun/energy/laser || installation == /obj/item/weapon/gun/energy/pulse_rifle)
- // laser guns and pulse rifles have an orange icon
- icon_state = "[lasercolor]orange_target_prism"
+ if(powered())
+ if(enabled)
+ if(iconholder)
+ //lasers have a orange icon
+ icon_state = "orange_target_prism"
else
- // anything else has a blue icon
- icon_state = "[lasercolor]target_prism"
+ //almost everything has a blue icon
+ icon_state = "target_prism"
else
- icon_state = "[lasercolor]grey_target_prism"
- stat &= ~NOPOWER
+ icon_state = "grey_target_prism"
else
- spawn(rand(0, 15))
- src.icon_state = "[lasercolor]grey_target_prism"
- stat |= NOPOWER
+ icon_state = "grey_target_prism"
+
+/obj/machinery/porta_turret/Destroy()
+ //deletes its own cover with it
+ qdel(cover) // qdel
+ ..()
+
+/obj/machinery/porta_turret/proc/isLocked(mob/user)
+ if(ailock && (isrobot(user) || isAI(user)))
+ user << "There seems to be a firewall preventing you from accessing this device."
+ return 1
+
+ if(locked && !(isrobot(user) || isAI(user)))
+ user << "Access denied."
+ return 1
+
+ return 0
+
+/obj/machinery/porta_turret/attack_ai(mob/user)
+ if(isLocked(user))
+ return
+
+ ui_interact(user)
+
+/obj/machinery/porta_turret/attack_hand(mob/user)
+ if(isLocked(user))
+ return
+
+ ui_interact(user)
+
+/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["access"] = !isLocked(user)
+ data["locked"] = locked
+ data["enabled"] = enabled
+ data["is_lethal"] = 1
+ data["lethal"] = lethal
+
+ if(data["access"])
+ var/settings[0]
+ settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
+ settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
+ settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
+ settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
+ settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
+ settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
+ data["settings"] = settings
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/porta_turret/proc/HasController()
+ var/area/A = get_area(src)
+ return A && A.turret_controls.len > 0
+
+/obj/machinery/porta_turret/CanUseTopic(var/mob/user)
+ if(HasController())
+ user << "Turrets can only be controlled using the assigned turret controller."
+ return STATUS_CLOSE
+
+ if(isLocked(user))
+ return STATUS_CLOSE
+
+ if(!anchored)
+ usr << "\The [src] has to be secured first!"
+ return STATUS_CLOSE
+
+ return STATUS_INTERACTIVE
+
+/obj/machinery/porta_turret/Topic(href, href_list, var/nowindow = 0)
+ if(..())
+ return 1
+
+ if(href_list["command"] && href_list["value"])
+ var/value = text2num(href_list["value"])
+ if(href_list["command"] == "enable")
+ enabled = value
+ else if(href_list["command"] == "lethal")
+ lethal = value
+ else if(href_list["command"] == "check_synth")
+ check_synth = value
+ else if(href_list["command"] == "check_weapons")
+ check_weapons = value
+ else if(href_list["command"] == "check_records")
+ check_records = value
+ else if(href_list["command"] == "check_arrest")
+ check_arrest = value
+ else if(href_list["command"] == "check_access")
+ check_access = value
+ else if(href_list["command"] == "check_anomalies")
+ check_anomalies = value
+
+ return 1
+
+/obj/machinery/porta_turret/power_change()
+ if(powered())
+ stat &= ~NOPOWER
+ update_icon()
+ else
+ spawn(rand(0, 15))
+ stat |= NOPOWER
+ update_icon()
-
-/obj/machinery/porta_turret/attackby(obj/item/W as obj, mob/user as mob)
+/obj/machinery/porta_turret/attackby(obj/item/I, mob/user)
if(stat & BROKEN)
- if(istype(W, /obj/item/weapon/crowbar))
+ if(istype(I, /obj/item/weapon/crowbar))
+ //If the turret is destroyed, you can remove it with a crowbar to
+ //try and salvage its components
+ user << "You begin prying the metal coverings off."
+ if(do_after(user, 20))
+ if(prob(70))
+ user << "You remove the turret and salvage some components."
+ if(installation)
+ var/obj/item/weapon/gun/energy/Gun = new installation(loc)
+ Gun.power_supply.charge = gun_charge
+ Gun.update_icon()
+ if(prob(50))
+ new /obj/item/stack/sheet/metal(loc, rand(1,4))
+ if(prob(50))
+ new /obj/item/device/assembly/prox_sensor(loc)
+ else
+ user << "You remove the turret but did not manage to salvage anything."
+ qdel(src) // qdel
- // If the turret is destroyed, you can remove it with a crowbar to
- // try and salvage its components
- user << "You begin prying the metal coverings off."
- sleep(20)
- if(prob(70))
- user << "You remove the turret and salvage some components."
- if(installation)
- var/obj/item/weapon/gun/energy/Gun = new installation(src.loc)
- Gun.power_supply.charge=gun_charge
- Gun.update_icon()
- lasercolor = null
- if(prob(50)) new /obj/item/stack/sheet/metal( loc, rand(1,4))
- if(prob(50)) new /obj/item/device/assembly/prox_sensor(locate(x,y,z))
- else
- user << "You remove the turret but did not manage to salvage anything."
- del(src)
+ else if((istype(I, /obj/item/weapon/wrench)))
+ if(enabled || raised)
+ user << "You cannot unsecure an active turret!"
+ return
+ if(wrenching)
+ user << "Someone is already [anchored ? "un" : ""]securing the turret!"
+ return
+ if(!anchored && isinspace())
+ user << "Cannot secure turrets in space!"
+ return
+ user.visible_message( \
+ "[user] begins [anchored ? "un" : ""]securing the turret.", \
+ "You begin [anchored ? "un" : ""]securing the turret." \
+ )
- if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged))
- // Emagging the turret makes it go bonkers and stun everyone. It also makes
- // the turret shoot much, much faster.
+ wrenching = 1
+ if(do_after(user, 50))
+ //This code handles moving the turret around. After all, it's a portable turret!
+ if(!anchored)
+ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1)
+ anchored = 1
+ invisibility = INVISIBILITY_LEVEL_TWO
+ update_icon()
+ user << "You secure the exterior bolts on the turret."
+ create_cover()
+ else if(anchored)
+ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1)
+ anchored = 0
+ user << "You unsecure the exterior bolts on the turret."
+ invisibility = 0
+ update_icon()
+ qdel(cover) //deletes the cover, and the turret instance itself becomes its own cover. - qdel
+ wrenching = 0
- user << "\red You short out [src]'s threat assessment circuits."
- spawn(0)
- for(var/mob/O in hearers(src, null))
- O.show_message("\red [src] hums oddly...", 1)
- emagged = 1
- src.on = 0 // turns off the turret temporarily
- sleep(60) // 6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
- on = 1 // turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
-
- else if((istype(W, /obj/item/weapon/wrench)) && (!on))
- if(raised) return
- // This code handles moving the turret around. After all, it's a portable turret!
-
- if(!anchored)
- anchored = 1
- invisibility = INVISIBILITY_LEVEL_TWO
- icon_state = "[lasercolor]grey_target_prism"
- user << "You secure the exterior bolts on the turret."
- cover=new/obj/machinery/porta_turret_cover(src.loc) // create a new turret. While this is handled in process(), this is to workaround a bug where the turret becomes invisible for a split second
- cover.Parent_Turret = src // make the cover's parent src
+ else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda))
+ //Behavior lock/unlock mangement
+ if(allowed(user))
+ locked = !locked
+ user << "Controls are now [locked ? "locked" : "unlocked"]."
+ updateUsrDialog()
else
- anchored = 0
- user << "You unsecure the exterior bolts on the turret."
- icon_state = "turretCover"
- invisibility = 0
- del(cover) // deletes the cover, and the turret instance itself becomes its own cover.
-
- else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- // Behavior lock/unlock mangement
- if (allowed(user))
- locked = !src.locked
- user << "Controls are now [locked ? "locked." : "unlocked."]"
- else
- user << "\red Access denied."
+ user << "Access denied."
else
- // if the turret was attacked with the intention of harming it:
- src.health -= W.force * 0.5
- if (src.health <= 0)
- src.die()
- if ((W.force * 0.5) > 1) // if the force of impact dealt at least 1 damage, the turret gets pissed off
+ //if the turret was attacked with the intention of harming it:
+ user.changeNext_move(CLICK_CD_MELEE)
+ take_damage(I.force * 0.5)
+ playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1)
+ if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off
if(!attacked && !emagged)
attacked = 1
spawn()
sleep(60)
attacked = 0
+
..()
+
+/obj/machinery/porta_turret/emag_act(user as mob)
+ if(!emagged)
+ //Emagging the turret makes it go bonkers and stun everyone. It also makes
+ //the turret shoot much, much faster.
+ user << "You short out [src]'s threat assessment circuits."
+ visible_message("[src] hums oddly...")
+ emagged = 1
+ iconholder = 1
+ controllock = 1
+ enabled = 0 //turns off the turret temporarily
+ sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
+ enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
+/obj/machinery/porta_turret/proc/take_damage(var/force)
+ health -= force
+ if (force > 5 && prob(45))
+ spark_system.start()
+ if(health <= 0)
+ die() //the death process :(
+/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj)
-/obj/machinery/porta_turret/bullet_act(var/obj/item/projectile/Proj)
- if(on)
+ if(Proj.damage_type == HALLOSS || Proj.damage_type == STAMINA)
+ return
+
+ if(enabled)
if(!attacked && !emagged)
attacked = 1
spawn()
sleep(60)
attacked = 0
- if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
- health -= Proj.damage
-
..()
- if(prob(45) && Proj.damage > 0) src.spark_system.start()
- if (src.health <= 0)
- src.die() // the death process :(
- if((src.lasercolor == "b") && (src.disabled == 0))
- if(istype(Proj, /obj/item/projectile/lasertag/red))
- src.disabled = 1
- del (Proj)
- sleep(100)
- src.disabled = 0
- if((src.lasercolor == "r") && (src.disabled == 0))
- if(istype(Proj, /obj/item/projectile/lasertag/blue))
- src.disabled = 1
- del (Proj)
- sleep(100)
- src.disabled = 0
- return
+ if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
+ take_damage(Proj.damage)
/obj/machinery/porta_turret/emp_act(severity)
- if(on)
- // if the turret is on, the EMP no matter how severe disables the turret for a while
- // and scrambles its settings, with a slight chance of having an emag effect
- check_records=pick(0,1)
- criminals=pick(0,1)
- auth_weapons=pick(0,1)
- stun_all=pick(0,0,0,0,1) // stun_all is a pretty big deal, so it's least likely to get turned on
- if(prob(5)) emagged=1
- on=0
+ if(enabled)
+ //if the turret is on, the EMP no matter how severe disables the turret for a while
+ //and scrambles its settings, with a slight chance of having an emag effect
+ check_arrest = prob(50)
+ check_records = prob(50)
+ check_weapons = prob(50)
+ check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on
+ check_anomalies = prob(50)
+ if(prob(5))
+ emagged = 1
+
+ enabled=0
sleep(rand(60,600))
- if(!on)
- on=1
+ if(!enabled)
+ enabled=1
..()
/obj/machinery/porta_turret/ex_act(severity)
- if(severity >= 3) // turret dies if an explosion touches it!
- qdel(src)
- else
- src.die()
-
-/obj/machinery/porta_turret/proc/die() // called when the turret dies, ie, health <= 0
- src.health = 0
- src.density = 0
- src.stat |= BROKEN // enables the BROKEN bit
- src.icon_state = "[lasercolor]destroyed_target_prism"
- invisibility=0
- src.spark_system.start() // creates some sparks because they look cool
- src.density=1
- qdel(cover) // deletes the cover - no need on keeping it there!
+ switch (severity)
+ if (1)
+ qdel(src)
+ if (2)
+ if (prob(25))
+ qdel(src)
+ else
+ take_damage(150) //should instakill most turrets
+ if (3)
+ take_damage(50)
+/obj/machinery/porta_turret/proc/die() //called when the turret dies, ie, health <= 0
+ health = 0
+ density = 0
+ stat |= BROKEN //enables the BROKEN bit
+ invisibility = 0
+ spark_system.start() //creates some sparks because they look cool
+ density = 1
+ update_icon()
+ del(cover) //deletes the cover - no need on keeping it there! - del
+/obj/machinery/porta_turret/proc/create_cover()
+ if(cover == null && anchored)
+ cover = new /obj/machinery/porta_turret_cover(loc) //if the turret has no cover and is anchored, give it a cover
+ cover.Parent_Turret = src //assign the cover its Parent_Turret, which would be this (src)
/obj/machinery/porta_turret/process()
- // the main machinery process
+ //the main machinery process
- //set background = 1
+ set background = BACKGROUND_ENABLED
- if(src.cover==null && anchored) // if it has no cover and is anchored
- if (stat & BROKEN) // if the turret is borked
- del(cover) // delete its cover, assuming it has one. Workaround for a pesky little bug
+ if(cover == null && anchored) //if it has no cover and is anchored
+ if(stat & BROKEN) //if the turret is borked
+ qdel(cover) //delete its cover, assuming it has one. Workaround for a pesky little bug - qdel
else
-
- src.cover = new /obj/machinery/porta_turret_cover(src.loc) // if the turret has no cover and is anchored, give it a cover
- src.cover.Parent_Turret = src // assign the cover its Parent_Turret, which would be this (src)
+ create_cover()
if(stat & (NOPOWER|BROKEN))
- // if the turret has no power or is broken, make the turret pop down if it hasn't already
+ //if the turret has no power or is broken, make the turret pop down if it hasn't already
popDown()
return
- if(!on)
- // if the turret is off, make it pop down
+ if(!enabled)
+ //if the turret is off, make it pop down
popDown()
return
- var/list/targets = list() // list of primary targets
- var/list/secondarytargets = list() // targets that are least important
+ var/list/targets = list() //list of primary targets
+ var/list/secondarytargets = list() //targets that are least important
- if(src.check_anomalies) // if its set to check for xenos/carps, check for non-mob "crittersssss"(And simple_animals)
- for(var/mob/living/simple_animal/C in view(7,src))
- if(C.stat)
- continue
- // Ignore lazarus-injected mobs.
- if(C.faction == "lazarus")
- continue
- targets += C
+ for(var/obj/mecha/ME in view(7,src))
+ assess_and_assign(ME.occupant, targets, secondarytargets)
- for (var/mob/living/carbon/C in view(7,src)) // loops through all living carbon-based lifeforms in view(12)
- if(istype(C, /mob/living/carbon/alien) && src.check_anomalies) // git those fukken xenos
- if(!C.stat) // if it's dead/dying, there's no need to keep shooting at it.
- targets += C
+ for(var/obj/spacepod/SP in view(7,src))
+ assess_and_assign(SP.occupant, targets, secondarytargets)
+
+ for(var/obj/vehicle/train/T in view(7,src))
+ assess_and_assign(T.load, targets, secondarytargets)
- else
- if(emagged) // if emagged, HOLY SHIT EVERYONE IS DANGEROUS beep boop beep
- targets += C
- else
- if (C.stat || C.handcuffed) // if the perp is handcuffed or dead/dying, no need to bother really
- continue // move onto next potential victim!
+ for(var/mob/living/C in view(7,src)) //loops through all living lifeforms in view
+ assess_and_assign(C, targets, secondarytargets)
- var/dst = get_dist(src, C) // if it's too far away, why bother?
- if (dst > 7)
- continue
+ if(!tryToShootAt(targets))
+ if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets
+ spawn()
+ popDown() // no valid targets, close the cover
- if(ai) // If it's set to attack all nonsilicons, target them!
- if(C.lying)
- if(lasercolor)
- continue
- else
- secondarytargets += C
- continue
- else
- targets += C
- continue
+/obj/machinery/porta_turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets)
+ switch(assess_living(L))
+ if(TURRET_PRIORITY_TARGET)
+ targets += L
+ if(TURRET_SECONDARY_TARGET)
+ secondarytargets += L
- if (istype(C, /mob/living/carbon/human)) // if the target is a human, analyze threat level
- if(src.assess_perp(C)<4)
- continue // if threat level < 4, keep going
+/obj/machinery/porta_turret/proc/assess_living(var/mob/living/L)
+ if(!istype(L))
+ return TURRET_NOT_TARGET
- else if (istype(C, /mob/living/carbon/monkey))
- continue // Don't target monkeys or borgs/AIs you dumb shit
+ if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var
+ return TURRET_NOT_TARGET
- if (C.lying) // if the perp is lying down, it's still a target but a less-important target
- secondarytargets += C
- continue
+ if(!L)
+ return TURRET_NOT_TARGET
- targets += C // if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee
+ // If emagged not even the dead get a rest
+ if(emagged)
+ return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET
- if (targets.len>0) // if there are targets to shoot
+ if(issilicon(L)) // Don't target silica
+ return TURRET_NOT_TARGET
- var/atom/t = pick(targets) // pick a perp from the list of targets. Targets go first because they are the most important
+ if(L.stat) //if the perp is dead/dying, no need to bother really
+ return TURRET_NOT_TARGET //move onto next potential victim!
- if (istype(t, /mob/living)) // if a mob
- var/mob/living/M = t // simple typecasting
- if (M.stat!=2) // if the target is not dead
- spawn() popUp() // pop the turret up if it's not already up.
- dir=get_dir(src,M) // even if you can't shoot, follow the target
- spawn() shootAt(M) // shoot the target, finally
+ var/dst = get_dist(src, L) //if it's too far away, why bother?
+ if(dst > 7)
+ return 0
- else
- if(secondarytargets.len>0) // if there are no primary targets, go for secondary targets
- var/mob/t = pick(secondarytargets)
- if (istype(t, /mob/living))
- if (t.stat!=2)
- spawn() popUp()
- dir=get_dir(src,t)
- shootAt(t)
- else
- spawn() popDown()
+ if(check_synth) //If it's set to attack all non-silicons, target them!
+ if(L.lying)
+ return lethal ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET
+ return TURRET_PRIORITY_TARGET
-/obj/machinery/porta_turret/proc
- popUp() // pops the turret up
- if(disabled)
- return
- if(raising || raised) return
- if(stat & BROKEN) return
- invisibility=0
- raising=1
- flick("popup",cover)
- sleep(5)
- sleep(5)
- raising=0
- cover.icon_state="openTurretCover"
- raised=1
- layer=4
+ if(iscuffed(L)) // If the target is handcuffed, leave it alone
+ return TURRET_NOT_TARGET
- popDown() // pops the turret down
- if(disabled)
- return
- if(raising || !raised) return
- if(stat & BROKEN) return
- layer=3
- raising=1
- flick("popdown",cover)
- sleep(10)
- raising=0
- cover.icon_state="turretCover"
- raised=0
- invisibility=2
- icon_state="[lasercolor]grey_target_prism"
+ if(isanimal(L) || ismonkey(L)) // Animals are not so dangerous
+ return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET
+ if(isalien(L)) // Xenos are dangerous
+ return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET
+ if(ishuman(L)) //if the target is a human, analyze threat level
+ if(assess_perp(L, check_weapons, check_records, check_arrest) < 4)
+ return TURRET_NOT_TARGET //if threat level < 4, keep going
-/obj/machinery/porta_turret/proc/assess_perp(mob/living/carbon/human/perp as mob)
- var/threatcount = 0 // the integer returned
+ if(L.lying) //if the perp is lying down, it's still a target but a less-important target
+ return lethal ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET
- if(src.emagged) return 10 // if emagged, always return 10.
+ return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee
- if((stun_all && !src.allowed(perp)) || attacked && !src.allowed(perp))
- // if the turret has been attacked or is angry, target all non-sec people
- if(!src.allowed(perp))
- return 10
+/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets)
+ if(targets.len && last_target && (last_target in targets) && target(last_target))
+ return 1
- if(auth_weapons) // check for weapon authorization
- if((isnull(perp.wear_id)) || (istype(perp.wear_id.GetID(), /obj/item/weapon/card/id/syndicate)))
-
- if((src.allowed(perp)) && !(src.lasercolor)) // if the perp has security access, return 0
- return 0
-
- if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/projectile/shotgun)) || istype(perp.l_hand, /obj/item/weapon/melee/baton))
- threatcount += 4
-
- if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/projectile/shotgun)) || istype(perp.r_hand, /obj/item/weapon/melee/baton))
- threatcount += 4
-
- if(istype(perp.belt, /obj/item/weapon/gun) || istype(perp.belt, /obj/item/weapon/melee/baton))
- threatcount += 2
-
- if((src.lasercolor) == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve
- threatcount = 0//But does not target anyone else
- if(istype(perp.wear_suit, /obj/item/clothing/suit/redtag))
- threatcount += 4
- if((istype(perp.r_hand,/obj/item/weapon/gun/energy/laser/redtag)) || (istype(perp.l_hand,/obj/item/weapon/gun/energy/laser/redtag)))
- threatcount += 4
- if(istype(perp.belt, /obj/item/weapon/gun/energy/laser/redtag))
- threatcount += 2
-
- if((src.lasercolor) == "r")
- threatcount = 0
- if(istype(perp.wear_suit, /obj/item/clothing/suit/bluetag))
- threatcount += 4
- if((istype(perp.r_hand,/obj/item/weapon/gun/energy/laser/bluetag)) || (istype(perp.l_hand,/obj/item/weapon/gun/energy/laser/bluetag)))
- threatcount += 4
- if(istype(perp.belt, /obj/item/weapon/gun/energy/laser/bluetag))
- threatcount += 2
-
- if (src.check_records) // if the turret can check the records, check if they are set to *Arrest* on records
- for (var/datum/data/record/E in data_core.general)
-
- var/perpname = perp.name
- if (perp.wear_id)
- var/obj/item/weapon/card/id/id = perp.wear_id.GetID()
- if (id)
- perpname = id.registered_name
-
- if (E.fields["name"] == perpname)
- for (var/datum/data/record/R in data_core.security)
- if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*"))
- threatcount = 4
- break
-
-
-
- return threatcount
-
-
-
-
-
-/obj/machinery/porta_turret/proc/shootAt(var/atom/movable/target) // shoots at a target
+ while(targets.len > 0)
+ var/mob/living/M = pick(targets)
+ targets -= M
+ if(target(M))
+ return 1
+
+/obj/machinery/porta_turret/proc/popUp() //pops the turret up
if(disabled)
return
+ if(raising || raised)
+ return
+ if(stat & BROKEN)
+ return
+ playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
+ invisibility = 0
+ raising = 1
+ flick("popup", cover)
+ sleep(10)
+ raising = 0
+ cover.icon_state = "openTurretCover"
+ raised = 1
+ layer = 4
+ update_icon()
- if(lasercolor && (istype(target,/mob/living/carbon/human)))
- var/mob/living/carbon/human/H = target
- if(H.lying)
+/obj/machinery/porta_turret/proc/popDown() //pops the turret down
+ last_target = null
+ if(disabled)
+ return
+ if(raising || !raised)
+ return
+ if(stat & BROKEN)
+ return
+ playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
+ layer = 3
+ raising = 1
+ flick("popdown", cover)
+ sleep(10)
+ raising = 0
+ cover.icon_state = "turretCover"
+ raised = 0
+ invisibility = INVISIBILITY_LEVEL_TWO
+ update_icon()
+
+/obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp)
+ if((check_access || attacked) && !allowed(perp))
+ //if the turret has been attacked or is angry, target all non-authorized personnel, see req_access
+ return 10
+
+ return ..()
+
+/obj/machinery/porta_turret/proc/target(var/mob/living/target)
+ if(disabled)
+ return
+ if(target)
+ last_target = target
+ spawn()
+ popUp() //pop the turret up if it's not already up.
+ dir = get_dir(src, target) //even if you can't shoot, follow the target
+ spawn()
+ shootAt(target)
+ return 1
+ return
+
+/obj/machinery/porta_turret/proc/shootAt(var/mob/living/target)
+ //any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power!
+ if(!emagged) //if it hasn't been emagged, it has to obey a cooldown rate
+ if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged
return
-
- if(!emagged) // if it hasn't been emagged, it has to obey a cooldown rate
- if(last_fired || !raised) return // prevents rapid-fire shooting, unless it's been emagged
last_fired = 1
spawn()
sleep(shot_delay)
@@ -638,47 +591,65 @@ Status: [] "},
var/turf/T = get_turf(src)
var/turf/U = get_turf(target)
- if (!istype(T) || !istype(U))
+ if(!istype(T) || !istype(U))
return
- if (!raised) // the turret has to be raised in order to fire - makes sense, right?
+ if(!raised) //the turret has to be raised in order to fire - makes sense, right?
return
- // any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power!
-
- if(iconholder)
- icon_state = "[lasercolor]target_prism"
- else
- icon_state = "[lasercolor]orange_target_prism"
- if(sound)
- playsound(src.loc, 'sound/weapons/Taser.ogg', 75, 1)
+ update_icon()
var/obj/item/projectile/A
- if(emagged)
- A = new eprojectile( loc )
+ if(emagged || lethal)
+ A = new eprojectile(loc)
+ playsound(loc, eshot_sound, 75, 1)
else
- A = new projectile( loc )
- A.original = target.loc
- if(!emagged)
- use_power(reqpower)
- else
- use_power((reqpower*2))
- // Shooting Code:
+ A = new projectile(loc)
+ playsound(loc, shot_sound, 75, 1)
+ A.original = target
+
+ // Lethal/emagged turrets use twice the power due to higher energy beams
+ // Emagged turrets again use twice as much power due to higher firing rates
+ use_power(reqpower * (2 * (emagged || lethal)) * (2 * emagged))
+
+ //Shooting Code:
A.current = T
A.yo = U.y - T.y
A.xo = U.x - T.x
- spawn( 1 )
+ spawn(1)
A.process()
- return
+/datum/turret_checks
+ var/enabled
+ var/lethal
+ var/check_synth
+ var/check_access
+ var/check_records
+ var/check_arrest
+ var/check_weapons
+ var/check_anomalies
+ var/ailock
+/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
+ if(controllock)
+ return
+ src.enabled = TC.enabled
+ src.lethal = TC.lethal
+ src.iconholder = TC.lethal
+
+ check_synth = TC.check_synth
+ check_access = TC.check_access
+ check_records = TC.check_records
+ check_arrest = TC.check_arrest
+ check_weapons = TC.check_weapons
+ check_anomalies = TC.check_anomalies
+ ailock = TC.ailock
+
+ src.power_change()
/*
-
Portable turret constructions
-
Known as "turret frame"s
-
*/
/obj/machinery/porta_turret_construct
@@ -686,66 +657,66 @@ Status: [] "},
icon = 'icons/obj/turrets.dmi'
icon_state = "turret_frame"
density=1
- var/build_step = 0 // the current step in the building process
- var/finish_name="turret" // the name applied to the product turret
- var/installation = null // the gun type installed
- var/gun_charge = 0 // the gun charge of the gun type installed
+ var/target_type = /obj/machinery/porta_turret // The type we intend to build
+ var/build_step = 0 //the current step in the building process
+ var/finish_name="turret" //the name applied to the product turret
+ var/installation = null //the gun type installed
+ var/gun_charge = 0 //the gun charge of the gun type installed
-
-/obj/machinery/porta_turret_construct/attackby(obj/item/W as obj, mob/user as mob)
-
- // this is a bit unweildy but self-explanitory
+/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user)
+ //this is a bit unwieldy but self-explanatory
switch(build_step)
- if(0) // first step
- if(istype(W, /obj/item/weapon/wrench) && !anchored)
- playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
- user << "\blue You secure the external bolts."
+ if(0) //first step
+ if(istype(I, /obj/item/weapon/wrench) && !anchored)
+ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1)
+ user << "You secure the external bolts."
anchored = 1
build_step = 1
return
- else if(istype(W, /obj/item/weapon/crowbar) && !anchored)
- playsound(src.loc, 'sound/items/Crowbar.ogg', 75, 1)
- user << "You dismantle the turret construction."
+ else if(istype(I, /obj/item/weapon/crowbar) && !anchored)
+ playsound(loc, 'sound/items/Crowbar.ogg', 75, 1)
+ user << "You dismantle the turret construction."
new /obj/item/stack/sheet/metal( loc, 5)
- del(src)
+ qdel(src) // qdel
return
if(1)
- if(istype(W, /obj/item/stack/sheet/metal))
- if(W:amount>=2) // requires 2 metal sheets
- user << "\blue You add some metal armor to the interior frame."
+ if(istype(I, /obj/item/stack/sheet/metal))
+ var/obj/item/stack/sheet/metal/M = I
+ if(M.use(2))
+ user << "You add some metal armor to the interior frame."
build_step = 2
- W:amount -= 2
icon_state = "turret_frame2"
- if(W:amount <= 0)
- del(W)
- return
+ else
+ user << "You need two sheets of metal to continue construction."
+ return
- else if(istype(W, /obj/item/weapon/wrench))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
- user << "You unfasten the external bolts."
+ else if(istype(I, /obj/item/weapon/wrench))
+ playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
+ user << "You unfasten the external bolts."
anchored = 0
build_step = 0
return
if(2)
- if(istype(W, /obj/item/weapon/wrench))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
- user << "\blue You bolt the metal armor into place."
+ if(istype(I, /obj/item/weapon/wrench))
+ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1)
+ user << "You bolt the metal armor into place."
build_step = 3
return
- else if(istype(W, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = W
- if(!WT.isOn()) return
- if (WT.get_fuel() < 5) // uses up 5 fuel.
- user << "\red You need more fuel to complete this task."
+ else if(istype(I, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = I
+ if(!WT.isOn())
+ return
+ if(WT.get_fuel() < 5) //uses up 5 fuel.
+ user << "You need more fuel to complete this task."
return
- playsound(src.loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
+ playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
if(do_after(user, 20))
if(!src || !WT.remove_fuel(5, user)) return
build_step = 1
@@ -755,130 +726,143 @@ Status: [] "},
if(3)
- if(istype(W, /obj/item/weapon/gun/energy)) // the gun installation part
+ if(istype(I, /obj/item/weapon/gun/energy)) //the gun installation part
+
+ if(isrobot(user))
+ return
+ var/obj/item/weapon/gun/energy/E = I //typecasts the item to an energy gun
+ if(!user.unEquip(I))
+ user << "\the [I] is stuck to your hand, you cannot put it in \the [src]"
+ return
+ installation = I.type //installation becomes I.type
+ gun_charge = E.power_supply.charge //the gun's charge is stored in gun_charge
+ user << "You add [I] to the turret."
+
+ if(istype(installation, /obj/item/weapon/gun/energy/laser/bluetag) || istype(installation, /obj/item/weapon/gun/energy/laser/redtag))
+ target_type = /obj/machinery/porta_turret/tag
+ else
+ target_type = /obj/machinery/porta_turret
- var/obj/item/weapon/gun/energy/E = W // typecasts the item to an energy gun
- installation = W.type // installation becomes W.type
- gun_charge = E.power_supply.charge // the gun's charge is stored in src.gun_charge
- user << "\blue You add \the [W] to the turret."
build_step = 4
- del(W) // delete the gun :(
+ qdel(I) //delete the gun :( qdel
return
- else if(istype(W, /obj/item/weapon/wrench))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
- user << "You remove the turret's metal armor bolts."
+ else if(istype(I, /obj/item/weapon/wrench))
+ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1)
+ user << "You remove the turret's metal armor bolts."
build_step = 2
return
if(4)
- if(isprox(W))
+ if(isprox(I))
+ if(!user.unEquip(I))
+ user << "\the [I] is stuck to your hand, you cannot put it in \the [src]"
+ return
build_step = 5
- user << "\blue You add the prox sensor to the turret."
- del(W)
+ qdel(I) // qdel
+ user << "You add the prox sensor to the turret."
return
- // attack_hand() removes the gun
+ //attack_hand() removes the gun
if(5)
- if(istype(W, /obj/item/weapon/screwdriver))
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+ if(istype(I, /obj/item/weapon/screwdriver))
+ playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1)
build_step = 6
- user << "\blue You close the internal access hatch."
+ user << "You close the internal access hatch."
return
- // attack_hand() removes the prox sensor
+ //attack_hand() removes the prox sensor
if(6)
- if(istype(W, /obj/item/stack/sheet/metal))
- if(W:amount>=2)
- user << "\blue You add some metal armor to the exterior frame."
+ if(istype(I, /obj/item/stack/sheet/metal))
+ var/obj/item/stack/sheet/metal/M = I
+ if(M.use(2))
+ user << "You add some metal armor to the exterior frame."
build_step = 7
- W:amount -= 2
- if(W:amount <= 0)
- del(W)
- return
+ else
+ user << "You need two sheets of metal to continue construction."
+ return
- else if(istype(W, /obj/item/weapon/screwdriver))
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+ else if(istype(I, /obj/item/weapon/screwdriver))
+ playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1)
build_step = 5
- user << "You open the internal access hatch."
+ user << "You open the internal access hatch."
return
if(7)
- if(istype(W, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = W
+ if(istype(I, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = I
if(!WT.isOn()) return
- if (WT.get_fuel() < 5)
- user << "\red You need more fuel to complete this task."
+ if(WT.get_fuel() < 5)
+ user << "You need more fuel to complete this task."
- playsound(src.loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
+ playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
if(do_after(user, 30))
- if(!src || !WT.remove_fuel(5, user)) return
+ if(!src || !WT.remove_fuel(5, user))
+ return
build_step = 8
- user << "\blue You weld the turret's armor down."
+ user << "You weld the turret's armor down."
- // The final step: create a full turret
- var/obj/machinery/porta_turret/Turret = new/obj/machinery/porta_turret(locate(x,y,z))
+ //The final step: create a full turret
+ var/obj/machinery/porta_turret/Turret = new target_type(loc)
Turret.name = finish_name
- Turret.installation = src.installation
- Turret.gun_charge = src.gun_charge
+ Turret.installation = installation
+ Turret.gun_charge = gun_charge
+ Turret.enabled = 0
+ Turret.setup()
-// Turret.cover=new/obj/machinery/porta_turret_cover(src.loc)
+// Turret.cover=new/obj/machinery/porta_turret_cover(loc)
// Turret.cover.Parent_Turret=Turret
// Turret.cover.name = finish_name
- Turret.New()
- del(src)
+ qdel(src) // qdel
- else if(istype(W, /obj/item/weapon/crowbar))
- playsound(src.loc, 'sound/items/Crowbar.ogg', 75, 1)
- user << "You pry off the turret's exterior armor."
- new /obj/item/stack/sheet/metal( loc, 2)
+ else if(istype(I, /obj/item/weapon/crowbar))
+ playsound(loc, 'sound/items/Crowbar.ogg', 75, 1)
+ user << "You pry off the turret's exterior armor."
+ new /obj/item/stack/sheet/metal(loc, 2)
build_step = 6
return
- if (istype(W, /obj/item/weapon/pen)) // you can rename turrets like bots!
- var/t = input(user, "Enter new turret name", src.name, src.finish_name) as text
- t = copytext(sanitize(t), 1, MAX_MESSAGE_LEN)
- if (!t)
+ if(istype(I, /obj/item/weapon/pen)) //you can rename turrets like bots!
+ var/t = input(user, "Enter new turret name", name, finish_name) as text
+ t = sanitize(copytext(t, 1, MAX_MESSAGE_LEN))
+ if(!t)
return
- if (!in_range(src, usr) && src.loc != usr)
+ if(!in_range(src, usr) && loc != usr)
return
- src.finish_name = t
+ finish_name = t
return
..()
-
-/obj/machinery/porta_turret_construct/attack_hand(mob/user as mob)
+/obj/machinery/porta_turret_construct/attack_hand(mob/user)
switch(build_step)
if(4)
- if(!installation) return
+ if(!installation)
+ return
build_step = 3
- var/obj/item/weapon/gun/energy/Gun = new installation(src.loc)
- Gun.power_supply.charge=gun_charge
+ var/obj/item/weapon/gun/energy/Gun = new installation(loc)
+ Gun.power_supply.charge = gun_charge
Gun.update_icon()
installation = null
gun_charge = 0
- user << "You remove \the [Gun] from the turret frame."
+ user << "You remove [Gun] from the turret frame."
if(5)
- user << "You remove the prox sensor from the turret frame."
- new/obj/item/device/assembly/prox_sensor(locate(x,y,z))
+ user << "You remove the prox sensor from the turret frame."
+ new /obj/item/device/assembly/prox_sensor(loc)
build_step = 4
+/obj/machinery/porta_turret_construct/attack_ai()
+ return
-
-
-
-
-
-
-
-
+/************************
+* PORTABLE TURRET COVER *
+************************/
/obj/machinery/porta_turret_cover
name = "turret"
@@ -889,179 +873,14 @@ Status: [] "},
density = 0
var/obj/machinery/porta_turret/Parent_Turret = null
+/obj/machinery/porta_turret_cover/attack_ai(mob/user)
+ return attack_hand(user)
-
-// The below code is pretty much just recoded from the initial turret object. It's necessary but uncommented because it's exactly the same!
-
-/obj/machinery/porta_turret_cover/attack_ai(mob/user as mob)
- . = ..()
- if (.)
- return
- var/dat
- if(!(Parent_Turret.lasercolor))
- dat += text({"
-Automatic Portable Turret Installation
-Status: [] "},
-
-"[Parent_Turret.on ? "On" : "Off"]" )
-
-
-
- user << browse("Automatic Portable Turret Installation[dat]", "window=autosec")
- onclose(user, "autosec")
- return
+/obj/machinery/porta_turret_cover/attack_hand(mob/user)
+ return Parent_Turret.attack_hand(user)
/obj/machinery/porta_turret_cover/Topic(href, href_list)
- if (..())
- return
- usr.set_machine(src)
- Parent_Turret.add_fingerprint(usr)
- src.add_fingerprint(usr)
- if ((href_list["power"]) && (Parent_Turret.allowed(usr)))
- if(Parent_Turret.anchored)
- if (Parent_Turret.on)
- Parent_Turret.on=0
- else
- Parent_Turret.on=1
- else
- usr << "\red It has to be secured first!"
+ Parent_Turret.Topic(href, href_list, 1) // Calling another object's Topic requires that we claim to not have a window, otherwise BYOND's base proc will runtime.
- updateUsrDialog()
- return
-
- switch(href_list["operation"])
- if ("authweapon")
- Parent_Turret.auth_weapons = !Parent_Turret.auth_weapons
- if ("checkrecords")
- Parent_Turret.check_records = !Parent_Turret.check_records
- if ("shootcrooks")
- Parent_Turret.criminals = !Parent_Turret.criminals
- if("shootall")
- Parent_Turret.stun_all = !Parent_Turret.stun_all
- if("checkxenos")
- Parent_Turret.check_anomalies = !Parent_Turret.check_anomalies
-
- updateUsrDialog()
-
-
-
-/obj/machinery/porta_turret_cover/attackby(obj/item/W as obj, mob/user as mob)
-
- if ((istype(W, /obj/item/weapon/card/emag)) && (!Parent_Turret.emagged))
- user << "\red You short out [Parent_Turret]'s threat assessment circuits."
- spawn(0)
- for(var/mob/O in hearers(Parent_Turret, null))
- O.show_message("\red [Parent_Turret] hums oddly...", 1)
- Parent_Turret.emagged = 1
- Parent_Turret.on = 0
- sleep(40)
- Parent_Turret.on = 1
-
- else if((istype(W, /obj/item/weapon/wrench)) && (!Parent_Turret.on))
- if(Parent_Turret.raised) return
-
- if(!Parent_Turret.anchored)
- Parent_Turret.anchored = 1
- Parent_Turret.invisibility = INVISIBILITY_LEVEL_TWO
- Parent_Turret.icon_state = "grey_target_prism"
- user << "You secure the exterior bolts on the turret."
- else
- Parent_Turret.anchored = 0
- user << "You unsecure the exterior bolts on the turret."
- Parent_Turret.icon_state = "turretCover"
- Parent_Turret.invisibility = 0
- del(src)
-
- else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if (Parent_Turret.allowed(user))
- Parent_Turret.locked = !Parent_Turret.locked
- user << "Controls are now [Parent_Turret.locked ? "locked." : "unlocked."]"
- updateUsrDialog()
- else
- user << "\red Access denied."
-
- else
- Parent_Turret.health -= W.force * 0.5
- if (Parent_Turret.health <= 0)
- Parent_Turret.die()
- if ((W.force * 0.5) > 2)
- if(!Parent_Turret.attacked && !Parent_Turret.emagged)
- Parent_Turret.attacked = 1
- spawn()
- sleep(30)
- Parent_Turret.attacked = 0
- ..()
-
-
-
-
-/obj/machinery/porta_turret/stationary
- emagged = 1
-
- New()
- installation = new/obj/item/weapon/gun/energy/laser(src.loc)
- ..()
+/obj/machinery/porta_turret_cover/attackby(obj/item/I, mob/user)
+ Parent_Turret.attackby(I, user)
diff --git a/code/game/machinery/programmable_unloader.dm b/code/game/machinery/programmable_unloader.dm
index c481d6fa32c..291b5849d2f 100644
--- a/code/game/machinery/programmable_unloader.dm
+++ b/code/game/machinery/programmable_unloader.dm
@@ -144,7 +144,7 @@
/obj/machinery/programmable/Topic(href, href_list)
if(..())
- return
+ return 1
usr.set_machine(src)
add_fingerprint(usr)
switch(href_list["operation"])
@@ -195,20 +195,7 @@
updateUsrDialog()
return
-/obj/machinery/programmable/attackby(obj/item/I as obj, mob/user as mob)
- if(istype(I,/obj/item/weapon/card/emag))
- if(emagged)
- return
- user << "You swipe the unloader with your card. After a moment's grinding, it beeps in a sinister fashion."
- playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
- emagged = 1
- overrides += emag_overrides
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(2, 1, src)
- s.start()
-
- return
+/obj/machinery/programmable/attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I,/obj/item/weapon/wrench)) // code borrowed from pipe dispenser
if (unwrenched==0)
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
@@ -281,7 +268,18 @@
I.loc = src
RefreshParts()
+/obj/machinery/programmable/emag_act(user as mob)
+ if(emagged)
+ return
+ user << "You swipe the unloader with your card. After a moment's grinding, it beeps in a sinister fashion."
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
+ emagged = 1
+ overrides += emag_overrides
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(2, 1, src)
+ s.start()
+ return
/obj/machinery/programmable/process()
if (!output || !input)
@@ -512,7 +510,7 @@
return
playsound(loc, "punch", 25, 1, -1)
- if(M_HULK in H.mutations) damage += 5
+ if(HULK in H.mutations) damage += 5
if(damage < 5)
visible_message("[H] gives \the [src] a weak punch.")
@@ -582,7 +580,7 @@
..()
resetlists()
- attackby(obj/item/I as obj, mob/user as mob)
+ attackby(obj/item/I as obj, mob/user as mob, params)
if(istype(I,/obj/item/device/multitool))
hacking = (hacking?0:1)
if(hacking)
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 758b76814ab..8f79948d09c 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -13,7 +13,7 @@
var/icon_state_charging = "recharger1"
var/icon_state_idle = "recharger0"
-/obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob)
+/obj/machinery/recharger/attackby(obj/item/weapon/G as obj, mob/user as mob, params)
if(istype(user,/mob/living/silicon))
return
if(istype(G, /obj/item/weapon/gun/energy) || istype(G, /obj/item/weapon/melee/baton) || istype(G,/obj/item/device/laptop))
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index eea611886da..6b26c084997 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -19,17 +19,20 @@
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
- component_parts += new /obj/item/weapon/cell/high(src)
+ component_parts += new /obj/item/weapon/stock_parts/cell/high(src)
RefreshParts()
build_icon()
/obj/machinery/recharge_station/upgraded/New()
..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/cyborgrecharger(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor/super(src)
component_parts += new /obj/item/weapon/stock_parts/capacitor/super(src)
component_parts += new /obj/item/weapon/stock_parts/manipulator/pico(src)
- component_parts += new /obj/item/weapon/cell/hyper(src)
+ component_parts += new /obj/item/weapon/stock_parts/cell/hyper(src)
RefreshParts()
+ build_icon()
/obj/machinery/recharge_station/RefreshParts()
recharge_speed = 0
@@ -38,7 +41,7 @@
recharge_speed += C.rating * 100
for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
repairs += M.rating - 1
- for(var/obj/item/weapon/cell/C in component_parts)
+ for(var/obj/item/weapon/stock_parts/cell/C in component_parts)
recharge_speed *= C.maxcharge / 10000
/obj/machinery/recharge_station/process()
@@ -76,7 +79,7 @@
else
icon_state = "borgcharger0"
-/obj/machinery/recharge_station/attackby(obj/item/P as obj, mob/user as mob)
+/obj/machinery/recharge_station/attackby(obj/item/P as obj, mob/user as mob, params)
if (istype(P, /obj/item/weapon/screwdriver))
if(src.occupant)
user << "The maintenance panel is locked."
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 27e46c668b5..3cc7919424b 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -31,14 +31,8 @@ var/const/SAFETY_COOLDOWN = 100
update_icon()
-/obj/machinery/recycler/attackby(var/obj/item/I, var/mob/user)
- if(istype(I, /obj/item/weapon/card/emag) && !emagged)
- emagged = 1
- if(safety_mode)
- safety_mode = 0
- update_icon()
- playsound(src.loc, "sparks", 75, 1, -1)
- else if(istype(I, /obj/item/weapon/screwdriver) && emagged)
+/obj/machinery/recycler/attackby(var/obj/item/I, var/mob/user, params)
+ if(istype(I, /obj/item/weapon/screwdriver) && emagged)
emagged = 0
update_icon()
user << "You reset the crusher to its default factory settings."
@@ -46,6 +40,14 @@ var/const/SAFETY_COOLDOWN = 100
..()
return
add_fingerprint(user)
+
+/obj/machinery/recycler/emag_act(user as mob)
+ if(!emagged)
+ emagged = 1
+ if(safety_mode)
+ safety_mode = 0
+ update_icon()
+ playsound(src.loc, "sparks", 75, 1, -1)
/obj/machinery/recycler/update_icon()
..()
@@ -138,7 +140,7 @@ var/const/SAFETY_COOLDOWN = 100
// Remove and recycle the equipped items.
for(var/obj/item/I in L.get_equipped_items())
- if(L.u_equip(I))
+ if(L.unEquip(I))
recycle(I, 0)
// Instantly lie down, also go unconscious from the pain, before you die.
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index d358e39c67f..ed6e201a3ff 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -55,6 +55,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
var/dpt = ""; //the department which will be receiving the message
var/priority = -1 ; //Priority of the message being sent
luminosity = 0
+ var/datum/announcement/announcement = new
/obj/machinery/requests_console/power_change()
..()
@@ -70,6 +71,10 @@ var/list/obj/machinery/requests_console/allConsoles = list()
/obj/machinery/requests_console/New()
..()
+
+ announcement.title = "[department] announcement"
+ announcement.newscast = 1
+
name = "[department] Requests Console"
allConsoles += src
//req_console_departments += department
@@ -189,7 +194,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
else //main menu
screen = 0
- announceAuth = 0
+ reset_announce()
if (newmessagepriority == 1)
dat += text("There are new messages ")
if (newmessagepriority == 2)
@@ -240,17 +245,13 @@ var/list/obj/machinery/requests_console/allConsoles = list()
if("2") priority = 2
else priority = -1
else
- message = ""
- announceAuth = 0
+ reset_announce()
screen = 0
if(href_list["sendAnnouncement"])
if(!announcementConsole) return
- for(var/mob/M in player_list)
- if(!istype(M,/mob/new_player))
- M << "[department] announcement: [message]"
- announceAuth = 0
- message = ""
+ announcement.Announce(message)
+ reset_announce()
screen = 0
if( href_list["department"] && message )
@@ -357,7 +358,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
return
//err... hacking code, which has no reason for existing... but anyway... it's supposed to unlock priority 3 messanging on that console (EXTREME priority...) the code for that actually exists.
-/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob)
+/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob, params)
/*
if (istype(O, /obj/item/weapon/crowbar))
if(open)
@@ -389,8 +390,9 @@ var/list/obj/machinery/requests_console/allConsoles = list()
var/obj/item/weapon/card/id/ID = O
if (access_RC_announce in ID.GetAccess())
announceAuth = 1
+ announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name
else
- announceAuth = 0
+ reset_announce()
user << "\red You are not authorized to send announcements."
updateUsrDialog()
if (istype(O, /obj/item/weapon/stamp))
@@ -399,3 +401,8 @@ var/list/obj/machinery/requests_console/allConsoles = list()
msgStamped = text("Stamped with the [T.name]")
updateUsrDialog()
return
+
+/obj/machinery/requests_console/proc/reset_announce()
+ announceAuth = 0
+ message = ""
+ announcement.announcer = ""
diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm
index 65f31c94c0d..504f7ef8956 100644
--- a/code/game/machinery/robot_fabricator.dm
+++ b/code/game/machinery/robot_fabricator.dm
@@ -11,7 +11,7 @@
idle_power_usage = 20
active_power_usage = 5000
-/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob)
+/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
if (istype(O, /obj/item/stack/sheet/metal))
if (src.metal_amount < 150000.0)
var/count = 0
diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm
index dbb5a9d2712..f2e45ccdc41 100644
--- a/code/game/machinery/seed_extractor.dm
+++ b/code/game/machinery/seed_extractor.dm
@@ -6,7 +6,7 @@
density = 1
anchored = 1
-obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob)
+obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
// Fruits and vegetables.
if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown))
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index e6225f648d7..07570114f00 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -25,16 +25,7 @@
if(!height || air_group) return 0
else return ..()
-//Looks like copy/pasted code... I doubt 'need_rebuild' is even used here - Nodrak
-/obj/machinery/shield/proc/update_nearby_tiles(need_rebuild)
- if(!air_master) return 0
-
- air_master.mark_for_update(get_turf(src))
-
- return 1
-
-
-/obj/machinery/shield/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/shield/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(!istype(W)) return
//Calculate damage
@@ -47,7 +38,7 @@
if (src.health <= 0)
- visible_message("\blue The [src] dissapates")
+ visible_message("\blue The [src] dissipates")
del(src)
return
@@ -60,7 +51,7 @@
src.health -= max_health*0.75 //3/4 health as damage
if(src.health <= 0)
- visible_message("\blue The [src] dissapates")
+ visible_message("\blue The [src] dissipates")
del(src)
return
@@ -72,7 +63,7 @@
health -= Proj.damage
..()
if(health <=0)
- visible_message("\blue The [src] dissapates")
+ visible_message("\blue The [src] dissipates")
del(src)
return
opacity = 1
@@ -121,7 +112,7 @@
//Handle the destruction of the shield
if (src.health <= 0)
- visible_message("\blue The [src] dissapates")
+ visible_message("\blue The [src] dissipates")
del(src)
return
@@ -250,7 +241,7 @@
user << "The device must first be secured to the floor."
return
-/obj/machinery/shieldgen/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/shieldgen/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/card/emag))
malfunction = 1
update_icon()
@@ -335,7 +326,7 @@
// var/maxshieldload = 200
var/obj/structure/cable/attached // the attached cable
var/storedpower = 0
- flags = FPRINT | CONDUCT
+ flags = CONDUCT
use_power = 0
/obj/machinery/shieldwallgen/proc/power()
@@ -470,7 +461,7 @@
CF.dir = field_dir
-/obj/machinery/shieldwallgen/attackby(obj/item/W, mob/user)
+/obj/machinery/shieldwallgen/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weapon/wrench))
if(active)
user << "Turn off the field generator first."
@@ -498,8 +489,8 @@
user << "\red Access denied."
else
- src.add_fingerprint(user)
- visible_message("\red The [src.name] has been hit with the [W.name] by [user.name]!")
+ add_fingerprint(user)
+ ..()
/obj/machinery/shieldwallgen/proc/cleanup(var/NSEW)
var/obj/machinery/shieldwall/F
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index d2d93afba2c..54355179c8e 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -1,3 +1,4 @@
+var/datum/announcement/minor/slotmachine_announcement = new(do_log = 0)
/obj/machinery/slot_machine
name = "Slot Machine"
desc = "Gambling for the antisocial."
@@ -55,7 +56,7 @@
if (roll == 1)
for(var/mob/O in hearers(src, null))
O.show_message(text("[] says, 'JACKPOT! You win [src.money]!'", src), 1)
- command_alert("Congratulations [usr.name] on winning the Jackpot!", "Jackpot Winner")
+ slotmachine_announcement.Announce("Congratulations [usr.name] on winning the Jackpot!", "Jackpot Winner")
usr.mind.initial_account.money += src.money
src.money = 0
else if (roll > 1 && roll <= 10)
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 9a6849dcdf4..9877ac6f138 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -5,13 +5,12 @@
icon_state = "sheater0"
name = "space heater"
desc = "Made by Space Amish using traditional space techniques, this heater is guaranteed not to set the station on fire."
- var/obj/item/weapon/cell/cell
+ var/obj/item/weapon/stock_parts/cell/cell
var/on = 0
var/open = 0
var/set_temperature = 50 // in celcius, add T0C for kelvin
var/heating_power = 40000
- flags = FPRINT
New()
@@ -51,15 +50,15 @@
cell.emp_act(severity)
..(severity)
- attackby(obj/item/I, mob/user)
- if(istype(I, /obj/item/weapon/cell))
+ attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/weapon/stock_parts/cell))
if(open)
if(cell)
user << "There is already a power cell inside."
return
else
// insert cell
- var/obj/item/weapon/cell/C = usr.get_active_hand()
+ var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand()
if(istype(C))
user.drop_item()
cell = C
@@ -120,8 +119,8 @@
Topic(href, href_list)
- if (usr.stat)
- return
+ if (..())
+ return 1
if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
@@ -144,7 +143,7 @@
if("cellinstall")
if(open && !cell)
- var/obj/item/weapon/cell/C = usr.get_active_hand()
+ var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand()
if(istype(C))
usr.drop_item()
cell = C
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 0b81ccabbeb..66c26a8b654 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -153,7 +153,7 @@
/obj/machinery/suit_storage_unit/Topic(href, href_list) //I fucking HATE this proc
if(..())
- return
+ return 1
if ((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon/ai)))
usr.set_machine(src)
if (href_list["toggleUV"])
@@ -470,7 +470,7 @@
return
-/obj/machinery/suit_storage_unit/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/suit_storage_unit/attackby(obj/item/I as obj, mob/user as mob, params)
if(!src.ispowered)
return
if(istype(I, /obj/item/weapon/screwdriver))
@@ -631,7 +631,7 @@
user << "\blue The console controls are far too complicated for your tiny brain!"
return
-/obj/machinery/suit_cycler/attackby(obj/item/I as obj, mob/user as mob)
+/obj/machinery/suit_cycler/attackby(obj/item/I as obj, mob/user as mob, params)
if(electrified != 0)
if(src.shock(user, 100))
@@ -681,24 +681,6 @@
src.updateUsrDialog()
return
- else if(istype(I,/obj/item/weapon/card/emag))
-
- if(emagged)
- user << "\red The cycler has already been subverted."
- return
-
- var/obj/item/weapon/card/emag/E = I
- src.updateUsrDialog()
- E.uses--
-
- //Clear the access reqs, disable the safeties, and open up all paintjobs.
- user << "\red You run the sequencer across the interface, corrupting the operating protocols."
- departments = list("Engineering","Mining","Medical","Security","Atmos","^%###^%$")
- emagged = 1
- safeties = 0
- req_access = list()
- return
-
else if(istype(I,/obj/item/clothing/head/helmet/space))
if(locked)
@@ -748,6 +730,19 @@
return
..()
+
+/obj/machinery/suit_cycler/emag_act(user as mob)
+ if(emagged)
+ user << "\red The cycler has already been subverted."
+ return
+
+ //Clear the access reqs, disable the safeties, and open up all paintjobs.
+ user << "\red You run the sequencer across the interface, corrupting the operating protocols."
+ departments = list("Engineering","Mining","Medical","Security","Atmos","^%###^%$")
+ emagged = 1
+ safeties = 0
+ req_access = list()
+ return
/obj/machinery/suit_cycler/attack_hand(mob/user as mob)
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index a4cc791bff9..916ae1a23af 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -39,6 +39,8 @@
onclose(user, "syndbeacon")
Topic(href, href_list)
+ if(..())
+ return 1
if(href_list["betraitor"])
if(charges < 1)
src.updateUsrDialog()
@@ -154,7 +156,7 @@
return
- attackby(obj/item/weapon/W as obj, mob/user as mob)
+ attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W,/obj/item/weapon/screwdriver))
if(active)
user << "\red You need to deactivate the beacon first!"
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index fcce1742ff9..314f14e0ffa 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -48,7 +48,7 @@
/obj/machinery/syndicatebomb/update_icon()
icon_state = "[initial(icon_state)][active ? "-active" : "-inactive"][open_panel ? "-wires" : ""]"
-/obj/machinery/syndicatebomb/attackby(var/obj/item/I, var/mob/user)
+/obj/machinery/syndicatebomb/attackby(var/obj/item/I, var/mob/user, params)
if(istype(I, /obj/item/weapon/wrench))
if(!anchored)
if(!isturf(src.loc) || istype(src.loc, /turf/space))
@@ -138,7 +138,7 @@
var/turf/bombturf = get_turf(src)
var/area/A = get_area(bombturf)
if(payload && !istype(payload, /obj/item/weapon/bombcore/training))
- message_admins("[key_name(user)]? has primed a [name] ([payload]) for detonation at [A.name] (JMP).")
+ msg_admin_attack("[key_name(user)]? has primed a [name] ([payload]) for detonation at [A.name] (JMP).")
log_game("[key_name(user)] has primed a [name] ([payload]) for detonation at [A.name]([bombturf.x],[bombturf.y],[bombturf.z])")
payload.adminlog = "The [src.name] that [key_name(user)] had primed detonated!"
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index d2dbe48ce03..995bfe7afac 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -20,7 +20,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
use_power = 1
idle_power_usage = 25
machinetype = 5
- heatgen = 0
delay = 7
circuitboard = "/obj/item/weapon/circuitboard/telecomms/broadcaster"
@@ -59,7 +58,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"],, signal.data["compression"], signal.data["level"], signal.frequency)
+ signal.data["realname"], signal.data["vname"],,
+ signal.data["compression"], signal.data["level"], signal.frequency,
+ signal.data["verb"], signal.data["language"] )
/** #### - Simple Broadcast - #### **/
@@ -84,7 +85,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency)
+ signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency,
+ signal.data["verb"], signal.data["language"])
if(!message_delay)
message_delay = 1
@@ -95,7 +97,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/* --- Do a snazzy animation! --- */
flick("broadcaster_send", src)
-/obj/machinery/telecomms/broadcaster/Destroy()
+/obj/machinery/telecomms/broadcaster/Del()
// In case message_delay is left on 1, otherwise it won't reset the list and people can't say the same thing twice anymore.
if(message_delay)
message_delay = 0
@@ -117,7 +119,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
use_power = 0
idle_power_usage = 0
machinetype = 6
- heatgen = 0
var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal)
@@ -142,19 +143,21 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/datum/radio_frequency/connection = signal.data["connection"]
- if(connection.frequency == SYND_FREQ) // if syndicate broadcast, just
+ if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency)
+ signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency,
+ signal.data["verb"], signal.data["language"])
else
if(intercept)
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
- signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency)
+ signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency,
+ signal.data["verb"], signal.data["language"])
@@ -218,7 +221,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M,
var/vmask, var/vmessage, var/obj/item/device/radio/radio,
var/message, var/name, var/job, var/realname, var/vname,
- var/data, var/compression, var/list/level, var/freq)
+ var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null)
+
/* ###### Prepare the radio connection ###### */
@@ -246,16 +250,14 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
if(R.receive_range(display_freq, level) > -1)
radios += R
- // --- Broadcast to syndicate radio! ---
+ // --- Broadcast to antag radios! ---
else if(data == 3)
-
- var/datum/radio_frequency/syndicateconnection = radio_controller.return_frequency(SYND_FREQ)
-
- for (var/obj/item/device/radio/R in syndicateconnection.devices["[RADIO_CHAT]"])
-
- if(R.receive_range(SYND_FREQ, level) > -1)
- radios += R
+ for(var/antag_freq in ANTAG_FREQS)
+ var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(antag_freq)
+ for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
+ if(R.receive_range(antag_freq, level) > -1)
+ radios += R
// --- Broadcast to ALL radio devices ---
@@ -283,6 +285,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/* --- Loop through the receivers and categorize them --- */
+ if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
+ continue
if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes.
continue
@@ -324,88 +328,17 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
if (length(heard_masked) || length(heard_normal) || length(heard_voice) || length(heard_garbled) || length(heard_gibberish))
/* --- Some miscellaneous variables to format the string output --- */
- var/part_a = "" // goes in the actual output
- var/freq_text // the name of the channel
-
- // --- Set the name of the channel ---
- switch(display_freq)
-
- if(SYND_FREQ)
- freq_text = "#unkn"
- if(COMM_FREQ)
- freq_text = "Command"
- if(1351)
- freq_text = "Science"
- if(1355)
- freq_text = "Medical"
- if(1357)
- freq_text = "Engineering"
- if(1359)
- freq_text = "Security"
- if(1347)
- freq_text = "Supply"
- if(1349)
- freq_text = "Service"
- if(1441)
- freq_text = "Special Ops"
- if(1443)
- freq_text = "Response Team"
- if(1447)
- freq_text = "AI Private"
- //There's probably a way to use the list var of channels in code\game\communications.dm to make the dept channels non-hardcoded, but I wasn't in an experimentive mood. --NEO
-
-
- // --- If the frequency has not been assigned a name, just use the frequency as the name ---
-
- if(!freq_text)
- freq_text = format_frequency(display_freq)
-
- // --- Some more pre-message formatting ---
+ var/freq_text = get_frequency_name(display_freq)
var/part_b_extra = ""
if(data == 3) // intercepted radio message
part_b_extra = " (Intercepted)"
- var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra]" // Tweaked for security headsets -- TLE
+ var/part_a = "\icon[radio]\[[freq_text]\][part_b_extra]" // goes in the actual output
+
+ // --- Some more pre-message formatting ---
+ var/part_b = "" // Tweaked for security headsets -- TLE
var/part_c = ""
- // syndies!
- if (display_freq == SYND_FREQ)
- part_a = ""
-
- // centcomm channels (deathsquid and ert)
- else if (display_freq in CENT_FREQS)
- part_a = ""
-
- // command channel
- else if (display_freq == COMM_FREQ)
- part_a = ""
-
- // AI private channel
- else if (display_freq == 1447)
- part_a = ""
-
- // department radio formatting (poorly optimized, ugh)
- else if (display_freq == SEC_FREQ)
- part_a = ""
-
- else if (display_freq == ENG_FREQ)
- part_a = ""
-
- else if (display_freq == SCI_FREQ)
- part_a = ""
-
- else if (display_freq == MED_FREQ)
- part_a = ""
-
- else if (display_freq == SUP_FREQ) // cargo
- part_a = ""
-
- else if (display_freq == SRV_FREQ) // cargo
- part_a = ""
- // If all else fails and it's a dept_freq, color me purple!
- else if (display_freq in DEPT_FREQS)
- part_a = ""
-
// --- Filter the message; place it in quotes apply a verb ---
@@ -424,133 +357,67 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
//BR.messages_admin += blackbox_admin_msg
if(istype(blackbox))
switch(display_freq)
- if(1459)
+ if(PUB_FREQ)
blackbox.msg_common += blackbox_msg
- if(1351)
+ if(SCI_FREQ)
blackbox.msg_science += blackbox_msg
- if(1353)
+ if(COMM_FREQ)
blackbox.msg_command += blackbox_msg
- if(1355)
+ if(MED_FREQ)
blackbox.msg_medical += blackbox_msg
- if(1357)
+ if(ENG_FREQ)
blackbox.msg_engineering += blackbox_msg
- if(1359)
+ if(SEC_FREQ)
blackbox.msg_security += blackbox_msg
- if(1441)
+ if(DTH_FREQ)
blackbox.msg_deathsquad += blackbox_msg
- if(1213)
+ if(SYND_FREQ)
blackbox.msg_syndicate += blackbox_msg
- if(1347)
+ if(SUP_FREQ)
blackbox.msg_cargo += blackbox_msg
- if(1349)
+ if(SRV_FREQ)
blackbox.msg_service += blackbox_msg
else
blackbox.messages += blackbox_msg
//End of research and feedback code.
- var/aitrack = ""
- var/aiopen = ""
/* ###### Send the message ###### */
/* --- Process all the mobs that heard a masked voice (understood) --- */
if (length(heard_masked))
- var/N = name
- var/J = job
- var/rendered = "[part_a][N][part_b][quotedmsg][part_c]"
for (var/mob/R in heard_masked)
- aitrack = ""
- aiopen = "\[OPEN\] "
- if(data == 4)
- aitrack = ""
-
- if(istype(R, /mob/living/silicon/ai))
- R.show_message("[part_a][aitrack][N] ([J]) [aiopen][part_b][quotedmsg][part_c]", 2)
- else
- R.show_message(rendered, 2)
+ R.hear_radio(message,verbage, speaking, part_a, part_b, M, 0, name)
/* --- Process all the mobs that heard the voice normally (understood) --- */
if (length(heard_normal))
- var/rendered = "[part_a][realname][part_b][quotedmsg][part_c]"
-
for (var/mob/R in heard_normal)
- aitrack = ""
- aiopen = "\[OPEN\] "
- if(data == 4)
- aitrack = ""
-
- if(istype(R, /mob/living/silicon/ai))
- R.show_message("[part_a][aitrack][realname] ([job]) [aiopen][part_b][quotedmsg][part_c]", 2)
- else
- R.show_message(rendered, 2)
+ R.hear_radio(message, verbage, speaking, part_a, part_b, M, 0, realname)
/* --- Process all the mobs that heard the voice normally (did not understand) --- */
if (length(heard_voice))
- var/rendered = "[part_a][vname][part_b][vmessage][part_c]"
-
for (var/mob/R in heard_voice)
- aitrack = ""
- aiopen = "\[OPEN\] "
- if(data == 4)
- aitrack = ""
-
-
- if(istype(R, /mob/living/silicon/ai))
- R.show_message("[part_a][aitrack][vname] ([job]) [aiopen][part_b][vmessage]][part_c]", 2)
- else
- R.show_message(rendered, 2)
+ R.hear_radio(message,verbage, speaking, part_a, part_b, M,0, vname)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
if (length(heard_garbled))
- if(M)
- quotedmsg = M.say_quote(stars(message))
- else
- quotedmsg = stars(quotedmsg)
-
- var/rendered = "[part_a][vname][part_b][quotedmsg][part_c]"
-
for (var/mob/R in heard_garbled)
- aitrack = ""
- aiopen = "\[OPEN\] "
- if(data == 4)
- aitrack = ""
-
-
- if(istype(R, /mob/living/silicon/ai))
- R.show_message("[part_a][aitrack][vname][aiopen][part_b][quotedmsg][part_c]", 2)
- else
- R.show_message(rendered, 2)
+ R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, vname)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
if (length(heard_gibberish))
- if(M)
- quotedmsg = M.say_quote(Gibberish(message, compression + 50))
- else
- quotedmsg = Gibberish(quotedmsg, compression + 50)
-
- var/rendered = "[part_a][Gibberish(name, compression + 50)][part_b][quotedmsg][part_c]"
-
for (var/mob/R in heard_gibberish)
- aitrack = ""
- aiopen = "\[OPEN\] "
- if(data == 4)
- aitrack = ""
-
-
- if(istype(R, /mob/living/silicon/ai))
- R.show_message("[part_a][aitrack][Gibberish(realname, compression + 50)] ([Gibberish(job, compression + 50)]) [aiopen][part_b][quotedmsg][part_c]", 2)
- else
- R.show_message(rendered, 2)
-
+ R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1)
+ return 1
/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level)
@@ -588,15 +455,15 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
receive |= R.send_hear(display_freq)
- // --- Broadcast to syndicate radio! ---
+ // --- Broadcast to antag radios! ---
else if(data == 3)
- var/datum/radio_frequency/syndicateconnection = radio_controller.return_frequency(SYND_FREQ)
-
- for (var/obj/item/device/radio/R in syndicateconnection.devices["[RADIO_CHAT]"])
- var/turf/position = get_turf(R)
- if(position && position.z == level)
- receive |= R.send_hear(SYND_FREQ)
+ for(var/freq in ANTAG_FREQS)
+ var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(freq)
+ for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
+ var/turf/position = get_turf(R)
+ if(position && position.z == level)
+ receive |= R.send_hear(freq)
// --- Broadcast to ALL radio devices ---
@@ -621,6 +488,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/* --- Loop through the receivers and categorize them --- */
+ if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
+ continue
// --- Check for compression ---
@@ -648,34 +517,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
/* --- Some miscellaneous variables to format the string output --- */
var/part_a = "" // goes in the actual output
- var/freq_text // the name of the channel
-
- // --- Set the name of the channel ---
- switch(display_freq)
-
- if(SYND_FREQ)
- freq_text = "#unkn"
- if(COMM_FREQ)
- freq_text = "Command"
- if(1351)
- freq_text = "Science"
- if(1355)
- freq_text = "Medical"
- if(1357)
- freq_text = "Engineering"
- if(1359)
- freq_text = "Security"
- if(1347)
- freq_text = "Supply"
- if(1349)
- freq_text = "Service"
- //There's probably a way to use the list var of channels in code\game\communications.dm to make the dept channels non-hardcoded, but I wasn't in an experimentive mood. --NEO
-
-
- // --- If the frequency has not been assigned a name, just use the frequency as the name ---
-
- if(!freq_text)
- freq_text = format_frequency(display_freq)
+ var/freq_text = get_frequency_name(display_freq)
// --- Some more pre-message formatting ---
@@ -689,7 +531,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra]" // Tweaked for security headsets -- TLE
var/part_c = ""
- if (display_freq==SYND_FREQ)
+ if (display_freq in ANTAG_FREQS)
part_a = ""
else if (display_freq==COMM_FREQ)
part_a = ""
@@ -705,25 +547,25 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
//BR.messages_admin += blackbox_admin_msg
if(istype(blackbox))
switch(display_freq)
- if(1459)
+ if(PUB_FREQ)
blackbox.msg_common += blackbox_msg
- if(1351)
+ if(SCI_FREQ)
blackbox.msg_science += blackbox_msg
- if(1353)
+ if(COMM_FREQ)
blackbox.msg_command += blackbox_msg
- if(1355)
+ if(MED_FREQ)
blackbox.msg_medical += blackbox_msg
- if(1357)
+ if(ENG_FREQ)
blackbox.msg_engineering += blackbox_msg
- if(1359)
+ if(SEC_FREQ)
blackbox.msg_security += blackbox_msg
- if(1441)
+ if(DTH_FREQ)
blackbox.msg_deathsquad += blackbox_msg
- if(1213)
+ if(SYND_FREQ)
blackbox.msg_syndicate += blackbox_msg
- if(1347)
+ if(SUP_FREQ)
blackbox.msg_cargo += blackbox_msg
- if(1349)
+ if(SRV_FREQ)
blackbox.msg_service += blackbox_msg
else
blackbox.messages += blackbox_msg
@@ -767,7 +609,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/turf/position = get_turf(src)
return (position.z in signal.data["level"] && signal.data["done"])
-/atom/proc/telecomms_process()
+/atom/proc/telecomms_process(var/do_sleep = 1)
// First, we want to generate a new radio signal
var/datum/signal/signal = new
@@ -785,13 +627,14 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
"done" = 0,
"level" = pos.z // The level it is being broadcasted at.
)
- signal.frequency = 1459// Common channel
+ signal.frequency = PUB_FREQ// Common channel
//#### Sending the signal to all subspace receivers ####//
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
R.receive_signal(signal)
- sleep(rand(10,25))
+ if(do_sleep)
+ sleep(rand(10,25))
//world.log << "Level: [signal.data["level"]] - Done: [signal.data["done"]]"
diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm
index 9bd46e57ce7..2fb2cd3cc69 100644
--- a/code/game/machinery/telecomms/logbrowser.dm
+++ b/code/game/machinery/telecomms/logbrowser.dm
@@ -217,7 +217,7 @@
updateUsrDialog()
return
- attackby(var/obj/item/weapon/D as obj, var/mob/user as mob)
+ attackby(var/obj/item/weapon/D as obj, var/mob/user as mob, params)
if(istype(D, /obj/item/weapon/screwdriver))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -244,9 +244,11 @@
A.icon_state = "4"
A.anchored = 1
del(src)
- else if(istype(D, /obj/item/weapon/card/emag) && !emagged)
+ src.updateUsrDialog()
+ return
+
+ emag_act(user as mob)
+ if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
user << "\blue You you disable the security protocols"
- src.updateUsrDialog()
- return
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index af05db47db6..55a4e604e0f 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -13,7 +13,7 @@
var/construct_op = 0
-/obj/machinery/telecomms/attackby(obj/item/P as obj, mob/user as mob)
+/obj/machinery/telecomms/attackby(obj/item/P as obj, mob/user as mob, params)
// Using a multitool lets you access the receiver's interface
if(istype(P, /obj/item/device/multitool))
diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm
index c1bc006e4b3..5d14fc2f9bc 100644
--- a/code/game/machinery/telecomms/presets.dm
+++ b/code/game/machinery/telecomms/presets.dm
@@ -29,7 +29,7 @@
hide = 1
toggled = 1
//anchored = 1
- //use_power = 0
+ use_power = 0
//idle_power_usage = 0
heatgen = 0
autolinkers = list("c_relay")
@@ -42,6 +42,13 @@
autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical",
"supply", "service", "common", "command", "engineering", "security",
"receiverA", "receiverB", "broadcasterA", "broadcasterB")
+
+/obj/machinery/telecomms/hub/preset_cent
+ id = "CentComm Hub"
+ network = "tcommsat"
+ use_power = 0
+ autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay",
+ "centcomm", "receiverCent", "broadcasterCent")
//Receivers
@@ -67,7 +74,13 @@
for(var/i = 1441, i < 1489, i += 2)
freq_listening |= i
..()
-
+
+/obj/machinery/telecomms/receiver/preset_cent
+ id = "CentComm Receiver"
+ network = "tcommsat"
+ use_power = 0
+ autolinkers = list("receiverCent")
+ freq_listening = list(ERT_FREQ, DTH_FREQ)
//Buses
@@ -99,6 +112,13 @@
for(var/i = 1441, i < 1489, i += 2)
freq_listening |= i
..()
+
+/obj/machinery/telecomms/bus/preset_cent
+ id = "CentComm Bus"
+ network = "tcommsat"
+ use_power = 0
+ freq_listening = list(ERT_FREQ, DTH_FREQ)
+ autolinkers = list("processorCent", "centcomm")
//Processors
@@ -121,6 +141,12 @@
id = "Processor 4"
network = "tcommsat"
autolinkers = list("processor4")
+
+/obj/machinery/telecomms/processor/preset_cent
+ id = "CentComm Processor"
+ network = "tcommsat"
+ use_power = 0
+ autolinkers = list("processorCent")
//Servers
@@ -175,6 +201,11 @@
freq_listening = list(1359)
autolinkers = list("security")
+/obj/machinery/telecomms/server/presets/centcomm
+ id = "CentComm Server"
+ freq_listening = list(ERT_FREQ, DTH_FREQ)
+ use_power = 0
+ autolinkers = list("centcomm")
//Broadcasters
@@ -191,3 +222,9 @@
id = "Broadcaster B"
network = "tcommsat"
autolinkers = list("broadcasterB")
+
+/obj/machinery/telecomms/broadcaster/preset_cent
+ id = "CentComm Broadcaster"
+ network = "tcommsat"
+ use_power = 0
+ autolinkers = list("broadcasterCent")
\ No newline at end of file
diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm
index 995d0fc239f..23969177606 100644
--- a/code/game/machinery/telecomms/telemonitor.dm
+++ b/code/game/machinery/telecomms/telemonitor.dm
@@ -126,7 +126,7 @@
updateUsrDialog()
return
- attackby(var/obj/item/weapon/D as obj, var/mob/user as mob)
+ attackby(var/obj/item/weapon/D as obj, var/mob/user as mob, params)
if(istype(D, /obj/item/weapon/screwdriver))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -153,9 +153,11 @@
A.icon_state = "4"
A.anchored = 1
del(src)
- else if(istype(D, /obj/item/weapon/card/emag) && !emagged)
- playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
- emagged = 1
- user << "\blue You you disable the security protocols"
src.updateUsrDialog()
return
+
+ emag_act(user as mob)
+ if(!emagged)
+ playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
+ emagged = 1
+ user << "\blue You you disable the security protocols"
\ No newline at end of file
diff --git a/code/game/machinery/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm
index fed56a35124..889623b60e6 100644
--- a/code/game/machinery/telecomms/traffic_control.dm
+++ b/code/game/machinery/telecomms/traffic_control.dm
@@ -206,7 +206,7 @@
return
-/obj/machinery/computer/telecomms/traffic/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob)
+/obj/machinery/computer/telecomms/traffic/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob, params)
if(istype(D, /obj/item/weapon/screwdriver))
playsound(get_turf(src), 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
@@ -233,12 +233,14 @@
A.icon_state = "4"
A.anchored = 1
del(src)
- else if(istype(D, /obj/item/weapon/card/emag) && !emagged)
+ src.updateUsrDialog()
+ return
+
+/obj/machinery/computer/telecomms/traffic/emag_act(user as mob)
+ if(!emagged)
playsound(get_turf(src), 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
user << "\blue You you disable the security protocols"
- src.updateUsrDialog()
- return
/obj/machinery/computer/telecomms/traffic/proc/canAccess(var/mob/user)
if(issilicon(user) || in_range(user, src))
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index 9e18e2ce265..9d30d7207af 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -30,20 +30,13 @@
break
return power_station
-/obj/machinery/computer/teleporter/attackby(I as obj, mob/living/user as mob)
- if(istype(I,/obj/item/weapon/card/emag)) // If hit by an emag.
- var/obj/item/weapon/card/emag/E = I
- if(!emagged)
- if(E.uses)
- E.uses--
- emagged = 1
- user << "\blue The teleporter can now lock on to Syndicate beacons!"
- else
- ui_interact(user)
- else if(istype(I, /obj/item/device/gps))
+/obj/machinery/computer/teleporter/attackby(I as obj, mob/living/user as mob, params)
+ if(istype(I, /obj/item/device/gps))
var/obj/item/device/gps/L = I
if(L.locked_location && !(stat & (NOPOWER|BROKEN)))
- user.before_take_item(L)
+ if(!user.unEquip(L))
+ user << "\the [I] is stuck to your hand, you cannot put it in \the [src]"
+ return
L.loc = src
locked = L
user << "You insert the GPS device into the [name]'s slot."
@@ -53,6 +46,13 @@
else
..()
return
+
+/obj/machinery/computer/teleporter/emag_act(user as mob)
+ if(!emagged)
+ emagged = 1
+ user << "\blue The teleporter can now lock on to Syndicate beacons!"
+ else
+ ui_interact(user)
/obj/machinery/computer/teleporter/attack_paw(mob/user)
usr << "You are too primitive to use this computer."
@@ -80,7 +80,8 @@
data["calibrated"] = null
data["accurate"] = null
data["regime"] = regime_set
- data["target"] = (!target) ? "None" : get_area(target)
+ var/area/targetarea = get_area(target)
+ data["target"] = (!target) ? "None" : sanitize(targetarea.name)
data["calibrating"] = calibrating
data["locked"] = locked
@@ -93,7 +94,7 @@
/obj/machinery/computer/teleporter/Topic(href, href_list)
if(..())
- return
+ return 1
if(href_list["eject"])
eject()
@@ -179,7 +180,7 @@
var/turf/T = get_turf(R)
if (!T)
continue
- if(T.z == 2 || T.z > 7)
+ if((T.z in config.admin_levels) || T.z > 7)
continue
if(R.syndicate == 1 && emagged == 0)
continue
@@ -200,7 +201,7 @@
continue
var/turf/T = get_turf(M)
if(!T) continue
- if(T.z == 2) continue
+ if((T.z in config.admin_levels)) continue
var/tmpname = M.real_name
if(areaindex[tmpname])
tmpname = "[tmpname] ([++areaindex[tmpname]])"
@@ -222,7 +223,7 @@
var/turf/T = get_turf(R)
if (!T || !R.teleporter_hub || !R.teleporter_console)
continue
- if(T.z == 2 || T.z > 7)
+ if((T.z in config.admin_levels) || T.z > 7)
continue
var/tmpname = T.loc.name
if(areaindex[tmpname])
@@ -279,7 +280,7 @@
component_parts += new /obj/item/bluespace_crystal/artificial(null)
component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
RefreshParts()
-
+
/obj/machinery/teleport/hub/upgraded/New()
..()
component_parts = list()
@@ -328,7 +329,7 @@
//--FalseIncarnate
return
-/obj/machinery/teleport/hub/attackby(obj/item/W, mob/user)
+/obj/machinery/teleport/hub/attackby(obj/item/W, mob/user, params)
if(default_deconstruction_screwdriver(user, "tele-o", "tele0", W))
return
@@ -413,7 +414,7 @@
teleporter_hub.update_icon()
..()
-/obj/machinery/teleport/station/attackby(var/obj/item/weapon/W, mob/user)
+/obj/machinery/teleport/station/attackby(var/obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/device/multitool) && !panel_open)
var/obj/item/device/multitool/M = W
if(M.buffer && istype(M.buffer, /obj/machinery/teleport/station) && M.buffer != src)
diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm
new file mode 100644
index 00000000000..83bd5b5217d
--- /dev/null
+++ b/code/game/machinery/turret_control.dm
@@ -0,0 +1,221 @@
+////////////////////////
+//Turret Control Panel//
+////////////////////////
+
+/area
+ // Turrets use this list to see if individual power/lethal settings are allowed
+ var/list/turret_controls = list()
+
+/obj/machinery/turretid
+ name = "turret control panel"
+ desc = "Used to control a room's automated defenses."
+ icon = 'icons/obj/machines/turret_control.dmi'
+ icon_state = "control_standby"
+ anchored = 1
+ density = 0
+ var/enabled = 0
+ var/lethal = 0
+ var/locked = 1
+ var/area/control_area //can be area name, path or nothing.
+
+ var/check_arrest = 1 //checks if the perp is set to arrest
+ var/check_records = 1 //checks if a security record exists at all
+ var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
+ var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
+ var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
+ var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
+ var/ailock = 0 //Silicons cannot use this
+
+ req_access = list(access_ai_upload)
+
+/obj/machinery/turretid/stun
+ enabled = 1
+ icon_state = "control_stun"
+
+/obj/machinery/turretid/lethal
+ enabled = 1
+ lethal = 1
+ icon_state = "control_kill"
+
+/obj/machinery/turretid/Del()
+ if(control_area)
+ var/area/A = control_area
+ if(A && istype(A))
+ A.turret_controls -= src
+ ..()
+
+/obj/machinery/turretid/initialize()
+ if(!control_area)
+ var/area/CA = get_area(src)
+ control_area = CA.master
+ else if(istext(control_area))
+ for(var/area/A in world)
+ if(A.name && A.name==control_area)
+ control_area = A.master
+ break
+
+ if(control_area)
+ var/area/A = control_area
+ if(istype(A))
+ A.turret_controls += src
+ else
+ control_area = null
+
+ power_change() //Checks power and initial settings
+ return
+
+/obj/machinery/turretid/proc/isLocked(mob/user)
+ if(ailock && (isrobot(user) || isAI(user)))
+ user << "There seems to be a firewall preventing you from accessing this device."
+ return 1
+
+ if(locked && !(isrobot(user) || isAI(user)))
+ user << "Access denied."
+ return 1
+
+ return 0
+
+/obj/machinery/turretid/attackby(obj/item/weapon/W, mob/user)
+ if(stat & BROKEN)
+ return
+
+ if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
+ if(src.allowed(usr))
+ if(emagged)
+ user << "The turret control is unresponsive."
+ else
+ locked = !locked
+ user << "You [ locked ? "lock" : "unlock"] the panel."
+ return
+ return ..()
+
+/obj/machinery/turretid/emag_act(user as mob)
+ if(!emagged)
+ user << "You short out the turret controls' access analysis module."
+ emagged = 1
+ locked = 0
+ ailock = 0
+ return
+
+/obj/machinery/turretid/attack_ai(mob/user as mob)
+ if(isLocked(user))
+ return
+
+ ui_interact(user)
+
+/obj/machinery/turretid/attack_hand(mob/user as mob)
+ if(isLocked(user))
+ return
+
+ ui_interact(user)
+
+/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["access"] = !isLocked(user)
+ data["locked"] = locked
+ data["enabled"] = enabled
+ data["is_lethal"] = 1
+ data["lethal"] = lethal
+
+ if(data["access"])
+ var/settings[0]
+ settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
+ settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
+ settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
+ settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
+ settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
+ settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
+ data["settings"] = settings
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0)
+ if(..())
+ return 1
+
+ if(isLocked(usr))
+ return 1
+
+ if(href_list["command"] && href_list["value"])
+ var/value = text2num(href_list["value"])
+ if(href_list["command"] == "enable")
+ enabled = value
+ else if(href_list["command"] == "lethal")
+ lethal = value
+ else if(href_list["command"] == "check_synth")
+ check_synth = value
+ else if(href_list["command"] == "check_weapons")
+ check_weapons = value
+ else if(href_list["command"] == "check_records")
+ check_records = value
+ else if(href_list["command"] == "check_arrest")
+ check_arrest = value
+ else if(href_list["command"] == "check_access")
+ check_access = value
+ else if(href_list["command"] == "check_anomalies")
+ check_anomalies = value
+
+ updateTurrets()
+ return 1
+
+/obj/machinery/turretid/proc/updateTurrets()
+ var/datum/turret_checks/TC = new
+ TC.enabled = enabled
+ TC.lethal = lethal
+ TC.check_synth = check_synth
+ TC.check_access = check_access
+ TC.check_records = check_records
+ TC.check_arrest = check_arrest
+ TC.check_weapons = check_weapons
+ TC.check_anomalies = check_anomalies
+ TC.ailock = ailock
+
+ if(istype(control_area))
+ for(var/area/sub_area in control_area.related)
+ for (var/obj/machinery/porta_turret/aTurret in sub_area)
+ aTurret.setState(TC)
+
+ update_icon()
+
+/obj/machinery/turretid/power_change()
+ ..()
+ updateTurrets()
+ update_icon()
+
+/obj/machinery/turretid/update_icon()
+ ..()
+ if(stat & NOPOWER)
+ icon_state = "control_off"
+ else if (enabled)
+ if (lethal)
+ icon_state = "control_kill"
+ else
+ icon_state = "control_stun"
+ else
+ icon_state = "control_standby"
+
+/obj/machinery/turretid/emp_act(severity)
+ if(enabled)
+ //if the turret is on, the EMP no matter how severe disables the turret for a while
+ //and scrambles its settings, with a slight chance of having an emag effect
+
+ check_arrest = pick(0, 1)
+ check_records = pick(0, 1)
+ check_weapons = pick(0, 1)
+ check_access = pick(0, 0, 0, 0, 1) // check_access is a pretty big deal, so it's least likely to get turned on
+ check_anomalies = pick(0, 1)
+
+ enabled=0
+ updateTurrets()
+
+ sleep(rand(60,600))
+ if(!enabled)
+ enabled=1
+ updateTurrets()
+
+ ..()
diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm
index 507d0c1dfe7..171586d255a 100644
--- a/code/game/machinery/turrets.dm
+++ b/code/game/machinery/turrets.dm
@@ -299,7 +299,8 @@
/obj/machinery/turret/attackby(obj/item/weapon/W, mob/user)//I can't believe no one added this before/N
..()
- playsound(get_turf(src), 'sound/weapons/smash.ogg', 60, 1)
+ user.changeNext_move(CLICK_CD_MELEE)
+ playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1)
src.spark_system.start()
src.health -= W.force * 0.5
if (src.health <= 0)
@@ -330,127 +331,31 @@
spawn(13)
qdel(src)
-/obj/machinery/turretid
- name = "turret deactivation control"
- icon = 'icons/obj/device.dmi'
- icon_state = "control_stun"
- anchored = 1
- density = 0
- var/enabled = 1
- var/lethal = 0
- var/locked = 1
- var/control_area //can be area name, path or nothing.
- var/ailock = 0 // AI cannot use this
- req_access = list(access_ai_upload)
-
-/obj/machinery/turretid/New()
- ..()
- if(!control_area)
- var/area/CA = get_area(src)
- if(CA.master && CA.master != CA)
- control_area = CA.master
- else
- control_area = CA
- else if(istext(control_area))
- for(var/area/A in world)
- if(A.name && A.name==control_area)
- control_area = A
- break
- //don't have to check if control_area is path, since get_area_all_atoms can take path.
- return
-
-/obj/machinery/turretid/attackby(obj/item/weapon/W, mob/user)
- if(stat & BROKEN) return
- if (istype(user, /mob/living/silicon))
- return src.attack_hand(user)
-
- if (istype(W, /obj/item/weapon/card/emag) && !emagged)
- user << "\red You short out the turret controls' access analysis module."
- emagged = 1
- locked = 0
- if(user.machine==src)
- src.attack_hand(user)
-
- return
-
- else if( get_dist(src, user) == 0 ) // trying to unlock the interface
- if (src.allowed(usr))
- if(emagged)
- user << "The turret control is unresponsive."
- return
-
- locked = !locked
- user << "You [ locked ? "lock" : "unlock"] the panel."
- if (locked)
- if (user.machine==src)
- user.unset_machine()
- user << browse(null, "window=turretid")
- else
- if (user.machine==src)
- src.attack_hand(user)
- else
- user << "Access denied."
-
-/obj/machinery/turretid/attack_ai(mob/user as mob)
- if(!ailock)
- return attack_hand(user)
- else
- user << "There seems to be a firewall preventing you from accessing this device."
-
-/obj/machinery/turretid/attack_hand(mob/user as mob)
- if ( get_dist(src, user) > 0 )
- if ( !issilicon(user) )
- user << "You are too far away."
- user.unset_machine()
- user << browse(null, "window=turretid;size=500x250")
- return
-
- user.set_machine(src)
- var/loc = src.loc
- if (istype(loc, /turf))
- loc = loc:loc
- if (!istype(loc, /area))
- user << text("Turret badly positioned - loc.loc is [].", loc)
- return
- var/area/area = loc
- var/t = ""
-
- if(src.locked && (!istype(user, /mob/living/silicon)))
- t += "
Swipe ID card to unlock interface
"
- else
- if (!istype(user, /mob/living/silicon))
- t += "
Swipe ID card to lock interface
"
- t += text("Turrets [] - []? \n", src.enabled?"activated":"deactivated", src, src.enabled?"Disable":"Enable")
- t += text("Currently set for [] - Change to []? \n", src.lethal?"lethal":"stun repeatedly", src, src.lethal?"Stun repeatedly":"Lethal")
-
- //user << browse(t, "window=turretid;size=500x250")
- //onclose(user, "turretid")
- var/datum/browser/popup = new(user, "turretid", "Turret Control Panel ([area.name])", 500, 250)
- popup.set_content(t)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
-
-
+
/obj/machinery/turret/attack_animal(mob/living/simple_animal/M as mob)
+ M.changeNext_move(CLICK_CD_MELEE)
+ M.do_attack_animation(src)
if(M.melee_damage_upper == 0) return
if(!(stat & BROKEN))
- visible_message("\red [M] [M.attacktext] [src]!")
- M.attack_log += text("\[[time_stamp()]\] attacked [src.name]")
+ visible_message("[M] [M.attacktext] [src]!")
+ add_logs(M, src, "attacked", admin=0)
//src.attack_log += text("\[[time_stamp()]\] was attacked by [M.name] ([M.ckey])")
src.health -= M.melee_damage_upper
if (src.health <= 0)
src.die()
else
- M << "\red That object is useless to you."
+ M << "That object is useless to you."
return
/obj/machinery/turret/attack_alien(mob/living/carbon/alien/humanoid/M as mob)
+ M.changeNext_move(CLICK_CD_MELEE)
+ M.do_attack_animation(src)
if(!(stat & BROKEN))
- playsound(get_turf(src), 'sound/weapons/slash.ogg', 25, 1, -1)
- visible_message("\red [] has slashed at []!", M, src)
+ playsound(src.loc, 'sound/weapons/slash.ogg', 25, 1, -1)
+ visible_message("[M] has slashed at [src]!")
src.health -= 15
if (src.health <= 0)
src.die()
@@ -460,9 +365,9 @@
-/obj/machinery/turretid/Topic(href, href_list)
- if(..())
- return
+/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0)
+ if(..(href, href_list))
+ return 1
if (src.locked)
if (!istype(usr, /mob/living/silicon))
usr << "Control panel is locked!"
@@ -473,13 +378,8 @@
else if (href_list["toggleLethal"])
src.lethal = !src.lethal
src.updateTurrets()
- src.attack_hand(usr)
-
-/obj/machinery/turretid/proc/updateTurrets()
- if(control_area)
- for (var/obj/machinery/turret/aTurret in get_area_all_atoms(control_area))
- aTurret.setState(enabled, lethal)
- src.update_icons()
+ if(!nowindow)
+ src.attack_hand(usr)
/obj/machinery/turretid/proc/update_icons()
if (src.enabled)
@@ -558,8 +458,9 @@
/obj/machinery/gun_turret/bullet_act(var/obj/item/projectile/Proj)
- take_damage(Proj.damage)
- return
+ if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
+ take_damage(Proj.damage)
+ return
/obj/machinery/gun_turret/proc/die()
state = 2
@@ -572,7 +473,10 @@
return attack_hand(user)
-/obj/machinery/gun_turret/attack_alien(mob/user as mob)
+/obj/machinery/gun_turret/attack_alien(mob/living/user as mob)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
+ playsound(src.loc, 'sound/weapons/slash.ogg', 25, 1, -1)
user.visible_message("[user] slashes at [src]", "You slash at [src]")
take_damage(15)
return
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 348456523eb..c8987cccad0 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -1,17 +1,39 @@
-#define CAT_NORMAL 0
-#define CAT_HIDDEN 1
-#define CAT_COIN 2
+#define CAT_NORMAL 1
+#define CAT_HIDDEN 2 // also used in corresponding wires/vending.dm
+#define CAT_COIN 4
+/**
+ * Datum used to hold information about a product in a vending machine
+ */
/datum/data/vending_product
- var/product_name = "generic"
+ var/product_name = "generic" // Display name for the product
var/product_path = null
- var/amount = 0
+ var/amount = 0 // Amount held in the vending machine
var/max_amount = 0
- var/price = 0
- var/display_color = "blue"
- var/category = CAT_NORMAL
+ var/price = 0 // Price to buy one
+ var/display_color = null // Display color for vending machine listing
+ var/category = CAT_NORMAL // CAT_HIDDEN for contraband, CAT_COIN for premium
+/datum/data/vending_product/New(var/path, var/name = null, var/amount = 1, var/price = 0, var/color = null, var/category = CAT_NORMAL)
+ ..()
+ src.product_path = path
+
+ if(!name)
+ var/atom/tmp = new path
+ src.product_name = initial(tmp.name)
+ del(tmp)
+ else
+ src.product_name = name
+
+ src.amount = amount
+ src.price = price
+ src.display_color = color
+ src.category = category
+
+/**
+ * A vending machine
+ */
/obj/machinery/vending
name = "Vendomat"
desc = "A generic vending machine."
@@ -20,11 +42,23 @@
layer = 2.9
anchored = 1
density = 1
+
+ var/icon_vend //Icon_state when vending
+ var/icon_deny //Icon_state when denying access
+
+ // Power
+ use_power = 1
+ idle_power_usage = 10
+ var/vend_power_usage = 150
+
+ // Vending-related
var/active = 1 //No sales pitches if off!
- var/delay_product_spawn // If set, uses sleep() in product spawn proc (mostly for seeds to retrieve correct names).
var/vend_ready = 1 //Are we ready to vend?? Is it time??
var/vend_delay = 10 //How long does it take to vend?
- var/datum/data/vending_product/currently_vending = null // A /datum/data/vending_product instance of what we're paying for right now.
+ var/categories = CAT_NORMAL // Bitmask of cats we're currently showing
+ var/datum/data/vending_product/currently_vending = null // What we're requesting payment for right now
+ var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI
+ var/status_error = 0 // Set to 1 if status_message is an error
// To be filled out at compile time
var/list/products = list() // For each, use the following pattern:
@@ -32,27 +66,33 @@
var/list/premium = list() // No specified amount = only one in stock
var/list/prices = list() // Prices for each item, list(/type/path = price), items not in the list don't have a price.
- var/product_slogans = "" //String of slogans separated by semicolons, optional
- var/product_ads = "" //String of small ad messages in the vending screen - random chance
+ // List of vending_product items available.
var/list/product_records = list()
var/list/hidden_records = list()
- var/list/coin_records = list()
+
+ // // Variables used to initialize advertising
+ var/product_slogans = "" //String of slogans separated by semicolons, optional
+ var/product_ads = "" //String of small ad messages in the vending screen - random chance
+
+ var/list/ads_list = list()
+
+ // Stuff relating vocalizations
var/list/slogan_list = list()
- var/list/small_ads = list() //Small ad messages in the vending screen - random chance of popping up whenever you open it
var/vend_reply //Thank you for shopping!
+ var/shut_up = 0 //Stop spouting those godawful pitches!
var/last_reply = 0
- var/obj/item/weapon/vending_refill/refill_canister = null //The type of refill canisters used by this machine.
var/last_slogan = 0 //When did we last pitch?
var/slogan_delay = 6000 //How long until we can pitch again?
- var/icon_vend //Icon_state when vending!
- var/icon_deny //Icon_state when vending!
- //var/emagged = 0 //Ignores if somebody doesn't have card access to that machine.
+
+ var/obj/item/weapon/vending_refill/refill_canister = null //The type of refill canisters used by this machine.
+
+ // Things that can go wrong
+ emagged = 0 //Ignores if somebody doesn't have card access to that machine.
var/seconds_electrified = 0 //Shock customers like an airlock.
var/shoot_inventory = 0 //Fire items at customers! We're broken!
var/shoot_speed = 3 //How hard are we firing the items?
var/shoot_chance = 2 //How often are we firing the items?
- var/shut_up = 0 //Stop spouting those godawful pitches!
- var/extended_inventory = 0 //can we access the hidden inventory?
+
var/scan_id = 1
var/obj/item/weapon/coin/coin
var/datum/wires/vending/wires = null
@@ -60,32 +100,57 @@
/obj/machinery/vending/New()
..()
wires = new(src)
- spawn(4)
- src.slogan_list = text2list(src.product_slogans, ";")
+ spawn(50)
+ if(src.product_slogans)
+ src.slogan_list += text2list(src.product_slogans, ";")
- // So not all machines speak at the exact same time.
- // The first time this machine says something will be at slogantime + this random value,
- // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated.
- src.last_slogan = world.time + rand(0, slogan_delay)
+ // So not all machines speak at the exact same time.
+ // The first time this machine says something will be at slogantime + this random value,
+ // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated.
+ src.last_slogan = world.time + rand(0, slogan_delay)
- src.build_inventory(products)
- //Add hidden inventory
- src.build_inventory(contraband, 1)
- src.build_inventory(premium, 0, 1)
+ if(src.product_ads)
+ src.ads_list += text2list(src.product_ads, ";")
+
+ src.build_inventory()
power_change()
-
-
return
return
+/**
+ * Build src.produdct_records from the products lists
+ *
+ * src.products, src.contraband, src.premium, and src.prices allow specifying
+ * products that the vending machine is to carry without manually populating
+ * src.product_records.
+ */
+/obj/machinery/vending/proc/build_inventory()
+ var/list/all_products = list(
+ list(src.products, CAT_NORMAL),
+ list(src.contraband, CAT_HIDDEN),
+ list(src.premium, CAT_COIN))
+
+ for(var/current_list in all_products)
+ var/category = current_list[2]
+
+ for(var/entry in current_list[1])
+ var/datum/data/vending_product/product = new/datum/data/vending_product(entry)
+
+ product.price = (entry in src.prices) ? src.prices[entry] : 0
+ product.amount = (current_list[1][entry]) ? current_list[1][entry] : 1
+ product.max_amount = product.amount
+ product.category = category
+
+ src.product_records.Add(product)
/obj/machinery/vending/Destroy()
- del(wires)
+ del(wires) // qdel
wires = null
- del(coin)
- coin = null
+ if(coin)
+ del(coin) // qdel
+ coin = null
..()
/obj/machinery/vending/ex_act(severity)
@@ -115,41 +180,6 @@
else
del(src)
-
-/obj/machinery/vending/proc/build_inventory(var/list/productlist,hidden=0,req_coin=0,start_empty = null)
- for(var/typepath in productlist)
- var/amount = productlist[typepath]
- var/price = prices[typepath]
- if(isnull(amount)) amount = 1
-
- var/atom/temp = new typepath(null)
- var/datum/data/vending_product/R = new /datum/data/vending_product()
-
- R.product_path = typepath
- if(!start_empty)
- R.amount = amount
- R.max_amount = amount
- R.price = price
- R.display_color = pick("red","blue","green")
- if(hidden)
- R.category=CAT_HIDDEN
- hidden_records += R
- else if(req_coin)
- R.category=CAT_COIN
- coin_records += R
- else
- R.category=CAT_NORMAL
- product_records += R
-
- if(delay_product_spawn)
- sleep(3)
- R.product_name = temp.name
- else
- R.product_name = temp.name
-
-// world << "Added: [R.product_name]] - [R.amount] - [R.product_path]"
-
-
/obj/machinery/vending/proc/refill_inventory(obj/item/weapon/vending_refill/refill, datum/data/vending_product/machine, mob/user)
var/total = 0
@@ -179,11 +209,42 @@
break
return total
-/obj/machinery/vending/attackby(obj/item/weapon/W, mob/user)
- if(panel_open)
- if(default_unfasten_wrench(user, W, time = 60))
- return
+/obj/machinery/vending/attackby(obj/item/weapon/W, mob/user, params)
+ if (currently_vending && vendor_account && !vendor_account.suspended)
+ var/paid = 0
+ var/handled = 0
+ if(istype(W, /obj/item/weapon/card/id))
+ var/obj/item/weapon/card/id/C = W
+ paid = pay_with_card(C)
+ handled = 1
+ else if (istype(W, /obj/item/weapon/spacecash))
+ var/obj/item/weapon/spacecash/C = W
+ paid = pay_with_cash(C, user)
+ handled = 1
+ if(paid)
+ src.vend(currently_vending, usr)
+ return
+ else if(handled)
+ nanomanager.update_uis(src)
+ return // don't smack that machine with your 2 thalers
+
+ if(default_unfasten_wrench(user, W, time = 60))
+ return
+
+ if(istype(W, /obj/item/weapon/screwdriver) && anchored)
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ panel_open = !panel_open
+ user << "You [panel_open ? "open" : "close"] the maintenance panel."
+ overlays.Cut()
+ if(panel_open)
+ overlays += image(icon, "[initial(icon_state)]-panel")
+ nanomanager.update_uis(src) // Speaker switch is on the main UI, not wires UI
+ return
+
+ if(panel_open)
+ if(istype(W, /obj/item/device/multitool)||istype(W, /obj/item/weapon/wirecutters))
+ return attack_hand(user)
if(component_parts && istype(W, /obj/item/weapon/crowbar))
var/datum/data/vending_product/machine = product_records
for(var/datum/data/vending_product/machine_content in machine)
@@ -194,32 +255,13 @@
if(!machine_content.amount)
break
default_deconstruction_crowbar(W)
-
- if(istype(W, /obj/item/weapon/card/emag))
- emagged = 1
- user << "You short out the product lock on [src]"
- return
- else if(istype(W, /obj/item/weapon/screwdriver) && anchored)
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
- panel_open = !panel_open
- user << "You [panel_open ? "open" : "close"] the maintenance panel."
- overlays.Cut()
- if(panel_open)
- overlays += image(icon, "[initial(icon_state)]-panel")
- updateUsrDialog()
- return
- else if(istype(W, /obj/item/device/multitool)||istype(W, /obj/item/weapon/wirecutters))
- if(panel_open)
- attack_hand(user)
- return
- else if(istype(W, /obj/item/weapon/card) && currently_vending)
- var/obj/item/weapon/card/I = W
- scan_card(I)
- else if(istype(W, /obj/item/weapon/coin) && premium.len > 0)
+ if(istype(W, /obj/item/weapon/coin) && premium.len > 0)
user.drop_item()
W.loc = src
coin = W
- user << "You insert [W] into [src]."
+ categories |= CAT_COIN
+ user << "\blue You insert the [W] into the [src]"
+ nanomanager.update_uis(src)
return
else if(istype(W, refill_canister) && refill_canister != null)
if(stat & (BROKEN|NOPOWER))
@@ -241,175 +283,188 @@
else
..()
+/obj/machinery/vending/emag_act(user as mob)
+ emagged = 1
+ user << "You short out the product lock on [src]"
+ return
+/**
+ * Receive payment with cashmoney.
+ *
+ * usr is the mob who gets the change.
+ */
+/obj/machinery/vending/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, mob/user)
+ if(currently_vending.price > cashmoney.worth)
+ // This is not a status display message, since it's something the character
+ // themselves is meant to see BEFORE putting the money in
+ usr << "\icon[cashmoney] That is not enough money."
+ return 0
-/obj/machinery/vending/proc/scan_card(var/obj/item/weapon/card/I)
- if(!currently_vending) return
- if (istype(I, /obj/item/weapon/card/id))
- var/obj/item/weapon/card/id/C = I
- visible_message("[usr] swipes a card through [src].")
- if(vendor_account)
- var/datum/money_account/D = attempt_account_access_nosec(C.associated_account_number)
- if(D)
- var/transaction_amount = currently_vending.price
- if(transaction_amount <= D.money)
+ // Bills (banknotes) cannot really have worth different than face value,
+ // so we have to eat the bill and spit out change in a bundle
+ // This is really dirty, but there's no superclass for all bills, so we
+ // just assume that all spacecash that's not something else is a bill
- //transfer the money
- D.money -= transaction_amount
- vendor_account.money += transaction_amount
+ visible_message("[usr] inserts a credit chip into [src].")
+ var/left = cashmoney.worth - currently_vending.price
+ usr.unEquip(cashmoney)
+ del(cashmoney)
- //create entries in the two account transaction logs
- var/datum/transaction/T = new()
- T.target_name = "[vendor_account.owner_name] (via [src.name])"
- T.purpose = "Purchase of [currently_vending.product_name]"
- if(transaction_amount > 0)
- T.amount = "([transaction_amount])"
- else
- T.amount = "[transaction_amount]"
- T.source_terminal = src.name
- T.date = current_date_string
- T.time = worldtime2text()
- D.transaction_log.Add(T)
- //
- T = new()
- T.target_name = D.owner_name
- T.purpose = "Purchase of [currently_vending.product_name]"
- T.amount = "[transaction_amount]"
- T.source_terminal = src.name
- T.date = current_date_string
- T.time = worldtime2text()
- vendor_account.transaction_log.Add(T)
+ if(left)
+ dispense_cash(left, src.loc, user)
- // Vend the item
- src.vend(src.currently_vending, usr)
- currently_vending = null
- src.updateUsrDialog()
- else
- usr << "\icon[src]You don't have that much money!"
- else
- usr << "\icon[src]Unable to access account. Check security settings and try again."
+ // Vending machines have no idea who paid with cash
+ credit_purchase("(cash)")
+ return 1
+
+/**
+ * Scan a card and attempt to transfer payment from associated account.
+ *
+ * Takes payment for whatever is the currently_vending item. Returns 1 if
+ * successful, 0 if failed
+ */
+/obj/machinery/vending/proc/pay_with_card(var/obj/item/weapon/card/id/I)
+ visible_message("[usr] swipes a card through [src].")
+ var/datum/money_account/customer_account = attempt_account_access_nosec(I.associated_account_number)
+ if (!customer_account)
+ src.status_message = "Error: Unable to access account. Please contact technical support if problem persists."
+ src.status_error = 1
+ return 0
+
+ if(customer_account.suspended)
+ src.status_message = "Unable to access account: account suspended."
+ src.status_error = 1
+ return 0
+
+ // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is
+ // empty at high security levels
+ if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2)
+ var/attempt_pin = input("Enter pin code", "Vendor transaction") as num
+ customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2)
+
+ if(!customer_account)
+ src.status_message = "Unable to access account: incorrect credentials."
+ src.status_error = 1
+ return 0
+
+ if(currently_vending.price > customer_account.money)
+ src.status_message = "Insufficient funds in account."
+ src.status_error = 1
+ return 0
+ else
+ // Okay to move the money at this point
+
+ // debit money from the purchaser's account
+ customer_account.money -= currently_vending.price
+
+ // create entry in the purchaser's account log
+ var/datum/transaction/T = new()
+ T.target_name = "[vendor_account.owner_name] (via [src.name])"
+ T.purpose = "Purchase of [currently_vending.product_name]"
+ if(currently_vending.price > 0)
+ T.amount = "([currently_vending.price])"
else
- usr << "\icon[src]Unable to access vendor account. Please record the machine ID and call CentComm Support."
+ T.amount = "[currently_vending.price]"
+ T.source_terminal = src.name
+ T.date = current_date_string
+ T.time = worldtime2text()
+ customer_account.transaction_log.Add(T)
-/obj/machinery/vending/attack_paw(mob/user as mob)
- return attack_hand(user)
+ // Give the vendor the money. We use the account owner name, which means
+ // that purchases made with stolen/borrowed card will look like the card
+ // owner made them
+ credit_purchase(customer_account.owner_name)
+ return 1
+
+/**
+ * Add money for current purchase to the vendor account.
+ *
+ * Called after the money has already been taken from the customer.
+ */
+/obj/machinery/vending/proc/credit_purchase(var/target as text)
+ vendor_account.money += currently_vending.price
+
+ var/datum/transaction/T = new()
+ T.target_name = target
+ T.purpose = "Purchase of [currently_vending.product_name]"
+ T.amount = "[currently_vending.price]"
+ T.source_terminal = src.name
+ T.date = current_date_string
+ T.time = worldtime2text()
+ vendor_account.transaction_log.Add(T)
/obj/machinery/vending/attack_ai(mob/user as mob)
return attack_hand(user)
-/obj/machinery/vending/proc/GetProductIndex(var/datum/data/vending_product/P)
- var/list/plist
- switch(P.category)
- if(CAT_NORMAL)
- plist=product_records
- if(CAT_HIDDEN)
- plist=hidden_records
- if(CAT_COIN)
- plist=coin_records
- else
- warning("UNKNOWN CATEGORY [P.category] IN TYPE [P.product_path] INSIDE [type]!")
- return plist.Find(P)
-
-/obj/machinery/vending/proc/GetProductByID(var/pid, var/category)
- switch(category)
- if(CAT_NORMAL)
- return product_records[pid]
- if(CAT_HIDDEN)
- return hidden_records[pid]
- if(CAT_COIN)
- return coin_records[pid]
- else
- warning("UNKNOWN PRODUCT: PID: [pid], CAT: [category] INSIDE [type]!")
- return null
+/obj/machinery/vending/attack_paw(mob/user as mob)
+ return attack_hand(user)
/obj/machinery/vending/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
- user.set_machine(src)
- if(seconds_electrified != 0)
- if(shock(user, 100))
+ if(src.seconds_electrified != 0)
+ if(src.shock(user, 100))
return
- var/vendorname = (src.name) //import the machine's name
+ ui_interact(user)
+ wires.Interact(user)
- if(src.currently_vending)
- var/dat = "
[vendorname]
" //display the name, and added a horizontal rule
+/**
+ * Display the NanoUI window for the vending machine.
+ *
+ * See NanoUI documentation for details.
+ */
+/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ user.set_machine(src)
- // AUTOFIXED BY fix_string_idiocy.py
- // C:\Users\Rob\Documents\Projects\vgstation13\code\game\machinery\vending.dm:260: dat += "You have selected [currently_vending.product_name]. Please ensure your ID is in your ID holder or hand. "
- dat += {"You have selected [currently_vending.product_name]. Please ensure your ID is in your ID holder or hand.
- Pay |
- Cancel"}
- // END AUTOFIX
- user << browse(dat, "window=vending")
- onclose(user, "")
- return
-
- var/dat = "
[vendorname]
" //display the name, and added a horizontal rule
- dat += "Select an item:
" //the rest is just general spacing and bolding
-
- if (premium.len > 0)
- dat += "Coin slot: [coin ? coin : "No coin inserted"] (Remove)
"
-
- if (src.product_records.len == 0)
- dat += "No product loaded!"
+ var/list/data = list()
+ if(currently_vending)
+ data["mode"] = 1
+ data["product"] = currently_vending.product_name
+ data["price"] = currently_vending.price
+ data["message_err"] = 0
+ data["message"] = src.status_message
+ data["message_err"] = src.status_error
else
- var/list/display_records = list()
- display_records += src.product_records
+ data["mode"] = 0
+ var/list/listed_products = list()
- if(src.extended_inventory)
- display_records += src.hidden_records
- if(src.coin)
- display_records += src.coin_records
+ for(var/key = 1 to src.product_records.len)
+ var/datum/data/vending_product/I = src.product_records[key]
- for (var/datum/data/vending_product/R in display_records)
+ if(!(I.category & src.categories))
+ continue
- // AUTOFIXED BY fix_string_idiocy.py
- // C:\Users\Rob\Documents\Projects\vgstation13\code\game\machinery\vending.dm:285: dat += "[R.product_name]:"
- dat += {"[R.product_name]:
- [R.amount] "}
- // END AUTOFIX
- if(R.price)
- dat += " (Price: [R.price])"
- if (R.amount > 0)
- var/idx=GetProductIndex(R)
- dat += " (Vend)"
- else
- dat += " SOLD OUT"
- dat += " "
+ listed_products.Add(list(list(
+ "key" = key,
+ "name" = sanitize(I.product_name),
+ "price" = I.price,
+ "color" = I.display_color,
+ "amount" = I.amount)))
- dat += ""
+ data["products"] = listed_products
- if(panel_open)
- dat += wires()
+ if(src.coin)
+ data["coin"] = src.coin.name
- if(product_slogans != "")
- dat += "The speaker switch is [shut_up ? "off" : "on"]. Toggle"
+ if(src.panel_open)
+ data["panel"] = 1
+ data["speaker"] = src.shut_up ? 0 : 1
+ else
+ data["panel"] = 0
- user << browse(dat, "window=vending")
- onclose(user, "")
- return
-
-// returns the wire panel text
-/obj/machinery/vending/proc/wires()
- return wires.GetInteractWindow()
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "vending_machine.tmpl", src.name, 440, 600)
+ ui.set_initial_data(data)
+ ui.open()
/obj/machinery/vending/Topic(href, href_list)
if(..())
- return
+ return 1
- if(istype(usr,/mob/living/silicon))
- if(istype(usr,/mob/living/silicon/robot))
- var/mob/living/silicon/robot/R = usr
- if(!(R.module && istype(R.module,/obj/item/weapon/robot_module/butler) ) )
- usr << "\red The vending machine refuses to interface with you, as you are not in its target demographic!"
- return
- else
- usr << "\red The vending machine refuses to interface with you, as you are not in its target demographic!"
- return
-
- if(href_list["remove_coin"])
+ if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon))
if(!coin)
usr << "There is no coin in this machine."
return
@@ -419,67 +474,86 @@
usr.put_in_hands(coin)
usr << "\blue You remove the [coin] from the [src]"
coin = null
- usr.set_machine(src)
+ categories &= ~CAT_COIN
+ if (href_list["pay"])
+ if(currently_vending && vendor_account && !vendor_account.suspended)
+ if(istype(usr, /mob/living/carbon/human))
+ var/paid = 0
+ var/handled = 0
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/weapon/card/card = null
+ if(istype(H.wear_id,/obj/item/weapon/card))
+ card = H.wear_id
+ paid = pay_with_card(card)
+ handled = 1
+ else if(istype(H.get_active_hand(), /obj/item/weapon/card))
+ card = H.get_active_hand()
+ paid = pay_with_card(card)
+ handled = 1
+ if(paid)
+ src.vend(currently_vending, usr)
+ return
+ else if(handled)
+ nanomanager.update_uis(src)
+ return // don't smack that machine with your 2 credits
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))))
if ((href_list["vend"]) && (src.vend_ready) && (!currently_vending))
- if (!allowed(usr) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- usr << "\red Access denied." //Unless emagged of course
- flick(src.icon_deny,src)
+ if(istype(usr,/mob/living/silicon))
+ if(istype(usr,/mob/living/silicon/robot))
+ var/mob/living/silicon/robot/R = usr
+ if(!(R.module && istype(R.module,/obj/item/weapon/robot_module/butler) ))
+ usr << "\red The vending machine refuses to interface with you, as you are not in its target demographic!"
+ return
+ else
+ usr << "\red The vending machine refuses to interface with you, as you are not in its target demographic!"
+ return
+
+ if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
+ usr << "Access denied." //Unless emagged of course
+ flick(icon_deny,src)
return
- var/idx=text2num(href_list["vend"])
- var/cat=text2num(href_list["cat"])
+ var/key = text2num(href_list["vend"])
+ var/datum/data/vending_product/R = product_records[key]
- var/datum/data/vending_product/R = GetProductByID(idx,cat)
- if (!R || !istype(R) || !R.product_path || R.amount <= 0)
+ // This should not happen unless the request from NanoUI was bad
+ if(!(R.category & src.categories))
return
- if(R.price == null)
+ if(R.price <= 0)
src.vend(R, usr)
else
src.currently_vending = R
- src.updateUsrDialog()
+ if(!vendor_account || vendor_account.suspended)
+ src.status_message = "This machine is currently unable to process payments due to problems with the associated account."
+ src.status_error = 1
+ else
+ src.status_message = "Please swipe a card or insert cash to pay for the item."
+ src.status_error = 0
- return
-
- else if (href_list["cancel_buying"])
+ else if (href_list["cancelpurchase"])
src.currently_vending = null
- src.updateUsrDialog()
- return
-
- else if (href_list["buy"])
- if(istype(usr, /mob/living/carbon/human))
- var/mob/living/carbon/human/H=usr
- var/obj/item/weapon/card/card = null
- if(istype(H.wear_id,/obj/item/weapon/card))
- card=H.wear_id
- else if(istype(H.get_active_hand(),/obj/item/weapon/card))
- card=H.get_active_hand()
- if(card)
- scan_card(card)
- return
else if ((href_list["togglevoice"]) && (src.panel_open))
src.shut_up = !src.shut_up
src.add_fingerprint(usr)
- src.updateUsrDialog()
- else
- usr << browse(null, "window=vending")
- return
- return
+ nanomanager.update_uis(src)
/obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user)
- if (!allowed(user) && !emagged && wires.IsIndexCut(VENDING_WIRE_IDSCAN)) //For SECURE VENDING MACHINES YEAH
- user << "\red Access denied." //Unless emagged of course
+ if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
+ usr << "Access denied." //Unless emagged of course
flick(src.icon_deny,src)
return
src.vend_ready = 0 //One thing at a time!!
+ src.status_message = "Vending..."
+ src.status_error = 0
+ nanomanager.update_uis(src)
- if (R in coin_records)
+ if (R.category & CAT_COIN)
if(!coin)
user << "\blue You need to insert a coin to get this item."
return
@@ -489,8 +563,10 @@
else
user << "\blue You weren't able to pull the coin out fast enough, the machine ate it, string and all."
del(coin)
+ categories &= ~CAT_COIN
else
del(coin)
+ categories &= ~CAT_COIN
R.amount--
@@ -499,16 +575,16 @@
src.speak(src.vend_reply)
src.last_reply = world.time
- use_power(5)
+ use_power(vend_power_usage) //actuators and stuff
if (src.icon_vend) //Show the vending animation if needed
flick(src.icon_vend,src)
spawn(src.vend_delay)
new R.product_path(get_turf(src))
+ src.status_message = ""
+ src.status_error = 0
src.vend_ready = 1
- return
-
- src.updateUsrDialog()
-
+ currently_vending = null
+ nanomanager.update_uis(src)
/obj/machinery/vending/proc/stock(var/datum/data/vending_product/R, var/mob/user)
if(src.panel_open)
@@ -771,9 +847,13 @@
product_slogans = "Carts to go!"
icon_state = "cart"
icon_deny = "cart-deny"
- products = list(/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/engineering = 10,/obj/item/weapon/cartridge/security = 10,
- /obj/item/weapon/cartridge/janitor = 10,/obj/item/weapon/cartridge/signal/toxins = 10,/obj/item/device/pda/heads = 10,
- /obj/item/weapon/cartridge/captain = 3,/obj/item/weapon/cartridge/quartermaster = 10)
+ products = list(/obj/item/device/pda =10,/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/chemistry = 10,
+ /obj/item/weapon/cartridge/engineering = 10,/obj/item/weapon/cartridge/atmos = 10,/obj/item/weapon/cartridge/janitor = 10,
+ /obj/item/weapon/cartridge/signal/toxins = 10,/obj/item/weapon/cartridge/signal = 10,/obj/item/weapon/cartridge = 10)
+ contraband = list(/obj/item/weapon/cartridge/clown = 1,/obj/item/weapon/cartridge/mime = 1)
+ prices = list(/obj/item/device/pda =300,/obj/item/weapon/cartridge/medical = 200,/obj/item/weapon/cartridge/chemistry = 150,/obj/item/weapon/cartridge/engineering = 100,
+ /obj/item/weapon/cartridge/atmos = 75,/obj/item/weapon/cartridge/janitor = 100,/obj/item/weapon/cartridge/signal/toxins = 150,
+ /obj/item/weapon/cartridge/signal = 75,/obj/item/weapon/cartridge = 50)
/obj/machinery/vending/cigarette
name = "Cigarette machine" //OCD had to be uppercase to look nice with the new formating
@@ -809,7 +889,7 @@
/obj/item/weapon/reagent_containers/glass/bottle/stoxin = 4,/obj/item/weapon/reagent_containers/glass/bottle/toxin = 4,
/obj/item/weapon/reagent_containers/syringe/antiviral = 4,/obj/item/weapon/reagent_containers/syringe = 12,
/obj/item/device/healthanalyzer = 5,/obj/item/weapon/reagent_containers/glass/beaker = 4, /obj/item/weapon/reagent_containers/dropper = 2,
- /obj/item/stack/medical/advanced/bruise_pack = 3, /obj/item/stack/medical/advanced/ointment = 3, /obj/item/stack/medical/splint = 2)
+ /obj/item/stack/medical/advanced/bruise_pack = 3, /obj/item/stack/medical/advanced/ointment = 3, /obj/item/stack/medical/splint = 2, /obj/item/device/sensor_device = 2)
contraband = list(/obj/item/weapon/reagent_containers/pill/tox = 3,/obj/item/weapon/reagent_containers/pill/stox = 4,/obj/item/weapon/reagent_containers/pill/antitox = 6)
@@ -860,7 +940,7 @@
icon_state = "sec"
icon_deny = "sec-deny"
req_access_txt = "1"
- products = list(/obj/item/weapon/handcuffs = 8,/obj/item/weapon/grenade/flashbang = 4,/obj/item/device/flash = 5,
+ products = list(/obj/item/weapon/restraints/handcuffs = 8,/obj/item/weapon/restraints/handcuffs/cable/zipties = 8,/obj/item/weapon/grenade/flashbang = 4,/obj/item/device/flash = 5,
/obj/item/weapon/reagent_containers/food/snacks/donut/normal = 12,/obj/item/weapon/storage/box/evidence = 6,/obj/item/device/flashlight/seclite = 4)
contraband = list(/obj/item/clothing/glasses/sunglasses = 2,/obj/item/weapon/storage/fancy/donut_box = 2,/obj/item/device/hailer = 5)
@@ -883,19 +963,45 @@
product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!"
product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!"
icon_state = "seeds"
- delay_product_spawn = 1
products = list(/obj/item/seeds/bananaseed = 3,/obj/item/seeds/berryseed = 3,/obj/item/seeds/carrotseed = 3,/obj/item/seeds/chantermycelium = 3,/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/replicapod = 3,/obj/item/seeds/soyaseed = 3,
/obj/item/seeds/sunflowerseed = 3,/obj/item/seeds/tomatoseed = 3,/obj/item/seeds/towermycelium = 3,/obj/item/seeds/wheatseed = 3,/obj/item/seeds/appleseed = 3,
/obj/item/seeds/poppyseed = 3,/obj/item/seeds/ambrosiavulgarisseed = 3,/obj/item/seeds/whitebeetseed = 3,/obj/item/seeds/watermelonseed = 3,/obj/item/seeds/limeseed = 3,
/obj/item/seeds/lemonseed = 3,/obj/item/seeds/orangeseed = 3,/obj/item/seeds/grassseed = 3,/obj/item/seeds/cocoapodseed = 3,
- /obj/item/seeds/cabbageseed = 3,/obj/item/seeds/grapeseed = 3,/obj/item/seeds/pumpkinseed = 3,/obj/item/seeds/cherryseed = 3,/obj/item/seeds/plastiseed = 3,/obj/item/seeds/riceseed = 3)
+ /obj/item/seeds/cabbageseed = 3,/obj/item/seeds/grapeseed = 3,/obj/item/seeds/pumpkinseed = 3,/obj/item/seeds/cherryseed = 3,/obj/item/seeds/plastiseed = 3,/obj/item/seeds/riceseed = 3,
+ /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/coffeeaseed = 3, /obj/item/seeds/teaasperaseed = 3)
contraband = list(/obj/item/seeds/amanitamycelium = 2,/obj/item/seeds/glowshroom = 2,/obj/item/seeds/libertymycelium = 2,/obj/item/seeds/nettleseed = 2,
/obj/item/seeds/plumpmycelium = 2,/obj/item/seeds/reishimycelium = 2)
premium = list(/obj/item/weapon/reagent_containers/spray/waterflower = 1)
+/**
+ * Populate hydroseeds product_records
+ *
+ * This needs to be customized to fetch the actual names of the seeds, otherwise
+ * the machine would simply list "packet of seeds" times 20
+ */
+/obj/machinery/vending/hydroseeds/build_inventory()
+ var/list/all_products = list(
+ list(src.products, CAT_NORMAL),
+ list(src.contraband, CAT_HIDDEN),
+ list(src.premium, CAT_COIN))
+
+ for(var/current_list in all_products)
+ var/category = current_list[2]
+
+ for(var/entry in current_list[1])
+ var/obj/item/seeds/S = new entry(src)
+ var/name = S.name
+ var/datum/data/vending_product/product = new/datum/data/vending_product(entry, name)
+
+ product.price = (entry in src.prices) ? src.prices[entry] : 0
+ product.amount = (current_list[1][entry]) ? current_list[1][entry] : 1
+ product.max_amount = product.amount
+ product.category = category
+
+ src.product_records.Add(product)
/obj/machinery/vending/magivend
name = "MagiVend"
@@ -919,10 +1025,10 @@
/obj/item/clothing/head/helmet/gladiator = 1,/obj/item/clothing/under/gimmick/rank/captain/suit = 1,/obj/item/clothing/head/flatcap = 1,
/obj/item/clothing/suit/storage/labcoat/mad = 1,/obj/item/clothing/glasses/gglasses = 1,/obj/item/clothing/shoes/jackboots = 1,
/obj/item/clothing/under/schoolgirl = 1,/obj/item/clothing/head/kitty = 1,/obj/item/clothing/under/blackskirt = 1,/obj/item/clothing/head/beret = 1,
- /obj/item/clothing/tie/waistcoat = 1,/obj/item/clothing/under/suit_jacket = 1,/obj/item/clothing/head/that =1,/obj/item/clothing/under/kilt = 1,/obj/item/clothing/head/beret = 1,/obj/item/clothing/tie/waistcoat = 1,
+ /obj/item/clothing/accessory/waistcoat = 1,/obj/item/clothing/under/suit_jacket = 1,/obj/item/clothing/head/that =1,/obj/item/clothing/under/kilt = 1,/obj/item/clothing/head/beret = 1,/obj/item/clothing/accessory/waistcoat = 1,
/obj/item/clothing/glasses/monocle =1,/obj/item/clothing/head/bowlerhat = 1,/obj/item/weapon/cane = 1,/obj/item/clothing/under/sl_suit = 1,
/obj/item/clothing/mask/fakemoustache = 1,/obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1,/obj/item/clothing/head/plaguedoctorhat = 1,/obj/item/clothing/mask/gas/plaguedoctor = 1,
- /obj/item/clothing/under/owl = 1,/obj/item/clothing/mask/gas/owl_mask = 1,/obj/item/clothing/suit/apron = 1,/obj/item/clothing/under/waiter = 1,
+ /obj/item/clothing/suit/apron = 1,/obj/item/clothing/under/waiter = 1,
/obj/item/clothing/under/pirate = 1,/obj/item/clothing/suit/pirate_brown = 1,/obj/item/clothing/suit/pirate_black =1,/obj/item/clothing/under/pirate_rags =1,/obj/item/clothing/head/pirate = 1,/obj/item/clothing/head/bandana = 1,
/obj/item/clothing/head/bandana = 1,/obj/item/clothing/under/soviet = 1,/obj/item/clothing/head/ushanka = 1,/obj/item/clothing/suit/imperium_monk = 1,
/obj/item/clothing/mask/gas/cyborg = 1,/obj/item/clothing/suit/holidaypriest = 1,/obj/item/clothing/head/wizard/marisa/fake = 1,
@@ -930,9 +1036,9 @@
/obj/item/clothing/suit/wizrobe/fake = 1,/obj/item/clothing/head/wizard/fake = 1,/obj/item/weapon/staff = 3,/obj/item/clothing/mask/gas/sexyclown = 1,
/obj/item/clothing/under/sexyclown = 1,/obj/item/clothing/mask/gas/sexymime = 1,/obj/item/clothing/under/sexymime = 1,/obj/item/clothing/suit/apron/overalls = 1,
/obj/item/clothing/head/rabbitears =1, /obj/item/clothing/head/sombrero = 1, /obj/item/clothing/suit/poncho = 1,
- /obj/item/clothing/suit/poncho/green = 1, /obj/item/clothing/suit/poncho/red = 1, /obj/item/clothing/tie/blue = 1, /obj/item/clothing/tie/red = 1, /obj/item/clothing/tie/black = 1, /obj/item/clothing/tie/horrible = 1,
- /obj/item/clothing/under/maid = 1, /obj/item/clothing/under/janimaid = 1)
- contraband = list(/obj/item/clothing/suit/judgerobe = 1,/obj/item/clothing/head/powdered_wig = 1,/obj/item/weapon/gun/magic/wand = 1)
+ /obj/item/clothing/suit/poncho/green = 1, /obj/item/clothing/suit/poncho/red = 1, /obj/item/clothing/accessory/blue = 1, /obj/item/clothing/accessory/red = 1, /obj/item/clothing/accessory/black = 1, /obj/item/clothing/accessory/horrible = 1,
+ /obj/item/clothing/under/maid = 1, /obj/item/clothing/under/janimaid = 1, /obj/item/clothing/under/pants/camo = 1, /obj/item/clothing/mask/bandana = 1, /obj/item/clothing/mask/bandana/black = 1)
+ contraband = list(/obj/item/clothing/suit/judgerobe = 1,/obj/item/clothing/head/powdered_wig = 1,/obj/item/weapon/gun/magic/wand = 1, /obj/item/clothing/mask/balaclava=1)
premium = list(/obj/item/clothing/suit/hgpirate = 1, /obj/item/clothing/head/hgpiratecap = 1, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1)
refill_canister = /obj/item/weapon/vending_refill/autodrobe
@@ -951,7 +1057,7 @@
desc = "A kitchen and restaurant equipment vendor"
product_ads = "Mm, food stuffs!;Food and food accessories.;Get your plates!;You like forks?;I like forks.;Woo, utensils.;You don't really need these..."
icon_state = "dinnerware"
- products = list(/obj/item/weapon/tray = 8,/obj/item/weapon/kitchen/utensil/fork = 6,/obj/item/weapon/kitchenknife = 3,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass = 8,/obj/item/clothing/suit/chef/classic = 2,/obj/item/weapon/reagent_containers/food/condiment/pack/ketchup = 5,/obj/item/weapon/reagent_containers/food/condiment/pack/hotsauce = 5)
+ products = list(/obj/item/weapon/storage/bag/tray = 8,/obj/item/weapon/kitchen/utensil/fork = 6,/obj/item/weapon/kitchenknife = 3,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass = 8,/obj/item/clothing/suit/chef/classic = 2,/obj/item/weapon/reagent_containers/food/condiment/pack/ketchup = 5,/obj/item/weapon/reagent_containers/food/condiment/pack/hotsauce = 5)
contraband = list(/obj/item/weapon/kitchen/utensil/spoon = 2,/obj/item/weapon/kitchen/utensil/knife = 2,/obj/item/weapon/kitchen/rollingpin = 2, /obj/item/weapon/butch = 2)
/obj/machinery/vending/sovietsoda
@@ -979,8 +1085,8 @@
icon_state = "engivend"
icon_deny = "engivend-deny"
req_access_txt = "11" //Engineering Equipment access
- products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/module/power_control = 10,/obj/item/weapon/airalarm_electronics = 10,/obj/item/weapon/cell/high = 10)
- contraband = list(/obj/item/weapon/cell/potato = 3)
+ products = list(/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/module/power_control = 10,/obj/item/weapon/airalarm_electronics = 10,/obj/item/weapon/stock_parts/cell/high = 10)
+ contraband = list(/obj/item/weapon/stock_parts/cell/potato = 3)
premium = list(/obj/item/weapon/storage/belt/utility = 3)
//This one's from bay12
@@ -993,7 +1099,7 @@
products = list(/obj/item/clothing/under/rank/chief_engineer = 4,/obj/item/clothing/under/rank/engineer = 4,/obj/item/clothing/shoes/orange = 4,/obj/item/clothing/head/hardhat = 4,
/obj/item/weapon/storage/belt/utility = 4,/obj/item/clothing/glasses/meson = 4,/obj/item/clothing/gloves/yellow = 4, /obj/item/weapon/screwdriver = 12,
/obj/item/weapon/crowbar = 12,/obj/item/weapon/wirecutters = 12,/obj/item/device/multitool = 12,/obj/item/weapon/wrench = 12,/obj/item/device/t_scanner = 12,
- /obj/item/stack/cable_coil/heavyduty = 8, /obj/item/weapon/cell = 8, /obj/item/weapon/weldingtool = 8,/obj/item/clothing/head/welding = 8,
+ /obj/item/stack/cable_coil/heavyduty = 8, /obj/item/weapon/stock_parts/cell = 8, /obj/item/weapon/weldingtool = 8,/obj/item/clothing/head/welding = 8,
/obj/item/weapon/light/tube = 10,/obj/item/clothing/suit/fire = 4, /obj/item/weapon/stock_parts/scanning_module = 5,/obj/item/weapon/stock_parts/micro_laser = 5,
/obj/item/weapon/stock_parts/matter_bin = 5,/obj/item/weapon/stock_parts/manipulator = 5,/obj/item/weapon/stock_parts/console_screen = 5)
// There was an incorrect entry (cablecoil/power). I improvised to cablecoil/heavyduty.
@@ -1008,7 +1114,7 @@
icon_deny = "robotics-deny"
req_access_txt = "29"
products = list(/obj/item/clothing/suit/storage/labcoat = 4,/obj/item/clothing/under/rank/roboticist = 4,/obj/item/stack/cable_coil = 4,/obj/item/device/flash = 4,
- /obj/item/weapon/cell/high = 12, /obj/item/device/assembly/prox_sensor = 3,/obj/item/device/assembly/signaler = 3,/obj/item/device/healthanalyzer = 3,
+ /obj/item/weapon/stock_parts/cell/high = 12, /obj/item/device/assembly/prox_sensor = 3,/obj/item/device/assembly/signaler = 3,/obj/item/device/healthanalyzer = 3,
/obj/item/weapon/scalpel = 2,/obj/item/weapon/circular_saw = 2,/obj/item/weapon/tank/anesthetic = 2,/obj/item/clothing/mask/breath/medical = 5,
/obj/item/weapon/screwdriver = 5,/obj/item/weapon/crowbar = 5)
//everything after the power cell had no amounts, I improvised. -Sayu
@@ -1097,11 +1203,11 @@
/obj/item/clothing/under/pants/camo = 1,/obj/item/clothing/under/pants/blackjeans=2,/obj/item/clothing/under/pants/khaki=2,
/obj/item/clothing/under/pants/white=2,/obj/item/clothing/under/pants/red=1,/obj/item/clothing/under/pants/black=2,
/obj/item/clothing/under/pants/tan=2,/obj/item/clothing/under/pants/blue=1,/obj/item/clothing/under/pants/track=1,
- /obj/item/clothing/tie/scarf/red=1,/obj/item/clothing/tie/scarf/green=1,/obj/item/clothing/tie/scarf/darkblue=1,
- /obj/item/clothing/tie/scarf/purple=1,/obj/item/clothing/tie/scarf/yellow=1,/obj/item/clothing/tie/scarf/orange=1,
- /obj/item/clothing/tie/scarf/lightblue=1,/obj/item/clothing/tie/scarf/white=1,/obj/item/clothing/tie/scarf/black=1,
- /obj/item/clothing/tie/scarf/zebra=1,/obj/item/clothing/tie/scarf/christmas=1,/obj/item/clothing/tie/stripedredscarf=1,
- /obj/item/clothing/tie/stripedbluescarf=1,/obj/item/clothing/tie/stripedgreenscarf=1,/obj/item/clothing/tie/waistcoat=1,
+ /obj/item/clothing/accessory/scarf/red=1,/obj/item/clothing/accessory/scarf/green=1,/obj/item/clothing/accessory/scarf/darkblue=1,
+ /obj/item/clothing/accessory/scarf/purple=1,/obj/item/clothing/accessory/scarf/yellow=1,/obj/item/clothing/accessory/scarf/orange=1,
+ /obj/item/clothing/accessory/scarf/lightblue=1,/obj/item/clothing/accessory/scarf/white=1,/obj/item/clothing/accessory/scarf/black=1,
+ /obj/item/clothing/accessory/scarf/zebra=1,/obj/item/clothing/accessory/scarf/christmas=1,/obj/item/clothing/accessory/stripedredscarf=1,
+ /obj/item/clothing/accessory/stripedbluescarf=1,/obj/item/clothing/accessory/stripedgreenscarf=1,/obj/item/clothing/accessory/waistcoat=1,
/obj/item/clothing/under/sundress=2,/obj/item/clothing/under/stripeddress = 1, /obj/item/clothing/under/sailordress = 1, /obj/item/clothing/under/redeveninggown = 1, /obj/item/clothing/under/blacktango=1,/obj/item/clothing/suit/jacket=3,
/obj/item/clothing/glasses/regular=2,/obj/item/clothing/head/sombrero=1,/obj/item/clothing/suit/poncho=1,
/obj/item/clothing/suit/ianshirt=1,/obj/item/clothing/shoes/laceup=2,/obj/item/clothing/shoes/black=4,
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index c72b2338cba..19108eec93d 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -151,7 +151,7 @@
if (S.chained == 1)
S.chained = 0
S.slowdown = SHOES_SLOWDOWN
- new /obj/item/weapon/handcuffs( src )
+ new /obj/item/weapon/restraints/handcuffs( src )
S.icon_state = new_shoe_icon_state
S._color = _color
S.name = new_shoe_name
@@ -194,7 +194,7 @@
/obj/machinery/washing_machine/update_icon()
icon_state = "wm_[state][panel]"
-/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
/*if(istype(W,/obj/item/weapon/screwdriver))
panel = !panel
user << "\blue you [panel ? "open" : "close"] the [src]'s maintenance panel"*/
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index ac2986f18a2..22e89cfe000 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -30,52 +30,96 @@
insisting++
else
- user << "You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers."
- user << "Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first."
-
- charges--
- insisting = 0
-
- if (!(M_HULK in user.mutations))
- user.dna.SetSEState(HULKBLOCK,1)
-
- if (!(M_LASER in user.mutations))
- user.mutations.Add(M_LASER)
-
- if (!(M_XRAY in user.mutations))
- user.mutations.Add(M_XRAY)
- user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS)
- user.see_in_dark = 8
- user.see_invisible = SEE_INVISIBLE_LEVEL_TWO
-
- if (!(M_RESIST_COLD in user.mutations))
- user.mutations.Add(M_RESIST_COLD)
-
- if (!(M_RESIST_HEAT in user.mutations))
- user.mutations.Add(M_RESIST_HEAT)
-
- if (!(M_TK in user.mutations))
- user.mutations.Add(M_TK)
-
- /* Not used
- if(!(HEAL in user.mutations))
- user.mutations.Add(HEAL)
- */
-
- user.update_mutations()
+ user << "The power of the Wish Granter have turned you into the superhero the station deserves. You are a masked vigilante, and answer to no man. Will you use your newfound strength to protect the innocent, or will you hunt the guilty?"
ticker.mode.traitors += user.mind
- user.mind.special_role = "Avatar of the Wish Granter"
+ user.mind.special_role = "The Hero The Station Deserves"
- var/datum/objective/silence/silence = new
- silence.owner = user.mind
- user.mind.objectives += silence
+
+ var/mob/living/carbon/human/M = user
+
+ var/wish = input("You want to...","Wish") as anything in list("Protect the innocent","Hunt the guilty")
+ switch(wish)
+ if("Protect the innocent")
+ M.fully_replace_character_name(M.real_name, "Owlman")
+
+ var/datum/objective/protect/protect = new
+ protect.owner = user.mind
+ user.mind.objectives += protect
+
+ for(var/obj/item/W in M)
+ M.unEquip(W)
+
+ M.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(M), slot_shoes)
+ M.equip_to_slot_or_del(new /obj/item/clothing/under/owl(M), slot_w_uniform)
+ M.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings(M), slot_wear_suit)
+ M.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/owl_mask(M), slot_wear_mask)
+
+ var/obj/item/weapon/card/id/syndicate/W = new(M)
+ W.name = "[M.real_name]'s ID Card (Superhero)"
+ W.access = get_all_accesses()
+ W.assignment = "Superhero"
+ W.registered_name = M.real_name
+ M.equip_to_slot_or_del(W, slot_wear_id)
+
+ M.regenerate_icons()
+
+ if("Hunt the guilty")
+ M.fully_replace_character_name(M.real_name, "The Griffin")
+
+ var/datum/objective/assassinate/assasinate = new
+ assasinate.owner = user.mind
+ user.mind.objectives += assasinate
+
+ for(var/obj/item/W in M)
+ M.unEquip(W)
+
+ M.equip_to_slot_or_del(new /obj/item/clothing/shoes/griffin(M), slot_shoes)
+ M.equip_to_slot_or_del(new /obj/item/clothing/under/griffin(M), slot_w_uniform)
+ M.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings/griffinwings(M), slot_wear_suit)
+ M.equip_to_slot_or_del(new /obj/item/clothing/head/griffin(M), slot_head)
+
+ var/obj/item/weapon/card/id/syndicate/W = new(M)
+ W.name = "[M.real_name]'s ID Card (Supervillain)"
+ W.access = get_all_accesses()
+ W.assignment = "Supervillain"
+ W.registered_name = M.real_name
+ M.equip_to_slot_or_del(W, slot_wear_id)
+
+ M.regenerate_icons()
var/obj_count = 1
for(var/datum/objective/OBJ in user.mind.objectives)
user << "Objective #[obj_count]: [OBJ.explanation_text]"
obj_count++
- user << "You have a very bad feeling about this."
+ charges--
+ insisting = 0
+
+ if (!(HULK in user.mutations))
+ user.dna.SetSEState(HULKBLOCK,1)
+
+ if (!(LASER in user.mutations))
+ user.mutations.Add(LASER)
+
+ if (!(XRAY in user.mutations))
+ user.mutations.Add(XRAY)
+ user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS)
+ user.see_in_dark = 8
+ user.see_invisible = SEE_INVISIBLE_LEVEL_TWO
+
+ if (!(RESIST_COLD in user.mutations))
+ user.mutations.Add(RESIST_COLD)
+
+ if (!(RESIST_HEAT in user.mutations))
+ user.mutations.Add(RESIST_HEAT)
+
+ if (!(TK in user.mutations))
+ user.mutations.Add(TK)
+
+ if(!(REGEN in user.mutations))
+ user.mutations.Add(REGEN)
+
+ user.update_mutations()
return
\ No newline at end of file
diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm
index 2c723a2e530..26d9d066fd8 100644
--- a/code/game/mecha/combat/gygax.dm
+++ b/code/game/mecha/combat/gygax.dm
@@ -53,7 +53,7 @@
ME.attach(src)
return
-/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/cell/C=null)
+/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/stock_parts/cell/C=null)
if(C)
C.forceMove(src)
cell = C
diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm
index c332603ab46..b5951d306c2 100644
--- a/code/game/mecha/combat/marauder.dm
+++ b/code/game/mecha/combat/marauder.dm
@@ -73,7 +73,7 @@
src.smoke_system.attach(src)
return
-/obj/mecha/combat/marauder/seraph/New()
+/obj/mecha/combat/marauder/seraph/loaded/New()
..()//Let it equip whatever is needed.
var/obj/item/mecha_parts/mecha_equipment/ME
if(equipment.len)//Now to remove it and equip anew.
diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm
index cf3cd0022a6..c618ec8c72f 100644
--- a/code/game/mecha/equipment/tools/tools.dm
+++ b/code/game/mecha/equipment/tools/tools.dm
@@ -389,7 +389,7 @@
range = RANGED
action(atom/target)
- if(!action_checks(target) || src.loc.z == 2) return
+ if(!action_checks(target) || (src.loc.z in config.admin_levels)) return
var/turf/T = get_turf(target)
if(T)
set_ready_state(0)
@@ -410,7 +410,7 @@
action(atom/target)
- if(!action_checks(target) || src.loc.z == 2) return
+ if(!action_checks(target) || (src.loc.z in config.admin_levels)) return
var/list/theareas = list()
for(var/area/AR in orange(100, chassis))
if(AR in theareas) continue
@@ -548,9 +548,9 @@
if(!chassis) return
return "* [src.name]"
- proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
+ proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(!action_checks(user))
- return chassis.dynattackby(W,user)
+ return chassis.dynattackby(W,user, params)
chassis.log_message("Attacked by [W]. Attacker - [user]")
if(prob(chassis.deflect_chance*deflect_coeff))
user << "\red The [W] bounces off [chassis] armor."
@@ -920,7 +920,7 @@
return 0
return
- attackby(weapon,mob/user)
+ attackby(weapon,mob/user, params)
var/result = load_fuel(weapon)
if(isnull(result))
user.visible_message("[user] tries to shove [weapon] into [src]. What a dumb-ass.","[fuel] traces minimal. [weapon] cannot be used as fuel.")
diff --git a/code/game/mecha/equipment/tools/unused_tools.dm b/code/game/mecha/equipment/tools/unused_tools.dm
index 68578913813..50fcc9d62bb 100644
--- a/code/game/mecha/equipment/tools/unused_tools.dm
+++ b/code/game/mecha/equipment/tools/unused_tools.dm
@@ -129,11 +129,11 @@
chassis.proc_res["dynattackby"] = src
return
- proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
+ proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(!action_checks(user) || !active)
return
user.electrocute_act(shock_damage, src)
- return chassis.dynattackby(W,user)
+ return chassis.dynattackby(W,user, params)
/*
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index a4c8b5a527a..fc8c5ee658a 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -158,7 +158,7 @@
var/mob/living/carbon/human/H = M
if(isobj(H.shoes))
var/thingy = H.shoes
- H.drop_from_inventory(H.shoes)
+ H.unEquip(H.shoes)
walk_away(thingy,chassis,15,2)
spawn(20)
if(thingy)
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index 59883f8b8df..8de93482907 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -37,7 +37,7 @@
component_parts += new /obj/item/weapon/stock_parts/capacitor(src)
component_parts += new /obj/item/stack/cable_coil(src, 1)
RefreshParts()
- recharging_turf = get_step(loc, dir)
+ locate_recharge_turf()
/obj/machinery/mech_bay_recharge_port/upgraded/New()
..()
@@ -51,6 +51,9 @@
component_parts += new /obj/item/stack/cable_coil(src, 1)
RefreshParts()
+/obj/machinery/mech_bay_recharge_port/proc/locate_recharge_turf()
+ recharging_turf = get_step(loc, dir)
+
/obj/machinery/mech_bay_recharge_port/RefreshParts()
var/MC
for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
@@ -76,12 +79,12 @@
recharge_console.update_icon()
-/obj/machinery/mech_bay_recharge_port/attackby(obj/item/I, mob/user)
+/obj/machinery/mech_bay_recharge_port/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "recharge_port-o", "recharge_port", I))
return
if(default_change_direction_wrench(user, I))
- recharging_turf = get_step(loc, dir)
+ locate_recharge_turf()
return
if(exchange_parts(user, I))
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 5b7a50c8594..e69394432be 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -498,7 +498,7 @@
return result
-/obj/machinery/mecha_part_fabricator/attackby(obj/W as obj, mob/user as mob)
+/obj/machinery/mecha_part_fabricator/attackby(obj/W as obj, mob/user as mob, params)
if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W))
return
@@ -513,10 +513,7 @@
return 1
else
user << "You can't load \the [name] while it's opened."
- return 1
-
- if(istype(W,/obj/item/weapon/card/emag))
- emag()
+ return 1
if(istype(W, /obj/item/stack))
var/material
@@ -561,6 +558,9 @@
else
user << "\The [src] cannot hold any more [sname] sheet\s."
return
+
+/obj/machinery/mecha_part_fabricator/emag_act(user as mob)
+ emag()
/obj/machinery/mecha_part_fabricator/proc/material2name(var/ID)
return copytext(ID,2)
\ No newline at end of file
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index d34d2bb0f2e..8633d1c871f 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -29,7 +29,7 @@
var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act.
//the values in this list show how much damage will pass through, not how much will be absorbed.
var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1)
- var/obj/item/weapon/cell/cell
+ var/obj/item/weapon/stock_parts/cell/cell
var/state = 0
var/list/log = new
var/last_message = 0
@@ -115,7 +115,7 @@
internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
return internal_tank
-/obj/mecha/proc/add_cell(var/obj/item/weapon/cell/C=null)
+/obj/mecha/proc/add_cell(var/obj/item/weapon/stock_parts/cell/C=null)
if(C)
C.forceMove(src)
cell = C
@@ -453,7 +453,9 @@
src.destroy()
return
-/obj/mecha/attack_hand(mob/user as mob)
+/obj/mecha/attack_hand(mob/living/user as mob)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
src.log_message("Attack by hand/paw. Attacker - [user].",1)
if(ishuman(user))
@@ -461,7 +463,7 @@
call(/obj/item/clothing/gloves/space_ninja/proc/drain)("MECHA",src,user:wear_suit)
return
- if ((M_HULK in user.mutations) && !prob(src.deflect_chance))
+ if ((HULK in user.mutations) && !prob(src.deflect_chance))
src.take_damage(15)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
user.visible_message("[user] hits [src.name], doing some damage.", "You hit [src.name] with all your might. The metal creaks and bends.")
@@ -470,12 +472,14 @@
src.log_append_to_last("Armor saved.")
return
-/obj/mecha/attack_paw(mob/user as mob)
+/obj/mecha/attack_paw(mob/living/user as mob)
return src.attack_hand(user)
-/obj/mecha/attack_alien(mob/user as mob)
+/obj/mecha/attack_alien(mob/living/user as mob)
src.log_message("Attack by alien. Attacker - [user].",1)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
if(!prob(src.deflect_chance))
src.take_damage(15)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
@@ -496,6 +500,7 @@
if(user.melee_damage_upper == 0)
user.emote("[user.friendly] [src]")
else
+ user.do_attack_animation(src)
if(!prob(src.deflect_chance))
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
src.take_damage(damage)
@@ -674,8 +679,10 @@
src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL))
return
-/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/living/user as mob, params)
src.log_message("Attacked by [W]. Attacker - [user]")
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
if(prob(src.deflect_chance))
user << "\red The [W] bounces off [src.name] armor."
src.log_append_to_last("Armor saved.")
@@ -695,18 +702,7 @@
////// AttackBy //////
//////////////////////
-/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob)
-
- if(istype(W, /obj/item/weapon/card/emag))
- if(istype(src, /obj/mecha/working/ripley) && emagged == 0)
- emagged = 1
- usr << "\blue You slide the [W] through the [src]'s ID slot."
- playsound(src.loc, "sparks", 100, 1)
- src.desc += "\red The mech's equiptment slots spark dangerously!"
- else
- usr <<"\red The [src]'s ID slot rejects the [W]."
- return
-
+/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/mmi/posibrain))
if(mmi_move_inside(W,user))
@@ -781,7 +777,7 @@
user << "You screw the cell in place"
return
- else if(istype(W, /obj/item/weapon/cell))
+ else if(istype(W, /obj/item/weapon/stock_parts/cell))
if(state==4)
if(!src.cell)
user << "You install the powercell"
@@ -809,7 +805,9 @@
return
else if(istype(W, /obj/item/mecha_parts/mecha_tracking))
- user.drop_from_inventory(W)
+ if(!user.unEquip(W))
+ user << "\the [W] is stuck to your hand, you cannot put it in \the [src]"
+ return
W.forceMove(src)
user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]")
return
@@ -862,6 +860,15 @@
*/
return
+/obj/mecha/emag_act(user as mob)
+ if(istype(src, /obj/mecha/working/ripley) && emagged == 0)
+ emagged = 1
+ usr << "\blue You slide the card through the [src]'s ID slot."
+ playsound(src.loc, "sparks", 100, 1)
+ src.desc += "\red The mech's equiptment slots spark dangerously!"
+ else
+ usr <<"\red The [src]'s ID slot rejects the card."
+ return
/*
@@ -1118,7 +1125,9 @@
else if(mmi_as_oc.brainmob.stat)
user << "Beta-rhythm below acceptable level."
return 0
- user.drop_from_inventory(mmi_as_oc)
+ if(!user.unEquip(mmi_as_oc))
+ user << "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]"
+ return
var/mob/brainmob = mmi_as_oc.brainmob
brainmob.reset_view(src)
/*
@@ -1231,6 +1240,8 @@
/////////////////////////
/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H)
+ if(!ishuman(H))
+ return 0
for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt))
if(src.check_access(ID,src.operation_req_access))
return 1
diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm
index 37e1c1ee13e..223f6d4aa24 100644
--- a/code/game/mecha/mecha_construction_paths.dm
+++ b/code/game/mecha/mecha_construction_paths.dm
@@ -91,7 +91,7 @@
const_holder.icon_state = "ripley0"
const_holder.density = 1
const_holder.overlays.len = 0
- qdel(src)
+ del(src)
return
diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm
index 58b2d2215ce..6237bcfd523 100644
--- a/code/game/mecha/mecha_parts.dm
+++ b/code/game/mecha/mecha_parts.dm
@@ -7,7 +7,7 @@
icon = 'icons/mecha/mech_construct.dmi'
icon_state = "blank"
w_class = 6
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
origin_tech = "programming=2;materials=2"
var/construction_time = 100
var/list/construction_cost = list("metal"=20000,"glass"=5000)
@@ -18,9 +18,9 @@
icon_state = "backbone"
var/datum/construction/construct
construction_cost = list("metal"=20000)
- flags = FPRINT | CONDUCT
+ flags = CONDUCT
- attackby(obj/item/W as obj, mob/user as mob)
+ attackby(obj/item/W as obj, mob/user as mob, params)
if(!construct || !construct.action(W, user))
..()
return
@@ -409,7 +409,7 @@
icon_state = "std_mod"
item_state = "electronic"
board_type = "other"
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
force = 5.0
w_class = 2.0
throwforce = 5.0
diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm
index 62c8c7c5d47..b2c4b0bc0db 100644
--- a/code/game/mecha/mecha_wreckage.dm
+++ b/code/game/mecha/mecha_wreckage.dm
@@ -30,7 +30,7 @@
return
-/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if(salvage_num <= 0)
diff --git a/code/game/objects/effects/aliens.dm b/code/game/objects/effects/aliens.dm
index c3a05eae825..985b3bca9ba 100644
--- a/code/game/objects/effects/aliens.dm
+++ b/code/game/objects/effects/aliens.dm
@@ -101,7 +101,8 @@
/obj/structure/alien/resin/attack_hand(mob/living/user)
- if(M_HULK in user.mutations)
+ if(HULK in user.mutations)
+ user.do_attack_animation(src)
user.visible_message("[user] destroys [src]!")
health = 0
healthcheck()
@@ -112,6 +113,8 @@
/obj/structure/alien/resin/attack_alien(mob/living/user)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src)
if(islarva(user))
return
user.visible_message("[user] claws at the resin!")
@@ -122,7 +125,8 @@
healthcheck()
-/obj/structure/alien/resin/attackby(obj/item/I, mob/living/user)
+/obj/structure/alien/resin/attackby(obj/item/I, mob/living/user, params)
+ user.changeNext_move(CLICK_CD_MELEE)
health -= I.force
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
healthcheck()
@@ -202,7 +206,8 @@
del(src)
-/obj/structure/alien/weeds/attackby(obj/item/I, mob/user)
+/obj/structure/alien/weeds/attackby(obj/item/I, mob/user, params)
+ user.changeNext_move(CLICK_CD_MELEE)
if(I.attack_verb.len)
visible_message("[user] has [pick(I.attack_verb)] [src] with [I]!")
else
@@ -355,7 +360,8 @@
healthcheck()
-/obj/structure/alien/egg/attackby(obj/item/I, mob/user)
+/obj/structure/alien/egg/attackby(obj/item/I, mob/user, params)
+ user.changeNext_move(CLICK_CD_MELEE)
if(I.attack_verb.len)
visible_message("[user] has [pick(I.attack_verb)] [src] with [I]!")
else
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 2a0235652ac..a0b3fff9c7b 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -35,7 +35,7 @@
qdel(src)
-/obj/effect/anomaly/attackby(obj/item/I, mob/user)
+/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/analyzer))
user << "Analyzing... [src]'s unstable field is fluctuating along frequency [aSignal.code]:[format_frequency(aSignal.frequency)]."
diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm
index d32c2b9b7e1..7f3de1cf8c6 100644
--- a/code/game/objects/effects/decals/contraband.dm
+++ b/code/game/objects/effects/decals/contraband.dm
@@ -48,7 +48,7 @@ obj/structure/sign/poster/New(var/serial)
icon_state = design.icon_state // poster[serial_number]
..()
-obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob)
+obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/wirecutters))
playsound(loc, 'sound/items/Wirecutter.ogg', 100, 1)
if(ruined)
@@ -89,7 +89,7 @@ obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob)
del(src)
-//seperated to reduce code duplication. Moved here for ease of reference and to unclutter r_wall/attackby()
+//seperated to reduce code duplication. Moved here for ease of reference and to unclutter r_wall/attackby(, params)
/turf/simulated/wall/proc/place_poster(var/obj/item/weapon/contraband/poster/P, var/mob/user)
if(!istype(src,/turf/simulated/wall))
diff --git a/code/game/objects/effects/decals/posters/tgposters.dm b/code/game/objects/effects/decals/posters/tgposters.dm
index 2605bfaf20f..6fa936d4ca6 100644
--- a/code/game/objects/effects/decals/posters/tgposters.dm
+++ b/code/game/objects/effects/decals/posters/tgposters.dm
@@ -103,3 +103,13 @@
name = "Borg Fancy v2"
desc = "Borg Fancy, Now only taking the most fancy."
icon_state="poster21"
+
+/datum/poster/tg_22
+ name = "The Griffin"
+ desc = " The Griffin commands you to be the worst you can be. Will you?"
+ icon_state="poster22"
+
+/datum/poster/tg_23
+ name = "The Owl"
+ desc = " The Owl would do his best to protect the station. Will you?"
+ icon_state="poster23"
\ No newline at end of file
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 27db5475d94..73ccbd3aab3 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -11,14 +11,23 @@ would spawn and follow the beaker, even if it is carried or thrown.
icon = 'icons/effects/effects.dmi'
mouse_opacity = 0
unacidable = 1//So effect are not targeted by alien acid.
- flags = TABLEPASS
+
+/datum/effect/effect/proc/fadeOut(var/atom/A, var/frames = 16)
+ if(A.alpha == 0) //Handle already transparent case
+ return
+ if(frames == 0)
+ frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame".
+ var/step = A.alpha / frames
+ for(var/i = 0, i < frames, i++)
+ A.alpha -= step
+ sleep(world.tick_lag)
+ return
/obj/effect/effect/water
name = "water"
icon = 'icons/effects/effects.dmi'
icon_state = "extinguish"
var/life = 15.0
- flags = TABLEPASS
mouse_opacity = 0
/obj/effect/effect/smoke
@@ -183,36 +192,40 @@ steam.start() -- spawns the effect
return
/datum/effect/effect/system/spark_spread
- set_up(n = 3, c = 0, loca)
- number = n > 10 ? 10 : n
- cardinals = c
+ var/total_sparks = 0 // To stop it being spammed and lagging!
- if (istype(loca, /turf/))
+ set_up(n = 3, c = 0, loca)
+ if(n > 10)
+ n = 10
+ number = n
+ cardinals = c
+ if(istype(loca, /turf/))
location = loca
else
location = get_turf(loca)
start()
- for (var/i = 1 to number)
- spawn()
- if (holder)
- location = get_turf(holder)
-
- var/obj/effect/effect/sparks/sparks = getFromPool(/obj/effect/effect/sparks, location)
- playsound(location, "sparks", 100, 1)
+ var/i = 0
+ for(i=0, i 20)
+ return
+ spawn(0)
+ if(holder)
+ src.location = get_turf(holder)
+ var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location)
+ src.total_sparks++
var/direction
-
- if (cardinals)
+ if(src.cardinals)
direction = pick(cardinal)
else
direction = pick(alldirs)
-
- for (var/j = 0, j < pick(1, 2, 3), j++)
+ for(i=0, i 0)
- devastation = min (MAX_EXPLOSION_RANGE, devastation + round(amount/12))
+ devastation = min (MAX_EX_DEVESTATION_RANGE, devastation + round(amount/12))
if (round(amount/6) > 0)
- heavy = min (MAX_EXPLOSION_RANGE, heavy + round(amount/6))
+ heavy = min (MAX_EX_HEAVY_RANGE, heavy + round(amount/6))
if (round(amount/3) > 0)
- light = min (MAX_EXPLOSION_RANGE, light + round(amount/3))
+ light = min (MAX_EX_LIGHT_RANGE, light + round(amount/3))
if (flash && flashing_factor)
flash += (round(amount/4) * flashing_factor)
diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm
index 61ab55b4c68..e8b8cc4bdaf 100644
--- a/code/game/objects/effects/glowshroom.dm
+++ b/code/game/objects/effects/glowshroom.dm
@@ -133,7 +133,7 @@
floor = 1
return 1
-/obj/effect/glowshroom/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/effect/glowshroom/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
endurance -= W.force
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index 1963e2fb29a..a040e460b2c 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -32,4 +32,10 @@
/obj/effect/overlay/coconut
name = "Coconuts"
icon = 'icons/misc/beach.dmi'
- icon_state = "coconuts"
\ No newline at end of file
+ icon_state = "coconuts"
+
+/obj/effect/overlay/adminoverlay
+ name = "adminoverlay"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "admin"
+ layer = 4.1
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index e11b35ac207..62a9bb7a57b 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -20,7 +20,7 @@
qdel(src)
return
-/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user)
+/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user, params)
if(W.attack_verb.len)
visible_message("\red \The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]")
else
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index cedc2e47cd8..399455d208c 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -5,13 +5,11 @@
var/no_embed = 0 // For use in item_attack.dm
var/icon/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
var/blood_overlay_color = null
- var/abstract = 0
var/item_state = null
var/r_speed = 1.0
var/health = null
var/hitsound = null
var/w_class = 3.0
- flags = FPRINT | TABLEPASS
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
pass_flags = PASSTABLE
pressure_resistance = 5
@@ -35,7 +33,6 @@
var/permeability_coefficient = 1 // for chemicals/diseases
var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit)
var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up
- var/canremove = 1 //Mostly for Ninja code at this point but basically will not allow the item to be removed if set to 0. /N
var/reflect_chance = 0 //This var dictates what % of a time an object will reflect an energy based weapon's shot
var/armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
var/list/allowed = null //suit storage stuff.
@@ -56,7 +53,7 @@
/obj/item/Destroy()
if(istype(src.loc, /mob))
var/mob/H = src.loc
- H.drop_from_inventory(src) // items at the very least get unequipped from their mob before being deleted
+ H.unEquip(src) // items at the very least get unequipped from their mob before being deleted
if(reagents && istype(reagents))
reagents.my_atom = null
reagents.delete()
@@ -129,7 +126,7 @@
if(5.0)
size = "huge"
else
- //if ((M_CLUMSY in usr.mutations) && prob(50)) t = "funny-looking"
+ //if ((CLUMSY in usr.mutations) && prob(50)) t = "funny-looking"
usr << "This is a [src.blood_DNA ? "bloody " : ""]\icon[src][src.name]. It is a [size] item."
if(src.desc)
usr << src.desc
@@ -151,19 +148,15 @@
S.remove_from_storage(src)
src.throwing = 0
- if (src.loc == user)
- //canremove==0 means that object may not be removed. You can still wear it. This only applies to clothing. /N
- if(!src.canremove)
+ if (loc == user)
+ if(!user.unEquip(src))
return 0
- else
- user.u_equip(src)
else
- if(isliving(src.loc))
+ if(isliving(loc))
return 0
- user.next_move = max(user.next_move+2,world.time + 2)
- src.pickup(user)
+ pickup(user)
add_fingerprint(user)
user.put_in_active_hand(src)
return 1
@@ -176,7 +169,7 @@
if(!A.has_fine_manipulation || w_class >= 4)
if(src in A.contents) // To stop Aliens having items stuck in their pockets
- A.drop_from_inventory(src)
+ A.unEquip(src)
user << "Your claws aren't capable of such fine manipulation."
return
@@ -187,16 +180,12 @@
M.client.screen -= src
src.throwing = 0
if (src.loc == user)
- //canremove==0 means that object may not be removed. You can still wear it. This only applies to clothing. /N
- if(istype(src, /obj/item/clothing) && !src:canremove)
+ if(!user.unEquip(src))
return
- else
- user.u_equip(src)
else
if(istype(src.loc, /mob/living))
return
src.pickup(user)
- user.next_move = max(user.next_move+2,world.time + 2)
user.put_in_active_hand(src)
return
@@ -207,7 +196,7 @@
if(!A.has_fine_manipulation || w_class >= 4)
if(src in A.contents) // To stop Aliens having items stuck in their pockets
- A.drop_from_inventory(src)
+ A.unEquip(src)
user << "Your claws aren't capable of such fine manipulation."
return
attack_paw(A)
@@ -222,7 +211,7 @@
// Due to storage type consolidation this should get used more now.
// I have cleaned it up a little, but it could probably use more. -Sayu
-/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W,/obj/item/weapon/storage))
var/obj/item/weapon/storage/S = W
if(S.use_to_pickup)
@@ -311,7 +300,7 @@
if(istype(src, /obj/item/clothing/under) || istype(src, /obj/item/clothing/suit))
- if(M_FAT in H.mutations)
+ if(FAT in H.mutations)
testing("[M] TOO FAT TO WEAR [src]!")
if(!(flags & ONESIZEFITSALL))
if(!disable_warning)
@@ -422,6 +411,8 @@
return 0
return 1
if(slot_l_store)
+ if(flags & NODROP) //Pockets aren't visible, so you can't move NODROP items into them.
+ return 0
if(H.l_store)
return 0
if(!H.w_uniform)
@@ -433,6 +424,8 @@
if( w_class <= 2 || (slot_flags & SLOT_POCKET) )
return 1
if(slot_r_store)
+ if(flags & NODROP)
+ return 0
if(H.r_store)
return 0
if(!H.w_uniform)
@@ -445,6 +438,8 @@
return 1
return 0
if(slot_s_store)
+ if(flags & NODROP) //Suit storage NODROP items drop if you take a suit off, this is to prevent people exploiting this.
+ return 0
if(H.s_store)
return 0
if(!H.wear_suit)
@@ -465,13 +460,13 @@
if(slot_handcuffed)
if(H.handcuffed)
return 0
- if(!istype(src, /obj/item/weapon/handcuffs))
+ if(!istype(src, /obj/item/weapon/restraints/handcuffs))
return 0
return 1
if(slot_legcuffed)
if(H.legcuffed)
return 0
- if(!istype(src, /obj/item/weapon/legcuffs))
+ if(!istype(src, /obj/item/weapon/restraints/legcuffs))
return 0
return 1
if(slot_in_backpack)
@@ -480,6 +475,19 @@
if(B.contents.len < B.storage_slots && w_class <= B.max_w_class)
return 1
return 0
+ if(slot_tie)
+ if(!H.w_uniform)
+ if(!disable_warning)
+ H << "You need a jumpsuit before you can attach this [name]."
+ return 0
+ var/obj/item/clothing/under/uniform = H.w_uniform
+ if(uniform.accessories.len && !uniform.can_attach_accessory(src))
+ if (!disable_warning)
+ H << "You already have an accessory of this type attached to your [uniform]."
+ return 0
+ if( !(slot_flags & SLOT_TIE) )
+ return 0
+ return 1
return 0 //Unsupported slot
//END HUMAN
@@ -551,7 +559,7 @@
if( src in usr )
attack_self(usr)
return
- else if(istype(src, /obj/item/clothing/tie))
+ else if(istype(src, /obj/item/clothing/accessory))
if(istype(src.loc,/obj/item/clothing/under))
attack_self(usr)
@@ -604,7 +612,7 @@
M.LAssailant = user
src.add_fingerprint(user)
- //if((M_CLUMSY in user.mutations) && prob(50))
+ //if((CLUMSY in user.mutations) && prob(50))
// M = user
/*
M << "\red You stab yourself in the eye."
diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm
index f2e760a34a5..764be72b131 100644
--- a/code/game/objects/items/apc_frame.dm
+++ b/code/game/objects/items/apc_frame.dm
@@ -5,9 +5,9 @@
desc = "Used for repairing or building APCs"
icon = 'icons/obj/apc_repair.dmi'
icon_state = "apc_frame"
- flags = FPRINT | TABLEPASS| CONDUCT
+ flags = CONDUCT
-/obj/item/apc_frame/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/apc_frame/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
if (istype(W, /obj/item/weapon/wrench))
new /obj/item/stack/sheet/metal( get_turf(src.loc), 2 )
diff --git a/code/game/objects/items/ashtray.dm b/code/game/objects/items/ashtray.dm
index 5ec88ac8bdf..bf554463a42 100644
--- a/code/game/objects/items/ashtray.dm
+++ b/code/game/objects/items/ashtray.dm
@@ -14,14 +14,14 @@
src.pixel_x = rand(-6, 6)
return
-/obj/item/ashtray/attackby(obj/item/weapon/W as obj, mob/user as mob)
+/obj/item/ashtray/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (health < 1)
return
if (istype(W,/obj/item/weapon/cigbutt) || istype(W,/obj/item/clothing/mask/cigarette) || istype(W, /obj/item/weapon/match))
if (contents.len >= max_butts)
user << "This ashtray is full."
return
- user.u_equip(W)
+ user.unEquip(W)
W.loc = src
if (istype(W,/obj/item/clothing/mask/cigarette))
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index 3ff89c5fd60..8eaf699205d 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -40,14 +40,14 @@
density = 0
- attackby(W as obj, mob/user as mob)
+ attackby(W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/pen))
var/t = input(user, "What would you like the label to be?", text("[]", src.name), null) as text
if (user.get_active_hand() != W)
return
if (!in_range(src, user) && src.loc != user)
return
- t = copytext(sanitize(t),1,MAX_MESSAGE_LEN)
+ t = sanitize(copytext(t,1,MAX_MESSAGE_LEN))
if (t)
src.name = "body bag - "
src.name += t
@@ -127,7 +127,7 @@
usr << "\red You can't fold that up anymore.."
..()
- attackby(W as obj, mob/user as mob)
+ attackby(W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))
if(src.allowed(user))
src.locked = !src.locked
diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm
index b47a2a30684..a68258e6a1c 100644
--- a/code/game/objects/items/candle.dm
+++ b/code/game/objects/items/candle.dm
@@ -22,7 +22,7 @@
icon_state = "candle[i][lit ? "_lit" : ""]"
- attackby(obj/item/weapon/W as obj, mob/user as mob)
+ attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
@@ -59,7 +59,8 @@
if(!wax)
new/obj/item/trash/candle(src.loc)
if(istype(src.loc, /mob))
- src.dropped()
+ var/mob/M = src.loc
+ M.unEquip(src, 1) //src is being deleted anyway
del(src)
update_icon()
if(istype(loc, /turf)) //start a fire if possible
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 826de6fd4c4..914ec7d6be0 100755
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -11,7 +11,6 @@ var/global/list/obj/item/device/pda/PDAs = list()
icon_state = "pda"
item_state = "electronic"
w_class = 1.0
- flags = FPRINT | TABLEPASS
slot_flags = SLOT_PDA | SLOT_BELT
//Main variables
@@ -51,6 +50,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both.
var/ownjob = null //related to above
+ var/ownrank = null // this one is rank, never alt title
var/obj/item/device/paicard/pai = null // A slot for a personal AI device
@@ -129,15 +129,15 @@ var/global/list/obj/item/device/pda/PDAs = list()
icon_state = "pda-captain"
detonate = 0
//toff = 1
-
+
/obj/item/device/pda/heads/ntrep
default_cartridge = /obj/item/weapon/cartridge/supervisor
icon_state = "pda-h"
-
+
/obj/item/device/pda/heads/magistrate
default_cartridge = /obj/item/weapon/cartridge/supervisor
icon_state = "pda-h"
-
+
/obj/item/device/pda/heads/blueshield
default_cartridge = /obj/item/weapon/cartridge/hos
icon_state = "pda-h"
@@ -204,7 +204,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/geneticist
default_cartridge = /obj/item/weapon/cartridge/medical
icon_state = "pda-genetics"
-
+
/obj/item/device/pda/centcom
default_cartridge = /obj/item/weapon/cartridge/centcom
icon_state = "pda-h"
@@ -217,9 +217,13 @@ var/global/list/obj/item/device/pda/PDAs = list()
detonate = 0
-/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text)
+/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text, newrank as null|text)
owner = newname
ownjob = newjob
+ if(newrank)
+ ownrank = newrank
+ else
+ ownrank = ownjob
name = newname + " (" + ownjob + ")"
@@ -575,6 +579,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
id_check(U, 1)
if("UpdateInfo")
ownjob = id.assignment
+ ownrank = id.rank
name = "PDA-[owner] ([ownjob])"
if("Eject")//Ejects the cart, only done from hub.
if (!isnull(cartridge))
@@ -684,7 +689,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
U << "The PDA softly beeps."
ui.close()
else
- t = copytext(sanitize(t), 1, 20)
+ t = sanitize(copytext(t, 1, 20))
ttone = t
else
ui.close()
@@ -892,7 +897,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/proc/create_message(var/mob/living/U = usr, var/obj/item/device/pda/P)
var/t = input(U, "Please enter message", name, null) as text
- t = copytext(sanitize(t), 1, MAX_MESSAGE_LEN)
+ t = sanitize(copytext(t, 1, MAX_MESSAGE_LEN))
t = readd_quotes(t)
if (!t || !istype(P))
return
@@ -1050,7 +1055,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
return
// access to status display signals
-/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob)
+/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob, params)
..()
if(istype(C, /obj/item/weapon/cartridge) && !cartridge)
cartridge = C
@@ -1069,6 +1074,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
+ ownrank = idcard.rank
name = "PDA-[owner] ([ownjob])"
user << "Card scanned."
else
diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm
index 63f1082884f..e3ea25af4e4 100644
--- a/code/game/objects/items/devices/PDA/radio.dm
+++ b/code/game/objects/items/devices/PDA/radio.dm
@@ -48,6 +48,8 @@
signal.data[key4] = value4
frequency.post_signal(src, signal, filter = s_filter)
+
+ return
/obj/item/radio/integrated/receive_signal(datum/signal/signal)
/*var/obj/item/device/pda/P = src.loc
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 7816473110e..144b920631c 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -4,7 +4,6 @@
icon_state = "aicard" // aicard-full
item_state = "electronic"
w_class = 2.0
- flags = FPRINT | TABLEPASS
slot_flags = SLOT_BELT
var/flush = null
origin_tech = "programming=4;materials=4"
@@ -137,4 +136,4 @@
if(2.0)
if(prob(50)) del(src)
if(3.0)
- if(prob(25)) del(src)
+ if(prob(25)) del(src)
diff --git a/code/game/objects/items/devices/autopsy.dm b/code/game/objects/items/devices/autopsy.dm
index 8c5fbf7661e..67a424a309f 100644
--- a/code/game/objects/items/devices/autopsy.dm
+++ b/code/game/objects/items/devices/autopsy.dm
@@ -7,7 +7,7 @@
desc = "Extracts information on wounds."
icon = 'icons/obj/autopsy_scanner.dmi'
icon_state = ""
- flags = FPRINT | TABLEPASS | CONDUCT
+ flags = CONDUCT
w_class = 1.0
origin_tech = "materials=1;biotech=1"
var/list/datum/autopsy_data_scanner/wdata = list()
diff --git a/code/game/objects/items/devices/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm
new file mode 100644
index 00000000000..057cdb765fc
--- /dev/null
+++ b/code/game/objects/items/devices/camera_bug.dm
@@ -0,0 +1,371 @@
+#define VANILLA_BUG 0
+#define UNIVERSAL_BUG 1
+#define NETWORK_BUG 2
+#define SABOTAGE_BUG 3
+#define ADVANCED_BUG 4
+#define ADMIN_BUG 5
+
+#define BUGMODE_LIST 0
+#define BUGMODE_MONITOR 1
+#define BUGMODE_TRACK 2
+
+
+
+/obj/item/device/camera_bug
+ name = "camera bug"
+ desc = "For illicit snooping through the camera network."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "camera_bug"
+ w_class = 1.0
+ item_state = "camera_bug"
+ icon_override = 'icons/mob/in-hand/tools.dmi'
+ throw_speed = 4
+ throw_range = 20
+
+ var/obj/machinery/camera/current = null
+ var/obj/item/expansion = null
+ var/bugtype = VANILLA_BUG
+
+ var/last_net_update = 0
+ var/last_bugtype = VANILLA_BUG
+ var/list/bugged_cameras = list()
+ var/skip_bugcheck = 0
+
+ var/track_mode = BUGMODE_LIST
+ var/last_tracked = 0
+ var/refresh_interval = 50
+
+ var/tracked_name = null
+ var/atom/tracking = null
+
+ var/last_found = null
+ var/last_seen = null
+
+/obj/item/device/camera_bug/New()
+ ..()
+ processing_objects += src
+
+/obj/item/device/camera_bug/Destroy()
+ if(expansion)
+ qdel(expansion)
+ expansion = null
+ del(src)
+/* Easier to just call del() than this nonsense
+ get_cameras()
+ for(var/cam_tag in bugged_cameras)
+ var/obj/machinery/camera/camera = bugged_cameras[cam_tag]
+ if(camera.bug == src)
+ camera.bug = null
+ bugged_cameras = list()
+ if(tracking)
+ tracking = null
+ ..()
+*/
+
+/obj/item/device/camera_bug/interact(var/mob/user = usr)
+ var/datum/browser/popup = new(user, "camerabug","Camera Bug",nref=src)
+ popup.set_content(menu(get_cameras()))
+ popup.open()
+
+/obj/item/device/camera_bug/attack_self(mob/user as mob)
+ user.set_machine(src)
+ interact(user)
+
+/obj/item/device/camera_bug/check_eye(var/mob/user as mob)
+ if (user.stat || loc != user || !user.canmove || user.eye_blind || !current)
+ user.reset_view(null)
+ user.unset_machine()
+ return null
+
+ var/turf/T = get_turf(user.loc)
+ if(T.z != current.z || (!skip_bugcheck && current.bug != src) || !current.can_use())
+ user << "[src] has lost the signal."
+ current = null
+ user.reset_view(null)
+ user.unset_machine()
+ return null
+
+ return 1
+
+/obj/item/device/camera_bug/proc/get_cameras()
+ if(bugtype != last_bugtype || ( (bugtype in list(UNIVERSAL_BUG,NETWORK_BUG,ADMIN_BUG)) && world.time > (last_net_update + 100)))
+ bugged_cameras = list()
+ last_bugtype = bugtype
+ for(var/obj/machinery/camera/camera in cameranet.cameras)
+ if(camera.stat || !camera.can_use())
+ continue
+ switch(bugtype)
+ if(VANILLA_BUG,SABOTAGE_BUG,ADVANCED_BUG)
+ if(camera.bug == src)
+ bugged_cameras[camera.c_tag] = camera
+ if(UNIVERSAL_BUG)
+ if(camera.bug)
+ bugged_cameras[camera.c_tag] = camera
+ if(NETWORK_BUG,ADMIN_BUG)
+ if(length(list("SS13","MINE")&camera.network))
+ bugged_cameras[camera.c_tag] = camera
+ sortList(bugged_cameras)
+ return bugged_cameras
+
+
+/obj/item/device/camera_bug/proc/menu(var/list/cameras)
+ if(!cameras || !cameras.len)
+ return "No bugged cameras found."
+
+ var/html
+ switch(track_mode)
+ if(BUGMODE_LIST)
+ html = "