")
text = replacetext(text, "\[cell\]", "")
text = replacetext(text, "\[logo\]", " ")
- text = replacetext(text, "\[time\]", "[gameTimestamp()]") // TO DO
+ text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
if(P)
text = "[text]"
else
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index e4d834a35a0..00aae01d07b 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -23,15 +23,27 @@
return wtime + (time_offset + wusage) * world.tick_lag
//Returns the world time in english
-/proc/worldtime2text(time = world.time)
- time = (round_start_time ? (time - round_start_time) : (time - world.time))
- return "[round(time / 36000)+12]:[(time / 600 % 60) < 10 ? add_zero(time / 600 % 60, 1) : time / 600 % 60]"
+/proc/worldtime2text()
+ return gameTimestamp("hh:mm:ss", world.time)
-/proc/time_stamp()
- return time2text(world.timeofday, "hh:mm:ss")
+/proc/time_stamp(format = "hh:mm:ss", show_ds)
+ var/time_string = time2text(world.timeofday, format)
+ return show_ds ? "[time_string]:[world.timeofday % 10]" : time_string
-/proc/gameTimestamp(format = "hh:mm:ss") // Get the game time in text
- return time2text(world.time - timezoneOffset + 432000, format)
+/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
+ if(!wtime)
+ wtime = world.time
+ return time2text(wtime - GLOB.timezoneOffset, format)
+
+/* This is used for displaying the "station time" equivelent of a world.time value
+ Calling it with no args will give you the current time, but you can specify a world.time-based value as an argument
+ - You can use this, for example, to do "This will expire at [station_time_at(world.time + 500)]" to display a "station time" expiration date
+ which is much more useful for a player)*/
+/proc/station_time(time=world.time, display_only=FALSE)
+ return ((((time - round_start_time)) + GLOB.gametime_offset) % 864000) - (display_only ? GLOB.timezoneOffset : 0)
+
+/proc/station_time_timestamp(format = "hh:mm:ss", time=world.time)
+ return time2text(station_time(time, TRUE), format)
/* Returns 1 if it is the selected month and day */
proc/isDay(var/month, var/day)
@@ -165,4 +177,11 @@ proc/isDay(var/month, var/day)
else
day = "1 day"
- return "[day][hour][minute][second]"
\ No newline at end of file
+ return "[day][hour][minute][second]"
+
+GLOBAL_VAR_INIT(midnight_rollovers, 0)
+GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0)
+/proc/update_midnight_rollover()
+ if(world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
+ return GLOB.midnight_rollovers++
+ return GLOB.midnight_rollovers
\ No newline at end of file
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index f106d579e94..c4d210088fd 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -526,6 +526,14 @@ proc/GaussRandRound(var/sigma,var/roundto)
return toReturn
+//Searches contents of the atom and returns the sum of all w_class of obj/item within
+/atom/proc/GetTotalContentsWeight(searchDepth = 5)
+ var/weight = 0
+ var/list/content = GetAllContents(searchDepth)
+ for(var/obj/item/I in content)
+ weight += I.w_class
+ return weight
+
//Step-towards method of determining whether one atom can see another. Similar to viewers()
/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate.
var/turf/current = get_turf(source)
@@ -777,15 +785,15 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(toupdate.len)
for(var/turf/simulated/T1 in toupdate)
- air_master.remove_from_active(T1)
+ SSair.remove_from_active(T1)
T1.CalculateAdjacentTurfs()
- air_master.add_to_active(T1,1)
+ SSair.add_to_active(T1,1)
if(fromupdate.len)
for(var/turf/simulated/T2 in fromupdate)
- air_master.remove_from_active(T2)
+ SSair.remove_from_active(T2)
T2.CalculateAdjacentTurfs()
- air_master.add_to_active(T2,1)
+ SSair.add_to_active(T2,1)
@@ -941,7 +949,7 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(toupdate.len)
for(var/turf/simulated/T1 in toupdate)
T1.CalculateAdjacentTurfs()
- air_master.add_to_active(T1,1)
+ SSair.add_to_active(T1,1)
return copiedobjs
@@ -1337,7 +1345,7 @@ Standard way to write links -Sayu
if(covered_locations & HEAD)
return 0
if("eyes")
- if(covered_locations & HEAD || face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES)
+ if(face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES || eyesmouth_covered & HEADCOVERSEYES)
return 0
if("mouth")
if(covered_locations & HEAD || face_covered & HIDEFACE || eyesmouth_covered & MASKCOVERSMOUTH)
@@ -1914,3 +1922,10 @@ var/mob/dview/dview_mob = new
for(var/atom/thing in here)
if(istype(thing, type) && (check_shift && thing.pixel_x == shift_x && thing.pixel_y == shift_y))
. += thing
+
+//gives us the stack trace from CRASH() without ending the current proc.
+/proc/stack_trace(msg)
+ CRASH(msg)
+
+/datum/proc/stack_trace(msg)
+ CRASH(msg)
\ No newline at end of file
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index be703845fb3..959c930bbf1 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -13,6 +13,8 @@ var/list/diona_names = file2list ("config/names/diona.txt")
var/list/verbs = file2list("config/names/verbs.txt")
var/list/adjectives = file2list("config/names/adjectives.txt")
+var/list/dream_strings = file2list("config/names/dreams.txt")
+var/list/nightmare_strings = file2list("config/names/nightmares.txt")
//loaded on startup because of "
//would include in rsc if ' was used
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index cb1ce8f10a7..0cc712799c7 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -19,7 +19,6 @@ var/global/list/all_areas = list()
var/global/list/machines = list()
var/global/list/machine_processing = list()
var/global/list/fast_processing = list()
-var/global/list/atmos_machinery = list()
var/global/list/processing_power_items = list() //items that ask to be called every cycle
var/global/list/rcd_list = list() //list of Rapid Construction Devices.
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index bc61c854ab4..91514a79a7e 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -2,8 +2,6 @@ var/global/obj/effect/overlay/plmaster = null
var/global/obj/effect/overlay/slmaster = null
var/global/obj/effect/overlay/icemaster = null
-// nanomanager, the manager for Nano UIs
-var/datum/nanomanager/nanomanager = new()
// Event Manager, the manager for events.
var/datum/event_manager/event_manager = new()
// Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it.
@@ -74,8 +72,10 @@ var/score_dmgestkey = null
var/TAB = " "
-var/timezoneOffset = 0 // The difference betwen midnight (of the host computer) and 0 world.ticks.
+GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks.
// For FTP requests. (i.e. downloading runtime logs.)
// However it'd be ok to use for accessing attack logs and such too, which are even laggier.
-var/fileaccess_timer = 0
\ No newline at end of file
+var/fileaccess_timer = 0
+
+GLOBAL_VAR_INIT(gametime_offset, 432000) // 12:00 in seconds
\ No newline at end of file
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index f30809d32ef..f300608e699 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -127,9 +127,7 @@
if(A == loc || (A in loc) || (sdepth != -1 && sdepth <= 2))
// No adjacency needed
if(W)
- var/resolved = A.attackby(W,src)
- if(!resolved && A && W)
- W.afterattack(A,src,1,params) // 1 indicates adjacency
+ W.melee_attack_chain(src, A, params)
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
@@ -145,10 +143,7 @@
if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
if(A.Adjacent(src)) // see adjacent.dm
if(W)
- // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example, params)
- var/resolved = A.attackby(W,src,params)
- if(!resolved && A && W)
- W.afterattack(A,src,1,params) // 1: clicking something Adjacent
+ W.melee_attack_chain(src, A, params)
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index 631044f5c97..a00c9561cd3 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -81,10 +81,7 @@
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents)
if(A == loc || (A in loc) || (A in contents))
- // No adjacency checks
- var/resolved = A.attackby(W,src,params, params)
- if(!resolved && A && W)
- W.afterattack(A,src,1,params)
+ W.melee_attack_chain(src, A, params)
return
if(!isturf(loc))
@@ -93,9 +90,7 @@
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc && isturf(A.loc.loc))
if(isturf(A) || isturf(A.loc))
if(A.Adjacent(src)) // see adjacent.dm
- var/resolved = A.attackby(W, src, params, params)
- if(!resolved && A && W)
- W.afterattack(A, src, 1, params)
+ W.melee_attack_chain(src, A, params)
return
else
W.afterattack(A, src, 0, params)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 977b68ae5a3..545dcfd0380 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -16,6 +16,9 @@
var/datum/hud/hud = null
appearance_flags = NO_CLIENT_COLOR
+/obj/screen/take_damage()
+ return
+
/obj/screen/Destroy()
master = null
return ..()
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 8d132ed2586..58b12ed8857 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -1,36 +1,33 @@
+/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
+ if(pre_attackby(target, user, params))
+ // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
+ var/resolved = target.attackby(src, user, params)
+ if(!resolved && target && !qdeleted(src))
+ afterattack(target, user, 1, params) // 1: clicking something Adjacent
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
return
+/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby!
+ return TRUE //return FALSE to avoid calling attackby after this proc does stuff
+
// No comment
-/atom/proc/attackby(obj/item/W, mob/living/user, params)
+/atom/proc/attackby(obj/item/W, mob/user, params)
return
-/atom/movable/attackby(obj/item/W, mob/living/user, params)
- user.changeNext_move(CLICK_CD_MELEE)
- user.do_attack_animation(src)
- if(!(W.flags&NOBLUDGEON))
- visible_message("[src] has been hit by [user] with [W].")
+/obj/attackby(obj/item/I, mob/living/user, params)
+ return I.attack_obj(src, user)
-/mob/living/attackby(obj/item/I, mob/user, params)
+/mob/living/attackby(obj/item/I, mob/living/user, params)
user.changeNext_move(CLICK_CD_MELEE)
if(attempt_harvest(I, user))
- return
- I.attack(src, user)
+ return 1
+ return I.attack(src, user)
-
-// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
-// Click parameters is the params string from byond Click() code, see that documentation.
-/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
- return
-
-
-/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone)
-
- if(!istype(M)) // not sure if this is the right thing...
+/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone)
+ if(flags & (NOBLUDGEON))
return 0
- var/messagesource = M
if(can_operate(M)) //Checks if mob is lying down on table for surgery
if(istype(src,/obj/item/robot_parts))//popup override for direct attach
@@ -46,7 +43,7 @@
else
return 1
- if(istype(src,/obj/item/weapon/screwdriver) && M.get_species() == "Machine")
+ if(isscrewdriver(src) && M.get_species() == "Machine")
if(!attempt_initiate_surgery(src, M, user))
return 0
else
@@ -57,136 +54,87 @@
else
return 1
- if(istype(M,/mob/living/carbon/brain))
- var/mob/living/carbon/brain/B = M
- messagesource = B.container
- if(hitsound && force > 0)
- playsound(loc, hitsound, 50, 1, -1)
- /////////////////////////
+ if(!force)
+ playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
+ else if(hitsound)
+ playsound(loc, hitsound, get_clamped_volume(), 1, -1)
+
user.lastattacked = M
M.lastattacker = user
- add_logs(user, M, "attacked", name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])", print_attack_log = (force > 0))//print it if stuff deals damage
- if(!iscarbon(user))
- M.LAssailant = null
- else
- M.LAssailant = user
+ if(user != M)
+ user.do_attack_animation(M)
+ M.attacked_by(src, user, def_zone)
- /////////////////////////
-
- if(isanimal(M))
- var/mob/living/simple_animal/S = M
- S.attacked_by(src, user)
- return 0 // No sanic-speed double-attacks for you - simple mobs will handle being attacked on their own
- var/power = force
-
- if(!istype(M, /mob/living/carbon/human))
- if(istype(M, /mob/living/carbon/slime))
- var/mob/living/carbon/slime/slime = M
- if(prob(25))
- to_chat(user, "[src] passes right through [M]!")
- return
-
- if(power > 0)
- slime.attacked += 10
-
- if(slime.Discipline && prob(50)) // wow, buddy, why am I getting attacked??
- slime.Discipline = 0
-
- if(power >= 3)
- if(slime.is_adult)
- if(prob(5 + round(power/2)))
-
- if(slime.Victim)
- if(prob(80) && !slime.client)
- slime.Discipline++
- slime.Victim = null
- slime.anchored = 0
-
- spawn()
- if(slime)
- slime.SStun = 1
- sleep(rand(5,20))
- if(slime)
- slime.SStun = 0
-
- spawn(0)
- if(slime)
- slime.canmove = 0
- step_away(slime, user)
- if(prob(25 + power))
- sleep(2)
- if(slime && user)
- step_away(slime, user)
- slime.canmove = 1
-
- else
- if(prob(10 + power*2))
- if(slime)
- if(slime.Victim)
- if(prob(80) && !slime.client)
- slime.Discipline++
-
- if(slime.Discipline == 1)
- slime.attacked = 0
-
- spawn()
- if(slime)
- slime.SStun = 1
- sleep(rand(5,20))
- if(slime)
- slime.SStun = 0
-
- slime.Victim = null
- slime.anchored = 0
-
-
- spawn(0)
- if(slime && user)
- step_away(slime, user)
- slime.canmove = 0
- if(prob(25 + power*4))
- sleep(2)
- if(slime && user)
- step_away(slime, user)
- slime.canmove = 1
-
-
- var/showname = "."
- if(user)
- showname = " by [user]."
- user.do_attack_animation(src)
- if(!(user in viewers(M, null)))
- showname = "."
-
- for(var/mob/O in viewers(messagesource, null))
- if(attack_verb.len)
- O.show_message("[M] has been [pick(attack_verb)] with [src][showname] ", 1)
- else
- O.show_message("[M] has been attacked with [src][showname] ", 1)
-
- if(!showname && user)
- if(user.client)
- to_chat(user, "You attack [M] with [src]. ")
-
-
-
- if(istype(M, /mob/living/carbon/human))
- return M:attacked_by(src, user, def_zone) //make sure to return whether we have hit or miss
- else
- switch(damtype)
- if("brute")
- if(istype(src, /mob/living/carbon/slime))
- M.adjustBrainLoss(power)
-
- else
-
- M.take_organ_damage(power)
- if(prob(33)) // Added blood for whacking non-humans too
- M.add_splatter_floor()
- if("fire")
- M.take_organ_damage(0, power)
- to_chat(M, "Aargh it burns!")
- M.updatehealth()
+ add_logs(user, M, "attacked", name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", print_attack_log = (force > 0))//print it if stuff deals damage
add_fingerprint(user)
+
+
+//the equivalent of the standard version of attack() but for object targets.
+/obj/item/proc/attack_obj(obj/O, mob/living/user)
+ if(flags & (NOBLUDGEON))
+ return
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(O)
+ O.attacked_by(src, user)
+
+/atom/movable/proc/attacked_by()
+ return
+
+/obj/attacked_by(obj/item/I, mob/living/user)
+ if(I.force)
+ user.visible_message("[user] has hit [src] with [I]!", "You hit [src] with [I]!")
+ take_damage(I.force, I.damtype, "melee", 1)
+
+/mob/living/attacked_by(obj/item/I, mob/living/user, def_zone)
+ send_item_attack_message(I, user)
+ if(I.force)
+ apply_damage(I.force, I.damtype, def_zone)
+ if(I.damtype == BRUTE)
+ if(prob(33))
+ I.add_mob_blood(src)
+ var/turf/location = get_turf(src)
+ add_splatter_floor(location)
+ if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
+ user.add_mob_blood(src)
+ return TRUE //successful attack
+
+/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
+ if(!I.force)
+ user.visible_message("[user] gently taps [src] with [I].",\
+ "This weapon is ineffective, it does no damage!")
+ else if(I.force < force_threshold || I.damtype == STAMINA)
+ visible_message("[I] bounces harmlessly off of [src].",\
+ "[I] bounces harmlessly off of [src]!")
+ else
+ return ..()
+
+// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
+// Click parameters is the params string from byond Click() code, see that documentation.
+/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ return
+
+/obj/item/proc/get_clamped_volume()
+ if(w_class)
+ if(force)
+ return Clamp((force + w_class) * 4, 30, 100)// Add the item's force to its weight class and multiply by 4, then clamp the value between 30 and 100
+ else
+ return Clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
+
+/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area)
+ if(I.discrete)
+ return
+ var/message_verb = "attacked"
+ if(I.attack_verb && I.attack_verb.len)
+ message_verb = "[pick(I.attack_verb)]"
+ else if(!I.force)
+ return
+ var/message_hit_area = ""
+ if(hit_area)
+ message_hit_area = " in the [hit_area]"
+ var/attack_message = "[src] has been [message_verb][message_hit_area] with [I]."
+ if(user in viewers(src, null))
+ attack_message = "[user] has [message_verb] [src][message_hit_area] with [I]!"
+ visible_message("[attack_message]",\
+ "[attack_message]")
return 1
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index 5fe1663d7e8..4fc377e9e37 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -34,8 +34,6 @@
/**
* Config vars
*/
- // Process name
- var/name
// Process schedule interval
// This controls how often the process would run under ideal conditions.
@@ -364,7 +362,7 @@
var/highestRunTime = round(highest_run_time, 0.001)
var/deferTime = round(cpu_defer_count / 10 * world.tick_lag, 0.01)
if(!statclick)
- statclick = new (src)
+ statclick = new /obj/effect/statclick/debug(src)
stat("[name]", statclick.update("T#[getTicks()] | AR [averageRunTime] | LR [lastRunTime] | HR [highestRunTime] | D [deferTime]"))
/datum/controller/process/proc/catchException(var/exception/e, var/thrower)
diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm
index e8f723586d8..9fcf6d13759 100644
--- a/code/controllers/ProcessScheduler/core/processScheduler.dm
+++ b/code/controllers/ProcessScheduler/core/processScheduler.dm
@@ -232,7 +232,7 @@ var/global/datum/controller/processScheduler/processScheduler
stat("Processes", "Scheduler not running")
return
if(!statclick)
- statclick = new (src)
+ statclick = new /obj/effect/statclick/debug(src)
stat("Processes", statclick.update("[processes.len] (R [running.len] / Q [queued.len] / I [idle.len])"))
for(var/datum/controller/process/p in processes)
p.statProcess()
diff --git a/code/controllers/Processes/air.dm b/code/controllers/Processes/air.dm
deleted file mode 100644
index e7937593c19..00000000000
--- a/code/controllers/Processes/air.dm
+++ /dev/null
@@ -1,197 +0,0 @@
-var/kill_air = 0
-
-var/global/datum/controller/process/air_system/air_master
-
-/datum/controller/process/air_system
- var/list/excited_groups = list()
- var/list/active_turfs = list()
- var/list/hotspots = list()
-
- //Special functions lists
- var/list/turf/simulated/active_super_conductivity = list()
- var/list/turf/simulated/high_pressure_delta = list()
-
- var/current_cycle = 0
- var/failed_ticks = 0
- var/tick_progress = 0
-
- // Stats
- var/last_active = 0
- var/last_excited = 0
- var/last_hpd = 0
- var/last_hotspots = 0
- var/last_asc = 0
-
-
-/datum/controller/process/air_system/setup()
- name = "air"
- schedule_interval = 4
- start_delay = 4
-
- var/watch = start_watch()
- log_startup_progress("Processing geometry...")
- setup_overlays() // Assign icons and such for gas-turf-overlays
- setup_allturfs() // Get all currently active tiles that need processing each atmos tick.
- log_startup_progress(" Geometry processed in [stop_watch(watch)]s.")
-
-/datum/controller/process/air_system/doWork()
- if(kill_air)
- return 1
- current_cycle++
- process_pipenets()
- process_atmos_machinery()
- process_active_turfs()
- process_excited_groups()
- process_high_pressure_delta()
- process_hotspots()
- process_super_conductivity()
- return 1
-
-/datum/controller/process/air_system/statProcess()
- ..()
- stat(null, "[last_active] active")
- stat(null, "[last_excited] EG | [last_hpd] HPD | [last_asc] ASC | [last_hotspots] Hot")
- stat(null, "[pipe_networks.len] pipe nets, [deferred_pipenet_rebuilds.len] deferred")
- stat(null, "[atmos_machinery.len] atmos machines")
-
-DECLARE_GLOBAL_CONTROLLER(air_system, air_master)
-
-/datum/controller/process/air_system/proc/process_hotspots()
- last_hotspots = hotspots.len
- for(var/obj/effect/hotspot/H in hotspots)
- H.process()
- SCHECK
-
-/datum/controller/process/air_system/proc/process_super_conductivity()
- last_asc = active_super_conductivity.len
- for(var/turf/simulated/T in active_super_conductivity)
- T.super_conduct()
- SCHECK
-
-/datum/controller/process/air_system/proc/process_high_pressure_delta()
- last_hpd = high_pressure_delta.len
- for(var/turf/T in high_pressure_delta)
- T.high_pressure_movements()
- T.pressure_difference = 0
- SCHECK
- high_pressure_delta.Cut()
-
-/datum/controller/process/air_system/proc/process_active_turfs()
- last_active = active_turfs.len
- for(var/turf/simulated/T in active_turfs)
- T.process_cell()
- SCHECK
-
-/datum/controller/process/air_system/proc/process_pipenets()
- for(last_object in deferred_pipenet_rebuilds)
- var/obj/machinery/atmospherics/M = last_object
- if(istype(M) && isnull(M.gcDestroyed))
- try
- M.build_network()
- catch(var/exception/e)
- catchException(e, M)
- SCHECK
- else
- catchBadType(M)
- deferred_pipenet_rebuilds -= M
-
- for(last_object in pipe_networks)
- var/datum/pipeline/pipeNetwork = last_object
- if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
- try
- pipeNetwork.process()
- catch(var/exception/e)
- catchException(e, pipeNetwork)
- SCHECK
- else
- catchBadType(pipeNetwork)
- pipe_networks -= pipeNetwork
-
-/datum/controller/process/air_system/proc/process_atmos_machinery()
- for(last_object in atmos_machinery)
- var/obj/machinery/M = last_object
- if(istype(M) && isnull(M.gcDestroyed))
- try
- if(M.process_atmos() == PROCESS_KILL)
- atmos_machinery.Remove(M)
- continue
- catch(var/exception/e)
- catchException(e, M)
- else
- catchBadType(M)
- atmos_machinery -= M
-
- SCHECK
-
-
-/datum/controller/process/air_system/proc/remove_from_active(var/turf/simulated/T)
- if(istype(T))
- T.excited = 0
- active_turfs -= T
- if(T.excited_group)
- T.excited_group.garbage_collect()
-
-/datum/controller/process/air_system/proc/add_to_active(var/turf/simulated/T, var/blockchanges = 1)
- if(istype(T) && T.air)
- T.excited = 1
- active_turfs |= T
- if(blockchanges && T.excited_group)
- T.excited_group.garbage_collect()
- else
- for(var/direction in cardinal)
- if(!(T.atmos_adjacent_turfs & direction))
- continue
- var/turf/simulated/S = get_step(T, direction)
- if(istype(S))
- add_to_active(S)
-
-/datum/controller/process/air_system/proc/setup_allturfs(var/turfs_in = world)
- for(var/turf/simulated/T in turfs_in)
- T.CalculateAdjacentTurfs()
- if(!T.blocks_air)
- T.update_visuals()
- for(var/direction in cardinal)
- if(!(T.atmos_adjacent_turfs & direction))
- continue
- var/turf/enemy_tile = get_step(T, direction)
- if(istype(enemy_tile,/turf/simulated/))
- var/turf/simulated/enemy_simulated = enemy_tile
- if(!T.air.compare(enemy_simulated.air))
- T.excited = 1
- active_turfs |= T
- break
- else
- if(!T.air.check_turf_total(enemy_tile))
- T.excited = 1
- active_turfs |= T
-
-/datum/controller/process/air_system/proc/process_excited_groups()
- last_excited = excited_groups.len
- for(var/datum/excited_group/EG in excited_groups)
- EG.breakdown_cooldown++
- if(EG.breakdown_cooldown == 10)
- EG.self_breakdown()
- SCHECK
- return
- if(EG.breakdown_cooldown > 20)
- EG.dismantle()
- SCHECK
-
-/datum/controller/process/air_system/proc/setup_overlays()
- plmaster = new /obj/effect/overlay()
- plmaster.icon = 'icons/effects/tile_effects.dmi'
- plmaster.icon_state = "plasma"
- plmaster.layer = FLY_LAYER
- plmaster.mouse_opacity = 0
-
- slmaster = new /obj/effect/overlay()
- slmaster.icon = 'icons/effects/tile_effects.dmi'
- slmaster.icon_state = "sleeping_agent"
- slmaster.layer = FLY_LAYER
- slmaster.mouse_opacity = 0
-
- icemaster = new /obj/effect/overlay()
- icemaster.icon = 'icons/turf/overlays.dmi'
- icemaster.icon_state = "snowfloor"
- icemaster.layer = TURF_LAYER+0.1
- icemaster.mouse_opacity = 0
diff --git a/code/controllers/Processes/fires.dm b/code/controllers/Processes/fires.dm
deleted file mode 100644
index 26d42833d84..00000000000
--- a/code/controllers/Processes/fires.dm
+++ /dev/null
@@ -1,24 +0,0 @@
-var/global/datum/controller/process/fire/fire_master
-
-/datum/controller/process/fire
- var/list/burning = list()
-
-/datum/controller/process/fire/setup()
- name = "fire"
- schedule_interval = 20 //every 2 seconds
- log_startup_progress("Fire process starting up.")
-
-/datum/controller/process/fire/statProcess()
- ..()
- stat(null, "[burning.len] burning objects")
-
-/datum/controller/process/fire/doWork()
- for(var/obj/burningobj in burning)
- if(burningobj.burn_state == ON_FIRE)
- if(burningobj.burn_world_time < world.time)
- burningobj.burn()
- SCHECK
- else
- burning.Remove(burningobj)
-
-DECLARE_GLOBAL_CONTROLLER(fire, fire_master)
diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/Processes/inactivity.dm
deleted file mode 100644
index b08df7983fc..00000000000
--- a/code/controllers/Processes/inactivity.dm
+++ /dev/null
@@ -1,16 +0,0 @@
-/*/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)]")
- to_chat(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/shuttles.dm b/code/controllers/Processes/shuttles.dm
index 4f47bda014c..57ca886d6e1 100644
--- a/code/controllers/Processes/shuttles.dm
+++ b/code/controllers/Processes/shuttles.dm
@@ -13,6 +13,7 @@ var/const/CALL_SHUTTLE_REASON_LENGTH = 12
var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds)
var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
+ var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher
var/area/emergencyLastCallLoc
var/emergencyNoEscape
@@ -134,7 +135,11 @@ DECLARE_GLOBAL_CONTROLLER(shuttle, shuttle_master)
var/area/signal_origin = get_area(user)
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
- emergency.request(null, 0.5, signal_origin, html_decode(emergency_reason), 1)
+ var/extra_minutes = 0
+ var/priority_time = emergencyCallTime * 0.5
+ if(world.time - emergency_sec_level_time < priority_time)
+ extra_minutes = 5
+ emergency.request(null, 0.5 + extra_minutes / (emergencyCallTime / 600), signal_origin, html_decode(emergency_reason), 1)
else
emergency.request(null, 1, signal_origin, html_decode(emergency_reason), 0)
diff --git a/code/controllers/Processes/spacedrift.dm b/code/controllers/Processes/spacedrift.dm
deleted file mode 100644
index d799aeb99d9..00000000000
--- a/code/controllers/Processes/spacedrift.dm
+++ /dev/null
@@ -1,53 +0,0 @@
-var/global/datum/controller/process/spacedrift/drift_master
-
-/datum/controller/process/spacedrift
- var/list/processing_list = list()
-
-/datum/controller/process/spacedrift/setup()
- name = "spacedrift"
- schedule_interval = 5
- start_delay = 20
- log_startup_progress("Spacedrift starting up.")
-
-/datum/controller/process/spacedrift/statProcess()
- ..()
- stat(null, "P:[processing_list.len]")
-
-/datum/controller/process/spacedrift/doWork()
- var/list/currentrun = processing_list.Copy()
-
- while(currentrun.len)
- var/atom/movable/AM = currentrun[currentrun.len]
- currentrun.len--
- if(!AM)
- processing_list -= AM
- SCHECK
- continue
-
- if(AM.inertia_next_move > world.time)
- SCHECK
- continue
-
- if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0))
- AM.inertia_dir = 0
-
- if(!AM.inertia_dir)
- AM.inertia_last_loc = null
- processing_list -= AM
- SCHECK
- continue
-
- var/old_dir = AM.dir
- var/old_loc = AM.loc
- AM.inertia_moving = TRUE
- step(AM, AM.inertia_dir)
- AM.inertia_moving = FALSE
- AM.inertia_next_move = world.time + AM.inertia_move_delay
- if(AM.loc == old_loc)
- AM.inertia_dir = 0
-
- AM.setDir(old_dir)
- AM.inertia_last_loc = AM.loc
- SCHECK
-
-DECLARE_GLOBAL_CONTROLLER(spacedrift, drift_master)
\ No newline at end of file
diff --git a/code/controllers/Processes/sun.dm b/code/controllers/Processes/sun.dm
deleted file mode 100644
index bd9644b4536..00000000000
--- a/code/controllers/Processes/sun.dm
+++ /dev/null
@@ -1,61 +0,0 @@
-var/global/datum/controller/process/sun/sun
-
-/datum/controller/process/sun
- var/angle
- var/dx
- var/dy
- var/rate
- var/list/solars = list() // for debugging purposes, references solars list at the constructor
-
-/datum/controller/process/sun/setup()
- name = "sun"
- schedule_interval = 600 // every 60 seconds
- log_startup_progress("Sun ticker starting up.")
-
- angle = rand (0,360) // the station position to the sun is randomised at round start
- rate = rand(50,200)/100 // 50% - 200% of standard rotation
- if(prob(50)) // same chance to rotate clockwise than counter-clockwise
- rate = -rate
-
-/datum/controller/process/sun/doWork()
- calc_position()
- update_solar_machinery()
-
-DECLARE_GLOBAL_CONTROLLER(sun, sun)
-
-// 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
-/datum/controller/process/sun/proc/calc_position()
- angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
-
- // now calculate and cache the (dx,dy) increments for line drawing
- var/s = sin(angle)
- var/c = cos(angle)
-
- // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)"
- // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions
-
- if(abs(s) < abs(c))
- dx = s / abs(c)
- dy = c / abs(c)
- else
- dx = s / abs(s)
- dy = c / abs(s)
-
-//now tell the solar control computers to update their status and linked devices
-/datum/controller/process/sun/proc/update_solar_machinery()
- for(last_object in solars)
- var/obj/machinery/power/solar_control/SC = last_object
- if(istype(SC) && isnull(SC.gcDestroyed))
- if(!SC.powernet)
- solars -= SC
- continue
- try
- SC.update()
- catch(var/exception/e)
- catchException(e, SC)
- SCHECK
- else
- catchBadType(SC)
- solars -= SC
diff --git a/code/controllers/Processes/throwing.dm b/code/controllers/Processes/throwing.dm
deleted file mode 100644
index a11c1252d35..00000000000
--- a/code/controllers/Processes/throwing.dm
+++ /dev/null
@@ -1,138 +0,0 @@
-#define MAX_THROWING_DIST 512 // 2 z-levels on default width
-#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
-
-var/global/datum/controller/process/throwing/throw_master
-
-/datum/controller/process/throwing
- var/list/processing_list
-
-/datum/controller/process/throwing/setup()
- name = "throwing"
- schedule_interval = 1
- start_delay = 20
- log_startup_progress("Throw ticker starting up.")
-
-/datum/controller/process/throwing/statProcess()
- ..()
- stat(null, "P:[processing_list.len]")
-
-/datum/controller/process/throwing/started()
- ..()
- if(!processing_list)
- processing_list = list()
-
-/datum/controller/process/throwing/doWork()
- for(last_object in processing_list)
- var/atom/movable/AM = last_object
- if(istype(AM) && isnull(AM.gcDestroyed))
- var/datum/thrownthing/TT = processing_list[AM]
- if(istype(TT) && isnull(TT.gcDestroyed))
- TT.tick()
- SCHECK
- else
- catchBadType(TT)
- processing_list -= AM
- AM.throwing = null
- else
- catchBadType(AM)
- processing_list -= AM
- SCHECK
-
-DECLARE_GLOBAL_CONTROLLER(throwing, throw_master)
-
-/datum/thrownthing
- var/atom/movable/thrownthing
- var/atom/target
- var/turf/target_turf
- var/init_dir
- var/maxrange
- var/speed
- var/mob/thrower
- var/diagonals_first
- var/dist_travelled = 0
- var/start_time
- var/dist_x
- var/dist_y
- var/dx
- var/dy
- var/pure_diagonal
- var/diagonal_error
- var/datum/callback/callback
-
-/datum/thrownthing/proc/tick()
- var/atom/movable/AM = thrownthing
- if(!isturf(AM.loc) || !AM.throwing)
- finalize()
- return
-
- if(dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
- finalize()
- return
-
- var/atom/step
-
- // calculate how many tiles to move, making up for any missed ticks.
- if((dist_travelled >= maxrange || AM.loc == target_turf) && has_gravity(AM, AM.loc))
- finalize()
- return
-
- if(dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
- step = get_step(AM, get_dir(AM, target_turf))
- else
- step = get_step(AM, init_dir)
-
- if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
- if(diagonal_error >= 0 && max(dist_x, dist_y) - dist_travelled != 1) // we do a step forward unless we're right before the target
- step = get_step(AM, dx)
- diagonal_error += (diagonal_error < 0) ? dist_x / 2 : -dist_y
-
- if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
- finalize()
- return
-
- AM.Move(step, get_dir(AM, step))
-
- if(!AM.throwing) // we hit something during our move
- finalize(hit = TRUE)
- return
-
- dist_travelled++
-
- if(dist_travelled > MAX_THROWING_DIST)
- finalize()
- return
-
-/datum/thrownthing/proc/finalize(hit = FALSE)
- set waitfor = 0
- throw_master.processing_list -= thrownthing
- // done throwning, either because it hit something or it finished moving
- thrownthing.throwing = null
- if(!hit)
- for(var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on.
- var/atom/A = thing
- if(A == target)
- hit = 1
- thrownthing.throw_impact(A, src)
- break
- if(!hit)
- thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
- thrownthing.newtonian_move(init_dir)
- else
- thrownthing.newtonian_move(init_dir)
- if(callback)
- callback.Invoke()
-
-/datum/thrownthing/proc/hit_atom(atom/A)
- thrownthing.throw_impact(A, src)
- thrownthing.newtonian_move(init_dir)
- finalize(TRUE)
-
-/datum/thrownthing/proc/hitcheck()
- for(var/thing in get_turf(thrownthing))
- var/atom/movable/AM = thing
- if(AM == thrownthing)
- continue
- if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
- thrownthing.throwing = null
- thrownthing.throw_impact(AM, src)
- return TRUE
\ No newline at end of file
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 1e95c7b8977..cf9a05c54db 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -189,6 +189,20 @@
var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default
+ // StonedMC
+ var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
+
+ // Highpop tickrates
+ var/base_mc_tick_rate = 1
+ var/high_pop_mc_tick_rate = 1.1
+
+ var/high_pop_mc_mode_amount = 65
+ var/disable_high_pop_mc_mode_amount = 60
+
+ // Nightshift
+ var/randomize_shift_time = FALSE
+ var/enable_night_shifts = FALSE
+
/datum/configuration/New()
var/list/L = subtypesof(/datum/game_mode)
for(var/T in L)
@@ -595,8 +609,18 @@
shutdown_shell_command = value
if("disable_karma")
- disable_karma = 1
+ config.disable_karma = 1
+ if("tick_limit_mc_init")
+ config.tick_limit_mc_init = text2num(value)
+ if("base_mc_tick_rate")
+ config.base_mc_tick_rate = text2num(value)
+ if("high_pop_mc_tick_rate")
+ config.high_pop_mc_tick_rate = text2num(value)
+ if("high_pop_mc_mode_amount")
+ config.high_pop_mc_mode_amount = text2num(value)
+ if("disable_high_pop_mc_mode_amount")
+ config.disable_high_pop_mc_mode_amount = text2num(value)
else
diary << "Unknown setting in configuration: '[name]'"
@@ -659,6 +683,10 @@
MAX_EX_FLAME_RANGE = BombCap
if("default_laws")
config.default_laws = text2num(value)
+ if("randomize_shift_time")
+ config.randomize_shift_time = TRUE
+ if("enable_night_shifts")
+ config.enable_night_shifts = TRUE
else
diary << "Unknown setting in configuration: '[name]'"
diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm
new file mode 100644
index 00000000000..06547d120d5
--- /dev/null
+++ b/code/controllers/controller.dm
@@ -0,0 +1,19 @@
+/datum/controller
+ var/name
+ // The object used for the clickable stat() button.
+ var/obj/effect/statclick/statclick
+
+/datum/controller/proc/Initialize()
+
+//cleanup actions
+/datum/controller/proc/Shutdown()
+
+//when we enter dmm_suite.load_map
+/datum/controller/proc/StartLoadingMap()
+
+//when we exit dmm_suite.load_map
+/datum/controller/proc/StopLoadingMap()
+
+/datum/controller/proc/Recover()
+
+/datum/controller/proc/stat_entry()
\ No newline at end of file
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index afb0e68d83f..78303449f26 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -1,39 +1,97 @@
-var/global/datum/controller/failsafe/failsafe
+GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
-/datum/controller/failsafe // This thing pretty much just keeps poking the controllers.
- processing_interval = 100 // Poke the controllers every 10 seconds.
- /*
- * 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).
- */
+/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
+ name = "Failsafe"
- // master
- var/masterControllerIteration = 0
- var/masterControllerAlertLevel = 0
+ // The length of time to check on the MC (in deciseconds).
+ // Set to 0 to disable.
+ var/processing_interval = 20
+ // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
+ var/defcon = 5
+ //the world.time of the last check, so the mc can restart US if we hang.
+ // (Real friends look out for *eachother*)
+ var/lasttick = 0
+
+ // Track the MC iteration to make sure its still on track.
+ var/master_iteration = 0
+ var/running = TRUE
/datum/controller/failsafe/New()
- . = ..()
+ // Highlander-style: there can only be one! Kill off the old and replace it with the new.
+ if(Failsafe != src)
+ if(istype(Failsafe))
+ qdel(Failsafe)
+ Failsafe = src
+ Initialize()
- // 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)
+/datum/controller/failsafe/Initialize()
+ set waitfor = 0
+ Failsafe.Loop()
+ if(!qdeleted(src))
+ qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
- failsafe = src
+/datum/controller/failsafe/Destroy()
+ running = FALSE
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
- //failsafe.process()
+/datum/controller/failsafe/proc/Loop()
+ while(running)
+ lasttick = world.time
+ if(!Master)
+ // Replace the missing Master! This should never, ever happen.
+ new /datum/controller/master()
+ // Only poke it if overrides are not in effect.
+ if(processing_interval > 0)
+ if(Master.processing && Master.iteration)
+ // Check if processing is done yet.
+ if(Master.iteration == master_iteration)
+ switch(defcon)
+ if(4,5)
+ --defcon
+ if(3)
+ message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.")
+ --defcon
+ if(2)
+ to_chat(admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.")
+ --defcon
+ if(1)
-/datum/controller/failsafe/proc/process()
- processing = 1
+ to_chat(admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...")
+ --defcon
+ var/rtn = Recreate_MC()
+ if(rtn > 0)
+ defcon = 4
+ master_iteration = 0
+ to_chat(admins, "MC restarted successfully")
+ else if(rtn < 0)
+ log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
+ to_chat(admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.")
+ //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
+ //no need to handle that specially when defcon 0 can handle it
+ if(0) //DEFCON 0! (mc failed to restart)
+ var/rtn = Recreate_MC()
+ if(rtn > 0)
+ defcon = 4
+ master_iteration = 0
+ to_chat(admins, "MC restarted successfully")
+ else
+ defcon = min(defcon + 1,5)
+ master_iteration = Master.iteration
+ if(defcon <= 1)
+ sleep(processing_interval * 2)
+ else
+ sleep(processing_interval)
+ else
+ defcon = 5
+ sleep(initial(processing_interval))
- spawn(0)
- set background = BACKGROUND_ENABLED
+/datum/controller/failsafe/proc/defcon_pretty()
+ return defcon
- while(1) // More efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop.
- iteration++
+/datum/controller/failsafe/stat_entry()
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(src, "Initializing...")
- sleep(processing_interval)
+ stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
new file mode 100644
index 00000000000..ab92ca49be7
--- /dev/null
+++ b/code/controllers/globals.dm
@@ -0,0 +1,69 @@
+GLOBAL_REAL(GLOB, /datum/controller/global_vars)
+
+/datum/controller/global_vars
+ name = "Global Variables"
+
+ var/list/gvars_datum_protected_varlist
+ var/list/gvars_datum_in_built_vars
+ var/list/gvars_datum_init_order
+
+/datum/controller/global_vars/New()
+ if(GLOB)
+ CRASH("Multiple instances of global variable controller created")
+ GLOB = src
+
+ var/datum/controller/exclude_these = new
+ gvars_datum_in_built_vars = exclude_these.vars + list("gvars_datum_protected_varlist", "gvars_datum_in_built_vars", "gvars_datum_init_order")
+ qdel(exclude_these)
+
+ log_to_dd("[vars.len - gvars_datum_in_built_vars.len] global variables")
+
+ Initialize()
+
+/datum/controller/global_vars/Destroy(force)
+ stack_trace("Some fucker qdel'd the global holder!")
+ if(!force)
+ return QDEL_HINT_LETMELIVE
+
+ QDEL_NULL(statclick)
+ gvars_datum_protected_varlist.Cut()
+ gvars_datum_in_built_vars.Cut()
+
+ GLOB = null
+
+ return ..()
+
+/datum/controller/global_vars/stat_entry()
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(src, "Initializing...")
+
+ stat("Globals:", statclick.update("Edit"))
+
+/datum/controller/global_vars/can_vv_get(var_name)
+ if(gvars_datum_protected_varlist[var_name])
+ return FALSE
+ return ..()
+
+/datum/controller/global_vars/vv_edit_var(var_name, var_value)
+ if(gvars_datum_protected_varlist[var_name])
+ return FALSE
+ return ..()
+
+/datum/controller/global_vars/Initialize()
+ gvars_datum_init_order = list()
+ gvars_datum_protected_varlist = list("gvars_datum_protected_varlist" = TRUE)
+ var/list/global_procs = typesof(/datum/controller/global_vars/proc)
+ var/expected_len = vars.len - gvars_datum_in_built_vars.len
+ if(global_procs.len != expected_len)
+ warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!")
+ if(global_procs.len)
+ var/list/expected_global_procs = vars - gvars_datum_in_built_vars
+ for(var/I in global_procs)
+ expected_global_procs -= replacetext("[I]", "InitGlobal", "")
+ log_to_dd("Missing procs: [expected_global_procs.Join(", ")]")
+ for(var/I in global_procs)
+ var/start_tick = world.time
+ call(src, I)()
+ var/end_tick = world.time
+ if(end_tick - start_tick)
+ warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!")
\ No newline at end of file
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
new file mode 100644
index 00000000000..28395717f08
--- /dev/null
+++ b/code/controllers/master.dm
@@ -0,0 +1,612 @@
+ /**
+ * StonedMC
+ *
+ * Designed to properly split up a given tick among subsystems
+ * Note: if you read parts of this code and think "why is it doing it that way"
+ * Odds are, there is a reason
+ *
+ **/
+
+//This is the ABSOLUTE ONLY THING that should init globally like this
+GLOBAL_REAL(Master, /datum/controller/master) = new
+
+//THIS IS THE INIT ORDER
+//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
+//GOT IT MEMORIZED?
+
+/datum/controller/master
+ name = "Master"
+
+ // Are we processing (higher values increase the processing delay by n ticks)
+ var/processing = TRUE
+ // How many times have we ran
+ var/iteration = 0
+
+ // world.time of last fire, for tracking lag outside of the mc
+ var/last_run
+
+ // List of subsystems to process().
+ var/list/subsystems
+
+ // Vars for keeping track of tick drift.
+ var/init_timeofday
+ var/init_time
+ var/tickdrift = 0
+
+ var/sleep_delta = 1
+
+ var/make_runtime = 0
+
+ var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
+
+ // The type of the last subsystem to be process()'d.
+ var/last_type_processed
+
+ var/datum/controller/subsystem/queue_head //Start of queue linked list
+ var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
+ var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
+ var/queue_priority_count_bg = 0 //Same, but for background subsystems
+ var/map_loading = FALSE //Are we loading in a new map?
+
+ var/current_runlevel //for scheduling different subsystems for different stages of the round
+ var/sleep_offline_after_initializations = TRUE
+
+ var/static/restart_clear = 0
+ var/static/restart_timeout = 0
+ var/static/restart_count = 0
+
+ var/static/random_seed
+
+ //current tick limit, assigned before running a subsystem.
+ //used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
+ var/static/current_ticklimit = TICK_LIMIT_RUNNING
+
+/datum/controller/master/New()
+ makeDatumRefLists()
+ load_configuration()
+ // Highlander-style: there can only be one! Kill off the old and replace it with the new.
+
+ if(!random_seed)
+ random_seed = rand(1, 1e9)
+ rand_seed(random_seed)
+
+ var/list/_subsystems = list()
+ subsystems = _subsystems
+ if(Master != src)
+ if(istype(Master))
+ Recover()
+ qdel(Master)
+ else
+ var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
+ sortTim(subsytem_types, /proc/cmp_subsystem_init)
+ for(var/I in subsytem_types)
+ _subsystems += new I
+ Master = src
+
+ if(!GLOB)
+ new /datum/controller/global_vars
+
+/datum/controller/master/Destroy()
+ ..()
+ // Tell qdel() to Del() this object.
+ return QDEL_HINT_HARDDEL_NOW
+
+/datum/controller/master/Shutdown()
+ processing = FALSE
+ sortTim(subsystems, /proc/cmp_subsystem_init)
+ reverseRange(subsystems)
+ for(var/datum/controller/subsystem/ss in subsystems)
+ log_to_dd("Shutting down [ss.name] subsystem...")
+ ss.Shutdown()
+ log_to_dd("Shutdown complete")
+
+// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
+// -1 if we encountered a runtime trying to recreate it
+/proc/Recreate_MC()
+ . = -1 //so if we runtime, things know we failed
+ if(world.time < Master.restart_timeout)
+ return 0
+ if(world.time < Master.restart_clear)
+ Master.restart_count *= 0.5
+
+ var/delay = 50 * ++Master.restart_count
+ Master.restart_timeout = world.time + delay
+ Master.restart_clear = world.time + (delay * 2)
+ Master.processing = FALSE //stop ticking this one
+ try
+ new/datum/controller/master()
+ catch
+ return -1
+ return 1
+
+
+/datum/controller/master/Recover()
+ var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
+ for(var/varname in Master.vars)
+ switch (varname)
+ if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk.
+ continue
+ else
+ var/varval = Master.vars[varname]
+ if(istype(varval, /datum)) // Check if it has a type var.
+ var/datum/D = varval
+ msg += "\t [varname] = [D]([D.type])\n"
+ else
+ msg += "\t [varname] = [varval]\n"
+ log_to_dd(msg)
+
+ var/datum/controller/subsystem/BadBoy = Master.last_type_processed
+ var/FireHim = FALSE
+ if(istype(BadBoy))
+ msg = null
+ LAZYINITLIST(BadBoy.failure_strikes)
+ switch(++BadBoy.failure_strikes[BadBoy.type])
+ if(2)
+ msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again."
+ FireHim = TRUE
+ if(3)
+ msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
+ BadBoy.flags |= SS_NO_FIRE
+ if(msg)
+ to_chat(admins, "[msg]")
+ log_to_dd(msg)
+
+ if(istype(Master.subsystems))
+ if(FireHim)
+ Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
+ subsystems = Master.subsystems
+ current_runlevel = Master.current_runlevel
+ StartProcessing(10)
+ else
+ to_chat(world, "The Master Controller is having some issues, we will need to re-initialize EVERYTHING")
+ Initialize(20, TRUE)
+
+
+// Please don't stuff random bullshit here,
+// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
+/datum/controller/master/Initialize(delay, init_sss)
+ set waitfor = 0
+
+ if(delay)
+ sleep(delay)
+
+ if(init_sss)
+ init_subtypes(/datum/controller/subsystem, subsystems)
+
+ to_chat(world, "Initializing subsystems...")
+
+ // Sort subsystems by init_order, so they initialize in the correct order.
+ sortTim(subsystems, /proc/cmp_subsystem_init)
+
+ var/start_timeofday = REALTIMEOFDAY
+ // Initialize subsystems.
+ current_ticklimit = config.tick_limit_mc_init
+ for(var/datum/controller/subsystem/SS in subsystems)
+ if(SS.flags & SS_NO_INIT)
+ continue
+ SS.Initialize(REALTIMEOFDAY)
+ CHECK_TICK
+ current_ticklimit = TICK_LIMIT_RUNNING
+ var/time = (REALTIMEOFDAY - start_timeofday) / 10
+
+ var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
+ to_chat(world, "[msg]")
+ log_to_dd(msg)
+
+ if(!current_runlevel)
+ SetRunLevel(1)
+
+ // Sort subsystems by display setting for easy access.
+ sortTim(subsystems, /proc/cmp_subsystem_display)
+ // Set world options.
+ if(sleep_offline_after_initializations)
+ world.sleep_offline = TRUE
+ // world.fps = CONFIG_GET(number/fps) // TIGER TODO
+ world.tick_lag = config.Ticklag
+ var/initialized_tod = REALTIMEOFDAY
+ sleep(1)
+ initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
+ // Loop.
+ Master.StartProcessing(0)
+
+/datum/controller/master/proc/SetRunLevel(new_runlevel)
+ var/old_runlevel = current_runlevel
+ if(isnull(old_runlevel))
+ old_runlevel = "NULL"
+
+ testing("MC: Runlevel changed from [old_runlevel] to [new_runlevel]")
+ current_runlevel = log(2, new_runlevel) + 1
+ if(current_runlevel < 1)
+ CRASH("Attempted to set invalid runlevel: [new_runlevel]")
+
+// Starts the mc, and sticks around to restart it if the loop ever ends.
+/datum/controller/master/proc/StartProcessing(delay)
+ set waitfor = 0
+ if(delay)
+ sleep(delay)
+ testing("Master starting processing")
+ var/rtn = Loop()
+ if(rtn > 0 || processing < 0)
+ return //this was suppose to happen.
+ //loop ended, restart the mc
+ log_game("MC crashed or runtimed, restarting")
+ message_admins("MC crashed or runtimed, restarting")
+ var/rtn2 = Recreate_MC()
+ if(rtn2 <= 0)
+ log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
+ message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
+ Failsafe.defcon = 2
+
+// Main loop.
+/datum/controller/master/proc/Loop()
+ . = -1
+ //Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
+ // local vars rock
+
+ //all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
+ var/list/tickersubsystems = list()
+ var/list/runlevel_sorted_subsystems = list(list(), list(), list(), list(), list(), list(), list(), list()) //ensure we always have as many runlevels as we need to operate with no subsystems (8 currently)
+ var/timer = world.time
+ for(var/thing in subsystems)
+ var/datum/controller/subsystem/SS = thing
+ if(SS.flags & SS_NO_FIRE)
+ continue
+ SS.queued_time = 0
+ SS.queue_next = null
+ SS.queue_prev = null
+ SS.state = SS_IDLE
+ if(SS.flags & SS_TICKER)
+ tickersubsystems += SS
+ timer += world.tick_lag * rand(1, 5)
+ SS.next_fire = timer
+ continue
+
+ var/ss_runlevels = SS.runlevels
+ var/added_to_any = FALSE
+ for(var/I in 1 to GLOB.bitflags.len)
+ if(ss_runlevels & GLOB.bitflags[I])
+ while(runlevel_sorted_subsystems.len < I)
+ runlevel_sorted_subsystems += list(list())
+ runlevel_sorted_subsystems[I] += SS
+ added_to_any = TRUE
+ if(!added_to_any)
+ WARNING("[SS.name] subsystem is not SS_NO_FIRE but also does not have any runlevels set!")
+
+ queue_head = null
+ queue_tail = null
+ //these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
+ //(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
+ sortTim(tickersubsystems, /proc/cmp_subsystem_priority)
+ for(var/I in runlevel_sorted_subsystems)
+ sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
+ I += tickersubsystems
+
+ var/cached_runlevel = current_runlevel
+ var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
+
+ init_timeofday = REALTIMEOFDAY
+ init_time = world.time
+
+ iteration = 1
+ var/error_level = 0
+ var/sleep_delta = 1
+ var/list/subsystems_to_check
+ //the actual loop.
+
+ while(1)
+ tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
+ var/starting_tick_usage = TICK_USAGE
+ if(processing <= 0)
+ current_ticklimit = TICK_LIMIT_RUNNING
+ sleep(10)
+ continue
+
+ //Anti-tick-contention heuristics:
+ //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
+ // (because sleeps are processed in the order received, longer sleeps are more likely to run first)
+ if(starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
+ sleep_delta *= 2
+ current_ticklimit = TICK_LIMIT_RUNNING * 0.5
+ sleep(world.tick_lag * (processing * sleep_delta))
+ continue
+
+ //Byond resumed us late. assume it might have to do the same next tick
+ if(last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
+ sleep_delta += 1
+
+ sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
+
+ if(starting_tick_usage > (TICK_LIMIT_MC * 0.75)) //we ran 3/4 of the way into the tick
+ sleep_delta += 1
+
+ //debug
+ if(make_runtime)
+ var/datum/controller/subsystem/SS
+ SS.can_fire = 0
+
+ if(!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick + (Failsafe.processing_interval * 5)) < world.time))
+ new/datum/controller/failsafe() // (re)Start the failsafe.
+
+ //now do the actual stuff
+ if(!queue_head || !(iteration % 3))
+ var/checking_runlevel = current_runlevel
+ if(cached_runlevel != checking_runlevel)
+ //resechedule subsystems
+ cached_runlevel = checking_runlevel
+ current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
+ var/stagger = world.time
+ for(var/I in current_runlevel_subsystems)
+ var/datum/controller/subsystem/SS = I
+ if(SS.next_fire <= world.time)
+ stagger += world.tick_lag * rand(1, 5)
+ SS.next_fire = stagger
+
+ subsystems_to_check = current_runlevel_subsystems
+ else
+ subsystems_to_check = tickersubsystems
+
+ if(CheckQueue(subsystems_to_check) <= 0)
+ if(!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
+ log_to_dd("MC: SoftReset() failed, crashing")
+ return
+ if(!error_level)
+ iteration++
+ error_level++
+ current_ticklimit = TICK_LIMIT_RUNNING
+ sleep(10)
+ continue
+
+ if(queue_head)
+ if(RunQueue() <= 0)
+ if(!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
+ log_to_dd("MC: SoftReset() failed, crashing")
+ return
+ if(!error_level)
+ iteration++
+ error_level++
+ current_ticklimit = TICK_LIMIT_RUNNING
+ sleep(10)
+ continue
+ error_level--
+ if(!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync
+ queue_priority_count = 0
+ queue_priority_count_bg = 0
+
+ iteration++
+ last_run = world.time
+ src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
+ current_ticklimit = TICK_LIMIT_RUNNING
+ if(processing * sleep_delta <= world.tick_lag)
+ current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
+ sleep(world.tick_lag * (processing * sleep_delta))
+
+
+
+
+// This is what decides if something should run.
+/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
+ . = 0 //so the mc knows if we runtimed
+
+ //we create our variables outside of the loops to save on overhead
+ var/datum/controller/subsystem/SS
+ var/SS_flags
+
+ for(var/thing in subsystemstocheck)
+ if(!thing)
+ subsystemstocheck -= thing
+ SS = thing
+ if(SS.state != SS_IDLE)
+ continue
+ if(SS.can_fire <= 0)
+ continue
+ if(SS.next_fire > world.time)
+ continue
+ SS_flags = SS.flags
+ if(SS_flags & SS_NO_FIRE)
+ subsystemstocheck -= SS
+ continue
+ if((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time)
+ continue
+ SS.enqueue()
+ . = 1
+
+
+// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
+/datum/controller/master/proc/RunQueue()
+ . = 0
+ var/datum/controller/subsystem/queue_node
+ var/queue_node_flags
+ var/queue_node_priority
+ var/queue_node_paused
+
+ var/current_tick_budget
+ var/tick_precentage
+ var/tick_remaining
+ var/ran = TRUE //this is right
+ var/ran_non_ticker = FALSE
+ var/bg_calc //have we swtiched current_tick_budget to background mode yet?
+ var/tick_usage
+
+ //keep running while we have stuff to run and we haven't gone over a tick
+ // this is so subsystems paused eariler can use tick time that later subsystems never used
+ while(ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
+ ran = FALSE
+ bg_calc = FALSE
+ current_tick_budget = queue_priority_count
+ queue_node = queue_head
+ while(queue_node)
+ if(ran && TICK_USAGE > TICK_LIMIT_RUNNING)
+ break
+
+ queue_node_flags = queue_node.flags
+ queue_node_priority = queue_node.queued_priority
+
+ //super special case, subsystems where we can't make them pause mid way through
+ //if we can't run them this tick (without going over a tick)
+ //we bump up their priority and attempt to run them next tick
+ //(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
+ // in those cases, so we just let them run)
+ if(queue_node_flags & SS_NO_TICK_CHECK)
+ if(queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
+ queue_node.queued_priority += queue_priority_count * 0.1
+ queue_priority_count -= queue_node_priority
+ queue_priority_count += queue_node.queued_priority
+ current_tick_budget -= queue_node_priority
+ queue_node = queue_node.queue_next
+ continue
+
+ if((queue_node_flags & SS_BACKGROUND) && !bg_calc)
+ current_tick_budget = queue_priority_count_bg
+ bg_calc = TRUE
+
+ tick_remaining = TICK_LIMIT_RUNNING - TICK_USAGE
+
+ if(current_tick_budget > 0 && queue_node_priority > 0)
+ tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
+ else
+ tick_precentage = tick_remaining
+
+ tick_precentage = max(tick_precentage*0.5, tick_precentage - queue_node.tick_overrun)
+
+ current_ticklimit = round(TICK_USAGE + tick_precentage)
+
+ if(!(queue_node_flags & SS_TICKER))
+ ran_non_ticker = TRUE
+ ran = TRUE
+
+ queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
+ last_type_processed = queue_node
+
+ queue_node.state = SS_RUNNING
+
+ tick_usage = TICK_USAGE
+ var/state = queue_node.ignite(queue_node_paused)
+ tick_usage = TICK_USAGE - tick_usage
+
+ if(state == SS_RUNNING)
+ state = SS_IDLE
+ current_tick_budget -= queue_node_priority
+
+
+ if(tick_usage < 0)
+ tick_usage = 0
+ queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage - tick_precentage))
+ queue_node.state = state
+
+ if(state == SS_PAUSED)
+ queue_node.paused_ticks++
+ queue_node.paused_tick_usage += tick_usage
+ queue_node = queue_node.queue_next
+ continue
+
+ queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
+ tick_usage += queue_node.paused_tick_usage
+
+ queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
+
+ queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
+ queue_node.paused_ticks = 0
+ queue_node.paused_tick_usage = 0
+
+ if(queue_node_flags & SS_BACKGROUND) //update our running total
+ queue_priority_count_bg -= queue_node_priority
+ else
+ queue_priority_count -= queue_node_priority
+
+ queue_node.last_fire = world.time
+ queue_node.times_fired++
+
+ if(queue_node_flags & SS_TICKER)
+ queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
+ else if(queue_node_flags & SS_POST_FIRE_TIMING)
+ queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun / 100))
+ else if(queue_node_flags & SS_KEEP_TIMING)
+ queue_node.next_fire += queue_node.wait
+ else
+ queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun / 100))
+
+ queue_node.queued_time = 0
+
+ //remove from queue
+ queue_node.dequeue()
+
+ queue_node = queue_node.queue_next
+
+ . = 1
+
+//resets the queue, and all subsystems, while filtering out the subsystem lists
+// called if any mc's queue procs runtime or exit improperly.
+/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
+ . = 0
+ log_to_dd("MC: SoftReset called, resetting MC queue state.")
+ if(!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
+ log_to_dd("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
+ return
+ var/subsystemstocheck = subsystems + ticker_SS
+ for(var/I in runlevel_SS)
+ subsystemstocheck |= I
+
+ for(var/thing in subsystemstocheck)
+ var/datum/controller/subsystem/SS = thing
+ if(!SS || !istype(SS))
+ //list(SS) is so if a list makes it in the subsystem list, we remove the list, not the contents
+ subsystems -= list(SS)
+ ticker_SS -= list(SS)
+ for(var/I in runlevel_SS)
+ I -= list(SS)
+ log_to_dd("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
+ continue
+ if(SS.queue_next && !istype(SS.queue_next))
+ log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
+ SS.queue_next = null
+ if(SS.queue_prev && !istype(SS.queue_prev))
+ log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
+ SS.queue_prev = null
+ SS.queued_priority = 0
+ SS.queued_time = 0
+ SS.state = SS_IDLE
+ if(queue_head && !istype(queue_head))
+ log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
+ queue_head = null
+ if(queue_tail && !istype(queue_tail))
+ log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
+ queue_tail = null
+ queue_priority_count = 0
+ queue_priority_count_bg = 0
+ log_to_dd("MC: SoftReset: Finished.")
+ . = 1
+
+
+
+/datum/controller/master/stat_entry()
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(src, "Initializing...")
+
+ stat("Byond", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
+ stat("Master Controller", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
+
+// Currently unimplemented
+/datum/controller/master/StartLoadingMap()
+ //disallow more than one map to load at once, multithreading it will just cause race conditions
+ while(map_loading)
+ stoplag()
+ for(var/S in subsystems)
+ var/datum/controller/subsystem/SS = S
+ SS.StartLoadingMap()
+ map_loading = TRUE
+
+/datum/controller/master/StopLoadingMap(bounds = null)
+ map_loading = FALSE
+ for(var/S in subsystems)
+ var/datum/controller/subsystem/SS = S
+ SS.StopLoadingMap()
+
+
+/datum/controller/master/proc/UpdateTickRate()
+ if(!processing)
+ return
+ var/client_count = length(clients)
+ if(client_count < config.disable_high_pop_mc_mode_amount)
+ processing = config.base_mc_tick_rate
+ else if(client_count > config.high_pop_mc_mode_amount)
+ processing = config.high_pop_mc_tick_rate
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 2dc0e38e66c..11cd37343fe 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -1,6 +1,6 @@
-//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one.
-//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs)
-//WIP, needs lots of work still
+// old deprecated rusted piece of shit MC
+// All this does now is misc. world init stuff because too lazy to put it somewhere else
+// It used to run all of the repeating processes controlling the game but now the SMC and Process Scheduler do that
var/global/datum/controller/game_controller/master_controller //Set in world.New()
@@ -10,16 +10,6 @@ var/global/last_tick_duration = 0
var/global/air_processing_killed = 0
var/global/pipe_processing_killed = 0
-/datum/controller
- var/processing = 0
- var/iteration = 0
- var/processing_interval = 0
-
- // Dummy object to let us click it to debug while in the stat panel
- var/obj/effect/statclick/debug/statclick
-
-/datum/controller/proc/recover() // If we are replacing an existing controller (due to a crash) we attempt to preserve as much as we can.
-
/datum/controller/game_controller
var/list/shuttle_list // For debugging and VV
@@ -46,8 +36,6 @@ var/global/pipe_processing_killed = 0
return QDEL_HINT_HARDDEL_NOW
/datum/controller/game_controller/proc/setup()
- world.tick_lag = config.Ticklag
-
preloadTemplates()
if(!config.disable_away_missions)
createRandomZlevel()
@@ -94,26 +82,4 @@ var/global/pipe_processing_killed = 0
count++
log_startup_progress(" Initialized [count] objects in [stop_watch(watch)]s.")
- watch = start_watch()
- count = 0
- log_startup_progress("Initializing atmospherics machinery...")
- for(var/obj/machinery/atmospherics/unary/U in machines)
- if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
- var/obj/machinery/atmospherics/unary/vent_pump/T = U
- T.broadcast_status()
- count++
- else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
- var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
- T.broadcast_status()
- count++
- log_startup_progress(" Initialized [count] atmospherics machines in [stop_watch(watch)]s.")
-
- watch = start_watch()
- count = 0
- log_startup_progress("Initializing pipe networks...")
- for(var/obj/machinery/atmospherics/machine in machines)
- machine.build_network()
- count++
- log_startup_progress(" Initialized [count] pipes in [stop_watch(watch)]s.")
-
log_startup_progress("Finished object initializations in [stop_watch(overwatch)]s.")
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
new file mode 100644
index 00000000000..a78750bf8d0
--- /dev/null
+++ b/code/controllers/subsystem.dm
@@ -0,0 +1,215 @@
+
+/datum/controller/subsystem
+ // Metadata; you should define these.
+ name = "fire coderbus" //name of the subsystem
+ var/init_order = INIT_ORDER_DEFAULT //order of initialization. Higher numbers are initialized first, lower numbers later. Use defines in __DEFINES/subsystems.dm for easy understanding of order.
+ var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
+ var/priority = FIRE_PRIORITY_DEFAULT //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
+
+ var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
+
+ var/initialized = FALSE //set to TRUE after it has been initialized, will obviously never be set if the subsystem doesn't initialize
+
+ //set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
+ // use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list
+ var/can_fire = TRUE
+
+ // Bookkeeping variables; probably shouldn't mess with these.
+ var/last_fire = 0 //last world.time we called fire()
+ var/next_fire = 0 //scheduled world.time for next fire()
+ var/cost = 0 //average time to execute
+ var/tick_usage = 0 //average tick usage
+ var/tick_overrun = 0 //average tick overrun
+ var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
+ var/paused_ticks = 0 //ticks this ss is taking to run right now.
+ var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
+ var/ticks = 1 //how many ticks does this ss take to run on avg.
+ var/times_fired = 0 //number of times we have called fire()
+ var/queued_time = 0 //time we entered the queue, (for timing and priority reasons)
+ var/queued_priority //we keep a running total to make the math easier, if priority changes mid-fire that would break our running total, so we store it here
+ //linked list stuff for the queue
+ var/datum/controller/subsystem/queue_next
+ var/datum/controller/subsystem/queue_prev
+
+ var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
+
+ var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
+
+//Do not override
+///datum/controller/subsystem/New()
+
+// Used to initialize the subsystem BEFORE the map has loaded
+// Called AFTER Recover if that is called
+// Prefer to use Initialize if possible
+/datum/controller/subsystem/proc/PreInit()
+ return
+
+//This is used so the mc knows when the subsystem sleeps. do not override.
+/datum/controller/subsystem/proc/ignite(resumed = 0)
+ set waitfor = 0
+ . = SS_SLEEPING
+ fire(resumed)
+ . = state
+ if(state == SS_SLEEPING)
+ state = SS_IDLE
+ if(state == SS_PAUSING)
+ var/QT = queued_time
+ enqueue()
+ state = SS_PAUSED
+ queued_time = QT
+
+//previously, this would have been named 'process()' but that name is used everywhere for different things!
+//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
+//Sleeping in here prevents future fires until returned.
+/datum/controller/subsystem/proc/fire(resumed = 0)
+ flags |= SS_NO_FIRE
+ throw EXCEPTION("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
+
+/datum/controller/subsystem/Destroy()
+ dequeue()
+ can_fire = 0
+ flags |= SS_NO_FIRE
+ Master.subsystems -= src
+ return ..()
+
+//Queue it to run.
+// (we loop thru a linked list until we get to the end or find the right point)
+// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
+/datum/controller/subsystem/proc/enqueue()
+ var/SS_priority = priority
+ var/SS_flags = flags
+ var/datum/controller/subsystem/queue_node
+ var/queue_node_priority
+ var/queue_node_flags
+
+ for(queue_node = Master.queue_head; queue_node; queue_node = queue_node.queue_next)
+ queue_node_priority = queue_node.queued_priority
+ queue_node_flags = queue_node.flags
+
+ if(queue_node_flags & SS_TICKER)
+ if(!(SS_flags & SS_TICKER))
+ continue
+ if(queue_node_priority < SS_priority)
+ break
+
+ else if(queue_node_flags & SS_BACKGROUND)
+ if(!(SS_flags & SS_BACKGROUND))
+ break
+ if(queue_node_priority < SS_priority)
+ break
+
+ else
+ if(SS_flags & SS_BACKGROUND)
+ continue
+ if(SS_flags & SS_TICKER)
+ break
+ if(queue_node_priority < SS_priority)
+ break
+
+ queued_time = world.time
+ queued_priority = SS_priority
+ state = SS_QUEUED
+ if(SS_flags & SS_BACKGROUND) //update our running total
+ Master.queue_priority_count_bg += SS_priority
+ else
+ Master.queue_priority_count += SS_priority
+
+ queue_next = queue_node
+ if(!queue_node)//we stopped at the end, add to tail
+ queue_prev = Master.queue_tail
+ if(Master.queue_tail)
+ Master.queue_tail.queue_next = src
+ else //empty queue, we also need to set the head
+ Master.queue_head = src
+ Master.queue_tail = src
+
+ else if(queue_node == Master.queue_head)//insert at start of list
+ Master.queue_head.queue_prev = src
+ Master.queue_head = src
+ queue_prev = null
+ else
+ queue_node.queue_prev.queue_next = src
+ queue_prev = queue_node.queue_prev
+ queue_node.queue_prev = src
+
+
+/datum/controller/subsystem/proc/dequeue()
+ if(queue_next)
+ queue_next.queue_prev = queue_prev
+ if(queue_prev)
+ queue_prev.queue_next = queue_next
+ if(src == Master.queue_tail)
+ Master.queue_tail = queue_prev
+ if(src == Master.queue_head)
+ Master.queue_head = queue_next
+ queued_time = 0
+ if(state == SS_QUEUED)
+ state = SS_IDLE
+
+
+/datum/controller/subsystem/proc/pause()
+ . = 1
+ switch(state)
+ if(SS_RUNNING)
+ state = SS_PAUSED
+ if(SS_SLEEPING)
+ state = SS_PAUSING
+
+
+//used to initialize the subsystem AFTER the map has loaded
+/datum/controller/subsystem/Initialize(start_timeofday)
+ initialized = TRUE
+ var/time = (REALTIMEOFDAY - start_timeofday) / 10
+ var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
+ to_chat(world, "[msg]")
+ log_to_dd(msg)
+ return time
+
+//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
+/datum/controller/subsystem/stat_entry(msg)
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(src, "Initializing...")
+
+ if(can_fire && !(SS_NO_FIRE & flags))
+ msg = "[round(cost, 1)]ms|[round(tick_usage, 1)]%([round(tick_overrun, 1)]%)|[round(ticks, 0.1)]\t[msg]"
+ else
+ msg = "OFFLINE\t[msg]"
+
+ var/title = name
+ if(can_fire)
+ title = "\[[state_letter()]][title]"
+
+ stat(title, statclick.update(msg))
+
+/datum/controller/subsystem/proc/state_letter()
+ switch(state)
+ if(SS_RUNNING)
+ . = "R"
+ if(SS_QUEUED)
+ . = "Q"
+ if(SS_PAUSED, SS_PAUSING)
+ . = "P"
+ if(SS_SLEEPING)
+ . = "S"
+ if(SS_IDLE)
+ . = " "
+
+//could be used to postpone a costly subsystem for (default one) var/cycles, cycles
+//for instance, during cpu intensive operations like explosions
+/datum/controller/subsystem/proc/postpone(cycles = 1)
+ if(next_fire - world.time < wait)
+ next_fire += (wait*cycles)
+
+//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
+//should attempt to salvage what it can from the old instance of subsystem
+/datum/controller/subsystem/Recover()
+
+/datum/controller/subsystem/vv_edit_var(var_name, var_value)
+ switch(var_name)
+ if("can_fire")
+ //this is so the subsystem doesn't rapid fire to make up missed ticks causing more lag
+ if(var_value)
+ next_fire = world.time + wait
+ if("queued_priority") //editing this breaks things.
+ return 0
+ . = ..()
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
new file mode 100644
index 00000000000..4434053e4ec
--- /dev/null
+++ b/code/controllers/subsystem/air.dm
@@ -0,0 +1,370 @@
+#define SSAIR_DEFERREDPIPENETS 1
+#define SSAIR_PIPENETS 2
+#define SSAIR_ATMOSMACHINERY 3
+#define SSAIR_ACTIVETURFS 4
+#define SSAIR_EXCITEDGROUPS 5
+#define SSAIR_HIGHPRESSURE 6
+#define SSAIR_HOTSPOTS 7
+#define SSAIR_SUPERCONDUCTIVITY 8
+
+SUBSYSTEM_DEF(air)
+ name = "Atmospherics"
+ init_order = INIT_ORDER_AIR
+ priority = FIRE_PRIORITY_AIR
+ wait = 5
+ flags = SS_BACKGROUND
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/cost_turfs = 0
+ var/cost_groups = 0
+ var/cost_highpressure = 0
+ var/cost_hotspots = 0
+ var/cost_superconductivity = 0
+ var/cost_pipenets = 0
+ var/cost_deferred_pipenets = 0
+ var/cost_atmos_machinery = 0
+
+ var/list/excited_groups = list()
+ var/list/active_turfs = list()
+ var/list/hotspots = list()
+ var/list/networks = list()
+ var/list/atmos_machinery = list()
+ var/list/pipe_init_dirs_cache = list()
+
+
+
+ //Special functions lists
+ var/list/active_super_conductivity = list()
+ var/list/high_pressure_delta = list()
+
+
+ var/list/currentrun = list()
+ var/currentpart = SSAIR_PIPENETS
+
+/datum/controller/subsystem/air/stat_entry(msg)
+ msg += "C:{"
+ msg += "AT:[round(cost_turfs,1)]|"
+ msg += "EG:[round(cost_groups,1)]|"
+ msg += "HP:[round(cost_highpressure,1)]|"
+ msg += "HS:[round(cost_hotspots,1)]|"
+ msg += "SC:[round(cost_superconductivity,1)]|"
+ msg += "PN:[round(cost_pipenets,1)]|"
+ msg += "DPN:[round(cost_deferred_pipenets,1)]|"
+ msg += "AM:[round(cost_atmos_machinery,1)]"
+ msg += "} "
+ msg += "AT:[active_turfs.len]|"
+ msg += "EG:[excited_groups.len]|"
+ msg += "HS:[hotspots.len]|"
+ msg += "PN:[networks.len]|"
+ msg += "HP:[high_pressure_delta.len]|"
+ msg += "AS:[active_super_conductivity.len]|"
+ msg += "AT/MS:[round((cost ? active_turfs.len/cost : 0),0.1)]"
+ ..(msg)
+
+
+/datum/controller/subsystem/air/Initialize(timeofday)
+ setup_overlays() // Assign icons and such for gas-turf-overlays
+ setup_allturfs()
+ setup_atmos_machinery()
+ setup_pipenets()
+ ..()
+
+
+/datum/controller/subsystem/air/fire(resumed = 0)
+ var/timer = TICK_USAGE_REAL
+
+ if(currentpart == SSAIR_DEFERREDPIPENETS || !resumed)
+ process_deferred_pipenets(resumed)
+ cost_deferred_pipenets = MC_AVERAGE(cost_deferred_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_PIPENETS
+
+ if(currentpart == SSAIR_PIPENETS || !resumed)
+ process_pipenets(resumed)
+ cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_ATMOSMACHINERY
+
+ if(currentpart == SSAIR_ATMOSMACHINERY)
+ timer = TICK_USAGE_REAL
+ process_atmos_machinery(resumed)
+ cost_atmos_machinery = MC_AVERAGE(cost_atmos_machinery, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_ACTIVETURFS
+
+ if(currentpart == SSAIR_ACTIVETURFS)
+ timer = TICK_USAGE_REAL
+ process_active_turfs(resumed)
+ cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_EXCITEDGROUPS
+
+ if(currentpart == SSAIR_EXCITEDGROUPS)
+ timer = TICK_USAGE_REAL
+ process_excited_groups(resumed)
+ cost_groups = MC_AVERAGE(cost_groups, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_HIGHPRESSURE
+
+ if(currentpart == SSAIR_HIGHPRESSURE)
+ timer = TICK_USAGE_REAL
+ process_high_pressure_delta(resumed)
+ cost_highpressure = MC_AVERAGE(cost_highpressure, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_HOTSPOTS
+
+ if(currentpart == SSAIR_HOTSPOTS)
+ timer = TICK_USAGE_REAL
+ process_hotspots(resumed)
+ cost_hotspots = MC_AVERAGE(cost_hotspots, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_SUPERCONDUCTIVITY
+
+ if(currentpart == SSAIR_SUPERCONDUCTIVITY)
+ timer = TICK_USAGE_REAL
+ process_super_conductivity(resumed)
+ cost_superconductivity = MC_AVERAGE(cost_superconductivity, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ currentpart = SSAIR_PIPENETS
+
+/datum/controller/subsystem/air/proc/process_deferred_pipenets(resumed = 0)
+ if(!resumed)
+ src.currentrun = deferred_pipenet_rebuilds.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/obj/machinery/atmospherics/A = currentrun[currentrun.len]
+ currentrun.len--
+ if(A)
+ A.build_network()
+ else
+ deferred_pipenet_rebuilds.Remove(A)
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
+ if(!resumed)
+ src.currentrun = networks.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/datum/pipeline/thing = currentrun[currentrun.len]
+ currentrun.len--
+ if(thing)
+ thing.process()
+ else
+ networks.Remove(thing)
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0)
+ var/seconds = wait * 0.1
+ if(!resumed)
+ src.currentrun = atmos_machinery.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/obj/machinery/M = currentrun[currentrun.len]
+ currentrun.len--
+ if(!M || (M.process_atmos(seconds) == PROCESS_KILL))
+ atmos_machinery.Remove(M)
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_super_conductivity(resumed = 0)
+ if(!resumed)
+ src.currentrun = active_super_conductivity.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/turf/simulated/T = currentrun[currentrun.len]
+ currentrun.len--
+ T.super_conduct()
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_hotspots(resumed = 0)
+ if(!resumed)
+ src.currentrun = hotspots.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/obj/effect/hotspot/H = currentrun[currentrun.len]
+ currentrun.len--
+ if(H)
+ H.process()
+ else
+ hotspots -= H
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_high_pressure_delta(resumed = 0)
+ while(high_pressure_delta.len)
+ var/turf/simulated/T = high_pressure_delta[high_pressure_delta.len]
+ high_pressure_delta.len--
+ T.high_pressure_movements()
+ T.pressure_difference = 0
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
+ //cache for sanic speed
+ var/fire_count = times_fired
+ if(!resumed)
+ src.currentrun = active_turfs.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/turf/simulated/T = currentrun[currentrun.len]
+ currentrun.len--
+ if(T)
+ T.process_cell(fire_count)
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_excited_groups(resumed = 0)
+ if(!resumed)
+ src.currentrun = excited_groups.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/datum/excited_group/EG = currentrun[currentrun.len]
+ currentrun.len--
+ EG.breakdown_cooldown++
+ if(EG.breakdown_cooldown == 10)
+ EG.self_breakdown()
+ else if(EG.breakdown_cooldown >= 20)
+ EG.dismantle()
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/remove_from_active(turf/simulated/T)
+ active_turfs -= T
+ active_super_conductivity -= T // bug: if a turf is hit by ex_act 1 while processing, it can end up in super conductivity as /turf/space and cause runtimes
+ if(currentpart == SSAIR_ACTIVETURFS || currentpart == SSAIR_SUPERCONDUCTIVITY)
+ currentrun -= T
+ if(istype(T))
+ T.excited = 0
+ if(T.excited_group)
+ T.excited_group.garbage_collect()
+
+/datum/controller/subsystem/air/proc/add_to_active(turf/simulated/T, blockchanges = 1)
+ if(istype(T) && T.air)
+ T.excited = 1
+ active_turfs |= T
+ if(currentpart == SSAIR_ACTIVETURFS)
+ currentrun |= T
+ if(blockchanges && T.excited_group)
+ T.excited_group.garbage_collect()
+ else
+ for(var/direction in cardinal)
+ if(!(T.atmos_adjacent_turfs & direction))
+ continue
+ var/turf/simulated/S = get_step(T, direction)
+ if(istype(S))
+ add_to_active(S)
+
+/datum/controller/subsystem/air/proc/setup_allturfs(var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)))
+ var/list/active_turfs = src.active_turfs
+
+ for(var/thing in turfs_to_init)
+ var/turf/T = thing
+ active_turfs -= T
+ if(T.blocks_air)
+ continue
+ T.Initialize_Atmos(times_fired)
+ CHECK_TICK
+
+/turf/simulated/proc/resolve_active_graph()
+ . = list()
+ var/datum/excited_group/EG = excited_group
+ if(blocks_air || !air)
+ return
+ if(!EG)
+ EG = new
+ EG.add_turf(src)
+
+ for(var/turf/simulated/ET in atmos_adjacent_turfs)
+ if(ET.blocks_air || !ET.air)
+ continue
+
+ var/ET_EG = ET.excited_group
+ if(ET_EG)
+ if(ET_EG != EG)
+ EG.merge_groups(ET_EG)
+ EG = excited_group //merge_groups() may decide to replace our current EG
+ else
+ EG.add_turf(ET)
+ if(!ET.excited)
+ ET.excited = 1
+ . += ET
+
+/datum/controller/subsystem/air/proc/setup_atmos_machinery()
+ var/watch = start_watch()
+ var/count = 0
+ log_startup_progress("Initializing atmospherics machinery...")
+ for(var/obj/machinery/atmospherics/unary/U in machines)
+ if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
+ var/obj/machinery/atmospherics/unary/vent_pump/T = U
+ T.broadcast_status()
+ count++
+ else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
+ var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
+ T.broadcast_status()
+ count++
+ log_startup_progress(" Initialized [count] atmospherics machines in [stop_watch(watch)]s.")
+
+//this can't be done with setup_atmos_machinery() because
+// all atmos machinery has to initalize before the first
+// pipenet can be built.
+/datum/controller/subsystem/air/proc/setup_pipenets()
+ var/watch = start_watch()
+ var/count = 0
+ log_startup_progress("Initializing pipe networks...")
+ for(var/obj/machinery/atmospherics/machine in machines)
+ machine.build_network()
+ count++
+ log_startup_progress(" Initialized [count] pipes in [stop_watch(watch)]s.")
+
+/datum/controller/subsystem/air/proc/setup_overlays()
+ plmaster = new /obj/effect/overlay()
+ plmaster.icon = 'icons/effects/tile_effects.dmi'
+ plmaster.icon_state = "plasma"
+ plmaster.layer = FLY_LAYER
+ plmaster.mouse_opacity = 0
+
+ slmaster = new /obj/effect/overlay()
+ slmaster.icon = 'icons/effects/tile_effects.dmi'
+ slmaster.icon_state = "sleeping_agent"
+ slmaster.layer = FLY_LAYER
+ slmaster.mouse_opacity = 0
+
+ icemaster = new /obj/effect/overlay()
+ icemaster.icon = 'icons/turf/overlays.dmi'
+ icemaster.icon_state = "snowfloor"
+ icemaster.layer = TURF_LAYER + 0.1
+ icemaster.mouse_opacity = 0
+
+#undef SSAIR_PIPENETS
+#undef SSAIR_ATMOSMACHINERY
+#undef SSAIR_ACTIVETURFS
+#undef SSAIR_EXCITEDGROUPS
+#undef SSAIR_HIGHPRESSURE
+#undef SSAIR_HOTSPOT
+#undef SSAIR_SUPERCONDUCTIVITY
diff --git a/code/controllers/subsystem/fires.dm b/code/controllers/subsystem/fires.dm
new file mode 100644
index 00000000000..50624aa8db0
--- /dev/null
+++ b/code/controllers/subsystem/fires.dm
@@ -0,0 +1,37 @@
+SUBSYSTEM_DEF(fires)
+ name = "Fires"
+ priority = FIRE_PRIOTITY_BURNING
+ flags = SS_NO_INIT|SS_BACKGROUND
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/list/currentrun = list()
+ var/list/processing = list()
+
+/datum/controller/subsystem/fires/stat_entry()
+ ..("P:[processing.len]")
+
+
+/datum/controller/subsystem/fires/fire(resumed = 0)
+ if(!resumed)
+ src.currentrun = processing.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ var/obj/O = currentrun[currentrun.len]
+ currentrun.len--
+ if(!O || qdeleted(O))
+ processing -= O
+ if(MC_TICK_CHECK)
+ return
+ continue
+
+ if(O.burn_state == ON_FIRE)
+ if(O.burn_world_time < world.time)
+ O.burn()
+ else
+ processing -= O
+
+ if(MC_TICK_CHECK)
+ return
diff --git a/code/controllers/subsystem/nanoui.dm b/code/controllers/subsystem/nanoui.dm
new file mode 100644
index 00000000000..c8cbbc62013
--- /dev/null
+++ b/code/controllers/subsystem/nanoui.dm
@@ -0,0 +1,33 @@
+SUBSYSTEM_DEF(nanoui)
+ name = "Nanoui"
+ wait = 9
+ flags = SS_NO_INIT
+ priority = FIRE_PRIORITY_NANOUI
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+
+ var/list/currentrun = list()
+ var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key.
+ var/list/processing_uis = list() // A list of processing UIs, ungrouped.
+
+/datum/controller/subsystem/nanoui/Shutdown()
+ close_all_uis()
+
+/datum/controller/subsystem/nanoui/stat_entry()
+ ..("P:[processing_uis.len]")
+
+/datum/controller/subsystem/nanoui/fire(resumed = 0)
+ if(!resumed)
+ src.currentrun = processing_uis.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ var/datum/nanoui/ui = currentrun[currentrun.len]
+ currentrun.len--
+ if(ui && ui.user && ui.src_object)
+ ui.process()
+ else
+ processing_uis.Remove(ui)
+ if(MC_TICK_CHECK)
+ return
+
diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm
new file mode 100644
index 00000000000..b24a1d45e59
--- /dev/null
+++ b/code/controllers/subsystem/nightshift.dm
@@ -0,0 +1,59 @@
+SUBSYSTEM_DEF(nightshift)
+ name = "Night Shift"
+ init_order = INIT_ORDER_NIGHTSHIFT
+ priority = FIRE_PRIORITY_NIGHTSHIFT
+ wait = 600
+ flags = SS_NO_TICK_CHECK
+
+ var/nightshift_active = FALSE
+ var/nightshift_start_time = 702000 //7:30 PM, station time
+ var/nightshift_end_time = 270000 //7:30 AM, station time
+ var/nightshift_first_check = 30 SECONDS
+
+ var/high_security_mode = FALSE
+
+/datum/controller/subsystem/nightshift/Initialize()
+ if(!config.enable_night_shifts)
+ can_fire = FALSE
+ if(config.randomize_shift_time)
+ GLOB.gametime_offset = rand(0, 23) HOURS
+ return ..()
+
+/datum/controller/subsystem/nightshift/fire(resumed = FALSE)
+ if(world.time - round_start_time < nightshift_first_check)
+ return
+ check_nightshift()
+
+/datum/controller/subsystem/nightshift/proc/announce(message)
+ priority_announcement.Announce(message, new_sound = 'sound/misc/notice2.ogg', new_title = "Automated Lighting System Announcement")
+
+/datum/controller/subsystem/nightshift/proc/check_nightshift()
+ var/emergency = security_level >= SEC_LEVEL_RED
+ var/announcing = TRUE
+ var/time = station_time()
+ var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time)
+ if(high_security_mode != emergency)
+ high_security_mode = emergency
+ if(night_time)
+ announcing = FALSE
+ if(!emergency)
+ announce("Restoring night lighting configuration to normal operation.")
+ else
+ announce("Disabling night lighting: Station is in a state of emergency.")
+ if(emergency)
+ night_time = FALSE
+ if(nightshift_active != night_time)
+ update_nightshift(night_time, announcing)
+
+/datum/controller/subsystem/nightshift/proc/update_nightshift(active, announce = TRUE)
+ nightshift_active = active
+ if(announce)
+ if(active)
+ announce("Good evening, crew. To reduce power consumption and stimulate the circadian rhythms of some species, all of the lights aboard the station have been dimmed for the night.")
+ else
+ announce("Good morning, crew. As it is now day time, all of the lights aboard the station have been restored to their former brightness.")
+ for(var/A in apcs)
+ var/obj/machinery/power/apc/APC = A
+ if(is_station_level(APC.z))
+ APC.set_nightshift(active)
+ CHECK_TICK
diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm
new file mode 100644
index 00000000000..fcc62a2fa50
--- /dev/null
+++ b/code/controllers/subsystem/spacedrift.dm
@@ -0,0 +1,59 @@
+SUBSYSTEM_DEF(spacedrift)
+ name = "Space Drift"
+ priority = FIRE_PRIORITY_SPACEDRIFT
+ wait = 5
+ flags = SS_NO_INIT|SS_KEEP_TIMING
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/list/currentrun = list()
+ var/list/processing = list()
+
+/datum/controller/subsystem/spacedrift/stat_entry()
+ ..("P:[processing.len]")
+
+
+/datum/controller/subsystem/spacedrift/fire(resumed = 0)
+ if(!resumed)
+ src.currentrun = processing.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ var/atom/movable/AM = currentrun[currentrun.len]
+ currentrun.len--
+ if(!AM)
+ processing -= AM
+ if (MC_TICK_CHECK)
+ return
+ continue
+
+ if(AM.inertia_next_move > world.time)
+ if (MC_TICK_CHECK)
+ return
+ continue
+
+ if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0))
+ AM.inertia_dir = 0
+
+ if(!AM.inertia_dir)
+ AM.inertia_last_loc = null
+ processing -= AM
+ if (MC_TICK_CHECK)
+ return
+ continue
+
+ var/old_dir = AM.dir
+ var/old_loc = AM.loc
+ AM.inertia_moving = TRUE
+ step(AM, AM.inertia_dir)
+ AM.inertia_moving = FALSE
+ AM.inertia_next_move = world.time + AM.inertia_move_delay
+ if(AM.loc == old_loc)
+ AM.inertia_dir = 0
+
+ AM.setDir(old_dir)
+ AM.inertia_last_loc = AM.loc
+ if(MC_TICK_CHECK)
+ return
+
diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm
new file mode 100644
index 00000000000..8e5e47e7abf
--- /dev/null
+++ b/code/controllers/subsystem/sun.dm
@@ -0,0 +1,42 @@
+SUBSYSTEM_DEF(sun)
+ name = "Sun"
+ wait = 600
+ flags = SS_NO_TICK_CHECK|SS_NO_INIT
+ var/angle
+ var/dx
+ var/dy
+ var/rate
+ var/list/solars = list()
+
+/datum/controller/subsystem/sun/PreInit()
+ angle = rand (0,360) // the station position to the sun is randomised at round start
+ rate = rand(50,200)/100 // 50% - 200% of standard rotation
+ if(prob(50)) // same chance to rotate clockwise than counter-clockwise
+ rate = -rate
+
+/datum/controller/subsystem/sun/stat_entry(msg)
+ ..("P:[solars.len]")
+
+/datum/controller/subsystem/sun/fire()
+ angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
+
+ // now calculate and cache the (dx,dy) increments for line drawing
+ var/s = sin(angle)
+ var/c = cos(angle)
+
+ // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)"
+ // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions
+
+ if(abs(s) < abs(c))
+ dx = s / abs(c)
+ dy = c / abs(c)
+ else
+ dx = s / abs(s)
+ dy = c / abs(s)
+
+ //now tell the solar control computers to update their status and linked devices
+ for(var/obj/machinery/power/solar_control/SC in solars)
+ if(!SC.powernet)
+ solars.Remove(SC)
+ continue
+ SC.update()
\ No newline at end of file
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
new file mode 100644
index 00000000000..b74e3a1a6c9
--- /dev/null
+++ b/code/controllers/subsystem/throwing.dm
@@ -0,0 +1,148 @@
+#define MAX_THROWING_DIST 512 // 2 z-levels on default width
+#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
+
+SUBSYSTEM_DEF(throwing)
+ name = "Throwing"
+ priority = FIRE_PRIORITY_THROWING
+ wait = 1
+ flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/list/currentrun
+ var/list/processing = list()
+
+/datum/controller/subsystem/throwing/stat_entry()
+ ..("P:[processing.len]")
+
+/datum/controller/subsystem/throwing/fire(resumed = 0)
+ if(!resumed)
+ src.currentrun = processing.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(length(currentrun))
+ var/atom/movable/AM = currentrun[currentrun.len]
+ var/datum/thrownthing/TT = currentrun[AM]
+ currentrun.len--
+ if(!AM || !TT)
+ processing -= AM
+ if(MC_TICK_CHECK)
+ return
+ continue
+
+ TT.tick()
+
+ if(MC_TICK_CHECK)
+ return
+
+ currentrun = null
+
+/datum/thrownthing
+ var/atom/movable/thrownthing
+ var/atom/target
+ var/turf/target_turf
+ var/init_dir
+ var/maxrange
+ var/speed
+ var/mob/thrower
+ var/diagonals_first
+ var/dist_travelled = 0
+ var/start_time
+ var/dist_x
+ var/dist_y
+ var/dx
+ var/dy
+ var/pure_diagonal
+ var/diagonal_error
+ var/datum/callback/callback
+ var/paused = FALSE
+ var/delayed_time = 0
+ var/last_move = 0
+
+/datum/thrownthing/proc/tick()
+ var/atom/movable/AM = thrownthing
+ if(!isturf(AM.loc) || !AM.throwing)
+ finalize()
+ return
+
+ if(paused)
+ delayed_time += world.time - last_move
+ return
+
+ if(dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
+ finalize()
+ return
+
+ var/atom/step
+
+ last_move = world.time
+
+ //calculate how many tiles to move, making up for any missed ticks.
+ var/tilestomove = CEILING(min(((((world.time + world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed * MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1)
+ while(tilestomove-- > 0)
+ if((dist_travelled >= maxrange || AM.loc == target_turf) && has_gravity(AM, AM.loc))
+ finalize()
+ return
+
+ if(dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
+ step = get_step(AM, get_dir(AM, target_turf))
+ else
+ step = get_step(AM, init_dir)
+
+ if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
+ if (diagonal_error >= 0 && max(dist_x, dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
+ step = get_step(AM, dx)
+ diagonal_error += (diagonal_error < 0) ? dist_x / 2 : -dist_y
+
+ if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
+ finalize()
+ return
+
+ AM.Move(step, get_dir(AM, step))
+
+ if(!AM.throwing) // we hit something during our move
+ finalize(hit = TRUE)
+ return
+
+ dist_travelled++
+
+ if(dist_travelled > MAX_THROWING_DIST)
+ finalize()
+ return
+
+/datum/thrownthing/proc/finalize(hit = FALSE, target = null)
+ set waitfor = 0
+ SSthrowing.processing -= thrownthing
+ //done throwing, either because it hit something or it finished moving
+ thrownthing.throwing = null
+ if(!hit)
+ for(var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on.
+ var/atom/A = thing
+ if(A == target)
+ hit = 1
+ thrownthing.throw_impact(A, src)
+ break
+ if(!hit)
+ thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
+ thrownthing.newtonian_move(init_dir)
+ else
+ thrownthing.newtonian_move(init_dir)
+
+ if(target)
+ thrownthing.throw_impact(target, src)
+
+ if(callback)
+ callback.Invoke()
+
+/datum/thrownthing/proc/hit_atom(atom/A)
+ finalize(hit = TRUE, target = A)
+
+/datum/thrownthing/proc/hitcheck()
+ for(var/thing in get_turf(thrownthing))
+ var/atom/movable/AM = thing
+ if(AM == thrownthing)
+ continue
+ if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
+ finalize(hit = TRUE, target = AM)
+ return TRUE
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index 1cc9787420d..126defb41c2 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -17,7 +17,10 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
return
-/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote","Diseases","Fires","Mob","NPC AI","Shuttle","Timer","Weather","Space","Mob Hunt Server"))
+/client/proc/debug_controller(controller in list("Master",
+ "failsafe","Scheduler","StonedMaster","Ticker","Air","Jobs","Sun","Radio","Configuration","pAI",
+ "Cameras","Garbage", "Transfer Controller","Event","Alarm","Nano","Vote","Fires",
+ "Mob","NPC AI","Shuttle","Timer","Weather","Space","Mob Hunt Server"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -28,19 +31,25 @@
debug_variables(master_controller)
feedback_add_details("admin_verb","DMC")
if("failsafe")
- debug_variables(failsafe)
+ debug_variables(Failsafe)
feedback_add_details("admin_verb", "dfailsafe")
+ if("Scheduler")
+ debug_variables(processScheduler)
+ feedback_add_details("admin_verb","DprocessScheduler")
+ if("StonedMaster")
+ debug_variables(Master)
+ feedback_add_details("admin_verb","Dsmc")
if("Ticker")
debug_variables(ticker)
feedback_add_details("admin_verb","DTicker")
if("Air")
- debug_variables(air_master)
+ debug_variables(SSair)
feedback_add_details("admin_verb","DAir")
if("Jobs")
debug_variables(job_master)
feedback_add_details("admin_verb","DJobs")
if("Sun")
- debug_variables(sun)
+ debug_variables(SSsun)
feedback_add_details("admin_verb","DSun")
if("Radio")
debug_variables(radio_controller)
@@ -63,17 +72,14 @@
if("Garbage")
debug_variables(garbageCollector)
feedback_add_details("admin_verb","DGarbage")
- if("Scheduler")
- debug_variables(processScheduler)
- feedback_add_details("admin_verb","DprocessScheduler")
if("Nano")
- debug_variables(nanomanager)
+ debug_variables(SSnanoui)
feedback_add_details("admin_verb","DNano")
if("Vote")
debug_variables(vote)
feedback_add_details("admin_verb","DVote")
if("Fires")
- debug_variables(fire_master)
+ debug_variables(SSfires)
feedback_add_details("admin_verb","DFires")
if("Mob")
debug_variables(mob_master)
diff --git a/code/datums/action.dm b/code/datums/action.dm
index e12bd371a6d..4ee33474049 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -101,7 +101,7 @@
//Presets for item actions
/datum/action/item_action
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
-
+ var/use_itemicon = TRUE
/datum/action/item_action/New(Target)
..()
var/obj/item/I = target
@@ -121,17 +121,19 @@
return 1
/datum/action/item_action/ApplyIcon(obj/screen/movable/action_button/current_button)
- current_button.overlays.Cut()
- if(target)
- var/obj/item/I = target
- var/old_layer = I.layer
- var/old_plane = I.plane
- I.layer = 21
- I.plane = HUD_PLANE
- current_button.overlays += I
- I.layer = old_layer
- I.plane = old_plane
-
+ if(use_itemicon)
+ current_button.overlays.Cut()
+ if(target)
+ var/obj/item/I = target
+ var/old_layer = I.layer
+ var/old_plane = I.plane
+ I.layer = 21
+ I.plane = HUD_PLANE
+ current_button.overlays += I
+ I.layer = old_layer
+ I.plane = old_plane
+ else
+ ..()
/datum/action/item_action/toggle_light
name = "Toggle Light"
diff --git a/code/datums/diseases/kingstons.dm b/code/datums/diseases/kingstons.dm
new file mode 100644
index 00000000000..abad7dcf720
--- /dev/null
+++ b/code/datums/diseases/kingstons.dm
@@ -0,0 +1,95 @@
+/datum/disease/kingstons
+ name = "Kingstons Syndrome"
+ max_stages = 4
+ spread_text = "Airborne"
+ cure_text = "Milk"
+ cures = list("milk")
+ cure_chance = 50
+ agent = "Nya Virus"
+ viable_mobtypes = list(/mob/living/carbon/human)
+ permeability_mod = 0.75
+ desc = "If left untreated the subject will turn into a feline. In felines it has... OTHER... effects."
+ severity = DANGEROUS
+
+/datum/disease/kingstons/stage_act()
+ ..()
+ switch(stage)
+ if(1)
+ if(prob(10))
+ if(affected_mob.get_species() == "Tajaran")
+ to_chat(affected_mob, "You feel good.")
+ else
+ to_chat(affected_mob, "You feel like playing with string.")
+ if(2)
+ if(prob(10))
+ if(affected_mob.get_species() == "Tajaran")
+ to_chat(affected_mob, "Something in your throat itches.")
+ else
+ to_chat(affected_mob, "You NEED to find a mouse.")
+ if(3)
+ if(prob(10))
+ if(affected_mob.get_species() == "Tajaran")
+ to_chat(affected_mob, "You feel something in your throat!")
+ affected_mob.emote("cough")
+ else
+ affected_mob.say(pick(list("Mew", "Meow!", "Nya!~")))
+ if(4)
+ if(prob(5))
+ if(affected_mob.get_species() == "Tajaran")
+ affected_mob.visible_message("[affected_mob] coughs up a hairball!", \
+ "You cough up a hairball!")
+ affected_mob.Stun(5)
+ else
+ affected_mob.visible_message("[affected_mob]'s form contorts into something more feline!", \
+ "YOU TURN INTO A TAJARAN!")
+ var/mob/living/carbon/human/catface = affected_mob
+ catface.set_species("Tajaran")
+
+
+/datum/disease/kingstons/advanced
+ name = "Advanced Kingstons Syndrome"
+ spread_text = "Airborne"
+ cure_text = "Plasma"
+ cures = list("plasma")
+ cure_chance = 50
+ agent = "AMB45DR Bacteria"
+ viable_mobtypes = list(/mob/living/carbon/human)
+ permeability_mod = 0.75
+ desc = "If left untreated the subject will mutate to a different species."
+ severity = BIOHAZARD
+ var/list/virspecies = list("Human", "Tajaran", "Unathi", "Skrell", "Vulpkanin")//no karma races sorrys.
+ var/list/virsuffix = list("pox", "rot", "flu", "cough", "-gitis", "cold", "rash", "itch", "decay")
+ var/chosentype
+ var/chosensuff
+
+/datum/disease/kingstons/advanced/New()
+ chosentype = pick(virspecies)
+ chosensuff = pick(virsuffix)
+
+ name = "[chosentype] [chosensuff]"
+
+/datum/disease/kingstons/advanced/stage_act()
+ ..()
+ switch(stage)
+ if(1)
+ if(prob(10))
+ to_chat(affected_mob, "You feel awkward.")
+ if(2)
+ if(prob(10))
+ to_chat(affected_mob, "You itch.")
+ if(3)
+ if(prob(10))
+ to_chat(affected_mob, "Your skin starts to flake!")
+
+ if(4)
+ if(prob(5))
+ if(affected_mob.get_species() != chosentype)
+ affected_mob.visible_message("[affected_mob]'s skin splits and form contorts!", \
+ "Your body mutates into a [chosentype]!")
+ var/mob/living/carbon/human/twisted = affected_mob
+ twisted.set_species(chosentype)
+ else
+ affected_mob.visible_message("[affected_mob] scratches at thier skin!", \
+ "You scratch your skin to try not to itch!")
+ affected_mob.adjustBruteLoss(-5)
+ affected_mob.adjustStaminaLoss(5)
\ No newline at end of file
diff --git a/code/datums/diseases/lycancoughy.dm b/code/datums/diseases/lycancoughy.dm
new file mode 100644
index 00000000000..2c5a8752cbb
--- /dev/null
+++ b/code/datums/diseases/lycancoughy.dm
@@ -0,0 +1,45 @@
+/datum/disease/lycan
+ name = "Lycancoughy"
+ form = "Infection"
+ max_stages = 4
+ spread_text = "On contact"
+ spread_flags = CONTACT_GENERAL
+ cure_text = "Ethanol"
+ cures = list("ethanol")
+ agent = "Excess Snuggles"
+ viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey)
+ desc = "If left untreated subject will regurgitate... puppies."
+ severity = MEDIUM
+
+/datum/disease/lycan/stage_act()
+ ..()
+ switch(stage)
+ if(2) //also changes say, see say.dm
+ if(prob(5))
+ to_chat(affected_mob, "You itch.")
+ affected_mob.emote("cough")
+ if(3)
+ if(prob(10))
+ to_chat(affected_mob, "You hear faint barking.")
+ if(prob(5))
+ to_chat(affected_mob, "You crave meat.")
+ affected_mob.emote("cough")
+ if(prob(2))
+ to_chat(affected_mob, "Your stomach growls!")
+ if(4)
+ if(prob(10))
+ to_chat(affected_mob, "Your stomach barks?!")
+ if(prob(5))
+ affected_mob.visible_message("[affected_mob] howls!", \
+ "You howl!")
+ affected_mob.AdjustConfused(rand(6, 8))
+ if(prob(3))
+ var/list/puppytype = list(/mob/living/simple_animal/pet/corgi/puppy, /mob/living/simple_animal/pet/pug, /mob/living/simple_animal/pet/fox)
+ var/mob/living/puppypicked = pick(puppytype)
+ affected_mob.visible_message("[affected_mob] coughs up [initial(puppypicked.name)]!", \
+ "You cough up [initial(puppypicked.name)]?!")
+ affected_mob.emote("cough")
+ affected_mob.adjustBruteLoss(5)
+ new puppypicked(affected_mob.loc)
+ new puppypicked(affected_mob.loc)
+ return
diff --git a/code/datums/diseases/pierrot_throat.dm b/code/datums/diseases/pierrot_throat.dm
index 2bf0ca308a8..f2f9e1371b1 100644
--- a/code/datums/diseases/pierrot_throat.dm
+++ b/code/datums/diseases/pierrot_throat.dm
@@ -26,3 +26,38 @@
if(4)
if(prob(5))
affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) )
+
+
+/datum/disease/pierrot_throat/advanced
+ name = "Advanced Pierrot's Throat"
+ spread_text = "Airborne"
+ cure_text = "Banana products, especially banana bread."
+ cures = list("banana")
+ cure_chance = 75
+ agent = "H0NI<42.B4n4 Virus"
+ viable_mobtypes = list(/mob/living/carbon/human)
+ permeability_mod = 0.75
+ desc = "If left untreated the subject will probably drive others to insanity and go insane themselves."
+ severity = DANGEROUS
+
+/datum/disease/pierrot_throat/advanced/stage_act()
+ ..()
+ switch(stage)
+ if(1)
+ if(prob(10))
+ to_chat(affected_mob, "You feel very silly.")
+ if(prob(5))
+ to_chat(affected_mob, "You feel like making a joke.")
+ if(2)
+ if(prob(10))
+ to_chat(affected_mob, "You don't just start seeing rainbows... YOU ARE RAINBOWS!")
+ if(3)
+ if(prob(10))
+ to_chat(affected_mob, "Your thoughts are interrupted by a loud HONK!")
+ affected_mob << 'sound/items/AirHorn.ogg'
+ if(4)
+ if(prob(5))
+ affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) )
+
+ if(!istype(affected_mob.wear_mask, /obj/item/clothing/mask/gas/virusclown_hat))
+ affected_mob.equip_to_slot(new /obj/item/clothing/mask/gas/virusclown_hat(src), slot_wear_mask)
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 3e1c3207a87..76a13e22e26 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -89,7 +89,7 @@
current.mind = null
leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it
- nanomanager.user_transferred(current, new_character)
+ SSnanoui.user_transferred(current, new_character)
if(new_character.mind) //remove any mind currently in our new body's mind variable
new_character.mind.current = null
diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm
index 4195052fdda..2a8408dfe4a 100644
--- a/code/datums/spells/mind_transfer.dm
+++ b/code/datums/spells/mind_transfer.dm
@@ -20,15 +20,8 @@ Make sure spells that are removed from spell_list are actually removed and delet
Also, you never added distance checking after target is selected. I've went ahead and did that.
*/
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride)
- if(!targets.len)
- to_chat(user, "No mind found.")
- return
- if(targets.len > 1)
- to_chat(user, "Too many minds! You're not a hive damnit!")//Whaa...aat?
- return
-
- var/mob/living/target = targets[1]
+ var/mob/living/target = targets[range]
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
to_chat(user, "They are too far away!")
@@ -50,6 +43,10 @@ Also, you never added distance checking after target is selected. I've went ahea
to_chat(user, "Their mind is resisting your spell.")
return
+ if(istype(target, /mob/living/silicon))
+ to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.")
+ return
+
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
var/mob/caster = user//The wizard/whomever doing the body transferring.
diff --git a/code/datums/statclick.dm b/code/datums/statclick.dm
index e10a63b8356..c7273e4744b 100644
--- a/code/datums/statclick.dm
+++ b/code/datums/statclick.dm
@@ -5,8 +5,8 @@
var/target
/obj/effect/statclick/New(ntarget, text)
- name = text
target = ntarget
+ name = text
/obj/effect/statclick/proc/update(text)
name = text
@@ -15,25 +15,22 @@
/obj/effect/statclick/debug
var/class
-/obj/effect/statclick/debug/New(ntarget)
- name = "Initializing..."
- target = ntarget
- if(istype(target, /datum/controller/process))
- class = "process"
- else if(istype(target, /datum/controller/processScheduler))
- class = "scheduler"
- else if(istype(target, /datum/controller))
- class = "controller"
- else if(istype(target, /datum))
- class = "datum"
- else
- class = "unknown"
-
-// This bit is called when clicked in the stat panel
/obj/effect/statclick/debug/Click()
- if(!is_admin(usr))
+ if(!is_admin(usr) || !target)
return
+ if(!class)
+ if(istype(target, /datum/controller/process))
+ class = "process"
+ else if(istype(target, /datum/controller/processScheduler))
+ class = "scheduler"
+ if(istype(target, /datum/controller/subsystem))
+ class = "subsystem"
+ else if(istype(target, /datum/controller))
+ class = "controller"
+ else if(istype(target, /datum))
+ class = "datum"
+ else
+ class = "unknown"
usr.client.debug_variables(target)
-
- message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
+ message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
\ No newline at end of file
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 15d78f37276..1feea397ac0 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -388,6 +388,15 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
containertype = /obj/structure/closet/crate/secure/plasma
containername = "energy gun crate"
+/datum/supply_packs/security/armory/epistol // costs 3/5ths of the normal e-guns for 3/4ths the total ammo, making it cheaper to arm more people, but less convient for any one person
+ name = "Energy Pistol Crate"
+ contains = list(/obj/item/weapon/gun/energy/gun/mini,
+ /obj/item/weapon/gun/energy/gun/mini,
+ /obj/item/weapon/gun/energy/gun/mini)
+ cost = 15
+ 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,
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index e4f2f3b5d1b..0d4a871fc06 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -963,6 +963,13 @@ var/list/uplink_items = list()
item = /obj/item/weapon/storage/backpack/duffel/syndie/surgery
cost = 4
+/datum/uplink_item/device_tools/bonerepair
+ name = "Prototype Bone Repair Kit"
+ desc = "Stolen prototype bone repair nanites. Contains one nanocalcium autoinjector and guide."
+ reference = "NCAI"
+ item = /obj/item/weapon/storage/box/syndie_kit/bonerepair
+ cost = 6
+
/datum/uplink_item/device_tools/military_belt
name = "Military Belt"
desc = "A robust seven-slot red belt made for carrying a broad variety of weapons, ammunition and explosives"
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index ece19c01e88..cef139cc476 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -164,9 +164,10 @@ var/const/AIRLOCK_WIRE_LIGHT = 512
//one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not),
//raises them if they are down (only if power's on)
if(!A.locked)
- A.lock()
- else
- A.unlock()
+ if(A.lock())
+ A.audible_message("You hear a click from the bottom of the door.", null, 1)
+ else if(A.unlock())
+ A.audible_message("You hear a click from the bottom of the door.", null, 1)
if(AIRLOCK_WIRE_BACKUP_POWER1)
//two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter).
diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm
index 644a472acf3..a901695c880 100644
--- a/code/datums/wires/autolathe.dm
+++ b/code/datums/wires/autolathe.dm
@@ -10,10 +10,10 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4
switch(index)
if(AUTOLATHE_HACK_WIRE)
return "Hack"
-
+
if(AUTOLATHE_SHOCK_WIRE)
return "Shock"
-
+
if(AUTOLATHE_DISABLE_WIRE)
return "Disable"
@@ -69,6 +69,6 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4
updateUIs()
/datum/wires/autolathe/proc/updateUIs()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(holder)
- nanomanager.update_uis(holder)
\ No newline at end of file
+ SSnanoui.update_uis(holder)
\ No newline at end of file
diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm
index 2034c59576b..5b704a4526b 100644
--- a/code/datums/wires/nuclearbomb.dm
+++ b/code/datums/wires/nuclearbomb.dm
@@ -11,12 +11,12 @@ var/const/NUCLEARBOMB_WIRE_SAFETY = 4
switch(index)
if(NUCLEARBOMB_WIRE_LIGHT)
return "Bomb Light"
-
+
if(NUCLEARBOMB_WIRE_TIMING)
return "Bomb Timing"
-
+
if(NUCLEARBOMB_WIRE_SAFETY)
- return "Bomb Safety"
+ return "Bomb Safety"
/datum/wires/nuclearbomb/CanUse(mob/living/L)
var/obj/machinery/nuclearbomb/N = holder
@@ -77,6 +77,6 @@ var/const/NUCLEARBOMB_WIRE_SAFETY = 4
N.lighthack = !N.lighthack
/datum/wires/nuclearbomb/proc/updateUIs()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(holder)
- nanomanager.update_uis(holder)
\ No newline at end of file
+ SSnanoui.update_uis(holder)
\ No newline at end of file
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index b192045d29d..2b86f1247f6 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -76,7 +76,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown",
ui_interact(user)
/datum/wires/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "wires.tmpl", holder.name, window_x, window_y)
ui.open()
@@ -174,7 +174,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown",
else
to_chat(L, "You need a remote signaller!")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
//
@@ -184,12 +184,12 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown",
// Called when wires cut/mended.
/datum/wires/proc/UpdateCut(index, mended)
if(holder)
- nanomanager.update_uis(holder)
+ SSnanoui.update_uis(holder)
// Called when wire pulsed. Add code here.
/datum/wires/proc/UpdatePulsed(index)
if(holder)
- nanomanager.update_uis(holder)
+ SSnanoui.update_uis(holder)
/datum/wires/proc/CanUse(mob/L)
return 1
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 723d26a451a..83880039a73 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -53,15 +53,15 @@
return cameras
-/area/proc/atmosalert(danger_level, var/alarm_source)
+/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE)
if(danger_level == ATMOS_ALARM_NONE)
atmosphere_alarm.clearAlarm(src, alarm_source)
else
atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level)
- //Check all the alarms before lowering atmosalm. Raising is perfectly fine.
+ //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care.
for(var/obj/machinery/alarm/AA in src)
- if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level)
+ if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force)
danger_level = max(danger_level, AA.danger_level)
if(danger_level != atmosalm)
@@ -87,7 +87,7 @@
if(!D.welded)
D.activate_alarm()
if(D.operating)
- D.nextstate = CLOSED
+ D.nextstate = FD_CLOSED
else if(!D.density)
spawn(0)
D.close()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index bf02fa02ed8..33265ff8a49 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -153,9 +153,8 @@
/atom/proc/emp_act(var/severity)
return
-/atom/proc/bullet_act(var/obj/item/projectile/Proj, def_zone)
- Proj.on_hit(src, 0, def_zone)
- return 0
+/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
+ . = P.on_hit(src, 0, def_zone)
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 0e982a27288..6ca5047ac6c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -217,14 +217,15 @@
return 1
inertia_last_loc = loc
- drift_master.processing_list[src] = src
+ SSspacedrift.processing[src] = src
return 1
//called when src is thrown into hit_atom
/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum)
set waitfor = 0
- return hit_atom.hitby(src)
+ if(exists(hit_atom))
+ return hit_atom.hitby(src)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked)
if(!anchored && hitpush)
@@ -301,7 +302,7 @@
if(spin && !no_spin && !no_spin_thrown)
SpinAnimation(5, 1)
- throw_master.processing_list[src] = TT
+ SSthrowing.processing[src] = TT
TT.tick()
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 43054c24923..391b8fa8ee8 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -371,7 +371,7 @@
I.forceMove(src)
src.disk = I
to_chat(user, "You insert [I].")
- nanomanager.update_uis(src) // update all UIs attached to src()
+ SSnanoui.update_uis(src) // update all UIs attached to src()
return
else
..()
@@ -469,7 +469,7 @@
return
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
@@ -580,7 +580,7 @@
irradiating = src.radiation_duration
var/lock_state = src.connected.locked
src.connected.locked = 1//lock it
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
@@ -685,7 +685,7 @@
irradiating = src.radiation_duration
var/lock_state = src.connected.locked
src.connected.locked = 1//lock it
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
@@ -753,7 +753,7 @@
irradiating = src.radiation_duration
var/lock_state = src.connected.locked
src.connected.locked = 1 //lock it
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10*src.radiation_duration) // sleep for radiation_duration seconds
@@ -888,7 +888,7 @@
irradiating = 2
var/lock_state = src.connected.locked
src.connected.locked = 1//lock it
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
sleep(2 SECONDS)
diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm
index aaea991d5cf..d448419b72d 100644
--- a/code/game/dna/genes/disabilities.dm
+++ b/code/game/dna/genes/disabilities.dm
@@ -128,10 +128,12 @@
/datum/dna/gene/disability/colourblindness/activate(var/mob/M, var/connected, var/flags)
..()
M.update_client_colour() //Handle the activation of the colourblindness on the mob.
+ M.update_icons() //Apply eyeshine as needed.
/datum/dna/gene/disability/colourblindness/deactivate(var/mob/M, var/connected, var/flags)
..()
M.update_client_colour() //Handle the deactivation of the colourblindness on the mob.
+ M.update_icons() //Remove eyeshine as needed.
/datum/dna/gene/disability/deaf
name="Deafness"
diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm
index 656be0d4cb9..e9d69c2b904 100644
--- a/code/game/dna/genes/powers.dm
+++ b/code/game/dna/genes/powers.dm
@@ -164,10 +164,12 @@
/datum/dna/gene/basic/xray/activate(mob/living/M, connected, flags)
..()
M.update_sight()
+ M.update_icons() //Apply eyeshine as needed.
/datum/dna/gene/basic/xray/deactivate(mob/living/M, connected, flags)
..()
M.update_sight()
+ M.update_icons() //Remove eyeshine as needed.
/datum/dna/gene/basic/tk
name="Telekenesis"
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index c9e15d20f38..c6dc0833afb 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -34,6 +34,8 @@
maxHealth = 40
melee_damage_lower = 2
melee_damage_upper = 4
+ obj_damage = 20
+ environment_smash = ENVIRONMENT_SMASH_STRUCTURES
attacktext = "hits"
attack_sound = 'sound/weapons/genhit1.ogg'
speak_emote = list("pulses")
@@ -158,6 +160,7 @@
maxHealth = 240
melee_damage_lower = 20
melee_damage_upper = 20
+ obj_damage = 60
attacktext = "hits"
attack_sound = 'sound/effects/blobattack.ogg'
speak_emote = list("gurgles")
@@ -165,7 +168,7 @@
maxbodytemp = 360
force_threshold = 10
mob_size = MOB_SIZE_LARGE
- environment_smash = 3
+ environment_smash = ENVIRONMENT_SMASH_RWALLS
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
pressure_resistance = 100 //100 kPa difference required to push
throw_pressure_limit = 120 //120 kPa difference required to throw
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 4ef8fe93626..00592b7812d 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -121,10 +121,10 @@
armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 30, bio = 30, rad = 30)
/obj/item/clothing/suit/space/cult
- name = "cult armour"
+ name = "cult armor"
icon_state = "cult_armour"
item_state = "cult_armour"
- desc = "A bulky suit of armour, bristling with spikes. It looks space proof."
+ desc = "A bulky suit of armor, bristling with spikes. It looks space proof."
w_class = WEIGHT_CLASS_NORMAL
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank)
slowdown = 1
@@ -343,7 +343,6 @@
else
to_chat(C, "The veil cannot be torn here!")
-
/obj/item/clothing/suit/space/eva/plasmaman/cultist
name = "plasmaman cultist armor"
icon_state = "plasmaman_cult"
@@ -358,3 +357,42 @@
base_state = "plasmamanCult_helmet"
desc = "A helmet designed by cultists. It glows menacingly with unearthly flames."
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
+
+/obj/item/weapon/melee/cultblade/ghost
+ name = "eldritch sword"
+ force = 15
+ flags = NODROP
+
+/obj/item/weapon/melee/cultblade/ghost/dropped(mob/living/carbon/human/user)
+ ..()
+ qdel(src)
+
+/obj/item/clothing/head/culthood/alt/ghost
+ flags = NODROP
+
+/obj/item/clothing/head/culthood/alt/ghost/dropped(mob/living/carbon/human/user)
+ ..()
+ qdel(src)
+
+/obj/item/clothing/suit/cultrobes/alt/ghost
+ flags = NODROP
+
+/obj/item/clothing/suit/cultrobes/alt/ghost/dropped(mob/living/carbon/human/user)
+ ..()
+ qdel(src)
+
+/obj/item/clothing/shoes/cult/ghost
+ flags = NODROP
+
+/obj/item/clothing/shoes/cult/ghost/dropped(mob/living/carbon/human/user)
+ ..()
+ qdel(src)
+
+/datum/outfit/ghost_cultist
+ name = "Cultist Ghost"
+
+ uniform = /obj/item/clothing/under/color/black
+ suit = /obj/item/clothing/suit/cultrobes/alt/ghost
+ shoes = /obj/item/clothing/shoes/cult/ghost
+ head = /obj/item/clothing/head/culthood/alt/ghost
+ r_hand = /obj/item/weapon/melee/cultblade/ghost
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm
index 69b6de0c73e..d489387ad76 100644
--- a/code/game/gamemodes/cult/cult_objectives.dm
+++ b/code/game/gamemodes/cult/cult_objectives.dm
@@ -21,10 +21,10 @@
if("convert")
explanation = "We must increase our influence before we can summon [ticker.mode.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions."
if("bloodspill")
- spilltarget = 70 + rand(0,player_list.len * 3)
+ spilltarget = 100 + rand(0,player_list.len * 3)
explanation = "We must prepare this place for [ticker.mode.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles."
if("sacrifice")
- explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for his blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
+ explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for their blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
@@ -47,7 +47,7 @@
message_admins("Picking a new Cult objective.")
var/new_objective = "eldergod"
//the idea here is that if the cult performs well, the should get more objectives before they can summon Nar-Sie.
- if(cult.len >= 4)//if there are less than 4 remaining cultists, they get a free pass to the summon objective.
+ if(cult.len >= 4) //if there are less than 4 remaining cultists, they get a free pass to the summon objective.
if(current_objective <= prenarsie_objectives)
var/list/unconvertables = get_unconvertables()
if(unconvertables.len <= (cult.len * 2))//if cultists are getting radically outnumbered, they get a free pass to the summon objective.
@@ -81,12 +81,13 @@
spilltarget = 100 + rand(0,player_list.len * 3)
explanation = "We must prepare this place for [ticker.mode.cultdat.entity_title1]'s coming. Spread blood and gibs over [spilltarget] of the Station's floor tiles."
if("sacrifice")
- explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for his blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
+ explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for their blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
for(var/datum/mind/cult_mind in cult)
- to_chat(cult_mind.current, "You and your acolytes have completed your task, but this place requires yet more preparation!")
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation] "
+ if(cult_mind)
+ to_chat(cult_mind.current, "You and your acolytes have completed your task, but this place requires yet more preparation!")
+ to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
+ cult_mind.memory += "Objective #[current_objective]: [explanation] "
message_admins("New Cult Objective: [new_objective]")
log_admin("New Cult Objective: [new_objective]")
@@ -95,12 +96,13 @@
/datum/game_mode/cult/proc/gtfo_phase()//YOU HAD ONE JOB
var/explanation
- objectives +="survive"
+ objectives += "survive"
explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
for(var/datum/mind/cult_mind in cult)
- to_chat(cult_mind.current, "You and your acolytes suddenly feel the urge to do your best, but survive!")
- to_chat(cult_mind.current, "Objective Survive: [explanation]")
- cult_mind.memory += "Objective Survive: [explanation] "
+ if(cult_mind)
+ to_chat(cult_mind.current, "You and your acolytes suddenly feel the urge to do your best, but survive!")
+ to_chat(cult_mind.current, "Objective Survive: [explanation]")
+ cult_mind.memory += "Objective Survive: [explanation] "
/datum/game_mode/cult/proc/second_phase()
@@ -115,9 +117,10 @@
explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin."
for(var/datum/mind/cult_mind in cult)
- to_chat(cult_mind.current, "You and your acolytes have succeeded in preparing the station for the ultimate ritual!")
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation] "
+ if(cult_mind)
+ to_chat(cult_mind.current, "You and your acolytes have succeeded in preparing the station for the ultimate ritual!")
+ to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
+ cult_mind.memory += "Objective #[current_objective]: [explanation] "
/datum/game_mode/cult/proc/third_phase()
current_objective++
@@ -132,15 +135,16 @@
switch(last_objective)
if("harvest")
- explanation = "[ticker.mode.cultdat.entity_title1] hungers for his first meal of this never-ending day. Offer him [harvest_target] humans in sacrifice."
+ explanation = "[ticker.mode.cultdat.entity_title1] hungers for their first meal of this never-ending day. Offer them [harvest_target] humans in sacrifice."
if("hijack")
- explanation = "[ticker.mode.cultdat.entity_name] wishes for his troops to start the assault on Centcom immediately. Hijack the escape shuttle and don't let a single non-cultist board it."
+ explanation = "[ticker.mode.cultdat.entity_name] wishes for their troops to start the assault on Centcom immediately. Hijack the escape shuttle and don't let a single non-cultist board it."
if("massacre")
explanation = "[ticker.mode.cultdat.entity_name] wants to watch you as you massacre the remaining humans on the station (until less than [massacre_target] humans are left alive)."
for(var/datum/mind/cult_mind in cult)
- to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
- cult_mind.memory += "Objective #[current_objective]: [explanation] "
+ if(cult_mind)
+ to_chat(cult_mind.current, "Objective #[current_objective]: [explanation]")
+ cult_mind.memory += "Objective #[current_objective]: [explanation] "
message_admins("Last Cult Objective: [last_objective]")
log_admin("Last Cult Objective: [last_objective]")
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index 55d2a51de61..72535d9d1e5 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -109,6 +109,7 @@
target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \
Soulstones used on construct shells will move that soul into a powerful construct of your choice.
"
+
text += "Rite of Resurrection This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \
the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \
within a few seconds, they will be brought back to life, healed of all ailments.
"
@@ -293,7 +294,7 @@
var/mob/living/carbon/human/H = user
var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot")
var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone))
- user.visible_message("[user] cuts open their \The [affecting] and begins writing in their own blood!", "You slice open your [affecting] and begin drawing a sigil of [ticker.mode.cultdat.entity_title3].")
+ user.visible_message("[user] cuts open their [affecting] and begins writing in their own blood!", "You slice open your [affecting] and begin drawing a sigil of [ticker.mode.cultdat.entity_title3].")
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting)
if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user)))
for(var/V in shields)
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 3649a2fb88f..9b94627d664 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -36,6 +36,8 @@ To draw a rune, use an arcane tome.
var/req_keyword = 0 //If the rune requires a keyword - go figure amirite
var/keyword //The actual keyword for the rune
+ var/invoke_damage = 0 //how much damage invokers take when invoking it
+
/obj/effect/rune/New(loc, set_keyword)
..()
@@ -118,6 +120,8 @@ structure_check() searches for nearby cultist structures required for the invoca
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
var/list/invokers = list() //people eligible to invoke the rune
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
+ if(invisibility == INVISIBILITY_OBSERVER)//hidden rune
+ return
if(user)
chanters |= user
invokers |= user
@@ -146,11 +150,14 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/proc/invoke(var/list/invokers)
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
- if(invocation)
- for(var/M in invokers)
- var/mob/living/L = M
+ for(var/M in invokers)
+ var/mob/living/L = M
+ if(invocation)
L.say(invocation)
L.changeNext_move(CLICK_CD_MELEE)//THIS IS WHY WE CAN'T HAVE NICE THINGS
+ if(invoke_damage)
+ L.apply_damage(invoke_damage, BRUTE)
+ to_chat(L, "[src] saps your strength!")
do_invoke_glow()
/obj/effect/rune/proc/burn_invokers(var/list/mobstoburn)
@@ -181,13 +188,13 @@ structure_check() searches for nearby cultist structures required for the invoca
cultist_name = "malformed rune"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
invocation = "Ra'sha yoka!"
+ invoke_damage = 30
/obj/effect/rune/malformed/invoke(var/list/invokers)
..()
for(var/M in invokers)
var/mob/living/L = M
- to_chat(M, "You feel your life force draining. [ticker.mode.cultdat.entity_title3] is displeased.")
- L.apply_damage(30, BRUTE)
+ to_chat(L, "You feel your life force draining. [ticker.mode.cultdat.entity_title3] is displeased.")
qdel(src)
/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes
@@ -252,6 +259,7 @@ var/list/teleport_runes = list()
icon_state = "2"
req_keyword = 1
var/listkey
+ invoke_damage = 6 //but theres some checks for z-level
/obj/effect/rune/teleport/New(loc, set_keyword)
..()
@@ -316,6 +324,12 @@ var/list/teleport_runes = list()
to_chat(user, "You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].")
if(moveuserlater)
user.forceMove(get_turf(actual_selected_rune))
+ var/mob/living/carbon/human/H = user
+ if(user.z != T.z)
+ H.bleed(5)
+ user.apply_damage(5, BRUTE)
+ else
+ H.bleed(rand(5,10))
else
fail_invoke()
@@ -458,6 +472,8 @@ var/list/teleport_runes = list()
T.gib()
rune_in_use = 0
+
+
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Tear Reality (God)"
@@ -806,6 +822,7 @@ var/list/teleport_runes = list()
cultist_desc = "when invoked, makes an invisible wall to block passage. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
icon_state = "1"
+ invoke_damage = 2
/obj/effect/rune/wall/examine(mob/user)
..()
@@ -831,6 +848,8 @@ var/list/teleport_runes = list()
req_cultists = 2
allow_excess_invokers = 1
icon_state = "5"
+ invoke_damage = 5
+ var/summontime = 0
/obj/effect/rune/summon/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
@@ -856,13 +875,24 @@ var/list/teleport_runes = list()
fail_invoke()
log_game("Summon Cultist rune failed - target in away mission")
return
- cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \
- "Overwhelming vertigo consumes you as you are hurled through the air!")
+ if((cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) && invokers < 3)
+ to_chat(user, "The summoning of [cultist_to_summon] is being blocked somehow! You need 3 chanters to counter it!")
+ fail_invoke()
+ new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning
+ log_game("Summon Cultist rune failed - holywater in target")
+ return
+
..()
- visible_message("A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!")
- user.apply_damage(10, BRUTE, "head")
- cultist_to_summon.forceMove(get_turf(src))
- qdel(src)
+ if(cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained())
+ summontime = 20
+
+ if(do_after(user, summontime, target = loc))
+ cultist_to_summon.visible_message("[cultist_to_summon] suddenly disappears in a flash of red light!", \
+ "Overwhelming vertigo consumes you as you are hurled through the air!")
+ visible_message("A foggy shape materializes atop [src] and solidifies into [cultist_to_summon]!")
+
+ cultist_to_summon.forceMove(get_turf(src))
+ qdel(src)
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
/obj/effect/rune/blood_boil
@@ -872,6 +902,7 @@ var/list/teleport_runes = list()
icon_state = "4"
construct_invoke = 0
req_cultists = 3
+ invoke_damage = 15
/obj/effect/rune/blood_boil/do_invoke_glow()
return
@@ -889,10 +920,6 @@ var/list/teleport_runes = list()
to_chat(C, "Your blood boils in your veins!")
C.take_overall_damage(45,45)
C.Stun(7)
- for(var/M in invokers)
- var/mob/living/L = M
- L.apply_damage(15, BRUTE, pick("l_arm", "r_arm"))
- to_chat(L,"[src] saps your strength!")
qdel(src)
explosion(T, -1, 0, 1, 5)
@@ -942,6 +969,9 @@ var/list/teleport_runes = list()
construct_invoke = 0
color = rgb(200, 0, 0)
var/list/summoned_guys = list()
+ var/ghost_limit = 5
+ var/ghosts = 0
+ invoke_damage = 10
/obj/effect/rune/manifest/New(loc)
..()
@@ -950,11 +980,21 @@ var/list/teleport_runes = list()
notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
/obj/effect/rune/manifest/can_invoke(mob/living/user)
+ if(ghosts >= ghost_limit)
+ to_chat(user, "You are sustaining too many ghosts to summon more!")
+ fail_invoke()
+ log_game("Manifest rune failed - too many summoned ghosts")
+ return list()
if(!(user in get_turf(src)))
to_chat(user,"You must be standing on [src]!")
fail_invoke()
log_game("Manifest rune failed - user not standing on rune")
return list()
+ if(user.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
+ to_chat(user, "Ghosts can't summon more ghosts!")
+ fail_invoke()
+ log_game("Manifest rune failed - user is a ghost")
+ return list()
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !jobban_isbanned(O, ROLE_SYNDICATE))
@@ -976,8 +1016,12 @@ var/list/teleport_runes = list()
var/mob/living/carbon/human/new_human = new(get_turf(src))
new_human.real_name = ghost_to_spawn.real_name
new_human.alpha = 150 //Makes them translucent
+ new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
+ new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts
new_human.color = "grey" //heh..cult greytide...litterly...
..()
+
+ playsound(src, 'sound/misc/exit_blood.ogg', 50, 1)
visible_message("A cloud of red mist forms above [src], and from within steps... a man.")
to_chat(user, "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...")
var/obj/machinery/shield/N = new(get_turf(src))
@@ -989,6 +1033,7 @@ var/list/teleport_runes = list()
new_human.key = ghost_to_spawn.key
ticker.mode.add_cultist(new_human.mind, 0)
summoned_guys |= new_human
+ ghosts++
to_chat(new_human, "You are a servant of [ticker.mode.cultdat.entity_title3]. You have been made semi-corporeal by the cult of [ticker.mode.cultdat.entity_name], and you are to serve them at all costs.")
while(user in get_turf(src))
@@ -1004,6 +1049,7 @@ var/list/teleport_runes = list()
for(var/obj/I in new_human)
new_human.unEquip(I)
summoned_guys -= new_human
+ ghosts--
new_human.dust()
/obj/effect/rune/manifest/Destroy()
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 42fb3105f18..b58f3f3878b 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -164,7 +164,7 @@
T.purpose = "Payment"
T.amount = pay
T.date = current_date_string
- T.time = worldtime2text()
+ T.time = station_time_timestamp()
T.source_terminal = "\[CLASSIFIED\] Terminal #[rand(111,333)]"
M.mind.initial_account.transaction_log.Add(T)
msg += "You have been sent the $[pay], as agreed."
@@ -281,10 +281,11 @@
// Get a list of all the people who want to be the antagonist for this round, except those with incompatible species
for(var/mob/new_player/player in players)
- if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species))
- player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them."
- candidates += player.mind
- players -= player
+ if(!player.skip_antag)
+ if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species))
+ player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them."
+ candidates += player.mind
+ players -= player
// If we don't have enough antags, draft people who voted for the round.
if(candidates.len < recommended_enemies)
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index fb4ba668bef..2390e3c19e5 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -35,7 +35,8 @@ var/round_start_time = 0
var/obj/screen/cinematic = null //used for station explosion cinematic
- var/round_end_announced = 0 // Spam Prevention. Announce round end only once.
+ var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\
+
/datum/controller/gameticker/proc/pregame()
login_music = pick(\
@@ -55,6 +56,7 @@ var/round_start_time = 0
if(pregame_timeleft <= 0)
current_state = GAME_STATE_SETTING_UP
+ Master.SetRunLevel(RUNLEVEL_SETUP)
while(!setup())
/datum/controller/gameticker/proc/votetimer()
@@ -77,6 +79,7 @@ var/round_start_time = 0
runnable_modes = config.get_runnable_modes()
if(runnable_modes.len==0)
current_state = GAME_STATE_PREGAME
+ Master.SetRunLevel(RUNLEVEL_LOBBY)
to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.")
return 0
if(secret_force_mode != "secret")
@@ -96,6 +99,7 @@ var/round_start_time = 0
mode = null
current_state = GAME_STATE_PREGAME
job_master.ResetOccupations()
+ Master.SetRunLevel(RUNLEVEL_LOBBY)
return 0
//Configure mode and assign player to special mode stuff
@@ -108,6 +112,7 @@ var/round_start_time = 0
current_state = GAME_STATE_PREGAME
to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.")
job_master.ResetOccupations()
+ Master.SetRunLevel(RUNLEVEL_LOBBY)
return 0
if(hide_mode)
@@ -125,6 +130,7 @@ var/round_start_time = 0
equip_characters()
data_core.manifest()
current_state = GAME_STATE_PLAYING
+ Master.SetRunLevel(RUNLEVEL_GAME)
callHook("roundstart")
@@ -385,6 +391,7 @@ var/round_start_time = 0
if((!mode.explosion_in_progress && game_finished) || force_ending)
current_state = GAME_STATE_FINISHED
+ Master.SetRunLevel(RUNLEVEL_POSTGAME)
auto_toggle_ooc(1) // Turn it on
spawn
declare_completion()
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index e80ba05eccb..be18793554f 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -90,6 +90,7 @@
melee_damage_upper = 15
melee_damage_type = STAMINA
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
+ obj_damage = 0
environment_smash = 0
attacktext = "shocks"
attack_sound = 'sound/effects/EMPulse.ogg'
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index 89be0391463..b7aef130bc0 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -22,6 +22,7 @@
maxHealth = INFINITY //The spirit itself is invincible
health = INFINITY
environment_smash = 0
+ obj_damage = 40
melee_damage_lower = 15
melee_damage_upper = 15
AIStatus = AI_OFF
@@ -342,8 +343,20 @@
"Lilac" = "#C7A0F6", \
"Orchid" = "#F62CF5")
+ var/bio_list = list("Rose" = "#F62C6B", \
+ "Peony" = "#E54750", \
+ "Lily" = "#F6562C", \
+ "Daisy" = "#ECCD39", \
+ "Zinnia" = "#89F62C", \
+ "Ivy" = "#5DF62C", \
+ "Iris" = "#2CF6B8", \
+ "Petunia" = "#51A9D4", \
+ "Violet" = "#8A347C", \
+ "Lilac" = "#C7A0F6", \
+ "Orchid" = "#F62CF5")
+
var/picked_name
- var/picked_color = pick("#FFFFFF","#000000","#808080","#A52A2A","#FF0000","#8B0000","#DC143C","#FFA500","#FFFF00","#008000","#00FF00","#006400","#00FFFF","#0000FF","#000080","#008080","#800080","#4B0082")
+// var/picked_color = pick("#FFFFFF","#000000","#808080","#A52A2A","#FF0000","#8B0000","#DC143C","#FFA500","#FFFF00","#008000","#00FF00","#006400","#00FFFF","#0000FF","#000080","#008080","#800080","#4B0082")
switch(theme)
if("magic")
@@ -372,15 +385,18 @@
to_chat(user, "[G.tech_fluff_string].")
G.speak_emote = list("states")
if("bio")
- G.name_color = picked_color
- G.icon = 'icons/mob/mob.dmi'
+ color = pick(bio_list) //technically not colors, just using the same flowers as tech currerntly
+ G.name_color = tech_list[color]
picked_name = pick("brood", "hive", "nest")
to_chat(user, "[G.bio_fluff_string].")
- G.name = "[picked_name] swarm"
- G.color = picked_color
- G.real_name = "[picked_name] swarm"
- G.icon_living = "headcrab"
- G.icon_state = "headcrab"
+
+ G.name = "[color] [picked_name]"
+ G.real_name = "[color] [picked_name]"
+ G.icon_living = "[theme][color]"
+ G.icon_state = "[theme][color]"
+ G.icon_dead = "[theme][color]"
+
+ to_chat(user, "[G.bio_fluff_string].")
G.attacktext = "swarms"
G.speak_emote = list("chitters")
diff --git a/code/game/gamemodes/miniantags/guardian/types/assassin.dm b/code/game/gamemodes/miniantags/guardian/types/assassin.dm
index 28626133557..426824f2e8c 100644
--- a/code/game/gamemodes/miniantags/guardian/types/assassin.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/assassin.dm
@@ -45,6 +45,8 @@
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
armour_penetration = initial(armour_penetration)
+ obj_damage = initial(obj_damage)
+ environment_smash = initial(environment_smash)
alpha = initial(alpha)
if(!forced)
to_chat(src, "You exit stealth.")
@@ -61,7 +63,9 @@
melee_damage_lower = 50
melee_damage_upper = 50
armour_penetration = 100
- new /obj/effect/temp_visual/guardian/phase(get_turf(src))
+ obj_damage = 0
+ environment_smash = ENVIRONMENT_SMASH_NONE
+ new /obj/effect/temp_visual/guardian/phase/out(get_turf(src))
alpha = 15
if(!forced)
to_chat(src, "You enter stealth, empowering your next attack.")
diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
index 648339743fd..695782728fa 100644
--- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
@@ -32,6 +32,8 @@
ranged = 1
melee_damage_lower = 10
melee_damage_upper = 10
+ obj_damage = initial(obj_damage)
+ environment_smash = initial(environment_smash)
alpha = 255
range = 13
incorporeal_move = 0
@@ -41,6 +43,8 @@
ranged = 0
melee_damage_lower = 0
melee_damage_upper = 0
+ obj_damage = 0
+ environment_smash = ENVIRONMENT_SMASH_NONE
alpha = 60
range = 255
incorporeal_move = 1
diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm
index 10327778a65..8dfc3d39796 100644
--- a/code/game/gamemodes/miniantags/guardian/types/standard.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm
@@ -1,6 +1,7 @@
/mob/living/simple_animal/hostile/guardian/punch
melee_damage_lower = 20
melee_damage_upper = 20
+ obj_damage = 80
damage_transfer = 0.4
playstyle_string = "As a Standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls."
environment_smash = 2
diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm
index 28456e39984..def7f1fdd76 100644
--- a/code/game/gamemodes/miniantags/morph/morph.dm
+++ b/code/game/gamemodes/miniantags/morph/morph.dm
@@ -23,6 +23,7 @@
maxHealth = 150
health = 150
environment_smash = 1
+ obj_damage = 50
melee_damage_lower = 20
melee_damage_upper = 20
see_in_dark = 8
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index 416610dd1c2..3c0523daa3c 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -29,6 +29,7 @@
health = 200
environment_smash = 1
//universal_understand = 1
+ obj_damage = 50
melee_damage_lower = 30
melee_damage_upper = 30
see_in_dark = 8
@@ -114,7 +115,7 @@
health = 500
melee_damage_upper = 60
melee_damage_lower = 60
- environment_smash = 3 //Smashes through EVERYTHING - r-walls included
+ environment_smash = ENVIRONMENT_SMASH_RWALLS //Smashes through EVERYTHING - r-walls included
faction = list("cult")
playstyle_string = "You are a Harbringer of the Slaughter. Brought forth by the servants of Nar-Sie, you have a single purpose: slaughter the heretics \
who do not worship your master. You may use the ability 'Blood Crawl' near a pool of blood to enter it and become incorporeal. Using the ability again near a blood pool will allow you \
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 6e085df1a5c..e3e30f51a36 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -46,7 +46,7 @@ var/bomb_set
if(timeleft <= 0)
spawn
explode()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
/obj/machinery/nuclearbomb/attackby(obj/item/weapon/O as obj, mob/user as mob, params)
@@ -177,7 +177,7 @@ var/bomb_set
return
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = physical_state)
ui.open()
@@ -280,11 +280,11 @@ var/bomb_set
timeleft = min(max(round(src.timeleft), 120), 600)
if(href_list["timer"])
if(timing == -1.0)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(safety)
to_chat(usr, "The safety is still on.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
timing = !(timing)
if(timing)
@@ -314,7 +314,7 @@ var/bomb_set
if(removal_stage == 5)
anchored = 0
visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(!isinspace())
@@ -326,7 +326,7 @@ var/bomb_set
else
to_chat(usr, "There is nothing to anchor to!")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/machinery/nuclearbomb/ex_act(severity)
return
@@ -400,6 +400,7 @@ var/bomb_set
name = "nuclear authentication disk"
desc = "Better keep this safe."
icon_state = "nucleardisk"
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 30, bio = 0, rad = 0)
/obj/item/weapon/disk/nuclear/New()
..()
diff --git a/code/game/gamemodes/shadowling/ascendant_shadowling.dm b/code/game/gamemodes/shadowling/ascendant_shadowling.dm
index a957c16c50e..f640174d537 100644
--- a/code/game/gamemodes/shadowling/ascendant_shadowling.dm
+++ b/code/game/gamemodes/shadowling/ascendant_shadowling.dm
@@ -28,7 +28,7 @@
minbodytemp = 0
maxbodytemp = INFINITY
- environment_smash = 3
+ environment_smash = ENVIRONMENT_SMASH_RWALLS
faction = list("faithless")
diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm
index 394f7fcc7da..781d417929f 100644
--- a/code/game/gamemodes/vampire/vampire.dm
+++ b/code/game/gamemodes/vampire/vampire.dm
@@ -384,8 +384,8 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
var/ay = owner.y
for(var/i = 1 to 20)
- ax += sun.dx
- ay += sun.dy
+ ax += SSsun.dx
+ ay += SSsun.dy
var/turf/T = locate(round(ax, 0.5), round(ay, 0.5), owner.z)
diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm
index 244b3b04977..55d7588c152 100644
--- a/code/game/machinery/Freezer.dm
+++ b/code/game/machinery/Freezer.dm
@@ -8,6 +8,7 @@
use_power = 1
current_heat_capacity = 1000
layer = 3
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
/obj/machinery/atmospherics/unary/cold_sink/freezer/New()
..()
@@ -99,7 +100,7 @@
/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
@@ -159,8 +160,8 @@
var/max_temperature = 0
anchored = 1.0
layer = 3
-
current_heat_capacity = 1000
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
/obj/machinery/atmospherics/unary/heat_reservoir/heater/New()
..()
@@ -257,7 +258,7 @@
/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 728575fee21..a8f79cab534 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -140,7 +140,7 @@
ui_interact(user)
/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770)
ui.open()
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 3b20943e46d..748e5ac9bb6 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -321,7 +321,7 @@
/obj/machinery/body_scanconsole/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "adv_med.tmpl", "Body Scanner", 690, 600)
ui.open()
@@ -474,7 +474,7 @@
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
P.info = "Body Scan - [href_list["name"]] "
- P.info += "Time of scan: [worldtime2text(world.time)]
"
+ P.info += "Time of scan: [station_time_timestamp()]
"
P.info += "[printing_text]"
P.info += "
Notes: "
P.name = "Body Scan - [href_list["name"]]"
diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm
index 13d619aef9f..b4c725f7d59 100644
--- a/code/game/machinery/ai_slipper.dm
+++ b/code/game/machinery/ai_slipper.dm
@@ -4,6 +4,7 @@
icon_state = "motion3"
layer = 3
anchored = 1.0
+ armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/uses = 20
var/disabled = TRUE
var/lethal = 0
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index ee6493973e5..d0f75dbe9e1 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -80,6 +80,7 @@
active_power_usage = 8
power_channel = ENVIRON
req_one_access = list(access_atmospherics, access_engine_equip)
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/alarm_id = null
var/frequency = 1439
//var/skipprocess = 0 //Experimenting
@@ -795,7 +796,7 @@
return thresholds
/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state)
ui.open()
@@ -921,7 +922,7 @@
return 1
if(href_list["atmos_reset"])
- if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src))
+ if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE))
apply_danger_level(ATMOS_ALARM_NONE)
alarmActivated = 0
update_icon()
diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm
index 62996473291..fcb9bd0d28e 100644
--- a/code/game/machinery/atmo_control.dm
+++ b/code/game/machinery/atmo_control.dm
@@ -129,11 +129,11 @@ obj/machinery/air_sensor
initialize()
..()
- atmos_machinery += src
+ SSair.atmos_machinery += src
set_frequency(frequency)
Destroy()
- atmos_machinery -= src
+ SSair.atmos_machinery -= src
if(radio_controller)
radio_controller.remove_object(src,frequency)
return ..()
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index 8057c6edf23..f4ae632791f 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -50,6 +50,7 @@ var/datum/canister_icons/canister_icon_container = new()
density = 1
var/health = 100.0
flags = CONDUCT
+ armor = list(melee = 50, bullet = 50, laser = 50, energy = 100, bomb = 10, bio = 100, rad = 100)
var/menu = 0
//used by nanoui: 0 = main menu, 1 = relabel
@@ -366,7 +367,7 @@ update_flag
..()
- nanomanager.update_uis(src) // Update all NanoUIs attached to src
+ SSnanoui.update_uis(src) // Update all NanoUIs attached to src
@@ -388,7 +389,7 @@ update_flag
return
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm
index d3f1f25a52d..061615ffeda 100644
--- a/code/game/machinery/atmoalter/meter.dm
+++ b/code/game/machinery/atmoalter/meter.dm
@@ -5,6 +5,7 @@
icon_state = "meterX"
var/obj/machinery/atmospherics/pipe/target = null
anchored = 1
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
power_channel = ENVIRON
var/frequency = 0
var/id
@@ -18,14 +19,14 @@
/obj/machinery/meter/New()
..()
- atmos_machinery += src
+ SSair.atmos_machinery += src
target = locate(/obj/machinery/atmospherics/pipe) in loc
if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it
id_tag = id
return 1
/obj/machinery/meter/Destroy()
- atmos_machinery -= src
+ SSair.atmos_machinery -= src
target = null
return ..()
diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm
index 45dcb5f7880..32d5f953d0a 100644
--- a/code/game/machinery/atmoalter/portable_atmospherics.dm
+++ b/code/game/machinery/atmoalter/portable_atmospherics.dm
@@ -1,6 +1,7 @@
/obj/machinery/portable_atmospherics
name = "atmoalter"
use_power = 0
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/datum/gas_mixture/air_contents = new
var/obj/machinery/atmospherics/unary/portables_connector/connected_port
@@ -13,7 +14,7 @@
/obj/machinery/portable_atmospherics/New()
..()
- atmos_machinery += src
+ SSair.atmos_machinery += src
air_contents.volume = volume
air_contents.temperature = T20C
@@ -36,7 +37,7 @@
update_icon()
/obj/machinery/portable_atmospherics/Destroy()
- atmos_machinery -= src
+ SSair.atmos_machinery -= src
disconnect()
QDEL_NULL(air_contents)
QDEL_NULL(holding)
diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm
index 4fca317bf0d..fb7732018e9 100644
--- a/code/game/machinery/atmoalter/pump.dm
+++ b/code/game/machinery/atmoalter/pump.dm
@@ -107,7 +107,7 @@
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm
index 17f671d9cc8..cbbea15bfea 100644
--- a/code/game/machinery/atmoalter/scrubber.dm
+++ b/code/game/machinery/atmoalter/scrubber.dm
@@ -118,7 +118,7 @@
/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm
index a9d288fe37e..18af424c4d6 100644
--- a/code/game/machinery/atmoalter/zvent.dm
+++ b/code/game/machinery/atmoalter/zvent.dm
@@ -11,10 +11,10 @@
/obj/machinery/zvent/New()
..()
- atmos_machinery += src
+ SSair.atmos_machinery += src
/obj/machinery/zvent/Destroy()
- atmos_machinery -= src
+ SSair.atmos_machinery -= src
return ..()
/obj/machinery/zvent/process_atmos()
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 346949414fa..dfc983bb803 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -82,7 +82,7 @@
ui_interact(user)
/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550)
ui.open()
@@ -175,7 +175,7 @@
return 1
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(exchange_parts(user, O))
@@ -239,7 +239,7 @@
use_power(max(500, inserted / 10))
qdel(O)
busy = 0
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/machinery/autolathe/attack_ghost(mob/user)
interact(user)
@@ -318,7 +318,7 @@
screen = AUTOLATHE_SEARCH_MENU
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
/obj/machinery/autolathe/RefreshParts()
@@ -353,7 +353,7 @@
else
var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff)
materials.use_amount(materials_used)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
sleep(32/coeff)
if(is_stack)
var/obj/item/stack/S = new D.build_path(BuildTurf)
@@ -362,7 +362,7 @@
var/obj/item/new_item = new D.build_path(BuildTurf)
new_item.materials[MAT_METAL] /= coeff
new_item.materials[MAT_GLASS] /= coeff
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
desc = initial(desc)
/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index b0179ff493f..61bcf2a15b8 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -7,7 +7,7 @@
idle_power_usage = 5
active_power_usage = 10
layer = 5
-
+ armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/datum/wires/camera/wires = null // Wires datum
var/list/network = list("SS13")
var/c_tag = null
diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm
index 1447be96d4f..379c798888b 100644
--- a/code/game/machinery/computer/HolodeckControl.dm
+++ b/code/game/machinery/computer/HolodeckControl.dm
@@ -332,7 +332,7 @@
// Holographic Items!
/turf/simulated/floor/holofloor/
thermal_conductivity = 0
-
+ icon_state = "plating"
/turf/simulated/floor/holofloor/grass
name = "Lush Grass"
icon_state = "grass1"
@@ -441,14 +441,14 @@
icon_state = "sword[item_color]"
hitsound = "sound/weapons/blade1.ogg"
w_class = WEIGHT_CLASS_BULKY
- playsound(user, 'sound/weapons/saberon.ogg', 50, 1)
+ playsound(user, 'sound/weapons/saberon.ogg', 20, 1)
to_chat(user, "[src] is now active.")
else
force = 3
icon_state = "sword0"
hitsound = "swing_hit"
w_class = WEIGHT_CLASS_SMALL
- playsound(user, 'sound/weapons/saberoff.ogg', 50, 1)
+ playsound(user, 'sound/weapons/saberoff.ogg', 20, 1)
to_chat(user, "[src] can now be concealed.")
if(istype(user,/mob/living/carbon/human))
var/mob/living/carbon/human/H = user
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 1a53fcfa4a5..e9f36eb4eaa 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -93,7 +93,7 @@
// onclose(user, "op")
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455)
ui.open()
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index bcdd075bea6..a3b9bd25815 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -33,7 +33,7 @@
to_chat(user, "You have been locked out from this console!")
/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500)
@@ -90,7 +90,7 @@
if(radio == 0 || radio == 1)
occupant.aiRadio.disabledAi = radio
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
update_icon()
return
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 67a6c6801b8..9284ac7d03d 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -22,7 +22,7 @@ var/global/list/minor_air_alarms = list()
ui_interact(user)
/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_alert.tmpl", src.name, 500, 500)
ui.open()
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 1a90398f922..4721131f8cc 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -125,7 +125,7 @@
return access
/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800)
@@ -211,7 +211,7 @@
network += net
break
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
// Check if camera is accessible when jumping
/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M)
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index d6792073f9d..d10c5c92808 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -138,7 +138,7 @@ var/time_last_changed_position = 0
id_card.loc = src
modify = id_card
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
attack_hand(user)
//Check if you can't touch a job in any way whatsoever
@@ -225,7 +225,7 @@ var/time_last_changed_position = 0
/obj/machinery/computer/card/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 775, 700)
ui.open()
@@ -406,7 +406,7 @@ var/time_last_changed_position = 0
modify.registered_name = temp_name
else
visible_message("[src] buzzes rudely.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if("account")
if(is_authenticated(usr) && !target_dept)
@@ -414,7 +414,7 @@ var/time_last_changed_position = 0
if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf)))
var/account_num = text2num(href_list["account"])
modify.associated_account_number = account_num
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if("mode")
mode = text2num(href_list["mode_target"])
@@ -425,11 +425,11 @@ var/time_last_changed_position = 0
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
spawn(50)
printing = null
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
var/obj/item/weapon/paper/P = new(loc)
if(mode == 2)
- P.name = text("crew manifest ([])", worldtime2text())
+ P.name = "crew manifest ([station_time_timestamp()])"
P.info = {"Crew Manifest
[data_core ? data_core.get_manifest(0) : ""]
@@ -493,7 +493,7 @@ var/time_last_changed_position = 0
opened_positions[edit_job_target]++
log_game("[key_name(usr)] has opened a job slot for job \"[j]\".")
message_admins("[key_name_admin(usr)] has opened a job slot for job \"[j.title]\".")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if("make_job_unavailable")
// MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS
@@ -513,7 +513,7 @@ var/time_last_changed_position = 0
opened_positions[edit_job_target]--
log_game("[key_name(usr)] has closed a job slot for job \"[j]\".")
message_admins("[key_name_admin(usr)] has closed a job slot for job \"[j.title]\".")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if("prioritize_job")
// TOGGLE WHETHER JOB APPEARS AS PRIORITIZED IN THE LOBBY
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 1eef8430d7b..9e7daea4661 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -88,7 +88,7 @@
W.loc = src
src.diskette = W
to_chat(user, "You insert [W].")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
else if(istype(W, /obj/item/device/multitool))
var/obj/item/device/multitool/M = W
@@ -122,7 +122,7 @@
return
// Set up the Nano UI
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520)
ui.open()
@@ -204,16 +204,16 @@
scan_mob(scanner.occupant)
loading = 0
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(href_list["task"])
switch(href_list["task"])
if("autoprocess")
autoprocess = 1
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if("stopautoprocess")
autoprocess = 0
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
//No locking an open scanner.
else if((href_list["lock"]) && (!isnull(src.scanner)))
@@ -257,12 +257,12 @@
if("load")
if((isnull(src.diskette)) || isnull(src.diskette.buf))
src.temp = "Error: The disk's data could not be read."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(isnull(src.active_record))
src.temp = "Error: No active record was found."
src.menu = 1
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
src.active_record = src.diskette.buf.copy()
@@ -277,7 +277,7 @@
else if(href_list["save_disk"]) //Save to disk!
if((isnull(src.diskette)) || (src.diskette.read_only) || (isnull(src.active_record)))
src.temp = "Error: The data could not be saved."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
// DNA2 makes things a little simpler.
@@ -294,7 +294,7 @@
src.temp = "Save \[[href_list["save_disk"]]\] successful."
else if(href_list["refresh"])
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
else if(href_list["selectpod"])
var/obj/machinery/clonepod/selected = locate(href_list["selectpod"])
@@ -346,7 +346,7 @@
scan_mode = 0
src.add_fingerprint(usr)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0)
@@ -358,7 +358,7 @@
return
if((isnull(subject)) || (!(ishuman(subject))) || (!subject.dna) || (NO_SCAN in subject.species.species_traits))
scantemp = "Error: Unable to locate valid genetic data."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(subject.get_int_organ(/obj/item/organ/internal/brain))
var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain)
@@ -366,31 +366,31 @@
var/datum/species/S = all_species[Brn.dna.species] // stepladder code wooooo
if(NO_SCAN in S.species_traits)
scantemp = "Error: Subject's brain is incompatible."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(!subject.get_int_organ(/obj/item/organ/internal/brain))
scantemp = "Error: No signs of intelligence detected."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(subject.suiciding)
scantemp = "Error: Subject's brain is not responding to scanning stimuli."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if((!subject.ckey) || (!subject.client))
scantemp = "Error: Mental interface failure."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
scantemp = "Error: Mental interface failure."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(scan_brain && !subject.get_int_organ(/obj/item/organ/internal/brain))
scantemp = "Error: No brain found."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(!isnull(find_record(subject.ckey)))
scantemp = "Subject already in database."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
subject.dna.check_integrity()
@@ -427,7 +427,7 @@
src.records += R
scantemp = "Subject successfully scanned. " + extra_info
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index e46182b735e..76d583df982 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -95,14 +95,14 @@
if(istype(id))
crew_announcement.announcer = GetNameAndAssignmentFromId(id)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(href_list["logout"])
authenticated = COMM_AUTHENTICATION_NONE
crew_announcement.announcer = ""
setMenuState(usr,COMM_SCREEN_MAIN)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(!is_authenticated(usr))
@@ -145,11 +145,11 @@
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
if(message_cooldown)
to_chat(usr, "Please allow at least one minute to pass between announcements.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement")
if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
crew_announcement.Announce(input)
message_cooldown = 1
@@ -159,7 +159,7 @@
if("callshuttle")
var/input = input(usr, "Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") as text|null
if(!input || ..() || !is_authenticated(usr))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
call_shuttle_proc(usr, input)
@@ -170,7 +170,7 @@
if("cancelshuttle")
if(isAI(usr) || isrobot(usr))
to_chat(usr, "Firewalls prevent you from recalling the shuttle.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No")
if(response == "Yes")
@@ -229,11 +229,11 @@
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
if(centcomm_message_cooldown)
to_chat(usr, "Arrays recycling. Please stand by.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") as text|null
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
Nuke_request(input, usr)
to_chat(usr, "Request sent.")
@@ -248,14 +248,14 @@
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
if(centcomm_message_cooldown)
to_chat(usr, "Arrays recycling. Please stand by.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
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.", "") as text|null
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
Centcomm_announce(input, usr)
- print_centcom_report(input, worldtime2text() +" Captain's Message")
+ print_centcom_report(input, station_time_timestamp() + " Captain's Message")
to_chat(usr, "Message transmitted.")
log_say("[key_name(usr)] has made a Centcomm announcement: [input]")
centcomm_message_cooldown = 1
@@ -268,11 +268,11 @@
if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged))
if(centcomm_message_cooldown)
to_chat(usr, "Arrays recycling. Please stand by.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] 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.", "") as text|null
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
Syndicate_announce(input, usr)
to_chat(usr, "Message transmitted.")
@@ -313,14 +313,14 @@
atc.squelched = !atc.squelched
to_chat(usr, "ATC traffic is now: [atc.squelched ? "Disabled" : "Enabled"].")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
/obj/machinery/computer/communications/emag_act(user as mob)
if(!emagged)
src.emagged = 1
to_chat(user, "You scramble the communication routing circuits!")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/machinery/computer/communications/attack_ai(var/mob/user as mob)
return src.attack_hand(user)
@@ -340,7 +340,7 @@
/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui)
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
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index d016a0df7bd..1fa129262fb 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -41,7 +41,7 @@
ui_interact(user)
/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380)
ui.open()
@@ -453,7 +453,7 @@
var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
- active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [worldtime2text()] [t1]"
+ active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()] [t1]"
if(href_list["del_c"])
var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
@@ -515,7 +515,7 @@
else
P.info += "Medical Record Lost! "
P.info += ""
- P.name = "paper- 'Medical Record'"
+ P.name = "paper- 'Medical Record: [active1.fields["name"]]'"
printing = 0
return 1
diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm
index a0ddb949449..58043bbf258 100644
--- a/code/game/machinery/computer/message.dm
+++ b/code/game/machinery/computer/message.dm
@@ -39,8 +39,6 @@
/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(isscrewdriver(O) && emag)
diff --git a/code/game/machinery/computer/pod_tracking_console.dm b/code/game/machinery/computer/pod_tracking_console.dm
index 75bad25bfab..a9e967ab171 100644
--- a/code/game/machinery/computer/pod_tracking_console.dm
+++ b/code/game/machinery/computer/pod_tracking_console.dm
@@ -14,7 +14,7 @@
ui_interact(user)
/obj/machinery/computer/podtracker/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "pod_tracking.tmpl", "Pod Tracking Console", 400, 500)
ui.open()
@@ -47,4 +47,4 @@
return 1
if(href_list["refresh"])
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index ec41de193b8..c3d367ae716 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -35,7 +35,7 @@
return 0
/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500)
ui.open()
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 8289e8714e3..9425d6df984 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -44,7 +44,7 @@
ui_interact(user)
/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 380)
ui.open()
@@ -198,7 +198,7 @@
active2.fields["criminal"] = "Released"
var/newstatus = active2.fields["criminal"]
log_admin("[key_name_admin(usr)] set secstatus of [their_rank] [their_name] to [newstatus], comment: [t1]")
- active2.fields["comments"] += "Set to [newstatus] by [usr.name] ([rank]) on [current_date_string] [worldtime2text()], comment: [t1]"
+ active2.fields["comments"] += "Set to [newstatus] by [usr.name] ([rank]) on [current_date_string] [station_time_timestamp()], comment: [t1]"
update_all_mob_security_hud()
if("rank")
if(active1)
@@ -366,7 +366,7 @@
else
P.info += "Security Record Lost! "
P.info += ""
- P.name = "paper - 'Security Record'"
+ P.name = "paper - 'Security Record: [active1.fields["name"]]'"
printing = 0
/* Removed due to BYOND issue
@@ -404,7 +404,7 @@
var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
- active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [worldtime2text()] [t1]"
+ active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()] [t1]"
else if(href_list["del_c"])
var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm
index 66d0bfdbaee..156a685655e 100644
--- a/code/game/machinery/computer/skills.dm
+++ b/code/game/machinery/computer/skills.dm
@@ -43,7 +43,7 @@
ui_interact(user)
/obj/machinery/computer/skills/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "skills_data.tmpl", name, 800, 380)
ui.open()
@@ -232,7 +232,7 @@
else
P.info += "General Record Lost! "
P.info += ""
- P.name = "paper - 'Employment Record'"
+ P.name = "paper - 'Employment Record: [active1.fields["name"]]'"
printing = 0
if(href_list["field"])
diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm
index e2119247409..4006e005b12 100644
--- a/code/game/machinery/computer/telecrystalconsoles.dm
+++ b/code/game/machinery/computer/telecrystalconsoles.dm
@@ -137,7 +137,7 @@ var/list/possible_uplinker_IDs = list("Alfa","Bravo","Charlie","Delta","Echo","F
var/list/transferlog = list()
/obj/machinery/computer/telecrystals/boss/proc/logTransfer(var/logmessage)
- transferlog += ("[worldtime2text()] [logmessage]")
+ transferlog += ("[station_time_timestamp()] [logmessage]")
/obj/machinery/computer/telecrystals/boss/proc/scanUplinkers()
for(var/obj/machinery/computer/telecrystals/uplinker/A in range(scanrange, src.loc))
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 938011c1fc8..2c862b5cb0c 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -6,7 +6,7 @@
anchored = 1.0
layer = 2.8
interact_offline = 1
-
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/on = 0
var/temperature_archived
var/mob/living/carbon/occupant = null
@@ -185,7 +185,7 @@
*/
/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 5f12a4f0076..527a0155c2e 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -24,42 +24,59 @@
#define AIRLOCK_OPENING 4
#define AIRLOCK_DENY 5
#define AIRLOCK_EMAG 6
+
+#define AIRLOCK_SECURITY_NONE 0 //Normal airlock //Wires are not secured
+#define AIRLOCK_SECURITY_METAL 1 //Medium security airlock //There is a simple metal over wires (use welder)
+#define AIRLOCK_SECURITY_PLASTEEL_I_S 2 //Sliced inner plating (use crowbar), jumps to 0
+#define AIRLOCK_SECURITY_PLASTEEL_I 3 //Removed outer plating, second layer here (use welder)
+#define AIRLOCK_SECURITY_PLASTEEL_O_S 4 //Sliced outer plating (use crowbar)
+#define AIRLOCK_SECURITY_PLASTEEL_O 5 //There is first layer of plasteel (use welder)
+#define AIRLOCK_SECURITY_PLASTEEL 6 //Max security airlock //Fully secured wires (use wirecutters to remove grille, that is electrified)
+
+#define AIRLOCK_INTEGRITY_N 300 // Normal airlock integrity
+#define AIRLOCK_INTEGRITY_MULTIPLIER 1.5 // How much reinforced doors health increases
+#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
+#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
var/list/airlock_overlays = list()
/obj/machinery/door/airlock
name = "airlock"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "closed"
- autoclose = 1
+ max_integrity = 300
+ integrity_failure = 70
+ damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
+ autoclose = TRUE
explosion_block = 1
assemblytype = /obj/structure/door_assembly
normalspeed = 1
+ var/security_level = 0 //How much are wires secured
var/aiControlDisabled = FALSE //If TRUE, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
var/hackProof = FALSE // if TRUE, this door can't be hacked by the AI
var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
var/main_power_lost_until = 0 //World time when main power is restored.
var/backup_power_lost_until = -1 //World time when backup power is restored.
- var/electrified_timer = null
- var/main_power_timer = null
- var/backup_power_timer = null
+ var/electrified_timer
+ var/main_power_timer
+ var/backup_power_timer
var/spawnPowerRestoreRunning = 0
- var/locked = 0
- var/lights = 1 // bolt lights show by default
- var/datum/wires/airlock/wires = null
+ var/lights = TRUE // bolt lights show by default
+ var/datum/wires/airlock/wires
var/aiDisabledIdScanner = 0
var/aiHacking = 0
- var/obj/machinery/door/airlock/closeOther = null
- var/closeOtherId = null
+ var/obj/machinery/door/airlock/closeOther
+ var/closeOtherId
var/lockdownbyai = 0
var/justzap = 0
- var/safe = 1
- var/obj/item/weapon/airlock_electronics/electronics = null
+ var/obj/item/weapon/airlock_electronics/electronics
var/hasShocked = 0 //Prevents multiple shocks from happening
var/obj/item/weapon/note //Any papers pinned to the airlock
var/previous_airlock = /obj/structure/door_assembly //what airlock assembly mineral plating was applied to
- var/airlock_material = null //material of inner filling; if its an airlock with glass, this should be set to "glass"
+ var/airlock_material //material of inner filling; if its an airlock with glass, this should be set to "glass"
var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
var/note_overlay_file = 'icons/obj/doors/airlocks/station/overlays.dmi' //Used for papers and photos pinned to the airlock
+ var/normal_integrity = AIRLOCK_INTEGRITY_N
+ var/prying_so_hard = FALSE
var/image/old_frame_overlay //keep those in order to prevent unnecessary updating
var/image/old_filling_overlay
@@ -67,6 +84,7 @@ var/list/airlock_overlays = list()
var/image/old_panel_overlay
var/image/old_weld_overlay
var/image/old_sparks_overlay
+ var/image/old_dam_overlay
var/image/old_note_overlay
var/doorOpen = 'sound/machines/airlock_open.ogg'
@@ -100,14 +118,25 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/initialize()
. = ..()
if(closeOtherId != null)
- for(var/obj/machinery/door/airlock/A in airlocks)
- if(A.closeOtherId == closeOtherId && A != src)
- closeOther = A
- break
+ addtimer(src, "update_other_id", 5)
if(glass)
airlock_material = "glass"
+ if(security_level > AIRLOCK_SECURITY_METAL)
+ obj_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
+ max_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
+ else
+ obj_integrity = normal_integrity
+ max_integrity = normal_integrity
+ if(damage_deflection == AIRLOCK_DAMAGE_DEFLECTION_N && security_level > AIRLOCK_SECURITY_METAL)
+ damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
update_icon()
+/obj/machinery/door/airlock/proc/update_other_id()
+ for(var/obj/machinery/door/airlock/A in airlocks)
+ if(A.closeOtherId == closeOtherId && A != src)
+ closeOther = A
+ break
+
/obj/machinery/door/airlock/Destroy()
QDEL_NULL(electronics)
QDEL_NULL(wires)
@@ -299,6 +328,7 @@ About the new airlock wires panel:
var/image/lights_overlay
var/image/panel_overlay
var/image/weld_overlay
+ var/image/damag_overlay
var/image/sparks_overlay
var/image/note_overlay
var/notetype = note_type()
@@ -311,10 +341,17 @@ About the new airlock wires panel:
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
- panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
+ if(security_level)
+ panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
+ else
+ panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
- if(lights)
+ if(obj_integrity Wires are protected!")
+ return
wires.Interact(user)
else
- ..(user)
- return
+ ..()
/obj/machinery/door/airlock/CanUseTopic(mob/user)
if(!issilicon(user) && !isobserver(user))
@@ -689,26 +777,145 @@ About the new airlock wires panel:
return 1
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
- if(!issilicon(usr))
+ if(!issilicon(user))
if(isElectrified())
if(shock(user, 75))
return
- if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
- return
add_fingerprint(user)
- if((iswelder(C) && !operating && density))
- var/obj/item/weapon/weldingtool/W = C
- if(W.remove_fuel(0,user))
- if(!welded)
- welded = 1
- else
- welded = null
- update_icon()
- return
- else
- return
- else if(isscrewdriver(C))
+
+ if(panel_open)
+ switch(security_level)
+ if(AIRLOCK_SECURITY_NONE)
+ if(istype(C, /obj/item/stack/sheet/metal))
+ var/obj/item/stack/sheet/metal/S = C
+ if(S.get_amount() < 2)
+ to_chat(user, "You need at least 2 metal sheets to reinforce [src].")
+ return
+ to_chat(user, "You start reinforcing [src]...")
+ if(do_after(user, 20, 1, target = src))
+ if(!panel_open || !S.use(2))
+ return
+ user.visible_message("[user] reinforce \the [src] with metal.",
+ "You reinforce \the [src] with metal.")
+ security_level = AIRLOCK_SECURITY_METAL
+ update_icon()
+ return
+ else if(istype(C, /obj/item/stack/sheet/plasteel))
+ var/obj/item/stack/sheet/plasteel/S = C
+ if(S.get_amount() < 2)
+ to_chat(user, "You need at least 2 plasteel sheets to reinforce [src].")
+ return
+ to_chat(user, "You start reinforcing [src]...")
+ if(do_after(user, 20, 1, target = src))
+ if(!panel_open || !S.use(2))
+ return
+ user.visible_message("[user] reinforce \the [src] with plasteel.",
+ "You reinforce \the [src] with plasteel.")
+ security_level = AIRLOCK_SECURITY_PLASTEEL
+ modify_max_integrity(normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER)
+ damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
+ update_icon()
+ return
+ if(AIRLOCK_SECURITY_METAL)
+ if(iswelder(C))
+ var/obj/item/weapon/weldingtool/WT = C
+ if(!WT.remove_fuel(2, user))
+ return
+ to_chat(user, "You begin cutting the panel's shielding...")
+ playsound(loc, WT.usesound, 40, 1)
+ if(do_after(user, 40 * WT.toolspeed, 1, target = src))
+ if(!panel_open || !WT.isOn())
+ return
+ playsound(loc, WT.usesound, 50, 1)
+ user.visible_message("[user] cuts through \the [src]'s shielding.",
+ "You cut through \the [src]'s shielding.",
+ "You hear welding.")
+ security_level = AIRLOCK_SECURITY_NONE
+ spawn_atom_to_turf(/obj/item/stack/sheet/metal, user.loc, 2)
+ update_icon()
+ return
+ if(AIRLOCK_SECURITY_PLASTEEL_I_S)
+ if(iscrowbar(C))
+ var/obj/item/weapon/crowbar/W = C
+ to_chat(user, "You start removing the inner layer of shielding...")
+ playsound(src, W.usesound, 100, 1)
+ if(do_after(user, 40 * W.toolspeed, 1, target = src))
+ if(!panel_open)
+ return
+ if(security_level != AIRLOCK_SECURITY_PLASTEEL_I_S)
+ return
+ user.visible_message("[user] remove \the [src]'s shielding.",
+ "You remove \the [src]'s inner shielding.")
+ security_level = AIRLOCK_SECURITY_NONE
+ modify_max_integrity(normal_integrity)
+ damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
+ spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
+ update_icon()
+ return
+ if(AIRLOCK_SECURITY_PLASTEEL_I)
+ if(iswelder(C))
+ var/obj/item/weapon/weldingtool/WT = C
+ if(!WT.remove_fuel(2, user))
+ return
+ to_chat(user, "You begin cutting the inner layer of shielding...")
+ playsound(loc, WT.usesound, 40, 1)
+ if(do_after(user, 40 * WT.toolspeed, 1, target = src))
+ if(!panel_open || !WT.isOn())
+ return
+ playsound(loc, WT.usesound, 50, 1)
+ user.visible_message("[user] cuts through \the [src]'s shielding.",
+ "You cut through \the [src]'s shielding.",
+ "You hear welding.")
+ security_level = AIRLOCK_SECURITY_PLASTEEL_I_S
+ return
+ if(AIRLOCK_SECURITY_PLASTEEL_O_S)
+ if(iscrowbar(C))
+ var/obj/item/weapon/crowbar/W = C
+ to_chat(user, "You start removing outer layer of shielding...")
+ playsound(src, W.usesound, 100, 1)
+ if(do_after(user, 40 * W.toolspeed, 1, target = src))
+ if(!panel_open)
+ return
+ if(security_level != AIRLOCK_SECURITY_PLASTEEL_O_S)
+ return
+ user.visible_message("[user] remove \the [src]'s shielding.",
+ "You remove \the [src]'s shielding.")
+ security_level = AIRLOCK_SECURITY_PLASTEEL_I
+ spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
+ return
+ if(AIRLOCK_SECURITY_PLASTEEL_O)
+ if(iswelder(C))
+ var/obj/item/weapon/weldingtool/WT = C
+ if(!WT.remove_fuel(2, user))
+ return
+ to_chat(user, "You begin cutting the outer layer of shielding...")
+ playsound(loc, WT.usesound, 40, 1)
+ if(do_after(user, 40 * WT.toolspeed, 1, target = src))
+ if(!panel_open || !WT.isOn())
+ return
+ playsound(loc, WT.usesound, 50, 1)
+ user.visible_message("[user] cuts through \the [src]'s shielding.",
+ "You cut through \the [src]'s shielding.",
+ "You hear welding.")
+ security_level = AIRLOCK_SECURITY_PLASTEEL_O_S
+ return
+ if(AIRLOCK_SECURITY_PLASTEEL)
+ if(iswirecutter(C))
+ var/obj/item/weapon/wirecutters/W = C
+ if(arePowerSystemsOn() && shock(user, 60)) // Protective grille of wiring is electrified
+ return
+ to_chat(user, "You start cutting through the outer grille.")
+ playsound(src, W.usesound, 100, 1)
+ if(do_after(user, 10 * W.toolspeed, 1, target = src))
+ if(!panel_open)
+ return
+ user.visible_message("[user] cut through \the [src]'s outer grille.",
+ "You cut through \the [src]'s outer grille.")
+ security_level = AIRLOCK_SECURITY_PLASTEEL_O
+ return
+
+ if(isscrewdriver(C))
panel_open = !panel_open
to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the airlock.")
playsound(loc, C.usesound, 50, 1)
@@ -740,74 +947,105 @@ About the new airlock wires panel:
user.visible_message("[user] pins [C] to [src].", "You pin [C] to [src].")
note = C
update_icon()
- else if(iscrowbar(C) || istype(C, /obj/item/weapon/twohanded/fireaxe))
- var/beingcrowbarred = null
- if(iscrowbar(C))
- beingcrowbarred = 1 //derp, Agouri
- else
- beingcrowbarred = 0
- if(beingcrowbarred && panel_open && (emagged || (density && welded && !operating && !arePowerSystemsOn() && !locked)))
- playsound(loc, C.usesound, 100, 1)
- user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.")
- if(do_after(user, 40 * C.toolspeed, target = src))
- if(loc)
- deconstruct(TRUE, user)
- return
- if(istype(C, /obj/item/weapon/crowbar/power) && density)
- if(isElectrified())
- shock(user, 100)//it's like sticking a fork in a power socket
- return
-
- if(locked)
- to_chat(user, "The bolts are down, it won't budge!")
- return
-
- if(welded)
- to_chat(user, "It's welded, it won't budge!")
- return
-
- if(arePowerSystemsOn())
- var/obj/item/weapon/crowbar/power/P = C
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1) //is it aliens or just the CE being a dick?
- user.visible_message("[user] starts forcing [src] with \the [P]...", \
- "You begin forcing [src] with \the [P]...")
- if(do_after(user, P.airlock_open_time, target = src))
- user.visible_message("[user] forces [src] with \the [P].", \
- "You force [src] with \the [P].")
- open(2)
- if(density && !open(2))
- to_chat(user, "Despite your attempts, the [src] refuses to open.")
- return
- if(arePowerSystemsOn())
- to_chat(user, "The airlock's motors resist your efforts to force it.")
- else if(locked)
- to_chat(user, "The airlock's bolts prevent it from being forced.")
- else if(!welded && !operating)
- if(density)
- if(!beingcrowbarred) //being fireaxe'd
- var/obj/item/weapon/twohanded/fireaxe/F = C
- if(F.wielded)
- spawn(0)
- open(1)
- else
- to_chat(user, "You need to be wielding \the [C] to do that.")
- else
- spawn(0)
- open(1)
- else
- if(!beingcrowbarred)
- var/obj/item/weapon/twohanded/fireaxe/F = C
- if(F.wielded)
- spawn(0)
- close(1)
- else
- to_chat(user, "You need to be wielding \the [C] to do that.")
- else
- spawn(0)
- close(1)
else
- ..()
- return
+ return ..()
+
+/obj/machinery/door/airlock/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
+ if(!operating && density)
+ if(user.a_intent != INTENT_HELP)
+ if(W.remove_fuel(0, user))
+ user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \
+ "You begin [welded ? "unwelding":"welding"] the airlock...", \
+ "You hear welding.")
+ playsound(loc, W.usesound, 40, 1)
+ if(do_after(user, 40 * W.toolspeed, 1, target = src, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
+ playsound(loc, 'sound/items/welder2.ogg', 50, 1)
+ welded = !welded
+ user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \
+ "You [welded ? "weld the airlock shut":"unweld the airlock"].")
+ update_icon()
+ else
+ if(obj_integrity < max_integrity)
+ if(W.remove_fuel(0, user))
+ user.visible_message("[user] is welding the airlock.", \
+ "You begin repairing the airlock...", \
+ "You hear welding.")
+ playsound(loc, W.usesound, 40, 1)
+ if(do_after(user, 40 * W.toolspeed, 1, target = src, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
+ playsound(loc, 'sound/items/welder2.ogg', 50, 1)
+ obj_integrity = max_integrity
+ stat &= ~BROKEN
+ user.visible_message("[user.name] has repaired [src].", \
+ "You finish repairing the airlock.")
+ update_icon()
+ else
+ to_chat(user, "The airlock doesn't need repairing.")
+
+/obj/machinery/door/airlock/proc/weld_checks(obj/item/weapon/weldingtool/W, mob/user)
+ return !operating && density && user && W && W.isOn() && user.loc
+
+/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user)
+ var/beingcrowbarred = null
+ if(iscrowbar(I))
+ beingcrowbarred = 1
+ else
+ beingcrowbarred = 0
+ if(beingcrowbarred && panel_open && ((emagged) || (density && welded && !operating && !arePowerSystemsOn() && !locked)))
+ playsound(loc, I.usesound, 100, 1)
+ user.visible_message("[user] removes the electronics from the airlock assembly.", \
+ "You start to remove electronics from the airlock assembly...")
+ if(do_after(user, 40 * I.toolspeed, target = src))
+ deconstruct(TRUE, user)
+ return
+ else if(arePowerSystemsOn())
+ to_chat(user, "The airlock's motors resist your efforts to force it!")
+ else if(locked)
+ to_chat(user, "The airlock's bolts prevent it from being forced!")
+ else if(!welded && !operating)
+ if(!beingcrowbarred) //being fireaxe'd
+ var/obj/item/weapon/twohanded/fireaxe/F = I
+ if(F.wielded)
+ spawn(0)
+ if(density)
+ open(1)
+ else
+ close(1)
+ else
+ to_chat(user, "You need to be wielding the fire axe to do that!")
+ else
+ spawn(0)
+ if(density)
+ open(1)
+ else
+ close(1)
+
+ if(istype(I, /obj/item/weapon/crowbar/power))
+ if(isElectrified())
+ shock(user, 100)//it's like sticking a forck in a power socket
+ return
+
+ if(!density)//already open
+ return
+
+ if(locked)
+ to_chat(user, "The bolts are down, it won't budge!")
+ return
+
+ if(welded)
+ to_chat(user, "It's welded, it won't budge!")
+ return
+
+ var/time_to_open = 5
+ if(arePowerSystemsOn() && !prying_so_hard)
+ time_to_open = 50
+ playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1) //is it aliens or just the CE being a dick?
+ prying_so_hard = TRUE
+ var/result = do_after(user, time_to_open, target = src)
+ prying_so_hard = FALSE
+ if(result)
+ open(1)
+ if(density && !open(1))
+ to_chat(user, "Despite your attempts, [src] refuses to open.")
/obj/machinery/door/airlock/open(forced=0)
if(operating || welded || locked || emagged)
@@ -824,10 +1062,6 @@ About the new airlock wires panel:
closeOther.close()
if(!density)
return TRUE
- if(operating)
- return
- if(!ticker)
- return 0
operating = TRUE
update_icon(AIRLOCK_OPENING, 1)
sleep(1)
@@ -837,7 +1071,7 @@ About the new airlock wires panel:
density = FALSE
air_update_turf(1)
sleep(1)
- layer = open_layer
+ layer = OPEN_DOOR_LAYER
update_icon(AIRLOCK_OPEN, 1)
operating = FALSE
@@ -850,6 +1084,8 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/close(forced=0, override = 0)
if((operating & !override) || welded || locked || emagged)
return
+ if(density)
+ return TRUE
if(!forced)
//despite the name, this wire is for general door control.
//Bolts are already covered by the check for locked, above
@@ -857,11 +1093,9 @@ About the new airlock wires panel:
return
if(safe)
for(var/turf/turf in locs)
- if(locate(/mob/living) in turf)
- // playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) //THE BUZZING IT NEVER STOPS -Pete
- spawn(60)
- autoclose()
- return
+ for(var/atom/movable/M in turf)
+ if(M.density && M != src) //something is blocking the door
+ addtimer(src, "autoclose", 60)
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
@@ -870,13 +1104,11 @@ About the new airlock wires panel:
playsound(loc, doorClose, 30, 1)
var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
if(killthis)
- killthis.ex_act(2)//Smashin windows
+ killthis.ex_act(EXPLODE_HEAVY)//Smashin windows
- if(density)
- return TRUE
operating = TRUE
update_icon(AIRLOCK_CLOSING, 1)
- layer = closed_layer
+ layer = CLOSED_DOOR_LAYER
if(!override)
sleep(1)
density = TRUE
@@ -892,13 +1124,10 @@ About the new airlock wires panel:
update_icon(AIRLOCK_CLOSED, 1)
operating = FALSE
if(safe)
- if(locate(/mob/living) in get_turf(src))
- sleep(1)
- open()
-
+ CheckForMobs()
return TRUE
-/obj/machinery/door/airlock/proc/lock(forced=0)
+/obj/machinery/door/airlock/lock(forced=0)
if(locked)
return 0
@@ -910,7 +1139,7 @@ About the new airlock wires panel:
update_icon()
return 1
-/obj/machinery/door/airlock/proc/unlock(forced=0)
+/obj/machinery/door/airlock/unlock(forced=0)
if(!locked)
return
@@ -941,11 +1170,34 @@ About the new airlock wires panel:
return 1
/obj/machinery/door/airlock/emp_act(severity)
+ ..()
if(prob(40/severity))
var/duration = world.time + SecondsToTicks(30 / severity)
if(duration > electrified_until)
electrify(duration)
- ..()
+
+/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
+ add_fingerprint(user)
+ if(isElectrified())
+ shock(user, 100) //Mmm, fried xeno!
+ return
+ if(!density) //Already open
+ return
+ if(locked || welded) //Extremely generic, as aliens only understand the basics of how airlocks work.
+ to_chat(user, "[src] refuses to budge!")
+ return
+ user.visible_message("[user] begins prying open [src].",\
+ "You begin digging your claws into [src] with all your might!",\
+ "You hear groaning metal...")
+ var/time_to_open = 5
+ if(arePowerSystemsOn())
+ time_to_open = 50 //Powered airlocks take longer to open, and are loud.
+ playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1)
+
+
+ if(do_after(user, time_to_open, target = src))
+ if(density && !open(1)) //The airlock is still closed, but something prevented it opening. (Another player noticed and bolted/welded the airlock in time!)
+ to_chat(user, "Despite your efforts, [src] managed to resist your attempts to open it!")
/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason
..()
@@ -982,6 +1234,19 @@ About the new airlock wires panel:
open()
safe = TRUE
+/obj/machinery/door/airlock/obj_break(damage_flag)
+ if(!(flags & BROKEN) && can_deconstruct)
+ stat |= BROKEN
+ if(!panel_open)
+ panel_open = TRUE
+ wires.CutAll()
+ update_icon()
+
+/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
+ . = ..()
+ if(obj_integrity < (0.75 * max_integrity))
+ update_icon()
+
/obj/machinery/door/airlock/deconstruct(disassembled = TRUE, mob/user)
if(can_deconstruct)
var/obj/structure/door_assembly/DA
@@ -998,13 +1263,16 @@ About the new airlock wires panel:
DA.previous_assembly = previous_airlock
DA.update_name()
DA.update_icon()
+
+ if(!disassembled)
+ if(DA)
+ DA.obj_integrity = DA.max_integrity * 0.5
if(user)
to_chat(user, "You remove the airlock electronics.")
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics(loc)
- if(!req_access)
- check_access()
+ check_access()
if(req_access.len)
ae.conf_access = req_access
else if(req_one_access.len)
@@ -1050,3 +1318,16 @@ About the new airlock wires panel:
#undef AIRLOCK_OPENING
#undef AIRLOCK_DENY
#undef AIRLOCK_EMAG
+
+#undef AIRLOCK_SECURITY_NONE
+#undef AIRLOCK_SECURITY_METAL
+#undef AIRLOCK_SECURITY_PLASTEEL_I_S
+#undef AIRLOCK_SECURITY_PLASTEEL_I
+#undef AIRLOCK_SECURITY_PLASTEEL_O_S
+#undef AIRLOCK_SECURITY_PLASTEEL_O
+#undef AIRLOCK_SECURITY_PLASTEEL
+
+#undef AIRLOCK_INTEGRITY_N
+#undef AIRLOCK_INTEGRITY_MULTIPLIER
+#undef AIRLOCK_DAMAGE_DEFLECTION_N
+#undef AIRLOCK_DAMAGE_DEFLECTION_R
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index 223a3f1d50e..764a817c7bf 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -5,10 +5,12 @@
/obj/machinery/door/airlock/command
icon = 'icons/obj/doors/airlocks/station/command.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_com
+ normal_integrity = 450
/obj/machinery/door/airlock/security
icon = 'icons/obj/doors/airlocks/station/security.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_sec
+ normal_integrity = 450
/obj/machinery/door/airlock/engineering
icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
@@ -22,6 +24,7 @@
name = "maintenance access"
icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_mai
+ normal_integrity = 250
/obj/machinery/door/airlock/maintenance/external
name = "external airlock access"
@@ -63,6 +66,7 @@
/obj/machinery/door/airlock/command/glass
opacity = 0
glass = TRUE
+ normal_integrity = 400
/obj/machinery/door/airlock/engineering/glass
opacity = 0
@@ -71,6 +75,7 @@
/obj/machinery/door/airlock/security/glass
opacity = 0
glass = TRUE
+ normal_integrity = 400
/obj/machinery/door/airlock/medical/glass
opacity = 0
@@ -99,6 +104,7 @@
/obj/machinery/door/airlock/maintenance/external/glass
opacity = 0
glass = TRUE
+ normal_integrity = 200
//////////////////////////////////
/*
@@ -127,9 +133,11 @@
name = "diamond airlock"
icon = 'icons/obj/doors/airlocks/station/diamond.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_diamond
+ normal_integrity = 1000
explosion_block = 2
/obj/machinery/door/airlock/diamond/glass
+ normal_integrity = 950
opacity = 0
glass = TRUE
@@ -186,7 +194,8 @@
log_game("Plasma airlock ignited by [key_name(user)] in ([x],[y],[z])")
investigate_log("was ignited by [key_name(user)]","atmos")
ignite(is_hot(C))
- ..()
+ else
+ return ..()
/obj/machinery/door/airlock/plasma/BlockSuperconductivity() //we don't stop the heat~
return 0
@@ -239,8 +248,10 @@
assemblytype = /obj/structure/door_assembly/door_assembly_titanium
icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
+ normal_integrity = 400
/obj/machinery/door/airlock/titanium/glass
+ normal_integrity = 350
opacity = 0
glass = TRUE
@@ -287,6 +298,8 @@
opacity = 0
explosion_block = 2
assemblytype = /obj/structure/door_assembly/door_assembly_centcom
+ normal_integrity = 1000
+ security_level = 6
//////////////////////////////////
/*
@@ -299,6 +312,8 @@
overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_vault
explosion_block = 2
+ normal_integrity = 400 // reverse engieneerd: 400 * 1.5 (sec lvl 6) = 600 = original
+ security_level = 6
//////////////////////////////////
/*
@@ -363,6 +378,9 @@
overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_highsecurity
explosion_block = 2
+ normal_integrity = 500
+ security_level = 1
+ damage_deflection = 30
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
@@ -381,10 +399,7 @@
if((iswelder(C) && !operating && density))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user))
- if(!welded)
- welded = 1
- else
- welded = null
+ welded = !welded
update_icon()
return
else
@@ -412,9 +427,12 @@
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
note_overlay_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_abductor
+ damage_deflection = 30
explosion_block = 3
hackProof = TRUE
aiControlDisabled = 1
+ normal_integrity = 700
+ security_level = 1
//////////////////////////////////
/*
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 5914d9b5d87..b8f00b60131 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -40,11 +40,11 @@
for(var/obj/machinery/computer/prisoner/C in prisoncomputer_list)
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(C.loc)
- P.name = "[id] log - [logname] [worldtime2text()]"
+ P.name = "[id] log - [logname] [station_time_timestamp()]"
P.info = "[id] - Brig record
"
P.info += {"[station_name()] - Security Department
Admission data:
- Log generated at: [worldtime2text()]
+ Log generated at: [station_time_timestamp()]
Detainee: [logname]
Duration: [seconds_to_time(timetoset / 10)]
Charge(s): [logcharges]
@@ -68,7 +68,7 @@
rank = I.assignment
if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized
R.fields["comments"] = list()
- R.fields["comments"] += "Autogenerated by [name] on [current_date_string] [worldtime2text()] Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]."
+ R.fields["comments"] += "Autogenerated by [name] on [current_date_string] [station_time_timestamp()] Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]."
update_all_mob_security_hud()
return 1
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 842b08b577c..bfb758bae75 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -3,41 +3,53 @@
desc = "It opens and closes."
icon = 'icons/obj/doors/Doorint.dmi'
icon_state = "door1"
- anchored = 1
+ anchored = TRUE
opacity = 1
- density = 1
+ density = TRUE
layer = OPEN_DOOR_LAYER
power_channel = ENVIRON
- var/open_layer = OPEN_DOOR_LAYER
- var/closed_layer = CLOSED_DOOR_LAYER
+ max_integrity = 350
+ armor = list(melee = 30, bullet = 30, laser = 20, energy = 20, bomb = 10, bio = 100, rad = 100)
+ var/closingLayer = CLOSED_DOOR_LAYER
var/visible = 1
- var/operating = 0
+ var/operating = FALSE
var/autoclose = 0
var/autoclose_timer
- var/glass = 0
+ var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them.
+ var/locked = FALSE //whether the door is bolted or not.
+ var/glass = FALSE
var/welded = FALSE
var/normalspeed = 1
var/auto_close_time = 150
var/auto_close_time_dangerous = 5
var/assemblytype //the type of door frame to drop during deconstruction
- var/heat_proof = 0 // For rglass-windowed airlocks and 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.
+ var/datum/effect_system/spark_spread/spark_system
+ var/damage_deflection = 10
+ var/real_explosion_block //ignore this, just use explosion_block
+ var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors
+ var/emergency = FALSE
//Multi-tile doors
var/width = 1
/obj/machinery/door/New()
- . = ..()
- if(density)
- layer = closed_layer //Above most items if closed
- else
- layer = open_layer //Under all objects if opened. 2.7 due to tables being at 2.6
-
+ ..()
+ set_init_door_layer()
update_dir()
update_freelook_sight()
airlocks += src
+ spark_system = new /datum/effect_system/spark_spread
+ spark_system.set_up(2, 1, src)
+
+ //doors only block while dense though so we have to use the proc
+ real_explosion_block = explosion_block
+ explosion_block = EXPLOSION_BLOCK_PROC
+
+/obj/machinery/door/proc/set_init_door_layer()
+ if(density)
+ layer = closingLayer
+ else
+ layer = initial(layer)
/obj/machinery/door/setDir(newdir)
..()
@@ -64,34 +76,43 @@
if(autoclose_timer)
deltimer(autoclose_timer)
autoclose_timer = 0
+ if(spark_system)
+ QDEL_NULL(spark_system)
return ..()
/obj/machinery/door/Bumped(atom/AM)
- if(panel_open || operating || emagged)
+ if(operating || emagged)
return
- if(isliving(AM))
- var/mob/living/M = AM
- if(world.time - M.last_bumped <= 10)
- return //Can bump-open one airlock per second. This is to prevent shock spam.
- M.last_bumped = world.time
- if(!M.restrained())
+ if(ismob(AM))
+ var/mob/B = AM
+ if((isrobot(B)) && B.stat)
+ return
+ if(isliving(AM))
+ var/mob/living/M = AM
+ if(world.time - M.last_bumped <= 10)
+ return //Can bump-open one airlock per second. This is to prevent shock spam.
+ M.last_bumped = world.time
+ if(M.restrained() && !check_access(null))
+ return
if(M.mob_size > MOB_SIZE_SMALL)
bumpopen(M)
else if(ispet(M))
var/mob/living/simple_animal/A = AM
if(A.collar)
bumpopen(M)
- return
+ return
- if(istype(AM, /obj/mecha))
+ if(ismecha(AM))
var/obj/mecha/mecha = AM
if(density)
- if(mecha.occupant && (allowed(mecha.occupant) || check_access_list(mecha.operation_req_access) || emergency == 1))
+ if(mecha.occupant)
+ if(world.time - mecha.occupant.last_bumped <= 10)
+ return
+ if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access))
open()
else
do_animate("deny")
return
- return
/obj/machinery/door/Move(new_loc, new_dir)
var/turf/T = loc
@@ -122,14 +143,13 @@
user = null
if(density && !emagged)
- if(allowed(user) || emergency == 1)
+ if(allowed(user))
open()
- if(istype(user, /mob/living/simple_animal/bot))
+ if(isbot(user))
var/mob/living/simple_animal/bot/B = user
B.door_opened(src)
else
do_animate("deny")
- return
/obj/machinery/door/attack_ai(mob/user)
return attack_hand(user)
@@ -139,37 +159,74 @@
return attack_hand(user)
/obj/machinery/door/attack_hand(mob/user)
- return attackby(user, user)
+ return try_to_activate_door(user)
/obj/machinery/door/attack_tk(mob/user)
if(requiresID() && !allowed(null))
return
..()
-/obj/machinery/door/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/device/detective_scanner))
- return
-
- if(isrobot(user))
- return //borgs can't attack doors open because it conflicts with their AI-like interaction with them.
-
+/obj/machinery/door/proc/try_to_activate_door(mob/user)
add_fingerprint(user)
if(operating || emagged)
return
- if(density && (istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade)))
- emag_act(user)
- return 1
-
- if(allowed(user) || emergency == 1 || user.can_advanced_admin_interact())
+ if(!requiresID())
+ user = null //so allowed(user) always succeeds
+ if(allowed(user) || user.can_advanced_admin_interact())
if(density)
open()
else
close()
return
-
if(density)
do_animate("deny")
+/obj/machinery/door/allowed(mob/M)
+ if(emergency)
+ return TRUE
+ return ..()
+
+/obj/machinery/door/proc/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
+ return
+
+/obj/machinery/door/proc/try_to_crowbar(obj/item/I, mob/user)
+ return
+
+/obj/machinery/door/attackby(obj/item/I, mob/user, params)
+ if(user.a_intent != INTENT_HARM && (iscrowbar(I) || istype(I, /obj/item/weapon/twohanded/fireaxe)))
+ try_to_crowbar(I, user)
+ return 1
+ else if(iswelder(I))
+ try_to_weld(I, user)
+ return 1
+ else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM)
+ try_to_activate_door(user)
+ return 1
+ return ..()
+
+/obj/machinery/door/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
+ if(damage_flag == "melee" && damage_amount < damage_deflection)
+ return 0
+ . = ..()
+
+/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
+ . = ..()
+ if(. && obj_integrity > 0)
+ if(damage_amount >= 10 && prob(30))
+ spark_system.start()
+
+/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
+ switch(damage_type)
+ if(BRUTE)
+ if(glass)
+ playsound(loc, 'sound/effects/glasshit.ogg', 90, 1)
+ else if(damage_amount)
+ playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
+ else
+ playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
+ if(BURN)
+ playsound(loc, 'sound/items/welder.ogg', 100, 1)
+
/obj/machinery/door/emag_act(mob/user)
if(density)
flick("door_spark", src)
@@ -178,37 +235,17 @@
emagged = 1
return 1
-/obj/machinery/door/blob_act()
- if(prob(40))
- qdel(src)
- return
-
/obj/machinery/door/emp_act(severity)
if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) )
spawn(0)
open()
..()
-/obj/machinery/door/ex_act(severity)
- switch(severity)
- if(1)
- qdel(src)
- if(2)
- if(prob(25))
- qdel(src)
- if(3)
- if(prob(80))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
- return
-
/obj/machinery/door/update_icon()
if(density)
icon_state = "door1"
else
icon_state = "door0"
- return
/obj/machinery/door/proc/do_animate(animation)
switch(animation)
@@ -223,27 +260,24 @@
else
flick("doorc1", src)
if("deny")
- flick("door_deny", src)
- return
+ if(!stat)
+ flick("door_deny", src)
/obj/machinery/door/proc/open()
if(!density)
- return 1
+ return TRUE
if(operating)
return
- if(!ticker)
- return 0
- operating = 1
-
+ operating = TRUE
do_animate("opening")
set_opacity(0)
sleep(5)
- density = 0
+ density = FALSE
sleep(5)
- layer = open_layer
+ layer = initial(layer)
update_icon()
set_opacity(0)
- operating = 0
+ operating = FALSE
air_update_turf(1)
update_freelook_sight()
@@ -251,34 +285,51 @@
if(autoclose)
autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1)
- return 1
+ return TRUE
/obj/machinery/door/proc/close()
if(density)
- return 1
- if(operating)
+ return TRUE
+ if(operating || welded)
return
- operating = 1
+ if(safe)
+ for(var/atom/movable/M in get_turf(src))
+ if(M.density && M != src) //something is blocking the door
+ if(autoclose)
+ addtimer(src, "autoclose", 60)
+ return
+
+ operating = TRUE
if(autoclose_timer)
deltimer(autoclose_timer)
autoclose_timer = 0
do_animate("closing")
- layer = closed_layer
+ layer = closingLayer
sleep(5)
- density = 1
+ density = TRUE
sleep(5)
update_icon()
if(visible && !glass)
- set_opacity(1) //caaaaarn!
+ set_opacity(1)
operating = 0
air_update_turf(1)
update_freelook_sight()
- return
+ if(safe)
+ CheckForMobs()
+ else
+ crush()
+ return TRUE
+
+/obj/machinery/door/proc/CheckForMobs()
+ if(locate(/mob/living) in get_turf(src))
+ sleep(1)
+ open()
/obj/machinery/door/proc/crush()
for(var/mob/living/L in get_turf(src))
+ L.visible_message("[src] closes on [L], crushing them!", "[src] closes on you and crushes you!")
if(isalien(L)) //For xenos
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans.
L.emote("roar")
@@ -291,19 +342,21 @@
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE)
var/turf/location = get_turf(src)
L.add_splatter_floor(location)
+ for(var/obj/mecha/M in get_turf(src))
+ M.take_damage(DOOR_CRUSH_DAMAGE)
/obj/machinery/door/proc/requiresID()
return 1
+/obj/machinery/door/proc/hasPower()
+ return !(stat & NOPOWER)
+
/obj/machinery/door/proc/autoclose()
autoclose_timer = 0
- if(!qdeleted(src) && !density && !operating && autoclose)
+ if(!qdeleted(src) && !density && !operating && !locked && !welded && autoclose)
close()
- return
/obj/machinery/door/proc/update_freelook_sight()
- // Glass door glass = 1
- // don't check then?
if(!glass && cameranet)
cameranet.updateVisibility(src, 0)
@@ -315,6 +368,12 @@
/obj/machinery/door/morgue
icon = 'icons/obj/doors/doormorgue.dmi'
+/obj/machinery/door/proc/lock()
+ return
+
+/obj/machinery/door/proc/unlock()
+ return
+
/obj/machinery/door/proc/hostile_lockdown(mob/origin)
if(!stat) //So that only powered doors are closed.
close() //Close ALL the doors!
@@ -322,3 +381,33 @@
/obj/machinery/door/proc/disable_lockdown()
if(!stat) //Opens only powered doors.
open() //Open everything!
+
+/obj/machinery/door/blob_act(obj/structure/blob/B)
+ if(isturf(loc))
+ var/turf/T = loc
+ if(T.intact && level == 1) //the blob doesn't destroy thing below the floor
+ return
+ take_damage(400, BRUTE, "melee", 0, get_dir(src, B))
+
+/obj/machinery/door/ex_act(severity, target)
+ if(severity)
+ severity = max(1, severity - 1)
+ else
+ severity = 0
+ if(resistance_flags & INDESTRUCTIBLE)
+ return
+ if(target == src)
+ obj_integrity = 0
+ qdel(src)
+ return
+ switch(severity)
+ if(1)
+ obj_integrity = 0
+ qdel(src)
+ if(2)
+ take_damage(rand(100, 250), BRUTE, "bomb", 0)
+ if(3)
+ take_damage(rand(10, 90), BRUTE, "bomb", 0)
+
+/obj/machinery/door/GetExplosionBlock()
+ return density ? real_explosion_block : 0
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index fc0fc16d9cf..95af58d2767 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -4,8 +4,8 @@
#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove
#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart
-/var/const/OPEN = 1
-/var/const/CLOSED = 2
+/var/const/FD_OPEN = 1
+/var/const/FD_CLOSED = 2
/obj/machinery/door/firedoor
name = "firelock"
@@ -14,11 +14,17 @@
icon_state = "door_open"
opacity = 0
density = FALSE
+ burn_state = FIRE_PROOF
+ max_integrity = 300
heat_proof = TRUE
glass = TRUE
- closed_layer = 3.11
+ explosion_block = 1
+ safe = FALSE
+ layer = BELOW_OPEN_DOOR_LAYER
+ closingLayer = CLOSED_FIREDOOR_LAYER
auto_close_time = 50
assemblytype = /obj/structure/firelock_frame
+ armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100)
var/can_force = TRUE
var/force_open_time = 300
var/can_crush = TRUE
@@ -26,6 +32,22 @@
var/boltslocked = TRUE
var/active_alarm = FALSE
+/obj/machinery/door/firedoor/examine(mob/user)
+ ..()
+ if(!density)
+ to_chat(user, "It is open, but could be pried closed.")
+ else if(!welded)
+ to_chat(user, "It is closed, but could be pried open. Deconstruction would require it to be welded shut.")
+ else if(boltslocked)
+ to_chat(user, "It is welded shut. The floor bolts have been locked by screws.")
+ else
+ to_chat(user, "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor.")
+
+/obj/machinery/door/firedoor/closed
+ icon_state = "door_closed"
+ opacity = TRUE
+ density = TRUE
+
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(panel_open || operating)
return
@@ -33,14 +55,6 @@
return ..()
return 0
-/obj/machinery/door/firedoor/ex_act(severity)
- switch(severity)
- if(1)
- qdel(src)
- if(2)
- if(prob(50))
- qdel(src)
-
/obj/machinery/door/firedoor/power_change()
if(powered(power_channel))
stat &= ~NOPOWER
@@ -49,60 +63,6 @@
stat |= NOPOWER
update_icon()
-/obj/machinery/door/firedoor/attackby(obj/item/weapon/C, mob/user, params)
- add_fingerprint(user)
-
- if(operating)
- return
-
- if(iswelder(C))
- if(!density)
- return
- var/obj/item/weapon/weldingtool/W = C
- if(W.remove_fuel(0, user))
- welded = !welded
- to_chat(user, "You [welded ? "welded" : "unwelded"] \the [src]")
- update_icon()
- return
-
- if(welded)
- if(iswrench(C))
- if(boltslocked)
- to_chat(user, "There are screws locking the bolts in place!")
- return
- playsound(get_turf(src), C.usesound, 50, 1)
- user.visible_message("[user] starts undoing [src]'s bolts...", \
- "You start unfastening [src]'s floor bolts...")
- if(!do_after(user, 50 * C.toolspeed, target = src))
- return
- playsound(get_turf(src), C.usesound, 50, 1)
- user.visible_message("[user] unfastens [src]'s bolts.", \
- "You undo [src]'s floor bolts.")
- deconstruct(TRUE)
- return
- else if(isscrewdriver(C))
- user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts...", \
- "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts...")
- playsound(get_turf(src), C.usesound, 50, 1)
- boltslocked = !boltslocked
- return
- else
- to_chat(user, "\The [src] is welded solid!")
- return
-
- if(iscrowbar(C) || istype(C, /obj/item/weapon/twohanded/fireaxe))
- if(istype(C, /obj/item/weapon/twohanded/fireaxe))
- var/obj/item/weapon/twohanded/fireaxe/F = C
- if(!F.wielded)
- return
-
- user.visible_message("[user] forces \the [src] with [C].",
- "You force \the [src] with [C].")
- if(density)
- open()
- else
- close()
-
/obj/machinery/door/firedoor/attack_hand(mob/user)
if(operating || !density)
return
@@ -122,6 +82,57 @@
"You bang on \the [src].")
playsound(get_turf(src), 'sound/effects/Glassknock.ogg', 10, 1)
+/obj/machinery/door/firedoor/attackby(obj/item/weapon/C, mob/user, params)
+ add_fingerprint(user)
+
+ if(operating)
+ return
+
+ if(welded)
+ if(iswrench(C))
+ if(boltslocked)
+ to_chat(user, "There are screws locking the bolts in place!")
+ return
+ playsound(get_turf(src), C.usesound, 50, 1)
+ user.visible_message("[user] starts undoing [src]'s bolts...", \
+ "You start unfastening [src]'s floor bolts...")
+ if(!do_after(user, 50 * C.toolspeed, target = src))
+ return
+ playsound(get_turf(src), C.usesound, 50, 1)
+ user.visible_message("[user] unfastens [src]'s bolts.", \
+ "You undo [src]'s floor bolts.")
+ deconstruct(TRUE)
+ return
+ if(isscrewdriver(C))
+ user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \
+ "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.")
+ playsound(get_turf(src), C.usesound, 50, 1)
+ boltslocked = !boltslocked
+ return
+
+ return ..()
+
+/obj/machinery/door/firedoor/try_to_activate_door(mob/user)
+ return
+
+/obj/machinery/door/firedoor/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
+ if(W.remove_fuel(0, user))
+ playsound(get_turf(src), W.usesound, 50, 1)
+ user.visible_message("[user] starts [welded ? "unwelding" : "welding"] [src].", "You start welding [src].")
+ if(do_after(user, 40 * W.toolspeed, 1, target=src))
+ welded = !welded
+ to_chat(user, "[user] [welded ? "welds" : "unwelds"] [src].", "You [welded ? "weld" : "unweld"] [src].")
+ update_icon()
+
+/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user)
+ if(welded || operating)
+ return
+
+ if(density)
+ open()
+ else
+ close()
+
/obj/machinery/door/firedoor/attack_ai(mob/user)
forcetoggle()
@@ -147,7 +158,7 @@
/obj/machinery/door/firedoor/update_icon()
overlays.Cut()
- if(active_alarm && !(stat & NOPOWER))
+ if(active_alarm && hasPower())
overlays += image('icons/obj/doors/Doorfire.dmi', "alarmlights")
if(density)
icon_state = "door_closed"
@@ -177,8 +188,6 @@
/obj/machinery/door/firedoor/close()
. = ..()
- if(can_crush)
- crush()
latetoggle()
/obj/machinery/door/firedoor/autoclose()
@@ -186,18 +195,18 @@
. = ..()
/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE)
- if(operating || stat & NOPOWER || !nextstate)
+ if(operating || !hasPower() || !nextstate)
return
switch(nextstate)
- if(OPEN)
+ if(FD_OPEN)
nextstate = null
open(auto_close)
- if(CLOSED)
+ if(FD_CLOSED)
nextstate = null
close()
/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE)
- if(!magic && (operating || stat & NOPOWER))
+ if(!magic && (operating || !hasPower()))
return
if(density)
open(auto_close)
@@ -211,6 +220,7 @@
F.constructionStep = CONSTRUCTION_PANEL_OPEN
else
F.constructionStep = CONSTRUCTION_WIRES_EXPOSED
+ F.obj_integrity = F.max_integrity * 0.5
F.update_icon()
qdel(src)
@@ -219,6 +229,11 @@
flags = ON_BORDER
can_crush = FALSE
+/obj/machinery/door/firedoor/border_only/closed
+ icon_state = "door_closed"
+ opacity = TRUE
+ density = TRUE
+
/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
return 1
@@ -246,13 +261,10 @@
icon = 'icons/obj/doors/Doorfire.dmi'
glass = FALSE
opacity = 1
+ explosion_block = 2
assemblytype = /obj/structure/firelock_frame/heavy
can_force = FALSE
-
-/obj/machinery/door/firedoor/heavy/ex_act(severity)
- switch(severity)
- if(1)
- qdel(src)
+ max_integrity = 550
/obj/item/weapon/firelock_electronics
name = "firelock electronics"
@@ -270,28 +282,25 @@
desc = "A partially completed firelock."
icon = 'icons/obj/doors/Doorfire.dmi'
icon_state = "frame1"
- anchored = 0
- density = 1
+ anchored = FALSE
+ density = TRUE
+ max_integrity = 300
var/constructionStep = CONSTRUCTION_NOCIRCUIT
var/reinforced = 0
-/obj/structure/firelock_frame/heavy
- name = "heavy firelock frame"
- reinforced = 1
-
/obj/structure/firelock_frame/examine(mob/user)
..()
switch(constructionStep)
if(CONSTRUCTION_PANEL_OPEN)
- to_chat(user, "There is a small metal plate covering the wires.")
+ to_chat(user, "It is unbolted from the floor. A small loosely connected metal plate is covering the wires.")
+ if(!reinforced)
+ to_chat(user, "It could be reinforced with plasteel.")
if(CONSTRUCTION_WIRES_EXPOSED)
- to_chat(user, "Wires are trailing from the maintenance panel.")
+ to_chat(user, "The maintenance plate has been pried away, and wires are trailing.")
if(CONSTRUCTION_GUTTED)
- to_chat(user, "The circuit board is visible.")
+ to_chat(user, "The maintenance panel is missing wires and the circuit board is loosely connected.")
if(CONSTRUCTION_NOCIRCUIT)
- to_chat(user, "There are no electronics in the frame.")
- if(reinforced)
- to_chat(user, "The frame is reinforced.")
+ to_chat(user, "There are no firelock electronics in the frame. The frame could be cut apart.")
/obj/structure/firelock_frame/update_icon()
..()
@@ -371,19 +380,17 @@
constructionStep = CONSTRUCTION_GUTTED
update_icon()
return
- if(iswelder(C))
- var/obj/item/weapon/weldingtool/W = C
- if(W.remove_fuel(1, user))
- playsound(get_turf(src), C.usesound, 50, 1)
- user.visible_message("[user] starts welding a metal plate into [src]...", \
- "You begin welding the cover plate back onto [src]...")
- if(!do_after(user, 80 * C.toolspeed, target = src))
- return
- if(constructionStep != CONSTRUCTION_WIRES_EXPOSED)
- return
- playsound(get_turf(src), C.usesound, 50, 1)
- user.visible_message("[user] welds the metal plate into [src].", \
- "You weld [src]'s cover plate into place, hiding the wires.")
+ if(iscrowbar(C))
+ playsound(get_turf(src), C.usesound, 50, 1)
+ user.visible_message("[user] starts prying a metal plate into [src]...", \
+ "You begin prying the cover plate back onto [src]...")
+ if(!do_after(user, 80 * C.toolspeed, target = src))
+ return
+ if(constructionStep != CONSTRUCTION_WIRES_EXPOSED)
+ return
+ playsound(get_turf(src), C.usesound, 50, 1)
+ user.visible_message("[user] pries the metal plate into [src].", \
+ "You pry [src]'s cover plate into place, hiding the wires.")
constructionStep = CONSTRUCTION_PANEL_OPEN
update_icon()
return
@@ -428,7 +435,7 @@
playsound(get_turf(src), W.usesound, 50, 1)
user.visible_message("[user] begins cutting apart [src]'s frame...", \
"You begin slicing [src] apart...")
- if(!do_after(user, 80 * W.toolspeed, target = src))
+ if(!do_after(user, 40 * W.toolspeed, target = src))
return
if(constructionStep != CONSTRUCTION_NOCIRCUIT)
return
@@ -458,3 +465,13 @@
update_icon()
return
return ..()
+
+/obj/structure/firelock_frame/heavy
+ name = "heavy firelock frame"
+ reinforced = 1
+
+#undef CONSTRUCTION_COMPLETE
+#undef CONSTRUCTION_PANEL_OPEN
+#undef CONSTRUCTION_WIRES_EXPOSED
+#undef CONSTRUCTION_GUTTED
+#undef CONSTRUCTION_NOCIRCUIT
\ No newline at end of file
diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm
index d64b08efedf..b20d99b2613 100644
--- a/code/game/machinery/doors/poddoor.dm
+++ b/code/game/machinery/doors/poddoor.dm
@@ -1,107 +1,62 @@
/obj/machinery/door/poddoor
name = "blast door"
- desc = "That looks like it doesn't open easily."
- icon = 'icons/obj/doors/rapid_pdoor.dmi'
- icon_state = "pdoor1"
+ desc = "A heavy duty blast door that opens mechanically."
+ icon = 'icons/obj/doors/blastdoor.dmi'
+ icon_state = "closed"
+ layer = BLASTDOOR_LAYER
+ closingLayer = CLOSED_BLASTDOOR_LAYER
explosion_block = 3
- heat_proof = 1
+ heat_proof = TRUE
+ safe = FALSE
+ armor = list(melee = 50, bullet = 100, laser = 100, energy = 100, bomb = 50, bio = 100, rad = 100)
+ burn_state = FIRE_PROOF
+ damage_deflection = 70
var/id_tag = 1.0
var/protected = 1
/obj/machinery/door/poddoor/preopen
- icon_state = "pdoor0"
- density = 0
+ icon_state = "open"
+ density = FALSE
opacity = 0
/obj/machinery/door/poddoor/Bumped(atom/AM)
- if(!density)
- return ..()
+ if(density)
+ return
else
return 0
//"BLAST" doors are obviously stronger than regular doors when it comes to BLASTS.
/obj/machinery/door/poddoor/ex_act(severity, target)
- switch(severity)
- if(1)
- if(prob(80))
- qdel(src)
- else
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
- if(2)
- if(prob(20))
- qdel(src)
- else
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
-
- if(3)
- if(prob(80))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(2, 1, src)
- s.start()
-
-/obj/machinery/door/poddoor/attackby(obj/item/weapon/C, mob/user, params)
- add_fingerprint(user)
- if(!(iscrowbar(C) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1)))
+ if(severity == 3)
return
- if((density && (stat & NOPOWER) && !operating))
- spawn(0)
- operating = 1
- flick("pdoorc0", src)
- icon_state = "pdoor0"
- set_opacity(0)
- sleep(15)
- density = 0
- operating = 0
- return
- return
+ ..()
-/obj/machinery/door/poddoor/open()
- if(operating || emagged) //doors can still open when emag-disabled
- return
- if(!ticker)
- return 0
- if(!operating) //in case of emag
- operating = 1
- flick("pdoorc0", src)
- icon_state = "pdoor0"
- set_opacity(0)
- sleep(5)
- density = 0
- sleep(5)
- air_update_turf(1)
- update_freelook_sight()
+/obj/machinery/door/poddoor/do_animate(animation)
+ switch(animation)
+ if("opening")
+ flick("opening", src)
+ playsound(src, 'sound/machines/blastdoor.ogg', 30, 1)
+ if("closing")
+ flick("closing", src)
+ playsound(src, 'sound/machines/blastdoor.ogg', 30, 1)
- if(operating) //emag again
- operating = 0
- if(autoclose)
- spawn(150)
- autoclose()
- return 1
+/obj/machinery/door/poddoor/update_icon()
+ if(density)
+ icon_state = "closed"
+ else
+ icon_state = "open"
-/obj/machinery/door/poddoor/close()
- if(operating)
- return
- operating = 1
- flick("pdoorc1", src)
- icon_state = "pdoor1"
- set_opacity(initial(opacity))
- air_update_turf(1)
- update_freelook_sight()
- sleep(5)
- crush()
- density = 1
- sleep(5)
+/obj/machinery/door/poddoor/try_to_activate_door(mob/user)
+ return
- operating = 0
- return
+/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user)
+ if(!hasPower())
+ open()
/obj/machinery/door/poddoor/multi_tile // Whoever wrote the old code for multi-tile spesspod doors needs to burn in hell.
name = "large pod door"
layer = CLOSED_DOOR_LAYER
+ closingLayer = CLOSED_DOOR_LAYER
/obj/machinery/door/poddoor/multi_tile/four_tile_ver/
icon = 'icons/obj/doors/1x4blast_vert.dmi'
diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm
index 270d3a064e4..e09d40689e2 100644
--- a/code/game/machinery/doors/shutters.dm
+++ b/code/game/machinery/doors/shutters.dm
@@ -1,69 +1,14 @@
/obj/machinery/door/poddoor/shutters
+ gender = PLURAL
name = "shutters"
- desc = "Heavy duty metal shutters that opens mechanically."
- icon = 'icons/obj/doors/rapid_pdoor.dmi'
- icon_state = "shutter1"
+ desc = "Heavy duty metal shutters that open mechanically."
+ icon = 'icons/obj/doors/shutters.dmi'
+ layer = SHUTTER_LAYER
+ closingLayer = SHUTTER_LAYER
+ damage_deflection = 20
dir = EAST
-/obj/machinery/door/poddoor/shutters/New()
- ..()
- layer = 3.1
-
/obj/machinery/door/poddoor/shutters/preopen
- icon_state = "shutter0"
- density = 0
+ icon_state = "open"
+ density = FALSE
opacity = 0
-
-/obj/machinery/door/poddoor/shutters/attackby(obj/item/weapon/C, mob/user, params)
- add_fingerprint(user)
- if(!(iscrowbar(C) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1)))
- return
- if(density && (stat & NOPOWER) && !operating)
- operating = 1
- spawn(-1)
- flick("shutterc0", src)
- icon_state = "shutter0"
- sleep(15)
- density = 0
- set_opacity(0)
- operating = 0
- return
- return
-
-/obj/machinery/door/poddoor/shutters/open()
- if(operating || emagged) //doors can still open when emag-disabled
- return
- if(!ticker)
- return 0
- if(!operating) //in case of emag
- operating = 1
- flick("shutterc0", src)
- icon_state = "shutter0"
- sleep(10)
- density = 0
- set_opacity(0)
- air_update_turf(1)
- update_freelook_sight()
-
- if(operating) //emag again
- operating = 0
- if(autoclose)
- spawn(150)
- autoclose() //TODO: note to self: look into this ~Carn
- return 1
-
-/obj/machinery/door/poddoor/shutters/close()
- if(operating)
- return
- operating = 1
- flick("shutterc1", src)
- icon_state = "shutter1"
- density = 1
- if(visible)
- set_opacity(1)
- air_update_turf(1)
- update_freelook_sight()
-
- sleep(10)
- operating = 0
- return
\ No newline at end of file
diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm
index 76caba78cc6..c2330d3633a 100644
--- a/code/game/machinery/doors/unpowered.dm
+++ b/code/game/machinery/doors/unpowered.dm
@@ -1,7 +1,5 @@
/obj/machinery/door/unpowered
- autoclose = 0
explosion_block = 1
- var/locked = 0
/obj/machinery/door/unpowered/Bumped(atom/AM)
if(locked)
@@ -9,11 +7,13 @@
..()
/obj/machinery/door/unpowered/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade))
- return
if(locked)
return
- return ..()
+ else
+ return ..()
+
+/obj/machinery/door/unpowered/emag_act()
+ return
/obj/machinery/door/unpowered/shuttle
icon = 'icons/turf/shuttle.dmi'
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index b9910e00997..5490b529564 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -3,32 +3,58 @@
desc = "A strong door."
icon = 'icons/obj/doors/windoor.dmi'
icon_state = "left"
- visible = 0.0
+ layer = ABOVE_WINDOW_LAYER
+ closingLayer = ABOVE_WINDOW_LAYER
+ visible = 0
flags = ON_BORDER
opacity = 0
dir = EAST
- var/obj/item/weapon/airlock_electronics/electronics = null
+ max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file
+ integrity_failure = 0
+ armor = list(melee = 20, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100)
+ unacidable = 1
+ var/obj/item/weapon/airlock_electronics/electronics
var/base_state = "left"
- var/health = 150.0 //If you change this, consider changing ../door/window/brigdoor/ health at the bottom of this .dm file
+ var/reinf = 0
+ var/shards = 2
+ var/rods = 2
+ var/cable = 1
+ var/list/debris = list()
/obj/machinery/door/window/New()
..()
-
if(req_access && req_access.len)
icon_state = "[icon_state]"
base_state = icon_state
-
if(!color)
color = color_windows(src)
- return
+ for(var/i in 1 to shards)
+ debris += new /obj/item/weapon/shard(src)
+ if(rods)
+ debris += new /obj/item/stack/rods(src, rods)
+ if(cable)
+ debris += new /obj/item/stack/cable_coil(src, cable)
/obj/machinery/door/window/Destroy()
- density = 0
- if(health == 0)
+ density = FALSE
+ for(var/I in debris)
+ qdel(I)
+ if(obj_integrity == 0)
playsound(src, "shatter", 70, 1)
QDEL_NULL(electronics)
return ..()
+/obj/machinery/door/window/update_icon()
+ if(density)
+ icon_state = base_state
+ else
+ icon_state = "[base_state]open"
+
+/obj/machinery/door/window/examine(mob/user)
+ ..()
+ if(emagged)
+ to_chat(user, "Its access panel is smoking slightly.")
+
/obj/machinery/door/window/proc/open_and_close()
open()
if(check_access(null))
@@ -41,19 +67,18 @@
if(operating || !density)
return
if(!ismob(AM))
- if(istype(AM, /obj/mecha))
+ if(ismecha(AM))
var/obj/mecha/mecha = AM
if(mecha.occupant && allowed(mecha.occupant))
open_and_close()
else
- flick(text("[]deny", base_state), src)
+ do_animate("deny")
return
if(!ticker)
return
var/mob/living/M = AM
- if(!M.restrained() && M.mob_size > MOB_SIZE_SMALL)
+ if(!M.restrained() && M.mob_size > MOB_SIZE_SMALL && (!(isrobot(M) && M.stat)))
bumpopen(M)
- return
/obj/machinery/door/window/bumpopen(mob/user)
if(operating || !density)
@@ -65,8 +90,7 @@
if(allowed(user))
open_and_close()
else
- flick(text("[]deny", base_state), src)
- return
+ do_animate("deny")
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
@@ -84,7 +108,7 @@
//used in the AStar algorithm to determinate if the turf the door is on is passable
/obj/machinery/door/window/CanAStarPass(obj/item/weapon/card/id/ID, to_dir)
- return !density || (dir != to_dir) || (check_access(ID) && !(stat & NOPOWER))
+ return !density || (dir != to_dir) || (check_access(ID) && hasPower())
/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSGLASS))
@@ -97,41 +121,39 @@
/obj/machinery/door/window/open(forced=0)
if(operating) //doors can still open when emag-disabled
return 0
- if(!ticker)
- return 0
if(!forced)
- if(stat & NOPOWER)
+ if(!hasPower())
return 0
if(forced < 2)
if(emagged)
return 0
if(!operating) //in case of emag
operating = 1
- flick(text("[]opening", base_state), src)
+ do_animate("opening")
playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1)
- icon_state = text("[]open", base_state)
+ icon_state ="[base_state]open"
sleep(10)
- density = 0
+ density = FALSE
// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn
air_update_turf(1)
update_freelook_sight()
if(operating) //emag again
- operating = 0
+ operating = FALSE
return 1
/obj/machinery/door/window/close(forced=0)
if(operating)
return 0
if(!forced)
- if(stat & NOPOWER)
+ if(!hasPower())
return 0
if(forced < 2)
if(emagged)
return 0
operating = 1
- flick(text("[]closing", base_state), src)
+ do_animate("closing")
playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1)
icon_state = base_state
@@ -145,47 +167,30 @@
operating = 0
return 1
-/obj/machinery/door/window/take_damage(damage)
- health = max(0, health - damage)
- if(health <= 0)
- var/debris = list(
- new /obj/item/weapon/shard(loc),
- new /obj/item/weapon/shard(loc),
- new /obj/item/stack/rods(loc, 2),
- new /obj/item/stack/cable_coil(loc, 2)
- )
+/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
+ switch(damage_type)
+ if(BRUTE)
+ playsound(loc, 'sound/effects/glasshit.ogg', 90, 1)
+ if(BURN)
+ playsound(loc, 'sound/items/welder.ogg', 100, 1)
+
+
+/obj/machinery/door/window/deconstruct(disassembled = TRUE)
+ if(can_deconstruct && !disassembled)
for(var/obj/fragment in debris)
+ fragment.forceMove(get_turf(src))
transfer_fingerprints_to(fragment)
- density = 0
- qdel(src)
- return
+ debris -= fragment
+ qdel(src)
-/obj/machinery/door/window/bullet_act(obj/item/projectile/Proj)
- if(Proj.damage)
- if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
- take_damage(round(Proj.damage / 2))
+/obj/machinery/door/window/narsie_act()
+ color = NARSIE_WINDOW_COLOUR
+
+/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ if(exposed_temperature > T0C + (reinf ? 1600 : 800))
+ take_damage(round(exposed_volume / 200), BURN, 0, 0)
..()
-//When an object is thrown at the window
-/obj/machinery/door/window/hitby(atom/movable/AM)
- ..()
- var/tforce = 0
- if(ismob(AM))
- tforce = 40
- else if(isobj(AM))
- var/obj/O = AM
- tforce = O.throwforce
- playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1)
- take_damage(tforce)
-
-/obj/machinery/door/window/mech_melee_attack(obj/mecha/M)
- if(M.damtype == "brute")
- playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
- M.occupant_message("You hit [src].")
- visible_message("[src] has been hit by [M.name].")
- take_damage(M.force)
- return
-
/obj/machinery/door/window/attack_ai(mob/user)
return attack_hand(user)
@@ -193,56 +198,18 @@
if(user.can_advanced_admin_interact())
return attack_hand(user)
-/obj/machinery/door/window/proc/attack_generic(mob/user, damage = 0)
- if(operating)
- return
- user.changeNext_move(CLICK_CD_MELEE)
- user.do_attack_animation(src)
- playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
- user.visible_message("[user] smashes against the [name].", \
- "[user] smashes against the [name].")
- take_damage(damage)
-
-/obj/machinery/door/window/attack_alien(mob/living/user)
- if(islarva(user))
- return
- attack_generic(user, 25)
-
-/obj/machinery/door/window/attack_animal(mob/living/user)
- if(!isanimal(user))
- return
- var/mob/living/simple_animal/M = user
- if(M.melee_damage_upper > 0 && (M.melee_damage_type == BRUTE || M.melee_damage_type == BURN))
- attack_generic(M, M.melee_damage_upper)
-
-/obj/machinery/door/window/attack_slime(mob/living/carbon/slime/user)
- if(!user.is_adult)
- return
- attack_generic(user, 25)
-
/obj/machinery/door/window/attack_hand(mob/user)
return attackby(user, user)
/obj/machinery/door/window/emag_act(mob/user, obj/weapon)
if(!operating && density && !emagged)
+ emagged = TRUE
operating = 1
flick("[base_state]spark", src)
+ playsound(src, "sparks", 75, 1)
sleep(6)
- operating = 0
- desc += " Its access panel is smoking slightly."
- if(istype(weapon, /obj/item/weapon/melee/energy/blade))
- var/obj/item/weapon/melee/energy/blade/B
- var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
- spark_system.set_up(5, 0, loc)
- spark_system.start()
- playsound(loc, "sparks", 50, 1)
- playsound(loc, B.usesound, 50, 1)
- visible_message(" The glass door was sliced open by [user]!")
- open(2)
- emagged = 1
- return 1
- open()
- emagged = 1
+ operating = FALSE
+ open(2)
return 1
/obj/machinery/door/window/attackby(obj/item/weapon/I, mob/living/user, params)
@@ -253,117 +220,100 @@
add_fingerprint(user)
- //Ninja swords? You may pass.
- if(density && (istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade)))
- emag_act(user,I)
- return 1
-
- if(isscrewdriver(I))
- if(density || operating)
- to_chat(user, "You need to open the door to access the maintenance panel.")
+ if(can_deconstruct)
+ if(isscrewdriver(I))
+ if(density || operating)
+ to_chat(user, "You need to open the door to access the maintenance panel!")
+ return
+ playsound(src.loc, I.usesound, 50, 1)
+ panel_open = !panel_open
+ to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [src.name].")
return
- playsound(loc, I.usesound, 50, 1)
- panel_open = !panel_open
- to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [name].")
- return
- if(iscrowbar(I))
- if(panel_open && !density && !operating)
- playsound(loc, I.usesound, 100, 1)
- user.visible_message("[user] removes the electronics from the [name].", \
- "You start to remove electronics from the [name].")
- if(do_after(user, 40 * I.toolspeed, target = src))
- if(panel_open && !density && !operating && loc)
- var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc)
- switch(base_state)
- if("left")
- WA.facing = "l"
- if("right")
- WA.facing = "r"
- if("leftsecure")
- WA.facing = "l"
- WA.secure = 1
- if("rightsecure")
- WA.facing = "r"
- WA.secure = 1
- WA.anchored = 1
- WA.state= "02"
- WA.dir = dir
- WA.ini_dir = dir
- WA.update_icon()
- WA.created_name = name
+ if(iscrowbar(I))
+ if(panel_open && !density && !operating)
+ playsound(loc, I.usesound, 100, 1)
+ user.visible_message("[user] removes the electronics from the [name].", \
+ "You start to remove electronics from the [name]...")
+ if(do_after(user, 40 * I.toolspeed, target = src))
+ if(panel_open && !density && !operating && loc)
+ var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc)
+ switch(base_state)
+ if("left")
+ WA.facing = "l"
+ if("right")
+ WA.facing = "r"
+ if("leftsecure")
+ WA.facing = "l"
+ WA.secure = TRUE
+ if("rightsecure")
+ WA.facing = "r"
+ WA.secure = TRUE
+ WA.anchored = TRUE
+ WA.state= "02"
+ WA.setDir(dir)
+ WA.ini_dir = dir
+ WA.update_icon()
+ WA.created_name = name
+
+ if(emagged)
+ to_chat(user, "You discard the damaged electronics.")
+ qdel(src)
+ return
+
+ to_chat(user, "You remove the airlock electronics.")
+
+ var/obj/item/weapon/airlock_electronics/ae
+ if(!electronics)
+ ae = new/obj/item/weapon/airlock_electronics(loc)
+ if(!req_access)
+ check_access()
+ if(req_access.len)
+ ae.conf_access = req_access
+ else if(req_one_access.len)
+ ae.conf_access = req_one_access
+ ae.one_access = 1
+ else
+ ae = electronics
+ electronics = null
+ ae.forceMove(loc)
- if(emagged)
- to_chat(user, "You discard the damaged electronics.")
qdel(src)
- return
+ return
+ return ..()
- to_chat(user, "You removed the airlock electronics!")
-
- var/obj/item/weapon/airlock_electronics/ae
- if(!electronics)
- ae = new/obj/item/weapon/airlock_electronics(loc)
- if(!req_access)
- check_access()
- if(req_access.len)
- ae.conf_access = req_access
- else if(req_one_access.len)
- ae.conf_access = req_one_access
- ae.one_access = 1
- else
- ae = electronics
- electronics = null
- ae.loc = loc
-
- qdel(src)
- return
-
-
- //If windoor is unpowered, crowbar, fireaxe and armblade can force it.
- if(iscrowbar(I) || istype(I, /obj/item/weapon/twohanded/fireaxe))
- if(stat & NOPOWER)
- if(density)
- open(2)
- else
- close(2)
- return
-
- //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(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
- playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
- visible_message("\The [src] has been hit by [user] with [I].")
- if(I.damtype == BURN || I.damtype == BRUTE)
- take_damage(aforce)
- return
-
- if(!requiresID())
- //don't care who they are or what they have, act as if they're NOTHING
- user = null
-
- if(allowed(user) || user.can_advanced_admin_interact())
+/obj/machinery/door/window/try_to_crowbar(obj/item/I, mob/user)
+ if(!hasPower())
if(density)
- open()
+ open(2)
else
- close()
+ close(2)
+ else
+ to_chat(user, "The door's motors resist your efforts to force it!")
- else if(density)
- flick(text("[]deny", base_state), src)
-
- return
+/obj/machinery/door/window/do_animate(animation)
+ switch(animation)
+ if("opening")
+ flick("[base_state]opening", src)
+ if("closing")
+ flick("[base_state]closing", src)
+ if("deny")
+ flick("[base_state]deny", src)
/obj/machinery/door/window/brigdoor
name = "secure door"
- icon = 'icons/obj/doors/windoor.dmi'
icon_state = "leftsecure"
base_state = "leftsecure"
- health = 300.0 //Stronger doors for prison (regular window door health is 200)
+ max_integrity = 300 //Stronger doors for prison (regular window door health is 200)
+ reinf = 1
+ explosion_block = 1
var/id = null
+/obj/machinery/door/window/brigdoor/security/cell
+ name = "cell door"
+ desc = "For keeping in criminal scum."
+ req_access = list(access_brig)
+
/obj/machinery/door/window/northleft
dir = NORTH
@@ -427,3 +377,35 @@
dir = SOUTH
icon_state = "rightsecure"
base_state = "rightsecure"
+
+/obj/machinery/door/window/brigdoor/security/cell/northleft
+ dir = NORTH
+
+/obj/machinery/door/window/brigdoor/security/cell/eastleft
+ dir = EAST
+
+/obj/machinery/door/window/brigdoor/security/cell/westleft
+ dir = WEST
+
+/obj/machinery/door/window/brigdoor/security/cell/southleft
+ dir = SOUTH
+
+/obj/machinery/door/window/brigdoor/security/cell/northright
+ dir = NORTH
+ icon_state = "rightsecure"
+ base_state = "rightsecure"
+
+/obj/machinery/door/window/brigdoor/security/cell/eastright
+ dir = EAST
+ icon_state = "rightsecure"
+ base_state = "rightsecure"
+
+/obj/machinery/door/window/brigdoor/security/cell/westright
+ dir = WEST
+ icon_state = "rightsecure"
+ base_state = "rightsecure"
+
+/obj/machinery/door/window/brigdoor/security/cell/southright
+ dir = SOUTH
+ icon_state = "rightsecure"
+ base_state = "rightsecure"
diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm
index 99b5189e206..27ee0197514 100644
--- a/code/game/machinery/embedded_controller/airlock_controllers.dm
+++ b/code/game/machinery/embedded_controller/airlock_controllers.dm
@@ -21,7 +21,7 @@
name = "Advanced Airlock Controller"
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290)
ui.open()
@@ -77,7 +77,7 @@
tag_secure = 1
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290)
ui.open()
@@ -140,7 +140,7 @@
icon_state = "access_control_off"
/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220)
ui.open()
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 2c48f91b422..523a11884a9 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -12,6 +12,7 @@ FIRE ALARM
var/timing = 0.0
var/lockdownbyai = 0
anchored = 1.0
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 100)
use_power = 1
idle_power_usage = 2
active_power_usage = 6
@@ -168,7 +169,7 @@ FIRE ALARM
ui_interact(user)
/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "firealarm.tmpl", name, 400, 400, state = state)
ui.open()
diff --git a/code/game/machinery/guestpass.dm b/code/game/machinery/guestpass.dm
index bc70868cf8f..8e043af82ad 100644
--- a/code/game/machinery/guestpass.dm
+++ b/code/game/machinery/guestpass.dm
@@ -19,9 +19,9 @@
/obj/item/weapon/card/id/guest/examine(mob/user)
..(user)
if(world.time < expiration_time)
- to_chat(user, "This pass expires at [worldtime2text(expiration_time)].")
+ to_chat(user, "This pass expires at [station_time_timestamp("hh:mm:ss", expiration_time)].")
else
- to_chat(user, "It expired at [worldtime2text(expiration_time)].")
+ to_chat(user, "It expired at [station_time_timestamp("hh:mm:ss", expiration_time)].")
to_chat(user, "It grants access to following areas:")
for(var/A in temp_access)
to_chat(user, "[get_access_desc(A)].")
@@ -166,13 +166,13 @@
if("issue")
if(giver)
var/number = add_zero("[rand(0,9999)]", 4)
- var/entry = "\[[worldtime2text()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Grants access to following areas: "
+ var/entry = "\[[station_time()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Grants access to following areas: "
for(var/i=1 to accesses.len)
var/A = accesses[i]
if(A)
var/area = get_access_desc(A)
entry += "[i > 1 ? ", [area]" : "[area]"]"
- entry += ". Expires at [worldtime2text(world.time + duration*10*60)]."
+ entry += ". Expires at [station_time(world.time + duration*10*60)]."
internal_log.Add(entry)
var/obj/item/weapon/card/id/guest/pass = new(src.loc)
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 811eceb8e23..41d81a9aef0 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -36,7 +36,7 @@ var/list/holopads = list()
icon_state = "holopad0"
layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them.
-
+ armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/mob/living/silicon/ai/master//Which AI, if any, is controlling the object? Only one AI may control a hologram at any time.
var/last_request = 0 //to prevent request spam. ~Carn
var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating.
diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm
index 44a5cc3eee4..ba5867eb260 100755
--- a/code/game/machinery/igniter.dm
+++ b/code/game/machinery/igniter.dm
@@ -3,6 +3,7 @@
desc = "It's useful for igniting plasma."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "igniter1"
+ armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0)
var/id = null
var/on = 1.0
anchored = 1.0
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 0497ac716f0..09eed9d2ac8 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -149,6 +149,8 @@ Class Procs:
fast_processing -= src
/obj/machinery/New() //new
+ if(!armor)
+ armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0)
machines += src
..()
@@ -170,10 +172,9 @@ Class Procs:
return PROCESS_KILL
/obj/machinery/emp_act(severity)
- if(use_power && stat == 0)
+ if(use_power && !stat)
use_power(7500/severity)
-
- new/obj/effect/temp_visual/emp(loc)
+ new /obj/effect/temp_visual/emp(loc)
..()
/obj/machinery/ex_act(severity)
@@ -414,12 +415,18 @@ Class Procs:
/obj/machinery/proc/spawn_frame(disassembled)
var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc)
+ . = M
+ M.anchored = anchored
if(!disassembled)
M.obj_integrity = M.max_integrity * 0.5 //the frame is already half broken
transfer_fingerprints_to(M)
M.state = 2
M.icon_state = "box_1"
+/obj/machinery/obj_break(damage_flag)
+ if(can_deconstruct)
+ stat |= BROKEN
+
/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/icon_state_open, var/icon_state_closed, var/obj/item/weapon/screwdriver/S)
if(istype(S))
playsound(loc, S.usesound, 50, 1)
@@ -495,7 +502,20 @@ Class Procs:
to_chat(user, "[bicon(C)] [C.name]")
/obj/machinery/examine(mob/user)
- ..(user)
+ ..()
+ if(stat & BROKEN)
+ to_chat(user, "It looks broken and non-functional.")
+ if(!(resistance_flags & INDESTRUCTIBLE))
+ if(burn_state == ON_FIRE)
+ to_chat(user, "It's on fire!")
+ var/healthpercent = (obj_integrity/max_integrity) * 100
+ switch(healthpercent)
+ if(50 to 99)
+ to_chat(user, "It looks slightly damaged.")
+ if(25 to 50)
+ to_chat(user, "It appears heavily damaged.")
+ if(0 to 25)
+ to_chat(user, "It's falling apart!")
if(user.research_scanner && component_parts)
display_parts(user)
@@ -584,6 +604,6 @@ Class Procs:
if(prob(85) && explosive)
explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0)
else if(prob(50))
- emp_act(2)
+ emp_act(EMP_LIGHT)
else
- ex_act(2)
+ ex_act(EXPLODE_HEAVY)
diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm
index 16f7c047dd2..e8f5b743dfd 100644
--- a/code/game/machinery/navbeacon.dm
+++ b/code/game/machinery/navbeacon.dm
@@ -10,7 +10,7 @@
level = 1 // underfloor
layer = 2.5
anchored = 1
-
+ armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0)
var/open = 0 // true if cover is open
var/locked = 1 // true if controls are locked
var/location = "" // location response text
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index a67580d0ebb..36b1ca0d837 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -78,6 +78,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
desc = "A standard Nanotrasen-licensed newsfeed handler for use in commercial space stations. All the news you absolutely have no use for, in one place!"
icon = 'icons/obj/terminals.dmi'
icon_state = "newscaster_normal"
+ armor = list(melee = 50, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
var/screen = NEWSCASTER_MAIN
var/paper_remaining = 15
var/securityCaster = 0
@@ -185,7 +186,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
user.set_machine(src)
if(can_scan(user))
scan_user(user)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "newscaster.tmpl", name, 400, 600)
ui.open()
@@ -565,7 +566,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
else if(href_list["jobs"])
screen = NEWSCASTER_JOBS
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
/obj/machinery/newscaster/attackby(obj/item/I, mob/living/user, params)
diff --git a/code/game/machinery/poolcontroller.dm b/code/game/machinery/poolcontroller.dm
index c169f3c0a59..661a814c6fb 100644
--- a/code/game/machinery/poolcontroller.dm
+++ b/code/game/machinery/poolcontroller.dm
@@ -100,7 +100,10 @@
if(drownee.losebreath > 20) //You've probably got bigger problems than drowning at this point, so we won't add to it until you get that under control.
return
- add_logs(src, drownee, "drowned", null, null, 0) //log it to their VV, but don't spam the admins' chats with the logs
+ if(isLivingSSD(drownee))
+ add_logs(src, drownee, "drowned", null, null, 0, 1) // Notify admins, since the person is SSD
+ else
+ add_logs(src, drownee, "drowned", null, null, 0, 0) // Do not notify admins.
if(drownee.stat) //Mob is in critical.
drownee.AdjustLoseBreath(3, bound_lower = 0, bound_upper = 20)
drownee.visible_message("\The [drownee] appears to be drowning!","You're quickly drowning!") //inform them that they are fucked.
@@ -126,7 +129,7 @@
/obj/machinery/poolcontroller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "poolcontroller.tmpl", "Pool Controller Interface", 520, 410)
ui.open()
diff --git a/code/game/machinery/portable_tag_turret.dm b/code/game/machinery/portable_tag_turret.dm
index 1944da8f071..610f7d22f9c 100644
--- a/code/game/machinery/portable_tag_turret.dm
+++ b/code/game/machinery/portable_tag_turret.dm
@@ -43,7 +43,7 @@
iconholder = 1
/obj/machinery/porta_turret/tag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
ui.open()
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 98013119723..73431fc0c94 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -8,13 +8,12 @@
icon = 'icons/obj/turrets.dmi'
icon_state = "turretCover"
anchored = 1
-
density = 0
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
power_channel = EQUIP //drains power from the EQUIPMENT channel
-
+ armor = list(melee = 50, bullet = 30, laser = 30, energy = 30, bomb = 30, bio = 0, rad = 0)
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
@@ -213,7 +212,7 @@ var/list/turret_icons
ui_interact(user)
/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 320)
ui.open()
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 21e91f3759b..d3799a5a20b 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -38,6 +38,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
anchored = 1
icon = 'icons/obj/terminals.dmi'
icon_state = "req_comp0"
+ armor = list(melee = 70, bullet = 30, laser = 30, energy = 30, bomb = 0, bio = 0, rad = 0)
var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department
var/list/message_log = list() //List of all messages
var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines.
@@ -130,7 +131,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
ui_interact(user)
/obj/machinery/requests_console/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "request_console.tmpl", "[department] Request Console", 520, 410)
ui.open()
@@ -220,7 +221,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
radiochannel = "AI Private"
if(recipient == "Cargo Bay")
radiochannel = "Supply"
- message_log += "Message sent to [recipient] at [worldtime2text()] [message]"
+ message_log += "Message sent to [recipient] at [station_time_timestamp()] [message]"
Radio.autosay("Alert; a new requests console message received for [recipient] from [department]", null, "[radiochannel]")
else
audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4)
@@ -264,7 +265,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
if(href_list["toggleSilent"])
silent = !silent
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
//err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messanging on that console (EXTREME priority...), but the code for that was removed.
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index ca6484d8dd4..822cc6d9978 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -22,7 +22,7 @@
ui_interact(user)
/obj/machinery/slot_machine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "slotmachine.tmpl", name, 350, 200)
ui.open()
@@ -113,5 +113,5 @@
T.purpose = "Slot Winnings"
T.amount = "[amt]"
T.date = current_date_string
- T.time = worldtime2text()
+ T.time = station_time_timestamp()
account.transaction_log.Add(T)
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 600dc3704fa..5e09185c002 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -5,6 +5,7 @@
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."
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 100)
var/obj/item/weapon/stock_parts/cell/cell
var/on = 0
var/open = 0
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 614f4d8f53f..414ec8d2e62 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -98,7 +98,7 @@
message2 = "Error!"
else
message1 = "TIME"
- message2 = worldtime2text()
+ message2 = station_time_timestamp("hh:mm")
update_display(message1, message2, use_warn)
return 1
if(STATUS_DISPLAY_MESSAGE) //custom messages
@@ -126,7 +126,7 @@
return 1
if(STATUS_DISPLAY_TIME)
message1 = "TIME"
- message2 = worldtime2text()
+ message2 = station_time_timestamp("hh:mm")
update_display(message1, message2)
return 1
return 0
diff --git a/code/game/machinery/supply_display.dm b/code/game/machinery/supply_display.dm
index 9c945fff989..863d76ef7b6 100644
--- a/code/game/machinery/supply_display.dm
+++ b/code/game/machinery/supply_display.dm
@@ -10,7 +10,7 @@
message2 = "Docked"
else
message1 = "TIME"
- message2 = worldtime2text()
+ message2 = station_time_timestamp("hh:mm")
else
message1 = "CARGO"
message2 = shuttle_master.supply.getTimerStr()
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index 8479e4e051b..843b889478d 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -70,7 +70,7 @@
return
// Set up the Nano UI
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "teleporter_console.tmpl", "Teleporter Console UI", 400, 400)
ui.open()
@@ -99,16 +99,16 @@
if(href_list["eject"])
eject()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(!check_hub_connection())
to_chat(usr, "Error: Unable to detect hub.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(calibrating)
to_chat(usr, "Error: Calibration in progress. Stand by.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(href_list["regimeset"])
@@ -116,32 +116,32 @@
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = 0
reset_regime()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(href_list["settarget"])
power_station.engaged = 0
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = 0
set_target(usr)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(href_list["lock"])
power_station.engaged = 0
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = 0
target = get_turf(locked.locked_location)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(href_list["calibrate"])
if(!target)
to_chat(usr, "Error: No target set to calibrate to.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3)
to_chat(usr, "Hub is already calibrated.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
src.visible_message("Processing hub calibration to target...")
calibrating = 1
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration
calibrating = 0
if(check_hub_connection())
@@ -149,9 +149,9 @@
src.visible_message("Calibration complete.")
else
src.visible_message("Error: Unable to detect hub.")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/machinery/computer/teleporter/proc/check_hub_connection()
if(!power_station)
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index 878ea0737aa..0b59a2efa16 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -244,24 +244,29 @@
/obj/machinery/transformer/xray/proc/scan(var/obj/item/I)
- var/badcount = 0
- for(var/obj/item/weapon/gun/G in src.loc)
- badcount++
- for(var/obj/item/device/transfer_valve/B in src.loc)
- badcount++
- for(var/obj/item/weapon/kitchen/knife/K in src.loc)
- badcount++
- for(var/obj/item/weapon/grenade/plastic/c4/KK in src.loc)
- badcount++
- for(var/obj/item/weapon/melee/ML in src.loc)
- badcount++
- if(badcount)
+ if(scan_rec(I))
playsound(src.loc, 'sound/effects/alert.ogg', 50, 0)
flick("separator-AO0",src)
else
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
sleep(30)
+/obj/machinery/transformer/xray/proc/scan_rec(var/obj/item/I)
+ if(istype(I, /obj/item/weapon/gun))
+ return TRUE
+ if(istype(I, /obj/item/device/transfer_valve))
+ return TRUE
+ if(istype(I, /obj/item/weapon/kitchen/knife))
+ return TRUE
+ if(istype(I, /obj/item/weapon/grenade/plastic/c4))
+ return TRUE
+ if(istype(I, /obj/item/weapon/melee))
+ return TRUE
+ for(var/obj/item/C in I.contents)
+ if(scan_rec(C))
+ return TRUE
+ return FALSE
+
/obj/machinery/transformer/equipper
name = "Auto-equipper 9000"
desc = "Either in employ of people who cannot dress themselves, or Wallace and Gromit."
diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm
index ad5aeb5fc61..4bcf869ba64 100644
--- a/code/game/machinery/turret_control.dm
+++ b/code/game/machinery/turret_control.dm
@@ -25,7 +25,7 @@
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
-
+
var/syndicate = 0
var/faction = "" // Turret controls can only access turrets that are in the same faction
@@ -39,21 +39,21 @@
enabled = 1
lethal = 1
icon_state = "control_kill"
-
+
/obj/machinery/turretid/syndicate
enabled = 1
lethal = 1
icon_state = "control_kill"
-
+
lethal = 1
check_arrest = 0
check_records = 0
check_weapons = 0
check_access = 0
- check_anomalies = 1
+ check_anomalies = 1
check_synth = 1
ailock = 1
-
+
syndicate = 1
faction = "syndicate"
@@ -139,7 +139,7 @@
ui_interact(user)
/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
ui.open()
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 917cd73c83d..07365fb0c9a 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -42,7 +42,7 @@
layer = 2.9
anchored = 1
density = 1
-
+ armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
var/icon_vend //Icon_state when vending
var/icon_deny //Icon_state when denying access
@@ -97,6 +97,9 @@
var/obj/item/weapon/coin/coin
var/datum/wires/vending/wires = null
+ var/item_slot = FALSE
+ var/obj/item/inserted_item = null
+
/obj/machinery/vending/New()
..()
wires = new(src)
@@ -119,6 +122,10 @@
return
+/obj/machinery/vending/Destroy()
+ eject_item()
+ return ..()
+
/**
* Build src.produdct_records from the products lists
*
@@ -227,7 +234,7 @@
vend(currently_vending, usr)
return
else if(handled)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return // don't smack that machine with your 2 thalers
if(default_unfasten_wrench(user, I, time = 60))
@@ -240,7 +247,7 @@
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
+ SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI
return
if(panel_open)
@@ -262,7 +269,7 @@
coin = I
categories |= CAT_COIN
to_chat(user, "You insert the [I] into the [src]")
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
else if(istype(I, refill_canister) && refill_canister != null)
if(stat & (BROKEN|NOPOWER))
@@ -281,9 +288,54 @@
return;
else
to_chat(user, "You should probably unscrew the service panel first.")
+ else if(item_slot_check(user, I))
+ insert_item(user, I)
+ return
else
..()
+//Override this proc to do per-machine checks on the inserted item, but remember to call the parent to handle these generic checks before your logic!
+/obj/machinery/vending/proc/item_slot_check(mob/user, obj/item/I)
+ if(!item_slot)
+ return FALSE
+ if(alert(user, "Do you want to attempt to insert [I]?", "","Yes", "No") == "No")
+ return FALSE
+ if(inserted_item)
+ to_chat(user, "There is something already inserted!")
+ return FALSE
+ return TRUE
+
+/* Example override for item_slot_check proc:
+/obj/machinery/vending/example/item_slot_check(mob/user, obj/item/I)
+ if(!..())
+ return FALSE
+ if(!istype(I, /obj/item/toy))
+ to_chat(user, "[I] isn't compatible with this machine's slot.")
+ return FALSE
+ return TRUE
+*/
+
+/obj/machinery/vending/proc/insert_item(mob/user, obj/item/I)
+ if(!item_slot || inserted_item)
+ return
+ if(!user.canUnEquip(I))
+ to_chat(user, "[I] is stuck to your hand, you can't seem to put it down!")
+ return
+
+ user.unEquip(I)
+ inserted_item = I
+ I.forceMove(src)
+
+ to_chat(user, "You insert [I] into [src].")
+ SSnanoui.update_uis(src)
+
+/obj/machinery/vending/proc/eject_item()
+ if(!item_slot || !inserted_item)
+ return
+ inserted_item.forceMove(get_turf(src))
+ inserted_item = null
+ SSnanoui.update_uis(src)
+
/obj/machinery/vending/emag_act(user as mob)
emagged = 1
to_chat(user, "You short out the product lock on [src]")
@@ -378,7 +430,7 @@
T.amount = "[currently_vending.price]"
T.source_terminal = src.name
T.date = current_date_string
- T.time = worldtime2text()
+ T.time = station_time_timestamp()
vendor_account.transaction_log.Add(T)
/obj/machinery/vending/attack_ai(mob/user)
@@ -406,7 +458,7 @@
/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "vending_machine.tmpl", src.name, 440, 600)
ui.open()
@@ -439,12 +491,21 @@
data["products"] = listed_products
- if(src.coin)
- data["coin"] = src.coin.name
+ if(coin)
+ data["coin"] = coin.name
- if(src.panel_open)
+ if(item_slot)
+ data["item_slot"] = 1
+ if(inserted_item)
+ data["inserted_item"] = inserted_item
+ else
+ data["inserted_item"] = null
+ else
+ data["item_slot"] = 0
+
+ if(panel_open)
data["panel"] = 1
- data["speaker"] = src.shut_up ? 0 : 1
+ data["speaker"] = shut_up ? 0 : 1
else
data["panel"] = 0
return data
@@ -463,6 +524,9 @@
to_chat(usr, "You remove [coin] from [src].")
categories &= ~CAT_COIN
+ if(href_list["remove_item"])
+ eject_item()
+
if(href_list["pay"])
if(currently_vending && vendor_account && !vendor_account.suspended)
var/paid = 0
@@ -481,7 +545,7 @@
vend(currently_vending, usr)
return
else if(handled)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return // don't smack that machine with your 2 credits
if((href_list["vend"]) && vend_ready && !currently_vending)
@@ -520,7 +584,7 @@
shut_up = !src.shut_up
add_fingerprint(usr)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user)
if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
@@ -535,7 +599,7 @@
vend_ready = 0 //One thing at a time!!
status_message = "Vending..."
status_error = 0
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
if(R.category & CAT_COIN)
if(!coin)
@@ -562,13 +626,31 @@
use_power(vend_power_usage) //actuators and stuff
if(icon_vend) //Show the vending animation if needed
flick(icon_vend,src)
- spawn(src.vend_delay)
- new R.product_path(get_turf(src))
+ spawn(vend_delay)
+ do_vend(R)
status_message = ""
status_error = 0
vend_ready = 1
currently_vending = null
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
+
+//override this proc to add handling for what to do with the vended product when you have a inserted item and remember to include a parent call for this generic handling
+/obj/machinery/vending/proc/do_vend(datum/data/vending_product/R)
+ if(!item_slot || !inserted_item)
+ new R.product_path(get_turf(src))
+ return TRUE
+ return FALSE
+
+/* Example override for do_vend proc:
+/obj/machinery/vending/example/do_vend(datum/data/vending_product/R)
+ if(..())
+ return
+ var/obj/item/vended = new R.product_path()
+ if(inserted_item.force == initial(inserted_item.force)
+ inserted_item.force += vended.force
+ inserted_item.damtype = vended.damtype
+ qdel(vended)
+*/
/obj/machinery/vending/proc/stock(var/datum/data/vending_product/R, var/mob/user)
if(panel_open)
@@ -701,6 +783,7 @@
/obj/item/weapon/reagent_containers/food/drinks/bottle/vermouth = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/rum = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/wine = 5,
+ /obj/item/weapon/reagent_containers/food/drinks/bag/goonbag = 3,
/obj/item/weapon/reagent_containers/food/drinks/bottle/cognac = 5,
/obj/item/weapon/reagent_containers/food/drinks/bottle/kahlua = 5,
/obj/item/weapon/reagent_containers/food/drinks/cans/beer = 6,
@@ -721,11 +804,13 @@
product_slogans = "I hope nobody asks me for a bloody cup o' tea...;Alcohol is humanity's friend. Would you abandon a friend?;Quite delighted to serve you!;Is nobody thirsty on this station?"
product_ads = "Drink up!;Booze is good for you!;Alcohol is humanity's best friend.;Quite delighted to serve you!;Care for a nice, cold beer?;Nothing cures you like booze!;Have a sip!;Have a drink!;Have a beer!;Beer is good for you!;Only the finest alcohol!;Best quality booze since 2053!;Award-winning wine!;Maximum alcohol!;Man loves beer.;A toast for progress!"
refill_canister = /obj/item/weapon/vending_refill/boozeomat
+
/obj/machinery/vending/assist
products = list( /obj/item/device/assembly/prox_sensor = 5,/obj/item/device/assembly/igniter = 3,/obj/item/device/assembly/signaler = 4,
/obj/item/weapon/wirecutters = 1, /obj/item/weapon/cartridge/signal = 4)
contraband = list(/obj/item/device/flashlight = 5,/obj/item/device/assembly/timer = 2, /obj/item/device/assembly/voice = 2, /obj/item/device/assembly/health = 2)
product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!"
+ armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0)
/obj/machinery/vending/boozeomat/New()
..()
@@ -737,18 +822,22 @@
component_parts += new /obj/item/weapon/vending_refill/boozeomat(0)
component_parts += new /obj/item/weapon/vending_refill/boozeomat(0)
+
/obj/machinery/vending/coffee
name = "\improper Hot Drinks machine"
desc = "A vending machine which dispenses hot drinks."
product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies"
icon_state = "coffee"
icon_vend = "coffee-vend"
+ item_slot = TRUE
vend_delay = 34
products = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25,/obj/item/weapon/reagent_containers/food/drinks/tea = 25,/obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25,
- /obj/item/weapon/reagent_containers/food/drinks/chocolate = 10, /obj/item/weapon/reagent_containers/food/drinks/chicken_soup = 10,/obj/item/weapon/reagent_containers/food/drinks/weightloss = 10)
+ /obj/item/weapon/reagent_containers/food/drinks/chocolate = 10, /obj/item/weapon/reagent_containers/food/drinks/chicken_soup = 10,/obj/item/weapon/reagent_containers/food/drinks/weightloss = 10,
+ /obj/item/weapon/reagent_containers/food/drinks/mug = 15)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/ice = 10)
+ premium = list(/obj/item/weapon/reagent_containers/food/drinks/mug/novelty = 5)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25, /obj/item/weapon/reagent_containers/food/drinks/tea = 25, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25, /obj/item/weapon/reagent_containers/food/drinks/chocolate = 25,
- /obj/item/weapon/reagent_containers/food/drinks/chicken_soup = 30,/obj/item/weapon/reagent_containers/food/drinks/weightloss = 50)
+ /obj/item/weapon/reagent_containers/food/drinks/chicken_soup = 30,/obj/item/weapon/reagent_containers/food/drinks/weightloss = 50, /obj/item/weapon/reagent_containers/food/drinks/mug = 50)
refill_canister = /obj/item/weapon/vending_refill/coffee
/obj/machinery/vending/coffee/New()
@@ -761,6 +850,33 @@
component_parts += new /obj/item/weapon/vending_refill/coffee(0)
component_parts += new /obj/item/weapon/vending_refill/coffee(0)
+/obj/machinery/vending/coffee/item_slot_check(mob/user, obj/item/I)
+ if(!..())
+ return FALSE
+ if(!(istype(I, /obj/item/weapon/reagent_containers/glass) || istype(I, /obj/item/weapon/reagent_containers/food/drinks)))
+ to_chat(user, "[I] is not compatible with this machine.")
+ return FALSE
+ if(!I.is_open_container())
+ to_chat(user, "Time of death: [worldtime2text(timeofdeath)]
"
+ scan_data += "Time of death: [station_time_timestamp("hh:mm:ss", timeofdeath)]
"
var/n = 1
for(var/wdata_idx in wdata)
@@ -146,7 +146,7 @@
if(damaging_weapon)
scan_data += "Severity: [damage_desc] "
scan_data += "Hits by weapon: [total_hits] "
- scan_data += "Approximate time of wound infliction: [worldtime2text(age)] "
+ scan_data += "Approximate time of wound infliction: [station_time(age)] "
scan_data += "Affected limbs: [D.organ_names] "
scan_data += "Possible weapons: "
for(var/weapon_name in weapon_chances)
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index e8d023ff101..d8baaf0ea2c 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -99,7 +99,7 @@
/obj/item/device/radio/electropack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio_electro.tmpl", "[name]", 400, 500)
ui.open()
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 67009e3366d..e353834ca22 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -107,7 +107,7 @@ var/global/list/default_medbay_channels = list(
return ui_interact(user)
/obj/item/device/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 550)
ui.open()
@@ -793,7 +793,7 @@ var/global/list/default_medbay_channels = list(
. = ..()
/obj/item/device/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 430, 500)
ui.open()
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index dfec1d1ba09..3aba9d2d95b 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -160,7 +160,7 @@ REAGENT SCANNER
user.show_message("\t Damage Specifics: [OX] - [TX] - [BU] - [BR]")
user.show_message("Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1)
if(M.timeofdeath && (M.stat == DEAD || (M.status_flags & FAKEDEATH)))
- user.show_message("Time of Death: [worldtime2text(M.timeofdeath)]")
+ user.show_message("Time of Death: [station_time_timestamp("hh:mm:ss", M.timeofdeath)]")
if(istype(M, /mob/living/carbon/human) && mode == 1)
var/mob/living/carbon/human/H = M
var/list/damaged = H.get_damaged_organs(1,1)
@@ -432,7 +432,7 @@ REAGENT SCANNER
sleep(50)
var/obj/item/weapon/paper/P = new(get_turf(src))
- P.name = "Mass Spectrometer Scanner Report: [worldtime2text()]"
+ P.name = "Mass Spectrometer Scanner Report: [station_time_timestamp()]"
P.info = "Mass Spectrometer Data Analysis:
Trace chemicals detected: [datatoprint]
"
if(ismob(loc))
@@ -502,7 +502,7 @@ REAGENT SCANNER
sleep(50)
var/obj/item/weapon/paper/P = new(get_turf(src))
- P.name = "Reagent Scanner Report: [worldtime2text()]"
+ P.name = "Reagent Scanner Report: [station_time_timestamp()]"
P.info = "Reagent Scanner Data Analysis:
Chemical agents detected: [datatoprint]
"
if(ismob(loc))
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 9c293bd6073..c27c6a3f435 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -46,7 +46,7 @@
w_class = I.w_class
update_icon()
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
//TODO: Have this take an assemblyholder
else if(isassembly(I))
var/obj/item/device/assembly/A = I
@@ -67,7 +67,7 @@
msg_admin_attack("[key_name_admin(user)]attached [A] to a transfer valve.")
log_game("[key_name_admin(user)] attached [A] to a transfer valve.")
attacher = user
- nanomanager.update_uis(src) // update all UIs attached to src
+ SSnanoui.update_uis(src) // update all UIs attached to src
/obj/item/device/transfer_valve/HasProximity(atom/movable/AM)
@@ -90,7 +90,7 @@
/obj/item/device/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm
index 0c21ef212c9..d4906aa17db 100644
--- a/code/game/objects/items/devices/uplinks.dm
+++ b/code/game/objects/items/devices/uplinks.dm
@@ -129,7 +129,7 @@ var/list/world_uplinks = list()
if(!UI)
return
UI.buy(src,usr)
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/* var/list/L = UI.spawn_item(get_turf(usr),src)
if(ishuman(usr))
@@ -190,7 +190,7 @@ var/list/world_uplinks = list()
/obj/item/device/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state)
var/title = "Remote Uplink"
// update the ui if it exists, returns null if no ui is passed/found
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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
@@ -224,7 +224,7 @@ var/list/world_uplinks = list()
if(!( istype(usr, /mob/living/carbon/human)))
return 1
var/mob/user = usr
- var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, "main")
+ var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
if((usr.contents.Find(src.loc) || (in_range(src.loc, usr) && istype(src.loc.loc, /turf))))
usr.set_machine(src)
if(..(href, href_list))
@@ -246,7 +246,7 @@ var/list/world_uplinks = list()
show_descriptions = !show_descriptions
update_nano_data()
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
/obj/item/device/uplink/hidden/proc/update_nano_data(var/id)
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 4c9bbb38747..1a4de35470c 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -34,6 +34,7 @@
R.notify_ai(2)
R.uneq_all()
+ R.sight_mode = null
R.hands.icon_state = "nomod"
R.icon_state = "robot"
R.module.remove_subsystems_and_actions(R)
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 2a78511d2c7..a30c3e1b716 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -271,7 +271,6 @@
created_window = /obj/structure/window/plasmabasic
full_window = /obj/structure/window/full/plasmabasic
-
/obj/item/stack/sheet/plasmaglass/attack_self(mob/user as mob)
construct_window(user)
@@ -359,7 +358,7 @@
origin_tech = "plasmatech=2;materials=2"
created_window = /obj/structure/window/plasmareinforced
full_window = /obj/structure/window/full/plasmareinforced
-
+ armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0)
/obj/item/stack/sheet/plasmarglass/attack_self(mob/user as mob)
construct_window(user)
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index ccfe2a47c22..f6aedaf5701 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -133,9 +133,9 @@ var/global/list/datum/stack_recipe/plasteel_recipes = list(
new /datum/stack_recipe("Mass Driver frame", /obj/machinery/mass_driver_frame, 3, time = 50, one_per_turf = 1),
null,
new /datum/stack_recipe_list("airlock assemblies", list(
- new /datum/stack_recipe("vault door assembly", /obj/structure/door_assembly/door_assembly_vault, 6, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("high security airlock assembly", /obj/structure/door_assembly/door_assembly_highsecurity, 6, time = 50, one_per_turf = 1, on_floor = 1),
- ), 4),
+ new /datum/stack_recipe("vault door assembly", /obj/structure/door_assembly/door_assembly_vault, 8, time = 50, one_per_turf = 1, on_floor = 1),
+ )),
)
/obj/item/stack/sheet/plasteel
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 4979b94eb92..5d4104f57ac 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -151,21 +151,21 @@
icon = 'icons/obj/weapons.dmi'
icon_state = "sword0"
item_state = "sword0"
- var/active = 0.0
+ var/active = FALSE
w_class = WEIGHT_CLASS_SMALL
attack_verb = list("attacked", "struck", "hit")
-/obj/item/toy/sword/attack_self(mob/user as mob)
- active = !(active)
+/obj/item/toy/sword/attack_self(mob/user)
+ active = !active
if(active)
to_chat(user, "You extend the plastic blade with a quick flick of your wrist.")
- playsound(user, 'sound/weapons/saberon.ogg', 50, 1)
+ playsound(user, 'sound/weapons/saberon.ogg', 20, 1)
icon_state = "swordblue"
item_state = "swordblue"
w_class = WEIGHT_CLASS_BULKY
else
to_chat(user, "You push the plastic blade back down into the handle.")
- playsound(user, 'sound/weapons/saberoff.ogg', 50, 1)
+ playsound(user, 'sound/weapons/saberoff.ogg', 20, 1)
icon_state = "sword0"
item_state = "sword0"
w_class = WEIGHT_CLASS_SMALL
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index cc905783223..087ced385fb 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -80,7 +80,7 @@ RCD
playsound(loc, 'sound/machines/click.ogg', 50, 1)
to_chat(user, "The RCD now holds [matter]/[max_matter] matter-units.")
desc = "A RCD. It currently holds [matter]/[max_matter] matter-units."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return
@@ -92,7 +92,7 @@ RCD
ui_interact(user)
/obj/item/weapon/rcd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state)
- ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "rcd.tmpl", "[name]", 450, 400, state = state)
ui.open()
@@ -187,16 +187,16 @@ RCD
if(!proximity) return
if(istype(A,/area/shuttle)||istype(A,/turf/space/transit))
return 0
- if(!(istype(A, /turf) || istype(A, /obj/machinery/door/airlock) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/window)))
+ if(!(istype(A, /turf) || istype(A, /obj/machinery/door/airlock) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/window) || istype(A, /obj/structure/lattice)))
return 0
switch(mode)
if(1)
- if(istype(A, /turf/space))
+ if(istype(A, /turf/space) || istype(A, /obj/structure/lattice))
if(useResource(1, user))
to_chat(user, "Building Floor...")
activate()
- var/turf/AT = A
+ var/turf/AT = get_turf(A)
AT.ChangeTurf(/turf/simulated/floor/plating)
return 1
return 0
@@ -351,14 +351,14 @@ RCD
to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.")
return 0
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
/obj/item/weapon/rcd/proc/useResource(var/amount, var/mob/user)
if(matter < amount)
return 0
matter -= amount
desc = "A RCD. It currently holds [matter]/[max_matter] matter-units."
- nanomanager.update_uis(src)
+ SSnanoui.update_uis(src)
return 1
/obj/item/weapon/rcd/proc/checkResource(var/amount, var/mob/user)
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index dc4f23657b5..2c080cdafcc 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -123,9 +123,9 @@
if(guest_pass)
to_chat(user, "There is a guest pass attached to this ID card")
if(world.time < guest_pass.expiration_time)
- to_chat(user, "It expires at [worldtime2text(guest_pass.expiration_time)].")
+ to_chat(user, "It expires at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)].")
else
- to_chat(user, "It expired at [worldtime2text(guest_pass.expiration_time)].")
+ to_chat(user, "It expired at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)].")
to_chat(user, "It grants access to following areas:")
for(var/A in guest_pass.temp_access)
to_chat(user, "[get_access_desc(A)].")
diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm
index fde8461e2ec..6db5dd0997f 100644
--- a/code/game/objects/items/weapons/cigs.dm
+++ b/code/game/objects/items/weapons/cigs.dm
@@ -22,8 +22,8 @@ LIGHTERS ARE IN LIGHTERS.DM
slot_flags = SLOT_EARS|SLOT_MASK
w_class = WEIGHT_CLASS_TINY
body_parts_covered = null
- attack_verb = list("burnt", "singed")
- var/lit = 0
+ attack_verb = null
+ var/lit = FALSE
var/icon_on = "cigon" //Note - these are in masks.dmi not in cigarette.dmi
var/icon_off = "cigoff"
var/type_butt = /obj/item/weapon/cigbutt
@@ -128,7 +128,11 @@ LIGHTERS ARE IN LIGHTERS.DM
/obj/item/clothing/mask/cigarette/proc/light(flavor_text = null)
if(!src.lit)
src.lit = 1
+ name = "lit [name]"
+ attack_verb = list("burnt", "singed")
+ hitsound = 'sound/items/welder.ogg'
damtype = "fire"
+ force = 4
if(reagents.get_reagent_amount("plasma")) // the plasma explodes when exposed to fire
var/datum/effect_system/reagents_explosion/e = new()
e.set_up(round(reagents.get_reagent_amount("plasma") / 2.5, 1), get_turf(src), 0, 0)
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index 9601b86beec..ee0a5510240 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -193,7 +193,7 @@
//Burn it based on transfered gas
target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE
//location.hotspot_expose(1000,500,1)
- air_master.add_to_active(target, 0)
+ SSair.add_to_active(target, 0)
return
diff --git a/code/game/objects/items/weapons/grenades/custom_grenades.dm b/code/game/objects/items/weapons/grenades/custom_grenades.dm
index 82252c3a340..e9b78bbe90d 100644
--- a/code/game/objects/items/weapons/grenades/custom_grenades.dm
+++ b/code/game/objects/items/weapons/grenades/custom_grenades.dm
@@ -111,23 +111,23 @@
payload_name = "lubricant"
stage = 2
- New()
- ..()
- var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
- B1.reagents.add_reagent("lube",50)
- beakers += B1
-/obj/item/weapon/grenade/chem_grenade/lube/remote
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/signaler)
-/obj/item/weapon/grenade/chem_grenade/lube/prox
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/prox_sensor)
-/obj/item/weapon/grenade/chem_grenade/lube/tripwire
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/infra)
+/obj/item/weapon/grenade/chem_grenade/lube/New()
+ ..()
+ var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
+ B1.reagents.add_reagent("lube",50)
+ beakers += B1
+
+/obj/item/weapon/grenade/chem_grenade/lube/remote/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/signaler)
+
+/obj/item/weapon/grenade/chem_grenade/lube/prox/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/prox_sensor)
+
+/obj/item/weapon/grenade/chem_grenade/lube/tripwire/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/infra)
// Basic explosion grenade
@@ -135,32 +135,68 @@
payload_name = "conventional"
stage = 2
- New()
- ..()
- var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
- var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
- B1.reagents.add_reagent("glycerol",30) // todo: someone says NG is overpowered, test.
- B1.reagents.add_reagent("sacid",15)
- B2.reagents.add_reagent("sacid",15)
- B2.reagents.add_reagent("facid",30)
- beakers += B1
- beakers += B2
+/obj/item/weapon/grenade/chem_grenade/explosion/New()
+ ..()
+ var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src)
+ var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src)
+ B1.reagents.add_reagent("glycerol",30) // todo: someone says NG is overpowered, test.
+ B1.reagents.add_reagent("sacid",15)
+ B2.reagents.add_reagent("sacid",15)
+ B2.reagents.add_reagent("facid",30)
+ beakers += B1
+ beakers += B2
// Assembly Variants
-/obj/item/weapon/grenade/chem_grenade/explosion/remote
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/signaler)
+/obj/item/weapon/grenade/chem_grenade/explosion/remote/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/signaler)
+
+/obj/item/weapon/grenade/chem_grenade/explosion/prox/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/prox_sensor)
+
+/obj/item/weapon/grenade/chem_grenade/explosion/mine/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/mousetrap)
+
+
+// Water + Potassium = Boom
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium
+ payload_name = "chem explosive"
+ stage = 2
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/New()
+ ..()
+ var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src)
+ var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src)
+ B1.reagents.add_reagent("water",100)
+ B2.reagents.add_reagent("potassium",100)
+ beakers += B1
+ beakers += B2
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/remote/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/signaler)
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/prox/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/prox_sensor)
+
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/tripwire/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/infra)
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/tripwire_armed/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/infra/armed)
+
+/obj/item/weapon/grenade/chem_grenade/waterpotassium/tripwire_armed_stealth/New()
+ ..()
+ CreateDefaultTrigger(/obj/item/device/assembly/infra/armed/stealth)
-/obj/item/weapon/grenade/chem_grenade/explosion/prox
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/prox_sensor)
-/obj/item/weapon/grenade/chem_grenade/explosion/mine
- New()
- ..()
- CreateDefaultTrigger(/obj/item/device/assembly/mousetrap)
// Basic EMP grenade
/obj/item/weapon/grenade/chem_grenade/emp
diff --git a/code/game/objects/items/weapons/holosign.dm b/code/game/objects/items/weapons/holosign.dm
index 3ec25dfabaf..c96c5f6f207 100644
--- a/code/game/objects/items/weapons/holosign.dm
+++ b/code/game/objects/items/weapons/holosign.dm
@@ -50,3 +50,4 @@
icon = 'icons/obj/janitor.dmi'
icon_state = "holosign"
anchored = 1
+ armor = list(melee = 0, bullet = 50, laser = 50, energy = 50, bomb = 0, bio = 0, rad = 0)
diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm
index fb863582b83..f269542d5c8 100644
--- a/code/game/objects/items/weapons/lighters.dm
+++ b/code/game/objects/items/weapons/lighters.dm
@@ -13,7 +13,7 @@
throwforce = 4
flags = CONDUCT
slot_flags = SLOT_BELT
- attack_verb = list("burnt", "singed")
+ attack_verb = null
var/lit = 0
/obj/item/weapon/lighter/zippo
@@ -38,6 +38,10 @@
w_class = WEIGHT_CLASS_BULKY
icon_state = icon_on
item_state = icon_on
+ force = 5
+ damtype = "fire"
+ hitsound = 'sound/items/welder.ogg'
+ attack_verb = list("burnt", "singed")
if(istype(src, /obj/item/weapon/lighter/zippo) )
user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.")
playsound(src.loc, 'sound/items/ZippoLight.ogg', 25, 1)
@@ -62,6 +66,9 @@
w_class = WEIGHT_CLASS_TINY
icon_state = icon_off
item_state = icon_off
+ hitsound = "swing_hit"
+ force = 0
+ attack_verb = null //human_defense.dm takes care of it
if(istype(src, /obj/item/weapon/lighter/zippo) )
user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing. Wow.")
playsound(src.loc, 'sound/items/ZippoClose.ogg', 25, 1)
@@ -145,47 +152,74 @@
desc = "A simple match stick, used for lighting fine smokables."
icon = 'icons/obj/cigarettes.dmi'
icon_state = "match_unlit"
- var/lit = 0
+ var/lit = FALSE
+ var/burnt = FALSE
var/smoketime = 5
w_class = WEIGHT_CLASS_TINY
origin_tech = "materials=1"
- attack_verb = list("burnt", "singed")
+ attack_verb = null
/obj/item/weapon/match/process()
var/turf/location = get_turf(src)
smoketime--
if(smoketime < 1)
- icon_state = "match_burnt"
- lit = -1
- processing_objects.Remove(src)
- return
+ matchburnout()
if(location)
location.hotspot_expose(700, 5)
return
-/obj/item/weapon/match/dropped(mob/user as mob)
- if(lit == 1)
- lit = -1
+/obj/item/weapon/match/fire_act()
+ matchignite()
+
+/obj/item/weapon/match/proc/matchignite()
+ if(!lit && !burnt)
+ lit = TRUE
+ icon_state = "match_lit"
+ damtype = "fire"
+ force = 3
+ hitsound = 'sound/items/welder.ogg'
+ item_state = "cigon"
+ name = "lit match"
+ desc = "A match. This one is lit."
+ attack_verb = list("burnt","singed")
+ processing_objects.Add(src)
+ update_icon()
+
+/obj/item/weapon/match/proc/matchburnout()
+ if(lit)
+ lit = FALSE
+ burnt = TRUE
damtype = "brute"
+ force = initial(force)
icon_state = "match_burnt"
item_state = "cigoff"
name = "burnt match"
desc = "A match. This one has seen better days."
+ attack_verb = list("flicked")
processing_objects.Remove(src)
- return ..()
-/obj/item/weapon/match/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
+/obj/item/weapon/match/dropped(mob/user)
+ matchburnout()
+ . = ..()
+
+/obj/item/weapon/match/attack(mob/living/carbon/M, mob/living/carbon/user)
if(!isliving(M))
return ..()
- if(lit == 1) M.IgniteMob()
- if(!istype(M, /mob))
- return ..()
-
- if(istype(M.wear_mask, /obj/item/clothing/mask/cigarette) && user.zone_sel.selecting == "mouth" && lit == 1)
- var/obj/item/clothing/mask/cigarette/cig = M.wear_mask
+ if(lit && M.IgniteMob())
+ message_admins("[key_name_admin(user)] set [key_name_admin(M)] on fire")
+ log_game("[key_name(user)] set [key_name(M)] on fire")
+ var/obj/item/clothing/mask/cigarette/cig = help_light_cig(M)
+ if(lit && cig && user.a_intent == INTENT_HELP)
+ if(cig.lit)
+ to_chat(user, "[cig] is already lit.")
if(M == user)
cig.attackby(src, user)
else
- cig.light("[user] holds the [name] out for [M], and lights the [cig.name].")
+ cig.light("[user] holds [src] out for [M], and lights [cig].")
else
..()
+
+/obj/item/proc/help_light_cig(mob/living/M)
+ var/mask_item = M.get_item_by_slot(slot_wear_mask)
+ if(istype(mask_item, /obj/item/clothing/mask/cigarette))
+ return mask_item
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index 88311c5f149..23edcf29a36 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -12,18 +12,18 @@
icon_state ="bookEngineering"
author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
title = "Station Repairs and Construction"
- /*dat = {"
+ dat = {"
-
+
- "}*/
+ "}
/obj/item/weapon/book/manual/engineering_particle_accelerator
name = "Particle Accelerator User's Guide"
@@ -32,7 +32,7 @@
title = "Particle Accelerator User's Guide"
//big pile of shit below.
- /*dat = {"
+ dat = {"
+
+
+
+ Experienced user's guide
+
+ Setting up
+
+
+ - Wrench all pieces to the floor
+ - Add wires to all the pieces
+ - Close all the panels with your screwdriver
+
+
+ Use
+
+
+ - Open the control panel
+ - Set the speed to 2
+ - Start firing at the singularity generator
+ - When the singularity reaches a large enough size so it starts moving on it's own set the speed down to 0, but don't shut it off
+ - Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?
+
+
+
+ "}
+
+
+/obj/item/weapon/book/manual/supermatter_engine
+ name = "Supermatter Engine User's Guide"
+ icon_state = "bookParticleAccelerator" //TEMP FIXME
+ author = "Waleed Asad"
+ title = "Supermatter Engine User's Guide"
+
+ dat = {"Engineering notes on single-stage Supermatter engine,
+ -Waleed Asad
+
+ A word of caution, do not enter the engine room, for any reason, without radiation protection and mesons on. The status of the engine may be unpredictable even when you believe it is .off.. This is an important level of personal protection.
+
+ The engine has two basic modes of functionality. He has observed that it is capable of both a safe level of operation and a modified, high output mode.
+
+ Notes on starting the basic function mode, dubbed .Heat-Primary Mode..
+
+ 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.
+
+ 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.
+
+ 3. Apply N2 gas. Retrieve the two N2 canisters from storage and bring them to the engine room. Attach one of them to the input section of the engine gas system located next to the collectors. Keep it attached until the N2 pressure is low enough to turn the canister light red. Replace it with the second canister to keep N2 pressure at optimal levels.
+
+ 4. Begin primary emitter burst series. This means firing a single emitter for its first four shots. It is important to move to this step quickly. The onboard SMES units may not have enough power to run the emitters if left alone too long on-station. This engine can produce enough power on its own to run the entire station, ignoring the SMES units completely, and is wired to do so.
+
+ 5. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.)
+
+ 6. Begin secondary emitter burst series. Before firing the emitter again, check the power in the line with a multimeter (Do not forget electrical gloves.) The engine is running at high efficiency when the value exceeds 200,000 power units.
+
+ 7. Maintain engine power. When power in the lines gets low, add an additional emitter burst series to bring power to normal levels.
+
+
+
+ The second mode for running the engine uses a gas mix to produce a reaction within the Supermatter. This mode requires CE or Atmospheric help to setup. This has been dubbed the .O2-Reaction Mode..
+
+ THIS MODE CAN CAUSE A RUNAWAY REACTION, LEADING TO CATASTROPHIC FAILURE IF NOT MAINTAINED. NEVER FORGET ABOUT THE ENGINE IN THIS MODE.
+
+ Additionally, this mode can be used for what is called a .Cold Start.. If the station has no power in the SMES to run the emitters, using this mode will allow enough power output to run them, and quickly reach an acceptable level of power output.
+
+ 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.
+
+ 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.
+
+ 3. Modify the engine room filters. Unlike the Heat-Primary Mode, it is important to change the filters attached to the gas system to stop filtering O2, and start filtering Carbon Molecules. O2-Reaction Mode produces far more plasma than Heat-Primary, therefor filtering it off is essential.
+
+ 4. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.) If you check the power in the system lines at this point you will find that it is constantly going up. Indeed, with just the addition of O2 to the Supermatter, it will begin outputting power.
+
+ 5. Begin primary emitter burst series. Fire a single emitter for a series of four pulses, or a single series, and turn it off. Do not over power the Supermatter. The reaction is self sustaining and propagating. As long as O2 is in the chamber, it will continue outputting MORE power.
+
+ 6. Maintain follow up operations. Remember to check the temp of the core gas and switch to the Heat-Primary function, or vent the core room when problems begin if required.
+
+ Notes on Supermatter Reaction Function and Drawbacks-
+
+ After several hours of observation an interesting phenomenon was witnessed. The Supermatter undergoes a constant self-sustaining reaction when given an extremely high O2 concentration. Anything about 80% or higher typically will cause this reaction. The Supermatter will continue to react whenever this gas mix is in the same room as the Supermatter.
+
+ To understand why O2-Reaction mode is dangerous, the core principle of the Supermatter must be understood. The Supermatter emits three things when .not safe,. that is any time it is giving off power. These things are:
+
+ *Radiation (which is converted into power by the collectors,)
+ *Heat (which is removed via the gas exchange system and coolers,)
+ *External gas (in the form of plasma and O2.)
+
+ When in Heat-Primary mode, far more heat and plasma are produced than radiation. In O2-Reaction mode, very little heat and only moderate amounts of plasma are produced, however HUGE amounts of energy leaving the Supermatter is in the form of radiation.
+
+ The O2-Reaction engine mode has a single drawback which has been eluded to more than once so far and that is very simple. The engine room will continue to grow hotter as the constant reaction continues. Eventually, there will be what he calls the .critical gas mix.. This is the point at which the constant adding of plasma to the mix of air around the Supermatter changes the gas concentration to below the tolerance. When this happens, two things occur. First, the Supermatter switches to its primary mode of operation where in huge amounts of heat are produced by the engine rather than low amounts with high power output. Second, an uncontrollable increase in heat within the Supermatter chamber will occur. This will lead to a spark-up, igniting the plasma in the Supermatter chamber, wildly increasing both pressure and temperature.
+
+ While the O2-Reaction mode is dangerous, it does produce heavy amounts of energy. Consider using this mode only in short amounts to fill the SMES, and switch back later in the shift to keep things flowing normally.
+
+
+ Notes on Supermatter Containment and Emergency Procedures-
+
+ While a constant vigil on the Supermatter is not required, regular checkups are important. Verify the temp of gas leaving the Supermatter chamber for unsafe levels, and ensure that the plasma in the chamber is at a safe concentration. Of course, also make sure the chamber is not on fire. A fire in the core chamber is very difficult to put out. As any Toxin scientist can tell you, even low amounts of plasma can burn at very high temperatures. This burning creates a huge increase in pressure and more importantly, temperature of the crystal itself.
+
+ The Supermatter is strong, but not invincible. When the Supermatter is heated too much, its crystal structure will attempt to liquify. The change in atomic structure of the Supermatter leads to a single reaction, a massive explosion. The computer chip attached to the Supermatter core will warn the station when stability is threatened. It will then offer a second warning, when things have become dangerously close to total destruction of the core.
+
+ Located both within the supermatter monitoring room and engine room is the vent control button. This button allows the Core Vent Controls to be accessed, venting the room to space. Remember however, that this process takes time. If a fire is raging, and the pressure is higher than fathomable, it will take a great deal of time to vent the room. Also located in the supermatter monitoring room is the emergency core eject button. A new core can be ordered from cargo. It is often not worth the lives of the crew to hold on to it, not to mention the structural damage. However, if by some mistake the Supermatter is pushed off or removed from the mass ejector it sits on, manual reposition will be required. Which is very dangerous and often leads to death.
+
+ The Supermatter is extremely dangerous. More dangerous than people give it credit for. It can destroy you in an instant, without hesitation, reducing you to a pile of dust. When working closely with Supermatter it is.. suggested to get a genetic backup and do not wear any items of value to you. The Supermatter core can be pulled if grabbed properly by the base, but pushing is not possible.
+
+
+ In Closing-
+
+ Remember that the Supermatter is dangerous, and the core is dangerous still. Venting the core room is always an option if you are even remotely worried, utilizing Atmospherics to properly ready the room once more for core function. It is always a good idea to check up regularly on the temperature of gas leaving the chamber, as well as the power in the system lines. Lastly, once again remember, never touch the Supermatter with anything. Ever.
+
+ -Waleed Asad, Senior Engine Technician."}
+
+/obj/item/weapon/book/manual/engineering_hacking
+ name = "Hacking"
+ icon_state ="bookHacking"
+ author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
+ title = "Hacking"
+//big pile of shit below.
+
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/engineering_singularity_safety
+ name = "Singularity Safety in Special Circumstances"
+ icon_state ="bookEngineeringSingularitySafety"
+ author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
+ title = "Singularity Safety in Special Circumstances"
+//big pile of shit below.
+
+ dat = {"
+
+
+
+
+ Singularity Safety in Special Circumstances
+
+ Power outage
+
+ A power problem has made the entire station loose power? Could be station-wide wiring problems or syndicate power sinks. In any case follow these steps:
+
+ Step one: PANIC!
+ Step two: Get your ass over to engineering! QUICKLY!!!
+ Step three: Get to the Area Power Controller which controls the power to the emitters.
+ Step four: Swipe it with your ID card - if it doesn't unlock, continue with step 15.
+ Step five: Open the console and disengage the cover lock.
+ Step six: Pry open the APC with a Crowbar.
+ Step seven: Take out the empty power cell.
+ Step eight: Put in the new, full power cell - if you don't have one, continue with step 15.
+ Step nine: Quickly put on a Radiation suit.
+ Step ten: Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
+ Step eleven: Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
+ Step twelve: Grab your crowbar and pry away the tile closest to the APC.
+ Step thirteen: Use the wirecutters to cut the wire which is conecting the grid to the terminal.
+ Step fourteen: Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
+ Step fifteen: GET THE FUCK OUT OF THERE!!!
+
+
+ Shields get damaged
+
+ Step one: GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!
+
+
+ "}
+
+/obj/item/weapon/book/manual/hydroponics_pod_people
+ name = "The Human Harvest - From seed to market"
+ icon_state ="bookHydroponicsPodPeople"
+ author = "Farmer John"
+ title = "The Human Harvest - From seed to market"
+ dat = {"
+
+
+
+
+ Growing Humans
+
+ Why would you want to grow humans? Well I'm expecting most readers to be in the slave trade, but a few might actually
+ want to revive fallen comrades. Growing pod people is easy, but prone to disaster.
+
+
+ - Find a dead person who is in need of cloning.
+ - Take a blood sample with a syringe.
+ - Inject a seed pack with the blood sample.
+ - Plant the seeds.
+ - Tend to the plants water and nutrition levels until it is time to harvest the cloned human.
+
+
+ It really is that easy! Good luck!
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/medical_cloning
+ name = "Cloning techniques of the 26th century"
+ icon_state ="bookCloning"
+ author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
+ title = "Cloning techniques of the 26th century"
+//big pile of shit below.
+
+ dat = {"
+
+
+
+
+
+ How to Clone People
+ So thereÂ’s 50 dead people lying on the floor, chairs are spinning like no tomorrow and you havenÂ’t the foggiest idea of what to do? Not to worry! This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, genetics probably isnÂ’t for you and you should get a job-change as soon as possible before youÂ’re sued for malpractice.
+
+
+ - Acquire body
+ - Strip body
+ - Put body in cloning machine
+ - Scan body
+ - Clone body
+ - Get clean Structurel Enzymes for the body
+ - Put body in morgue
+ - Await cloned body
+ - Use the clean SW injector
+ - Give person clothes back
+ - Send person on their way
+
+
+ Step 1: Acquire body
+ This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step.
+
+ Step 2: Strip body
+ The cloning machine does not like abiotic items. What this means is you canÂ’t clone anyone if theyÂ’re wearing clothes, so take all of it off. If itÂ’s just one person, itÂ’s courteous to put their possessions in the closet. If you have about seven people awaiting cloning, just leave the piles where they are, but donÂ’t mix them around and for GodÂ’s sake donÂ’t let people in to steal them.
+
+ Step 3: Put body in cloning machine
+ Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset.
+
+ Step 4: Scan body
+ Go onto the computer and scan the body by pressing ‘Scan - ’. If you’re successful, they will be added to the records (note that this can be done at any time, even with living people, so that they can be cloned without a body in the event that they are lying dead on port solars and didn‘t turn on their suit sensors)! If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, click ‘Refresh‘ and try scanning them again. If there’s no success, threaten them with gibbing. Still no success? Skip over to Step 7 and don‘t continue after it, as you have an unresponsive body and it cannot be cloned. If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over.
+
+ Step 5: Clone body
+ Now that the body has a record, click ’View Records’, click the subject’s name, and then click ‘Clone’ to start the cloning process. Congratulations! You’re halfway there. Remember not to ‘Eject’ the cloning pod as this will kill the developing clone and you’ll have to start the process again.
+
+ Step 6: Get clean SEs for body
+ Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they’re done. If you’re a competent Geneticist, you will already have one ready on your working computer. If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click ‘Injector’ to get the SEs in syringe form. Put this in your pocket or something for when the body is done.
+
+ Step 7: Put body in morgue
+ Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion.
+
+ Step 8: Await cloned body
+ Now go back to the lab and wait for your patient to be cloned. It wonÂ’t be long now, I promise.
+
+ Step 9: Use the clean SE injector on person
+ Has your body been cloned yet? Great! As soon as the guy pops out, grab your injector and jab it in them. Once youÂ’ve injected them, they now have clean Structural Enzymes and their defects, if any, will disappear in a short while.
+
+ Step 10: Give person clothes back
+ Obviously the person will be naked after they have been cloned. Provided you werenÂ’t an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. No matter how cruel you are, itÂ’s simply against protocol to force your patients to walk outside naked.
+
+ Step 11: Send person on their way
+ Give the patient one last check-over - make sure they donÂ’t still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. Once youÂ’re done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance.
+
+ If youÂ’ve gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target.
+
+
+
+
+
+ "}
+
+
+/obj/item/weapon/book/manual/ripley_build_and_repair
+ name = "APLU \"Ripley\" Construction and Operation Manual"
+ icon_state ="book"
+ author = "Weyland-Yutani Corp" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
+ title = "APLU \"Ripley\" Construction and Operation Manual"
+//big pile of shit below.
+
+ dat = {"
+
+
+
+
+
+ Weyland-Yutani - Building Better Worlds
+ Autonomous Power Loader Unit \"Ripley\"
+
+ Specifications:
+
+ - Class: Autonomous Power Loader
+ - Scope: Logistics and Construction
+ - Weight: 820kg (without operator and with empty cargo compartment)
+ - Height: 2.5m
+ - Width: 1.8m
+ - Top speed: 5km/hour
+ - Operation in vacuum/hostile environment: Possible
+
- Airtank Volume: 500liters
+ - Devices:
+
+ - Hydraulic Clamp
+ - High-speed Drill
+
+
+ - Propulsion Device: Powercell-powered electro-hydraulic system.
+ - Powercell capacity: Varies.
+
+
+ Construction:
+
+ - Connect all exosuit parts to the chassis frame
+ - Connect all hydraulic fittings and tighten them up with a wrench
+ - Adjust the servohydraulics with a screwdriver
+ - Wire the chassis. (Cable is not included.)
+ - Use the wirecutters to remove the excess cable if needed.
+ - Install the central control module (Not included. Use supplied datadisk to create one).
+ - Secure the mainboard with a screwdriver.
+ - Install the peripherals control module (Not included. Use supplied datadisk to create one).
+ - Secure the peripherals control module with a screwdriver
+ - Install the internal armor plating (Not included due to Nanotrasen regulations. Can be made using 5 metal sheets.)
+ - Secure the internal armor plating with a wrench
+ - Weld the internal armor plating to the chassis
+ - Install the external reinforced armor plating (Not included due to Nanotrasen regulations. Can be made using 5 reinforced metal sheets.)
+ - Secure the external reinforced armor plating with a wrench
+ - Weld the external reinforced armor plating to the chassis
+
+ - Additional Information:
+ - The firefighting variation is made in a similar fashion.
+ - A firesuit must be connected to the Firefighter chassis for heat shielding.
+ - Internal armor is plasteel for additional strength.
+ - External armor must be installed in 2 parts, totaling 10 sheets.
+ - Completed mech is more resiliant against fire, and is a bit more durable overall
+ - Nanotrasen is determined to the safety of its
investments employees.
+
+
+
+
+ Operation
+ Coming soon...
+ "}
+
+/obj/item/weapon/book/manual/experimentor
+ name = "Mentoring your Experiments"
+ icon_state = "rdbook"
+ author = "Dr. H.P. Kritz"
+ title = "Mentoring your Experiments"
+ dat = {"
+
+
+
+
+ THE E.X.P.E.R.I-MENTOR
+ The Enhanced Xenobiological Period Extraction (and) Restoration Instructor is a machine designed to discover the secrets behind every item in existence.
+ With advanced technology, it can process 99.95% of items, and discover their uses and secrets.
+ The E.X.P.E.R.I-MENTOR is a Research apparatus that takes items, and through a process of elimination, it allows you to deduce new technological designs from them.
+ Due to the volatile nature of the E.X.P.E.R.I-MENTOR, there is a slight chance for malfunction, potentially causing irreparable damage to you or your environment.
+ However, upgrading the apparatus has proven to decrease the chances of undesirable, potentially life-threatening outcomes.
+ Please note that the E.X.P.E.R.I-MENTOR uses a state-of-the-art random generator, which has a larger entropy than the observable universe,
+ therefore it can generate wildly different results each day, therefore it is highly suggested to re-scan objects of interests frequently (e.g. each shift).
+
+ BASIC PROCESS
+ The usage of the E.X.P.E.R.I-MENTOR is quite simple:
+
+ - Find an item with a technological background
+ - Insert the item into the E.X.P.E.R.I-MENTOR
+ - Cycle through each processing method of the device.
+ - Stand back, even in case of a successful experiment, as the machine might produce undesired behaviour.
+
+
+ ADVANCED USAGE
+ The E.X.P.E.R.I-MENTOR has a variety of uses, beyond menial research work. The different results can be used to combat localised events, or even to get special items.
+
+ The E.X.P.E.R.I-MENTOR's OBLITERATE function has the added use of transferring the destroyed item's material into a linked lathe.
+
+ The IRRADIATE function can be used to transform items into other items, resulting in potential upgrades (or downgrades).
+
+ Users should remember to always wear appropriate protection when using the machine, because malfunction can occur at any moment!
+
+ EVENTS
+ GLOBAL (happens at any time):
+
+ - DETECTION MALFUNCTION - The machine's onboard sensors have malfunctioned, causing it to redefine the item's experiment type.
+ Produces the message: The E.X.P.E.R.I-MENTOR's onboard detection system has malfunctioned!
+
+ - IANIZATION - The machine's onboard corgi-filter has malfunctioned, causing it to produce a corgi from.. somewhere.
+ Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ian-izing the air around it!
+
+ - RUNTIME ERROR - The machine's onboard C4T-P processor has encountered a critical error, causing it to produce a cat from.. somewhere.
+ Produces the message: The E.X.P.E.R.I-MENTOR encounters a run-time error!
+
+ - B100DG0D.EXE - The machine has encountered an unknown subroutine, which has been injected into it's runtime. It upgrades the held item!
+ Produces the message: The E.X.P.E.R.I-MENTOR improves the banana, drawing the life essence of those nearby!
+
+ - POWERSINK - The machine's PSU has tripped the charging mechanism! It consumes massive amounts of power!
+ Produces the message: The E.X.P.E.R.I-MENTOR begins to smoke and hiss, shaking violently!
+
+ FAIL:
+ This event is produced when the item mismatches the selected experiment.
+ Produces a random message similar to: "the Banana rumbles, and shakes, the experiment was a failure!"
+
+ POKE:
+
+ - WILD ARMS - The machine's gryoscopic processors malfunction, causing it to lash out at nearby people with it's arms.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions and destroys the banana, lashing it's arms out at nearby people!
+
+ - MISTYPE - The machine's interface has been garbled, and it switches to OBLITERATE.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions!
+
+ - THROW - The machine's spatial recognition device has shifted several meters across the room, causing it to try and repostion the item there.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, throwing the banana!
+
+ IRRADIATE:
+
+ - RADIATION LEAK - The machine's shield has failed, resulting in a toxic radiation leak.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking radiation!
+
+ - RADIATION DUMP - The machine's recycling and containment functions have failed, resulting in a dump of toxic waste around it
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing toxic waste!
+
+ - MUTATION - The machine's radio-isotope level meter has malfunctioned, causing it over-irradiate the item, making it transform.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, transforming the banana!
+
+ GAS:
+
+ - TOXIN LEAK - The machine's filtering and vent systems have failed, resulting in a cloud of toxic gas being expelled.
+ Produces the message: The E.X.P.E.R.I-MENTOR destroys the banana, leaking dangerous gas!
+
+ - GAS LEAK - The machine's vent systems have failed, resulting in a cloud of harmless, but obscuring gas.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing harmless gas!
+
+ - ELECTROMAGNETIC IONS - The machine's electrolytic scanners have failed, causing a dangerous Electromagnetic reaction.
+ Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ionizing the air around it!
+
+ HEAT:
+
+ - TOASTER - The machine's heating coils have come into contact with the machine's gas storage, causing a large, sudden blast of flame.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and releasing a burst of flame!
+
+ - SAUNA - The machine's vent loop has sprung a leak, resulting in a large amount of superheated air being dumped around it.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking hot air!
+
+ - EMERGENCY VENT - The machine's temperature gauge has malfunctioned, resulting in it attempting to cool the area around it, but instead, dumping a cloud of steam.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, activating it's emergency coolant systems!
+
+ COLD:
+
+ - FREEZER - The machine's cooling loop has sprung a leak, resulting in a cloud of super-cooled liquid being blasted into the air.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and releasing a dangerous cloud of coolant!
+
+ - FRIDGE - The machine's cooling loop has been exposed to the outside air, resulting in a large decrease in temperature.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and leaking cold air!
+
+ - SNOWSTORM - The machine's cooling loop has come into contact with the heating coils, resulting in a sudden blast of cool air.
+ Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, releasing a flurry of chilly air as the banana pops out!
+
+ OBLITERATE:
+
+ - IMPLOSION - The machine's pressure leveller has malfunctioned, causing it to pierce the space-time momentarily, making everything in the area fly towards it.
+ Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes way too many levels too high, crushing right through space-time!
+
+ - DISTORTION - The machine's pressure leveller has completely disabled, resulting in a momentary space-time distortion, causing everything to fly around.
+ Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes one level too high, crushing right into space-time!
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/research_and_development
+ name = "Research and Development 101"
+ icon_state = "rdbook"
+ author = "Dr. L. Ight"
+ title = "Research and Development 101"
+ dat = {"
+
+
+
+
+
+
+ Science For Dummies
+ So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process:
+
+ - 1) Deconstruct items in the Destructive Analyzer to advance technology or improve the design.
+ - 2) Build unlocked designs in the Protolathe and Circuit Imprinter
+ - 3) Repeat!
+
+
+ Those are the basic steps to furthing science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter.
+
+ The R&D Console
+ The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, because if it gets damaged, you'll lose your data! In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database.
+ NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices.
+
+ Destructive Analyzer
+ This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, you can destroy the object to further those sciences. Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. If their reliability is high enough ,it'll also advance their related technologies.
+
+ Circuit Imprinter
+ This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules.
+
+ Protolathe
+ This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. The downside is that, again, not all devices you make are 100% reliable when you first discover them.
+
+ Reliability and You
+ As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. Once the device reachs a certain minimum reliability, you'll gain tech advancements from it.
+
+ Building a Better Machine
+ Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. If you use these parts when constructing something, its attributes may be improved. For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine.
+
+
+ "}
+
+
+/obj/item/weapon/book/manual/robotics_cyborgs
+ name = "Cyborgs for Dummies"
+ icon_state = "borgbook"
+ author = "XISC"
+ title = "Cyborgs for Dummies"
+ dat = {"
+
+
+
+
+
+ Cyborgs for Dummies
+
+ Chapters
+
+
+ - Cyborg Related Equipment
+ - Cyborg Modules
+ - Cyborg Construction
+ - Cyborg Maintenance
+ - Cyborg Repairs
+ - In Case of Emergency
+
+
+
+
+
+ Exosuit Fabricator
+ The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs can not be built. It seems that they may also benefit from advanced research techniques.
+
+ Cyborg Recharging Station
+ This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge.
+
+ Robotics Control Console
+ This useful piece of equipment can be used to immobolize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or Nanotrasen may see to it that it never happens again.
+
+
+
+ When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules.
+
+ Standard Cyborg
+ The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks. A Standard Cyborg comes with:
+
+ - Crowbar
+ - Stun Baton
+ - Health Analyzer
+ - Fire Extinguisher
+
+
+ Engineering Cyborg
+ The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks. An Engineering Cyborg comes with:
+
+ - A basic set of engineering tools
+ - Metal Synthesizer
+ - Reinforced Glass Synthesizer
+ - An RCD
+ - Wire Synthesizer
+ - Fire Extinguisher
+ - Built-in Optical Meson Scanners
+
+
+ Mining Cyborg
+ The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines. A Mining Cyborg comes with:
+
+ - Jackhammer
+ - Shovel
+ - Mining Satchel
+ - Built-in Optical Meson Scanners
+
+
+ Security Cyborg
+ The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit. A Security Cyborg comes with:
+
+ - Stun Baton
+ - Handcuffs
+ - Taser
+
+
+ Janitor Cyborg
+ The Janitor Cyborg module is equipped with various cleaning-facilitating devices. A Janitor Cyborg comes with:
+
+ - Mop
+ - Hand Bucket
+ - Cleaning Spray Synthesizer and Spray Nozzle
+
+
+ Service Cyborg
+ The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros" A Service Cyborg comes with:
+
+ - Shaker
+ - Industrail Dropper
+ - Platter
+ - Beer Synthesizer
+ - Zippo Lighter
+ - Rapid-Service-Fabricator (Produces various entertainment and refreshment objects)
+ - Pen
+
+
+
+ Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies. The required materials to make a cyborg are:
+
+ - Metal
+ - Two Flashes
+ - One Power Cell (Preferrably rated to 15000w)
+ - Some electrical wires
+ - One Human Brain
+ - One Man-Machine Interface
+
+ Once you have acquired the materials, you can start on construction of your cyborg. To construct a cyborg, follow the steps below:
+
+ - Start the Exosuit Fabricators constructing all of the cyborg parts
+ - While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface
+ - Once you have a Robot Head, place your two flashes inside the eye sockets
+ - Once you have your Robot Chest, wire the Robot chest, then insert the power cell
+ - Attach all of the Robot parts to the Robot frame
+ - Insert the Man-Machine Interface (With the Brain inside) Into the Robot Body
+ - Congratulations! You have a new cyborg!
+
+
+
+ Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring.
+
+ Replacing a Power Cell
+ Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell. The steps to replace a cell are follows:
+
+ - Unlock the Cyborg's Interface by swiping your ID on it
+ - Open the Cyborg's outer panel using a crowbar
+ - Remove the old power cell
+ - Insert the new power cell
+ - Close the Cyborg's outer panel using a crowbar
+ - Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell
+
+
+ Exposing the Internal Wiring
+ Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs. You can easily expose the internal wiring by following the steps below:
+
+ - Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"
+ - Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel
+
+ To re-seal the cyborg's internal wiring:
+
+ - Use a screwdriver to secure the cyborg's internal panel
+ - Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg
+
+
+
+ Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs).
+
+ Dents
+ If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure.
+ Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder.
+
+ Excessive Heat Damage
+ If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly. To replace the internal wiring follow the steps below:
+
+ - Unlock the Cyborg's Interface by swiping your ID
+ - Open the Cyborg's External Panel using a crowbar
+ - Remove the Cyborg's Power Cell
+ - Using a screwdriver, expose the internal wiring or the Cyborg
+ - Replace the damaged wires inside the cyborg
+ - Secure the internal wiring cover using a screwdriver
+ - Insert the Cyborg's Power Cell
+ - Close the Cyborg's External Panel using a crowbar
+ - Lock the Cyborg's Interface by swiping your ID
+
+ These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency.
+
+
+ In case of emergency, there are a few steps you can take.
+
+ "Rogue" Cyborgs
+ If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution.
+ To repair the situation, follow these steps:
+
+ - Locate the nearest robotics console
+ - Determine which cyborgs are "Rogue"
+ - Press the lockdown button to immobolize the cyborg
+ - Locate the cyborg
+ - Expose the cyborg's internal wiring
+ - Check to make sure the LawSync and AI Sync lights are lit
+ - If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's Law Sync
+ - Proceed to a cyborg upload console. Nanotrasen usually places these in the same location as AI uplaod consoles.
+ - Use a "Reset" upload moduleto reset the cyborg's laws
+ - Proceed to a Robotics Control console
+ - Remove the lockdown on the cyborg
+
+
+ As a last resort
+ If all else fails in a case of cyborg-related emergency. There may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg.
+ WARNING: Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of Nanotrasen equipment, and you may be punished for detonating them without reason.
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/security_space_law
+ name = "Space Law"
+ desc = "A set of Nanotrasen guidelines for keeping law and order on their space stations."
+ icon_state = "bookSpaceLaw"
+ author = "Nanotrasen"
+ title = "Space Law"
+ dat = {"
+
+
+
+
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/security_space_law/black
+ name = "Space Law - Limited Edition"
+ desc = "A leather-bound, immaculately-written copy of JUSTICE."
+ icon_state = "bookSpaceLawblack"
+ title = "Space Law - Limited Edition"
+
+/obj/item/weapon/book/manual/engineering_guide
+ name = "Engineering Textbook"
+ icon_state ="bookEngineering2"
+ author = "Engineering Encyclopedia"
+ title = "Engineering Textbook"
+ dat = {"
+
+
+
+
+
+
+
+
+
+ "}
+
+
+/obj/item/weapon/book/manual/chef_recipes
+ name = "Chef Recipes"
+ icon_state = "cooked_book"
+ author = "Victoria Ponsonby"
+ title = "Chef Recipes"
+ dat = {"
+
+
+
+
+
+ Food for Dummies
+ Here is a guide on basic food recipes and also how to not poison your customers accidentally.
+
+ Basics:
+ Knead an egg and some flour to make dough. Bake that to make a bun or flatten and cut it.
+
+ Burger:
+ Put a bun and some meat into the microwave and turn it on. Then wait.
+
+ Bread:
+ Put some dough and an egg into the microwave and then wait.
+
+ Waffles:
+ Add two lumps of dough and 10u of sugar to the microwave and then wait.
+
+ Popcorn:
+ Add 1 corn to the microwave and wait.
+
+ Meat Steak:
+ Put a slice of meat, 1 unit of salt and 1 unit of pepper into the microwave and wait.
+
+ Meat Pie:
+ Put a flattened piece of dough and some meat into the microwave and wait.
+
+ Boiled Spaghetti:
+ Put the spaghetti (processed flour) and 5 units of water into the microwave and wait.
+
+ Donuts:
+ Add some dough and 5 units of sugar to the microwave and wait.
+
+ Fries:
+ Add one potato to the processor, then bake them in the microwave.
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/barman_recipes
+ name = "Barman Recipes"
+ icon_state = "barbook"
+ author = "Sir John Rose"
+ title = "Barman Recipes"
+ dat = {"
+
+
+
+
+
+ Drinks for dummies
+ Heres a guide for some basic drinks.
+
+ Manly Dorf:
+ Mix ale and beer into a glass.
+
+ Grog:
+ Mix rum and water into a glass.
+
+ Black Russian:
+ Mix vodka and kahlua into a glass.
+
+ Irish Cream:
+ Mix cream and whiskey into a glass.
+
+ Screwdriver:
+ Mix vodka and orange juice into a glass.
+
+ Cafe Latte:
+ Mix milk and coffee into a glass.
+
+ Mead:
+ Mix Enzyme, water and sugar into a glass.
+
+ Gin Tonic:
+ Mix gin and tonic into a glass.
+
+ Classic Martini:
+ Mix vermouth and gin into a glass.
+
+
+
+
+ "}
+
+
+/obj/item/weapon/book/manual/detective
+ name = "The Film Noir: Proper Procedures for Investigations"
+ icon_state ="bookDetective"
+ author = "Nanotrasen"
+ title = "The Film Noir: Proper Procedures for Investigations"
+ dat = {"
+
+
+
+
+ Detective Work
+
+ Between your bouts of self-narration, and drinking whiskey on the rocks, you might get a case or two to solve.
+ To have the best chance to solve your case, follow these directions:
+
+
+ - Go to the crime scene.
+ - Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog.)
+ - Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too.
+ - Return to your office.
+ - Using your forensic scanning computer, scan your Scanner to upload all of your evidence into the database.
+ - Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled.
+ - If you have 80% or more of the print (The print is displayed) go to step 10, otherwise continue to step 8.
+ - Look for clues from the suit fibres you found on your perp, and go about looking for more evidence with this new information, scanning as you go.
+ - Try to get a fingerprint card of your perp, as if used in the computer, the prints will be completed on their dossier.
+ - Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it.
+ - Since you now have both your dossier and the name of the person, print both out as evidence, and get security to nab your baddie.
+ - Give yourself a pat on the back and a bottle of the ships finest vodka, you did it!.
+
+
+ It really is that easy! Good luck!
+
+
+ "}
+
+/obj/item/weapon/book/manual/nuclear
+ name = "Fission Mailed: Nuclear Sabotage 101"
+ icon_state ="bookNuclear"
+ author = "Syndicate"
+ title = "Fission Mailed: Nuclear Sabotage 101"
+ dat = {"
+ Nuclear Explosives 101:
+ Hello and thank you for choosing the Syndicate for your nuclear information needs.
+ Today's crash course will deal with the operation of a Fusion Class Nanotrasen made Nuclear Device.
+ First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE.
+ Pressing any button on the compacted bomb will cause it to extend and bolt itself into place.
+ If this is done to unbolt it one must completely log in which at this time may not be possible.
+ To make the nuclear device functional:
+ Place the nuclear device in the designated detonation zone.
+ Extend and anchor the nuclear device from its interface.
+ Insert the nuclear authorisation disk into slot.
+ Type numeric authorisation code into the keypad. This should have been provided. Note: If you make a mistake press R to reset the device.
+ Press the E button to log onto the device.
+ You now have activated the device. To deactivate the buttons at anytime for example when you've already prepped the bomb for detonation remove the auth disk OR press the R on the keypad.
+ Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option.
+ Note: Nanotrasen is a pain in the neck.
+ Toggle off the SAFETY.
+ Note: You wouldn't believe how many Syndicate Operatives with doctorates have forgotten this step.
+ So use the - - and + + to set a det time between 5 seconds and 10 minutes.
+ Then press the timer toggle button to start the countdown.
+ Now remove the auth. disk so that the buttons deactivate.
+ Note: THE BOMB IS STILL SET AND WILL DETONATE
+ Now before you remove the disk if you need to move the bomb you can:
+ Toggle off the anchor, move it, and re-anchor.
+ Good luck. Remember the order:
+ Disk, Code, Safety, Timer, Disk, RUN!
+ Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.
+ Good luck!
+ "}
+
+/obj/item/weapon/book/manual/atmospipes
+ name = "Pipes and You: Getting To Know Your Scary Tools"
+ icon_state = "pipingbook"
+ author = "Maria Crash, Senior Atmospherics Technician"
+ title = "Pipes and You: Getting To Know Your Scary Tools"
+ dat = {"
+
+
+
+
+
+
+
+
+ - Author's Forward
+ - Basic Piping
+ - Insulated Pipes
+ - Atmospherics Devices
+ - Heat Exchange Systems
+ - Final Checks
+
+
+
+
+ Or: What the fuck does a "passive gate" do?
+
+ Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does.
+ Apparently there is an unnatural fear of these arcane devices and their "gases". Spooky, spooky. So,
+ this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does.
+ You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates,
+ or something like that. Just what stuff does.
+
+
+
+ The boring ones.
+ TMost ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction.
+ That's about it. Even so, here's all of your wonderful pipe options.
+
+ Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this
+ standard one-meter size, so most things will take up as much space as one of these.
+ Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
+ Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
+ 4-way manifold: A four-way junction.
+ Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
+ Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
+ Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.
+
+
+ Special Public Service Announcement.
+ Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.
+
+
+ They actually do something.
+ This is usually where people get frightened, afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now.
+ Stop it. It's unbecoming. Most of these are fairly straightforward.
+
+ Gas Pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 4500 kPa (kilopascals).
+ Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa
+ (though 18 is preferred using internals, for various reasons).
+ Volume pump: This pump goes based on volume, instead of pressure, and the possible maximum pressure it can create in the pipe on the recieving end is double the gas pump because of this,
+ clocking in at an incredible 9000 kPa. If a pipe with this is destroyed or damaged, and this pressure of gas escapes, it can be incredibly dangerous depending on the size of the pipe filled.
+ Don't hook this to the distribution loop, or you will make babies cry and the Chief Engineer brutally beat you.
+ Passive gate: This is essentially a cap on the pressure of gas allowed to flow in a specific direction.
+ When turned on, instead of actively pumping gas, it measures the pressure flowing through it, and whatever pressure you set is the maximum: it'll cap after that.
+ In addition, it only lets gas flow one way. The direction the gas flows is opposite the red handle on it, which is confusing to people used to the red stripe on pumps pointing the way.
+ Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
+ Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon". Actvating a panic siphon without very good reason will kill someone. Don't do it.
+ Meter: A little box with some gagues and numbers. Fasten it to any pipe or manifold, and it'll read you the pressure in it. Very useful.
+ Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2", for reference.
+ Can output this gas at pressures from 0-4500 kPa.
+ Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe,
+ the rest will continue out the other side. Can also output from 0-4500 kPa.
+
+
+ Will not set you on fire.
+ These systems are used to transfer heat only between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt),
+ a higher temperature will raise pressure, and a lower one will lower temperature.
+
+ Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
+ Bent Pipe: Take a wild guess.
+ Junction:Junction:The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
+ Heat Exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch.
+ This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions...
+
+
+ That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please.
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/evaguide
+ name = "EVA Gear and You: Not Spending All Day Inside"
+ icon_state = "evabook"
+ author = "Maria Crash, Senior Atmospherics Technician"
+ title = "EVA Gear and You: Not Spending All Day Inside"
+ dat = {"
+
+
+
+
+
+
+
+
+ - A forward on using EVA gear
+ - Donning a Civilian Suits
+ - Putting on a Hardsuit
+ - Final Checks
+
+
+
+
+ Or: How not to suffocate because there's a hole in your shoes
+
+ EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training,
+ but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into two sections:
+ Civilian suits and hardsuits.
+
+
+ The bulkiest things this side of Alpha Centauri
+ These suits are the grey ones that are stored in EVA. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiaion or physical impact.
+ As Medical, Engineering, Security, and Mining all have hardsuits of their own, these don't see much use, but knowing how to put them on is quite useful anyways.
+
+ First, take the suit. It should be in three pieces: A top, a bottom, and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however,
+ put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit,
+ but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness.
+ The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it.
+ Put on the helmet now, straight forward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened.
+ There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit,
+ but it is reccomended to have a full-sized tank on your back for EVA.
+
+
+ Heavy, uncomfortable, still the best option.
+ These suits come in Engineering, Mining, and the Armory. There's also a couple Medical Hardsuits in EVA. These provide a lot more protection than the standard suits.
+
+ Similarly to the other suits, these are split into three parts. Fastening the pant and top are mostly the same as the other spacesuits, with the exception that these are a bit heavier,
+ though not as bulky. The helmet goes on differently, with the air tube feeing into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise,
+ and then is screwed in for one and a quarter full rotations clockwise, leaving the faceplate directly in front of you. There is a small button on the right side of the helmet that activates the helmet light.
+ The tanks that fasten onto the side slot are emergency tanks, as well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.
+
+
+ Are all seals fastened correctly?
+ Do you either have shoes on under the suit, or magnetic boots on over it?
+ Do you have a mask on and internals on the suit or your back?
+ Do you have a way to communicate with the station in case something goes wrong?
+ Do you have a second person watching if this is a training session?
+
+ If you don't have any further issues, go out and do whatever is necessary.
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/faxes
+ name = "A Guide to Faxes"
+ desc = "A NanoTrasen-approved guide to writing faxes"
+ icon_state = "book6"
+ author = "NanoTrasen"
+ title = "A Guide to Faxes"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - What's a Fax?
+ - When to Fax?
+ - How to Fax?
+
+
+
+
+ Faxes are your main method of communicating with the NAS Trurl, better known as Central Command.
+ Faxes allow personnel on the station to maintain open lines of communication with the NAS Trurl, allowing for vital information to flow both ways.
+ Being written communications, proper grammar, syntax and typography is required, in addition to a signature and, if applicable, a stamp. Failure to sign faxes will lead to an automatic rejection.
+ We at NanoTrasen provide Fax Machines to every Head of Staff, in addition to the Magistrate, NanoTrasen Representative, and Internal Affairs Agents.
+ This means that we trust the recipients of these fax machines to only use them in the proper circumstances (see When to Fax?).
+
+
+ While it is up to the discretion of each individual person to decide when to fax Central Command, there are some simple guidelines on when to do this.
+ Firstly, any situation that can reasonably be solved on-site, should be handled on-site. Knowledge of Standard Operating Procedure is mandatory for everyone with access to a fax machine.
+ Resolving issues on-site not only leads to more expedient problem-solving, it also frees up company resources and provides valuable work experience for all parties involved.
+ This means that you should work with the Heads of Staff concerning personnel and workplace issues, and attempt to resolve situations with them. If, for whatever reason, the relevent Head of Staff is not available or receptive, consider speaking with the Captain and/or NanoTrasen Representative.
+ If, for whatever reason, these issues cannot be solved on-site, either due to incompetence or just plain refusal to cooperate, faxing Central Command becomes a viable option.
+ Secondly, station status reports should be sent occasionally, but never at the start of the shift. Remember, we assign personnel to the station. We do not need a repeat of what we just signed off on.
+ Thirdly, staff/departmental evaluations are always welcome, especially in cases of noticeable (in)competence. Just as a brilliant coworker can be rewarded, an incompetent one can be punished.
+ Fourthly, do not issue faxes asking for sentences. You have an entire Security department and an associated Detective, not to mention on-site Space Law manuals.
+ Lastly, please pay attention to context. If the station is facing a massive emergency, such as a Class 7-10 Blob Organism, most, if not all, non-relevant faxes will be duly ignored.
+
+
+ Sending a fax is simple. Simply insert your ID into the fax machine, then log in.
+ Once logged in, insert a piece of paper and select the destination from the provided list. Remember, you can rename your fax from within the fax machine's menu.
+ You can send faxes to any other fax machine on the station, which can be a very useful tool when you need to issue broad communications to all of the Heads of Staff.
+ To send a fax to Central Command, simply select the correct destination, and send the fax. Keep in mind, the communication arrays need to recharge after sending a fax to Central Command, so make sure you sent everything you need.
+ Lastly, paper bundles can also be faxed as a single item, so feel free to bundle up all relevant documentation and send it in at once.
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_science
+ name = "Science Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all scientific activities."
+ icon_state = "book6"
+ author = "Nanotrasen"
+ title = "Science Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Research Director
+ - Roboticist
+ - Scientist
+ - Geneticist
+ - Exotic Implants
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+ Code Green
+
+ - The Research Director must make sure Research is being done. Research must be completed by the end of the shift, assuming Science is provided the materials for it by Supply;
+ - The Research Director is permitted to carry a telescopic baton;
+ - The Research Director is permitted to carry their Reactive Teleport Armour on their person. However, it is highly recommended they keep it inactive unless necessary, for personal safety;
+ - The Research Director is not permitted to authorize the construction of AI Units without the Captain's approval. An exception is made if the station was not provided with an AI Unit, or a previous AI Unit had to be destroyed.
+ - The Research Director is not permitted to authorize Anomalous Artifacts to be brought onto the station prior to full testing and cataloguing;
+ - The Research Director must keep the Communications Decryption Key on their person at all times, or at least somewhere safe and out of reach;
+ - The Research Director is permitted to add beneficial scripts to Telecommunications;
+ - The Research Director is permitted to change the AI Unit's lawset, provided they receive general approval from the Captain and another Head of Staff. If there are no other Heads of Staff available, Captain approval will suffice;
+ - The Research Director must work with Robotics to make sure all Cyborgs remain slaved to the station's AI Unit, except in such a situation where the AI Unit has been subverted or is malfunctioning.
+
+
+ Code Blue
+
+ - All Guidelines carry over from Code Green
+
+
+ Code Red
+
+ - Guidelines 1, 3, 4, 6, 7, 8 and 9 carry over from Code Green;
+ - In addition to the a telescopic baton, the Research Director is permitted to carry a single weapon created in the Protolathe, provided they receive authorization from the Head of Security. Exception is made during extreme emergencies, such as Nuclear Operatives or Blob Organisms.
+
+
+
+
+ Code Green
+
+ - The Roboticist is not permitted to construct Combat Mechs without express permission from the Captain and/or Head of Security. This refers to the Durand, Gygax and Phazon. If permitted, the Mechs is to be delivered to the Armory for storage. The Research Director is placed under the same restrictions;
+ - The Roboticist is freely permitted to construct Utility Mechs, along with any assorted Utility Equipment. This refers to Ripleys (to be handed to Mining), Firefighting Ripleys (to be handed to Atmospherics) and the Odysseus Medical Mech (to be handed to Medical). The HONK Mech is not to be constructed without full approval by the Research Director and Captain;
+ - The Roboticist is freely permitted to construct Cyborgs and all assorted equipment;
+ - The Roboticist is not permitted to transfer personnel MMIs into Cyborgs without express written consent from the person in question. The consent form should be kept safe;
+ - The Roboticist is not permitted to construct AI Units without express consent from the Captain;
+ - The Roboticist must place a Tracking Beacon on all constructed Mechs;
+ - The Roboticist must work together with the Research Director to make sure all Cyborgs remain slaved to the station's AI Unit, except in such a situation where the AI Unit has been subverted or is malfunctioning;
+ - The Roboticist must DNA-Lock all parked Mechs prior to delivery. DNA-Lock must be removed when the Mech is delivered to its final destination
+
+
+ Code Blue
+
+ - Guidelines 2, 3, 4, 5, 6, 7 and 8 carry over from Code Green;
+ - The Roboticist is permitted to construct Combat Mechs without prior consent, but must deliver them to the Armory for storage. Failure to comply will result in the Combat Mech being destroyed. Exception is made for extreme emergencies, such as a Blob Organism or Nuclear Operatives, where the Roboticist may pilot the Mech themselves. However, even in these circumstances, the Mech must be delivered to the Armory after the emergency is over. The Research Director is placed under the same restrictions;
+
+
+ Code Red
+
+ - Guidelines 3, 4, 5, 6, 7, 8 and 9 carry over from Code Green;
+ - All Guidelines carry over from Code Blue.
+
+
+
+
+ Code Green
+
+ - Scientists are not permitted to bring Grenades outside of Science;
+ - Scientists are not permitted to bring Toxins Bombs outside of Science. Exception is made if the Toxins Bomb is handed to Mining, as it can be useful for mining operations;
+ - While not mandatory, it is highly recommended that Scientists give a prior warning before a Toxins Test. This must be done via the Common Communication Channel, with at least ten (10) seconds between the warning and detonation;
+ - Scientists are not permitted to use Telescience equipment to acquire objects, items or personnel they do not have access to;
+ - Scientists are, however, permitted to use Telescience equipment to recover dead personnel, provided Medical cannot reach them;
+ - Scientists must, at all times, keep live slimes and Golden Extract-based lifeforms inside Xenobiology pens, except when transporting them to new cells. Peaceful Golden Extract lifeforms may be released with the express permission of the Research Director. In addition, injecting plasma into Golden Extract is strictly forbidden;
+ - Scientists are not permitted to bring Anomalous Artifacts aboard the station without express verbal consent from the Research Director. Regular Xenoarchaeological artifacts are permitted;
+ - Scientists are not permitted to construct the Portable Wormhole Generator without express permission from the Research Director. In addition, Scientists are not to hand out Weapon Lockboxes to any non-Security or non-Command personnel without express permission from the Head of Security;
+
+
+ Code Blue
+
+ - Guidelines 3, 4, 5, 6, 7 and 9 carry over from Code Green;
+ - Scientists are permitted to bring Grenades outside of Science, but only for delivery to the Armory;
+ - Scientists are permitted to bring Toxins Bombs outside of Science, but only for delivery to the Armory. In addition, the Mining exception still applies
+
+
+ Code Red
+
+ - Guidelines, 3, 4, 5, 6, 7 and 9 carry over from Code Green;
+ - All Guidelines carry over from Code Blue.
+
+
+
+
+ Code Green
+
+ - The Geneticist is not permitted to ignore Cloning, and must provide Clean SE Injectors when required, as well as humanized animals if required for Surgery. In addition, the Geneticist must make sure that Cloning is stocked with Biomass;
+ - The Geneticist is permitted to test Genetic Powers on themselves. However, they are not to utilize these powers on any crewmembers, nor abuse them to obtain items/personnel outside their access;
+ - The Geneticist is permitted to grant Genetic Powers to Command Staff at their discretion, provided prior permission is requested and granted. All staff must be warned of the full effects of the SE Injector. The Geneticist is not, however, obligated to grant powers, unless the Research Director issues a direct order;
+ - The Geneticist is not permitted to grant Powers to non-Command Staff without express verbal consent from the Research Director. Both the Chief Medical Officer and the Research Director maintain full authority to forcefully remove these Powers if they are abused;
+ - The Geneticist must place all discarded humanized animals in the Morgue. It is recommended that said discarded humanized animals be directed to the Crematorium;
+ - The Geneticist is not permitted to provide body doubles, unless the Research Director approves it. In addition, Security is to be notified of all doubles;
+ - The Geneticist is not permitted to alter personnel's UI Status, unless it has been previously tampered with by hostile elements, or permission is given;
+ - The Geneticist is not permitted to use sentient humanoids as test subjects unless the sentient humanoid has granted their permission, on paper.
+
+
+ Code Blue
+
+ - All Guidelines carry over from Code Green
+
+
+ Code Red
+
+ - All Guidelines carry over from Code Green. In regards to Guideline 4, the Geneticist is now permitted to grant Powers to Security personnel, under the same conditions as detailed in Guideline 3.
+
+
+
+
+ Exotic Implants refer to Xeno Organs, Cybernetic Implants or any such exotic materials.
+
+ - General utility implants (such as Welding Shield, Nutriment or Reviver) are unregulated, and may be handed out freely;
+ - X-Ray Vision and Thermal Vision implants may be handed out freely, but may have their implantation vetoed by the Chief Medical Officer and/or Research Director (see below);
+ - Medical HUDs must be approved by the Chief Medical Officer before implantation, and Security HUDs require express permission from the Head of Security or Warden;
+ - Combat-capable Implants (such as the CNS Rebooter or Anti-Drop) are not be handed out without express permission from the Head of Security;
+ - Cybernetic Implantation should be performed in Surgery or any such sterilized environment, to reduce the risk of internal infection. If no Surgeons or Doctors are available, the Roboticist can fill in;
+ - The Chief Medical Officer and Research Director have the power to veto any Cybernetic Implantation or Xeno Organ Implantation if they believe it threatens the stability of the station or crew. Only the Captain may override this veto;
+ - Xeno Organs may be harvested at will, but may not be implanted without express permission from the Chief Medical Officer. Egg-Laying Organs from Xenomorph Lifeforms are strictly forbidden;
+ - Failure to follow these Guidelines makes the offending party liable to having their Exotic Implants forcefully removed.
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_medical
+ name = "Medical Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all medical activities."
+ icon_state = "book7"
+ author = "Nanotrasen"
+ title = "Medical Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Chief Medical Officer
+ - Medical Doctor
+ - Chemist
+ - Geneticist
+ - Virologist
+ - Paramedic
+ - Psychologist
+ - Surgery
+ - Viral Outbreak Procedures
+ - Coroner Procedures
+ - Exotic Implants
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ - The Chief Medical Officer is permitted to carry a regular Defibrillator or a Compact Defibrillator on their person at all times;
+ - The Chief Medical Officer is permitted to carry a telescopic baton. In case Genetic Powers need to be forcefully removed, they are cleared to carry a Syringe Gun;
+ - The Chief Medical Officer is not permitted to allow the creation of poisonous or explosive mixtures in Chemistry without express consent from the Captain or, failing that, the presence of a clear and urgent danger to the integrity of the station, except of course in situations where Chemical Implants are required;
+ - The Chief Medical Officer is not permitted to allow the release of any virus without a full list of its symptoms, as well as the creation of a vial of antibodies, to be kept in a secure location. The virus may not have any harmful symptoms whatsoever, though neutral/harmless symptoms are permitted;
+ - The Chief Medical Officer must make sure that any cloneable corpses are, in fact, cloned.
+
+
+
+
+
+ - Though not mandatory, it is recommended that Doctors wear Sterile Masks and Latex/Nitrile gloves when handling patients. This Guideline becomes mandatory during Viral Outbreaks;
+ - Nurses should focus on helping Medical Doctors and Surgeons in whatever they require, and tending to patients that require light care. If necessary, they can stand in for regular Medical Doctor duties;
+ - Surgeons are expected to fulfill the duties of regular Medical Doctors if there are no active Surgical Procedures undergoing;
+ - Medical Doctors must ensure there is at least one (1) Defibrillator available for use, at all times, next to or near the Cryotubes;
+ - Medical Doctors must maintain the entirety of Medbay in an hygienic state. This includes, but is not limited to, cleaning organic residue, fluids and corpses;
+ - Medical Doctors must place all corpses inside body bags. If there is an assigned Coroner, the Morgue Trays must be correctly tagged;
+ - Medical Doctors must, together with Geneticists and Chemists, make sure that Cloning is stocked with Biomass. In addition, Medical Doctors must make sure that the Morgue does not contain cloneable corpses;
+ - Medical Doctors must certify that all cloned personnel are put in the Cryotubes after Cloning, and receive either a dose of Mutadone or a Clean SE Injector, in addition to Mannitol. An exception is made if the Cloning Pod was fully upgraded by Science;
+ - Medical Doctors are not permitted to leave Medbay to perform recreational activities if there are unattended patients requiring treatment;
+ - Medical Doctors must stabilize patients before delivering them to Surgery. If the patient presents Internal Bleeding, they are to be rushed to Surgery post haste.
+
+
+
+
+
+
+ - The Chemist is not permitted to experiment with explosive mixtures;
+ - The Chemist is not permitted to experiment with poisonous mixtures and/or narcotics;
+ - The Chemist is not permitted to experiment with Life or other Omnizine-derived mixtures apart from Omnizine or Strange Reagent;
+ - The Chemist is not permitted to produce alcoholic beverages;
+ - Chemists must, together with Geneticists and Medical Doctors, make sure that Cloning is stocked with Biomass;
+ - The Chemist must ensure that the Medical Fridge is stocked with at least enough medication to handle Brute, Burn, Respiratory, Toxic and Brain damage. Failure to follow this Guideline within thirty (30) minutes is to be considered a breach of Standard Operating Procedure
+ - The Chemist is not allowed to leave Chemistry unattended if the Medical Fridge is devoid of Medication, except in such a case as Chemistry is unusable or if Fungus needs to be collected
+
+
+
+
+ Code Green
+
+ - The Geneticist is not permitted to ignore Cloning, and must provide Clean SE Injectors when required, as well as humanized animals if required for Surgery. In addition, the Geneticist must make sure that Cloning is stocked with Biomass;
+ - The Geneticist is permitted to test Genetic Powers on themselves. However, they are not to utilize these powers on any crewmembers, nor abuse them to obtain items/personnel outside their access;
+ - The Geneticist is permitted to grant Genetic Powers to Command Staff at their discretion, provided prior permission is requested and granted. All staff must be warned of the full effects of the SE Injector. The Geneticist is not, however, obligated to grant powers, unless the Research Director issues a direct order;
+ - The Geneticist is not permitted to grant Powers to non-Command Staff without express verbal consent from the Research Director. Both the Chief Medical Officer and the Research Director maintain full authority to forcefully remove these Powers if they are abused;
+ - The Geneticist must place all discarded humanized animals in the Morgue. It is recommended that said discarded humanized animals be directed to the Crematorium;
+ - The Geneticist is not permitted to provide body doubles, unless the Research Director approves it. In addition, Security is to be notified of all doubles;
+ - The Geneticist is not permitted to alter personnel's UI Status, unless it has been previously tampered with by hostile elements, or permission is given;
+ - The Geneticist is not permitted to use sentient humanoids as test subjects unless the sentient humanoid has granted their permission, on paper.
+
+
+ Code Blue
+
+ - All Guidelines carry over from Code Green
+
+
+ Code Red
+
+ - All Guidelines carry over from Code Green. In regards to Guideline 4, the Geneticist is now permitted to grant Powers to Security personnel, under the same conditions as detailed in Guideline 3.
+
+
+
+
+
+ - The Virologist must always wear adequate protection (such as a Biosuit and Internals for Airborne Viruses) when handling infected personnel and Test Animals. Exception is made for IPC Virologists, for obvious reasons;
+ - The Virologist must only test viral samples on the provided Test Animals. Said Test Animals are to be maintained inside their pen, and disposed of via Virology's Disposals Chutes if dead, to prevent possible contamination. In addition, the Virologist may not, under any circumstances whatsoever, leave Virology while infected by a Viral Pathogen that spreads by Contact or Airborne means, unless permitted by the Chief Medical Officer;
+ - The Virologist may not, under any circumstance whatsoever, release an active virus without prior consent from Chief Medical Officer. Contact and/or Airborne viruses may only be released with consent from the Chief Medical officer and Captain. In the event a Contact and/or Airborne virus is released, the crew must be informed, and Vaccines should be ready for any personnel that choose to opt out of being infected;
+ - The Virologist must ensure that all Viral Samples are kept on their person at all times, or at the very least in a secure location (such as the Virology Fridge);
+ - The Virologist must work together with Medical Staff, especially Chemistry, if there is a cure that requires manufacturing;
+ - In the event of a lethal Viral Outbreak, the Virologist must work together with the Chief Medical Officer and/or Chemists and/or Bartender to produce a cure. Failure to keep casualties down to, at most, 25% of the station's crew is to be considered a breach of Standard Operating Procedure for everyone involved.
+
+
+
+
+
+ - The Paramedic is not permitted to perform Field Surgery unless there are no available Medical Doctors or the Operating Rooms are unusable;
+ - The Paramedic is permitted to perform Surgical Procedures inside an Operating Room. However, Doctors/Surgeons should take precedence;
+ - The Paramedic is fully permitted to carry a Defibrillator on their person at all times, provided they leave at least one (1) Defibrillator for use in Medbay;
+ - The Paramedic must stabilize all patients before bringing them to the Medical Bay. If the patient presents with Internal Bleeding, they are to be rushed to Surgery post haste;
+ - In such a case as a patient is found dead, and cannot be brought back via Defibrillation, the Paramedic must ensure that said patient is brought to Cloning, and Medbay is notified;
+ - The Paramedic must carry, at all times, enough materials to provide for adequate first aid of all Major Injury Types (Brute, Burn, Toxic, Respiratory and Brain)
+
+
+
+
+
+ - The Psychologist may perform a full psychological evaluation on anyone, along with any potential treatment, provided the person in question seeks them out;
+ - The Psychologist may not force someone to receive therapy if the person does not want it. Exception is made for violent criminals, if the Head of Security or Magistrate orders it;
+ - The Psychologist is not permitted to administer any medication without consent from their patient;
+ - The Psychologist is not permitted to muzzle or straightjacket anyone without express permission from the Chief Medical Officer or Head of Security. An exception is made for violent and/or out of control patients;
+ - The Psychologist may recommend a patient's demotion if they find their psychological condition to be unfit;
+ - The Psychologist may request to consult prisoners in Permanent Imprisonment. This must happen inside the Brig, preferably inside the Permabrig, and only with Warden and/or Head of Security authorization. This should be done under the supervision of a member of Security with Permabrig access
+
+
+
+
+
+ - Attending Surgeon must use Latex/Nitrile gloves in order to prevent infection. Though not mandatory, a Sterile Mask is recommended;
+ - Attending Surgeon is to keep the Operating Room in an hygienic condition at all times, again, to prevent infection;
+ - Attending Surgeon is to wash his/her hands between different patients, again, to prevent infection;
+ - Attending Surgeon is to use either Anesthetics or Sedatives (for species that cannot breathe Anesthetics) during Surgical Procedures. Exception is made if the patient requests otherwise;
+ - Attending Surgeon is not to remove any legal Implants (such as Mindshield or Tracking Implants) from the patient, unless requested by Security;
+ - If a patient requests that a lost limb be replaced with an organic, rather than mechanical, substitute, said limb must be harvested from a compatible humanized Test Animal (such as Monkeys for Humans, or Farwas for Tajarans). Exception is made if the patient deliberately requests otherwise;
+ - Attending Surgeon is not to bring any of the Surgical Tools outside of their respective Operating Room, and must in fact ensure the Operating Room maintains its proper inventory. This includes ensuring that the Anesthetics Equipment be kept inside the OR
+
+
+
+
+ Definition: A Viral Outbreak is defined as a situation where a Viral Pathogen has infected a significant portion of the crew (>10%)
+
+ - All Medbay personnel are to contribute in fighting the outbreak if there are no other critical patients requiring assistance. Eliminating the Viral Threat becomes number one priority;
+ - Personnel are to be informed of known symptoms, and directed to Medbay immediately if they are suffering from them;
+ - All infected personnel are to be confined to either an Isolated Room, or Virology;
+ - A blood sample is to be taken from an infected person, for study;
+ - If any infected personnel attempt to leave containment, Medbay Quarantine is to be initiated immediately, and only lifted when more patients need to be admitted, or the Viral Outbreak is over;
+ - A single infected person may volunteer to receive a dose of Radium in order to develop Antibodies. Radium must not be administered without consent. Otherwise, animal testing is to be conducted in order to obtain Antibodies;
+ - Once Antibodies are produced, they are to be diluted, then handed out to all infected personnel. Injecting infected personnel with Radium after Antibodies have been extracted is forbidden. In the event of a large enough crisis, directly injecting blood with the relevant Antibodies is permissible;
+ - Viral Pathogen should be cataloged and analyzed, in case any stray cases remained untreated;
+ - Cured personnel should have a sample of their blood removed for the purpose of creating antibodies, until there are no infected personnel left;
+ - In case the Viral Pathogen leads to fluid leakage, cleaning these fluids is to be considered top priority;
+ - Once the Viral Outbreak is over, all personnel are to return to regular duties.
+
+
+
+
+
+ - For the sake of hygiene, the Coroner should wear a Sterile Mask when handling corpses;
+ - The Coroner must inject/apply Formaldehyde to all corpses, and place them in body bags;
+ - The Coroner must perform a full autopsy on all corpses, and keep a record of it, in written format. If foul play is suspected, Security must be contacted;
+ - The Coroner must correctly tag the Morgue Trays in order to identify the corpse within, as well as Cause of Death;
+ - The Coroner must ensure Security-based DNR Notices (such as executed personnel, for instance) are respected;
+ - The Coroner must ensure that every ID from unclonable bodies is delivered to either the relevant Head of Staff, or the Head of Personnel. This applies to any Medbay personnel placing a body in the Morgue
+
+
+
+
+ Exotic Implants refer to Xeno Organs, Cybernetic Implants or any such exotic materials.
+
+ - General utility implants (such as Welding Shield, Nutriment or Reviver) are unregulated, and may be handed out freely;
+ - X-Ray Vision and Thermal Vision implants may be handed out freely, but may have their implantation vetoed by the Chief Medical Officer and/or Research Director (see below);
+ - Medical HUDs must be approved by the Chief Medical Officer before implantation, and Security HUDs require express permission from the Head of Security or Warden;
+ - Combat-capable Implants (such as the CNS Rebooter or Anti-Drop) are not be handed out without express permission from the Head of Security;
+ - Cybernetic Implantation should be performed in Surgery or any such sterilized environment, to reduce the risk of internal infection. If no Surgeons or Doctors are available, the Roboticist can fill in;
+ - The Chief Medical Officer and Research Director have the power to veto any Cybernetic Implantation or Xeno Organ Implantation if they believe it threatens the stability of the station or crew. Only the Captain may override this veto;
+ - Xeno Organs may be harvested at will, but may not be implanted without express permission from the Chief Medical Officer. Egg-Laying Organs from Xenomorph Lifeforms are strictly forbidden;
+ - Failure to follow these Guidelines makes the offending party liable to having their Exotic Implants forcefully removed.
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_engineering
+ name = "Engineering Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all engineering activities."
+ icon_state = "book3"
+ author = "Nanotrasen"
+ title = "Engineering Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Chief Engineer
+ - Station Engineer
+ - Atmospherics Technician
+ - Mechanic
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ - The Chief Engineer must make sure that the Gravitational Singularity Engine and/or Tesla Engine and/or Solar Panels are fully set up and wired before any further action is taken by themselves or their team;
+ - The Chief Engineer, along with the Research Director, is responsible for maintaining the integrity of Telecommunications. The Chief Engineer may not upload malicious scripts or in any way hinder the proper functionality of Telecommunications, and must diagnose and repair any issues that arise;
+ - The Chief Engineer is not to authorize the ordering of a Supermatter Shard before any power source is fully set up;
+ - The Chief Engineer is bound to the same rules regarding the axe as Atmospheric Technicians;
+ - The Chief Engineer is permitted to carry a telescopic baton and a flash;
+ - The Chief Engineer is responsible for maintaining the integrity of the Gravitational Singularity Engine and/or the Supermatter Engine and/or the Tesla Engine. Neglecting this duty is grounds for termination should the Engine malfunction;
+ - The Chief Engineer is responsible for maintaining the integrity of the Cyberiad's Atmospherics System. Failure to maintain this integrity is grounds for termination;
+ - The Chief Engineer may declare an area "Condemned", if it is damaged to the point where repairs cannot reasonably be completed within an acceptable frame of time;
+ - The Chief Engineer is permitted to grant Building Permits to crewmembers, but must keep the Station Blueprints in a safe location at all times.
+
+
+
+
+
+ - Engineers must properly activate and wire the Gravitational Singularity Engine and/or Tesla Engine and/or the Solar Panels at the start of the shift, before any other actions are undertaken;
+ - Engineers are responsible for maintaining the integrity of the Gravitational Singularity Engine and/or the Supermatter Engine and/or the Tesla Engine. Neglecting this duty is grounds for termination should the Engine malfunction;
+ - Engineers are not permitted to construct additional power sources (Supermatter Engines, additional Tesla Engines, additional Gravitational Singularity Engines or additional Solar Panels) until at least one (1) power source is correctly wired and set up;
+ - Engineers are permitted to carry out solo reconstruction/rebuilding/personal projects if there is no damage to the station that requires fixing;
+ - Engineers must periodically check on the Gravitational Singularity Engine, if it is the chosen method of power generation, in intervals of, at most, thirty (30) minutes. While the Tesla Engine is not as prone to malfunction, this action should still be undertaken for it;
+ - Engineers must constantly monitor the Supermatter Engine, if it is the chosen method of power generation, if it is currently active (ie, under Emitter Fire). This is not negotiable;
+ - Engineers must respond promptly to breaches, regardless of size. Failure to report within fifteen (15) minutes will be considered a breach of Standard Operating Procedure, unless there are no spare Engineers to report or an Atmospheric Technician has arrived on scene first. All Hazard Zones must be cordoned off with Engineering Tape, for the sake of everyone else;
+ - Engineers are permitted to hack doors to gain unauthorized access to locations if said locations happen to require urgent repairs;
+ - Engineers are to maintain the integrity of the Cyberiad's Power Network. In addition, hotwiring the Gravitational Singularity Engine, Supermatter Engine or Tesla Engine is strictly forbidden;
+ - Engineers must ensure there is at least one (1) engineering hardsuit available on the station at all times, unless there is an emergency that requires the use of all suits.
+
+
+
+
+
+ - Atmospheric Technicians are permitted to completely repipe the Atmospherics Piping Setup, provided they do not pump harmful gases into anywhere except the Turbine;
+ - Atmospheric Technicians are not permitted to create volatile mixes using Plasma and Oxygen, nor are they permitted to create any potentially harmful mixes with Carbon Dioxide and/or Nitrous Oxide. An exception is made when working with the Turbine;
+ - Atmospheric Technicians are permitted to cool Plasma and store it for later use in Radiation Collectors. Likewise, they are permitted to cool Nitrogen or Carbon Dioxide and store it for use as coolant for the Supermatter Engine;
+ - Atmospheric Technicians are not permitted to take the axe out of its case unless there is an immediate and urgent threat to their life or urgent access to crisis locations is necessary. The axe must be returned to the case afterwards, and the case locked;
+ - Atmospheric Technicians are not permitted to tamper with the default values on Air Alarms. They are, however, permitted to create small, acclimatized rooms for species that require special atmospheric conditions (such as Plasmamen and Vox), provided they receive express permission from the Chief Engineer;
+ - Atmospheric Technicians must periodically check on the Central Alarms Computer, in periods of, at most, thirty (30) minutes;
+ - Atmospheric Technicians must respond promptly to piping and station breaches. Failure to report within fifteen (15) minutes will be considered a breach of Standard Operating Procedure, unless there are no spare Atmospheric Technicians to report, or an Engineer has arrived on scene first. All Hazard Zones must be cordoned off with Engineering Tape, for the sake of everyone else
+
+
+
+
+
+ - The Mechanic is not permitted to fit any weaponry onto constructed Space Pods without express permission by the Head of Security;
+ - The Mechanic is permitted to construct Space Pods for any crewmember that requests one, provided the Pods do not have any weaponry. Anyone possessing a Pod is to follow Mechanic SOP, Civilians included;
+ - The Mechanic is not permitted to enter the Security Pod Bay, unless the Security Pod Pilot or Head of Security permit it;
+ - The Mechanic is not permitted to bring any Space Pod into the actual station
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_service
+ name = "Service Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all service activities."
+ icon_state = "book4"
+ author = "Nanotrasen"
+ title = "Service Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Chef
+ - Bartender
+ - Botanist
+ - Clown
+ - Mime
+ - Chaplain
+ - Janitor
+ - Barber
+ - Librarian
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ - The Chef is not permitted to use the corpses of deceased personnel for meat unless given specific permission from the Chief Medical Officer. Exception is made for changelings and any other executed personnel not slated for Borgifications;
+ - The Chef is permitted to use Ambrosia and other such light narcotics in the production of food;
+ - The Chef must produce at least three (3) dishes of any food within twenty (20) minutes. Failure to do so is to be considered a breach of Standard Operating Procedure;
+ - The Chef is not permitted to leave the kitchen unattended for longer than fifteen (15) minutes if there is no food available for consumption. Exception is made if there are no ingredients, or if the Kitchen is unusable/a hazard zone
+
+
+
+
+
+ - The Bartender is not permitted to carry their shotgun outside the bar. However, they may obtain permission from the Head of Security to shorten the barrel for easier transportation. Shortening the barrel without authorization is grounds for confiscation of the Bartender's shotgun;
+ - The Bartender is permitted to use their shotgun on unruly bar patrons in order to throw them out if they are being disruptive. They are not, however, permitted to apply lethal, or near-lethal force;
+ - The Bartender is exempt from legal ramifications when dutifully removing unruly (ie, overtly hostile) patrons from the Bar, provided, of course, they followed Guideline 2;
+ - The Bartender is not permitted to possess regular (ie, lethal) shotgun ammunition. Only beanbag slugs are permitted. Exception is made during major emergencies, such as Nuclear Operatives or Blob Organisms;
+ - The Bartender has full permission to forcefully throw out anyone who climbs over the bar counter without permission, up to and including personnel who may have access to the side windoor. They are not, however, permitted to do so if the person in question uses the door, or is on an active investigation;
+ - The Bartender is permitted to ask for monetary payment in exchange for drinks
+
+
+
+
+
+ - Botanists are permitted to grow narcotics, presuming they do not distribute it;
+ - Botanists must provide the Chef with adequate Botanical Supplies, per the Chef's request;
+ - Botanists are not permitted to cause unregulated plantlife to spread outside of Hydroponics or other such designated locations;
+ - Botanists are not permitted to hand out (spatially) unstable Botanical Supplies to non-Hydroponics personnel;
+ - Botanists are not permitted to harvest Amanitin or other such plant/fungi-derived poisons, unless specifically requested by the Head of Security and/or Captain.
+
+
+
+
+
+ - The Clown is permitted to, and freely exempt from any consequences of, slipping literally anyone, assuming it does not interfere with active Security duty, or in any way endangers other personnel (such as slipping a Paramedic who's dragging a wounded person to Medbay);
+ - The Clown is not permitted to remove their Clown Shoes or Clown Mask. Exception is made if removing them is truly necessary for the sake of their clowning performance (such as being a satire of bad clowns);
+ - The Clown is not permitted to hold anything but water in their Sunflower;
+ - The Clown is not permitted to use Space Lube on anything. Exception is made during major emergencies involving hostile humanoids, whereby use of Space Lube may be condoned to help the crew;
+ - The Clown must legitimately attempt to be funny and/or entertaining at least once every fifteen (15) minutes. A simple pun will suffice. Continuously slipping people for no reason does not constitute humour. The joke is supposed to be funny for everyone;
+ - The Clown is permitted to, and freely exempt from any consequences of, performing any harmless prank that does not directly conflict with the above Guidelines
+
+
+
+
+
+
+ - The Mime is not permitted to talk, under any circumstance whatsoever. A Mime who breaks the Vow of Silence is to be stripped of their rank post haste;
+ - The Mime is permitted to use written words to communicate, either via paper or PDA, but are discouraged from automatically resorting to it when miming will suffice;
+ - The Mime must actually mime something at least once every thirty (30) minutes. Standing against an invisible wall will suffice.
+
+
+
+
+
+ - The Chaplain is not permitted to execute Bible Healing without consent, unless the person in question is in Critical Condition and there are no doctors, as doing so incurs the risk of causing brain damage;
+ - The Chaplain may not draw the Null Rod or Holy Sword on any personnel. Using these items on any personnel is grounds to have these items confiscated, unless there is a clear and present danger to their life;
+ - The Chaplain may not actively discriminate against any personnel on the grounds that it is a religious tenet of their particular faith;
+ - The Chaplain may not perform funerals for any personnel that have since been cloned;
+ - The Chaplain may, however, freely conduct funerals for non-cloneable personnel. All funerals must be concluded with the use of the Mass Driver or Crematorium.
+
+
+
+
+
+ - The Janitor must promptly respond to any call from the crew for them to clean. Failure to respond within fifteen (15) minutes is to be considered a breach of Standard Operating Procedure;
+ - If the Janitor's work leaves any surface slippery, they are to place wet floor signs, either physical or holographic. During major crises, such as Nuclear Operatives or Blob Organisms, the Janitor is to refrain from creating any slippery surfaces whatsoever;
+ - The Janitor is not to use Cleaning Foam Grenades as pranking implements. Cleaning Foam Grenades are to be used to clean large surfaces at once, only;
+ - During Viral Outbreaks, the Janitors must don their Biosuit, and focus on cleaning any biological waste, until such a point as the Viral Pathogen is deemed eliminated;
+ - The Janitor may not deploy bear traps anywhere, unless there are actually large wild animals on the station.
+
+
+
+
+
+ - The Barber may not give unsolicited haircuts/dye jobs to any personnel;
+ - The Barber must perform haircuts/dye jobs as per the request of personnel, and not from personal taste
+
+
+
+
+
+ - The Librarian is to keep at least one (1) shelf stocked with books for the station's personnel;
+ - The Librarian is permitted to conduct journalism on any part of the station. However, they are not entitled to participation in trials, and must receive authorization from the Head of Security or Magistrate.
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_supply
+ name = "Supply Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all supply activities."
+ icon_state = "book1"
+ author = "Nanotrasen"
+ title = "Supply Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Quarter Master
+ - Cargo Technician
+ - Shaft Miner
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ - The Quartermaster must ensure that every approved order is delivered within 15 minutes of having been placed and approved;
+ - In the event of a major crisis, such as Nuclear Operatives or a Blob Organism, expediency is to be favored over paperwork, as excessive bureaucracy may be detrimental to the well-being of the station;
+ - The Quartermaster is permitted to hack the Autolathe, or to have a Cargo Tech do so, assuming they do not produce illegal materials;
+ - The Quartermaster is not permitted to authorize the ordering of Security equipment and/or gear without express permission from the Head of Security and/or Captain. An exception is made during extreme emergencies, such as Nuclear Operatives or a Blob Organism, where said equipment is to be delivered to Security, post haste;
+ - The Quartermaster must ensure at least one copy of every Order Form (ie, the forms produced by the Requests Console) is kept inside Cargo. The same applies for Cargo Technicians;
+ - The Quartermaster is not permitted to produce .357 speedloaders for the Detective without express permission from the Head of Security;
+ - The Quartermaster is not to keep any illegal items that are flushed down Disposals, and must deliver them to Security. The same applies for Cargo Technicians and Shaft Miners;
+ - The Quartermaster is not permitted to hack the MULE Delivery Bots so that they may ride them, or that they may go faster. The same applies for Cargo Technicians and Shaft Miners;
+ - The Quartermaster is permitted to authorize non-departmental orders (such as a Medical Doctor asking for Insulated Gloves) without express permission from the respective Head of Staff (in this example, the Chief Engineer), utilizing their best judgement, although they may still request a stamped form. However, any breach of Standard Operating Procedure and/or Space Law that results from said order will also implicate the Quartermaster;
+ - The Quartermaster is not permitted to authorize a Supermatter Crate without express permission from the Chief Engineer.
+
+
+
+
+
+ - Cargo Technicians are bound to the same rules as the Quartermaster regarding restricted crates (see above);
+ - Cargo Technicians are not permitted to order items for sole personal use without express consent from the Quartermaster. Exception is made if there are more than 500 Supply Requisition Points available and no outstanding orders. Cargo Technicians may not deplete the entire stock of Requisition Points with this;
+ - Cargo Technicians are not permitted to order non-essential items (such as cats, or clothing) without express consent from the Quartermaster;
+ - Cargo Technicians are not permitted to force and/or break open locked crates. The same applies to the Quartermaster. Exception is made for Abandoned Crates found by Mining;
+ - Cargo Technicians are not permitted to authorize non-departmental orders (such as a Medical Doctor asking for Insulated Gloves) without express permission from the Quartermaster;
+ - Cargo Technicians must ensure that every approved order is delivered within 15 minutes of having been placed and approved;
+ - Cargo Technicians must send back all crates that have been ordered, with accompanying stamped manifest inside the crate;
+ - Cargo Technicians should ensure that a single Department does not fully drain the Ore Redemption Machine, as it can be utilized by multiple Departments;
+ - Cargo Technicians are not permitted to ask for money in exchange for legal orders. The same applies to the Quartermaster;
+ - Cargo Technicians are not permitted to trade items and/or favors in exchange for items with regular personnel without express consent from the Quartermaster.
+
+
+
+
+
+ - Shaft Miners are not permitted to bring Gibtonite aboard the station;
+ - Shaft Miners must deliver at least 1000 Points of mined material to the Ore Redemption Machine within one (1) hour;
+ - Shaft Miners are not permitted to hoard materials. All mined materials are to be left in the Ore Redemption Machine;
+ - Shaft Miners are not permitted to throw people into manufactured wormholes, nor are they permitted to trick people into using Bluespace Crystals, or throwing Bluespace Crystals at anyone;
+ - Shaft Miners are not permitted to mine their way into the Labor Camp;
+ - Should Shaft Miners encounter Xenomorph lifeforms, they are to report to Medbay immediately
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_security
+ name = "Security Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all security activities."
+ icon_state = "book2"
+ author = "Nanotrasen"
+ title = "Security Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Head of Security
+ - Security Officer
+ - Warden
+ - Detective
+ - Security Pod Pilot
+ - Brig Physician
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+ Code Green
+
+ - The Head of Security is permitted to carry out arrests under the same conditions as their Security Officers;
+ - The Head of Security is permitted to carry a taser, a flash, a flashbang, a stunbaton and a can of pepperspray. While permitted to carry their unique Energy Gun, they are discouraged from doing so for safety concerns, and should keep it on Stun/Disable;
+ - The Head of Security is not obligated to provide a trial, but is encouraged to allow legal representation should the suspect request it. This only applies to Capital Crimes;
+ - The Head of Security may not, under any circumstance, overrule a Magistrate, unless their decisions are blatantly breaking Standard Operating Procedure and/or Space Law, in which case Central Command is to be contacted as well;
+ - The Head of Security must follow the same guidelines as the Warden for Armory equipment, portable flashers and deployable barriers;
+ - The Head of Security is not permitted to collect equipment from the Armory to carry on their person;
+ - The Head of Security is permitted to either use their regular coat, or armored trenchcoat;
+ - The Head of Security is permitted to wear their unique gas mask;
+ - The Head of Security may not overrule established sentences, unless further evidence is brought to light or the prisoner in question attempts to escape
+
+
+ Code Blue
+
+ - All Guidelines carry over from Code Green. In regards to Guideline 2, the Head of Security is now encouraged to carry their unique Energy Gun
+
+
+ Code Red
+
+ - Guidelines 1, 3, 4, 5, 7, 8 and 9 are carried over from Code Green;
+ - The Head of Security is permitted to take whatever equipment they require from the Armory, provided they leave enough equipment for the rest of the Security force;
+ - The Head of Security is required to produce a Station Announcement regarding the nature of the confirmed threat that caused Code Red;
+ - Lethal Force is permitted if the target is confirmed to be guilty of Capital Crimes and actively, and aggressively, resists arrest
+
+
+
+
+ Code Green
+
+ - Security Officers are required to state the reasons behind an arrest before any further action is taken. Exception is made if the suspect refuses to stop;
+ - Security Officers must attempt to bring all suspects or witnesses to the Brig without handcuffing or incapacitating them. Should the suspect not cooperate, the officer may proceed as usual;
+ - No weapons are to be unholstered until the suspect attempts to run away or becomes actively hostile;
+ - Security Officers are permitted to carry a taser, a flash, a flashbang, a stunbaton and a can of pepperspray;
+ - Security Officers may not demand access to the interior of other Departments during regular patrols. However, asking for access from the Head of Personnel is still acceptable;
+ - Security officers are not permitted to have weapons drawn during regular patrols;
+ - Security officers are permitted to conduct searches, provided there is reasonable evidence/suspicion that the person in question has committed a crime. Any further searches require a warrant from the Head of Security, Captain or Magistrate;
+ - Lethal Force is not authorized unless there is a clear and immediate threat to the station's integrity or the Officer's life
+
+
+ Code Blue
+
+ - Guidelines 1, 2, 4 and 8 are carried over from Code Green;
+ - Security Officers are permitted to carry around any weapons or equipment available in the Armory, at the Warden's discretion, but never more than one at a time. Exception is made for severe emergencies, such as Blob Organisms or Nuclear Operatives;
+ - Security Officers are permitted to carry weapons in hand during regular patrols, although this is not advised;
+ - Security Officers are permitted to present weapons during arrests;
+ - Security Officers may demand entry to specific Departments during regular patrols;
+ - Security Officers may randomly search crewmembers, but are not allowed to apply any degree of force unless said crewmember acts overtly hostile. Crew who refuse to be searched may be stunned and cuffed for the search;
+ - Security Officers are permitted to leave prisoners bucklecuffed should they act hostile.
+
+
+ Code Red
+
+ - Guidelines 2, 3, 4, 5, 6 and 7 are carried over from Code Blue;
+ - Security Officers may arrest crewmembers with no stated reason if there is evidence they are involved in criminal activities;
+ - Security Officers may forcefully relocate crewmembers to their respective Departments if necessary;
+ - Lethal Force is permitted if the target is confirmed to be guilty of Capital Crimes and actively, and aggressively, resists arrest.
+
+
+
+
+ Code Green
+
+ - The Warden may not perform arrests if there are Security Officers active;
+ - The Warden must conduct a thorough search of every prisoner's belongings, including pockets, PDA slots, any coat pockets and suit storage slots;
+ - The Warden is not obligated to provide a trial, but is encouraged to allow legal representation should the suspect request it. This only applies to Capital Crimes;
+ - The Warden may not hand out any weapons or armour from the Armory, except for extra tasers. Hardsuits may be issued if emergency E.V.A action is required. Exception is made if there is an immediate threat that requires attention, such as Nuclear Operatives, or rioters;
+ - The Warden is permitted to carry a taser, a flash, a stunbaton, a flashbang and a can of pepperspray;
+ - The Warden may not place the portable flashers within the Brig;
+ - The Warden may not place the deployable barriers within the Brig;
+ - The Warden must read to every prisoner the crimes they are sentenced to;
+ - The Warden is not permitted to leave prisoners bucklecuffed to their beds. An exception is made if the prisoners acts overtly hostile or attempts to breach the cell in order to escape.
+
+
+ Code Blue
+
+ - Guidelines 1, 2, 3, 5 and 8 are carried over from Code Green;
+ - The Warden is permitted to hand out all equipment from the Armory. Energy and Laser guns are only to be handed out with Head of Security or Captain's approval, as they present a lethal risk, or if there is an immediate threat, such as Blob Organisms or Nuclear Operatives;
+ - The Warden is permitted to place the portable flashers inside the Brig;
+ - The Warden is permitted to place the deployable barriers inside the Brig
+
+
+ Code Red
+
+ - Guidelines 2, 3, 5 and 8 are carried over from Code Green;
+ - Guidelines 3 and 4 are carried over from Code Blue. In addition, the Warden may also carry any weapon from the Armory, but never more than one at a time;
+ - The Warden is permitted to distribute any weapon or piece of equipment in the Armory. This includes whatever Research has provided;
+ - The Warden is permitted to carry out arrests freely;
+ - Lethal Force is permitted if the target is confirmed to be guilty of Capital Crimes and actively, and aggressively, resists arrest.
+
+
+
+
+ Code Green
+
+ - The Detective may not perform arrests or searches unless given specific permission by the Head of Security or Warden. Exception is made if there are no active Officers or Warden;
+ - The Detective may not intentionally go around Security officers to perform arrests. If Officers are available, arrests may only be performed if there is an immediate violent threat to the Detective or anyone around them;
+ - The Detective may not unholster their revolver unless a clear and present danger to their life is present;
+ - The Detective may carry their revolver, along with spare ammunition;
+ - The Detective may carry their telescopic baton;
+ - Should the Detective be assaulted by a crewmember, they must use the issued Telescopic Baton to apprehend them. Using the revolver is permitted only if the crewmember attempts to escape and there are no Officers available for backup;
+ - The Detective may not search anyone in the Brig without permission from the Warden or Head of Security;
+ - The Detective is not permitted to modify their revolver to chamber lethal rounds, under any circumstance;
+ - The Detective must compile all evidence gathered into organized dossiers, and have at least one copy available at all times.
+
+
+ Code Blue
+
+ - Guidelines 4, 5, and 9 are carried over from Code Green;
+ - If sufficient forensic evidence is collected, and there are no Security Officers available at the time, the Detective is permitted to carry out arrests if a prior warning is given via Security Comms;
+ - The Detective is obligated to inform the suspect of the crimes they are accused;
+ - The Detective may search any suspect in the Brig;
+ - The Detective may pull aside any suspect for an interrogation, provided they receive authorization from the Head of Security or the Warden;
+ - The Detective may unholster their revolver whenever they deem it necessary, though it is recommended they do so sparsely
+ - Lethal Force is not permitted, unless there is a clear and immediate danger to the Detective's life
+
+
+ Code Red
+
+ - Guidelines 4, 5 and 9 carry over from Code Green;
+ - Guidelines 2, 3, 4, 5, 6 and 7 carry over from Code Blue;
+ - The Detective may freely discharge his revolver when handling confirmed threats;
+ - Lethal Force is permitted if the target is confirmed to be guilty of Capital Crimes and actively, and aggressively, resists arrest.
+
+
+
+
+ Code Green
+
+ - The Security Pod Pilot is permitted to carry out arrests under the same conditions as a Security Officer;
+ - The Security Pod Pilot is permitted to carry a taser, a flash, a stunbaton, a flashbang and a can of pepperspray;
+ - The Security Pod Pilot is not permitted to bring the Security Pod inside the station, except for designated Pod Bays;
+ - The Security Pod Pilot is not permitted to swap the Security Pod's weapon systems to the Laser Module unless a lethal threat, such as Space Carp or Attack Drones, is present;
+ - The Security Pod Pilot is not permitted to use the Laser Module during arrests, and must switch to the Disabler Module;
+ - The Security Pod Pilot must carry around a spare set of tools and energy cell, for their own sake;
+ - The Security Pod Pilot may immediately, and without warning, conduct arrests on individuals attempting to perform E.V.A actions near the AI Satellite. Exception is made if the AI Unit is malfunctioning;
+ - The Security Pod Pilot is not permitted to explore the area surrounding the station, and must therefore be confined to the immediate orbital area of the NSS Cyberiad, the NXS Klapaucius (the Telecomms Satellite) and the Mining/Research Asteroid. Exception is made if the Head of Security permits otherwise.
+
+
+ Code Blue
+
+ - Guidelines 1, 2, 7 and 8 are carried over from Code Green. In regards to Guideline 2, Pod Pilots are now also permitted to carry around Bulletproof Armour or Riot Gear;
+ - Pod Pilots are permitted to carry weapons in hand during regular patrols;
+ - Pod Pilots are permitted to present weapons during arrests;
+ - Pod Pilots may demand entry to specific Departments during regular patrols;
+ - Pod Pilots may randomly search crewmembers, but are not allowed to apply any degree of force unless said crewmember acts overtly hostile;;
+ - Pod Pilots are permitted to leave prisoners bucklecuffed should they act hostile.
+
+
+ Code Red
+
+ - Guidelines 1, 2, 7 and 8 carry over from Code Green;
+ - All Guidelines carry over from Code Blue;
+ - The Security Pod Pilot is permitted to carry any weapon from the Armory, but never more than one at a time. Exception is made for severe emergencies, such as Blob Organisms or Nuclear Operatives;
+ - The Security Pod Pilot is permitted to bring the Security Pod inside the station in extreme emergencies;
+ - The Security Pod Pilot is permitted to permanently install the Laser Module onto the Security Pod;
+ - Lethal Force is permitted if the target is confirmed to be guilty of Capital Crimes and actively, and aggressively, resists arrest.
+
+
+
+
+ Code Green
+
+ - The Brig Physician may not, under any circumstance, arrest anyone;
+ - The Brig Physician may not, under any circumstance, interfere with Security's duties;
+ - The Brig Physician may not, under any circumstance, directly alter a sentence, or attempt to contest one with Security personnel. Contacting a Magistrate/IAA/NT Representative is still acceptable;
+ - The Brig Physician must wait until someone is brigged and their timer starts before bringing them to the Brig Medbay. Exception is made if the Head of Security or Warden allows it, or there is a problem that requires immediate medical aid to prevent death;
+ - The Brig Physician may not stop a timer if a prisoner is brought into the Brig Medbay for treatment. The timer is to continue while they are treated. If the timer runs out during medical treatment, the prisoner is to be released;
+ - The Brig Physician may not restrain a prisoner unless they are actively hostile;
+ - The Brig Physician is permitted to carry a flash and a can of pepperspray;
+ - The Brig Physician must maintain the Brig Medbay, himself and all treated prisoners in a hygienic condition. Should the need arise, this extends to the rest of the Brig as well;
+ - The Brig Physician must escort all prisoners requiring surgery to Medbay personally, and make sure that they are returned to the Brig before being released. The Brig Physician may also choose to construct a smaller surgery room inside the Brig Medbay.
+
+
+ Code Blue
+
+ - All Guidelines carry over from Code Green;
+ - Additional equipment may be provided by the Warden for self-defense;
+
+
+ Code Red
+
+ - All guidelines carry over from Code Green;
+ - All guidelines carry over from Code Blue.
+
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_legal
+ name = "Legal Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all legal activities."
+ icon_state = "book1"
+ author = "Nanotrasen"
+ title = "Legal Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Punishments
+ - Brigging
+ - Permabrigging
+ - Execution: General
+ - Execution: Electric Chair
+ - Execution: Lethal Injection
+ - Execution:Firing Squad
+
+
+
+
+ These are the procedures for standard punishments, and should be followed unless an emergency makes them unable to be followed.
+
+
+
+ - No prisoner is to be held for longer than ten (10) minutes in Processing if no evidence against them is readily available. Should the ten (10) minutes expire without any evidence of any crimes coming to light, the prisoner is to be released. Otherwise, proceed with the following guidelines:
+ - The prisoner is to be cuffed, and brought to their cell.
+ - The prisoner is to be stripped of all belongings, save for their uniform, headset, ID, PDA and shoes. Vox are to retain their internals, plasmamen are to retain internals and their suit.
+ - The prisoner is then to be uncuffed. If they are a violent risk, they may be bucklecuffed, flashed, then have their cuffs removed.
+ - The timer for the cell is to be set, and the charges declared.
+ - Prisoners attempting to break the windows of the cell are to be flashed and their timers reset.
+ - Removal of the prisoner's headset may ONLY occur if the prisoner is using the headset to encourage further crimes or co-ordinate an escape attempt.
+
+
+
+
+
+ - Prisoner must be cuffed, and their ID must be terminated.
+ - Prisoner must be stripped of all belongings, except for his/her headset and ID Card. Said belongings must be placed in one of the lockers next to the Interrogation Room.
+ - Prisoner must be clothed in a Prison Uniform and Orange Shoes.
+ - Prisoner must be brought to the Permabrig area, and the doors behind closed properly.
+ - Prisoner must be bucklecuffed to one of the beds.
+ - Prisoner must have his cuffs removed, then be flashed or stunned, and the cuffs recovered.
+ - All Security agents must then leave the Permabrig.
+ - In the case of an attempted escape or riot, the Nitrous Oxide control is to be used.
+
+
+
+
+
+ - Prisoner must be cuffed, and their ID must be terminated.
+ - Prisoner must be stripped of all belongings, except for his/her headset and ID Card. Said belongings must be placed in one of the lockers next to the Interrogation Room.
+ - Prisoner must be clothed in a Prison Uniform and Orange Shoes.
+ - Prisoner must be brought to the Prisoner Transfer room.
+ - A Chaplain may be present if requested, and allowed by the HoS.
+ - It is advised, but not required, to have a Brig Physician or other medical personell in attendance to verify death.
+ - Authorization must be given by the Captain and/or Magistrate. Without authorization, executions are murder.
+ - Though not obligatory, it is recommended that all executed prisoners be considered for borgification post-execution.
+
+
+
+
+
+ - Prisoner must be bucklecuffed to the electric chair.
+ - Prisoner must be allowed his/her final words, after which the chair will be activated.
+ - Prisoner's pulse is to be checked to confirm death.
+ - Prisoner must then be borged, fired into space via mass driver, cremated, or placed in the morgue with a DNR Notice, at the discretion of the Magistrate, Captain or Head of Security.
+
+
+
+
+
+ - Prisoner must be bucklecuffed to the electric chair or bed.
+ - Prisoner must be allowed his/her final words, after which the injection will be applied.
+ - Prisoner's pulse is to be checked to confirm death.
+ - Prisoner must then be borged, fired into space via mass driver, cremated, or placed in the morgue with a DNR Notice, at the discretion of the Magistrate, Captain or Head of Security.
+
+
+
+
+
+ - Prisoner must be brought to the Firing Range.
+ - Prisoner must be bucklecuffed to a chair.
+ - Prisoner must be allowed his/her final words, after which authorised security personell are to open fire with any of the following: Energy Gun, Advanced Energy Gun, Laser Gun, Revolver, Shotgun, or any ranged weapon manufactured by Research.
+ - Prisoner's pulse is to be checked to confirm death.
+ - Prisoner must then be borged, fired into space via mass driver, cremated, or placed in the morgue with a DNR Notice, at the discretion of the Magistrate, Captain or Head of Security.
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_general
+ name = "Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all station activities."
+ icon_state = "book1"
+ author = "Nanotrasen"
+ title = "Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - FOREWORD
+ - Code Green
+ - Code Blue
+ - Code Red
+ - Code Gamma
+ - Hiring Policies
+ - Firing Policies
+ - Causes for Demotion and Dismissal
+ - Situational SoP
+ - Evacuation
+ - Viral Outbreak Procedures
+ - Fire and Environmental Hazards
+ - Meteor Storm
+ - Singularity Containment Failure
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ All clear.
+ Default operating level. No immediate or clear threat to the station. All departments may carry out work as normal.
+ This alert level can be set at the Communications Console with a Captain level ID.
+ All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.
+
+ Security:
+
+ - Weapons worn by security/headstaff are to be holstered, except in emergencies.
+ - Security must respect the privacy of crew members and no unauthorized searches are allowed. Searches of any kind may only be done with a signed warrant by the Head of Security or higher, or if there's evidence of criminal activity.
+
+
+ Locations:
+
+ - Secure areas are recommended to be left unbolted. This includes EVA, Teleporter, AI Upload, Engineering Secure Storage, and Tech Storage.
+
+
+ Crew:
+
+ - Crew members may freely walk in the hallways
+ - Suit sensors are not mandatory.
+
+
+
+
+ There is a suspected threat.
+ Raised alert level. Suspected threat to the station. Issued by Central Command, the Captain, or a Head of Staff vote. This alert level can be set at the Communications Console with a Captain level ID.
+ Security staff may have weapons visible, random searches are permitted.
+
+ Security:
+
+ - Security may have weapons visible, but not drawn unless needed.
+ - Energy guns, laser guns and riot gear are allowed to be given out to security personnel with clearance from the Warden or HoS.
+ - Body Armour and helmets are recommended but not mandatory.
+ - Random body and workplace searches are allowed without warrant.
+
+
+ Locations:
+
+ - Secure areas may be bolted down. This includes EVA, Teleporter, AI Upload, Engineering Secure Storage, and Tech Storage.
+
+
+ Crew:
+
+ - Employees are recommended to comply with all security requests.
+ - Suit sensors are mandatory, but coordinate positions are not required.
+
+
+
+
+ There is a confirmed threat.
+ Maximum alert level. Confirmed threat to the station or severe damage. Issued by Central Command, the Captain, or a Head of Staff vote. This alert level can only be set via the Keycard Authentication Devices in each Heads of Staff office and by swiping two Heads of Staff ID cards simultaneously.
+ Security staff to be on high alert, random searches are permitted and recommended.
+
+ Security:
+
+ - Security may have weapons drawn at all times.
+ - Body Armour and helmets are mandatory. Riot gear is also recommended for appropriate situations.
+ - Random body and workplace searches are allowed and recommended.
+
+
+ Locations:
+
+ - Secure areas are recommended to be bolted.
+
+
+ Crew:
+
+ - Suit sensors and coordinate positions are mandatory.
+ - All crew members must remain in their departments.
+ - Employees are required to comply with all security requests.
+ - Emergency Response Team may be authorised. All crew are to comply with their direction.
+
+
+
+
+ Extremely hostile threat onboard the station.
+ GAMMA Security level has been set by Centcom.
+ Security is to have weapons at hand at all time, random searches are permitted and Martial Law is declared.
+
+ Security:
+
+ - Security may have weapons drawn at all times.
+ - Body Armour and helmets are mandatory. Riot gear is also recommended for appropriate situations.
+ - Random body and workplace searches are allowed and recommended.
+ - GAMMA Armory unlocked for security personnel.
+
+
+ Locations:
+
+ - Secure areas are to be bolted.
+
+
+ Crew:
+
+ - Employees are required to comply with all security requests.
+ - All civilians are to seek their nearest head for transportation to a safe location.
+ - All personnel are required to defend the station and help security with dealing with the threat. All crew must follow direct orders from Security Personell or Head of Staff.
+
+
+
+
+
+ - Authorisation from the relevant Department Head is required to be hired into a Department. If none exists, the HoP or Captain's authorisation is required.
+ - Promotion to a Department Head requires authorisation from the Captain or Acting Captain.
+ - CentComm Authorisation is required for hiring the following: Blueshield, Security Pod Pilot, Magistrate, Brig Physician, Nanotrasen Representative, Mechanic.
+ - If no Department Head has yet been sent, any promotion to said position is on a temporary basis until one arrives.
+ - All Security personnel are to be mindshield implanted.
+
+
+
+
+
+ - If a crew is to be dismissed, their ID is to be terminated.
+ - Demotion may be to any rank lower than their current. Assistant or Janitor are recommended for punishments.
+ - Demotion or Dismissal must be authorised by the relevant department head, or Captain.
+ - Demotion or Dismissal must have due cause.
+
+
+
+
+
+ - A medium or higher crime may be grounds for dismissal, at the department head's discretion.
+ - A Capital Crime requires dismissal.
+ - Any crew shown not to have the skills or knowledge necessary for the position should be dismissed.
+ - Failure to follow SoP may be grounds for dismissal, at the Department Head's discretion.
+ - Failure to follow SoP causing harm to crew requires dismissal, and brig time.
+ - Refusal to follow reasonable and legal orders from relevant department head is grounds for dismissal. Their status as reasonable and legal is to be judged by the HoP or Captain.
+ - A crew member creating an abusive and hostile work environment may be dismissed or demoted. This is to be judged by the Department Head and HoP.
+ - Demotion or dismissal may be in person, or declared on a radio channel. If demoted or dismissed, an employee is required to attend the HoP office to hand in their ID, and to leave all items as part of their job at their workplace.
+ - Reasonable time is to be allowed for the fired persons to obtain a new set of clothing from the locker room.
+ - Failure or refusal to hand in any items, ID, etc, of their previous job, is to be considered theft.
+
+
+
+
+ The following situations have specific SoP. Failure to follow these may result in demotion/dismissal, or detaining by security if failure to follow them presents a significant risk.
+
+
+
+
+ - All personnel are required to assist with evacuation. All crew must be evacuated, regardless of conscious state.
+ - A Capital Crime requires dismissal.
+ - All prisoners are to be brought to the secure area of the escape shuttle, unless doing so would cause unnecessary risk for crew.
+ - Bodies are to be brought back to Central Command for processing.
+ - AI units may be brought to Central Command on portable card devices (Intelicards) if structural failure is likely.
+ - Shortening time to shuttle launch may be authorised if a clear threat to life, limb, or shuttle integrity is present.
+
+
+
+
+ Definition: A Viral Outbreak is defined as a situation where a Viral Pathogen has infected a significant portion of the crew (>10%)
+
+ - All Medbay personnel are to contribute in fighting the outbreak if there are no other critical patients requiring assistance. Eliminating the Viral Threat becomes number one priority;
+ - Personnel are to be informed of known symptoms, and directed to Medbay immediately if they are suffering from them;
+ - All infected personnel are to be confined to either an Isolated Room, or Virology;
+ - A blood sample is to be taken from an infected person, for study;
+ - If any infected personnel attempt to leave containment, Medbay Quarantine is to be initiated immediately, and only lifted when more patients need to be admitted, or the Viral Outbreak is over;
+ - A single infected person may volunteer to receive a dose of Radium in order to develop Antibodies. Radium must not be administered without consent. Otherwise, animal testing is to be conducted in order to obtain Antibodies;
+ - Once Antibodies are produced, they are to be diluted, then handed out to all infected personnel. Injecting infected personnel with Radium after Antibodies have been extracted is forbidden. In the event of a large enough crisis, directly injecting blood with the relevant Antibodies is permissible;
+ - Viral Pathogen should be cataloged and analyzed, in case any stray cases remained untreated;
+ - Cured personnel should have a sample of their blood removed for the purpose of creating antibodies, until there are no infected personnel left;
+ - In case the Viral Pathogen leads to fluid leakage, cleaning these fluids is to be considered top priority;
+ - Once the Viral Outbreak is over, all personnel are to return to regular duties.
+
+
+
+
+
+ - Immediate evacuation of all untrained personnel.
+ - Fire alarms to be used to control hazard.
+ - Atmospheric Technicians are to remove hazard.
+
+
+
+
+
+ - All crew to move to central parts of the station.
+ - Damage is to be repaired by engineering personnel after the threat has passed.
+ - Personel that are doing EVA maintenance should seek shelter immediately.
+
+
+
+
+
+ - Observation of Singularity movement.
+ - Evacuation to be called if deemed a major threat to station integrity.
+ - Demotion of Chief Engineer and reparation of Engine if no threat manifests.
+
+
+
+
+
+
+ "}
+
+/obj/item/weapon/book/manual/sop_command
+ name = "Command Standard Operating Procedures"
+ desc = "A set of guidelines aiming at the safe conduct of all Command activities."
+ icon_state = "book4"
+ author = "Nanotrasen"
+ title = "Command Standard Operating Procedures"
+ dat = {"
+
+
+
+
+
+
+
+
+
+
+ - Foreword
+ - Captain
+ - Head of Personnel
+ - NanoTrasen Representative
+ - Blueshield Officer
+ - AI
+
+
+
+
+ Job SOP should not be a considered a checklist of conditions to fire someone over, and should not be rigidly followed to the letter in detriment of circumstances and context.
+ As always, SOP can be malleable if the situation so requires, and the decision to punish a crewmember for breaching it ultimately falls onto the relevant Head of Staff,
+ for Department Members, or Captain, for the Head of Staff.
+
+
+
+ - The Captain is not permitted to perform regular Security Duty. However, they may still assist Security if they are understaffed, or if they see a crime being committed. However, the Captain is not permitted to take items from the Armory under normal circumstances, unless authorized by the Head of Security. In addition, the Captain may not requisition weaponry for themselves from Cargo and/or Science, unless there's an immediate threat to station and/or crew;
+ - If a Department lacks a Head of Staff, the Captain should make reasonable efforts to appoint an Acting Head of Staff, if there are available personnel to fill the position;
+ - The Captain is to ensure that Space Law is being correctly applied. This should be done in cooperation with the Head of Security;
+ - The Captain is not to leave the NSS Cyberiad unless given specific permission by Central Command, or it happens to be the end of the shift. This includes via space or via the Gateway. To do so is to be considered abandoning their posts and is grounds for termination;
+ - The Captain must keep the Nuclear Authentication Disk on their person at all times or, failing that, in the possession of the Head of Security or Blueshield;
+ - The Captain is to attempt to resolve every issue that arises in Command locally before contacting Central Command;
+ - The Captain is not permitted to carry their Antique Laser Gun or Space Armor unless there's an immediate emergency that requires attending to;
+ - The Captain, despite being in charge of the Cyberiad, is not independent from NanoTrasen. Any attempts to disregard general company policy are to be considered an instant condition for contract termination;
+ - The Captain may only promote personnel to a Acting Head of Staff position if there is no assigned Head of Staff associated with the Department. Said Acting Head of Staff must be a member of the Department they are to lead. See below for more information on Chain of Command;
+ - The Captain may not fire any Head of Staff without reasonable justification (ie, incompetency, criminal activity, or otherwise any action that endangers/compromises the station and/or crew). The Captain may not fire any Central Command VIPs (ie, Blueshield, Magistrate, NanoTrasen Representative) without permission from Central Command, unless they are blatantly acting against the well-being and safety of the crew and station.
+
+
+
+
+ - The Head of Personnel may not transfer any personnel to another Department without authorization from the relevant Head of Staff. If no Head of Staff is available, the Head of Personnel may make a judgement call. This does not apply to Security, which always requires authorization from the Head of Security, or Genetics, which requires both Chief Medical Officer and Research Director approval. If there is no Head of Security active, no transfers are allowed to Security without authorization from the Captain;
+ - The Head of Personnel may not give any personnel increased access without authorization from the relevant Head of Staff. This includes the Head of Personnel. In addition, the Head of Personnel may only give Captain-Level access to someone if they are the Acting Captain. This access is to be removed when a proper Captain arrives on the station;
+ - The Head of Personnel may not increase any Job Openings unless the relevant Head of Staff approves;
+ - The Head of Personnel may not fire any personnel without authorization from the relevant Head of Staff, unless other conditions apply (see Space Law and General Standard Operating Procedure);
+ - The Head of Personnel may not promote any personnel to the following Jobs without authorization from Central Command: Barber, Brig Physician, NanoTrasen Representative, Blueshield, Security Pod Pilot, Mechanic and Magistrate; (This is due to them being karma locked. Do not promote people to these positions without approval from the Administrators);
+ - The Head of Personnel is free to utilize paperwork at their discretion. However, during major station emergencies, expediency should take precedence over bureaucracy;
+ - The Head of Personnel may not leave their office unmanned if there are personnel waiting in line. Failure to respond to personnel with a legitimate request within ten (10) minutes, either via radio or in person, is to be considered a breach of Standard Operating Procedure;
+ - Despite nominally being in charge of Supply, the Head of Personnel should allow the Quartermaster to run the Department, unless they prove themselves to be incompetent/dangerous;
+ - The Head of Personnel is bound to the same rules regarding ordering Cargo Crates as the Quartermaster and Cargo Technicians. In addition, the Head of Personnel may not order unneeded, non-essential items against the wishes of Cargo;
+ - The Head of Personnel is not permitted to perform Security duty. The Head of Personnel is permitted to carry an Energy Gun, for self-defence only.
+
+
+
+
+ - The NanoTrasen Representative is to ensure that every Department is following Standard Operating Procedure, up to and including the respective Head of Staff. If a Head of Staff is not available for a Department, the NanoTrasen Representative must ensure that the Captain appoints an Acting Head of Staff for said Department;
+ - The NanoTrasen Representative must attempt to resolve any breach of Standard Operating Procedure locally before contacting Central Command. This is an imperative: Standard Operating Procedure should always be followed unless there is a very good reason not to;
+ - The NanoTrasen Representative must, together with the Magistrate and Head of Security, ensure that Space Law is being followed and correctly applied;
+ - The NanoTrasen Representative may not threaten the use of a fax in order to gain leverage over any personnel, up to and including Command. In addition they may not threaten to fire, or have Central Command, fire anyone, unless they actually possess a demotion note;
+ - The NanoTrasen Representative is permitted to carry their Stun-Cane, or a Telescopic Baton if the Stun-Cane is lost.
+
+
+
+
+ - The Blueshield may not conduct arrests under the same conditions as Security. However, they may apprehend any personnel that trespass on a Head of Staff Office or Command Area, any personnel that steal from those locations, or any personnel that steal from and/or injure any Head of Staff or Central Command VIP. However, all apprehended personnel are to be processed by Security personnel;
+ - The Blueshield is to put the lives of Command over those of any other personnel, the Blueshield included. Their continued well-being is the Blueshield's top priority. This includes applying basic first aid and making sure they are revived if killed;
+ - The Blueshield is to protect the lives of Command personnel, not follow their orders to a fault. The Blueshield is not to interfere with legal demotions or arrests. To do so is to place themselves under the Special Modifier Aiding and Abetting;
+ - The Blueshield is not to apply Lethal Force unless there is a clear and present danger to their life, or to the life of a member of Command, and the assailant cannot be non-lethally detained.
+
+
+
+ The following are procedures for AI Maintenance:
+
+ - Only the Captain or Research Director may enter the AI Upload to perform Law Changes (see below), and only the Captain, Research Director or Chief Engineer may enter the AI Core to perform a Carding (see below);
+ - No Law Changes are to be performed without approval from the Captain and Research Director. The only Lawsets to be used are those provided by NanoTrasen. Failure to legally perform a Law Change is to be considered Sabotage. Command must be informed prior to the Law Change, and all objections must be taken into consideration. If the number of Command personnel opposing the Law Change is greater than the number of Command personnel in favour, the Law Change is not to be done. If the Law Change is performed, the crew is to be immediately informed of the new Law(s);
+ - The AI may not be Carded unless it it clearly malfunctioning or subverted. However, any member of Command may card it if the AI agrees to it, either at the end of the shift, or due to external circumstances (such as massive damage to the AI Satellite);
+ - The AI Upload and Minisat Antechamber Turrets are to be kept on Non-Lethal in Code Green and Code Blue. The AI Core Turrets are to be kept on Lethal at all times. If a legal Law Change or Carding is occurring, the Turrets are to be disabled;
+ - If the AI Unit is not malfunctioning or subverted, any attempt at performing an illegal Carding or Law Change is to be responded to with non-lethal force. If the illegal attempts persist, and the perpetrator is demonstrably hostile, lethal force from Command/Security is permitted;
+ - Freeform Laws are only to be added if absolutely necessary due to external circumstances (such as major station emergencies). Adding unnecessary Freeform Laws is not permitted. Exception is made if the AI Unit and majority of Command agree to the Freeform Law that is proposed;
+ - Any use of the "Purge" Module is to be followed by the upload of a NanoTrasen-approved Lawset immediately. AI Units must be bound to a Lawset at all times.
+
+
+
+
+ "}
\ No newline at end of file
diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl
index 7446b2ca1a9..234074fe577 100644
--- a/nano/templates/apc.tmpl
+++ b/nano/templates/apc.tmpl
@@ -4,7 +4,7 @@
Interface Lock:
- {{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.locked ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.locked ? null : 'selected'))}}
+ {{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.siliconLock ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.siliconLock ? null : 'selected'))}}
{{else}}
@@ -177,4 +177,21 @@
{{/if}}
+
+
+ Night Shift Lighting:
+
+
+ {{if data.locked}}
+ {{if data.nightshiftLights}}
+ On
+ {{else}}
+ Off
+ {{/if}}
+ {{else}}
+ {{:helper.link('Enabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? 'selected' : null)}}{{:helper.link('Disabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? null : 'selected')}}
+ {{/if}}
+
+
+
diff --git a/nano/templates/vending_machine.tmpl b/nano/templates/vending_machine.tmpl
index 44f54996ba2..1849e3e51d5 100644
--- a/nano/templates/vending_machine.tmpl
+++ b/nano/templates/vending_machine.tmpl
@@ -33,6 +33,17 @@
{{:helper.link(data.coin, 'eject', {'remove_coin' : 1})}}
{{/if}}
+{{if data.item_slot == 1}}
+Item Slot
+
+ Item Slot:
+ {{if data.inserted_item}}
+ {{:helper.link(data.inserted_item, 'eject', {'remove_item' : 1})}}
+ {{else}}
+ Empty
+ {{/if}}
+
+{{/if}}
{{else data.mode == 1}}
Item selected
diff --git a/paradise.dme b/paradise.dme
index fe4daedb1ee..56dc61e5140 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -15,6 +15,7 @@
#include "code\_compile_options.dm"
#include "code\hub.dm"
#include "code\world.dm"
+#include "code\__DEFINES\_globals.dm"
#include "code\__DEFINES\_readme.dm"
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\atmospherics.dm"
@@ -38,8 +39,9 @@
#include "code\__DEFINES\lighting.dm"
#include "code\__DEFINES\machines.dm"
#include "code\__DEFINES\math.dm"
+#include "code\__DEFINES\MC.dm"
#include "code\__DEFINES\misc.dm"
-#include "code\__DEFINES\mob.dm"
+#include "code\__DEFINES\mobs.dm"
#include "code\__DEFINES\pda.dm"
#include "code\__DEFINES\preferences.dm"
#include "code\__DEFINES\process_scheduler.dm"
@@ -52,12 +54,14 @@
#include "code\__DEFINES\sound.dm"
#include "code\__DEFINES\stat.dm"
#include "code\__DEFINES\status_effects.dm"
+#include "code\__DEFINES\subsystems.dm"
#include "code\__DEFINES\tick.dm"
#include "code\__DEFINES\typeids.dm"
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\zlevel.dm"
#include "code\__HELPERS\_string_lists.dm"
#include "code\__HELPERS\AnimationLibrary.dm"
+#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\constants.dm"
#include "code\__HELPERS\experimental.dm"
#include "code\__HELPERS\files.dm"
@@ -78,6 +82,10 @@
#include "code\__HELPERS\type2type.dm"
#include "code\__HELPERS\unique_ids.dm"
#include "code\__HELPERS\unsorted.dm"
+#include "code\__HELPERS\sorts\__main.dm"
+#include "code\__HELPERS\sorts\InsertSort.dm"
+#include "code\__HELPERS\sorts\MergeSort.dm"
+#include "code\__HELPERS\sorts\TimSort.dm"
#include "code\_DATASTRUCTURES\heap.dm"
#include "code\_DATASTRUCTURES\linked_lists.dm"
#include "code\_DATASTRUCTURES\priority_queue.dm"
@@ -169,36 +177,40 @@
#include "code\ATMOSPHERICS\pipes\simple\pipe_simple_visible.dm"
#include "code\controllers\communications.dm"
#include "code\controllers\configuration.dm"
+#include "code\controllers\controller.dm"
#include "code\controllers\failsafe.dm"
+#include "code\controllers\globals.dm"
#include "code\controllers\hooks-defs.dm"
#include "code\controllers\hooks.dm"
+#include "code\controllers\master.dm"
#include "code\controllers\master_controller.dm"
+#include "code\controllers\subsystem.dm"
#include "code\controllers\verbs.dm"
#include "code\controllers\voting.dm"
-#include "code\controllers\Processes\air.dm"
#include "code\controllers\Processes\alarm.dm"
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\fast_process.dm"
-#include "code\controllers\Processes\fires.dm"
#include "code\controllers\Processes\garbage.dm"
-#include "code\controllers\Processes\inactivity.dm"
#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\mob.dm"
#include "code\controllers\Processes\nano_mob_hunter.dm"
-#include "code\controllers\Processes\nanoui.dm"
#include "code\controllers\Processes\npcai.dm"
#include "code\controllers\Processes\npcpool.dm"
#include "code\controllers\Processes\obj.dm"
#include "code\controllers\Processes\shuttles.dm"
-#include "code\controllers\Processes\spacedrift.dm"
-#include "code\controllers\Processes\sun.dm"
-#include "code\controllers\Processes\throwing.dm"
#include "code\controllers\Processes\ticker.dm"
#include "code\controllers\Processes\timer.dm"
#include "code\controllers\Processes\weather.dm"
#include "code\controllers\ProcessScheduler\core\process.dm"
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
+#include "code\controllers\subsystem\air.dm"
+#include "code\controllers\subsystem\fires.dm"
+#include "code\controllers\subsystem\nightshift.dm"
+#include "code\controllers\subsystem\nanoui.dm"
+#include "code\controllers\subsystem\spacedrift.dm"
+#include "code\controllers\subsystem\sun.dm"
+#include "code\controllers\subsystem\throwing.dm"
#include "code\datums\action.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"
@@ -247,7 +259,9 @@
#include "code\datums\diseases\fluspanish.dm"
#include "code\datums\diseases\food_poisoning.dm"
#include "code\datums\diseases\gbs.dm"
+#include "code\datums\diseases\kingstons.dm"
#include "code\datums\diseases\kuru.dm"
+#include "code\datums\diseases\lycancoughy.dm"
#include "code\datums\diseases\magnitis.dm"
#include "code\datums\diseases\pierrot_throat.dm"
#include "code\datums\diseases\retrovirus.dm"
@@ -734,6 +748,7 @@
#include "code\game\objects\effects\spawners\random_spawners.dm"
#include "code\game\objects\effects\spawners\vaultspawner.dm"
#include "code\game\objects\effects\spawners\windowspawner.dm"
+#include "code\game\objects\effects\temporary_visuals\clockcult.dm"
#include "code\game\objects\effects\temporary_visuals\cult.dm"
#include "code\game\objects\effects\temporary_visuals\miscellaneous.dm"
#include "code\game\objects\effects\temporary_visuals\temporary_visual.dm"
@@ -1370,6 +1385,7 @@
#include "code\modules\food_and_drinks\drinks\drinks\bottle.dm"
#include "code\modules\food_and_drinks\drinks\drinks\cans.dm"
#include "code\modules\food_and_drinks\drinks\drinks\drinkingglass.dm"
+#include "code\modules\food_and_drinks\drinks\drinks\mugs.dm"
#include "code\modules\food_and_drinks\drinks\drinks\shotglass.dm"
#include "code\modules\food_and_drinks\food\candy.dm"
#include "code\modules\food_and_drinks\food\condiment.dm"
@@ -1846,9 +1862,9 @@
#include "code\modules\modular_computers\NTNet\NTNet_relay.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm"
#include "code\modules\nano\nanoexternal.dm"
-#include "code\modules\nano\nanomanager.dm"
#include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoui.dm"
+#include "code\modules\nano\subsystem.dm"
#include "code\modules\nano\interaction\admin.dm"
#include "code\modules\nano\interaction\base.dm"
#include "code\modules\nano\interaction\conscious.dm"
diff --git a/sound/items/teri_horn.ogg b/sound/items/teri_horn.ogg
new file mode 100644
index 00000000000..a8b50dffdb4
Binary files /dev/null and b/sound/items/teri_horn.ogg differ
diff --git a/sound/machines/blastdoor.ogg b/sound/machines/blastdoor.ogg
new file mode 100644
index 00000000000..2de1b55999c
Binary files /dev/null and b/sound/machines/blastdoor.ogg differ
|