")
text = replacetext(text, "\[cell\]", "")
text = replacetext(text, "\[logo\]", " ")
+ text = replacetext(text, "\[time\]", "[gameTimestamp()]") // TO DO
if(P)
text = "[text]"
else
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index 4b71a2c7162..6d28c2c0cd4 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -69,4 +69,107 @@ proc/isDay(var/month, var/day)
/proc/seconds_to_time(var/seconds as num)
var/numSeconds = seconds % 60
var/numMinutes = (seconds - numSeconds) / 60
- return "[numMinutes] [numMinutes > 1 ? "minutes" : "minute"] and [numSeconds] seconds."
\ No newline at end of file
+ return "[numMinutes] [numMinutes > 1 ? "minutes" : "minute"] and [numSeconds] seconds."
+
+//Takes a value of time in deciseconds.
+//Returns a text value of that number in hours, minutes, or seconds.
+/proc/DisplayTimeText(time_value)
+ var/second = time_value * 0.1
+ var/second_adjusted = null
+ var/second_rounded = FALSE
+ var/minute = null
+ var/hour = null
+ var/day = null
+
+ if(!second)
+ return "0 seconds"
+ if(second >= 60)
+ minute = round_down(second / 60)
+ second = round(second - (minute * 60), 0.1)
+ second_rounded = TRUE
+ if(second) //check if we still have seconds remaining to format, or if everything went into minute.
+ second_adjusted = round(second) //used to prevent '1 seconds' being shown
+ if(day || hour || minute)
+ if(second_adjusted == 1 && second >= 1)
+ second = " and 1 second"
+ else if(second > 1)
+ second = " and [second_adjusted] seconds"
+ else //shows a fraction if seconds is < 1
+ if(second_rounded) //no sense rounding again if it's already done
+ second = " and [second] seconds"
+ else
+ second = " and [round(second, 0.1)] seconds"
+ else
+ if(second_adjusted == 1 && second >= 1)
+ second = "1 second"
+ else if(second > 1)
+ second = "[second_adjusted] seconds"
+ else
+ if(second_rounded)
+ second = "[second] seconds"
+ else
+ second = "[round(second, 0.1)] seconds"
+ else
+ second = null
+
+ if(!minute)
+ return "[second]"
+ if(minute >= 60)
+ hour = round_down(minute / 60,1)
+ minute = (minute - (hour * 60))
+ if(minute) //alot simpler from here since you don't have to worry about fractions
+ if(minute != 1)
+ if((day || hour) && second)
+ minute = ", [minute] minutes"
+ else if((day || hour) && !second)
+ minute = " and [minute] minutes"
+ else
+ minute = "[minute] minutes"
+ else
+ if((day || hour) && second)
+ minute = ", 1 minute"
+ else if((day || hour) && !second)
+ minute = " and 1 minute"
+ else
+ minute = "1 minute"
+ else
+ minute = null
+
+ if(!hour)
+ return "[minute][second]"
+ if(hour >= 24)
+ day = round_down(hour / 24,1)
+ hour = (hour - (day * 24))
+ if(hour)
+ if(hour != 1)
+ if(day && (minute || second))
+ hour = ", [hour] hours"
+ else if(day && (!minute || !second))
+ hour = " and [hour] hours"
+ else
+ hour = "[hour] hours"
+ else
+ if(day && (minute || second))
+ hour = ", 1 hour"
+ else if(day && (!minute || !second))
+ hour = " and 1 hour"
+ else
+ hour = "1 hour"
+ else
+ hour = null
+
+ if(!day)
+ return "[hour][minute][second]"
+ if(day > 1)
+ day = "[day] days"
+ else
+ day = "1 day"
+
+ 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/configuration.dm b/code/_globalvars/configuration.dm
index 88d4594e740..d26dc711339 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -2,7 +2,6 @@ var/datum/configuration/config = null
var/host = null
var/join_motd = null
-var/station_name = "NSS Cyberiad"
var/game_version = "Custom ParaCode"
var/changelog_hash = md5('html/changelog.html') //used to check if the CL changed
var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544)
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 644740c9e20..28ebfca6b1b 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -25,6 +25,7 @@ var/global/list/respawnable_list = list() //List of all mobs, dead or in mindl
var/global/list/non_respawnable_keys = list() //List of ckeys that are excluded from respawning for remainder of round.
var/global/list/simple_animal_list = list() //List of all simple animals, including clientless
var/global/list/snpc_list = list() //List of all snpc's, including clientless
+var/global/list/bots_list = list() //List of all bots(beepsky, medibots,etc)
var/global/list/med_hud_users = list()
var/global/list/sec_hud_users = list()
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..15a61078514 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.
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/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..88fdb83da10 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -189,6 +189,16 @@
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
+
/datum/configuration/New()
var/list/L = subtypesof(/datum/game_mode)
for(var/T in L)
@@ -597,6 +607,17 @@
if("disable_karma")
disable_karma = 1
+ if("tick_limit_mc_init")
+ tick_limit_mc_init = text2num(value)
+ if("base_mc_tick_rate")
+ base_mc_tick_rate = text2num(value)
+ if("high_pop_mc_tick_rate")
+ high_pop_mc_tick_rate = text2num(value)
+ if("high_pop_mc_mode_amount")
+ high_pop_mc_mode_amount = text2num(value)
+ if("disable_high_pop_mc_mode_amount")
+ disable_high_pop_mc_mode_amount = text2num(value)
+
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/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/cargoprofile.dm b/code/datums/cargoprofile.dm
index 4636495489f..310593d0d6b 100644
--- a/code/datums/cargoprofile.dm
+++ b/code/datums/cargoprofile.dm
@@ -1,4 +1,3 @@
-#define MAXCOIL 30
/datum/cargoprofile
var/name = "All Items"
var/id = "all" // unique ID for the UI
@@ -787,4 +786,3 @@
var/punches = punch(M,remaining / PUNCH_WORK)
if(punches>1)master.sleep++
return punches * PUNCH_WORK
-#undef MAXCOIL
diff --git a/code/datums/hud.dm b/code/datums/hud.dm
index c29b5daf5b3..04ebcc68ed5 100644
--- a/code/datums/hud.dm
+++ b/code/datums/hud.dm
@@ -1,4 +1,5 @@
/* HUD DATUMS */
+var/global/list/all_huds = list()
///GLOBAL HUD LIST
var/datum/atom_hud/huds = list( \
@@ -7,6 +8,7 @@ var/datum/atom_hud/huds = list( \
DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(), \
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(), \
DATA_HUD_DIAGNOSTIC = new/datum/atom_hud/data/diagnostic(), \
+ DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(), \
DATA_HUD_HYDROPONIC = new/datum/atom_hud/data/hydroponic(), \
GAME_HUD_NATIONS = new/datum/atom_hud/antag(), \
ANTAG_HUD_CULT = new/datum/atom_hud/antag(), \
@@ -26,6 +28,18 @@ var/datum/atom_hud/huds = list( \
var/list/mob/hudusers = list() //list with all mobs who can see the hud
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
+
+/datum/atom_hud/New()
+ all_huds += src
+
+/datum/atom_hud/Destroy()
+ for(var/v in hudusers)
+ remove_hud_from(v)
+ for(var/v in hudatoms)
+ remove_from_hud(v)
+ all_huds -= src
+ return ..()
+
/datum/atom_hud/proc/remove_hud_from(mob/M)
if(!M)
return
@@ -82,7 +96,7 @@ var/datum/atom_hud/huds = list( \
serv_huds += serv.thrallhud
- for(var/datum/atom_hud/hud in (huds|serv_huds))//|gang_huds))
+ for(var/datum/atom_hud/hud in (all_huds|serv_huds))//|gang_huds))
if(src in hud.hudusers)
hud.add_hud_to(src)
diff --git a/code/datums/material_container.dm b/code/datums/material_container.dm
index 2ae6da75863..8e31e8ae033 100644
--- a/code/datums/material_container.dm
+++ b/code/datums/material_container.dm
@@ -40,6 +40,8 @@
materials[MAT_BANANIUM] = new /datum/material/bananium()
if(mat_list[MAT_TRANQUILLITE])
materials[MAT_TRANQUILLITE] = new /datum/material/tranquillite()
+ if(mat_list[MAT_TITANIUM])
+ materials[MAT_TITANIUM] = new /datum/material/titanium()
/datum/material_container/Destroy()
QDEL_LIST_ASSOC_VAL(materials)
@@ -276,6 +278,13 @@
material_type = MAT_TRANQUILLITE
sheet_type = /obj/item/stack/sheet/mineral/tranquillite
+/datum/material/titanium
+
+/datum/material/titanium/New()
+ ..()
+ material_type = MAT_TITANIUM
+ sheet_type = /obj/item/stack/sheet/mineral/titanium
+
/datum/material/biomass
/datum/material/biomass/New()
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/mutable_appearance.dm b/code/datums/mutable_appearance.dm
new file mode 100644
index 00000000000..ea94aa6407b
--- /dev/null
+++ b/code/datums/mutable_appearance.dm
@@ -0,0 +1,13 @@
+// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
+// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
+// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
+
+// Mutable appearances are children of images, just so you know.
+
+// Helper similar to image()
+/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
+ var/mutable_appearance/MA = new()
+ MA.icon = icon
+ MA.icon_state = icon_state
+ MA.layer = layer
+ return MA
\ No newline at end of file
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/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
new file mode 100644
index 00000000000..26d8ec67ef0
--- /dev/null
+++ b/code/datums/status_effects/buffs.dm
@@ -0,0 +1,41 @@
+//Largely beneficial effects go here, even if they have drawbacks. An example is provided in Shadow Mend.
+
+/datum/status_effect/shadow_mend
+ id = "shadow_mend"
+ duration = 30
+ alert_type = /obj/screen/alert/status_effect/shadow_mend
+
+/obj/screen/alert/status_effect/shadow_mend
+ name = "Shadow Mend"
+ desc = "Shadowy energies wrap around your wounds, sealing them at a price. After healing, you will slowly lose health every three seconds for thirty seconds."
+ icon_state = "shadow_mend"
+
+/datum/status_effect/shadow_mend/on_apply()
+ owner.visible_message("Violet light wraps around [owner]'s body!", "Violet light wraps around your body!")
+ playsound(owner, 'sound/magic/teleport_app.ogg', 50, 1)
+ return ..()
+
+/datum/status_effect/shadow_mend/tick()
+ owner.adjustBruteLoss(-15)
+ owner.adjustFireLoss(-15)
+
+/datum/status_effect/shadow_mend/on_remove()
+ owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...")
+ playsound(owner, 'sound/magic/teleport_diss.ogg', 50, 1)
+ owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
+
+
+/datum/status_effect/void_price
+ id = "void_price"
+ duration = 300
+ tick_interval = 30
+ alert_type = /obj/screen/alert/status_effect/void_price
+
+/obj/screen/alert/status_effect/void_price
+ name = "Void Price"
+ desc = "Black tendrils cinch tightly against you, digging wicked barbs into your flesh."
+ icon_state = "shadow_mend"
+
+/datum/status_effect/void_price/tick()
+ playsound(owner, 'sound/weapons/bite.ogg', 50, 1)
+ owner.adjustBruteLoss(3)
\ No newline at end of file
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
new file mode 100644
index 00000000000..cfafa912b64
--- /dev/null
+++ b/code/datums/status_effects/debuffs.dm
@@ -0,0 +1,10 @@
+//OTHER DEBUFFS
+
+/datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
+ id = "cult_ghost"
+ duration = -1
+ alert_type = null
+
+/datum/status_effect/cultghost/tick()
+ if(owner.reagents)
+ owner.reagents.del_reagent("holywater") //can't be deconverted
\ No newline at end of file
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
new file mode 100644
index 00000000000..b82fc629eef
--- /dev/null
+++ b/code/datums/status_effects/neutral.dm
@@ -0,0 +1,9 @@
+//entirely neutral or internal status effects go here
+
+/datum/status_effect/high_five
+ id = "high_five"
+ duration = 25
+ alert_type = null
+
+/datum/status_effect/high_five/on_timeout()
+ owner.visible_message("[owner] was left hanging....")
\ No newline at end of file
diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm
new file mode 100644
index 00000000000..86590c613d2
--- /dev/null
+++ b/code/datums/status_effects/status_effect.dm
@@ -0,0 +1,120 @@
+
+//Status effects are used to apply temporary or permanent effects to mobs. Mobs are aware of their status effects at all times.
+//This file contains their code, plus code for applying and removing them.
+//When making a new status effect, add a define to status_effects.dm in __DEFINES for ease of use!
+
+/datum/status_effect
+ var/id = "effect" //Used for screen alerts.
+ var/duration = -1 //How long the status effect lasts in DECISECONDS. Enter -1 for an effect that never ends unless removed through some means.
+ var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second.
+ var/mob/living/owner //The mob affected by the status effect.
+ var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another
+ var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
+ var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
+ var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
+ var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
+
+/datum/status_effect/New(list/arguments)
+ on_creation(arglist(arguments))
+
+/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
+ if(new_owner)
+ owner = new_owner
+ if(owner)
+ LAZYADD(owner.status_effects, src)
+ if(!owner || !on_apply())
+ qdel(src)
+ return
+ if(duration != -1)
+ duration = world.time + duration
+ tick_interval = world.time + tick_interval
+ if(alert_type)
+ var/obj/screen/alert/status_effect/A = owner.throw_alert(id, alert_type)
+ A.attached_effect = src //so the alert can reference us, if it needs to
+ linked_alert = A //so we can reference the alert, if we need to
+ fast_processing.Add(src)
+ return TRUE
+
+/datum/status_effect/Destroy()
+ fast_processing.Remove(src)
+ if(owner)
+ owner.clear_alert(id)
+ LAZYREMOVE(owner.status_effects, src)
+ on_remove()
+ owner = null
+ return ..()
+
+/datum/status_effect/proc/process()
+ if(!owner)
+ qdel(src)
+ return
+ if(tick_interval < world.time)
+ tick()
+ tick_interval = world.time + initial(tick_interval)
+ if(duration != -1 && duration < world.time)
+ on_timeout()
+ qdel(src)
+
+/datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself.
+ return TRUE
+/datum/status_effect/proc/tick() //Called every tick.
+/datum/status_effect/proc/on_timeout()//called when a buff times out
+/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
+/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted
+ owner.clear_alert(id)
+ LAZYREMOVE(owner.status_effects, src)
+ owner = null
+ qdel(src)
+
+////////////////
+// ALERT HOOK //
+////////////////
+
+/obj/screen/alert/status_effect
+ name = "Curse of Mundanity"
+ desc = "You don't feel any different..."
+ var/datum/status_effect/attached_effect
+
+//////////////////
+// HELPER PROCS //
+//////////////////
+
+/mob/living/proc/apply_status_effect(effect, ...) //applies a given status effect to this mob, returning the effect if it was successful
+ . = FALSE
+ var/datum/status_effect/S1 = effect
+ LAZYINITLIST(status_effects)
+ for(var/datum/status_effect/S in status_effects)
+ if(S.id == initial(S1.id) && S.status_type)
+ if(S.status_type == STATUS_EFFECT_REPLACE)
+ S.be_replaced()
+ else
+ return
+ var/list/arguments = args.Copy()
+ arguments[1] = src
+ S1 = new effect(arguments)
+ . = S1
+
+/mob/living/proc/remove_status_effect(effect) //removes all of a given status effect from this mob, returning TRUE if at least one was removed
+ . = FALSE
+ if(status_effects)
+ var/datum/status_effect/S1 = effect
+ for(var/datum/status_effect/S in status_effects)
+ if(initial(S1.id) == S.id)
+ qdel(S)
+ . = TRUE
+
+/mob/living/proc/has_status_effect(effect) //returns the effect if the mob calling the proc owns the given status effect
+ . = FALSE
+ if(status_effects)
+ var/datum/status_effect/S1 = effect
+ for(var/datum/status_effect/S in status_effects)
+ if(initial(S1.id) == S.id)
+ return S
+
+/mob/living/proc/has_status_effect_list(effect) //returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of
+ . = list()
+ if(status_effects)
+ var/datum/status_effect/S1 = effect
+ for(var/datum/status_effect/S in status_effects)
+ if(initial(S1.id) == S.id)
+ . += S
\ No newline at end of file
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 b1b3a2732a1..cef139cc476 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -58,7 +58,7 @@ var/const/AIRLOCK_WIRE_LIGHT = 512
if(A.isElectrified())
if(A.shock(L, 100))
return 0
- if(A.p_open)
+ if(A.panel_open)
return 1
return 0
@@ -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/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 5444ccf6e68..90b45a19170 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -61,6 +61,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/no_teleportlocs = 0
var/outdoors = 0 //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room)
+ var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
@@ -1868,6 +1869,7 @@ area/security/podbay
/area/toxins/xenobiology
name = "\improper Xenobiology Lab"
icon_state = "toxmix"
+ xenobiology_compatible = TRUE
/area/toxins/xenobiology/xenoflora_storage
name = "\improper Xenoflora Storage"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 723d26a451a..b473b830e39 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -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 f38a741b114..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))
@@ -613,6 +612,10 @@ var/list/blood_splatter_icons = list()
/atom/proc/ratvar_act()
return
+//This proc is called on the location of an atom when the atom is Destroy()'d
+/atom/proc/handle_atom_del(atom/A)
+ return
+
/atom/proc/atom_say(message)
if(!message)
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 1ce51657e85..6ca5047ac6c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -36,6 +36,8 @@
attempt_init()
/atom/movable/Destroy()
+ if(loc)
+ loc.handle_atom_del(src)
for(var/atom/movable/AM in contents)
qdel(AM)
var/turf/un_opaque
@@ -215,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)
@@ -299,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/data_huds.dm b/code/game/data_huds.dm
index 604e92a47e0..7b6ada9364b 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -46,6 +46,12 @@
/datum/atom_hud/data/diagnostic
hud_icons = list (DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD)
+/datum/atom_hud/data/diagnostic/advanced
+ hud_icons = list (DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_PATH_HUD)
+
+/datum/atom_hud/data/bot_path
+ hud_icons = list(DIAG_PATH_HUD)
+
/datum/atom_hud/data/hydroponic
hud_icons = list (PLANT_NUTRIENT_HUD, PLANT_WATER_HUD, PLANT_STATUS_HUD, PLANT_HEALTH_HUD, PLANT_TOXIN_HUD, PLANT_PEST_HUD, PLANT_WEED_HUD)
@@ -70,8 +76,15 @@
return 0
//helper for getting the appropriate health status UPDATED BY PUCKABOO2 TO INCLUDE NEGATIVES.
-/proc/RoundHealth(health)
- switch(health)
+/proc/RoundHealth(mob/living/M)
+ if(M.stat == DEAD || (M.status_flags & FAKEDEATH))
+ return "health-100" //what's our health? it doesn't matter, we're dead, or faking
+ var/maxi_health = M.maxHealth
+ if(iscarbon(M) && M.health < 0)
+ maxi_health = 100 //so crit shows up right for aliens and other high-health carbon mobs; noncarbons don't have crit.
+ var/resulthealth = (M.health / maxi_health) * 100
+
+ switch(resulthealth)
if(100 to INFINITY)
return "health100"
if(95 to 100)
@@ -120,6 +133,7 @@
return "health-100" //doc u had 1 job
return "0"
+
///HOOKS
//called when a human changes suit sensors
@@ -128,22 +142,26 @@
B.update_suit_sensors(src)
-//called when a carbon changes health
-/mob/living/carbon/proc/med_hud_set_health()
+//called when a living mob changes health
+/mob/living/proc/med_hud_set_health()
var/image/holder = hud_list[HEALTH_HUD]
- if(stat == 2)
- holder.icon_state = "hudhealth-100"
- else
- holder.icon_state = "hud[RoundHealth(health)]"
+ holder.icon_state = "hud[RoundHealth(src)]"
+
//called when a carbon changes stat, virus or XENO_HOST
-/mob/living/carbon/proc/med_hud_set_status()
+/mob/living/proc/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
- //var/image/holder2 = hud_list[STATUS_HUD_OOC]
- var/mob/living/simple_animal/borer/B = has_brain_worms()
- if(stat == 2)
+ if(stat == DEAD)
+ holder.icon_state = "huddead"
+ else
+ holder.icon_state = "hudhealthy"
+
+//called when a carbon changes stat, virus or XENO_HOST
+/mob/living/carbon/med_hud_set_status()
+ var/image/holder = hud_list[STATUS_HUD]
+ var/mob/living/simple_animal/borer/B = has_brain_worms()
+ if(stat == DEAD)
holder.icon_state = "huddead"
- //holder2.icon_state = "huddead"
else if(status_flags & XENO_HOST)
holder.icon_state = "hudxeno"
else if(check_virus())
@@ -152,7 +170,6 @@
holder.icon_state = "hudbrainworm"
else
holder.icon_state = "hudhealthy"
- //holder2.icon_state = "hudhealthy"
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 d3edbfe593f..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)"
@@ -495,6 +511,7 @@ var/list/teleport_runes = list()
message_admins("[key_name_admin(user)] tried to summonn an eldritch horror when the objective was wrong")
burn_invokers(invokers)
log_game("Summon Nar-Sie rune failed - improper objective")
+ return
if(!is_station_level(user.z))
message_admins("[key_name_admin(user)] tried to summon an eldritch horror off station")
burn_invokers(invokers)
@@ -805,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)
..()
@@ -830,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]
@@ -855,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
@@ -871,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
@@ -888,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)
@@ -941,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)
..()
@@ -949,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))
@@ -975,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))
@@ -988,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))
@@ -1003,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/gameticker.dm b/code/game/gamemodes/gameticker.dm
index fb4ba668bef..6612f1e7192 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -55,6 +55,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 +78,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 +98,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 +111,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 +129,7 @@ var/round_start_time = 0
equip_characters()
data_core.manifest()
current_state = GAME_STATE_PLAYING
+ Master.SetRunLevel(RUNLEVEL_GAME)
callHook("roundstart")
@@ -385,6 +390,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/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index eac7a2a8500..c8bc0e1e15e 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -601,3 +601,12 @@ Congratulations! You are now trained for invasive xenobiology research!"}
icon_closed = "abductor"
icon_opened = "abductoropen"
material_drop = /obj/item/stack/sheet/mineral/abductor
+
+/obj/structure/door_assembly/door_assembly_abductor
+ name = "alien airlock assembly"
+ icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
+ base_name = "alien airlock"
+ overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
+ airlock_type = /obj/machinery/door/airlock/abductor
+ material_type = /obj/item/stack/sheet/mineral/abductor
+ noglass = TRUE
diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm
index 54bdaf1b9cf..ba7f91e2c37 100644
--- a/code/game/gamemodes/miniantags/borer/borer.dm
+++ b/code/game/gamemodes/miniantags/borer/borer.dm
@@ -115,6 +115,7 @@
/mob/living/simple_animal/borer/New(atom/newloc, var/gen=1)
..(newloc)
+ remove_from_all_data_huds()
generation = gen
add_language("Cortical Link")
notify_ghosts("A cortical borer has been created in [get_area(src)]!", enter_link = "(Click to enter)", source = src, action = NOTIFY_ATTACK)
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index d052939cb14..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'
@@ -112,6 +113,7 @@
loot = list(/obj/effect/decal/cleanable/blood/gibs/robot, /obj/item/weapon/ore/bluespace_crystal/artificial)
deathmessage = "The swarmer explodes with a sharp pop!"
del_on_death = 1
+ hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD)
/mob/living/simple_animal/hostile/swarmer/Login()
..()
@@ -127,8 +129,22 @@
..()
add_language("Swarmer", 1)
verbs -= /mob/living/verb/pulled
+ for(var/datum/atom_hud/data/diagnostic/diag_hud in huds)
+ diag_hud.add_to_hud(src)
updatename()
+/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
+ var/image/holder = hud_list[DIAG_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon_state = "huddiag[RoundDiagBar(health / maxHealth)]"
+
+/mob/living/simple_animal/hostile/swarmer/med_hud_set_status()
+ var/image/holder = hud_list[DIAG_STAT_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ holder.icon_state = "hudstat"
+
/mob/living/simple_animal/hostile/swarmer/Stat()
..()
if(statpanel("Status"))
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index 42003013441..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
@@ -41,6 +42,21 @@
var/adminseal = FALSE
var/name_color = "white"//only used with protector shields for the time being
+/mob/living/simple_animal/hostile/guardian/med_hud_set_health()
+ if(summoner)
+ var/image/holder = hud_list[HEALTH_HUD]
+ holder.icon_state = "hud[RoundHealth(summoner)]"
+
+/mob/living/simple_animal/hostile/guardian/med_hud_set_status()
+ if(summoner)
+ var/image/holder = hud_list[STATUS_HUD]
+ var/icon/I = icon(icon, icon_state, dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ if(summoner.stat == DEAD)
+ holder.icon_state = "huddead"
+ else
+ holder.icon_state = "hudhealthy"
+
/mob/living/simple_animal/hostile/guardian/Life() //Dies if the summoner dies
..()
if(summoner)
@@ -88,7 +104,8 @@
resulthealth = round((summoner.health / summoner.maxHealth) * 100)
if(hud_used)
hud_used.guardianhealthdisplay.maptext = "[resulthealth]% "
-
+ med_hud_set_health()
+ med_hud_set_status()
/mob/living/simple_animal/hostile/guardian/adjustHealth(amount) //The spirit is invincible, but passes on damage to the summoner
var/damage = amount * damage_transfer
@@ -326,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")
@@ -356,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/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm
index c8001050ad1..d51e4cefd45 100644
--- a/code/game/gamemodes/miniantags/guardian/types/healer.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm
@@ -52,6 +52,9 @@
C.adjustOxyLoss(-5)
C.adjustToxLoss(-5)
heal_cooldown = world.time + 20
+ if(C == summoner)
+ med_hud_set_health()
+ med_hud_set_status()
/mob/living/simple_animal/hostile/guardian/healer/ToggleMode()
if(loc == summoner)
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/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 338484a5ebc..caec78f3594 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -53,6 +53,7 @@
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has.
var/image/ghostimage = null //Visible to ghost with darkness off
+
/mob/living/simple_animal/revenant/Life()
..()
if(revealed && essence <= 0)
@@ -117,6 +118,7 @@
ghostimage = image(src.icon,src,src.icon_state)
ghost_darkness_images |= ghostimage
updateallghostimages()
+ remove_from_all_data_huds()
spawn(5)
if(src.mind)
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index 25ebd7bc379..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
@@ -57,6 +58,7 @@
/mob/living/simple_animal/slaughter/New()
..()
+ remove_from_all_data_huds()
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell)
whisper_action = new()
@@ -113,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/objective.dm b/code/game/gamemodes/objective.dm
index 01d4e0ed3a7..ea796011e96 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -264,7 +264,7 @@ var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datu
if(!location)
return 0
- if(istype(location, /turf/simulated/shuttle/floor4)) // Fails traitors if they are in the shuttle brig -- Polymorph
+ if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph
return 0
if(location.onCentcom() || location.onSyndieBase())
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..6a1fa93c665 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()
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..74e2928b262 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()
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..c3a650f47be 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,7 +425,7 @@ 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)
@@ -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..40594f8ddb7 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,11 +248,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 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")
@@ -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..8e3f5b9adb1 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()
@@ -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 464381f0fa5..eede404d065 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()
@@ -366,9 +366,10 @@
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
else if(href_list["print_p"])
if(!printing)
printing = 1
@@ -377,6 +378,7 @@
if(istype(active1, /datum/data/record) && data_core.general.Find(active1))
create_record_photo(active1)
printing = 0
+*/
else if(href_list["printlogs"])
if(cell_logs.len && !printing)
@@ -510,6 +512,8 @@
/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list())
temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
+/* Proc disabled due to BYOND Issue
+
/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R)
// basically copy-pasted from the camera code but different enough that it has to be redone
var/icon/photoimage = get_record_photo(R)
@@ -536,6 +540,8 @@
var/obj/item/weapon/photo/PH = new/obj/item/weapon/photo(loc)
PH.construct(P)
+*/
+
/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R)
// similar to the code to make a photo, but of course the actual rendering is completely different
var/icon/res = icon('icons/effects/96x96.dmi', "")
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/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm
index d50d0355d5c..b29d79f40d8 100644
--- a/code/game/machinery/computer/specops_shuttle.dm
+++ b/code/game/machinery/computer/specops_shuttle.dm
@@ -231,7 +231,7 @@ var/specops_shuttle_timeleft = 0
for(var/turf/T in get_area_turfs(end_location) )
var/mob/M = locate(/mob) in T
- to_chat(M, "You have arrived to [station_name]. Commence operation!")
+ to_chat(M, "You have arrived to [station_name()]. Commence operation!")
for(var/obj/machinery/computer/specops_shuttle/S in world)
S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY
@@ -275,8 +275,8 @@ var/specops_shuttle_timeleft = 0
dat = temp
else
dat += {" Special Operations Shuttle
- \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
- [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.* \n ":specops_shuttle_at_station ? "\nShuttle standing by... \n ":"\nDepart to [station_name] \n "]
+ \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
+ [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.* \n ":specops_shuttle_at_station ? "\nShuttle standing by... \n ":"\nDepart to [station_name()] \n "]
\nClose"}
user << browse(dat, "window=computer;size=575x450")
@@ -318,7 +318,7 @@ var/specops_shuttle_timeleft = 0
to_chat(usr, "The Special Operations shuttle is unable to leave.")
return
- to_chat(usr, "The Special Operations shuttle will arrive on [station_name] in [(SPECOPS_MOVETIME/10)] seconds.")
+ to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.")
temp += "Shuttle departing.
OK"
updateUsrDialog()
@@ -337,4 +337,4 @@ var/specops_shuttle_timeleft = 0
add_fingerprint(usr)
updateUsrDialog()
- return
\ No newline at end of file
+ return
diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm
index 14a3fdb72ca..b1a278a8955 100644
--- a/code/game/machinery/computer/syndicate_specops_shuttle.dm
+++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm
@@ -172,7 +172,7 @@ var/syndicate_elite_shuttle_timeleft = 0
for(var/turf/T in get_area_turfs(end_location) )
var/mob/M = locate(/mob) in T
- to_chat(M, "You have arrived to [station_name]. Commence operation!")
+ to_chat(M, "You have arrived to [station_name()]. Commence operation!")
/proc/syndicate_elite_can_move()
if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0
@@ -209,8 +209,8 @@ var/syndicate_elite_shuttle_timeleft = 0
dat = temp
else
dat = {" Special Operations Shuttle
- \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
- [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.* \n ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline \n ":"\nDepart to [station_name] \n "]
+ \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name()] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
+ [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.* \n ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline \n ":"\nDepart to [station_name()] \n "]
\nClose"}
user << browse(dat, "window=computer;size=575x450")
@@ -237,7 +237,7 @@ var/syndicate_elite_shuttle_timeleft = 0
to_chat(usr, "The Syndicate Elite shuttle is unable to leave.")
return
- to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.")
+ to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.")
temp = "Shuttle departing.
OK"
updateUsrDialog()
@@ -257,4 +257,4 @@ var/syndicate_elite_shuttle_timeleft = 0
add_fingerprint(usr)
updateUsrDialog()
- return
\ No newline at end of file
+ return
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/dance_machine.dm b/code/game/machinery/dance_machine.dm
new file mode 100644
index 00000000000..a7ddc648df6
--- /dev/null
+++ b/code/game/machinery/dance_machine.dm
@@ -0,0 +1,479 @@
+/obj/machinery/disco
+ name = "radiant dance machine mark IV"
+ desc = "The first three prototypes were discontinued after mass casualty incidents."
+ icon = 'icons/obj/lighting.dmi'
+ icon_state = "disco0"
+ anchored = FALSE
+ atom_say_verb = "states"
+ density = TRUE
+ var/active = FALSE
+ var/list/rangers = list()
+ var/charge = 35
+ var/stop = 0
+ var/list/spotlights = list()
+ var/list/sparkles = list()
+ var/static/list/songs = list(
+ new /datum/track("Engineering's Basic Beat", 'sound/misc/disco.ogg', 600, 5),
+ new /datum/track("Engineering's Domination Dance", 'sound/misc/e1m1.ogg', 950, 6),
+ new /datum/track("Engineering's Superiority Shimmy", 'sound/misc/paradox.ogg', 2400, 4),
+ new /datum/track("Engineering's Ultimate High-Energy Hustle", 'sound/misc/boogie2.ogg', 1770, 5),
+ )
+ var/datum/track/selection = null
+
+/datum/track
+ var/song_name = "generic"
+ var/song_path = null
+ var/song_length = 0
+ var/song_beat = 0
+ var/GBP_required = 0
+
+/datum/track/New(name, path, length, beat)
+ song_name = name
+ song_path = path
+ song_length = length
+ song_beat = beat
+
+/obj/machinery/disco/proc/add_track(file, name, length, beat)
+ var/sound/S = file
+ if(!istype(S))
+ return
+ if(!name)
+ name = "[file]"
+ if(!beat)
+ beat = 5
+ if(!length)
+ length = 2400 //Unless there's a way to discern via BYOND.
+ var/datum/track/T = new /datum/track(name, file, length, beat)
+ songs += T
+
+/obj/machinery/disco/New()
+ . = ..()
+ selection = songs[1]
+
+
+/obj/machinery/disco/Destroy()
+ dance_over()
+ selection = null
+ return ..()
+
+/obj/machinery/disco/attackby(obj/item/O, mob/user, params)
+ if(!active)
+ if(iswrench(O))
+ if(!anchored && !isinspace())
+ to_chat(user,"You secure [src] to the floor.")
+ anchored = TRUE
+ else if(anchored)
+ to_chat(user,"You unsecure and disconnect [src].")
+ anchored = FALSE
+ playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
+ return
+ return ..()
+
+/obj/machinery/disco/update_icon()
+ if(active)
+ icon_state = "disco1"
+ else
+ icon_state = "disco0"
+ ..()
+
+
+/obj/machinery/disco/attack_hand(mob/user)
+ if(..())
+ return
+
+ interact(user)
+
+/obj/machinery/disco/interact(mob/user)
+ if(!anchored)
+ to_chat(user,"This device must be anchored by a wrench!")
+ return
+ if(!Adjacent(user) && !isAI(user))
+ return
+ user.set_machine(src)
+ var/list/dat = list()
+ dat +=" "
+ dat += " Select Track "
+ dat += "Track Selected: [selection.song_name] "
+ dat += "Track Length: [DisplayTimeText(selection.song_length)]
"
+ dat += " DJ's Soundboard: "
+ dat +=""
+ dat += " Air Horn "
+ dat += " Station Alert "
+ dat += " Warning Siren "
+ dat += " Honk"
+ dat += " Shotgun Pump"
+ dat += " Gunshot"
+ dat += " Esword"
+ dat += " Harm Alarm"
+ var/datum/browser/popup = new(user, "vending", "Radiance Dance Machine - Mark IV", 400, 350)
+ popup.set_content(dat.Join())
+ popup.open()
+
+
+/obj/machinery/disco/Topic(href, href_list)
+ if(..())
+ return
+ add_fingerprint(usr)
+ switch(href_list["action"])
+ if("toggle")
+ if(qdeleted(src))
+ return
+ if(!active)
+ if(stop > world.time)
+ to_chat(usr, " Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].")
+ playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
+ return
+ active = TRUE
+ update_icon()
+ dance_setup()
+ processing_objects.Add(src)
+ lights_spin()
+ updateUsrDialog()
+ else if(active)
+ stop = 0
+ updateUsrDialog()
+ if("select")
+ if(active)
+ to_chat(usr, " Error: You cannot change the song until the current one is over.")
+ return
+
+ var/list/available = list()
+ for(var/datum/track/S in songs)
+ available[S.song_name] = S
+ var/selected = input(usr, "Choose your song", "Track:") as null|anything in available
+ if(qdeleted(src) || !selected || !istype(available[selected], /datum/track))
+ return
+ selection = available[selected]
+ updateUsrDialog()
+ if("horn")
+ deejay('sound/items/airhorn2.ogg')
+ if("alert")
+ deejay('sound/misc/notice1.ogg')
+ if("siren")
+ deejay('sound/machines/engine_alert1.ogg')
+ if("honk")
+ deejay('sound/items/bikehorn.ogg')
+ if("pump")
+ deejay('sound/weapons/shotgunpump.ogg')
+ if("pop")
+ deejay('sound/weapons/gunshot3.ogg')
+ if("saber")
+ deejay('sound/weapons/saberon.ogg')
+ if("harm")
+ deejay('sound/ai/harmalarm.ogg')
+
+/obj/machinery/disco/proc/deejay(S)
+ if(qdeleted(src) || !active || charge < 5)
+ to_chat(usr, " The device is not able to play more DJ sounds at this time.")
+ return
+ charge -= 5
+ playsound(src, S, 300, 1)
+
+/obj/machinery/disco/proc/dance_setup()
+ stop = world.time + selection.song_length
+ var/turf/cen = get_turf(src)
+ FOR_DVIEW(var/turf/t, 3, get_turf(src),INVISIBILITY_LIGHTING)
+ if(t.x == cen.x && t.y > cen.y)
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "red"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1+get_dist(src, L)
+ spotlights+=L
+ continue
+ if(t.x == cen.x && t.y < cen.y)
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "purple"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1+get_dist(src, L)
+ spotlights+=L
+ continue
+ if(t.x > cen.x && t.y == cen.y)
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "#ffff00"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1+get_dist(src, L)
+ spotlights+=L
+ continue
+ if(t.x < cen.x && t.y == cen.y)
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "green"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1+get_dist(src, L)
+ spotlights+=L
+ continue
+ if((t.x+1 == cen.x && t.y+1 == cen.y) || (t.x+2==cen.x && t.y+2 == cen.y))
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "sw"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1.4+get_dist(src, L)
+ spotlights+=L
+ continue
+ if((t.x-1 == cen.x && t.y-1 == cen.y) || (t.x-2==cen.x && t.y-2 == cen.y))
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "ne"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1.4+get_dist(src, L)
+ spotlights+=L
+ continue
+ if((t.x-1 == cen.x && t.y+1 == cen.y) || (t.x-2==cen.x && t.y+2 == cen.y))
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "se"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1.4+get_dist(src, L)
+ spotlights+=L
+ continue
+ if((t.x+1 == cen.x && t.y-1 == cen.y) || (t.x+2==cen.x && t.y-2 == cen.y))
+ var/obj/item/device/flashlight/spotlight/L = new /obj/item/device/flashlight/spotlight(t)
+ L.light_color = "nw"
+ L.light_power = 30 - (get_dist(src, L) * 8)
+ L.range = 1.4+get_dist(src, L)
+ spotlights+=L
+ continue
+ continue
+ END_FOR_DVIEW
+
+/obj/machinery/disco/proc/hierofunk()
+ for(var/i in 1 to 10)
+ new /obj/effect/temp_visual/hierophant/telegraph/edge(get_turf(src))
+ sleep(5)
+
+/obj/machinery/disco/proc/lights_spin()
+ for(var/i in 1 to 25)
+ if(qdeleted(src) || !active)
+ return
+ var/obj/effect/overlay/sparkles/S = new /obj/effect/overlay/sparkles(src)
+ S.alpha = 0
+ sparkles += S
+ switch(i)
+ if(1 to 8)
+ spawn(0)
+ S.orbit(src, 30, TRUE, 60, 36, TRUE, FALSE)
+ if(9 to 16)
+ spawn(0)
+ S.orbit(src, 62, TRUE, 60, 36, TRUE, FALSE)
+ if(17 to 24)
+ spawn(0)
+ S.orbit(src, 95, TRUE, 60, 36, TRUE, FALSE)
+ if(25)
+ S.pixel_y = 7
+ S.forceMove(get_turf(src))
+ sleep(7)
+ if(selection.song_name == "Engineering's Ultimate High-Energy Hustle")
+ sleep(280)
+ for(var/obj/reveal in sparkles)
+ reveal.alpha = 255
+ while(active)
+ for(var/obj/item/device/flashlight/spotlight/glow in spotlights) // The multiples reflects custom adjustments to each colors after dozens of tests
+ if(qdeleted(src) || !active || qdeleted(glow))
+ return
+ if(glow.light_color == "red")
+ glow.light_color = "nw"
+ glow.light_power = glow.light_power * 1.48
+ glow.light_range = 0
+ glow.update_light()
+ continue
+ if(glow.light_color == "nw")
+ glow.light_color = "green"
+ glow.light_range = glow.range * 1.1
+ glow.light_power = glow.light_power * 2 // Any changes to power must come in pairs to neutralize it for other colors
+ glow.update_light()
+ continue
+ if(glow.light_color == "green")
+ glow.light_color = "sw"
+ glow.light_power = glow.light_power * 0.5
+ glow.light_range = 0
+ glow.update_light()
+ continue
+ if(glow.light_color == "sw")
+ glow.light_color = "purple"
+ glow.light_power = glow.light_power * 2.27
+ glow.light_range = glow.range * 1.15
+ glow.update_light()
+ continue
+ if(glow.light_color == "purple")
+ glow.light_color = "se"
+ glow.light_power = glow.light_power * 0.44
+ glow.light_range = 0
+ glow.update_light()
+ continue
+ if(glow.light_color == "se")
+ glow.light_color = "#ffff00"
+ glow.light_range = glow.range * 0.9
+ glow.update_light()
+ continue
+ if(glow.light_color == "#ffff00")
+ glow.light_color = "ne"
+ glow.light_range = 0
+ glow.update_light()
+ continue
+ if(glow.light_color == "ne")
+ glow.light_color = "red"
+ glow.light_power = glow.light_power * 0.68
+ glow.light_range = glow.range * 0.85
+ glow.update_light()
+ continue
+ if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up
+ INVOKE_ASYNC(src, .proc/hierofunk)
+ sleep(selection.song_beat)
+
+
+/obj/machinery/disco/proc/dance(mob/living/M) //Show your moves
+ set waitfor = FALSE
+ switch(rand(0,9))
+ if(0 to 1)
+ dance2(M)
+ if(2 to 3)
+ dance3(M)
+ if(4 to 6)
+ dance4(M)
+ if(7 to 9)
+ dance5(M)
+
+/obj/machinery/disco/proc/dance2(mob/living/M)
+ for(var/i = 1, i < 10, i++)
+ for(var/d in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH))
+ M.setDir(d)
+ if(i == WEST && !M.incapacitated())
+ M.SpinAnimation(7, 1)
+ sleep(1)
+ sleep(20)
+
+/obj/machinery/disco/proc/dance3(mob/living/M)
+ var/matrix/initial_matrix = matrix(M.transform)
+ for(var/i in 1 to 75)
+ if(!M)
+ return
+ switch(i)
+ if(1 to 15)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0, 1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(16 to 30)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(1, -1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(31 to 45)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(-1, -1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(46 to 60)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(-1, 1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(61 to 75)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(1, 0)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ M.setDir(turn(M.dir, 90))
+ switch(M.dir)
+ if(NORTH)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,3)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(SOUTH)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,-3)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(EAST)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(3,0)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(WEST)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(-3,0)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ sleep(1)
+ M.lying_fix()
+
+
+/obj/machinery/disco/proc/dance4(mob/living/M)
+ var/speed = rand(1, 3)
+ set waitfor = 0
+ var/time = 30
+ while(time)
+ sleep(speed)
+ for(var/i in 1 to speed)
+ M.setDir(pick(cardinal))
+ M.resting = !M.resting
+ M.update_canmove()
+ time--
+
+/obj/machinery/disco/proc/dance5(mob/living/M)
+ animate(M, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0)
+ var/matrix/initial_matrix = matrix(M.transform)
+ for(var/i in 1 to 60)
+ if(!M)
+ return
+ if(i<31)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(i>30)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,-1)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ M.setDir(turn(M.dir, 90))
+ switch(M.dir)
+ if(NORTH)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,3)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(SOUTH)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(0,-3)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(EAST)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(3,0)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ if(WEST)
+ initial_matrix = matrix(M.transform)
+ initial_matrix.Translate(-3,0)
+ animate(M, transform = initial_matrix, time = 1, loop = 0)
+ sleep(1)
+ M.lying_fix()
+
+
+
+/mob/living/proc/lying_fix()
+ animate(src, transform = null, time = 1, loop = 0)
+ lying_prev = 0
+
+/obj/machinery/disco/proc/dance_over()
+ QDEL_LIST(spotlights)
+ QDEL_LIST(sparkles)
+ for(var/mob/living/L in rangers)
+ if(!L || !L.client)
+ continue
+ L.stop_sound_channel(CHANNEL_JUKEBOX)
+ rangers = list()
+
+
+
+/obj/machinery/disco/process()
+ if(charge < 35)
+ charge += 1
+ if(world.time < stop && active)
+ var/sound/song_played = sound(selection.song_path)
+
+ for(var/mob/M in range(10,src))
+ if(!(M in rangers))
+ rangers[M] = TRUE
+ M.playsound_local(get_turf(M), null, 100, channel = CHANNEL_JUKEBOX, S = song_played)
+ if(prob(5+(allowed(M) * 4)) && M.canmove)
+ dance(M)
+ for(var/mob/L in rangers)
+ if(get_dist(src, L) > 10)
+ rangers -= L
+ if(!L || !L.client)
+ continue
+ L.stop_sound_channel(CHANNEL_JUKEBOX)
+ else if(active)
+ active = FALSE
+ processing_objects.Remove(src)
+ dance_over()
+ playsound(src,'sound/machines/terminal_off.ogg',50,1)
+ icon_state = "disco0"
+ stop = world.time + 100
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 15f0a73808c..527a0155c2e 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -5,7 +5,7 @@
mend - mends a wire and makes any necessary state changes
canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in)
canAIHack - 1 if the AI can hack into the airlock to recover control, 0 if not. Also returns 0 if the AI does not *need* to hack it.
- arePowerSystemsOn - 1 if the main or backup power are functioning, 0 if not. Does not check whether the power grid is charged or an APC has equipment on or anything like that. (Check (stat & NOPOWER) for that)
+ arePowerSystemsOn - 1 if the main or backup power are functioning, 0 if not.
requiresIDs - 1 if the airlock is requiring IDs, 0 if not
isAllPowerCut - 1 if the main and backup power both have cut wires.
regainMainPower - handles the effect of main power coming back on.
@@ -18,37 +18,75 @@
// Wires for the airlock are located in the datum folder, inside the wires datum folder.
+#define AIRLOCK_CLOSED 1
+#define AIRLOCK_CLOSING 2
+#define AIRLOCK_OPEN 3
+#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/doorint.dmi'
- icon_state = "door_closed"
- autoclose = 1
+ icon = 'icons/obj/doors/airlocks/station/public.dmi'
+ icon_state = "closed"
+ 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/aiControlDisabled = 0 //If 1, 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 = 0 // if 1, this door can't be hacked by the AI
+ 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/mineral = null
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 //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
+ var/image/old_lights_overlay
+ 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'
var/doorClose = 'sound/machines/airlock_close.ogg'
var/doorDeni = 'sound/machines/DeniedBeep.ogg' // i'm thinkin' Deni's
@@ -80,12 +118,24 @@ 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
- if(welded)
- update_icon()
+ 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)
@@ -99,8 +149,14 @@ About the new airlock wires panel:
if(electrified_timer)
deltimer(electrified_timer)
electrified_timer = null
+ qdel(note)
return ..()
+/obj/machinery/door/airlock/handle_atom_del(atom/A)
+ if(A == note)
+ note = null
+ update_icon()
+
/obj/machinery/door/airlock/bumpopen(mob/living/user) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite
if(!issilicon(usr))
if(isElectrified())
@@ -250,65 +306,240 @@ About the new airlock wires panel:
else
return 0
-/obj/machinery/door/airlock/update_icon()
- if(overlays)
- overlays.Cut()
- overlays = list()
- if(emergency && arePowerSystemsOn())
- overlays += image('icons/obj/doors/doorint.dmi', "elights")
- if(density)
- if(locked && lights)
- icon_state = "door_locked"
- else
- icon_state = "door_closed"
- if(p_open || welded)
- if(p_open)
- overlays += image(icon, "panel_open")
- if(welded)
- overlays += image(icon, "welded")
- else
- icon_state = "door_open"
+/obj/machinery/door/airlock/update_icon(state=0, override=0)
+ if(operating && !override)
+ return
+ switch(state)
+ if(0)
+ if(density)
+ state = AIRLOCK_CLOSED
+ else
+ state = AIRLOCK_OPEN
+ icon_state = ""
+ if(AIRLOCK_OPEN, AIRLOCK_CLOSED)
+ icon_state = ""
+ if(AIRLOCK_DENY, AIRLOCK_OPENING, AIRLOCK_CLOSING, AIRLOCK_EMAG)
+ icon_state = "nonexistenticonstate" //MADNESS
+ set_airlock_overlays(state)
- return
+/obj/machinery/door/airlock/proc/set_airlock_overlays(state)
+ var/image/frame_overlay
+ var/image/filling_overlay
+ 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()
+
+ switch(state)
+ if(AIRLOCK_CLOSED)
+ frame_overlay = get_airlock_overlay("closed", icon)
+ if(airlock_material)
+ filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
+ else
+ filling_overlay = get_airlock_overlay("fill_closed", icon)
+ if(panel_open)
+ 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(obj_integrity Its access panel is smoking slightly.")
+ if(note)
+ if(!in_range(user, src))
+ to_chat(user, "There's a [note.name] pinned to the front. You can't [note_type() == "note" ? "read" : "see"] it from here.")
+ else
+ to_chat(user, "There's a [note.name] pinned to the front...")
+ note.examine(user)
+
+ if(panel_open)
+ switch(security_level)
+ if(AIRLOCK_SECURITY_NONE)
+ to_chat(user, "Its wires are exposed!")
+ if(AIRLOCK_SECURITY_METAL)
+ to_chat(user, "Its wires are hidden behind a welded metal cover.")
+ if(AIRLOCK_SECURITY_PLASTEEL_I_S)
+ to_chat(user, "There is some shredded plasteel inside.")
+ if(AIRLOCK_SECURITY_PLASTEEL_I)
+ to_chat(user, "Its wires are behind an inner layer of plasteel.")
+ if(AIRLOCK_SECURITY_PLASTEEL_O_S)
+ to_chat(user, "There is some shredded plasteel inside.")
+ if(AIRLOCK_SECURITY_PLASTEEL_O)
+ to_chat(user, "There is a welded plasteel cover hiding its wires.")
+ if(AIRLOCK_SECURITY_PLASTEEL)
+ to_chat(user, "There is a protective grille over its panel.")
+ else if(security_level)
+ if(security_level == AIRLOCK_SECURITY_METAL)
+ to_chat(user, "It looks a bit stronger.")
+ else
+ to_chat(user, "It looks very robust.")
/obj/machinery/door/airlock/attack_ghost(mob/user)
- if(p_open)
+ if(panel_open)
wires.Interact(user)
ui_interact(user)
@@ -316,7 +547,7 @@ About the new airlock wires panel:
ui_interact(user)
/obj/machinery/door/airlock/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, "door_control.tmpl", "Door Controls - [src]", 600, 375)
ui.open()
@@ -343,52 +574,51 @@ About the new airlock wires panel:
return data
/obj/machinery/door/airlock/proc/hack(mob/user)
- if(aiHacking==0)
- aiHacking=1
- spawn(20)
- //TODO: Make this take a minute
- to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
- sleep(50)
- if(canAIControl())
- to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
- aiHacking=0
- return
- else if(!canAIHack(user))
- to_chat(user, "We've lost our connection! Unable to hack airlock.")
- aiHacking=0
- return
- to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
- sleep(20)
- to_chat(user, "Attempting to hack into airlock. This may take some time.")
- sleep(200)
- if(canAIControl())
- to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
- aiHacking=0
- return
- else if(!canAIHack(user))
- to_chat(user, "We've lost our connection! Unable to hack airlock.")
- aiHacking=0
- return
- to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
- sleep(170)
- if(canAIControl())
- to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
- aiHacking=0
- return
- else if(!canAIHack(user))
- to_chat(user, "We've lost our connection! Unable to hack airlock.")
- aiHacking=0
- return
- to_chat(user, "Transfer complete. Forcing airlock to execute program.")
- sleep(50)
- //disable blocked control
- aiControlDisabled = 2
- to_chat(user, "Receiving control information from airlock.")
- sleep(10)
- //bring up airlock dialog
- aiHacking = 0
- if(user)
- attack_ai(user)
+ set waitfor = 0
+ if(!aiHacking)
+ aiHacking = TRUE
+ to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
+ sleep(50)
+ if(canAIControl())
+ to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
+ aiHacking = FALSE
+ return
+ else if(!canAIHack(user))
+ to_chat(user, "Connection lost! Unable to hack airlock.")
+ aiHacking=0
+ return
+ to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
+ sleep(20)
+ to_chat(user, "Attempting to hack into airlock. This may take some time.")
+ sleep(200)
+ if(canAIControl())
+ to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
+ aiHacking = FALSE
+ return
+ else if(!canAIHack(user))
+ to_chat(user, "Connection lost! Unable to hack airlock.")
+ aiHacking = FALSE
+ return
+ to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
+ sleep(170)
+ if(canAIControl())
+ to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
+ aiHacking = FALSE
+ return
+ else if(!canAIHack(user))
+ to_chat(user, "Connection lost! Unable to hack airlock.")
+ aiHacking = FALSE
+ return
+ to_chat(user, "Transfer complete. Forcing airlock to execute program.")
+ sleep(50)
+ //disable blocked control
+ aiControlDisabled = 2
+ to_chat(user, "Receiving control information from airlock.")
+ sleep(10)
+ //bring up airlock dialog
+ aiHacking = FALSE
+ if(user)
+ attack_ai(user)
/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target, height=0)
if(isElectrified() && density && istype(mover, /obj/item))
@@ -407,7 +637,7 @@ About the new airlock wires panel:
if(ishuman(user) && prob(40) && density)
var/mob/living/carbon/human/H = user
- if(H.getBrainLoss() >= 60)
+ if(H.getBrainLoss() >= 60 && Adjacent(user))
playsound(loc, 'sound/effects/bang.ogg', 25, 1)
if(!istype(H.head, /obj/item/clothing/head/helmet))
visible_message("[user] headbutts the airlock.")
@@ -420,11 +650,13 @@ About the new airlock wires panel:
visible_message("[user] headbutts the airlock. Good thing they're wearing a helmet.")
return
- if(p_open)
+ if(panel_open)
+ if(security_level)
+ to_chat(user, "Wires are protected!")
+ return
wires.Interact(user)
else
- ..(user)
- return
+ ..()
/obj/machinery/door/airlock/CanUseTopic(mob/user)
if(!issilicon(user) && !isobserver(user))
@@ -463,9 +695,11 @@ About the new airlock wires panel:
if("main_power")
if(!main_power_lost_until)
loseMainPower()
+ update_icon()
if("backup_power")
if(!backup_power_lost_until)
loseBackupPower()
+ update_icon()
if("bolts")
if(isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.")
@@ -530,6 +764,7 @@ About the new airlock wires panel:
else if(activate && !lights)
lights = 1
to_chat(usr, "The door bolt lights have been enabled.")
+ update_icon()
if("emergency")
// Emergency access
if(emergency)
@@ -538,38 +773,162 @@ About the new airlock wires panel:
else
emergency = 1
to_chat(usr, "Emergency access has been enabled.")
-
- update_icon()
+ update_icon()
return 1
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
-// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user))
- 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))
- p_open = !p_open
- to_chat(user, "You [p_open ? "open":"close"] the maintenance panel of the airlock.")
+
+ 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)
update_icon()
else if(iswirecutter(C))
- return attack_hand(user)
+ if(note)
+ user.visible_message("[user] cuts down [note] from [src].", "You remove [note] from [src].")
+ playsound(src, 'sound/items/Wirecutter.ogg', 50, 1)
+ note.forceMove(get_turf(user))
+ note = null
+ update_icon()
+ else
+ return attack_hand(user)
else if(ismultitool(C))
return attack_hand(user)
else if(istype(C, /obj/item/device/assembly/signaler))
@@ -577,166 +936,117 @@ About the new airlock wires panel:
else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE
var/obj/item/weapon/pai_cable/cable = C
cable.plugin(src, user)
- 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 && p_open && (emagged || (density && welded && (!operating || emagged) && !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))
- to_chat(user, "You removed the airlock electronics!")
-
- var/obj/structure/door_assembly/da = new assemblytype(loc)
- da.heat_proof_finished = heat_proof //tracks whether there's rglass in
- da.anchored = 1
- if(mineral)
- da.glass = mineral
- //else if(glass)
- else if(glass && !da.glass)
- da.glass = 1
- da.state = 1
- da.created_name = name
- da.update_state()
-
- 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
- if(emagged)
- ae.icon_state = "door_electronics_smoked"
- operating = 0
-
- qdel(src)
- 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 if(istype(C, /obj/item/weapon/paper) || istype(C, /obj/item/weapon/photo))
+ if(note)
+ to_chat(user, "There's already something pinned to this airlock! Use wirecutters to remove it.")
+ return
+ if(!user.unEquip(C))
+ to_chat(user, "For some reason, you can't attach [C]!")
+ return
+ C.forceMove(src)
+ user.visible_message("[user] pins [C] to [src].", "You pin [C] to [src].")
+ note = C
+ update_icon()
else
- ..()
- return
+ return ..()
-/obj/machinery/door/airlock/plasma/attackby(obj/C, mob/user, params)
- if(is_hot(C) > 300)
- message_admins("Plasma airlock ignited by [key_name_admin(user)] in ([x],[y],[z] - JMP)")
- log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])")
- investigate_log("was ignited by [key_name(user)]","atmos")
- ignite(is_hot(C))
- ..()
-
-/obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params)
-// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user))
- if(!issilicon(user))
- if(isElectrified())
- if(shock(user, 75))
- return
- if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
- return
-
- if(istype(C, /obj/item/weapon/grenade/plastic/c4))
- to_chat(user, "The hatch is coated with a product that prevents the shaped charge from sticking!")
- return
-
- if(istype(C, /obj/item/mecha_parts/mecha_equipment/rcd) || istype(C, /obj/item/weapon/rcd))
- to_chat(user, "The hatch is made of an advanced compound that cannot be deconstructed using an RCD.")
- 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
+/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
- return
-
-/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
-// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user))
- 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
+ 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
- welded = null
- update_icon()
+ 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)
return 0
@@ -750,11 +1060,32 @@ About the new airlock wires panel:
playsound(loc, doorOpen, 30, 1)
if(closeOther != null && istype(closeOther, /obj/machinery/door/airlock/) && !closeOther.density)
closeOther.close()
- return ..()
+ if(!density)
+ return TRUE
+ operating = TRUE
+ update_icon(AIRLOCK_OPENING, 1)
+ sleep(1)
+ set_opacity(0)
+ update_freelook_sight()
+ sleep(4)
+ density = FALSE
+ air_update_turf(1)
+ sleep(1)
+ layer = OPEN_DOOR_LAYER
+ update_icon(AIRLOCK_OPEN, 1)
+ operating = FALSE
+
+ // The `addtimer` system has the advantage of being cancelable
+ if(autoclose)
+ autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1)
+
+ return TRUE
/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
@@ -762,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)
@@ -775,33 +1104,30 @@ 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 1
- operating = 1
- do_animate("closing")
- layer = 3.1
+ operating = TRUE
+ update_icon(AIRLOCK_CLOSING, 1)
+ layer = CLOSED_DOOR_LAYER
if(!override)
- sleep(5)
- density = 1
+ sleep(1)
+ density = TRUE
+ air_update_turf(1)
+ if(!override)
+ sleep(4)
if(!safe)
crush()
- if(!override)
- sleep(5)
- update_icon()
if(visible && !glass)
set_opacity(1)
- operating = 0
- air_update_turf(1)
update_freelook_sight()
+ sleep(1)
+ update_icon(AIRLOCK_CLOSED, 1)
+ operating = FALSE
if(safe)
- if(locate(/mob/living) in get_turf(src))
- open()
+ CheckForMobs()
+ return TRUE
- return
-
-/obj/machinery/door/airlock/proc/lock(forced=0)
+/obj/machinery/door/airlock/lock(forced=0)
if(locked)
return 0
@@ -813,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
@@ -830,12 +1156,48 @@ About the new airlock wires panel:
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut)
return !density || (check_access(ID) && !locked && arePowerSystemsOn())
+/obj/machinery/door/airlock/emag_act(mob/user)
+ if(!operating && density && arePowerSystemsOn() && !emagged)
+ operating = TRUE
+ update_icon(AIRLOCK_EMAG, 1)
+ sleep(6)
+ if(qdeleted(src))
+ return
+ operating = FALSE
+ if(!open())
+ update_icon(AIRLOCK_CLOSED, 1)
+ emagged = TRUE
+ 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
..()
@@ -872,8 +1234,100 @@ 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
+ if(assemblytype)
+ DA = new assemblytype(loc)
+ else
+ DA = new /obj/structure/door_assembly(loc)
+ //If you come across a null assemblytype, it will produce the default assembly instead of disintegrating.
+ DA.heat_proof_finished = heat_proof //tracks whether there's rglass in
+ DA.anchored = TRUE
+ DA.glass = src.glass
+ DA.state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS
+ DA.created_name = name
+ 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)
+ 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)
+ ae.icon_state = "door_electronics_smoked"
+ operating = 0
+ qdel(src)
+
+/obj/machinery/door/airlock/proc/note_type() //Returns a string representing the type of note pinned to this airlock
+ if(!note)
+ return
+ else if(istype(note, /obj/item/weapon/paper))
+ return "note"
+ else if(istype(note, /obj/item/weapon/photo))
+ return "photo"
+
/obj/machinery/door/airlock/narsie_act()
var/turf/T = get_turf(src)
- var/obj/machinery/door/airlock/cult/A = new(T)
+ var/runed = prob(20)
+ var/obj/machinery/door/airlock/cult/A
+ if(glass)
+ if(runed)
+ A = new/obj/machinery/door/airlock/cult/glass(T)
+ else
+ A = new/obj/machinery/door/airlock/cult/unruned/glass(T)
+ else
+ if(runed)
+ A = new/obj/machinery/door/airlock/cult(T)
+ else
+ A = new/obj/machinery/door/airlock/cult/unruned(T)
A.name = name
qdel(src)
+
+#undef AIRLOCK_CLOSED
+#undef AIRLOCK_CLOSING
+#undef AIRLOCK_OPEN
+#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 08c16d77654..764a817c7bf 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -3,47 +3,55 @@
*/
/obj/machinery/door/airlock/command
- icon = 'icons/obj/doors/Doorcom.dmi'
+ 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/Doorsec.dmi'
+ 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/Dooreng.dmi'
+ icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_eng
/obj/machinery/door/airlock/medical
- icon = 'icons/obj/doors/Doormed.dmi'
+ icon = 'icons/obj/doors/airlocks/station/medical.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_med
/obj/machinery/door/airlock/maintenance
name = "maintenance access"
- icon = 'icons/obj/doors/Doormaint.dmi'
+ 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"
+ icon = 'icons/obj/doors/airlocks/station/maintenanceexternal.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_extmai
/obj/machinery/door/airlock/mining
name = "mining airlock"
- icon = 'icons/obj/doors/Doormining.dmi'
+ icon = 'icons/obj/doors/airlocks/station/mining.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_min
/obj/machinery/door/airlock/atmos
name = "atmospherics airlock"
- icon = 'icons/obj/doors/Dooratmo.dmi'
+ icon = 'icons/obj/doors/airlocks/station/atmos.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_atmo
/obj/machinery/door/airlock/research
- icon = 'icons/obj/doors/Doorresearch.dmi'
+ icon = 'icons/obj/doors/airlocks/station/research.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_research
/obj/machinery/door/airlock/freezer
name = "freezer airlock"
- icon = 'icons/obj/doors/Doorfreezer.dmi'
+ icon = 'icons/obj/doors/airlocks/station/freezer.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_fre
/obj/machinery/door/airlock/science
- icon = 'icons/obj/doors/Doorsci.dmi'
+ icon = 'icons/obj/doors/airlocks/station/science.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_science
//////////////////////////////////
@@ -51,53 +59,52 @@
Station Airlocks Glass
*/
-/obj/machinery/door/airlock/glass_command
- icon = 'icons/obj/doors/Doorcomglass.dmi'
+/obj/machinery/door/airlock/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_com
- glass = 1
+ glass = TRUE
-/obj/machinery/door/airlock/glass_engineering
- icon = 'icons/obj/doors/Doorengglass.dmi'
+/obj/machinery/door/airlock/command/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_eng
- glass = 1
+ glass = TRUE
+ normal_integrity = 400
-/obj/machinery/door/airlock/glass_security
- icon = 'icons/obj/doors/Doorsecglass.dmi'
+/obj/machinery/door/airlock/engineering/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_sec
- glass = 1
+ glass = TRUE
-/obj/machinery/door/airlock/glass_medical
- icon = 'icons/obj/doors/Doormedglass.dmi'
+/obj/machinery/door/airlock/security/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_med
- glass = 1
+ glass = TRUE
+ normal_integrity = 400
-/obj/machinery/door/airlock/glass_research
- icon = 'icons/obj/doors/Doorresearchglass.dmi'
+/obj/machinery/door/airlock/medical/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_research
- glass = 1
+ glass = TRUE
-/obj/machinery/door/airlock/glass_mining
- icon = 'icons/obj/doors/Doorminingglass.dmi'
+/obj/machinery/door/airlock/research/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_min
- glass = 1
+ glass = TRUE
-/obj/machinery/door/airlock/glass_atmos
- icon = 'icons/obj/doors/Dooratmoglass.dmi'
+/obj/machinery/door/airlock/mining/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_atmo
- glass = 1
+ glass = TRUE
-/obj/machinery/door/airlock/glass_science
- icon = 'icons/obj/doors/Doorsciglass.dmi'
+/obj/machinery/door/airlock/atmos/glass
opacity = 0
- assemblytype = /obj/structure/door_assembly/door_assembly_science
- glass = 1
+ glass = TRUE
+
+/obj/machinery/door/airlock/science/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/maintenance/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/maintenance/external/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 200
//////////////////////////////////
/*
@@ -106,24 +113,39 @@
/obj/machinery/door/airlock/gold
name = "gold airlock"
- icon = 'icons/obj/doors/Doorgold.dmi'
- mineral = "gold"
+ icon = 'icons/obj/doors/airlocks/station/gold.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_gold
+
+/obj/machinery/door/airlock/gold/glass
+ opacity = 0
+ glass = TRUE
/obj/machinery/door/airlock/silver
name = "silver airlock"
- icon = 'icons/obj/doors/Doorsilver.dmi'
- mineral = "silver"
+ icon = 'icons/obj/doors/airlocks/station/silver.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_silver
+
+/obj/machinery/door/airlock/silver/glass
+ opacity = 0
+ glass = TRUE
/obj/machinery/door/airlock/diamond
name = "diamond airlock"
- icon = 'icons/obj/doors/Doordiamond.dmi'
- mineral = "diamond"
+ 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
/obj/machinery/door/airlock/uranium
name = "uranium airlock"
desc = "And they said I was crazy."
- icon = 'icons/obj/doors/Dooruranium.dmi'
- mineral = "uranium"
+ icon = 'icons/obj/doors/airlocks/station/uranium.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_uranium
var/event_step = 20
/obj/machinery/door/airlock/uranium/New()
@@ -136,11 +158,15 @@
L.apply_effect(15,IRRADIATE,0)
addtimer(src, "radiate", event_step)
+/obj/machinery/door/airlock/uranium/glass
+ opacity = 0
+ glass = TRUE
+
/obj/machinery/door/airlock/plasma
name = "plasma airlock"
desc = "No way this can end badly."
- icon = 'icons/obj/doors/Doorplasma.dmi'
- mineral = "plasma"
+ icon = 'icons/obj/doors/airlocks/station/plasma.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_plasma
/obj/machinery/door/airlock/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > 300)
@@ -152,23 +178,47 @@
/obj/machinery/door/airlock/plasma/proc/PlasmaBurn(temperature)
atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 500)
- new/obj/structure/door_assembly(loc)
+ var/obj/structure/door_assembly/DA
+ DA = new /obj/structure/door_assembly(loc)
+ if(glass)
+ DA.glass = TRUE
+ if(heat_proof)
+ DA.heat_proof_finished = TRUE
+ DA.update_icon()
+ DA.update_name()
qdel(src)
+/obj/machinery/door/airlock/plasma/attackby(obj/C, mob/user, params)
+ if(is_hot(C) > 300)
+ message_admins("Plasma airlock ignited by [key_name_admin(user)] in ([x],[y],[z] - JMP)")
+ 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
+/obj/machinery/door/airlock/plasma/glass
+ opacity = 0
+ glass = TRUE
+
/obj/machinery/door/airlock/bananium
name = "bananium airlock"
- icon = 'icons/obj/doors/Doorbananium.dmi'
- mineral = "bananium"
+ desc = "Honkhonkhonk"
+ icon = 'icons/obj/doors/airlocks/station/bananium.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_bananium
doorOpen = 'sound/items/bikehorn.ogg'
doorClose = 'sound/items/bikehorn.ogg'
+/obj/machinery/door/airlock/bananium/glass
+ opacity = 0
+ glass = TRUE
+
/obj/machinery/door/airlock/tranquillite
name = "tranquillite airlock"
- icon = 'icons/obj/doors/Doorfreezer.dmi'
- mineral = "tranquillite"
+ icon = 'icons/obj/doors/airlocks/station/freezer.dmi'
doorOpen = null // it's silent!
doorClose = null
doorDeni = null
@@ -177,21 +227,47 @@
/obj/machinery/door/airlock/sandstone
name = "sandstone airlock"
- icon = 'icons/obj/doors/Doorsand.dmi'
- mineral = "sandstone"
+ icon = 'icons/obj/doors/airlocks/station/sandstone.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_sandstone
+
+/obj/machinery/door/airlock/sandstone/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/wood
+ name = "wooden airlock"
+ icon = 'icons/obj/doors/airlocks/station/wood.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_wood
+
+/obj/machinery/door/airlock/wood/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/titanium
+ name = "shuttle airlock"
+ 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
//////////////////////////////////
/*
Station2 Airlocks
*/
-/obj/machinery/door/airlock/glass
- name = "glass airlock"
- icon = 'icons/obj/doors/Doorglass.dmi'
+/obj/machinery/door/airlock/public
+ icon = 'icons/obj/doors/airlocks/station2/glass.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_public
+
+/obj/machinery/door/airlock/public/glass
opacity = 0
- glass = 1
- doorOpen = 'sound/machines/windowdoor.ogg'
- doorClose = 'sound/machines/windowdoor.ogg'
+ glass = TRUE
//////////////////////////////////
/*
@@ -200,20 +276,30 @@
/obj/machinery/door/airlock/external
name = "external airlock"
- icon = 'icons/obj/doors/Doorext.dmi'
+ icon = 'icons/obj/doors/airlocks/external/external.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
+ note_overlay_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_ext
doorOpen = 'sound/machines/airlock_ext_open.ogg'
doorClose = 'sound/machines/airlock_ext_close.ogg'
+/obj/machinery/door/airlock/external/glass
+ opacity = 0
+ glass = TRUE
+
//////////////////////////////////
/*
CentCom Airlocks
*/
/obj/machinery/door/airlock/centcom
- icon = 'icons/obj/doors/Doorele.dmi'
+ icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi'
opacity = 0
+ explosion_block = 2
assemblytype = /obj/structure/door_assembly/door_assembly_centcom
+ normal_integrity = 1000
+ security_level = 6
//////////////////////////////////
/*
@@ -222,9 +308,12 @@
/obj/machinery/door/airlock/vault
name = "vault door"
- icon = 'icons/obj/doors/vault.dmi'
+ icon = 'icons/obj/doors/airlocks/vault/vault.dmi'
+ 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
//////////////////////////////////
/*
@@ -233,7 +322,9 @@
/obj/machinery/door/airlock/hatch
name = "airtight hatch"
- icon = 'icons/obj/doors/Doorhatchele.dmi'
+ icon = 'icons/obj/doors/airlocks/hatch/centcom.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
+ note_overlay_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_hatch
/obj/machinery/door/airlock/hatch/gamma
@@ -243,9 +334,37 @@
unacidable = 1
is_special = 1
+/obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params)
+ if(!issilicon(user))
+ if(isElectrified())
+ if(shock(user, 75))
+ return
+ if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
+ return
+
+ if(istype(C, /obj/item/weapon/grenade/plastic/c4))
+ to_chat(user, "The hatch is coated with a product that prevents the shaped charge from sticking!")
+ return
+
+ if(istype(C, /obj/item/mecha_parts/mecha_equipment/rcd) || istype(C, /obj/item/weapon/rcd))
+ to_chat(user, "The hatch is made of an advanced compound that cannot be deconstructed using an RCD.")
+ return
+
+ add_fingerprint(user)
+ if((iswelder(C) && !operating && density))
+ var/obj/item/weapon/weldingtool/W = C
+ if(W.remove_fuel(0,user))
+ welded = !welded
+ update_icon()
+ return
+ else
+ return
+
/obj/machinery/door/airlock/maintenance_hatch
name = "maintenance hatch"
- icon = 'icons/obj/doors/Doorhatchmaint2.dmi'
+ icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
+ note_overlay_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_mhatch
//////////////////////////////////
@@ -255,15 +374,37 @@
/obj/machinery/door/airlock/highsecurity
name = "high tech security airlock"
- icon = 'icons/obj/doors/hightechsecurity.dmi'
+ icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi'
+ 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"
hackProof = 1
aiControlDisabled = 1
+/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
+ 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))
+ welded = !welded
+ update_icon()
+ return
+ else
+ return
+
//////////////////////////////////
/*
Shuttle Airlocks
@@ -271,9 +412,28 @@
/obj/machinery/door/airlock/shuttle
name = "shuttle airlock"
- icon = 'icons/obj/doors/doorshuttle.dmi'
+ icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_shuttle
+/obj/machinery/door/airlock/shuttle/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/abductor
+ name = "alien airlock"
+ desc = "With humanity's current technological level, it could take years to hack this advanced airlock... or maybe we should give a screwdriver a try?"
+ icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
+ 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
+
//////////////////////////////////
/*
Cult Airlocks
@@ -281,33 +441,35 @@
/obj/machinery/door/airlock/cult
name = "cult airlock"
- icon = 'icons/obj/doors/doorcult.dmi'
+ icon = 'icons/obj/doors/airlocks/cult/runed/cult.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/cult/runed/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_cult
- hackProof = 1
- aiControlDisabled = 1
+ hackProof = TRUE
+ aiControlDisabled = TRUE
+ var/openingoverlaytype = /obj/effect/temp_visual/cult/door
var/friendly = FALSE
/obj/machinery/door/airlock/cult/New()
..()
+ new openingoverlaytype(loc)
/obj/machinery/door/airlock/cult/canAIControl(mob/user)
- return (iscultist(user))
+ return (iscultist(user) && !isAllPowerLoss())
-/obj/machinery/door/airlock/cult/allowed(mob/M)
+/obj/machinery/door/airlock/cult/allowed(mob/living/L)
if(!density)
return 1
- if(friendly || \
- iscultist(M) || \
- istype(M, /mob/living/simple_animal/shade) || \
- istype(M, /mob/living/simple_animal/construct))
+ if(friendly || iscultist(L) || isshade(L)|| isconstruct(L))
+ new openingoverlaytype(loc)
return 1
else
+ new /obj/effect/temp_visual/cult/sac(loc)
var/atom/throwtarget
- throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(M, src)))
- M << pick(sound('sound/hallucinations/turn_around1.ogg',0,1,50), sound('sound/hallucinations/turn_around2.ogg',0,1,50))
- M.Weaken(2)
+ throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
+ L << pick(sound('sound/hallucinations/turn_around1.ogg',0,1,50), sound('sound/hallucinations/turn_around2.ogg',0,1,50))
+ L.Weaken(2)
spawn(0)
- M.throw_at(throwtarget, 5, 1,src)
+ L.throw_at(throwtarget, 5, 1,src)
return 0
/obj/machinery/door/airlock/cult/narsie_act()
@@ -316,6 +478,29 @@
/obj/machinery/door/airlock/cult/friendly
friendly = TRUE
+/obj/machinery/door/airlock/cult/glass
+ glass = TRUE
+ opacity = 0
+
+/obj/machinery/door/airlock/cult/glass/friendly
+ friendly = TRUE
+
+/obj/machinery/door/airlock/cult/unruned
+ icon = 'icons/obj/doors/airlocks/cult/unruned/cult.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/cult/unruned/overlays.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_cult/unruned
+ openingoverlaytype = /obj/effect/temp_visual/cult/door/unruned
+
+/obj/machinery/door/airlock/cult/unruned/friendly
+ friendly = TRUE
+
+/obj/machinery/door/airlock/cult/unruned/glass
+ glass = TRUE
+ opacity = 0
+
+/obj/machinery/door/airlock/cult/unruned/glass/friendly
+ friendly = TRUE
+
//////////////////////////////////
/*
Misc Airlocks
@@ -323,11 +508,17 @@
//Terribly sorry for the code doubling, but things go derpy otherwise.
/obj/machinery/door/airlock/multi_tile
+ name = "large airlock"
+ dir = EAST
width = 2
+ icon = 'icons/obj/doors/airlocks/glass_large/glass_large.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/glass_large/overlays.dmi'
+ note_overlay_file = 'icons/obj/doors/airlocks/glass_large/overlays.dmi'
+ assemblytype = "obj/structure/door_assembly/multi_tile"
+
+/obj/machinery/door/airlock/multi_tile/narsie_act()
+ return
/obj/machinery/door/airlock/multi_tile/glass
- name = "large glass airlock"
- icon = 'icons/obj/doors/Door2x1glass.dmi'
opacity = 0
- glass = 1
- assemblytype = "obj/structure/door_assembly/multi_tile"
+ glass = TRUE
diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm
index 839fb387ddb..cdada603a9b 100644
--- a/code/game/machinery/doors/alarmlock.dm
+++ b/code/game/machinery/doors/alarmlock.dm
@@ -1,6 +1,7 @@
/obj/machinery/door/airlock/alarmlock
name = "glass alarm airlock"
- icon = 'icons/obj/doors/Doorglass.dmi'
+ icon = 'icons/obj/doors/airlocks/station2/glass.dmi'
+ overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi'
opacity = 0
glass = 1
autoclose = 0
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index db63d48935f..bfb758bae75 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -1,48 +1,55 @@
-#define DOOR_OPEN_LAYER 2.7 //Under all objects if opened. 2.7 due to tables being at 2.6
-#define DOOR_CLOSED_LAYER 3.1 //Above most items if closed
-
/obj/machinery/door
name = "door"
desc = "It opens and closes."
icon = 'icons/obj/doors/Doorint.dmi'
icon_state = "door1"
- anchored = 1
+ anchored = TRUE
opacity = 1
- density = 1
- layer = DOOR_OPEN_LAYER
+ density = TRUE
+ layer = OPEN_DOOR_LAYER
power_channel = ENVIRON
- var/open_layer = DOOR_OPEN_LAYER
- var/closed_layer = DOOR_CLOSED_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/p_open = 0
- 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
- dir = EAST
var/width = 1
/obj/machinery/door/New()
- . = ..()
- if(density)
- layer = closed_layer
- else
- layer = open_layer
-
+ ..()
+ 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)
..()
@@ -69,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(p_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
@@ -127,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)
@@ -144,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)
@@ -183,72 +235,49 @@
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)
if("opening")
- if(p_open)
+ if(panel_open)
flick("o_doorc0", src)
else
flick("doorc0", src)
if("closing")
- if(p_open)
+ if(panel_open)
flick("o_doorc1", src)
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()
@@ -256,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")
@@ -296,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)
@@ -320,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!
@@ -327,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 947bcf6059d..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,21 +32,29 @@
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(p_open || operating)
+ if(panel_open || operating)
return
if(!density)
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 059c6268005..e09d40689e2 100644
--- a/code/game/machinery/doors/shutters.dm
+++ b/code/game/machinery/doors/shutters.dm
@@ -1,68 +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"
-
-/obj/machinery/door/poddoor/shutters/New()
- ..()
- layer = 3.1
+ 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/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 3e3ff6fc67b..5490b529564 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -3,31 +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
- var/obj/item/weapon/airlock_electronics/electronics = null
+ dir = EAST
+ 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))
@@ -40,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)
@@ -64,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))
@@ -83,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))
@@ -96,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
@@ -144,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)
@@ -192,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)
@@ -252,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)
- p_open = !p_open
- to_chat(user, "You [p_open ? "open":"close"] the maintenance panel of the [name].")
- return
- if(iscrowbar(I))
- if(p_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(p_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
@@ -426,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/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 f7272e8e29a..36b1ca0d837 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -4,6 +4,7 @@
/datum/feed_message
var/author = ""
+ var/title = ""
var/body = ""
var/message_type = "Story"
var/backup_body = ""
@@ -42,8 +43,10 @@
is_admin_channel = 0
total_view_count = 0
-/datum/feed_channel/proc/announce_news()
- return "Breaking news from [channel_name]!"
+/datum/feed_channel/proc/announce_news(title="")
+ if(title)
+ return "Breaking news from [channel_name]: [title]"
+ return "Breaking news from [channel_name]"
/datum/feed_channel/station/announce_news()
return "New Station Announcement Available"
@@ -75,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
@@ -87,6 +91,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
// 1 = there has
var/scanned_user = "Unknown" //Will contain the name of the person who currently uses the newscaster
var/msg = "" //Feed message
+ var/msg_title = "" // Feed message title
var/obj/item/weapon/photo/photo = null
var/channel_name = "" //the feed channel which will be receiving the feed, or being created
var/c_locked = 0 //Will our new channel be locked to public submissions?
@@ -181,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()
@@ -213,6 +218,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
if(3)
data["scanned_user"] = scanned_user
data["channel_name"] = channel_name
+ data["title"] = msg_title
data["msg"] = msg
data["photo"] = photo ? 1 : 0
if(4)
@@ -236,7 +242,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
var/list/messages = list()
data["messages"] = messages
for(var/datum/feed_message/M in viewing_channel.messages)
- messages[++messages.len] = list("body" = M.body, "img" = M.img ? icon2base64(M.img) : null, "message_type" = M.message_type, "author" = M.author, "view_count" = M.view_count)
+ messages[++messages.len] = list("title" = M.title, "body" = M.body, "img" = M.img ? icon2base64(M.img) : null, "message_type" = M.message_type, "author" = M.author, "view_count" = M.view_count)
if(8, 9)
data["channel_name"] = viewing_channel.channel_name
data["ref"] = "\ref[viewing_channel]"
@@ -247,7 +253,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
var/list/messages = list()
data["messages"] = messages
for(var/datum/feed_message/M in viewing_channel.messages)
- messages[++messages.len] = list("body" = M.body, "body_redacted" = (M.body == REDACTED ? 1 : 0) , "message_type" = M.message_type, "author" = M.author, "author_redacted" = (M.author == REDACTED ? 1 : 0), "ref" = "\ref[M]", "view_count" = M.view_count)
+ messages[++messages.len] = list("title" = M.title, "body" = M.body, "body_redacted" = (M.body == REDACTED ? 1 : 0) , "message_type" = M.message_type, "author" = M.author, "author_redacted" = (M.author == REDACTED ? 1 : 0), "ref" = "\ref[M]", "view_count" = M.view_count)
if(10)
var/wanted_already = 0
var/end_param = 1
@@ -283,9 +289,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
return 1
if(href_list["set_channel_name"])
- channel_name = sanitizeSQL(strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")))
- while(findtext(channel_name," ") == 1)
- channel_name = copytext(channel_name, 2, lentext(channel_name) + 1)
+ channel_name = trim(sanitize(strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", ""))))
else if(href_list["set_channel_lock"])
c_locked = !c_locked
@@ -333,10 +337,12 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
available_channels += F.channel_name
channel_name = strip_html_simple(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels)
+ else if(href_list["set_message_title"])
+ msg_title = trim(strip_html(input(usr, "Write a title for your feed story", "Network Channel Handler", "")))
+ msg_title = dd_limittext(msg_title, 256)
+
else if(href_list["set_new_message"])
- msg = strip_html(input(usr, "Write your feed story", "Network Channel Handler", ""))
- while(findtext(msg, " ") == 1)
- msg = copytext(msg, 2, lentext(msg) + 1)
+ msg = trim(strip_html(input(usr, "Write your feed story", "Network Channel Handler", "")))
else if(href_list["set_attachment"])
AttachPhoto(usr)
@@ -355,6 +361,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
else
var/datum/feed_message/newMsg = new /datum/feed_message
newMsg.author = scanned_user
+ newMsg.title = msg_title
newMsg.body = msg
if(photo)
newMsg.img = photo.img
@@ -363,7 +370,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
for(var/datum/feed_channel/FC in news_network.network_channels)
if(FC.channel_name == channel_name)
FC.messages += newMsg //Adding message to the network's appropriate feed_channel
- announcement = FC.announce_news()
+ announcement = FC.announce_news(msg_title)
break
temp = "Feed story successfully submitted to [channel_name]."
temp_back_screen = NEWSCASTER_MAIN
@@ -408,14 +415,10 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
screen = NEWSCASTER_W_ISSUE_H
else if(href_list["set_wanted_name"])
- channel_name = strip_html(input(usr, "Provide the name of the wanted person", "Network Security Handler", ""))
- while(findtext(channel_name, " ") == 1)
- channel_name = copytext(channel_name, 2, lentext(channel_name) + 1)
+ channel_name = trim(strip_html(input(usr, "Provide the name of the wanted person", "Network Security Handler", "")))
else if(href_list["set_wanted_desc"])
- msg = strip_html(input(usr, "Provide the a description of the wanted person and any other details you deem important", "Network Security Handler", ""))
- while(findtext(msg, " ") == 1)
- msg = copytext(msg, 2, lentext(msg) + 1)
+ msg = trim(strip_html(input(usr, "Provide the a description of the wanted person and any other details you deem important", "Network Security Handler", "")))
else if(href_list["submit_wanted"])
var/input_param = text2num(href_list["submit_wanted"])
@@ -534,6 +537,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
if(screen == NEWSCASTER_MAIN)
scanned_user = "Unknown"
msg = ""
+ msg_title = ""
c_locked = 0
channel_name = ""
viewing_channel = null
@@ -562,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)
@@ -687,7 +691,8 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
var/i = 0
for(var/datum/feed_message/MESSAGE in C.messages)
i++
- dat+="-[MESSAGE.body] "
+ dat+="[MESSAGE.title] "
+ dat+="[MESSAGE.body] "
if(MESSAGE.img)
user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
dat+=" "
diff --git a/code/game/machinery/poolcontroller.dm b/code/game/machinery/poolcontroller.dm
index c169f3c0a59..068004dd2f9 100644
--- a/code/game/machinery/poolcontroller.dm
+++ b/code/game/machinery/poolcontroller.dm
@@ -126,7 +126,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/recycler.dm b/code/game/machinery/recycler.dm
index 9cf58846feb..43fda1c6e00 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -24,7 +24,7 @@ var/const/SAFETY_COOLDOWN = 100
component_parts += new /obj/item/weapon/circuitboard/recycler(null)
component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
- materials = new /datum/material_container(src, list(MAT_METAL=1, MAT_GLASS=1, MAT_SILVER=1, MAT_GOLD=1, MAT_DIAMOND=1, MAT_PLASMA=1, MAT_URANIUM=1, MAT_BANANIUM=1, MAT_TRANQUILLITE=1))
+ materials = new /datum/material_container(src, list(MAT_METAL=1, MAT_GLASS=1, MAT_SILVER=1, MAT_GOLD=1, MAT_DIAMOND=1, MAT_PLASMA=1, MAT_URANIUM=1, MAT_BANANIUM=1, MAT_TRANQUILLITE=1, MAT_TITANIUM=1))
RefreshParts()
update_icon()
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 21e91f3759b..3ca16195333 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()
@@ -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..cef81731255 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()
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/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..8b625b73856 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]")
@@ -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, "\The [src] needs time to recharge!")
return
+
+/obj/item/device/flashlight/spotlight //invisible lighting source
+ name = "disco light"
+ desc = "Groovy..."
+ icon_state = null
+ light_color = null
+ brightness_on = 0
+ light_range = 0
+ light_power = 10
+ alpha = 0
+ layer = 0
+ on = TRUE
+ anchored = TRUE
+ var/range = null
+ unacidable = TRUE
+ burn_state = LAVA_PROOF
\ No newline at end of file
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/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/flag.dm b/code/game/objects/items/flag.dm
index 75b2ad94d95..3c9be2c67de 100644
--- a/code/game/objects/items/flag.dm
+++ b/code/game/objects/items/flag.dm
@@ -1,4 +1,6 @@
/obj/item/flag
+ name = "flag"
+ desc = "It's a flag."
icon = 'icons/obj/flag.dmi'
icon_state = "ntflag"
lefthand_file = 'icons/mob/inhands/flags_lefthand.dmi'
@@ -6,13 +8,19 @@
w_class = WEIGHT_CLASS_BULKY
burntime = 20
burn_state = FLAMMABLE
+ var/rolled = FALSE
/obj/item/flag/attackby(obj/item/weapon/W, mob/user, params)
..()
if(is_hot(W) && burn_state != ON_FIRE)
- user.visible_message("[user] lights the [name] with [W].")
+ user.visible_message("[user] lights [src] with [W].", "You light [src] with [W].", "You hear a low whoosh.")
fire_act()
+/obj/item/flag/attack_self(mob/user)
+ rolled = !rolled
+ user.visible_message("[user] [rolled ? "rolls up" : "unfurls"] [src].", "You [rolled ? "roll up" : "unfurl"] [src].", "You hear fabric rustling.")
+ update_icon()
+
/obj/item/flag/fire_act(global_overlay = FALSE)
..()
update_icon()
@@ -23,16 +31,24 @@
/obj/item/flag/update_icon()
overlays.Cut()
+ updateFlagIcon()
+ item_state = icon_state
+ if(rolled)
+ icon_state = "[icon_state]_rolled"
if(burn_state == ON_FIRE)
+ item_state = "[item_state]_fire"
+ if(burn_state == ON_FIRE && rolled)
+ overlays += image('icons/obj/flag.dmi', src , "fire_rolled")
+ else if(burn_state == ON_FIRE && !rolled)
overlays += image('icons/obj/flag.dmi', src , "fire")
- item_state = "[icon_state]_fire"
- else
- item_state = initial(icon_state)
if(ismob(loc))
var/mob/M = loc
M.update_inv_r_hand()
M.update_inv_l_hand()
+/obj/item/flag/proc/updateFlagIcon()
+ icon_state = initial(icon_state)
+
/obj/item/flag/nt
name = "Nanotrasen flag"
desc = "A flag proudly boasting the logo of NT."
@@ -187,11 +203,16 @@
desc = "A poor recreation of the official NT flag. It seems to shimmer a little."
icon_state = "ntflag"
origin_tech = "syndicate=4;magnets=4"
- var/used = 0
+ var/updated_icon_state = null
+ var/used = FALSE
+
+/obj/item/flag/chameleon/New()
+ updated_icon_state = icon_state
+ ..()
/obj/item/flag/chameleon/attack_self(mob/user)
if(used)
- return
+ return ..()
var/list/flag_types = typesof(/obj/item/flag) - list(src.type, /obj/item/flag)
var/list/flag = list()
@@ -208,12 +229,16 @@
var/obj/item/flag/chosen_flag = flag[input_flag]
- if(chosen_flag)
+ if(chosen_flag && !used)
name = chosen_flag.name
icon_state = chosen_flag.icon_state
+ updated_icon_state = icon_state
desc = chosen_flag.desc
- used = 1
+ used = TRUE
/obj/item/flag/chameleon/burn()
explosion(loc,1,2,4,4, flame_range = 4)
qdel(src)
+
+/obj/item/flag/chameleon/updateFlagIcon()
+ icon_state = updated_icon_state
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/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 92426b17688..3757faffba9 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -97,14 +97,14 @@ var/global/list/datum/stack_recipe/abductor_recipes = list ( \
new/datum/stack_recipe("alien bed", /obj/structure/stool/bed/abductor, 2, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("alien locker", /obj/structure/closet/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("alien table frame", /obj/structure/table_frame/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("alien airlock assembly", /obj/structure/door_assembly/door_assembly_abductor, 4, time = 20, one_per_turf = 1, on_floor = 1), \
null, \
new/datum/stack_recipe("alien floor tile", /obj/item/stack/tile/mineral/abductor, 1, 4, 20), \
)
/obj/item/stack/sheet/mineral
- force = 5.0
+ force = 5
throwforce = 5
- w_class = WEIGHT_CLASS_NORMAL
throw_speed = 3
/obj/item/stack/sheet/mineral/New()
@@ -225,6 +225,56 @@ var/global/list/datum/stack_recipe/abductor_recipes = list ( \
..()
recipes = tranquillite_recipes
+/*
+ * Titanium
+ */
+/obj/item/stack/sheet/mineral/titanium
+ name = "titanium"
+ icon_state = "sheet-titanium"
+ singular_name = "titanium sheet"
+ force = 5
+ throwforce = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ throw_speed = 1
+ throw_range = 3
+ sheettype = "titanium"
+ materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT)
+
+var/global/list/datum/stack_recipe/titanium_recipes = list (
+ new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20),
+ )
+
+/obj/item/stack/sheet/mineral/titanium/New(loc, amount=null)
+ recipes = titanium_recipes
+ ..()
+
+/obj/item/stack/sheet/mineral/titanium/fifty
+ amount = 50
+
+
+/*
+ * Plastitanium
+ */
+/obj/item/stack/sheet/mineral/plastitanium
+ name = "plastitanium"
+ icon_state = "sheet-plastitanium"
+ singular_name = "plastitanium sheet"
+ force = 5
+ throwforce = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ throw_speed = 1
+ throw_range = 3
+ sheettype = "plastitanium"
+ materials = list(MAT_TITANIUM=2000, MAT_PLASMA=2000)
+
+var/global/list/datum/stack_recipe/plastitanium_recipes = list (
+ new/datum/stack_recipe("plas-titanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20),
+ )
+
+/obj/item/stack/sheet/mineral/plastitanium/New(loc, amount=null)
+ recipes = plastitanium_recipes
+ ..()
+
/obj/item/stack/sheet/mineral/enruranium
name = "enriched uranium"
icon_state = "sheet-enruranium"
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 52cf7e10c69..f6aedaf5701 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -61,6 +61,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list(
null,
new /datum/stack_recipe_list("airlock assemblies", list(
new /datum/stack_recipe("standard airlock assembly", /obj/structure/door_assembly, 4, time = 50, one_per_turf = 1, on_floor = 1),
+ new /datum/stack_recipe("public airlock assembly", /obj/structure/door_assembly/door_assembly_public, 4, time = 50, one_per_turf = 1, on_floor = 1), \
new /datum/stack_recipe("command airlock assembly", /obj/structure/door_assembly/door_assembly_com, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("security airlock assembly", /obj/structure/door_assembly/door_assembly_sec, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("engineering airlock assembly", /obj/structure/door_assembly/door_assembly_eng, 4, time = 50, one_per_turf = 1, on_floor = 1),
@@ -70,11 +71,12 @@ var/global/list/datum/stack_recipe/metal_recipes = list(
new /datum/stack_recipe("science airlock assembly", /obj/structure/door_assembly/door_assembly_science, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("medical airlock assembly", /obj/structure/door_assembly/door_assembly_med, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("maintenance airlock assembly", /obj/structure/door_assembly/door_assembly_mai, 4, time = 50, one_per_turf = 1, on_floor = 1),
+ new /datum/stack_recipe("external maintenance airlock assembly", /obj/structure/door_assembly/door_assembly_extmai, 4, time = 50, one_per_turf = 1, on_floor = 1), \
new /datum/stack_recipe("external airlock assembly", /obj/structure/door_assembly/door_assembly_ext, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("freezer airlock assembly", /obj/structure/door_assembly/door_assembly_fre, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("airtight hatch assembly", /obj/structure/door_assembly/door_assembly_hatch, 4, time = 50, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("maintenance hatch assembly", /obj/structure/door_assembly/door_assembly_mhatch, 4, time = 50, one_per_turf = 1, on_floor = 1),
- new /datum/stack_recipe("multi-tile airlock assembly", /obj/structure/door_assembly/multi_tile, 4, time = 50, one_per_turf = 1, on_floor = 1),
+ new /datum/stack_recipe("multi-tile airlock assembly", /obj/structure/door_assembly/multi_tile, 8, time = 50, one_per_turf = 1, on_floor = 1),
)),
null,
new /datum/stack_recipe("mass driver button frame", /obj/item/mounted/frame/driver_button, 1, time = 50, one_per_turf = 0, on_floor = 1),
@@ -131,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, 4, time = 50, one_per_turf = 1, on_floor = 1),
- new /datum/stack_recipe("high security airlock assembly", /obj/structure/door_assembly/door_assembly_highsecurity, 4, time = 50, one_per_turf = 1, on_floor = 1),
- ), 4),
+ 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),
+ 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
@@ -142,7 +144,7 @@ var/global/list/datum/stack_recipe/plasteel_recipes = list(
desc = "This sheet is an alloy of iron and plasma."
icon_state = "sheet-plasteel"
item_state = "sheet-metal"
- materials = list(MAT_METAL=6000, MAT_PLASMA=6000)
+ materials = list(MAT_METAL=2000, MAT_PLASMA=2000)
throwforce = 10.0
flags = CONDUCT
origin_tech = "materials=2"
@@ -368,6 +370,7 @@ var/global/list/datum/stack_recipe/plastic_recipes = list ( \
new/datum/stack_recipe("plastic ashtray", /obj/item/ashtray/plastic, 2, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("plastic fork", /obj/item/weapon/kitchen/utensil/pfork, 1, on_floor = 1), \
new/datum/stack_recipe("plastic spoon", /obj/item/weapon/kitchen/utensil/pspoon, 1, on_floor = 1), \
+ new/datum/stack_recipe("plastic spork", /obj/item/weapon/kitchen/utensil/pspork, 1, on_floor = 1), \
new/datum/stack_recipe("plastic knife", /obj/item/weapon/kitchen/knife/plastic, 1, on_floor = 1), \
new/datum/stack_recipe("plastic bag", /obj/item/weapon/storage/bag/plasticbag, 3, on_floor = 1), \
new/datum/stack_recipe("bear mould", /obj/item/weapon/kitchen/mould/bear, 1, on_floor = 1), \
diff --git a/code/game/objects/items/stacks/tiles/tile_mineral.dm b/code/game/objects/items/stacks/tiles/tile_mineral.dm
index 8e830f27407..86590026f53 100644
--- a/code/game/objects/items/stacks/tiles/tile_mineral.dm
+++ b/code/game/objects/items/stacks/tiles/tile_mineral.dm
@@ -111,4 +111,22 @@ var/global/list/datum/stack_recipe/silverfancy_tile_recipes = list ( \
icon_state = "tile_abductor"
origin_tech = "materials=6;abductor=1"
turf_type = /turf/simulated/floor/mineral/abductor
- mineralType = "abductor"
\ No newline at end of file
+ mineralType = "abductor"
+
+/obj/item/stack/tile/mineral/titanium
+ name = "titanium tile"
+ singular_name = "titanium floor tile"
+ desc = "A tile made of titanium, used for shuttles."
+ icon_state = "tile_shuttle"
+ turf_type = /turf/simulated/floor/mineral/titanium
+ mineralType = "titanium"
+ materials = list(MAT_TITANIUM=500)
+
+/obj/item/stack/tile/mineral/plastitanium
+ name = "plas-titanium tile"
+ singular_name = "plas-titanium floor tile"
+ desc = "A tile made of plas-titanium, used for very evil shuttles."
+ icon_state = "tile_darkshuttle"
+ turf_type = /turf/simulated/floor/mineral/plastitanium
+ mineralType = "plastitanium"
+ materials = list(MAT_TITANIUM=250, MAT_PLASMA=250)
\ No newline at end of file
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 2c23c6ae2b0..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
@@ -209,6 +209,7 @@
force_wielded = 0
origin_tech = null
attack_verb = list("attacked", "struck", "hit")
+ brightness_on = 0
/obj/item/weapon/twohanded/dualsaber/toy/hit_reaction()
return 0
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index b053c48b893..1a3cb1754ac 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -34,18 +34,22 @@ RCD
var/list/door_accesses_list = list()
var/one_access
var/locked = 1
- var/static/list/allowed_door_types = list(/obj/machinery/door/airlock = "Standard",
- /obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/security = "Security",
- /obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/medical = "Medical",
- /obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/external = "External",
- /obj/machinery/door/airlock/glass = "Standard (Glass)", /obj/machinery/door/airlock/freezer = "Freezer",
- /obj/machinery/door/airlock/glass_command = "Command (Glass)", /obj/machinery/door/airlock/glass_engineering = "Engineering (Glass)",
- /obj/machinery/door/airlock/glass_security = "Security (Glass)", /obj/machinery/door/airlock/glass_medical = "Medical (Glass)",
- /obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/atmos = "Atmospherics",
- /obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/glass_research = "Research (Glass)",
- /obj/machinery/door/airlock/glass_mining = "Mining (Glass)", /obj/machinery/door/airlock/glass_atmos = "Atmospherics (Glass)",
- /obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/glass_science = "Science (Glass)",
- /obj/machinery/door/airlock/hatch = "Airtight Hatch", /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch")
+ var/static/list/allowed_door_types = list(
+ /obj/machinery/door/airlock = "Standard", /obj/machinery/door/airlock/glass = "Standard (Glass)",
+ /obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/command/glass = "Command (Glass)",
+ /obj/machinery/door/airlock/security = "Security", /obj/machinery/door/airlock/security/glass = "Security (Glass)",
+ /obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)",
+ /obj/machinery/door/airlock/medical = "Medical", /obj/machinery/door/airlock/medical/glass = "Medical (Glass)",
+ /obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)",
+ /obj/machinery/door/airlock/external = "External", /obj/machinery/door/airlock/external/glass = "External (Glass)",
+ /obj/machinery/door/airlock/maintenance/external = "External Maintenance", /obj/machinery/door/airlock/maintenance/external/glass = "External Maintenance (Glass)",
+ /obj/machinery/door/airlock/freezer = "Freezer",
+ /obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/mining/glass = "Mining (Glass)",
+ /obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/research/glass = "Research (Glass)",
+ /obj/machinery/door/airlock/atmos = "Atmospherics", /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)",
+ /obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/science/glass = "Science (Glass)",
+ /obj/machinery/door/airlock/hatch = "Airtight Hatch",
+ /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch")
/obj/item/weapon/rcd/New()
desc = "A RCD. It currently holds [matter]/[max_matter] matter-units."
@@ -76,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
@@ -88,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()
@@ -347,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/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/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm
index 13b0f0b8140..fb10c7cedc2 100644
--- a/code/game/objects/items/weapons/grenades/ghettobomb.dm
+++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm
@@ -1,70 +1,6 @@
//improvised explosives//
-//iedcasing assembly crafting//
-/obj/item/weapon/reagent_containers/food/drinks/cans/attackby(var/obj/item/I, mob/user as mob)
- if(istype(I, /obj/item/device/assembly/igniter))
- var/obj/item/device/assembly/igniter/G = I
- var/obj/item/weapon/grenade/iedcasing/W = new /obj/item/weapon/grenade/iedcasing
- user.unEquip(G)
- user.unEquip(src)
- user.put_in_hands(W)
- to_chat(user, "You stuff the [I] in the [src], emptying the contents beforehand.")
- W.underlays += image(src.icon, icon_state = src.icon_state)
- qdel(I)
- qdel(src)
-
-
/obj/item/weapon/grenade/iedcasing
- name = "improvised explosive assembly"
- desc = "An igniter stuffed into an aluminum shell."
- w_class = WEIGHT_CLASS_SMALL
- icon = 'icons/obj/grenade.dmi'
- icon_state = "improvised_grenade"
- item_state = "flashbang"
- throw_speed = 3
- throw_range = 7
- flags = CONDUCT
- slot_flags = SLOT_BELT
- var/assembled = 0
- active = 1
- det_time = 50
- display_timer = 0
- var/range = 3
- var/times = list()
-
-
-
-/obj/item/weapon/grenade/iedcasing/afterattack(atom/target, mob/user , flag) //Filling up the can
- if(assembled == 0)
- if( istype(target, /obj/structure/reagent_dispensers/fueltank))
- if(target.reagents.total_volume < 50)
- to_chat(user, "There's not enough fuel left to work with.")
- return
- var/obj/structure/reagent_dispensers/fueltank/F = target
- F.reagents.remove_reagent("fuel", 50, 1)//Deleting 50 fuel from the welding fuel tank,
- assembled = 1
- to_chat(user, "You've filled the makeshift explosive with welding fuel.")
- playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6)
- desc = "An improvised explosive assembly. Filled to the brim with 'Explosive flavor'"
- overlays += image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_filled")
- return
-
-
-/obj/item/weapon/grenade/iedcasing/attackby(var/obj/item/I, mob/user as mob) //Wiring the can for ignition
- if(istype(I, /obj/item/stack/cable_coil))
- if(assembled == 1)
- var/obj/item/stack/cable_coil/C = I
- C.use(1)
- assembled = 2
- to_chat(user, "You wire the igniter to detonate the fuel.")
- desc = "A weak, improvised explosive."
- overlays += image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_wired")
- name = "improvised explosive"
- active = 0
- det_time = rand(30,80)
-
-
-/obj/item/weapon/grenade/iedcasing/filled
name = "improvised firebomb"
desc = "A weak, improvised incendiary device."
w_class = WEIGHT_CLASS_SMALL
@@ -77,53 +13,53 @@
slot_flags = SLOT_BELT
active = 0
det_time = 50
- assembled = 2
+ display_timer = 0
+ var/list/times
-
-
-/obj/item/weapon/grenade/iedcasing/filled/New(loc)
+/obj/item/weapon/grenade/iedcasing/New()
..()
- overlays += image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_filled")
- overlays += image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_wired")
- times = list("5" = 10, "-1" = 20, "[rand(30,80)]" = 50, "[rand(65,180)]" = 20)// "Premature, Dud, Short Fuse, Long Fuse"=[weighting value]
+ overlays += "improvised_grenade_filled"
+ overlays += "improvised_grenade_wired"
+ times = list("5" = 10, "-1" = 20, "[rand(30, 80)]" = 50, "[rand(65, 180)]" = 20)// "Premature, Dud, Short Fuse, Long Fuse"=[weighting value]
det_time = text2num(pickweight(times))
if(det_time < 0) //checking for 'duds'
- range = 1
det_time = rand(30,80)
- else
- range = pick(2,2,2,3,3,3,4)
/obj/item/weapon/grenade/iedcasing/CheckParts(list/parts_list)
..()
var/obj/item/weapon/reagent_containers/food/drinks/cans/can = locate() in contents
if(can)
- underlays += can
+ can.pixel_x = 0 //Reset the sprite's position to make it consistent with the rest of the IED
+ can.pixel_y = 0
+ var/mutable_appearance/can_underlay = new(can)
+ can_underlay.layer = FLOAT_LAYER
+ can_underlay.plane = FLOAT_PLANE
+ underlays += can_underlay
-/obj/item/weapon/grenade/iedcasing/attack_self(mob/user as mob) //
+/obj/item/weapon/grenade/iedcasing/attack_self(mob/user) //
if(!active)
if(clown_check(user))
to_chat(user, "You light the [name]!")
- active = 1
- overlays -= image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_filled")
+ active = TRUE
+ overlays -= "improvised_grenade_filled"
icon_state = initial(icon_state) + "_active"
add_fingerprint(user)
var/turf/bombturf = get_turf(src)
var/area/A = get_area(bombturf)
- message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP)")
- log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])")
+ message_admins("[ADMIN_LOOKUPFLW(user)] has primed a [name] for detonation at [ADMIN_COORDJMP(bombturf)].")
+ log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] [COORD(bombturf)].")
if(iscarbon(user))
var/mob/living/carbon/C = user
C.throw_mode_on()
- spawn(det_time)
- prime()
+ addtimer(src, "prime", det_time)
/obj/item/weapon/grenade/iedcasing/prime() //Blowing that can up
update_mob()
- explosion(src.loc,-1,-1,-1, flame_range = range) // no explosive damage, only a large fireball.
+ explosion(loc, -1, -1, 2, flame_range = 4) // small explosion, plus a very large fireball.
qdel(src)
/obj/item/weapon/grenade/iedcasing/examine(mob/user)
- ..(user)
+ ..()
to_chat(user, "You can't tell when it will explode!")
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 0b7850bba1f..24bcb7a8c2c 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -29,7 +29,7 @@
if(ishuman(C))
var/mob/living/carbon/human/H = C
- if(!(H.get_organ("l_hand") || H.get_organ("r_hand")))
+ if(!H.has_left_hand() || !H.has_right_hand())
to_chat(user, "How do you suggest handcuffing someone with no hands?")
return
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/kitchen.dm b/code/game/objects/items/weapons/kitchen.dm
index 4bf40a3ae15..c2067935ee6 100644
--- a/code/game/objects/items/weapons/kitchen.dm
+++ b/code/game/objects/items/weapons/kitchen.dm
@@ -82,6 +82,18 @@
icon_state = "pspoon"
attack_verb = list("attacked", "poked")
+/obj/item/weapon/kitchen/utensil/spork
+ name = "spork"
+ desc = "It's a spork. Marvel at its innovative design."
+ icon_state = "spork"
+ attack_verb = list("attacked", "sporked")
+
+/obj/item/weapon/kitchen/utensil/pspork
+ name = "plastic spork"
+ desc = "It's a plastic spork. It's the fork side of the spoon!"
+ icon_state = "pspork"
+ attack_verb = list("attacked", "sporked")
+
/*
* Knives
*/
diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm
index 21845a47515..21d8828310f 100644
--- a/code/game/objects/items/weapons/legcuffs.dm
+++ b/code/game/objects/items/weapons/legcuffs.dm
@@ -52,23 +52,12 @@
if(sig)
to_chat(user, "This beartrap already has a signaler hooked up to it!")
return
- IED = I
- switch(IED.assembled)
- if(0,1) //if it's not fueled/hooked up
- to_chat(user, "You haven't prepared this IED yet!")
- IED = null
- return
- if(2,3)
- user.drop_item()
- I.forceMove(src)
- message_admins("[key_name_admin(user)] has rigged a beartrap with an IED.")
- log_game("[key_name(user)] has rigged a beartrap with an IED.")
- to_chat(user, "You sneak the [IED] underneath the pressure plate and connect the trigger wire.")
- desc = "A trap used to catch bears and other legged creatures. There is an IED hooked up to it."
- else
- to_chat(user, "You shouldn't be reading this message! Contact a coder or someone, something broke!")
- IED = null
- return
+ user.drop_item()
+ I.forceMove(src)
+ message_admins("[key_name_admin(user)] has rigged a beartrap with an IED.")
+ log_game("[key_name(user)] has rigged a beartrap with an IED.")
+ to_chat(user, "You sneak [IED] underneath the pressure plate and connect the trigger wire.")
+ desc = "A trap used to catch bears and other legged creatures. There is an IED hooked up to it."
if(istype(I, /obj/item/device/assembly/signaler))
if(IED)
to_chat(user, "This beartrap already has an IED hooked up to it!")
@@ -110,9 +99,6 @@
if(IED && isturf(src.loc))
IED.active = 1
- IED.overlays -= image('icons/obj/grenade.dmi', icon_state = "improvised_grenade_filled")
- IED.icon_state = initial(icon_state) + "_active"
- IED.assembled = 3
message_admins("[key_name_admin(usr)] has triggered an IED-rigged [name].")
log_game("[key_name(usr)] has triggered an IED-rigged [name].")
spawn(IED.det_time)
@@ -127,7 +113,7 @@
H.apply_damage(trap_damage, BRUTE,"chest")
else
H.apply_damage(trap_damage, BRUTE,(pick("l_leg", "r_leg")))
- if(!H.legcuffed) //beartrap can't cuff you leg if there's already a beartrap or legcuffs.
+ if(!H.legcuffed && H.get_num_legs() >= 2) //beartrap can't cuff you leg if there's already a beartrap or legcuffs.
H.legcuffed = src
forceMove(H)
H.update_inv_legcuffed()
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 = {"
';
+ saved += $messages.html();
+ saved = saved.replace(/&/g, '&');
+ saved = saved.replace(/Chat Log \
+ '+
+ saved
+ openWindow(finalText);
+ } else { // request returned http error
+ openWindow('Style Doc Retrieve Error: '+xmlHttp.statusText);
+ }
+ }
+
+ // timeout and request errors
+ xmlHttp.timeout = 300;
+ xmlHttp.ontimeout = function (e) {
+ openWindow('XMLHttpRequest Timeout');
+ }
+ xmlHttp.onerror = function (e) {
+ openWindow('XMLHttpRequest Error: '+xmlHttp.statusText);
+ }
+ // css needs special headers
xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- xmlHttp.send();
- saved += '';
-
- saved += $messages.html();
- saved = saved.replace(/&/g, '&');
- saved = saved.replace(/
- {{: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}}
diff --git a/nano/templates/newscaster.tmpl b/nano/templates/newscaster.tmpl
index c589bde4702..16c201407e3 100644
--- a/nano/templates/newscaster.tmpl
+++ b/nano/templates/newscaster.tmpl
@@ -96,6 +96,13 @@ Property of Nanotrasen
{{:helper.link('Edit', 'pencil', {'set_channel_receiving' : 1})}}
+
+ Title:
+
+ {{:data.title}}
+ {{:helper.link('Edit', 'pencil', {'set_message_title' : 1})}}
+
+
Message body:
@@ -132,7 +139,8 @@ Property of Nanotrasen
{{else}}
{{for data.messages}}
- - {{:value.body}}
+ {{:value.title}}
+ {{:value.body}}
{{if value.img}}

{{/if}}
@@ -198,7 +206,8 @@ Property of Nanotrasen
{{:helper.link(data.author_redacted ? 'Undo author censorship' : 'Censor channel author', null, {'censor_channel_author' : data.ref})}}
{{for data.messages}}
- - {{:value.body}}
+ {{:value.title}}
+ {{:value.body}}
{{if value.img}}

{{/if}}
@@ -217,7 +226,7 @@ Property of Nanotrasen
{{else data.screen == 9}}
{{:data.channel_name}} [created by: {{:data.author}}]
- Channel messages listed below. If you deem them dangerous to the station, you can {{:helper.link('Bestow a D-Notice upon the channel', null, {'toggle_d_notice' : data.ref})}}
+ Channel messages listed below. If you deem them dangerous to the station, you can {{:helper.link(data.censored ? 'Remove the D-Notice on the channel' : 'Bestow a D-Notice upon the channel', null, {'toggle_d_notice' : data.ref})}}
{{if data.censored}}
ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
@@ -226,7 +235,8 @@ Property of Nanotrasen
{{else}}
{{for data.messages}}
- - {{:value.body}}
+ {{:value.title}}
+ {{:value.body}}
{{if value.img}}

{{/if}}
diff --git a/nano/templates/secure_data.tmpl b/nano/templates/secure_data.tmpl
index d4d129e808b..bba32023a7f 100644
--- a/nano/templates/secure_data.tmpl
+++ b/nano/templates/secure_data.tmpl
@@ -29,24 +29,24 @@ Used In File(s): \code\game\machinery\computer\security.dm
hidden.parent("td").parent("tr").hide()
}
}
-
+
function selectTextField(){
var filter_text = document.getElementById('filter');
filter_text.focus();
filter_text.select();
}
-
+
$(window).load(function() {
selectTextField();
updateSearch();
});
-
+
$("#filter").keyup(function() {
updateSearch();
});
{{/if}}
- |