")
text = replacetext(text, "\[cell\]", "")
- text = replacetext(text, "\[logo\]", " ")
+ text = replacetext(text, "\[logo\]", "​ ")
text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
if(!no_font)
if(P)
@@ -615,5 +615,3 @@ proc/checkhtml(var/t)
text = replacetext(text, " | ", "\[cell\]")
text = replacetext(text, " ", "\[logo\]")
return text
-
-#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, ""))
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index 340d7a3b134..f205c7cf0ce 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -11,25 +11,6 @@
#define TICKS2DS(T) ((T) TICKS)
-#define TimeOfGame (get_game_time())
-#define TimeOfTick (world.tick_usage*0.01*world.tick_lag)
-
-/proc/get_game_time()
- var/global/time_offset = 0
- var/global/last_time = 0
- var/global/last_usage = 0
-
- var/wtime = world.time
- var/wusage = world.tick_usage * 0.01
-
- if(last_time < wtime && last_usage > 1)
- time_offset += last_usage - 1
-
- last_time = wtime
- last_usage = wusage
-
- return wtime + (time_offset + wusage) * world.tick_lag
-
/* This proc should only be used for world/Topic.
* If you want to display the time for which dream daemon has been running ("round time") use worldtime2text.
* If you want to display the canonical station "time" (aka the in-character time of the station) use station_time_timestamp
@@ -98,14 +79,14 @@ proc/isDay(var/month, var/day)
* Returns "watch handle" (really just a timestamp :V)
*/
/proc/start_watch()
- return TimeOfGame
+ return REALTIMEOFDAY
/**
* Returns number of seconds elapsed.
* @param wh number The "Watch Handle" from start_watch(). (timestamp)
*/
/proc/stop_watch(wh)
- return round(0.1 * (TimeOfGame - wh), 0.1)
+ return round(0.1 * (REALTIMEOFDAY - wh), 0.1)
/proc/numberToMonthName(number)
return GLOB.month_names.Find(number)
@@ -114,7 +95,7 @@ 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."
+ return "[numMinutes] [numMinutes > 1 ? "minutes" : "minute"] and [numSeconds] seconds"
//Take a value in seconds and makes it display like a clock
/proc/seconds_to_clock(var/seconds as num)
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
index ee6c5d10fe0..352a6fa3680 100644
--- a/code/__HELPERS/traits.dm
+++ b/code/__HELPERS/traits.dm
@@ -65,6 +65,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_WATERBREATH "waterbreathing"
#define TRAIT_BLOODCRAWL "bloodcrawl"
#define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat"
+#define TRAIT_JESTER "jester"
// common trait sources
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm
index 549f5c71016..b66b1a11b90 100644
--- a/code/__HELPERS/unique_ids.dm
+++ b/code/__HELPERS/unique_ids.dm
@@ -16,8 +16,6 @@
GLOBAL_VAR_INIT(next_unique_datum_id, 1)
-// /client/var/tmp/unique_datum_id = null
-
/datum/proc/UID()
if(!unique_datum_id)
var/tag_backup = tag
@@ -37,8 +35,6 @@ GLOBAL_VAR_INIT(next_unique_datum_id, 1)
var/datum/D = locate(copytext(uid, 1, splitat))
- // We might locate a client instead of a datum, but just using : is easier
- // than actually checking and typecasting
- if(D && D:unique_datum_id == uid)
+ if(D && D.unique_datum_id == uid)
return D
return null
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index aa130bb3ca9..fc0f7520e0e 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -25,11 +25,9 @@
if(!( istext(HTMLstring) ))
CRASH("Given non-text argument!")
- return
else
if(length(HTMLstring) != 7)
CRASH("Given non-HTML argument!")
- return
var/textr = copytext(HTMLstring, 2, 4)
var/textg = copytext(HTMLstring, 4, 6)
var/textb = copytext(HTMLstring, 6, 8)
@@ -46,7 +44,6 @@
if(length(textb) < 2)
textr = text("0[]", textb)
return text("#[][][]", textr, textg, textb)
- return
//Returns the middle-most value
/proc/dd_range(var/low, var/high, var/num)
@@ -332,8 +329,9 @@ Turf and target are seperate in case you want to teleport some distance from a t
/proc/select_active_ai_with_fewest_borgs()
var/mob/living/silicon/ai/selected
var/list/active = active_ais()
- for(var/mob/living/silicon/ai/A in active)
- if(!selected || (selected.connected_robots > A.connected_robots))
+ for(var/thing in active)
+ var/mob/living/silicon/ai/A = thing
+ if(!selected || (length(selected.connected_robots) > length(A.connected_robots)))
selected = A
return selected
@@ -437,16 +435,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
return "[round((powerused * 0.000001), 0.001)] MW"
return "[round((powerused * 0.000000001), 0.0001)] GW"
-//E = MC^2
-/proc/convert2energy(var/M)
- var/E = M*(SPEED_OF_LIGHT_SQ)
- return E
-
-//M = E/C^2
-/proc/convert2mass(var/E)
- var/M = E/(SPEED_OF_LIGHT_SQ)
- return M
-
//Forces a variable to be posative
/proc/modulus(var/M)
if(M >= 0)
@@ -540,21 +528,6 @@ Returns 1 if the chain up to the area contains the given typepath
/proc/between(var/low, var/middle, var/high)
return max(min(middle, high), low)
-
-
-#if DM_VERSION > 513
-#warn 513 is definitely stable now, remove this
-#endif
-#if DM_VERSION < 513
-/proc/arctan(x)
- var/y=arcsin(x/sqrt(1+x*x))
- return y
-/proc/islist(list/list)
- if(istype(list))
- return 1
- return 0
-#endif
-
//returns random gauss number
proc/GaussRand(var/sigma)
var/x,y,rsq
@@ -1520,7 +1493,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
//orbit() can run without it (swap orbiting for A)
//but then you can never stop it and that's just silly.
/atom/movable/var/atom/orbiting = null
-
+/atom/movable/var/cached_transform = null
//A: atom to orbit
//radius: range to orbit at, radius of the circle formed by orbiting
//clockwise: whether you orbit clockwise or anti clockwise
@@ -1538,6 +1511,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
orbiting = A
var/matrix/initial_transform = matrix(transform)
+ cached_transform = initial_transform
var/lastloc = loc
//Head first!
@@ -1555,8 +1529,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
SpinAnimation(rotation_speed, -1, clockwise, rotation_segments)
- //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit
- transform = initial_transform
while(orbiting && orbiting == A && A.loc)
var/targetloc = get_turf(A)
if(!lockinorbit && loc != lastloc && loc != targetloc)
@@ -1570,12 +1542,14 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(orbiting == A) //make sure we haven't started orbiting something else.
orbiting = null
- SpinAnimation(0,0)
+ SpinAnimation(0, 0)
+ transform = cached_transform
/atom/movable/proc/stop_orbit()
orbiting = null
+ transform = cached_transform
//Centers an image.
//Requires:
@@ -2035,6 +2009,18 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
tX = splittext(tX[1], ":")
tX = tX[1]
var/list/actual_view = getviewsize(C ? C.view : world.view)
- tX = Clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx)
- tY = Clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy)
+ tX = clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx)
+ tY = clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy)
return locate(tX, tY, tZ)
+
+/proc/CallAsync(datum/source, proctype, list/arguments)
+ set waitfor = FALSE
+ return call(source, proctype)(arglist(arguments))
+
+/proc/IsFrozen(atom/A)
+ if(A in GLOB.frozen_atom_list)
+ return TRUE
+ return FALSE
+
+/// Waits at a line of code until X is true
+#define UNTIL(X) while(!(X)) stoplag()
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 536ea01cc96..342e572177a 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -1,6 +1,14 @@
#define DEBUG
//#define TESTING
+// Uncomment the following line to compile unit tests.
+// #define UNIT_TESTS
+
+
+#ifdef TRAVISBUILDING
+#define UNIT_TESTS
+#endif
+
#ifdef TESTING
//#define GC_FAILURE_HARD_LOOKUP //makes paths that fail to GC call find_references before del'ing.
//implies FIND_REF_NO_CHECK_TICK
@@ -11,8 +19,6 @@
#define IS_MODE_COMPILED(MODE) (ispath(text2path("/datum/game_mode/"+(MODE))))
-#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary.
-
//Don't set this very much higher then 1024 unless you like inviting people in to dos your server with message spam
#define MAX_MESSAGE_LEN 1024
#define MAX_PAPER_MESSAGE_LEN 3072
@@ -20,11 +26,13 @@
#define MAX_BOOK_MESSAGE_LEN 9216
#define MAX_NAME_LEN 50 //diona names can get loooooooong
-// Version check, terminates compilation if someone is using a version of BYOND that's too old
-#if DM_VERSION < 510
-#error OUTDATED VERSION ERROR - \
-Due to BYOND features used in this codebase, you must update to version 510 or later to compile. \
-This may require updating to a beta release.
+//Update this whenever you need to take advantage of more recent byond features
+#define MIN_COMPILER_VERSION 513
+#define MIN_COMPILER_BUILD 1514
+#if DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD
+//Don't forget to update this part
+#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update.
+#error You need version 513.1514 or higher
#endif
// Macros that must exist before world.dm
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 2caa158757c..17444f66207 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -18,6 +18,8 @@ GLOBAL_LIST_EMPTY(player_list) //List of all mobs **with clients attached**.
GLOBAL_LIST_EMPTY(mob_list) //List of all mobs, including clientless
GLOBAL_LIST_EMPTY(silicon_mob_list) //List of all silicon mobs, including clientless
GLOBAL_LIST_EMPTY(mob_living_list) //all instances of /mob/living and subtypes
+GLOBAL_LIST_EMPTY(carbon_list) //all instances of /mob/living/carbon and subtypes, notably does not contain simple animals
+GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and subtypes
GLOBAL_LIST_EMPTY(spirits) //List of all the spirits, including Masks
GLOBAL_LIST_EMPTY(alive_mob_list) //List of all alive mobs, including clientless. Excludes /mob/new_player
GLOBAL_LIST_EMPTY(dead_mob_list) //List of all dead mobs, including clientless. Excludes /mob/new_player
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 931a0e394e8..ab3bfa85451 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -16,7 +16,6 @@ GLOBAL_LIST_INIT(prisoncomputer_list, list())
GLOBAL_LIST_INIT(celltimers_list, list()) // list of all cell timers
GLOBAL_LIST_INIT(cell_logs, list())
GLOBAL_LIST_INIT(navigation_computers, list())
-GLOBAL_LIST_INIT(zombie_infection_list, list())
GLOBAL_LIST_INIT(all_areas, list())
GLOBAL_LIST_INIT(machines, list())
@@ -48,7 +47,10 @@ GLOBAL_LIST_EMPTY(ladders)
GLOBAL_LIST_INIT(active_diseases, list()) //List of Active disease in all mobs; purely for quick referencing.
GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects
-
+GLOBAL_LIST_EMPTY(alert_consoles) // Station alert consoles, /obj/machinery/computer/station_alert
GLOBAL_LIST_EMPTY(explosive_walls)
GLOBAL_LIST_EMPTY(engine_beacon_list)
+
+/// List of wire colors for each object type of that round. One for airlocks, one for vendors, etc.
+GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value.
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 0ea513708a1..50e70bc62a4 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -15,6 +15,8 @@ GLOBAL_VAR(world_asset_log)
GLOBAL_PROTECT(world_asset_log)
GLOBAL_VAR(runtime_summary_log)
GLOBAL_PROTECT(runtime_summary_log)
+GLOBAL_VAR(tgui_log)
+GLOBAL_PROTECT(tgui_log)
GLOBAL_LIST_EMPTY(jobMax)
GLOBAL_PROTECT(jobMax)
@@ -32,4 +34,6 @@ GLOBAL_PROTECT(IClog)
GLOBAL_LIST_EMPTY(OOClog)
GLOBAL_PROTECT(OOClog)
+GLOBAL_DATUM_INIT(logging, /datum/logging, new /datum/logging())
+
GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs"))
diff --git a/code/_globalvars/mapping.dm b/code/_globalvars/mapping.dm
index ca1c899e301..e43062a18b7 100644
--- a/code/_globalvars/mapping.dm
+++ b/code/_globalvars/mapping.dm
@@ -3,8 +3,9 @@
#define Z_SOUTH 3
#define Z_WEST 4
-GLOBAL_LIST_INIT(cardinal, list( NORTH, SOUTH, EAST, WEST ))
+GLOBAL_LIST_INIT(cardinal, list(NORTH, SOUTH, EAST, WEST))
GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
+GLOBAL_LIST_INIT(alldirs2, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST, NORTH, SOUTH, EAST, WEST))
GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
// This must exist early on or shit breaks bad
@@ -54,3 +55,7 @@ GLOBAL_LIST_EMPTY(space_ruins_templates)
GLOBAL_LIST_EMPTY(lava_ruins_templates)
GLOBAL_LIST_EMPTY(shelter_templates)
GLOBAL_LIST_EMPTY(shuttle_templates)
+
+// Teleport locations
+GLOBAL_LIST_EMPTY(teleportlocs)
+GLOBAL_LIST_EMPTY(ghostteleportlocs)
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index 32d6370f3d0..283d92a954f 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -4,7 +4,7 @@ GLOBAL_DATUM(slmaster, /obj/effect/overlay)
GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule
GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
-// Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it.
+// Announcer intercom, because too much stuff creates an intercom for one message then qdel()s it.
GLOBAL_DATUM_INIT(global_announcer, /obj/item/radio/intercom, create_global_announcer())
GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_command_announcer())
@@ -89,7 +89,7 @@ GLOBAL_VAR_INIT(copier_items_printed_logged, FALSE)
GLOBAL_VAR(map_name) // Self explanatory
-GLOBAL_DATUM(data_core, /datum/datacore) // Station datacore, manifest, etc
+GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc
GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled
@@ -101,3 +101,4 @@ GLOBAL_PROTECT(dbcon)
GLOBAL_LIST_EMPTY(ability_verbs) // Create-level abilities
GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE))
+
diff --git a/code/_globalvars/sensitive.dm b/code/_globalvars/sensitive.dm
index b1e6751c4dd..4c04b2d480b 100644
--- a/code/_globalvars/sensitive.dm
+++ b/code/_globalvars/sensitive.dm
@@ -9,4 +9,4 @@ GLOBAL_REAL_VAR(sqlfdbkdb) = "test"
GLOBAL_REAL_VAR(sqlfdbklogin) = "root"
GLOBAL_REAL_VAR(sqlfdbkpass) = ""
GLOBAL_REAL_VAR(sqlfdbktableprefix) = "erro_"
-
+GLOBAL_REAL_VAR(sql_version) = 0
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index fbb04d4c021..777a7625fe7 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -145,12 +145,10 @@
/mob/living/silicon/ai/MiddleClickOn(var/atom/A)
A.AIMiddleClick(src)
-/*
- The following criminally helpful code is just the previous code cleaned up;
- I have no idea why it was in atoms.dm instead of respective files.
-*/
-/atom/proc/AICtrlShiftClick(var/mob/user) // Examines
+// DEFAULT PROCS TO OVERRIDE
+
+/atom/proc/AICtrlShiftClick(mob/user) // Examines
if(user.client)
user.examinate(src)
return
@@ -158,74 +156,93 @@
/atom/proc/AIAltShiftClick()
return
-/obj/machinery/door/airlock/AIAltShiftClick() // Sets/Unsets Emergency Access Override
- if(density)
- Topic(src, list("src" = UID(), "command"="emergency", "activate" = "1"), 1) // 1 meaning no window (consistency!)
- else
- Topic(src, list("src" = UID(), "command"="emergency", "activate" = "0"), 1)
- return
-
-/atom/proc/AIShiftClick(var/mob/user)
+/atom/proc/AIShiftClick(mob/living/user) // borgs use this too
if(user.client)
user.examinate(src)
return
-/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors!
- if(density)
- Topic(src, list("src" = UID(), "command"="open", "activate" = "1"), 1) // 1 meaning no window (consistency!)
- else
- Topic(src, list("src" = UID(), "command"="open", "activate" = "0"), 1)
+/atom/proc/AICtrlClick(mob/living/silicon/ai/user)
return
-/atom/proc/AICtrlClick(var/mob/living/silicon/ai/user)
- return
-
-/obj/machinery/door/airlock/AICtrlClick() // Bolts doors
- if(locked)
- Topic(src, list("src" = UID(), "command"="bolts", "activate" = "0"), 1)// 1 meaning no window (consistency!)
- else
- Topic(src, list("src" = UID(), "command"="bolts", "activate" = "1"), 1)
-
-/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs.
- Topic("breaker=1", list("breaker"="1"), 0) // 0 meaning no window (consistency! wait...)
-
-/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets
- Topic(src, list("src" = UID(), "command"="enable", "value"="[!enabled]"), 1) // 1 meaning no window (consistency!)
-
-/atom/proc/AIAltClick(var/atom/A)
+/atom/proc/AIAltClick(atom/A)
AltClick(A)
-/obj/machinery/door/airlock/AIAltClick() // Electrifies doors.
- if(!electrified_until)
- // permanent shock
- Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "1"), 1) // 1 meaning no window (consistency!)
- else
- // disable/6 is not in Topic; disable/5 disables both temporary and permanent shock
- Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "0"), 1)
+/atom/proc/AIMiddleClick(mob/living/user)
return
+/mob/living/silicon/ai/TurfAdjacent(turf/T)
+ return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T))
+
+
+// APC
+
+/obj/machinery/power/apc/AICtrlClick(mob/living/user) // turns off/on APCs.
+ toggle_breaker(user)
+
+
+// TURRETCONTROL
+
+/obj/machinery/turretid/AICtrlClick(mob/living/silicon/ai/user) //turns off/on Turrets
+ enabled = !enabled
+ updateTurrets()
+
/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets
- Topic(src, list("src" = UID(), "command"="lethal", "value"="[!lethal]"), 1) // 1 meaning no window (consistency!)
+ if(lethal_is_configurable)
+ lethal = !lethal
+ updateTurrets()
-/atom/proc/AIMiddleClick()
- return
+// AIRLOCKS
-/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights.
- if(!src.lights)
- Topic(src, list("src" = UID(), "command"="lights", "activate" = "1"), 1) // 1 meaning no window (consistency!)
+/obj/machinery/door/airlock/AIAltShiftClick(mob/user) // Sets/Unsets Emergency Access Override
+ emergency = !emergency
+ update_icon()
+
+/obj/machinery/door/airlock/AIShiftClick(mob/user) // Opens and closes doors!
+ if(welded)
+ to_chat(user, "The airlock has been welded shut!")
+ if(locked)
+ locked = !locked
+ if(density)
+ open()
else
- Topic(src, list("src" = UID(), "command"="lights", "activate" = "0"), 1)
- return
+ close()
-/obj/machinery/ai_slipper/AICtrlClick() //Turns liquid dispenser on or off
+/obj/machinery/door/airlock/AICtrlClick(mob/living/silicon/ai/user) // Bolts doors
+ locked = !locked
+ update_icon()
+
+/obj/machinery/door/airlock/AIAltClick(mob/living/silicon/ai/user) // Electrifies doors.
+ if(wires.is_cut(WIRE_ELECTRIFY))
+ to_chat(user, "The electrification wire is cut - Cannot electrify the door.")
+ if(isElectrified())
+ electrify(0) // un-shock
+ else
+ electrify(-1) // permanent shock
+
+
+/obj/machinery/door/airlock/AIMiddleClick(mob/living/user) // Toggles door bolt lights.
+ if(wires.is_cut(WIRE_BOLT_LIGHT))
+ to_chat(user, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
+ else if(lights)
+ lights = FALSE
+ to_chat(user, "The door bolt lights have been disabled.")
+ else if(!lights)
+ lights = TRUE
+ to_chat(user, "The door bolt lights have been enabled.")
+ update_icon()
+
+// FIRE ALARMS
+
+/obj/machinery/firealarm/AICtrlClick()
+ if(enabled)
+ reset()
+ else
+ alarm()
+
+// AI-CONTROLLED SLIP GENERATOR IN AI CORE
+
+/obj/machinery/ai_slipper/AICtrlClick(mob/living/silicon/ai/user) //Turns liquid dispenser on or off
ToggleOn()
/obj/machinery/ai_slipper/AIAltClick() //Dispenses liquid if on
Activate()
-
-//
-// Override AdjacentQuick for AltClicking
-//
-
-/mob/living/silicon/ai/TurfAdjacent(var/turf/T)
- return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T))
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index d840f64bb4a..36b6d647b7d 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -73,7 +73,9 @@
var/dragged = modifiers["drag"]
if(dragged && !modifiers[dragged])
return
-
+ if(IsFrozen(A) && !is_admin(usr))
+ to_chat(usr, "Interacting with admin-frozen players is not permitted.")
+ return
if(modifiers["middle"] && modifiers["shift"] && modifiers["ctrl"])
MiddleShiftControlClickOn(A)
return
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index f9e5cbf3e5a..e61b9bdc8b6 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -170,6 +170,9 @@
/obj/machinery/ai_slipper/BorgAltClick() //Dispenses liquid if on
Activate()
+/obj/machinery/firealarm/BorgCtrlClick()
+ AICtrlClick()
+
/*
As with AI, these are not used in click code,
because the code for robots is specific, not generic.
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index 58e82733abe..b069ff31eb7 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -161,10 +161,13 @@
#define ui_bot_pull "EAST-2:26,SOUTH:7"
//Ghosts
-#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24"
-#define ui_ghost_orbit "SOUTH:6,CENTER-1:24"
-#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24"
-#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
+#define ui_ghost_jumptomob "SOUTH:6,CENTER-2"
+#define ui_ghost_orbit "SOUTH:6,CENTER-1"
+#define ui_ghost_reenter_corpse "SOUTH:6,CENTER"
+#define ui_ghost_teleport "SOUTH:6,CENTER+1"
+#define ui_ghost_respawn_list "SOUTH:6,CENTER+2"
+#define ui_ghost_respawn_mob "SOUTH:6+1,CENTER+2"
+#define ui_ghost_respawn_pai "SOUTH:6+2,CENTER+2"
//HUD styles. Please ensure HUD_VERSIONS is the same as the maximum index. Index order defines how they are cycled in F12.
#define HUD_STYLE_STANDARD 1
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index ada0ce8cd30..856f724f925 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -64,7 +64,7 @@
/obj/screen/ai/alerts/Click()
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
- AI.subsystem_alarm_monitor()
+ AI.ai_alerts()
/obj/screen/ai/announcement
name = "Make Announcement"
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index d49c0ee59eb..7b53bce22de 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -3,7 +3,7 @@
//PUBLIC - call these wherever you want
-/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
+/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim)
/*
Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already
@@ -19,9 +19,8 @@
if(!category)
return
- var/obj/screen/alert/alert
- if(alerts[category])
- alert = alerts[category]
+ var/obj/screen/alert/alert = LAZYACCESS(alerts, category)
+ if(alert)
if(alert.override_alerts)
return 0
if(new_master && new_master != alert.master)
@@ -57,22 +56,24 @@
alert.icon_state = "[initial(alert.icon_state)][severity]"
alert.severity = severity
- alerts[category] = alert
+ LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist
if(client && hud_used)
hud_used.reorganize_alerts()
- alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
- animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
- if(alert.timeout)
- spawn(alert.timeout)
- if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout)
- clear_alert(category)
- alert.timeout = world.time + alert.timeout - world.tick_lag
+ if(!no_anim)
+ alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
+ animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
+
+ var/timeout = timeout_override || alert.timeout
+ if(timeout)
+ addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout)
+ alert.timeout = world.time + timeout - world.tick_lag
+
return alert
// Proc to clear an existing alert.
/mob/proc/clear_alert(category, clear_override = FALSE)
- var/obj/screen/alert/alert = alerts[category]
+ var/obj/screen/alert/alert = LAZYACCESS(alerts, category)
if(!alert)
return 0
if(alert.override_alerts && !clear_override)
@@ -95,7 +96,6 @@
var/alerttooltipstyle = ""
var/override_alerts = FALSE //If it is overriding other alerts of the same type
-
/obj/screen/alert/MouseEntered(location,control,params)
openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle)
@@ -103,6 +103,12 @@
/obj/screen/alert/MouseExited()
closeToolTip(usr)
+/obj/screen/alert/proc/do_timeout(mob/M, category)
+ if(!M || !M.alerts)
+ return
+
+ if(timeout && M.alerts[category] == src && world.time >= timeout)
+ M.clear_alert(category)
//Gas alerts
/obj/screen/alert/not_enough_oxy
@@ -508,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws."
timeout = 300
var/atom/target = null
var/action = NOTIFY_JUMP
+ var/show_time_left = FALSE // If true you need to call START_PROCESSING manually
+ var/image/time_left_overlay // The last image showing the time left
+ var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate
+
+/obj/screen/alert/notify_action/process()
+ if(show_time_left)
+ var/timeleft = timeout - world.time
+ if(timeleft <= 0)
+ return PROCESS_KILL
+
+ if(time_left_overlay)
+ overlays -= time_left_overlay
+
+ var/obj/O = new
+ O.maptext = "[CEILING(timeleft / 10, 1)]"
+ O.maptext_width = O.maptext_height = 128
+ var/matrix/M = new
+ M.Translate(4, 16)
+ O.transform = M
+
+ var/image/I = image(O)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 1
+ overlays += I
+
+ time_left_overlay = I
+ qdel(O)
+ ..()
/obj/screen/alert/notify_action/Destroy()
target = null
@@ -516,20 +550,55 @@ so as to remain in compliance with the most up-to-date laws."
/obj/screen/alert/notify_action/Click()
if(!usr || !usr.client)
return
- if(!target)
- return
var/mob/dead/observer/G = usr
if(!istype(G))
return
- switch(action)
- if(NOTIFY_ATTACK)
- target.attack_ghost(G)
- if(NOTIFY_JUMP)
- var/turf/T = get_turf(target)
- if(T && isturf(T))
- G.loc = T
- if(NOTIFY_FOLLOW)
- G.ManualFollow(target)
+
+ if(poll)
+ if(poll.sign_up(G))
+ // Add a small overlay to indicate we've signed up
+ display_signed_up()
+ else if(target)
+ switch(action)
+ if(NOTIFY_ATTACK)
+ target.attack_ghost(G)
+ if(NOTIFY_JUMP)
+ var/turf/T = get_turf(target)
+ if(T && isturf(T))
+ G.loc = T
+ if(NOTIFY_FOLLOW)
+ G.ManualFollow(target)
+
+/obj/screen/alert/notify_action/Topic(href, href_list)
+ if(..())
+ return TRUE
+
+ if(href_list["signup"] && isobserver(usr) && poll?.sign_up(usr))
+ display_signed_up()
+
+/obj/screen/alert/notify_action/proc/display_signed_up()
+ var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector")
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 2
+ overlays += I
+
+/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1)
+ if(stacks <= 1)
+ return
+
+ var/obj/O = new
+ O.maptext = "[stacks]x"
+ O.maptext_width = O.maptext_height = 128
+ var/matrix/M = new
+ M.Translate(4, 2)
+ O.transform = M
+
+ var/image/I = image(O)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE + 1
+ overlays += I
+
+ qdel(O)
/obj/screen/alert/notify_soulstone
name = "Soul Stone"
@@ -585,12 +654,14 @@ so as to remain in compliance with the most up-to-date laws."
// Re-render all alerts - also called in /datum/hud/show_hud() because it's needed there
/datum/hud/proc/reorganize_alerts()
var/list/alerts = mymob.alerts
+ if(!alerts)
+ return FALSE
var/icon_pref
if(!hud_shown)
- for(var/i = 1, i <= alerts.len, i++)
+ for(var/i in 1 to alerts.len)
mymob.client.screen -= alerts[alerts[i]]
- return 1
- for(var/i = 1, i <= alerts.len, i++)
+ return TRUE
+ for(var/i in 1 to alerts.len)
var/obj/screen/alert/alert = alerts[alerts[i]]
if(alert.icon_state == "template")
if(!icon_pref)
@@ -611,10 +682,10 @@ so as to remain in compliance with the most up-to-date laws."
. = ""
alert.screen_loc = .
mymob.client.screen |= alert
- return 1
+ return TRUE
/mob
- var/list/alerts = list() // contains /obj/screen/alert only // On /mob so clientless mobs will throw alerts properly
+ var/list/alerts // lazy list. contains /obj/screen/alert only // On /mob so clientless mobs will throw alerts properly
/obj/screen/alert/Click(location, control, params)
if(!usr || !usr.client)
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index 600c1105e8e..67df5eb3013 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -40,6 +40,55 @@
var/mob/dead/observer/G = usr
G.dead_tele()
+/obj/screen/ghost/respawn_list
+ name = "Ghost spawns"
+ icon = 'icons/mob/screen_midnight.dmi'
+ icon_state = "template"
+
+/obj/screen/ghost/respawn_list/Initialize(mapload)
+ . = ..()
+ update_hidden_state()
+
+/obj/screen/ghost/respawn_list/Click()
+ var/client/C = hud.mymob.client
+ hud.inventory_shown = !hud.inventory_shown
+ if(hud.inventory_shown)
+ C.screen += hud.toggleable_inventory
+ else
+ C.screen -= hud.toggleable_inventory
+ update_hidden_state()
+
+/obj/screen/ghost/respawn_list/proc/update_hidden_state()
+ var/matrix/M = matrix(transform)
+ M.Turn(-90)
+
+ overlays.Cut()
+ var/image/img = image('icons/mob/actions/actions.dmi', src, (hud && hud.inventory_shown) ? "hide" : "show")
+ img.transform = M
+ overlays += img
+
+/obj/screen/ghost/respawn_mob
+ name = "Mob spawners"
+ icon_state = "mob_spawner"
+
+/obj/screen/ghost/respawn_mob/Click()
+ var/mob/dead/observer/G = usr
+ G.open_spawners_menu()
+
+/obj/screen/ghost/respawn_pai
+ name = "Configure pAI"
+ icon_state = "pai"
+
+/obj/screen/ghost/respawn_pai/Click()
+ var/mob/dead/observer/G = usr
+ if(!GLOB.paiController.check_recruit(G))
+ to_chat(G, "You are not eligible to become a pAI.")
+ return
+ GLOB.paiController.recruitWindow(G)
+
+/datum/hud/ghost
+ inventory_shown = FALSE
+
/datum/hud/ghost/New(mob/owner)
..()
var/obj/screen/using
@@ -59,6 +108,22 @@
using = new /obj/screen/ghost/teleport()
using.screen_loc = ui_ghost_teleport
static_inventory += using
+ static_inventory += using
+
+ using = new /obj/screen/ghost/respawn_list()
+ using.screen_loc = ui_ghost_respawn_list
+ static_inventory += using
+
+ using = new /obj/screen/ghost/respawn_mob()
+ using.screen_loc = ui_ghost_respawn_mob
+ toggleable_inventory += using
+
+ using = new /obj/screen/ghost/respawn_pai()
+ using.screen_loc = ui_ghost_respawn_pai
+ toggleable_inventory += using
+
+ for(var/obj/screen/S in (static_inventory + toggleable_inventory))
+ S.hud = src
/datum/hud/ghost/show_hud()
mymob.client.screen = list()
diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm
index d628124e052..6c9e95f457e 100644
--- a/code/_onclick/hud/guardian.dm
+++ b/code/_onclick/hud/guardian.dm
@@ -13,7 +13,7 @@
using.icon_state = mymob.a_intent
static_inventory += using
action_intent = using
-
+
using = new /obj/screen/guardian/Manifest()
using.screen_loc = ui_rhand
static_inventory += using
@@ -49,8 +49,8 @@
/obj/screen/guardian/Manifest/Click()
if(isguardian(usr))
var/mob/living/simple_animal/hostile/guardian/G = usr
- G.Manifest()
-
+ if(G.loc == G.summoner)
+ G.Manifest()
/obj/screen/guardian/Recall
icon_state = "recall"
diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm
index 68847cbd97d..d5fb5cad4fa 100644
--- a/code/_onclick/hud/parallax.dm
+++ b/code/_onclick/hud/parallax.dm
@@ -246,7 +246,7 @@
if(!view)
view = world.view
var/list/new_overlays = list()
- var/count = Ceiling(view/(480/world.icon_size))+1
+ var/count = CEILING(view/(480/world.icon_size), 1)+1
for(var/x in -count to count)
for(var/y in -count to count)
if(x == 0 && y == 0)
diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm
index 872659d714b..eda0cc5640d 100644
--- a/code/_onclick/hud/picture_in_picture.dm
+++ b/code/_onclick/hud/picture_in_picture.dm
@@ -102,8 +102,8 @@
overlays += standard_background
/obj/screen/movable/pic_in_pic/proc/set_view_size(width, height, do_refresh = TRUE)
- width = Clamp(width, 0, max_dimensions)
- height = Clamp(height, 0, max_dimensions)
+ width = clamp(width, 0, max_dimensions)
+ height = clamp(height, 0, max_dimensions)
src.width = width
src.height = height
diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm
index 14080274617..f048c3c393d 100644
--- a/code/_onclick/hud/robot.dm
+++ b/code/_onclick/hud/robot.dm
@@ -201,7 +201,7 @@
if(!R.robot_modules_background)
return
- var/display_rows = Ceiling(R.module.modules.len / 8)
+ var/display_rows = CEILING(R.module.modules.len / 8, 1)
R.robot_modules_background.screen_loc = "CENTER-4:16,SOUTH+1:7 to CENTER+3:16,SOUTH+[display_rows]:7"
R.client.screen += R.robot_modules_background
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index e27002cf118..4fd5b956df5 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -75,13 +75,13 @@
if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
- user.lastattacked = M
- M.lastattacker = user
+ M.lastattacker = user.real_name
+ M.lastattackerckey = user.ckey
user.do_attack_animation(M)
. = M.attacked_by(src, user, def_zone)
- add_attack_logs(user, M, "Attacked with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
+ add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_fingerprint(user)
@@ -135,9 +135,9 @@
/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
+ 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
+ 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)
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 460bc0193f6..3452400edbb 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -15,7 +15,6 @@
// Otherwise jump
else
- following = null
forceMove(get_turf(A))
update_parallax_contents()
@@ -64,7 +63,7 @@
if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the health_scan var.
return
if(user.client && user.health_scan)
- if(issilicon(src) || ismachine(src))
+ if(issilicon(src) || ismachineperson(src))
robot_healthscan(user, src)
else if(ishuman(src))
healthscan(user, src, 1, TRUE)
diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm
deleted file mode 100644
index ff55f35cb57..00000000000
--- a/code/_onclick/rig.dm
+++ /dev/null
@@ -1,84 +0,0 @@
-
-#define MIDDLE_CLICK 0
-#define ALT_CLICK 1
-#define CTRL_CLICK 2
-#define MAX_HARDSUIT_CLICK_MODE 2
-
-/client
- var/hardsuit_click_mode = MIDDLE_CLICK
-
-/client/verb/toggle_hardsuit_mode()
- set name = "Toggle Hardsuit Activation Mode"
- set desc = "Switch between hardsuit activation modes."
- set category = "OOC"
-
- hardsuit_click_mode++
- if(hardsuit_click_mode > MAX_HARDSUIT_CLICK_MODE)
- hardsuit_click_mode = 0
-
- switch(hardsuit_click_mode)
- if(MIDDLE_CLICK)
- to_chat(src, "Hardsuit activation mode set to middle-click.")
- if(ALT_CLICK)
- to_chat(src, "Hardsuit activation mode set to alt-click.")
- if(CTRL_CLICK)
- to_chat(src, "Hardsuit activation mode set to control-click.")
- else
- // should never get here, but just in case:
- log_runtime(EXCEPTION("Bad hardsuit click mode: [hardsuit_click_mode] - expected 0 to [MAX_HARDSUIT_CLICK_MODE]"), src)
- to_chat(src, "Somehow you bugged the system. Setting your hardsuit mode to middle-click.")
- hardsuit_click_mode = MIDDLE_CLICK
-
-/mob/living/MiddleClickOn(atom/A)
- if(client && client.hardsuit_click_mode == MIDDLE_CLICK)
- if(HardsuitClickOn(A))
- return
- ..()
-
-/mob/living/AltClickOn(atom/A)
- if(client && client.hardsuit_click_mode == ALT_CLICK)
- if(HardsuitClickOn(A))
- return
- ..()
-
-/mob/living/CtrlClickOn(atom/A)
- if(client && client.hardsuit_click_mode == CTRL_CLICK)
- if(HardsuitClickOn(A))
- return
- ..()
-
-/mob/living/proc/can_use_rig()
- return 0
-
-/mob/living/carbon/human/can_use_rig()
- return 1
-
-/mob/living/carbon/brain/can_use_rig()
- return istype(loc, /obj/item/mmi)
-
-/mob/living/silicon/ai/can_use_rig()
- return istype(loc, /obj/item/aicard)
-
-/mob/living/silicon/pai/can_use_rig()
- return loc == card
-
-/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0)
- if(!can_use_rig() || (next_move > world.time))
- return 0
- var/obj/item/rig/rig = get_rig()
- if(istype(rig) && !rig.offline && rig.selected_module)
- if(src != rig.wearer)
- if(rig.ai_can_move_suit(src, check_user_module = 1))
- message_admins("[key_name_admin(src)] is trying to force \the [key_name_admin(rig.wearer)] to use a hardsuit module.")
- else
- return 0
- rig.selected_module.engage(A, alert_ai)
- if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns
- changeNext_move(CLICK_CD_MELEE)
- return 1
- return 0
-
-#undef MIDDLE_CLICK
-#undef ALT_CLICK
-#undef CTRL_CLICK
-#undef MAX_HARDSUIT_CLICK_MODE
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 2f4811de380..756ce6f8527 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -36,7 +36,7 @@
var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi)
var/vote_no_dead = 0 // dead people can't vote (tbi)
// var/enable_authentication = 0 // goon authentication
- var/del_new_on_log = 1 // del's new players if they log before they spawn in
+ var/del_new_on_log = 1 // qdel's new players if they log before they spawn in
var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard
var/traitor_scaling = 0 //if amount of traitors scales based on amount of players
var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other
@@ -61,7 +61,6 @@
var/usewhitelist = 0
var/mods_are_mentors = 0
var/load_jobs_from_txt = 0
- var/ToRban = 0
var/automute_on = 0 //enables automuting/spam prevention
var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access.
var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized
@@ -200,8 +199,8 @@
var/disable_away_missions = 0 // disable away missions
var/disable_space_ruins = 0 //disable space ruins
- var/extra_space_ruin_levels_min = 2
- var/extra_space_ruin_levels_max = 4
+ var/extra_space_ruin_levels_min = 4
+ var/extra_space_ruin_levels_max = 8
var/ooc_allowed = 1
var/looc_allowed = 1
@@ -265,6 +264,11 @@
src.votable_modes += "secret"
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Config reload blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to reload configuration via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to reload configuration via advanced proc-call")
+ return
var/list/Lines = file2list(filename)
for(var/t in Lines)
@@ -573,9 +577,6 @@
if("humans_need_surnames")
humans_need_surnames = 1
- if("tor_ban")
- ToRban = 1
-
if("automute_on")
automute_on = 1
@@ -641,31 +642,31 @@
config.max_maint_drones = text2num(value)
if("expected_round_length")
- config.expected_round_length = MinutesToTicks(text2num(value))
+ config.expected_round_length = text2num(value) MINUTES
if("event_custom_start_mundane")
var/values = text2numlist(value, ";")
- config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))
+ config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_custom_start_moderate")
var/values = text2numlist(value, ";")
- config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))
+ config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_custom_start_major")
var/values = text2numlist(value, ";")
- config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))
+ config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_delay_lower")
var/values = text2numlist(value, ";")
- config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1])
- config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2])
- config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3])
+ config.event_delay_lower[EVENT_LEVEL_MUNDANE] = values[1] MINUTES
+ config.event_delay_lower[EVENT_LEVEL_MODERATE] = values[2] MINUTES
+ config.event_delay_lower[EVENT_LEVEL_MAJOR] = values[3] MINUTES
if("event_delay_upper")
var/values = text2numlist(value, ";")
- config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1])
- config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2])
- config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3])
+ config.event_delay_upper[EVENT_LEVEL_MUNDANE] = values[1] MINUTES
+ config.event_delay_upper[EVENT_LEVEL_MODERATE] = values[2] MINUTES
+ config.event_delay_upper[EVENT_LEVEL_MAJOR] = values[3] MINUTES
if("starlight")
config.starlight = 1
@@ -701,7 +702,7 @@
config.max_loadout_points = text2num(value)
if("round_abandon_penalty_period")
- config.round_abandon_penalty_period = MinutesToTicks(text2num(value))
+ config.round_abandon_penalty_period = text2num(value) MINUTES
if("medal_hub_address")
config.medal_hub_address = value
@@ -808,8 +809,12 @@
log_config("Unknown setting in configuration: '[name]'")
/datum/configuration/proc/loadsql(filename) // -- TLE
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "SQL configuration reload blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to reload SQL configuration via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to reload SQL configuration via advanced proc-call")
+ return
var/list/Lines = file2list(filename)
- var/db_version = 0
for(var/t in Lines)
if(!t) continue
@@ -848,10 +853,13 @@
if("feedback_tableprefix")
sqlfdbktableprefix = value
if("db_version")
- db_version = text2num(value)
+ sql_version = text2num(value)
else
log_config("Unknown setting in configuration: '[name]'")
- if(config.sql_enabled && db_version != SQL_VERSION)
+
+ // The unit tests have their own version of this check, which wont hold the server up infinitely, so this is disabled if we are running unit tests
+ #ifndef UNIT_TESTS
+ if(config.sql_enabled && sql_version != SQL_VERSION)
config.sql_enabled = 0
log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!")
spawn(60)
@@ -859,6 +867,7 @@
SSticker.ticker_going = FALSE
spawn(600)
to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED. Please check database version for recent upstream changes!")
+ #endif
/datum/configuration/proc/loadoverflowwhitelist(filename)
var/list/Lines = file2list(filename)
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
index 5a520b547e1..691f19201f6 100644
--- a/code/controllers/globals.dm
+++ b/code/controllers/globals.dm
@@ -14,7 +14,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
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)
+ QDEL_IN(exclude_these, 0) //signal logging isn't ready
Initialize()
diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm
deleted file mode 100644
index 59510162e1c..00000000000
--- a/code/controllers/hooks-defs.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Startup hook.
- * Called in world.dm when the server starts.
- */
-/hook/startup
-
-/**
- * Roundstart hook.
- * Called in gameticker.dm when a round starts.
- */
-/hook/roundstart
diff --git a/code/controllers/hooks.dm b/code/controllers/hooks.dm
deleted file mode 100644
index 48f1199ef76..00000000000
--- a/code/controllers/hooks.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * @file hooks.dm
- * Implements hooks, a simple way to run code on pre-defined events.
- */
-
-/** @page hooks Code hooks
- * @section hooks Hooks
- * A hook is defined under /hook in the type tree.
- *
- * To add some code to be called by the hook, define a proc under the type, as so:
- * @code
- /hook/foo/proc/bar()
- if(1)
- return 1 //Sucessful
- else
- return 0 //Error, or runtime.
- * @endcode
- * All hooks must return nonzero on success, as runtimes will force return null.
- */
-
-/**
- * Calls a hook, executing every piece of code that's attached to it.
- * @param hook Identifier of the hook to call.
- * @returns 1 if all hooked code runs successfully, 0 otherwise.
- */
-/proc/callHook(hook, list/args=null)
- var/hook_path = text2path("/hook/[hook]")
- if(!hook_path)
- error("Invalid hook '/hook/[hook]' called.")
- return 0
-
- var/caller = new hook_path
- var/status = 1
- for(var/P in typesof("[hook_path]/proc"))
- if(!call(caller, P)(arglist(args)))
- error("Hook '[P]' failed or runtimed.")
- status = 0
-
- return status
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index fea79c2f7a5..b8c3cdbf1e2 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -451,14 +451,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// 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
+ if(!(queue_node_flags & SS_BACKGROUND))
+ 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)
+ if(!bg_calc && (queue_node_flags & SS_BACKGROUND))
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
@@ -511,7 +512,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
- if(queue_node_flags & SS_BACKGROUND) //update our running total
+ if(bg_calc) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index 4a125003389..c9934b128cf 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -1,7 +1,7 @@
/datum/controller/subsystem
// Metadata; you should define these.
- name = "fire coderbus" //name of the subsystem
+ name = "fire codertrain" //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
@@ -35,7 +35,7 @@
var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
- var/offline_implications = "None" // What are the implications of this SS being offlined?
+ var/offline_implications = "None. No immediate action is needed." // What are the implications of this SS being offlined?
//Do not override
///datum/controller/subsystem/New()
@@ -89,7 +89,7 @@
queue_node_flags = queue_node.flags
if(queue_node_flags & SS_TICKER)
- if(!(SS_flags & SS_TICKER))
+ if((SS_flags & (SS_TICKER|SS_BACKGROUND)) != SS_TICKER)
continue
if(queue_node_priority < SS_priority)
break
diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm
index 037b9a2bd0a..25ccda3349c 100644
--- a/code/controllers/subsystem/afk.dm
+++ b/code/controllers/subsystem/afk.dm
@@ -21,7 +21,8 @@ SUBSYSTEM_DEF(afk)
/datum/controller/subsystem/afk/fire()
var/list/toRemove = list()
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
if(!H?.ckey) // Useless non ckey creatures
continue
@@ -49,7 +50,7 @@ SUBSYSTEM_DEF(afk)
if(mins_afk >= config.auto_cryo_afk && A.can_get_auto_cryod)
if(A.fast_despawn)
toRemove += H.ckey
- warn(H, "You are have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.")
+ warn(H, "You have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.")
log_afk_action(H, mins_afk, T, "despawned", "AFK in a fast despawn area")
force_cryo_human(H)
else
@@ -67,7 +68,7 @@ SUBSYSTEM_DEF(afk)
else if(afk_players[H.ckey] != AFK_ADMINS_WARNED && mins_afk >= config.auto_despawn_afk)
log_afk_action(H, mins_afk, T, "despawned")
- warn(H, "You are have been despawned after being AFK for [mins_afk] minutes.")
+ warn(H, "You have been despawned after being AFK for [mins_afk] minutes.")
toRemove += H.ckey
force_cryo_human(H)
diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm
index 289adf6de40..3d91d763be3 100644
--- a/code/controllers/subsystem/alarm.dm
+++ b/code/controllers/subsystem/alarm.dm
@@ -1,31 +1,31 @@
-SUBSYSTEM_DEF(alarms)
- name = "Alarms"
- init_order = INIT_ORDER_ALARMS // 2
- offline_implications = "Alarms (Power, camera, fire, etc) will no longer be checked. No immediate action is needed."
- var/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
- var/datum/alarm_handler/burglar/burglar_alarm = new()
- var/datum/alarm_handler/camera/camera_alarm = new()
- var/datum/alarm_handler/fire/fire_alarm = new()
- var/datum/alarm_handler/motion/motion_alarm = new()
- var/datum/alarm_handler/power/power_alarm = new()
- var/list/datum/alarm/all_handlers
+SUBSYSTEM_DEF(alarm)
+ name = "Alarm"
+ flags = SS_NO_INIT | SS_NO_FIRE
+ var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list(), "Burglar" = list())
-/datum/controller/subsystem/alarms/Initialize(start_timeofday)
- all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
- return ..()
+/datum/controller/subsystem/alarm/proc/triggerAlarm(class, area/A, list/O, obj/alarmsource)
+ var/list/L = alarms[class]
+ for(var/I in L)
+ if(I == A.name)
+ var/list/alarm = L[I]
+ var/list/sources = alarm[3]
+ if(!(alarmsource.UID() in sources))
+ sources += alarmsource.UID()
+ return TRUE
+ L[A.name] = list(get_area_name(A, TRUE), O, list(alarmsource.UID()))
+ SEND_SIGNAL(SSalarm, COMSIG_TRIGGERED_ALARM, class, A, O, alarmsource)
+ return TRUE
-/datum/controller/subsystem/alarms/fire()
- for(var/datum/alarm_handler/AH in all_handlers)
- AH.process()
+/datum/controller/subsystem/alarm/proc/cancelAlarm(class, area/A, obj/origin)
+ var/list/L = alarms[class]
+ var/cleared = FALSE
+ for(var/I in L)
+ if(I == A.name)
+ var/list/alarm = L[I]
+ var/list/srcs = alarm[3]
+ srcs -= origin.UID()
+ if(!length(srcs))
+ cleared = TRUE
+ L -= I
-/datum/controller/subsystem/alarms/proc/active_alarms()
- var/list/all_alarms = new ()
- for(var/datum/alarm_handler/AH in all_handlers)
- var/list/alarms = AH.alarms
- all_alarms += alarms
-
- return all_alarms
-
-/datum/controller/subsystem/alarms/proc/number_of_active_alarms()
- var/list/alarms = active_alarms()
- return alarms.len
+ SEND_SIGNAL(SSalarm, COMSIG_CANCELLED_ALARM, class, A, origin, cleared)
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
deleted file mode 100644
index 4eb468a0952..00000000000
--- a/code/controllers/subsystem/chat.dm
+++ /dev/null
@@ -1,67 +0,0 @@
-SUBSYSTEM_DEF(chat)
- name = "Chat"
- flags = SS_TICKER|SS_NO_INIT
- wait = 1
- priority = FIRE_PRIORITY_CHAT
- init_order = INIT_ORDER_CHAT
- offline_implications = "Chat messages will no longer be cleanly queued. No immediate action is needed."
-
- var/list/payload = list()
-
-
-/datum/controller/subsystem/chat/fire()
- for(var/i in payload)
- var/client/C = i
- if(C)
- C << output(payload[C], "browseroutput:output")
- payload -= C
-
- if(MC_TICK_CHECK)
- return
-
-
-/datum/controller/subsystem/chat/proc/queue(target, message, flag)
- if(!target || !message)
- return
-
- if(!istext(message))
- stack_trace("to_chat called with invalid input type")
- return
-
- if(target == world)
- target = GLOB.clients
-
- //Some macros remain in the string even after parsing and fuck up the eventual output
- message = replacetext(message, "\improper", "")
- message = replacetext(message, "\proper", "")
- message += " "
-
-
- //url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
- //Do the double-encoding here to save nanoseconds
- var/twiceEncoded = url_encode(url_encode(message))
-
- if(islist(target))
- for(var/I in target)
- var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
-
- if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
- continue
-
- if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- continue
-
- payload[C] += twiceEncoded
-
- else
- var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
-
- if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
- return
-
- if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
- C.chatOutput.messageQueue += message
- return
-
- payload[C] += twiceEncoded
diff --git a/code/controllers/subsystem/cleanup.dm b/code/controllers/subsystem/cleanup.dm
new file mode 100644
index 00000000000..f5962c45547
--- /dev/null
+++ b/code/controllers/subsystem/cleanup.dm
@@ -0,0 +1,44 @@
+/**
+ * # Cleanup Subsystem
+ *
+ * For now, all it does is periodically clean the supplied global lists of any null values they may contain.
+ *
+ * Why is this important?
+ *
+ * Sometimes, these lists can gain nulls due to errors.
+ * For example, when a dead player trasitions from the `dead_mob_list` to the `alive_mob_list`, a null value may get stuck in the dead mob list.
+ * This can cause issues when other code tries to do things with the values in the list, but are instead met with null values.
+ * These problems are incredibly hard to track down and fix, so this subsystem is a solution to that.
+ */
+SUBSYSTEM_DEF(cleanup)
+ name = "Null cleanup"
+ wait = 30 SECONDS
+ flags = SS_POST_FIRE_TIMING
+ priority = FIRE_PRIORITY_CLEANUP
+ init_order = INIT_ORDER_CLEANUP
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+ offline_implications = "Certain global lists will no longer be cleared of nulls, which may result in runtimes. No immediate action is needed."
+ /// A list of global lists we want the subsystem to clean.
+ var/list/lists_to_clean
+
+/datum/controller/subsystem/cleanup/Initialize(start_timeofday)
+ // If you want this subsystem to clean out nulls from a specific list, add it here.
+ lists_to_clean = list(
+ GLOB.clients = "clients",
+ GLOB.player_list = "player_list",
+ GLOB.mob_list = "mob_list",
+ GLOB.alive_mob_list = "alive_mob_list",
+ GLOB.dead_mob_list = "dead_mob_list",
+ GLOB.human_list = "human_list",
+ GLOB.carbon_list = "carbon_list"
+ )
+ return ..()
+
+/datum/controller/subsystem/cleanup/fire(resumed)
+ for(var/L in lists_to_clean)
+ var/list/_list = L
+ var/prev_length = length(_list)
+ listclearnulls(_list)
+
+ if(length(_list) < prev_length)
+ stack_trace("Found a null value in GLOB.[lists_to_clean[_list]]!")
diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index ad77904bac7..390c6bc1a62 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -90,7 +90,7 @@ SUBSYSTEM_DEF(events)
if(E.isRunning)
message += "and is still running."
else
- if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes
+ if(E.endedAt - E.startedAt > 5 MINUTES) // Only mention end time if the entire duration was more than 5 minutes
message += "and ended at [station_time_timestamp("hh:mm:ss", E.endedAt)]."
else
message += "and ran to completion."
@@ -212,38 +212,38 @@ SUBSYSTEM_DEF(events)
if(href_list["toggle_report"])
report_at_round_end = !report_at_round_end
- admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.")
+ log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.")
else if(href_list["dec_timer"])
var/datum/event_container/EC = locate(href_list["event"])
var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"])))
EC.next_event_time -= decrease
- admin_log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).")
+ log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).")
else if(href_list["inc_timer"])
var/datum/event_container/EC = locate(href_list["event"])
var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"])))
EC.next_event_time += increase
- admin_log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).")
+ log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).")
else if(href_list["select_event"])
var/datum/event_container/EC = locate(href_list["select_event"])
var/datum/event_meta/EM = EC.SelectEvent()
if(EM)
- admin_log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.")
+ log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.")
else if(href_list["pause"])
var/datum/event_container/EC = locate(href_list["pause"])
EC.delayed = !EC.delayed
- admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.")
+ log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.")
else if(href_list["interval"])
var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null
if(delay && delay > 0)
var/datum/event_container/EC = locate(href_list["interval"])
EC.delay_modifier = delay
- admin_log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].")
+ log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].")
else if(href_list["stop"])
if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes")
return
var/datum/event/E = locate(href_list["stop"])
var/datum/event_meta/EM = E.event_meta
- admin_log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
E.kill()
else if(href_list["view_events"])
selected_event_container = locate(href_list["view_events"])
@@ -265,23 +265,23 @@ SUBSYSTEM_DEF(events)
var/datum/event_meta/EM = locate(href_list["set_weight"])
EM.weight = weight
if(EM != new_event)
- admin_log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].")
+ log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].")
else if(href_list["toggle_oneshot"])
var/datum/event_meta/EM = locate(href_list["toggle_oneshot"])
EM.one_shot = !EM.one_shot
if(EM != new_event)
- admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
else if(href_list["toggle_enabled"])
var/datum/event_meta/EM = locate(href_list["toggle_enabled"])
EM.enabled = !EM.enabled
- admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
else if(href_list["remove"])
if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes")
return
var/datum/event_meta/EM = locate(href_list["remove"])
var/datum/event_container/EC = locate(href_list["EC"])
EC.available_events -= EM
- admin_log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
+ log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.")
else if(href_list["add"])
if(!new_event.name || !new_event.event_type)
return
@@ -289,12 +289,12 @@ SUBSYSTEM_DEF(events)
return
new_event.severity = selected_event_container.severity
selected_event_container.available_events += new_event
- admin_log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].")
+ log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].")
new_event = new
else if(href_list["clear"])
var/datum/event_container/EC = locate(href_list["clear"])
if(EC.next_event)
- admin_log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.")
+ log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.")
EC.next_event = null
Interact(usr)
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index cbf17d05fbf..e50c372584a 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -4,10 +4,10 @@ SUBSYSTEM_DEF(garbage)
wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
- init_order = INIT_ORDER_GARBAGE
+ init_order = INIT_ORDER_GARBAGE // Why does this have an init order if it has SS_NO_INIT?
offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended."
- var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+ var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
//Stat tracking
var/delslasttick = 0 // number of del()'s we've done this tick
diff --git a/code/controllers/subsystem/ghost_spawns.dm b/code/controllers/subsystem/ghost_spawns.dm
new file mode 100644
index 00000000000..ea0aefb88bd
--- /dev/null
+++ b/code/controllers/subsystem/ghost_spawns.dm
@@ -0,0 +1,275 @@
+SUBSYSTEM_DEF(ghost_spawns)
+ name = "Ghost Spawns"
+ init_order = INIT_ORDER_EVENTS
+ flags = SS_BACKGROUND
+ wait = 1 SECONDS
+ runlevels = RUNLEVEL_GAME
+ offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended."
+
+ /// List of polls currently ongoing, to be checked on next fire()
+ var/list/datum/candidate_poll/currently_polling
+ /// Whether there are active polls or not
+ var/polls_active = FALSE
+ /// Number of polls performed since the start
+ var/total_polls = 0
+ /// The poll that's closest to finishing
+ var/datum/candidate_poll/next_poll_to_finish
+
+/datum/controller/subsystem/ghost_spawns/fire()
+ if(!polls_active)
+ return
+ if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still..
+ currently_polling = list()
+
+ for(var/poll in currently_polling)
+ var/datum/candidate_poll/P = poll
+ if(P.time_left() <= 0)
+ polling_finished(P)
+
+/**
+ * Polls for candidates with a question and a preview of the role
+ *
+ * This proc replaces /proc/pollCandidates.
+ * Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL)
+ * Arguments:
+ * * question - The question to ask to potential candidates
+ * * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored
+ * * antag_age_check - Whether to filter out potential candidates who don't have an old enough account
+ * * poll_time - How long to poll for in deciseconds
+ * * ignore_respawnability - Whether to ignore the player's respawnability
+ * * min_hours - The amount of hours needed for a potential candidate to be eligible
+ * * flash_window - Whether the poll should flash a potential candidate's game window
+ * * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD
+ * * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert
+ */
+/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source)
+ log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds")
+
+ // Start firing
+ polls_active = TRUE
+ total_polls++
+
+ var/datum/candidate_poll/P = new(role, question, poll_time)
+ LAZYADD(currently_polling, P)
+
+ // We're the poll closest to completion
+ if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left())
+ next_poll_to_finish = P
+
+ var/category = "[P.hash]_notify_action"
+
+ for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
+ if(!is_eligible(M, role, antag_age_check, role, min_hours, check_antaghud))
+ continue
+
+ SEND_SOUND(M, 'sound/misc/notice2.ogg')
+ if(flash_window)
+ window_flash(M.client)
+
+ // If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one,
+ // we need to keep the first one's timeout rather than use the shorter one
+ var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category)
+ var/alert_time = poll_time
+ var/alert_poll = P
+ if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag))
+ alert_time = current_alert.timeout - world.time + world.tick_lag
+ alert_poll = current_alert.poll
+
+ // Send them an on-screen alert
+ var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE)
+ if(!A)
+ continue
+
+ A.icon = ui_style2icon(M.client?.prefs.UI_style)
+ A.name = "Looking for candidates"
+ A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)"
+ A.show_time_left = TRUE
+ A.poll = alert_poll
+
+ // Sign up inheritance and stacking
+ var/inherited_sign_up = FALSE
+ var/num_stack = 1
+ for(var/existing_poll in currently_polling)
+ var/datum/candidate_poll/P2 = existing_poll
+ if(P != P2 && P.hash == P2.hash)
+ // If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one
+ if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE))
+ A.display_signed_up()
+ inherited_sign_up = TRUE
+ // This number is used to display the number of polls the alert regroups
+ num_stack++
+ if(num_stack > 1)
+ A.display_stacks(num_stack)
+
+ // Image to display
+ var/image/I
+ if(source)
+ if(!ispath(source))
+ var/atom/S = source
+ var/old_layer = S.layer
+ var/old_plane = S.plane
+
+ S.layer = FLOAT_LAYER
+ S.plane = FLOAT_PLANE
+ A.overlays += S
+ S.layer = old_layer
+ S.plane = old_plane
+ else
+ I = image(source, layer = FLOAT_LAYER, dir = SOUTH)
+ else
+ // Just use a generic image
+ I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH)
+
+ if(I)
+ I.layer = FLOAT_LAYER
+ I.plane = FLOAT_PLANE
+ A.overlays += I
+
+ // Chat message
+ var/act_jump = ""
+ if(isatom(source))
+ act_jump = "\[Teleport]"
+ var/act_signup = "\[Sign Up]"
+ to_chat(M, "Now looking for candidates [role ? "to play as \an [get_roletext(role)]" : "\"[question]\""]. [act_jump] [act_signup]")
+
+ // Start processing it so it updates visually the timer
+ START_PROCESSING(SSprocessing, A)
+ A.process()
+
+ // Sleep until the time is up
+ UNTIL(P.finished)
+ return P.signed_up
+
+/**
+ * Returns whether an observer is eligible to be an event mob
+ *
+ * Arguments:
+ * * M - The mob to check eligibility
+ * * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE
+ * * antag_age_check - Whether to check the account's age or not for the given role.
+ * * role_text - The role's clean text. Used for checking job bans to determine eligibility
+ * * min_hours - The amount of minimum hours the client needs before being eligible
+ * * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not
+ */
+/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud)
+ . = FALSE
+ if(!M.key || !M.client)
+ return
+ if(role)
+ if(!(role in M.client.prefs.be_special))
+ return
+ if(antag_age_check)
+ if(!player_old_enough_antag(M.client, role))
+ return
+ if(role_text)
+ if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate"))
+ return
+ if(config.use_exp_restrictions && min_hours)
+ if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
+ return
+ if(check_antaghud && cannotPossess(M))
+ return
+
+ return TRUE
+
+/**
+ * Called by the subsystem when a poll's timer runs out
+ *
+ * Can be called manually to finish a poll prematurely
+ * Arguments:
+ * * P - The poll to finish
+ */
+/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P)
+ // Trim players who aren't eligible anymore
+ var/len_pre_trim = length(P.signed_up)
+ P.trim_candidates()
+ log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming")
+
+ P.finished = TRUE
+ currently_polling -= P
+
+ // Determine which is the next poll closest the completion or "disable" firing if there's none
+ if(!length(currently_polling))
+ polls_active = FALSE
+ next_poll_to_finish = null
+ else if(P == next_poll_to_finish)
+ next_poll_to_finish = null
+ for(var/poll in currently_polling)
+ var/datum/candidate_poll/P2 = poll
+ if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left())
+ next_poll_to_finish = P2
+
+/datum/controller/subsystem/ghost_spawns/stat_entry(msg)
+ msg += "Active: [length(currently_polling)] | Total: [total_polls]"
+ if(next_poll_to_finish)
+ msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)"
+ ..(msg)
+
+// The datum that describes one instance of candidate polling
+/datum/candidate_poll
+ var/role // The role the poll is for
+ var/question // The question asked to observers
+ var/duration // The duration of the poll
+ var/list/mob/dead/observer/signed_up // The players who signed up to this poll
+ var/time_started // The world.time at which the poll was created
+ var/finished = FALSE // Whether the polling is finished
+ var/hash // Used to categorize in the alerts system
+
+/datum/candidate_poll/New(polled_role, polled_question, poll_duration)
+ role = polled_role
+ question = polled_question
+ duration = poll_duration
+ signed_up = list()
+ time_started = world.time
+ hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7)
+ return ..()
+
+/**
+ * Attempts to sign a (controlled) mob up
+ *
+ * Will fail if the mob is already signed up or the poll's timer ran out.
+ * Does not check for eligibility
+ * Arguments:
+ * * M - The (controlled) mob to sign up
+ * * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls
+ */
+/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE)
+ . = FALSE
+ if(!istype(M) || !M.key || !M.client)
+ return
+ if(M in signed_up)
+ if(!silent)
+ to_chat(M, "You have already signed up for this!")
+ return
+ if(time_left() <= 0)
+ if(!silent)
+ to_chat(M, "Sorry, you were too late for the consideration!")
+ SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
+ return
+
+ signed_up += M
+ if(!silent)
+ to_chat(M, "You have signed up for this role! A candidate will be picked randomly soon..")
+ // Sign them up for any other polls with the same mob type
+ for(var/existing_poll in SSghost_spawns.currently_polling)
+ var/datum/candidate_poll/P = existing_poll
+ if(src != P && hash == P.hash && !(M in P.signed_up))
+ P.sign_up(M, TRUE)
+
+ return TRUE
+
+/**
+ * Deletes any candidates who may have disconnected from the list
+ */
+/datum/candidate_poll/proc/trim_candidates()
+ listclearnulls(signed_up)
+ for(var/mob in signed_up)
+ var/mob/M = mob
+ if(!M.key || !M.client)
+ signed_up -= M
+
+/**
+ * Returns the time left for a poll
+ */
+/datum/candidate_poll/proc/time_left()
+ return duration - (world.time - time_started)
diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm
index 289505a153c..8d6a7e39852 100644
--- a/code/controllers/subsystem/icon_smooth.dm
+++ b/code/controllers/subsystem/icon_smooth.dm
@@ -19,8 +19,18 @@ SUBSYSTEM_DEF(icon_smooth)
can_fire = 0
/datum/controller/subsystem/icon_smooth/Initialize()
- smooth_zlevel(1,TRUE)
- smooth_zlevel(2,TRUE)
+ log_startup_progress("Smoothing atoms...")
+ // Smooth EVERYTHING in the world
+ for(var/turf/T in world)
+ if(T.smooth)
+ smooth_icon(T)
+ for(var/A in T)
+ var/atom/AA = A
+ if(AA.smooth)
+ smooth_icon(AA)
+ CHECK_TICK
+
+ // Incase any new atoms were added to the smoothing queue for whatever reason
var/queue = smooth_queue
smooth_queue = list()
for(var/V in queue)
diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm
index 9dd6fce1554..ecbf1471993 100644
--- a/code/controllers/subsystem/input.dm
+++ b/code/controllers/subsystem/input.dm
@@ -121,3 +121,8 @@ SUBSYSTEM_DEF(input)
for(var/i in 1 to clients.len)
var/client/C = clients[i]
C.keyLoop()
+
+/datum/controller/subsystem/input/Recover()
+ macro_sets = SSinput.macro_sets
+ movement_keys = SSinput.movement_keys
+ alt_movement_keys = SSinput.alt_movement_keys
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index ef89317d0f5..3adfdf3972f 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/fire()
if(!config.sql_enabled || !config.use_exp_tracking)
return
- update_exp(5,0)
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0)
/datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station"))
occupations = list()
@@ -644,6 +644,27 @@ SUBSYSTEM_DEF(jobs)
oldjobdatum.current_positions--
newjobdatum.current_positions++
+/datum/controller/subsystem/jobs/proc/notify_dept_head(jobtitle, antext)
+ // Used to notify the department head of jobtitle X that their employee was brigged, demoted or terminated
+ if(!jobtitle || !antext)
+ return
+ var/datum/job/tgt_job = GetJob(jobtitle)
+ if(!tgt_job)
+ return
+ if(!tgt_job.department_head[1])
+ return
+ var/boss_title = tgt_job.department_head[1]
+ var/obj/item/pda/target_pda
+ for(var/obj/item/pda/check_pda in GLOB.PDAs)
+ if(check_pda.ownrank == boss_title)
+ target_pda = check_pda
+ break
+ if(!target_pda)
+ return
+ var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
+ if(PM && PM.can_receive())
+ PM.notify("Automated Notification: \"[antext]\" (Unable to Reply)")
+
/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom)
var/record_html = ""
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index 3fc92a3346a..6761e9647d9 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -1,16 +1,15 @@
-GLOBAL_LIST_EMPTY(lighting_update_lights) // List of lighting sources queued for update.
-GLOBAL_LIST_EMPTY(lighting_update_corners) // List of lighting corners queued for update.
-GLOBAL_LIST_EMPTY(lighting_update_objects) // List of lighting objects queued for update.
-
SUBSYSTEM_DEF(lighting)
name = "Lighting"
wait = 2
init_order = INIT_ORDER_LIGHTING
flags = SS_TICKER
offline_implications = "Lighting will no longer update. Shuttle call recommended."
+ var/static/list/sources_queue = list() // List of lighting sources queued for update.
+ var/static/list/corners_queue = list() // List of lighting corners queued for update.
+ var/static/list/objects_queue = list() // List of lighting objects queued for update.
/datum/controller/subsystem/lighting/stat_entry()
- ..("L:[GLOB.lighting_update_lights.len]|C:[GLOB.lighting_update_corners.len]|O:[GLOB.lighting_update_objects.len]")
+ ..("L:[length(sources_queue)]|C:[length(corners_queue)]|O:[length(objects_queue)]")
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!initialized)
@@ -31,9 +30,10 @@ SUBSYSTEM_DEF(lighting)
MC_SPLIT_TICK_INIT(3)
if(!init_tick_checks)
MC_SPLIT_TICK
+ var/list/queue = sources_queue
var/i = 0
- for(i in 1 to GLOB.lighting_update_lights.len)
- var/datum/light_source/L = GLOB.lighting_update_lights[i]
+ for(i in 1 to length(queue))
+ var/datum/light_source/L = queue[i]
L.update_corners()
@@ -44,14 +44,15 @@ SUBSYSTEM_DEF(lighting)
else if(MC_TICK_CHECK)
break
if(i)
- GLOB.lighting_update_lights.Cut(1, i+1)
+ queue.Cut(1, i + 1)
i = 0
if(!init_tick_checks)
MC_SPLIT_TICK
- for (i in 1 to GLOB.lighting_update_corners.len)
- var/datum/lighting_corner/C = GLOB.lighting_update_corners[i]
+ queue = corners_queue
+ for(i in 1 to length(queue))
+ var/datum/lighting_corner/C = queue[i]
C.update_objects()
C.needs_update = FALSE
@@ -60,15 +61,16 @@ SUBSYSTEM_DEF(lighting)
else if(MC_TICK_CHECK)
break
if(i)
- GLOB.lighting_update_corners.Cut(1, i+1)
+ queue.Cut(1, i + 1)
i = 0
if(!init_tick_checks)
MC_SPLIT_TICK
- for (i in 1 to GLOB.lighting_update_objects.len)
- var/atom/movable/lighting_object/O = GLOB.lighting_update_objects[i]
+ queue = objects_queue
+ for(i in 1 to length(queue))
+ var/atom/movable/lighting_object/O = queue[i]
if(QDELETED(O))
continue
@@ -80,7 +82,7 @@ SUBSYSTEM_DEF(lighting)
else if(MC_TICK_CHECK)
break
if(i)
- GLOB.lighting_update_objects.Cut(1, i+1)
+ queue.Cut(1, i + 1)
/datum/controller/subsystem/lighting/Recover()
diff --git a/code/controllers/subsystem/machinery.dm b/code/controllers/subsystem/machinery.dm
index 7e78e2b9df1..cebc6a5f768 100644
--- a/code/controllers/subsystem/machinery.dm
+++ b/code/controllers/subsystem/machinery.dm
@@ -42,7 +42,7 @@ SUBSYSTEM_DEF(machines)
while(currentrun.len)
var/obj/O = currentrun[currentrun.len]
currentrun.len--
- if(O)
+ if(O && !QDELETED(O))
var/datum/powernet/newPN = new() // create a new powernet...
propagate_network(O, newPN)//... and propagate it to the other side of the cable
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 67e2b5c5e88..163b2635d0e 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -11,24 +11,57 @@ SUBSYSTEM_DEF(mapping)
createRandomZlevel()
// Seed space ruins
if(!config.disable_space_ruins)
- var/timer = start_watch()
- log_startup_progress("Creating random space levels...")
- seedRuins(list(level_name_to_num(EMPTY_AREA)), rand(0, 3), /area/space, GLOB.space_ruins_templates)
- log_startup_progress("Loaded random space levels in [stop_watch(timer)]s.")
-
// load in extra levels of space ruins
-
+ var/load_zlevels_timer = start_watch()
+ log_startup_progress("Creating random space levels...")
var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max)
for(var/i = 1, i <= num_extra_space, i++)
- var/zlev = GLOB.space_manager.add_new_zlevel("[EMPTY_AREA] #[i]", linkage = CROSSLINKED, traits = list(REACHABLE))
- seedRuins(list(zlev), rand(0, 3), /area/space, GLOB.space_ruins_templates)
+ GLOB.space_manager.add_new_zlevel("Ruin Area #[i]", linkage = CROSSLINKED, traits = list(REACHABLE, SPAWN_RUINS))
+ log_startup_progress("Loaded random space levels in [stop_watch(load_zlevels_timer)]s.")
+
+ // Now spawn ruins, random budget between 20 and 30 for all zlevels combined.
+ // While this may seem like a high number, the amount of ruin Z levels can be anywhere between 3 and 7.
+ // Note that this budget is not split evenly accross all zlevels
+ log_startup_progress("Seeding ruins...")
+ var/seed_ruins_timer = start_watch()
+ seedRuins(levels_by_trait(SPAWN_RUINS), rand(20, 30), /area/space, GLOB.space_ruins_templates)
+ log_startup_progress("Successfully seeded ruins in [stop_watch(seed_ruins_timer)]s.")
+
+ // Makes a blank space level for the sake of randomness
+ GLOB.space_manager.add_new_zlevel("Empty Area", linkage = CROSSLINKED, traits = list(REACHABLE))
+
// Setup the Z-level linkage
GLOB.space_manager.do_transition_setup()
// Spawn Lavaland ruins and rivers.
+ log_startup_progress("Populating lavaland...")
+ var/lavaland_setup_timer = start_watch()
seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, GLOB.lava_ruins_templates)
spawn_rivers(list(level_name_to_num(MINING)))
+ log_startup_progress("Successfully populated lavaland in [stop_watch(lavaland_setup_timer)]s.")
+
+ // Now we make a list of areas for teleport locs
+ // TOOD: Make these locs into lists on the SS itself, not globs
+ for(var/area/AR in world)
+ if(AR.no_teleportlocs)
+ continue
+ if(GLOB.teleportlocs[AR.name])
+ continue
+ var/turf/picked = safepick(get_area_turfs(AR.type))
+ if(picked && is_station_level(picked.z))
+ GLOB.teleportlocs[AR.name] = AR
+
+ GLOB.teleportlocs = sortAssoc(GLOB.teleportlocs)
+
+ for(var/area/AR in world)
+ if(GLOB.ghostteleportlocs[AR.name])
+ continue
+ var/list/turfs = get_area_turfs(AR.type)
+ if(turfs.len)
+ GLOB.ghostteleportlocs[AR.name] = AR
+
+ GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs)
return ..()
diff --git a/code/controllers/subsystem/nano_mob_hunter.dm b/code/controllers/subsystem/nano_mob_hunter.dm
index 9094bf9fe2b..b8a5d09e0b5 100644
--- a/code/controllers/subsystem/nano_mob_hunter.dm
+++ b/code/controllers/subsystem/nano_mob_hunter.dm
@@ -2,6 +2,7 @@ SUBSYSTEM_DEF(mob_hunt)
name = "Nano-Mob Hunter GO Server"
init_order = INIT_ORDER_NANOMOB
priority = FIRE_PRIORITY_NANOMOB // Low priority, no need for MC_TICK_CHECK due to extremely low performance impact.
+ flags = SS_NO_INIT
offline_implications = "Nano-Mob Hunter will no longer spawn mobs. No immediate action is needed."
var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this
var/list/normal_spawns = list()
@@ -103,12 +104,12 @@ SUBSYSTEM_DEF(mob_hunt)
return
if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready)
battle_turn = pick("Red", "Blue")
- red_terminal.audible_message("Battle starting!", null, 5)
- blue_terminal.audible_message("Battle starting!", null, 5)
+ red_terminal.atom_say("Battle starting!")
+ blue_terminal.atom_say("Battle starting!")
if(battle_turn == "Red")
- red_terminal.audible_message("Red Player's Turn!", null, 5)
+ red_terminal.atom_say("Red Player's Turn!")
else if(battle_turn == "Blue")
- blue_terminal.audible_message("Blue Player's Turn!", null, 5)
+ blue_terminal.atom_say("Blue Player's Turn!")
/datum/controller/subsystem/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type)
if(!team || !raw_damage)
@@ -135,11 +136,11 @@ SUBSYSTEM_DEF(mob_hunt)
winner_terminal.ready = 0
loser_terminal.ready = 0
if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle
- winner_terminal.audible_message("Your rival surrendered!", null, 2)
+ winner_terminal.atom_say("Your rival surrendered!")
else
var/progress_message = winner_terminal.mob_info.gain_exp()
- winner_terminal.audible_message("[winner_terminal.team] Player wins!", null, 5)
- winner_terminal.audible_message(progress_message, null, 2)
+ winner_terminal.atom_say("[winner_terminal.team] Player wins!")
+ winner_terminal.atom_say(progress_message)
/datum/controller/subsystem/mob_hunt/proc/end_turn()
red_terminal.updateUsrDialog()
@@ -148,7 +149,7 @@ SUBSYSTEM_DEF(mob_hunt)
return
if(battle_turn == "Red")
battle_turn = "Blue"
- blue_terminal.audible_message("Blue's turn.", null, 5)
+ blue_terminal.atom_say("Blue's turn.")
else if(battle_turn == "Blue")
battle_turn = "Red"
- blue_terminal.audible_message("Red's turn.", null, 5)
+ blue_terminal.atom_say("Red's turn.")
diff --git a/code/controllers/subsystem/processing/dcs.dm b/code/controllers/subsystem/processing/dcs.dm
new file mode 100644
index 00000000000..a223f4676f6
--- /dev/null
+++ b/code/controllers/subsystem/processing/dcs.dm
@@ -0,0 +1,55 @@
+PROCESSING_SUBSYSTEM_DEF(dcs)
+ name = "Datum Component System"
+ flags = SS_NO_INIT
+
+ var/list/elements_by_type = list()
+ // Update this if you add in components which actually use this as a processor
+ offline_implications = "This SS doesnt actually process anything yet. No immediate action is needed."
+
+/datum/controller/subsystem/processing/dcs/Recover()
+ comp_lookup = SSdcs.comp_lookup
+
+/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments)
+ var/datum/element/eletype = arguments[1]
+ var/element_id = eletype
+
+ if(!ispath(eletype, /datum/element))
+ CRASH("Attempted to instantiate [eletype] as a /datum/element")
+
+ if(initial(eletype.element_flags) & ELEMENT_BESPOKE)
+ element_id = GetIdFromArguments(arguments)
+
+ . = elements_by_type[element_id]
+ if(.)
+ return
+ . = elements_by_type[element_id] = new eletype
+
+/****
+ * Generates an id for bespoke elements when given the argument list
+ * Generating the id here is a bit complex because we need to support named arguments
+ * Named arguments can appear in any order and we need them to appear after ordered arguments
+ * We assume that no one will pass in a named argument with a value of null
+ **/
+/datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments)
+ var/datum/element/eletype = arguments[1]
+ var/list/fullid = list("[eletype]")
+ var/list/named_arguments = list()
+ for(var/i in initial(eletype.id_arg_index) to length(arguments))
+ var/key = arguments[i]
+ var/value
+ if(istext(key))
+ value = arguments[key]
+ if(!(istext(key) || isnum(key)))
+ key = "\ref[key]"
+ key = "[key]" // Key is stringified so numbers dont break things
+ if(!isnull(value))
+ if(!(istext(value) || isnum(value)))
+ value = "\ref[value]"
+ named_arguments["[key]"] = value
+ else
+ fullid += "[key]"
+
+ if(length(named_arguments))
+ named_arguments = sortList(named_arguments)
+ fullid += named_arguments
+ return list2params(fullid)
diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm
index 9622e021469..37761ca8d60 100644
--- a/code/controllers/subsystem/processing/fastprocess.dm
+++ b/code/controllers/subsystem/processing/fastprocess.dm
@@ -4,3 +4,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess)
name = "Fast Processing"
wait = 2
stat_tag = "FP"
+ offline_implications = "Objects using the 'Fast Processing' processor will no longer process. Shuttle call recommended."
diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm
new file mode 100644
index 00000000000..3d571d2a13d
--- /dev/null
+++ b/code/controllers/subsystem/processing/instruments.dm
@@ -0,0 +1,86 @@
+PROCESSING_SUBSYSTEM_DEF(instruments)
+ name = "Instruments"
+ init_order = INIT_ORDER_INSTRUMENTS
+ wait = 1
+ flags = SS_TICKER|SS_BACKGROUND|SS_KEEP_TIMING
+ offline_implications = "Instruments will no longer play. No immediate action is needed."
+
+ /// List of all instrument data, associative id = datum
+ var/list/datum/instrument/instrument_data
+ /// List of all song datums.
+ var/list/datum/song/songs
+ /// Max lines in songs
+ var/musician_maxlines = 600
+ /// Max characters per line in songs
+ var/musician_maxlinechars = 300
+ /// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this.
+ var/musician_hearcheck_mindelay = 5
+ /// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels.
+ var/max_instrument_channels = MAX_INSTRUMENT_CHANNELS
+ /// Current number of channels allocated for instruments
+ var/current_instrument_channels = 0
+ /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer.
+ var/list/synthesizer_instrument_ids
+
+/datum/controller/subsystem/processing/instruments/Initialize()
+ initialize_instrument_data()
+ synthesizer_instrument_ids = get_allowed_instrument_ids()
+ return ..()
+
+/**
+ * Initializes all instrument datums
+ */
+/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data()
+ instrument_data = list()
+ for(var/path in subtypesof(/datum/instrument))
+ var/datum/instrument/I = path
+ if(initial(I.abstract_type) == path)
+ continue
+ I = new path
+ I.Initialize()
+ if(!I.id)
+ qdel(I)
+ continue
+ else
+ instrument_data[I.id] = I
+ CHECK_TICK
+
+/**
+ * Reserves a sound channel for a given instrument datum
+ *
+ * Arguments:
+ * * I - The instrument datum
+ */
+/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I)
+ if(current_instrument_channels > max_instrument_channels)
+ return
+ . = SSsounds.reserve_sound_channel(I)
+ if(!isnull(.))
+ current_instrument_channels++
+
+/**
+ * Called when a datum/song is created
+ *
+ * Arguments:
+ * * S - The created datum/song
+ */
+/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S)
+ LAZYADD(songs, S)
+
+/**
+ * Called when a datum/song is deleted
+ *
+ * Arguments:
+ * * S - The deleted datum/song
+ */
+/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S)
+ LAZYREMOVE(songs, S)
+
+/**
+ * Returns the instrument datum at the given ID or path
+ *
+ * Arguments:
+ * * id_or_path - The ID or path of the instrument
+ */
+/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path)
+ return instrument_data["[id_or_path]"]
diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm
index 26021fb267a..2a05c04af58 100644
--- a/code/controllers/subsystem/processing/obj.dm
+++ b/code/controllers/subsystem/processing/obj.dm
@@ -3,3 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(obj)
priority = FIRE_PRIORITY_OBJ
flags = SS_NO_INIT
wait = 20
+ offline_implications = "Objects using the 'Objects' processor will no longer process. Shuttle call recommended."
diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm
index a8bc823bbbe..5302314589d 100644
--- a/code/controllers/subsystem/processing/processing.dm
+++ b/code/controllers/subsystem/processing/processing.dm
@@ -9,6 +9,7 @@ SUBSYSTEM_DEF(processing)
var/stat_tag = "P" //Used for logging
var/list/processing = list()
var/list/currentrun = list()
+ offline_implications = "Objects using the default processor will no longer process. Shuttle call recommended."
/datum/controller/subsystem/processing/stat_entry()
..("[stat_tag]:[processing.len]")
diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm
index 247d71d05b8..67b6a1b8dd7 100644
--- a/code/controllers/subsystem/shuttles.dm
+++ b/code/controllers/subsystem/shuttles.dm
@@ -14,9 +14,9 @@ SUBSYSTEM_DEF(shuttle)
//emergency shuttle stuff
var/obj/docking_port/mobile/emergency/emergency
var/obj/docking_port/mobile/emergency/backup/backup_shuttle
- var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds)
- var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
- var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
+ var/emergencyCallTime = SHUTTLE_CALLTIME //time taken for emergency shuttle to reach the station when called (in deciseconds)
+ var/emergencyDockTime = SHUTTLE_DOCKTIME //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
+ var/emergencyEscapeTime = SHUTTLE_ESCAPETIME //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher
var/area/emergencyLastCallLoc
var/emergencyNoEscape
@@ -93,6 +93,11 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
+/datum/controller/subsystem/shuttle/proc/secondsToRefuel()
+ var/elapsed = world.time - SSticker.round_start_time
+ var/remaining = round((config.shuttle_refuel_delay - elapsed) / 10)
+ return remaining > 0 ? remaining : 0
+
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
if(!emergency)
WARNING("requestEvac(): There is no emergency shuttle, but the shuttle was called. Using the backup shuttle instead.")
@@ -107,7 +112,7 @@ SUBSYSTEM_DEF(shuttle)
return
emergency = backup_shuttle
- if(world.time - SSticker.round_start_time < config.shuttle_refuel_delay)
+ if(secondsToRefuel())
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again.")
return
@@ -131,7 +136,7 @@ SUBSYSTEM_DEF(shuttle)
call_reason = trim(html_encode(call_reason))
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH)
- to_chat(user, "You must provide a reason.")
+ to_chat(user, "Reason is too short. [CALL_SHUTTLE_REASON_LENGTH] character minimum.")
return
var/area/signal_origin = get_area(user)
@@ -192,7 +197,7 @@ SUBSYSTEM_DEF(shuttle)
var/obj/machinery/computer/communications/C = thing
if(C.stat & BROKEN)
continue
- else if(istype(thing, /datum/computer_file/program/comm) || istype(thing, /obj/item/circuitboard/communications))
+ else if(istype(thing, /obj/item/circuitboard/communications))
continue
var/turf/T = get_turf(thing)
diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm
new file mode 100644
index 00000000000..33d97fcfe04
--- /dev/null
+++ b/code/controllers/subsystem/sounds.dm
@@ -0,0 +1,165 @@
+#define DATUMLESS "NO_DATUM"
+
+SUBSYSTEM_DEF(sounds)
+ name = "Sounds"
+ init_order = INIT_ORDER_SOUNDS
+ flags = SS_NO_FIRE
+ offline_implications = "Sounds may not play correctly. Shuttle call recommended."
+
+ var/using_channels_max = CHANNEL_HIGHEST_AVAILABLE // BYOND max channels
+ /// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
+ var/random_channels_min = 50
+ // Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing.
+ /// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
+ var/list/using_channels
+ /// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved.
+ var/list/using_channels_by_datum
+ // Special datastructure for fast channel management
+ /// List of all channels as numbers
+ var/list/channel_list
+ /// Associative list of all reserved channels associated to their position. "[channel_number]" = index as number
+ var/list/reserved_channels
+ /// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel.
+ var/channel_random_low
+ /// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
+ var/channel_reserve_high
+
+/datum/controller/subsystem/sounds/Initialize()
+ setup_available_channels()
+ return ..()
+
+/**
+ * Sets up all available sound channels
+ */
+/datum/controller/subsystem/sounds/proc/setup_available_channels()
+ channel_list = list()
+ reserved_channels = list()
+ using_channels = list()
+ using_channels_by_datum = list()
+ for(var/i in 1 to using_channels_max)
+ channel_list += i
+ channel_random_low = 1
+ channel_reserve_high = length(channel_list)
+
+/**
+ * Removes a channel from using list
+ *
+ * Arguments:
+ * * channel - The channel number
+ */
+/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
+ var/text_channel = num2text(channel)
+ var/using = using_channels[text_channel]
+ using_channels -= text_channel
+ if(!using) // datum channel
+ using_channels_by_datum[using] -= channel
+ if(!length(using_channels_by_datum[using]))
+ using_channels_by_datum -= using
+ free_channel(channel)
+
+/**
+ * Frees all the channels a datum is using
+ *
+ * Arguments:
+ * * D - The datum
+ */
+/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D)
+ var/list/L = using_channels_by_datum[D]
+ if(!L)
+ return
+ for(var/channel in L)
+ using_channels -= num2text(channel)
+ free_channel(channel)
+ using_channels_by_datum -= D
+
+/**
+ * Frees all datumless channels
+ */
+/datum/controller/subsystem/sounds/proc/free_datumless_channels()
+ free_datum_channels(DATUMLESS)
+
+/**
+ * NO AUTOMATIC CLEANUP - If you use this, you better manually free it later!
+ *
+ * Returns an integer for channel
+ */
+/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
+ . = reserve_channel()
+ if(!.) // oh no..
+ return FALSE
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = DATUMLESS
+ LAZYADD(using_channels_by_datum[DATUMLESS], .)
+
+/**
+ * Reserves a channel for a datum. Automatic cleanup only when the datum is deleted.
+ *
+ * Returns an integer for channel
+ * Arguments:
+ * * D - The datum
+ */
+/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
+ if(!D) // i don't like typechecks but someone will fuck it up
+ CRASH("Attempted to reserve sound channel without datum using the managed proc.")
+ . = reserve_channel()
+ if(!.)
+ return FALSE
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = D
+ LAZYADD(using_channels_by_datum[D], .)
+
+/**
+ * Reserves a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/reserve_channel()
+ PRIVATE_PROC(TRUE)
+ if(channel_reserve_high <= random_channels_min) // out of channels
+ return
+ var/channel = channel_list[channel_reserve_high]
+ reserved_channels[num2text(channel)] = channel_reserve_high--
+ return channel
+
+/**
+ * Frees a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/free_channel(number)
+ PRIVATE_PROC(TRUE)
+ var/text_channel = num2text(number)
+ var/index = reserved_channels[text_channel]
+ if(!index)
+ CRASH("Attempted to (internally) free a channel that wasn't reserved.")
+ reserved_channels -= text_channel
+ // push reserve index up, which makes it now on a channel that is reserved
+ channel_reserve_high++
+ // swap the reserved channel with the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used.
+ channel_list.Swap(channel_reserve_high, index)
+ // now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
+ // get it, and update position.
+ var/text_reserved = num2text(channel_list[index])
+ if(!reserved_channels[text_reserved]) // if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
+ return
+ reserved_channels[text_reserved] = index
+
+/**
+ * Random available channel, returns text
+ */
+/datum/controller/subsystem/sounds/proc/random_available_channel_text()
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = "[channel_list[channel_random_low++]]"
+
+/**
+ * Random available channel, returns number
+ */
+/datum/controller/subsystem/sounds/proc/random_available_channel()
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = channel_list[channel_random_low++]
+
+/**
+ * How many channels we have left
+ */
+/datum/controller/subsystem/sounds/proc/available_channels_left()
+ return length(channel_list) - random_channels_min
+
+#undef DATUMLESS
diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm
new file mode 100644
index 00000000000..c4240021137
--- /dev/null
+++ b/code/controllers/subsystem/tgui.dm
@@ -0,0 +1,286 @@
+ /**
+ * tgui subsystem
+ *
+ * Contains all tgui state and subsystem code.
+ **/
+
+
+SUBSYSTEM_DEF(tgui)
+ name = "TGUI"
+ wait = 9
+ flags = SS_NO_INIT
+ priority = FIRE_PRIORITY_NANOUI // Yes I am aware that this is TGUI and I used the nanoUI fire priority. Dont @ me.
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+ offline_implications = "All TGUIs will no longer process. Shuttle call recommended."
+
+ 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.
+ var/basehtml // The HTML base used for all UIs.
+
+/datum/controller/subsystem/tgui/PreInit()
+ basehtml = file2text('tgui/packages/tgui/public/tgui.html')
+
+/datum/controller/subsystem/tgui/Shutdown()
+ close_all_uis()
+
+/datum/controller/subsystem/tgui/stat_entry()
+ ..("P:[processing_uis.len]")
+
+/datum/controller/subsystem/tgui/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/tgui/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
+
+ /**
+ * public
+ *
+ * Get a open UI given a user, src_object, and ui_key and try to update it with data.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ * optional ui datum/tgui The UI to be updated, if it exists.
+ * optional force_open bool If the UI should be re-opened instead of updated.
+ *
+ * return datum/tgui The found UI.
+ **/
+/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE)
+ if(isnull(ui)) // No UI was passed, so look for one.
+ ui = get_open_ui(user, src_object, ui_key)
+
+ if(!isnull(ui))
+ var/data = src_object.tgui_data(user) // Get data from the src_object.
+ if(!force_open) // UI is already open; update it.
+ ui.push_data(data)
+ else // Re-open it anyways.
+ ui.reinitialize(null, data)
+ return ui // We found the UI, return it.
+ else
+ return null // We couldn't find a UI.
+
+ /**
+ * private
+ *
+ * Get a open UI given a user, src_object, and ui_key.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * required src_object datum The object/datum which owns the UI.
+ * required ui_key string The ui_key of the UI.
+ *
+ * return datum/tgui The found UI.
+ **/
+/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return null // No UIs open.
+ else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
+ return null // No UIs open for this object.
+
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object.
+ if(ui.user == user) // Make sure we have the right user
+ return ui
+
+ return null // Couldn't find a UI!
+
+ /**
+ * private
+ *
+ * Update all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs updated.
+ **/
+/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return 0 // Couldn't find any UIs for this object.
+
+ var/update_count = 0
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.process(force = 1) // Update the UI.
+ update_count++ // Count each UI we update.
+ return update_count
+
+ /**
+ * private
+ *
+ * Close all UIs attached to src_object.
+ *
+ * required src_object datum The object/datum which owns the UIs.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
+ var/src_object_key = "[src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return 0 // Couldn't find any UIs for this object.
+
+ var/close_count = 0
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Close *ALL* UIs
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_all_uis()
+ var/close_count = 0
+ for(var/src_object_key in open_uis)
+ for(var/ui_key in open_uis[src_object_key])
+ for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
+ if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid.
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Update all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only update UIs belonging this src_object.
+ * optional ui_key string If provided, only update UIs with this UI key.
+ *
+ * return int The number of UIs updated.
+ **/
+/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
+ if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0)
+ return 0 // Couldn't find any UIs for this user.
+
+ var/update_count = 0
+ for(var/datum/tgui/ui in user.open_tguis)
+ if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
+ ui.process(force = 1) // Update the UI.
+ update_count++ // Count each UI we upadte.
+ return update_count
+
+ /**
+ * private
+ *
+ * Close all UIs belonging to a user.
+ *
+ * required user mob The mob who opened/is using the UI.
+ * optional src_object datum If provided, only close UIs belonging this src_object.
+ * optional ui_key string If provided, only close UIs with this UI key.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
+ if(isnull(user.open_tguis) || !istype(user.open_tguis, /list) || open_uis.len == 0)
+ return 0 // Couldn't find any UIs for this user.
+
+ var/close_count = 0
+ for(var/datum/tgui/ui in user.open_tguis)
+ if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
+ ui.close() // Close the UI.
+ close_count++ // Count each UI we close.
+ return close_count
+
+ /**
+ * private
+ *
+ * Add a UI to the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be added.
+ **/
+/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
+ var/src_object_key = "[ui.src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
+ else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
+ open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key.
+
+ // Append the UI to all the lists.
+ ui.user.open_tguis |= ui
+ var/list/uis = open_uis[src_object_key][ui.ui_key]
+ uis |= ui
+ processing_uis |= ui
+
+ /**
+ * private
+ *
+ * Remove a UI from the list of open UIs.
+ *
+ * required ui datum/tgui The UI to be removed.
+ *
+ * return bool If the UI was removed or not.
+ **/
+/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
+ var/src_object_key = "[ui.src_object.UID()]"
+ if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
+ return FALSE // It wasn't open.
+ else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
+ return FALSE // It wasn't open.
+
+ processing_uis.Remove(ui) // Remove it from the list of processing UIs.
+ if(ui.user) // If the user exists, remove it from them too.
+ ui.user.open_tguis.Remove(ui)
+ var/Ukey = ui.ui_key
+ var/list/uis = open_uis[src_object_key][Ukey] // Remove it from the list of open UIs.
+ uis.Remove(ui)
+ if(!uis.len)
+ var/list/uiobj = open_uis[src_object_key]
+ uiobj.Remove(Ukey)
+ if(!uiobj.len)
+ open_uis.Remove(src_object_key)
+
+ return TRUE // Let the caller know we did it.
+
+ /**
+ * private
+ *
+ * Handle client logout, by closing all their UIs.
+ *
+ * required user mob The mob which logged out.
+ *
+ * return int The number of UIs closed.
+ **/
+/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
+ return close_user_uis(user)
+
+ /**
+ * private
+ *
+ * Handle clients switching mobs, by transferring their UIs.
+ *
+ * required user source The client's original mob.
+ * required user target The client's new mob.
+ *
+ * return bool If the UIs were transferred.
+ **/
+/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
+ if(!source || isnull(source.open_tguis) || !istype(source.open_tguis, /list) || open_uis.len == 0)
+ return FALSE // The old mob had no open UIs.
+
+ if(isnull(target.open_tguis) || !istype(target.open_tguis, /list))
+ target.open_tguis = list() // Create a list for the new mob if needed.
+
+ for(var/datum/tgui/ui in source.open_tguis)
+ ui.user = target // Inform the UIs of their new owner.
+ target.open_tguis.Add(ui) // Transfer all the UIs.
+
+ source.open_tguis.Cut() // Clear the old list.
+ return TRUE // Let the caller know we did it.
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 228f1a7c528..e53c91ac20d 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -61,7 +61,7 @@ SUBSYSTEM_DEF(ticker)
if(GAME_STATE_STARTUP)
// This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails
round_start_time = world.time + (config.pregame_timestart * 10)
- to_chat(world, "Welcome to the pre-game lobby!")
+ to_chat(world, "Welcome to the pre-game lobby!")
to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds")
current_state = GAME_STATE_PREGAME
fire() // TG says this is a good idea
@@ -180,7 +180,14 @@ SUBSYSTEM_DEF(ticker)
current_state = GAME_STATE_PLAYING
Master.SetRunLevel(RUNLEVEL_GAME)
- callHook("roundstart")
+ // Generate the list of playable AI cores in the world
+ for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
+ if(S.name != "AI")
+ continue
+ if(locate(/mob/living) in S.loc)
+ continue
+ GLOB.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S))
+
//here to initialize the random events nicely at round start
setup_economy()
@@ -210,11 +217,11 @@ SUBSYSTEM_DEF(ticker)
for(var/obj/effect/landmark/spacepod/random/R in L)
qdel(R)
- to_chat(world, "Enjoy the game!")
+ to_chat(world, "Enjoy the game!")
world << sound('sound/AI/welcome.ogg')// Skie
if(SSholiday.holidays)
- to_chat(world, "and...")
+ to_chat(world, "and...")
for(var/holidayname in SSholiday.holidays)
var/datum/holiday/holiday = SSholiday.holidays[holidayname]
to_chat(world, "[holiday.greet()]")
@@ -280,7 +287,21 @@ SUBSYSTEM_DEF(ticker)
if(N.client)
N.new_player_panel_proc()
- return 1
+ // Now that every other piece of the round has initialized, lets setup player job scaling
+ var/playercount = length(GLOB.clients)
+ var/highpop_trigger = 80
+
+ if(playercount >= highpop_trigger)
+ log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - loading highpop job config")
+ SSjobs.LoadJobs("config/jobs_highpop.txt")
+ else
+ log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config")
+
+ #ifdef UNIT_TESTS
+ RunUnitTests()
+ #endif
+ return TRUE
+
/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null)
if(cinematic)
@@ -405,7 +426,7 @@ SUBSYSTEM_DEF(ticker)
EquipCustomItems(player)
if(captainless)
for(var/mob/M in GLOB.player_list)
- if(!istype(M,/mob/new_player))
+ if(!isnewplayer(M))
to_chat(M, "Captainship not forced on anyone.")
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
diff --git a/code/controllers/subsystem/tickets/mentor_tickets.dm b/code/controllers/subsystem/tickets/mentor_tickets.dm
index d8bae77840c..af4ea2e914d 100644
--- a/code/controllers/subsystem/tickets/mentor_tickets.dm
+++ b/code/controllers/subsystem/tickets/mentor_tickets.dm
@@ -1,21 +1,28 @@
GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets)
/datum/controller/subsystem/tickets/mentor_tickets/New()
- NEW_SS_GLOBAL(SSmentor_tickets);
- PreInit();
+ NEW_SS_GLOBAL(SSmentor_tickets)
+ PreInit()
/datum/controller/subsystem/tickets/mentor_tickets
name = "Mentor Tickets"
+ offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed."
ticket_system_name = "Mentor Tickets"
ticket_name = "Mentor Ticket"
span_class = "mentorhelp"
+ other_ticket_name = "Admin"
+ other_ticket_permission = R_ADMIN
close_rights = R_MENTOR | R_ADMIN
-
-/datum/controller/subsystem/tickets/mentor_tickets/message_staff(var/msg)
- message_mentorTicket(msg)
+ rights_needed = R_MENTOR | R_ADMIN | R_MOD
/datum/controller/subsystem/tickets/mentor_tickets/Initialize()
- close_messages = list("- [ticket_name] Closed -",
- "Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.",
+ close_messages = list("- [ticket_name] Closed -",
+ "Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.",
"Your [ticket_name] has now been closed.")
return ..()
+
+/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg)
+ message_mentorTicket(msg)
+
+/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T)
+ SStickets.newTicket(T.clientName, T.content, T.title)
diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm
index 058569250ec..64f91e96b6c 100644
--- a/code/controllers/subsystem/tickets/tickets.dm
+++ b/code/controllers/subsystem/tickets/tickets.dm
@@ -12,24 +12,30 @@
SUBSYSTEM_DEF(tickets)
name = "Admin Tickets"
+ init_order = INIT_ORDER_TICKETS
+ wait = 300
+ priority = FIRE_PRIORITY_TICKETS
+ offline_implications = "Admin tickets will no longer be marked as stale. No immediate action is needed."
+ flags = SS_BACKGROUND
+
var/span_class = "adminticket"
var/ticket_system_name = "Admin Tickets"
var/ticket_name = "Admin Ticket"
var/close_rights = R_ADMIN
+ var/rights_needed = R_ADMIN | R_MOD
+
+ /// The name of the other ticket type to convert to
+ var/other_ticket_name = "Mentor"
+ /// Which permission to look for when seeing if there is staff available for the other ticket type
+ var/other_ticket_permission = R_MENTOR
var/list/close_messages
- init_order = INIT_ORDER_TICKETS
- wait = 300
- priority = FIRE_PRIORITY_TICKETS
-
- flags = SS_BACKGROUND
-
var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized
var/ticketCounter = 1
/datum/controller/subsystem/tickets/Initialize()
close_messages = list("- [ticket_name] Rejected! -",
- "Please try to be calm, clear, and descriptive in admin helps, do not assume the staff member has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.",
+ "Please try to be calm, clear, and descriptive in admin helps, do not assume the staff member has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.",
"Your [ticket_name] has now been closed.")
return ..()
@@ -112,10 +118,38 @@ SUBSYSTEM_DEF(tickets)
message_staff("[usr.client] / ([usr]) resolved [ticket_name] number [N]")
to_chat_safe(returnClient(N), "Your [ticket_name] has now been resolved.")
return TRUE
-
+
+/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId)
+ if(!check_rights(rights_needed))
+ return
+ if(alert("Are you sure to convert this ticket to an '[other_ticket_name]' ticket?",,"Yes","No") != "Yes")
+ return
+ if(!other_ticket_system_staff_check())
+ return
+ var/datum/ticket/T = allTickets[ticketId]
+ convert_ticket(T)
+
+/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check()
+ var/list/staff = staff_countup(other_ticket_permission)
+ if(!staff[1])
+ if(alert("No active staff online to answer the ticket. Are you sure you want to convert the ticket?",, "No", "Yes") != "Yes")
+ return FALSE
+ return TRUE
+
+/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T)
+ T.ticketState = TICKET_CLOSED
+ var/client/C = usr.client
+ to_chat_safe(T.clientName, list("[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.",\
+ "Be sure to use the correct type of help next time!"))
+ message_staff("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.")
+ log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.")
+ create_other_system_ticket(T)
+
+/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T)
+ SSmentor_tickets.newTicket(T.clientName, T.content, T.title)
/datum/controller/subsystem/tickets/proc/autoRespond(N)
- if(!check_rights(R_ADMIN|R_MOD))
+ if(!check_rights(rights_needed))
return
var/datum/ticket/T = allTickets[N]
@@ -124,19 +158,20 @@ SUBSYSTEM_DEF(tickets)
if(alert(usr, "[T.ticketState == TICKET_OPEN ? "Another admin appears to already be handling this." : "This ticket is already marked as closed or resolved"] Are you sure you want to continue?", "Confirmation", "Yes", "No") != "Yes")
return
T.assignStaff(C)
-
- var/response_phrases = list("Thanks" = "Thanks, have a Paradise day!",
+
+ var/response_phrases = list("Thanks" = "Thanks, have a Paradise day!",
"Handling It" = "The issue is being looked into, thanks.",
"Already Resolved" = "The problem has been resolved already.",
"Mentorhelp" = "Please redirect your question to Mentorhelp, as they are better experienced with these types of questions.",
"Happens Again" = "Thanks, let us know if it continues to happen.",
- "Clear Cache" = "To fix a blank screen, please leave the game and clear your Byond Cache. To clear your Byond Cache, there is a Settings icon in the top right of the launcher. After you click that, go into the Games tab and hit the Clear Cache button. If the issue persists a few minutes after rejoining and doing this, please adminhelp again and state you cleared your cache." ,
+ "Github Issue Report" = "To report a bug, please go to our Github page. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends.",
+ "Clear Cache" = "To fix a blank screen, go to the 'Special Verbs' tab and press 'Reload UI Resources'. If that fails, clear your BYOND cache (instructions provided with 'Reload UI Resources'). If that still fails, please adminhelp again, stating you have already done the following." ,
"IC Issue" = "This is an In Character (IC) issue and will not be handled by admins. You could speak to Security, Internal Affairs, a Departmental Head, Nanotrasen Representetive, or any other relevant authority currently on station.",
"Reject" = "Reject",
"Man Up" = "Man Up",
"Appeal on the Forums" = "Appealing a ban must occur on the forums. Privately messaging, or adminhelping about your ban will not resolve it. To appeal your ban, please head to [config.banappeals]"
)
-
+
var/sorted_responses = list()
for(var/key in response_phrases) //build a new list based on the short descriptive keys of the master list so we can send this as the input instead of the full paragraphs to the admin choosing which autoresponse
sorted_responses += key
@@ -156,14 +191,17 @@ SUBSYSTEM_DEF(tickets)
resolveTicket(N)
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ")
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
+ if("Mentorhelp")
+ convert_ticket(T)
else
var/msg_sound = sound('sound/effects/adminhelp.ogg')
SEND_SOUND(returnClient(N), msg_sound)
- to_chat(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
+ to_chat_safe(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
T.lastStaffResponse = "Autoresponse: [message_key]"
resolveTicket(N)
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
+
//Set ticket state with key N to closed
/datum/controller/subsystem/tickets/proc/closeTicket(N)
var/datum/ticket/T = allTickets[N]
@@ -351,7 +389,7 @@ UI STUFF
dat += "| [T.content[i]] | "
dat += "
"
- dat += "Re-Open[check_rights(R_ADMIN|R_MOD, 0) ? "Auto": ""]Resolve
"
+ dat += "Re-Open[check_rights(rights_needed, 0) ? "Auto": ""]Resolve
"
if(!T.staffAssigned)
dat += "No staff member assigned to this [ticket_name] - Take Ticket "
@@ -366,6 +404,7 @@ UI STUFF
dat += "
"
dat += "Close Ticket"
+ dat += "Convert Ticket"
var/datum/browser/popup = new(user, "[ticket_system_name]detail", "[ticket_system_name] #[T.ticketNum]", 1000, 600)
popup.set_content(dat)
@@ -447,7 +486,6 @@ UI STUFF
return
if(closeTicket(indexNum))
showDetailUI(usr, indexNum)
-
if(href_list["detailreopen"])
var/indexNum = text2num(href_list["detailreopen"])
@@ -468,6 +506,10 @@ UI STUFF
var/indexNum = text2num(href_list["autorespond"])
autoRespond(indexNum)
+ if(href_list["convert_ticket"])
+ var/indexNum = text2num(href_list["convert_ticket"])
+ convert_to_other_ticket(indexNum)
+
if(href_list["resolveall"])
if(ticket_system_name == "Mentor Tickets")
usr.client.resolveAllMentorTickets()
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index 7bb6a496274..53d77e8fad6 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(timer)
if(timer.timeToRun < head_offset)
bucket_resolution = null //force bucket recreation
- CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ stack_trace("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if(timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
@@ -172,7 +172,7 @@ SUBSYSTEM_DEF(timer)
if(timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
bucket_resolution = null //force bucket recreation
- CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ stack_trace("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if(timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
spent += timer
diff --git a/code/modules/fancytitle/fancytitle.dm b/code/controllers/subsystem/titlescreen.dm
similarity index 85%
rename from code/modules/fancytitle/fancytitle.dm
rename to code/controllers/subsystem/titlescreen.dm
index d1cf08ff58c..e597f74e05f 100644
--- a/code/modules/fancytitle/fancytitle.dm
+++ b/code/controllers/subsystem/titlescreen.dm
@@ -1,4 +1,9 @@
-/hook/startup/proc/setup_title_screen()
+SUBSYSTEM_DEF(title)
+ name = "Title Screen"
+ flags = SS_NO_FIRE
+ init_order = INIT_ORDER_TITLE
+
+/datum/controller/subsystem/title/Initialize()
var/list/provisional_title_screens = flist("config/title_screens/images/")
var/list/title_screens = list()
var/use_rare_screens = prob(1)
@@ -29,5 +34,5 @@
for(var/turf/unsimulated/wall/splashscreen/splash in world)
splash.icon = icon
- return TRUE
- return FALSE
+
+ return ..()
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index b0110258da7..0b91e6b3467 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -367,7 +367,7 @@ SUBSYSTEM_DEF(vote)
var/votedesc = capitalize(mode)
if(mode == "custom")
votedesc += " ([question])"
- admin_log_and_message_admins("cancelled the running [votedesc] vote.")
+ log_and_message_admins("cancelled the running [votedesc] vote.")
reset()
if("toggle_restart")
if(admin)
diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm
index bf405444110..30186f80ad1 100644
--- a/code/controllers/subsystem/weather.dm
+++ b/code/controllers/subsystem/weather.dm
@@ -57,7 +57,6 @@ SUBSYSTEM_DEF(weather)
break
if(!ispath(weather_datum_type, /datum/weather))
CRASH("run_weather called with invalid weather_datum_type: [weather_datum_type || "null"]")
- return
if(isnull(z_levels))
z_levels = levels_by_trait(initial(weather_datum_type.target_trait))
@@ -65,7 +64,6 @@ SUBSYSTEM_DEF(weather)
z_levels = list(z_levels)
else if(!islist(z_levels))
CRASH("run_weather called with invalid z_levels: [z_levels || "null"]")
- return
var/datum/weather/W = new weather_datum_type(z_levels)
W.telegraph()
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index 3eaed85c303..8d02529ae6b 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -7,7 +7,7 @@
set name = "Restart Controller"
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
- if(!holder)
+ if(!check_rights(R_DEBUG))
return
switch(controller)
if("Master")
@@ -20,13 +20,14 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI",
- "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires",
+ "Cameras", "Garbage", "Event", "Nano", "Vote", "Fires",
"Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
- if(!holder) return
+ if(!check_rights(R_DEBUG))
+ return
switch(controller)
if("failsafe")
debug_variables(Failsafe)
@@ -64,9 +65,6 @@
if("Event")
debug_variables(SSevents)
feedback_add_details("admin_verb","DEvent")
- if("Alarm")
- debug_variables(SSalarms)
- feedback_add_details("admin_verb", "DAlarm")
if("Nano")
debug_variables(SSnanoui)
feedback_add_details("admin_verb","DNano")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index b7af84e9ddf..11ab496d386 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -190,9 +190,6 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
-/datum/action/item_action/toggle_headphones
- name = "Toggle Headphones"
-
/datum/action/item_action/toggle_helmet_light
name = "Toggle Helmet Light"
@@ -232,19 +229,6 @@
button.name = name
..()
-/datum/action/item_action/synthswitch
- name = "Change Synthesizer Instrument"
- desc = "Change the type of instrument your synthesizer is playing as."
-
-/datum/action/item_action/synthswitch/Trigger()
- if(istype(target, /obj/item/instrument/piano_synth))
- var/obj/item/instrument/piano_synth/synth = target
- var/chosen = input("Choose the type of instrument you want to use", "Instrument Selection", "piano") as null|anything in synth.insTypes
- if(!synth.insTypes[chosen])
- return
- return synth.changeInstrument(chosen)
- return ..()
-
/datum/action/item_action/vortex_recall
name = "Vortex Recall"
desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time. If the beacon is still attached, will detach it."
@@ -257,6 +241,9 @@
return 0
return ..()
+/datum/action/item_action/change_headphones_song
+ name = "Change Headphones Song"
+
/datum/action/item_action/toggle
/datum/action/item_action/toggle/New(Target)
@@ -339,7 +326,7 @@
/datum/action/item_action/remove_tape/Trigger(attack_self = FALSE)
if(..())
- GET_COMPONENT_FROM(DT, /datum/component/ducttape, target)
+ var/datum/component/ducttape/DT = target.GetComponent(/datum/component/ducttape)
DT.remove_tape(target, usr)
/datum/action/item_action/toggle_jetpack
diff --git a/code/datums/armor.dm b/code/datums/armor.dm
new file mode 100644
index 00000000000..0de7dde1ee7
--- /dev/null
+++ b/code/datums/armor.dm
@@ -0,0 +1,69 @@
+#define ARMORID "armor-[melee]-[bullet]-[laser]-[energy]-[bomb]-[bio]-[rad]-[fire]-[acid]-[magic]"
+
+/proc/getArmor(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0)
+ . = locate(ARMORID)
+ if (!.)
+ . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic)
+
+/datum/armor
+ var/melee
+ var/bullet
+ var/laser
+ var/energy
+ var/bomb
+ var/bio
+ var/rad
+ var/fire
+ var/acid
+ var/magic
+
+/datum/armor/New(melee_value = 0, bullet_value = 0, laser_value = 0, energy_value = 0, bomb_value = 0, bio_value = 0, rad_value = 0, fire_value = 0, acid_value = 0, magic_value = 0)
+ melee = melee_value
+ bullet = bullet_value
+ laser = laser_value
+ energy = energy_value
+ bomb = bomb_value
+ bio = bio_value
+ rad = rad_value
+ fire = fire_value
+ acid = acid_value
+ magic = magic_value
+ tag = ARMORID
+
+/datum/armor/proc/modifyRating(melee_value = 0, bullet_value = 0, laser_value = 0, energy_value = 0, bomb_value = 0, bio_value = 0, rad_value = 0, fire_value = 0, acid_value = 0, magic_value = 0)
+ return getArmor(melee + melee_value, bullet + bullet_value, laser + laser_value, energy + energy_value, bomb + bomb_value, bio + bio_value, rad + rad_value, fire + fire_value, acid + acid_value, magic + magic_value)
+
+/datum/armor/proc/modifyAllRatings(modifier = 0)
+ return getArmor(melee + modifier, bullet + modifier, laser + modifier, energy + modifier, bomb + modifier, bio + modifier, rad + modifier, fire + modifier, acid + modifier, magic + modifier)
+
+/datum/armor/proc/setRating(melee_value, bullet_value, laser_value, energy_value, bomb_value, bio_value, rad_value, fire_value, acid_value, magic_value)
+ return getArmor((isnull(melee_value) ? melee : melee_value),\
+ (isnull(bullet_value) ? bullet : bullet_value),\
+ (isnull(laser_value) ? laser : laser_value),\
+ (isnull(energy_value) ? energy : energy_value),\
+ (isnull(bomb_value) ? bomb : bomb_value),\
+ (isnull(bio_value) ? bio : bio_value),\
+ (isnull(rad_value) ? rad : rad_value),\
+ (isnull(fire_value) ? fire : fire_value),\
+ (isnull(acid_value) ? acid : acid_value),\
+ (isnull(magic_value) ? magic : magic_value))
+
+/datum/armor/proc/getRating(rating)
+ return vars[rating]
+
+/datum/armor/proc/getList()
+ return list("melee" = melee, "bullet" = bullet, "laser" = laser, "energy" = energy, "bomb" = bomb, "bio" = bio, "rad" = rad, "fire" = fire, "acid" = acid, "magic" = magic)
+
+/datum/armor/proc/attachArmor(datum/armor/AA)
+ return getArmor(melee + AA.melee, bullet + AA.bullet, laser + AA.laser, energy + AA.energy, bomb + AA.bomb, bio + AA.bio, rad + AA.rad, fire + AA.fire, acid + AA.acid, magic + AA.magic)
+
+/datum/armor/proc/detachArmor(datum/armor/AA)
+ return getArmor(melee - AA.melee, bullet - AA.bullet, laser - AA.laser, energy - AA.energy, bomb - AA.bomb, bio - AA.bio, rad - AA.rad, fire - AA.fire, acid - AA.acid, magic - AA.magic)
+
+/datum/armor/vv_edit_var(var_name, var_value)
+ if (var_name == NAMEOF(src, tag))
+ return FALSE
+ . = ..()
+ tag = ARMORID // update tag in case armor values were edited
+
+#undef ARMORID
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 86f816662c0..3b5c9d2f5fb 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -99,11 +99,11 @@
//Position the effect so the beam is one continous line
var/a
if(abs(Pixel_x)>32)
- a = Pixel_x > 0 ? round(Pixel_x/32) : Ceiling(Pixel_x/32)
+ a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
X.x += a
Pixel_x %= 32
if(abs(Pixel_y)>32)
- a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32)
+ a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
X.y += a
Pixel_y %= 32
@@ -131,6 +131,5 @@
/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
- spawn(0)
- newbeam.Start()
+ INVOKE_ASYNC(newbeam, /datum/beam.proc/Start)
return newbeam
diff --git a/code/datums/cache/crew.dm b/code/datums/cache/crew.dm
index 21ec5ebcc79..f92bab9cf7a 100644
--- a/code/datums/cache/crew.dm
+++ b/code/datums/cache/crew.dm
@@ -4,7 +4,7 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
cache_data = list()
..()
-/datum/repository/crew/proc/health_data(var/turf/T)
+/datum/repository/crew/proc/health_data(turf/T)
var/list/crewmembers = list()
if(!T)
return crewmembers
@@ -18,50 +18,40 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
if(world.time < cache_entry.timestamp)
return cache_entry.data
- var/tracked = scan()
- for(var/obj/item/clothing/under/C in tracked)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
+ var/obj/item/clothing/under/C = H.w_uniform
+ if(!C || C.sensor_mode == SUIT_SENSOR_OFF || !C.has_sensor)
+ continue
var/turf/pos = get_turf(C)
- if((C) && (C.has_sensor) && (pos) && (T && pos.z == T.z) && (C.sensor_mode != SUIT_SENSOR_OFF))
- if(istype(C.loc, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = C.loc
- if(H.w_uniform != C)
- continue
+ if(!T || pos.z != T.z)
+ continue
+ var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]")
- var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]")
+ crewmemberData["sensor_type"] = C.sensor_mode
+ crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown")
+ crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job")
+ crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job")
- crewmemberData["sensor_type"] = C.sensor_mode
- crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown")
- crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job")
- crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job")
+ if(C.sensor_mode >= SUIT_SENSOR_BINARY)
+ crewmemberData["dead"] = H.stat > UNCONSCIOUS
- if(C.sensor_mode >= SUIT_SENSOR_BINARY)
- crewmemberData["dead"] = H.stat > UNCONSCIOUS
+ if(C.sensor_mode >= SUIT_SENSOR_VITAL)
+ crewmemberData["oxy"] = round(H.getOxyLoss(), 1)
+ crewmemberData["tox"] = round(H.getToxLoss(), 1)
+ crewmemberData["fire"] = round(H.getFireLoss(), 1)
+ crewmemberData["brute"] = round(H.getBruteLoss(), 1)
- if(C.sensor_mode >= SUIT_SENSOR_VITAL)
- crewmemberData["oxy"] = round(H.getOxyLoss(), 1)
- crewmemberData["tox"] = round(H.getToxLoss(), 1)
- crewmemberData["fire"] = round(H.getFireLoss(), 1)
- crewmemberData["brute"] = round(H.getBruteLoss(), 1)
+ if(C.sensor_mode >= SUIT_SENSOR_TRACKING)
+ var/area/A = get_area(H)
+ crewmemberData["area"] = sanitize(A.name)
+ crewmemberData["x"] = pos.x
+ crewmemberData["y"] = pos.y
- if(C.sensor_mode >= SUIT_SENSOR_TRACKING)
- var/area/A = get_area(H)
- crewmemberData["area"] = sanitize(A.name)
- crewmemberData["x"] = pos.x
- crewmemberData["y"] = pos.y
-
- crewmembers[++crewmembers.len] = crewmemberData
+ crewmembers[++crewmembers.len] = crewmemberData
crewmembers = sortByKey(crewmembers, "name")
cache_entry.timestamp = world.time + 5 SECONDS
cache_entry.data = crewmembers
return crewmembers
-
-/datum/repository/crew/proc/scan()
- var/list/tracked = list()
- for(var/mob/living/carbon/human/H in GLOB.mob_list)
- if(istype(H.w_uniform, /obj/item/clothing/under))
- var/obj/item/clothing/under/C = H.w_uniform
- if(C.has_sensor)
- tracked |= C
- return tracked
diff --git a/code/datums/components/README.md b/code/datums/components/README.md
index 9d06a255dd7..03f7d3a5875 100644
--- a/code/datums/components/README.md
+++ b/code/datums/components/README.md
@@ -2,134 +2,8 @@
## Concept
-Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SEND_SIGNAL` call. Now every component that want's to can also know about this happening.
+Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening.
-### In the code
+See [this thread](https://tgstation13.org/phpBB/viewtopic.php?f=5&t=22674) for an introduction to the system as a whole.
-#### Slippery things
-
-At the time of this writing, every object that is slippery overrides atom/Crossed does some checks, then slips the mob. Instead of all those Crossed overrides they could add a slippery component to all these objects. And have the checks in one proc that is run by the Crossed event
-
-#### Powercells
-
-A lot of objects have powercells. The `get_cell()` proc was added to give generic access to the cell var if it had one. This is just a specific use case of `GetComponent()`
-
-#### Radios
-
-The radio object as it is should not exist, given that more things use the _concept_ of radios rather than the object itself. The actual function of the radio can exist in a component which all the things that use it (Request consoles, actual radios, the SM shard) can add to themselves.
-
-#### Standos
-
-Stands have a lot of procs which mimic mob procs. Rather than inserting hooks for all these procs in overrides, the same can be accomplished with signals
-
-## API
-
-### Defines
-
-1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned. This will be noted in the runtime logs
-
-### Vars
-
-1. `/datum/var/list/datum_components` (private)
- * Lazy associated list of type -> component/list of components.
-1. `/datum/var/list/comp_lookup` (private)
- * Lazy associated list of signal -> registree/list of registrees
-1. `/datum/var/list/signal_procs` (private)
- * Associated lazy list of signals -> `/datum/callback`s that will be run when the parent datum receives that signal
-1. `/datum/var/signal_enabled` (protected, boolean)
- * If the datum is signal enabled. If not, it will not react to signals
- * `FALSE` by default, set to `TRUE` when a signal is registered
-1. `/datum/component/var/dupe_mode` (protected, enum)
- * How duplicate component types are handled when added to the datum.
- * `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
- * `COMPONENT_DUPE_ALLOWED`: The components will be treated as separate, `GetComponent()` will return the first added
- * `COMPONENT_DUPE_UNIQUE`: New component will be deleted, old component will first have `/datum/component/proc/InheritComponent(datum/component/new, TRUE)` on it
- * `COMPONENT_DUPE_UNIQUE_PASSARGS`: New component will never exist and instead its initialization arguments will be passed on to the old component.
-1. `/datum/component/var/dupe_type` (protected, type)
- * Definition of a duplicate component type
- * `null` means exact match on `type` (default)
- * Any other type means that and all subtypes
-1. `/datum/component/var/datum/parent` (protected, read-only)
- * The datum this component belongs to
- * Never `null` in child procs
-1. `report_signal_origin` (protected, boolean)
- * If `TRUE`, will invoke the callback when signalled with the signal type as the first argument.
- * `FALSE` by default.
-
-### Procs
-
-1. `/datum/proc/GetComponent(component_type(type)) -> datum/component?` (public, final)
- * Returns a reference to a component of component_type if it exists in the datum, null otherwise
-1. `/datum/proc/GetComponents(component_type(type)) -> list` (public, final)
- * Returns a list of references to all components of component_type that exist in the datum
-1. `/datum/proc/GetExactComponent(component_type(type)) -> datum/component?` (public, final)
- * Returns a reference to a component whose type MATCHES component_type if that component exists in the datum, null otherwise
-1. `GET_COMPONENT(varname, component_type)` OR `GET_COMPONENT_FROM(varname, component_type, src)`
- * Shorthand for `var/component_type/varname = src.GetComponent(component_type)`
-1. `SEND_SIGNAL(target, sigtype, ...)` (public, final)
- * Use to send signals to target datum
- * Extra arguments are to be specified in the signal definition
- * Returns a bitflag with signal specific information assembled from all activated components
- * Arguments are packaged in a list and handed off to _SendSignal()
-1. `/datum/proc/AddComponent(component_type(type), ...) -> datum/component` (public, final)
- * Creates an instance of `component_type` in the datum and passes `...` to its `Initialize()` call
- * Sends the `COMSIG_COMPONENT_ADDED` signal to the datum
- * All components a datum owns are deleted with the datum
- * Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
- * If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
- * Properly handles duplicate situations based on the `dupe_mode` var
-1. `/datum/proc/LoadComponent(component_type(type), ...) -> datum/component` (public, final)
- * Equivalent to calling `GetComponent(component_type)` where, if the result would be `null`, returns `AddComponent(component_type, ...)` instead
-1. `/datum/proc/ComponentActivated(datum/component/C)` (abstract, async)
- * Called on a component's `parent` after a signal received causes it to activate. `src` is the parameter
- * Will only be called if a component's callback returns `TRUE`
-1. `/datum/proc/TakeComponent(datum/component/C)` (public, final)
- * Properly transfers ownership of a component from one datum to another
- * Signals `COMSIG_COMPONENT_REMOVING` on the parent
- * Called on the datum you want to own the component with another datum's component
-1. `/datum/proc/_SendSignal(signal, list/arguments)` (private, final)
- * Handles most of the actual signaling procedure
- * Will runtime if used on datums with an empty component list
-1. `/datum/proc/RegisterSignal(datum/target, signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final)
- * If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments
- * Makes the datum listen for the specified `signal` on it's `parent` datum.
- * When that signal is received `proc_ref` will be called on the component, along with associated arguments
- * Example proc ref: `.proc/OnEvent`
- * If a previous registration is overwritten by the call, a runtime occurs. Setting `override` to TRUE prevents this
- * These callbacks run asyncronously
- * Returning `TRUE` from these callbacks will trigger a `TRUE` return from the `SendSignal()` that initiated it
-1. `/datum/component/New(datum/parent, ...)` (private, final)
- * Runs internal setup for the component
- * Extra arguments are passed to `Initialize()`
-1. `/datum/component/Initialize(...)` (abstract, no-sleep)
- * Called by `New()` with the same argments excluding `parent`
- * Component does not exist in `parent`'s `datum_components` list yet, although `parent` is set and may be used
- * Signals will not be received while this function is running
- * Component may be deleted after this function completes without being attached
- * Do not call `qdel(src)` from this function
-1. `/datum/component/Destroy(force(bool), silent(bool))` (virtual, no-sleep)
- * Sends the `COMSIG_COMPONENT_REMOVING` signal to the parent datum if the `parent` isn't being qdeleted
- * Properly removes the component from `parent` and cleans up references
- * Setting `force` makes it not check for and remove the component from the parent
- * Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal
-1. `/datum/component/proc/InheritComponent(datum/component/C, i_am_original(boolean))` (abstract, no-sleep)
- * Called on a component when a component of the same type was added to the same parent
- * See `/datum/component/var/dupe_mode`
- * `C`'s type will always be the same of the called component
-1. `/datum/component/proc/AfterComponentActivated()` (abstract, async)
- * Called on a component that was activated after it's `parent`'s `ComponentActivated()` is called
-1. `/datum/component/proc/OnTransfer(datum/new_parent)` (abstract, no-sleep)
- * Called before `new_parent` is assigned to `parent` in `TakeComponent()`
- * Allows the component to react to ownership transfers
-1. `/datum/component/proc/_RemoveFromParent()` (private, final)
- * Clears `parent` and removes the component from it's component list
-1. `/datum/component/proc/_JoinParent` (private, final)
- * Tries to add the component to it's `parent`s `datum_components` list
-1. `/datum/component/proc/RegisterWithParent` (abstract, no-sleep)
- * Used to register the signals that should be on the `parent` object
- * Use this if you plan on the component transfering between parents
-1. `/datum/component/proc/UnregisterFromParent` (abstract, no-sleep)
- * Counterpart to `RegisterWithParent()`
- * Used to unregister the signals that should only be on the `parent` object
-
-### See/Define signals and their arguments in __DEFINES\components.dm
+### See/Define signals and their arguments in [__DEFINES\dcs\signals.dm](../../__DEFINES/dcs/signals.dm)
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index e733ca78d2b..f895b8d81b1 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -1,21 +1,87 @@
+/**
+ * # Component
+ *
+ * The component datum
+ *
+ * A component should be a single standalone unit
+ * of functionality, that works by receiving signals from it's parent
+ * object to provide some single functionality (i.e a slippery component)
+ * that makes the object it's attached to cause people to slip over.
+ * Useful when you want shared behaviour independent of type inheritance
+ */
/datum/component
+ /**
+ * Defines how duplicate existing components are handled when added to a datum
+ *
+ * See [COMPONENT_DUPE_*][COMPONENT_DUPE_ALLOWED] definitions for available options
+ */
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
+
+ /**
+ * The type to check for duplication
+ *
+ * `null` means exact match on `type` (default)
+ *
+ * Any other type means that and all subtypes
+ */
var/dupe_type
+
+ /// The datum this components belongs to
var/datum/parent
- //only set to true if you are able to properly transfer this component
- //At a minimum RegisterWithParent and UnregisterFromParent should be used
- //Make sure you also implement PostTransfer for any post transfer handling
+
+ /**
+ * Only set to true if you are able to properly transfer this component
+ *
+ * At a minimum [RegisterWithParent][/datum/component/proc/RegisterWithParent] and [UnregisterFromParent][/datum/component/proc/UnregisterFromParent] should be used
+ *
+ * Make sure you also implement [PostTransfer][/datum/component/proc/PostTransfer] for any post transfer handling
+ */
var/can_transfer = FALSE
-/datum/component/New(datum/P, ...)
- parent = P
- var/list/arguments = args.Copy(2)
+/**
+ * Create a new component.
+ *
+ * Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize]
+ *
+ * Arguments:
+ * * datum/P the parent datum this component reacts to signals from
+ */
+/datum/component/New(list/raw_args)
+ parent = raw_args[1]
+ var/list/arguments = raw_args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
+ stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
qdel(src, TRUE, TRUE)
- CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
+ return
- _JoinParent(P)
+ _JoinParent(parent)
+/**
+ * Called during component creation with the same arguments as in new excluding parent.
+ *
+ * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
+ */
+/datum/component/proc/Initialize(...)
+ return
+
+/**
+ * Properly removes the component from `parent` and cleans up references
+ *
+ * Arguments:
+ * * force - makes it not check for and remove the component from the parent
+ * * silent - deletes the component without sending a [COMSIG_COMPONENT_REMOVING] signal
+ */
+/datum/component/Destroy(force=FALSE, silent=FALSE)
+ if(!force && parent)
+ _RemoveFromParent()
+ if(!silent)
+ SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
+ parent = null
+ return ..()
+
+/**
+ * Internal proc to handle behaviour of components when joining a parent
+ */
/datum/component/proc/_JoinParent()
var/datum/P = parent
//lazy init the parent's dc list
@@ -51,21 +117,9 @@
RegisterWithParent()
-// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
-/datum/component/proc/RegisterWithParent()
- return
-
-/datum/component/proc/Initialize(...)
- return
-
-/datum/component/Destroy(force=FALSE, silent=FALSE)
- if(!force && parent)
- _RemoveFromParent()
- if(!silent)
- SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
- parent = null
- return ..()
-
+/**
+ * Internal proc to handle behaviour when being removed from a parent
+ */
/datum/component/proc/_RemoveFromParent()
var/datum/P = parent
var/list/dc = P.datum_components
@@ -84,10 +138,41 @@
UnregisterFromParent()
+/**
+ * Register the component with the parent object
+ *
+ * Use this proc to register with your parent object
+ *
+ * Overridable proc that's called when added to a new parent
+ */
+/datum/component/proc/RegisterWithParent()
+ return
+
+/**
+ * Unregister from our parent object
+ *
+ * Use this proc to unregister from your parent object
+ *
+ * Overridable proc that's called when removed from a parent
+ * *
+ */
/datum/component/proc/UnregisterFromParent()
return
-/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proc_or_callback, override = FALSE)
+/**
+ * Register to listen for a signal from the passed in target
+ *
+ * This sets up a listening relationship such that when the target object emits a signal
+ * the source datum this proc is called upon, will recieve a callback to the given proctype
+ * Return values from procs registered must be a bitfield
+ *
+ * Arguments:
+ * * datum/target The target to listen for signals from
+ * * sig_type_or_types Either a string signal name, or a list of signal names (strings)
+ * * proctype The proc to call back when the signal is emitted
+ * * override If a previous registration exists you must explicitly set this
+ */
+/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
@@ -100,15 +185,12 @@
if(!lookup)
target.comp_lookup = lookup = list()
- if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
- proc_or_callback = CALLBACK(src, proc_or_callback)
-
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
for(var/sig_type in sig_types)
if(!override && procs[target][sig_type])
stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
- procs[target][sig_type] = proc_or_callback
+ procs[target][sig_type] = proctype
if(!lookup[sig_type]) // Nothing has registered here yet
lookup[sig_type] = src
@@ -122,6 +204,17 @@
signal_enabled = TRUE
+/**
+ * Stop listening to a given signal from target
+ *
+ * Breaks the relationship between target and source datum, removing the callback when the signal fires
+ *
+ * Doesn't care if a registration exists or not
+ *
+ * Arguments:
+ * * datum/target Datum to stop listening to signals from
+ * * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
+ */
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
@@ -129,6 +222,8 @@
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
+ if(!signal_procs[target][sig])
+ continue
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
@@ -151,41 +246,96 @@
if(!signal_procs[target].len)
signal_procs -= target
+/**
+ * Called on a component when a component of the same type was added to the same parent
+ *
+ * See [/datum/component/var/dupe_mode]
+ *
+ * `C`'s type will always be the same of the called component
+ */
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
+
+/**
+ * Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE]
+ *
+ * See [/datum/component/var/dupe_mode]
+ *
+ * `C`'s type will always be the same of the called component
+ *
+ * return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
+ */
+/datum/component/proc/CheckDupeComponent(datum/component/C, ...)
+ return
+
+
+/**
+ * Callback Just before this component is transferred
+ *
+ * Use this to do any special cleanup you might need to do before being deregged from an object
+ */
/datum/component/proc/PreTransfer()
return
+/**
+ * Callback Just after a component is transferred
+ *
+ * Use this to do any special setup you need to do after being moved to a new object
+ *
+ * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
+ */
/datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
+/**
+ * Internal proc to create a list of our type and all parent types
+ */
/datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick
var/current_type = parent_type
. = list(our_type, current_type)
//and since most components are root level + 1, this won't even have to run
- while(current_type != /datum/component)
+ while (current_type != /datum/component)
current_type = type2parent(current_type)
. += current_type
+/**
+ * Internal proc to handle most all of the signaling procedure
+ *
+ * Will runtime if used on datums with an empty component list
+ *
+ * Use the [SEND_SIGNAL] define instead
+ */
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = comp_lookup[sigtype]
if(!length(target))
var/datum/C = target
if(!C.signal_enabled)
return NONE
- var/datum/callback/CB = C.signal_procs[src][sigtype]
- return CB.InvokeAsync(arglist(arguments))
+ var/proctype = C.signal_procs[src][sigtype]
+ return NONE | CallAsync(C, proctype, arguments)
. = NONE
for(var/I in target)
var/datum/C = I
if(!C.signal_enabled)
continue
- var/datum/callback/CB = C.signal_procs[src][sigtype]
- . |= CB.InvokeAsync(arglist(arguments))
+ var/proctype = C.signal_procs[src][sigtype]
+ . |= CallAsync(C, proctype, arguments)
-/datum/proc/GetComponent(c_type)
+// The type arg is casted so initial works, you shouldn't be passing a real instance into this
+/**
+ * Return any component assigned to this datum of the given type
+ *
+ * This will throw an error if it's possible to have more than one component of that type on the parent
+ *
+ * Arguments:
+ * * datum/component/c_type The typepath of the component you want to get a reference to
+ */
+/datum/proc/GetComponent(datum/component/c_type)
+ RETURN_TYPE(c_type)
+ if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
+ stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
return null
@@ -193,7 +343,19 @@
if(length(.))
return .[1]
-/datum/proc/GetExactComponent(c_type)
+// The type arg is casted so initial works, you shouldn't be passing a real instance into this
+/**
+ * Return any component assigned to this datum of the exact given type
+ *
+ * This will throw an error if it's possible to have more than one component of that type on the parent
+ *
+ * Arguments:
+ * * datum/component/c_type The typepath of the component you want to get a reference to
+ */
+/datum/proc/GetExactComponent(datum/component/c_type)
+ RETURN_TYPE(c_type)
+ if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
+ stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
return null
@@ -205,6 +367,12 @@
return C
return null
+/**
+ * Get all components of a given type that are attached to this datum
+ *
+ * Arguments:
+ * * c_type The component type path
+ */
/datum/proc/GetComponents(c_type)
var/list/dc = datum_components
if(!dc)
@@ -213,7 +381,19 @@
if(!length(.))
return list(.)
-/datum/proc/AddComponent(new_type, ...)
+/**
+ * Creates an instance of `new_type` in the datum and attaches to it as parent
+ *
+ * Sends the [COMSIG_COMPONENT_ADDED] signal to the datum
+ *
+ * Returns the component that was created. Or the old component in a dupe situation where [COMPONENT_DUPE_UNIQUE] was set
+ *
+ * If this tries to add a component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
+ *
+ * Properly handles duplicate situations based on the `dupe_mode` var
+ */
+/datum/proc/_AddComponent(list/raw_args)
+ var/new_type = raw_args[1]
var/datum/component/nt = new_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
@@ -228,7 +408,7 @@
new_comp = nt
nt = new_comp.type
- args[1] = src
+ raw_args[1] = src
if(dm != COMPONENT_DUPE_ALLOWED)
if(!dt)
@@ -239,37 +419,62 @@
switch(dm)
if(COMPONENT_DUPE_UNIQUE)
if(!new_comp)
- new_comp = new nt(arglist(args))
+ new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
old_comp.InheritComponent(new_comp, TRUE)
QDEL_NULL(new_comp)
if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp)
- new_comp = new nt(arglist(args))
+ new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
new_comp.InheritComponent(old_comp, FALSE)
QDEL_NULL(old_comp)
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp)
- var/list/arguments = args.Copy(2)
- old_comp.InheritComponent(null, TRUE, arguments)
+ var/list/arguments = raw_args.Copy(2)
+ arguments.Insert(1, null, TRUE)
+ old_comp.InheritComponent(arglist(arguments))
else
old_comp.InheritComponent(new_comp, TRUE)
+ if(COMPONENT_DUPE_SELECTIVE)
+ var/list/arguments = raw_args.Copy()
+ arguments[1] = new_comp
+ var/make_new_component = TRUE
+ for(var/i in GetComponents(new_type))
+ var/datum/component/C = i
+ if(C.CheckDupeComponent(arglist(arguments)))
+ make_new_component = FALSE
+ QDEL_NULL(new_comp)
+ break
+ if(!new_comp && make_new_component)
+ new_comp = new nt(raw_args)
else if(!new_comp)
- new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
+ new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
- new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
+ new_comp = new nt(raw_args) // Dupes are allowed, act like normal
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
return new_comp
return old_comp
+/**
+ * Get existing component of type, or create it and return a reference to it
+ *
+ * Use this if the item needs to exist at the time of this call, but may not have been created before now
+ *
+ * Arguments:
+ * * component_type The typepath of the component to create or return
+ * * ... additional arguments to be passed when creating the component if it does not exist
+ */
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type)
if(!.)
- return AddComponent(arglist(args))
+ return _AddComponent(args)
+/**
+ * Removes the component from parent, ends up with a null parent
+ */
/datum/component/proc/RemoveComponent()
if(!parent)
return
@@ -279,6 +484,14 @@
parent = null
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
+/**
+ * Transfer this component to another parent
+ *
+ * Component is taken from source datum
+ *
+ * Arguments:
+ * * datum/component/target Target datum to transfer to
+ */
/datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src)
return
@@ -295,6 +508,14 @@
if(target == AddComponent(target))
target._JoinParent()
+/**
+ * Transfer all components to target
+ *
+ * All components from source datum are taken
+ *
+ * Arguments:
+ * * /datum/target the target to move the components to
+ */
/datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components
if(!dc)
@@ -309,5 +530,8 @@
if(C.can_transfer)
target.TakeComponent(comps)
+/**
+ * Return the object that is the host of any UI's that this component has
+ */
/datum/component/nano_host()
return parent
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
index 3bd740973a3..fc781b288a9 100644
--- a/code/datums/components/caltrop.dm
+++ b/code/datums/components/caltrop.dm
@@ -42,7 +42,7 @@
if(!(flags & CALTROP_BYPASS_SHOES) && (H.shoes || feetCover))
return
- if((H.flying) || H.buckled)
+ if(H.flying || H.floating || H.buckled)
return
var/damage = rand(min_damage, max_damage)
diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm
index 6a0f686b13a..876cf0c0507 100644
--- a/code/datums/components/decal.dm
+++ b/code/datums/components/decal.dm
@@ -7,7 +7,7 @@
var/first_dir // This only stores the dir arg from init
-/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description, _alpha=255)
+/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable = CLEAN_GOD, _color, _layer = TURF_LAYER, _description, _alpha = 255)
if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha))
return COMPONENT_INCOMPATIBLE
first_dir = _dir
diff --git a/code/datums/components/ducttape.dm b/code/datums/components/ducttape.dm
index 98512589a46..49931db8edc 100644
--- a/code/datums/components/ducttape.dm
+++ b/code/datums/components/ducttape.dm
@@ -37,10 +37,10 @@
I.anchored = initial(I.anchored)
for(var/datum/action/item_action/remove_tape/RT in I.actions)
RT.Remove(user)
- RT.Destroy()
+ qdel(RT)
I.overlays.Cut(tape_overlay)
user.transfer_fingerprints_to(I)
- Destroy()
+ qdel(src)
/datum/component/ducttape/proc/afterattack(obj/item/I, atom/target, mob/user, proximity, params)
if(!proximity)
diff --git a/code/datums/components/edit_complainer.dm b/code/datums/components/edit_complainer.dm
new file mode 100644
index 00000000000..7910515cb45
--- /dev/null
+++ b/code/datums/components/edit_complainer.dm
@@ -0,0 +1,23 @@
+// This is just a bit of fun while making an example for global signal
+/datum/component/edit_complainer
+ var/list/say_lines
+
+/datum/component/edit_complainer/Initialize(list/text)
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ var/static/list/default_lines = list(
+ "CentComm's profligacy frays another thread.",
+ "Another tug at the weave.",
+ "Who knows when the stresses will finally shatter the form?",
+ "Even now a light shines through the cracks.",
+ "CentComm once more twists knowledge beyond its authority.",
+ "There is an uncertain air in the mansus.",
+ )
+ say_lines = text || default_lines
+
+ RegisterSignal(SSdcs, COMSIG_GLOB_VAR_EDIT, .proc/var_edit_react)
+
+/datum/component/edit_complainer/proc/var_edit_react(datum/source, list/arguments)
+ var/atom/movable/master = parent
+ master.atom_say(pick(say_lines))
diff --git a/code/datums/components/jestosterone.dm b/code/datums/components/jestosterone.dm
deleted file mode 100644
index f37f1f03efb..00000000000
--- a/code/datums/components/jestosterone.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-/datum/component/jestosterone
- var/mind_type //Is the affected mob a clown / mime?
-
-/datum/component/jestosterone/Initialize(mind_type_arg)
- mind_type = mind_type_arg
diff --git a/code/datums/components/label.dm b/code/datums/components/label.dm
index 26cb99c7e3c..c6d0c595ebb 100644
--- a/code/datums/components/label.dm
+++ b/code/datums/components/label.dm
@@ -32,12 +32,12 @@
This proc will fire after the parent is hit by a hand labeler which is trying to apply another label.
Since the parent already has a label, it will remove the old one from the parent's name, and apply the new one.
*/
-/datum/component/label/InheritComponent(datum/component/label/new_comp , i_am_original, list/arguments)
+/datum/component/label/InheritComponent(datum/component/label/new_comp , i_am_original, _label_name)
remove_label()
if(new_comp)
label_name = new_comp.label_name
else
- label_name = arguments[1]
+ label_name = _label_name
apply_label()
/**
diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm
new file mode 100644
index 00000000000..a7b81856468
--- /dev/null
+++ b/code/datums/components/slippery.dm
@@ -0,0 +1,56 @@
+/**
+ * # Slip Component
+ *
+ * This is a component that can be applied to any movable atom (mob or obj).
+ *
+ * While the atom has this component, any human mob that walks over it will have a chance to slip.
+ * Duration, tiles moved, and so on, depend on what variables are passed in when the component is added.
+ *
+ */
+/datum/component/slippery
+ /// Text that gets displayed in the slip proc, i.e. "user slips on [description]"
+ var/description
+ /// The amount of stun to apply after slip.
+ var/stun
+ /// The amount of weaken to apply after slip.
+ var/weaken
+ /// The chance that walking over the parent will slip you.
+ var/slip_chance
+ /// The amount of tiles someone will be moved after slip.
+ var/slip_tiles
+ /// TRUE If this slip can be avoided by walking.
+ var/walking_is_safe
+ /// FALSE if you want no slip shoes to make you immune to the slip
+ var/slip_always
+ /// The verb that players will see when someone slips on the parent. In the form of "You [slip_verb]ped on".
+ var/slip_verb
+
+/datum/component/slippery/Initialize(_description, _stun = 0, _weaken = 0, _slip_chance = 100, _slip_tiles = 0, _walking_is_safe = TRUE, _slip_always = FALSE, _slip_verb = "slip")
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ description = _description
+ stun = max(0, _stun)
+ weaken = max(0, _weaken)
+ slip_chance = max(0, _slip_chance)
+ slip_tiles = max(0, _slip_tiles)
+ walking_is_safe = _walking_is_safe
+ slip_always = _slip_always
+ slip_verb = _slip_verb
+
+/datum/component/slippery/RegisterWithParent()
+ RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Slip)
+
+/datum/component/slippery/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED))
+
+/**
+ Called whenever the parent recieves either the `MOVABLE_CROSSED` signal or the `ATOM_ENTERED` signal.
+
+ Calls the `victim`'s `slip()` proc with the component's variables as arguments.
+ Additionally calls the parent's `after_slip()` proc on the `victim`.
+*/
+/datum/component/slippery/proc/Slip(datum/source, mob/living/carbon/human/victim)
+ if(istype(victim) && !victim.flying && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb))
+ var/atom/movable/owner = parent
+ owner.after_slip(victim)
diff --git a/code/datums/components/spooky.dm b/code/datums/components/spooky.dm
new file mode 100644
index 00000000000..f5ee9c94666
--- /dev/null
+++ b/code/datums/components/spooky.dm
@@ -0,0 +1,58 @@
+/datum/component/spooky
+ var/too_spooky = TRUE //will it spawn a new instrument?
+
+/datum/component/spooky/Initialize()
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/spectral_attack)
+
+/datum/component/spooky/proc/spectral_attack(datum/source, mob/living/carbon/C, mob/user)
+ if(ishuman(user)) //this weapon wasn't meant for mortals.
+ var/mob/living/carbon/human/U = user
+ if(!istype(U.dna.species, /datum/species/skeleton))
+ U.adjustStaminaLoss(35) //Extra Damage
+ U.Jitter(35)
+ U.stuttering = 20
+ if(U.getStaminaLoss() > 95)
+ to_chat(U, "Your ears weren't meant for this spectral sound.")
+ spectral_change(U)
+ return
+
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if(istype(H.dna.species, /datum/species/skeleton))
+ return //undeads are unaffected by the spook-pocalypse.
+ C.Jitter(35)
+ C.stuttering = 20
+ if(!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman))
+ C.adjustStaminaLoss(25) //boneless humanoids don't lose the will to live
+ to_chat(C, "DOOT")
+ spectral_change(H)
+
+ else //the sound will spook monkeys.
+ C.Jitter(15)
+ C.stuttering = 20
+
+/datum/component/spooky/proc/spectral_change(mob/living/carbon/human/H, mob/user)
+ if((H.getStaminaLoss() > 95) && (!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman) && !istype(H.dna.species, /datum/species/skeleton)))
+ H.Stun(20)
+ H.set_species(/datum/species/skeleton)
+ H.visible_message("[H] has given up on life as a mortal.")
+ var/T = get_turf(H)
+ if(too_spooky)
+ if(prob(30))
+ new/obj/item/instrument/saxophone/spectral(T)
+ else if(prob(30))
+ new/obj/item/instrument/trumpet/spectral(T)
+ else if(prob(30))
+ new/obj/item/instrument/trombone/spectral(T)
+ else
+ to_chat(H, "The spooky gods forgot to ship your instrument. Better luck next unlife.")
+ to_chat(H, "You are the spooky skeleton!")
+ to_chat(H, "A new life and identity has begun. Help your fellow skeletons into bringing out the spooky-pocalypse. You haven't forgotten your past life, and are still beholden to past loyalties.")
+ change_name(H) //time for a new name!
+
+/datum/component/spooky/proc/change_name(mob/living/carbon/human/H)
+ var/t = stripped_input(H, "Enter your new skeleton name", H.real_name, null, MAX_NAME_LEN)
+ if(!t)
+ t = "spooky skeleton"
+ H.real_name = t
+ H.name = t
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 818e78e8d2e..f79439f2b3b 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -16,7 +16,7 @@
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), .proc/play_squeak)
- if(ismovableatom(parent))
+ if(ismovable(parent))
RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), .proc/play_squeak)
RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/play_squeak_crossed)
RegisterSignal(parent, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react)
@@ -67,6 +67,14 @@
var/obj/item/projectile/P = AM
if(P.original != parent)
return
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.flying)
+ return
+ if(isliving(AM))
+ var/mob/living/L = M
+ if(L.floating)
+ return
var/atom/current_parent = parent
if(isturf(current_parent.loc))
play_squeak()
diff --git a/code/datums/components/swarming.dm b/code/datums/components/swarming.dm
new file mode 100644
index 00000000000..16ddc66280e
--- /dev/null
+++ b/code/datums/components/swarming.dm
@@ -0,0 +1,55 @@
+/datum/component/swarming
+ var/offset_x = 0
+ var/offset_y = 0
+ var/is_swarming = FALSE
+ var/list/swarm_members = list()
+
+/datum/component/swarming/Initialize(max_x = 24, max_y = 24)
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+ offset_x = rand(-max_x, max_x)
+ offset_y = rand(-max_y, max_y)
+
+ RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/join_swarm)
+ RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/leave_swarm)
+
+/datum/component/swarming/Destroy()
+ for(var/other in swarm_members)
+ var/datum/component/swarming/other_swarm = other
+ other_swarm.swarm_members -= src
+ if(!other_swarm.swarm_members.len)
+ other_swarm.unswarm()
+ swarm_members = null
+ return ..()
+
+/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/AM)
+ var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming)
+ if(!other_swarm)
+ return
+ swarm()
+ swarm_members |= other_swarm
+ other_swarm.swarm()
+ other_swarm.swarm_members |= src
+
+/datum/component/swarming/proc/leave_swarm(datum/source, atom/movable/AM)
+ var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming)
+ if(!other_swarm || !(other_swarm in swarm_members))
+ return
+ swarm_members -= other_swarm
+ if(!swarm_members.len)
+ unswarm()
+ other_swarm.swarm_members -= src
+ if(!other_swarm.swarm_members.len)
+ other_swarm.unswarm()
+
+/datum/component/swarming/proc/swarm()
+ var/atom/movable/owner = parent
+ if(!is_swarming)
+ is_swarming = TRUE
+ animate(owner, pixel_x = owner.pixel_x + offset_x, pixel_y = owner.pixel_y + offset_y, time = 2)
+
+/datum/component/swarming/proc/unswarm()
+ var/atom/movable/owner = parent
+ if(is_swarming)
+ animate(owner, pixel_x = owner.pixel_x - offset_x, pixel_y = owner.pixel_y - offset_y, time = 2)
+ is_swarming = FALSE
diff --git a/code/datums/components/waddling.dm b/code/datums/components/waddling.dm
deleted file mode 100644
index a1f538e4dd7..00000000000
--- a/code/datums/components/waddling.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-/datum/component/waddling
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
-
-/datum/component/waddling/Initialize()
- if(!isliving(parent))
- return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/Waddle)
-
-/datum/component/waddling/proc/Waddle()
- var/mob/living/L = parent
- if(L.incapacitated() || L.lying)
- return
- animate(L, pixel_z = 4, time = 0)
- animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2)
- animate(pixel_z = 0, transform = matrix(), time = 0)
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 7bfc79e4e71..0e98179e2f9 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -1,7 +1,3 @@
-/hook/startup/proc/createDatacore()
- GLOB.data_core = new /datum/datacore()
- return 1
-
/datum/datacore
var/list/medical = list()
var/list/general = list()
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index b2b09003934..dc6101630bf 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -1,5 +1,14 @@
// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0)
+/**
+ * Proc to check if a datum allows proc calls on it
+ *
+ * Returns TRUE if you can call a proc on the datum, FALSE if you cant
+ *
+ */
+/datum/proc/CanProcCall(procname)
+ return TRUE
+
/datum/proc/can_vv_get(var_name)
return TRUE
@@ -819,6 +828,59 @@
message_admins("[key_name(usr)] has made [A] process normally")
return TRUE
+ else if(href_list["modifyarmor"])
+ if(!check_rights(R_DEBUG|R_ADMIN))
+ return
+ var/obj/A = locateUID(href_list["modifyarmor"])
+ if(!istype(A))
+ return
+ A.var_edited = TRUE
+ var/list/armorlist = A.armor.getList()
+ var/list/displaylist
+
+ var/result
+ do
+ displaylist = list()
+ for(var/key in armorlist)
+ displaylist += "[key] = [armorlist[key]]"
+ result = input(usr, "Select an armor type to modify..", "Modify armor") as null|anything in displaylist + "(ADD ALL)" + "(SET ALL)" + "(DONE)"
+
+ if(result == "(DONE)")
+ break
+ else if(result == "(ADD ALL)" || result == "(SET ALL)")
+ var/new_amount = input(usr, result == "(ADD ALL)" ? "Enter armor to add to all types:" : "Enter new armor value for all types:", "Modify all types") as num|null
+ if(isnull(new_amount))
+ continue
+ var/proper_amount = text2num(new_amount)
+ if(isnull(proper_amount))
+ continue
+ for(var/key in armorlist)
+ armorlist[key] = (result == "(ADD ALL)" ? armorlist[key] : 0) + proper_amount
+ else if(result)
+ var/list/fields = splittext(result, " = ")
+ if(length(fields) != 2)
+ continue
+ var/type = fields[1]
+ if(isnull(armorlist[type]))
+ continue
+ var/new_amount = input(usr, "Enter new armor value for [type]:", "Modify [type]") as num|null
+ if(isnull(new_amount))
+ continue
+ var/proper_amount = text2num(new_amount)
+ if(isnull(proper_amount))
+ continue
+ armorlist[type] = proper_amount
+ while(result)
+
+ if(!result || !A)
+ return TRUE
+
+ A.armor = A.armor.setRating(armorlist["melee"], armorlist["bullet"], armorlist["laser"], armorlist["energy"], armorlist["bomb"], armorlist["bio"], armorlist["rad"], armorlist["fire"], armorlist["acid"], armorlist["magic"])
+
+ log_admin("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]")
+ message_admins("[key_name(usr)] modified the armor on [A] to: melee = [armorlist["melee"]], bullet = [armorlist["bullet"]], laser = [armorlist["laser"]], energy = [armorlist["energy"]], bomb = [armorlist["bomb"]], bio = [armorlist["bio"]], rad = [armorlist["rad"]], fire = [armorlist["fire"]], acid = [armorlist["acid"]], magic = [armorlist["magic"]]")
+ return TRUE
+
else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */
if(!check_rights(R_DEBUG|R_ADMIN)) return
@@ -1179,22 +1241,6 @@
log_admin("[key_name(usr)] has removed the organ [rem_organ] from [key_name(M)]")
qdel(rem_organ)
- else if(href_list["fix_nano"])
- if(!check_rights(R_DEBUG)) return
-
- var/mob/H = locateUID(href_list["fix_nano"])
-
- if(!istype(H) || !H.client)
- to_chat(usr, "This can only be done on mobs with clients")
- return
-
- H.client.reload_nanoui_resources()
-
- to_chat(usr, "Resource files sent")
- to_chat(H, "Your NanoUI Resource files have been refreshed")
-
- log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)]")
-
else if(href_list["regenerateicons"])
if(!check_rights(0)) return
diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm
index 9aeade59a1d..318dd176e45 100644
--- a/code/datums/diseases/advance/advance.dm
+++ b/code/datums/diseases/advance/advance.dm
@@ -171,7 +171,6 @@ GLOBAL_LIST_INIT(advance_cures, list(
if(!symptoms || !symptoms.len)
CRASH("We did not have any symptoms before generating properties.")
- return
var/list/properties = list("resistance" = 1, "stealth" = 0, "stage_rate" = 1, "transmittable" = 1, "severity" = 0)
@@ -196,9 +195,9 @@ GLOBAL_LIST_INIT(advance_cures, list(
visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC
// The more symptoms we have, the less transmittable it is but some symptoms can make up for it.
- SetSpread(Clamp(2 ** (properties["transmittable"] - symptoms.len), BLOOD, AIRBORNE))
- permeability_mod = max(Ceiling(0.4 * properties["transmittable"]), 1)
- cure_chance = 15 - Clamp(properties["resistance"], -5, 5) // can be between 10 and 20
+ SetSpread(clamp(2 ** (properties["transmittable"] - symptoms.len), BLOOD, AIRBORNE))
+ permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1)
+ cure_chance = 15 - clamp(properties["resistance"], -5, 5) // can be between 10 and 20
stage_prob = max(properties["stage_rate"], 2)
SetSeverity(properties["severity"])
GenerateCure(properties)
@@ -245,7 +244,7 @@ GLOBAL_LIST_INIT(advance_cures, list(
// Will generate a random cure, the less resistance the symptoms have, the harder the cure.
/datum/disease/advance/proc/GenerateCure(list/properties = list())
if(properties && properties.len)
- var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, GLOB.advance_cures.len)
+ var/res = clamp(properties["resistance"] - (symptoms.len / 2), 1, GLOB.advance_cures.len)
// to_chat(world, "Res = [res]")
cures = list(GLOB.advance_cures[res])
@@ -396,8 +395,9 @@ GLOBAL_LIST_INIT(advance_cures, list(
for(var/datum/disease/advance/AD in GLOB.active_diseases)
AD.Refresh()
- for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list))
- if(!is_station_level(H.z))
+ for(var/thing in shuffle(GLOB.human_list))
+ var/mob/living/carbon/human/H = thing
+ if(H.stat == DEAD || !is_station_level(H.z))
continue
if(!H.HasDisease(D))
H.ForceContractDisease(D)
diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm
index 3afb48ce534..e8486a8f408 100644
--- a/code/datums/diseases/critical.dm
+++ b/code/datums/diseases/critical.dm
@@ -159,7 +159,7 @@
var/mob/living/carbon/human/H = affected_mob
if(NO_HUNGER in H.dna.species.species_traits)
return TRUE
- if(ismachine(H))
+ if(ismachineperson(H))
return TRUE
return ..()
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index f619e9aa9bd..3e3e1696e61 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -57,6 +57,9 @@
W.plane = initial(W.plane)
W.loc = affected_mob.loc
W.dropped(affected_mob)
+ if(isobj(affected_mob.loc))
+ var/obj/O = affected_mob.loc
+ O.force_eject_occupant()
var/mob/living/new_mob = new new_form(affected_mob.loc)
if(istype(new_mob))
new_mob.a_intent = "harm"
diff --git a/code/datums/elements/_element.dm b/code/datums/elements/_element.dm
new file mode 100644
index 00000000000..46a295f90be
--- /dev/null
+++ b/code/datums/elements/_element.dm
@@ -0,0 +1,55 @@
+/**
+ * A holder for simple behaviour that can be attached to many different types
+ *
+ * Only one element of each type is instanced during game init.
+ * Otherwise acts basically like a lightweight component.
+ */
+/datum/element
+ /// Option flags for element behaviour
+ var/element_flags = NONE
+ /**
+ * The index of the first attach argument to consider for duplicate elements
+ *
+ * Is only used when flags contains [ELEMENT_BESPOKE]
+ *
+ * This is infinity so you must explicitly set this
+ */
+ var/id_arg_index = INFINITY
+
+/// Activates the functionality defined by the element on the given target datum
+/datum/element/proc/Attach(datum/target)
+ SHOULD_CALL_PARENT(1)
+ if(type == /datum/element)
+ return ELEMENT_INCOMPATIBLE
+ SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
+ if(element_flags & ELEMENT_DETACH)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach, override = TRUE)
+
+/// Deactivates the functionality defines by the element on the given datum
+/datum/element/proc/Detach(datum/source, force)
+ SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
+ SHOULD_CALL_PARENT(1)
+ UnregisterSignal(source, COMSIG_PARENT_QDELETING)
+
+/datum/element/Destroy(force)
+ if(!force)
+ return QDEL_HINT_LETMELIVE
+ SSdcs.elements_by_type -= type
+ return ..()
+
+//DATUM PROCS
+
+/// Finds the singleton for the element type given and attaches it to src
+/datum/proc/_AddElement(list/arguments)
+ var/datum/element/ele = SSdcs.GetElement(arguments)
+ arguments[1] = src
+ if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
+ CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
+
+/**
+ * Finds the singleton for the element type given and detaches it from src
+ * You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
+ */
+/datum/proc/_RemoveElement(list/arguments)
+ var/datum/element/ele = SSdcs.GetElement(arguments)
+ ele.Detach(src)
diff --git a/code/datums/elements/waddling.dm b/code/datums/elements/waddling.dm
new file mode 100644
index 00000000000..e8870653941
--- /dev/null
+++ b/code/datums/elements/waddling.dm
@@ -0,0 +1,25 @@
+/datum/element/waddling
+
+/datum/element/waddling/Attach(datum/target)
+ . = ..()
+ if(!ismovable(target))
+ return ELEMENT_INCOMPATIBLE
+ if(isliving(target))
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/LivingWaddle)
+ else
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/Waddle)
+
+/datum/element/waddling/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
+
+/datum/element/waddling/proc/LivingWaddle(mob/living/target)
+ if(target.incapacitated() || target.lying)
+ return
+ Waddle(target)
+
+/datum/element/waddling/proc/Waddle(atom/movable/target)
+ animate(target, pixel_z = 4, time = 0)
+ var/prev_trans = matrix(target.transform)
+ animate(pixel_z = 0, transform = turn(target.transform, pick(-12, 0, 12)), time = 2)
+ animate(pixel_z = 0, transform = prev_trans, time = 0)
diff --git a/code/datums/gas_mixture.dm b/code/datums/gas_mixture.dm
index 63b70b512ee..91bebcdad39 100644
--- a/code/datums/gas_mixture.dm
+++ b/code/datums/gas_mixture.dm
@@ -7,33 +7,22 @@ What are the archived variables for?
#define SPECIFIC_HEAT_TOXIN 200
#define SPECIFIC_HEAT_AIR 20
#define SPECIFIC_HEAT_CDO 30
-#define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \
- (carbon_dioxide*SPECIFIC_HEAT_CDO + (oxygen+nitrogen)*SPECIFIC_HEAT_AIR + toxins*SPECIFIC_HEAT_TOXIN)
+#define SPECIFIC_HEAT_N2O 40
+#define SPECIFIC_HEAT_AGENT_B 300
+
+#define HEAT_CAPACITY_CALCULATION(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b) \
+ (carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN + sleeping_agent * SPECIFIC_HEAT_N2O + agent_b * SPECIFIC_HEAT_AGENT_B)
#define MINIMUM_HEAT_CAPACITY 0.0003
-#define QUANTIZE(variable) (round(variable,0.0001))
-
-/datum/gas
- var/moles = 0
- var/specific_heat = 0
-
- var/moles_archived = 0
-
-/datum/gas/sleeping_agent
- specific_heat = 40
-
-/datum/gas/oxygen_agent_b
- specific_heat = 300
-
-/datum/gas/volatile_fuel
- specific_heat = 30
-
+#define QUANTIZE(variable) (round(variable, 0.0001))
/datum/gas_mixture
var/oxygen = 0
var/carbon_dioxide = 0
var/nitrogen = 0
var/toxins = 0
+ var/sleeping_agent = 0
+ var/agent_b = 0
var/volume = CELL_VOLUME
@@ -41,13 +30,12 @@ What are the archived variables for?
var/last_share
- var/list/datum/gas/trace_gases = list()
-
-
var/tmp/oxygen_archived
var/tmp/carbon_dioxide_archived
var/tmp/nitrogen_archived
var/tmp/toxins_archived
+ var/tmp/sleeping_agent_archived
+ var/tmp/agent_b_archived
var/tmp/temperature_archived
@@ -55,35 +43,24 @@ What are the archived variables for?
//PV=nRT - related procedures
/datum/gas_mixture/proc/heat_capacity()
- var/heat_capacity = HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins)
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- heat_capacity += trace_gas.moles*trace_gas.specific_heat
- return heat_capacity
+ return HEAT_CAPACITY_CALCULATION(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b)
/datum/gas_mixture/proc/heat_capacity_archived()
- var/heat_capacity_archived = HEAT_CAPACITY_CALCULATION(oxygen_archived,carbon_dioxide_archived,nitrogen_archived,toxins_archived)
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- heat_capacity_archived += trace_gas.moles_archived*trace_gas.specific_heat
- return heat_capacity_archived
+ return HEAT_CAPACITY_CALCULATION(oxygen_archived, carbon_dioxide_archived, nitrogen_archived, toxins_archived, sleeping_agent_archived, agent_b_archived)
/datum/gas_mixture/proc/total_moles()
- var/moles = oxygen + carbon_dioxide + nitrogen + toxins
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- moles += trace_gas.moles
+ var/moles = oxygen + carbon_dioxide + nitrogen + toxins + sleeping_agent + agent_b
return moles
+/datum/gas_mixture/proc/total_trace_moles()
+ var/moles = sleeping_agent + agent_b
+ return moles
/datum/gas_mixture/proc/return_pressure()
- if(volume>0)
- return total_moles()*R_IDEAL_GAS_EQUATION*temperature/volume
+ if(volume > 0)
+ return total_moles() * R_IDEAL_GAS_EQUATION * temperature / volume
return 0
@@ -96,7 +73,7 @@ What are the archived variables for?
/datum/gas_mixture/proc/thermal_energy()
- return temperature*heat_capacity()
+ return temperature * heat_capacity()
//Procedures used for very specific events
@@ -105,28 +82,23 @@ What are the archived variables for?
/datum/gas_mixture/proc/react(atom/dump_location)
var/reacting = 0 //set to 1 if a notable reaction occured (used by pipe_network)
- if(trace_gases.len > 0)
- if(temperature > 900)
- if(toxins > MINIMUM_HEAT_CAPACITY && carbon_dioxide > MINIMUM_HEAT_CAPACITY)
- var/datum/gas/oxygen_agent_b/trace_gas = locate(/datum/gas/oxygen_agent_b/) in trace_gases
- if(trace_gas)
- var/reaction_rate = min(carbon_dioxide*0.75, toxins*0.25, trace_gas.moles*0.05)
+ if(agent_b && temperature > 900)
+ if(toxins > MINIMUM_HEAT_CAPACITY && carbon_dioxide > MINIMUM_HEAT_CAPACITY)
+ var/reaction_rate = min(carbon_dioxide * 0.75, toxins * 0.25, agent_b * 0.05)
- carbon_dioxide -= reaction_rate
- oxygen += reaction_rate
+ carbon_dioxide -= reaction_rate
+ oxygen += reaction_rate
- trace_gas.moles -= reaction_rate*0.05
+ agent_b -= reaction_rate * 0.05
- temperature += (reaction_rate*20000)/heat_capacity()
+ temperature += (reaction_rate * 20000) / heat_capacity()
- reacting = 1
+ reacting = 1
fuel_burnt = 0
if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
-// to_chat(world, "pre [temperature], [oxygen], [toxins]")
if(fire() > 0)
reacting = 1
-// to_chat(world, "post [temperature], [oxygen], [toxins]")
return reacting
@@ -134,24 +106,6 @@ What are the archived variables for?
var/energy_released = 0
var/old_heat_capacity = heat_capacity()
- var/datum/gas/volatile_fuel/fuel_store = locate(/datum/gas/volatile_fuel/) in trace_gases
- if(fuel_store) //General volatile gas burn
- var/burned_fuel = 0
-
- if(oxygen < fuel_store.moles)
- burned_fuel = oxygen
- fuel_store.moles -= burned_fuel
- oxygen = 0
- else
- burned_fuel = fuel_store.moles
- oxygen -= fuel_store.moles
- trace_gases -= fuel_store
- fuel_store = null
-
- energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel
- carbon_dioxide += burned_fuel
- fuel_burnt += burned_fuel
-
//Handle plasma burning
if(toxins > MINIMUM_HEAT_CAPACITY)
var/plasma_burn_rate = 0
@@ -161,13 +115,13 @@ What are the archived variables for?
if(temperature > PLASMA_UPPER_TEMPERATURE)
temperature_scale = 1
else
- temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
+ temperature_scale = (temperature - PLASMA_MINIMUM_BURN_TEMPERATURE) / (PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE)
if(temperature_scale > 0)
oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale
- if(oxygen > toxins*PLASMA_OXYGEN_FULLBURN)
- plasma_burn_rate = (toxins*temperature_scale)/PLASMA_BURN_RATE_DELTA
+ if(oxygen > toxins * PLASMA_OXYGEN_FULLBURN)
+ plasma_burn_rate = (toxins * temperature_scale) / PLASMA_BURN_RATE_DELTA
else
- plasma_burn_rate = (temperature_scale*(oxygen/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA
+ plasma_burn_rate = (temperature_scale * (oxygen / PLASMA_OXYGEN_FULLBURN)) / PLASMA_BURN_RATE_DELTA
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
toxins -= plasma_burn_rate
oxygen -= plasma_burn_rate*oxygen_burn_rate
@@ -175,12 +129,12 @@ What are the archived variables for?
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
- fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate)
+ fuel_burnt += (plasma_burn_rate) * (1 + oxygen_burn_rate)
if(energy_released > 0)
var/new_heat_capacity = heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
- temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity
+ temperature = (temperature * old_heat_capacity + energy_released) / new_heat_capacity
return fuel_burnt
@@ -231,10 +185,8 @@ What are the archived variables for?
carbon_dioxide_archived = carbon_dioxide
nitrogen_archived = nitrogen
toxins_archived = toxins
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- trace_gas.moles_archived = trace_gas.moles
+ sleeping_agent_archived = sleeping_agent
+ agent_b_archived = agent_b
temperature_archived = temperature
@@ -244,55 +196,45 @@ What are the archived variables for?
if(!giver)
return 0
- if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ if(abs(temperature - giver.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
var/giver_heat_capacity = giver.heat_capacity()
var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
if(combined_heat_capacity != 0)
- temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity
+ temperature = (giver.temperature * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
oxygen += giver.oxygen
carbon_dioxide += giver.carbon_dioxide
nitrogen += giver.nitrogen
toxins += giver.toxins
-
- for(var/gas in giver.trace_gases)
- var/datum/gas/trace_gas = gas
- var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- trace_gases += corresponding
- corresponding.moles += trace_gas.moles
+ sleeping_agent += giver.sleeping_agent
+ agent_b += giver.agent_b
return 1
/datum/gas_mixture/remove(amount)
var/sum = total_moles()
- amount = min(amount,sum) //Can not take more air than tile has!
+ amount = min(amount, sum) //Can not take more air than tile has!
if(amount <= 0)
return null
var/datum/gas_mixture/removed = new
- removed.oxygen = QUANTIZE((oxygen/sum)*amount)
- removed.nitrogen = QUANTIZE((nitrogen/sum)*amount)
- removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount)
- removed.toxins = QUANTIZE((toxins/sum)*amount)
+ removed.oxygen = QUANTIZE((oxygen / sum) * amount)
+ removed.nitrogen = QUANTIZE((nitrogen/ sum) * amount)
+ removed.carbon_dioxide = QUANTIZE((carbon_dioxide / sum) * amount)
+ removed.toxins = QUANTIZE((toxins / sum) * amount)
+ removed.sleeping_agent = QUANTIZE((sleeping_agent / sum) * amount)
+ removed.agent_b = QUANTIZE((agent_b / sum) * amount)
oxygen -= removed.oxygen
nitrogen -= removed.nitrogen
carbon_dioxide -= removed.carbon_dioxide
toxins -= removed.toxins
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- var/datum/gas/corresponding = new trace_gas.type()
- removed.trace_gases += corresponding
-
- corresponding.moles = (trace_gas.moles/sum)*amount
- trace_gas.moles -= corresponding.moles
+ sleeping_agent -= removed.sleeping_agent
+ agent_b -= removed.agent_b
removed.temperature = temperature
@@ -307,23 +249,19 @@ What are the archived variables for?
var/datum/gas_mixture/removed = new
- removed.oxygen = QUANTIZE(oxygen*ratio)
- removed.nitrogen = QUANTIZE(nitrogen*ratio)
- removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio)
- removed.toxins = QUANTIZE(toxins*ratio)
+ removed.oxygen = QUANTIZE(oxygen * ratio)
+ removed.nitrogen = QUANTIZE(nitrogen * ratio)
+ removed.carbon_dioxide = QUANTIZE(carbon_dioxide * ratio)
+ removed.toxins = QUANTIZE(toxins * ratio)
+ removed.sleeping_agent = QUANTIZE(sleeping_agent * ratio)
+ removed.agent_b = QUANTIZE(agent_b * ratio)
oxygen -= removed.oxygen
nitrogen -= removed.nitrogen
carbon_dioxide -= removed.carbon_dioxide
toxins -= removed.toxins
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- var/datum/gas/corresponding = new trace_gas.type()
- removed.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*ratio
- trace_gas.moles -= corresponding.moles
+ sleeping_agent -= removed.sleeping_agent
+ agent_b -= removed.agent_b
removed.temperature = temperature
@@ -334,14 +272,8 @@ What are the archived variables for?
carbon_dioxide = sample.carbon_dioxide
nitrogen = sample.nitrogen
toxins = sample.toxins
-
- trace_gases.len=null
- for(var/gas in sample.trace_gases)
- var/datum/gas/trace_gas = gas
- var/datum/gas/corresponding = new trace_gas.type()
- trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles
+ sleeping_agent = sample.sleeping_agent
+ agent_b = sample.agent_b
temperature = sample.temperature
@@ -352,6 +284,8 @@ What are the archived variables for?
carbon_dioxide = model.carbon_dioxide
nitrogen = model.nitrogen
toxins = model.toxins
+ sleeping_agent = model.sleeping_agent
+ agent_b = model.agent_b
//acounts for changes in temperature
var/turf/model_parent = model.parent_type
@@ -361,26 +295,25 @@ What are the archived variables for?
return 1
/datum/gas_mixture/check_turf(turf/model, atmos_adjacent_turfs = 4)
- var/delta_oxygen = (oxygen_archived - model.oxygen)/(atmos_adjacent_turfs+1)
- var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/(atmos_adjacent_turfs+1)
- var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/(atmos_adjacent_turfs+1)
- var/delta_toxins = (toxins_archived - model.toxins)/(atmos_adjacent_turfs+1)
+ var/delta_oxygen = (oxygen_archived - model.oxygen) / (atmos_adjacent_turfs + 1)
+ var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide) / (atmos_adjacent_turfs + 1)
+ var/delta_nitrogen = (nitrogen_archived - model.nitrogen) / (atmos_adjacent_turfs + 1)
+ var/delta_toxins = (toxins_archived - model.toxins) / (atmos_adjacent_turfs + 1)
+ var/delta_sleeping_agent = (sleeping_agent_archived - model.sleeping_agent) / (atmos_adjacent_turfs + 1)
+ var/delta_agent_b = (agent_b_archived - model.agent_b) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (temperature_archived - model.temperature)
- if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_sleeping_agent) >= sleeping_agent_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_agent_b) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_agent_b) >= agent_b_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)))
return 0
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
return 0
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4)
- return 0
-
return 1
/datum/gas_mixture/proc/check_turf_total(turf/model) //I want this proc to die a painful death
@@ -388,30 +321,32 @@ What are the archived variables for?
var/delta_carbon_dioxide = (carbon_dioxide - model.carbon_dioxide)
var/delta_nitrogen = (nitrogen - model.nitrogen)
var/delta_toxins = (toxins - model.toxins)
+ var/delta_sleeping_agent = (sleeping_agent - model.sleeping_agent)
+ var/delta_agent_b = (agent_b - model.agent_b)
var/delta_temperature = (temperature - model.temperature)
- if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \
- || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins*MINIMUM_AIR_RATIO_TO_SUSPEND)))
+ if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_sleeping_agent) >= sleeping_agent * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
+ || ((abs(delta_agent_b) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_agent_b) >= agent_b * MINIMUM_AIR_RATIO_TO_SUSPEND)))
return 0
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
return 0
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- if(trace_gas.moles > MINIMUM_AIR_TO_SUSPEND*4)
- return 0
-
return 1
/datum/gas_mixture/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
- if(!sharer) return 0
- var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/(atmos_adjacent_turfs+1)
- var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/(atmos_adjacent_turfs+1)
- var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/(atmos_adjacent_turfs+1)
- var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/(atmos_adjacent_turfs+1)
+ if(!sharer)
+ return 0
+ var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived) / (atmos_adjacent_turfs + 1)
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived) / (atmos_adjacent_turfs + 1)
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived) / (atmos_adjacent_turfs + 1)
+ var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived) / (atmos_adjacent_turfs + 1)
+ var/delta_sleeping_agent = QUANTIZE(sleeping_agent_archived - sharer.sleeping_agent_archived) / (atmos_adjacent_turfs + 1)
+ var/delta_agent_b = QUANTIZE(agent_b_archived - sharer.agent_b_archived) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (temperature_archived - sharer.temperature_archived)
@@ -423,28 +358,42 @@ What are the archived variables for?
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
- var/delta_air = delta_oxygen+delta_nitrogen
+ var/delta_air = delta_oxygen + delta_nitrogen
if(delta_air)
- var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air
+ var/air_heat_capacity = SPECIFIC_HEAT_AIR * delta_air
if(delta_air > 0)
heat_capacity_self_to_sharer += air_heat_capacity
else
heat_capacity_sharer_to_self -= air_heat_capacity
if(delta_carbon_dioxide)
- var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide
+ var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO * delta_carbon_dioxide
if(delta_carbon_dioxide > 0)
heat_capacity_self_to_sharer += carbon_dioxide_heat_capacity
else
heat_capacity_sharer_to_self -= carbon_dioxide_heat_capacity
if(delta_toxins)
- var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins
+ var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN * delta_toxins
if(delta_toxins > 0)
heat_capacity_self_to_sharer += toxins_heat_capacity
else
heat_capacity_sharer_to_self -= toxins_heat_capacity
+ if(delta_sleeping_agent)
+ var/sleeping_agent_heat_capacity = SPECIFIC_HEAT_N2O * delta_sleeping_agent
+ if(delta_sleeping_agent > 0)
+ heat_capacity_self_to_sharer += sleeping_agent_heat_capacity
+ else
+ heat_capacity_sharer_to_self -= sleeping_agent_heat_capacity
+
+ if(delta_agent_b)
+ var/agent_b_heat_capacity = SPECIFIC_HEAT_AGENT_B * delta_agent_b
+ if(delta_agent_b > 0)
+ heat_capacity_self_to_sharer += agent_b_heat_capacity
+ else
+ heat_capacity_sharer_to_self -= agent_b_heat_capacity
+
old_self_heat_capacity = heat_capacity()
old_sharer_heat_capacity = sharer.heat_capacity()
@@ -460,83 +409,40 @@ What are the archived variables for?
toxins -= delta_toxins
sharer.toxins += delta_toxins
- var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins)
- last_share = abs(delta_oxygen) + abs(delta_carbon_dioxide) + abs(delta_nitrogen) + abs(delta_toxins)
+ sleeping_agent -= delta_sleeping_agent
+ sharer.sleeping_agent += delta_sleeping_agent
- var/list/trace_types_considered = list()
+ agent_b -= delta_agent_b
+ sharer.agent_b += delta_agent_b
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases
- var/delta = 0
-
- if(corresponding)
- delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/(atmos_adjacent_turfs+1)
- else
- corresponding = new trace_gas.type()
- sharer.trace_gases += corresponding
-
- delta = trace_gas.moles_archived/(atmos_adjacent_turfs+1)
-
- trace_gas.moles -= delta
- corresponding.moles += delta
-
- if(delta)
- var/individual_heat_capacity = trace_gas.specific_heat*delta
- if(delta > 0)
- heat_capacity_self_to_sharer += individual_heat_capacity
- else
- heat_capacity_sharer_to_self -= individual_heat_capacity
-
- moved_moles += delta
- last_share += abs(delta)
-
- trace_types_considered += trace_gas.type
-
- for(var/gas in sharer.trace_gases)
- var/datum/gas/trace_gas = gas
- if(trace_gas.type in trace_types_considered)
- continue
- var/datum/gas/corresponding
- var/delta = 0
- corresponding = new trace_gas.type()
- trace_gases += corresponding
-
- delta = trace_gas.moles_archived/5
-
- trace_gas.moles -= delta
- corresponding.moles += delta
-
- //Guaranteed transfer from sharer to self
- var/individual_heat_capacity = trace_gas.specific_heat*delta
- heat_capacity_sharer_to_self += individual_heat_capacity
-
- moved_moles += -delta
- last_share += abs(delta)
+ var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins + delta_sleeping_agent + delta_agent_b)
+ last_share = abs(delta_oxygen) + abs(delta_carbon_dioxide) + abs(delta_nitrogen) + abs(delta_toxins) + abs(delta_sleeping_agent) + abs(delta_agent_b)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer
var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self
if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY)
- temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity
+ temperature = (old_self_heat_capacity * temperature - heat_capacity_self_to_sharer * temperature_archived + heat_capacity_sharer_to_self * sharer.temperature_archived) / new_self_heat_capacity
if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY)
- sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity
+ sharer.temperature = (old_sharer_heat_capacity * sharer.temperature - heat_capacity_sharer_to_self * sharer.temperature_archived + heat_capacity_self_to_sharer * temperature_archived) / new_sharer_heat_capacity
if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY)
- if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity
+ if(abs(new_sharer_heat_capacity / old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity
temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT)
if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
- var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - sharer.temperature_archived*(sharer.total_moles() - moved_moles)
- return delta_pressure*R_IDEAL_GAS_EQUATION/volume
+ var/delta_pressure = temperature_archived * (total_moles() + moved_moles) - sharer.temperature_archived * (sharer.total_moles() - moved_moles)
+ return delta_pressure * R_IDEAL_GAS_EQUATION / volume
/datum/gas_mixture/mimic(turf/model, atmos_adjacent_turfs = 4)
- var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/(atmos_adjacent_turfs+1)
- var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/(atmos_adjacent_turfs+1)
- var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/(atmos_adjacent_turfs+1)
- var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/(atmos_adjacent_turfs+1)
+ var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen) / (atmos_adjacent_turfs + 1)
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide) / (atmos_adjacent_turfs + 1)
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen) / (atmos_adjacent_turfs + 1)
+ var/delta_toxins = QUANTIZE(toxins_archived - model.toxins) / (atmos_adjacent_turfs + 1)
+ var/delta_sleeping_agent = QUANTIZE(sleeping_agent_archived - model.sleeping_agent) / (atmos_adjacent_turfs + 1)
+ var/delta_agent_b = QUANTIZE(agent_b_archived - model.agent_b) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (temperature_archived - model.temperature)
@@ -546,57 +452,54 @@ What are the archived variables for?
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
- var/delta_air = delta_oxygen+delta_nitrogen
+ var/delta_air = delta_oxygen + delta_nitrogen
if(delta_air)
- var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air
- heat_transferred -= air_heat_capacity*model.temperature
+ var/air_heat_capacity = SPECIFIC_HEAT_AIR * delta_air
+ heat_transferred -= air_heat_capacity * model.temperature
heat_capacity_transferred -= air_heat_capacity
if(delta_carbon_dioxide)
- var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide
- heat_transferred -= carbon_dioxide_heat_capacity*model.temperature
+ var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO * delta_carbon_dioxide
+ heat_transferred -= carbon_dioxide_heat_capacity * model.temperature
heat_capacity_transferred -= carbon_dioxide_heat_capacity
if(delta_toxins)
- var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins
- heat_transferred -= toxins_heat_capacity*model.temperature
+ var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN * delta_toxins
+ heat_transferred -= toxins_heat_capacity * model.temperature
heat_capacity_transferred -= toxins_heat_capacity
+ if(delta_sleeping_agent)
+ var/sleeping_agent_heat_capacity = SPECIFIC_HEAT_N2O * delta_sleeping_agent
+ heat_transferred -= sleeping_agent_heat_capacity * model.temperature
+ heat_capacity_transferred -= sleeping_agent_heat_capacity
+
+ if(delta_agent_b)
+ var/agent_b_heat_capacity = SPECIFIC_HEAT_AGENT_B * delta_agent_b
+ heat_transferred -= agent_b_heat_capacity * model.temperature
+ heat_capacity_transferred -= agent_b_heat_capacity
+
old_self_heat_capacity = heat_capacity()
oxygen -= delta_oxygen
carbon_dioxide -= delta_carbon_dioxide
nitrogen -= delta_nitrogen
toxins -= delta_toxins
+ sleeping_agent -= delta_sleeping_agent
+ agent_b -= delta_agent_b
- var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins)
- last_share = abs(delta_oxygen) + abs(delta_carbon_dioxide) + abs(delta_nitrogen) + abs(delta_toxins)
-
- if(trace_gases.len)
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- var/delta = 0
-
- delta = trace_gas.moles_archived/(atmos_adjacent_turfs+1)
-
- trace_gas.moles -= delta
-
- var/heat_cap_transferred = delta*trace_gas.specific_heat
- heat_transferred += heat_cap_transferred*temperature_archived
- heat_capacity_transferred += heat_cap_transferred
- moved_moles += delta
- moved_moles += abs(delta)
+ var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins + delta_sleeping_agent + delta_agent_b)
+ last_share = abs(delta_oxygen) + abs(delta_carbon_dioxide) + abs(delta_nitrogen) + abs(delta_toxins) + abs(delta_sleeping_agent) + abs(delta_agent_b)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/new_self_heat_capacity = old_self_heat_capacity - heat_capacity_transferred
if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY)
- temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*temperature_archived)/new_self_heat_capacity
+ temperature = (old_self_heat_capacity * temperature - heat_capacity_transferred * temperature_archived) / new_self_heat_capacity
temperature_mimic(model, model.thermal_conductivity)
if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
- var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - model.temperature*(model.oxygen+model.carbon_dioxide+model.nitrogen+model.toxins)
- return delta_pressure*R_IDEAL_GAS_EQUATION/volume
+ var/delta_pressure = temperature_archived * (total_moles() + moved_moles) - model.temperature * (model.oxygen + model.carbon_dioxide + model.nitrogen + model.toxins + model.sleeping_agent + model.agent_b)
+ return delta_pressure * R_IDEAL_GAS_EQUATION / volume
else
return 0
@@ -608,11 +511,11 @@ What are the archived variables for?
var/sharer_heat_capacity = sharer.heat_capacity_archived()
if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
- var/heat = conduction_coefficient*delta_temperature* \
- (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity))
+ var/heat = conduction_coefficient*delta_temperature * \
+ (self_heat_capacity * sharer_heat_capacity / (self_heat_capacity + sharer_heat_capacity))
- temperature -= heat/self_heat_capacity
- sharer.temperature += heat/sharer_heat_capacity
+ temperature -= heat / self_heat_capacity
+ sharer.temperature += heat / sharer_heat_capacity
/datum/gas_mixture/temperature_mimic(turf/model, conduction_coefficient)
var/delta_temperature = (temperature - model.temperature)
@@ -620,10 +523,10 @@ What are the archived variables for?
var/self_heat_capacity = heat_capacity()
if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
- var/heat = conduction_coefficient*delta_temperature* \
- (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity))
+ var/heat = conduction_coefficient * delta_temperature * \
+ (self_heat_capacity * model.heat_capacity / (self_heat_capacity + model.heat_capacity))
- temperature -= heat/self_heat_capacity
+ temperature -= heat / self_heat_capacity
/datum/gas_mixture/temperature_turf_share(turf/simulated/sharer, conduction_coefficient)
var/delta_temperature = (temperature_archived - sharer.temperature)
@@ -631,52 +534,36 @@ What are the archived variables for?
var/self_heat_capacity = heat_capacity()
if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
- var/heat = conduction_coefficient*delta_temperature* \
- (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity))
+ var/heat = conduction_coefficient * delta_temperature * \
+ (self_heat_capacity * sharer.heat_capacity / (self_heat_capacity + sharer.heat_capacity))
- temperature -= heat/self_heat_capacity
- sharer.temperature += heat/sharer.heat_capacity
+ temperature -= heat / self_heat_capacity
+ sharer.temperature += heat / sharer.heat_capacity
/datum/gas_mixture/compare(datum/gas_mixture/sample)
- if((abs(oxygen-sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \
- ((oxygen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen)))
+ if((abs(oxygen - sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((oxygen < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.oxygen) || (oxygen > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.oxygen)))
return 0
- if((abs(nitrogen-sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \
- ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen)))
+ if((abs(nitrogen - sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((nitrogen < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.nitrogen) || (nitrogen > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.nitrogen)))
return 0
- if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \
- ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide)))
+ if((abs(carbon_dioxide - sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((carbon_dioxide < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.carbon_dioxide) || (carbon_dioxide > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.carbon_dioxide)))
return 0
- if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \
- ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins)))
+ if((abs(toxins - sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((toxins < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.toxins) || (toxins > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.toxins)))
+ return 0
+ if((abs(sleeping_agent - sample.sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((sleeping_agent < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.sleeping_agent) || (sleeping_agent > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.sleeping_agent)))
+ return 0
+ if((abs(agent_b - sample.agent_b) > MINIMUM_AIR_TO_SUSPEND) && \
+ ((agent_b < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.agent_b) || (agent_b > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.agent_b)))
return 0
if(total_moles() > MINIMUM_AIR_TO_SUSPEND)
- if((abs(temperature-sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
- ((temperature < (1-MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || (temperature > (1+MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature)))
+ if((abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
+ ((temperature < (1 - MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND) * sample.temperature) || (temperature > (1 + MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND) * sample.temperature)))
return 0
-
- for(var/gas in sample.trace_gases)
- var/datum/gas/trace_gas = gas
- if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND)
- var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
- if(corresponding)
- if((abs(trace_gas.moles - corresponding.moles) > MINIMUM_AIR_TO_SUSPEND) && \
- ((corresponding.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles) || (corresponding.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles)))
- return 0
- else
- return 0
-
- for(var/gas in trace_gases)
- var/datum/gas/trace_gas = gas
- if(trace_gas.moles > MINIMUM_AIR_TO_SUSPEND)
- var/datum/gas/corresponding = locate(trace_gas.type) in sample.trace_gases
- if(corresponding)
- if((abs(trace_gas.moles - corresponding.moles) > MINIMUM_AIR_TO_SUSPEND) && \
- ((trace_gas.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*corresponding.moles) || (trace_gas.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*corresponding.moles)))
- return 0
- else
- return 0
return 1
@@ -691,12 +578,12 @@ What are the archived variables for?
//Does handle trace gases!
/datum/gas_mixture/proc/get_breath_partial_pressure(gas_pressure)
- return (gas_pressure*R_IDEAL_GAS_EQUATION*temperature)/BREATH_VOLUME
+ return (gas_pressure * R_IDEAL_GAS_EQUATION * temperature) / BREATH_VOLUME
//Reverse of the above
/datum/gas_mixture/proc/get_true_breath_pressure(breath_pp)
- return (breath_pp*BREATH_VOLUME)/(R_IDEAL_GAS_EQUATION*temperature)
+ return (breath_pp * BREATH_VOLUME) / (R_IDEAL_GAS_EQUATION * temperature)
//Mathematical proofs:
/*
diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm
index c10ca5a488b..8eefe5efa0d 100644
--- a/code/datums/helper_datums/construction_datum.dm
+++ b/code/datums/helper_datums/construction_datum.dm
@@ -69,10 +69,10 @@
/datum/construction/proc/check_all_steps(atom/used_atom,mob/user as mob) //check all steps, remove matching one.
for(var/i=1;i<=steps.len;i++)
- var/list/L = steps[i];
+ var/list/L = steps[i]
if(do_tool_or_atom_check(used_atom, L["key"]) && custom_action(i, used_atom, user))
steps[i]=null;//stupid byond list from list removal...
- listclearnulls(steps);
+ listclearnulls(steps)
if(!steps.len)
spawn_result(user)
return 1
diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm
index a54707842db..ee5a4df4f5b 100644
--- a/code/datums/helper_datums/map_template.dm
+++ b/code/datums/helper_datums/map_template.dm
@@ -17,7 +17,7 @@
name = rename
/datum/map_template/proc/preload_size(path)
- var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1)
+ var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, shouldCropMap = FALSE, measureOnly = TRUE)
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
@@ -49,7 +49,7 @@
// if given a multi-z template
// it might need to be adapted for that when that time comes
GLOB.space_manager.add_dirt(placement.z)
- var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1)
+ var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, shouldCropMap = TRUE)
if(!bounds)
return 0
if(bot_left == null || top_right == null)
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index 9bba49f2678..4e20d2a675e 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -211,7 +211,7 @@
var/datum/gas_mixture/A = F.air
// Can most things breathe?
- if(A.trace_gases.len)
+ if(A.sleeping_agent)
continue
if(A.oxygen < 16)
continue
diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm
index 9b7d52b5390..2a659477b47 100644
--- a/code/datums/log_record.dm
+++ b/code/datums/log_record.dm
@@ -3,32 +3,51 @@
var/raw_time // When did this happen?
var/what // What happened
var/who // Who did it
- var/target // Who/what was targeted (can be a string)
- var/turf/where // Where did it happen
+ var/target // Who/what was targeted
+ var/where // Where did it happen
/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time)
log_type = _log_type
-
- who = get_subject_text(_who)
+
+ who = get_subject_text(_who, _log_type)
what = _what
- target = get_subject_text(_target)
- if(!_where)
+ target = get_subject_text(_target, _log_type)
+ if(!istext(_where) && !isturf(_where))
_where = get_turf(_who)
- where = _where
+ if(isturf(_where))
+ var/turf/T = _where
+ where = ADMIN_COORDJMP(T)
+ else
+ where = _where
if(!_raw_time)
_raw_time = world.time
raw_time = _raw_time
-/datum/log_record/proc/get_subject_text(subject)
+/datum/log_record/proc/get_subject_text(subject, log_type)
if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind))
- return key_name_admin(subject)
- if(isatom(subject))
+ . = key_name_admin(subject)
+ if(should_log_health(log_type) && isliving(subject))
+ . += get_health_string(subject)
+ else if(isatom(subject))
var/atom/A = subject
- return A.name
- if(istype(subject, /datum))
+ . = A.name
+ else if(istype(subject, /datum))
var/datum/D = subject
return D.type
- return subject
+ else
+ . = subject
+
+/datum/log_record/proc/get_health_string(var/mob/living/L)
+ var/OX = L.getOxyLoss() > 50 ? "[L.getOxyLoss()]" : L.getOxyLoss()
+ var/TX = L.getToxLoss() > 50 ? "[L.getToxLoss()]" : L.getToxLoss()
+ var/BU = L.getFireLoss() > 50 ? "[L.getFireLoss()]" : L.getFireLoss()
+ var/BR = L.getBruteLoss() > 50 ? "[L.getBruteLoss()]" : L.getBruteLoss()
+ return " ([L.health]: [OX] - [TX] - [BU] - [BR])"
+
+/datum/log_record/proc/should_log_health(log_type)
+ if(log_type == ATTACK_LOG || log_type == DEFENSE_LOG)
+ return TRUE
+ return FALSE
/proc/compare_log_record(datum/log_record/A, datum/log_record/B)
var/time_diff = A.raw_time - B.raw_time
diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm
index 67eb3d21d86..c5a17507606 100644
--- a/code/datums/log_viewer.dm
+++ b/code/datums/log_viewer.dm
@@ -1,31 +1,45 @@
-#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
+#define UPDATE_CKEY_MOB(__ckey) var/mob/result = selected_ckeys_mobs[__ckey];\
+if(!result || result.ckey != __ckey){\
+ result = get_mob_by_ckey(__ckey);\
+ selected_ckeys_mobs[__ckey] = result;\
+}
+
+#define RECORD_WARN_LIMIT 1000
+#define RECORD_HARD_LIMIT 2500
/datum/log_viewer
var/time_from = 0
var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up
- var/list/selected_mobs = list() // The mobs in question
- var/list/selected_log_types = list() // The log types being searched for
-
+ var/list/selected_mobs = list() // The mobs in question.
+ var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to
+ var/list/mob/selected_ckeys_mobs = list()
+ var/list/selected_log_types = ALL_LOGS // The log types being searched for
var/list/log_records = list() // Found and sorted records
/datum/log_viewer/proc/clear_all()
selected_mobs.Cut()
- selected_log_types.Cut()
+ selected_log_types = ALL_LOGS
+ selected_ckeys.Cut()
+ selected_ckeys_mobs.Cut()
time_from = initial(time_from)
time_to = initial(time_to)
log_records.Cut()
return
-/datum/log_viewer/proc/search()
+/datum/log_viewer/proc/search(user)
log_records.Cut() // Empty the old results
var/list/invalid_mobs = list()
+ var/list/ckeys = selected_ckeys.Copy()
for(var/i in selected_mobs)
var/mob/M = i
- if(!M || QDELETED(M))
+ if(!M || QDELETED(M) || !M.last_known_ckey)
invalid_mobs |= M
continue
+ ckeys |= M.last_known_ckey
+
+ for(var/ckey in ckeys)
for(var/log_type in selected_log_types)
- var/list/logs = M.logs[log_type]
+ var/list/logs = GLOB.logging.get_logs_by_type(ckey, log_type)
var/len_logs = length(logs)
if(len_logs)
var/start_index = get_earliest_log_index(logs)
@@ -36,8 +50,8 @@
continue
log_records.Add(logs.Copy(start_index, end_index + 1))
- if(invalid_mobs.len)
- to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.")
+ if(length(invalid_mobs))
+ to_chat(user, "The search criteria contained invalid mobs. They have been removed from the criteria.")
for(var/i in invalid_mobs)
selected_mobs -= i // Cleanup
@@ -91,9 +105,23 @@
return start
return 0
-/datum/log_viewer/proc/add_mob(mob/user, mob/M)
+/datum/log_viewer/proc/add_mobs(list/mob/mobs)
+ if(!length(mobs))
+ return
+ for(var/i in mobs)
+ add_mob(usr, i, FALSE)
+
+/datum/log_viewer/proc/add_ckey(mob/user, ckey)
+ if(!user || !ckey)
+ return
+ selected_ckeys |= ckey
+ UPDATE_CKEY_MOB(ckey)
+ show_ui(user)
+
+/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE)
if(!M || !user)
return
+
selected_mobs |= M
show_ui(user)
@@ -102,9 +130,9 @@
var/all_log_types = ALL_LOGS
var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;"
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
- var/dat
- dat += ""
- dat += ""
+ var/list/dat = list()
+ dat += " "
+ dat += " "
dat += " Time Search Range: [gameTimestamp(wtime = time_from)]"
dat += " To: [gameTimestamp(wtime = time_to)]"
dat += " "
@@ -115,20 +143,26 @@
if(QDELETED(M))
selected_mobs -= i
continue
- dat += " [M.name]"
+ dat += " [get_display_name(M)]"
dat += " Add Mob"
dat += " Clear All Mobs"
dat += " "
+ dat += " Ckeys being used:"
+ for(var/ckey in selected_ckeys)
+ dat += " [get_ckey_name(ckey)]"
+ dat += " Add ckey"
+ dat += " Clear All ckeys"
+ dat += " "
+
dat += " Log Types:"
- for(var/i in all_log_types)
- var/log_type = i
+ for(var/log_type in all_log_types)
var/enabled = (log_type in selected_log_types)
var/text
var/style
if(enabled)
text = " [log_type]"
- style = "background: [get_logtype_color(i)]"
+ style = "background: [get_logtype_color(log_type)]"
else
text = log_type
@@ -142,9 +176,9 @@
// Search results
var/tdStyleTime = "width:80px; text-align:center;"
var/tdStyleType = "width:80px; text-align:center;"
- var/tdStyleWho = "width:300px; text-align:center;"
+ var/tdStyleWho = "width:400px; text-align:center;"
var/tdStyleWhere = "width:150px; text-align:center;"
- dat += " "
+ dat += " "
dat += " "
dat += "| When | Type | Who | What | Target | Where | "
for(var/i in log_records)
@@ -153,13 +187,12 @@
dat +="| [time] | [L.log_type] | \
[L.who] | [L.what] | \
- [L.target] | [ADMIN_COORDJMP(L.where)] | "
-
+ [L.target] | [L.where] | "
dat += " "
dat += " "
- var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600)
- popup.set_content(dat)
+ var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600)
+ popup.set_content(dat.Join())
popup.open()
/datum/log_viewer/Topic(href, href_list)
@@ -188,6 +221,19 @@
return
if(href_list["search"])
search(usr)
+ var/records_len = length(log_records)
+ if(records_len > RECORD_WARN_LIMIT)
+ var/datum/log_record/last_record = log_records[RECORD_WARN_LIMIT]
+ var/last_time = gameTimestamp(wtime = last_record.raw_time - 9.99)
+ var/answer = alert(usr, "More than [RECORD_WARN_LIMIT] records were found. continuing will take a long time. This won't cause much lag for the server. Time at the [RECORD_WARN_LIMIT]th record '[last_time]'", "Warning", "Continue", "Limit to [RECORD_WARN_LIMIT]", "Cancel")
+ if(answer == "Limit to [RECORD_WARN_LIMIT]")
+ log_records.Cut(RECORD_WARN_LIMIT)
+ else if(answer == "Cancel")
+ log_records.Cut()
+ else
+ if(records_len > RECORD_HARD_LIMIT)
+ to_chat(usr, " Record limit reached. Limiting to [RECORD_HARD_LIMIT].")
+ log_records.Cut(RECORD_HARD_LIMIT)
show_ui(usr)
return
if(href_list["clear_all"])
@@ -198,17 +244,31 @@
selected_mobs.Cut()
show_ui(usr)
return
+ if(href_list["clear_ckeys"])
+ selected_ckeys.Cut()
+ selected_ckeys_mobs.Cut()
+ show_ui(usr)
+ return
if(href_list["add_mob"])
var/list/mobs = getpois(TRUE, TRUE)
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
A.on_close(CALLBACK(src, .proc/add_mob, usr))
return
+ if(href_list["add_ckey"])
+ var/list/ckeys = GLOB.logging.get_ckeys_logged()
+ var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys)
+ A.on_close(CALLBACK(src, .proc/add_ckey, usr))
+ return
if(href_list["remove_mob"])
var/mob/M = locate(href_list["remove_mob"])
if(M)
selected_mobs -= M
show_ui(usr)
return
+ if(href_list["remove_ckey"])
+ selected_ckeys -= href_list["remove_ckey"]
+ show_ui(usr)
+ return
if(href_list["toggle_log_type"])
var/log_type = href_list["toggle_log_type"]
if(log_type in selected_log_types)
@@ -232,4 +292,28 @@
return "deepskyblue"
if(MISC_LOG)
return "gray"
+ if(DEADCHAT_LOG)
+ return "#cc00c6"
+ if(OOC_LOG)
+ return "#002eb8"
+ if(LOOC_LOG)
+ return "#6699CC"
return "slategray"
+
+/datum/log_viewer/proc/get_display_name(mob/M)
+ var/name = M.name
+ if(M.name != M.real_name)
+ name = "[name] ([M.real_name])"
+ if(isobserver(M))
+ name = "[name] (DEAD)"
+ return "\[[M.last_known_ckey]\] [name]"
+
+/datum/log_viewer/proc/get_ckey_name(ckey)
+ UPDATE_CKEY_MOB(ckey)
+ var/mob/M = selected_ckeys_mobs[ckey]
+
+ return get_display_name(M)
+
+#undef UPDATE_CKEY_MOB
+#undef RECORD_WARN_LIMIT
+#undef RECORD_HARD_LIMIT
diff --git a/code/datums/logging.dm b/code/datums/logging.dm
new file mode 100644
index 00000000000..bac26610e5e
--- /dev/null
+++ b/code/datums/logging.dm
@@ -0,0 +1,47 @@
+/datum/logging
+ var/list/datum/log_record/logs = list() // Assoc list of assoc lists (ckey, (log_type, list/logs))
+
+/datum/logging/proc/add_log(ckey, datum/log_record/log)
+ if(!ckey)
+ log_debug("GLOB.logging.add_log called with an invalid ckey")
+ return
+
+ if(!logs[ckey])
+ logs[ckey] = list()
+
+ var/list/log_types_list = logs[ckey]
+
+ if(!log_types_list[log.log_type])
+ log_types_list[log.log_type] = list()
+
+ var/list/datum/log_record/log_records = log_types_list[log.log_type]
+ log_records.Add(log)
+
+/datum/logging/proc/get_ckeys_logged()
+ var/list/ckeys = list()
+ for(var/ckey in logs)
+ ckeys.Add(ckey)
+ return ckeys
+
+/* Returns the logs of a given ckey and log_type
+ * If no logs exist it will return an empty list
+*/
+/datum/logging/proc/get_logs_by_type(ckey, log_type)
+ if(!ckey)
+ log_debug("GLOB.logging.get_logs_by_type called with an invalid ckey")
+ return
+ if(!log_type || !(log_type in ALL_LOGS))
+ log_debug("GLOB.logging.get_logs_by_type called with an invalid log_type '[log_type]'")
+ return
+
+ var/list/log_types_list = logs[ckey]
+ // Check if logs exist for the ckey
+ if(!length(log_types_list))
+ return list()
+
+ var/list/datum/log_record/log_records = log_types_list[log_type]
+
+ // Check if logs exist for this type
+ if(!log_records)
+ return list()
+ return log_records
diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm
index f44a87bdd7a..006e92c305c 100644
--- a/code/datums/looping_sounds/looping_sound.dm
+++ b/code/datums/looping_sounds/looping_sound.dm
@@ -71,7 +71,7 @@
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
- S.channel = open_sound_channel()
+ S.channel = SSsounds.random_available_channel()
S.volume = volume
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 7d3a73abf24..0c992bbb32c 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -39,7 +39,6 @@
var/role_alt_title
var/datum/job/assigned_job
- var/list/kills = list()
var/list/datum/objective/objectives = list()
var/list/datum/objective/special_verbs = list()
var/list/targets = list()
@@ -48,7 +47,6 @@
var/miming = 0 // Mime's vow of silence
var/list/antag_datums
- var/speech_span // What span any body this mind has talks in.
var/datum/changeling/changeling //changeling holder
var/linglink
var/datum/vampire/vampire //vampire holder
@@ -66,8 +64,6 @@
var/isblessed = FALSE // is this person blessed by a chaplain?
var/num_blessed = 0 // for prayers
- // the world.time since the mob has been brigged, or -1 if not at all
- var/brigged_since = -1
var/suicided = FALSE
//put this here for easier tracking ingame
@@ -90,6 +86,9 @@
if(antag_datum.delete_on_mind_deletion)
qdel(i)
antag_datums = null
+ current = null
+ original = null
+ soulOwner = null
return ..()
/datum/mind/proc/transfer_to(mob/living/new_character)
@@ -101,14 +100,6 @@
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
- for(var/log_type in current.logs) // Copy the old logs
- var/list/logs = current.logs[log_type]
- if(new_character.logs[log_type])
- new_character.logs[log_type] += logs.Copy() // Append the old ones
- new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time
- else
- new_character.logs[log_type] = logs.Copy() // Just copy them
-
SSnanoui.user_transferred(current, new_character)
if(new_character.mind) //remove any mind currently in our new body's mind variable
@@ -545,11 +536,20 @@
if(objective&&(objective.type in objective_list) && objective:target)
def_target = objective.target.current
possible_targets = sortAtom(possible_targets)
- possible_targets += "Free objective"
- var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets
- if(!new_target)
- return
+ var/new_target
+ if(length(possible_targets) > 0)
+ if(alert(usr, "Do you want to pick the objective yourself? No will randomise it", "Pick objective", "Yes", "No") == "Yes")
+ possible_targets += "Free objective"
+ new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets
+ else
+ new_target = pick(possible_targets)
+
+ if(!new_target)
+ return
+ else
+ to_chat(usr, " No possible target found. Defaulting to a Free objective.")
+ new_target = "Free objective"
var/objective_path = text2path("/datum/objective/[new_obj_type]")
if(new_target == "Free objective")
@@ -1411,9 +1411,10 @@
return A
/datum/mind/proc/announce_objectives()
- to_chat(current, " Your current objectives:")
- for(var/line in splittext(gen_objective_text(), " "))
- to_chat(current, line)
+ if(current)
+ to_chat(current, " Your current objectives:")
+ for(var/line in splittext(gen_objective_text(), " "))
+ to_chat(current, line)
/datum/mind/proc/find_syndicate_uplink()
var/list/L = current.get_contents()
@@ -1582,25 +1583,6 @@
L = agent_landmarks[team]
H.forceMove(L.loc)
-
-// check whether this mind's mob has been brigged for the given duration
-// have to call this periodically for the duration to work properly
-/datum/mind/proc/is_brigged(duration)
- var/turf/T = current.loc
- if(!istype(T))
- brigged_since = -1
- return 0
-
- var/is_currently_brigged = current.is_in_brig()
- if(!is_currently_brigged)
- brigged_since = -1
- return 0
-
- if(brigged_since == -1)
- brigged_since = world.time
-
- return (duration <= world.time - brigged_since)
-
/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S)
spell_list += S
S.action.Grant(current)
diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm
index 14bc959fbb1..2b48d5da5f7 100644
--- a/code/datums/outfits/outfit_admin.dm
+++ b/code/datums/outfits/outfit_admin.dm
@@ -222,10 +222,10 @@
name = "NT Undercover Operative"
// Disguised NT special forces, sent to quietly eliminate or keep tabs on people in high positions (e.g: captain)
- uniform = /obj/item/clothing/under/color/black
+ uniform = /obj/item/clothing/under/color/random
back = /obj/item/storage/backpack
belt = /obj/item/storage/belt/utility/full/multitool
- gloves = /obj/item/clothing/gloves/combat
+ gloves = /obj/item/clothing/gloves/color/yellow
shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/centcom
id = /obj/item/card/id
diff --git a/code/datums/pipe_datums.dm b/code/datums/pipe_datums.dm
index 83b77425447..8d3b0807f9c 100644
--- a/code/datums/pipe_datums.dm
+++ b/code/datums/pipe_datums.dm
@@ -351,16 +351,6 @@ GLOBAL_LIST_EMPTY(rpd_pipe_list) //Some pipes we don't want to be dispensable
pipe_id = PIPE_CIRCULATOR
pipe_icon = "circ"
-/datum/pipes/atmospheric/omni_filter
- pipe_name = "omni filter"
- pipe_id = PIPE_OMNI_FILTER
- pipe_icon = "omni_filter"
-
-/datum/pipes/atmospheric/omni_mixer
- pipe_name = "omni mixer"
- pipe_id = PIPE_OMNI_MIXER
- pipe_icon = "omni_mixer"
-
/datum/pipes/atmospheric/insulated
pipe_name = "insulated pipe"
pipe_id = PIPE_INSULATED_STRAIGHT
diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm
index dae85c1726f..4a812b40e4e 100644
--- a/code/datums/progressbar.dm
+++ b/code/datums/progressbar.dm
@@ -39,7 +39,7 @@
if(user.client)
user.client.images += bar
- progress = Clamp(progress, 0, goal)
+ progress = clamp(progress, 0, goal)
bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]"
if(!shown)
user.client.images += bar
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index f826b6ff0d2..a19d75275fa 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -16,7 +16,7 @@
name = "Biodome Winter"
id = "biodome-winter"
description = "For those getaways where you want to get back to nature, but you don't want to leave the fortified military compound where you spend your days. \
- Includes a unique(*) laser pistol display case, and the recently introduced I.C.E(tm)."
+ Includes the recently introduced I.C.E(tm)."
suffix = "lavaland_biodome_winter.dmm"
/datum/map_template/ruin/lavaland/biodome/clown
diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm
index 018d5bb3496..3541be89983 100644
--- a/code/datums/ruins/space.dm
+++ b/code/datums/ruins/space.dm
@@ -175,6 +175,7 @@
description = "The crew of a space station awaken one hundred years after a crisis. Awaking to a derelict space station on the verge of collapse, and a hostile force of invading \
hivebots. Can the surviving crew overcome the odds and survive and rebuild, or will the cold embrace of the stars become their new home?"
cost = 2
+ allow_duplicates = FALSE
/datum/map_template/ruin/space/wizardcrash
id = "wizardcrash"
@@ -182,3 +183,94 @@
name = "Crashed Wizard Shuttle"
description = "A shuttle of the Wizard Federation, sent out to crush some wandless scum. Unfortunately, the pilot suffered a magic-related accident and the shuttle crashed into a nearby asteroid."
cost = 2
+
+/datum/map_template/ruin/space/abandonedtele
+ id = "abandonedtele"
+ suffix = "abandonedtele.dmm"
+ name = "Abandoned Teleporter"
+ description = "An old teleporter, seemingly part of what used to be a larger satellite."
+
+/datum/map_template/ruin/space/blowntcommsat
+ id = "blowntcommsat"
+ suffix = "blowntcommsat.dmm"
+ name = "Blown-out Telecommunications Satellite"
+ description = "The remains of an old telecommunications satellite once utilised by NanoTrasen. It lays derelict, with quite a few pieces missing."
+ cost = 5 // This is a chonky boy
+ allow_duplicates = FALSE // Absolutely huge, also has its own APC and the area isnt set to allow many
+
+/datum/map_template/ruin/space/clownmime
+ id = "clownmime"
+ suffix = "clownmime.dmm"
+ name = "Clown & Mime Mineral Deposits"
+ description = "A crash site of two opposing factions, both trying to complete mining trips for their own valuable minerals. While all the crew have long perished, the minerals are likely intact."
+
+/datum/map_template/ruin/space/dj
+ id = "dj"
+ suffix = "dj.dmm"
+ name = "Russian DJ Station"
+ description = "An old russian listening station, long since defunct and lifeless, however the equipment is likely still in working condition."
+ cost = 2
+
+/datum/map_template/ruin/space/druglab
+ id = "druglab"
+ suffix = "druglab.dmm"
+ name = "Drug Lab"
+ description = "An old abandoned \"Chemistry\" site, which has a strong aura of amphetamines around it."
+
+/datum/map_template/ruin/space/syndiedepot
+ id = "syndiedepot"
+ suffix = "syndiedepot.dmm"
+ name = "Suspicious Supply Depot"
+ description = "A syndicate supply depot, heavily stocked, but heavily guarded with an assortment of shields, sentry bots, armed operatives and more."
+ allow_duplicates = FALSE // One of these is enough
+ always_place = TRUE // This is on the always spawn list because of the shielding chance
+ cost = 0 // Force spawned so shouldnt have a cost
+
+/datum/map_template/ruin/space/ussp_tele
+ id = "ussp_tele"
+ suffix = "ussp_tele.dmm"
+ name = "USSP Teleporter"
+ description = "An old, almost fully destroyed teleporter, seemingly part of what used to be a much larger structure."
+
+/datum/map_template/ruin/space/ussp
+ id = "ussp"
+ suffix = "ussp.dmm"
+ name = "USSP"
+ description = "A decript station of seemingly russian origin. The last contact had with this station was a distress signal, and the rest was dark."
+ allow_duplicates = FALSE // One of these has enough loot
+ cost = 5 // This ruin is 100x100 tiles, so we dont want it to be treated like a 10x10 meteor
+
+/datum/map_template/ruin/space/whiteship
+ id = "whiteship"
+ suffix = "whiteship.dmm"
+ name = "NT Medical Ship"
+ description = "An old, abandoned NT medical ship. Its computer can navigate to other landmarks within space with ease."
+ allow_duplicates = FALSE // I dont even want to think about what happens if you have 2 shuttles with the same ID. Likely scary stuff.
+ always_place = TRUE // Its designed to make exploring other space ruins more accessible
+ cost = 0 // Force spawned so shouldnt have a cost
+
+/datum/map_template/ruin/space/syndiecakesfactory
+ id = "Syndiecakes Factory"
+ suffix = "syndiecakesfactory.dmm"
+ name = "Syndicakes Factory"
+ description = "Syndicate used to get funds selling corgi cakes produced here. Was it hit by meteors or by a Nanotrasen comando?"
+ allow_duplicates = FALSE
+ cost = 2 //telecomms + multiple mobs
+
+/datum/map_template/ruin/space/debris1
+ id = "debris1"
+ suffix = "debris1.dmm"
+ name = "Debris field 1"
+ description = "A bunch of metal chunks, wires and space waste"
+
+/datum/map_template/ruin/space/debris2
+ id = "debris2"
+ suffix = "debris2.dmm"
+ name = "Debris field 2"
+ description = "A bunch of metal chunks, wires and space waste that used to be some kind of secure storage facility"
+
+/datum/map_template/ruin/space/debris3
+ id = "debris3"
+ suffix = "debris3.dmm"
+ name = "Debris field 3"
+ description = "A bunch of metal chunks, wires and space waste. It used to be an arcade."
diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm
index 53517d5dbc9..dacc08dee31 100644
--- a/code/datums/shuttles.dm
+++ b/code/datums/shuttles.dm
@@ -9,8 +9,9 @@
var/admin_notes
/datum/map_template/shuttle/New()
- shuttle_id = "[port_id]_[suffix]"
- mappath = "[prefix][shuttle_id].dmm"
+ if(port_id && suffix)
+ shuttle_id = "[port_id]_[suffix]"
+ mappath = "[prefix][shuttle_id].dmm"
. = ..()
/datum/map_template/shuttle/emergency
@@ -132,3 +133,8 @@
suffix = "admin"
name = "NTV Argos"
description = "Default Admin ship. An older ship used for special operations."
+
+/datum/map_template/shuttle/admin/armory
+ suffix = "armory"
+ name = "NRV Sparta"
+ description = "Armory Shuttle, with plenty of guns to hand out and some general supplies."
diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm
index a651edbc79c..a196a60fcc5 100644
--- a/code/datums/spawners_menu.dm
+++ b/code/datums/spawners_menu.dm
@@ -6,44 +6,47 @@
qdel(src)
owner = new_owner
-/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = GLOB.ghost_state, datum/nanoui/master_ui = null)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/datum/spawners_menu/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui_state/state = GLOB.tgui_observer_state, datum/tgui/master_ui = null)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "spawners_menu.tmpl", "Spawners Menu", 700, 600, master_ui, state = state)
+ ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state = state)
ui.open()
-/datum/spawners_menu/ui_data(mob/user)
+/datum/spawners_menu/tgui_data(mob/user)
var/list/data = list()
data["spawners"] = list()
for(var/spawner in GLOB.mob_spawners)
var/list/this = list()
this["name"] = spawner
this["desc"] = ""
+ this["important_info"] = ""
+ this["fluff"] = ""
this["uids"] = list()
- for(var/spawner_obj in GLOB.mob_spawners[spawner])
+ for(var/spawner_obj in GLOB.mob_spawners[spawner])//each spawner can contain multiple actual spawners, we use only one desc/info
this["uids"] += "\ref[spawner_obj]"
- if(!this["desc"])
+ if(!this["desc"]) //haven't set descriptions yet
if(istype(spawner_obj, /obj/effect/mob_spawn))
var/obj/effect/mob_spawn/MS = spawner_obj
- this["desc"] = MS.flavour_text
+ this["desc"] = MS.description
+ this["important_info"] = MS.important_info
+ this["fluff"] = MS.flavour_text
else
var/obj/O = spawner_obj
this["desc"] = O.desc
this["amount_left"] = LAZYLEN(GLOB.mob_spawners[spawner])
data["spawners"] += list(this)
-
return data
-/datum/spawners_menu/Topic(href, href_list)
+/datum/spawners_menu/tgui_act(action, params)
if(..())
- return 1
- var/spawners = replacetext(href_list["uid"], ",", ";")
+ return
+ var/spawners = replacetext(params["ID"], ",", ";")
var/list/possible_spawners = params2list(spawners)
var/obj/effect/mob_spawn/MS = locate(pick(possible_spawners))
if(!MS || !istype(MS))
log_runtime(EXCEPTION("A ghost tried to interact with an invalid spawner, or the spawner didn't exist."))
return
- switch(href_list["action"])
+ switch(action)
if("jump")
owner.forceMove(get_turf(MS))
. = TRUE
diff --git a/code/datums/spell.dm b/code/datums/spell.dm
index 6b114971258..cb10700e5c6 100644
--- a/code/datums/spell.dm
+++ b/code/datums/spell.dm
@@ -237,10 +237,10 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
if(action)
action.UpdateButtonIcon()
-/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells
+/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr, make_attack_logs = TRUE) //if recharge is started is important for the trigger spells
before_cast(targets)
invocation()
- if(user && user.ckey)
+ if(user && user.ckey && make_attack_logs)
add_attack_logs(user, targets, "cast the spell [name]", ATKLOG_ALL)
spawn(0)
if(charge_type == "recharge" && recharge)
@@ -444,6 +444,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
return
+// Normally, AoE spells will generate an attack log for every turf they loop over, while searching for targets.
+// With this override, all /aoe_turf type spells will only generate 1 log, saying that the user has cast the spell.
+/obj/effect/proc_holder/spell/aoe_turf/perform(list/targets, recharge, mob/user, make_attack_logs)
+ add_attack_logs(user, null, "Cast the AoE spell [name]", ATKLOG_ALL)
+ return ..(targets, recharge, user, FALSE)
/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B)
//Checks for obstacles from A to B
diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm
index dc2efcc1e91..e4c652ae1d3 100644
--- a/code/datums/spells/knock.dm
+++ b/code/datums/spells/knock.dm
@@ -30,8 +30,6 @@
SC.locked = 0
C.open()
- return
-
/obj/effect/proc_holder/spell/aoe_turf/knock/greater
name = "Greater Knock"
desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!"
diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm
index 2a8e1d94526..a0c41b57001 100644
--- a/code/datums/spells/summonitem.dm
+++ b/code/datums/spells/summonitem.dm
@@ -75,8 +75,6 @@
B.transfer_identity(C)
C.death()
add_attack_logs(target, C, "Magically debrained INTENT: [uppertext(target.a_intent)]")*/
- if(C.stomach_contents && (item_to_retrieve in C.stomach_contents))
- C.stomach_contents -= item_to_retrieve
for(var/X in C.bodyparts)
var/obj/item/organ/external/part = X
if(item_to_retrieve in part.embedded_objects)
diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm
index 6a951ac66a0..94383f1b1e2 100644
--- a/code/datums/spells/wizard.dm
+++ b/code/datums/spells/wizard.dm
@@ -423,7 +423,7 @@
M.Weaken(stun_amt)
to_chat(M, " You're thrown back by a mystical force!")
spawn(0)
- AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time.
+ AM.throw_at(throwtarget, ((clamp((maxthrow - (clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time.
/obj/effect/proc_holder/spell/targeted/sacred_flame
name = "Sacred Flame"
diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm
index 74abf104465..2580b1c9935 100644
--- a/code/datums/status_effects/status_effect.dm
+++ b/code/datums/status_effects/status_effect.dm
@@ -5,7 +5,7 @@
/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/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second. Setting this to -1 will stop processing if duration is also unlimited.
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
@@ -31,7 +31,8 @@
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
- START_PROCESSING(SSfastprocess, src)
+ if(duration > 0 || initial(tick_interval) > 0) //don't process if we don't care
+ START_PROCESSING(SSfastprocess, src)
return TRUE
/datum/status_effect/Destroy()
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index a18a9d74f28..c8f2b6bb99b 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -633,7 +633,6 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
contains = list(/obj/machinery/power/emitter,
/obj/machinery/power/emitter)
cost = 10
- containertype = /obj/structure/closet/crate/secure
containername = "emitter crate"
access = ACCESS_CE
containertype = /obj/structure/closet/crate/secure/engineering
@@ -717,7 +716,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
name = "Supermatter Shard Crate"
contains = list(/obj/machinery/power/supermatter_shard)
cost = 50 //So cargo thinks twice before killing themselves with it
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/engineering
containername = "supermatter shard crate"
access = ACCESS_CE
@@ -728,7 +727,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
/obj/item/pipe/circulator,
/obj/item/pipe/circulator)
cost = 25
- containertype = /obj/structure/closet/crate/secure
+ containertype = /obj/structure/closet/crate/secure/engineering
containername = "thermo-electric generator crate"
access = ACCESS_CE
announce_beacons = list("Engineering" = list("Chief Engineer's Desk", "Atmospherics"))
@@ -1174,11 +1173,10 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containername = "fox crate"
/datum/supply_packs/organic/butterfly
- name = "Butterflies Crate"
+ name = "Butterfly Crate"
cost = 50
containertype = /obj/structure/closet/critter/butterfly
- containername = "butterflies crate"
- contraband = 1
+ containername = "butterfly crate"
/datum/supply_packs/organic/deer
name = "Deer Crate"
@@ -1767,6 +1765,16 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
cost = 20
containername = "polo supply crate"
+/datum/supply_packs/misc/boxing //For non log spamming cargo brawls!
+ name = "Boxing Supply Crate"
+ // 4 boxing gloves
+ contains = list(/obj/item/clothing/gloves/boxing/blue,
+ /obj/item/clothing/gloves/boxing/green,
+ /obj/item/clothing/gloves/boxing/yellow,
+ /obj/item/clothing/gloves/boxing)
+ cost = 15
+ containername = "boxing supply crate"
+
///////////// Station Goals
/datum/supply_packs/misc/station_goal
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index a0c9a19e4b0..3562cfd9d80 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -188,15 +188,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/storage/box/syndie_kit/fake_revolver
cost = 1
job = list("Clown")
-/*
-/datum/uplink_item/stealthy_weapons/romerol_kit
- name = "Romerol"
- reference = "ROM"
- desc = "A highly experimental bioterror agent which creates dormant nodules to be etched into the grey matter of the brain. On death, these nodules take control of the dead body, causing limited revivification, along with slurred speech, aggression, and the ability to infect others with this agent."
- item = /obj/item/storage/box/syndie_kit/romerol
- cost = 25
- cant_discount = TRUE
-*/
//mime
/datum/uplink_item/jobspecific/caneshotgun
name = "Cane Shotgun and Assassination Shells"
@@ -247,6 +238,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 2
job = list("Chef")
+/datum/uplink_item/jobspecific/Chef_CQC
+ name = " A chefs manual to CQC"
+ desc = "An old manual teaching you how to bring your home advantage outside the kitchen."
+ reference = "CCQC"
+ item = /obj/item/CQC_manual/chef
+ cost = 12
+ job = list("Chef")
+
//Chaplain
/datum/uplink_item/jobspecific/voodoo
@@ -737,6 +736,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 12 // normally 18
gamemodes = list(/datum/game_mode/nuclear)
+/datum/uplink_item/ammo/bulldog_XLmagsbag
+ name = "Bulldog - 12g XL Magazine Duffel Bag"
+ desc = "A duffel bag containing three 16 round drum magazines(Slug, Buckshot, Dragon's Breath)."
+ reference = "12XLDB"
+ item = /obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
+ cost = 12 // normally 18
+ gamemodes = list(/datum/game_mode/nuclear)
+
/datum/uplink_item/ammo/smg
name = "C-20r - .45 Magazine"
desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage."
@@ -1726,8 +1733,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
U.purchase_log += " [bicon(C)]"
for(var/item in bought_items)
- new item(C)
- U.purchase_log += " [bicon(item)]"
+ var/obj/purchased = new item(C)
+ U.purchase_log += " [bicon(purchased)]"
log_game("[key_name(usr)] purchased a surplus crate with [jointext(itemlog, ", ")]")
/datum/uplink_item/bundles_TC/telecrystal
diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm
index 9f98a481dbf..0233e0aee71 100644
--- a/code/datums/weather/weather_types/floor_is_lava.dm
+++ b/code/datums/weather/weather_types/floor_is_lava.dm
@@ -35,6 +35,8 @@
return
if(!L.client) //Only sentient people are going along with it!
return
+ if(L.flying)
+ return
L.adjustFireLoss(3)
/datum/weather/floor_is_lava/fake
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index e3d90b8d075..ff7a0169c15 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -1,67 +1,29 @@
// Wires for airlocks
/datum/wires/airlock/secure
- random = 1
+ randomize = TRUE
/datum/wires/airlock
holder_type = /obj/machinery/door/airlock
- wire_count = 12
- window_x = 410
- window_y = 570
+ wire_count = 12 // 10 actual, 2 duds.
+ proper_name = "Airlock"
+ window_x = 400
+ window_y = 101
-#define AIRLOCK_WIRE_IDSCAN 1
-#define AIRLOCK_WIRE_MAIN_POWER1 2
-#define AIRLOCK_WIRE_DOOR_BOLTS 4
-#define AIRLOCK_WIRE_BACKUP_POWER1 8
-#define AIRLOCK_WIRE_OPEN_DOOR 16
-#define AIRLOCK_WIRE_AI_CONTROL 32
-#define AIRLOCK_WIRE_ELECTRIFY 64
-#define AIRLOCK_WIRE_SAFETY 128
-#define AIRLOCK_WIRE_SPEED 256
-#define AIRLOCK_WIRE_LIGHT 512
+/datum/wires/airlock/New(atom/_holder)
+ wires = list(
+ WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_DOOR_BOLTS, WIRE_BACKUP_POWER1, WIRE_OPEN_DOOR,
+ WIRE_AI_CONTROL, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SPEED, WIRE_BOLT_LIGHT
+ )
+ return ..()
-/datum/wires/airlock/GetWireName(index)
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
- return "ID Scan"
-
- if(AIRLOCK_WIRE_MAIN_POWER1)
- return "Primary Power"
-
- if(AIRLOCK_WIRE_DOOR_BOLTS)
- return "Door Bolts"
-
- if(AIRLOCK_WIRE_BACKUP_POWER1)
- return "Primary Backup Power"
-
- if(AIRLOCK_WIRE_OPEN_DOOR)
- return "Door State"
-
- if(AIRLOCK_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Door Safeties"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Door Timing"
-
- if(AIRLOCK_WIRE_ELECTRIFY)
- return "Bolt Lights"
-
-/datum/wires/airlock/CanUse(mob/living/L)
+/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
- if(iscarbon(L))
- if(A.Adjacent(L))
- if(A.isElectrified())
- if(A.shock(L, 100))
- return 0
+ if(iscarbon(user) && A.Adjacent(user) && A.isElectrified() && A.shock(user, 100))
+ return FALSE
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/airlock/get_status()
. = ..()
@@ -70,21 +32,20 @@
. += "The door bolts [A.locked ? "have fallen!" : "look up."]"
. += "The door bolt lights are [(A.lights && haspower) ? "on." : "off!"]"
- . += "The test light is [haspower ? "on." : "off!"]"
- . += "The 'AI control allowed' light is [(A.aiControlDisabled == 0 && !A.emagged && haspower) ? "on" : "off"]."
+ . += "The test light is [haspower ? "on." : "off!"]"
+ . += "The 'AI control allowed' light is [(A.aiControlDisabled == AICONTROLDISABLED_OFF && !A.emagged && haspower) ? "on" : "off"]."
. += "The 'Check Wiring' light is [(A.safe == 0 && haspower) ? "on" : "off"]."
. += "The 'Check Timing Mechanism' light is [(A.normalspeed == 0 && haspower) ? "on" : "off"]."
. += "The emergency lights are [(A.emergency && haspower) ? "on" : "off"]."
-/datum/wires/airlock/UpdateCut(index, mended)
-
+/datum/wires/airlock/on_cut(wire, mend)
var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
- A.aiDisabledIdScanner = !mended
- if(AIRLOCK_WIRE_MAIN_POWER1)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.aiDisabledIdScanner = !mend
+ if(WIRE_MAIN_POWER1)
- if(!mended)
+ if(!mend)
//Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user.
A.loseMainPower()
A.shock(usr, 50)
@@ -92,9 +53,9 @@
A.regainMainPower()
A.shock(usr, 50)
- if(AIRLOCK_WIRE_BACKUP_POWER1)
+ if(WIRE_BACKUP_POWER1)
- if(!mended)
+ if(!mend)
//Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user.
A.loseBackupPower()
A.shock(usr, 50)
@@ -102,113 +63,109 @@
A.regainBackupPower()
A.shock(usr, 50)
- if(AIRLOCK_WIRE_DOOR_BOLTS)
+ if(WIRE_DOOR_BOLTS)
- if(!mended)
+ if(!mend)
//Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present)
A.lock(1)
A.update_icon()
- if(AIRLOCK_WIRE_AI_CONTROL)
+ if(WIRE_AI_CONTROL)
- if(!mended)
+ if(!mend)
//one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all.
- //aiControlDisabled: 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.
- if(A.aiControlDisabled == 0)
- A.aiControlDisabled = 1
- else if(A.aiControlDisabled == -1)
- A.aiControlDisabled = 2
+ //aiControlDisabled: see explanation in code\__DEFINES\construction.dm#32
+ if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
+ A.aiControlDisabled = AICONTROLDISABLED_ON
+ else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
+ A.aiControlDisabled = AICONTROLDISABLED_BYPASS
else
- if(A.aiControlDisabled == 1)
- A.aiControlDisabled = 0
- else if(A.aiControlDisabled == 2)
- A.aiControlDisabled = -1
+ if(A.aiControlDisabled == AICONTROLDISABLED_ON)
+ A.aiControlDisabled = AICONTROLDISABLED_OFF
+ else if(A.aiControlDisabled == AICONTROLDISABLED_BYPASS)
+ A.aiControlDisabled = AICONTROLDISABLED_PERMA
- if(AIRLOCK_WIRE_ELECTRIFY)
- if(!mended)
+ if(WIRE_ELECTRIFY)
+ if(!mend)
//Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted.
A.electrify(-1)
else
A.electrify(0)
return // Don't update the dialog.
- if(AIRLOCK_WIRE_SAFETY)
- A.safe = mended
+ if(WIRE_SAFETY)
+ A.safe = mend
- if(AIRLOCK_WIRE_SPEED)
- A.autoclose = mended
- if(mended)
+ if(WIRE_SPEED)
+ A.autoclose = mend
+ if(mend)
if(!A.density)
- spawn(0)
- A.close()
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
- if(AIRLOCK_WIRE_LIGHT)
- A.lights = mended
+ if(WIRE_BOLT_LIGHT)
+ A.lights = mend
A.update_icon()
..()
-/datum/wires/airlock/UpdatePulsed(index)
-
+/datum/wires/airlock/on_pulse(wire)
var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(AIRLOCK_WIRE_IDSCAN)
+ switch(wire)
+ if(WIRE_IDSCAN)
//Sending a pulse through flashes the red light on the door (if the door has power).
if(A.arePowerSystemsOn() && A.density)
A.do_animate("deny")
if(A.emergency)
A.emergency = 0
A.update_icon()
- if(AIRLOCK_WIRE_MAIN_POWER1)
+
+ if(WIRE_MAIN_POWER1)
//Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter).
A.loseMainPower()
- if(AIRLOCK_WIRE_DOOR_BOLTS)
+
+ if(WIRE_DOOR_BOLTS)
//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)
if(A.lock())
- A.audible_message(" You hear a click from the bottom of the door.", null, 1)
+ A.audible_message(" You hear a click from the bottom of the door.", hearing_distance = 1)
else if(A.unlock())
- A.audible_message(" You hear a click from the bottom of the door.", null, 1)
+ A.audible_message(" You hear a click from the bottom of the door.", hearing_distance = 1)
- if(AIRLOCK_WIRE_BACKUP_POWER1)
+ if(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).
A.loseBackupPower()
- if(AIRLOCK_WIRE_AI_CONTROL)
- if(A.aiControlDisabled == 0)
- A.aiControlDisabled = 1
- else if(A.aiControlDisabled == -1)
- A.aiControlDisabled = 2
- spawn(10)
- if(A)
- if(A.aiControlDisabled == 1)
- A.aiControlDisabled = 0
- else if(A.aiControlDisabled == 2)
- A.aiControlDisabled = -1
+ if(WIRE_AI_CONTROL)
+ if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
+ A.aiControlDisabled = AICONTROLDISABLED_ON
+ else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
+ A.aiControlDisabled = AICONTROLDISABLED_BYPASS
- if(AIRLOCK_WIRE_ELECTRIFY)
+ addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/ai_control_callback), 1 SECONDS)
+
+ if(WIRE_ELECTRIFY)
//one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds.
A.electrify(30)
- if(AIRLOCK_WIRE_OPEN_DOOR)
+
+ if(WIRE_OPEN_DOOR)
//tries to open the door without ID
//will succeed only if the ID wire is cut or the door requires no access and it's not emagged
if(A.emagged) return
if(!A.requiresID() || A.check_access(null))
- spawn(0)
- if(A.density)
- A.open()
- else
- A.close()
- if(AIRLOCK_WIRE_SAFETY)
+ if(A.density)
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/open)
+ else
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
+
+ if(WIRE_SAFETY)
A.safe = !A.safe
if(!A.density)
- spawn(0)
- A.close()
+ INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close)
- if(AIRLOCK_WIRE_SPEED)
+ if(WIRE_SPEED)
A.normalspeed = !A.normalspeed
- if(AIRLOCK_WIRE_LIGHT)
+ if(WIRE_BOLT_LIGHT)
A.lights = !A.lights
A.update_icon()
diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm
index f6b49b0dd99..cd13c9fe1d8 100644
--- a/code/datums/wires/alarm.dm
+++ b/code/datums/wires/alarm.dm
@@ -2,35 +2,22 @@
/datum/wires/alarm
holder_type = /obj/machinery/alarm
wire_count = 5
+ window_x = 385
+ window_y = 90
+ proper_name = "Air alarm"
-#define AALARM_WIRE_IDSCAN 1
-#define AALARM_WIRE_POWER 2
-#define AALARM_WIRE_SYPHON 4
-#define AALARM_WIRE_AI_CONTROL 8
-#define AALARM_WIRE_AALARM 16
+/datum/wires/alarm/New(atom/_holder)
+ wires = list(
+ WIRE_IDSCAN , WIRE_MAIN_POWER1 , WIRE_SYPHON,
+ WIRE_AI_CONTROL, WIRE_AALARM
+ )
+ return ..()
-/datum/wires/alarm/GetWireName(index)
- switch(index)
- if(AALARM_WIRE_IDSCAN)
- return "ID Scan"
-
- if(AALARM_WIRE_POWER)
- return "Power"
-
- if(AALARM_WIRE_SYPHON)
- return "Syphon"
-
- if(AALARM_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(AALARM_WIRE_AALARM)
- return "Atmospherics Alarm"
-
-/datum/wires/alarm/CanUse(mob/living/L)
+/datum/wires/alarm/interactable(mob/user)
var/obj/machinery/alarm/A = holder
if(A.wiresexposed)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/alarm/get_status()
. = ..()
@@ -39,75 +26,60 @@
. += "The Air Alarm is [(A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "offline." : "working properly!"]"
. += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]."
-/datum/wires/alarm/UpdateCut(index, mended)
+/datum/wires/alarm/on_cut(wire, mend)
var/obj/machinery/alarm/A = holder
- switch(index)
- if(AALARM_WIRE_IDSCAN)
- if(!mended)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ if(!mend)
A.locked = 1
-// to_chat(world, "Idscan wire cut")
- if(AALARM_WIRE_POWER)
+ if(WIRE_MAIN_POWER1)
A.shock(usr, 50)
- A.shorted = !mended
+ A.shorted = !mend
A.update_icon()
-// to_chat(world, "Power wire cut")
- if(AALARM_WIRE_AI_CONTROL)
- A.aidisabled = !mended
-// to_chat(world, "AI Control Wire Cut")
+ if(WIRE_AI_CONTROL)
+ A.aidisabled = !mend
- if(AALARM_WIRE_SYPHON)
- if(!mended)
+ if(WIRE_SYPHON)
+ if(!mend)
A.mode = 3 // AALARM_MODE_PANIC
A.apply_mode()
-// to_chat(world, "Syphon Wire Cut")
- if(AALARM_WIRE_AALARM)
- if(A.alarm_area.atmosalert(2, A))
- A.post_alert(2)
+ if(WIRE_AALARM)
+ if(A.alarm_area.atmosalert(ATMOS_ALARM_DANGER, A))
+ A.post_alert(ATMOS_ALARM_DANGER)
A.update_icon()
..()
-/datum/wires/alarm/UpdatePulsed(index)
+/datum/wires/alarm/on_pulse(wire)
var/obj/machinery/alarm/A = holder
- switch(index)
- if(AALARM_WIRE_IDSCAN)
+ switch(wire)
+ if(WIRE_IDSCAN)
A.locked = !A.locked
-// to_chat(world, "Idscan wire pulsed")
- if(AALARM_WIRE_POWER)
-// to_chat(world, "Power wire pulsed")
- if(A.shorted == 0)
- A.shorted = 1
+ if(WIRE_MAIN_POWER1)
+ if(!A.shorted)
+ A.shorted = TRUE
A.update_icon()
+ addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/unshort_callback), 120 SECONDS)
- spawn(12000)
- if(A.shorted == 1)
- A.shorted = 0
- A.update_icon()
-
-
- if(AALARM_WIRE_AI_CONTROL)
-// to_chat(world, "AI Control wire pulsed")
- if(A.aidisabled == 0)
- A.aidisabled = 1
+ if(WIRE_AI_CONTROL)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
A.updateDialog()
- spawn(100)
- if(A.aidisabled == 1)
- A.aidisabled = 0
+ addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/enable_ai_control_callback), 10 SECONDS)
- if(AALARM_WIRE_SYPHON)
-// to_chat(world, "Syphon wire pulsed")
+
+ if(WIRE_SYPHON)
if(A.mode == 1) // AALARM_MODE_SCRUB
A.mode = 3 // AALARM_MODE_PANIC
else
A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode()
- if(AALARM_WIRE_AALARM)
-// to_chat(world, "Aalarm wire pulsed")
- if(A.alarm_area.atmosalert(0, A))
- A.post_alert(0)
+ if(WIRE_AALARM)
+ if(A.alarm_area.atmosalert(ATMOS_ALARM_NONE, A))
+ A.post_alert(ATMOS_ALARM_NONE)
A.update_icon()
..()
diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm
index b1076c8e29b..49931d8fa31 100644
--- a/code/datums/wires/apc.dm
+++ b/code/datums/wires/apc.dm
@@ -1,25 +1,13 @@
/datum/wires/apc
holder_type = /obj/machinery/power/apc
wire_count = 4
+ proper_name = "APC"
+ window_x = 355
+ window_y = 97
-#define APC_WIRE_IDSCAN 1
-#define APC_WIRE_MAIN_POWER1 2
-#define APC_WIRE_MAIN_POWER2 4
-#define APC_WIRE_AI_CONTROL 8
-
-/datum/wires/apc/GetWireName(index)
- switch(index)
- if(APC_WIRE_IDSCAN)
- return "ID Scan"
-
- if(APC_WIRE_MAIN_POWER1)
- return "Primary Power"
-
- if(APC_WIRE_MAIN_POWER2)
- return "Secondary Power"
-
- if(APC_WIRE_AI_CONTROL)
- return "AI Control"
+/datum/wires/apc/New(atom/_holder)
+ wires = list(WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_AI_CONTROL)
+ return ..()
/datum/wires/apc/get_status()
. = ..()
@@ -29,66 +17,53 @@
. += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]."
-/datum/wires/apc/CanUse(mob/living/L)
+/datum/wires/apc/interactable(mob/user)
var/obj/machinery/power/apc/A = holder
if(A.panel_open && !A.opened)
return TRUE
return FALSE
-/datum/wires/apc/UpdatePulsed(index)
+/datum/wires/apc/on_pulse(wire)
var/obj/machinery/power/apc/A = holder
- switch(index)
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.locked = FALSE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/relock_callback), 30 SECONDS)
- if(APC_WIRE_IDSCAN)
- A.locked = 0
- spawn(300)
- if(A)
- A.locked = 1
- A.updateDialog()
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
+ if(!A.shorted)
+ A.shorted = TRUE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_main_power_callback), 120 SECONDS)
- if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
- if(A.shorted == 0)
- A.shorted = 1
- spawn(1200)
- if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
- A.shorted = 0
- A.updateDialog()
-
- if(APC_WIRE_AI_CONTROL)
- if(A.aidisabled == 0)
- A.aidisabled = 1
-
- spawn(10)
- if(A && !IsIndexCut(APC_WIRE_AI_CONTROL))
- A.aidisabled = 0
- A.updateDialog()
+ if(WIRE_AI_CONTROL)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
+ addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_ai_control_callback), 1 SECONDS)
..()
-/datum/wires/apc/UpdateCut(index, mended)
+/datum/wires/apc/on_cut(wire, mend)
var/obj/machinery/power/apc/A = holder
- switch(index)
- if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
-
- if(!mended)
+ switch(wire)
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
+ if(!mend)
A.shock(usr, 50)
- A.shorted = 1
+ A.shorted = TRUE
- else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
- A.shorted = 0
+ else if(!is_cut(WIRE_MAIN_POWER1) && !is_cut(WIRE_MAIN_POWER2))
+ A.shorted = FALSE
A.shock(usr, 50)
- if(APC_WIRE_AI_CONTROL)
-
- if(!mended)
- if(A.aidisabled == 0)
- A.aidisabled = 1
+ if(WIRE_AI_CONTROL)
+ if(!mend)
+ if(!A.aidisabled)
+ A.aidisabled = TRUE
else
- if(A.aidisabled == 1)
- A.aidisabled = 0
+ if(A.aidisabled)
+ A.aidisabled = FALSE
..()
diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm
index c1413abd0cc..0d2aab5a81f 100644
--- a/code/datums/wires/autolathe.dm
+++ b/code/datums/wires/autolathe.dm
@@ -1,21 +1,13 @@
/datum/wires/autolathe
holder_type = /obj/machinery/autolathe
wire_count = 10
+ proper_name = "Autolathe"
+ window_x = 340
+ window_y = 55
-#define AUTOLATHE_HACK_WIRE 1
-#define AUTOLATHE_SHOCK_WIRE 2
-#define AUTOLATHE_DISABLE_WIRE 4
-
-/datum/wires/autolathe/GetWireName(index)
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
- return "Hack"
-
- if(AUTOLATHE_SHOCK_WIRE)
- return "Shock"
-
- if(AUTOLATHE_DISABLE_WIRE)
- return "Disable"
+/datum/wires/autolathe/New(atom/_holder)
+ wires = list(WIRE_AUTOLATHE_HACK, WIRE_ELECTRIFY, WIRE_AUTOLATHE_DISABLE)
+ return ..()
/datum/wires/autolathe/get_status()
. = ..()
@@ -24,51 +16,38 @@
. += "The green light is [A.shocked ? "off" : "on"]."
. += "The blue light is [A.hacked ? "off" : "on"]."
-/datum/wires/autolathe/CanUse()
+/datum/wires/autolathe/interactable(mob/user)
var/obj/machinery/autolathe/A = holder
+ if(iscarbon(user) && A.Adjacent(user) && A.shocked && A.shock(user, 100))
+ return FALSE
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/autolathe/UpdateCut(index, mended)
+/datum/wires/autolathe/on_cut(wire, mend)
var/obj/machinery/autolathe/A = holder
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
- A.adjust_hacked(!mended)
- if(AUTOLATHE_SHOCK_WIRE)
- A.shocked = !mended
- if(AUTOLATHE_DISABLE_WIRE)
- A.disabled = !mended
+ switch(wire)
+ if(WIRE_AUTOLATHE_HACK)
+ A.adjust_hacked(!mend)
+ if(WIRE_ELECTRIFY)
+ A.shocked = !mend
+ if(WIRE_AUTOLATHE_DISABLE)
+ A.disabled = !mend
..()
-/datum/wires/autolathe/UpdatePulsed(index)
- if(IsIndexCut(index))
+/datum/wires/autolathe/on_pulse(wire)
+ if(is_cut(wire))
return
var/obj/machinery/autolathe/A = holder
- switch(index)
- if(AUTOLATHE_HACK_WIRE)
+ switch(wire)
+ if(WIRE_AUTOLATHE_HACK)
A.adjust_hacked(!A.hacked)
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.adjust_hacked(0)
- updateUIs()
- if(AUTOLATHE_SHOCK_WIRE)
- A.shocked = !A.shocked
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.shocked = 0
- updateUIs()
- if(AUTOLATHE_DISABLE_WIRE)
- A.disabled = !A.disabled
- updateUIs()
- spawn(50)
- if(A && !IsIndexCut(index))
- A.disabled = 0
- updateUIs()
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_hacked_callback), 5 SECONDS)
-/datum/wires/autolathe/proc/updateUIs()
- SSnanoui.update_uis(src)
- if(holder)
- SSnanoui.update_uis(holder)
+ if(WIRE_ELECTRIFY)
+ A.shocked = !A.shocked
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_electrified_callback), 5 SECONDS)
+
+ if(WIRE_AUTOLATHE_DISABLE)
+ A.disabled = !A.disabled
+ addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_disabled_callback), 5 SECONDS)
diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm
index a6b1549fbe1..39e0cb2c3e1 100644
--- a/code/datums/wires/camera.dm
+++ b/code/datums/wires/camera.dm
@@ -1,9 +1,15 @@
// Wires for cameras.
/datum/wires/camera
- random = 0
holder_type = /obj/machinery/camera
wire_count = 2
+ proper_name = "Camera"
+ window_x = 350
+ window_y = 95
+
+/datum/wires/camera/New(atom/_holder)
+ wires = list(WIRE_FOCUS, WIRE_MAIN_POWER1)
+ return ..()
/datum/wires/camera/get_status()
. = ..()
@@ -11,51 +17,40 @@
. += "The focus light is [(C.view_range == initial(C.view_range)) ? "on" : "off"]."
. += "The power link light is [C.can_use() ? "on" : "off"]."
-/datum/wires/camera/CanUse(mob/living/L)
+/datum/wires/camera/interactable(mob/user)
var/obj/machinery/camera/C = holder
if(!C.panel_open)
return FALSE
return TRUE
-#define CAMERA_WIRE_FOCUS 1
-#define CAMERA_WIRE_POWER 2
-
-/datum/wires/camera/GetWireName(index)
- switch(index)
- if(CAMERA_WIRE_FOCUS)
- return "Focus"
-
- if(CAMERA_WIRE_POWER)
- return "Power"
-
-/datum/wires/camera/UpdateCut(index, mended)
+/datum/wires/camera/on_cut(wire, mend)
var/obj/machinery/camera/C = holder
- switch(index)
- if(CAMERA_WIRE_FOCUS)
- var/range = (mended ? initial(C.view_range) : C.short_range)
+ switch(wire)
+ if(WIRE_FOCUS)
+ var/range = (mend ? initial(C.view_range) : C.short_range)
C.setViewRange(range)
- if(CAMERA_WIRE_POWER)
- if(C.status && !mended || !C.status && mended)
+ if(WIRE_MAIN_POWER1)
+ if(C.status && !mend || !C.status && mend)
C.toggle_cam(usr, TRUE)
C.obj_integrity = C.max_integrity //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex.
..()
-/datum/wires/camera/UpdatePulsed(index)
+/datum/wires/camera/on_pulse(wire)
var/obj/machinery/camera/C = holder
- if(IsIndexCut(index))
+ if(is_cut(wire))
return
- switch(index)
- if(CAMERA_WIRE_FOCUS)
+ switch(wire)
+ if(WIRE_FOCUS)
var/new_range = (C.view_range == initial(C.view_range) ? C.short_range : initial(C.view_range))
C.setViewRange(new_range)
- if(CAMERA_WIRE_POWER)
+ if(WIRE_MAIN_POWER1)
C.toggle_cam(null) // Deactivate the camera
..()
/datum/wires/camera/proc/CanDeconstruct()
- if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS))
+ if(is_cut(WIRE_MAIN_POWER1) && is_cut(WIRE_FOCUS))
return TRUE
else
return FALSE
diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm
index 47fd627d597..988aa0f37a8 100644
--- a/code/datums/wires/explosive.dm
+++ b/code/datums/wires/explosive.dm
@@ -1,36 +1,36 @@
/datum/wires/explosive
wire_count = 1
+ proper_name = "Explosive"
+ window_x = 320
+ window_y = 50
-#define WIRE_EXPLODE 1
-
-/datum/wires/explosive/GetWireName(index)
- switch(index)
- if(WIRE_EXPLODE)
- return "Explode"
+/datum/wires/explosive/New(atom/_holder)
+ wires = list(WIRE_EXPLODE)
+ return ..()
/datum/wires/explosive/proc/explode()
return
-/datum/wires/explosive/UpdatePulsed(index)
- switch(index)
+/datum/wires/explosive/on_pulse(wire)
+ switch(wire)
if(WIRE_EXPLODE)
explode()
..()
-/datum/wires/explosive/UpdateCut(index, mended)
- switch(index)
+/datum/wires/explosive/on_cut(wire, mend)
+ switch(wire)
if(WIRE_EXPLODE)
- if(!mended)
+ if(!mend)
explode()
..()
/datum/wires/explosive/gibtonite
holder_type = /obj/item/twohanded/required/gibtonite
-/datum/wires/explosive/gibtonite/CanUse(mob/L)
- return 1
+/datum/wires/explosive/gibtonite/interactable(mob/user)
+ return TRUE
-/datum/wires/explosive/gibtonite/UpdateCut(index, mended)
+/datum/wires/explosive/gibtonite/on_cut(wire, mend)
return
/datum/wires/explosive/gibtonite/explode()
diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm
index 988b520854f..cb13b0d6718 100644
--- a/code/datums/wires/mulebot.dm
+++ b/code/datums/wires/mulebot.dm
@@ -1,90 +1,35 @@
/datum/wires/mulebot
- random = 1
+ randomize = TRUE
holder_type = /mob/living/simple_animal/bot/mulebot
wire_count = 10
- window_x = 410
+ proper_name = "Mulebot"
+ window_x = 370
+ window_y = -12
-#define MULEBOT_WIRE_POWER1 1 // power connections
-#define MULEBOT_WIRE_POWER2 2
-#define MULEBOT_WIRE_AVOIDANCE 4 // mob avoidance
-#define MULEBOT_WIRE_LOADCHECK 8 // load checking (non-crate)
-#define MULEBOT_WIRE_MOTOR1 16 // motor wires
-#define MULEBOT_WIRE_MOTOR2 32 //
-#define MULEBOT_WIRE_REMOTE_RX 64 // remote recv functions
-#define MULEBOT_WIRE_REMOTE_TX 128 // remote trans status
-#define MULEBOT_WIRE_BEACON_RX 256 // beacon ping recv
+/datum/wires/mulebot/New(atom/_holder)
+ wires = list(
+ WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_MOB_AVOIDANCE,
+ WIRE_LOADCHECK, WIRE_MOTOR1, WIRE_MOTOR2,
+ WIRE_REMOTE_RX, WIRE_REMOTE_TX, WIRE_BEACON_RX
+ )
+ return ..()
-/datum/wires/mulebot/GetWireName(index)
- switch(index)
- if(MULEBOT_WIRE_POWER1)
- return "Primary Power"
-
- if(MULEBOT_WIRE_POWER2)
- return "Secondary Power"
-
- if(MULEBOT_WIRE_AVOIDANCE)
- return "Mob Avoidance"
-
- if(MULEBOT_WIRE_LOADCHECK)
- return "Load Checking"
-
- if(MULEBOT_WIRE_MOTOR1)
- return "Primary Motor"
-
- if(MULEBOT_WIRE_MOTOR2)
- return "Secondary Motor"
-
- if(MULEBOT_WIRE_REMOTE_RX)
- return "Remote Signal Receiver"
-
- if(MULEBOT_WIRE_REMOTE_TX)
- return "Remote Signal Sender"
-
- if(MULEBOT_WIRE_BEACON_RX)
- return "Navigation Beacon Receiver"
-
-/datum/wires/mulebot/CanUse(mob/living/L)
+/datum/wires/mulebot/interactable(mob/user)
var/mob/living/simple_animal/bot/mulebot/M = holder
if(M.open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/mulebot/UpdatePulsed(index)
- switch(index)
- if(MULEBOT_WIRE_POWER1, MULEBOT_WIRE_POWER2)
+/datum/wires/mulebot/on_pulse(wire)
+ switch(wire)
+ if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2)
holder.visible_message(" [bicon(holder)] The charge light flickers.")
- if(MULEBOT_WIRE_AVOIDANCE)
+ if(WIRE_MOB_AVOIDANCE)
holder.visible_message(" [bicon(holder)] The external warning lights flash briefly.")
- if(MULEBOT_WIRE_LOADCHECK)
+ if(WIRE_LOADCHECK)
holder.visible_message(" [bicon(holder)] The load platform clunks.")
- if(MULEBOT_WIRE_MOTOR1, MULEBOT_WIRE_MOTOR2)
+ if(WIRE_MOTOR1, WIRE_MOTOR2)
holder.visible_message(" [bicon(holder)] The drive motor whines briefly.")
else
holder.visible_message(" [bicon(holder)] You hear a radio crackle.")
..()
-
-// HELPER PROCS
-
-/datum/wires/mulebot/proc/Motor1()
- return !(wires_status & MULEBOT_WIRE_MOTOR1)
-
-/datum/wires/mulebot/proc/Motor2()
- return !(wires_status & MULEBOT_WIRE_MOTOR2)
-
-/datum/wires/mulebot/proc/HasPower()
- return !(wires_status & MULEBOT_WIRE_POWER1) && !(wires_status & MULEBOT_WIRE_POWER2)
-
-/datum/wires/mulebot/proc/LoadCheck()
- return !(wires_status & MULEBOT_WIRE_LOADCHECK)
-
-/datum/wires/mulebot/proc/MobAvoid()
- return !(wires_status & MULEBOT_WIRE_AVOIDANCE)
-
-/datum/wires/mulebot/proc/RemoteTX()
- return !(wires_status & MULEBOT_WIRE_REMOTE_TX)
-
-/datum/wires/mulebot/proc/RemoteRX()
- return !(wires_status & MULEBOT_WIRE_REMOTE_RX)
-
-/datum/wires/mulebot/proc/BeaconRX()
- return !(wires_status & MULEBOT_WIRE_BEACON_RX)
diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm
index 3b8ff5db098..51918c02ff4 100644
--- a/code/datums/wires/nuclearbomb.dm
+++ b/code/datums/wires/nuclearbomb.dm
@@ -1,28 +1,20 @@
/datum/wires/nuclearbomb
holder_type = /obj/machinery/nuclearbomb
- random = 1
- wire_count = 7
+ randomize = TRUE
+ wire_count = 7 // 3 actual, 4 duds.
+ proper_name = "Nuclear bomb"
+ window_x = 345
+ window_y = 75
-#define NUCLEARBOMB_WIRE_LIGHT 1
-#define NUCLEARBOMB_WIRE_TIMING 2
-#define NUCLEARBOMB_WIRE_SAFETY 4
+/datum/wires/nuclearbomb/New(atom/_holder)
+ wires = list(WIRE_BOMB_LIGHT, WIRE_BOMB_TIMING, WIRE_BOMB_SAFETY)
+ return ..()
-/datum/wires/nuclearbomb/GetWireName(index)
- switch(index)
- if(NUCLEARBOMB_WIRE_LIGHT)
- return "Bomb Light"
-
- if(NUCLEARBOMB_WIRE_TIMING)
- return "Bomb Timing"
-
- if(NUCLEARBOMB_WIRE_SAFETY)
- return "Bomb Safety"
-
-/datum/wires/nuclearbomb/CanUse(mob/living/L)
+/datum/wires/nuclearbomb/interactable(mob/user)
var/obj/machinery/nuclearbomb/N = holder
if(N.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/nuclearbomb/get_status()
. = ..()
@@ -31,52 +23,36 @@
. += "The device is is [N.safety ? "quiet" : "whirring"]."
. += "The lights are [N.lighthack ? "static" : "functional"]."
-/datum/wires/nuclearbomb/UpdatePulsed(index)
+/datum/wires/nuclearbomb/on_pulse(wire)
var/obj/machinery/nuclearbomb/N = holder
- switch(index)
- if(NUCLEARBOMB_WIRE_LIGHT)
+ switch(wire)
+ if(WIRE_BOMB_LIGHT)
N.lighthack = !N.lighthack
- updateUIs()
- spawn(100)
- N.lighthack = !N.lighthack
- updateUIs()
- if(NUCLEARBOMB_WIRE_TIMING)
+ addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_lighthack_callback), 10 SECONDS)
+
+ if(WIRE_BOMB_TIMING)
if(N.timing)
message_admins("[key_name_admin(usr)] pulsed a nuclear bomb's detonation wire, causing it to explode ( JMP)")
N.explode()
- if(NUCLEARBOMB_WIRE_SAFETY)
- N.safety = !N.safety
- updateUIs()
- spawn(100)
- N.safety = !N.safety
- if(N.safety == 1)
- if(!N.is_syndicate)
- set_security_level(N.previous_level)
- N.visible_message(" The [N] quiets down.")
- if(!N.lighthack)
- if(N.icon_state == "nuclearbomb2")
- N.icon_state = "nuclearbomb1"
- else
- N.visible_message(" The [N] emits a quiet whirling noise!")
- updateUIs()
-/datum/wires/nuclearbomb/UpdateCut(index, mended)
+ if(WIRE_BOMB_SAFETY)
+ N.safety = !N.safety
+ addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_safety_callback), 10 SECONDS)
+
+/datum/wires/nuclearbomb/on_cut(wire, mend)
var/obj/machinery/nuclearbomb/N = holder
- switch(index)
- if(NUCLEARBOMB_WIRE_SAFETY)
+ switch(wire)
+ if(WIRE_BOMB_SAFETY)
if(N.timing)
message_admins("[key_name_admin(usr)] cut a nuclear bomb's timing wire, causing it to explode ( JMP)")
N.explode()
- if(NUCLEARBOMB_WIRE_TIMING)
+
+ if(WIRE_BOMB_TIMING)
if(!N.lighthack)
if(N.icon_state == "nuclearbomb2")
N.icon_state = "nuclearbomb1"
N.timing = 0
- GLOB.bomb_set = 0
- if(NUCLEARBOMB_WIRE_LIGHT)
- N.lighthack = !N.lighthack
+ GLOB.bomb_set = FALSE
-/datum/wires/nuclearbomb/proc/updateUIs()
- SSnanoui.update_uis(src)
- if(holder)
- SSnanoui.update_uis(holder)
+ if(WIRE_BOMB_LIGHT)
+ N.lighthack = !N.lighthack
diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm
index d6e68a79cfb..e285f4cf898 100644
--- a/code/datums/wires/particle_accelerator.dm
+++ b/code/datums/wires/particle_accelerator.dm
@@ -1,67 +1,52 @@
/datum/wires/particle_acc/control_box
wire_count = 5
holder_type = /obj/machinery/particle_accelerator/control_box
+ proper_name = "Particle accelerator control"
+ window_x = 361
+ window_y = 22
-#define PARTICLE_TOGGLE_WIRE 1 // Toggles whether the PA is on or not.
-#define PARTICLE_STRENGTH_WIRE 2 // Determines the strength of the PA.
-#define PARTICLE_INTERFACE_WIRE 4 // Determines the interface showing up.
-#define PARTICLE_LIMIT_POWER_WIRE 8 // Determines how strong the PA can be.
+/datum/wires/particle_acc/control_box/New(atom/_holder)
+ wires = list(WIRE_PARTICLE_POWER, WIRE_PARTICLE_STRENGTH, WIRE_PARTICLE_INTERFACE, WIRE_PARTICLE_POWER_LIMIT)
+ return ..()
-/datum/wires/particle_acc/control_box/GetWireName(index)
- switch(index)
- if(PARTICLE_TOGGLE_WIRE)
- return "Power Toggle"
-
- if(PARTICLE_STRENGTH_WIRE)
- return "Strength"
-
- if(PARTICLE_INTERFACE_WIRE)
- return "Interface"
-
- if(PARTICLE_LIMIT_POWER_WIRE)
- return "Maximum Power"
-
-/datum/wires/particle_acc/control_box/CanUse(mob/living/L)
+/datum/wires/particle_acc/control_box/interactable(mob/user)
var/obj/machinery/particle_accelerator/control_box/C = holder
if(C.construction_state == 2)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/particle_acc/control_box/UpdatePulsed(index)
+/datum/wires/particle_acc/control_box/on_pulse(wire)
var/obj/machinery/particle_accelerator/control_box/C = holder
- switch(index)
-
- if(PARTICLE_TOGGLE_WIRE)
+ switch(wire)
+ if(WIRE_PARTICLE_POWER)
C.toggle_power()
- if(PARTICLE_STRENGTH_WIRE)
+ if(WIRE_PARTICLE_STRENGTH)
C.add_strength()
- if(PARTICLE_INTERFACE_WIRE)
+ if(WIRE_PARTICLE_INTERFACE)
C.interface_control = !C.interface_control
- if(PARTICLE_LIMIT_POWER_WIRE)
+ if(WIRE_PARTICLE_POWER_LIMIT)
C.visible_message("[bicon(C)] [C] makes a large whirring noise.")
..()
-/datum/wires/particle_acc/control_box/UpdateCut(index, mended)
+/datum/wires/particle_acc/control_box/on_cut(wire, mend)
var/obj/machinery/particle_accelerator/control_box/C = holder
- switch(index)
-
- if(PARTICLE_TOGGLE_WIRE)
- if(C.active == !mended)
+ switch(wire)
+ if(WIRE_PARTICLE_POWER)
+ if(C.active == !mend)
C.toggle_power()
- if(PARTICLE_STRENGTH_WIRE)
-
- for(var/i = 1; i < 3; i++)
+ if(WIRE_PARTICLE_STRENGTH)
+ for(var/i in 1 to 2)
C.remove_strength()
- if(PARTICLE_INTERFACE_WIRE)
- C.interface_control = mended
+ if(WIRE_PARTICLE_INTERFACE)
+ C.interface_control = mend
- if(PARTICLE_LIMIT_POWER_WIRE)
- C.strength_upper_limit = (mended ? 2 : 3)
+ if(WIRE_PARTICLE_POWER_LIMIT)
+ C.strength_upper_limit = (mend ? 2 : 3)
if(C.strength_upper_limit < C.strength)
C.remove_strength()
..()
diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm
index 90a72935c53..1efee71692d 100644
--- a/code/datums/wires/radio.dm
+++ b/code/datums/wires/radio.dm
@@ -1,52 +1,44 @@
/datum/wires/radio
holder_type = /obj/item/radio
wire_count = 3
+ proper_name = "Radio"
+ window_x = 330
+ window_y = 37
-#define RADIO_WIRE_SIGNAL 1
-#define RADIO_WIRE_RECEIVE 2
-#define RADIO_WIRE_TRANSMIT 4
+/datum/wires/radio/New(atom/_holder)
+ wires = list(WIRE_RADIO_SIGNAL, WIRE_RADIO_RECEIVER, WIRE_RADIO_TRANSMIT)
+ return ..()
-/datum/wires/radio/GetWireName(index)
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- return "Signal"
-
- if(RADIO_WIRE_RECEIVE)
- return "Receiver"
-
- if(RADIO_WIRE_TRANSMIT)
- return "Transmitter"
-
-/datum/wires/radio/CanUse(mob/living/L)
+/datum/wires/radio/interactable(mob/user)
var/obj/item/radio/R = holder
if(R.b_stat)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/radio/UpdatePulsed(index)
+/datum/wires/radio/on_pulse(wire)
var/obj/item/radio/R = holder
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_RECEIVE)
- R.broadcasting = R.listening && !IsIndexCut(RADIO_WIRE_TRANSMIT)
+ switch(wire)
+ if(WIRE_RADIO_SIGNAL)
+ R.listening = !R.listening && !is_cut(WIRE_RADIO_RECEIVER)
+ R.broadcasting = R.listening && !is_cut(WIRE_RADIO_TRANSMIT)
- if(RADIO_WIRE_RECEIVE)
- R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_RECEIVER)
+ R.listening = !R.listening && !is_cut(WIRE_RADIO_SIGNAL)
- if(RADIO_WIRE_TRANSMIT)
- R.broadcasting = !R.broadcasting && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_TRANSMIT)
+ R.broadcasting = !R.broadcasting && !is_cut(WIRE_RADIO_SIGNAL)
..()
-/datum/wires/radio/UpdateCut(index, mended)
+/datum/wires/radio/on_cut(wire, mend)
var/obj/item/radio/R = holder
- switch(index)
- if(RADIO_WIRE_SIGNAL)
- R.listening = mended && !IsIndexCut(RADIO_WIRE_RECEIVE)
- R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_TRANSMIT)
+ switch(wire)
+ if(WIRE_RADIO_SIGNAL)
+ R.listening = mend && !is_cut(WIRE_RADIO_RECEIVER)
+ R.broadcasting = mend && !is_cut(WIRE_RADIO_TRANSMIT)
- if(RADIO_WIRE_RECEIVE)
- R.listening = mended && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_RECEIVER)
+ R.listening = mend && !is_cut(WIRE_RADIO_SIGNAL)
- if(RADIO_WIRE_TRANSMIT)
- R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_SIGNAL)
+ if(WIRE_RADIO_TRANSMIT)
+ R.broadcasting = mend && !is_cut(WIRE_RADIO_SIGNAL)
..()
diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm
index 789a0472869..927980c57fa 100644
--- a/code/datums/wires/robot.dm
+++ b/code/datums/wires/robot.dm
@@ -1,32 +1,14 @@
/datum/wires/robot
- random = 1
+ randomize = TRUE
holder_type = /mob/living/silicon/robot
wire_count = 5
+ window_x = 340
+ window_y = 106
+ proper_name = "Cyborg"
-// /vg/ ordering
-
-#define BORG_WIRE_MAIN_POWER 1 // The power wires do nothing whyyyyyyyyyyyyy
-#define BORG_WIRE_LOCKED_DOWN 2
-#define BORG_WIRE_CAMERA 4
-#define BORG_WIRE_AI_CONTROL 8 // Not used on MoMMIs
-#define BORG_WIRE_LAWCHECK 16 // Not used on MoMMIs
-
-/datum/wires/robot/GetWireName(index)
- switch(index)
- if(BORG_WIRE_MAIN_POWER)
- return "Main Power"
-
- if(BORG_WIRE_LOCKED_DOWN)
- return "Lockdown"
-
- if(BORG_WIRE_CAMERA)
- return "Camera"
-
- if(BORG_WIRE_AI_CONTROL)
- return "AI Control"
-
- if(BORG_WIRE_LAWCHECK)
- return "Law Check"
+/datum/wires/robot/New(atom/_holder)
+ wires = list(WIRE_AI_CONTROL, WIRE_BORG_CAMERA, WIRE_BORG_LAWCHECK, WIRE_BORG_LOCKED)
+ return ..()
/datum/wires/robot/get_status()
. = ..()
@@ -36,71 +18,53 @@
. += "The Camera light is [(R.camera && R.camera.status == 1) ? "on" : "off"]."
. += "The lockdown light is [R.lockcharge ? "on" : "off"]."
-/datum/wires/robot/UpdateCut(index, mended)
-
+/datum/wires/robot/on_cut(wire, mend)
var/mob/living/silicon/robot/R = holder
- switch(index)
- if(BORG_WIRE_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI
- if(!mended)
- if(R.lawupdate == 1)
+ switch(wire)
+ if(WIRE_BORG_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI
+ if(!mend)
+ if(R.lawupdate)
to_chat(R, "LawSync protocol engaged.")
+ R.lawsync()
R.show_laws()
else
- if(R.lawupdate == 0 && !R.emagged)
- R.lawupdate = 1
+ if(!R.lawupdate && !R.emagged)
+ R.lawupdate = TRUE
- if(BORG_WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
- if(!mended)
+ if(WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
+ if(!mend)
if(R.connected_ai)
- R.connected_ai = null
+ R.disconnect_from_ai()
- if(BORG_WIRE_CAMERA)
+ if(WIRE_BORG_CAMERA)
if(!isnull(R.camera) && !R.scrambledcodes)
- R.camera.status = mended
+ R.camera.status = mend
R.camera.toggle_cam(usr, 0) // Will kick anyone who is watching the Cyborg's camera.
- if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh
- if(R.lawupdate)
- R.lawsync()
-
- if(BORG_WIRE_LOCKED_DOWN)
- R.SetLockdown(!mended)
+ if(WIRE_BORG_LOCKED)
+ R.SetLockdown(!mend)
..()
-/datum/wires/robot/UpdatePulsed(index)
-
+/datum/wires/robot/on_pulse(wire)
var/mob/living/silicon/robot/R = holder
- switch(index)
- if(BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
+ switch(wire)
+ if(WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI
if(!R.emagged)
- R.connected_ai = select_active_ai()
- R.notify_ai(1)
+ R.connect_to_ai(select_active_ai())
- if(BORG_WIRE_CAMERA)
+ if(WIRE_BORG_CAMERA)
if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes)
R.camera.toggle_cam(usr, 0) // Kick anyone watching the Cyborg's camera, doesn't display you disconnecting the camera.
R.visible_message("[R]'s camera lense focuses loudly.")
to_chat(R, "Your camera lense focuses loudly.")
- if(BORG_WIRE_LOCKED_DOWN)
+ if(WIRE_BORG_LOCKED)
R.SetLockdown(!R.lockcharge) // Toggle
..()
-/datum/wires/robot/CanUse(mob/living/L)
+/datum/wires/robot/interactable(mob/user)
var/mob/living/silicon/robot/R = holder
if(R.wiresexposed)
- return 1
- return 0
-
-/datum/wires/robot/proc/IsCameraCut()
- return wires_status & BORG_WIRE_CAMERA
-
-/datum/wires/robot/proc/LockedCut()
- return wires_status & BORG_WIRE_LOCKED_DOWN
-
-/datum/wires/robot/proc/CanLawCheck()
- return wires_status & BORG_WIRE_LAWCHECK
-
-/datum/wires/robot/proc/AIHasControl()
- return wires_status & BORG_WIRE_AI_CONTROL
+ return TRUE
+ return FALSE
diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm
index a65377ead1a..3955b1849b0 100644
--- a/code/datums/wires/smartfridge.dm
+++ b/code/datums/wires/smartfridge.dm
@@ -1,35 +1,26 @@
/datum/wires/smartfridge
holder_type = /obj/machinery/smartfridge
wire_count = 3
+ proper_name = "Smartfridge"
+ window_x = 340
+ window_y = 103
+
+/datum/wires/smartfridge/New(atom/_holder)
+ wires = list(WIRE_ELECTRIFY, WIRE_IDSCAN, WIRE_THROW_ITEM)
+ return ..()
/datum/wires/smartfridge/secure
- random = 1
- wire_count = 4
+ randomize = TRUE
+ wire_count = 4 // 3 actual, 1 dud.
+ window_y = 97
-#define SMARTFRIDGE_WIRE_ELECTRIFY 1
-#define SMARTFRIDGE_WIRE_THROW 2
-#define SMARTFRIDGE_WIRE_IDSCAN 4
-
-/datum/wires/smartfridge/GetWireName(index)
- switch(index)
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(SMARTFRIDGE_WIRE_THROW)
- return "Item Throw"
-
- if(SMARTFRIDGE_WIRE_IDSCAN)
- return "ID Scan"
-
-/datum/wires/smartfridge/CanUse(mob/living/L)
+/datum/wires/smartfridge/interactable(mob/user)
var/obj/machinery/smartfridge/S = holder
- if(!issilicon(L))
- if(S.seconds_electrified)
- if(S.shock(L, 100))
- return 0
+ if(iscarbon(user) && S.Adjacent(user) && S.seconds_electrified && S.shock(user, 100))
+ return FALSE
if(S.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/smartfridge/get_status()
. = ..()
@@ -38,27 +29,27 @@
. += "The red light is [S.shoot_inventory ? "off" : "blinking"]."
. += "A [S.scan_id ? "purple" : "yellow"] light is on."
-/datum/wires/smartfridge/UpdatePulsed(index)
+/datum/wires/smartfridge/on_pulse(wire)
var/obj/machinery/smartfridge/S = holder
- switch(index)
- if(SMARTFRIDGE_WIRE_THROW)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
S.shoot_inventory = !S.shoot_inventory
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
+ if(WIRE_ELECTRIFY)
S.seconds_electrified = 30
- if(SMARTFRIDGE_WIRE_IDSCAN)
+ if(WIRE_IDSCAN)
S.scan_id = !S.scan_id
..()
-/datum/wires/smartfridge/UpdateCut(index, mended)
+/datum/wires/smartfridge/on_cut(wire, mend)
var/obj/machinery/smartfridge/S = holder
- switch(index)
- if(SMARTFRIDGE_WIRE_THROW)
- S.shoot_inventory = !mended
- if(SMARTFRIDGE_WIRE_ELECTRIFY)
- if(mended)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
+ S.shoot_inventory = !mend
+ if(WIRE_ELECTRIFY)
+ if(mend)
S.seconds_electrified = 0
else
S.seconds_electrified = -1
- if(SMARTFRIDGE_WIRE_IDSCAN)
- S.scan_id = 1
+ if(WIRE_IDSCAN)
+ S.scan_id = TRUE
..()
diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm
index 175b36e6081..828e56884bf 100644
--- a/code/datums/wires/suitstorage.dm
+++ b/code/datums/wires/suitstorage.dm
@@ -1,24 +1,13 @@
/datum/wires/suitstorage
holder_type = /obj/machinery/suit_storage_unit
wire_count = 8
+ proper_name = "Suit storage unit"
+ window_x = 350
+ window_y = 85
-#define SSU_WIRE_ID 1
-#define SSU_WIRE_SHOCK 2
-#define SSU_WIRE_SAFETY 4
-#define SSU_WIRE_UV 8
-
-
-/datum/wires/suitstorage/GetWireName(index)
- switch(index)
- if(SSU_WIRE_ID)
- return "ID lock"
- if(SSU_WIRE_SHOCK)
- return "Shock wire"
- if(SSU_WIRE_SAFETY)
- return "Safety wire"
- if(SSU_WIRE_UV)
- return "UV wire"
-
+/datum/wires/suitstorage/New(atom/_holder)
+ wires = list(WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SSU_UV)
+ return ..()
/datum/wires/suitstorage/get_status()
. = ..()
@@ -28,42 +17,48 @@
. += "The green light is [A.shocked ? "on" : "off"]."
. += "The UV display shows [A.uv_super ? "15 nm" : "185 nm"]."
-datum/wires/suitstorage/CanUse()
+datum/wires/suitstorage/interactable(mob/user)
var/obj/machinery/suit_storage_unit/A = holder
+ if(iscarbon(user) && A.Adjacent(user) && A.shocked)
+ return A.shock(user, 100)
if(A.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/suitstorage/UpdateCut(index, mended)
+/datum/wires/suitstorage/on_cut(wire, mend)
var/obj/machinery/suit_storage_unit/A = holder
- switch(index)
- if(SSU_WIRE_ID)
- A.secure = mended
- if(SSU_WIRE_SAFETY)
- A.safeties = mended
- if(SSU_WIRE_SHOCK)
- A.shocked = !mended
+ switch(wire)
+ if(WIRE_IDSCAN)
+ A.secure = mend
+
+ if(WIRE_SAFETY)
+ A.safeties = mend
+
+ if(WIRE_ELECTRIFY)
+ A.shocked = !mend
A.shock(usr, 50)
- if(SSU_WIRE_UV)
- A.uv_super = !mended
+
+ if(WIRE_SSU_UV)
+ A.uv_super = !mend
..()
-datum/wires/suitstorage/UpdatePulsed(index)
+datum/wires/suitstorage/on_pulse(wire)
var/obj/machinery/suit_storage_unit/A = holder
- if(IsIndexCut(index))
+ if(is_cut(wire))
return
- switch(index)
- if(SSU_WIRE_ID)
+ switch(wire)
+ if(WIRE_IDSCAN)
A.secure = !A.secure
- if(SSU_WIRE_SAFETY)
+
+ if(WIRE_SAFETY)
A.safeties = !A.safeties
- if(SSU_WIRE_SHOCK)
+
+ if(WIRE_ELECTRIFY)
A.shocked = !A.shocked
if(A.shocked)
A.shock(usr, 100)
- spawn(50)
- if(A && !IsIndexCut(index))
- A.shocked = FALSE
- if(SSU_WIRE_UV)
+ addtimer(CALLBACK(A, /obj/machinery/suit_storage_unit/.proc/check_electrified_callback), 5 SECONDS)
+
+ if(WIRE_SSU_UV)
A.uv_super = !A.uv_super
..()
diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm
index b490d150efe..bd996f72fcc 100644
--- a/code/datums/wires/syndicatebomb.dm
+++ b/code/datums/wires/syndicatebomb.dm
@@ -1,47 +1,31 @@
/datum/wires/syndicatebomb
- random = TRUE
+ randomize = TRUE
holder_type = /obj/machinery/syndicatebomb
wire_count = 5
+ proper_name = "Syndicate bomb"
+ window_x = 320
+ window_y = 22
-#define BOMB_WIRE_BOOM 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut
-#define BOMB_WIRE_UNBOLT 2 // Unbolts the bomb if cut, hint on pulsed
-#define BOMB_WIRE_DELAY 4 // Raises the timer on pulse, does nothing on cut
-#define BOMB_WIRE_PROCEED 8 // Lowers the timer, explodes if cut while the bomb is active
-#define BOMB_WIRE_ACTIVATE 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut
+/datum/wires/syndicatebomb/New(atom/_holder)
+ wires = list(WIRE_BOMB_DELAY, WIRE_EXPLODE, WIRE_BOMB_UNBOLT,WIRE_BOMB_PROCEED, WIRE_BOMB_ACTIVATE)
+ return ..()
-/datum/wires/syndicatebomb/GetWireName(index)
- switch(index)
- if(BOMB_WIRE_BOOM)
- return "Explode"
-
- if(BOMB_WIRE_UNBOLT)
- return "Unbolt"
-
- if(BOMB_WIRE_DELAY)
- return "Delay"
-
- if(BOMB_WIRE_PROCEED)
- return "Proceed"
-
- if(BOMB_WIRE_ACTIVATE)
- return "Activate"
-
-/datum/wires/syndicatebomb/CanUse(mob/living/L)
+/datum/wires/syndicatebomb/interactable(mob/user)
var/obj/machinery/syndicatebomb/P = holder
if(P.open_panel)
return TRUE
return FALSE
-/datum/wires/syndicatebomb/UpdatePulsed(index)
+/datum/wires/syndicatebomb/on_pulse(wire)
var/obj/machinery/syndicatebomb/B = holder
- switch(index)
- if(BOMB_WIRE_BOOM)
+ switch(wire)
+ if(WIRE_EXPLODE)
if(B.active)
holder.visible_message(" [bicon(B)] An alarm sounds! It's go-")
B.explode_now = TRUE
- if(BOMB_WIRE_UNBOLT)
+ if(WIRE_BOMB_UNBOLT)
holder.visible_message(" [bicon(holder)] The bolts spin in place for a moment.")
- if(BOMB_WIRE_DELAY)
+ if(WIRE_BOMB_DELAY)
if(B.delayedbig)
holder.visible_message(" [bicon(B)] The bomb has already been delayed.")
else
@@ -49,7 +33,7 @@
playsound(B, 'sound/machines/chime.ogg', 30, 1)
B.detonation_timer += 300
B.delayedbig = TRUE
- if(BOMB_WIRE_PROCEED)
+ if(WIRE_BOMB_PROCEED)
holder.visible_message(" [bicon(B)] The bomb buzzes ominously!")
playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1)
var/seconds = B.seconds_remaining()
@@ -59,7 +43,7 @@
B.detonation_timer -= 100
else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy.
B.detonation_timer = world.time + 100
- if(BOMB_WIRE_ACTIVATE)
+ if(WIRE_BOMB_ACTIVATE)
if(!B.active && !B.defused)
holder.visible_message(" [bicon(B)] You hear the bomb start ticking!")
B.activate()
@@ -72,11 +56,11 @@
B.delayedlittle = TRUE
..()
-/datum/wires/syndicatebomb/UpdateCut(index, mended)
+/datum/wires/syndicatebomb/on_cut(wire, mend)
var/obj/machinery/syndicatebomb/B = holder
- switch(index)
- if(BOMB_WIRE_BOOM)
- if(mended)
+ switch(wire)
+ if(WIRE_EXPLODE)
+ if(mend)
B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage.
else
if(B.active)
@@ -84,19 +68,18 @@
B.explode_now = TRUE
else
B.defused = TRUE
- if(BOMB_WIRE_UNBOLT)
- if(!mended && B.anchored)
+ if(WIRE_BOMB_UNBOLT)
+ if(!mend && B.anchored)
holder.visible_message(" [bicon(B)] The bolts lift out of the ground!")
playsound(B, 'sound/effects/stealthoff.ogg', 30, 1)
B.anchored = FALSE
- if(BOMB_WIRE_PROCEED)
- if(!mended && B.active)
+ if(WIRE_BOMB_PROCEED)
+ if(!mend && B.active)
holder.visible_message(" [bicon(B)] An alarm sounds! It's go-")
B.explode_now = TRUE
- if(BOMB_WIRE_ACTIVATE)
- if(!mended && B.active)
+ if(WIRE_BOMB_ACTIVATE)
+ if(!mend && B.active)
holder.visible_message(" [bicon(B)] The timer stops! The bomb has been defused!")
- B.active = FALSE
B.defused = TRUE
B.update_icon()
..()
diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm
index bc513c6c6c0..f4dbf39017a 100644
--- a/code/datums/wires/tesla_coil.dm
+++ b/code/datums/wires/tesla_coil.dm
@@ -1,23 +1,23 @@
/datum/wires/tesla_coil
wire_count = 1
holder_type = /obj/machinery/power/tesla_coil
+ proper_name = "Tesla coil"
+ window_x = 320
+ window_y = 50
-#define TESLACOIL_WIRE_ZAP 1
+/datum/wires/tesla_coil/New(atom/_holder)
+ wires = list(WIRE_TESLACOIL_ZAP)
+ return ..()
-/datum/wires/tesla_coil/GetWireName(index)
- switch(index)
- if(TESLACOIL_WIRE_ZAP)
- return "Zap"
-
-/datum/wires/tesla_coil/CanUse(mob/living/L)
+/datum/wires/tesla_coil/interactable(mob/user)
var/obj/machinery/power/tesla_coil/T = holder
if(T && T.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
-/datum/wires/tesla_coil/UpdatePulsed(index)
+/datum/wires/tesla_coil/on_pulse(wire)
var/obj/machinery/power/tesla_coil/T = holder
- switch(index)
- if(TESLACOIL_WIRE_ZAP)
+ switch(wire)
+ if(WIRE_TESLACOIL_ZAP)
T.zap()
..()
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index c9e6032afee..299e18d815a 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -1,35 +1,21 @@
/datum/wires/vending
holder_type = /obj/machinery/vending
wire_count = 4
+ window_y = 112
+ window_x = 350
+ proper_name = "Vending machine"
-#define VENDING_WIRE_THROW 1
-#define VENDING_WIRE_CONTRABAND 2
-#define VENDING_WIRE_ELECTRIFY 4
-#define VENDING_WIRE_IDSCAN 8
+/datum/wires/vending/New(atom/_holder)
+ wires = list(WIRE_THROW_ITEM, WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_CONTRABAND)
+ return ..()
-/datum/wires/vending/GetWireName(index)
- switch(index)
- if(VENDING_WIRE_THROW)
- return "Item Throw"
-
- if(VENDING_WIRE_CONTRABAND)
- return "Contraband"
-
- if(VENDING_WIRE_ELECTRIFY)
- return "Electrification"
-
- if(VENDING_WIRE_IDSCAN)
- return "ID Scan"
-
-/datum/wires/vending/CanUse(mob/living/L)
+/datum/wires/vending/interactable(mob/user)
var/obj/machinery/vending/V = holder
- if(!istype(L, /mob/living/silicon))
- if(V.seconds_electrified)
- if(V.shock(L, 100))
- return 0
+ if(!istype(user, /mob/living/silicon) && V.seconds_electrified && V.shock(user, 100))
+ return FALSE
if(V.panel_open)
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/wires/vending/get_status()
. = ..()
@@ -39,31 +25,31 @@
. += "The green light is [V.extended_inventory ? "on" : "off"]."
. += "A [V.scan_id ? "purple" : "yellow"] light is on."
-/datum/wires/vending/UpdatePulsed(index)
+/datum/wires/vending/on_pulse(wire)
var/obj/machinery/vending/V = holder
- switch(index)
- if(VENDING_WIRE_THROW)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
V.shoot_inventory = !V.shoot_inventory
- if(VENDING_WIRE_CONTRABAND)
+ if(WIRE_CONTRABAND)
V.extended_inventory = !V.extended_inventory
- if(VENDING_WIRE_ELECTRIFY)
+ if(WIRE_ELECTRIFY)
V.seconds_electrified = 30
- if(VENDING_WIRE_IDSCAN)
+ if(WIRE_IDSCAN)
V.scan_id = !V.scan_id
..()
-/datum/wires/vending/UpdateCut(index, mended)
+/datum/wires/vending/on_cut(wire, mend)
var/obj/machinery/vending/V = holder
- switch(index)
- if(VENDING_WIRE_THROW)
- V.shoot_inventory = !mended
- if(VENDING_WIRE_CONTRABAND)
+ switch(wire)
+ if(WIRE_THROW_ITEM)
+ V.shoot_inventory = !mend
+ if(WIRE_CONTRABAND)
V.extended_inventory = FALSE
- if(VENDING_WIRE_ELECTRIFY)
- if(mended)
+ if(WIRE_ELECTRIFY)
+ if(mend)
V.seconds_electrified = 0
else
V.seconds_electrified = -1
- if(VENDING_WIRE_IDSCAN)
- V.scan_id = 1
+ if(WIRE_IDSCAN)
+ V.scan_id = TRUE
..()
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 907e6a695e1..27d1448ac77 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -1,332 +1,456 @@
-// Wire datums. Created by Giacomand.
-// Was created to replace a horrible case of copy and pasted code with no care for maintability.
-// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires
-// Protolathe wires, APC wires and Camera wires!
-
-#define MAX_FLAG 65535
-
-GLOBAL_LIST_EMPTY(same_wires)
-// 12 colours, if you're adding more than 12 wires then add more colours here
-GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink"))
/datum/wires
+ /// TRUE if the wires will be different every time a new wire datum is created.
+ var/randomize = FALSE
+ /// The atom the wires belong too. For example: an airlock.
+ var/atom/holder
+ /// The holder type; used to make sure that the holder is the correct type.
+ var/holder_type
+ /// The display name for the TGUI window. For example, given the var is "APC"...
+ /// When the TGUI window is opened, "wires" will be appended to it's title, and it would become "APC wires".
+ var/proper_name = "Unknown"
+ /// The total number of wires that our holder atom has.
+ var/wire_count = NONE
+ /// A list of all wires. For a list of valid wires defines that can go here, see `code/__DEFINES/wires.dm`
+ var/list/wires
+ /// A list of all cut wires. The same values that can go into `wires` will get added and removed from this list.
+ var/list/cut_wires
+ /// An associative list with the wire color as the key, and the wire define as the value.
+ var/list/colors
+ /// An associative list of signalers attached to the wires. The wire color is the key, and the signaler object reference is the value.
+ var/list/assemblies
+ /// The width of the wire TGUI window.
+ var/window_x = 300
+ /// The height of the wire TGUI window. Will get longer as needed, based on the `wire_count`.
+ var/window_y = 100
- var/random = 0 // Will the wires be different for every single instance.
- var/atom/holder = null // The holder
- var/holder_type = null // The holder type; used to make sure that the holder is the correct type.
- var/wire_count = 0 // Max is 16
- var/wires_status = 0 // BITFLAG OF WIRES
-
- var/list/wires = list()
- var/list/signallers = list()
-
- var/table_options = " align='center'"
- var/row_options1 = " width='80px'"
- var/row_options2 = " width='260px'"
- var/window_x = 370
- var/window_y = 470
-
-/datum/wires/New(atom/holder)
+/datum/wires/New(atom/_holder)
..()
- src.holder = holder
- if(!istype(holder, holder_type))
+ if(!istype(_holder, holder_type))
CRASH("Our holder is null/the wrong type!")
+
+ holder = _holder
+ cut_wires = list()
+ colors = list()
+ assemblies = list()
+
+ // Add in the appropriate amount of dud wires.
+ var/wire_len = length(wires)
+ if(wire_len < wire_count) // If the amount of "real" wires is less than the total we're suppose to have...
+ add_duds(wire_count - wire_len) // Add in the appropriate amount of duds to reach `wire_count`.
+
+ // If the randomize is true, we need to generate a new set of wires and ignore any wire color directories.
+ if(randomize)
+ randomize()
return
- // Generate new wires
- if(random)
- GenerateWires()
- // Get the same wires
+ if(!GLOB.wire_color_directory[holder_type])
+ randomize()
+ GLOB.wire_color_directory[holder_type] = colors
else
- // We don't have any wires to copy yet, generate some and then copy it.
- if(!GLOB.same_wires[holder_type])
- GenerateWires()
- GLOB.same_wires[holder_type] = src.wires.Copy()
- else
- var/list/wires = GLOB.same_wires[holder_type]
- src.wires = wires // Reference the wires list.
+ colors = GLOB.wire_color_directory[holder_type]
/datum/wires/Destroy()
holder = null
+ for(var/color in colors)
+ detach_assembly(color)
return ..()
-/datum/wires/proc/GenerateWires()
- var/list/colours_to_pick = GLOB.wireColours.Copy() // Get a copy, not a reference.
- var/list/indexes_to_pick = list()
- //Generate our indexes
- for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i)
- indexes_to_pick += i
- colours_to_pick.len = wire_count // Downsize it to our specifications.
+/**
+ * Randomly generates a new set of wires. and corresponding colors from the given pool. Assigns the information as an associative list, to `colors`.
+ *
+ * In the `colors` list, the name of the color is the key, and the wire is the value.
+ * For example: `colors["red"] = WIRE_ELECTRIFY`. This will look like `list("red" = WIRE_ELECTRIFY)` internally.
+ */
+datum/wires/proc/randomize()
+ var/static/list/possible_colors = list("red", "blue", "green", "silver", "orange", "brown", "gold", "white", "cyan", "magenta", "purple", "pink")
+ var/list/my_possible_colors = possible_colors.Copy()
- while(colours_to_pick.len && indexes_to_pick.len)
- // Pick and remove a colour
- var/colour = pick_n_take(colours_to_pick)
-
- // Pick and remove an index
- var/index = pick_n_take(indexes_to_pick)
-
- src.wires[colour] = index
- //wires = shuffle(wires)
-
-/datum/wires/proc/get_status()
- return list()
+ for(var/wire in shuffle(wires))
+ colors[pick_n_take(my_possible_colors)] = wire
+/**
+ * Proc called when the user attempts to interact with wires UI.
+ *
+ * Checks if the user exists, is a mob, the wires are attached to something (`holder`) and makes sure `interactable(user)` returns TRUE.
+ * If all the checks succeed, open the TGUI interface for the user.
+ *
+ * Arugments:
+ * * user - the mob trying to interact with the wires.
+ */
/datum/wires/proc/Interact(mob/user)
- if(user && istype(user) && holder && CanUse(user))
- ui_interact(user)
+ if(user && istype(user) && holder && interactable(user))
+ tgui_interact(user)
-/datum/wires/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/**
+ * Base proc, intended to be overriden. Wire datum specific checks you want to run before the TGUI is shown to the user should go here.
+ */
+/datum/wires/proc/interactable(mob/user)
+ return TRUE
+
+/// Users will be interacting with our holder object and not the wire datum directly, therefore we need to return the holder.
+/datum/wires/tgui_host()
+ return holder
+
+/datum/wires/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state)
+ ui = SStgui.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 = new(user, src, ui_key, "Wires", "[proper_name] wires", window_x, window_y + wire_count * 30, master_ui, state)
ui.open()
-/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state)
- var/data[0]
- var/list/replace_colours = null
+/datum/wires/tgui_data(mob/user)
+ var/list/data = list()
+ var/list/replace_colors
+
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes)
- if(eyes && (COLOURBLIND in H.mutations))
- replace_colours = eyes.replace_colours
+ if(eyes && (COLOURBLIND in H.mutations)) // Check if the human has colorblindness.
+ replace_colors = eyes.replace_colours // Get the colorblind replacement colors list.
+ var/list/wires_list = list()
- var/list/W[0]
- for(var/colour in wires)
- var/new_colour = colour
- var/colour_name = colour
- if(colour in replace_colours)
- new_colour = replace_colours[colour]
- if(new_colour in LIST_REPLACE_RENAME)
- colour_name = LIST_REPLACE_RENAME[new_colour]
+ for(var/color in colors)
+ var/replaced_color = color
+ var/color_name = color
+
+ if(color in replace_colors) // If this color is one that needs to be replaced using the colorblindness list.
+ replaced_color = replace_colors[color]
+ if(replaced_color in LIST_COLOR_RENAME) // If its an ugly written color name like "darkolivegreen", rename it to something like "dark green".
+ color_name = LIST_COLOR_RENAME[replaced_color]
else
- colour_name = new_colour
- else
- new_colour = colour
- colour_name = new_colour
- W[++W.len] = list("colour_name" = capitalize(colour_name), "seen_colour" = capitalize(new_colour),"colour" = capitalize(colour), "cut" = IsColourCut(colour), "index" = can_see_wire_index(user) ? GetWireName(GetIndex(colour)) : null, "attached" = IsAttached(colour))
+ color_name = replaced_color // Else just keep the normal color name
- if(W.len > 0)
- data["wires"] = W
+ wires_list += list(list(
+ "seen_color" = replaced_color, // The color of the wire that the mob will see. This will be the same as `color` if the user is NOT colorblind.
+ "color_name" = color_name, // The wire's name. This will be the same as `color` if the user is NOT colorblind.
+ "color" = color, // The "real" color of the wire. No replacements.
+ "wire" = can_see_wire_info(user) && !is_dud_color(color) ? get_wire(color) : null, // Wire define information like "Contraband" or "Door Bolts".
+ "cut" = is_color_cut(color), // Whether the wire is cut or not. Used to display "cut" or "mend".
+ "attached" = is_attached(color) // Whether or not a signaler is attached to this wire.
+ ))
+ data["wires"] = wires_list
+ // Get the information shown at the bottom of wire TGUI window, such as "The red light is blinking", etc.
+ // If the user is colorblind, we need to replace these colors as well.
var/list/status = get_status()
- if(replace_colours)
- var/i
- for(i=1, i<=status.len, i++)
- for(var/colour in replace_colours)
- var/new_colour = replace_colours[colour]
- if(new_colour in LIST_REPLACE_RENAME)
- new_colour = LIST_REPLACE_RENAME[new_colour]
- if(findtext(status[i],colour))
- status[i] = replacetext(status[i],colour,new_colour)
- break
- data["status_len"] = status.len
- data["status"] = status
+ if(replace_colors)
+ var/i
+ for(i in 1 to length(status))
+ for(var/color in replace_colors)
+ var/new_color = replace_colors[color]
+ if(new_color in LIST_COLOR_RENAME)
+ new_color = LIST_COLOR_RENAME[new_color]
+ if(findtext(status[i], color))
+ status[i] = replacetext(status[i], color, new_color)
+ break
+
+ data["status"] = status
return data
-/datum/wires/nano_host()
- return holder
+/datum/wires/tgui_act(action, list/params)
+ if(..())
+ return
-/datum/wires/proc/can_see_wire_index(mob/user)
+ var/mob/user = usr
+ if(!interactable(user))
+ return
+
+ var/obj/item/I = user.get_active_hand()
+ var/color = lowertext(params["wire"])
+ holder.add_hiddenprint(user)
+
+ switch(action)
+ // Toggles the cut/mend status.
+ if("cut")
+ if(!istype(I, /obj/item/wirecutters) && !user.can_admin_interact())
+ to_chat(user, " You need wirecutters!")
+ return
+
+ if(istype(I))
+ playsound(holder, I.usesound, 20, 1)
+ cut_color(color)
+ return TRUE
+
+ // Pulse a wire.
+ if("pulse")
+ if(!istype(I, /obj/item/multitool) && !user.can_admin_interact())
+ to_chat(user, " You need a multitool!")
+ return
+
+ playsound(holder, 'sound/weapons/empty.ogg', 20, 1)
+ pulse_color(color)
+
+ // If they pulse the electrify wire, call interactable() and try to shock them.
+ if(get_wire(color) == WIRE_ELECTRIFY)
+ interactable(user)
+
+ return TRUE
+
+ // Attach a signaler to a wire.
+ if("attach")
+ if(is_attached(color))
+ var/obj/item/O = detach_assembly(color)
+ if(O)
+ user.put_in_hands(O)
+ return TRUE
+
+ if(!istype(I, /obj/item/assembly/signaler))
+ to_chat(user, " You need a remote signaller!")
+ return
+
+ if(user.drop_item())
+ attach_assembly(color, I)
+ return TRUE
+ else
+ to_chat(user, " [user.get_active_hand()] is stuck to your hand!")
+
+/**
+ * Proc called to determine if the user can see wire define information, such as "Contraband", "Door Bolts", etc.
+ *
+ * If the user is an admin, or has a multitool which reveals wire information in their active hand, the proc returns TRUE.
+ *
+ * Arguments:
+ * * user - the mob who is interacting with the wires.
+ */
+/datum/wires/proc/can_see_wire_info(mob/user)
if(user.can_admin_interact())
return TRUE
else if(istype(user.get_active_hand(), /obj/item/multitool))
var/obj/item/multitool/M = user.get_active_hand()
if(M.shows_wire_information)
return TRUE
-
return FALSE
-/datum/wires/Topic(href, href_list)
- if(..())
- return 1
- var/mob/L = usr
- if(CanUse(L) && href_list["action"])
- var/obj/item/I = L.get_active_hand()
- var/colour = lowertext(href_list["wire"])
- holder.add_hiddenprint(L)
- switch(href_list["action"])
- if("cut") // Toggles the cut/mend status
- if(istype(I, /obj/item/wirecutters) || L.can_admin_interact())
- if(istype(I))
- playsound(holder, I.usesound, 20, 1)
- CutWireColour(colour)
- else
- to_chat(L, " You need wirecutters!")
- if("pulse")
- if(istype(I, /obj/item/multitool) || L.can_admin_interact())
- playsound(holder, 'sound/weapons/empty.ogg', 20, 1)
- PulseColour(colour)
- else
- to_chat(L, " You need a multitool!")
- if("attach")
- if(IsAttached(colour))
- var/obj/item/O = Detach(colour)
- if(O)
- L.put_in_hands(O)
- else
- if(istype(I, /obj/item/assembly/signaler))
- if(L.drop_item())
- Attach(colour, I)
- else
- to_chat(L, " [L.get_active_hand()] is stuck to your hand!")
- else
- to_chat(L, " You need a remote signaller!")
+/**
+ * Base proc, intended to be overwritten. Put wire information you'll see at the botton of the TGUI window here, such as "The red light is blinking".
+ */
+/datum/wires/proc/get_status()
+ return list()
- SSnanoui.update_uis(src)
- return 1
+/**
+ * Clears the `colors` list, and randomizes it to a new set of color-to-wire relations.
+ */
+/datum/wires/proc/shuffle_wires()
+ colors.Cut()
+ randomize()
-//
-// Overridable Procs
-//
+/**
+ * Repairs all cut wires.
+ */
+/datum/wires/proc/repair()
+ cut_wires.Cut()
-// Called when wires cut/mended.
-/datum/wires/proc/UpdateCut(index, mended)
- if(holder)
- SSnanoui.update_uis(holder)
+/**
+ * Adds in dud wires, which do nothing when cut/pulsed.
+ *
+ * Arguments:
+ * * duds - the amount of dud wires to generate.
+ */
+/datum/wires/proc/add_duds(duds)
+ while(duds)
+ var/dud = WIRE_DUD_PREFIX + "[--duds]"
+ if(dud in wires)
+ continue
+ wires += dud
-// Called when wire pulsed. Add code here.
-/datum/wires/proc/UpdatePulsed(index)
- if(holder)
- SSnanoui.update_uis(holder)
+/**
+ * Determines if the passed in wire is a dud or not. Returns TRUE if the wire is a dud, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/is_dud(wire)
+ return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)
-/datum/wires/proc/CanUse(mob/L)
- return 1
+/**
+ * Returns TRUE if the wire that corresponds to the passed in color is a dud. FALSE otherwise.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/is_dud_color(color)
+ return is_dud(get_wire(color))
-/datum/wires/CanUseTopic(mob/user, datum/topic_state/state)
- if(!CanUse(user))
- return STATUS_CLOSE
- return ..()
+/**
+ * Gets the wire associated with the color passed in.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/get_wire(color)
+ return colors[color]
-// Example of use:
-/*
+/**
+ * Determines if the passed in wire is cut or not. Returns TRUE if it's cut, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/is_cut(wire)
+ return (wire in cut_wires)
-#define NAME_WIRE_BOLTED 1
-#define NAME_WIRE_SHOCKED 2
-#define NAME_WIRE_SAFETY 4
-#define NAME_WIRE_POWER 8
+/**
+ * Determines if the wire associated with the passed in color, is cut or not. Returns TRUE if it's cut, FALSE otherwise.
+ *
+ * Arugments:
+ * * wire - a wire color.
+ */
+/datum/wires/proc/is_color_cut(color)
+ return is_cut(get_wire(color))
-/datum/wires/door/UpdateCut(var/index, var/mended)
- var/obj/machinery/door/airlock/A = holder
- switch(index)
- if(NAME_WIRE_BOLTED)
- if(!mended)
- A.bolt()
- if(NAME_WIRE_SHOCKED)
- A.shock()
- if(NAME_WIRE_SAFETY)
- A.safety()
+/**
+ * Determines if all of the wires are cut. Returns TRUE they're all cut, FALSE otherwise.
+ */
+/datum/wires/proc/is_all_cut()
+ return (length(cut_wires) == length(wires))
-*/
-
-
-//
-// Helper Procs
-//
-
-/datum/wires/proc/PulseColour(colour)
- PulseIndex(GetIndex(colour))
-
-/datum/wires/proc/PulseIndex(index)
- if(IsIndexCut(index))
- return
- UpdatePulsed(index)
-
-/datum/wires/proc/GetIndex(colour)
- if(wires[colour])
- var/index = wires[colour]
- return index
+/**
+ * Cut or mend a wire. Calls `on_cut()`.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/cut(wire)
+ if(is_cut(wire))
+ cut_wires -= wire
+ on_cut(wire, mend = TRUE)
else
- CRASH("[colour] is not a key in wires.")
+ cut_wires += wire
+ on_cut(wire, mend = FALSE)
-/datum/wires/proc/GetWireName(index)
+/**
+ * Cut the wire which corresponds with the passed in color.
+ *
+ * Arugments:
+ * * color - a wire color.
+ */
+/datum/wires/proc/cut_color(color)
+ cut(get_wire(color))
+
+/**
+ * Cuts a random wire.
+ */
+/datum/wires/proc/cut_random()
+ cut(wires[rand(1, length(wires))])
+
+/**
+ * Cuts all wires.
+ */
+/datum/wires/proc/cut_all()
+ for(var/wire in wires)
+ cut(wire)
+
+/**
+ * Proc called when any wire is cut.
+ *
+ * Base proc, intended to be overriden.
+ * Place an behavior you want to happen when certain wires are cut, into this proc.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'.
+ * * mend - TRUE if we're mending the wire. FALSE if we're cutting.
+ */
+/datum/wires/proc/on_cut(wire, mend = FALSE)
return
-//
-// Is Index/Colour Cut procs
-//
+/**
+ * Pulses the given wire. Calls `on_pulse()`.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`.
+ */
+/datum/wires/proc/pulse(wire)
+ if(is_cut(wire))
+ return
+ on_pulse(wire)
-/datum/wires/proc/IsColourCut(colour)
- var/index = GetIndex(colour)
- return IsIndexCut(index)
+/**
+ * Pulses the wire associated with the given color.
+ *
+ * Arugments:
+ * * wire - a wire color.
+ */
+/datum/wires/proc/pulse_color(color)
+ pulse(get_wire(color))
-/datum/wires/proc/IsIndexCut(index)
- return (index & wires_status)
+/**
+ * Proc called when any wire is pulsed.
+ *
+ * Base proc, intended to be overriden.
+ * Place behavior you want to happen when certain wires are pulsed, into this proc.
+ *
+ * Arugments:
+ * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'.
+ */
+/datum/wires/proc/on_pulse(wire)
+ return
-//
-// Signaller Procs
-//
+/**
+ * Proc called when an attached signaler receives a signal.
+ *
+ * Searches through the `assemblies` list for the wire that the signaler is attached to. Pulses the wire when it's found.
+ *
+ * Arugments:
+ * * S - the attached signaler receiving the signal.
+ */
+/datum/wires/proc/pulse_assembly(obj/item/assembly/signaler/S)
+ for(var/color in assemblies)
+ if(S == assemblies[color])
+ pulse_color(color)
+ return TRUE
-/datum/wires/proc/IsAttached(colour)
- if(signallers[colour])
- return 1
- return 0
+/**
+ * Proc called when a mob tries to attach a signaler to a wire.
+ *
+ * Makes sure that `S` is actually a signaler and that something is not already attached to the wire.
+ * Adds the signaler to the `assemblies` list as a value, with the `color` as a the key.
+ *
+ * Arguments:
+ * * color - the wire color.
+ * * S - the signaler that a mob is trying to attach.
+ */
+/datum/wires/proc/attach_assembly(color, obj/item/assembly/signaler/S)
+ if(S && istype(S) && !is_attached(color))
+ assemblies[color] = S
+ S.forceMove(holder)
+ S.connected = src
+ return S
-/datum/wires/proc/GetAttached(colour)
- if(signallers[colour])
- return signallers[colour]
+/**
+ * Proc called when a mob tries to detach a signaler from a wire.
+ *
+ * First checks if there is a signaler on the wire. If so, removes the signaler, and clears it from `assemblies` list.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/detach_assembly(color)
+ var/obj/item/assembly/signaler/S = get_attached(color)
+ if(S && istype(S))
+ assemblies -= color
+ S.connected = null
+ S.forceMove(holder.drop_location())
+ return S
+
+/**
+ * Gets the signaler attached to the given wire color, if there is one.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/get_attached(color)
+ if(assemblies[color])
+ return assemblies[color]
return null
-/datum/wires/proc/Attach(colour, obj/item/assembly/signaler/S)
- if(colour && S)
- if(!IsAttached(colour))
- signallers[colour] = S
- S.loc = holder
- S.connected = src
- return S
-
-/datum/wires/proc/Detach(colour)
- if(colour)
- var/obj/item/assembly/signaler/S = GetAttached(colour)
- if(S)
- signallers -= colour
- S.connected = null
- S.loc = holder.loc
- return S
-
-
-/datum/wires/proc/Pulse(obj/item/assembly/signaler/S)
-
- for(var/colour in signallers)
- if(S == signallers[colour])
- PulseColour(colour)
- break
-
-
-//
-// Cut Wire Colour/Index procs
-//
-
-/datum/wires/proc/CutWireColour(colour)
- var/index = GetIndex(colour)
- CutWireIndex(index)
-
-/datum/wires/proc/CutWireIndex(index)
- if(IsIndexCut(index))
- wires_status &= ~index
- UpdateCut(index, 1)
- else
- wires_status |= index
- UpdateCut(index, 0)
-
-/datum/wires/proc/RandomCut()
- var/r = rand(1, wires.len)
- CutWireIndex(r)
-
-/datum/wires/proc/CutAll()
- for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i)
- CutWireIndex(i)
-
-/datum/wires/proc/IsAllCut()
- if(wires_status == (1 << wire_count) - 1)
- return 1
- return 0
-
-//
-//Shuffle and Mend
-//
-
-/datum/wires/proc/Shuffle()
- wires_status = 0
- GenerateWires()
+/**
+ * Checks if the given wire has a signaler on it.
+ *
+ * Arguments:
+ * * color - the wire color.
+ */
+/datum/wires/proc/is_attached(color)
+ if(assemblies[color])
+ return TRUE
diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm
index 1f8c83f5f04..1d6fa5619e2 100644
--- a/code/defines/procs/AStar.dm
+++ b/code/defines/procs/AStar.dm
@@ -29,15 +29,15 @@ Actual Adjacent procs :
//////////////////////
//A* nodes variables
-/PathNode
+/datum/pathnode
var/turf/source //turf associated with the PathNode
- var/PathNode/prevNode //link to the parent PathNode
+ var/datum/pathnode/prevNode //link to the parent PathNode
var/f //A* Node weight (f = g + h)
var/g //A* movement cost variable
var/h //A* heuristic variable
var/nt //count the number of Nodes traversed
-/PathNode/New(s,p,pg,ph,pnt)
+/datum/pathnode/New(s,p,pg,ph,pnt)
source = s
prevNode = p
g = pg
@@ -46,7 +46,7 @@ Actual Adjacent procs :
source.PNode = src
nt = pnt
-/PathNode/proc/calc_f()
+/datum/pathnode/proc/calc_f()
f = g + h
//////////////////////
@@ -54,11 +54,11 @@ Actual Adjacent procs :
//////////////////////
//the weighting function, used in the A* algorithm
-/proc/PathWeightCompare(PathNode/a, PathNode/b)
+/proc/PathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return a.f - b.f
//reversed so that the Heap is a MinHeap rather than a MaxHeap
-/proc/HeapPathWeightCompare(PathNode/a, PathNode/b)
+/proc/HeapPathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return b.f - a.f
//wrapper that returns an empty list if A* failed to find a path
@@ -82,13 +82,13 @@ Actual Adjacent procs :
return 0
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
- var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list
+ var/datum/heap/open = new /datum/heap(/proc/HeapPathWeightCompare) //the open list
var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any
- var/PathNode/cur //current processed turf
+ var/datum/pathnode/cur //current processed turf
//initialization
- open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0))
+ open.Insert(new /datum/pathnode(start,null,0,call(start,dist)(end),0))
//then run the main loop
while(!open.IsEmpty() && !path)
@@ -125,7 +125,7 @@ Actual Adjacent procs :
var/newg = cur.g + call(cur.source,dist)(T)
if(!T.PNode) //is not already in open list, so add it
- open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1))
+ open.Insert(new /datum/pathnode(T,cur,newg,call(T,dist)(end),cur.nt+1))
else //is already in open list, check if it's a better way from the current turf
if(newg < T.PNode.g)
T.PNode.prevNode = cur
@@ -137,7 +137,7 @@ Actual Adjacent procs :
}
//cleaning after us
- for(var/PathNode/PN in open.L)
+ for(var/datum/pathnode/PN in open.L)
PN.source.PNode = null
for(var/turf/T in closed)
T.PNode = null
diff --git a/code/defines/procs/admin.dm b/code/defines/procs/admin.dm
index fb8ca665906..23b485ad252 100644
--- a/code/defines/procs/admin.dm
+++ b/code/defines/procs/admin.dm
@@ -83,10 +83,6 @@
var/message = "[key_name(whom, 0)][isAntag(whom) ? "(ANTAG)" : ""][isLivingSSD(whom) ? "(SSD!)": ""]"
return message
-/proc/log_and_message_admins(var/message as text)
+/proc/log_and_message_admins(message)
log_admin("[key_name(usr)] " + message)
message_admins("[key_name_admin(usr)] " + message)
-
-/proc/admin_log_and_message_admins(var/message as text)
- log_admin("[key_name(usr)] " + message)
- message_admins("[key_name_admin(usr)] " + message, 1)
diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm
index 76e5a1fd0a6..90a5b0db40c 100644
--- a/code/defines/procs/dbcore.dm
+++ b/code/defines/procs/dbcore.dm
@@ -72,12 +72,21 @@ DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return n
DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, " DB query blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to create a DB query via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to create a DB query via advanced proc-call")
+ return
if(sql_query) src.sql = sql_query
if(connection_handler) src.db_connection = connection_handler
if(cursor_handler) src.default_cursor = cursor_handler
_db_query = _dm_db_new_query()
return ..()
+DBQuery/CanProcCall()
+ // dont even try it
+ return FALSE
+
DBQuery
var/sql // The sql query being executed.
diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm
index 094e4f9ca2b..8f3eeeaa6fa 100644
--- a/code/defines/procs/statistics.dm
+++ b/code/defines/procs/statistics.dm
@@ -29,8 +29,9 @@
var/laname
var/lakey
if(H.lastattacker)
- laname = sanitizeSQL(H.lastattacker:real_name)
- lakey = sanitizeSQL(H.lastattacker:key)
+ laname = sanitizeSQL(H.lastattacker)
+ if(H.lastattackerckey)
+ lakey = sanitizeSQL(H.lastattackerckey)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])")
@@ -64,8 +65,9 @@
var/laname
var/lakey
if(H.lastattacker)
- laname = sanitizeSQL(H.lastattacker:real_name)
- lakey = sanitizeSQL(H.lastattacker:key)
+ laname = sanitizeSQL(H.lastattacker)
+ if(H.lastattackerckey)
+ lakey = sanitizeSQL(H.lastattackerckey)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])")
diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm
index 1d7bb9753a0..5b44f80895b 100644
--- a/code/defines/vox_sounds.dm
+++ b/code/defines/vox_sounds.dm
@@ -1,7 +1,16 @@
// List is required to compile the resources into the game when it loads.
// Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable.
-GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg',
+GLOBAL_LIST_INIT(vox_alerts, list(
+"bizwarn" = 'sound/vox_fem/bizwarn.ogg',
+"bloop" = 'sound/vox_fem/bloop.ogg',
+"buzwarn" = 'sound/vox_fem/buzwarn.ogg',
+"dadeda" = 'sound/vox_fem/dadeda.ogg',
+"deeoo" = 'sound/vox_fem/deeoo.ogg'
+))
+
+GLOBAL_LIST_INIT(vox_sounds, list(
+"," = 'sound/vox_fem/,.ogg',
"." = 'sound/vox_fem/..ogg',
"a" = 'sound/vox_fem/a.ogg',
"abortions" = 'sound/vox_fem/abortions.ogg',
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index edfae8fd53e..9a333d268f3 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -13,35 +13,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
*/
-/*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*/
-GLOBAL_LIST_EMPTY(teleportlocs)
-/hook/startup/proc/process_teleport_locs()
- for(var/area/AR in world)
- if(AR.no_teleportlocs) continue
- if(GLOB.teleportlocs.Find(AR.name)) continue
- var/turf/picked = safepick(get_area_turfs(AR.type))
- if(picked && is_station_level(picked.z))
- GLOB.teleportlocs += AR.name
- GLOB.teleportlocs[AR.name] = AR
-
- GLOB.teleportlocs = sortAssoc(GLOB.teleportlocs)
-
- return 1
-
-GLOBAL_LIST_EMPTY(ghostteleportlocs)
-/hook/startup/proc/process_ghost_teleport_locs()
- for(var/area/AR in world)
- if(GLOB.ghostteleportlocs.Find(AR.name)) continue
- var/list/turfs = get_area_turfs(AR.type)
- if(turfs.len)
- GLOB.ghostteleportlocs += AR.name
- GLOB.ghostteleportlocs[AR.name] = AR
-
- GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs)
-
- return 1
-
/*-----------------------------------------------------------------------------*/
@@ -70,7 +41,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/traitor.ogg')
+ ambientsounds = SPACE_SOUNDS
/area/space/nearstation
icon_state = "space_near"
@@ -79,10 +50,10 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/space/atmosalert()
return
-/area/space/fire_alert()
+/area/space/firealert(obj/source)
return
-/area/space/fire_reset()
+/area/space/firereset(obj/source)
return
/area/space/readyalert()
@@ -117,39 +88,33 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape
name = "\improper Emergency Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle2"
nad_allowed = TRUE
/area/shuttle/pod_1
name = "\improper Escape Pod One"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
/area/shuttle/pod_2
name = "\improper Escape Pod Two"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
/area/shuttle/pod_3
name = "\improper Escape Pod Three"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
parallax_movedir = EAST
/area/shuttle/pod_4
name = "\improper Escape Pod Four"
- music = "music/escape.ogg"
icon_state = "shuttle"
nad_allowed = TRUE
parallax_movedir = EAST
/area/shuttle/escape_pod1
name = "\improper Escape Pod One"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod1/station
@@ -163,7 +128,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod2
name = "\improper Escape Pod Two"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod2/station
@@ -177,7 +141,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod3
name = "\improper Escape Pod Three"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod3/station
@@ -191,7 +154,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/escape_pod5 //Pod 4 was lost to meteors
name = "\improper Escape Pod Five"
- music = "music/escape.ogg"
nad_allowed = TRUE
/area/shuttle/escape_pod5/station
@@ -205,7 +167,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/mining
name = "\improper Mining Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/transport
@@ -246,7 +207,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/siberia
name = "\improper Labor Camp Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/specops
@@ -329,7 +289,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/shuttle/research
name = "\improper Research Shuttle"
- music = "music/escape.ogg"
icon_state = "shuttle"
/area/shuttle/research/station
@@ -498,6 +457,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
nad_allowed = TRUE
+ ambientsounds = HIGHSEC_SOUNDS
/area/syndicate_mothership/control
name = "\improper Syndicate Control Room"
@@ -519,6 +479,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
valid_territory = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+ ambientsounds = MINING_SOUNDS
/area/asteroid/cave // -- TLE
name = "\improper Asteroid - Underground"
@@ -565,6 +526,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/exploration/methlab
name = "\improper Abandoned Drug Lab"
icon_state = "green"
+ there_can_be_many = TRUE
//Abductors
/area/abductor_ship
@@ -633,7 +595,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Maintenance
/area/maintenance
- ambientsounds = list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg', 'sound/ambience/ambimaint5.ogg')
+ ambientsounds = MAINTENANCE_SOUNDS
valid_territory = FALSE
/area/maintenance/atmos_control
@@ -688,10 +650,6 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "Engineering Maintenance"
icon_state = "amaint"
-/area/maintenance/engi_shuttle
- name = "Engineering Shuttle Access"
- icon_state = "maint_e_shuttle"
-
/area/maintenance/storage
name = "Atmospherics Maintenance"
icon_state = "green"
@@ -803,12 +761,11 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/bridge
name = "\improper Bridge"
icon_state = "bridge"
- music = "signal"
+ ambientsounds = list('sound/ambience/signal.ogg')
/area/bridge/meeting_room
name = "\improper Heads of Staff Meeting Room"
icon_state = "meeting"
- music = null
/area/crew_quarters/captain
name = "\improper Captain's Office"
@@ -969,10 +926,12 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "\improper Abandoned Library"
icon_state = "library"
+/area/chapel
+ icon_state = "chapel"
+ ambientsounds = HOLY_SOUNDS
+
/area/chapel/main
name = "\improper Chapel"
- icon_state = "chapel"
- ambientsounds = list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg','sound/ambience/ambicha4.ogg','sound/music/traitor.ogg')
/area/chapel/office
name = "\improper Chapel Office"
@@ -1107,7 +1066,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Engineering
/area/engine
- ambientsounds = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
+ ambientsounds = ENGINEERING_SOUNDS
/area/engine/engine_smes
name = "\improper Engineering SMES"
@@ -1161,6 +1120,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
requires_power = FALSE
valid_territory = FALSE
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
+ ambientsounds = ENGINEERING_SOUNDS
/area/solar/auxport
name = "\improper Fore Port Solar Array"
@@ -1227,18 +1187,18 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/teleporter
name = "\improper Teleporter"
icon_state = "teleporter"
- music = "signal"
+ ambientsounds = ENGINEERING_SOUNDS
/area/gateway
name = "\improper Gateway"
icon_state = "teleporter"
- music = "signal"
+ ambientsounds = ENGINEERING_SOUNDS
/area/AIsattele
name = "\improper Abandoned Teleporter"
icon_state = "teleporter"
- music = "signal"
- ambientsounds = list('sound/ambience/ambimalf.ogg')
+ ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/signal.ogg')
+ there_can_be_many = TRUE
/area/toxins/explab
name = "\improper E.X.P.E.R.I-MENTOR Lab"
@@ -1250,42 +1210,39 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//MedBay
+/area/medical
+ ambientsounds = MEDICAL_SOUNDS
+
/area/medical/medbay
name = "\improper Medbay"
icon_state = "medbay"
- music = 'sound/ambience/signal.ogg'
//Medbay is a large area, these additional areas help level out APC load.
/area/medical/medbay2
name = "\improper Medbay"
icon_state = "medbay2"
- music = 'sound/ambience/signal.ogg'
/area/medical/medbay3
name = "\improper Medbay"
icon_state = "medbay3"
- music = 'sound/ambience/signal.ogg'
/area/medical/biostorage
name = "\improper Medical Storage"
icon_state = "medbaysecstorage"
- music = 'sound/ambience/signal.ogg'
/area/medical/reception
name = "\improper Medbay Reception"
icon_state = "medbay"
- music = 'sound/ambience/signal.ogg'
/area/medical/psych
name = "\improper Psych Room"
icon_state = "medbaypsych"
- music = 'sound/ambience/signal.ogg'
+ ambientsounds = list('sound/ambience/aurora_caelus_short.ogg')
/area/medical/medbreak
name = "\improper Break Room"
icon_state = "medbaybreak"
- music = 'sound/ambience/signal.ogg'
/area/medical/patients_rooms
name = "\improper Patient's Rooms"
@@ -1342,7 +1299,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/medical/morgue
name = "\improper Morgue"
icon_state = "morgue"
- ambientsounds = list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg')
+ ambientsounds = SPOOKY_SOUNDS
/area/medical/chemistry
name = "\improper Chemistry"
@@ -1390,6 +1347,9 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
//Security
+/area/security
+ ambientsounds = HIGHSEC_SOUNDS
+
/area/security/main
name = "\improper Security Office"
icon_state = "securityoffice"
@@ -1522,6 +1482,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/security/detectives_office
name = "\improper Detective's Office"
icon_state = "detective"
+ ambientsounds = list('sound/ambience/ambidet1.ogg', 'sound/ambience/ambidet2.ogg')
/area/security/range
name = "\improper Firing Range"
@@ -1748,6 +1709,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/djstation
name = "\improper Ruskie DJ Station"
icon_state = "DJ"
+ there_can_be_many = TRUE
/area/djstation/solars
name = "\improper Ruskie DJ Station Solars"
@@ -1824,6 +1786,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/derelict/teleporter
name = "\improper Derelict Teleporter"
icon_state = "teleporter"
+ there_can_be_many = TRUE
/area/derelict/eva
name = "Derelict EVA Storage"
@@ -1865,84 +1828,12 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "Derelict Atmospherics"
icon_state = "red"
-//HALF-BUILT STATION (REPLACES DERELICT IN BAYCODE, ABOVE IS LEFT FOR DOWNSTREAM)
-
-/area/shuttle/constructionsite
- name = "\improper Construction Site Shuttle"
- icon_state = "yellow"
- parallax_movedir = EAST
-
-/area/shuttle/constructionsite/station
- name = "\improper Construction Site Shuttle"
-
-/area/shuttle/constructionsite/site
- name = "\improper Construction Site Shuttle"
-
-/area/constructionsite
- name = "\improper Construction Site"
- icon_state = "storage"
-
-/area/constructionsite/storage
- name = "\improper Construction Site Storage Area"
-
-/area/constructionsite/science
- name = "\improper Construction Site Research"
- icon_state = "medresearch"
-
-/area/constructionsite/bridge
- name = "\improper Construction Site Bridge"
- icon_state = "bridge"
-
-/area/constructionsite/hallway/center
- name = "\improper Construction Site Central Hallway"
- icon_state = "hallC"
-
-/area/constructionsite/hallway/engcore
- name = "\improper Construction Site Eng Core"
- icon_state = "green"
-
-/area/constructionsite/hallway/fore
- name = "\improper Construction Site Fore"
- icon_state = "green"
-
-/area/constructionsite/hallway/port
- name = "\improper Construction Site Port"
- icon_state = "hallP"
-
-/area/constructionsite/hallway/aft
- name = "\improper Construction Site Aft"
- icon_state = "hallA"
-
-/area/constructionsite/hallway/starboard
- name = "\improper Construction Site Starboard"
- icon_state = "hallS"
-
-/area/constructionsite/atmospherics
- name = "\improper Construction Site Atmospherics"
- icon_state = "atmos"
-
-/area/constructionsite/medical
- name = "\improper Construction Site Medbay"
- icon_state = "medbay"
-
-/area/constructionsite/ai
- name = "\improper Construction Computer Core"
- icon_state = "ai"
-
-/area/constructionsite/engineering
- name = "\improper Construction Site Engine Bay"
- icon_state = "engine"
-
-/area/solar/constructionsite
- name = "\improper Construction Site Solars"
- icon_state = "panelsA"
-
-
//Construction
/area/construction
name = "\improper Construction Area"
icon_state = "yellow"
+ ambientsounds = ENGINEERING_SOUNDS
/area/mining_construction
name = "Auxillary Base Construction"
@@ -2035,6 +1926,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
/area/ai_monitored/storage/eva
name = "EVA Storage"
icon_state = "eva"
+ ambientsounds = HIGHSEC_SOUNDS
/area/ai_monitored/storage/secure
name = "Secure Storage"
@@ -2045,7 +1937,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
icon_state = "storage"
/area/turret_protected/
- ambientsounds = list('sound/ambience/ambimalf.ogg')
+ ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg')
/area/turret_protected/ai_upload
name = "\improper AI Upload Chamber"
@@ -2107,7 +1999,8 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
// Telecommunications Satellite
/area/tcommsat
- ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
+ ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambitech.ogg',\
+ 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg', 'sound/ambience/ambimystery.ogg')
/area/tcommsat/chamber
name = "\improper Telecoms Central Compartment"
@@ -2155,6 +2048,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "\improper Strange Location"
icon_state = "away"
report_alerts = FALSE
+ ambientsounds = AWAY_MISSION_SOUNDS
/area/awaymission/example
name = "\improper Strange Station"
@@ -2169,7 +2063,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
icon_state = "beach"
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
requires_power = FALSE
- ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg')
+ ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambinice.ogg')
/area/awaymission/undersea
name = "Undersea"
diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm
index 8f65ac634f4..723574ee949 100644
--- a/code/game/area/ai_monitored.dm
+++ b/code/game/area/ai_monitored.dm
@@ -1,24 +1,30 @@
/area/ai_monitored
name = "AI Monitored Area"
- var/obj/machinery/camera/motioncamera = null
+ var/list/motioncameras = list()
+ var/list/motionTargets = list()
-
-/area/ai_monitored/LateInitialize()
+/area/ai_monitored/Initialize(mapload)
. = ..()
- // locate and store the motioncamera
- for(var/obj/machinery/camera/M in src)
- if(M.isMotion())
- motioncamera = M
- M.area_motion = src
- break
+ if(mapload)
+ for(var/obj/machinery/camera/M in src)
+ if(M.isMotion())
+ motioncameras.Add(M)
+ M.set_area_motion(src)
/area/ai_monitored/Entered(atom/movable/O)
..()
- if(ismob(O) && motioncamera)
- motioncamera.newTarget(O)
+ if(ismob(O) && length(motioncameras))
+ for(var/X in motioncameras)
+ var/obj/machinery/camera/cam = X
+ cam.newTarget(O)
+ return
/area/ai_monitored/Exited(atom/movable/O)
- if(ismob(O) && motioncamera)
- motioncamera.lostTarget(O)
+ ..()
+ if(ismob(O) && length(motioncameras))
+ for(var/X in motioncameras)
+ var/obj/machinery/camera/cam = X
+ cam.lostTargetRef(O.UID())
+ return
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 6ad45532641..de913ac9f91 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -26,7 +26,6 @@
var/power_equip = TRUE
var/power_light = TRUE
var/power_environ = TRUE
- var/music = null
var/used_equip = FALSE
var/used_light = FALSE
var/used_environ = FALSE
@@ -56,12 +55,12 @@
var/global/global_uid = 0
var/uid
- var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\
- 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\
- 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\
- 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\
- 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\
- 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
+ var/list/ambientsounds = GENERIC_SOUNDS
+
+ var/list/firedoors
+ var/list/cameras
+ var/list/firealarms
+ var/firedoors_last_closed_on = 0
var/fast_despawn = FALSE
var/can_get_auto_cryod = TRUE
@@ -131,35 +130,6 @@
cameras += C
return cameras
-
-/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE)
- if(report_alerts)
- if(danger_level == ATMOS_ALARM_NONE)
- SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source)
- else
- SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level)
-
- //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care.
- for(var/obj/machinery/alarm/AA in src)
- if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force)
- danger_level = max(danger_level, AA.danger_level)
-
- if(danger_level != atmosalm)
- if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING)
- //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise
- air_doors_open()
- else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER)
- air_doors_close()
-
- atmosalm = danger_level
- for(var/obj/machinery/alarm/AA in src)
- AA.update_icon()
-
- GLOB.air_alarm_repository.update_cache(src)
- return 1
- GLOB.air_alarm_repository.update_cache(src)
- return 0
-
/area/proc/air_doors_close()
if(!air_doors_activated)
air_doors_activated = TRUE
@@ -185,44 +155,151 @@
D.open()
-/area/proc/fire_alert()
- if(!fire)
- fire = 1 //used for firedoor checks
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- air_doors_close()
+/area/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
-/area/proc/fire_reset()
- if(fire)
- fire = 0 //used for firedoor checks
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- air_doors_open()
+/**
+ * Generate a power alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ */
+/area/proc/poweralert(state, obj/source)
+ if(state != poweralm)
+ poweralm = state
+ if(istype(source)) //Only report power alarms on the z-level where the source is located.
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ if(state)
+ C.network -= "Power Alarms"
+ else
+ C.network |= "Power Alarms"
- return
+ if(state)
+ SSalarm.cancelAlarm("Power", src, source)
+ else
+ SSalarm.triggerAlarm("Power", src, cameras, source)
-/area/proc/burglaralert(var/obj/trigger)
- if(always_unpowered == 1) //no burglar alarms in space/asteroid
+/**
+ * Generate an atmospheric alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ */
+/area/proc/atmosalert(danger_level, obj/source)
+ if(danger_level != atmosalm)
+ if(danger_level == ATMOS_ALARM_DANGER)
+
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network |= "Atmosphere Alarms"
+
+
+ SSalarm.triggerAlarm("Atmosphere", src, cameras, source)
+
+ else if(atmosalm == ATMOS_ALARM_DANGER)
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network -= "Atmosphere Alarms"
+
+ SSalarm.cancelAlarm("Atmosphere", src, source)
+
+ atmosalm = danger_level
+ return TRUE
+ return FALSE
+
+/**
+ * Try to close all the firedoors in the area
+ */
+/area/proc/ModifyFiredoors(opening)
+ if(firedoors)
+ firedoors_last_closed_on = world.time
+ for(var/FD in firedoors)
+ var/obj/machinery/door/firedoor/D = FD
+ var/cont = !D.welded
+ if(cont && opening) //don't open if adjacent area is on fire
+ for(var/I in D.affecting_areas)
+ var/area/A = I
+ if(A.fire)
+ cont = FALSE
+ break
+ if(cont && D.is_operational())
+ if(D.operating)
+ D.nextstate = opening ? FD_OPEN : FD_CLOSED
+ else if(!(D.density ^ opening))
+ INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/open : /obj/machinery/door/firedoor.proc/close))
+
+/**
+ * Generate a firealarm alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ *
+ * Also starts the area processing on SSobj
+ */
+/area/proc/firealert(obj/source)
+ if(always_unpowered) //no fire alarms in space/asteroid
return
- //Trigger alarm effect
- set_fire_alarm_effect()
+ if(!fire)
+ set_fire_alarm_effect()
+ ModifyFiredoors(FALSE)
+ for(var/item in firealarms)
+ var/obj/machinery/firealarm/F = item
+ F.update_icon()
- //Lockdown airlocks
- for(var/obj/machinery/door/airlock/A in src)
- spawn(0)
- A.close()
- if(A.density)
- A.lock()
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network |= "Fire Alarms"
- SSalarms.burglar_alarm.triggerAlarm(src, trigger)
- spawn(600)
- SSalarms.burglar_alarm.clearAlarm(src, trigger)
+ SSalarm.triggerAlarm("Fire", src, cameras, source)
-/area/proc/set_fire_alarm_effect()
- fire = 1
- updateicon()
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ START_PROCESSING(SSobj, src)
+
+/**
+ * Reset the firealarm alert for this area
+ *
+ * resets the alert sent to all ai players, alert consoles, drones and alarm monitor programs
+ * in the world
+ *
+ * Also cycles the icons of all firealarms and deregisters the area from processing on SSOBJ
+ */
+/area/proc/firereset(obj/source)
+ if(fire)
+ unset_fire_alarm_effects()
+ ModifyFiredoors(TRUE)
+ for(var/item in firealarms)
+ var/obj/machinery/firealarm/F = item
+ F.update_icon()
+
+ for(var/thing in cameras)
+ var/obj/machinery/camera/C = locateUID(thing)
+ if(!QDELETED(C) && is_station_level(C.z))
+ C.network -= "Fire Alarms"
+
+ SSalarm.cancelAlarm("Fire", src, source)
+
+ STOP_PROCESSING(SSobj, src)
+
+/**
+ * If 100 ticks has elapsed, toggle all the firedoors closed again
+ */
+/area/process()
+ if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds
+ ModifyFiredoors(FALSE)
+
+/**
+ * Close and lock a door passed into this proc
+ *
+ * Does this need to exist on area? probably not
+ */
+/area/proc/close_and_lock_door(obj/machinery/door/DOOR)
+ set waitfor = FALSE
+ DOOR.close()
+ if(DOOR.density)
+ DOOR.lock()
/area/proc/readyalert()
if(!eject)
@@ -246,13 +323,62 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
updateicon()
+/**
+ * Raise a burglar alert for this area
+ *
+ * Close and locks all doors in the area and alerts silicon mobs of a break in
+ *
+ * Alarm auto resets after 600 ticks
+ */
+/area/proc/burglaralert(obj/trigger)
+ if(always_unpowered) //no burglar alarms in space/asteroid
+ return
+
+ //Trigger alarm effect
+ set_fire_alarm_effect()
+ //Lockdown airlocks
+ for(var/obj/machinery/door/DOOR in src)
+ close_and_lock_door(DOOR)
+
+ if(SSalarm.triggerAlarm("Burglar", src, cameras, trigger))
+ //Cancel silicon alert after 1 minute
+ addtimer(CALLBACK(SSalarm, /datum/controller/subsystem/alarm.proc/cancelAlarm, "Burglar", src, trigger), 600)
+
+/**
+ * Trigger the fire alarm visual affects in an area
+ *
+ * Updates the fire light on fire alarms in the area and sets all lights to emergency mode
+ */
+/area/proc/set_fire_alarm_effect()
+ fire = TRUE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ for(var/alarm in firealarms)
+ var/obj/machinery/firealarm/F = alarm
+ F.update_fire_light(fire)
+ for(var/obj/machinery/light/L in src)
+ L.update()
+
+/**
+ * unset the fire alarm visual affects in an area
+ *
+ * Updates the fire light on fire alarms in the area and sets all lights to emergency mode
+ */
+/area/proc/unset_fire_alarm_effects()
+ fire = FALSE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ for(var/alarm in firealarms)
+ var/obj/machinery/firealarm/F = alarm
+ F.update_fire_light(fire)
+ for(var/obj/machinery/light/L in src)
+ L.update()
+
/area/proc/updateicon()
- if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
- if(fire && !eject && !party)
+ if((eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
+ if(!eject && !party)
icon_state = "red"
- else if(!fire && eject && !party)
+ else if(eject && !party)
icon_state = "red"
- else if(party && !fire && !eject)
+ else if(party && !eject)
icon_state = "party"
else
icon_state = "blue-red"
@@ -379,20 +505,24 @@
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys
- L.client.ambience_playing = 1
- L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ)
+ L.client.ambience_playing = TRUE
+ SEND_SOUND(L, sound('sound/ambience/shipambience.ogg', repeat = TRUE, wait = FALSE, volume = 35, channel = CHANNEL_BUZZ))
else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ))
- L.client.ambience_playing = 0
+ L.client.ambience_playing = FALSE
if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE))
var/sound = pick(ambientsounds)
if(!L.client.played)
- L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE)
- L.client.played = 1
- spawn(600) //ewww - this is very very bad
- if(L && L.client)
- L.client.played = 0
+ SEND_SOUND(L, sound(sound, repeat = FALSE, wait = FALSE, volume = 25, channel = CHANNEL_AMBIENCE))
+ L.client.played = TRUE
+ addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600)
+
+/**
+ * Reset the played var to false on the client
+ */
+/client/proc/ResetAmbiencePlayed()
+ played = FALSE
/area/proc/gravitychange(var/gravitystate = 0, var/area/A)
A.has_gravity = gravitystate
diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm
index 7a7c934df35..4a6606d836e 100644
--- a/code/game/area/areas/depot-areas.dm
+++ b/code/game/area/areas/depot-areas.dm
@@ -216,7 +216,8 @@
if(!silent)
announce_here("Depot Code BLUE", reason)
var/list/possible_bot_spawns = list()
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "syndi_depot_bot")
possible_bot_spawns |= L
if(possible_bot_spawns.len)
@@ -248,7 +249,8 @@
comms_online = TRUE
if(comms_online)
spawn(0)
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(prob(50))
if(L.name == "syndi_depot_backup")
var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/S = new /mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space(get_turf(L))
@@ -290,7 +292,7 @@
if(!reactor.has_overloaded)
reactor.overload(containment_failure)
else
- log_debug("Depot: [src] called activate_self_destruct with no reactor.");
+ log_debug("Depot: [src] called activate_self_destruct with no reactor.")
message_admins(" Syndicate Depot lacks reactor to initiate self-destruct. Must be destroyed manually.")
updateicon()
@@ -344,7 +346,8 @@
/area/syndicate_depot/core/proc/shields_up()
if(shield_list.len)
return
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "syndi_depot_shield")
var/obj/machinery/shieldwall/syndicate/S = new /obj/machinery/shieldwall/syndicate(L.loc)
shield_list += S.UID()
diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm
index 0b07fffda5e..b621bade493 100644
--- a/code/game/area/areas/mining.dm
+++ b/code/game/area/areas/mining.dm
@@ -7,7 +7,6 @@
/area/mine/explored
name = "Mine"
icon_state = "explored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
@@ -15,7 +14,7 @@
power_equip = FALSE
power_light = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = MINING_SOUNDS
flags = NONE
/area/mine/dangerous/explored/golem
@@ -24,7 +23,6 @@
/area/mine/unexplored
name = "Mine"
icon_state = "unexplored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
@@ -32,7 +30,7 @@
power_equip = FALSE
power_light = FALSE
outdoors = TRUE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = MINING_SOUNDS
flags = NONE
/area/mine/lobby
@@ -80,6 +78,7 @@
/area/mine/laborcamp/security
name = "Labor Camp Security"
icon_state = "security"
+ ambientsounds = HIGHSEC_SOUNDS
/area/mine/podbay
name = "Mining Podbay"
@@ -95,26 +94,24 @@
/area/lavaland/surface
name = "Lavaland"
icon_state = "explored"
- music = null
always_unpowered = TRUE
poweralm = FALSE
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
requires_power = TRUE
- ambientsounds = list('sound/ambience/ambilava.ogg')
+ ambientsounds = MINING_SOUNDS
/area/lavaland/underground
name = "Lavaland Caves"
icon_state = "unexplored"
- music = null
always_unpowered = TRUE
requires_power = TRUE
poweralm = FALSE
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
- ambientsounds = list('sound/ambience/ambilava.ogg')
+ ambientsounds = MINING_SOUNDS
/area/lavaland/surface/outdoors
name = "Lavaland Wastes"
diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm
index 5baf1fe315e..53544d8643b 100644
--- a/code/game/area/areas/ruins/lavaland.dm
+++ b/code/game/area/areas/ruins/lavaland.dm
@@ -5,6 +5,7 @@
/area/ruin/powered/clownplanet
icon_state = "dk_yellow"
+ ambientsounds = list('sound/music/clown.ogg')
/area/ruin/powered/animal_hospital
icon_state = "dk_yellow"
@@ -38,7 +39,7 @@
/area/ruin/unpowered/syndicate_lava_base
name = "Secret Base"
icon_state = "dk_yellow"
- ambientsounds = list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg')
+ ambientsounds = HIGHSEC_SOUNDS
report_alerts = FALSE
hide_attacklogs = TRUE
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 365e06aca4b..776a2bccd12 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -14,21 +14,18 @@
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = TRUE //filter for actions - used by lighting overlays
var/atom_say_verb = "says"
- var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays
-
+ var/bubble_icon = "default" ///what icon the mob uses for speechbubbles
+ var/dont_save = FALSE // For atoms that are temporary by necessity - like lighting overlays
///Chemistry.
var/container_type = NONE
var/datum/reagents/reagents = null
//This atom's HUD (med/sec, etc) images. Associative list.
- var/list/image/hud_list = list()
+ var/list/image/hud_list
//HUD images that this atom can provide.
var/list/hud_possible
- ///Chemistry.
-
-
//Value used to increment ex_act() if reactionary_explosions is on
var/explosion_block = 0
@@ -38,9 +35,9 @@
//Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default
var/can_leave_fibers = TRUE
- var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox
+ var/allow_spin = TRUE //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox
- var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff.
+ var/admin_spawned = FALSE //was this spawned by an admin? used for stat tracking stuff.
var/initialized = FALSE
@@ -67,7 +64,6 @@
// we were deleted
return
-
//Called after New if the map is being loaded. mapload = TRUE
//Called from base of New if the map is not being loaded. mapload = FALSE
//This base must be called or derivatives must set initialized to TRUE
@@ -101,7 +97,6 @@
return INITIALIZE_HINT_NORMAL
-
//called if Initialize returns INITIALIZE_HINT_LATELOAD
/atom/proc/LateInitialize()
return
@@ -114,34 +109,34 @@
return
/atom/proc/onCentcom()
+ . = FALSE
var/turf/T = get_turf(src)
if(!T)
- return 0
+ return
if(!is_admin_level(T.z))//if not, don't bother
- return 0
+ return
//check for centcomm shuttles
for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry"))
var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle)
if(T in M.areaInstance)
- return 1
+ return TRUE
//finally check for centcom itself
- return istype(T.loc,/area/centcom)
+ return istype(T.loc, /area/centcom)
/atom/proc/onSyndieBase()
+ . = FALSE
var/turf/T = get_turf(src)
if(!T)
- return 0
+ return
if(!is_admin_level(T.z))//if not, don't bother
- return 0
+ return
if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership))
- return 1
-
- return 0
+ return TRUE
/atom/Destroy()
if(alternate_appearances)
@@ -164,6 +159,34 @@
SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
dir = newdir
+/*
+ Sets the atom's pixel locations based on the atom's `dir` variable, and what pixel offset arguments are passed into it
+ If no arguments are supplied, `pixel_x` or `pixel_y` will be set to 0
+ Used primarily for when players attach mountable frames to walls (APC frame, fire alarm frame, etc.)
+*/
+/atom/proc/set_pixel_offsets_from_dir(pixel_north = 0, pixel_south = 0, pixel_east = 0, pixel_west = 0)
+ switch(dir)
+ if(NORTH)
+ pixel_y = pixel_north
+ if(SOUTH)
+ pixel_y = pixel_south
+ if(EAST)
+ pixel_x = pixel_east
+ if(WEST)
+ pixel_x = pixel_west
+ if(NORTHEAST)
+ pixel_y = pixel_north
+ pixel_x = pixel_east
+ if(NORTHWEST)
+ pixel_y = pixel_north
+ pixel_x = pixel_west
+ if(SOUTHEAST)
+ pixel_y = pixel_south
+ pixel_x = pixel_east
+ if(SOUTHWEST)
+ pixel_y = pixel_south
+ pixel_x = pixel_west
+
///Handle melee attack by a mech
/atom/proc/mech_melee_attack(obj/mecha/M)
return
@@ -202,7 +225,7 @@
else
return null
-/atom/proc/check_eye(user)
+/atom/proc/check_eye(mob/user)
return
/atom/proc/on_reagent_change()
@@ -217,11 +240,11 @@
/// Is this atom injectable into other atoms
/atom/proc/is_injectable(mob/user, allowmobs = TRUE)
- return reagents && (container_type & (INJECTABLE | REFILLABLE))
+ return reagents && (container_type & (INJECTABLE|REFILLABLE))
/// Can we draw from this atom with an injectable atom
/atom/proc/is_drawable(mob/user, allowmobs = TRUE)
- return reagents && (container_type & (DRAWABLE | DRAINABLE))
+ return reagents && (container_type & (DRAWABLE|DRAINABLE))
/// Can this atoms reagents be refilled
/atom/proc/is_refillable()
@@ -232,12 +255,12 @@
return reagents && (container_type & DRAINABLE)
/atom/proc/CheckExit()
- return 1
+ return TRUE
/atom/proc/HasProximity(atom/movable/AM as mob|obj)
return
-/atom/proc/emp_act(var/severity)
+/atom/proc/emp_act(severity)
return
/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
@@ -247,13 +270,13 @@
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
if(istype(src.loc, container))
- return 1
+ return TRUE
else if(src in container)
- return 1
- return
+ return TRUE
+ return FALSE
/*
- * atom/proc/search_contents_for(path,list/filter_path=null)
+ * atom/proc/search_contents_for(path, list/filter_path = null)
* Recursevly searches all atom contens (including contents contents and so on).
*
* ARGS: path - search atom contents for atoms of this type
@@ -262,7 +285,7 @@
* RETURNS: list of found atoms
*/
-/atom/proc/search_contents_for(path,list/filter_path=null)
+/atom/proc/search_contents_for(path, list/filter_path = null)
var/list/found = list()
for(var/atom/A in src)
if(istype(A, path))
@@ -274,7 +297,7 @@
if(!pass)
continue
if(A.contents.len)
- found += A.search_contents_for(path,filter_path)
+ found += A.search_contents_for(path, filter_path)
return found
@@ -395,43 +418,58 @@
/atom/proc/get_spooked()
return
-/atom/proc/add_hiddenprint(mob/living/M as mob)
- if(isnull(M)) return
- if(isnull(M.key)) return
+/**
+ Base proc, intended to be overriden.
+
+ This should only be called from one place: inside the slippery component.
+ Called after a human mob slips on this atom.
+
+ If you want the person who slipped to have something special done to them, put it here.
+*/
+/atom/proc/after_slip(mob/living/carbon/human/H)
+ return
+
+/atom/proc/add_hiddenprint(mob/living/M)
+ if(isnull(M))
+ return
+ if(isnull(M.key))
+ return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!istype(H.dna, /datum/dna))
- return 0
+ return FALSE
if(H.gloves)
if(fingerprintslast != H.ckey)
//Add the list if it does not exist.
if(!fingerprintshidden)
fingerprintshidden = list()
- fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key)
+ fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []", H.real_name, H.key)
fingerprintslast = H.ckey
- return 0
+ return FALSE
if(!fingerprints)
if(fingerprintslast != H.ckey)
//Add the list if it does not exist.
if(!fingerprintshidden)
fingerprintshidden = list()
- fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key)
+ fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []", H.real_name, H.key)
fingerprintslast = H.ckey
- return 1
+ return TRUE
else
if(fingerprintslast != M.ckey)
//Add the list if it does not exist.
if(!fingerprintshidden)
fingerprintshidden = list()
- fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key)
+ fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []", M.real_name, M.key)
fingerprintslast = M.ckey
return
//Set ignoregloves to add prints irrespective of the mob having gloves on.
-/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0)
- if(isnull(M)) return
- if(isnull(M.key)) return
+/atom/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE)
+ if(isnull(M))
+ return
+ if(isnull(M.key))
+ return
if(ishuman(M))
//Add the list if it does not exist.
if(!fingerprintshidden)
@@ -445,7 +483,7 @@
if(fingerprintslast != M.key)
fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]"
fingerprintslast = M.key
- return 0 //Now, lets get to the dirty work.
+ return FALSE //Now, lets get to the dirty work.
//First, make sure their DNA makes sense.
var/mob/living/carbon/human/H = M
if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32))
@@ -458,20 +496,20 @@
if(H.gloves)
var/obj/item/clothing/gloves/G = H.gloves
if(G.transfer_prints)
- ignoregloves = 1
+ ignoregloves = TRUE
//Now, deal with gloves.
if(!ignoregloves)
if(H.gloves && H.gloves != src)
if(fingerprintslast != H.ckey)
- fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key)
+ fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []", time_stamp(), H.real_name, H.key)
fingerprintslast = H.ckey
H.gloves.add_fingerprint(M)
- return 0
+ return FALSE
//More adminstuffz
if(fingerprintslast != H.ckey)
- fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key)
+ fingerprintshidden += text("\[[]\]Real name: [], Key: []", time_stamp(), H.real_name, H.key)
fingerprintslast = H.ckey
//Make the list if it does not exist.
@@ -484,18 +522,16 @@
// Add the fingerprints
fingerprints[full_print] = full_print
- return 1
+ return TRUE
else
//Smudge up dem prints some
if(fingerprintslast != M.ckey)
- fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key)
+ fingerprintshidden += text("\[[]\]Real name: [], Key: []", time_stamp(), M.real_name, M.key)
fingerprintslast = M.ckey
return
-
-/atom/proc/transfer_fingerprints_to(var/atom/A)
-
+/atom/proc/transfer_fingerprints_to(atom/A)
// Make sure everything are lists.
if(!islist(A.fingerprints))
A.fingerprints = list()
@@ -542,7 +578,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/proc/transfer_mob_blood_dna(mob/living/L)
var/new_blood_dna = L.get_blood_dna_list()
if(!new_blood_dna)
- return 0
+ return FALSE
return transfer_blood_dna(new_blood_dna)
/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L)
@@ -570,14 +606,13 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
var/old_length = blood_DNA.len
blood_DNA |= blood_dna
if(blood_DNA.len > old_length)
- return 1//some new blood DNA was added
-
+ return TRUE//some new blood DNA was added
//to add blood from a mob onto something, and transfer their dna info
/atom/proc/add_mob_blood(mob/living/M)
var/list/blood_dna = M.get_blood_dna_list()
if(!blood_dna)
- return 0
+ return FALSE
var/bloodcolor = "#A10808"
var/list/b_data = M.get_blood_data(M.get_blood_id())
if(b_data)
@@ -587,7 +622,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
//to add blood onto something, with blood dna info to include.
/atom/proc/add_blood(list/blood_dna, color)
- return 0
+ return FALSE
/obj/add_blood(list/blood_dna, color)
return transfer_blood_dna(blood_dna)
@@ -595,10 +630,10 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/obj/item/add_blood(list/blood_dna, color)
var/blood_count = !blood_DNA ? 0 : blood_DNA.len
if(!..())
- return 0
+ return FALSE
if(!blood_count)//apply the blood-splatter overlay if it isn't already in there
add_blood_overlay(color)
- return 1 //we applied blood to the item
+ return TRUE //we applied blood to the item
/obj/item/clothing/gloves/add_blood(list/blood_dna, color)
. = ..()
@@ -610,7 +645,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
B = new /obj/effect/decal/cleanable/blood/splatter(src)
B.transfer_blood_dna(blood_dna) //give blood info to the blood decal.
B.basecolor = color
- return 1 //we bloodied the floor
+ return TRUE //we bloodied the floor
/mob/living/carbon/human/add_blood(list/blood_dna, color)
if(wear_suit)
@@ -641,7 +676,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
verbs += /mob/living/carbon/human/proc/bloody_doodle
update_inv_gloves() //handles bloody hands overlays and updating
- return 1
+ return TRUE
/obj/item/proc/add_blood_overlay(color)
if(initial(icon) && initial(icon_state))
@@ -679,7 +714,6 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
if(.)
transfer_blood = 0
-
/obj/item/clothing/shoes/clean_blood()
..()
bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0)
@@ -688,23 +722,42 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
var/mob/M = loc
M.update_inv_shoes()
-
-/mob/living/carbon/human/clean_blood()
- if(gloves)
+/mob/living/carbon/human/clean_blood(clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE)
+ if(w_uniform && !(wear_suit && wear_suit.flags_inv & HIDEJUMPSUIT))
+ if(w_uniform.clean_blood())
+ update_inv_w_uniform()
+ if(gloves && !(wear_suit && wear_suit.flags_inv & HIDEGLOVES))
if(gloves.clean_blood())
- clean_blood()
- update_inv_gloves()
- gloves.germ_level = 0
- else
- ..() // Clear the Blood_DNA list
- if(bloody_hands)
- bloody_hands = 0
update_inv_gloves()
+ gloves.germ_level = 0
+ clean_hands = FALSE
+ if(shoes && !(wear_suit && wear_suit.flags_inv & HIDESHOES))
+ if(shoes.clean_blood())
+ update_inv_shoes()
+ clean_feet = FALSE
+ if(s_store && !(wear_suit && wear_suit.flags_inv & HIDESUITSTORAGE))
+ if(s_store.clean_blood())
+ update_inv_s_store()
+ if(lip_style && !(head && head.flags_inv & HIDEMASK))
+ lip_style = null
+ update_body()
+ if(glasses && !(wear_mask && wear_mask.flags_inv & HIDEEYES))
+ if(glasses.clean_blood())
+ update_inv_glasses()
+ if(l_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS))
+ if(l_ear.clean_blood())
+ update_inv_ears()
+ if(r_ear && !(wear_mask && wear_mask.flags_inv & HIDEEARS))
+ if(r_ear.clean_blood())
+ update_inv_ears()
+ if(belt)
+ if(belt.clean_blood())
+ update_inv_belt()
+ ..(clean_hands, clean_mask, clean_feet)
update_icons() //apply the now updated overlays to the mob
-
-/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE)
- playsound(src, 'sound/effects/splat.ogg', 50, 1)
+/atom/proc/add_vomit_floor(toxvomit = FALSE, green = FALSE)
+ playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
if(!isspaceturf(src))
var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit
var/vomit_reagent = green ? "green_vomit" : "vomit"
@@ -717,23 +770,24 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
// Make toxins vomit look different
if(toxvomit)
- this.icon_state = "vomittox_[pick(1,4)]"
+ this.icon_state = "vomittox_[pick(1, 4)]"
/atom/proc/get_global_map_pos()
- if(!islist(GLOB.global_map) || isemptylist(GLOB.global_map)) return
+ if(!islist(GLOB.global_map) || isemptylist(GLOB.global_map))
+ return
var/cur_x = null
var/cur_y = null
var/list/y_arr = null
- for(cur_x=1,cur_x<=GLOB.global_map.len,cur_x++)
+ for(cur_x in 1 to GLOB.global_map.len)
y_arr = GLOB.global_map[cur_x]
cur_y = y_arr.Find(src.z)
if(cur_y)
break
// to_chat(world, "X = [cur_x]; Y = [cur_y]")
if(cur_x && cur_y)
- return list("x"=cur_x,"y"=cur_y)
+ return list("x" = cur_x, "y" = cur_y)
else
- return 0
+ return null
// Used to provide overlays when using this atom as a viewing focus
// (cameras, locker tint, etc.)
@@ -746,7 +800,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
return
/atom/proc/checkpass(passflag)
- return pass_flags&passflag
+ return pass_flags & passflag
/atom/proc/isinspace()
if(isspaceturf(get_turf(src)))
@@ -787,9 +841,18 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/proc/atom_say(message)
if(!message)
return
- audible_message(" [src] [atom_say_verb], \"[message]\"")
+ var/list/speech_bubble_hearers = list()
+ for(var/mob/M in get_mobs_in_view(7, src))
+ M.show_message(" [src] [atom_say_verb], \"[message]\"", 2, null, 1)
+ if(M.client)
+ speech_bubble_hearers += M.client
-/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
+ if(length(speech_bubble_hearers))
+ var/image/I = image('icons/mob/talk.dmi', src, "[bubble_icon][say_test(message)]", FLY_LAYER)
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30)
+
+/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
return
/atom/vv_edit_var(var_name, var_value)
@@ -846,7 +909,6 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
atom_colours[colour_priority] = coloration
update_atom_colour()
-
/*
Removes an instance of colour_type from the atom's atom_colours list
*/
@@ -861,7 +923,6 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
atom_colours[colour_priority] = null
update_atom_colour()
-
/*
Resets the atom's color to null, and then sets it to the highest priority
colour available
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index ac77d3d4553..d463c85eaf6 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -44,22 +44,24 @@
/atom/movable/Destroy()
unbuckle_all_mobs(force = TRUE)
+
+ . = ..()
if(loc)
loc.handle_atom_del(src)
for(var/atom/movable/AM in contents)
qdel(AM)
+ LAZYCLEARLIST(client_mobs_in_contents)
loc = null
if(pulledby)
- if(pulledby.pulling == src)
- pulledby.pulling = null
- pulledby = null
- return ..()
+ pulledby.stop_pulling()
+ if(orbiting)
+ stop_orbit()
//Returns an atom's power cell, if it has one. Overload for individual items.
/atom/movable/proc/get_cell()
return
-/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+/atom/movable/proc/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(QDELETED(AM))
return FALSE
if(!(AM.can_be_pulled(src, state, force)))
@@ -85,7 +87,7 @@
if(ismob(AM))
var/mob/M = AM
add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL)
- if(!supress_message)
+ if(show_message)
visible_message(" [src] has grabbed [M] passively!")
return TRUE
@@ -118,12 +120,18 @@
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
-/atom/movable/proc/can_be_pulled(user, grab_state, force)
+/atom/movable/proc/can_be_pulled(user, grab_state, force, show_message = FALSE)
if(src == user || !isturf(loc))
return FALSE
- if(anchored || throwing)
+ if(anchored || move_resist == INFINITY)
+ if(show_message)
+ to_chat(user, " [src] appears to be anchored to the ground!")
+ return FALSE
+ if(throwing)
return FALSE
if(force < (move_resist * MOVE_FORCE_PULL_RATIO))
+ if(show_message)
+ to_chat(user, " [src] is too heavy to pull!")
return FALSE
return TRUE
@@ -233,6 +241,9 @@
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src)
+/atom/movable/Uncrossed(atom/movable/AM)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM)
+
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
if(A && yes)
SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
@@ -265,15 +276,8 @@
var/dest_z = (destturf ? destturf.z : null)
if(old_z != dest_z)
onTransitZ(old_z, dest_z)
- if(isturf(destination) && opacity)
- var/turf/new_loc = destination
- new_loc.reconsider_lights()
- if(isturf(old_loc) && opacity)
- old_loc.reconsider_lights()
-
- for(var/datum/light_source/L in light_sources)
- L.source_atom.update_light()
+ Moved(old_loc, NONE)
return 1
@@ -330,7 +334,6 @@
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
@@ -501,7 +504,7 @@
target.fingerprintshidden += fingerprintshidden
target.fingerprintslast = fingerprintslast
-/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
+/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!no_effect && (visual_effect_icon || used_item))
do_item_attack_animation(A, visual_effect_icon, used_item)
@@ -509,9 +512,6 @@
return //don't do an animation if attacking self
var/pixel_x_diff = 0
var/pixel_y_diff = 0
- var/final_pixel_y = initial(pixel_y)
- if(end_pixel_y)
- final_pixel_y = end_pixel_y
var/direction = get_dir(src, A)
if(direction & NORTH)
@@ -525,14 +525,15 @@
pixel_x_diff = -8
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
- animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2)
+ animate(pixel_x = pixel_x - pixel_x_diff, pixel_y = pixel_y - pixel_y_diff, time = 2)
/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item)
var/image/I
if(visual_effect_icon)
I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1)
else if(used_item)
- I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1)
+ I = image(icon = used_item, loc = A, layer = A.layer + 0.1)
+ I.plane = GAME_PLANE
// Scale the icon.
I.transform *= 0.75
@@ -574,3 +575,6 @@
/atom/movable/proc/portal_destroyed(obj/effect/portal/P)
return
+
+/atom/movable/proc/decompile_act(obj/item/matter_decompiler/C, mob/user) // For drones to decompile mobs and objs. See drone for an example.
+ return FALSE
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 08a7b77daff..6bd00d286fe 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -135,8 +135,6 @@
return "health-90"
else
return "health-100" //past this point, you're just in trouble
- return "0"
-
///HOOKS
@@ -270,7 +268,6 @@
return "crit"
else
return "dead"
- return "dead"
//Sillycone hooks
/mob/living/silicon/proc/diag_hud_set_health()
@@ -400,7 +397,6 @@
return "max"
else
return "zero"
- return "zero"
/obj/machinery/hydroponics/proc/plant_hud_set_nutrient()
var/image/holder = hud_list[PLANT_NUTRIENT_HUD]
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index a6ec98e8866..3158cf90303 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -7,6 +7,11 @@
#define NEGATE_MUTATION_THRESHOLD 30 // Occupants with over ## percent radiation threshold will not gain mutations
+#define PAGE_UI "ui"
+#define PAGE_SE "se"
+#define PAGE_BUFFER "buffer"
+#define PAGE_REJUVENATORS "rejuvenators"
+
//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0),
/datum/dna2/record
var/datum/dna/dna = null
@@ -118,17 +123,15 @@
if(usr.incapacitated())
return
-
- eject_occupant()
-
+ eject_occupant(usr)
add_fingerprint(usr)
/obj/machinery/dna_scannernew/Destroy()
- eject_occupant()
+ eject_occupant(null, TRUE)
return ..()
-/obj/machinery/dna_scannernew/proc/eject_occupant()
- go_out()
+/obj/machinery/dna_scannernew/proc/eject_occupant(user, force)
+ go_out(user, force)
for(var/obj/O in src)
if(!istype(O,/obj/item/circuitboard/clonescanner) && \
!istype(O,/obj/item/stock_parts) && \
@@ -144,7 +147,7 @@
set category = null
set name = "Enter DNA Scanner"
- if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other
+ if(usr.incapacitated() || usr.buckled) //are you cuffed, dying, lying, stunned or other
return
if(!ishuman(usr)) //Make sure they're a mob that has dna
to_chat(usr, " Try as you might, you can not climb up into the [src].")
@@ -163,6 +166,7 @@
occupant = usr
icon_state = "scanner_occupied"
add_fingerprint(usr)
+ SStgui.update_uis(src)
/obj/machinery/dna_scannernew/MouseDrop_T(atom/movable/O, mob/user)
if(!istype(O))
@@ -216,6 +220,7 @@
return
beaker = I
+ SStgui.update_uis(src)
I.forceMove(src)
user.visible_message("[user] adds \a [I] to \the [src]!", "You add \a [I] to \the [src]!")
return
@@ -262,6 +267,7 @@
M.forceMove(src)
occupant = M
icon_state = "scanner_occupied"
+ SStgui.update_uis(src)
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
@@ -271,18 +277,22 @@
occupant.notify_ghost_cloning(source = src)
-/obj/machinery/dna_scannernew/proc/go_out()
+/obj/machinery/dna_scannernew/proc/go_out(mob/user, force)
if(!occupant)
- to_chat(usr, " The scanner is empty!")
+ if(user)
+ to_chat(user, " The scanner is empty!")
return
-
- if(locked)
- to_chat(usr, " The scanner is locked!")
+ if(locked && !force)
+ if(user)
+ to_chat(user, " The scanner is locked!")
return
-
occupant.forceMove(loc)
occupant = null
icon_state = "scanner_open"
+ SStgui.update_uis(src)
+
+/obj/machinery/dna_scannernew/force_eject_occupant()
+ go_out(null, TRUE)
/obj/machinery/dna_scannernew/ex_act(severity)
if(occupant)
@@ -295,6 +305,7 @@
occupant = null
updateUsrDialog()
update_icon()
+ SStgui.update_uis(src)
// Checks if occupants can be irradiated/mutated - prevents exploits where wearing full rad protection would still let you gain mutations
/obj/machinery/dna_scannernew/proc/radiation_check()
@@ -332,12 +343,11 @@
var/injector_ready = FALSE //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/disk = null
- var/selected_menu_key = null
+ var/selected_menu_key = PAGE_UI
anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
- var/waiting_for_user_input = 0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS
@@ -346,7 +356,7 @@
I.forceMove(src)
disk = I
to_chat(user, "You insert [I].")
- SSnanoui.update_uis(src) // update all UIs attached to src()
+ SStgui.update_uis(src)
return
else
return ..()
@@ -398,35 +408,18 @@
if(stat & (NOPOWER|BROKEN))
return
- ui_interact(user)
+ tgui_interact(user)
- /**
- * The ui_interact proc is used to open and update Nano UIs
- * If ui_interact is not used then the UI will not update correctly
- * ui_interact is currently defined for /atom/movable
- *
- * @param user /mob The mob who is interacting with this ui
- * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
- *
- * @return nothing
- */
-/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
+/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(user == connected.occupant)
return
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700)
- // open the new ui window
+ ui = new(user, src, ui_key, "DNAModifier", name, 660, 700, master_ui, state)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/computer/scan_consolenew/ui_data(mob/user, datum/topic_state/state)
+/obj/machinery/computer/scan_consolenew/tgui_data(mob/user)
var/data[0]
data["selectedMenuKey"] = selected_menu_key
data["locked"] = connected.locked
@@ -489,7 +482,7 @@
occupantData["uniqueIdentity"] = connected.occupant.dna.uni_identity
occupantData["structuralEnzymes"] = connected.occupant.dna.struc_enzymes
occupantData["radiationLevel"] = connected.occupant.radiation
- data["occupant"] = occupantData;
+ data["occupant"] = occupantData
data["isBeakerLoaded"] = connected.beaker ? 1 : 0
data["beakerLabel"] = null
@@ -500,9 +493,12 @@
for(var/datum/reagent/R in connected.beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
+ // Transfer modal information if there is one
+ data["modal"] = tgui_modal_data(src)
+
return data
-/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
+/obj/machinery/computer/scan_consolenew/tgui_act(action, params)
if(..())
return FALSE // don't update uis
if(!istype(usr.loc, /turf))
@@ -511,405 +507,350 @@
return FALSE // don't update uis
if(irradiating) // Make sure that it isn't already irradiating someone...
return FALSE // don't update uis
+ if(stat & (NOPOWER|BROKEN))
+ return
add_fingerprint(usr)
- if(href_list["selectMenuKey"])
- selected_menu_key = href_list["selectMenuKey"]
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["toggleLock"])
- if((connected && connected.occupant))
- connected.locked = !(connected.locked)
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["pulseRadiation"])
- irradiating = radiation_duration
- var/lock_state = connected.locked
- connected.locked = TRUE //lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10 * radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
- connected.locked = lock_state
-
- if(!connected.occupant)
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
- connected.occupant.apply_effect(radiation, IRRADIATE, 0)
- if(connected.radiation_check())
- return TRUE
-
- if(prob(95))
- if(prob(75))
- randmutb(connected.occupant)
- else
- randmuti(connected.occupant)
- else
- if(prob(95))
- randmutg(connected.occupant)
- else
- randmuti(connected.occupant)
-
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["radiationDuration"])
- if(text2num(href_list["radiationDuration"]) > 0)
- if(radiation_duration < 20)
- radiation_duration += 2
- else
- if(radiation_duration > 2)
- radiation_duration -= 2
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["radiationIntensity"])
- if(text2num(href_list["radiationIntensity"]) > 0)
- if(radiation_intensity < 10)
- radiation_intensity++
- else
- if(radiation_intensity > 1)
- radiation_intensity--
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0)
- if(selected_ui_target < 15)
- selected_ui_target++
- selected_ui_target_hex = selected_ui_target
- switch(selected_ui_target)
- if(10)
- selected_ui_target_hex = "A"
- if(11)
- selected_ui_target_hex = "B"
- if(12)
- selected_ui_target_hex = "C"
- if(13)
- selected_ui_target_hex = "D"
- if(14)
- selected_ui_target_hex = "E"
- if(15)
- selected_ui_target_hex = "F"
- else
- selected_ui_target = 0
- selected_ui_target_hex = 0
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1)
- if(selected_ui_target > 0)
- selected_ui_target--
- selected_ui_target_hex = selected_ui_target
- switch(selected_ui_target)
- if(10)
- selected_ui_target_hex = "A"
- if(11)
- selected_ui_target_hex = "B"
- if(12)
- selected_ui_target_hex = "C"
- if(13)
- selected_ui_target_hex = "D"
- if(14)
- selected_ui_target_hex = "E"
- else
- selected_ui_target = 15
- selected_ui_target_hex = "F"
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click
- var/select_block = text2num(href_list["selectUIBlock"])
- var/select_subblock = text2num(href_list["selectUISubblock"])
- if((select_block <= DNA_UI_LENGTH) && (select_block >= 1))
- selected_ui_block = select_block
- if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
- selected_ui_subblock = select_subblock
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["pulseUIRadiation"])
- var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
-
- irradiating = radiation_duration
- var/lock_state = connected.locked
- connected.locked = TRUE //lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10 * radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
- connected.locked = lock_state
-
- if(!connected.occupant)
- return TRUE
-
- if(prob((80 + (radiation_duration / 2))))
- var/radiation = (radiation_intensity + radiation_duration)
- connected.occupant.apply_effect(radiation,IRRADIATE,0)
-
- if(connected.radiation_check())
- return TRUE
-
- block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
- connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
- connected.occupant.UpdateAppearance()
- else
- var/radiation = ((radiation_intensity * 2) + radiation_duration)
- connected.occupant.apply_effect(radiation, IRRADIATE, 0)
- if(connected.radiation_check())
- return TRUE
-
- if(prob(20 + radiation_intensity))
- randmutb(connected.occupant)
- domutcheck(connected.occupant, connected)
- else
- randmuti(connected.occupant)
- connected.occupant.UpdateAppearance()
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if(href_list["injectRejuvenators"])
- if(!connected.occupant)
- return FALSE
- var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5
- if(inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking
- inject_amount = 0
- if(inject_amount > 50)
- inject_amount = 50
- connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
- connected.beaker.reagents.reaction(connected.occupant)
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- ////////////////////////////////////////////////////////
-
- if(href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
- var/select_block = text2num(href_list["selectSEBlock"])
- var/select_subblock = text2num(href_list["selectSESubblock"])
- if((select_block <= DNA_SE_LENGTH) && (select_block >= 1))
- selected_se_block = select_block
- if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
- selected_se_subblock = select_subblock
- //testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).")
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["pulseSERadiation"])
- var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
- //var/original_block=block
- //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
-
- irradiating = radiation_duration
- var/lock_state = connected.locked
- connected.locked = TRUE //lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
-
- sleep(10 * radiation_duration) // sleep for radiation_duration seconds
-
- irradiating = 0
- connected.locked = lock_state
-
- if(connected.occupant)
- if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
- var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
- connected.occupant.apply_effect(radiation, IRRADIATE, 0)
-
- if(connected.radiation_check())
- return 1
-
- var/real_SE_block=selected_se_block
- block = miniscramble(block, radiation_intensity, radiation_duration)
- if(prob(20))
- if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
- real_SE_block++
- else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
- real_SE_block--
-
- //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
- connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
- domutcheck(connected.occupant, connected)
- else
- var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
- connected.occupant.apply_effect(radiation, IRRADIATE, 0)
-
- if(connected.radiation_check())
- return 1
-
- if(prob(80 - radiation_duration))
- //testing("Random bad mut!")
- randmutb(connected.occupant)
- domutcheck(connected.occupant, connected)
- else
- randmuti(connected.occupant)
- //testing("Random identity mut!")
- connected.occupant.UpdateAppearance()
- return TRUE // return 1 forces an update to all Nano uis attached to src
-
- if(href_list["ejectBeaker"])
- if(connected.beaker)
- var/obj/item/reagent_containers/glass/B = connected.beaker
- B.forceMove(connected.loc)
- connected.beaker = null
+ if(tgui_act_modal(action, params))
return TRUE
- if(href_list["ejectOccupant"])
- connected.eject_occupant()
- return TRUE
-
- // Transfer Buffer Management
- if(href_list["bufferOption"])
- var/bufferOption = href_list["bufferOption"]
-
- // These bufferOptions do not require a bufferId
- if(bufferOption == "wipeDisk")
- if((isnull(disk)) || (disk.read_only))
- //temphtml = "Invalid disk. Please try again."
- return FALSE
-
- disk.buf = null
- //temphtml = "Data saved."
- return TRUE
-
- if(bufferOption == "ejectDisk")
- if(!disk)
+ . = TRUE
+ switch(action)
+ if("selectMenuKey")
+ var/key = params["key"]
+ if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS)))
return
- disk.forceMove(get_turf(src))
- disk = null
- return TRUE
-
- // All bufferOptions from here on require a bufferId
- if(!href_list["bufferId"])
- return FALSE
-
- var/bufferId = text2num(href_list["bufferId"])
-
- if(bufferId < 1 || bufferId > 3)
- return FALSE // Not a valid buffer id
-
- if(bufferOption == "saveUI")
- if(connected.occupant && connected.occupant.dna)
- var/datum/dna2/record/databuf = new
- databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
- databuf.dna = connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- databuf.dna.real_name=connected.occupant.name
- databuf.name = "Unique Identifier"
- buffers[bufferId] = databuf
- return TRUE
-
- if(bufferOption == "saveUIAndUE")
- if(connected.occupant && connected.occupant.dna)
- var/datum/dna2/record/databuf = new
- databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
- databuf.dna = connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- databuf.dna.real_name=connected.occupant.dna.real_name
- databuf.name = "Unique Identifier + Unique Enzymes"
- buffers[bufferId] = databuf
- return TRUE
-
- if(bufferOption == "saveSE")
- if(connected.occupant && connected.occupant.dna)
- var/datum/dna2/record/databuf = new
- databuf.types = DNA2_BUF_SE
- databuf.dna = connected.occupant.dna.Clone()
- if(ishuman(connected.occupant))
- databuf.dna.real_name = connected.occupant.dna.real_name
- databuf.name = "Structural Enzymes"
- buffers[bufferId] = databuf
- return TRUE
-
- if(bufferOption == "clear")
- buffers[bufferId] = new /datum/dna2/record()
- return TRUE
-
- if(bufferOption == "changeLabel")
- var/datum/dna2/record/buf = buffers[bufferId]
- var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null)
- buf.name = text
- buffers[bufferId] = buf
- return TRUE
-
- if(bufferOption == "transfer")
- if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
- return TRUE
-
- irradiating = 2
+ selected_menu_key = key
+ if("toggleLock")
+ if(connected && connected.occupant)
+ connected.locked = !(connected.locked)
+ if("pulseRadiation")
+ irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
- SSnanoui.update_uis(src) // update all UIs attached to src
- sleep(2 SECONDS)
+ SStgui.update_uis(src)
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
- var/radiation = (rand(20,50) / connected.damage_coeff)
+ if(!connected.occupant)
+ return
+
+ var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
-
if(connected.radiation_check())
- return TRUE
+ return
- var/datum/dna2/record/buf = buffers[bufferId]
-
- if((buf.types & DNA2_BUF_UI))
- if((buf.types & DNA2_BUF_UE))
- connected.occupant.real_name = buf.dna.real_name
- connected.occupant.name = buf.dna.real_name
- connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
- else if(buf.types & DNA2_BUF_SE)
- connected.occupant.dna.SE = buf.dna.SE.Copy()
- connected.occupant.dna.UpdateSE()
- domutcheck(connected.occupant, connected)
- return TRUE
-
- if(bufferOption == "createInjector")
- if(injector_ready && !waiting_for_user_input)
-
- var/success = 1
- var/obj/item/dnainjector/I = new /obj/item/dnainjector
- var/datum/dna2/record/buf = buffers[bufferId]
- buf = buf.copy()
- if(href_list["createBlockInjector"])
- waiting_for_user_input=1
- var/list/selectedbuf
- if(buf.types & DNA2_BUF_SE)
- selectedbuf=buf.dna.SE
- else
- selectedbuf=buf.dna.UI
- var/blk = input(usr,"Select Block","Block") as null|anything in all_dna_blocks(selectedbuf)
- success = setInjectorBlock(I,blk,buf)
+ if(prob(95))
+ if(prob(75))
+ randmutb(connected.occupant)
else
- I.buf = buf
- waiting_for_user_input = 0
- if(success)
- I.forceMove(loc)
- I.name += " ([buf.name])"
- if(connected)
- I.damage_coeff = connected.damage_coeff
- injector_ready = FALSE
- spawn(300)
- injector_ready = TRUE
- return TRUE
+ randmuti(connected.occupant)
+ else
+ if(prob(95))
+ randmutg(connected.occupant)
+ else
+ randmuti(connected.occupant)
+ if("radiationDuration")
+ radiation_duration = clamp(text2num(params["value"]), 1, 20)
+ if("radiationIntensity")
+ radiation_intensity = clamp(text2num(params["value"]), 1, 10)
+ ////////////////////////////////////////////////////////
+ if("changeUITarget")
+ selected_ui_target = clamp(text2num(params["value"]), 1, 15)
+ selected_ui_target_hex = num2text(selected_ui_target, 1, 16)
+ if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click
+ var/select_block = text2num(params["block"])
+ var/select_subblock = text2num(params["subblock"])
+ if(!select_block || !select_subblock)
+ return
- if(bufferOption == "loadDisk")
- if((isnull(disk)) || (!disk.buf))
- //temphtml = "Invalid disk. Please try again."
- return FALSE
+ selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH)
+ selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
+ if("pulseUIRadiation")
+ var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
- buffers[bufferId] = disk.buf.copy()
- //temphtml = "Data loaded."
- return TRUE
+ irradiating = radiation_duration
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
- if(bufferOption == "saveDisk")
- if((isnull(disk)) || (disk.read_only))
- //temphtml = "Invalid disk. Please try again."
- return FALSE
+ SStgui.update_uis(src)
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
- var/datum/dna2/record/buf = buffers[bufferId]
+ irradiating = 0
+ connected.locked = lock_state
- disk.buf = buf.copy()
- disk.name = "data disk - '[buf.dna.real_name]'"
- //temphtml = "Data saved."
- return TRUE
+ if(!connected.occupant)
+ return
+ if(prob((80 + (radiation_duration / 2))))
+ var/radiation = (radiation_intensity + radiation_duration)
+ connected.occupant.apply_effect(radiation,IRRADIATE,0)
+
+ if(connected.radiation_check())
+ return
+
+ block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
+ connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
+ connected.occupant.UpdateAppearance()
+ else
+ var/radiation = ((radiation_intensity * 2) + radiation_duration)
+ connected.occupant.apply_effect(radiation, IRRADIATE, 0)
+ if(connected.radiation_check())
+ return
+
+ if(prob(20 + radiation_intensity))
+ randmutb(connected.occupant)
+ domutcheck(connected.occupant, connected)
+ else
+ randmuti(connected.occupant)
+ connected.occupant.UpdateAppearance()
+ ////////////////////////////////////////////////////////
+ if("injectRejuvenators")
+ if(!connected.occupant || !connected.beaker)
+ return
+ var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
+ if(!inject_amount)
+ return
+ connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
+ connected.beaker.reagents.reaction(connected.occupant)
+ ////////////////////////////////////////////////////////
+ if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
+ var/select_block = text2num(params["block"])
+ var/select_subblock = text2num(params["subblock"])
+ if(!select_block || !select_subblock)
+ return
+
+ selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH)
+ selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
+ if("pulseSERadiation")
+ var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
+ //var/original_block=block
+ //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
+
+ irradiating = radiation_duration
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
+
+ SStgui.update_uis(src)
+ sleep(10 * radiation_duration) // sleep for radiation_duration seconds
+
+ irradiating = 0
+ connected.locked = lock_state
+
+ if(connected.occupant)
+ if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
+ var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
+ connected.occupant.apply_effect(radiation, IRRADIATE, 0)
+
+ if(connected.radiation_check())
+ return 1
+
+ var/real_SE_block=selected_se_block
+ block = miniscramble(block, radiation_intensity, radiation_duration)
+ if(prob(20))
+ if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
+ real_SE_block++
+ else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
+ real_SE_block--
+
+ //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
+ connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
+ domutcheck(connected.occupant, connected)
+ else
+ var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
+ connected.occupant.apply_effect(radiation, IRRADIATE, 0)
+
+ if(connected.radiation_check())
+ return
+
+ if(prob(80 - radiation_duration))
+ //testing("Random bad mut!")
+ randmutb(connected.occupant)
+ domutcheck(connected.occupant, connected)
+ else
+ randmuti(connected.occupant)
+ //testing("Random identity mut!")
+ connected.occupant.UpdateAppearance()
+ if("ejectBeaker")
+ if(connected.beaker)
+ var/obj/item/reagent_containers/glass/B = connected.beaker
+ B.forceMove(connected.loc)
+ connected.beaker = null
+ if("ejectOccupant")
+ connected.eject_occupant()
+ // Transfer Buffer Management
+ if("bufferOption")
+ var/bufferOption = params["option"]
+ var/bufferId = text2num(params["id"])
+ if(bufferId < 1 || bufferId > 3) // Not a valid buffer id
+ return
+
+ var/datum/dna2/record/buffer = buffers[bufferId]
+ switch(bufferOption)
+ if("saveUI")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf = new
+ databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ databuf.dna.real_name=connected.occupant.name
+ databuf.name = "Unique Identifier"
+ buffers[bufferId] = databuf
+ if("saveUIAndUE")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf = new
+ databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ databuf.dna.real_name=connected.occupant.dna.real_name
+ databuf.name = "Unique Identifier + Unique Enzymes"
+ buffers[bufferId] = databuf
+ if("saveSE")
+ if(connected.occupant && connected.occupant.dna)
+ var/datum/dna2/record/databuf = new
+ databuf.types = DNA2_BUF_SE
+ databuf.dna = connected.occupant.dna.Clone()
+ if(ishuman(connected.occupant))
+ databuf.dna.real_name = connected.occupant.dna.real_name
+ databuf.name = "Structural Enzymes"
+ buffers[bufferId] = databuf
+ if("clear")
+ buffers[bufferId] = new /datum/dna2/record()
+ if("changeLabel")
+ tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
+ if("transfer")
+ if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
+ return
+
+ irradiating = 2
+ var/lock_state = connected.locked
+ connected.locked = TRUE //lock it
+
+ SStgui.update_uis(src)
+ sleep(2 SECONDS)
+
+ irradiating = 0
+ connected.locked = lock_state
+
+ var/radiation = (rand(20,50) / connected.damage_coeff)
+ connected.occupant.apply_effect(radiation, IRRADIATE, 0)
+
+ if(connected.radiation_check())
+ return
+
+ var/datum/dna2/record/buf = buffers[bufferId]
+
+ if((buf.types & DNA2_BUF_UI))
+ if((buf.types & DNA2_BUF_UE))
+ connected.occupant.real_name = buf.dna.real_name
+ connected.occupant.name = buf.dna.real_name
+ connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
+ else if(buf.types & DNA2_BUF_SE)
+ connected.occupant.dna.SE = buf.dna.SE.Copy()
+ connected.occupant.dna.UpdateSE()
+ domutcheck(connected.occupant, connected)
+ if("createInjector")
+ if(!injector_ready)
+ return
+ if(text2num(params["block"]) > 0)
+ var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI)
+ tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices)
+ else
+ create_injector(bufferId, TRUE)
+ if("loadDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ buffers[bufferId] = disk.buf.copy()
+ if("saveDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ var/datum/dna2/record/buf = buffers[bufferId]
+ disk.buf = buf.copy()
+ disk.name = "data disk - '[buf.dna.real_name]'"
+ if("wipeDisk")
+ if(isnull(disk) || disk.read_only)
+ return
+ disk.buf = null
+ if("ejectDisk")
+ if(!disk)
+ return
+ disk.forceMove(get_turf(src))
+ disk = null
+
+/**
+ * Creates a blank injector with the name of the buffer at the given buffer_id
+ *
+ * Arguments:
+ * * buffer_id - The ID of the buffer
+ * * copy_buffer - Whether the injector should copy the buffer contents
+ */
+/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE)
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+
+ // Cooldown
+ injector_ready = FALSE
+ addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS)
+
+ // Create it
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ var/obj/item/dnainjector/I = new()
+ I.forceMove(loc)
+ I.name += " ([buf.name])"
+ if(copy_buffer)
+ I.buf = buf.copy()
+ if(connected)
+ I.damage_coeff = connected.damage_coeff
+ return I
+
+/**
+ * Called when the injector creation cooldown finishes
+ */
+/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish()
+ injector_ready = TRUE
+
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params)
+ . = TRUE
+ var/id = params["id"] // The modal's ID
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ if("createInjectorBlock")
+ var/buffer_id = text2num(arguments["id"])
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ var/obj/item/dnainjector/I = create_injector(buffer_id)
+ setInjectorBlock(I, answer, buf.copy())
+ if("changeBufferLabel")
+ var/buffer_id = text2num(arguments["id"])
+ if(buffer_id < 1 || buffer_id > length(buffers))
+ return
+ var/datum/dna2/record/buf = buffers[buffer_id]
+ buf.name = answer
+ buffers[buffer_id] = buf
+ else
+ return FALSE
+ else
+ return FALSE
+
+
+#undef PAGE_UI
+#undef PAGE_SE
+#undef PAGE_BUFFER
+#undef PAGE_REJUVENATORS
/////////////////////////// DNA MACHINES
diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm
index 48ffb648121..a887959db48 100644
--- a/code/game/dna/genes/disabilities.dm
+++ b/code/game/dna/genes/disabilities.dm
@@ -65,7 +65,7 @@
/datum/dna/gene/disability/epilepsy/OnMobLife(mob/living/carbon/human/H)
if((prob(1) && H.paralysis < 1))
- H.visible_message(" [src] starts having a seizure!"," You have a seizure!")
+ H.visible_message(" [H] starts having a seizure!"," You have a seizure!")
H.Paralyse(10)
H.Jitter(1000)
@@ -249,14 +249,13 @@
block = GLOB.wingdingsblock
/datum/dna/gene/disability/wingdings/OnSay(mob/M, message)
- var/list/chars = string2charlist(message)
var/garbled_message = ""
- for(var/C in chars)
- if(C in GLOB.alphabet_uppercase)
+ for(var/i in 1 to length(message))
+ if(message[i] in GLOB.alphabet_uppercase)
garbled_message += pick(GLOB.alphabet_uppercase)
- else if(C in GLOB.alphabet)
+ else if(message[i] in GLOB.alphabet)
garbled_message += pick(GLOB.alphabet)
else
- garbled_message += C
+ garbled_message += message[i]
message = garbled_message
return message
diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm
index cc54d817752..9092448e398 100644
--- a/code/game/dna/genes/vg_powers.dm
+++ b/code/game/dna/genes/vg_powers.dm
@@ -174,7 +174,7 @@
M.update_dna()
- M.visible_message(" [src] morphs and changes [p_their()] appearance!", " You change your appearance!", " Oh, god! What the hell was that? It sounded like flesh getting squished and bone ground into a different shape!")
+ M.visible_message(" [M] morphs and changes [M.p_their()] appearance!", " You change your appearance!", " Oh, god! What the hell was that? It sounded like flesh getting squished and bone ground into a different shape!")
/datum/dna/gene/basic/grant_spell/remotetalk
name = "Telepathy"
diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm
index a11e5d922ba..641167c768c 100644
--- a/code/game/gamemodes/blob/blob.dm
+++ b/code/game/gamemodes/blob/blob.dm
@@ -46,7 +46,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
var/datum/mind/blob = pick(possible_blobs)
infected_crew += blob
blob.special_role = SPECIAL_ROLE_BLOB
- update_blob_icons_added(blob)
blob.restricted_roles = restricted_jobs
log_game("[key_name(blob)] has been selected as a Blob")
possible_blobs -= blob
@@ -152,6 +151,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
for(var/datum/mind/blob in infected_crew)
greet_blob(blob)
+ update_blob_icons_added(blob)
if(SSshuttle)
SSshuttle.emergencyNoEscape = 1
diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm
index 6128c22f66d..93b84dbb54d 100644
--- a/code/game/gamemodes/blob/blob_report.dm
+++ b/code/game/gamemodes/blob/blob_report.dm
@@ -19,7 +19,7 @@
intercepttext += "Message ends."
if(2)
var/nukecode = rand(10000, 99999)
- for(var/obj/machinery/nuclearbomb/bomb in world)
+ for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines)
if(bomb && bomb.r_code)
if(is_station_level(bomb.z))
bomb.r_code = nukecode
@@ -40,7 +40,7 @@
aiPlayer.set_zeroth_law(law)
to_chat(aiPlayer, "Laws Updated: [law]")
- print_command_report(intercepttext, interceptname)
+ print_command_report(intercepttext, interceptname, FALSE)
GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update")
/datum/station_state
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index b7bcd737dfd..34c717f11a8 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -7,7 +7,9 @@
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB
+ status_flags = NONE //No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you.
faction = list(ROLE_BLOB)
+ bubble_icon = "blob"
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 360
@@ -48,6 +50,7 @@
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
attacktext = "hits"
attack_sound = 'sound/weapons/genhit1.ogg'
+ flying = TRUE
speak_emote = list("pulses")
var/obj/structure/blob/factory/factory = null
var/list/human_overlays = list()
@@ -56,7 +59,7 @@
/mob/living/simple_animal/hostile/blob/blobspore/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE)
..()
- adjustBruteLoss(Clamp(0.01 * exposed_temperature, 1, 5))
+ adjustBruteLoss(clamp(0.01 * exposed_temperature, 1, 5))
/mob/living/simple_animal/hostile/blob/blobspore/CanPass(atom/movable/mover, turf/target, height=0)
@@ -86,8 +89,8 @@
is_zombie = TRUE
if(H.wear_suit)
var/obj/item/clothing/suit/armor/A = H.wear_suit
- if(A.armor && A.armor["melee"])
- maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
+ if(A.armor && A.armor.getRating("melee"))
+ maxHealth += A.armor.getRating("melee") //That zombie's got armor, I want armor!
maxHealth += 40
health = maxHealth
name = "blob zombie"
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index 672ab037cc5..dc8d0f5aba2 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -17,12 +17,12 @@
START_PROCESSING(SSobj, src)
GLOB.poi_list |= src
adjustcolors(color) //so it atleast appears
- if(!overmind)
- create_overmind(new_overmind)
- if(overmind)
- adjustcolors(overmind.blob_reagent_datum.color)
if(offspring)
is_offspring = 1
+ if(overmind)
+ adjustcolors(overmind.blob_reagent_datum.color)
+ if(!overmind)
+ create_overmind(new_overmind)
point_rate = new_rate
..(loc, h)
@@ -89,41 +89,41 @@
..()
-/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay)
+/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay)
if(overmind_get_delay > world.time && !override_delay)
return
- overmind_get_delay = world.time + 3000 // 5 minutes
+ overmind_get_delay = world.time + 5 MINUTES
if(overmind)
qdel(overmind)
+ INVOKE_ASYNC(src, .proc/get_new_overmind, new_overmind)
+
+/obj/structure/blob/core/proc/get_new_overmind(client/new_overmind)
var/mob/C = null
var/list/candidates = list()
-
- spawn()
- if(!new_overmind)
- if(is_offspring)
- candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
- else
- candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
-
- if(candidates.len)
- C = pick(candidates)
+ if(!new_overmind)
+ // sendit
+ if(is_offspring)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = src)
else
- C = new_overmind
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = src)
- if(C)
- var/mob/camera/blob/B = new(src.loc)
- B.key = C.key
- B.blob_core = src
- src.overmind = B
- color = overmind.blob_reagent_datum.color
- if(B.mind && !B.mind.special_role)
- B.mind.make_Overmind()
- spawn(0)
- if(is_offspring)
- B.is_offspring = TRUE
+ if(length(candidates))
+ C = pick(candidates)
+ else
+ C = new_overmind
+
+ if(C && !QDELETED(src))
+ var/mob/camera/blob/B = new(loc)
+ B.key = C.key
+ B.blob_core = src
+ overmind = B
+ color = overmind.blob_reagent_datum.color
+ if(B.mind && !B.mind.special_role)
+ B.mind.make_Overmind()
+ B.is_offspring = is_offspring
/obj/structure/blob/core/proc/lateblobtimer()
addtimer(CALLBACK(src, .proc/lateblobcheck), 50)
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index 0c2342328a2..316a136afc6 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -57,7 +57,7 @@
/mob/camera/blob/proc/add_points(var/points)
if(points != 0)
- blob_points = Clamp(blob_points + points, 0, max_blob_points)
+ blob_points = clamp(blob_points + points, 0, max_blob_points)
if(hud_used)
hud_used.blobpwrdisplay.maptext = " [round(src.blob_points)] "
diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm
index 6f2eed39499..350da7c7593 100644
--- a/code/game/gamemodes/blob/powers.dm
+++ b/code/game/gamemodes/blob/powers.dm
@@ -237,7 +237,7 @@
blobber.AIStatus = AI_OFF
blobber.LoseTarget()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
var/mob/C = pick(candidates)
if(C)
@@ -389,7 +389,7 @@
return
split_used = TRUE
- new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, "offspring")
+ new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, offspring = TRUE)
qdel(N)
if(SSticker && SSticker.mode.name == "blob")
diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm
index 886975a2c92..5e2c3befc9a 100644
--- a/code/game/gamemodes/blob/theblob.dm
+++ b/code/game/gamemodes/blob/theblob.dm
@@ -49,7 +49,7 @@
/obj/structure/blob/CanAStarPass(ID, dir, caller)
. = 0
- if(ismovableatom(caller))
+ if(ismovable(caller))
var/atom/movable/mover = caller
. = . || mover.checkpass(PASSBLOB)
@@ -74,9 +74,6 @@
/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand
-
- set background = BACKGROUND_ENABLED
-
RegenHealth()
if(run_action())//If we can do something here then we dont need to pulse more
@@ -179,7 +176,7 @@
return 0
var/armor_protection = 0
if(damage_flag)
- armor_protection = armor[damage_flag]
+ armor_protection = armor.getRating(damage_flag)
damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
if(overmind && damage_flag)
damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag)
diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm
index 9c33ffaaca5..b3b4000bbf1 100644
--- a/code/game/gamemodes/changeling/evolution_menu.dm
+++ b/code/game/gamemodes/changeling/evolution_menu.dm
@@ -370,16 +370,11 @@ GLOBAL_LIST_EMPTY(sting_paths)
mind.changeling.purchasedpowers += path
path.on_purchase(src)
else //for respec
- var/datum/action/changeling/hivemind_upload/S1 = new
+ var/datum/action/changeling/hivemind_pick/S1 = new
if(!mind.changeling.has_sting(S1))
mind.changeling.purchasedpowers+=S1
S1.Grant(src)
- var/datum/action/changeling/hivemind_download/S2 = new
- if(!mind.changeling.has_sting(S2))
- mind.changeling.purchasedpowers+=S2
- S2.Grant(src)
-
var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
mind.changeling.absorbed_dna |= C.dna.Clone()
mind.changeling.trim_dna()
diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm
index 34a2d67b58b..28afcaa9c36 100644
--- a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm
+++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm
@@ -49,7 +49,7 @@
/obj/item/organ/internal/cyberimp/eyes/shield/ling/on_life()
..()
var/obj/item/organ/internal/eyes/E = owner.get_int_organ(/obj/item/organ/internal/eyes)
- if(owner.eye_blind || owner.eye_blurry || (BLINDNESS in owner.mutations) || (NEARSIGHTED in owner.mutations) || (E.damage > 0))
+ if(owner.eye_blind || owner.eye_blurry || (BLINDNESS in owner.mutations) || (NEARSIGHTED in owner.mutations) || (E && E.damage > 0))
owner.reagents.add_reagent("oculine", 1)
/obj/item/organ/internal/cyberimp/eyes/shield/ling/prepare_eat()
@@ -76,11 +76,11 @@
var/mob/living/carbon/human/H = owner
H.weakeyes = 1
if(!H.vision_type)
- H.vision_type = new /datum/vision_override/nightvision
+ H.set_sight(/datum/vision_override/nightvision)
/obj/item/organ/internal/cyberimp/eyes/thermals/ling/remove(mob/living/carbon/M, special = 0)
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.weakeyes = 0
- H.vision_type = null
+ H.set_sight(null)
..()
diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm
index 98336608da4..18a5477fd3f 100644
--- a/code/game/gamemodes/changeling/powers/hivemind.dm
+++ b/code/game/gamemodes/changeling/powers/hivemind.dm
@@ -12,27 +12,36 @@
var/datum/changeling/changeling=user.mind.changeling
changeling.changeling_speak = 1
to_chat(user, " Use say \":g message\" to communicate with the other changelings.")
- var/datum/action/changeling/hivemind_upload/S1 = new
+ var/datum/action/changeling/hivemind_pick/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
S1.Grant(user)
- var/datum/action/changeling/hivemind_download/S2 = new
- if(!changeling.has_sting(S2))
- S2.Grant(user)
- changeling.purchasedpowers+=S2
return
// HIVE MIND UPLOAD/DOWNLOAD DNA
GLOBAL_LIST_EMPTY(hivemind_bank)
-/datum/action/changeling/hivemind_upload
+/datum/action/changeling/hivemind_pick
name = "Hive Channel DNA"
- desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it. Costs 10 chemicals."
- button_icon_state = "hivemind_channel"
+ desc = "Allows us to upload or absorb DNA in the airwaves. Does not count towards absorb objectives. Costs 10 chemicals."
+ button_icon_state = "hive_absorb"
chemical_cost = 10
dna_cost = -1
-/datum/action/changeling/hivemind_upload/sting_action(var/mob/user)
+/datum/action/changeling/hivemind_pick/sting_action(mob/user)
+ var/datum/changeling/changeling = user.mind.changeling
+ var/channel_pick = alert("Upload or Absorb DNA?", "Channel Select", "Upload", "Absorb")
+
+ if(channel_pick == "Upload")
+ dna_upload(user)
+ if(channel_pick == "Absorb")
+ if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it.
+ to_chat(user, " We have reached our capacity to store genetic information! We must transform before absorbing more.")
+ return
+ else
+ dna_absorb(user)
+
+/datum/action/changeling/proc/dna_upload(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/dna/DNA in (changeling.absorbed_dna+changeling.protected_dna))
@@ -56,23 +65,7 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
feedback_add_details("changeling_powers","HU")
return 1
-/datum/action/changeling/hivemind_download
- name = "Hive Absorb DNA"
- desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives. Costs 10 chemicals."
- button_icon_state = "hive_absorb"
- chemical_cost = 10
- dna_cost = -1
-
-/datum/action/changeling/hivemind_download/can_sting(var/mob/living/carbon/user)
- if(!..())
- return
- var/datum/changeling/changeling = user.mind.changeling
- if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it.
- to_chat(user, " We have reached our capacity to store genetic information! We must transform before absorbing more.")
- return
- return 1
-
-/datum/action/changeling/hivemind_download/sting_action(var/mob/user)
+/datum/action/changeling/proc/dna_absorb(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/dna/DNA in GLOB.hivemind_bank)
diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm
index ce62242ca0f..d12b970d884 100644
--- a/code/game/gamemodes/changeling/powers/mutations.dm
+++ b/code/game/gamemodes/changeling/powers/mutations.dm
@@ -321,7 +321,7 @@
if(INTENT_GRAB)
C.visible_message(" [L] is grabbed by [H]'s tentacle!"," A tentacle grabs you and pulls you towards [H]!")
- add_attack_logs(src, C, "[src] has grabbed [C] and pulled them towards [H] with a tentacle")
+ add_attack_logs(H, C, "[H] grabbed [C] with a changeling tentacle")
C.throw_at(get_step_towards(H,C), 8, 2, callback=CALLBACK(H, /mob/proc/tentacle_grab, C))
return 1
diff --git a/code/game/gamemodes/changeling/powers/swap_form.dm b/code/game/gamemodes/changeling/powers/swap_form.dm
index 0e02df12d88..97192c86a0f 100644
--- a/code/game/gamemodes/changeling/powers/swap_form.dm
+++ b/code/game/gamemodes/changeling/powers/swap_form.dm
@@ -18,12 +18,15 @@
if((NOCLONE || SKELETON || HUSK) in target.mutations)
to_chat(user, " DNA of [target] is ruined beyond usability!")
return
- if(!istype(target) || issmall(target) || (NO_DNA in target.dna.species.species_traits))
+ if(!istype(target) || !target.mind || issmall(target) || (NO_DNA in target.dna.species.species_traits))
to_chat(user, " [target] is not compatible with this ability.")
return
if(target.mind.changeling)
to_chat(user, " We are unable to swap forms with another changeling!")
return
+ if(target.has_brain_worms() || user.has_brain_worms())
+ to_chat(user, " A foreign presence repels us from this body!")
+ return
return 1
/datum/action/changeling/swap_form/sting_action(var/mob/living/carbon/user)
@@ -63,6 +66,8 @@
target.add_language("Changeling")
user.remove_language("Changeling")
user.regenerate_icons()
+ if(target.stat == DEAD && target.suiciding) //If Target committed suicide, unset flag for User
+ target.suiciding = 0
for(var/power in lingpowers)
var/datum/action/changeling/S = power
diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm
index 94bde26674b..fbfb75b8b6a 100644
--- a/code/game/gamemodes/changeling/powers/tiny_prick.dm
+++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm
@@ -36,11 +36,9 @@
to_chat(user, "We haven't prepared our sting yet!")
if(!iscarbon(target))
return
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if(H.isSynthetic())
- to_chat(user, " This won't work on synthetics.")
- return
+ if(ismachineperson(target))
+ to_chat(user, " This won't work on synthetics.")
+ return
if(!isturf(user.loc))
return
if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0))
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 1a9dd15c65e..433fc5b526a 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -20,8 +20,8 @@ GLOBAL_LIST_EMPTY(all_cults)
var/mob/living/carbon/human/H = mind.current
if(ismindshielded(H)) //mindshield protects against conversions unless removed
return FALSE
-// if(mind.offstation_role) cant convert offstation roles such as ghost spawns
-// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew
+ if(mind.offstation_role)
+ return FALSE
if(issilicon(mind.current))
return FALSE //can't convert machines, that's ratvar's thing
if(isguardian(mind.current))
@@ -84,8 +84,8 @@ GLOBAL_LIST_EMPTY(all_cults)
var/survivors = 0
/datum/game_mode/cult/announce()
- to_chat(world, " The current game mode is - Cult!")
- to_chat(world, " Some crewmembers are attempting to start a cult! \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult. \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.")
+ to_chat(world, " The current game mode is - Cult!")
+ to_chat(world, " Some crew members are attempting to start a cult! \nCultists - complete your objectives. Convert crew members to your cause by using the convert rune. Remember - there is no you, there is only the cult. \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.")
/datum/game_mode/cult/pre_setup()
@@ -147,8 +147,8 @@ GLOBAL_LIST_EMPTY(all_cults)
explanation = "Free objective."
if("eldergod")
explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune. The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin."
- to_chat(cult_mind.current, " Objective #[obj_count]: [explanation]")
- cult_mind.memory += " Objective #[obj_count]: [explanation] "
+ to_chat(cult_mind.current, " Objective #[obj_count]: [explanation]")
+ cult_mind.memory += " Objective #[obj_count]: [explanation] "
/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob)
@@ -210,7 +210,7 @@ GLOBAL_LIST_EMPTY(all_cults)
update_cult_icons_removed(cult_mind)
if(show_message)
for(var/mob/M in viewers(cult_mind.current))
- to_chat(M, " [cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!")
+ to_chat(M, " [cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!")
/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind)
@@ -231,6 +231,8 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/cult/proc/get_unconvertables()
var/list/ucs = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
+ if(player.mind && player.mind.offstation_role)
+ continue
if(!is_convertable_to_cult(player.mind))
ucs += player.mind
return ucs
@@ -284,11 +286,11 @@ GLOBAL_LIST_EMPTY(all_cults)
if(!check_cult_victory())
feedback_set_details("round_end_result","cult win - cult win")
feedback_set("round_end_result",acolytes_survived)
- to_chat(world, " The cult wins! It has succeeded in serving its dark masters!")
+ to_chat(world, " The cult wins! It has succeeded in serving its dark masters!")
else
feedback_set_details("round_end_result","cult loss - staff stopped the cult")
feedback_set("round_end_result",acolytes_survived)
- to_chat(world, " The staff managed to stop the cult!")
+ to_chat(world, " The staff managed to stop the cult!")
var/text = " Cultists escaped: [acolytes_survived]"
@@ -299,7 +301,7 @@ GLOBAL_LIST_EMPTY(all_cults)
switch(objectives[obj_count])
if("survive")
if(!check_survive())
- explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!"
+ explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!"
feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]")
else
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail."
@@ -307,7 +309,7 @@ GLOBAL_LIST_EMPTY(all_cults)
if("sacrifice")
if(sacrifice_target)
if(sacrifice_target in sacrificed)
- explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!"
+ explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!"
feedback_add_details("cult_objective","cult_sacrifice|SUCCESS")
else if(sacrifice_target && sacrifice_target.current)
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail."
@@ -317,7 +319,7 @@ GLOBAL_LIST_EMPTY(all_cults)
feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED")
if("eldergod")
if(!eldergod)
- explanation = "Summon [SSticker.cultdat.entity_name]. Success!"
+ explanation = "Summon [SSticker.cultdat.entity_name]. Success!"
feedback_add_details("cult_objective","cult_narsie|SUCCESS")
else
explanation = "Summon [SSticker.cultdat.entity_name]. Fail."
@@ -332,45 +334,45 @@ GLOBAL_LIST_EMPTY(all_cults)
if("convert")//convert half the crew
if(cult.len >= convert_target)
- explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!"
+ explanation = "Convert [convert_target] crew members ([cult.len] cultists at round end). Success!"
feedback_add_details("cult_objective","cult_convertion|SUCCESS")
else
- explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!"
+ explanation = "Convert [convert_target] crew members ([cult.len] total cultists). Fail."
feedback_add_details("cult_objective","cult_convertion|FAIL")
if("bloodspill")//cover a large portion of the station in blood
if(max_spilled_blood >= spilltarget)
- explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!"
+ explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!"
feedback_add_details("cult_objective","cult_bloodspill|SUCCESS")
else
- explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!"
+ explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail."
feedback_add_details("cult_objective","cult_bloodspill|FAIL")
if("harvest")
if(harvested > harvest_target)
- explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!"
+ explanation = "Offer [harvest_target] humanoids for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!"
feedback_add_details("cult_objective","cult_harvest|SUCCESS")
else
- explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!"
+ explanation = "Offer [harvest_target] humanoids for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail."
feedback_add_details("cult_objective","cult_harvest|FAIL")
if("hijack")
if(!escaped_shuttle)
- explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!"
+ explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!"
feedback_add_details("cult_objective","cult_hijack|SUCCESS")
else
- explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!"
+ explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail."
feedback_add_details("cult_objective","cult_hijack|FAIL")
if("massacre")
if(survivors < massacre_target)
- explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!"
+ explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humanoids left alive) Success!"
feedback_add_details("cult_objective","cult_massacre|SUCCESS")
else
- explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!"
+ explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humanoids left alive) Fail."
feedback_add_details("cult_objective","cult_massacre|FAIL")
- text += " Objective #[obj_count]: [explanation]"
+ text += " Objective #[obj_count]: [explanation]"
to_chat(world, text)
..()
@@ -379,7 +381,7 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/proc/auto_declare_completion_cult()
if(cult.len || (SSticker && GAMEMODE_IS_CULT))
- var/text = " The cultists were:"
+ var/text = " The cultists were:"
for(var/datum/mind/cultist in cult)
text += " [cultist.key] was [cultist.name] ("
diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm
index fcc39124233..0f637153f6c 100644
--- a/code/game/gamemodes/cult/cult_comms.dm
+++ b/code/game/gamemodes/cult/cult_comms.dm
@@ -24,7 +24,7 @@
return
if((MUTE in user.mutations) || user.mind.miming) //Under vow of silence/mute?
- user.visible_message("[user] appears to whisper to themselves.","You begin to whisper to yourself.") //Make them do *something* abnormal.
+ user.visible_message("[user] appears to whisper to [user.p_them()]self.","You begin to whisper to yourself.") //Make them do *something* abnormal.
else
user.whisper("O bidai nabora se[pick("'","`")]sma!") // Otherwise book club sayings.
sleep(10)
@@ -35,8 +35,8 @@
if(!((MUTE in user.mutations) || user.mind.miming)) // If they aren't mute/miming, commence the whisperting
user.whisper(message)
var/my_message
- if(istype(user, /mob/living/simple_animal/slaughter/cult)) //Harbringers of the Slaughter
- my_message = " Harbringer of the Slaughter: [message]"
+ if(istype(user, /mob/living/simple_animal/slaughter/cult)) //Harbingers of the Slaughter
+ my_message = " Harbinger of the Slaughter: [message]"
else
my_message = " [(ishuman(user) ? "Acolyte" : "Construct")] [user.real_name]: [message]"
for(var/mob/M in GLOB.player_list)
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index c0daac2a01a..a68e4c1e9ca 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -105,7 +105,7 @@
/obj/item/clothing/suit/magusred
name = "magus robes"
- desc = "A set of armored robes worn by the followers of Nar-Sie"
+ desc = "A set of armored robes worn by the followers of Nar-Sie."
icon_state = "magusred"
item_state = "magusred"
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
@@ -132,7 +132,7 @@
/obj/item/clothing/suit/hooded/cultrobes/cult_shield
name = "empowered cultist robe"
- desc = "Empowered garb which creates a powerful shield around the user."
+ desc = "An empowered garb which creates a powerful shield around the user."
icon_state = "cult_armour"
item_state = "cult_armour"
w_class = WEIGHT_CLASS_BULKY
@@ -140,11 +140,12 @@
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
allowed = list(/obj/item/tome,/obj/item/melee/cultblade)
var/current_charges = 3
+ var/shield_state = "shield-cult"
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie
/obj/item/clothing/head/hooded/cult_hoodie
name = "empowered cultist robe"
- desc = "Empowered garb which creates a powerful shield around the user."
+ desc = "An empowered garb which creates a powerful shield around the user."
icon_state = "cult_hoodalt"
armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10)
body_parts_covered = HEAD
@@ -164,33 +165,49 @@
if(current_charges)
owner.visible_message(" \The [attack_text] is deflected in a burst of blood-red sparks!")
current_charges--
+ playsound(loc, "sparks", 100, 1)
new /obj/effect/temp_visual/cult/sparks(get_turf(owner))
if(!current_charges)
owner.visible_message(" The runed shield around [owner] suddenly disappears!")
+ shield_state = "broken"
owner.update_inv_wear_suit()
return 1
return 0
-/obj/item/clothing/suit/hooded/cultrobes/berserker
+/obj/item/clothing/suit/hooded/cultrobes/cult_shield/special_overlays()
+ return mutable_appearance('icons/effects/cult_effects.dmi', shield_state, MOB_LAYER + 0.01)
+
+/obj/item/clothing/suit/hooded/cultrobes/flagellant_robe
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
- icon_state = "hardsuit-berserker"
- item_state = "hardsuit-berserker"
+ icon_state = "flagellantrobe"
+ item_state = "flagellantrobe"
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/tome,/obj/item/melee/cultblade)
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0)
slowdown = -1
- hoodtype = /obj/item/clothing/head/hooded/berserkerhood
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/species/vox/suit.dmi',
+ "Drask" = 'icons/mob/species/drask/suit.dmi',
+ "Grey" = 'icons/mob/species/grey/suit.dmi'
+ )
+ hoodtype = /obj/item/clothing/head/hooded/flagellant_hood
-/obj/item/clothing/head/hooded/berserkerhood
+/obj/item/clothing/head/hooded/flagellant_hood
name = "flagellant's robes"
desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
- icon_state = "culthood"
+ icon_state = "flagellanthood"
+ item_state = "flagellanthood"
flags_inv = HIDEFACE
flags_cover = HEADCOVERSEYES
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/species/vox/head.dmi',
+ "Drask" = 'icons/mob/species/drask/head.dmi',
+ "Grey" = 'icons/mob/species/grey/head.dmi'
+ )
/obj/item/whetstone/cult
name = "eldritch whetstone"
@@ -213,8 +230,8 @@
list_reagents = list("unholywater" = 40)
/obj/item/clothing/glasses/night/cultblind
- desc = "May the master guide you through the darkness and shield you from the light."
name = "zealot's blindfold"
+ desc = "May the master guide you through the darkness and shield you from the light."
icon_state = "blindfold"
item_state = "blindfold"
see_in_dark = 8
@@ -247,7 +264,7 @@
to_chat(user, " We have exhausted our ability to curse the shuttle.")
return
if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list)
- to_chat(user, " Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.")
+ to_chat(user, " Nar-Sie or her avatars are already on this plane, there is no delaying the end of all things.")
return
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
@@ -273,7 +290,7 @@
/obj/item/cult_shift
name = "veil shifter"
- desc = "This relic teleports you forward a medium distance."
+ desc = "This relic teleports you forward by a medium distance."
icon = 'icons/obj/cult.dmi'
icon_state ="shifter"
var/uses = 4
@@ -353,7 +370,7 @@
/obj/item/clothing/suit/cultrobesghost
name = "ghostly cult robes"
- desc = "A set of ethreal armored robes worn by the undead followers of a cult."
+ desc = "A set of ethereal armored robes worn by the undead followers of a cult."
icon_state = "cultrobesalt"
item_state = "cultrobesalt"
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm
index f1a188022a0..38bd1919c62 100644
--- a/code/game/gamemodes/cult/cult_objectives.dm
+++ b/code/game/gamemodes/cult/cult_objectives.dm
@@ -27,8 +27,8 @@
explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for [sacrifice_target.p_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]")
- cult_mind.memory += " Objective #[current_objective]: [explanation] "
+ to_chat(cult_mind.current, " Objective #[current_objective]: [explanation]")
+ cult_mind.memory += " Objective #[current_objective]: [explanation] "
/datum/game_mode/cult/proc/bypass_phase()
@@ -89,8 +89,8 @@
for(var/datum/mind/cult_mind in cult)
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] "
+ 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]")
@@ -104,8 +104,8 @@
for(var/datum/mind/cult_mind in cult)
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] "
+ to_chat(cult_mind.current, " Objective Survive: [explanation]")
+ cult_mind.memory += " Objective Survive: [explanation] "
/datum/game_mode/cult/proc/second_phase()
@@ -117,13 +117,13 @@
explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin."
else
objectives += "slaughter"
- explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin."
+ explanation = "Bring the Slaughter via the rune 'Call Forth The Slaughter'. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin."
for(var/datum/mind/cult_mind in cult)
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] "
+ 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++
@@ -138,16 +138,16 @@
switch(last_objective)
if("harvest")
- explanation = "[SSticker.cultdat.entity_title1] hungers for their first meal of this never-ending day. Offer them [harvest_target] humans in sacrifice."
+ explanation = "[SSticker.cultdat.entity_title1] hungers for their first meal of this never-ending day. Offer them [harvest_target] humanoids in sacrifice."
if("hijack")
- explanation = "[SSticker.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."
+ explanation = "[SSticker.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 = "[SSticker.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)."
+ explanation = "[SSticker.cultdat.entity_name] wants to watch you as you massacre the remaining crew on the station (until less than [massacre_target] humans are left alive)."
for(var/datum/mind/cult_mind in cult)
if(cult_mind)
- to_chat(cult_mind.current, " Objective #[current_objective]: [explanation]")
- cult_mind.memory += " Objective #[current_objective]: [explanation] "
+ 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/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 618abec4470..c06913c1abe 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -120,7 +120,7 @@
selection_prompt = "You study the schematics etched on the forge..."
selection_title = "Forge"
creation_message = " You work the forge as dark knowledge guides your hands, creating %ITEM%!"
- choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/berserker, \
+ choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe, \
"Cultist Hardsuit" = /obj/item/storage/box/cult)
/obj/structure/cult/functional/forge/New()
@@ -134,7 +134,7 @@
to_chat(user, " You may only dunk carbon-based creatures!")
return 0
if(G.affecting == LAVA_PROOF)
- to_chat(user, " Is immune to the lava!")
+ to_chat(user, " [G.affecting] is immune to lava!")
return 0
if(G.affecting.stat == DEAD)
to_chat(user, " [G.affecting] is dead!")
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index 9422ffcc716..823a9a8980e 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -50,7 +50,7 @@
if(iscultist(user) || user.stat == DEAD)
. += " The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]."
. += " Striking another cultist with it will purge holy water from them."
- . += " Striking a noncultist, however, will sear their flesh."
+ . += " Striking a non-cultist, however, will sear their flesh."
/obj/item/tome/attack(mob/living/M, mob/living/user)
if(!istype(M))
@@ -67,8 +67,8 @@
return
M.take_organ_damage(0, 15) //Used to be a random between 5 and 20
playsound(M, 'sound/weapons/sear.ogg', 50, 1)
- M.visible_message(" [user] strikes [M] with the arcane tome!", \
- " [user] strikes you with the tome, searing your flesh!")
+ M.visible_message(" [user] strikes [M] with [src]!", \
+ " [user] strikes you with [src], searing your flesh!")
flick("tome_attack", src)
user.do_attack_animation(M)
add_attack_logs(user, M, "Hit with [src]")
@@ -133,7 +133,7 @@
text += " Blood BoilWhen invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \
It requires three invokers. "
- text += " LeechingWhen invoked, this rune will transfer lifeforce from the victim to the invoker. "
+ text += " LeechingWhen invoked, this rune will transfer life force from the victim to the invoker. "
text += " Rite of Spectral ManifestationThis rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \
However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \
@@ -175,7 +175,7 @@
text += " Cult RobesCult robes are heavily armored robes. These robes are produced by the Talisman of Arming. "
- text += " SoulstoneA soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
+ text += " SoulstoneA soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead humanoid, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
The soul within can also be released as a Shade by using it in-hand. "
text += " Construct ShellA construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \
@@ -206,7 +206,7 @@
if(GAMEMODE_IS_CULT)
if(!canbypass)//not an admin-tome, check things
if(!cult_mode.narsie_condition_cleared)
- to_chat(user, " There is still more to do before unleashing [SSticker.cultdat.entity_name] power!")
+ to_chat(user, " There is still more to do before unleashing [SSticker.cultdat.entity_name]'s' power!")
return 0
if(!cult_mode.eldergod)
to_chat(user, " \"I am already here. There is no need to try to summon me now.\"")
@@ -220,14 +220,14 @@
if(!(A in GLOB.summon_spots))
to_chat(user, " [SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!")
return 0
- var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
+ var/confirm_final = alert(user, "This is the FINAL step to summon your deity's power. It is a long, painful ritual and the crew will be alerted to your presence.", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
if(confirm_final == "No" || confirm_final == null)
to_chat(user, " You decide to prepare further before scribing the rune.")
return 0
else
return 1
else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS
- var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
+ var/confirm_final = alert(user, "This is the FINAL step to summon your deity's power. It is a long, painful ritual and the crew will be alerted to your presence.", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No")
if(confirm_final == "No" || confirm_final == null)
to_chat(user, " You decide to prepare further before scribing the rune.")
return 0
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 8c83c88f3d0..02cd7b6db65 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -332,11 +332,11 @@ GLOBAL_LIST_EMPTY(teleport_runes)
user.forceMove(get_turf(actual_selected_rune))
var/mob/living/carbon/human/H = user
if(user.z != T.z)
- if(istype(H))
+ if(istype(H))
H.bleed(5)
user.apply_damage(5, BRUTE)
else
- if(istype(H))
+ if(istype(H))
H.bleed(rand(5,10))
else
fail_invoke()
@@ -498,7 +498,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/tome) && iscultist(user)))
- user.visible_message(" [user] begins erasing the [src]...", " You begin erasing the [src]...")
+ user.visible_message(" [user] begins erasing [src]...", " You begin erasing [src]...")
if(do_after(user, 50, target = src)) //Prevents accidental erasures.
log_game("Summon Narsie rune erased by [key_name(user)] with a tome")
message_admins("[key_name_admin(user)] erased a Narsie rune with a tome")
@@ -534,7 +534,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
/obj/effect/rune/slaughter/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/tome) && iscultist(user)))
- user.visible_message(" [user.name] begins erasing the [src]...", " You begin erasing the [src]...")
+ user.visible_message(" [user.name] begins erasing [src]...", " You begin erasing [src]...")
if(do_after(user, 50, target = src)) //Prevents accidental erasures.
log_game("Summon demon rune erased by [key_name(user)] with a tome")
message_admins("[key_name_admin(user)] erased a demon rune with a tome")
@@ -649,7 +649,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",time=20)
sleep(20)
if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src))
- mob_to_sacrifice.visible_message(" [mob_to_sacrifice] disintegrates into a pile of bones")
+ mob_to_sacrifice.visible_message(" [mob_to_sacrifice] disintegrates into a pile of bones.")
return
mob_to_sacrifice.dust()
if(!mob_to_revive || mob_to_revive.stat != DEAD)
@@ -691,7 +691,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
if(3 to 6)
playsound(E, 'sound/effects/EMPulse.ogg', 50, 1)
for(var/M in invokers)
- to_chat(M, " Your hair stands on end as a shockwave eminates from the rune!")
+ to_chat(M, " Your hair stands on end as a shockwave emanates from the rune!")
if(7 to INFINITY)
playsound(E, 'sound/effects/EMPulse.ogg', 100, 1)
for(var/M in invokers)
@@ -808,6 +808,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
allow_excess_invokers = 1
icon_state = "5"
invoke_damage = 5
+ var/summoning = FALSE
var/summontime = 0
/obj/effect/rune/summon/invoke(var/list/invokers)
@@ -819,6 +820,11 @@ GLOBAL_LIST_EMPTY(teleport_runes)
var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of [SSticker.cultdat.entity_title3]") as null|anything in cultists
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
return
+ if(summoning)
+ to_chat(user, " You are already summoning a target!")
+ fail_invoke()
+ return
+
if(!cultist_to_summon)
to_chat(user, " You require a summoning target!")
fail_invoke()
@@ -836,23 +842,25 @@ GLOBAL_LIST_EMPTY(teleport_runes)
return
var/hard_summon = (cultist_to_summon.reagents && cultist_to_summon.reagents.has_reagent("holywater")) || cultist_to_summon.restrained()
if(hard_summon && invokers.len < 3)
- to_chat(user, " The summoning of [cultist_to_summon] is being blocked somehow! You need 3 chanters to counter it!")
+ to_chat(user, " The summoning of [cultist_to_summon] is being blocked somehow! You need 3 invokers 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
-
+ summoning = TRUE
..()
if(hard_summon)
summontime = 20
- if(do_after(user, summontime, target = loc))
+ if(do_after(user, summontime, target = src))
+ summoning = FALSE // Here incase the proc stops after the qdel
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)
+ summoning = FALSE
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
/obj/effect/rune/blood_boil
@@ -982,7 +990,7 @@ GLOBAL_LIST_EMPTY(teleport_runes)
..()
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.")
+ visible_message(" A cloud of red mist forms above [src], and from within steps... a humanoid shape.")
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))
N.name = "Invoker's Shield"
diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm
index e7fb06c1b94..af0dfb5e6e3 100644
--- a/code/game/gamemodes/cult/talisman.dm
+++ b/code/game/gamemodes/cult/talisman.dm
@@ -1,10 +1,10 @@
/obj/item/paper/talisman
- icon = 'icons/obj/paper.dmi'
+ icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paper_talisman"
var/cultist_name = "talisman"
var/cultist_desc = "A basic talisman. It serves no purpose."
var/invocation = "Naise meam!"
- info = "  "
+ info = " ​ "
var/uses = 1
var/health_cost = 0 //The amount of health taken from the user when invoking the talisman
@@ -61,17 +61,17 @@
/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
var/dat = list()
- dat += " There are [uses] bloody runes on the parchment."
- dat += "Please choose the chant to be imbued into the fabric of reality. "
+ dat += " There are [uses] bloody runes on the parchment."
+ dat += "Please choose the chant to be imbued into the fabric of reality. "
dat += "
"
- dat += " N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists. "
- dat += " Bar'tea eas! - Provides 5 runed metal. "
- dat += " Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune. "
- dat += " Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range. "
- dat += " Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman. "
- dat += " Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes. "
- dat += " Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans. "
- dat += " Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person. "
+ dat += " N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists. "
+ dat += " Bar'tea eas! - Provides 5 runed metal. "
+ dat += " Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune. "
+ dat += " Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range. "
+ dat += " Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman. "
+ dat += " Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes. "
+ dat += " Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans. "
+ dat += " Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person. "
var/datum/browser/popup = new(user, "talisman", "", 400, 400)
popup.set_content(jointext(dat, ""))
popup.open()
@@ -290,7 +290,7 @@
/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1)
. = ..()
var/mob/living/carbon/human/H = user
- user.visible_message(" Otherworldly armor suddenly appears on [user]!", \
+ user.visible_message(" Otherworldly equipment suddenly appears on [user]!", \
" You speak the words of the talisman, arming yourself!")
H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit)
@@ -334,14 +334,14 @@
/obj/item/paper/talisman/construction/attack_self(mob/living/user)
if(iscultist(user))
- to_chat(user, " To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.")
+ to_chat(user, " To use this talisman, place it upon a stack of metal sheets or plasteel sheets!")
else
to_chat(user, " You see strange symbols on the paper. Are they supposed to mean something?")
/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user)
if(iscultist(user))
- to_chat(user, " This talisman will only work on a stack of metal sheets or plasteel sheets!!")
+ to_chat(user, " This talisman will only work on a stack of metal sheets or plasteel sheets!")
log_game("Construct talisman failed - not a valid target")
/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/game/gamemodes/devil/contracts/friend.dm b/code/game/gamemodes/devil/contracts/friend.dm
index 7760ee10ee7..8838dc8a371 100644
--- a/code/game/gamemodes/devil/contracts/friend.dm
+++ b/code/game/gamemodes/devil/contracts/friend.dm
@@ -17,7 +17,8 @@
/obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell)
. = ..()
owner = owner_mind
- flavour_text = " You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, [owner.p_they()] can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
+ description = "Be someone's loyal friend/slave. If they die, you die as well." //best I could think of in the moment, not sure how this role plays in practise, have never seen it.
+ flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, [owner.p_they()] can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
var/area/A = get_area(src)
if(!mapload && A)
notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = TRUE)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 2b604839c62..1d50606c190 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -291,7 +291,8 @@
///////////////////////////////////
/datum/game_mode/proc/get_living_heads()
. = list()
- for(var/mob/living/carbon/human/player in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/player = thing
var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative"
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions))
. |= player.mind
@@ -312,7 +313,8 @@
//////////////////////////////////////////////
/datum/game_mode/proc/get_living_sec()
. = list()
- for(var/mob/living/carbon/human/player in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/player = thing
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions))
. |= player.mind
@@ -321,7 +323,8 @@
////////////////////////////////////////
/datum/game_mode/proc/get_all_sec()
. = list()
- for(var/mob/living/carbon/human/player in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/player = thing
if(player.mind && (player.mind.assigned_role in GLOB.security_positions))
. |= player.mind
@@ -415,15 +418,15 @@ proc/display_roundstart_logout_report()
/proc/get_nuke_code()
var/nukecode = "ERROR"
- for(var/obj/machinery/nuclearbomb/bomb in world)
+ for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines)
if(bomb && bomb.r_code && is_station_level(bomb.z))
nukecode = bomb.r_code
return nukecode
/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS)
var/mob/dead/observer/theghost = null
- if(candidates.len)
+ if(length(candidates))
theghost = pick(candidates)
to_chat(M, " Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.")
@@ -504,7 +507,7 @@ proc/display_roundstart_logout_report()
message_text += G.get_report()
message_text += "
"
- print_command_report(message_text, "[command_name()] Orders")
+ print_command_report(message_text, "[command_name()] Orders", FALSE)
/datum/game_mode/proc/declare_station_goal_completion()
for(var/V in station_goals)
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index c1a907f9191..9f5db8d5dae 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -83,6 +83,36 @@
else
add_ranged_ability(user, enable_text)
+/datum/action/innate/ai/choose_modules
+ name = "Choose Modules"
+ desc = "Spend your processing time to gain a variety of different abilities."
+ button_icon_state = "choose_module"
+ auto_use_uses = FALSE // This is an infinite ability.
+
+/datum/action/innate/ai/choose_modules/Grant(mob/living/L)
+ . = ..()
+ owner_AI.malf_picker = new /datum/module_picker
+
+/datum/action/innate/ai/choose_modules/Trigger()
+ . = ..()
+ owner_AI.malf_picker.use(owner_AI)
+
+/datum/action/innate/ai/return_to_core
+ name = "Return to Main Core"
+ desc = "Leave the APC you are shunted to, and return to your core."
+ icon_icon = 'icons/obj/power.dmi'
+ button_icon_state = "apcemag"
+ auto_use_uses = FALSE // Here just to prevent the "You have X uses remaining" from popping up.
+
+/datum/action/innate/ai/return_to_core/Trigger()
+ . = ..()
+ var/obj/machinery/power/apc/apc = owner_AI.loc
+ if(!istype(apc)) // This shouldn't happen but here for safety.
+ to_chat(src, " You are already in your Main Core.")
+ return
+ apc.malfvacate()
+ qdel(src)
+
//The datum and interface for the malf unlock menu, which lets them choose actions to unlock.
/datum/module_picker
var/temp
@@ -96,13 +126,7 @@
if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade)
possible_modules += AM
-/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI.
- for(var/datum/AI_Module/AM in possible_modules)
- for(var/datum/action/A in AI.actions)
- if(istype(A, initial(AM.power_type)))
- qdel(A)
-
-/datum/module_picker/proc/use(user as mob)
+/datum/module_picker/proc/use(mob/user)
var/dat
dat += {" Select use of processing time: (currently #[processing_time] left.)
@@ -592,7 +616,7 @@
active = FALSE
return
var/turf/T = get_turf(owner_AI.eyeobj)
- new /obj/machinery/transformer/conveyor(T)
+ new /obj/machinery/transformer(T, owner_AI)
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
owner_AI.can_shunt = FALSE
to_chat(owner, " You are no longer able to shunt your core to APCs.")
@@ -652,7 +676,8 @@
button.desc = desc
/datum/action/innate/ai/blackout/Activate()
- for(var/obj/machinery/power/apc/apc in GLOB.apcs)
+ for(var/thing in GLOB.apcs)
+ var/obj/machinery/power/apc/apc
if(prob(30 * apc.overload))
apc.overload_lighting()
else
@@ -756,3 +781,17 @@
if(AI.eyeobj)
AI.eyeobj.relay_speech = TRUE
+/datum/AI_Module/large/cameracrack
+ module_name = "Core Camera Cracker"
+ mod_pick_name = "cameracrack"
+ description = "By shortcirucuting the camera network chip, it overheats, preventing the camera console from using your internal camera."
+ cost = 10
+ one_purchase = TRUE
+ upgrade = TRUE
+ unlock_text = " Network chip short circuited. Internal camera disconected from network. Minimal damage to other internal components."
+ unlock_sound = 'sound/items/wirecutter.ogg'
+
+/datum/AI_Module/large/cameracrack/upgrade(mob/living/silicon/ai/AI)
+ if(AI.builtInCamera)
+ QDEL_NULL(AI.builtInCamera)
+
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index c27b230b3b2..a0519fcbe56 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -25,6 +25,11 @@
var/combat_armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 90, "acid" = 90)
sprite_sheets = null
+/obj/item/clothing/suit/armor/abductor/vest/Initialize(mapload)
+ . = ..()
+ stealth_armor = getArmor(arglist(stealth_armor))
+ combat_armor = getArmor(arglist(combat_armor))
+
/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop()
flags ^= NODROP
if(ismob(loc))
@@ -475,8 +480,8 @@ Congratulations! You are now trained for invasive xenobiology research!"}
H.update_inv_r_hand()
/obj/item/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user)
- user.lastattacked = L
- L.lastattacker = user
+ L.lastattacker = user.real_name
+ L.lastattackerckey = user.ckey
L.Stun(7)
L.Weaken(7)
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
index 6e1b4a39d3a..d7354b87c5c 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
@@ -166,7 +166,6 @@
eject_abductee()
SendBack(H)
return " Specimen braindead - disposed."
- return " ERROR"
/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H)
diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm
index f99a00567ae..750473b1dfa 100644
--- a/code/game/gamemodes/miniantags/borer/borer.dm
+++ b/code/game/gamemodes/miniantags/borer/borer.dm
@@ -394,7 +394,7 @@
to_chat(src, " You are feeling far too docile to do that.")
return
- var content = ""
+ var/content = ""
content += " "
diff --git a/code/game/gamemodes/miniantags/borer/borer_event.dm b/code/game/gamemodes/miniantags/borer/borer_event.dm
index 245b8fa7b41..f5a3b7aa4af 100644
--- a/code/game/gamemodes/miniantags/borer/borer_event.dm
+++ b/code/game/gamemodes/miniantags/borer/borer_event.dm
@@ -16,7 +16,7 @@
/datum/event/borer_infestation/start()
var/list/vents = list()
- for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in world)
+ for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in SSair.atmos_machinery)
if(is_station_level(temp_vent.loc.z) && !temp_vent.welded)
//Stops cortical borers getting stuck in small networks. See: Security, Virology
if(temp_vent.parent.other_atmosmch.len > 50)
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 059eed3541c..7fe146d3e47 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -19,15 +19,13 @@
mob_name = "a swarmer"
death = FALSE
roundstart = FALSE
- flavour_text = {"
- You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.
- Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.
- Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage.
- Objectives:
+ important_info = "Follow your objectives, do not make the station inhospitable or try and kill crew."
+ flavour_text = "You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate."
+ description = {" Your goal is to create more of yourself by consuming the station. Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful. Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage.
+ Objectives:
1. Consume resources and replicate until there are no more resources left.
2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable.
- 3. Biological resources will be harvested at a later date; do not harm them.
- "}
+ 3. Biological resources will be harvested at a later date; do not harm them."}
/obj/effect/mob_spawn/swarmer/Initialize(mapload)
. = ..()
@@ -61,6 +59,7 @@
icon = 'icons/mob/swarmer.dmi'
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
speak_emote = list("tones")
+ bubble_icon = "swarmer"
health = 40
maxHealth = 40
status_flags = CANPUSH
@@ -362,6 +361,10 @@
to_chat(S, "This cryopod control computer should be preserved, it contains useful items and information about the inhabitants. Aborting.")
return FALSE
+/obj/structure/spacepoddoor/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
+ to_chat(S, "Disrupting this energy field would overload us. Aborting.")
+ return FALSE
+
/turf/simulated/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
var/isonshuttle = istype(loc, /area/shuttle)
for(var/turf/T in range(1, src))
@@ -604,7 +607,7 @@
if(!istype(L, /mob/living/simple_animal/hostile/swarmer))
playsound(loc,'sound/effects/snap.ogg',50, 1, -1)
L.electrocute_act(0, src, 1, TRUE, TRUE)
- if(isrobot(L) || L.isSynthetic())
+ if(isrobot(L) || ismachineperson(L))
L.Weaken(5)
qdel(src)
..()
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm
index fbceda6bd94..5c4b83e965c 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm
@@ -7,7 +7,7 @@
var/swarmer_report = "[command_name()] High-Priority Update"
swarmer_report += "
Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come \
through."
- print_command_report(swarmer_report, "Classified [command_name()] Update")
+ print_command_report(swarmer_report, "Classified [command_name()] Update", FALSE)
GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
/datum/event/spawn_swarmer/start()
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index c0e22515553..d063b742047 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -3,6 +3,7 @@
real_name = "Guardian Spirit"
desc = "A mysterious being that stands by it's charge, ever vigilant."
speak_emote = list("intones")
+ bubble_icon = "guardian"
response_help = "passes through"
response_disarm = "flails at"
response_harm = "punches"
@@ -14,7 +15,7 @@
a_intent = INTENT_HARM
can_change_intents = 0
stop_automated_movement = 1
- floating = 1
+ flying = TRUE
attack_sound = 'sound/weapons/punch1.ogg'
minbodytemp = 0
maxbodytemp = INFINITY
@@ -180,15 +181,19 @@
input = stripped_input(src, "Please enter a message to tell your summoner.", "Guardian", "")
else
input = message
- if(!input) return
+ if(!input)
+ return
- for(var/mob/M in GLOB.mob_list)
- if(M == summoner)
- to_chat(M, "[src]: [input]")
- log_say("(GUARDIAN to [key_name(M)]) [input]", src)
- else if(M in GLOB.dead_mob_list && M.client && M.stat == DEAD && !isnewplayer(M))
- to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]")
+ // Show the message to the host and to the guardian.
+ to_chat(summoner, "[src]: [input]")
to_chat(src, "[src]: [input]")
+ log_say("(GUARDIAN to [key_name(summoner)]) [input]", src)
+ create_log(SAY_LOG, "GUARDIAN to HOST: [input]", summoner)
+
+ // Show the message to any ghosts/dead players.
+ for(var/mob/M in GLOB.dead_mob_list)
+ if(M && M.client && M.stat == DEAD && !isnewplayer(M))
+ to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]")
//override set to true if message should be passed through instead of going to host communication
/mob/living/simple_animal/hostile/guardian/say(message, override = FALSE)
@@ -206,18 +211,24 @@
set category = "Guardian"
set desc = "Communicate telepathically with your guardian."
var/input = stripped_input(src, "Please enter a message to tell your guardian.", "Message", "")
- if(!input) return
+ if(!input)
+ return
- for(var/mob/M in GLOB.mob_list)
- if(istype(M, /mob/living/simple_animal/hostile/guardian))
- var/mob/living/simple_animal/hostile/guardian/G = M
- if(G.summoner == src)
- to_chat(G, "[src]: [input]")
- log_say("(GUARDIAN to [key_name(G)]) [input]", src)
+ // Find the guardian in our host's contents.
+ var/mob/living/simple_animal/hostile/guardian/G = locate() in contents
+ if(!G)
+ return
- else if(M in GLOB.dead_mob_list && M.client && M.stat == DEAD && !isnewplayer(M))
- to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]")
+ // Show the message to our guardian and to host.
+ to_chat(G, "[src]: [input]")
to_chat(src, "[src]: [input]")
+ log_say("(GUARDIAN to [key_name(G)]) [input]", src)
+ create_log(SAY_LOG, "HOST to GUARDIAN: [input]", G)
+
+ // Show the message to any ghosts/dead players.
+ for(var/mob/M in GLOB.dead_mob_list)
+ if(M && M.client && M.stat == DEAD && !isnewplayer(M))
+ to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]")
/mob/living/proc/guardian_recall()
set name = "Recall Guardian"
@@ -235,7 +246,7 @@
src.verbs -= /mob/living/proc/guardian_reset
for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list)
if(G.summoner == src)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G)
var/mob/dead/observer/new_stand = null
if(candidates.len)
new_stand = pick(candidates)
@@ -301,7 +312,7 @@
used = FALSE
return
to_chat(user, "[use_message]")
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = src)
var/mob/dead/observer/theghost = null
if(candidates.len)
@@ -352,7 +363,7 @@
if("Protector")
pickedtype = /mob/living/simple_animal/hostile/guardian/protector
- var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user)
+ var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user, user)
G.summoner = user
G.summoned = TRUE
G.key = key
diff --git a/code/game/gamemodes/miniantags/guardian/types/fire.dm b/code/game/gamemodes/miniantags/guardian/types/fire.dm
index 09a202afb91..bf8a908e979 100644
--- a/code/game/gamemodes/miniantags/guardian/types/fire.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/fire.dm
@@ -35,7 +35,7 @@
new /obj/effect/hallucination/delusion(target.loc, target, force_kind = "custom", duration = 200, skip_nearby = 0, custom_icon = icon_state, custom_icon_file = icon)
else
if(prob(45))
- if(ismovableatom(target))
+ if(ismovable(target))
var/atom/movable/M = target
if(!M.anchored && M != summoner)
new /obj/effect/temp_visual/guardian/phase/out(get_turf(M))
diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm
index e3217e0249a..c3f8046f0a9 100644
--- a/code/game/gamemodes/miniantags/guardian/types/healer.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm
@@ -65,7 +65,7 @@
if(loc == summoner)
if(toggle)
a_intent = INTENT_HARM
- hud_used.action_intent.icon_state = a_intent;
+ hud_used.action_intent.icon_state = a_intent
speed = 0
damage_transfer = 0.7
if(adminseal)
@@ -76,7 +76,7 @@
toggle = FALSE
else
a_intent = INTENT_HELP
- hud_used.action_intent.icon_state = a_intent;
+ hud_used.action_intent.icon_state = a_intent
speed = 1
damage_transfer = 1
if(adminseal)
@@ -130,7 +130,7 @@
if(beacon) //Check that the beacon still exists and is in a safe place. No instant kills.
if(beacon.air)
var/datum/gas_mixture/Z = beacon.air
- if(Z.oxygen >= 16 && !Z.toxins && Z.carbon_dioxide < 10 && !Z.trace_gases.len)
+ if(Z.oxygen >= 16 && !Z.toxins && Z.carbon_dioxide < 10 && !Z.sleeping_agent)
if((Z.temperature > 270) && (Z.temperature < 360))
var/pressure = Z.return_pressure()
if((pressure > 20) && (pressure < 550))
diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm
index bf84e199bab..2c716d5d4a4 100644
--- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm
@@ -3,12 +3,12 @@
layer = LYING_MOB_LAYER
/mob/living/simple_animal/hostile/guardian/beam
- melee_damage_lower = 7
- melee_damage_upper = 7
+ melee_damage_lower = 12
+ melee_damage_upper = 12
attacktext = "shocks"
melee_damage_type = BURN
attack_sound = 'sound/machines/defib_zap.ogg'
- damage_transfer = 0.7
+ damage_transfer = 0.6
range = 7
playstyle_string = "As a Lightning type, you will apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them."
magic_fluff_string = "..And draw the Tesla, a shocking, lethal source of power."
@@ -18,6 +18,17 @@
var/list/enemychains = list()
var/successfulshocks = 0
+/mob/living/simple_animal/hostile/guardian/beam/New(loc, mob/living/user)
+ . = ..()
+ if(!user)
+ return
+ summoner = user
+ if(!(NO_SHOCK in summoner.mutations))
+ summoner.mutations.Add(NO_SHOCK)
+
+/mob/living/simple_animal/hostile/guardian/beam/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE)
+ return FALSE //You are lightning, you should not be hurt by such things.
+
/mob/living/simple_animal/hostile/guardian/beam/AttackingTarget()
. = ..()
if(. && isliving(target) && target != src && target != summoner)
@@ -106,3 +117,8 @@
)
L.adjustFireLoss(1.2) //adds up very rapidly
. = 1
+
+/mob/living/simple_animal/hostile/guardian/beam/death(gibbed)
+ if(summoner && (NO_SHOCK in summoner.mutations))
+ summoner.mutations.Remove(NO_SHOCK)
+ return ..()
diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
index 496e31d1bd3..dab7dfb1eca 100644
--- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm
@@ -1,7 +1,7 @@
/obj/item/projectile/guardian
name = "crystal spray"
icon_state = "guardian"
- damage = 5
+ damage = 25
damage_type = BRUTE
armour_penetration = 100
@@ -11,7 +11,7 @@
melee_damage_upper = 10
damage_transfer = 0.9
projectiletype = /obj/item/projectile/guardian
- ranged_cooldown_time = 1 //fast!
+ ranged_cooldown_time = 5 //fast!
projectilesound = 'sound/effects/hit_on_shattered_glass.ogg'
ranged = 1
range = 13
diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm
index e9465492a27..7fc9e134195 100644
--- a/code/game/gamemodes/miniantags/morph/morph_event.dm
+++ b/code/game/gamemodes/miniantags/morph/morph_event.dm
@@ -3,7 +3,7 @@
/datum/event/spawn_morph/proc/get_morph()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph)
if(!candidates.len)
key_of_morph = null
return kill()
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index f86f018757e..cba5ea62a2a 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -16,6 +16,7 @@
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
incorporeal_move = 3
+ see_invisible = INVISIBILITY_REVENANT
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
maxHealth = INFINITY
@@ -33,7 +34,7 @@
status_flags = 0
wander = 0
density = 0
- flying = 1
+ flying = TRUE
move_resist = INFINITY
mob_size = MOB_SIZE_TINY
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
@@ -140,7 +141,7 @@
giveObjectivesandGoals()
giveSpells()
else
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant)
var/mob/dead/observer/theghost = null
if(candidates.len)
theghost = pick(candidates)
@@ -396,7 +397,7 @@
spawn()
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
- var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
qdel(R)
message_admins("No candidates were found for the new revenant. Oh well!")
diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
index 34bf06f07f7..91ed583efc7 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
@@ -265,8 +265,8 @@
if(!istype(T, /turf/simulated/floor/plating) && !istype(T, /turf/simulated/floor/engine/cult) && istype(T, /turf/simulated/floor) && prob(15))
var/turf/simulated/floor/floor = T
- if(floor.intact)
- floor.builtin_tile.loc = floor
+ if(floor.intact && floor.floor_tile)
+ new floor.floor_tile(floor)
floor.broken = 0
floor.burnt = 0
floor.make_plating(1)
diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
index 1f902a51806..aa76f3620ac 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
@@ -13,7 +13,7 @@
return
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
key_of_revenant = null
return kill()
@@ -26,13 +26,15 @@
var/datum/mind/player_mind = new /datum/mind(key_of_revenant)
player_mind.active = 1
var/list/spawn_locs = list()
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(isturf(L.loc))
switch(L.name)
if("revenantspawn")
spawn_locs += L.loc
if(!spawn_locs) //If we can't find any revenant spawns, try the carp spawns
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(isturf(L.loc))
switch(L.name)
if("carpspawn")
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index e3ce78d582a..e58be201521 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -45,8 +45,8 @@
var/gorecooldown = 0
var/vialspawned = FALSE
loot = list(/obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic, /obj/item/organ/internal/heart/demon)
- var/playstyle_string = "You are the Slaughter Demon, a terrible creature from another existence. You have a single desire: To kill. \
- You may Ctrl+Click on blood pools to travel through them, appearing and dissaapearing from the station at will. \
+ var/playstyle_string = "You are the Slaughter Demon, a terrible creature from another existence. You have a single desire: to kill. \
+ You may Ctrl+Click on blood pools to travel through them, appearing and dissapearing from the station at will. \
Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to feast. \
You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. "
del_on_death = 1
@@ -111,8 +111,8 @@
// Cult slaughter demon
/mob/living/simple_animal/slaughter/cult //Summoned as part of the cult objective "Bring the Slaughter"
- name = "harbringer of the slaughter"
- real_name = "harbringer of the Slaughter"
+ name = "harbinger of the slaughter"
+ real_name = "harbinger of the Slaughter"
desc = "An awful creature from beyond the realms of madness."
maxHealth = 500
health = 500
@@ -120,14 +120,14 @@
melee_damage_lower = 60
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 \
+ playstyle_string = "You are a Harbinger 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 \
to emerge from it. You are fast, powerful, and almost invincible. By dragging a dead or unconscious body into a blood pool with you, you will consume it after a time and fully regain \
- your health. You may use the Sense Victims in your Cultist tab to locate a random, living heretic."
+ your health. You may use the ability 'Sense Victims' in your Cultist tab to locate a random, living heretic."
/obj/effect/proc_holder/spell/targeted/sense_victims
name = "Sense Victims"
- desc = "Sense the location of heratics"
+ desc = "Sense the location of heretics"
charge_max = 0
clothes_req = 0
range = 20
@@ -157,7 +157,7 @@
/mob/living/simple_animal/slaughter/cult/New()
..()
spawn(5)
- var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100)
+ var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult)
if(!demon_candidates.len)
visible_message("[src] disappears in a flash of red light!")
qdel(src)
@@ -171,8 +171,8 @@
var/client/C = M.client
S.key = C.key
- S.mind.assigned_role = "Harbringer of the Slaughter"
- S.mind.special_role = "Harbringer of the Slaughter"
+ S.mind.assigned_role = "Harbinger of the Slaughter"
+ S.mind.special_role = "Harbinger of the Slaughter"
to_chat(S, playstyle_string)
SSticker.mode.add_cultist(S.mind)
var/obj/effect/proc_holder/spell/targeted/sense_victims/SV = new
@@ -259,7 +259,7 @@
// Eating a 2nd heart. Gives the ability to drag people into blood and eat them.
if(HAS_TRAIT(user, TRAIT_BLOODCRAWL))
- to_chat(user, "You feel diffr- CONSUME THEM! ")
+ to_chat(user, "You feel differ- CONSUME THEM! ")
ADD_TRAIT(user, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat")
qdel(src) // Replacing their demon heart with another demon heart is pointless, just delete this one and return.
return TRUE
@@ -312,7 +312,7 @@
if(M.revive())
M.grab_ghost(force = TRUE)
playsound(get_turf(src), feast_sound, 50, 1, -1)
- to_chat(M, "You leave the [src]'s warm embrace, and feel ready to take on the world.")
+ to_chat(M, "You leave [src]'s warm embrace, and feel ready to take on the world.")
..(M)
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index ad1146c0bc8..b4e7dec611f 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -53,7 +53,6 @@ proc/issyndicate(mob/living/M as mob)
for(var/datum/mind/synd_mind in syndicates)
synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs.
synd_mind.special_role = SPECIAL_ROLE_NUKEOPS
- synd_mind.offstation_role = TRUE
return 1
@@ -95,7 +94,8 @@ proc/issyndicate(mob/living/M as mob)
var/list/turf/synd_spawn = list()
- for(var/obj/effect/landmark/A in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/A = thing
if(A.name == "Syndicate-Spawn")
synd_spawn += get_turf(A)
qdel(A)
@@ -103,7 +103,7 @@ proc/issyndicate(mob/living/M as mob)
var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb")
- var/nuke_code = "[rand(10000, 99999)]"
+ var/nuke_code = rand(10000, 99999)
var/leader_selected = 0
var/agent_number = 1
var/spawnpos = 1
@@ -112,7 +112,7 @@ proc/issyndicate(mob/living/M as mob)
if(spawnpos > synd_spawn.len)
spawnpos = 2
synd_mind.current.loc = synd_spawn[spawnpos]
-
+ synd_mind.offstation_role = TRUE
forge_syndicate_objectives(synd_mind)
create_syndicate(synd_mind)
greet_syndicate(synd_mind)
@@ -140,7 +140,7 @@ proc/issyndicate(mob/living/M as mob)
/datum/game_mode/nuclear/proc/scale_telecrystals()
var/danger
danger = GLOB.player_list.len
- while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten
+ while(!ISMULTIPLE(++danger, 10)) //Increments danger up to the nearest multiple of ten
total_tc += danger * NUKESCALINGMODIFIER
@@ -450,7 +450,7 @@ proc/issyndicate(mob/living/M as mob)
if(foecount == GLOB.score_arrested)
GLOB.score_allarrested = 1
- for(var/obj/machinery/nuclearbomb/nuke in world)
+ for(var/obj/machinery/nuclearbomb/nuke in GLOB.machines)
if(nuke.r_code == "Nope") continue
var/turf/T = get_turf(nuke)
var/area/A = T.loc
@@ -491,13 +491,16 @@ proc/issyndicate(mob/living/M as mob)
for(var/datum/mind/M in SSticker.mode.syndicates)
foecount++
- for(var/mob/living/C in world)
+ for(var/mob in GLOB.mob_living_list)
+ var/mob/living/C = mob
if(ishuman(C) || isAI(C) || isrobot(C))
- if(C.stat == 2) continue
- if(!C.client) continue
+ if(C.stat == DEAD)
+ continue
+ if(!C.client)
+ continue
crewcount++
- var/obj/item/disk/nuclear/N = locate() in world
+ var/obj/item/disk/nuclear/N = locate() in GLOB.poi_list
if(istype(N))
var/atom/disk_loc = N.loc
while(!isturf(disk_loc))
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 8e38f106cc7..209f9689de1 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -14,59 +14,59 @@ GLOBAL_VAR(bomb_set)
icon_state = "nuclearbomb0"
density = 1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- var/deployable = 0
- var/extended = 0
- var/lighthack = 0
+ var/extended = FALSE
+ var/lighthack = FALSE
var/timeleft = 120
- var/timing = 0
+ var/timing = FALSE
+ var/exploded = FALSE
var/r_code = "ADMIN"
- var/code = ""
- var/yes_code = 0
- var/safety = 1
+ var/code
+ var/yes_code = FALSE
+ var/safety = TRUE
var/obj/item/disk/nuclear/auth = null
var/removal_stage = NUKE_INTACT
var/lastentered
- var/is_syndicate = 0
+ var/is_syndicate = FALSE
use_power = NO_POWER_USE
var/previous_level = ""
var/datum/wires/nuclearbomb/wires = null
/obj/machinery/nuclearbomb/syndicate
- is_syndicate = 1
+ is_syndicate = TRUE
/obj/machinery/nuclearbomb/New()
..()
- r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn.
+ r_code = rand(10000, 99999.0) // Creates a random code upon object spawn.
wires = new/datum/wires/nuclearbomb(src)
previous_level = get_security_level()
GLOB.poi_list |= src
/obj/machinery/nuclearbomb/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
GLOB.poi_list.Remove(src)
return ..()
/obj/machinery/nuclearbomb/process()
if(timing)
- GLOB.bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed.
+ GLOB.bomb_set = TRUE // So long as there is one nuke timing, it means one nuke is armed.
timeleft = max(timeleft - 2, 0) // 2 seconds per process()
if(timeleft <= 0)
INVOKE_ASYNC(src, .proc/explode)
- SSnanoui.update_uis(src)
return
/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params)
if(istype(O, /obj/item/disk/nuclear))
if(extended)
if(!user.drop_item())
- to_chat(user, "\The [O] is stuck to your hand!")
+ to_chat(user, "[O] is stuck to your hand!")
return
O.forceMove(src)
auth = O
add_fingerprint(user)
return attack_hand(user)
else
- to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.")
+ to_chat(user, "You need to deploy [src] first.")
return
return ..()
@@ -170,181 +170,157 @@ GLOBAL_VAR(bomb_set)
removal_stage = NUKE_SEALANT_OPEN
/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob)
- if(extended)
- attack_hand(user)
+ attack_hand(user)
/obj/machinery/nuclearbomb/attack_hand(mob/user as mob)
- if(extended)
- if(panel_open)
- wires.Interact(user)
- else
- ui_interact(user)
- else if(deployable)
- if(removal_stage != NUKE_MOBILE)
- anchored = 1
- visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!")
- else
- visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")
- if(!lighthack)
- flick("nuclearbombc", src)
- icon_state = "nuclearbomb1"
- extended = 1
- return
-
-/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- 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 = GLOB.physical_state)
- ui.open()
- ui.set_auto_update(1)
-
-/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state)
- var/data[0]
- data["is_syndicate"] = is_syndicate
- data["hacking"] = 0
- data["auth"] = is_auth(user)
- if(is_auth(user))
- if(yes_code)
- data["authstatus"] = timing ? "Functional/Set" : "Functional"
- else
- data["authstatus"] = "Auth. S2"
+ if(panel_open)
+ wires.Interact(user)
else
- if(timing)
- data["authstatus"] = "Set"
- else
- data["authstatus"] = "Auth. S1"
+ tgui_interact(user)
+
+/obj/machinery/nuclearbomb/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "NuclearBomb", name, 450, 300, master_ui, state)
+ ui.open()
+
+/obj/machinery/nuclearbomb/tgui_data(mob/user)
+ var/list/data = list()
+ data["extended"] = extended
+ data["authdisk"] = is_auth(user)
+ data["diskname"] = auth ? auth.name : FALSE
+ data["authcode"] = yes_code
+ data["authfull"] = data["authdisk"] && data["authcode"]
data["safe"] = safety ? "Safe" : "Engaged"
data["time"] = timeleft
data["timer"] = timing
data["safety"] = safety
data["anchored"] = anchored
- data["yescode"] = yes_code
- data["message"] = "AUTH"
if(is_auth(user))
- data["message"] = code
if(yes_code)
- data["message"] = "*****"
-
- return data
-
-/obj/machinery/nuclearbomb/verb/make_deployable()
- set category = "Object"
- set name = "Make Deployable"
- set src in oview(1)
-
- if(usr.stat || !usr.canmove || usr.restrained())
- return
-
- if(deployable)
- to_chat(usr, "You close several panels to make [src] undeployable.")
- deployable = 0
+ data["codemsg"] = "CLEAR CODE"
+ else if(code)
+ data["codemsg"] = "RE-ENTER CODE"
+ else
+ data["codemsg"] = "ENTER CODE"
else
- to_chat(usr, "You adjust some panels to make [src] deployable.")
- deployable = 1
- return
+ data["codemsg"] = "-----"
+ return data
/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user)
if(auth)
- return 1
+ return TRUE
else if(user.can_admin_interact())
- return 1
+ return TRUE
else
- return 0
+ return FALSE
-/obj/machinery/nuclearbomb/Topic(href, href_list)
+/obj/machinery/nuclearbomb/tgui_act(action, params)
if(..())
- return 1
-
- if(href_list["auth"])
- if(auth)
- auth.loc = loc
- yes_code = 0
- auth = null
- else
- var/obj/item/I = usr.get_active_hand()
- if(istype(I, /obj/item/disk/nuclear))
- usr.drop_item()
- I.loc = src
- auth = I
- if(is_auth(usr))
- if(href_list["type"])
- if(href_list["type"] == "E")
+ return
+ . = TRUE
+ if(exploded)
+ return
+ switch(action)
+ if("deploy")
+ if(removal_stage != NUKE_MOBILE)
+ anchored = TRUE
+ visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!")
+ else
+ visible_message("[src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")
+ if(!lighthack)
+ flick("nuclearbombc", src)
+ icon_state = "nuclearbomb1"
+ extended = TRUE
+ return
+ if("auth")
+ if(auth)
+ if(!usr.get_active_hand() && Adjacent(usr))
+ usr.put_in_hands(auth)
+ else
+ auth.forceMove(get_turf(src))
+ yes_code = FALSE
+ auth = null
+ else
+ var/obj/item/I = usr.get_active_hand()
+ if(istype(I, /obj/item/disk/nuclear))
+ usr.drop_item()
+ I.forceMove(src)
+ auth = I
+ return
+ if(!is_auth(usr)) // All requests below here require NAD inserted.
+ return FALSE
+ switch(action)
+ if("code")
+ if(yes_code) // Clear code
+ code = null
+ yes_code = FALSE
+ return
+ // If no code set, enter new one
+ var/tempcode = input(usr, "Code", "Input Code", null) as num|null
+ if(tempcode)
+ code = min(max(round(tempcode), 0), 999999)
if(code == r_code)
- yes_code = 1
+ yes_code = TRUE
code = null
else
code = "ERROR"
+ return
+
+ if(!yes_code) // All requests below here require both NAD inserted AND code correct
+ return
+
+ switch(action)
+ if("toggle_anchor")
+ if(removal_stage == NUKE_MOBILE)
+ anchored = FALSE
+ visible_message("[src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")
+ else if(isinspace())
+ to_chat(usr, "There is nothing to anchor to!")
+ return FALSE
else
- if(href_list["type"] == "R")
- yes_code = 0
- code = null
- else
- lastentered = text("[]", href_list["type"])
- if(text2num(lastentered) == null)
- var/turf/LOC = get_turf(usr)
- message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0)
- log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!")
- else
- code += lastentered
- if(length(code) > 5)
- code = "ERROR"
- if(yes_code)
- if(href_list["time"])
- var/time = text2num(href_list["time"])
- timeleft += time
- timeleft = min(max(round(src.timeleft), 120), 600)
- if(href_list["timer"])
- if(timing == -1.0)
- SSnanoui.update_uis(src)
- return
- if(safety)
- to_chat(usr, "The safety is still on.")
- SSnanoui.update_uis(src)
- return
- timing = !(timing)
- if(timing)
- if(!lighthack)
- icon_state = "nuclearbomb2"
- if(!safety)
- message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)")
- if(!is_syndicate)
- set_security_level("delta")
- GLOB.bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N
- else
- GLOB.bomb_set = 0
+ anchored = !(anchored)
+ if(anchored)
+ visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.")
else
+ visible_message("The anchoring bolts slide back into the depths of [src].")
+ return
+ if("set_time")
+ var/time = input(usr, "Detonation time (seconds, min 120, max 600)", "Input Time", 120) as num|null
+ if(time)
+ timeleft = min(max(round(time), 120), 600)
+ if("toggle_safety")
+ safety = !(safety)
+ if(safety)
+ if(!is_syndicate)
+ set_security_level(previous_level)
+ timing = FALSE
+ GLOB.bomb_set = FALSE
+ if("toggle_armed")
+ if(safety)
+ to_chat(usr, "The safety is still on.")
+ return
+ timing = !(timing)
+ if(timing)
+ if(!lighthack)
+ icon_state = "nuclearbomb2"
+ if(!safety)
+ message_admins("[key_name_admin(usr)] engaged a nuclear bomb [ADMIN_JMP(src)]")
if(!is_syndicate)
- set_security_level(previous_level)
- GLOB.bomb_set = 0
- if(!lighthack)
- icon_state = "nuclearbomb1"
- if(href_list["safety"])
- safety = !(safety)
- if(safety)
- if(!is_syndicate)
- set_security_level(previous_level)
- timing = 0
- GLOB.bomb_set = 0
- if(href_list["anchor"])
- if(removal_stage == NUKE_MOBILE)
- anchored = 0
- visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.")
- SSnanoui.update_uis(src)
- return
-
- if(!isinspace())
- anchored = !(anchored)
- if(anchored)
- visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.")
- else
- visible_message("The anchoring bolts slide back into the depths of [src].")
+ set_security_level("delta")
+ GLOB.bomb_set = TRUE // There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke
else
- to_chat(usr, "There is nothing to anchor to!")
+ GLOB.bomb_set = TRUE
+ else
+ if(!is_syndicate)
+ set_security_level(previous_level)
+ GLOB.bomb_set = FALSE
+ if(!lighthack)
+ icon_state = "nuclearbomb1"
- SSnanoui.update_uis(src)
/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B)
- if(timing == -1.0)
+ if(exploded)
return
if(timing) //boom
INVOKE_ASYNC(src, .proc/explode)
@@ -359,11 +335,11 @@ GLOBAL_VAR(bomb_set)
#define NUKERANGE 80
/obj/machinery/nuclearbomb/proc/explode()
if(safety)
- timing = 0
+ timing = FALSE
return
- timing = -1.0
- yes_code = 0
- safety = 1
+ exploded = TRUE
+ yes_code = FALSE
+ safety = TRUE
if(!lighthack)
icon_state = "nuclearbomb3"
playsound(src,'sound/machines/alarm.ogg',100,0,5)
@@ -407,6 +383,20 @@ GLOBAL_VAR(bomb_set)
return
return
+/obj/machinery/nuclearbomb/proc/reset_lighthack_callback()
+ lighthack = !lighthack
+
+/obj/machinery/nuclearbomb/proc/reset_safety_callback()
+ safety = !safety
+ if(safety == 1)
+ if(!is_syndicate)
+ set_security_level(previous_level)
+ visible_message("The [src] quiets down.")
+ if(!lighthack)
+ if(icon_state == "nuclearbomb2")
+ icon_state = "nuclearbomb1"
+ else
+ visible_message("The [src] emits a quiet whirling noise!")
//==========DAT FUKKEN DISK===============
/obj/item/disk/nuclear
diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm
index e8f4c22e377..f6e454bd737 100644
--- a/code/game/gamemodes/nuclear/pinpointer.dm
+++ b/code/game/gamemodes/nuclear/pinpointer.dm
@@ -357,7 +357,8 @@
var/list/name_counts = list()
var/list/names = list()
- for(var/mob/living/carbon/human/H in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
if(!trackable(H))
continue
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index bedc3c4809b..0b46264cb33 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/assassinate/check_completion()
if(target && target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 1
if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
return 1
@@ -111,7 +111,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/maroon/check_completion()
if(target && target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 1
if(!target.current.ckey)
return 1
@@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(!target) //If it's a free objective.
return 1
if(target.current)
- if(target.current.stat == DEAD || iszombie(target))
+ if(target.current.stat == DEAD)
return 0
if(issilicon(target.current))
return 0
@@ -262,7 +262,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
return 0
if(isbrain(owner.current))
return 0
- if(!owner.current || owner.current.stat == DEAD || iszombie(owner))
+ if(!owner.current || owner.current.stat == DEAD)
return 0
if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith
return 1
@@ -317,7 +317,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
explanation_text = "Die a glorious death."
/datum/objective/die/check_completion()
- if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner))
+ if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current))
return 1
if(issilicon(owner.current) && owner.current != owner.original)
return 1
@@ -399,6 +399,9 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(!steal_target)
return 1 // Free Objective
+ if(!owner.current)
+ return FALSE
+
var/list/all_items = owner.current.GetAllContents()
for(var/obj/I in all_items)
@@ -570,9 +573,10 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
// /vg/; Vox Inviolate for humans :V
/datum/objective/minimize_casualties
explanation_text = "Minimise casualties."
+
/datum/objective/minimize_casualties/check_completion()
- if(owner.kills.len>5) return 0
- return 1
+ return TRUE
+
//Vox heist objectives.
@@ -778,16 +782,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
explanation_text = "Follow the Inviolate. Minimise death and loss of resources."
/datum/objective/heist/inviolate_death/check_completion()
- var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail.
- var/vox_total_kills = 0
-
- var/datum/game_mode/heist/H = SSticker.mode
- for(var/datum/mind/raider in H.raiders)
- vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills
-
- if(vox_total_kills > vox_allowed_kills) return 0
- return 1
-
+ return TRUE
// Traders
// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for.
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index a38982905bc..213814d52e8 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -385,7 +385,8 @@
if(foecount == GLOB.score_arrested)
GLOB.score_allarrested = 1
- for(var/mob/living/carbon/human/player in world)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/player = thing
if(player.mind)
var/role = player.mind.assigned_role
if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director"))
@@ -415,7 +416,8 @@
for(var/datum/mind/M in SSticker.mode:revolutionaries)
if(M.current && M.current.stat != DEAD)
revcount++
- for(var/mob/living/carbon/human/player in world)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/player = thing
if(player.mind)
var/role = player.mind.assigned_role
if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director"))
@@ -425,7 +427,8 @@
if(player.mind in SSticker.mode.revolutionaries) continue
loycount++
- for(var/mob/living/silicon/X in world)
+ for(var/beepboop in GLOB.silicon_mob_list)
+ var/mob/living/silicon/X = beepboop
if(X.stat != DEAD)
loycount++
diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm
index 069b0915c00..2e370db5f7a 100644
--- a/code/game/gamemodes/scoreboard.dm
+++ b/code/game/gamemodes/scoreboard.dm
@@ -1,5 +1,21 @@
/datum/controller/subsystem/ticker/proc/scoreboard()
+ //Thresholds for Score Ratings
+ #define SINGULARITY_DESERVES_BETTER -3500
+ #define SINGULARITY_FODDER -3000
+ #define ALL_FIRED -2500
+ #define WASTE_OF_OXYGEN -2000
+ #define HEAP_OF_SCUM -1500
+ #define LAB_MONKEYS -1000
+ #define UNDESIREABLES -500
+ #define SERVANTS_OF_SCIENCE 500
+ #define GOOD_BUNCH 1000
+ #define MACHINE_THIRTEEN 1500
+ #define PROMOTIONS_FOR_EVERYONE 2000
+ #define AMBASSADORS_OF_DISCOVERY 3000
+ #define PRIDE_OF_SCIENCE 4000
+ #define NANOTRANSEN_FINEST 5000
+
//Print a list of antagonists to the server log
var/list/total_antagonists = list()
//Look into all mobs in world, dead or alive
@@ -25,7 +41,8 @@
GLOB.score_deadaipenalty++
GLOB.score_deadcrew++
- for(var/mob/living/carbon/human/I in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/I = thing
if(I.stat == DEAD && is_station_level(I.z))
GLOB.score_deadcrew++
@@ -44,7 +61,8 @@
var/dmg_score = 0
if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME)
- for(var/mob/living/carbon/human/E in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/E = thing
cash_score = 0
dmg_score = 0
var/turf/location = get_turf(E.loc)
@@ -71,7 +89,8 @@
// Check station's power levels
- for(var/obj/machinery/power/apc/A in GLOB.apcs)
+ for(var/thing in GLOB.apcs)
+ var/obj/machinery/power/apc/A = thing
if(!is_station_level(A.z)) continue
for(var/obj/item/stock_parts/cell/C in A.contents)
if(C.charge < 2300)
@@ -92,15 +111,14 @@
// Bonus Modifiers
- //var/traitorwins = score_traitorswon
var/deathpoints = GLOB.score_deadcrew * 25 //done
var/researchpoints = GLOB.score_researchdone * 30
var/eventpoints = GLOB.score_eventsendured * 50
var/escapoints = GLOB.score_escapees * 25 //done
- var/harvests = GLOB.score_stuffharvested * 5 //done
+ var/harvests = GLOB.score_stuffharvested * 5
var/shipping = GLOB.score_stuffshipped * 5
- var/mining = GLOB.score_oremined * 2 //done
- var/meals = GLOB.score_meals * 5 //done, but this only counts cooked meals, not drinks served
+ var/mining = GLOB.score_oremined * 2 //done, might want polishing
+ var/meals = GLOB.score_meals * 5
var/power = GLOB.score_powerloss * 20
var/messpoints
if(GLOB.score_mess != 0)
@@ -120,13 +138,9 @@
GLOB.score_crewscore += 2500
GLOB.score_powerbonus = 1
- if(GLOB.score_mess == 0)
- GLOB.score_crewscore += 3000
- GLOB.score_messbonus = 1
-
GLOB.score_crewscore += meals
- if(GLOB.score_allarrested)
+ if(GLOB.score_allarrested) // This only seems to be implemented for Rev and Nukies. -DaveKorhal
GLOB.score_crewscore *= 3 // This needs to be here for the bonus to be applied properly
@@ -176,26 +190,19 @@
dat += {"
General Statistics
- The Good:
-
- Useful Items Shipped: [GLOB.score_stuffshipped] ([GLOB.score_stuffshipped * 5] Points)
- Hydroponics Harvests: [GLOB.score_stuffharvested] ([GLOB.score_stuffharvested * 5] Points)
- Ore Mined: [GLOB.score_oremined] ([GLOB.score_oremined * 2] Points)
- Refreshments Prepared: [GLOB.score_meals] ([GLOB.score_meals * 5] Points)
- Research Completed: [GLOB.score_researchdone] ([GLOB.score_researchdone * 30] Points) "}
+ The Good
+ Ore Mined: [GLOB.score_oremined] ([GLOB.score_oremined * 2] Points) "}
if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) dat += "Shuttle Escapees: [GLOB.score_escapees] ([GLOB.score_escapees * 25] Points) "
- dat += {"Random Events Endured: [GLOB.score_eventsendured] ([GLOB.score_eventsendured * 50] Points)
- Whole Station Powered: [GLOB.score_powerbonus ? "Yes" : "No"] ([GLOB.score_powerbonus * 2500] Points)
- Ultra-Clean Station: [GLOB.score_mess ? "No" : "Yes"] ([GLOB.score_messbonus * 3000] Points)
- The bad:
+ dat += {"
+ Whole Station Powered: [GLOB.score_powerbonus ? "Yes" : "No"] ([GLOB.score_powerbonus * 2500] Points)
+ The Bad
Dead bodies on Station: [GLOB.score_deadcrew] (-[GLOB.score_deadcrew * 25] Points)
Uncleaned Messes: [GLOB.score_mess] (-[GLOB.score_mess] Points)
Station Power Issues: [GLOB.score_powerloss] (-[GLOB.score_powerloss * 20] Points)
- Rampant Diseases: [GLOB.score_disease] (-[GLOB.score_disease * 30] Points)
AI Destroyed: [GLOB.score_deadaipenalty ? "Yes" : "No"] (-[GLOB.score_deadaipenalty * 250] Points)
- The Weird
+ The Weird
Food Eaten: [GLOB.score_foodeaten] bites/sips
Times a Clown was Abused: [GLOB.score_clownabuse]
"}
@@ -217,22 +224,36 @@
var/score_rating = "The Aristocrats!"
switch(GLOB.score_crewscore)
- if(-99999 to -50000) score_rating = "Even the Singularity Deserves Better"
- if(-49999 to -5000) score_rating = "Singularity Fodder"
- if(-4999 to -1000) score_rating = "You're All Fired"
- if(-999 to -500) score_rating = "A Waste of Perfectly Good Oxygen"
- if(-499 to -250) score_rating = "A Wretched Heap of Scum and Incompetence"
- if(-249 to -100) score_rating = "Outclassed by Lab Monkeys"
- if(-99 to -21) score_rating = "The Undesirables"
- if(-20 to 20) score_rating = "Ambivalently Average"
- if(21 to 99) score_rating = "Not Bad, but Not Good"
- if(100 to 249) score_rating = "Skillful Servants of Science"
- if(250 to 499) score_rating = "Best of a Good Bunch"
- if(500 to 999) score_rating = "Lean Mean Machine Thirteen"
- if(1000 to 4999) score_rating = "Promotions for Everyone"
- if(5000 to 9999) score_rating = "Ambassadors of Discovery"
- if(10000 to 49999) score_rating = "The Pride of Science Itself"
- if(50000 to INFINITY) score_rating = "Nanotrasen's Finest"
+ if(-99999 to SINGULARITY_DESERVES_BETTER) score_rating = "Even the Singularity Deserves Better"
+ if(SINGULARITY_DESERVES_BETTER+1 to SINGULARITY_FODDER) score_rating = "Singularity Fodder"
+ if(SINGULARITY_FODDER+1 to ALL_FIRED) score_rating = "You're All Fired"
+ if(ALL_FIRED+1 to WASTE_OF_OXYGEN) score_rating = "A Waste of Perfectly Good Oxygen"
+ if(WASTE_OF_OXYGEN+1 to HEAP_OF_SCUM) score_rating = "A Wretched Heap of Scum and Incompetence"
+ if(HEAP_OF_SCUM+1 to LAB_MONKEYS) score_rating = "Outclassed by Lab Monkeys"
+ if(LAB_MONKEYS+1 to UNDESIREABLES) score_rating = "The Undesirables"
+ if(UNDESIREABLES+1 to SERVANTS_OF_SCIENCE-1) score_rating = "Ambivalently Average"
+ if(SERVANTS_OF_SCIENCE to GOOD_BUNCH-1) score_rating = "Skillful Servants of Science"
+ if(GOOD_BUNCH to MACHINE_THIRTEEN-1) score_rating = "Best of a Good Bunch"
+ if(MACHINE_THIRTEEN to PROMOTIONS_FOR_EVERYONE-1) score_rating = "Lean Mean Machine Thirteen"
+ if(PROMOTIONS_FOR_EVERYONE to AMBASSADORS_OF_DISCOVERY-1) score_rating = "Promotions for Everyone"
+ if(AMBASSADORS_OF_DISCOVERY to PRIDE_OF_SCIENCE-1) score_rating = "Ambassadors of Discovery"
+ if(PRIDE_OF_SCIENCE to NANOTRANSEN_FINEST-1) score_rating = "The Pride of Science Itself"
+ if(NANOTRANSEN_FINEST to INFINITY) score_rating = "Nanotrasen's Finest"
dat += "RATING: [score_rating]"
src << browse(dat, "window=roundstats;size=500x600")
+
+ #undef SINGULARITY_DESERVES_BETTER
+ #undef SINGULARITY_FODDER
+ #undef ALL_FIRED
+ #undef WASTE_OF_OXYGEN
+ #undef HEAP_OF_SCUM
+ #undef LAB_MONKEYS
+ #undef UNDESIREABLES
+ #undef SERVANTS_OF_SCIENCE
+ #undef GOOD_BUNCH
+ #undef MACHINE_THIRTEEN
+ #undef PROMOTIONS_FOR_EVERYONE
+ #undef AMBASSADORS_OF_DISCOVERY
+ #undef PRIDE_OF_SCIENCE
+ #undef NANOTRANSEN_FINEST
diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm
index 9cd91817af4..b51810e4967 100644
--- a/code/game/gamemodes/shadowling/shadowling.dm
+++ b/code/game/gamemodes/shadowling/shadowling.dm
@@ -74,7 +74,7 @@ Made by Xhuis
required_enemies = 2
recommended_enemies = 2
restricted_jobs = list("AI", "Cyborg")
- protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
+ protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
/datum/game_mode/shadowling/announce()
to_chat(world, "The current game mode is - Shadowling!")
@@ -101,7 +101,7 @@ Made by Xhuis
shadowlings--
var/thrall_scaling = round(num_players() / 3)
- required_thralls = Clamp(thrall_scaling, 15, 25)
+ required_thralls = clamp(thrall_scaling, 15, 25)
thrall_ratio = required_thralls / 15
warning_threshold = round(0.66 * required_thralls)
@@ -194,8 +194,8 @@ Made by Xhuis
return 1
var/mob/living/M = thrall_mind.current
if(issilicon(M))
- M.audible_message("[M] lets out a short blip.", \
- "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...")
+ M.audible_message("[M] lets out a short blip.")
+ to_chat(M, "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...")
else
M.visible_message("[M] looks like [M.p_their()] mind is [M.p_their()] own again!", \
"A piercing white light floods your eyes. Your mind is your own again! Though you try, you cannot remember anything about the shadowlings or your time \
@@ -244,8 +244,8 @@ Made by Xhuis
ling_mind.RemoveSpell(S)
var/mob/living/M = ling_mind.current
if(issilicon(M))
- M.audible_message("[M] lets out a short blip.", \
- "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...")
+ M.audible_message("[M] lets out a short blip.")
+ to_chat(M, "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...")
else
M.visible_message("[M] screams and contorts!", \
"THE LIGHT-- YOUR MIND-- BURNS--")
diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm
index 2cafb1bc219..27267d2ec02 100644
--- a/code/game/gamemodes/shadowling/shadowling_abilities.dm
+++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm
@@ -175,7 +175,6 @@
range = -1
include_user = 1
clothes_req = 0
- var/datum/vision_override/vision_path = /datum/vision_override/nightvision
action_icon_state = "darksight"
/obj/effect/proc_holder/spell/targeted/shadow_vision/cast(list/targets, mob/user = usr)
@@ -185,10 +184,10 @@
var/mob/living/carbon/human/H = target
if(!H.vision_type)
to_chat(H, "You shift the nerves in your eyes, allowing you to see in the dark.")
- H.vision_type = new vision_path
+ H.set_sight(/datum/vision_override/nightvision)
else
to_chat(H, "You return your vision to normal.")
- H.vision_type = null
+ H.set_sight(null)
/obj/effect/proc_holder/spell/targeted/shadow_vision/thrall
desc = "Thrall Darksight"
diff --git a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm
index 7896a4aab61..5fc5e1e78cc 100644
--- a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm
+++ b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm
@@ -161,7 +161,8 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u
for(var/mob/living/M in orange(7, H))
M.Weaken(10)
to_chat(M, "An immense pressure slams you onto the ground!")
- for(var/obj/machinery/power/apc/A in GLOB.apcs)
+ for(var/thing in GLOB.apcs)
+ var/obj/machinery/power/apc/A = thing
A.overload_lighting()
var/mob/living/simple_animal/ascendant_shadowling/A = new /mob/living/simple_animal/ascendant_shadowling(H.loc)
A.announce("VYSHA NERADA YEKHEZET U'RUU!!", 5, 'sound/hallucinations/veryfar_noise.ogg')
diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm
index 6c22313ef6e..75748448527 100644
--- a/code/game/gamemodes/vampire/vampire.dm
+++ b/code/game/gamemodes/vampire/vampire.dm
@@ -227,6 +227,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
if(istype(spell, /obj/effect/proc_holder/spell))
owner.mind.AddSpell(spell)
powers += spell
+ owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/vampire/proc/get_ability(path)
for(var/P in powers)
@@ -244,6 +245,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
powers -= ability
owner.mind.spell_list.Remove(ability)
qdel(ability)
+ owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp loses his vision based powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/vampire/proc/update_owner(var/mob/living/carbon/human/current) //Called when a vampire gets cloned. This updates vampire.owner to the new body.
if(current.mind && current.mind.vampire && current.mind.vampire.owner && (current.mind.vampire.owner != current))
@@ -277,6 +279,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
var/blood = 0
var/old_bloodtotal = 0 //used to see if we increased our blood total
var/old_bloodusable = 0 //used to see if we increased our blood usable
+ var/blood_volume_warning = 9999 //Blood volume threshold for warnings
if(owner.is_muzzled())
to_chat(owner, "[owner.wear_mask] prevents you from biting [H]!")
draining = null
@@ -289,13 +292,10 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
H.LAssailant = owner
while(do_mob(owner, H, 50))
if(!(owner.mind in SSticker.mode.vampires))
- to_chat(owner, "Your fangs have disappeared!")
+ to_chat(owner, "Your fangs have disappeared!")
return
old_bloodtotal = bloodtotal
old_bloodusable = bloodusable
- if(!H.blood_volume)
- to_chat(owner, "They've got no blood left to give.")
- break
if(H.stat < DEAD)
if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died
blood = min(20, H.blood_volume) // if they have less than 20 blood, give them the remnant else they get 20 blood
@@ -310,6 +310,17 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha
to_chat(owner, "You have accumulated [bloodtotal] [bloodtotal > 1 ? "units" : "unit"] of blood[bloodusable != old_bloodusable ? ", and have [bloodusable] left to use" : ""].")
check_vampire_upgrade()
H.blood_volume = max(H.blood_volume - 25, 0)
+ //Blood level warnings (Code 'borrowed' from Fulp)
+ if(H.blood_volume)
+ if(H.blood_volume <= BLOOD_VOLUME_BAD && blood_volume_warning > BLOOD_VOLUME_BAD)
+ to_chat(owner, "Your victim's blood volume is dangerously low.")
+ else if(H.blood_volume <= BLOOD_VOLUME_OKAY && blood_volume_warning > BLOOD_VOLUME_OKAY)
+ to_chat(owner, "Your victim's blood is at an unsafe level.")
+ blood_volume_warning = H.blood_volume //Set to blood volume, so that you only get the message once
+ else
+ to_chat(owner, "You have bled your victim dry!")
+ break
+
if(ishuman(owner))
var/mob/living/carbon/human/V = owner
if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index 2c7cee9d61a..7906deeb718 100644
--- a/code/game/gamemodes/vampire/vampire_powers.dm
+++ b/code/game/gamemodes/vampire/vampire_powers.dm
@@ -145,7 +145,7 @@
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
- desc= "Flush your system with spare blood to remove any incapacitating effects."
+ desc= "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
stat_allowed = 1
@@ -158,7 +158,7 @@
user.SetParalysis(0)
user.SetSleeping(0)
U.adjustStaminaLoss(-75)
- to_chat(user, "You flush your system with clean blood and remove any incapacitating effects.")
+ to_chat(user, "You instill your body with clean blood and remove any incapacitating effects.")
spawn(1)
if(usr.mind.vampire.get_ability(/datum/vampire_passive/regen))
for(var/i = 1 to 5)
@@ -354,10 +354,10 @@
var/datum/objective/protect/serve_objective = new
serve_objective.owner = user.mind
serve_objective.target = H.mind
- serve_objective.explanation_text = "You have been Enthralled by [user]. Follow [user.p_their()] every command."
+ serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.objectives += serve_objective
- to_chat(H, "You have been Enthralled by [user]. Follow [user.p_their()] every command.")
+ to_chat(H, "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.")
to_chat(user, "You have successfully Enthralled [H]. If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.")
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
@@ -507,13 +507,13 @@
to_chat(user, "You cannot find darkness to step to.")
return
+ turfs = list(pick(turfs)) // Pick a single turf for the vampire to jump to.
perform(turfs, user = user)
+// `targets` should only ever contain the 1 valid turf we're jumping to, even though its a list, that's just how the cast() proc works.
/obj/effect/proc_holder/spell/vampire/shadowstep/cast(list/targets, mob/user = usr)
spawn(0)
- var/turf/picked = pick(targets)
-
- if(!picked || !isturf(picked))
+ if(!LAZYLEN(targets)) // If for some reason the turf got deleted.
return
var/mob/living/U = user
U.ExtinguishMob()
@@ -525,7 +525,7 @@
animation.alpha = 127
animation.layer = 5
//animation.master = src
- user.forceMove(picked)
+ user.forceMove(targets[1])
spawn(10)
qdel(animation)
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 205fd63d9c6..44dec855f82 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -50,7 +50,8 @@
to_chat(H, "You already used this contract!")
return
used = 1
- var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1)
+ var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
new /obj/effect/particle_effect/smoke(H.loc)
@@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse)
if(M.assigned == assigned)
M.cooldown = cooldown
- var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100)
+ var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
spawn_copy(C.client, get_turf(user.loc), user)
@@ -470,7 +472,7 @@ GLOBAL_LIST_EMPTY(multiverse)
M.equip_to_slot_or_del(sword, slot_r_hand)
if("cyborg")
- if(!ismachine(M))
+ if(!ismachineperson(M))
for(var/obj/item/organ/O in M.bodyparts)
O.robotize(make_tough = 1)
M.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/eyepatch(M), slot_glasses)
@@ -866,8 +868,9 @@ GLOBAL_LIST_EMPTY(multiverse)
possible = list()
if(!link)
return
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- if(md5(H.dna.uni_identity) in link.fingerprints)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
+ if(H.stat != DEAD && (md5(H.dna.uni_identity) in link.fingerprints))
possible |= H
/obj/item/voodoo/proc/GiveHint(mob/victim,force=0)
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
index cc52b431249..d51b16d2cd3 100644
--- a/code/game/gamemodes/wizard/raginmages.dm
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -118,7 +118,8 @@
return FALSE
making_mage = TRUE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS)
+ var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source)
var/mob/dead/observer/harry = null
message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.")
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index 4bda254d930..aab4318c774 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -363,7 +363,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
- var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100)
+ var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index b76b31255e9..d0773947c21 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -1,20 +1,16 @@
/datum/spellbook_entry
var/name = "Entry Name"
-
+ var/is_ragin_restricted = FALSE // FALSE if this is buyable on ragin mages, TRUE if it's not.
var/spell_type = null
var/desc = ""
var/category = "Offensive"
var/log_name = "XX" //What it shows up as in logs
var/cost = 2
var/refundable = TRUE
- var/surplus = -1 // -1 for infinite, not used by anything atm
var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell
var/buy_word = "Learn"
var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook
-/datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied
- return TRUE
-
/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances
if(book.uses < cost || limit == 0)
return FALSE
@@ -218,12 +214,7 @@
spell_type = /obj/effect/proc_holder/spell/targeted/lichdom
log_name = "LD"
category = "Defensive"
-
-/datum/spellbook_entry/lichdom/IsSpellAvailable()
- if(SSticker.mode.name == "ragin' mages")
- return FALSE
- else
- return TRUE
+ is_ragin_restricted = TRUE
/datum/spellbook_entry/magicm
name = "Magic Missile"
@@ -325,14 +316,7 @@
desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you."
cost = 0
log_name = "SGH"
-
-/datum/spellbook_entry/summon/ghosts/IsSpellAvailable()
- if(!SSticker.mode) // In case spellbook is placed on map
- return FALSE
- if(SSticker.mode.name == "ragin' mages")
- return FALSE
- else
- return TRUE
+ is_ragin_restricted = TRUE
/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
new /datum/event/wizard/ghost()
@@ -345,14 +329,7 @@
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first."
log_name = "SG"
-
-/datum/spellbook_entry/summon/guns/IsSpellAvailable()
- if(!SSticker.mode) // In case spellbook is placed on map
- return FALSE
- if(SSticker.mode.name == "ragin' mages")
- return FALSE
- else
- return TRUE
+ is_ragin_restricted = TRUE
/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
feedback_add_details("wizard_spell_learned", log_name)
@@ -366,14 +343,7 @@
name = "Summon Magic"
desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time."
log_name = "SU"
-
-/datum/spellbook_entry/summon/magic/IsSpellAvailable()
- if(!SSticker.mode) // In case spellbook is placed on map
- return FALSE
- if(SSticker.mode.name == "ragin' mages")
- return FALSE
- else
- return TRUE
+ is_ragin_restricted = TRUE
/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
feedback_add_details("wizard_spell_learned", log_name)
@@ -400,8 +370,6 @@
dat += "[name]"
dat += " Cost:[cost] "
dat += "[desc] "
- if(surplus>=0)
- dat += "[surplus] left. "
return dat
//Artefacts
@@ -642,11 +610,12 @@
var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - /datum/spellbook_entry/loadout
for(var/T in entry_types)
var/datum/spellbook_entry/E = new T
- if(E.IsSpellAvailable())
- entries |= E
- categories |= E.category
- else
+ if(GAMEMODE_IS_RAGIN_MAGES && E.is_ragin_restricted)
qdel(E)
+ continue
+ entries |= E
+ categories |= E.category
+
main_tab = main_categories[1]
tab = categories[1]
diff --git a/code/game/gamemodes/wizard/wizloadouts.dm b/code/game/gamemodes/wizard/wizloadouts.dm
index 0e53d30f1bf..c050b27f273 100644
--- a/code/game/gamemodes/wizard/wizloadouts.dm
+++ b/code/game/gamemodes/wizard/wizloadouts.dm
@@ -20,6 +20,7 @@
log_name = "DL"
spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/fireball, \
/obj/effect/proc_holder/spell/targeted/rod_form, /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech, /obj/effect/proc_holder/spell/targeted/forcewall/greater)
+ is_ragin_restricted = TRUE
/datum/spellbook_entry/loadout/wands
name = "Utility Focus : Wands"
@@ -63,6 +64,7 @@
/obj/effect/proc_holder/spell/targeted/summonitem, /obj/effect/proc_holder/spell/noclothes, /obj/effect/proc_holder/spell/targeted/lichdom/gunslinger)
category = "Unique"
destroy_spellbook = TRUE
+ is_ragin_restricted = TRUE
/obj/effect/proc_holder/spell/targeted/lichdom/gunslinger/equip_lich(mob/living/carbon/human/H)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_suit(H), slot_wear_suit)
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index 7f93fd9fef6..48ac91c72fa 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -20,8 +20,6 @@
else
return check_access_list(acc)
- return 0
-
/obj/item/proc/GetAccess()
return list()
@@ -512,3 +510,26 @@ proc/get_all_job_icons() //For all existing HUD icons
return rankName
return "Unknown" //Return unknown if none of the above apply
+
+proc/get_accesslist_static_data(num_min_region = REGION_GENERAL, num_max_region = REGION_COMMAND)
+ var/list/retval
+ for(var/i in num_min_region to num_max_region)
+ var/list/accesses = list()
+ var/list/available_accesses
+ if(i == REGION_CENTCOMM) // Override necessary, because get_region_accesses(REGION_CENTCOM) returns BOTH CC and crew accesses.
+ available_accesses = get_all_centcom_access()
+ else
+ available_accesses = get_region_accesses(i)
+ for(var/access in available_accesses)
+ var/access_desc = (i == REGION_CENTCOMM) ? get_centcom_access_desc(access) : get_access_desc(access)
+ if (access_desc)
+ accesses += list(list(
+ "desc" = replacetext(access_desc, " ", " "),
+ "ref" = access,
+ ))
+ retval += list(list(
+ "name" = get_region_accesses_name(i),
+ "regid" = i,
+ "accesses" = accesses
+ ))
+ return retval
diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm
index 84f903dd2b1..a3333e1dc82 100644
--- a/code/game/jobs/job/engineering.dm
+++ b/code/game/jobs/job/engineering.dm
@@ -18,7 +18,7 @@
ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS,
ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM)
minimal_player_age = 21
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_ENGINEERING
outfit = /datum/outfit/job/chief_engineer
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index a30799f08b9..d5c5089c8af 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -51,6 +51,7 @@
var/disabilities_allowed = 1
var/transfer_allowed = TRUE // If false, ID computer will always discourage transfers to this job, even if player is eligible
+ var/hidden_from_job_prefs = FALSE // if true, job preferences screen never shows this job.
var/admin_only = 0
var/spawn_ert = 0
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index 2d1b091a10d..b1b81e0b849 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -16,7 +16,7 @@
ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE,
ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM)
minimal_player_age = 21
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_MEDICAL
outfit = /datum/outfit/job/cmo
diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm
index 684de933832..f6fdb02477b 100644
--- a/code/game/jobs/job/science.dm
+++ b/code/game/jobs/job/science.dm
@@ -18,7 +18,7 @@
ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD,
ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK)
minimal_player_age = 21
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_SCIENCE
// All science-y guys get bonuses for maxing out their tech.
required_objectives = list(
diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm
index a32656598df..6f4eb3759de 100644
--- a/code/game/jobs/job/security.dm
+++ b/code/game/jobs/job/security.dm
@@ -18,7 +18,7 @@
ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING,
ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_PILOT, ACCESS_WEAPONS)
minimal_player_age = 21
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_SECURITY
disabilities_allowed = 0
outfit = /datum/outfit/job/hos
@@ -64,7 +64,7 @@
minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS)
minimal_player_age = 21
exp_requirements = 600
- exp_type = EXP_TYPE_CREW
+ exp_type = EXP_TYPE_SECURITY
outfit = /datum/outfit/job/warden
/datum/outfit/job/warden
diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm
index b6321765861..fef9c2302b3 100644
--- a/code/game/jobs/job/silicon.dm
+++ b/code/game/jobs/job/silicon.dm
@@ -24,15 +24,15 @@
title = "Cyborg"
flag = JOB_CYBORG
department_flag = JOBCAT_ENGSEC
- total_positions = 1
- spawn_positions = 1
+ total_positions = 2
+ spawn_positions = 2
supervisors = "your laws and the AI" //Nodrak
department_head = list("AI")
selection_color = "#ddffdd"
minimal_player_age = 21
exp_requirements = 300
exp_type = EXP_TYPE_CREW
- alt_titles = list("Android", "Robot")
+ alt_titles = list("Robot")
/datum/job/cyborg/equip(mob/living/carbon/human/H)
if(!H)
diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm
index c56d27bb408..55ae7f4b3ae 100644
--- a/code/game/jobs/job/supervisor.dm
+++ b/code/game/jobs/job/supervisor.dm
@@ -13,7 +13,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
access = list() //See get_access()
minimal_access = list() //See get_access()
minimal_player_age = 30
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_COMMAND
disabilities_allowed = 0
outfit = /datum/outfit/job/captain
@@ -67,7 +67,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
req_admin_notify = 1
is_command = 1
minimal_player_age = 21
- exp_requirements = 300
+ exp_requirements = 1200
exp_type = EXP_TYPE_COMMAND
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS,
ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS,
@@ -223,6 +223,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
id = /obj/item/card/id/nanotrasen
l_pocket = /obj/item/flash
+ r_pocket = /obj/item/clothing/accessory/lawyers_badge
pda = /obj/item/pda/heads/magistrate
backpack_contents = list(
/obj/item/melee/classic_baton/telescopic = 1
@@ -262,9 +263,12 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
glasses = /obj/item/clothing/glasses/hud/security/sunglasses/read_only
id = /obj/item/card/id/security
l_pocket = /obj/item/laser_pointer
- r_pocket = /obj/item/flash
+ r_pocket = /obj/item/clothing/accessory/lawyers_badge
l_hand = /obj/item/storage/briefcase
pda = /obj/item/pda/lawyer
+ backpack_contents = list(
+ /obj/item/flash = 1
+ )
implants = list(/obj/item/implant/mindshield)
satchel = /obj/item/storage/backpack/satchel_sec
dufflebag = /obj/item/storage/backpack/duffel/security
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index d0064300b62..09328865b59 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -282,14 +282,14 @@
if(visualsOnly)
return
- if(ismachine(H))
+ if(ismachineperson(H))
var/obj/item/organ/internal/cyberimp/brain/clown_voice/implant = new
implant.insert(H)
H.dna.SetSEState(GLOB.clumsyblock, TRUE)
genemutcheck(H, GLOB.clumsyblock, null, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.clumsyblock)
- if(!ismachine(H))
+ if(!ismachineperson(H))
H.dna.SetSEState(GLOB.comicblock, TRUE)
genemutcheck(H, GLOB.comicblock, null, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.comicblock)
@@ -450,3 +450,24 @@
/obj/item/storage/box/lip_stick = 1,
/obj/item/storage/box/barber = 1
)
+
+/datum/job/explorer
+ title = "Explorer"
+ flag = JOB_EXPLORER
+ department_flag = JOBCAT_SUPPORT
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = "the head of personnel"
+ selection_color = "#dddddd"
+ access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS)
+ minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS)
+ outfit = /datum/outfit/job/explorer
+ hidden_from_job_prefs = TRUE
+
+/datum/outfit/job/explorer
+ // This outfit is never used, because there are no slots for this job.
+ // To get it, you have to go to the HOP and ask for a transfer to it.
+ name = "Explorer"
+ jobtype = /datum/job/explorer
+ uniform = /obj/item/clothing/under/color/random
+ shoes = /obj/item/clothing/shoes/black
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
index f0e6f9bff42..aa9114c2963 100644
--- a/code/game/jobs/job_exp.dm
+++ b/code/game/jobs/job_exp.dm
@@ -224,19 +224,17 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list(
else
return "none"
-/proc/update_exp(var/mins, var/ann = 0)
- if(!establish_db_connection())
- return -1
- spawn(0)
- for(var/client/L in GLOB.clients)
- if(L.inactivity >= (10 MINUTES))
- continue
- spawn(0)
- L.update_exp_client(mins, ann)
- sleep(10)
+/proc/update_exp(mins = 0, ann = 0)
+ if(!GLOB.dbcon.IsConnected())
+ return
+ for(var/client/L in GLOB.clients)
+ if(L.inactivity >= (10 MINUTES))
+ continue
+ L.update_exp_client(mins, ann)
+ CHECK_TICK
-/client/proc/update_exp_client(var/minutes, var/announce_changes = 0)
- if(!src ||!ckey)
+/client/proc/update_exp_client(minutes = 0, announce_changes = 0)
+ if(!src || !ckey || !GLOB.dbcon.IsConnected())
return
var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'")
if(!exp_read.Execute())
diff --git a/code/game/jobs/job_scaling.dm b/code/game/jobs/job_scaling.dm
deleted file mode 100644
index c062fe6c216..00000000000
--- a/code/game/jobs/job_scaling.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/hook/roundstart/proc/jobscaling()
- sleep(10 SECONDS) // give everyone time to finish spawning, and the lag to die down
- var/playercount = length(GLOB.clients)
- var/highpop_trigger = 80
-
- if(playercount >= highpop_trigger)
- log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - loading highpop job config");
- SSjobs.LoadJobs("config/jobs_highpop.txt")
- else
- log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config");
- return 1
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 8ff6b58e5c4..81cc54aa3c3 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -58,7 +58,8 @@ GLOBAL_LIST_INIT(support_positions, list(
"Barber",
"Magistrate",
"Nanotrasen Representative",
- "Blueshield"
+ "Blueshield",
+ "Explorer"
))
GLOBAL_LIST_INIT(supply_positions, list(
diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm
index ab1bd3b8067..46b98ce1030 100644
--- a/code/game/jobs/whitelist.dm
+++ b/code/game/jobs/whitelist.dm
@@ -2,10 +2,11 @@
GLOBAL_LIST_EMPTY(whitelist)
-/hook/startup/proc/loadWhitelist()
+/proc/init_whitelists()
if(config.usewhitelist)
load_whitelist()
- return 1
+ if(config.usealienwhitelist)
+ load_alienwhitelist()
/proc/load_whitelist()
GLOB.whitelist = file2list(WHITELISTFILE)
@@ -48,11 +49,6 @@ GLOBAL_LIST_EMPTY(whitelist)
GLOBAL_LIST_EMPTY(alien_whitelist)
-/hook/startup/proc/loadAlienWhitelist()
- if(config.usealienwhitelist)
- load_alienwhitelist()
- return 1
-
/proc/load_alienwhitelist()
var/text = file2text("config/alienwhitelist.txt")
if(!text)
diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm
index d9243005ead..a2a69fad357 100644
--- a/code/game/machinery/Freezer.dm
+++ b/code/game/machinery/Freezer.dm
@@ -104,59 +104,46 @@
to_chat(user, "Close the maintenance panel first.")
return
- src.ui_interact(user)
+ tgui_interact(user)
-/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300)
- // open the new ui window
+ ui = new(user, src, ui_key, "GasFreezer", "Gas Cooling System", 540, 200)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["on"] = on ? 1 : 0
- data["gasPressure"] = round(air_contents.return_pressure())
- data["gasTemperature"] = round(air_contents.temperature)
- data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1)
+/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(air_contents.return_pressure())
+ data["temperature"] = round(air_contents.temperature)
+ data["temperatureCelsius"] = round(air_contents.temperature - T0C, 1)
if(air_contents.total_moles() == 0 && air_contents.temperature == 0)
- data["gasTemperatureCelsius"] = 0
- data["minGasTemperature"] = round(min_temperature)
- data["maxGasTemperature"] = round(T20C)
- data["targetGasTemperature"] = round(current_temperature)
- data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1)
-
- var/temp_class = "good"
- if(air_contents.temperature > (T0C - 20))
- temp_class = "bad"
- else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100))
- temp_class = "average"
- data["gasTemperatureClass"] = temp_class
+ data["temperatureCelsius"] = 0
+ data["min"] = round(min_temperature)
+ data["max"] = round(T20C)
+ data["target"] = round(current_temperature)
+ data["targetCelsius"] = round(current_temperature - T0C, 1)
return data
-/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list)
+/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_act(action, params)
if(..())
- return 1
- if(href_list["toggleStatus"])
- src.on = !src.on
- update_icon()
- else if(href_list["minimum"])
- current_temperature = min_temperature
- else if(href_list["maximum"])
- current_temperature = T20C
- else if(href_list["temp"])
- var/amount = text2num(href_list["temp"])
- if(amount > 0)
- src.current_temperature = min(T20C, src.current_temperature+amount)
- else
- src.current_temperature = max(min_temperature, src.current_temperature+amount)
- src.add_fingerprint(usr)
- return 1
+ return
+ add_fingerprint(usr)
+ . = TRUE
+
+ switch(action)
+ if("power")
+ on = !on
+ update_icon()
+ if("minimum")
+ current_temperature = min_temperature
+ if("maximum")
+ current_temperature = T20C
+ if("temp")
+ var/amount = params["temp"]
+ amount = text2num(amount)
+ current_temperature = clamp(amount, T20C, min_temperature)
/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change()
..()
@@ -273,57 +260,46 @@
if(panel_open)
to_chat(user, "Close the maintenance panel first.")
return
- src.ui_interact(user)
+ tgui_interact(user)
-/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300)
- // open the new ui window
+ ui = new(user, src, ui_key, "GasFreezer", "Gas Heating System", 540, 200)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["on"] = on ? 1 : 0
- data["gasPressure"] = round(air_contents.return_pressure())
- data["gasTemperature"] = round(air_contents.temperature)
- data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1)
+/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(air_contents.return_pressure())
+ data["temperature"] = round(air_contents.temperature)
+ data["temperatureCelsius"] = round(air_contents.temperature - T0C, 1)
if(air_contents.total_moles() == 0 && air_contents.temperature == 0)
- data["gasTemperatureCelsius"] = 0
- data["minGasTemperature"] = round(T20C)
- data["maxGasTemperature"] = round(T20C+max_temperature)
- data["targetGasTemperature"] = round(current_temperature)
- data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1)
-
- var/temp_class = "normal"
- if(air_contents.temperature > (T20C+40))
- temp_class = "bad"
- data["gasTemperatureClass"] = temp_class
+ data["temperatureCelsius"] = 0
+ data["min"] = round(T20C)
+ data["max"] = round(T20C + max_temperature)
+ data["target"] = round(current_temperature)
+ data["targetCelsius"] = round(current_temperature - T0C, 1)
return data
-/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list)
+/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_act(action, params)
if(..())
- return 1
- if(href_list["toggleStatus"])
- src.on = !src.on
- update_icon()
- else if(href_list["minimum"])
- current_temperature = T20C
- else if(href_list["maximum"])
- current_temperature = max_temperature + T20C
- else if(href_list["temp"])
- var/amount = text2num(href_list["temp"])
- if(amount > 0)
- src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount)
- else
- src.current_temperature = max(T20C, src.current_temperature+amount)
- src.add_fingerprint(usr)
- return 1
+ return
+ add_fingerprint(usr)
+ . = TRUE
+
+ switch(action)
+ if("power")
+ on = !on
+ update_icon()
+ if("minimum")
+ current_temperature = T20C
+ if("maximum")
+ current_temperature = max_temperature + T20C
+ if("temp")
+ var/amount = params["temp"]
+ amount = text2num(amount)
+ current_temperature = clamp(amount, T20C, T20C + max_temperature)
/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change()
..()
diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm
index 7317099b8dc..3bcb9675c50 100644
--- a/code/game/machinery/OpTable.dm
+++ b/code/game/machinery/OpTable.dm
@@ -136,7 +136,7 @@
/obj/machinery/optable/wrench_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
if(I.use_tool(src, user, 20, volume = I.tool_volume))
to_chat(user, "You deconstruct the table.")
diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm
index 3185cdcddd4..332df41f086 100644
--- a/code/game/machinery/PDApainter.dm
+++ b/code/game/machinery/PDApainter.dm
@@ -30,15 +30,11 @@
/obj/machinery/pdapainter/New()
..()
var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads,
- /obj/item/pda/clear, /obj/item/pda/syndicate)
+ /obj/item/pda/clear, /obj/item/pda/syndicate, /obj/item/pda/chameleon, /obj/item/pda/chameleon/broken)
- for(var/P in typesof(/obj/item/pda)-blocked)
- var/obj/item/pda/D = new P
-
- //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA"
- D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works.
-
- src.colorlist += D
+ for(var/thing in typesof(/obj/item/pda) - blocked)
+ var/obj/item/pda/P = thing
+ colorlist[initial(P.icon_state)] = initial(P.desc)
/obj/machinery/pdapainter/Destroy()
QDEL_NULL(storedpda)
@@ -104,8 +100,8 @@
if(!in_range(src, user))
return
- storedpda.icon_state = P.icon_state
- storedpda.desc = P.desc
+ storedpda.icon_state = P
+ storedpda.desc = colorlist[P]
else
to_chat(user, "The [src] is empty.")
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 70d88e92b71..d33518be857 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -125,7 +125,7 @@
return attack_hand(user)
/obj/machinery/sleeper/attack_ghost(mob/user)
- return attack_hand(user)
+ tgui_interact(user)
/obj/machinery/sleeper/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
@@ -135,17 +135,17 @@
to_chat(user, "Close the maintenance panel first.")
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/sleeper/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770)
+ ui = new(user, src, ui_key, "Sleeper", "Sleeper", 550, 775)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state)
+/obj/machinery/sleeper/tgui_data(mob/user)
var/data[0]
+ data["amounts"] = amounts
data["hasOccupant"] = occupant ? 1 : 0
var/occupantData[0]
var/crisis = 0
@@ -210,9 +210,13 @@
if(beaker)
data["isBeakerLoaded"] = 1
if(beaker.reagents)
+ data["beakerMaxSpace"] = beaker.reagents.maximum_volume
data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume)
else
+ data["beakerMaxSpace"] = 0
data["beakerFreeSpace"] = 0
+ else
+ data["isBeakerLoaded"] = FALSE
var/chemicals[0]
for(var/re in possible_chems)
@@ -234,51 +238,52 @@
if(temp.id in occupant.reagents.overdose_list())
overdosing = 1
- // Because I don't know how to do this on the nano side
pretty_amount = round(reagent_amount, 0.05)
chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution)))
data["chemicals"] = chemicals
return data
-/obj/machinery/sleeper/Topic(href, href_list)
- if(!controls_inside && usr == occupant)
- return 0
-
+/obj/machinery/sleeper/tgui_act(action, params)
if(..())
- return 1
-
+ return
+ if(!controls_inside && usr == occupant)
+ return
if(panel_open)
to_chat(usr, "Close the maintenance panel first.")
- return 0
+ return
+ if(stat & (NOPOWER|BROKEN))
+ return
- if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai)))
- if(href_list["chemical"])
- if(occupant)
- if(occupant.stat == DEAD)
- to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.")
- else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems))
- inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"]))
- else
- to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!")
-
- if(href_list["removebeaker"])
+ . = TRUE
+ switch(action)
+ if("chemical")
+ if(!occupant)
+ return
+ if(occupant.stat == DEAD)
+ to_chat(usr, "This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.")
+ return
+ var/chemical = params["chemid"]
+ var/amount = text2num(params["amount"])
+ if(!length(chemical) || amount <= 0)
+ return
+ if(occupant.health > min_health || (chemical in emergency_chems))
+ inject_chemical(usr, chemical, amount)
+ else
+ to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!")
+ if("removebeaker")
remove_beaker()
-
- if(href_list["togglefilter"])
+ if("togglefilter")
toggle_filter()
-
- if(href_list["ejectify"])
+ if("ejectify")
eject()
-
- if(href_list["auto_eject_dead_on"])
+ if("auto_eject_dead_on")
auto_eject_dead = TRUE
-
- if(href_list["auto_eject_dead_off"])
+ if("auto_eject_dead_off")
auto_eject_dead = FALSE
-
- add_fingerprint(usr)
- return 1
+ else
+ return FALSE
+ add_fingerprint(usr)
/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
@@ -290,6 +295,7 @@
beaker = I
I.forceMove(src)
user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!")
+ SStgui.update_uis(src)
return
else
@@ -328,6 +334,7 @@
to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.")
add_fingerprint(user)
qdel(G)
+ SStgui.update_uis(src)
return
return ..()
@@ -374,9 +381,11 @@
occupant = null
updateUsrDialog()
update_icon()
+ SStgui.update_uis(src)
if(A == beaker)
beaker = null
updateUsrDialog()
+ SStgui.update_uis(src)
/obj/machinery/sleeper/emp_act(severity)
if(filtering)
@@ -394,7 +403,7 @@
qdel(src)
/obj/machinery/sleeper/proc/toggle_filter()
- if(filtering)
+ if(filtering || !beaker)
filtering = 0
else
filtering = 1
@@ -410,26 +419,28 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
+ SStgui.update_uis(src)
-/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
+/obj/machinery/sleeper/force_eject_occupant()
+ go_out()
+
+/obj/machinery/sleeper/proc/inject_chemical(mob/living/user, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "The sleeper does not offer that chemical!")
return
+ if(!(amount in amounts))
+ return
if(occupant)
if(occupant.reagents)
if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem)
occupant.reagents.add_reagent(chemical, amount)
- return
else
to_chat(user, "You can not inject any more of this chemical.")
- return
else
to_chat(user, "The patient rejects the chemicals!")
- return
else
to_chat(user, "There's no occupant in the sleeper!")
- return
/obj/machinery/sleeper/verb/eject()
set name = "Eject Sleeper"
@@ -456,6 +467,7 @@
filtering = 0
beaker.forceMove(usr.loc)
beaker = null
+ SStgui.update_uis(src)
add_fingerprint(usr)
return
@@ -508,6 +520,7 @@
add_fingerprint(user)
if(user.pulling == L)
user.stop_pulling()
+ SStgui.update_uis(src)
return
return
@@ -526,7 +539,7 @@
if(panel_open)
to_chat(usr, "Close the maintenance panel first.")
return
- if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other
+ if(usr.incapacitated() || usr.buckled) //are you cuffed, dying, lying, stunned or other
return
if(usr.has_buckled_mobs()) //mob attached to us
to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.")
@@ -544,6 +557,7 @@
for(var/obj/O in src)
qdel(O)
add_fingerprint(usr)
+ SStgui.update_uis(src)
return
return
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 1ee30b6e9d0..97ab80fc1d7 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -65,6 +65,7 @@
icon_state = "body_scanner_1"
add_fingerprint(user)
qdel(TYPECAST_YOUR_SHIT)
+ SStgui.update_uis(src)
return
return ..()
@@ -127,12 +128,13 @@
occupant = H
icon_state = "bodyscanner"
add_fingerprint(user)
+ SStgui.update_uis(src)
/obj/machinery/bodyscanner/attack_ai(user)
return attack_hand(user)
/obj/machinery/bodyscanner/attack_ghost(user)
- return attack_hand(user)
+ tgui_interact(user)
/obj/machinery/bodyscanner/attack_hand(user)
if(stat & (NOPOWER|BROKEN))
@@ -145,7 +147,7 @@
to_chat(user, "Close the maintenance panel first.")
return
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/bodyscanner/relaymove(mob/user)
if(user.incapacitated())
@@ -171,6 +173,10 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
+ SStgui.update_uis(src)
+
+/obj/machinery/bodyscanner/force_eject_occupant()
+ go_out()
/obj/machinery/bodyscanner/ex_act(severity)
if(occupant)
@@ -189,17 +195,16 @@
new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH!
qdel(src)
-/obj/machinery/bodyscanner/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/bodyscanner/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "BodyScanner", "Body Scanner", 690, 600)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/bodyscanner/ui_data(mob/user, datum/topic_state/state)
+/obj/machinery/bodyscanner/tgui_data(mob/user)
var/data[0]
- data["occupied"] = occupant ? 1 : 0
+ data["occupied"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -233,9 +238,9 @@
occupantData["hasBorer"] = occupant.has_brain_worms()
var/bloodData[0]
- bloodData["hasBlood"] = 0
+ bloodData["hasBlood"] = FALSE
if(!(NO_BLOOD in occupant.dna.species.species_traits))
- bloodData["hasBlood"] = 1
+ bloodData["hasBlood"] = TRUE
bloodData["volume"] = occupant.blood_volume
bloodData["percent"] = round(((occupant.blood_volume / BLOOD_VOLUME_NORMAL)*100))
bloodData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
@@ -279,19 +284,19 @@
if(E.status & ORGAN_BROKEN)
organStatus["broken"] = E.broken_description
if(E.is_robotic())
- organStatus["robotic"] = 1
+ organStatus["robotic"] = TRUE
if(E.status & ORGAN_SPLINTED)
- organStatus["splinted"] = 1
+ organStatus["splinted"] = TRUE
if(E.status & ORGAN_DEAD)
- organStatus["dead"] = 1
+ organStatus["dead"] = TRUE
organData["status"] = organStatus
if(istype(E, /obj/item/organ/external/chest) && occupant.is_lung_ruptured())
- organData["lungRuptured"] = 1
+ organData["lungRuptured"] = TRUE
if(E.internal_bleeding)
- organData["internalBleeding"] = 1
+ organData["internalBleeding"] = TRUE
extOrganData.Add(list(organData))
@@ -316,27 +321,33 @@
occupantData["blind"] = (BLINDNESS in occupant.mutations)
occupantData["colourblind"] = (COLOURBLIND in occupant.mutations)
- occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
+ occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
data["occupant"] = occupantData
return data
-/obj/machinery/bodyscanner/Topic(href, href_list)
+/obj/machinery/bodyscanner/tgui_act(action, params)
if(..())
- return 1
+ return
+ if(stat & (NOPOWER|BROKEN))
+ return
- if(href_list["ejectify"])
- eject()
-
- if(href_list["print_p"])
- visible_message("[src] rattles and prints out a sheet of paper.")
- var/obj/item/paper/P = new /obj/item/paper(loc)
- playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
- P.info = "Body Scan - [href_list["name"]] "
- P.info += "Time of scan: [station_time_timestamp()]
"
- P.info += "[generate_printing_text()]"
- P.info += "
Notes: "
- P.name = "Body Scan - [href_list["name"]]"
+ . = TRUE
+ switch(action)
+ if("ejectify")
+ eject()
+ if("print_p")
+ visible_message("[src] rattles and prints out a sheet of paper.")
+ var/obj/item/paper/P = new /obj/item/paper(loc)
+ playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
+ var/name = occupant ? occupant.name : "Unknown"
+ P.info = "Body Scan - [name] "
+ P.info += "Time of scan: [station_time_timestamp()]
"
+ P.info += "[generate_printing_text()]"
+ P.info += "
Notes: "
+ P.name = "Body Scan - [name]"
+ else
+ return FALSE
/obj/machinery/bodyscanner/proc/generate_printing_text()
var/dat = ""
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 02d95278e30..951f2a1e9cf 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -199,8 +199,8 @@
mode = AALARM_MODE_REPLACEMENT
apply_mode()
-/obj/machinery/alarm/New(var/loc, var/dir, var/building = 0)
- ..()
+/obj/machinery/alarm/New(loc, direction, building = 0)
+ . = ..()
GLOB.air_alarms += src
GLOB.air_alarms = sortAtom(GLOB.air_alarms)
@@ -211,18 +211,18 @@
src.loc = loc
if(dir)
- src.dir = dir
+ setDir(direction)
buildstage = 0
wiresexposed = 1
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
- pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
+ set_pixel_offsets_from_dir(-24, 24, -24, 24)
update_icon()
return
first_run()
/obj/machinery/alarm/Destroy()
+ SStgui.close_uis(wires)
GLOB.air_alarms -= src
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -251,7 +251,7 @@
/obj/machinery/alarm/proc/master_is_operating()
if(!alarm_area)
- alarm_area = areaMaster
+ alarm_area = get_area(src)
if(!alarm_area)
log_runtime(EXCEPTION("Air alarm /obj/machinery/alarm lacks alarm_area and areaMaster vars during proc/master_is_operating()"), src)
return FALSE
@@ -299,10 +299,7 @@
var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP)
cur_tlv = TLV["other"]
- var/other_moles = 0.0
- for(var/datum/gas/G in environment.trace_gases)
- other_moles+=G.moles
- var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP)
+ var/other_dangerlevel = cur_tlv.get_danger_level(environment.total_trace_moles() * GET_PP)
cur_tlv = TLV["temperature"]
var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
@@ -318,7 +315,7 @@
temperature_dangerlevel
)
- if(old_danger_level!=danger_level)
+ if(old_danger_level != danger_level)
apply_danger_level()
if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05)
@@ -326,16 +323,18 @@
apply_mode()
-/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment, var/datum/tlv/cur_tlv, var/turf/simulated/location)
+/obj/machinery/alarm/proc/handle_heating_cooling(datum/gas_mixture/environment, datum/tlv/cur_tlv, turf/simulated/location)
cur_tlv = TLV["temperature"]
//Handle temperature adjustment here.
if(environment.temperature < target_temperature - 2 || environment.temperature > target_temperature + 2 || regulating_temperature)
//If it goes too far, we should adjust ourselves back before stopping.
if(!cur_tlv.get_danger_level(target_temperature))
+ var/datum/gas_mixture/gas = location.remove_air(0.25 * environment.total_moles())
+ if(!gas)
+ return
if(!regulating_temperature)
regulating_temperature = 1
- visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click and a faint electronic hum.")
+ visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click and a faint electronic hum.")
if(target_temperature > MAX_TEMPERATURE)
target_temperature = MAX_TEMPERATURE
@@ -343,9 +342,8 @@
if(target_temperature < MIN_TEMPERATURE)
target_temperature = MIN_TEMPERATURE
- var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles())
var/heat_capacity = gas.heat_capacity()
- var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
+ var/energy_used = max(abs(heat_capacity * (gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE)
//Use power. Assuming that each power unit represents 1000 watts....
use_power(energy_used/1000, ENVIRON)
@@ -353,16 +351,15 @@
//We need to cool ourselves.
if(heat_capacity)
if(environment.temperature > target_temperature)
- gas.temperature -= energy_used/heat_capacity
+ gas.temperature -= energy_used / heat_capacity
else
- gas.temperature += energy_used/heat_capacity
+ gas.temperature += energy_used / heat_capacity
environment.merge(gas)
if(abs(environment.temperature - target_temperature) <= 0.5)
regulating_temperature = 0
- visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click as a faint electronic humming stops.")
+ visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.", "You hear a click as a faint electronic humming stops.")
/obj/machinery/alarm/update_icon()
if(wiresexposed)
@@ -538,28 +535,35 @@
"checks"= 0,
))
-/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level)
- if(report_danger_level && alarm_area.atmosalert(new_danger_level, src))
- post_alert(new_danger_level)
+/obj/machinery/alarm/proc/apply_danger_level()
+ var/new_area_danger_level = ATMOS_ALARM_NONE
+ for(var/obj/machinery/alarm/AA in alarm_area)
+ if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
+ new_area_danger_level = max(new_area_danger_level, AA.danger_level)
+ if(alarm_area.atmosalert(new_area_danger_level, src)) //if area was in normal state or if area was in alert state
+ post_alert(new_area_danger_level)
update_icon()
/obj/machinery/alarm/proc/post_alert(alert_level)
+ if(!report_danger_level)
+ return
var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
+
if(!frequency)
return
var/datum/signal/alert_signal = new
alert_signal.source = src
alert_signal.transmission_method = 1
- alert_signal.data["zone"] = alarm_area.name
+ alert_signal.data["zone"] = get_area_name(src, TRUE)
alert_signal.data["type"] = "Atmospheric"
- if(alert_level==2)
+ if(alert_level == ATMOS_ALARM_DANGER)
alert_signal.data["alert"] = "severe"
- else if(alert_level==1)
+ else if(alert_level == ATMOS_ALARM_WARNING)
alert_signal.data["alert"] = "minor"
- else if(alert_level==0)
+ else if(alert_level == ATMOS_ALARM_NONE)
alert_signal.data["alert"] = "clear"
frequency.post_signal(src, alert_signal)
@@ -625,10 +629,8 @@
var/plasma_percent = round(environment.toxins / total * 100, 2)
cur_tlv = TLV["other"]
- var/other_moles = 0.0
- for(var/datum/gas/G in environment.trace_gases)
- other_moles+=G.moles
- var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP)
+ var/other_moles = environment.total_trace_moles()
+ var/other_dangerlevel = cur_tlv.get_danger_level(other_moles * GET_PP)
cur_tlv = TLV["temperature"]
var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
@@ -895,14 +897,14 @@
if(href_list["atmos_alarm"])
if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src))
- apply_danger_level(ATMOS_ALARM_DANGER)
+ post_alert(ATMOS_ALARM_DANGER)
alarmActivated = 1
update_icon()
return 1
if(href_list["atmos_reset"])
if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE))
- apply_danger_level(ATMOS_ALARM_NONE)
+ post_alert(ATMOS_ALARM_NONE)
alarmActivated = 0
update_icon()
return 1
@@ -957,7 +959,7 @@
to_chat(user, "It does nothing")
return
else
- if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN))
+ if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.")
updateUsrDialog()
@@ -996,7 +998,7 @@
if(buildstage != AIR_ALARM_BUILDING)
return
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
to_chat(user, "You start prying out the circuit.")
if(!I.use_tool(src, user, 20, volume = I.tool_volume))
@@ -1036,7 +1038,7 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
- if(wires.wires_status == 31) // all wires cut
+ if(wires.is_all_cut()) // all wires cut
var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location())
new_coil.amount = 5
buildstage = AIR_ALARM_BUILDING
@@ -1082,6 +1084,15 @@
if(buildstage < 1)
. += "The circuit is missing."
+/obj/machinery/alarm/proc/unshort_callback()
+ if(shorted)
+ shorted = FALSE
+ update_icon()
+
+/obj/machinery/alarm/proc/enable_ai_control_callback()
+ if(aidisabled)
+ aidisabled = FALSE
+
/obj/machinery/alarm/all_access
name = "all-access air alarm"
desc = "This particular atmos control unit appears to have no access restrictions."
diff --git a/code/game/machinery/atmoalter/area_atmos_computer.dm b/code/game/machinery/atmoalter/area_atmos_computer.dm
index 3d53e130628..d7650d1cb68 100644
--- a/code/game/machinery/atmoalter/area_atmos_computer.dm
+++ b/code/game/machinery/atmoalter/area_atmos_computer.dm
@@ -164,7 +164,7 @@
var/turf/T = get_turf(src)
if(!T.loc) return
var/area/A = get_area(T)
- for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in world )
+ for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in SSair.atmos_machinery)
var/turf/T2 = get_turf(scrubber)
if(T2 && T2.loc)
var/area/A2 = T2.loc
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index d91fba5fa5c..ea273850d90 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -11,6 +11,7 @@
list("name" = "\[SPECIAL\]", "icon" = "whiters")
)
possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists
+ list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c"),
list("name" = "\[O2\]", "icon" = "blue-c"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"),
@@ -19,6 +20,7 @@
list("name" = "\[CAUTION\]", "icon" = "yellow-c")
)
possibletertcolor = list(
+ list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c-1"),
list("name" = "\[O2\]", "icon" = "blue-c-1"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"),
@@ -27,6 +29,7 @@
list("name" = "\[CAUTION\]", "icon" = "yellow-c-1")
)
possiblequartcolor = list(
+ list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c-2"),
list("name" = "\[O2\]", "icon" = "blue-c-2"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"),
@@ -34,12 +37,6 @@
list("name" = "\[Air\]", "icon" = "grey-c-2"),
list("name" = "\[CAUTION\]", "icon" = "yellow-c-2")
)
-
- possibledecals = list( //var that stores all possible decals, used by ui
- list("name" = "Low temperature canister", "icon" = "cold"),
- list("name" = "High temperature canister", "icon" = "hot"),
- list("name" = "Plasma containing canister", "icon" = "plasma")
- )
GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
/obj/machinery/portable_atmospherics/canister
@@ -52,22 +49,17 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
max_integrity = 250
integrity_failure = 100
- var/menu = 0
- //used by nanoui: 0 = main menu, 1 = relabel
-
var/valve_open = 0
var/release_pressure = ONE_ATMOSPHERE
var/list/canister_color //variable that stores colours
- var/list/decals //list that stores the decals
+ var/list/color_index // list which stores tgui color indexes for the recoloring options, to enable previously-set colors to show up right
//lists for check_change()
var/list/oldcolor
- var/list/olddecals
//passed to the ui to render the color lists
var/list/colorcontainer
- var/list/possibledecals
var/can_label = 1
var/filled = 0.5
@@ -82,18 +74,12 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
/obj/machinery/portable_atmospherics/canister/New()
..()
canister_color = list(
- "prim" = "yellow",
- "sec" = "none",
- "ter" = "none",
- "quart" = "none")
+ "prim" = "yellow",
+ "sec" = "none",
+ "ter" = "none",
+ "quart" = "none"
+ )
oldcolor = new /list()
- decals = list("cold" = 0, "hot" = 0, "plasma" = 0)
- colorcontainer = list()
- possibledecals = list()
- update_icon()
-
-/obj/machinery/portable_atmospherics/canister/proc/init_data_vars()
- //passed to the ui to render the color lists
colorcontainer = list(
"prim" = list(
"options" = GLOB.canister_icon_container.possiblemaincolor,
@@ -112,26 +98,8 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
"name" = "Quaternary color",
)
)
-
- //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied
- for(var/C in colorcontainer)
- if(C == "prim") continue
- var/list/L = colorcontainer[C]
- if(!(canister_color[C]) || (canister_color[C] == "none"))
- L.Add(list("anycolor" = 0))
- else
- L.Add(list("anycolor" = 1))
- colorcontainer[C] = L
-
- possibledecals = list()
-
- var/i
- var/list/L = GLOB.canister_icon_container.possibledecals
- for(i=1;i<=L.len;i++)
- var/list/LL = L[i]
- LL = LL.Copy() //make sure we don't edit the datum list
- LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI
- possibledecals.Add(LL)
+ color_index = list()
+ update_icon()
/obj/machinery/portable_atmospherics/canister/proc/check_change()
var/old_flag = update_flag
@@ -155,10 +123,6 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
update_flag |= 64
oldcolor = canister_color.Copy()
- if(list2params(olddecals) != list2params(decals))
- update_flag |= 128
- olddecals = decals.Copy()
-
if(update_flag == old_flag)
return 1
else
@@ -174,17 +138,16 @@ update_flag
16 = tank_pressure < 15*ONE_ATMOS
32 = tank_pressure go boom.
64 = colors
-128 = decals
-(note: colors and decals has to be applied every icon update)
+(note: colors has to be applied every icon update)
*/
- if(src.destroyed)
- src.overlays = 0
- src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever.
+ if(destroyed)
+ overlays = 0
+ icon_state = text("[]-1", canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever.
return
- if(icon_state != src.canister_color["prim"])
- icon_state = src.canister_color["prim"]
+ if(icon_state != canister_color["prim"])
+ icon_state = canister_color["prim"]
if(check_change()) //Returns 1 if no change needed to icons.
return
@@ -192,14 +155,12 @@ update_flag
overlays.Cut()
for(var/C in canister_color)
- if(C == "prim") continue
- if(canister_color[C] == "none") continue
+ if(C == "prim")
+ continue
+ if(canister_color[C] == "none")
+ continue
overlays.Add(canister_color[C])
- for(var/D in decals)
- if(decals[D])
- overlays.Add("decal-" + D)
-
if(update_flag & 1)
overlays += "can-open"
if(update_flag & 2)
@@ -213,35 +174,9 @@ update_flag
else if(update_flag & 32)
overlays += "can-o3"
- update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go.
+ update_flag &= ~68 //the flag 64 represents change, not states. As such, we have to reset them to be able to detect a change on the next go.
return
-//template modification exploit prevention, used in Topic()
-/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all")
- if(checkColor == "prim" || checkColor == "all")
- for(var/list/L in GLOB.canister_icon_container.possiblemaincolor)
- if(L["icon"] == inputVar)
- return 1
- if(checkColor == "sec" || checkColor == "all")
- for(var/list/L in GLOB.canister_icon_container.possibleseccolor)
- if(L["icon"] == inputVar)
- return 1
- if(checkColor == "ter" || checkColor == "all")
- for(var/list/L in GLOB.canister_icon_container.possibletertcolor)
- if(L["icon"] == inputVar)
- return 1
- if(checkColor == "quart" || checkColor == "all")
- for(var/list/L in GLOB.canister_icon_container.possiblequartcolor)
- if(L["icon"] == inputVar)
- return 1
- return 0
-
-/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar)
- for(var/list/L in GLOB.canister_icon_container.possibledecals)
- if(L["icon"] == inputVar)
- return 1
- return 0
-
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
if(exposed_temperature > temperature_resistance)
@@ -271,7 +206,7 @@ update_flag
stat |= BROKEN
density = FALSE
- playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
+ playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
update_icon()
if(holding)
@@ -297,7 +232,7 @@ update_flag
var/transfer_moles = 0
if((air_contents.temperature > 0) && (pressure_delta > 0))
- transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
+ transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
@@ -307,7 +242,7 @@ update_flag
else
loc.assume_air(removed)
air_update_turf()
- src.update_icon()
+ update_icon()
if(air_contents.return_pressure() < 1)
@@ -315,20 +250,20 @@ update_flag
else
can_label = 0
- src.updateDialog()
+ updateDialog()
return
/obj/machinery/portable_atmospherics/canister/return_air()
return air_contents
/obj/machinery/portable_atmospherics/canister/proc/return_temperature()
- var/datum/gas_mixture/GM = src.return_air()
+ var/datum/gas_mixture/GM = return_air()
if(GM && GM.volume>0)
return GM.temperature
return 0
/obj/machinery/portable_atmospherics/canister/proc/return_pressure()
- var/datum/gas_mixture/GM = src.return_air()
+ var/datum/gas_mixture/GM = return_air()
if(GM && GM.volume>0)
return GM.return_pressure()
return 0
@@ -343,142 +278,114 @@ update_flag
else if(valve_open && holding)
investigate_log("[key_name(user)] started a transfer into [holding]. ", "atmos")
-/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob)
- src.add_hiddenprint(user)
- return src.attack_hand(user)
+/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user)
+ add_hiddenprint(user)
+ return attack_hand(user)
-/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob)
- return src.ui_interact(user)
+/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user)
+ return tgui_interact(user)
-/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
- return src.ui_interact(user)
+/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user)
+ return tgui_interact(user)
-/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state)
- if(src.destroyed)
- return
-
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state)
- // open the new ui window
+ ui = new(user, src, ui_key, "Canister", name, 600, 350, master_ui, state)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-
-/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state)
- init_data_vars() //set up var/colorcontainer and var/possibledecals
-
- // this is the data which will be sent to the ui
- var/data[0]
- data["name"] = name
- data["menu"] = menu ? 1 : 0
- data["canLabel"] = can_label ? 1 : 0
- data["canister_color"] = canister_color
- data["colorContainer"] = colorcontainer.Copy()
- colorcontainer.Cut()
- data["possibleDecals"] = possibledecals.Copy()
- possibledecals.Cut()
+/obj/machinery/portable_atmospherics/canister/tgui_data()
+ var/data = list()
data["portConnected"] = connected_port ? 1 : 0
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
- data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
- data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
+ data["defaultReleasePressure"] = ONE_ATMOSPHERE
+ data["minReleasePressure"] = round(ONE_ATMOSPHERE / 10)
+ data["maxReleasePressure"] = round(ONE_ATMOSPHERE * 10)
data["valveOpen"] = valve_open ? 1 : 0
-
+ data["name"] = name
+ data["canLabel"] = can_label ? 1 : 0
+ data["colorContainer"] = colorcontainer.Copy()
+ data["color_index"] = color_index
data["hasHoldingTank"] = holding ? 1 : 0
if(holding)
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
-
return data
-/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)
+/obj/machinery/portable_atmospherics/canister/tgui_act(action, params)
if(..())
- return 1
-
- if(href_list["choice"] == "menu")
- menu = text2num(href_list["mode_target"])
-
- if(href_list["toggle"])
- var/logmsg
- if(valve_open)
- if(holding)
- logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding] "
- else
- logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air "
- else
- if(holding)
- logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding] "
- else
- logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air "
- if(air_contents.toxins > 0)
- message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)")
- log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]")
- var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases
- if(sleeping_agent && (sleeping_agent.moles > 1))
- message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)")
- log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]")
- investigate_log(logmsg, "atmos")
- release_log += logmsg
- valve_open = !valve_open
-
- if(href_list["remove_tank"])
- if(holding)
- if(valve_open)
- valve_open = 0
- release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding] "
- holding.loc = loc
- holding = null
-
- if(href_list["pressure_adj"])
- var/diff = text2num(href_list["pressure_adj"])
- if(diff > 0)
- release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
- else
- release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)
-
- if(href_list["rename"])
- if(can_label)
- var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN))
- if(can_label) //Exploit prevention
- if(T)
- name = T
+ return
+ var/can_min_release_pressure = round(ONE_ATMOSPHERE / 10)
+ var/can_max_release_pressure = round(ONE_ATMOSPHERE * 10)
+ . = TRUE
+ switch(action)
+ if("relabel")
+ if(can_label)
+ var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null, 1, MAX_NAME_LEN))
+ if(can_label) //Exploit prevention
+ if(T)
+ name = T
+ else
+ name = "canister"
else
- name = "canister"
+ to_chat(usr, "As you attempted to rename it the pressure rose!")
+ . = FALSE
+ if("pressure")
+ var/pressure = params["pressure"]
+ if(pressure == "reset")
+ pressure = ONE_ATMOSPHERE
+ else if(pressure == "min")
+ pressure = can_min_release_pressure
+ else if(pressure == "max")
+ pressure = can_max_release_pressure
+ else if(pressure == "input")
+ pressure = input("New release pressure ([can_min_release_pressure]-[can_max_release_pressure] kPa):", name, release_pressure) as num|null
+ if(isnull(pressure))
+ . = FALSE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ if(.)
+ release_pressure = clamp(round(pressure), can_min_release_pressure, can_max_release_pressure)
+ investigate_log("was set to [release_pressure] kPa by [key_name(usr)].", "atmos")
+ if("valve")
+ var/logmsg
+ valve_open = !valve_open
+ if(valve_open)
+ logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the [holding || "air"]. "
+ if(!holding)
+ logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the air. "
+ if(air_contents.toxins > 0)
+ message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)")
+ log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]")
+ if(air_contents.sleeping_agent > 0)
+ message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)")
+ log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]")
else
- to_chat(usr, "As you attempted to rename it the pressure rose!")
-
- if(href_list["choice"] == "Primary color")
- if(is_a_color(href_list["icon"],"prim"))
- canister_color["prim"] = href_list["icon"]
- if(href_list["choice"] == "Secondary color")
- if(href_list["icon"] == "none")
- canister_color["sec"] = "none"
- else if(is_a_color(href_list["icon"],"sec"))
- canister_color["sec"] = href_list["icon"]
- if(href_list["choice"] == "Tertiary color")
- if(href_list["icon"] == "none")
- canister_color["ter"] = "none"
- else if(is_a_color(href_list["icon"],"ter"))
- canister_color["ter"] = href_list["icon"]
- if(href_list["choice"] == "Quaternary color")
- if(href_list["icon"] == "none")
- canister_color["quart"] = "none"
- else if(is_a_color(href_list["icon"],"quart"))
- canister_color["quart"] = href_list["icon"]
-
- if(href_list["choice"] == "decals")
- if(is_a_decal(href_list["icon"]))
- decals[href_list["icon"]] = (decals[href_list["icon"]] == 0)
-
- src.add_fingerprint(usr)
+ logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding || "air"]. "
+ investigate_log(logmsg, "atmos")
+ release_log += logmsg
+ if("eject")
+ if(holding)
+ if(valve_open)
+ if(air_contents && (air_contents.toxins > 0 || air_contents.sleeping_agent > 0))
+ message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the air.")
+ release_log += "[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air "
+ investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air.", "atmos")
+ replace_tank(usr, FALSE)
+ if("recolor")
+ if(can_label)
+ var/ctype = params["ctype"]
+ var/cnum = text2num(params["nc"])
+ if(isnull(colorcontainer[ctype]))
+ message_admins("[key_name_admin(usr)] passed an invalid ctype var to a canister.")
+ return
+ var/newcolor = sanitize_integer(cnum, 0, length(colorcontainer[ctype]["options"]))
+ color_index[ctype] = newcolor
+ newcolor++ // javascript starts arrays at 0, byond (for some reason) starts them at 1, this converts JS values to byond values
+ canister_color[ctype] = colorcontainer[ctype]["options"][newcolor]["icon"]
+ add_fingerprint(usr)
update_icon()
- return 1
-
/obj/machinery/portable_atmospherics/canister/toxins
name = "Canister \[Toxin (Plasma)\]"
@@ -514,64 +421,45 @@ update_flag
..()
canister_color["prim"] = "orange"
- decals["plasma"] = 1
- src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.toxins = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/oxygen/New()
..()
canister_color["prim"] = "blue"
- src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.oxygen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/sleeping_agent/New()
..()
canister_color["prim"] = "redws"
- var/datum/gas/sleeping_agent/trace_gas = new
- air_contents.trace_gases += trace_gas
- trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.sleeping_agent = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
-
-//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0
-/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New()
- ..()
- var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1]
- trace_gas.moles = 9*4000
- spawn(100)
- var/turf/simulated/location = src.loc
- if(istype(src.loc))
- while(!location.air)
- sleep(1000)
- location.assume_air(air_contents)
- air_contents = new
- return 1
-
-
/obj/machinery/portable_atmospherics/canister/nitrogen/New()
..()
canister_color["prim"] = "red"
- src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.nitrogen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New()
..()
canister_color["prim"] = "black"
- src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.carbon_dioxide = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
@@ -579,17 +467,17 @@ update_flag
..()
canister_color["prim"] = "grey"
- src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
- src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ air_contents.oxygen = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
+ air_contents.nitrogen = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- src.update_icon()
+ update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/custom_mix/New()
..()
canister_color["prim"] = "whiters"
- src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD
+ update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD
return 1
/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I)
diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm
index 0144a7f93c2..4d54b162466 100644
--- a/code/game/machinery/atmoalter/pump.dm
+++ b/code/game/machinery/atmoalter/pump.dm
@@ -167,7 +167,7 @@
if(href_list["pressure_adj"])
var/diff = text2num(href_list["pressure_adj"])
- target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax)
+ target_pressure = clamp(target_pressure+diff, pressuremin, pressuremax)
update_icon()
src.add_fingerprint(usr)
diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm
index 36e340d1a12..d7a49e40d4b 100644
--- a/code/game/machinery/atmoalter/scrubber.dm
+++ b/code/game/machinery/atmoalter/scrubber.dm
@@ -81,17 +81,11 @@
filtered_out.carbon_dioxide = removed.carbon_dioxide
removed.carbon_dioxide = 0
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/sleeping_agent))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
+ filtered_out.sleeping_agent = removed.sleeping_agent
+ removed.sleeping_agent = 0
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/oxygen_agent_b))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
+ filtered_out.agent_b = removed.agent_b
+ removed.agent_b = 0
//Remix the resulting gases
air_contents.merge(filtered_out)
@@ -158,7 +152,7 @@
if(href_list["volume_adj"])
var/diff = text2num(href_list["volume_adj"])
- volume_rate = Clamp(volume_rate+diff, minrate, maxrate)
+ volume_rate = clamp(volume_rate+diff, minrate, maxrate)
src.add_fingerprint(usr)
@@ -193,24 +187,24 @@
icon_state = "scrubber:0"
/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
- if(istype(W, /obj/item/wrench))
- if(stationary)
- to_chat(user, "The bolts are too tight for you to unscrew!")
- return
- if(on)
- to_chat(user, "Turn it off first!")
- return
-
- anchored = !anchored
- playsound(loc, W.usesound, 50, 1)
- to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].")
- return
-
if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1)
atmosanalyzer_scan(air_contents, user)
return
return ..()
+/obj/machinery/portable_atmospherics/scrubber/huge/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ if(stationary)
+ to_chat(user, "The bolts are too tight for you to unscrew!")
+ return
+ if(on)
+ to_chat(user, "Turn it off first!")
+ return
+ if(!I.use_tool(src, user, 0, volume = I.tool_volume))
+ return
+ anchored = !anchored
+ to_chat(user, "You [anchored ? "wrench" : "unwrench"] [src].")
+
/obj/machinery/portable_atmospherics/scrubber/huge/stationary
name = "Stationary Air Scrubber"
stationary = 1
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 78367327a0c..3fffc598334 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -24,7 +24,7 @@
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 100
- var/busy = 0
+ var/busy = FALSE
var/prod_coeff
var/datum/wires/autolathe/wires = null
@@ -33,12 +33,12 @@
var/list/datum/design/matching_designs
var/temp_search
var/selected_category
- var/screen = 1
+ var/list/recipiecache = list()
var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported")
/obj/machinery/autolathe/New()
- AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
+ AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
..()
component_parts = list()
component_parts += new /obj/item/circuitboard/autolathe(null)
@@ -65,8 +65,9 @@
RefreshParts()
/obj/machinery/autolathe/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.retrieve_all()
return ..()
@@ -78,67 +79,125 @@
if(panel_open)
wires.Interact(user)
else
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/autolathe/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550)
+ ui = new(user, src, ui_key, "Autolathe", name, 750, 700, master_ui, state)
ui.open()
-/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- GET_COMPONENT(materials, /datum/component/material_container)
- var/data[0]
- data["screen"] = screen
+
+/obj/machinery/autolathe/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["categories"] = categories
+ if(!recipiecache.len)
+ var/list/recipes = list()
+ for(var/v in files.known_designs)
+ var/datum/design/D = files.known_designs[v]
+ var/list/cost_list = design_cost_data(D)
+ var/list/matreq = list()
+ for(var/list/x in cost_list)
+ if(!x["amount"])
+ continue
+ if(x["name"] == "metal") // Do not use MAT_METAL or MAT_GLASS here.
+ matreq["metal"] = x["amount"]
+ if(x["name"] == "glass")
+ matreq["glass"] = x["amount"]
+ var/obj/item/I = D.build_path
+ var/maxmult = 1
+ if(ispath(D.build_path, /obj/item/stack))
+ maxmult = D.maxstack
+ recipes.Add(list(list(
+ "name" = D.name,
+ "category" = D.category,
+ "uid" = D.UID(),
+ "requirements" = matreq,
+ "hacked" = ("hacked" in D.category) ? TRUE : FALSE,
+ "max_multiplier" = maxmult,
+ "image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]"
+ )))
+ recipiecache = recipes
+ data["recipes"] = recipiecache
+ return data
+
+/obj/machinery/autolathe/tgui_data(mob/user)
+ var/list/data = list() //..()
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
data["total_amount"] = materials.total_amount
data["max_amount"] = materials.max_amount
+ data["fill_percent"] = round((materials.total_amount / materials.max_amount) * 100)
data["metal_amount"] = materials.amount(MAT_METAL)
data["glass_amount"] = materials.amount(MAT_GLASS)
- switch(screen)
- if(AUTOLATHE_MAIN_MENU)
- data["uid"] = UID()
- data["categories"] = categories
- if(AUTOLATHE_CATEGORY_MENU)
- data["selected_category"] = selected_category
- var/list/designs = list()
- data["designs"] = designs
- for(var/v in files.known_designs)
- var/datum/design/D = files.known_designs[v]
- if(!(selected_category in D.category))
- continue
- var/list/design = list()
- designs[++designs.len] = design
- design["name"] = D.name
- design["id"] = D.id
- design["disabled"] = disabled || !can_build(D) ? "disabled" : null
- if(ispath(D.build_path, /obj/item/stack))
- design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY)
- else
- design["max_multiplier"] = null
- design["materials"] = design_cost_data(D)
- if(AUTOLATHE_SEARCH_MENU)
- data["search"] = temp_search
- var/list/designs = list()
- data["designs"] = designs
- for(var/datum/design/D in matching_designs)
- var/list/design = list()
- designs[++designs.len] = design
- design["name"] = D.name
- design["id"] = D.id
- design["disabled"] = disabled || !can_build(D) ? "disabled" : null
- if(ispath(D.build_path, /obj/item/stack))
- design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY)
- else
- design["max_multiplier"] = null
- design["materials"] = design_cost_data(D)
-
- data = queue_data(data)
+ data["busyname"] = FALSE
+ data["busyamt"] = 1
+ if(length(being_built) > 0)
+ var/datum/design/D = being_built[1]
+ data["busyname"] = istype(D) && D.name ? D.name : FALSE
+ data["busyamt"] = length(being_built) > 1 ? being_built[2] : 1
+ data["showhacked"] = hacked ? TRUE : FALSE
+ data["buildQueue"] = queue
+ data["buildQueueLen"] = queue.len
return data
+/obj/machinery/autolathe/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return FALSE
+
+ add_fingerprint(usr)
+
+ . = TRUE
+ switch(action)
+ if("clear_queue")
+ queue = list()
+ if("remove_from_queue")
+ var/index = text2num(params["remove_from_queue"])
+ if(isnum(index) && ISINRANGE(index, 1, queue.len))
+ remove_from_queue(index)
+ to_chat(usr, "Removed item from queue.")
+ if("make")
+ BuildTurf = loc
+ var/datum/design/design_last_ordered
+ design_last_ordered = locateUID(params["make"])
+ if(!istype(design_last_ordered))
+ to_chat(usr, "Invalid design")
+ return
+ if(!(design_last_ordered.build_type & AUTOLATHE))
+ to_chat(usr, "Invalid design (not buildable in autolathe, report this error.)")
+ return
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ if(design_last_ordered.materials["$metal"] > materials.amount(MAT_METAL))
+ to_chat(usr, "Invalid design (not enough metal)")
+ return
+ if(design_last_ordered.materials["$glass"] > materials.amount(MAT_GLASS))
+ to_chat(usr, "Invalid design (not enough glass)")
+ return
+ if(!hacked && ("hacked" in design_last_ordered.category))
+ to_chat(usr, "Invalid design (lathe requires hacking)")
+ return
+ //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier
+ var/multiplier = text2num(params["multiplier"])
+ var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY)
+ var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack)
+
+ if(!is_stack && (multiplier > 1))
+ return
+ if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc
+ message_admins("Player [key_name_admin(usr)] attempted to pass invalid multiplier [multiplier] to an autolathe in tgui_act. Possible href exploit.")
+ return
+ if((queue.len + 1) < queue_max_len)
+ add_to_queue(design_last_ordered, multiplier)
+ else
+ to_chat(usr, "The autolathe queue is full!")
+ if(!busy)
+ busy = TRUE
+ process_queue()
+ busy = FALSE
+
/obj/machinery/autolathe/proc/design_cost_data(datum/design/D)
var/list/data = list()
var/coeff = get_coeff(D)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/has_metal = 1
if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff)))
has_metal = 0
@@ -152,7 +211,7 @@
return data
/obj/machinery/autolathe/proc/queue_data(list/data)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/temp_metal = materials.amount(MAT_METAL)
var/temp_glass = materials.amount(MAT_GLASS)
data["processing"] = being_built.len ? get_processing_line() : null
@@ -184,14 +243,21 @@
if(istype(O, /obj/item/disk/design_disk))
var/obj/item/disk/design_disk/D = O
if(D.blueprint)
+ if(!(D.blueprint.build_type & AUTOLATHE)) // otherwise, would silently fail in AddDesign2Known
+ to_chat(user, "This design is not compatible with the autolathe.")
+ return 1
user.visible_message("[user] begins to load \the [O] in \the [src]...",
"You begin to load a design from \the [O]...",
"You hear the chatter of a floppy drive.")
playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
- busy = 1
+ busy = TRUE
if(do_after(user, 14.4, target = src))
+ if(!("Imported" in D.blueprint.category)) // R&D should always ensure this is set on design disks, but it doesn't.
+ D.blueprint.category += "Imported" // now it will actually show up in the list.
files.AddDesign2Known(D.blueprint)
- busy = 0
+ recipiecache = list()
+ SStgui.close_uis(src) // forces all connected users to re-open the TGUI. Imported entries won't show otherwise due to static_data
+ busy = FALSE
else
to_chat(user, "That disk does not have a design on it!")
return 1
@@ -221,7 +287,6 @@
to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.")
return
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I))
- SSnanoui.update_uis(src)
I.play_tool_sound(user, I.tool_volume)
/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I)
@@ -253,7 +318,7 @@
if(MAT_GLASS)
flick("autolathe_r", src)//plays glass insertion animation
use_power(min(1000, amount_inserted / 100))
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/autolathe/attack_ghost(mob/user)
interact(user)
@@ -263,86 +328,13 @@
return
interact(user)
-/obj/machinery/autolathe/Topic(href, href_list)
- if(..())
- return 1
-
- if(href_list["menu"])
- screen = text2num(href_list["menu"])
-
- if(href_list["category"])
- selected_category = href_list["category"]
- screen = AUTOLATHE_CATEGORY_MENU
-
- if(href_list["make"])
- BuildTurf = loc
-
- /////////////////
- //href protection
- var/datum/design/design_last_ordered
- design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design
- if(!design_last_ordered)
- return
- if(!(design_last_ordered.build_type & AUTOLATHE))
- return
-
- //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier
- var/multiplier = text2num(href_list["multiplier"])
- GET_COMPONENT(materials, /datum/component/material_container)
- var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY)
- var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack)
-
- if(!is_stack && (multiplier > 1))
- return
- if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc
- return
- /////////////////
-
- if((queue.len + 1) < queue_max_len)
- add_to_queue(design_last_ordered,multiplier)
- else
- to_chat(usr, "The autolathe queue is full!")
- if(!busy)
- busy = 1
- process_queue()
- busy = 0
-
- if(href_list["remove_from_queue"])
- var/index = text2num(href_list["remove_from_queue"])
- if(isnum(index) && IsInRange(index, 1, queue.len))
- remove_from_queue(index)
- if(href_list["queue_move"] && href_list["index"])
- var/index = text2num(href_list["index"])
- var/new_index = index + text2num(href_list["queue_move"])
- if(isnum(index) && isnum(new_index))
- if(IsInRange(new_index, 1, queue.len))
- queue.Swap(index,new_index)
- if(href_list["clear_queue"])
- queue = list()
- if(href_list["search"])
- if(href_list["to_search"])
- temp_search = href_list["to_search"]
- if(!temp_search)
- return
- matching_designs.Cut()
-
- for(var/v in files.known_designs)
- var/datum/design/D = files.known_designs[v]
- if(findtext(D.name, temp_search))
- matching_designs.Add(D)
-
- screen = AUTOLATHE_SEARCH_MENU
-
- SSnanoui.update_uis(src)
- return 1
-
/obj/machinery/autolathe/RefreshParts()
var/tot_rating = 0
prod_coeff = 0
for(var/obj/item/stock_parts/matter_bin/MB in component_parts)
tot_rating += MB.rating
tot_rating *= 25000
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.max_amount = tot_rating * 3
for(var/obj/item/stock_parts/manipulator/M in component_parts)
prod_coeff += M.rating - 1
@@ -355,7 +347,7 @@
desc = initial(desc)+"\nIt's building \a [initial(D.name)]."
var/is_stack = ispath(D.build_path, /obj/item/stack)
var/coeff = get_coeff(D)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/metal_cost = D.materials[MAT_METAL]
var/glass_cost = D.materials[MAT_GLASS]
var/power = max(2000, (metal_cost+glass_cost)*multiplier/5)
@@ -370,7 +362,7 @@
else
var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff)
materials.use_amount(materials_used)
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
sleep(32/coeff)
if(is_stack)
var/obj/item/stack/S = new D.build_path(BuildTurf)
@@ -379,7 +371,7 @@
var/obj/item/new_item = new D.build_path(BuildTurf)
new_item.materials[MAT_METAL] /= coeff
new_item.materials[MAT_GLASS] /= coeff
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
desc = initial(desc)
/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass)
@@ -387,7 +379,7 @@
return 0
var/coeff = get_coeff(D)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/metal_amount = materials.amount(MAT_METAL)
if(custom_metal)
metal_amount = custom_metal
@@ -454,7 +446,6 @@
D = listgetindex(listgetindex(queue, 1),1)
multiplier = listgetindex(listgetindex(queue,1),2)
being_built = new /list()
- //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"")
/obj/machinery/autolathe/proc/adjust_hacked(hack)
hacked = hack
@@ -467,3 +458,17 @@
for(var/datum/design/D in files.known_designs)
if("hacked" in D.category)
files.known_designs -= D.id
+ SStgui.close_uis(src) // forces all connected users to re-open the TGUI, thus adding/removing hacked entries from lists
+ recipiecache = list()
+
+/obj/machinery/autolathe/proc/check_hacked_callback()
+ if(!wires.is_cut(WIRE_AUTOLATHE_HACK))
+ adjust_hacked(FALSE)
+
+/obj/machinery/autolathe/proc/check_electrified_callback()
+ if(!wires.is_cut(WIRE_ELECTRIFY))
+ shocked = FALSE
+
+/obj/machinery/autolathe/proc/check_disabled_callback()
+ if(!wires.is_cut(WIRE_AUTOLATHE_DISABLE))
+ disabled = FALSE
diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm
index 796d5c552dd..5ec4206734c 100644
--- a/code/game/machinery/buttons.dm
+++ b/code/game/machinery/buttons.dm
@@ -189,12 +189,12 @@
active = 1
icon_state = "launcheract"
- for(var/obj/machinery/sparker/M in world)
+ for(var/obj/machinery/sparker/M in GLOB.machines)
if(M.id == id)
spawn( 0 )
M.spark()
- for(var/obj/machinery/igniter/M in world)
+ for(var/obj/machinery/igniter/M in GLOB.machines)
if(M.id == id)
use_power(50)
M.on = !( M.on )
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 24ffb494422..4e185feb4a8 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -28,13 +28,14 @@
var/view_range = 7
var/short_range = 2
+ var/alarm_on = FALSE
var/busy = FALSE
var/emped = FALSE //Number of consecutive EMP's on this camera
var/in_use_lights = 0 // TO BE IMPLEMENTED
var/toggle_sound = 'sound/items/wirecutter.ogg'
-/obj/machinery/camera/Initialize()
+/obj/machinery/camera/Initialize(mapload)
. = ..()
wires = new(src)
assembly = new(src)
@@ -44,11 +45,17 @@
GLOB.cameranet.cameras += src
GLOB.cameranet.addCamera(src)
+ if(isturf(loc))
+ LAZYADD(myArea.cameras, UID())
if(is_station_level(z) && prob(3) && !start_active)
toggle_cam(null, FALSE)
- wires.CutAll()
+ wires.cut_all()
+
+/obj/machinery/camera/proc/set_area_motion(area/A)
+ area_motion = A
/obj/machinery/camera/Destroy()
+ SStgui.close_uis(wires)
toggle_cam(null, FALSE) //kick anyone viewing out
QDEL_NULL(assembly)
if(istype(bug))
@@ -59,10 +66,14 @@
QDEL_NULL(wires)
GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
GLOB.cameranet.cameras -= src
+ if(isarea(myArea))
+ LAZYREMOVE(myArea.cameras, UID())
var/area/ai_monitored/A = get_area(src)
if(istype(A))
- A.motioncamera = null
+ A.motioncameras -= src
area_motion = null
+ cancelCameraAlarm()
+ cancelAlarm()
return ..()
/obj/machinery/camera/emp_act(severity)
@@ -252,7 +263,7 @@
if(status && !(flags & NODECONSTRUCT))
triggerCameraAlarm()
toggle_cam(null, FALSE)
- wires.CutAll()
+ wires.cut_all()
/obj/machinery/camera/deconstruct(disassembled = TRUE)
if(!(flags & NODECONSTRUCT))
@@ -282,9 +293,16 @@
status = !status
if(can_use())
GLOB.cameranet.addCamera(src)
+ if(isturf(loc))
+ myArea = get_area(src)
+ LAZYADD(myArea.cameras, UID())
+ else
+ myArea = null
else
set_light(0)
GLOB.cameranet.removeCamera(src)
+ if(isarea(myArea))
+ LAZYREMOVE(myArea.cameras, UID())
GLOB.cameranet.updateChunk(x, y, z)
var/change_msg = "deactivates"
if(status)
@@ -313,12 +331,12 @@
to_chat(O, "The screen bursts into static.")
/obj/machinery/camera/proc/triggerCameraAlarm()
- if(is_station_contact(z))
- SSalarms.camera_alarm.triggerAlarm(loc, src)
+ alarm_on = TRUE
+ SSalarm.triggerAlarm("Camera", get_area(src), list(UID()), src)
/obj/machinery/camera/proc/cancelCameraAlarm()
- if(is_station_contact(z))
- SSalarms.camera_alarm.clearAlarm(loc, src)
+ alarm_on = FALSE
+ SSalarm.cancelAlarm("Camera", get_area(src), src)
/obj/machinery/camera/proc/can_use()
if(!status)
@@ -360,15 +378,12 @@
for(var/obj/machinery/camera/C in oview(4, M))
if(C.can_use()) // check if camera disabled
return C
- break
return null
/proc/near_range_camera(mob/M)
for(var/obj/machinery/camera/C in range(4, M))
if(C.can_use()) // check if camera disabled
return C
- break
-
return null
/obj/machinery/camera/proc/Togglelight(on = FALSE)
@@ -417,8 +432,8 @@
/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets.
var/turf/prev_turf
-/obj/machinery/camera/portable/New()
- ..()
+/obj/machinery/camera/portable/Initialize(mapload)
+ . = ..()
assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed.
assembly.anchored = 0
assembly.update_icon()
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index 16fb6eca204..7d33361e4b9 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -1,60 +1,60 @@
/obj/machinery/camera
-
- var/list/motionTargets = list()
+ var/list/localMotionTargets = list()
var/detectTime = 0
var/area/ai_monitored/area_motion = null
- var/alarm_delay = 100
-
+ var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm()
/obj/machinery/camera/process()
// motion camera event loop
- if(stat & (EMPED|NOPOWER))
- return
if(!isMotion())
. = PROCESS_KILL
return
+ if(stat & (EMPED|NOPOWER))
+ return
if(detectTime > 0)
var/elapsed = world.time - detectTime
if(elapsed > alarm_delay)
triggerAlarm()
else if(detectTime == -1)
- for(var/mob/target in motionTargets)
- if(target.stat == 2) lostTarget(target)
- // If not detecting with motion camera...
- if(!area_motion)
- // See if the camera is still in range
- if(!in_range(src, target))
- // If they aren't in range, lose the target.
- lostTarget(target)
+ for(var/thing in getTargetList())
+ var/mob/target = locateUID(thing)
+ if(QDELETED(target) || target.stat == DEAD || (!area_motion && !in_range(src, target)))
+ //If not part of a monitored area and the camera is not in range or the target is dead
+ lostTargetRef(thing)
-/obj/machinery/camera/proc/newTarget(var/mob/target)
- if(istype(target, /mob/living/silicon/ai)) return 0
+/obj/machinery/camera/proc/getTargetList()
+ if(area_motion)
+ return area_motion.motionTargets
+ return localMotionTargets
+
+/obj/machinery/camera/proc/newTarget(mob/target)
+ if(isAI(target))
+ return FALSE
if(detectTime == 0)
detectTime = world.time // start the clock
- if(!(target in motionTargets))
- motionTargets += target
- return 1
+ var/list/targets = getTargetList()
+ targets |= target.UID()
+ return TRUE
-/obj/machinery/camera/proc/lostTarget(var/mob/target)
- if(target in motionTargets)
- motionTargets -= target
- if(motionTargets.len == 0)
+/obj/machinery/camera/proc/lostTargetRef(uid)
+ var/list/targets = getTargetList()
+ targets -= uid
+ if(length(targets))
cancelAlarm()
/obj/machinery/camera/proc/cancelAlarm()
- if(!status || (stat & NOPOWER))
- return FALSE
- if(detectTime == -1 && is_station_contact(z))
- SSalarms.motion_alarm.clearAlarm(loc, src)
+ if(detectTime == -1)
+ if(status)
+ SSalarm.cancelAlarm("Motion", get_area(src), src)
detectTime = 0
return TRUE
/obj/machinery/camera/proc/triggerAlarm()
- if(!status || (stat & NOPOWER))
+ if(!detectTime)
return FALSE
- if(!detectTime || !is_station_contact(z))
- return FALSE
- SSalarms.motion_alarm.triggerAlarm(loc, src)
+ if(status)
+ SSalarm.triggerAlarm("Motion", get_area(src), list(UID()), src)
+ visible_message("A red light flashes on the [src]!")
detectTime = -1
return TRUE
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index c0949fb3c9d..33c970639e5 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -2,7 +2,7 @@
// EMP
-/obj/machinery/camera/emp_proof/Initialize()
+/obj/machinery/camera/emp_proof/Initialize(mapload)
. = ..()
upgradeEmpProof()
@@ -11,19 +11,23 @@
/obj/machinery/camera/xray
icon_state = "xraycam" // Thanks to Krutchen for the icons.
-/obj/machinery/camera/xray/Initialize()
+/obj/machinery/camera/xray/Initialize(mapload)
. = ..()
upgradeXRay()
// MOTION
+/obj/machinery/camera/motion
+ name = "motion-sensitive security camera"
-/obj/machinery/camera/motion/Initialize()
+/obj/machinery/camera/motion/Initialize(mapload)
. = ..()
upgradeMotion()
// ALL UPGRADES
+/obj/machinery/camera/all
+ icon_state = "xraycamera" //mapping icon.
-/obj/machinery/camera/all/Initialize()
+/obj/machinery/camera/all/Initialize(mapload)
. = ..()
upgradeEmpProof()
upgradeXRay()
@@ -41,7 +45,7 @@
number = 1
var/area/A = get_area(src)
if(A)
- for(var/obj/machinery/camera/autoname/C in world)
+ for(var/obj/machinery/camera/autoname/C in GLOB.machines)
if(C == src) continue
var/area/CA = get_area(C)
if(CA.type == A.type)
@@ -78,6 +82,10 @@
// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list.
/obj/machinery/camera/proc/upgradeMotion()
+ if(isMotion())
+ return
+ if(name == initial(name))
+ name = "motion-sensitive security camera"
assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly))
setPowerUsage()
// Add it to machines that process
diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm
index cb5e4da5e95..30538390e8d 100644
--- a/code/game/machinery/computer/HolodeckControl.dm
+++ b/code/game/machinery/computer/HolodeckControl.dm
@@ -346,6 +346,22 @@
return
// HOLOFLOOR DOES NOT GIVE A FUCK
+/turf/simulated/floor/holofloor/space
+ name = "\proper space"
+ icon = 'icons/turf/space.dmi'
+ icon_state = "0"
+ plane = PLANE_SPACE
+
+/turf/simulated/floor/holofloor/space/Initialize(mapload)
+ icon_state = SPACE_ICON_STATE // so realistic
+ . = ..()
+
+/turf/simulated/floor/holofloor/space/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
+ underlay_appearance.icon = 'icons/turf/space.dmi'
+ underlay_appearance.icon_state = SPACE_ICON_STATE
+ underlay_appearance.plane = PLANE_SPACE
+ return TRUE
+
/obj/structure/table/holotable
flags = NODECONSTRUCT
canSmoothWith = list(/obj/structure/table/holotable)
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 21fddce64b7..1b083bd9c87 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -40,8 +40,7 @@
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
-
+ tgui_interact(user)
/obj/machinery/computer/operating/attack_hand(mob/user)
if(..(user))
@@ -52,16 +51,15 @@
add_fingerprint(user)
- ui_interact(user)
+ tgui_interact(user)
-/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/operating/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/computer/operating/tgui_data(mob/user)
var/data[0]
var/mob/living/carbon/human/occupant
if(table)
@@ -136,38 +134,43 @@
return data
-/obj/machinery/computer/operating/Topic(href, href_list)
+/obj/machinery/computer/operating/tgui_act(action, params)
if(..())
- return 1
+ return
+ if(stat & (NOPOWER|BROKEN))
+ return
+
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
- if(href_list["verboseOn"])
- verbose=1
- if(href_list["verboseOff"])
- verbose=0
- if(href_list["healthOn"])
- healthAnnounce=1
- if(href_list["healthOff"])
- healthAnnounce=0
- if(href_list["critOn"])
- crit=1
- if(href_list["critOff"])
- crit=0
- if(href_list["oxyOn"])
- oxy=1
- if(href_list["oxyOff"])
- oxy=0
- if(href_list["oxy_adj"]!=0)
- oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"])
- if(href_list["choiceOn"])
- choice=1
- if(href_list["choiceOff"])
- choice=0
- if(href_list["health_adj"]!=0)
- healthAlarm=healthAlarm+text2num(href_list["health_adj"])
- return
-
+ . = TRUE
+ switch(action)
+ if("verboseOn")
+ verbose = TRUE
+ if("verboseOff")
+ verbose = FALSE
+ if("healthOn")
+ healthAnnounce = TRUE
+ if("healthOff")
+ healthAnnounce = FALSE
+ if("critOn")
+ crit = TRUE
+ if("critOff")
+ crit = FALSE
+ if("oxyOn")
+ oxy = TRUE
+ if("oxyOff")
+ oxy = FALSE
+ if("oxy_adj")
+ oxyAlarm = clamp(text2num(params["new"]), -100, 100)
+ if("choiceOn")
+ choice = TRUE
+ if("choiceOff")
+ choice = FALSE
+ if("health_adj")
+ healthAlarm = clamp(text2num(params["new"]), -100, 100)
+ else
+ return FALSE
/obj/machinery/computer/operating/process()
@@ -178,6 +181,7 @@
atom_say("New patient detected, loading stats")
victim = table.victim
atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]")
+ SStgui.update_uis(src)
if(nextTick < world.time)
nextTick=world.time + OP_COMPUTER_COOLDOWN
if(crit && victim.health <= -50 )
diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm
index ba139e50855..44b55ed1d2c 100644
--- a/code/game/machinery/computer/ai_core.dm
+++ b/code/game/machinery/computer/ai_core.dm
@@ -153,6 +153,9 @@
to_chat(user, "You screw the circuit board into place.")
state = SCREWED_CORE
if(GLASS_CORE)
+ var/area/R = get_area(src)
+ message_admins("[key_name_admin(usr)] has completed an AI core in [R]: [ADMIN_COORDJMP(loc)].")
+ log_game("[key_name(usr)] has completed an AI core in [R]: [COORD(loc)].")
to_chat(user, "You connect the monitor.")
if(!brain)
var/open_for_latejoin = alert(user, "Would you like this core to be open for latejoining AIs?", "Latejoin", "Yes", "Yes", "No") == "Yes"
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index 0d715fe5759..38ec50abc6e 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -22,23 +22,22 @@
return ..()
/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/aifixer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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)
+ ui = new(user, src, ui_key, "AIFixer", name, 550, 500, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state)
+/obj/machinery/computer/aifixer/tgui_data(mob/user, datum/topic_state/state)
var/data[0]
+ data["occupant"] = (occupant ? occupant.name : null) // a null occupant isn't passed on if this is below the if.
if(occupant)
- data["occupant"] = occupant.name
data["reference"] = "\ref[occupant]"
data["integrity"] = (occupant.health+100)/2
data["stat"] = occupant.stat
@@ -48,45 +47,49 @@
var/laws[0]
for(var/datum/ai_law/law in occupant.laws.all_laws())
- laws.Add(list(list("law" = law.law, "number" = law.get_index())))
-
+ if(law in occupant.laws.ion_laws) // If we're an ion law, give it an ion index code
+ laws.Add(ionnum() + ". " + law.law)
+ else
+ laws.Add(num2text(law.get_index()) + ". " + law.law)
data["laws"] = laws
+ data["has_laws"] = length(laws)
return data
-/obj/machinery/computer/aifixer/Topic(href, href_list)
+/obj/machinery/computer/aifixer/tgui_act(action, params)
if(..())
- return 1
+ return
+ switch(action)
+ if("fix")
+ if(active) // Prevent from starting a fix while fixing.
+ to_chat(usr, "You are already fixing this AI!")
+ return
+ active = TRUE
+ INVOKE_ASYNC(src, .proc/fix_ai)
+ add_fingerprint(usr)
- if(href_list["fix"])
- active = 1
- while(occupant.health < 100)
- occupant.adjustOxyLoss(-1, FALSE)
- occupant.adjustFireLoss(-1, FALSE)
- occupant.adjustToxLoss(-1, FALSE)
- occupant.adjustBruteLoss(-1, FALSE)
- occupant.updatehealth()
- if(occupant.health >= 0 && occupant.stat == DEAD)
- occupant.update_revive()
- occupant.lying = 0
- update_icon()
- sleep(10)
- active = 0
- add_fingerprint(usr)
+ if("wireless")
+ occupant.control_disabled = !occupant.control_disabled
- if(href_list["wireless"])
- var/wireless = text2num(href_list["wireless"])
- if(wireless == 0 || wireless == 1)
- occupant.control_disabled = wireless
+ if("radio")
+ occupant.aiRadio.disabledAi = !occupant.aiRadio.disabledAi
- if(href_list["radio"])
- var/radio = text2num(href_list["radio"])
- if(radio == 0 || radio == 1)
- occupant.aiRadio.disabledAi = radio
-
- SSnanoui.update_uis(src)
update_icon()
- return
+ return TRUE
+
+/obj/machinery/computer/aifixer/proc/fix_ai() // Can we fix it? Probrably.
+ while(occupant.health < 100)
+ occupant.adjustOxyLoss(-1, FALSE)
+ occupant.adjustFireLoss(-1, FALSE)
+ occupant.adjustToxLoss(-1, FALSE)
+ occupant.adjustBruteLoss(-1, FALSE)
+ occupant.updatehealth()
+ if(occupant.health >= 0 && occupant.stat == DEAD)
+ occupant.update_revive()
+ occupant.lying = FALSE
+ update_icon()
+ sleep(10)
+ active = FALSE
/obj/machinery/computer/aifixer/update_icon()
..()
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 123cee3ae4e..0be4aa0449b 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -1,84 +1,90 @@
-GLOBAL_LIST_EMPTY(priority_air_alarms)
-GLOBAL_LIST_EMPTY(minor_air_alarms)
-
-
/obj/machinery/computer/atmos_alert
name = "atmospheric alert computer"
desc = "Used to access the station's atmospheric sensors."
circuit = /obj/item/circuitboard/atmos_alert
+ var/ui_x = 350
+ var/ui_y = 300
icon_keyboard = "atmos_key"
icon_screen = "alert:0"
light_color = LIGHT_COLOR_CYAN
+ var/list/priority_alarms = list()
+ var/list/minor_alarms = list()
+ var/receive_frequency = ATMOS_FIRE_FREQ
+ var/datum/radio_frequency/radio_connection
-/obj/machinery/computer/atmos_alert/New()
- ..()
- SSalarms.atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/.proc/update_icon)
+/obj/machinery/computer/atmos_alert/Initialize(mapload)
+ . = ..()
+ set_frequency(receive_frequency)
/obj/machinery/computer/atmos_alert/Destroy()
- SSalarms.atmosphere_alarm.unregister(src)
- return ..()
+ SSradio.remove_object(src, receive_frequency)
+ return ..()
/obj/machinery/computer/atmos_alert/attack_hand(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/atmos_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "AtmosAlertConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/atmos_alert/ui_data(mob/user, datum/topic_state/state)
- var/data[0]
- var/major_alarms[0]
- var/minor_alarms[0]
+/obj/machinery/computer/atmos_alert/tgui_data(mob/user)
+ var/list/data = list()
- for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.major_alarms())
- major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
-
- for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.minor_alarms())
- minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
-
- data["priority_alarms"] = major_alarms
- data["minor_alarms"] = minor_alarms
+ data["priority"] = list()
+ for(var/zone in priority_alarms)
+ data["priority"] |= zone
+ data["minor"] = list()
+ for(var/zone in minor_alarms)
+ data["minor"] |= zone
return data
-/obj/machinery/computer/atmos_alert/update_icon()
- var/list/alarms = SSalarms.atmosphere_alarm.major_alarms()
- if(alarms.len)
- icon_screen = "alert:2"
- else
- alarms = SSalarms.atmosphere_alarm.minor_alarms()
- if(alarms.len)
- icon_screen = "alert:1"
- else
- icon_screen = "alert:0"
- ..()
-
-/obj/machinery/computer/atmos_alert/Topic(href, href_list)
+/obj/machinery/computer/atmos_alert/tgui_act(action, params)
if(..())
- return 1
+ return
+ switch(action)
+ if("clear")
+ var/zone = params["zone"]
+ if(zone in priority_alarms)
+ to_chat(usr, "Priority alarm for [zone] cleared.")
+ priority_alarms -= zone
+ . = TRUE
+ if(zone in minor_alarms)
+ to_chat(usr, "Minor alarm for [zone] cleared.")
+ minor_alarms -= zone
+ . = TRUE
+ update_icon()
- if(href_list["clear_alarm"])
- var/datum/alarm/alarm = locate(href_list["clear_alarm"]) in SSalarms.atmosphere_alarm.alarms
- if(alarm)
- for(var/datum/alarm_source/alarm_source in alarm.sources)
- var/obj/machinery/alarm/air_alarm = alarm_source.source
- if(istype(air_alarm))
- var/list/new_ref = list("atmos_reset" = 1)
- air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic)
- update_icon()
- return 1
+/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, receive_frequency)
+ receive_frequency = new_frequency
+ radio_connection = SSradio.add_object(src, receive_frequency, RADIO_ATMOSIA)
-GLOBAL_DATUM_INIT(air_alarm_topic, /datum/topic_state/air_alarm_topic, new)
+/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal)
+ if(!signal)
+ return
-/datum/topic_state/air_alarm_topic/href_list(var/mob/user)
- var/list/extra_href = list()
- extra_href["remote_connection"] = 1
- extra_href["remote_access"] = 1
+ var/zone = signal.data["zone"]
+ var/severity = signal.data["alert"]
- return extra_href
+ if(!zone || !severity)
+ return
-/datum/topic_state/air_alarm_topic/can_use_topic(var/src_object, var/mob/user)
- return STATUS_INTERACTIVE
+ minor_alarms -= zone
+ priority_alarms -= zone
+ if(severity == "severe")
+ priority_alarms += zone
+ else if(severity == "minor")
+ minor_alarms += zone
+ update_icon()
+
+/obj/machinery/computer/atmos_alert/update_icon()
+ if(length(priority_alarms))
+ icon_screen = "alert:2"
+ else if(length(minor_alarms))
+ icon_screen = "alert:1"
+ else
+ icon_screen = "alert:0"
+ ..()
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index adc4175a45d..e91e631cc62 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -45,7 +45,6 @@
var/list/req_components = null
var/powernet = null
var/list/records = null
- var/frame_desc = null
var/contain_parts = 1
toolspeed = 1
usesound = 'sound/items/deconstruct.ogg'
@@ -64,7 +63,7 @@
var/atom/A = B
if(!ispath(A))
continue
- nice_list += list("[req_components[A]] [initial(A.name)]")
+ nice_list += list("[req_components[A]] [initial(A.name)]\s")
. += "Required components: [english_list(nice_list)]."
/obj/item/circuitboard/message_monitor
@@ -166,12 +165,9 @@
/obj/item/circuitboard/stationalert_engineering
name = "Circuit Board (Station Alert Console (Engineering))"
build_path = /obj/machinery/computer/station_alert
-/obj/item/circuitboard/stationalert_security
- name = "Circuit Board (Station Alert Console (Security))"
+/obj/item/circuitboard/stationalert
+ name = "Circuit Board (Station Alert Console)"
build_path = /obj/machinery/computer/station_alert
-/obj/item/circuitboard/stationalert_all
- name = "Circuit Board (Station Alert Console (All))"
- build_path = /obj/machinery/computer/station_alert/all
/obj/item/circuitboard/atmos_alert
name = "Circuit Board (Atmospheric Alert Computer)"
build_path = /obj/machinery/computer/atmos_alert
@@ -237,7 +233,10 @@
/obj/item/circuitboard/brigcells
name = "Circuit board (Brig Cell Control)"
build_path = /obj/machinery/computer/brigcells
-
+/obj/item/circuitboard/sm_monitor
+ name = "Circuit board (Supermatter Monitoring Console)"
+ build_path = /obj/machinery/computer/sm_monitor
+ origin_tech = "programming=2;powerstorage=2"
// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever
/obj/item/circuitboard/rdconsole
@@ -351,9 +350,6 @@
build_path = /obj/machinery/computer/telescience
origin_tech = "programming=3;bluespace=3;plasmatech=4"
-/obj/item/circuitboard/atmos_automation
- name = "Circuit board (Atmospherics Automation)"
- build_path = /obj/machinery/computer/general_air_control/atmos_automation
/obj/item/circuitboard/large_tank_control
name = "Circuit board (Atmospheric Tank Control)"
build_path = /obj/machinery/computer/general_air_control/large_tank_control
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 40005f8fd12..98f56feeb6a 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -25,7 +25,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/job/ntnavyofficer,
/datum/job/ntspecops,
/datum/job/civilian,
- /datum/job/syndicateofficer
+ /datum/job/syndicateofficer,
+ /datum/job/explorer // blacklisted so that HOPs don't try prioritizing it, then wonder why that doesn't work
)
// Jobs that appear in the list, and you can prioritize, but not open/close slots for
var/list/blacklisted_partial = list(
@@ -51,7 +52,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
//This is used to keep track of opened positions for jobs to allow instant closing
//Assoc array: "JobName" = (int)
- var/list/opened_positions = list();
+ var/list/opened_positions = list()
/obj/machinery/computer/card/proc/is_centcom()
return istype(src, /obj/machinery/computer/card/centcom)
@@ -389,7 +390,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(t1)))
return 0
if(t1 == "Custom")
- var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN))
+ var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE))
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t && modify)
SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name)
@@ -418,7 +419,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name)
- SSjobs.slot_job_transfer(modify.rank, t1)
+ if(modify.owner_uid)
+ SSjobs.slot_job_transfer(modify.rank, t1)
var/mob/living/carbon/human/H = modify.getPlayer()
if(istype(H))
@@ -435,7 +437,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/t2 = modify
if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf)))
- var/temp_name = reject_bad_name(href_list["reg"])
+ var/temp_name = reject_bad_name(href_list["reg"], TRUE)
if(temp_name)
modify.registered_name = temp_name
else
@@ -464,6 +466,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE)
if(delcount)
+ message_admins("[key_name_admin(usr)] has wiped all ID computer logs.")
+ usr.create_log(MISC_LOG, "wiped all ID computer logs.")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
SSnanoui.update_uis(src)
@@ -503,9 +507,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("terminate")
if(is_authenticated(usr) && !target_dept)
var/jobnamedata = modify.getRankAndAssignment()
- log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
- message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
+ var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN))
+ if(!reason || !is_authenticated(usr) || !modify)
+ return FALSE
+ var/m_ckey = modify.getPlayerCkey()
+ var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
+ log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
+ message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
+ usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name)
+ SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.assignment = "Terminated"
modify.access = list()
@@ -517,16 +528,20 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE))
visible_message("[src]: Heads may only demote members of their own department.")
return 0
-
+ var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"),1,MAX_MESSAGE_LEN))
+ if(!reason || !is_authenticated(usr) || !modify)
+ return 0
var/list/access = list()
var/datum/job/jobdatum = new /datum/job/civilian
access = jobdatum.get_access()
-
var/jobnamedata = modify.getRankAndAssignment()
- log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
- message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
+ var/m_ckey = modify.getPlayerCkey()
+ var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
+ log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
+ message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
+ usr.create_log(MISC_LOG, "demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name)
-
+ SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.access = access
modify.rank = "Civilian"
modify.assignment = "Demoted"
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 3091caa82a2..04a4f7e0db4 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -1,3 +1,6 @@
+#define MENU_MAIN 1
+#define MENU_RECORDS 2
+
/obj/machinery/computer/cloning
name = "cloning console"
icon = 'icons/obj/computer.dmi'
@@ -6,11 +9,11 @@
circuit = /obj/item/circuitboard/cloning
req_access = list(ACCESS_HEADS) //Only used for record deletion right now.
var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning.
- var/list/pods = list() //Linked cloning pods.
- var/temp = ""
- var/scantemp = "Scanner ready."
- var/menu = 1 //Which menu screen to display
- var/list/records = list()
+ var/list/pods = null //Linked cloning pods.
+ var/list/temp = null
+ var/list/scantemp = null
+ var/menu = MENU_MAIN //Which menu screen to display
+ var/list/records = null
var/datum/dna2/record/active_record = null
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
var/loading = 0 // Nice loading text
@@ -24,6 +27,9 @@
/obj/machinery/computer/cloning/Initialize()
..()
+ pods = list()
+ records = list()
+ set_scan_temp("Scanner ready.", "good")
updatemodules()
/obj/machinery/computer/cloning/Destroy()
@@ -91,7 +97,7 @@
W.loc = src
src.diskette = W
to_chat(user, "You insert [W].")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
return
else if(istype(W, /obj/item/multitool))
var/obj/item/multitool/M = W
@@ -117,19 +123,21 @@
return
updatemodules()
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+/obj/machinery/computer/cloning/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(stat & (NOPOWER|BROKEN))
return
- // Set up the Nano UI
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ var/datum/asset/cloning/assets = get_asset_datum(/datum/asset/cloning)
+ assets.send(user)
+
+ ui = SStgui.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 = new(user, src, ui_key, "CloningConsole", "Cloning Console", 640, 520)
ui.open()
-/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/computer/cloning/tgui_data(mob/user)
var/data[0]
data["menu"] = src.menu
data["scanner"] = sanitize("[src.scanner]")
@@ -143,7 +151,18 @@
if(pod.efficiency > 5)
canpodautoprocess = 1
- tempods.Add(list(list("pod" = "\ref[pod]", "name" = sanitize(capitalize(pod.name)), "biomass" = pod.biomass)))
+ var/status = "idle"
+ if(pod.mess)
+ status = "mess"
+ else if(pod.occupant && !(pod.stat & NOPOWER))
+ status = "cloning"
+ tempods.Add(list(list(
+ "pod" = "\ref[pod]",
+ "name" = sanitize(capitalize(pod.name)),
+ "biomass" = pod.biomass,
+ "status" = status,
+ "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
+ )))
data["pods"] = tempods
data["loading"] = loading
@@ -164,192 +183,194 @@
data["selected_pod"] = "\ref[selected_pod]"
var/list/temprecords[0]
for(var/datum/dna2/record/R in records)
- var tempRealName = R.dna.real_name
+ var/tempRealName = R.dna.real_name
temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName))))
data["records"] = temprecords
- if(src.menu == 3)
- if(src.active_record)
- data["activerecord"] = "\ref[src.active_record]"
- var/obj/item/implant/health/H = null
- if(src.active_record.implant)
- H = locate(src.active_record.implant)
+ if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
+ data["podready"] = 1
+ else
+ data["podready"] = 0
- if((H) && (istype(H)))
- data["health"] = H.sensehealth()
- data["realname"] = sanitize(src.active_record.dna.real_name)
- data["unidentity"] = src.active_record.dna.uni_identity
- data["strucenzymes"] = src.active_record.dna.struc_enzymes
- if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
- data["podready"] = 1
- else
- data["podready"] = 0
+ data["modal"] = tgui_modal_data(src)
return data
-/obj/machinery/computer/cloning/Topic(href, href_list)
+/obj/machinery/computer/cloning/tgui_act(action, params)
if(..())
- return 1
-
- if(loading)
+ return
+ if(stat & (NOPOWER|BROKEN))
return
- if(href_list["scan"] && scanner && scanner.occupant)
- scantemp = "Scanner ready."
-
- loading = 1
-
- spawn(20)
- if(can_brainscan() && scan_mode)
- scan_mob(scanner.occupant, scan_brain = 1)
- else
- scan_mob(scanner.occupant)
-
- loading = 0
- SSnanoui.update_uis(src)
-
- if(href_list["task"])
- switch(href_list["task"])
- if("autoprocess")
- autoprocess = 1
- SSnanoui.update_uis(src)
- if("stopautoprocess")
- autoprocess = 0
- SSnanoui.update_uis(src)
-
- //No locking an open scanner.
- else if((href_list["lock"]) && (!isnull(src.scanner)))
- if((!src.scanner.locked) && (src.scanner.occupant))
- src.scanner.locked = 1
- else
- src.scanner.locked = 0
-
- else if(href_list["view_rec"])
- src.active_record = locate(href_list["view_rec"])
- if(istype(src.active_record,/datum/dna2/record))
- if((isnull(src.active_record.ckey)))
- qdel(src.active_record)
- src.temp = "Error: Record corrupt."
- else
- src.menu = 3
- else
- src.active_record = null
- src.temp = "Error: Record missing."
-
- else if(href_list["del_rec"])
- if((!src.active_record) || (src.menu < 3))
- return
- if(src.menu == 3) //If we are viewing a record, confirm deletion
- src.temp = "Please confirm that you want to delete the record?"
- src.menu = 4
-
- else if(src.menu == 4)
- var/obj/item/card/id/C = usr.get_active_hand()
- if(istype(C)||istype(C, /obj/item/pda))
- if(src.check_access(C))
- src.records.Remove(src.active_record)
- qdel(src.active_record)
- src.temp = "Record deleted."
- src.menu = 2
+ . = TRUE
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_ANSWER)
+ if(params["id"] == "del_rec" && active_record)
+ var/obj/item/card/id/C = usr.get_active_hand()
+ if(!istype(C) && !istype(C, /obj/item/pda))
+ set_temp("ID not in hand.", "danger")
+ return
+ if(check_access(C))
+ records.Remove(active_record)
+ qdel(active_record)
+ set_temp("Record deleted.", "success")
+ menu = MENU_RECORDS
else
- src.temp = "Error: Access denied."
-
- else if(href_list["disk"]) //Load or eject.
- switch(href_list["disk"])
- if("load")
- if((isnull(src.diskette)) || isnull(src.diskette.buf))
- src.temp = "Error: The disk's data could not be read."
- SSnanoui.update_uis(src)
- return
- if(isnull(src.active_record))
- src.temp = "Error: No active record was found."
- src.menu = 1
- SSnanoui.update_uis(src)
- return
-
- src.active_record = src.diskette.buf.copy()
-
- src.temp = "Load successful."
-
- if("eject")
- if(!isnull(src.diskette))
- src.diskette.loc = src.loc
- src.diskette = null
-
- 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."
- SSnanoui.update_uis(src)
+ set_temp("Access denied.", "danger")
return
- // DNA2 makes things a little simpler.
- src.diskette.buf=src.active_record.copy()
- src.diskette.buf.types=0
- switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se
- if("ui")
- src.diskette.buf.types=DNA2_BUF_UI
- if("ue")
- src.diskette.buf.types=DNA2_BUF_UI|DNA2_BUF_UE
- if("se")
- src.diskette.buf.types=DNA2_BUF_SE
- src.diskette.name = "data disk - '[src.active_record.dna.real_name]'"
- src.temp = "Save \[[href_list["save_disk"]]\] successful."
+ switch(action)
+ if("scan")
+ if(!scanner || !scanner.occupant || loading)
+ return
+ set_scan_temp("Scanner ready.", "good")
+ loading = TRUE
- else if(href_list["refresh"])
- SSnanoui.update_uis(src)
-
- else if(href_list["selectpod"])
- var/obj/machinery/clonepod/selected = locate(href_list["selectpod"])
- if(istype(selected) && (selected in pods))
- selected_pod = selected
-
- else if(href_list["clone"])
- var/datum/dna2/record/C = locate(href_list["clone"])
- //Look for that player! They better be dead!
- if(istype(C))
- //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
- if(!pods.len)
- temp = "Error: No cloning pod detected."
- else
- var/obj/machinery/clonepod/pod = selected_pod
- var/cloneresult
- if(!selected_pod)
- temp = "Error: No cloning pod selected."
- else if(pod.occupant)
- temp = "Error: The cloning pod is currently occupied."
- else if(pod.biomass < CLONE_BIOMASS)
- temp = "Error: Not enough biomass."
- else if(pod.mess)
- temp = "Error: The cloning pod is malfunctioning."
- else if(!config.revival_cloning)
- temp = "Error: Unable to initiate cloning cycle."
+ spawn(20)
+ if(can_brainscan() && scan_mode)
+ scan_mob(scanner.occupant, scan_brain = TRUE)
else
- cloneresult = pod.growclone(C)
- if(cloneresult)
- if(cloneresult > 0)
- temp = "Initiating cloning cycle..."
- records.Remove(C)
- qdel(C)
- menu = 1
+ scan_mob(scanner.occupant)
+ loading = FALSE
+ SStgui.update_uis(src)
+ if("autoprocess")
+ autoprocess = text2num(params["on"]) > 0
+ if("lock")
+ if(isnull(scanner) || !scanner.occupant) //No locking an open scanner.
+ return
+ scanner.locked = !scanner.locked
+ if("view_rec")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ active_record = locate(ref)
+ if(istype(active_record))
+ if(isnull(active_record.ckey))
+ qdel(active_record)
+ set_temp("Error: Record corrupt.", "danger")
+ else
+ var/obj/item/implant/health/H = null
+ if(active_record.implant)
+ H = locate(active_record.implant)
+ var/list/payload = list(
+ activerecord = "\ref[active_record]",
+ health = (H && istype(H)) ? H.sensehealth() : "",
+ realname = sanitize(active_record.dna.real_name),
+ unidentity = active_record.dna.uni_identity,
+ strucenzymes = active_record.dna.struc_enzymes,
+ )
+ tgui_modal_message(src, action, "", null, payload)
+ else
+ active_record = null
+ set_temp("Error: Record missing.", "danger")
+ if("del_rec")
+ if(!active_record)
+ return
+ tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel")
+ if("disk") // Disk management.
+ if(!length(params["option"]))
+ return
+ switch(params["option"])
+ if("load")
+ if(isnull(diskette) || isnull(diskette.buf))
+ set_temp("Error: The disk's data could not be read.", "danger")
+ return
+ else if(isnull(active_record))
+ set_temp("Error: No active record was found.", "danger")
+ menu = MENU_MAIN
+ return
+
+ active_record = diskette.buf.copy()
+ set_temp("Successfully loaded from disk.", "success")
+ if("save")
+ if(isnull(diskette) || diskette.read_only || isnull(active_record))
+ set_temp("Error: The data could not be saved.", "danger")
+ return
+
+ // DNA2 makes things a little simpler.
+ var/types
+ switch(params["savetype"]) // Save as Ui/Ui+Ue/Se
+ if("ui")
+ types = DNA2_BUF_UI
+ if("ue")
+ types = DNA2_BUF_UI|DNA2_BUF_UE
+ if("se")
+ types = DNA2_BUF_SE
+ else
+ set_temp("Error: Invalid save format.", "danger")
+ return
+ diskette.buf = active_record.copy()
+ diskette.buf.types = types
+ diskette.name = "data disk - '[active_record.dna.real_name]'"
+ set_temp("Successfully saved to disk.", "success")
+ if("eject")
+ if(!isnull(diskette))
+ diskette.loc = loc
+ diskette = null
+ if("refresh")
+ SStgui.update_uis(src)
+ if("selectpod")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ var/obj/machinery/clonepod/selected = locate(ref)
+ if(istype(selected) && (selected in pods))
+ selected_pod = selected
+ if("clone")
+ var/ref = params["ref"]
+ if(!length(ref))
+ return
+ var/datum/dna2/record/C = locate(ref)
+ //Look for that player! They better be dead!
+ if(istype(C))
+ tgui_modal_clear(src)
+ //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
+ if(!length(pods))
+ set_temp("Error: No cloning pod detected.", "danger")
+ else
+ var/obj/machinery/clonepod/pod = selected_pod
+ var/cloneresult
+ if(!selected_pod)
+ set_temp("Error: No cloning pod selected.", "danger")
+ else if(pod.occupant)
+ set_temp("Error: The cloning pod is currently occupied.", "danger")
+ else if(pod.biomass < CLONE_BIOMASS)
+ set_temp("Error: Not enough biomass.", "danger")
+ else if(pod.mess)
+ set_temp("Error: The cloning pod is malfunctioning.", "danger")
+ else if(!config.revival_cloning)
+ set_temp("Error: Unable to initiate cloning cycle.", "danger")
else
- temp = "[C.name] => Initialisation failure."
-
+ cloneresult = pod.growclone(C)
+ if(cloneresult)
+ set_temp("Initiating cloning cycle...", "success")
+ records.Remove(C)
+ qdel(C)
+ menu = MENU_MAIN
+ else
+ set_temp("Error: Initialisation failure.", "danger")
+ else
+ set_temp("Error: Data corruption.", "danger")
+ if("menu")
+ menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS)
+ if("toggle_mode")
+ if(loading)
+ return
+ if(can_brainscan())
+ scan_mode = !scan_mode
+ else
+ scan_mode = FALSE
+ if("eject")
+ if(usr.incapacitated() || !scanner || loading)
+ return
+ scanner.eject_occupant(usr)
+ scanner.add_fingerprint(usr)
+ if("cleartemp")
+ temp = null
else
- temp = "Error: Data corruption."
-
- else if(href_list["menu"])
- src.menu = text2num(href_list["menu"])
- temp = ""
- scantemp = "Scanner ready."
- else if(href_list["toggle_mode"])
- if(can_brainscan())
- scan_mode = !scan_mode
- else
- scan_mode = 0
+ return FALSE
src.add_fingerprint(usr)
- SSnanoui.update_uis(src)
- return
/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0)
if(stat & NOPOWER)
@@ -360,46 +381,46 @@
return
if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna))
if(isalien(subject))
- scantemp = "Error: Xenomorphs are not scannable."
- SSnanoui.update_uis(src)
+ set_scan_temp("Xenomorphs are not scannable.", "bad")
+ SStgui.update_uis(src)
return
// can add more conditions for specific non-human messages here
else
- scantemp = "Error: Subject species is not scannable."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject species is not scannable.", "bad")
+ SStgui.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)
if(istype(Brn))
if(NO_SCAN in Brn.dna.species.species_traits)
- scantemp = "Error: [Brn.dna.species.name_plural] are not scannable."
- SSnanoui.update_uis(src)
+ set_scan_temp("[Brn.dna.species.name_plural] are not scannable.", "bad")
+ SStgui.update_uis(src)
return
if(!subject.get_int_organ(/obj/item/organ/internal/brain))
- scantemp = "Error: No brain detected in subject."
- SSnanoui.update_uis(src)
+ set_scan_temp("No brain detected in subject.", "bad")
+ SStgui.update_uis(src)
return
if(subject.suiciding)
- scantemp = "Error: Subject has committed suicide and is not scannable."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject has committed suicide and is not scannable.", "bad")
+ SStgui.update_uis(src)
return
if((!subject.ckey) || (!subject.client))
- scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad")
+ SStgui.update_uis(src)
return
if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
- scantemp = "Error: Subject has incompatible genetic mutations."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject has incompatible genetic mutations.", "bad")
+ SStgui.update_uis(src)
return
if(!isnull(find_record(subject.ckey)))
- scantemp = "Subject already in database."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject already in database.")
+ SStgui.update_uis(src)
return
for(var/obj/machinery/clonepod/pod in pods)
if(pod.occupant && pod.clonemind == subject.mind)
- scantemp = "Subject already getting cloned."
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject already getting cloned.")
+ SStgui.update_uis(src)
return
subject.dna.check_integrity()
@@ -434,8 +455,8 @@
R.mind = "\ref[subject.mind]"
src.records += R
- scantemp = "Subject successfully scanned. " + extra_info
- SSnanoui.update_uis(src)
+ set_scan_temp("Subject successfully scanned. [extra_info]", "good")
+ SStgui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
@@ -451,3 +472,30 @@
/obj/machinery/computer/cloning/proc/can_brainscan()
return (scanner && scanner.scan_level > 3)
+
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger
+ */
+/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
+
+/**
+ * Sets a temporary scan message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * color - The color of the message: (color name)
+ */
+/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE)
+ scantemp = list(text = text, color = color)
+ if(update_now)
+ SStgui.update_uis(src)
+
+#undef MENU_MAIN
+#undef MENU_RECORDS
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 9a1fd08d289..60563e692b3 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -1,35 +1,39 @@
#define COMM_SCREEN_MAIN 1
#define COMM_SCREEN_STAT 2
#define COMM_SCREEN_MESSAGES 3
-#define COMM_SCREEN_SECLEVEL 4
#define COMM_AUTHENTICATION_NONE 0
#define COMM_AUTHENTICATION_MIN 1
#define COMM_AUTHENTICATION_MAX 2
+#define COMM_MSGLEN_MINIMUM 6
+#define COMM_CCMSGLEN_MINIMUM 20
+
// The communications computer
/obj/machinery/computer/communications
name = "communications console"
- desc = "This can be used for various important functions. Still under developement."
+ desc = "This allows the Captain to contact Central Command, or change the alert level. It also allows the command staff to call the Escape Shuttle."
icon_keyboard = "tech_key"
icon_screen = "comm"
req_access = list(ACCESS_HEADS)
circuit = /obj/item/circuitboard/communications
- var/prints_intercept = 1
- var/authenticated = COMM_AUTHENTICATION_NONE
var/list/messagetitle = list()
var/list/messagetext = list()
- var/currmsg = 0
- var/aicurrmsg = 0
+ var/currmsg
+
+ var/authenticated = COMM_AUTHENTICATION_NONE
var/menu_state = COMM_SCREEN_MAIN
var/ai_menu_state = COMM_SCREEN_MAIN
- var/message_cooldown = 0
- var/centcomm_message_cooldown = 0
+ var/aicurrmsg
+
+ var/message_cooldown
+ var/centcomm_message_cooldown
var/tmp_alertlevel = 0
var/stat_msg1
var/stat_msg2
- var/display_type="blank"
+ var/display_type = "blank"
+ var/display_icon
var/datum/announcement/priority/crew_announcement = new
@@ -70,123 +74,113 @@
feedback_inc("alert_comms_blue",1)
tmp_alertlevel = 0
-/obj/machinery/computer/communications/Topic(href, href_list)
- if(..(href, href_list))
- return 1
-
- if(!is_secure_level(src.z))
+/obj/machinery/computer/communications/tgui_act(action, params)
+ if(..())
+ return
+ if(!is_secure_level(z))
to_chat(usr, "Unable to establish a connection: You're too far away from the station!")
- return 1
+ return
- if(href_list["login"])
+ . = TRUE
+
+ if(action == "auth")
if(!ishuman(usr))
to_chat(usr, "Access denied.")
+ return FALSE
+ // Logout function.
+ if(authenticated != COMM_AUTHENTICATION_NONE)
+ authenticated = COMM_AUTHENTICATION_NONE
+ crew_announcement.announcer = null
+ setMenuState(usr, COMM_SCREEN_MAIN)
return
-
+ // Login function.
var/list/access = usr.get_access()
if(allowed(usr))
authenticated = COMM_AUTHENTICATION_MIN
-
if(ACCESS_CAPTAIN in access)
authenticated = COMM_AUTHENTICATION_MAX
var/mob/living/carbon/human/H = usr
var/obj/item/card/id = H.get_idcard(TRUE)
if(istype(id))
crew_announcement.announcer = GetNameAndAssignmentFromId(id)
-
- SSnanoui.update_uis(src)
- return
-
- if(href_list["logout"])
- authenticated = COMM_AUTHENTICATION_NONE
- crew_announcement.announcer = ""
- setMenuState(usr,COMM_SCREEN_MAIN)
- SSnanoui.update_uis(src)
+ if(authenticated == COMM_AUTHENTICATION_NONE)
+ to_chat(usr, "You need to wear your ID.")
return
+ // All functions below this point require authentication.
if(!is_authenticated(usr))
- return 1
+ return FALSE
- switch(href_list["operation"])
+ switch(action)
if("main")
- setMenuState(usr,COMM_SCREEN_MAIN)
-
- if("changeseclevel")
- setMenuState(usr,COMM_SCREEN_SECLEVEL)
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("newalertlevel")
if(isAI(usr) || isrobot(usr))
to_chat(usr, "Firewalls prevent you from changing the alert level.")
- return 1
+ return
else if(usr.can_admin_interact())
- change_security_level(text2num(href_list["level"]))
- return 1
+ change_security_level(text2num(params["level"]))
+ return
else if(!ishuman(usr))
to_chat(usr, "Security measures prevent you from changing the alert level.")
- return 1
+ return
- var/mob/living/carbon/human/L = usr
- var/obj/item/card = L.get_active_hand()
- var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda
- if(istype(I, /obj/item/pda))
- var/obj/item/pda/pda = I
- I = pda.id
- if(I && istype(I))
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/card/id/I = H.get_idcard(TRUE)
+ if(istype(I))
if(ACCESS_CAPTAIN in I.access)
- change_security_level(text2num(href_list["level"]))
+ change_security_level(text2num(params["level"]))
else
to_chat(usr, "You are not authorized to do this.")
- setMenuState(usr,COMM_SCREEN_MAIN)
+ setMenuState(usr, COMM_SCREEN_MAIN)
else
- to_chat(usr, "You need to swipe your ID.")
+ to_chat(usr, "You need to wear your ID.")
if("announce")
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
- if(message_cooldown)
+ if(message_cooldown > world.time)
to_chat(usr, "Please allow at least one minute to pass between announcements.")
- 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))
- SSnanoui.update_uis(src)
+ if(!input || message_cooldown > world.time || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
+ return
+ if(length(input) < COMM_MSGLEN_MINIMUM)
+ to_chat(usr, "Message '[input]' is too short. [COMM_MSGLEN_MINIMUM] character minimum.")
return
crew_announcement.Announce(input)
- message_cooldown = 1
- spawn(600)//One minute cooldown
- message_cooldown = 0
+ message_cooldown = world.time + 600 //One minute
if("callshuttle")
var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","")
if(!input || ..() || !is_authenticated(usr))
- SSnanoui.update_uis(src)
return
-
call_shuttle_proc(usr, input)
if(SSshuttle.emergency.timer)
post_status("shuttle")
- setMenuState(usr,COMM_SCREEN_MAIN)
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("cancelshuttle")
if(isAI(usr) || isrobot(usr))
to_chat(usr, "Firewalls prevent you from recalling the shuttle.")
- SSnanoui.update_uis(src)
- return 1
+ return
var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No")
if(response == "Yes")
cancel_call_proc(usr)
if(SSshuttle.emergency.timer)
post_status("shuttle")
- setMenuState(usr,COMM_SCREEN_MAIN)
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("messagelist")
- currmsg = 0
- if(href_list["msgid"])
- setCurrentMessage(usr, text2num(href_list["msgid"]))
- setMenuState(usr,COMM_SCREEN_MESSAGES)
+ currmsg = null
+ aicurrmsg = null
+ if(params["msgid"])
+ setCurrentMessage(usr, text2num(params["msgid"]))
+ setMenuState(usr, COMM_SCREEN_MESSAGES)
if("delmessage")
- if(href_list["msgid"])
- currmsg = text2num(href_list["msgid"])
+ if(params["msgid"])
+ currmsg = text2num(params["msgid"])
var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No")
if(response == "Yes")
if(currmsg)
@@ -196,95 +190,95 @@
messagetitle.Remove(title)
messagetext.Remove(text)
if(currmsg == id)
- currmsg = 0
+ currmsg = null
if(aicurrmsg == id)
- aicurrmsg = 0
- setMenuState(usr,COMM_SCREEN_MESSAGES)
+ aicurrmsg = null
+ setMenuState(usr, COMM_SCREEN_MESSAGES)
if("status")
- setMenuState(usr,COMM_SCREEN_STAT)
+ setMenuState(usr, COMM_SCREEN_STAT)
// Status display stuff
if("setstat")
- display_type=href_list["statdisp"]
+ display_type = params["statdisp"]
switch(display_type)
if("message")
+ display_icon = null
post_status("message", stat_msg1, stat_msg2, usr)
if("alert")
- post_status("alert", href_list["alert"], user = usr)
+ display_icon = params["alert"]
+ post_status("alert", params["alert"], user = usr)
else
- post_status(href_list["statdisp"], user = usr)
- setMenuState(usr,COMM_SCREEN_STAT)
+ display_icon = null
+ post_status(params["statdisp"], user = usr)
+ setMenuState(usr, COMM_SCREEN_STAT)
if("setmsg1")
stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1)
- setMenuState(usr,COMM_SCREEN_STAT)
+ setMenuState(usr, COMM_SCREEN_STAT)
if("setmsg2")
stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2)
- setMenuState(usr,COMM_SCREEN_STAT)
+ setMenuState(usr, COMM_SCREEN_STAT)
if("nukerequest")
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
- if(centcomm_message_cooldown)
+ if(centcomm_message_cooldown > world.time)
to_chat(usr, "Arrays recycling. Please stand by.")
- 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.","")
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- SSnanoui.update_uis(src)
+ return
+ if(length(input) < COMM_CCMSGLEN_MINIMUM)
+ to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")
return
Nuke_request(input, usr)
to_chat(usr, "Request sent.")
log_game("[key_name(usr)] has requested the nuclear codes from Centcomm")
GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg')
- centcomm_message_cooldown = 1
- spawn(6000)//10 minute cooldown
- centcomm_message_cooldown = 0
- setMenuState(usr,COMM_SCREEN_MAIN)
+ centcomm_message_cooldown = world.time + 6000 // 10 minutes
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("MessageCentcomm")
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
- if(centcomm_message_cooldown)
+ if(centcomm_message_cooldown > world.time)
to_chat(usr, "Arrays recycling. Please stand by.")
- 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.", "")
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- SSnanoui.update_uis(src)
+ return
+ if(length(input) < COMM_CCMSGLEN_MINIMUM)
+ to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")
return
Centcomm_announce(input, usr)
print_centcom_report(input, station_time_timestamp() + " Captain's Message")
to_chat(usr, "Message transmitted.")
log_game("[key_name(usr)] has made a Centcomm announcement: [input]")
- centcomm_message_cooldown = 1
- spawn(6000)//10 minute cooldown
- centcomm_message_cooldown = 0
- setMenuState(usr,COMM_SCREEN_MAIN)
+ centcomm_message_cooldown = world.time + 6000 // 10 minutes
+ setMenuState(usr, COMM_SCREEN_MAIN)
// OMG SYNDICATE ...LETTERHEAD
if("MessageSyndicate")
if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged))
- if(centcomm_message_cooldown)
+ if(centcomm_message_cooldown > world.time)
to_chat(usr, "Arrays recycling. Please stand by.")
- 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.", "")
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
- SSnanoui.update_uis(src)
+ return
+ if(length(input) < COMM_CCMSGLEN_MINIMUM)
+ to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")
return
Syndicate_announce(input, usr)
to_chat(usr, "Message transmitted.")
log_game("[key_name(usr)] has made a Syndicate announcement: [input]")
- centcomm_message_cooldown = 1
- spawn(6000)//10 minute cooldown
- centcomm_message_cooldown = 0
- setMenuState(usr,COMM_SCREEN_MAIN)
+ centcomm_message_cooldown = world.time + 6000 // 10 minutes
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("RestoreBackup")
to_chat(usr, "Backup routing data restored!")
src.emagged = 0
- setMenuState(usr,COMM_SCREEN_MAIN)
+ setMenuState(usr, COMM_SCREEN_MAIN)
if("RestartNanoMob")
if(SSmob_hunt)
@@ -298,18 +292,13 @@
else
to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.")
- if("ToggleATC")
- GLOB.atc.squelched = !GLOB.atc.squelched
- to_chat(usr, "ATC traffic is now: [GLOB.atc.squelched ? "Disabled" : "Enabled"].")
- 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!")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/computer/communications/attack_ai(var/mob/user as mob)
return src.attack_hand(user)
@@ -325,28 +314,25 @@
to_chat(user, "Unable to establish a connection: You're too far away from the station!")
return
- ui_interact(user)
+ tgui_interact(user)
-/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 = SSnanoui.try_update_ui(user, src, ui_key, ui)
+/obj/machinery/computer/communications/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500)
- // open the new ui window
+ ui = new(user, src, ui_key, "CommunicationsComputer", name, 500, 600, master_ui, state)
ui.open()
-/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/machinery/computer/communications/tgui_data(mob/user)
+ var/list/data = list()
data["is_ai"] = isAI(user) || isrobot(user)
data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state
data["emagged"] = emagged
data["authenticated"] = is_authenticated(user, 0)
- data["screen"] = getMenuState(usr)
+ data["authmax"] = data["authenticated"] == COMM_AUTHENTICATION_MAX ? TRUE : FALSE
data["stat_display"] = list(
"type" = display_type,
+ "icon" = display_icon,
"line_1" = (stat_msg1 ? stat_msg1 : "-----"),
"line_2" = (stat_msg2 ? stat_msg2 : "-----"),
@@ -364,12 +350,20 @@
)
)
- data["security_level"] = GLOB.security_level
+ data["security_level"] = GLOB.security_level
+ switch(GLOB.security_level)
+ if(SEC_LEVEL_GREEN)
+ data["security_level_color"] = "green";
+ if(SEC_LEVEL_BLUE)
+ data["security_level_color"] = "blue";
+ if(SEC_LEVEL_RED)
+ data["security_level_color"] = "red";
+ else
+ data["security_level_color"] = "purple";
data["str_security_level"] = capitalize(get_security_level())
data["levels"] = list(
- list("id" = SEC_LEVEL_GREEN, "name" = "Green"),
- list("id" = SEC_LEVEL_BLUE, "name" = "Blue"),
- //SEC_LEVEL_RED = list("name"="Red"),
+ list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"),
+ list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"),
)
var/list/msg_data = list()
@@ -377,29 +371,30 @@
msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i)))
data["messages"] = msg_data
+
+ data["current_message"] = null
+ data["current_message_title"] = null
if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg))
data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg]
data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg]
data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null
+ data["msg_cooldown"] = message_cooldown ? (round((message_cooldown - world.time) / 10)) : 0
+ data["cc_cooldown"] = centcomm_message_cooldown ? (round((centcomm_message_cooldown - world.time) / 10)) : 0
- var/shuttle[0]
- switch(SSshuttle.emergency.mode)
- if(SHUTTLE_IDLE, SHUTTLE_RECALL)
- shuttle["callStatus"] = 2 //#define
- else
- shuttle["callStatus"] = 1
- if(SSshuttle.emergency.mode == SHUTTLE_CALL)
+ var/secondsToRefuel = SSshuttle.secondsToRefuel()
+ data["esc_callable"] = SSshuttle.emergency.mode == SHUTTLE_IDLE && !secondsToRefuel ? TRUE : FALSE
+ data["esc_recallable"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? TRUE : FALSE
+ data["esc_status"] = FALSE
+ if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
var/timeleft = SSshuttle.emergency.timeLeft()
- shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
-
- data["shuttle"] = shuttle
-
- data["atcSquelched"] = GLOB.atc.squelched
-
+ data["esc_status"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? "ETA:" : "RECALLING:"
+ data["esc_status"] += " [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
+ else if(secondsToRefuel)
+ data["esc_status"] = "Refueling: [secondsToRefuel / 60 % 60]:[add_zero(num2text(secondsToRefuel % 60), 2)]"
+ data["esc_section"] = data["esc_status"] || data["esc_callable"] || data["esc_recallable"] || data["lastCallLoc"]
return data
-
/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value)
if(isAI(user) || isrobot(user))
aicurrmsg = value
@@ -418,14 +413,6 @@
else
menu_state=value
-/obj/machinery/computer/communications/proc/getMenuState(var/mob/user)
- if(isAI(user) || isrobot(user))
- return ai_menu_state
- else
- return menu_state
-
-/proc/enable_prison_shuttle(var/mob/user);
-
/proc/call_shuttle_proc(var/mob/user, var/reason)
if(GLOB.sent_strike_team == 1)
to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.")
@@ -529,24 +516,16 @@
SSshuttle.autoEvac()
return ..()
-/proc/print_command_report(text = "", title = "Central Command Update")
+/proc/print_command_report(text = "", title = "Central Command Update", add_to_records = TRUE)
for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list)
if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z))
var/obj/item/paper/P = new /obj/item/paper(C.loc)
P.name = "paper- '[title]'"
P.info = text
P.update_icon()
- C.messagetitle.Add("[title]")
- C.messagetext.Add(text)
- for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list)
- var/turf/T = get_turf(P.computer)
- if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z))
- if(P.computer)
- var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT]
- if(printer)
- printer.print_text(text, "paper- '[title]'")
- P.messagetitle.Add("[title]")
- P.messagetext.Add(text)
+ if(add_to_records)
+ C.messagetitle.Add("[title]")
+ C.messagetext.Add(text)
/proc/print_centcom_report(text = "", title = "Incoming Message")
for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list)
@@ -557,12 +536,5 @@
P.update_icon()
C.messagetitle.Add("[title]")
C.messagetext.Add(text)
- for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list)
- var/turf/T = get_turf(P.computer)
- if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z))
- if(P.computer)
- var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT]
- if(printer)
- printer.print_text(text, "paper- '[title]'")
- P.messagetitle.Add("[title]")
- P.messagetext.Add(text)
+
+
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index aec41c80be3..2ec8611f420 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -135,7 +135,7 @@
/obj/machinery/computer/screwdriver_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
if(circuit && !(flags & NODECONSTRUCT))
if(I.use_tool(src, user, 20, volume = I.tool_volume))
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index a832e2b5d16..d6a1ccdd33c 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -8,7 +8,7 @@
active_power_usage = 500
light_color = LIGHT_COLOR_DARKBLUE
circuit = /obj/item/circuitboard/crew
- var/datum/nano_module/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
/obj/machinery/computer/crew/New()
crew_monitor = new(src)
@@ -20,17 +20,15 @@
/obj/machinery/computer/crew/attack_ai(mob/user)
attack_hand(user)
- ui_interact(user)
-
/obj/machinery/computer/crew/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- crew_monitor.ui_interact(user, ui_key, ui, force_open)
+/obj/machinery/computer/crew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ crew_monitor.tgui_interact(user, ui_key, ui, force_open)
/obj/machinery/computer/crew/interact(mob/user)
- crew_monitor.ui_interact(user)
+ crew_monitor.tgui_interact(user)
diff --git a/code/game/machinery/computer/depot.dm b/code/game/machinery/computer/depot.dm
index 62c82d9bd95..f2e89923eb4 100644
--- a/code/game/machinery/computer/depot.dm
+++ b/code/game/machinery/computer/depot.dm
@@ -165,7 +165,7 @@
alerts_when_broken = TRUE
/obj/machinery/computer/syndicate_depot/selfdestruct/get_menu(mob/user)
- var menutext = {"Syndicate Depot Fusion Reactor Control
+ var/menutext = {"Syndicate Depot Fusion Reactor Control
Disable Containment Field
"}
return menutext
@@ -193,10 +193,6 @@
/obj/machinery/computer/syndicate_depot/shieldcontrol/New()
. = ..()
perimeterarea = locate(/area/syndicate_depot/perimeter)
- if(istype(perimeterarea) && (GAMEMODE_IS_NUCLEAR || prob(20)))
- spawn(200)
- perimeterarea.perimeter_shields_up()
- depotarea.perimeter_shield_status = TRUE
/obj/machinery/computer/syndicate_depot/shieldcontrol/Destroy()
if(istype(perimeterarea) && perimeterarea.shield_list.len)
@@ -204,7 +200,7 @@
return ..()
/obj/machinery/computer/syndicate_depot/shieldcontrol/get_menu(mob/user)
- var menutext = {"Syndicate Depot Shield Grid Control
+ var/menutext = {"Syndicate Depot Shield Grid Control
"}
menutext += {"(SYNDI-LEADER) Whole-base Shield: [perimeterarea.shield_list.len ? "ON" : "OFF"] ([perimeterarea.shield_list.len ? "Disable" : "Enable"]) "}
menutext += {"(SYNDI-LEADER) Armory Shield: [depotarea.shield_list.len ? "ON" : "OFF"] ([depotarea.shield_list.len ? "Disable" : "Enable"]) "}
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index 9464c43b107..c733a7c92c1 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -1,10 +1,12 @@
-#define MED_DATA_MAIN 1 // Main menu
#define MED_DATA_R_LIST 2 // Record list
#define MED_DATA_MAINT 3 // Records maintenance
#define MED_DATA_RECORD 4 // Record
#define MED_DATA_V_DATA 5 // Virus database
#define MED_DATA_MEDBOT 6 // Medbot monitor
+#define FIELD(N, V, E) list(field = N, value = V, edit = E)
+#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB)
+
/obj/machinery/computer/med_data //TODO:SANITY
name = "medical records console"
desc = "This can be used to check medical records."
@@ -18,11 +20,45 @@
var/screen = null
var/datum/data/record/active1 = null
var/datum/data/record/active2 = null
- var/temp = null
+ var/list/temp = null
var/printing = null
+ // The below are used to make modal generation more convenient
+ var/static/list/field_edit_questions
+ var/static/list/field_edit_choices
light_color = LIGHT_COLOR_DARKBLUE
+/obj/machinery/computer/med_data/Initialize()
+ ..()
+ field_edit_questions = list(
+ // General
+ "sex" = "Please select new sex:",
+ "age" = "Please input new age:",
+ "fingerprint" = "Please input new fingerprint hash:",
+ "p_stat" = "Please select new physical status:",
+ "m_stat" = "Please select new mental status:",
+ // Medical
+ "blood_type" = "Please select new blood type:",
+ "b_dna" = "Please input new DNA:",
+ "mi_dis" = "Please input new minor disabilities:",
+ "mi_dis_d" = "Please summarize minor disabilities:",
+ "ma_dis" = "Please input new major disabilities:",
+ "ma_dis_d" = "Please summarize major disabilities:",
+ "alg" = "Please input new allergies:",
+ "alg_d" = "Please summarize allergies:",
+ "cdi" = "Please input new current diseases:",
+ "cdi_d" = "Please summarize current diseases:",
+ "notes" = "Please input new important notes:",
+ )
+ field_edit_choices = list(
+ // General
+ "sex" = list("Male", "Female"),
+ "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"),
+ "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"),
+ // Medical
+ "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"),
+ )
+
/obj/machinery/computer/med_data/Destroy()
active1 = null
active2 = null
@@ -33,7 +69,7 @@
usr.drop_item()
O.forceMove(src)
scan = O
- ui_interact(user)
+ tgui_interact(user)
return
return ..()
@@ -44,20 +80,25 @@
to_chat(user, "Unable to establish a connection: You're too far away from the station!")
return
add_fingerprint(user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/med_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "MedicalRecords", "Medical Records", 800, 380, master_ui, state)
ui.open()
+ ui.set_autoupdate(FALSE)
-/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/computer/med_data/tgui_data(mob/user)
var/data[0]
data["temp"] = temp
data["scan"] = scan ? scan.name : null
data["authenticated"] = authenticated
+ data["rank"] = rank
data["screen"] = screen
+ data["printing"] = printing
+ data["isAI"] = isAI(user)
+ data["isRobot"] = isrobot(user)
if(authenticated)
switch(screen)
if(MED_DATA_R_LIST)
@@ -72,17 +113,17 @@
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
var/list/fields = list()
general["fields"] = fields
- fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null)
- fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null)
- fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex")
- fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age")
- fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint")
- fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat")
- fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat")
+ fields[++fields.len] = FIELD("Name", active1.fields["name"], null)
+ fields[++fields.len] = FIELD("ID", active1.fields["id"], null)
+ fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex")
+ fields[++fields.len] = FIELD("Age", active1.fields["age"], "age")
+ fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint")
+ fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat")
+ fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat")
var/list/photos = list()
general["photos"] = photos
- photos[++photos.len] = list("photo" = active1.fields["photo-south"])
- photos[++photos.len] = list("photo" = active1.fields["photo-west"])
+ photos[++photos.len] = active1.fields["photo-south"]
+ photos[++photos.len] = active1.fields["photo-west"]
general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0)
general["empty"] = 0
else
@@ -93,17 +134,17 @@
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
var/list/fields = list()
medical["fields"] = fields
- fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0)
- fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1)
- fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0)
+ fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["blood_type"], "blood_type", FALSE)
+ fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE)
+ fields[++fields.len] = MED_FIELD("Minor Disabilities", active2.fields["mi_dis"], "mi_dis", FALSE)
+ fields[++fields.len] = MED_FIELD("Details", active2.fields["mi_dis_d"], "mi_dis_d", TRUE)
+ fields[++fields.len] = MED_FIELD("Major Disabilities", active2.fields["ma_dis"], "ma_dis", FALSE)
+ fields[++fields.len] = MED_FIELD("Details", active2.fields["ma_dis_d"], "ma_dis_d", TRUE)
+ fields[++fields.len] = MED_FIELD("Allergies", active2.fields["alg"], "alg", FALSE)
+ fields[++fields.len] = MED_FIELD("Details", active2.fields["alg_d"], "alg_d", TRUE)
+ fields[++fields.len] = MED_FIELD("Current Diseases", active2.fields["cdi"], "cdi", FALSE)
+ fields[++fields.len] = MED_FIELD("Details", active2.fields["cdi_d"], "cdi_d", TRUE)
+ fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE)
if(!active2.fields["comments"] || !islist(active2.fields["comments"]))
active2.fields["comments"] = list()
medical["comments"] = active2.fields["comments"]
@@ -121,13 +162,15 @@
data["virus"] += list(list("name" = DS.name, "D" = D))
if(MED_DATA_MEDBOT)
data["medbots"] = list()
- for(var/mob/living/simple_animal/bot/medbot/M in world)
+ for(var/mob/living/simple_animal/bot/medbot/M in GLOB.bots_list)
if(M.z != z)
continue
var/turf/T = get_turf(M)
if(T)
var/medbot = list()
+ var/area/A = get_area(T)
medbot["name"] = M.name
+ medbot["area"] = A.name
medbot["x"] = T.x
medbot["y"] = T.y
medbot["on"] = M.on
@@ -138,395 +181,290 @@
else
medbot["use_beaker"] = 0
data["medbots"] += list(medbot)
+
+ data["modal"] = tgui_modal_data(src)
return data
-/obj/machinery/computer/med_data/Topic(href, href_list)
+/obj/machinery/computer/med_data/tgui_act(action, params)
if(..())
- return 1
+ return
+ if(stat & (NOPOWER|BROKEN))
+ return
if(!GLOB.data_core.general.Find(active1))
active1 = null
if(!GLOB.data_core.medical.Find(active2))
active2 = null
- if(href_list["temp"])
- temp = null
+ . = TRUE
+ if(tgui_act_modal(action, params))
+ return
- if(href_list["temp_action"])
- if(href_list["temp_action"])
- var/temp_href = splittext(href_list["temp_action"], "=")
- switch(temp_href[1])
- if("del_all2")
- for(var/datum/data/record/R in GLOB.data_core.medical)
- qdel(R)
- setTemp("All records deleted.")
- if("p_stat")
- if(active1)
- switch(temp_href[2])
- if("deceased")
- active1.fields["p_stat"] = "*Deceased*"
- if("ssd")
- active1.fields["p_stat"] = "*SSD*"
- if("active")
- active1.fields["p_stat"] = "Active"
- if("unfit")
- active1.fields["p_stat"] = "Physically Unfit"
- if("disabled")
- active1.fields["p_stat"] = "Disabled"
- if("m_stat")
- if(active1)
- switch(temp_href[2])
- if("insane")
- active1.fields["m_stat"] = "*Insane*"
- if("unstable")
- active1.fields["m_stat"] = "*Unstable*"
- if("watch")
- active1.fields["m_stat"] = "*Watch*"
- if("stable")
- active1.fields["m_stat"] = "Stable"
- if("blood_type")
- if(active2)
- switch(temp_href[2])
- if("an")
- active2.fields["blood_type"] = "A-"
- if("bn")
- active2.fields["blood_type"] = "B-"
- if("abn")
- active2.fields["blood_type"] = "AB-"
- if("on")
- active2.fields["blood_type"] = "O-"
- if("ap")
- active2.fields["blood_type"] = "A+"
- if("bp")
- active2.fields["blood_type"] = "B+"
- if("abp")
- active2.fields["blood_type"] = "AB+"
- if("op")
- active2.fields["blood_type"] = "O+"
- if("del_r2")
- QDEL_NULL(active2)
-
- if(href_list["scan"])
- if(scan)
- scan.forceMove(loc)
- if(ishuman(usr) && !usr.get_active_hand())
- usr.put_in_hands(scan)
- scan = null
+ switch(action)
+ if("cleartemp")
+ temp = null
+ if("scan")
+ if(scan)
+ scan.forceMove(loc)
+ if(ishuman(usr) && !usr.get_active_hand())
+ usr.put_in_hands(scan)
+ scan = null
+ else
+ var/obj/item/I = usr.get_active_hand()
+ if(istype(I, /obj/item/card/id))
+ usr.drop_item()
+ I.forceMove(src)
+ scan = I
+ if("login")
+ var/login_type = text2num(params["login_type"])
+ if(login_type == LOGIN_TYPE_NORMAL && istype(scan))
+ if(check_access(scan))
+ authenticated = scan.registered_name
+ rank = scan.assignment
+ else if(login_type == LOGIN_TYPE_AI && isAI(usr))
+ authenticated = usr.name
+ rank = "AI"
+ else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr))
+ authenticated = usr.name
+ var/mob/living/silicon/robot/R = usr
+ rank = "[R.modtype] [R.braintype]"
+ if(authenticated)
+ active1 = null
+ active2 = null
+ screen = MED_DATA_R_LIST
else
- var/obj/item/I = usr.get_active_hand()
- if(istype(I, /obj/item/card/id))
- usr.drop_item()
- I.forceMove(src)
- scan = I
+ . = FALSE
- if(href_list["login"])
- if(isAI(usr))
- authenticated = usr.name
- rank = "AI"
- else if(isrobot(usr))
- authenticated = usr.name
- var/mob/living/silicon/robot/R = usr
- rank = "[R.modtype] [R.braintype]"
- else if(istype(scan, /obj/item/card/id))
- if(check_access(scan))
- authenticated = scan.registered_name
- rank = scan.assignment
-
- if(authenticated)
- active1 = null
- active2 = null
- screen = MED_DATA_MAIN
+ if(.)
+ return
if(authenticated)
- if(href_list["logout"])
- authenticated = null
- screen = null
- active1 = null
- active2 = null
+ . = TRUE
+ switch(action)
+ if("logout")
+ if(scan)
+ scan.forceMove(loc)
+ if(ishuman(usr) && !usr.get_active_hand())
+ usr.put_in_hands(scan)
+ scan = null
+ authenticated = null
+ screen = null
+ active1 = null
+ active2 = null
+ if("screen")
+ screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT)
+ active1 = null
+ active2 = null
+ if("vir")
+ var/type = text2path(params["vir"] || "")
+ if(!ispath(type, /datum/disease))
+ return
- if(href_list["screen"])
- screen = text2num(href_list["screen"])
- if(screen < 1)
- screen = MED_DATA_MAIN
+ var/datum/disease/D = new type(0)
+ var/list/payload = list(
+ name = D.name,
+ max_stages = D.max_stages,
+ spread_text = D.spread_text,
+ cure = D.cure_text || "None",
+ desc = D.desc,
+ severity = D.severity
+ );
+ tgui_modal_message(src, "virus", "", null, payload)
+ qdel(D)
+ if("del_all")
+ for(var/datum/data/record/R in GLOB.data_core.medical)
+ qdel(R)
+ set_temp("All medical records deleted.")
+ if("del_r")
+ if(active2)
+ set_temp("Medical record deleted.")
+ qdel(active2)
+ if("d_rec")
+ var/datum/data/record/general_record = locate(params["d_rec"] || "")
+ if(!GLOB.data_core.general.Find(general_record))
+ set_temp("Record not found.", "danger")
+ return
- active1 = null
- active2 = null
+ var/datum/data/record/medical_record
+ for(var/datum/data/record/M in GLOB.data_core.medical)
+ if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"])
+ medical_record = M
+ break
- if(href_list["vir"])
- var/type = href_list["vir"]
- var/datum/disease/D = new type(0)
- var/afs = ""
- for(var/mob/M in D.viable_mobtypes)
- afs += "[initial(M.name)];"
- var/severity = D.severity
- switch(severity)
- if("Harmful", "Minor")
- severity = "[severity]"
- if("Medium")
- severity = "[severity]"
- if("Dangerous!")
- severity = "[severity]"
- if("BIOHAZARD THREAT!")
- severity = "[severity]"
- setTemp({"Name: [D.name]
- Number of stages: [D.max_stages]
- Spread: [D.spread_text] Transmission
- Possible Cure: [(D.cure_text||"none")]
- Affected Lifeforms:[afs]
- Notes: [D.desc]
- Severity: [severity]"})
- qdel(D)
-
- if(href_list["del_all"])
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1")
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null)
- setTemp("Are you sure you wish to delete all records?", buttons)
-
- if(href_list["field"])
- if(..())
- return 1
- var/a1 = active1
- var/a2 = active2
- switch(href_list["field"])
- if("fingerprint")
- if(istype(active1, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["fingerprint"] = t1
- if("sex")
- if(istype(active1, /datum/data/record))
- if(active1.fields["sex"] == "Male")
- active1.fields["sex"] = "Female"
- else
- active1.fields["sex"] = "Male"
- if("age")
- if(istype(active1, /datum/data/record))
- var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["age"] = t1
- if("mi_dis")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["mi_dis"] = t1
- if("mi_dis_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["mi_dis_d"] = t1
- if("ma_dis")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["ma_dis"] = t1
- if("ma_dis_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["ma_dis_d"] = t1
- if("alg")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["alg"] = t1
- if("alg_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["alg_d"] = t1
- if("cdi")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["cdi"] = t1
- if("cdi_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["cdi_d"] = t1
- if("notes")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["notes"] = t1
- if("p_stat")
- if(istype(active1, /datum/data/record))
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null))
- setTemp("Physical Condition", buttons)
- if("m_stat")
- if(istype(active1, /datum/data/record))
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null))
- setTemp("Mental Condition", buttons)
- if("blood_type")
- if(istype(active2, /datum/data/record))
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null))
- buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null))
- setTemp("Blood Type", buttons)
- if("b_dna")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["b_dna"] = t1
- if("vir_name")
- var/datum/data/record/v = locate(href_list["edit_vir"])
- if(v)
- var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- v.fields["name"] = t1
- if("vir_desc")
- var/datum/data/record/v = locate(href_list["edit_vir"])
- if(v)
- var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- v.fields["description"] = t1
-
- if(href_list["del_r"])
- if(active2)
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null)
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
- setTemp("Are you sure you wish to delete the record (Medical Portion Only)?", buttons)
-
- if(href_list["d_rec"])
- var/datum/data/record/R = locate(href_list["d_rec"])
- var/datum/data/record/M = locate(href_list["d_rec"])
- if(!GLOB.data_core.general.Find(R))
- setTemp("Record not found!")
- return 1
- for(var/datum/data/record/E in GLOB.data_core.medical)
- if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])
- M = E
- active1 = R
- active2 = M
- screen = MED_DATA_RECORD
-
- if(href_list["new"])
- if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
- var/datum/data/record/R = new /datum/data/record()
- R.fields["name"] = active1.fields["name"]
- R.fields["id"] = active1.fields["id"]
- R.name = "Medical Record #[R.fields["id"]]"
- R.fields["blood_type"] = "Unknown"
- R.fields["b_dna"] = "Unknown"
- R.fields["mi_dis"] = "None"
- R.fields["mi_dis_d"] = "No minor disabilities have been declared."
- R.fields["ma_dis"] = "None"
- R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
- R.fields["alg"] = "None"
- R.fields["alg_d"] = "No allergies have been detected in this patient."
- R.fields["cdi"] = "None"
- R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
- R.fields["notes"] = "No notes."
- GLOB.data_core.medical += R
- active2 = R
+ active1 = general_record
+ active2 = medical_record
screen = MED_DATA_RECORD
-
- if(href_list["add_c"])
- if(!istype(active2, /datum/data/record))
- return 1
- var/a2 = active2
- var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()] [t1]"
-
- if(href_list["del_c"])
- var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
- if(istype(active2, /datum/data/record) && active2.fields["comments"][index])
- active2.fields["comments"] -= active2.fields["comments"][index]
-
- if(href_list["search"])
- var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null)
- if(!t1 || ..())
- return 1
- active1 = null
- active2 = null
- t1 = lowertext(t1)
- for(var/datum/data/record/R in GLOB.data_core.medical)
- if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
+ if("new")
+ if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
+ var/datum/data/record/R = new /datum/data/record()
+ R.fields["name"] = active1.fields["name"]
+ R.fields["id"] = active1.fields["id"]
+ R.name = "Medical Record #[R.fields["id"]]"
+ R.fields["blood_type"] = "Unknown"
+ R.fields["b_dna"] = "Unknown"
+ R.fields["mi_dis"] = "None"
+ R.fields["mi_dis_d"] = "No minor disabilities have been declared."
+ R.fields["ma_dis"] = "None"
+ R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
+ R.fields["alg"] = "None"
+ R.fields["alg_d"] = "No allergies have been detected in this patient."
+ R.fields["cdi"] = "None"
+ R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
+ R.fields["notes"] = "No notes."
+ GLOB.data_core.medical += R
active2 = R
- if(!active2)
- setTemp("Could not locate record [t1].")
- else
+ screen = MED_DATA_RECORD
+ set_temp("Medical record created.", "success")
+ if("del_c")
+ var/index = text2num(params["del_c"] || "")
+ if(!index || !istype(active2, /datum/data/record))
+ return
+
+ var/list/comments = active2.fields["comments"]
+ index = clamp(index, 1, length(comments))
+ if(comments[index])
+ comments.Cut(index, index + 1)
+ if("search")
+ active1 = null
+ active2 = null
+ var/t1 = lowertext(params["t1"] || "")
+ if(!length(t1))
+ return
+
+ for(var/datum/data/record/R in GLOB.data_core.medical)
+ if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
+ active2 = R
+ break
+ if(!active2)
+ set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger")
+ return
for(var/datum/data/record/E in GLOB.data_core.general)
if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"])
active1 = E
+ break
screen = MED_DATA_RECORD
+ if("print_p")
+ if(!printing)
+ printing = TRUE
+ playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
+ SStgui.update_uis(src)
+ addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS)
+ else
+ return FALSE
- if(href_list["print_p"])
- if(!printing)
- printing = 1
- playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
- sleep(50)
- var/obj/item/paper/P = new /obj/item/paper(loc)
- P.info = "Medical Record "
- if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
- P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
- \nSex: [active1.fields["sex"]]
- \nAge: [active1.fields["age"]]
- \nFingerprint: [active1.fields["fingerprint"]]
- \nPhysical Status: [active1.fields["p_stat"]]
- \nMental Status: [active1.fields["m_stat"]] "}
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params)
+ . = TRUE
+ var/id = params["id"] // The modal's ID
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_OPEN)
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/question = field_edit_questions[field]
+ var/choices = field_edit_choices[field]
+ if(length(choices))
+ tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices)
+ else
+ tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"])
+ if("add_c")
+ tgui_modal_input(src, id, "Please enter your message:")
else
- P.info += "General Record Lost! "
- if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
- P.info += {" \nMedical Data
- \nBlood Type: [active2.fields["blood_type"]]
- \nDNA: [active2.fields["b_dna"]] \n
- \nMinor Disabilities: [active2.fields["mi_dis"]]
- \nDetails: [active2.fields["mi_dis_d"]] \n
- \nMajor Disabilities: [active2.fields["ma_dis"]]
- \nDetails: [active2.fields["ma_dis_d"]] \n
- \nAllergies: [active2.fields["alg"]]
- \nDetails: [active2.fields["alg_d"]] \n
- \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
- \nDetails: [active2.fields["cdi_d"]] \n
- \nImportant Notes:
- \n\t[active2.fields["notes"]] \n
- \n
- Comments/Log "}
- for(var/c in active2.fields["comments"])
- P.info += "[c] "
- else
- P.info += "Medical Record Lost! "
- P.info += ""
- P.name = "paper- 'Medical Record: [active1.fields["name"]]'"
- printing = 0
- return 1
+ return FALSE
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/list/choices = field_edit_choices[field]
+ if(length(choices) && !(answer in choices))
+ return
-/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list())
- temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
+ if(field == "age")
+ var/new_age = text2num(answer)
+ if(new_age < AGE_MIN || new_age > AGE_MAX)
+ set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger")
+ return
+ answer = new_age
+
+ if(istype(active2) && (field in active2.fields))
+ active2.fields[field] = answer
+ else if(istype(active1) && (field in active1.fields))
+ active1.fields[field] = answer
+ if("add_c")
+ if(!length(answer) || !istype(active2) || !length(authenticated))
+ return
+ active2.fields["comments"] += list(list(
+ header = "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]",
+ text = answer
+ ))
+ else
+ return FALSE
+ else
+ return FALSE
+
+/**
+ * Called when the print timer finishes
+ */
+/obj/machinery/computer/med_data/proc/print_finish()
+ var/obj/item/paper/P = new /obj/item/paper(loc)
+ P.info = "Medical Record "
+ if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
+ P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
+ \nSex: [active1.fields["sex"]]
+ \nAge: [active1.fields["age"]]
+ \nFingerprint: [active1.fields["fingerprint"]]
+ \nPhysical Status: [active1.fields["p_stat"]]
+ \nMental Status: [active1.fields["m_stat"]] "}
+ else
+ P.info += "General Record Lost! "
+ if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
+ P.info += {" \nMedical Data
+ \nBlood Type: [active2.fields["blood_type"]]
+ \nDNA: [active2.fields["b_dna"]] \n
+ \nMinor Disabilities: [active2.fields["mi_dis"]]
+ \nDetails: [active2.fields["mi_dis_d"]] \n
+ \nMajor Disabilities: [active2.fields["ma_dis"]]
+ \nDetails: [active2.fields["ma_dis_d"]] \n
+ \nAllergies: [active2.fields["alg"]]
+ \nDetails: [active2.fields["alg_d"]] \n
+ \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
+ \nDetails: [active2.fields["cdi_d"]] \n
+ \nImportant Notes:
+ \n\t[active2.fields["notes"]] \n
+ \n
+ Comments/Log "}
+ for(var/c in active2.fields["comments"])
+ P.info += "[c] "
+ else
+ P.info += "Medical Record Lost! "
+ P.info += ""
+ P.name = "paper - 'Medical Record: [active1.fields["name"]]'"
+ printing = FALSE
+ SStgui.update_uis(src)
+
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger, virus
+ */
+/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
/obj/machinery/computer/med_data/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
@@ -564,9 +502,10 @@
icon_screen = "medlaptop"
density = 0
-#undef MED_DATA_MAIN
#undef MED_DATA_R_LIST
#undef MED_DATA_MAINT
#undef MED_DATA_RECORD
#undef MED_DATA_V_DATA
#undef MED_DATA_MEDBOT
+#undef FIELD
+#undef MED_FIELD
diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm
index 6ea095028a2..bafcd2d8ca2 100644
--- a/code/game/machinery/computer/pod.dm
+++ b/code/game/machinery/computer/pod.dm
@@ -23,7 +23,7 @@
timings = list()
times = list()
synced = list()
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
for(var/ident_tag in id_tags)
if((M.id_tag == ident_tag) && !(ident_tag in synced))
@@ -49,7 +49,7 @@
return
/obj/machinery/computer/pod/proc/solo_sync(var/ident_tag)
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
if((M.id_tag == ident_tag) && !(ident_tag in synced))
synced += ident_tag
@@ -78,7 +78,7 @@
if(stat & (NOPOWER|BROKEN))
return
var/anydriver = 0
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
if(M.id_tag == ident_tag)
anydriver = 1
@@ -94,7 +94,7 @@
sleep(20)
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
if(M.id_tag == ident_tag)
M.drive()
@@ -219,7 +219,7 @@
var/ident_tag = href_list["driver"]
var/t = text2num(href_list["power"])
t = min(max(0.25, t), 16)
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.id_tag == ident_tag)
M.power = t
powers[ident_tag] = t
@@ -294,7 +294,7 @@
if(stat & (NOPOWER|BROKEN))
return
var/anydriver = 0
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
if(M.id_tag == ident_tag)
anydriver = 1
@@ -303,10 +303,12 @@
return
var/spawn_marauder[] = new()
- for(var/obj/effect/landmark/L in world)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "Marauder Entry")
spawn_marauder.Add(L)
- for(var/obj/effect/landmark/L in world)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "Marauder Exit")
var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder))
P.invisibility = 101//So it is not seen by anyone.
@@ -320,7 +322,7 @@
M.open()
sleep(20)
- for(var/obj/machinery/mass_driver/M in world)
+ for(var/obj/machinery/mass_driver/M in GLOB.machines)
if(M.z != src.z) continue
if(M.id_tag == ident_tag)
M.drive()
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 6134860733a..ed3b3d95099 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -20,221 +20,210 @@
return
if(stat & (NOPOWER|BROKEN))
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob)
+/obj/machinery/computer/robotics/proc/is_authenticated(mob/user)
+ if(!istype(user))
+ return FALSE
if(user.can_admin_interact())
- return 1
- else if(allowed(user))
- return 1
- return 0
+ return TRUE
+ if(allowed(user))
+ return TRUE
+ return FALSE
-/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/**
+ * Does this borg show up in the console
+ *
+ * Returns TRUE if a robot will show up in the console
+ * Returns FALSE if a robot will not show up in the console
+ * Arguments:
+ * * R - The [mob/living/silicon/robot] to be checked
+ */
+/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R)
+ if(!istype(R))
+ return FALSE
+ if(istype(R, /mob/living/silicon/robot/drone))
+ return FALSE
+ if(R.scrambledcodes)
+ return FALSE
+ if(!atoms_share_level(src, R))
+ return FALSE
+ return TRUE
+
+/**
+ * Check if a user can send a lockdown/detonate command to a specific borg
+ *
+ * Returns TRUE if a user can send the command (does not guarantee it will work)
+ * Returns FALSE if a user cannot
+ * Arguments:
+ * * user - The [mob/user] to be checked
+ * * R - The [mob/living/silicon/robot] to be checked
+ * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access
+ */
+/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE)
+ if(!istype(user))
+ return FALSE
+ if(!console_shows(R))
+ return FALSE
+ if(isAI(user))
+ if(R.connected_ai != user)
+ if(telluserwhy)
+ to_chat(user, "AIs can only control cyborgs which are linked to them.")
+ return FALSE
+ if(isrobot(user))
+ if(R != user)
+ if(telluserwhy)
+ to_chat(user, "Cyborgs cannot control other cyborgs.")
+ return FALSE
+ return TRUE
+
+/**
+ * Check if the user is the right kind of entity to be able to hack borgs
+ *
+ * Returns TRUE if a user is a traitor AI, or aghost
+ * Returns FALSE otherwise
+ * Arguments:
+ * * user - The [mob/user] to be checked
+ */
+/obj/machinery/computer/robotics/proc/can_hack_any(mob/user)
+ if(!istype(user))
+ return FALSE
+ if(user.can_admin_interact())
+ return TRUE
+ if(!isAI(user))
+ return FALSE
+ return (user.mind.special_role && user.mind.original == user)
+
+/**
+ * Check if the user is allowed to hack a specific borg
+ *
+ * Returns TRUE if a user can hack the specific cyborg
+ * Returns FALSE if a user cannot
+ * Arguments:
+ * * user - The [mob/user] to be checked
+ * * R - The [mob/living/silicon/robot] to be checked
+ */
+/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R)
+ if(!can_hack_any(user))
+ return FALSE
+ if(!istype(R))
+ return FALSE
+ if(R.emagged)
+ return FALSE
+ if(R.connected_ai != user)
+ return FALSE
+ return TRUE
+
+/obj/machinery/computer/robotics/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "RoboticsControlConsole", name, 500, 460, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- var/list/robots = get_cyborgs(user)
- if(robots.len)
- data["robots"] = robots
+/obj/machinery/computer/robotics/tgui_data(mob/user)
+ var/list/data = list()
+ data["auth"] = is_authenticated(user)
+ data["can_hack"] = can_hack_any(user)
+ data["cyborgs"] = list()
data["safety"] = safety
- // Also applies for cyborgs. Hides the manual self-destruct button.
- data["is_ai"] = issilicon(user)
- data["allowed"] = is_authenticated(user)
+ for(var/mob/living/silicon/robot/R in GLOB.mob_list)
+ if(!console_shows(R))
+ continue
+ var/area/A = get_area(R)
+ var/turf/T = get_turf(R)
+ var/list/cyborg_data = list(
+ name = R.name,
+ uid = R.UID(),
+ locked_down = R.lockcharge,
+ locstring = "[A.name] ([T.x], [T.y])",
+ status = R.stat,
+ health = round(R.health * 100 / R.maxHealth, 0.1),
+ charge = R.cell ? round(R.cell.percent()) : null,
+ cell_capacity = R.cell ? R.cell.maxcharge : null,
+ module = R.module ? R.module.name : "No Module Detected",
+ synchronization = R.connected_ai,
+ is_hacked = R.connected_ai && R.emagged,
+ hackable = can_hack(user, R),
+ )
+ data["cyborgs"] += list(cyborg_data)
+ data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user))
return data
-/obj/machinery/computer/robotics/Topic(href, href_list)
+/obj/machinery/computer/robotics/tgui_act(action, params)
if(..())
- return 1
-
- var/mob/user = usr
- if(!is_authenticated(user))
- to_chat(user, "Access denied.")
return
-
- // Destroys the cyborg
- if(href_list["detonate"])
- var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"])
- if(!target || !istype(target))
- return
- if(isAI(user) && (target.connected_ai != user))
- to_chat(user, "Access Denied. This robot is not linked to you.")
- return
- // Cyborgs may blow up themselves via the console
- if((isrobot(user) && user != target) || !is_authenticated(user))
- to_chat(user, "Access Denied.")
- return
- var/choice = input("Really detonate [target.name]?") in list ("Yes", "No")
- if(choice != "Yes")
- return
- if(!target || !istype(target))
- return
-
- // Antagonistic cyborgs? Left here for downstream
- if(target.mind && target.mind.special_role && target.emagged)
- to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.")
- target.ResetSecurityCodes()
- else
- message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!")
- log_game("\[key_name(usr)] detonated [key_name(target)]!")
- to_chat(target, "Self-destruct command received.")
- if(target.connected_ai)
- to_chat(target.connected_ai, "
ALERT - Cyborg detonation detected: [target.name] ")
- spawn(10)
- target.self_destruct()
-
- // Locks or unlocks the cyborg
- else if(href_list["lockdown"])
- var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"])
- if(!target || !istype(target))
- return
-
- if(isAI(user) && (target.connected_ai != user))
- to_chat(user, "Access Denied. This robot is not linked to you.")
- return
-
- if(isrobot(user))
- to_chat(user, "Access Denied.")
- return
-
- var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No")
- if(choice != "Yes")
- return
-
- if(!target || !istype(target))
- return
-
- message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!")
- log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!")
- target.SetLockdown(!target.lockcharge)
- to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]")
- if(target.connected_ai)
- to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name] ")
-
- // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs.
- else if(href_list["hack"])
- var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"])
- if(!target || !istype(target))
- return
-
- // Antag AI checks
- if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user))
- to_chat(user, "Access Denied.")
- return
-
- if(target.connected_ai != user)
- to_chat(user, "Access Denied. This robot is not linked to you.")
- return
-
- if(target.emagged)
- to_chat(user, "Robot is already hacked.")
- return
-
- var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No")
- if(choice != "Yes")
- return
-
- if(!target || !istype(target))
- return
-
- message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!")
- log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!")
- target.emagged = 1
- to_chat(target, "Failsafe protocols overriden. New tools available.")
-
- // Arms the emergency self-destruct system
- else if(href_list["arm"])
- if(istype(user, /mob/living/silicon))
- to_chat(user, "Access Denied.")
- return
-
- safety = !safety
- to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.")
-
- // Destroys all accessible cyborgs if safety is disabled
- else if(href_list["nuke"])
- if(istype(user, /mob/living/silicon))
- to_chat(user, "Access Denied")
- return
- if(safety)
- to_chat(user, "Self-destruct aborted - safety active")
- return
-
- message_admins("[key_name_admin(usr)] detonated all cyborgs!")
- log_game("\[key_name(usr)] detonated all cyborgs!")
-
- for(var/mob/living/silicon/robot/R in GLOB.mob_list)
- if(istype(R, /mob/living/silicon/robot/drone))
- continue
- // Ignore antagonistic cyborgs
- if(R.scrambledcodes)
- continue
+ . = FALSE
+ if(!is_authenticated(usr))
+ to_chat(usr, "Access denied.")
+ return
+ switch(action)
+ if("arm") // Arms the emergency self-destruct system
+ if(issilicon(usr))
+ to_chat(usr, "Access Denied (silicon detected)")
+ return
+ safety = !safety
+ to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.")
+ . = TRUE
+ if("nuke") // Destroys all accessible cyborgs if safety is disabled
+ if(issilicon(usr))
+ to_chat(usr, "Access Denied (silicon detected)")
+ return
+ if(safety)
+ to_chat(usr, "Self-destruct aborted - safety active")
+ return
+ message_admins("[key_name_admin(usr)] detonated all cyborgs!")
+ log_game("\[key_name(usr)] detonated all cyborgs!")
+ for(var/mob/living/silicon/robot/R in GLOB.mob_list)
+ if(istype(R, /mob/living/silicon/robot/drone))
+ continue
+ // Ignore antagonistic cyborgs
+ if(R.scrambledcodes)
+ continue
+ to_chat(R, "Self-destruct command received.")
+ if(R.connected_ai)
+ to_chat(R.connected_ai, "
ALERT - Cyborg detonation detected: [R.name] ")
+ R.self_destruct()
+ . = TRUE
+ if("killbot") // destroys one specific cyborg
+ var/mob/living/silicon/robot/R = locateUID(params["uid"])
+ if(!can_control(usr, R, TRUE))
+ return
+ if(R.mind && R.mind.special_role && R.emagged)
+ to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.")
+ R.ResetSecurityCodes()
+ . = TRUE
+ return
+ var/turf/T = get_turf(R)
+ message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!")
+ log_game("\[key_name(usr)] detonated [key_name(R)]!")
to_chat(R, "Self-destruct command received.")
if(R.connected_ai)
to_chat(R.connected_ai, "
ALERT - Cyborg detonation detected: [R.name] ")
- spawn(10)
- R.self_destruct()
-
-// Proc: get_cyborgs()
-// Parameters: 1 (operator - mob which is operating the console.)
-// Description: Returns NanoUI-friendly list of accessible cyborgs.
-/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator)
- var/list/robots = list()
-
- for(var/mob/living/silicon/robot/R in GLOB.mob_list)
- // Ignore drones
- if(istype(R, /mob/living/silicon/robot/drone))
- continue
- // Ignore antagonistic cyborgs
- if(R.scrambledcodes)
- continue
-
- var/list/robot = list()
- robot["name"] = R.name
- if(R.stat)
- robot["status"] = "Not Responding"
- else if(!R.canmove)
- robot["status"] = "Lockdown"
- else
- robot["status"] = "Operational"
-
- if(R.cell)
- robot["cell"] = 1
- robot["cell_capacity"] = R.cell.maxcharge
- robot["cell_current"] = R.cell.charge
- robot["cell_percentage"] = round(R.cell.percent())
- else
- robot["cell"] = 0
-
- var/turf/pos = get_turf(R)
- var/area/bot_area = get_area(R)
- robot["xpos"] = pos.x
- robot["ypos"] = pos.y
- robot["zpos"] = pos.z
- robot["area"] = format_text(bot_area.name)
-
- robot["health"] = round(R.health * 100 / R.maxHealth,0.1)
-
- robot["module"] = R.module ? R.module.name : "None"
- robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None"
- robot["hackable"] = 0
- // Antag AIs know whether linked cyborgs are hacked or not.
- if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator))
- robot["hacked"] = R.emagged ? 1 : 0
- robot["hackable"] = R.emagged? 0 : 1
- robots.Add(list(robot))
- return robots
-
-// Proc: get_cyborg_by_name()
-// Parameters: 1 (name - Cyborg we are trying to find)
-// Description: Helper proc for finding cyborg by name
-/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name)
- if(!name)
- return
- for(var/mob/living/silicon/robot/R in GLOB.mob_list)
- if(R.name == name)
- return R
+ R.self_destruct()
+ . = TRUE
+ if("stopbot") // lock or unlock the borg
+ if(isrobot(usr))
+ to_chat(usr, "Access Denied.")
+ return
+ var/mob/living/silicon/robot/R = locateUID(params["uid"])
+ if(!can_control(usr, R, TRUE))
+ return
+ message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!")
+ log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!")
+ R.SetLockdown(!R.lockcharge)
+ to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]")
+ if(R.connected_ai)
+ to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name] ")
+ . = TRUE
+ if("hackbot") // AIs hacking/emagging a borg
+ var/mob/living/silicon/robot/R = locateUID(params["uid"])
+ if(!can_hack(usr, R))
+ return
+ var/choice = input("Really hack [R.name]? This cannot be undone.") in list("Yes", "No")
+ if(choice != "Yes")
+ return
+ log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
+ message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!")
+ R.emagged = TRUE
+ to_chat(R, "Failsafe protocols overriden. New tools available.")
+ . = TRUE
diff --git a/code/game/machinery/computer/sm_monitor.dm b/code/game/machinery/computer/sm_monitor.dm
new file mode 100644
index 00000000000..f2e01e6c4fa
--- /dev/null
+++ b/code/game/machinery/computer/sm_monitor.dm
@@ -0,0 +1,147 @@
+/obj/machinery/computer/sm_monitor
+ name = "supermatter monitoring console"
+ desc = "Used to monitor supermatter shards."
+ icon_keyboard = "power_key"
+ icon_screen = "smmon_0"
+ circuit = /obj/item/circuitboard/sm_monitor
+ light_color = LIGHT_COLOR_YELLOW
+ /// Cache-list of all supermatter shards
+ var/list/supermatters
+ /// Last status of the active supermatter for caching purposes
+ var/last_status
+ /// Reference to the active shard
+ var/obj/machinery/power/supermatter_shard/active
+
+/obj/machinery/computer/sm_monitor/Destroy()
+ active = null
+ return ..()
+
+/obj/machinery/computer/sm_monitor/attack_ai(mob/user)
+ attack_hand(user)
+
+/obj/machinery/computer/sm_monitor/attack_hand(mob/user)
+ add_fingerprint(user)
+ if(stat & (BROKEN|NOPOWER))
+ return
+ tgui_interact(user)
+
+/obj/machinery/computer/sm_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "SupermatterMonitor", name, 600, 325, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/sm_monitor/tgui_data(mob/user)
+ var/list/data = list()
+
+ if(istype(active))
+ var/turf/T = get_turf(active)
+ // If we somehow delam during this proc, handle it somewhat
+ if(!T)
+ active = null
+ refresh()
+ return
+ var/datum/gas_mixture/air = T.return_air()
+ if(!air)
+ active = null
+ return
+
+ data["active"] = TRUE
+ data["SM_integrity"] = active.get_integrity()
+ data["SM_power"] = active.power
+ data["SM_ambienttemp"] = air.temperature
+ data["SM_ambientpressure"] = air.return_pressure()
+ //data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01)
+ var/other_moles = air.total_trace_moles()
+ var/TM = air.total_moles()
+ if(TM)
+ data["SM_gas_O2"] = round(100*air.oxygen/TM, 0.01)
+ data["SM_gas_CO2"] = round(100*air.carbon_dioxide/TM, 0.01)
+ data["SM_gas_N2"] = round(100*air.nitrogen/TM, 0.01)
+ data["SM_gas_PL"] = round(100*air.toxins/TM, 0.01)
+ if(other_moles)
+ data["SM_gas_OTHER"] = round(100 * other_moles / TM, 0.01)
+ else
+ data["SM_gas_OTHER"] = 0
+ else
+ data["SM_gas_O2"] = 0
+ data["SM_gas_CO2"] = 0
+ data["SM_gas_N2"] = 0
+ data["SM_gas_PH"] = 0
+ data["SM_gas_OTHER"] = 0
+ else
+ var/list/SMS = list()
+ for(var/I in supermatters)
+ var/obj/machinery/power/supermatter_shard/S = I
+ var/area/A = get_area(S)
+ if(!A)
+ continue
+
+ SMS.Add(list(list(
+ "area_name" = A.name,
+ "integrity" = S.get_integrity(),
+ "uid" = S.UID()
+ )))
+
+ data["active"] = FALSE
+ data["supermatters"] = SMS
+
+ return data
+
+/**
+ * Supermatter List Refresher
+ *
+ * This proc loops through the list of supermatters in the atmos SS and adds them to this console's cache list
+ */
+/obj/machinery/computer/sm_monitor/proc/refresh()
+ supermatters = list()
+ var/turf/T = get_turf(tgui_host()) // Get the TGUI host incase this ever turned into a supermatter monitoring module for AIs to use or something
+ if(!T)
+ return
+ for(var/obj/machinery/power/supermatter_shard/S in SSair.atmos_machinery)
+ // Delaminating, not within coverage, not on a tile.
+ if(!(is_station_level(S.z) || is_mining_level(S.z) || atoms_share_level(S, T) || !istype(S.loc, /turf/simulated/)))
+ continue
+ supermatters.Add(S)
+
+ if(!(active in supermatters))
+ active = null
+
+/obj/machinery/computer/sm_monitor/process()
+ if(stat & (NOPOWER|BROKEN))
+ return FALSE
+
+ if(active)
+ var/new_status = active.get_status()
+ if(last_status != new_status)
+ last_status = new_status
+ if(last_status == SUPERMATTER_ERROR)
+ last_status = SUPERMATTER_INACTIVE
+ icon_screen = "smmon_[last_status]"
+ update_icon()
+
+ return TRUE
+
+/obj/machinery/computer/sm_monitor/tgui_act(action, params)
+ if(..())
+ return
+
+ if(stat & (BROKEN|NOPOWER))
+ return
+
+ . = TRUE
+
+ switch(action)
+ if("refresh")
+ refresh()
+
+ if("view")
+ var/newuid = params["view"]
+ for(var/obj/machinery/power/supermatter_shard/S in supermatters)
+ if(S.UID() == newuid)
+ active = S
+ break
+
+ if("back")
+ active = null
+
diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm
index f509ee42d34..2ba3b25b6f6 100644
--- a/code/game/machinery/computer/specops_shuttle.dm
+++ b/code/game/machinery/computer/specops_shuttle.dm
@@ -93,7 +93,7 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0)
GLOB.specops_shuttle_at_station = 0
- for(var/obj/machinery/computer/specops_shuttle/S in world)
+ for(var/obj/machinery/computer/specops_shuttle/S in GLOB.machines)
S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY
qdel(announcer)
@@ -160,10 +160,12 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0)
sleep(10)
var/spawn_marauder[] = new()
- for(var/obj/effect/landmark/L in world)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "Marauder Entry")
spawn_marauder.Add(L.loc)
- for(var/obj/effect/landmark/L in world)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "Marauder Exit")
var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder))
//P.invisibility = 101//So it is not seen by anyone.
@@ -233,7 +235,7 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0)
var/mob/M = locate(/mob) in T
to_chat(M, "You have arrived to [station_name()]. Commence operation!")
- for(var/obj/machinery/computer/specops_shuttle/S in world)
+ for(var/obj/machinery/computer/specops_shuttle/S in GLOB.machines)
S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY
qdel(announcer)
@@ -241,7 +243,7 @@ GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0)
/proc/specops_can_move()
if(GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom)
return 0
- for(var/obj/machinery/computer/specops_shuttle/S in world)
+ for(var/obj/machinery/computer/specops_shuttle/S in GLOB.machines)
if(world.timeofday <= S.specops_shuttle_timereset)
return 0
return 1
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index 607918442ea..3b4631b171a 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -6,48 +6,91 @@
icon_screen = "alert:0"
light_color = LIGHT_COLOR_CYAN
circuit = /obj/item/circuitboard/stationalert_engineering
- var/datum/nano_module/alarm_monitor/alarm_monitor
- var/monitor_type = /datum/nano_module/alarm_monitor/engineering
+ var/ui_x = 325
+ var/ui_y = 500
+ var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
-/obj/machinery/computer/station_alert/security
- monitor_type = /datum/nano_module/alarm_monitor/security
- circuit = /obj/item/circuitboard/stationalert_security
-
-/obj/machinery/computer/station_alert/all
- monitor_type = /datum/nano_module/alarm_monitor/all
- circuit = /obj/item/circuitboard/stationalert_all
-
-/obj/machinery/computer/station_alert/New()
- ..()
- alarm_monitor = new monitor_type(src)
- alarm_monitor.register(src, /obj/machinery/computer/station_alert/.proc/update_icon)
+/obj/machinery/computer/station_alert/Initialize(mapload)
+ . = ..()
+ GLOB.alert_consoles += src
+ RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
+ RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
/obj/machinery/computer/station_alert/Destroy()
- alarm_monitor.unregister(src)
- QDEL_NULL(alarm_monitor)
+ GLOB.alert_consoles -= src
return ..()
/obj/machinery/computer/station_alert/attack_ai(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- interact(user)
+ tgui_interact(user)
/obj/machinery/computer/station_alert/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
- interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/station_alert/interact(mob/user)
- alarm_monitor.ui_interact(user)
+/obj/machinery/computer/station_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "StationAlertConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/station_alert/tgui_data(mob/user)
+ var/list/data = list()
+
+ data["alarms"] = list()
+ for(var/class in SSalarm.alarms)
+ if(!(class in alarms_listend_for))
+ continue
+ data["alarms"][class] = list()
+ for(var/area in SSalarm.alarms[class])
+ for(var/thing in SSalarm.alarms[class][area][3])
+ var/atom/A = locateUID(thing)
+ if(atoms_share_level(A, src))
+ data["alarms"][class] += area
+
+ return data
+
+/obj/machinery/computer/station_alert/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
+ if(!(class in alarms_listend_for))
+ return
+ if(alarmsource.z != z)
+ return
+ if(stat & (BROKEN))
+ return
+ update_icon()
+
+/obj/machinery/computer/station_alert/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
+ if(!(class in alarms_listend_for))
+ return
+ if(origin.z != z)
+ return
+ if(stat & (BROKEN))
+ return
+ update_icon()
/obj/machinery/computer/station_alert/update_icon()
- if(alarm_monitor)
- var/list/alarms = alarm_monitor.major_alarms()
- if(alarms.len)
- icon_screen = "alert:2"
- else
- icon_screen = "alert:0"
+ var/active_alarms = FALSE
+ var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
+ for(var/cat in temp_alarm_list)
+ if(!(cat in alarms_listend_for))
+ continue
+ var/list/list/L = temp_alarm_list[cat].Copy()
+ for(var/alarm in L)
+ var/list/list/alm = L[alarm].Copy()
+ var/list/list/sources = alm[3].Copy()
+ for(var/thing in sources)
+ var/atom/A = locateUID(thing)
+ if(A && A.z != z)
+ L -= alarm
+ if(length(L))
+ active_alarms = TRUE
+ if(active_alarms)
+ icon_screen = "alert:2"
+ else
+ icon_screen = "alert:0"
..()
diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm
index 0a65b6be408..7ec7bd7a026 100644
--- a/code/game/machinery/computer/syndicate_specops_shuttle.dm
+++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm
@@ -23,7 +23,7 @@ GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0)
/proc/syndicate_elite_process()
var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work.
- var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located?
+ //var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located?
var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky.
var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values.
@@ -63,7 +63,6 @@ GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0)
to_chat(usr, "The Syndicate Elite shuttle is unable to leave.")
return
- sleep(600)
/*
//Begin Marauder launchpad.
spawn(0)//So it parallel processes it.
@@ -129,11 +128,12 @@ GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0)
if("ASSAULT3")
spawn(0)
M.close()
- */
elite_squad.readyreset()//Reset firealarm after the team launched.
+ */
//End Marauder launchpad.
- for(var/obj/effect/landmark/L in GLOB.landmarks_list)
+ for(var/thing in GLOB.landmarks_list)
+ var/obj/effect/landmark/L = thing
if(L.name == "Syndicate Breach Area")
explosion(L.loc,4,6,8,10,0)
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 92db2f4a419..ee29504fe99 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -218,7 +218,7 @@
//Machine Frame Circuit Boards
/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit,
-micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells.
+micro-manipulator, glass sheets, beaker, Microlaser, matter bin, power cells.
Note: Once everything is added to the public areas, will add MAT_METAL and MAT_GLASS to circuit boards since autolathe won't be able
to destroy them and players will be able to make replacements.
*/
@@ -226,7 +226,6 @@ to destroy them and players will be able to make replacements.
name = "circuit board (Booze-O-Mat Vendor)"
board_type = "machine"
origin_tech = "programming=1"
- frame_desc = "Requires 1 Resupply Canister."
build_path = /obj/machinery/vending/boozeomat
req_components = list(/obj/item/vending_refill/boozeomat = 1)
@@ -282,7 +281,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/power/smes
board_type = "machine"
origin_tech = "programming=3;powerstorage=3;engineering=3"
- frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor."
req_components = list(
/obj/item/stack/cable_coil = 5,
/obj/item/stock_parts/cell = 5,
@@ -321,7 +319,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer
board_type = "machine"
origin_tech = "programming=3;plasmatech=3"
- frame_desc = "Requires 2 Matter Bins, 2 Micro Lasers, 1 piece of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/micro_laser = 2,
@@ -346,7 +343,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/recharger
board_type = "machine"
origin_tech = "powerstorage=3;materials=2"
- frame_desc = "Requires 1 Capacitor"
req_components = list(/obj/item/stock_parts/capacitor = 1)
/obj/item/circuitboard/snow_machine
@@ -354,7 +350,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/snow_machine
board_type = "machine"
origin_tech = "programming=2;materials=2"
- frame_desc = "Requires 1 Matter Bin and 1 Micro Laser."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/micro_laser = 1)
@@ -364,7 +359,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/biogenerator
board_type = "machine"
origin_tech = "programming=2;biotech=3;materials=3"
- frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -376,7 +370,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/plantgenes
board_type = "machine"
origin_tech = "programming=3;biotech=3"
- frame_desc = "Requires 1 Manipulator, 1 Micro Laser, 1 Console Screen, and 1 Scanning Module."
req_components = list(
/obj/item/stock_parts/manipulator = 1,
/obj/item/stock_parts/micro_laser = 1,
@@ -390,7 +383,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/seed_extractor
board_type = "machine"
origin_tech = "programming=1"
- frame_desc = "Requires 1 Matter Bin and 1 Manipulator."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/manipulator = 1)
@@ -400,7 +392,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/hydroponics/constructable
board_type = "machine"
origin_tech = "programming=1;biotech=2"
- frame_desc = "Requires 2 Matter Bins, 1 Manipulator, and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 1,
@@ -411,7 +402,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/kitchen_machine/microwave
board_type = "machine"
origin_tech = "programming=2;magnets=2"
- frame_desc = "Requires 1 Micro Laser, 2 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/cable_coil = 2,
@@ -422,7 +412,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/kitchen_machine/oven
board_type = "machine"
origin_tech = "programming=2;magnets=2"
- frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stack/cable_coil = 5,
@@ -433,7 +422,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/kitchen_machine/grill
board_type = "machine"
origin_tech = "programming=2;magnets=2"
- frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stack/cable_coil = 5,
@@ -444,7 +432,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/kitchen_machine/candy_maker
board_type = "machine"
origin_tech = "programming=2;magnets=2"
- frame_desc = "Requires 1 Manipulator, 5 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/manipulator = 1,
/obj/item/stack/cable_coil = 5,
@@ -455,7 +442,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/cooker/deepfryer
board_type = "machine"
origin_tech = "programming=1"
- frame_desc = "Requires 2 Micro Lasers and 5 pieces of cable."
req_components = list(
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stack/cable_coil = 5)
@@ -568,7 +554,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/chem_dispenser
board_type = "machine"
origin_tech = "materials=4;programming=4;plasmatech=4;biotech=3"
- frame_desc = "Requires 2 Matter Bins, 1 Capacitor, 1 Manipulator, 1 Console Screen, and 1 Power Cell."
req_components = list( /obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/capacitor = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -609,7 +594,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/chem_heater
board_type = "machine"
origin_tech = "programming=2;engineering=2;biotech=2"
- frame_desc = "Requires 1 Micro Laser and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/sheet/glass = 1)
@@ -619,7 +603,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/reagentgrinder/empty
board_type = "machine"
origin_tech = "materials=2;engineering=2;biotech=2"
- frame_desc = "Requires 2 Manipulators and 1 Matter Bin."
req_components = list(
/obj/item/stock_parts/manipulator = 2,
/obj/item/stock_parts/matter_bin = 1)
@@ -640,7 +623,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/r_n_d/destructive_analyzer
board_type = "machine"
origin_tech = "magnets=2;engineering=2;programming=2"
- frame_desc = "Requires 1 Scanning Module, 1 Manipulator, and 1 Micro-Laser."
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -651,7 +633,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/autolathe
board_type = "machine"
origin_tech = "engineering=2;programming=2"
- frame_desc = "Requires 3 Matter Bins, 1 Manipulator, and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 3,
/obj/item/stock_parts/manipulator = 1,
@@ -662,7 +643,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/r_n_d/protolathe
board_type = "machine"
origin_tech = "engineering=2;programming=2"
- frame_desc = "Requires 2 Matter Bins, 2 Manipulators, and 2 Beakers."
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 2,
@@ -681,7 +661,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/r_n_d/circuit_imprinter
board_type = "machine"
origin_tech = "engineering=2;programming=2"
- frame_desc = "Requires 1 Matter Bin, 1 Manipulator, and 2 Beakers."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -692,7 +671,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/power/port_gen/pacman
board_type = "machine"
origin_tech = "programming=2;powerstorage=3;plasmatech=3;engineering=3"
- frame_desc = "Requires 1 Matter Bin, 1 Micro-Laser, 2 Pieces of Cable, and 1 Capacitor."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/micro_laser = 1,
@@ -714,7 +692,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/r_n_d/server
board_type = "machine"
origin_tech = "programming=3"
- frame_desc = "Requires 2 pieces of cable, and 1 Scanning Module."
req_components = list(
/obj/item/stack/cable_coil = 2,
/obj/item/stock_parts/scanning_module = 1)
@@ -724,7 +701,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/mecha_part_fabricator
board_type = "machine"
origin_tech = "programming=2;engineering=2"
- frame_desc = "Requires 2 Matter Bins, 1 Manipulator, 1 Micro-Laser and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 1,
@@ -736,7 +712,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/mecha_part_fabricator/spacepod
board_type = "machine"
origin_tech = "programming=2;engineering=2"
- frame_desc = "Requires 2 Matter Bins, 1 Manipulators, 1 Micro-Lasers, and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator = 1,
@@ -749,7 +724,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/clonepod
board_type = "machine"
origin_tech = "programming=2;biotech=2"
- frame_desc = "Requires 2 Manipulator, 2 Scanning Module, 2 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stack/cable_coil = 2,
/obj/item/stock_parts/scanning_module = 2,
@@ -761,7 +735,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/dna_scannernew
board_type = "machine"
origin_tech = "programming=2;biotech=2"
- frame_desc = "Requires 1 Scanning Module, 1 Manipulator, 1 Micro-Laser, 2 pieces of cable and 1 Console Screen."
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -774,7 +747,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/mech_bay_recharge_port
board_type = "machine"
origin_tech = "programming=3;powerstorage=3;engineering=3"
- frame_desc = "Requires 1 piece of cable and 5 Capacitors."
req_components = list(
/obj/item/stack/cable_coil = 1,
/obj/item/stock_parts/capacitor = 5)
@@ -784,7 +756,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/teleport/hub
board_type = "machine"
origin_tech = "programming=3;engineering=4;bluespace=4;materials=4"
- frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin."
req_components = list(
/obj/item/stack/ore/bluespace_crystal = 3,
/obj/item/stock_parts/matter_bin = 1)
@@ -794,7 +765,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/teleport/station
board_type = "machine"
origin_tech = "programming=4;engineering=4;bluespace=4;plasmatech=3"
- frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen."
req_components = list(
/obj/item/stack/ore/bluespace_crystal = 2,
/obj/item/stock_parts/capacitor = 2,
@@ -805,7 +775,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/teleport/perma
board_type = "machine"
origin_tech = "programming=3;engineering=4;bluespace=4;materials=4"
- frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin."
req_components = list(
/obj/item/stack/ore/bluespace_crystal = 3,
/obj/item/stock_parts/matter_bin = 1)
@@ -825,7 +794,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/telepad
board_type = "machine"
origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4"
- frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen."
req_components = list(
/obj/item/stack/ore/bluespace_crystal = 2,
/obj/item/stock_parts/capacitor = 1,
@@ -837,7 +805,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/quantumpad
board_type = "machine"
origin_tech = "programming=3;engineering=3;plasmatech=3;bluespace=4"
- frame_desc = "Requires 1 Bluespace Crystal, 1 Capacitor, 1 piece of cable and 1 Manipulator."
req_components = list(
/obj/item/stack/ore/bluespace_crystal = 1,
/obj/item/stock_parts/capacitor = 1,
@@ -849,7 +816,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/sleeper
board_type = "machine"
origin_tech = "programming=3;biotech=2;engineering=3"
- frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 2 Console Screens."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/manipulator = 1,
@@ -870,7 +836,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/bodyscanner
board_type = "machine"
origin_tech = "programming=3;biotech=2;engineering=3"
- frame_desc = "Requires 1 Scanning Module, 2 pieces of cable and 2 Console Screens."
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
/obj/item/stack/cable_coil = 2,
@@ -881,7 +846,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/atmospherics/unary/cryo_cell
board_type = "machine"
origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3"
- frame_desc = "Requires 1 Matter Bin, 1 piece of cable and 4 Console Screens."
req_components = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/stack/cable_coil = 1,
@@ -892,7 +856,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/recharge_station
board_type = "machine"
origin_tech = "powerstorage=3;engineering=3"
- frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator."
req_components = list(
/obj/item/stock_parts/capacitor = 2,
/obj/item/stock_parts/cell = 1,
@@ -904,7 +867,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/tcomms/relay
board_type = "machine"
origin_tech = "programming=2;engineering=2;bluespace=2"
- frame_desc = "Requires 2 Manipulators and 2 Cable Coil."
req_components = list(/obj/item/stock_parts/manipulator = 2, /obj/item/stack/cable_coil = 2)
/obj/item/circuitboard/tcomms/core
@@ -912,7 +874,6 @@ to destroy them and players will be able to make replacements.
build_path = /obj/machinery/tcomms/core
board_type = "machine"
origin_tech = "programming=2;engineering=2"
- frame_desc = "Requires 2 Manipulators and 2 Cable Coil."
req_components = list(/obj/item/stock_parts/manipulator = 2, /obj/item/stack/cable_coil = 2)
// End telecomms circuit boards
/obj/item/circuitboard/ore_redemption
@@ -975,50 +936,3 @@ to destroy them and players will be able to make replacements.
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/cable_coil = 3,
/obj/item/stack/sheet/glass = 1)
-
-//Selectable mode board, like vending machine boards
-/obj/item/circuitboard/logic_gate
- name = "circuit board (Logic Connector)"
- build_path = /obj/machinery/logic_gate
- board_type = "machine"
- origin_tech = "programming=1" //This stuff is pretty much the absolute basis of programming, so it's mostly useless for research
- req_components = list(/obj/item/stack/cable_coil = 1)
-
- var/list/names_paths = list(
- "NOT Gate" = /obj/machinery/logic_gate/not,
- "OR Gate" = /obj/machinery/logic_gate/or,
- "AND Gate" = /obj/machinery/logic_gate/and,
- "NAND Gate" = /obj/machinery/logic_gate/nand,
- "NOR Gate" = /obj/machinery/logic_gate/nor,
- "XOR Gate" = /obj/machinery/logic_gate/xor,
- "XNOR Gate" = /obj/machinery/logic_gate/xnor,
- "STATUS Gate" = /obj/machinery/logic_gate/status,
- "CONVERT Gate" = /obj/machinery/logic_gate/convert
- )
-
-/obj/item/circuitboard/logic_gate/New()
- ..()
- if(build_path == /obj/machinery/logic_gate) //If we spawn the base type board (determined by the base type machine as the build path), become a random gate board
- var/new_path = names_paths[pick(names_paths)]
- set_type(new_path)
-
-/obj/item/circuitboard/logic_gate/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/screwdriver))
- set_type(null, user)
- return
- return ..()
-
-/obj/item/circuitboard/logic_gate/proc/set_type(typepath, mob/user)
- var/new_name = "Logic Base"
- if(!typepath)
- new_name = input("Circuit Setting", "What would you change the board setting to?") in names_paths
- typepath = names_paths[new_name]
- else
- for(var/name in names_paths)
- if(names_paths[name] == typepath)
- new_name = name
- break
- build_path = typepath
- name = "circuit board ([new_name])"
- if(user)
- to_chat(user, "You set the board to [new_name].")
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index c7dc139446a..ba8f430937c 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -103,7 +103,7 @@
beaker.forceMove(drop_location())
beaker = null
-/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob)
+/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user)
if(O.loc == user) //no you can't pull things out of your ass
return
if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other
@@ -140,6 +140,7 @@
add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL)
if(user.pulling == L)
user.stop_pulling()
+ SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
@@ -177,14 +178,14 @@
return FALSE
-/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob)
+/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user)
if(user.stat)
return
go_out()
return
/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user)
- return attack_hand(user)
+ tgui_interact(user)
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user)
if(user == occupant)
@@ -194,36 +195,18 @@
to_chat(usr, "Close the maintenance panel first.")
return
- ui_interact(user)
+ tgui_interact(user)
-
- /**
- * The ui_interact proc is used to open and update Nano UIs
- * If ui_interact is not used then the UI will not update correctly
- * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
- *
- * @param user /mob The mob who is interacting with this ui
- * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
- *
- * @return nothing
- */
-/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480)
- // open the new ui window
+ ui = new(user, src, ui_key, "Cryo", "Cryo Cell", 520, 490)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user)
var/data[0]
data["isOperating"] = on
- data["hasOccupant"] = occupant ? 1 : 0
+ data["hasOccupant"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -237,7 +220,7 @@
occupantData["toxLoss"] = occupant.getToxLoss()
occupantData["fireLoss"] = occupant.getFireLoss()
occupantData["bodyTemperature"] = occupant.bodytemperature
- data["occupant"] = occupantData;
+ data["occupant"] = occupantData
data["cellTemperature"] = round(air_contents.temperature)
data["cellTemperatureStatus"] = "good"
@@ -246,7 +229,7 @@
else if(air_contents.temperature > TCRYO)
data["cellTemperatureStatus"] = "average"
- data["isBeakerLoaded"] = beaker ? 1 : 0
+ data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["beakerLabel"] = null
data["beakerVolume"] = 0
if(beaker)
@@ -259,48 +242,44 @@
data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE
return data
-/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
- if(usr == occupant)
- return 0 // don't update UIs attached to this object
+/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params)
+ if(..() || usr == occupant)
+ return
+ if(stat & (NOPOWER|BROKEN))
+ return
- if(..())
- return 0 // don't update UIs attached to this object
-
- if(href_list["switchOn"])
- on = TRUE
- update_icon()
-
- if(href_list["switchOff"])
- on = FALSE
- update_icon()
-
- if(href_list["auto_eject_healthy_on"])
- auto_eject_prefs |= AUTO_EJECT_HEALTHY
-
- if(href_list["auto_eject_healthy_off"])
- auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
-
- if(href_list["auto_eject_dead_on"])
- auto_eject_prefs |= AUTO_EJECT_DEAD
-
- if(href_list["auto_eject_dead_off"])
- auto_eject_prefs &= ~AUTO_EJECT_DEAD
-
- if(href_list["ejectBeaker"])
- if(beaker)
+ . = TRUE
+ switch(action)
+ if("switchOn")
+ on = TRUE
+ update_icon()
+ if("switchOff")
+ on = FALSE
+ update_icon()
+ if("auto_eject_healthy_on")
+ auto_eject_prefs |= AUTO_EJECT_HEALTHY
+ if("auto_eject_healthy_off")
+ auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
+ if("auto_eject_dead_on")
+ auto_eject_prefs |= AUTO_EJECT_DEAD
+ if("auto_eject_dead_off")
+ auto_eject_prefs &= ~AUTO_EJECT_DEAD
+ if("ejectBeaker")
+ if(!beaker)
+ return
beaker.forceMove(get_step(loc, SOUTH))
beaker = null
-
- if(href_list["ejectOccupant"])
- if(!occupant || isslime(usr) || ispAI(usr))
- return 0 // don't update UIs attached to this object
- add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
- go_out()
+ if("ejectOccupant")
+ if(!occupant || isslime(usr) || ispAI(usr))
+ return
+ add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
+ go_out()
+ else
+ return FALSE
add_fingerprint(usr)
- return 1 // update UIs attached to this object
-/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params)
+/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params)
if(istype(G, /obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/B = G
if(beaker)
@@ -313,6 +292,7 @@
beaker = B
add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]")
user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!")
+ SStgui.update_uis(src)
return
if(exchange_parts(user, G))
@@ -357,7 +337,7 @@
return
if(occupant)
- var/image/pickle = image(occupant.icon, occupant.icon_state)
+ var/mutable_appearance/pickle = mutable_appearance(occupant.icon, occupant.icon_state)
pickle.overlays = occupant.overlays
pickle.pixel_y = 22
@@ -443,6 +423,9 @@
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(get_step(loc, SOUTH))
+/obj/machinery/atmospherics/unary/cryo_cell/force_eject_occupant()
+ go_out()
+
/// Called when either the occupant is dead and the AUTO_EJECT_DEAD flag is present, OR the occupant is alive, has no external damage, and the AUTO_EJECT_HEALTHY flag is present.
/obj/machinery/atmospherics/unary/cryo_cell/proc/auto_eject(eject_flag)
on = FALSE
@@ -452,8 +435,9 @@
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
if(AUTO_EJECT_DEAD)
playsound(loc, 'sound/machines/buzz-sigh.ogg', 40)
+ SStgui.update_uis(src)
-/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob)
+/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M)
if(!istype(M))
to_chat(usr, "The cryo cell cannot handle such a lifeform!")
return
@@ -516,7 +500,7 @@
if(stat & (NOPOWER|BROKEN))
return
- if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other
+ if(usr.incapacitated() || usr.buckled) //are you cuffed, dying, lying, stunned or other
return
put_mob(usr)
@@ -527,7 +511,7 @@
/datum/data/function/proc/reset()
return
-/datum/data/function/proc/r_input(href, href_list, mob/user as mob)
+/datum/data/function/proc/r_input(href, href_list, mob/user)
return
/datum/data/function/proc/display()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 49b75588468..a13f87845ff 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -302,6 +302,7 @@
// Eject dead people
if(occupant.stat == DEAD)
go_out()
+ return
// Allow a gap between entering the pod and actually despawning.
if(world.time - time_entered < time_till_despawn)
@@ -671,6 +672,9 @@
to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.")
return
+ if(usr.incapacitated() || usr.buckled) //are you cuffed, dying, lying, stunned or other
+ return
+
visible_message("[usr] starts climbing into [src].")
if(do_after(usr, 20, target = usr))
@@ -765,9 +769,13 @@
return ..()
+
/proc/cryo_ssd(var/mob/living/carbon/person_to_cryo)
if(istype(person_to_cryo.loc, /obj/machinery/cryopod))
return 0
+ if(isobj(person_to_cryo.loc))
+ var/obj/O = person_to_cryo.loc
+ O.force_eject_occupant()
var/list/free_cryopods = list()
for(var/obj/machinery/cryopod/P in GLOB.machines)
if(!P.occupant && istype(get_area(P), /area/crew_quarters/sleep))
@@ -776,6 +784,9 @@
if(free_cryopods.len)
target_cryopod = safepick(free_cryopods)
if(target_cryopod.check_occupant_allowed(person_to_cryo))
+ var/turf/T = get_turf(person_to_cryo)
+ var/obj/effect/portal/SP = new /obj/effect/portal(T, null, null, 40)
+ SP.name = "NT SSD Teleportation Portal"
target_cryopod.take_occupant(person_to_cryo, 1)
return 1
return 0
diff --git a/code/game/machinery/defib_mount.dm b/code/game/machinery/defib_mount.dm
index 9ee32716e53..79d02155832 100644
--- a/code/game/machinery/defib_mount.dm
+++ b/code/game/machinery/defib_mount.dm
@@ -29,11 +29,10 @@
loc = location
if(direction)
- dir = direction
+ setDir(direction)
if(building)
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
- pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0
+ set_pixel_offsets_from_dir(30, -30, 30, -30)
/obj/machinery/defibrillator_mount/loaded/New() //loaded subtype for mapping use
..()
@@ -157,6 +156,6 @@
w_class = WEIGHT_CLASS_BULKY
/obj/item/mounted/frame/defib_mount/do_build(turf/on_wall, mob/user)
- new /obj/machinery/defibrillator_mount(get_turf(src), get_dir(on_wall, user), 1)
+ new /obj/machinery/defibrillator_mount(get_turf(src), get_dir(user, on_wall), 1)
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
qdel(src)
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 94a3219c159..ffb56109e9e 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -42,7 +42,7 @@
WELDER_ATTEMPT_REPAIR_MESSAGE
if(I.use_tool(src, user, 40, volume = I.tool_volume))
WELDER_REPAIR_SUCCESS_MESSAGE
- obj_integrity = Clamp(obj_integrity + 20, 0, max_integrity)
+ obj_integrity = clamp(obj_integrity + 20, 0, max_integrity)
update_icon()
return TRUE
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 73d0e9281e1..3f074c9051d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -38,6 +38,11 @@
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
+#define TGUI_GREEN 2
+#define TGUI_ORANGE 1
+#define TGUI_RED 0
+
+
GLOBAL_LIST_EMPTY(airlock_overlays)
/obj/machinery/door/airlock
@@ -54,7 +59,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
normalspeed = 1
siemens_strength = 1
var/security_level = 0 //How much are wires secured
- var/aiControlDisabled = FALSE //If TRUE, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
+ var/aiControlDisabled = AICONTROLDISABLED_OFF
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.
@@ -65,7 +70,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/spawnPowerRestoreRunning = 0
var/lights = TRUE // bolt lights show by default
var/datum/wires/airlock/wires
- var/aiDisabledIdScanner = 0
+ var/aiDisabledIdScanner = FALSE
var/aiHacking = 0
var/obj/machinery/door/airlock/closeOther
var/closeOtherId
@@ -80,6 +85,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
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/paintable = TRUE // If the airlock type can be painted with an airlock painter
var/image/old_frame_overlay //keep those in order to prevent unnecessary updating
var/image/old_filling_overlay
@@ -95,7 +101,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
var/boltUp = 'sound/machines/boltsup.ogg'
var/boltDown = 'sound/machines/boltsdown.ogg'
- var/is_special = 0
+ var/is_special = FALSE
/obj/machinery/door/airlock/welded
welded = TRUE
@@ -141,6 +147,7 @@ About the new airlock wires panel:
break
/obj/machinery/door/airlock/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(electronics)
QDEL_NULL(wires)
QDEL_NULL(note)
@@ -172,7 +179,7 @@ About the new airlock wires panel:
spawn (10)
justzap = 0
return
- else /*if(justzap)*/
+ else
return
else if(user.hallucination > 50 && prob(10) && !operating)
if(user.electrocute_act(50, src, 1, illusion = TRUE)) // We'll just go with a flat 50 damage, instead of doing powernet checks
@@ -187,15 +194,11 @@ About the new airlock wires panel:
return 1
return 0
-/obj/machinery/door/airlock/proc/isWireCut(wireIndex)
- // You can find the wires in the datum folder.
- return wires.IsIndexCut(wireIndex)
-
/obj/machinery/door/airlock/proc/canAIControl()
- return ((aiControlDisabled!=1) && (!isAllPowerLoss()));
+ return ((aiControlDisabled != AICONTROLDISABLED_ON) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/canAIHack()
- return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss()));
+ return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
if(stat & (NOPOWER|BROKEN))
@@ -203,37 +206,31 @@ About the new airlock wires panel:
return (main_power_lost_until==0 || backup_power_lost_until==0)
/obj/machinery/door/airlock/requiresID()
- return !(isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner)
+ return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner)
/obj/machinery/door/airlock/proc/isAllPowerLoss()
if(stat & (NOPOWER|BROKEN))
return 1
- if(mainPowerCablesCut() && backupPowerCablesCut())
+ if(wires.is_cut(WIRE_MAIN_POWER1) && wires.is_cut(WIRE_BACKUP_POWER1))
return 1
return 0
-/obj/machinery/door/airlock/proc/mainPowerCablesCut()
- return isWireCut(AIRLOCK_WIRE_MAIN_POWER1)
-
-/obj/machinery/door/airlock/proc/backupPowerCablesCut()
- return isWireCut(AIRLOCK_WIRE_BACKUP_POWER1)
-
/obj/machinery/door/airlock/proc/loseMainPower()
- main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60)
+ main_power_lost_until = wires.is_cut(WIRE_MAIN_POWER1) ? -1 : world.time + 60 SECONDS
if(main_power_lost_until > 0)
- main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE)
+ main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
// If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running
- if(backup_power_lost_until == -1 && !backupPowerCablesCut())
- backup_power_lost_until = world.time + SecondsToTicks(10)
- backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(10), TIMER_UNIQUE | TIMER_STOPPABLE)
+ if(backup_power_lost_until == -1 && !wires.is_cut(WIRE_BACKUP_POWER1))
+ backup_power_lost_until = world.time + 10 SECONDS
+ backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 10 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
// Disable electricity if required
if(electrified_until && isAllPowerLoss())
electrify(0)
/obj/machinery/door/airlock/proc/loseBackupPower()
- backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60)
+ backup_power_lost_until = wires.is_cut(WIRE_BACKUP_POWER1) ? -1 : world.time + 60 SECONDS
if(backup_power_lost_until > 0)
- backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE)
+ backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
// Disable electricity if required
if(electrified_until && isAllPowerLoss())
@@ -242,7 +239,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/regainMainPower()
main_power_timer = null
- if(!mainPowerCablesCut())
+ if(!wires.is_cut(WIRE_MAIN_POWER1))
main_power_lost_until = 0
// If backup power is currently active then disable, otherwise let it count down and disable itself later
if(!backup_power_lost_until)
@@ -252,7 +249,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/proc/regainBackupPower()
backup_power_timer = null
- if(!backupPowerCablesCut())
+ if(!wires.is_cut(WIRE_BACKUP_POWER1))
// Restore backup power only if main power is offline, otherwise permanently disable
backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0
update_icon()
@@ -263,7 +260,7 @@ About the new airlock wires panel:
electrified_timer = null
var/message = ""
- if(isWireCut(AIRLOCK_WIRE_ELECTRIFY) && arePowerSystemsOn())
+ if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn())
message = text("The electrification wire is cut - Door permanently electrified.")
electrified_until = -1
else if(duration && !arePowerSystemsOn())
@@ -280,9 +277,9 @@ About the new airlock wires panel:
else
shockedby += text("\[[time_stamp()]\] - EMP)")
message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]."
- electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration)
+ electrified_until = duration == -1 ? -1 : world.time + duration SECONDS
if(duration != -1)
- electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), SecondsToTicks(duration), TIMER_UNIQUE | TIMER_STOPPABLE)
+ electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), duration SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)
if(feedback && message)
to_chat(usr, message)
@@ -502,6 +499,36 @@ About the new airlock wires panel:
sleep(6)
update_icon(AIRLOCK_CLOSED)
+
+/// Called when a player uses an airlock painter on this airlock
+/obj/machinery/door/airlock/proc/change_paintjob(obj/item/airlock_painter/painter, mob/user)
+ if((!in_range(src, user) && loc != user)) // user should be adjacent to the airlock.
+ return
+
+ if(!painter.paint_setting)
+ to_chat(user, "You need to select a paintjob first.")
+ return
+
+ if(!paintable)
+ to_chat(user, "This type of airlock cannot be painted.")
+ return
+
+ var/obj/machinery/door/airlock/airlock = painter.available_paint_jobs["[painter.paint_setting]"] // get the airlock type path associated with the airlock name the user just chose
+ var/obj/structure/door_assembly/assembly = initial(airlock.assemblytype)
+
+ if(airlock_material == "glass" && initial(assembly.noglass)) // prevents painting glass airlocks with a paint job that doesn't have a glass version, such as the freezer
+ to_chat(user, "This paint job can only be applied to non-glass airlocks.")
+ return
+
+ if(do_after(user, 20, target = src))
+ // applies the user-chosen airlock's icon, overlays and assemblytype to the src airlock
+ painter.paint(user)
+ icon = initial(airlock.icon)
+ overlays_file = initial(airlock.overlays_file)
+ assemblytype = initial(airlock.assemblytype)
+ update_icon()
+
+
/obj/machinery/door/airlock/examine(mob/user)
. = ..()
if(emagged)
@@ -539,38 +566,58 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/attack_ghost(mob/user)
if(panel_open)
wires.Interact(user)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/door/airlock/attack_ai(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/door/airlock/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "AiAirlock", name, 600, 400, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1)
- data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1)
- data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1)
- data["open"] = !density
+/obj/machinery/door/airlock/tgui_data(mob/user)
+ var/list/data = list()
- var/commands[0]
- commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner,"enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
- commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised", "disabled" = "Dropped", "danger" = 0, "act" = 0)
- commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
- commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
- commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
- commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0)
- commands[++commands.len] = list("name" = "Emergency Access","command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0)
+ var/list/power = list()
+ power["main"] = main_power_lost_until ? TGUI_RED : TGUI_GREEN
+ power["main_timeleft"] = max(main_power_lost_until - world.time, 0) / 10
+ power["backup"] = backup_power_lost_until ? TGUI_RED : TGUI_GREEN
+ power["backup_timeleft"] = max(backup_power_lost_until - world.time, 0) / 10
+ data["power"] = power
+ if(electrified_until == -1)
+ data["shock"] = TGUI_RED
+ else if(electrified_until > 0)
+ data["shock"] = TGUI_ORANGE
+ else
+ data["shock"] = TGUI_GREEN
- data["commands"] = commands
+ data["shock_timeleft"] = max(electrified_until - world.time, 0) / 10
+ data["id_scanner"] = !aiDisabledIdScanner
+ data["emergency"] = emergency // access
+ data["locked"] = locked // bolted
+ data["lights"] = lights // bolt lights
+ data["safe"] = safe // safeties
+ data["speed"] = normalspeed // safe speed
+ data["welded"] = welded // welded
+ data["opened"] = !density // opened
+
+ var/list/wire = list()
+ wire["main_power"] = !wires.is_cut(WIRE_MAIN_POWER1)
+ wire["backup_power"] = !wires.is_cut(WIRE_BACKUP_POWER1)
+ wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY)
+ wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN)
+ wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS)
+ wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT)
+ wire["safe"] = !wires.is_cut(WIRE_SAFETY)
+ wire["timing"] = !wires.is_cut(WIRE_SPEED)
+
+ data["wires"] = wire
return data
+
/obj/machinery/door/airlock/proc/hack(mob/user)
set waitfor = 0
if(!aiHacking)
@@ -610,7 +657,7 @@ About the new airlock wires panel:
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
- aiControlDisabled = 2
+ aiControlDisabled = AICONTROLDISABLED_BYPASS
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
@@ -706,125 +753,125 @@ About the new airlock wires panel:
else
try_to_activate_door(user)
-/obj/machinery/door/airlock/CanUseTopic(mob/user)
- if(!issilicon(user) && !isobserver(user))
- return STATUS_CLOSE
- if(emagged)
- to_chat(user, "Unable to interface: Internal error.")
- return STATUS_CLOSE
- if(!canAIControl() && !isobserver(user))
- if(canAIHack(user))
- hack(user)
- else
- if(isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it.
- to_chat(user, "Unable to interface: Connection timed out.")
- else
- to_chat(user, "Unable to interface: Connection refused.")
- return STATUS_CLOSE
-
- return ..()
-
-/obj/machinery/door/airlock/Topic(href, href_list, nowindow = 0)
+/obj/machinery/door/airlock/tgui_act(action, params)
if(..())
- return 1
-
- var/activate = text2num(href_list["activate"])
- switch(href_list["command"])
- if("idscan")
- if(isWireCut(AIRLOCK_WIRE_IDSCAN))
- to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.")
- else if(activate && aiDisabledIdScanner)
- aiDisabledIdScanner = 0
- to_chat(usr, "IdScan feature has been enabled.")
- else if(!activate && !aiDisabledIdScanner)
- aiDisabledIdScanner = 1
- to_chat(usr, "IdScan feature has been disabled.")
- if("main_power")
+ return
+ if(!issilicon(usr) && !usr.can_admin_interact())
+ to_chat(usr, "Access denied. Only silicons may use this interface.")
+ return
+ if(issilicon(usr) && emagged)
+ to_chat(usr, "Unable to interface: Internal error.")
+ return
+ if(!canAIControl() && !isobserver(usr))
+ if(canAIHack(usr))
+ hack(usr)
+ else
+ if(isAllPowerLoss())
+ to_chat(usr, "Unable to interface: Connection timed out.")
+ else
+ to_chat(usr, "Unable to interface: Connection refused.")
+ return
+ . = TRUE
+ switch(action)
+ if("disrupt-main")
if(!main_power_lost_until)
loseMainPower()
update_icon()
- if("backup_power")
+ else
+ to_chat(usr, "Main power is already offline.")
+ . = FALSE
+ if("disrupt-backup")
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.")
- else if(activate && lock())
- to_chat(usr, "The door bolts have been dropped.")
- else if(!activate && unlock())
- to_chat(usr, "The door bolts have been raised.")
- if("electrify_temporary")
- if(activate && isWireCut(AIRLOCK_WIRE_ELECTRIFY))
- to_chat(usr, text("The electrification wire is cut - Door permanently electrified."))
- else if(!activate && electrified_until != 0)
- to_chat(usr, "The door is now un-electrified.")
- electrify(0)
- else if(activate) //electrify door for 30 seconds
- shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
- usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
- add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
- to_chat(usr, "The door is now electrified for thirty seconds.")
- electrify(30)
- if("electrify_permanently")
- if(isWireCut(AIRLOCK_WIRE_ELECTRIFY))
- to_chat(usr, text("The electrification wire is cut - Cannot electrify the door."))
- else if(!activate && electrified_until != 0)
- to_chat(usr, "The door is now un-electrified.")
- electrify(0)
- else if(activate)
- shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
- usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
- add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
- to_chat(usr, "The door is now electrified.")
- electrify(-1)
- if("open")
- if(welded)
- to_chat(usr, text("The airlock has been welded shut!"))
- else if(locked)
- to_chat(usr, text("The door bolts are down!"))
- else if(activate && density)
- open()
- else if(!activate && !density)
- close()
- if("safeties")
- // Safeties! We don't need no stinking safeties!
- if(isWireCut(AIRLOCK_WIRE_SAFETY))
- to_chat(usr, text("The safety wire is cut - Cannot secure the door."))
- else if(activate && safe)
- safe = 0
- else if(!activate && !safe)
- safe = 1
- if("timing")
- // Door speed control
- if(isWireCut(AIRLOCK_WIRE_SPEED))
- to_chat(usr, text("The timing wire is cut - Cannot alter timing."))
- else if(activate && normalspeed)
- normalspeed = 0
- else if(!activate && !normalspeed)
- normalspeed = 1
- if("lights")
- // Bolt lights
- if(isWireCut(AIRLOCK_WIRE_LIGHT))
- to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
- else if(!activate && lights)
- lights = 0
- to_chat(usr, "The door bolt lights have been disabled.")
- else if(activate && !lights)
- lights = 1
- to_chat(usr, "The door bolt lights have been enabled.")
- update_icon()
- if("emergency")
- // Emergency access
- if(emergency)
- emergency = 0
- to_chat(usr, "Emergency access has been disabled.")
else
- emergency = 1
- to_chat(usr, "Emergency access has been enabled.")
+ to_chat(usr, "Backup power is already offline.")
+ if("shock-restore")
+ to_chat(usr, "The door is now un-electrified.")
+ electrify(0)
+ if("shock-temp")
+ if(wires.is_cut(WIRE_ELECTRIFY))
+ to_chat(usr, "The electrification wire is cut - Door permanently electrified.")
+ . = FALSE
+ else
+ //electrify door for 30 seconds
+ shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
+ usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
+ add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
+ to_chat(usr, "The door is now electrified for thirty seconds.")
+ electrify(30)
+ if("shock-perm")
+ if(wires.is_cut(WIRE_ELECTRIFY))
+ to_chat(usr, "The electrification wire is cut - Cannot electrify the door.")
+ . = FALSE
+ else
+ shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
+ usr.create_attack_log("Electrified the [name] at [x] [y] [z]")
+ add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
+ to_chat(usr, "The door is now electrified.")
+ electrify(-1)
+ if("idscan-toggle")
+ if(wires.is_cut(WIRE_IDSCAN))
+ to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.")
+ . = FALSE
+ else if(aiDisabledIdScanner)
+ aiDisabledIdScanner = FALSE
+ to_chat(usr, "IdScan feature has been enabled.")
+ else
+ aiDisabledIdScanner = TRUE
+ to_chat(usr, "IdScan feature has been disabled.")
+ if("emergency-toggle")
+ emergency = !emergency
+ if(emergency)
+ to_chat(usr, "Emergency access has been enabled.")
+ else
+ to_chat(usr, "Emergency access has been disabled.")
update_icon()
- return 1
+ if("bolt-toggle")
+ if(wires.is_cut(WIRE_DOOR_BOLTS))
+ to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.")
+ else if(lock())
+ to_chat(usr, "The door bolts have been dropped.")
+ else if(unlock())
+ to_chat(usr, "The door bolts have been raised.")
+ if("light-toggle")
+ if(wires.is_cut(WIRE_BOLT_LIGHT))
+ to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
+ else if(lights)
+ lights = FALSE
+ to_chat(usr, "The door bolt lights have been disabled.")
+ else if(!lights)
+ lights = TRUE
+ to_chat(usr, "The door bolt lights have been enabled.")
+ update_icon()
+ if("safe-toggle")
+ if(wires.is_cut(WIRE_SAFETY))
+ to_chat(usr, "The safety wire is cut - Cannot secure the door.")
+ else if(safe)
+ safe = 0
+ to_chat(usr, "The door safeties have been disabled.")
+ else
+ safe = 1
+ to_chat(usr, "The door safeties have been enabled.")
+ if("speed-toggle")
+ if(wires.is_cut(WIRE_SPEED))
+ to_chat(usr, "The timing wire is cut - Cannot alter timing.")
+ else if(normalspeed)
+ normalspeed = 0
+ else
+ normalspeed = 1
+ if("open-close")
+ if(welded)
+ to_chat(usr, "The airlock has been welded shut!")
+ else if(locked)
+ to_chat(usr, "The door bolts are down!")
+ else if(density)
+ open()
+ else
+ close()
+ else
+ . = FALSE
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
add_fingerprint(user)
@@ -876,10 +923,14 @@ About the new airlock wires panel:
if(!user.unEquip(C))
to_chat(user, "For some reason, you can't attach [C]!")
return
+ C.add_fingerprint(user)
+ user.create_log(MISC_LOG, "put [C] on", src)
C.forceMove(src)
user.visible_message("[user] pins [C] to [src].", "You pin [C] to [src].")
note = C
update_icon()
+ else if(istype(C, /obj/item/airlock_painter))
+ change_paintjob(C, user)
else
return ..()
@@ -933,7 +984,7 @@ About the new airlock wires panel:
if(!panel_open || user.a_intent == INTENT_HARM)
return
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
if(security_level == AIRLOCK_SECURITY_PLASTEEL)
if(arePowerSystemsOn() && shock(user, 60)) // Protective grille of wiring is electrified
@@ -965,63 +1016,58 @@ About the new airlock wires panel:
. = TRUE
if(!I.tool_use_check(user, 0))
return
- switch(security_level)
- if(AIRLOCK_SECURITY_METAL)
- to_chat(user, "You begin cutting the panel's shielding...")
- if(!I.use_tool(src, user, 40, volume = I.tool_volume))
- return
- if(!panel_open)
- return
- 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)
- if(AIRLOCK_SECURITY_PLASTEEL_O)
- to_chat(user, "You begin cutting the outer layer of shielding...")
- if(!I.use_tool(src, user, 40, volume = I.tool_volume))
- return
- if(!panel_open)
- return
- 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
- if(AIRLOCK_SECURITY_PLASTEEL_I)
- to_chat(user, "You begin cutting the inner layer of shielding...")
- if(!I.use_tool(src, user, 40, volume = I.tool_volume))
- return
- if(!panel_open)
- return
- 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
- else
- if(user.a_intent != INTENT_HELP)
- user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \
- "You begin [welded ? "unwelding":"welding"] the airlock...", \
+ if(panel_open) // panel should be open before we try to slice out any shielding.
+ switch(security_level)
+ if(AIRLOCK_SECURITY_METAL)
+ to_chat(user, "You begin cutting the panel's shielding...")
+ if(!I.use_tool(src, user, 40, volume = I.tool_volume))
+ return
+ 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)
+ if(AIRLOCK_SECURITY_PLASTEEL_O)
+ to_chat(user, "You begin cutting the outer layer of shielding...")
+ if(!I.use_tool(src, user, 40, volume = I.tool_volume))
+ return
+ 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
+ if(AIRLOCK_SECURITY_PLASTEEL_I)
+ to_chat(user, "You begin cutting the inner layer of shielding...")
+ if(!I.use_tool(src, user, 40, volume = I.tool_volume))
+ return
+ 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
+ else
+ if(user.a_intent != INTENT_HELP)
+ user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \
+ "You begin [welded ? "unwelding":"welding"] the airlock...", \
+ "You hear welding.")
- if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
- if(!density && !welded)
- return
- welded = !welded
- user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \
- "You [welded ? "weld the airlock shut":"unweld the airlock"].")
- update_icon()
- else if(obj_integrity < max_integrity)
- user.visible_message("[user] is welding the airlock.", \
- "You begin repairing the airlock...", \
- "You hear welding.")
- if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
- obj_integrity = max_integrity
- stat &= ~BROKEN
- user.visible_message("[user.name] has repaired [src].", \
- "You finish repairing the airlock.")
+ if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
+ if(!density && !welded)
+ return
+ 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
- to_chat(user, "The airlock doesn't need repairing.")
+ else if(obj_integrity < max_integrity)
+ user.visible_message("[user] is welding the airlock.", \
+ "You begin repairing the airlock...", \
+ "You hear welding.")
+ if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
+ obj_integrity = max_integrity
+ stat &= ~BROKEN
+ user.visible_message("[user.name] has repaired [src].", \
+ "You finish repairing the airlock.")
+ update_icon()
+ else
+ to_chat(user, "The airlock doesn't need repairing.")
update_icon()
/obj/machinery/door/airlock/proc/weld_checks(obj/item/I, mob/user)
@@ -1034,7 +1080,7 @@ About the new airlock wires panel:
return
return TRUE
-/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) //*scream
+/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I)
if(operating)
return
if(istype(I, /obj/item/twohanded/fireaxe)) //let's make this more specific //FUCK YOU
@@ -1096,7 +1142,7 @@ About the new airlock wires panel:
if(operating || welded || locked || emagged)
return 0
if(!forced)
- if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
+ if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR))
return 0
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
@@ -1133,7 +1179,7 @@ About the new airlock wires panel:
if(!forced)
//despite the name, this wire is for general door control.
//Bolts are already covered by the check for locked, above
- if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
+ if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR))
return
if(safe)
for(var/turf/turf in locs)
@@ -1189,7 +1235,7 @@ About the new airlock wires panel:
return
if(!forced)
- if(operating || !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS))
+ if(operating || !arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS))
return
locked = 0
@@ -1217,7 +1263,7 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/emp_act(severity)
..()
if(prob(40/severity))
- var/duration = world.time + SecondsToTicks(30 / severity)
+ var/duration = world.time + (30 / severity) SECONDS
if(duration > electrified_until)
electrify(duration)
@@ -1284,7 +1330,7 @@ About the new airlock wires panel:
stat |= BROKEN
if(!panel_open)
panel_open = TRUE
- wires.CutAll()
+ wires.cut_all()
update_icon()
/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
@@ -1347,10 +1393,13 @@ About the new airlock wires panel:
if (ishuman(user) && user.a_intent == INTENT_GRAB)//grab that note
user.visible_message("[user] removes [note] from [src].", "You remove [note] from [src].")
playsound(src, 'sound/items/poster_ripped.ogg', 50, 1)
- else return FALSE
+ else
+ return FALSE
else
user.visible_message("[user] cuts down [note] from [src].", "You remove [note] from [src].")
playsound(src, 'sound/items/wirecutter.ogg', 50, 1)
+ note.add_fingerprint(user)
+ user.create_log(MISC_LOG, "removed [note] from", src)
user.put_in_hands(note)
note = null
update_icon()
@@ -1374,6 +1423,12 @@ About the new airlock wires panel:
A.name = name
qdel(src)
+/obj/machinery/door/airlock/proc/ai_control_callback()
+ if(aiControlDisabled == AICONTROLDISABLED_ON)
+ aiControlDisabled = AICONTROLDISABLED_OFF
+ else if(aiControlDisabled == AICONTROLDISABLED_BYPASS)
+ aiControlDisabled = AICONTROLDISABLED_PERMA
+
#undef AIRLOCK_CLOSED
#undef AIRLOCK_CLOSING
#undef AIRLOCK_OPEN
@@ -1393,3 +1448,7 @@ About the new airlock wires panel:
#undef AIRLOCK_INTEGRITY_MULTIPLIER
#undef AIRLOCK_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
+
+#undef TGUI_GREEN
+#undef TGUI_ORANGE
+#undef TGUI_RED
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index fccb77c5e2a..1d194507357 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -115,6 +115,7 @@
name = "gold airlock"
icon = 'icons/obj/doors/airlocks/station/gold.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_gold
+ paintable = FALSE
/obj/machinery/door/airlock/gold/glass
opacity = 0
@@ -124,6 +125,7 @@
name = "silver airlock"
icon = 'icons/obj/doors/airlocks/station/silver.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_silver
+ paintable = FALSE
/obj/machinery/door/airlock/silver/glass
opacity = 0
@@ -135,6 +137,7 @@
assemblytype = /obj/structure/door_assembly/door_assembly_diamond
normal_integrity = 1000
explosion_block = 2
+ paintable = FALSE
/obj/machinery/door/airlock/diamond/glass
normal_integrity = 950
@@ -146,6 +149,7 @@
desc = "And they said I was crazy."
icon = 'icons/obj/doors/airlocks/station/uranium.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_uranium
+ paintable = FALSE
var/event_step = 20
/obj/machinery/door/airlock/uranium/New()
@@ -169,6 +173,7 @@
desc = "No way this can end badly."
icon = 'icons/obj/doors/airlocks/station/plasma.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_plasma
+ paintable = FALSE
/obj/machinery/door/airlock/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
@@ -214,6 +219,7 @@
assemblytype = /obj/structure/door_assembly/door_assembly_bananium
doorOpen = 'sound/items/bikehorn.ogg'
doorClose = 'sound/items/bikehorn.ogg'
+ paintable = FALSE
/obj/machinery/door/airlock/bananium/glass
opacity = 0
@@ -227,11 +233,13 @@
doorDeni = null
boltUp = null
boltDown = null
+ paintable = FALSE
/obj/machinery/door/airlock/sandstone
name = "sandstone airlock"
icon = 'icons/obj/doors/airlocks/station/sandstone.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_sandstone
+ paintable = FALSE
/obj/machinery/door/airlock/sandstone/glass
opacity = 0
@@ -241,6 +249,7 @@
name = "wooden airlock"
icon = 'icons/obj/doors/airlocks/station/wood.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_wood
+ paintable = FALSE
/obj/machinery/door/airlock/wood/glass
opacity = 0
@@ -252,6 +261,7 @@
icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
normal_integrity = 400
+ paintable = FALSE
/obj/machinery/door/airlock/titanium/glass
normal_integrity = 350
@@ -317,6 +327,7 @@
explosion_block = 2
normal_integrity = 400 // reverse engieneerd: 400 * 1.5 (sec lvl 6) = 600 = original
security_level = 6
+ paintable = FALSE
//////////////////////////////////
/*
@@ -329,6 +340,7 @@
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
+ paintable = FALSE
/obj/machinery/door/airlock/hatch/syndicate
name = "syndicate hatch"
@@ -353,14 +365,14 @@
assemblytype = /obj/structure/door_assembly/door_assembly_vault
security_level = 6
hackProof = TRUE
- aiControlDisabled = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/hatch/gamma
name = "gamma level hatch"
- hackProof = 1
- aiControlDisabled = 1
+ hackProof = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
resistance_flags = FIRE_PROOF | ACID_PROOF
- is_special = 1
+ is_special = TRUE
/obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -400,6 +412,7 @@
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
+ paintable = FALSE
//////////////////////////////////
/*
@@ -415,11 +428,12 @@
normal_integrity = 500
security_level = 1
damage_deflection = 30
+ paintable = FALSE
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
- hackProof = 1
- aiControlDisabled = 1
+ hackProof = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -457,6 +471,7 @@
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
+ paintable = FALSE
/obj/machinery/door/airlock/shuttle/glass
opacity = 0
@@ -472,9 +487,10 @@
damage_deflection = 30
explosion_block = 3
hackProof = TRUE
- aiControlDisabled = 1
+ aiControlDisabled = AICONTROLDISABLED_ON
normal_integrity = 700
security_level = 1
+ paintable = FALSE
//////////////////////////////////
/*
@@ -488,7 +504,8 @@
assemblytype = /obj/structure/door_assembly/door_assembly_cult
damage_deflection = 10
hackProof = TRUE
- aiControlDisabled = TRUE
+ aiControlDisabled = AICONTROLDISABLED_ON
+ paintable = FALSE
var/openingoverlaytype = /obj/effect/temp_visual/cult/door
var/friendly = FALSE
@@ -575,6 +592,7 @@
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
+ paintable = FALSE
/obj/machinery/door/airlock/multi_tile/narsie_act()
return
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 29549a05630..268f2ede9fb 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -2,6 +2,7 @@
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Small Fonts"
+#define CELL_NONE "None"
///////////////////////////////////////////////////////////////////////////////////////////////
// Brig Door control displays.
@@ -31,10 +32,14 @@
maptext_height = 26
maptext_width = 32
maptext_y = -1
- var/occupant = "None"
- var/crimes = "None"
+ var/occupant = CELL_NONE
+ var/crimes = CELL_NONE
var/time = 0
- var/officer = "None"
+ var/officer = CELL_NONE
+ var/prisoner_name
+ var/prisoner_charge
+ var/prisoner_time
+ var/prisoner_hasrecord = FALSE
/obj/machinery/door_timer/New()
GLOB.celltimers_list += src
@@ -45,33 +50,29 @@
return ..()
/obj/machinery/door_timer/proc/print_report()
- var/logname = input(usr, "Name of the guilty?","[id] log name")
- var/logcharges = stripped_multiline_input(usr, "What have they been charged with?","[id] log charges")
-
- if(!logname || !logcharges)
+ if(occupant == CELL_NONE || crimes == CELL_NONE)
return 0
- occupant = logname
- crimes = logcharges
+
time = timetoset
officer = usr.name
for(var/obj/machinery/computer/prisoner/C in GLOB.prisoncomputer_list)
var/obj/item/paper/P = new /obj/item/paper(C.loc)
- P.name = "[id] log - [logname] [station_time_timestamp()]"
+ P.name = "[id] log - [occupant] [station_time_timestamp()]"
P.info = "[id] - Brig record
"
P.info += {"[station_name()] - Security Department
Admission data:
Log generated at: [station_time_timestamp()]
- Detainee: [logname]
+ Detainee: [occupant]
Duration: [seconds_to_time(timetoset / 10)]
- Charge(s): [logcharges]
+ Charge(s): [crimes]
Arresting Officer: [usr.name]
This log file was generated automatically upon activation of a cell timer."}
playsound(C.loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1)
GLOB.cell_logs += P
- var/datum/data/record/G = find_record("name", logname, GLOB.data_core.general)
+ var/datum/data/record/G = find_record("name", occupant, GLOB.data_core.general)
var/prisoner_drank = "unknown"
var/prisoner_trank = "unknown"
if(G)
@@ -80,53 +81,41 @@
if(G.fields["real_rank"]) // Ignore alt job titles - necessary for lookups
prisoner_trank = G.fields["real_rank"]
- var/datum/data/record/R = find_security_record("name", logname)
+ var/datum/data/record/R = find_security_record("name", occupant)
- var/announcetext = "Detainee [logname] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of, '[logcharges]'. \
+ var/timetext = seconds_to_time(timetoset / 10)
+ var/announcetext = "Detainee [occupant] ([prisoner_drank]) has been incarcerated for [timetext] for the crime of: '[crimes]'. \
Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]"
Radio.autosay(announcetext, name, "Security", list(z))
- if(prisoner_trank != "unknown")
- notify_dept_head(prisoner_trank, announcetext)
+ // Notify the actual criminal being brigged. This is a QOL thing to ensure they always know the charges against them.
+ // Announcing it on radio isn't enough, as they're unlikely to have sec radio.
+ notify_prisoner("You have been incarcerated for [timetext] for the crime of: '[crimes]'.")
+
+ if(prisoner_trank != "unknown" && prisoner_trank != "Civilian")
+ SSjobs.notify_dept_head(prisoner_trank, announcetext)
if(R)
prisoner = R
- R.fields["criminal"] = "Incarcerated"
+ R.fields["criminal"] = SEC_RECORD_STATUS_INCARCERATED
var/mob/living/carbon/human/M = usr
var/rank = "UNKNOWN RANK"
- if(istype(M) && M.wear_id)
- var/obj/item/card/id/I = M.wear_id
- rank = I.assignment
+ if(istype(M))
+ var/obj/item/card/id/I = M.get_id_card()
+ if(I)
+ rank = I.assignment
if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized
R.fields["comments"] = list()
- R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()] Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]."
+ R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()] Sentenced to [timetoset/10] seconds for the charges of \"[crimes]\" by [rank] [usr.name]."
update_all_mob_security_hud()
return 1
-
-/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext)
- if(!jobtitle || !antext)
- return
- if(jobtitle == "Civilian")
- // Don't notify the HoP about greytiding civilians
- return
- var/datum/job/brigged_job = SSjobs.GetJob(jobtitle)
- if(!brigged_job)
- return
- if(!brigged_job.department_head[1])
- return
- var/boss_title = brigged_job.department_head[1]
-
- var/obj/item/pda/target_pda
- for(var/obj/item/pda/check_pda in GLOB.PDAs)
- if(check_pda.ownrank == boss_title)
- target_pda = check_pda
- if(!target_pda)
- return
- var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
- if(PM && PM.can_receive())
- PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)")
-
+/obj/machinery/door_timer/proc/notify_prisoner(notifytext)
+ for(var/mob/living/carbon/human/H in range(4, get_turf(src)))
+ if(occupant == H.name)
+ to_chat(H, "[src] beeps, \"[notifytext]\"")
+ return
+ atom_say("[src] beeps, \"[occupant]: [notifytext]\"")
/obj/machinery/door_timer/Initialize()
..()
@@ -136,8 +125,7 @@
Radio.config(list("Security" = 0))
Radio.follow_target = src
- pixel_x = ((dir & 3)? (0) : (dir == 4 ? 32 : -32))
- pixel_y = ((dir & 3)? (dir ==1 ? 32 : -32) : (0))
+ set_pixel_offsets_from_dir(32, -32, 32, -32)
spawn(20)
for(var/obj/machinery/door/window/brigdoor/M in GLOB.airlocks)
@@ -175,12 +163,10 @@
if(timing)
if(timeleft() <= 0)
Radio.autosay("Timer has expired. Releasing prisoner.", name, "Security", list(z))
- occupant = "None"
+ occupant = CELL_NONE
timer_end() // open doors, reset timer, clear status screen
timing = 0
. = PROCESS_KILL
-
- updateUsrDialog()
update_icon()
else
timer_end()
@@ -203,8 +189,8 @@
if(!printed)
if(!print_report())
- timing = 0
- return 0
+ timing = FALSE
+ return FALSE
// Set releasetime
releasetime = world.timeofday + timetoset
@@ -237,14 +223,14 @@
return 0
// Reset vars
- occupant = "None"
- crimes = "None"
+ occupant = CELL_NONE
+ crimes = CELL_NONE
time = 0
- officer = "None"
+ officer = CELL_NONE
releasetime = 0
printed = 0
if(prisoner)
- prisoner.fields["criminal"] = "Released"
+ prisoner.fields["criminal"] = SEC_RECORD_STATUS_RELEASED
update_all_mob_security_hud()
prisoner = null
@@ -288,12 +274,11 @@
return
-//Allows AIs to use door_timer, see human attack_hand function below
/obj/machinery/door_timer/attack_ai(mob/user)
- interact(user)
+ tgui_interact(user)
/obj/machinery/door_timer/attack_ghost(mob/user)
- interact(user)
+ tgui_interact(user)
//Allows humans to use door_timer
//Opens dialog window when someone clicks on door timer
@@ -302,98 +287,109 @@
/obj/machinery/door_timer/attack_hand(mob/user)
if(..())
return
- interact(user)
+ tgui_interact(user)
-/obj/machinery/door_timer/interact(mob/user)
- // Used for the 'time left' display
- var/second = round(timeleft() % 60)
- var/minute = round((timeleft() - second) / 60)
+/obj/machinery/door_timer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "BrigTimer", name, 500, 450, master_ui, state)
+ ui.open()
- // Used for 'set timer'
- var/setsecond = round((timetoset / 10) % 60)
- var/setminute = round(((timetoset / 10) - setsecond) / 60)
+/obj/machinery/door_timer/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["spns"] = list()
+ for(var/mob/living/carbon/human/H in range(4, get_turf(src)))
+ if(H.handcuffed)
+ data["spns"] += H.name
+ return data
- user.set_machine(src)
+/obj/machinery/door_timer/tgui_data(mob/user)
+ var/list/data = list()
+ data["cell_id"] = name
+ data["occupant"] = occupant
+ data["crimes"] = crimes
+ data["brigged_by"] = officer
+ data["time_set"] = seconds_to_clock(timetoset / 10)
+ data["time_left"] = seconds_to_clock(timeleft())
+ data["timing"] = timing
+ data["isAllowed"] = allowed(user)
+ data["prisoner_name"] = prisoner_name
+ data["prisoner_charge"] = prisoner_charge
+ data["prisoner_time"] = prisoner_time
+ data["prisoner_hasrec"] = prisoner_hasrecord
+ return data
- // dat
- var/dat = " Timer System:"
- dat += " Door [id] controls "
+/obj/machinery/door_timer/allowed(mob/user)
+ if(user.can_admin_interact())
+ return TRUE
+ return ..()
- // Start/Stop timer
- if(timing)
- dat += "Stop Timer and open door "
- else
- dat += "Activate Timer and close door "
-
- // Time Left display (uses releasetime)
- dat += "Time Left: [(minute ? text("[minute]:") : null)][second] "
- dat += " "
-
- // Set Timer display (uses timetoset)
- if(timing)
- dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] Set "
- else
- dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] "
-
- // Controls
- dat += "Input Time"
-
- // Mounted flash controls
- for(var/obj/machinery/flasher/F in targets)
- if(F.last_flash && (F.last_flash + 150) > world.time)
- dat += " Flash Charging"
- else
- dat += " Activate Flash"
-
- dat += "
Close"
-
- var/datum/browser/popup = new(user, "door_timer", name, 400, 500)
- popup.set_content(dat)
- popup.open()
-
-
-//Function for using door_timer dialog input, checks if user has permission
-// href_list to
-// "timing" turns on timer
-// "tp" value to modify timer
-// "fc" activates flasher
-// "change" resets the timer to the timetoset amount while the timer is counting down
-// Also updates dialog window and timer icon
-/obj/machinery/door_timer/Topic(href, href_list)
+/obj/machinery/door_timer/tgui_act(action, params)
if(..())
- return 1
-
- if(!allowed(usr) && !usr.can_admin_interact())
- return 1
-
- usr.set_machine(src)
-
- if(href_list["timing"])
- timing = text2num(href_list["timing"])
-
- if(timing)
+ return
+ if(!allowed(usr))
+ to_chat(usr, "Access denied.")
+ return
+ . = TRUE
+ switch(action)
+ if("prisoner_name")
+ if(params["prisoner_name"])
+ prisoner_name = params["prisoner_name"]
+ else
+ prisoner_name = input("Prisoner Name:", name, prisoner_name) as text|null
+ if(prisoner_name)
+ var/datum/data/record/R = find_security_record("name", prisoner_name)
+ if(istype(R))
+ prisoner_hasrecord = TRUE
+ else
+ prisoner_hasrecord = FALSE
+ if("prisoner_charge")
+ prisoner_charge = input("Prisoner Charge:", name, prisoner_charge) as text|null
+ if("prisoner_time")
+ prisoner_time = input("Prisoner Time (in minutes):", name, prisoner_time) as num|null
+ prisoner_time = min(max(round(prisoner_time), 0), 60)
+ if("start")
+ if(!prisoner_name || !prisoner_charge || !prisoner_time)
+ return FALSE
+ timeset(prisoner_time * 60)
+ occupant = prisoner_name
+ crimes = prisoner_charge
+ prisoner_name = null
+ prisoner_charge = null
+ prisoner_time = null
+ timing = TRUE
timer_start()
- else
- timer_end()
- if(!isobserver(usr)) //spooky admin ghosts are in your brig, releasing your prisoners
- Radio.autosay("Timer stopped manually by [usr.name].", name, "Security", list(z))
-
- else
- if(href_list["settime"])
- var/time = min(max(round(return_time_input(usr)), 0), 3600)
- timeset(time)
-
- if(href_list["fc"])
+ update_icon()
+ if("restart_timer")
+ if(timing)
+ var/reset_reason = sanitize(copytext(input(usr, "Reason for resetting timer:", name, "") as text|null, 1, MAX_MESSAGE_LEN))
+ if(!reset_reason)
+ to_chat(usr, "Cancelled reset: reason field is required.")
+ return FALSE
+ releasetime = world.timeofday + timetoset
+ var/resettext = isobserver(usr) ? "for: [reset_reason]." : "by [usr.name] for: [reset_reason]."
+ Radio.autosay("Prisoner [occupant] had their timer reset [resettext]", name, "Security", list(z))
+ notify_prisoner("Your brig timer has been reset for: '[reset_reason]'.")
+ var/datum/data/record/R = find_security_record("name", occupant)
+ if(istype(R))
+ R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()] Timer reset [resettext]"
+ else
+ . = FALSE
+ if("stop")
+ if(timing)
+ timer_end()
+ var/stoptext = isobserver(usr) ? "from cell control." : "by [usr.name]."
+ Radio.autosay("Timer stopped manually [stoptext]", name, "Security", list(z))
+ else
+ . = FALSE
+ if("flash")
for(var/obj/machinery/flasher/F in targets)
- F.flash()
-
- if(href_list["change"])
- printed = 1
- timer_start()
-
- add_fingerprint(usr)
- updateUsrDialog()
- update_icon()
+ if(F.last_flash && (F.last_flash + 150) > world.time)
+ to_chat(usr, "Flash still charging.")
+ else
+ F.flash()
+ else
+ . = FALSE
//icon update function
@@ -502,3 +498,4 @@
#undef FONT_COLOR
#undef FONT_STYLE
#undef CHARS_PER_LINE
+#undef CELL_NONE
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 448c410c1c8..c509675868b 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -28,6 +28,11 @@
var/nextstate = null
var/boltslocked = TRUE
var/active_alarm = FALSE
+ var/list/affecting_areas
+
+/obj/machinery/door/firedoor/Initialize(mapload)
+ . = ..()
+ CalculateAffectingAreas()
/obj/machinery/door/firedoor/examine(mob/user)
. = ..()
@@ -40,11 +45,31 @@
else
. += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor."
+/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
+ remove_from_areas()
+ affecting_areas = get_adjacent_open_areas(src) | get_area(src)
+ for(var/I in affecting_areas)
+ var/area/A = I
+ LAZYADD(A.firedoors, src)
+
/obj/machinery/door/firedoor/closed
icon_state = "door_closed"
opacity = TRUE
density = TRUE
+//see also turf/AfterChange for adjacency shennanigans
+
+/obj/machinery/door/firedoor/proc/remove_from_areas()
+ if(affecting_areas)
+ for(var/I in affecting_areas)
+ var/area/A = I
+ LAZYREMOVE(A.firedoors, src)
+
+/obj/machinery/door/firedoor/Destroy()
+ remove_from_areas()
+ affecting_areas.Cut()
+ return ..()
+
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(panel_open || operating)
return
@@ -419,7 +444,7 @@
if(constructionStep != CONSTRUCTION_WIRES_EXPOSED)
return
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
user.visible_message("[user] starts cutting the wires from [src]...", \
@@ -442,7 +467,7 @@
if(locate(/obj/machinery/door/firedoor) in get_turf(src))
to_chat(user, "There's already a firelock there.")
return
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
user.visible_message("[user] starts bolting down [src]...", \
"You begin bolting [src]...")
diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm
index 05198a7a55f..a4b9510945a 100644
--- a/code/game/machinery/doppler_array.dm
+++ b/code/game/machinery/doppler_array.dm
@@ -89,12 +89,12 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user)
if(!logged_explosions.len)
- atom_say("No logs currently stored in internal database.")
+ atom_say("No logs currently stored in internal database.")
return
if(active_timers)
to_chat(user, "[src] is already printing something, please wait.")
return
- atom_say("Printing explosive log. Standby...")
+ atom_say("Printing explosive log. Standby...")
addtimer(CALLBACK(src, .proc/print), 50)
/obj/machinery/doppler_array/proc/print()
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 9beba3f4789..911f85be753 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -25,9 +25,15 @@ FIRE ALARM
active_power_usage = 6
power_channel = ENVIRON
resistance_flags = FIRE_PROOF
+
+ light_power = 0
+ light_range = 7
+ light_color = "#ff3232"
+
var/last_process = 0
var/wiresexposed = 0
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
+ var/enabled = FALSE
var/report_fire_alarms = TRUE // Should triggered fire alarms also trigger an actual alarm?
var/show_alert_level = TRUE // Should fire alarms display the current alert level?
@@ -191,6 +197,7 @@ FIRE ALARM
/obj/machinery/firealarm/obj_break(damage_flag)
if(!(stat & BROKEN) && !(flags & NODECONSTRUCT) && buildstage != 0) //can't break the electronics if there isn't any inside.
stat |= BROKEN
+ LAZYREMOVE(myArea.firealarms, src)
update_icon()
/obj/machinery/firealarm/deconstruct(disassembled = TRUE)
@@ -203,6 +210,14 @@ FIRE ALARM
new /obj/item/stack/cable_coil(loc, 3)
qdel(src)
+/obj/machinery/firealarm/proc/update_fire_light(fire)
+ if(fire == !!light_power)
+ return // do nothing if we're already active
+ if(fire)
+ set_light(l_power = 0.8)
+ else
+ set_light(l_power = 0)
+
/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed
if(stat & (NOPOWER|BROKEN))
return
@@ -286,36 +301,26 @@ FIRE ALARM
time = min(max(round(time), 0), 120)
/obj/machinery/firealarm/proc/reset()
- if(!working)
+ if(!working || !report_fire_alarms)
return
var/area/A = get_area(src)
- A.fire_reset()
+ A.firereset(src)
- for(var/obj/machinery/firealarm/FA in A)
- if(is_station_contact(z) && FA.report_fire_alarms)
- SSalarms.fire_alarm.clearAlarm(loc, FA)
-
-/obj/machinery/firealarm/proc/alarm(var/duration = 0)
- if(!working)
+/obj/machinery/firealarm/proc/alarm()
+ if(!working || !report_fire_alarms)
return
-
var/area/A = get_area(src)
- for(var/obj/machinery/firealarm/FA in A)
- if(is_station_contact(z) && FA.report_fire_alarms)
- SSalarms.fire_alarm.triggerAlarm(loc, FA, duration)
- else
- A.fire_alert() // Manually trigger alarms if the alarm isn't reported
-
+ A.firealert(src) // Manually trigger alarms if the alarm isn't reported
update_icon()
/obj/machinery/firealarm/New(location, direction, building)
- ..()
+ . = ..()
if(building)
buildstage = 0
wiresexposed = TRUE
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
- pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
+ setDir(direction)
+ set_pixel_offsets_from_dir(26, -26, 26, -26)
if(is_station_contact(z) && show_alert_level)
if(GLOB.security_level)
@@ -323,8 +328,14 @@ FIRE ALARM
else
overlays += image('icons/obj/monitors.dmi', "overlay_green")
+ myArea = get_area(src)
+ LAZYADD(myArea.firealarms, src)
update_icon()
+/obj/machinery/firealarm/Destroy()
+ LAZYREMOVE(myArea.firealarms, src)
+ return ..()
+
/*
FIRE ALARM CIRCUIT
Just a object used in constructing fire alarms
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index 57057ad510a..37766db58fb 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -144,7 +144,7 @@
active = 1
icon_state = "launcheract"
- for(var/obj/machinery/flasher/M in world)
+ for(var/obj/machinery/flasher/M in GLOB.machines)
if(M.id == id)
spawn()
M.flash()
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index fcd6de4020c..6d2586c46aa 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -195,7 +195,7 @@ GLOBAL_LIST_EMPTY(holopads)
for(var/mob/living/silicon/ai/AI in GLOB.ai_list)
if(!AI.client)
continue
- to_chat(AI, "Your presence is requested at \the [area].")
+ to_chat(AI, "Your presence is requested at \the [area].")
else
temp = "A request for AI presence was already sent recently. "
temp += "Main Menu"
diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm
index aca4254b517..aa541ec8619 100644
--- a/code/game/machinery/holosign.dm
+++ b/code/game/machinery/holosign.dm
@@ -67,7 +67,7 @@
else
icon_state = "light0"
- for(var/obj/machinery/holosign/M in world)
+ for(var/obj/machinery/holosign/M in GLOB.machines)
if(M.id == src.id)
spawn( 0 )
M.toggle()
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 19dc80f9a40..f02e037e8c5 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -116,8 +116,8 @@ Class Procs:
var/panel_open = 0
var/area/myArea
var/interact_offline = 0 // Can the machine be interacted with while de-powered.
- var/use_log = list()
- var/list/settagwhitelist = list()//WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST.
+ var/list/use_log // Init this list if you wish to add logging to your machine - currently only viewable in VV
+ var/list/settagwhitelist // (Init this list if needed) WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST.
atom_say_verb = "beeps"
var/siemens_strength = 0.7 // how badly will it shock you?
@@ -224,7 +224,7 @@ Class Procs:
var/obj/item/multitool/P = get_multitool(usr)
if(P && istype(P))
var/update_mt_menu = FALSE
- if("set_tag" in href_list)
+ if("set_tag" in href_list && settagwhitelist)
if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR)
message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)")
return FALSE
@@ -369,7 +369,6 @@ Class Procs:
/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames.
return
- return 0
/obj/machinery/proc/assign_uid()
uid = gl_uid
diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm
index b73fa94259f..d713817ca06 100644
--- a/code/game/machinery/magnet.dm
+++ b/code/game/machinery/magnet.dm
@@ -202,7 +202,7 @@
..()
if(autolink)
- for(var/obj/machinery/magnetic_module/M in world)
+ for(var/obj/machinery/magnetic_module/M in GLOB.machines)
if(M.freq == frequency && M.code == code)
magnets.Add(M)
@@ -224,7 +224,7 @@
/obj/machinery/magnetic_controller/process()
if(magnets.len == 0 && autolink)
- for(var/obj/machinery/magnetic_module/M in world)
+ for(var/obj/machinery/magnetic_module/M in GLOB.machines)
if(M.freq == frequency && M.code == code)
magnets.Add(M)
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 42452a9f8e2..a8c9710ff6c 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -106,10 +106,6 @@
else if(istype(make_from, /obj/machinery/atmospherics/unary/passive_vent))
src.pipe_type = PIPE_PASV_VENT
- else if(istype(make_from, /obj/machinery/atmospherics/omni/mixer))
- src.pipe_type = PIPE_OMNI_MIXER
- else if(istype(make_from, /obj/machinery/atmospherics/omni/filter))
- src.pipe_type = PIPE_OMNI_FILTER
else if(istype(make_from, /obj/machinery/atmospherics/binary/circulator))
src.pipe_type = PIPE_CIRCULATOR
@@ -259,7 +255,7 @@
return dir //dir|acw
if(PIPE_CONNECTOR, PIPE_UVENT, PIPE_PASV_VENT, PIPE_SCRUBBER, PIPE_HEAT_EXCHANGE, PIPE_INJECTOR)
return dir|flip
- if(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W, PIPE_OMNI_MIXER, PIPE_OMNI_FILTER)
+ if(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W)
return dir|flip|cw|acw
if(PIPE_MANIFOLD, PIPE_SUPPLY_MANIFOLD, PIPE_SCRUBBERS_MANIFOLD)
return flip|cw|acw
@@ -317,7 +313,7 @@
dir = 1
else if(dir==8)
dir = 4
- else if(pipe_type in list(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W, PIPE_OMNI_MIXER, PIPE_OMNI_FILTER))
+ else if(pipe_type in list(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W))
dir = 2
/obj/item/pipe/attack_self(mob/user as mob)
@@ -499,14 +495,6 @@
P.name = pipename
P.on_construction(dir, pipe_dir, color)
- if(PIPE_OMNI_MIXER)
- var/obj/machinery/atmospherics/omni/mixer/P = new(loc)
- P.on_construction(dir, pipe_dir, color)
-
- if(PIPE_OMNI_FILTER)
- var/obj/machinery/atmospherics/omni/filter/P = new(loc)
- P.on_construction(dir, pipe_dir, color)
-
user.visible_message( \
"[user] fastens the [src].", \
"You have fastened the [src].", \
diff --git a/code/game/machinery/portable_tag_turret.dm b/code/game/machinery/portable_tag_turret.dm
index 12bae1bbf5a..4b22315f387 100644
--- a/code/game/machinery/portable_tag_turret.dm
+++ b/code/game/machinery/portable_tag_turret.dm
@@ -6,6 +6,14 @@
// Reasonable defaults, in case someone manually spawns us
var/lasercolor = "r" //Something to do with lasertag turrets, blame Sieve for not adding a comment.
installation = /obj/item/gun/energy/laser/tag/red
+ targetting_is_configurable = FALSE
+ lethal_is_configurable = FALSE
+ shot_delay = 30
+ iconholder = 1
+ has_cover = FALSE
+ always_up = TRUE
+ raised = TRUE
+ req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
/obj/machinery/porta_turret/tag/red
@@ -18,43 +26,15 @@
icon_state = "[lasercolor]grey_target_prism"
/obj/machinery/porta_turret/tag/weapon_setup(var/obj/item/gun/energy/E)
- switch(E.type)
- if(/obj/item/gun/energy/laser/tag/blue)
- eprojectile = /obj/item/gun/energy/laser/tag/blue
- lasercolor = "b"
- req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
- check_arrest = 0
- check_records = 0
- check_weapons = 1
- check_access = 0
- check_anomalies = 0
- shot_delay = 30
+ return
- if(/obj/item/gun/energy/laser/tag/red)
- eprojectile = /obj/item/gun/energy/laser/tag/red
- lasercolor = "r"
- req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
- check_arrest = 0
- check_records = 0
- check_weapons = 1
- check_access = 0
- check_anomalies = 0
- shot_delay = 30
- iconholder = 1
-
-/obj/machinery/porta_turret/tag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- 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()
- ui.set_auto_update(1)
-
-/obj/machinery/porta_turret/tag/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["access"] = !isLocked(user)
- data["locked"] = locked
- data["enabled"] = enabled
- data["is_lethal"] = 0
+/obj/machinery/porta_turret/tag/tgui_data(mob/user)
+ var/list/data = list(
+ "locked" = isLocked(user), // does the current user have access?
+ "on" = enabled, // is turret turned on?
+ "lethal" = FALSE,
+ "lethal_is_configurable" = lethal_is_configurable
+ )
return data
/obj/machinery/porta_turret/tag/update_icon()
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index b63f4ab7f8b..346008fce3f 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -7,18 +7,18 @@
name = "turret"
icon = 'icons/obj/turrets.dmi'
icon_state = "turretCover"
- anchored = 1
- density = 0
+ anchored = TRUE
+ density = FALSE
use_power = IDLE_POWER_USE //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, fire = 90, acid = 90)
- 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/raised = FALSE //if the turret cover is "open" and the turret is raised
+ var/raising= FALSE //if the turret is currently opening or closing its cover
var/health = 80 //the turret's health
- var/locked = 1 //if the turret's behaviour control access is locked
- var/controllock = 0 //if the turret responds to control panels
+ var/locked = TRUE //if the turret's behaviour control access is locked
+ var/controllock = FALSE //if the turret responds to control panels. TRUE = does NOT respond
var/installation = /obj/item/gun/energy/gun/turret //the type of weapon installed
var/gun_charge = 0 //the charge of the gun inserted
@@ -31,68 +31,49 @@
var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire
var/shot_delay = 15 //1.5 seconds between each shot
- var/check_arrest = 1 //checks if the perp is set to arrest
- var/check_records = 1 //checks if a security record exists at all
- var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
- var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
- var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
- var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
- var/ailock = 0 // AI cannot use this
+ var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI
+ var/check_arrest = TRUE //checks if the perp is set to arrest
+ var/check_records = TRUE //checks if a security record exists at all
+ var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have
+ var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements
+ var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
+ var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
+ var/check_borgs = FALSE //if TRUE, target all cyborgs.
+ var/ailock = FALSE // if TRUE, AI cannot use this
- var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
+ var/attacked = FALSE //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
- var/enabled = 1 //determines if the turret is on
- var/lethal = 0 //whether in lethal or stun mode
- var/disabled = 0
+ var/enabled = TRUE //determines if the turret is on
+ var/lethal = FALSE //whether in lethal or stun mode
+ var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed
+ var/disabled = FALSE
var/shot_sound //what sound should play when the turret fires
var/eshot_sound //what sound should play when the emagged turret fires
var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks?
- var/wrenching = 0
+ var/wrenching = FALSE
var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range
- var/screen = 0 // Screen 0: main control, screen 1: access levels
- var/one_access = 0 // Determines if access control is set to req_one_access or req_access
+ var/one_access = FALSE // Determines if access control is set to req_one_access or req_access
+ var/region_min = REGION_GENERAL
+ var/region_max = REGION_COMMAND
- var/syndicate = 0 //is the turret a syndicate turret?
+ var/syndicate = FALSE //is the turret a syndicate turret?
var/faction = ""
- var/emp_vulnerable = 1 // Can be empd
+ var/emp_vulnerable = TRUE // Can be empd
var/scan_range = 7
- var/always_up = 0 //Will stay active
- var/has_cover = 1 //Hides the cover
+ var/always_up = FALSE //Will stay active
+ var/has_cover = TRUE //Hides the cover
-/obj/machinery/porta_turret/centcom
- name = "Centcom Turret"
- enabled = 0
- ailock = 1
- check_synth = 0
- check_access = 1
- check_arrest = 1
- check_records = 1
- check_weapons = 1
- check_anomalies = 1
-
-/obj/machinery/porta_turret/centcom/pulse
- name = "Pulse Turret"
- health = 200
- enabled = 1
- lethal = 1
- req_access = list(ACCESS_CENT_COMMANDER)
- installation = /obj/item/gun/energy/pulse/turret
-
-/obj/machinery/porta_turret/stationary
- ailock = 1
- lethal = 1
- installation = /obj/item/gun/energy/laser
/obj/machinery/porta_turret/Initialize(mapload)
. = ..()
if(req_access && req_access.len)
req_access.Cut()
req_one_access = list(ACCESS_SECURITY, ACCESS_HEADS)
- one_access = 1
+ one_access = TRUE
//Sets up a spark system
spark_system = new /datum/effect_system/spark_spread
@@ -110,7 +91,7 @@
if(req_one_access && req_one_access.len)
req_one_access.Cut()
req_access = list(ACCESS_CENT_SPECOPS)
- one_access = 0
+ one_access = FALSE
/obj/machinery/porta_turret/proc/setup()
var/obj/item/gun/energy/E = new installation //All energy-based weapons are applicable
@@ -185,152 +166,151 @@ GLOBAL_LIST_EMPTY(turret_icons)
else
icon_state = "turretCover"
-/obj/machinery/porta_turret/proc/isLocked(mob/user)
- if(ailock && (isrobot(user) || isAI(user)))
- to_chat(user, "There seems to be a firewall preventing you from accessing this device.")
- return 1
-
- if(locked && !(isrobot(user) || isAI(user) || isobserver(user)))
- to_chat(user, "Access denied.")
- return 1
-
- return 0
-
-/obj/machinery/porta_turret/attack_ai(mob/user)
- if(isLocked(user))
- return
-
- ui_interact(user)
-
-/obj/machinery/porta_turret/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/porta_turret/attack_hand(mob/user)
- if(isLocked(user))
- return
-
- ui_interact(user)
-
-/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- 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()
- ui.set_auto_update(1)
-
-/obj/machinery/porta_turret/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["access"] = !isLocked(user)
- data["screen"] = screen
- data["locked"] = locked
- data["enabled"] = enabled
- data["lethal_control"] = !syndicate ? 1 : 0
- data["lethal"] = lethal
-
- if(data["access"] && !syndicate)
- var/settings[0]
- settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
- settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
- settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
- settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
- settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
- settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
- data["settings"] = settings
-
- if(!syndicate)
- data["one_access"] = one_access
- var/accesses[0]
- var/list/access_list = get_all_accesses()
- for(var/access in access_list)
- var/name = get_access_desc(access)
- var/active
- if(one_access)
- active = (access in req_one_access)
- else
- active = (access in req_access)
- accesses[++accesses.len] = list("name" = name, "active" = active, "number" = access)
- data["accesses"] = accesses
- return data
-
/obj/machinery/porta_turret/proc/HasController()
var/area/A = get_area(src)
return A && A.turret_controls.len > 0
-/obj/machinery/porta_turret/CanUseTopic(var/mob/user)
+/obj/machinery/porta_turret/proc/access_is_configurable()
+ return targetting_is_configurable && !HasController()
+
+/obj/machinery/porta_turret/proc/isLocked(mob/user)
if(HasController())
- to_chat(user, "Turrets can only be controlled using the assigned turret controller.")
- return STATUS_CLOSE
+ return TRUE
+ if(isrobot(user) || isAI(user))
+ if(ailock)
+ to_chat(user, "There seems to be a firewall preventing you from accessing this device.")
+ return TRUE
+ else
+ return FALSE
+ if(isobserver(user))
+ if(user.can_admin_interact())
+ return FALSE
+ else
+ return TRUE
+ if(locked)
+ return TRUE
+ return FALSE
- if(isLocked(user))
- return STATUS_CLOSE
+/obj/machinery/porta_turret/attack_ai(mob/user)
+ tgui_interact(user)
- if(!anchored)
- to_chat(usr, "\The [src] has to be secured first!")
- return STATUS_CLOSE
+/obj/machinery/porta_turret/attack_ghost(mob/user)
+ tgui_interact(user)
- return ..()
+/obj/machinery/porta_turret/attack_hand(mob/user)
+ tgui_interact(user)
-/obj/machinery/porta_turret/Topic(href, href_list, var/nowindow = 0)
- if(..())
- return 1
-
- if(href_list["command"] && href_list["value"])
- var/value = text2num(href_list["value"])
- if(href_list["command"] == "enable")
- enabled = value
- else if(syndicate)
- return 1
- else if(href_list["command"] == "screen")
- screen = value
- else if(href_list["command"] == "lethal")
- lethal = value
- else if(href_list["command"] == "check_synth")
- check_synth = value
- else if(href_list["command"] == "check_weapons")
- check_weapons = value
- else if(href_list["command"] == "check_records")
- check_records = value
- else if(href_list["command"] == "check_arrest")
- check_arrest = value
- else if(href_list["command"] == "check_access")
- check_access = value
- else if(href_list["command"] == "check_anomalies")
- check_anomalies = value
-
- if(!syndicate)
- if(href_list["one_access"])
- toggle_one_access(href_list["one_access"])
-
- if(href_list["access"])
- toggle_access(href_list["access"])
-
- return 1
-
-/obj/machinery/porta_turret/proc/toggle_one_access(var/access)
- one_access = text2num(access)
-
- if(one_access == 1)
- req_one_access = req_access.Copy()
- req_access.Cut()
- else if(one_access == 0)
- req_access = req_one_access.Copy()
- req_one_access.Cut()
-
-/obj/machinery/porta_turret/proc/toggle_access(var/access)
- var/required = text2num(access)
- if(!(required in get_all_accesses()))
+/obj/machinery/porta_turret/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ if(HasController())
+ to_chat(user, "[src] can only be controlled using the assigned turret controller.")
return
+ if(!anchored)
+ to_chat(user, "[src] has to be secured first!")
+ return
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "PortableTurret", name, 500, access_is_configurable() ? 800 : 400)
+ ui.open()
- if(one_access)
- if((required in req_one_access))
- req_one_access -= required
- else
- req_one_access += required
- else
- if((required in req_access))
- req_access -= required
- else
- req_access += required
+/obj/machinery/porta_turret/tgui_data(mob/user)
+ var/list/data = list(
+ "locked" = isLocked(user), // does the current user have access?
+ "on" = enabled,
+ "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up
+ "lethal" = lethal,
+ "lethal_is_configurable" = lethal_is_configurable,
+ "check_weapons" = check_weapons,
+ "neutralize_noaccess" = check_access,
+ "one_access" = one_access,
+ "selectedAccess" = one_access ? req_one_access : req_access,
+ "access_is_configurable" = access_is_configurable(),
+ "neutralize_norecord" = check_records,
+ "neutralize_criminals" = check_arrest,
+ "neutralize_all" = check_synth,
+ "neutralize_unidentified" = check_anomalies,
+ "neutralize_cyborgs" = check_borgs
+ )
+ return data
+
+/obj/machinery/porta_turret/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["regions"] = get_accesslist_static_data(region_min, region_max)
+ return data
+
+/obj/machinery/porta_turret/tgui_act(action, params)
+ if (..())
+ return
+ if(isLocked(usr))
+ return
+ . = TRUE
+ switch(action)
+ if("power")
+ enabled = !enabled
+ if("lethal")
+ if(lethal_is_configurable)
+ lethal = !lethal
+ if(targetting_is_configurable)
+ switch(action)
+ if("authweapon")
+ check_weapons = !check_weapons
+ if("authaccess")
+ check_access = !check_access
+ if("authnorecord")
+ check_records = !check_records
+ if("autharrest")
+ check_arrest = !check_arrest
+ if("authxeno")
+ check_anomalies = !check_anomalies
+ if("authsynth")
+ check_synth = !check_synth
+ if("authborgs")
+ check_borgs = !check_borgs
+ if("set")
+ var/access = text2num(params["access"])
+ if(one_access)
+ if(!(access in req_one_access))
+ req_one_access += access
+ else
+ req_one_access -= access
+ else
+ if(!(access in req_access))
+ req_access += access
+ else
+ req_access -= access
+ if(access_is_configurable())
+ switch(action)
+ if("grant_region")
+ var/region = text2num(params["region"])
+ if(isnull(region))
+ return
+ if(one_access)
+ req_one_access |= get_region_accesses(region)
+ else
+ req_access |= get_region_accesses(region)
+ if("deny_region")
+ var/region = text2num(params["region"])
+ if(isnull(region))
+ return
+ if(one_access)
+ req_one_access -= get_region_accesses(region)
+ else
+ req_access -= get_region_accesses(region)
+ if("clear_all")
+ if(one_access)
+ req_one_access = list()
+ else
+ req_access = list()
+ if("grant_all")
+ if(one_access)
+ req_one_access = get_all_accesses()
+ else
+ req_access = get_all_accesses()
+ if("one_access")
+ if(one_access)
+ req_one_access = list()
+ else
+ req_access = list()
+ one_access = !one_access
/obj/machinery/porta_turret/power_change()
if(powered() || !use_power)
@@ -379,23 +359,25 @@ GLOBAL_LIST_EMPTY(turret_icons)
"You begin [anchored ? "un" : ""]securing the turret." \
)
- wrenching = 1
+ wrenching = TRUE
if(do_after(user, 50 * I.toolspeed, target = src))
//This code handles moving the turret around. After all, it's a portable turret!
if(!anchored)
playsound(loc, I.usesound, 100, 1)
- anchored = 1
+ anchored = TRUE
update_icon()
to_chat(user, "You secure the exterior bolts on the turret.")
else if(anchored)
playsound(loc, I.usesound, 100, 1)
- anchored = 0
+ anchored = FALSE
to_chat(user, "You unsecure the exterior bolts on the turret.")
update_icon()
- wrenching = 0
+ wrenching = FALSE
else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))
- if(allowed(user))
+ if(HasController())
+ to_chat(user, "Turrets regulated by a nearby turret controller are not unlockable.")
+ else if(allowed(user))
locked = !locked
to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].")
updateUsrDialog()
@@ -409,9 +391,9 @@ GLOBAL_LIST_EMPTY(turret_icons)
playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1)
if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off
if(!attacked && !emagged)
- attacked = 1
+ attacked = TRUE
spawn(60)
- attacked = 0
+ attacked = FALSE
..()
@@ -445,12 +427,12 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(user)
to_chat(user, "You short out [src]'s threat assessment circuits.")
visible_message("[src] hums oddly...")
- emagged = 1
+ emagged = TRUE
iconholder = 1
- controllock = 1
- enabled = 0 //turns off the turret temporarily
+ controllock = TRUE
+ enabled = FALSE //turns off the turret temporarily
sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
- enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
+ enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
/obj/machinery/porta_turret/take_damage(force)
if(!raised && !raising)
@@ -470,9 +452,9 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(enabled)
if(!attacked && !emagged)
- attacked = 1
+ attacked = TRUE
spawn(60)
- attacked = 0
+ attacked = FALSE
..()
@@ -489,12 +471,12 @@ GLOBAL_LIST_EMPTY(turret_icons)
check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on
check_anomalies = prob(50)
if(prob(5))
- emagged = 1
+ emagged = TRUE
enabled=0
- spawn(rand(60,600))
+ spawn(rand(60, 600))
if(!enabled)
- enabled=1
+ enabled = TRUE
..()
@@ -520,8 +502,6 @@ GLOBAL_LIST_EMPTY(turret_icons)
/obj/machinery/porta_turret/process()
//the main machinery process
- set background = BACKGROUND_ENABLED
-
if(stat & (NOPOWER|BROKEN))
if(!always_up)
//if the turret has no power or is broken, make the turret pop down if it hasn't already
@@ -587,8 +567,8 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(get_turf(L) == get_turf(src))
return TURRET_NOT_TARGET
- if(!emagged && !syndicate && (issilicon(L) || isbot(L))) // Don't target silica
- return TURRET_NOT_TARGET
+ if(!emagged && !syndicate && (issilicon(L) || isbot(L)))
+ return (check_borgs && isrobot(L)) ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET
if(L.stat && !emagged) //if the perp is dead/dying, no need to bother really
return TURRET_NOT_TARGET //move onto next potential victim!
@@ -645,7 +625,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
return
if(stat & BROKEN)
return
- set_raised_raising(raised, 1)
+ set_raised_raising(raised, TRUE)
playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
update_icon()
@@ -655,7 +635,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
sleep(10)
qdel(flick_holder)
- set_raised_raising(1, 0)
+ set_raised_raising(TRUE, FALSE)
update_icon()
/obj/machinery/porta_turret/proc/popDown() //pops the turret down
@@ -666,7 +646,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
return
if(stat & BROKEN)
return
- set_raised_raising(raised, 1)
+ set_raised_raising(raised, TRUE)
playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
update_icon()
@@ -676,7 +656,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
sleep(10)
qdel(flick_holder)
- set_raised_raising(0, 0)
+ set_raised_raising(FALSE, FALSE)
update_icon()
/obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp)
@@ -741,6 +721,27 @@ GLOBAL_LIST_EMPTY(turret_icons)
A.throw_at(target, scan_range, 1)
return A
+/obj/machinery/porta_turret/centcom
+ name = "Centcom Turret"
+ enabled = FALSE
+ ailock = TRUE
+ check_synth = FALSE
+ check_access = TRUE
+ check_arrest = TRUE
+ check_records = TRUE
+ check_weapons = TRUE
+ check_anomalies = TRUE
+ region_max = REGION_CENTCOMM // Non-turretcontrolled turrets at CC can have their access customized to check for CC accesses.
+
+/obj/machinery/porta_turret/centcom/pulse
+ name = "Pulse Turret"
+ health = 200
+ enabled = TRUE
+ lethal = TRUE
+ lethal_is_configurable = FALSE
+ req_access = list(ACCESS_CENT_COMMANDER)
+ installation = /obj/item/gun/energy/pulse/turret
+
/datum/turret_checks
var/enabled
var/lethal
@@ -750,6 +751,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
var/check_arrest
var/check_weapons
var/check_anomalies
+ var/check_borgs
var/ailock
/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
@@ -765,6 +767,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
check_arrest = TC.check_arrest
check_weapons = TC.check_weapons
check_anomalies = TC.check_anomalies
+ check_borgs = TC.check_borgs
ailock = TC.ailock
power_change()
@@ -793,7 +796,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(istype(I, /obj/item/wrench) && !anchored)
playsound(loc, I.usesound, 100, 1)
to_chat(user, "You secure the external bolts.")
- anchored = 1
+ anchored = TRUE
build_step = 1
return
@@ -818,7 +821,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
else if(istype(I, /obj/item/wrench))
playsound(loc, I.usesound, 75, 1)
to_chat(user, "You unfasten the external bolts.")
- anchored = 0
+ anchored = FALSE
build_step = 0
return
@@ -843,8 +846,10 @@ GLOBAL_LIST_EMPTY(turret_icons)
gun_charge = E.cell.charge //the gun's charge is stored in gun_charge
to_chat(user, "You add [I] to the turret.")
- if(istype(installation, /obj/item/gun/energy/laser/tag/blue) || istype(installation, /obj/item/gun/energy/laser/tag/red))
- target_type = /obj/machinery/porta_turret/tag
+ if(istype(E, /obj/item/gun/energy/laser/tag/blue))
+ target_type = /obj/machinery/porta_turret/tag/blue
+ else if(istype(E, /obj/item/gun/energy/laser/tag/red))
+ target_type = /obj/machinery/porta_turret/tag/red
else
target_type = /obj/machinery/porta_turret
@@ -936,7 +941,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
Turret.name = finish_name
Turret.installation = installation
Turret.gun_charge = gun_charge
- Turret.enabled = 0
+ Turret.enabled = FALSE
Turret.setup()
qdel(src)
@@ -983,25 +988,27 @@ GLOBAL_LIST_EMPTY(turret_icons)
var/icon_state_active = "syndieturret1"
var/icon_state_destroyed = "syndieturret2"
- syndicate = 1
+ syndicate = TRUE
installation = null
- always_up = 1
+ always_up = TRUE
use_power = NO_POWER_USE
- has_cover = 0
- raised = 1
+ has_cover = FALSE
+ raised = TRUE
scan_range = 9
faction = "syndicate"
- emp_vulnerable = 0
+ emp_vulnerable = FALSE
- lethal = 1
- check_arrest = 0
- check_records = 0
- check_weapons = 0
- check_access = 0
- check_anomalies = 1
- check_synth = 1
- ailock = 1
+ lethal = TRUE
+ lethal_is_configurable = FALSE
+ targetting_is_configurable = FALSE
+ check_arrest = FALSE
+ check_records = FALSE
+ check_weapons = FALSE
+ check_access = FALSE
+ check_anomalies = TRUE
+ check_synth = TRUE
+ ailock = TRUE
var/area/syndicate_depot/core/depotarea
/obj/machinery/porta_turret/syndicate/die()
@@ -1020,7 +1027,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(req_one_access && req_one_access.len)
req_one_access.Cut()
req_access = list(ACCESS_SYNDICATE)
- one_access = 0
+ one_access = FALSE
/obj/machinery/porta_turret/syndicate/update_icon()
if(stat & BROKEN)
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 82b473b50f0..6fb34254f3a 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -1,15 +1,17 @@
+#define RECHARGER_POWER_USAGE_GUN 250
+#define RECHARGER_POWER_USAGE_MISC 200
+
/obj/machinery/recharger
name = "recharger"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "recharger0"
desc = "A charging dock for energy based weaponry."
- anchored = 1
+ anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 4
active_power_usage = 200
pass_flags = PASSTABLE
- var/obj/item/charging = null
- var/using_power = FALSE
+
var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/modular_computer, /obj/item/rcs, /obj/item/bodyanalyzer)
var/icon_state_off = "rechargeroff"
var/icon_state_charged = "recharger2"
@@ -17,6 +19,9 @@
var/icon_state_idle = "recharger0"
var/recharge_coeff = 1
+ var/obj/item/charging = null // The item that is being charged
+ var/using_power = FALSE // Whether the recharger is actually transferring power or not, used for icon
+
/obj/machinery/recharger/New()
..()
component_parts = list()
@@ -34,32 +39,32 @@
if(allowed)
if(anchored)
if(charging)
- return 1
+ return TRUE
//Checks to make sure he's not in space doing it, and that the area got proper power.
var/area/a = get_area(src)
- if(!isarea(a) || a.power_equip == 0)
+ if(!isarea(a) || !a.power_equip)
to_chat(user, "[src] blinks red as you try to insert [G].")
- return 1
+ return TRUE
if(istype(G, /obj/item/gun/energy))
var/obj/item/gun/energy/E = G
if(!E.can_charge)
to_chat(user, "Your gun has no external power connector.")
- return 1
+ return TRUE
if(!user.drop_item())
- return 1
+ return TRUE
G.forceMove(src)
charging = G
use_power = ACTIVE_POWER_USE
+ using_power = check_cell_needs_recharging(get_cell_from(G))
update_icon()
else
to_chat(user, "[src] isn't connected to anything!")
- return 1
+ return TRUE
return ..()
-
/obj/machinery/recharger/crowbar_act(mob/user, obj/item/I)
if(panel_open && !charging && default_deconstruction_crowbar(user, I))
return TRUE
@@ -106,57 +111,15 @@
if(stat & (NOPOWER|BROKEN) || !anchored)
return
- using_power = FALSE
- if(charging)
- if(istype(charging, /obj/item/gun/energy))
- var/obj/item/gun/energy/E = charging
- if(E.cell.charge < E.cell.maxcharge)
- E.cell.give(E.cell.chargerate * recharge_coeff)
- E.on_recharge()
- use_power(250)
- using_power = TRUE
-
-
- if(istype(charging, /obj/item/melee/baton))
- var/obj/item/melee/baton/B = charging
- if(B.cell)
- if(B.cell.give(B.cell.chargerate))
- use_power(200)
- using_power = TRUE
-
- if(istype(charging, /obj/item/modular_computer))
- var/obj/item/modular_computer/C = charging
- var/obj/item/computer_hardware/battery/battery_module = C.all_components[MC_CELL]
- if(battery_module)
- var/obj/item/computer_hardware/battery/B = battery_module
- if(B.battery)
- if(B.battery.charge < B.battery.maxcharge)
- B.battery.give(B.battery.chargerate)
- use_power(200)
- using_power = TRUE
-
- if(istype(charging, /obj/item/rcs))
- var/obj/item/rcs/R = charging
- if(R.rcell)
- if(R.rcell.give(R.rcell.chargerate))
- use_power(200)
- using_power = TRUE
-
- if(istype(charging, /obj/item/bodyanalyzer))
- var/obj/item/bodyanalyzer/B = charging
- if(B.cell)
- if(B.cell.give(B.cell.chargerate))
- use_power(200)
- using_power = TRUE
-
- update_icon(using_power)
+ using_power = try_recharging_if_possible()
+ update_icon()
/obj/machinery/recharger/emp_act(severity)
if(stat & (NOPOWER|BROKEN) || !anchored)
..(severity)
return
- if(istype(charging, /obj/item/gun/energy))
+ if(istype(charging, /obj/item/gun/energy))
var/obj/item/gun/energy/E = charging
if(E.cell)
E.cell.emp_act(severity)
@@ -167,7 +130,11 @@
B.cell.charge = 0
..(severity)
-/obj/machinery/recharger/update_icon(using_power = FALSE) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
+/obj/machinery/recharger/power_change()
+ ..()
+ update_icon()
+
+/obj/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
if(stat & (NOPOWER|BROKEN) || !anchored)
icon_state = icon_state_off
return
@@ -179,6 +146,55 @@
return
icon_state = icon_state_idle
+/obj/machinery/recharger/proc/get_cell_from(obj/item/I)
+ if(istype(I, /obj/item/gun/energy))
+ var/obj/item/gun/energy/E = I
+ return E.cell
+
+ if(istype(I, /obj/item/melee/baton))
+ var/obj/item/melee/baton/B = I
+ return B.cell
+
+ if(istype(I, /obj/item/modular_computer))
+ var/obj/item/modular_computer/C = I
+ var/obj/item/computer_hardware/battery/B = C.all_components[MC_CELL]
+ if(B)
+ return B.battery
+
+ if(istype(I, /obj/item/rcs))
+ var/obj/item/rcs/R = I
+ return R.rcell
+
+ if(istype(I, /obj/item/bodyanalyzer))
+ var/obj/item/bodyanalyzer/B = I
+ return B.cell
+
+ return null
+
+/obj/machinery/recharger/proc/check_cell_needs_recharging(obj/item/stock_parts/cell/C)
+ if(!C || C.charge >= C.maxcharge)
+ return FALSE
+ return TRUE
+
+/obj/machinery/recharger/proc/recharge_cell(obj/item/stock_parts/cell/C, power_usage)
+ C.give(C.chargerate * recharge_coeff)
+ use_power(power_usage)
+
+/obj/machinery/recharger/proc/try_recharging_if_possible()
+ var/obj/item/stock_parts/cell/C = get_cell_from(charging)
+ if(!check_cell_needs_recharging(C))
+ return FALSE
+
+ if(istype(charging, /obj/item/gun/energy))
+ recharge_cell(C, RECHARGER_POWER_USAGE_GUN)
+
+ var/obj/item/gun/energy/E = charging
+ E.on_recharge()
+ else
+ recharge_cell(C, RECHARGER_POWER_USAGE_MISC)
+
+ return TRUE
+
/obj/machinery/recharger/examine(mob/user)
. = ..()
if(charging && (!in_range(user, src) && !issilicon(user) && !isobserver(user)))
@@ -204,3 +220,6 @@
icon_state_idle = "wrecharger0"
icon_state_charging = "wrecharger1"
icon_state_charged = "wrecharger2"
+
+#undef RECHARGER_POWER_USAGE_GUN
+#undef RECHARGER_POWER_USAGE_MISC
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index a17f9dfb9bd..b6da46a67c8 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -177,8 +177,8 @@
F.broken = 0
F.times_used = 0
F.icon_state = "flash"
- if(istype(O,/obj/item/gun/energy/disabler/cyborg))
- var/obj/item/gun/energy/disabler/cyborg/D = O
+ if(istype(O,/obj/item/gun/energy))
+ var/obj/item/gun/energy/D = O
if(D.cell.charge < D.cell.maxcharge)
var/obj/item/ammo_casing/energy/E = D.ammo_type[D.select]
D.cell.give(E.e_cost)
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 081a48c3da1..56019d22f7c 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -8,7 +8,7 @@
layer = MOB_LAYER+1 // Overhead
anchored = 1
density = 1
- damage_deflection = 10
+ damage_deflection = 15
var/safety_mode = 0 // Temporarily stops machine if it detects a mob
var/icon_name = "grinder-o"
var/blood = 0
@@ -19,8 +19,7 @@
var/item_recycle_sound = 'sound/machines/recycler.ogg'
/obj/machinery/recycler/New()
- AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_PLASTIC, MAT_BLUESPACE), 0,
- TRUE, null, null, null, TRUE)
+ AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_PLASTIC, MAT_BLUESPACE), 0, TRUE, null, null, null, TRUE)
..()
component_parts = list()
component_parts += new /obj/item/circuitboard/recycler(null)
@@ -37,7 +36,7 @@
mat_mod *= 50000
for(var/obj/item/stock_parts/manipulator/M in component_parts)
amt_made = 25 * M.rating //% of materials salvaged
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.max_amount = mat_mod
amount_produced = min(100, amt_made)
@@ -93,7 +92,6 @@
if(AM)
Bumped(AM)
-
/obj/machinery/recycler/Bumped(atom/movable/AM)
if(stat & (BROKEN|NOPOWER))
@@ -135,7 +133,7 @@
/obj/machinery/recycler/proc/recycle_item(obj/item/I)
I.forceMove(loc)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/material_amount = materials.get_item_material_amount(I)
if(!material_amount)
qdel(I)
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 095b2e7d56e..85799279ff4 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -59,7 +59,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
var/announceAuth = 0 //Will be set to 1 when you authenticate yourself for announcements
var/msgVerified = "" //Will contain the name of the person who varified it
var/msgStamped = "" //If a message is stamped, this will contain the stamp name
- var/message = "";
+ var/message = ""
var/recipient = ""; //the department which will be receiving the message
var/priority = -1 ; //Priority of the message being sent
light_range = 0
@@ -200,7 +200,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
var/log_msg = message
var/pass = 0
screen = RCS_SENTFAIL
- for(var/obj/machinery/message_server/MS in world)
+ for(var/obj/machinery/message_server/MS in GLOB.machines)
if(!MS.active) continue
MS.send_rc_message(ckey(href_list["department"]),department,log_msg,msgStamped,msgVerified,priority)
pass = 1
@@ -225,7 +225,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
message_log += "Message sent to [recipient] at [station_time_timestamp()] [message]"
Radio.autosay("Alert; a new requests console message received for [recipient] from [department]", null, "[radiochannel]")
else
- audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4)
+ atom_say("No server detected!")
//Handle screen switching
if(href_list["setScreen"])
@@ -256,7 +256,7 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
else if(world.time < print_cooldown)
error_message = "Please allow the printer time to prepare the next shipping label."
if(error_message)
- audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: [error_message]'"),,4)
+ atom_say("[error_message]")
return
print_label(ship_tag_name, ship_tag_index)
shipping_log += "Shipping Label printed for [ship_tag_name] [msgVerified]"
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index d0cf38a272d..1624ef4bb92 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -12,23 +12,26 @@
var/resultlvl = null
/obj/machinery/slot_machine/attack_hand(mob/user as mob)
+ tgui_interact(user)
+
+/obj/machinery/slot_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "SlotMachine", name, 350, 200, master_ui, state)
+ ui.open()
+
+/obj/machinery/slot_machine/tgui_data(mob/user)
+ var/list/data = list()
+ // Get account
account = user.get_worn_id_account()
if(!account)
if(istype(user.get_active_hand(), /obj/item/card/id))
account = get_card_account(user.get_active_hand())
else
account = null
- ui_interact(user)
-/obj/machinery/slot_machine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- 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()
- ui.set_auto_update(1)
-/obj/machinery/slot_machine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+ // Send data
data["working"] = working
data["money"] = account ? account.money : null
data["plays"] = plays
@@ -36,22 +39,23 @@
data["resultlvl"] = resultlvl
return data
-/obj/machinery/slot_machine/Topic(href, href_list)
+/obj/machinery/slot_machine/tgui_act(action, params)
+ if(..())
+ return
add_fingerprint(usr)
- if(href_list["ops"])
- if(text2num(href_list["ops"])) // Play
- if(working)
- return
- if(!account || account.money < 10)
- return
- if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
- return
- plays++
- working = 1
- icon_state = "slots-on"
- playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
- addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
+ if(action == "spin")
+ if(working)
+ return
+ if(!account || account.money < 10)
+ return
+ if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
+ return
+ plays++
+ working = TRUE
+ icon_state = "slots-on"
+ playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
+ addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
/obj/machinery/slot_machine/proc/spin_slots(userName)
switch(rand(1,4050))
@@ -59,44 +63,45 @@
atom_say("JACKPOT! [userName] has won a MILLION CREDITS!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of ONE MILLION CREDITS!", "Jackpot Winner")
result = "JACKPOT! You win one million credits!"
- resultlvl = "highlight"
+ resultlvl = "teal"
win_money(1000000, 'sound/goonstation/misc/airraid_loop.ogg')
if(2 to 5) // .07%
atom_say("Big Winner! [userName] has won a hundred thousand credits!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning a hundred thousand credits!", "Big Winner")
result = "Big Winner! You win a hundred thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(100000, 'sound/goonstation/misc/klaxon.ogg')
if(6 to 50) // 1.08%
atom_say("Big Winner! [userName] has won ten thousand credits!")
result = "You win ten thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(10000, 'sound/goonstation/misc/klaxon.ogg')
if(51 to 100) // 1.21%
atom_say("Winner! [userName] has won a thousand credits!")
result = "You win a thousand credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(1000, 'sound/goonstation/misc/bell.ogg')
if(101 to 200) // 2.44%
atom_say("Winner! [userName] has won a hundred credits!")
result = "You win a hundred credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(100, 'sound/goonstation/misc/bell.ogg')
if(201 to 300) // 2.44%
atom_say("Winner! [userName] has won fifty credits!")
result = "You win fifty credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(50)
if(301 to 1000) // 17.26%
atom_say("Winner! [userName] has won ten credits!")
result = "You win ten credits!"
- resultlvl = "good"
+ resultlvl = "green"
win_money(10)
else // 75.31%
- result = "No luck!"
- resultlvl = "average"
- working = 0
+ result = "No luck!"
+ resultlvl = "orange"
+ working = FALSE
icon_state = "slots-off"
+ SStgui.update_uis(src) // Push a UI update
/obj/machinery/slot_machine/proc/win_money(amt, sound='sound/machines/ping.ogg')
if(sound)
@@ -104,3 +109,9 @@
if(!account)
return
account.credit(amt, "Slot Winnings", "Slot Machine", account.owner_name)
+
+/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.tool_use_check(user, 0))
+ return
+ default_unfasten_wrench(user, I)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 3a6f3011747..822c4af3e1a 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -255,6 +255,7 @@
occupant_typecache = typecacheof(occupant_typecache)
/obj/machinery/suit_storage_unit/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(suit)
QDEL_NULL(helmet)
QDEL_NULL(mask)
@@ -740,10 +741,11 @@
if(!occupant)
return
- if(user != occupant)
- to_chat(occupant, "The machine kicks you out!")
- if(user.loc != loc)
- to_chat(occupant, "You leave the not-so-cozy confines of the SSU.")
+ if(user)
+ if(user != occupant)
+ to_chat(occupant, "The machine kicks you out!")
+ if(user.loc != loc)
+ to_chat(occupant, "You leave the not-so-cozy confines of [src].")
occupant.forceMove(loc)
occupant = null
if(!state_open)
@@ -751,6 +753,8 @@
update_icon()
return
+/obj/machinery/suit_storage_unit/force_eject_occupant()
+ eject_occupant()
/obj/machinery/suit_storage_unit/verb/get_out()
set name = "Eject Suit Storage Unit"
@@ -772,6 +776,8 @@
if(usr.stat != 0)
return
+ if(usr.incapacitated() || usr.buckled) //are you cuffed, dying, lying, stunned or other
+ return
if(!state_open)
to_chat(usr, "The unit's doors are shut.")
return
@@ -797,3 +803,7 @@
/obj/machinery/suit_storage_unit/attack_ai(mob/user as mob)
return attack_hand(user)
+
+/obj/machinery/suit_storage_unit/proc/check_electrified_callback()
+ if(!wires.is_cut(WIRE_ELECTRIFY))
+ shocked = FALSE
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index 6299be7be3d..a5cff57ad52 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -121,7 +121,8 @@
if(user)
to_chat(user, "The connected wire doesn't have enough current.")
return
- for(var/obj/singularity/singulo in GLOB.singularities)
+ for(var/thing in GLOB.singularities)
+ var/obj/singularity/singulo = thing
if(singulo.z == z)
singulo.target = src
icon_state = "[icontype]1"
@@ -132,7 +133,8 @@
/obj/machinery/power/singularity_beacon/proc/Deactivate(mob/user = null)
- for(var/obj/singularity/singulo in world)
+ for(var/thing in GLOB.singularities)
+ var/obj/singularity/singulo = thing
if(singulo.target == src)
singulo.target = null
icon_state = "[icontype]0"
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index a47adfd25e8..37dd2c6d439 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -77,11 +77,10 @@
update_icon()
try_detonate(TRUE)
//Counter terrorists win
- else if(!active || defused)
- if(defused && (payload in src))
+ else if(defused)
+ active = FALSE
+ if(payload in src)
payload.defuse()
- countdown.stop()
- STOP_PROCESSING(SSfastprocess, src)
/obj/machinery/syndicatebomb/New()
wires = new(src)
@@ -92,6 +91,7 @@
..()
/obj/machinery/syndicatebomb/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
QDEL_NULL(countdown)
STOP_PROCESSING(SSfastprocess, src)
@@ -175,7 +175,7 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
- if(open_panel && wires.IsAllCut())
+ if(open_panel && wires.is_all_cut())
if(payload)
to_chat(user, "You carefully pry out [payload].")
payload.loc = user.loc
@@ -189,7 +189,7 @@
/obj/machinery/syndicatebomb/welder_act(mob/user, obj/item/I)
. = TRUE
- if(payload || !wires.IsAllCut() || !open_panel)
+ if(payload || !wires.is_all_cut() || !open_panel)
return
if(!I.tool_use_check(user, 0))
return
@@ -244,7 +244,7 @@
/obj/machinery/syndicatebomb/proc/settings(mob/user)
var/new_timer = input(user, "Please set the timer.", "Timer", "[timer_set]") as num
if(can_interact(user)) //No running off and setting bombs from across the station
- timer_set = Clamp(new_timer, minimum_timer, maximum_timer)
+ timer_set = clamp(new_timer, minimum_timer, maximum_timer)
loc.visible_message("[bicon(src)] timer set for [timer_set] seconds.")
if(alert(user,"Would you like to start the countdown now?",,"Yes","No") == "Yes" && can_interact(user))
if(defused || active)
@@ -265,9 +265,6 @@
add_attack_logs(user, src, "has primed a [name] ([payload]) for detonation", ATKLOG_FEW)
payload.adminlog = "\The [src] that [key_name(user)] had primed detonated!"
-/obj/machinery/syndicatebomb/proc/isWireCut(var/index)
- return wires.IsIndexCut(index)
-
///Bomb Subtypes///
/obj/machinery/syndicatebomb/training
@@ -298,7 +295,7 @@
/obj/machinery/syndicatebomb/empty/New()
..()
- wires.CutAll()
+ wires.cut_all()
/obj/machinery/syndicatebomb/self_destruct
name = "self destruct device"
@@ -369,7 +366,7 @@
var/obj/machinery/syndicatebomb/holder = loc
if(istype(holder))
if(holder.wires)
- holder.wires.Shuffle()
+ holder.wires.shuffle_wires()
holder.defused = 0
holder.open_panel = 0
holder.delayedbig = FALSE
diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm
index a857afcef89..c3531c7f674 100644
--- a/code/game/machinery/tcomms/_base.dm
+++ b/code/game/machinery/tcomms/_base.dm
@@ -40,6 +40,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
var/network_id = "None"
/// Is the machine active
var/active = TRUE
+ /// Has the machine been hit by an ionspheric anomalie
+ var/ion = FALSE
/**
* Base Initializer
@@ -50,6 +52,19 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
. = ..()
GLOB.tcomms_machines += src
update_icon()
+ if((!mapload) && (usr))
+ // To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you
+ // get_area_name is fucking broken and uses a for(x in world) search
+ // It doesnt even work, is expensive, and returns 0
+ // Im not refactoring one thing which could risk breaking all admin location logs
+ // Fight me
+ log_action(usr, "constructed a new [src] at [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]", adminmsg = TRUE)
+ // Add in component parts for the sake of deconstruction
+ component_parts = list()
+ component_parts += new /obj/item/stock_parts/manipulator(null)
+ component_parts += new /obj/item/stock_parts/manipulator(null)
+ component_parts += new /obj/item/stack/cable_coil(null, 1)
+ component_parts += new /obj/item/stack/cable_coil(null, 1)
/**
* Base Destructor
@@ -58,6 +73,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
*/
/obj/machinery/tcomms/Destroy()
GLOB.tcomms_machines -= src
+ if(usr)
+ log_action(usr, "destroyed a [src] at [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]", adminmsg = TRUE)
return ..()
/**
@@ -67,7 +84,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
*/
/obj/machinery/tcomms/update_icon()
. = ..()
- if(!active || (stat & NOPOWER))
+ // Show the off sprite if were inactive, ion'd or unpowered
+ if(!active || (stat & NOPOWER) || ion)
icon_state = "[initial(icon_state)]_off"
else
icon_state = initial(icon_state)
@@ -88,23 +106,37 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
/**
- * Machine Enabler
+ * Start of Ion Anomaly Event
*
- * Quick and dirty proc to allow for the machine to be programatically enabled easily. Used for the anomaly event
+ * Proc to easily start an Ion Anomaly's effects, and update the icon
*/
-/obj/machinery/tcomms/proc/enable_machine()
- active = TRUE
+/obj/machinery/tcomms/proc/start_ion()
+ ion = TRUE
update_icon()
/**
- * Machine Disabler
+ * End of Ion Anomaly Event
*
- * Quick and dirty proc to allow for the machine to be programatically disabled easily. Used for the anomaly event
+ * Proc to easily stop an Ion Anomaly's effects, and update the icon
*/
-/obj/machinery/tcomms/proc/disable_machine()
- active = FALSE
+/obj/machinery/tcomms/proc/end_ion()
+ ion = FALSE
update_icon()
+/**
+ * Z-Level transit change helper
+ *
+ * Proc to make sure you cant have two of these active on a Z-level at once. It also makes sure to update the linkage
+ */
+/obj/machinery/tcomms/onTransitZ(old_z, new_z)
+ . = ..()
+ if(active)
+ active = FALSE
+ // This needs a timer because otherwise its on the shuttle Z and the message is missed
+ addtimer(CALLBACK(src, /atom.proc/visible_message, "Radio equipment on [src] has been overloaded by heavy bluespace interference. Please restart the machine."), 5)
+ update_icon()
+
+
/**
* Logging helper
*
@@ -171,6 +203,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
var/vname
/// List of all channels this can be sent or recieved on
var/list/zlevels = list()
+ /// Should this signal be re-broadcasted (Can be modified by NTTC, defaults to TRUE)
+ var/pass = TRUE
/**
* Destructor for the TCM datum.
@@ -301,7 +335,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
if(is_admin(R) && !R.get_preference(CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
- if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes.
+ if(isnewplayer(R)) // we don't want new players to hear messages. rare but generates runtimes.
continue
// --- Can understand the speech ---
@@ -457,3 +491,20 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
break
return ..()
+/**
+ * Screwdriver Act Handler
+ *
+ * Handles the screwdriver action for all tcomms machines, so they can be open and closed to be deconstructed
+ */
+/obj/machinery/tcomms/screwdriver_act(mob/user, obj/item/I)
+ . = TRUE
+ default_deconstruction_screwdriver(user, icon_state, icon_state, I)
+
+/**
+ * Crowbar Act Handler
+ *
+ * Handles the crowbar action for all tcomms machines, so they can be deconstructed
+ */
+/obj/machinery/tcomms/crowbar_act(mob/user, obj/item/I)
+ . = TRUE
+ default_deconstruction_crowbar(user, I)
diff --git a/code/game/machinery/tcomms/core.dm b/code/game/machinery/tcomms/core.dm
index 9e638913f9d..622ce2e2b9f 100644
--- a/code/game/machinery/tcomms/core.dm
+++ b/code/game/machinery/tcomms/core.dm
@@ -1,5 +1,6 @@
#define UI_TAB_CONFIG "CONFIG"
#define UI_TAB_LINKS "LINKS"
+#define UI_TAB_FILTER "FILTER"
/**
* # Telecommunications Core
@@ -13,6 +14,8 @@
name = "Telecommunications Core"
desc = "A large rack full of communications equipment. Looks important."
icon_state = "core"
+ // This starts as off so you cant make cores as hot spares
+ active = FALSE
/// The NTTC config for this device
var/datum/nttc_configuration/nttc = new()
/// List of all reachable devices
@@ -33,6 +36,12 @@
. = ..()
link_password = GenerateKey()
reachable_zlevels |= loc.z
+ component_parts += new /obj/item/circuitboard/tcomms/core(null)
+ if(check_power_on())
+ active = TRUE
+ else
+ visible_message("Error: Another core is already active in this sector. Power-up cancelled due to radio interference.")
+ update_icon()
/**
* Destructor for the core.
@@ -56,6 +65,9 @@
* * zlevel - The input z level to test
*/
/obj/machinery/tcomms/core/proc/zlevel_reachable(zlevel)
+ // Nothing is reachable if the core is offline, unpowered, or ion'd
+ if(!active || (stat & NOPOWER) || ion)
+ return FALSE
if(zlevel in reachable_zlevels)
return TRUE
else
@@ -71,8 +83,8 @@
* * tcm - The tcomms message datum
*/
/obj/machinery/tcomms/core/proc/handle_message(datum/tcomms_message/tcm)
- // Don't do anything with rejected signals, or if were offline, or if we have no power
- if(tcm.reject || !active || (stat & NOPOWER))
+ // Don't do anything with rejected signals, if were offline, if we are ion'd, or if we have no power
+ if(tcm.reject || !active || (stat & NOPOWER) || ion)
return FALSE
// Kill the signal if its on a z-level that isnt reachable
if(!zlevel_reachable(tcm.source_level))
@@ -81,6 +93,11 @@
// Now we can run NTTC
tcm = nttc.modify_message(tcm)
+ // If the signal shouldnt be broadcast, dont broadcast it
+ if(!tcm.pass)
+ // We still return TRUE here because the signal was handled, even though we didnt broadcast
+ return TRUE
+
// Now we generate the list of where that signal should go to
tcm.zlevels = reachable_zlevels
tcm.zlevels |= tcm.source_level
@@ -107,10 +124,42 @@
// Add all the linked relays in
for(var/obj/machinery/tcomms/relay/R in linked_relays)
// Only if the relay is active
- if(R.active)
+ if(R.active && !(R.stat & NOPOWER))
reachable_zlevels |= R.loc.z
+/**
+ * Z-Level transit change helper
+ *
+ * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels
+ */
+/obj/machinery/tcomms/core/onTransitZ(old_z, new_z)
+ . = ..()
+ refresh_zlevels()
+
+/**
+ * Power-on checker
+ *
+ * Checks the z-level to see if an existing core is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot
+ */
+/obj/machinery/tcomms/core/proc/check_power_on()
+ // Cancel if we are already on
+ if(active)
+ return TRUE
+
+ for(var/obj/machinery/tcomms/core/C in GLOB.tcomms_machines)
+ // Make sure we dont check ourselves
+ if(C == src)
+ continue
+ // We dont care about ones on other zlevels
+ if(!atoms_share_level(C, src))
+ continue
+ // If another core is active, return FALSE
+ if(C.active)
+ return FALSE
+ // If we got here there isnt an active core on this Z-level. So return true
+ return TRUE
+
//////////////
// UI STUFF //
//////////////
@@ -131,6 +180,7 @@
var/data[0]
// What tab are we on
data["tab"] = ui_tab
+ data["ion"] = ion
// Only send NTTC settings if were on the right tab. This saves on sending overhead.
if(ui_tab == UI_TAB_CONFIG)
@@ -168,6 +218,9 @@
data["entries"] += list(list("addr" = "\ref[R]", "net_id" = R.network_id, "sector" = R.loc.z, "status" = status, "status_color" = status_color))
// End the shit
+ if(ui_tab == UI_TAB_FILTER)
+ data["filtered_users"] = nttc.filtering
+
return data
/obj/machinery/tcomms/core/Topic(href, href_list)
@@ -177,15 +230,18 @@
if(href_list["tab"])
// Make sure its a valid tab
- if(href_list["tab"] in list(UI_TAB_CONFIG, UI_TAB_LINKS))
+ if(href_list["tab"] in list(UI_TAB_CONFIG, UI_TAB_LINKS, UI_TAB_FILTER))
ui_tab = href_list["tab"]
// Check if they did a href, but only for that current tab
if(ui_tab == UI_TAB_CONFIG)
// All the toggle on/offs go here
if(href_list["toggle_active"])
- active = !active
- update_icon()
+ if(check_power_on())
+ active = !active
+ update_icon()
+ else
+ to_chat(usr, "Error: Another core is already active in this sector. Power-up cancelled due to radio interference.")
// NTTC Toggles
if(href_list["nttc_toggle_jobs"])
nttc.toggle_jobs = !nttc.toggle_jobs
@@ -257,9 +313,35 @@
to_chat(usr, "Successfully changed password from [link_password] to [new_password].")
link_password = new_password
+ if(ui_tab == UI_TAB_FILTER)
+ if(href_list["add_filter"])
+ // This is a stripped input because I did NOT come this far for this system to be abused by HTML injection
+ var/name_to_add = stripped_input(usr, "Enter a name to add to the filtering list", "Name Entry")
+ if(name_to_add == "")
+ return
+ if(name_to_add in nttc.filtering)
+ to_chat(usr, "ERROR: User already in filtering list.")
+ else
+ nttc.filtering |= name_to_add
+ log_action(usr, "has added [name_to_add] to the NTTC filter list on core with ID [network_id]", TRUE)
+ to_chat(usr, "Successfully added [name_to_add] to the NTTC filtering list.")
+
+
+ if(href_list["remove_filter"])
+ var/name_to_remove = href_list["remove_filter"]
+ if(!(name_to_remove in nttc.filtering))
+ to_chat(usr, "ERROR: Name does not exist in filter list. Please file an issue report.")
+ else
+ var/confirm = alert(usr, "Are you sure you want to remove [name_to_remove] from the filtering list?", "Confirm Removal", "Yes", "No")
+ if(confirm == "Yes")
+ nttc.filtering -= name_to_remove
+ log_action(usr, "has removed [name_to_remove] from the NTTC filter list on core with ID [network_id]", TRUE)
+ to_chat(usr, "Successfully removed [name_to_remove] from the NTTC filtering list.")
+
// Hack to speed update the nanoUI
SSnanoui.update_uis(src)
#undef UI_TAB_CONFIG
#undef UI_TAB_LINKS
+#undef UI_TAB_FILTER
diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm
index 6ee158c6cc0..cd880970b23 100644
--- a/code/game/machinery/tcomms/nttc.dm
+++ b/code/game/machinery/tcomms/nttc.dm
@@ -2,7 +2,7 @@
NTTC system
This is basically the replacement for NTSL and allows tickbox features such as job titles and colours, without needing a script
This also means that there is no user input here, which means the system isnt prone to exploits since its only selecting options, no user input
- Basically, just imagine pfSense for tcomsm
+ Basically, just imagine pfSense for tcomms
All this code was written by Tigercat2000. I take no credit -aa07
*/
@@ -153,6 +153,10 @@
var/list/job_card_styles = list(
JOB_STYLE_1, JOB_STYLE_2, JOB_STYLE_3, JOB_STYLE_4
)
+
+ // List of people who will get blocked out of comms
+ var/list/filtering = list()
+
// Used to determine what languages are allowable for conversion. Generated during runtime.
var/list/valid_languages = list("--DISABLE--")
@@ -220,6 +224,9 @@
// Primary signal modification. This is where all of the variables behavior are actually implemented.
/datum/nttc_configuration/proc/modify_message(datum/tcomms_message/tcm)
+ // Check if they should be blacklisted right off the bat. We can save CPU if the message wont even be processed
+ if(tcm.sender_name in filtering)
+ tcm.pass = FALSE
// All job and coloring shit
if(toggle_job_color || toggle_name_color)
var/job = tcm.sender_job
diff --git a/code/game/machinery/tcomms/relay.dm b/code/game/machinery/tcomms/relay.dm
index 8c2bbf28bf2..bf5e6e21a9c 100644
--- a/code/game/machinery/tcomms/relay.dm
+++ b/code/game/machinery/tcomms/relay.dm
@@ -9,6 +9,8 @@
name = "Telecommunications Relay"
desc = "A large device with several radio antennas on it."
icon_state = "relay"
+ // This starts as off so you cant make cores as hot spares
+ active = FALSE
/// The host core for this relay
var/obj/machinery/tcomms/core/linked_core
/// ID of the hub to auto link to
@@ -25,6 +27,12 @@
*/
/obj/machinery/tcomms/relay/Initialize(mapload)
. = ..()
+ component_parts += new /obj/item/circuitboard/tcomms/relay(null)
+ if(check_power_on())
+ active = TRUE
+ else
+ visible_message("Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.")
+ update_icon()
if(mapload && autolink_id)
return INITIALIZE_HINT_LATELOAD
@@ -50,6 +58,40 @@
// Only ONE of these with one ID should exist per world
break
+/**
+ * Z-Level transit change helper
+ *
+ * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels on the linked core
+ */
+/obj/machinery/tcomms/relay/onTransitZ(old_z, new_z)
+ . = ..()
+ if(linked_core)
+ linked_core.refresh_zlevels()
+
+
+/**
+ * Power-on checker
+ *
+ * Checks the z-level to see if an existing relay is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot
+ */
+/obj/machinery/tcomms/relay/proc/check_power_on()
+ // Cancel if we are already on
+ if(active)
+ return TRUE
+
+ for(var/obj/machinery/tcomms/relay/R in GLOB.tcomms_machines)
+ // Make sure we dont check ourselves
+ if(R == src)
+ continue
+ // We dont care about ones on other zlevels
+ if(!atoms_share_level(R, src))
+ continue
+ // If another relay is active, return FALSE
+ if(R.active)
+ return FALSE
+ // If we got here there isnt an active relay on this Z-level. So return TRUE
+ return TRUE
+
/**
* Proc to link the relay to the core.
*
@@ -77,26 +119,15 @@
linked = FALSE
/**
- * Relay Enabler
+ * Power Change Handler
*
- * Modification to the standard one so that the links get updated
+ * Proc which ensures the host core has its zlevels updated (icons are updated by parent call)
*/
-/obj/machinery/tcomms/relay/enable_machine()
+/obj/machinery/tcomms/relay/power_change()
..()
if(linked_core)
linked_core.refresh_zlevels()
-/**
- * Relay Disabler
- *
- * Modification to the standard one so that the links get updated
- */
-/obj/machinery/tcomms/relay/disable_machine()
- ..()
- if(linked_core)
- linked_core.refresh_zlevels()
-
-
//////////////
// UI STUFF //
//////////////
@@ -137,10 +168,13 @@
// All the toggle on/offs go here
if(href_list["toggle_active"])
- active = !active
- update_icon()
- if(linked_core)
- linked_core.refresh_zlevels()
+ if(check_power_on())
+ active = !active
+ update_icon()
+ if(linked_core)
+ linked_core.refresh_zlevels()
+ else
+ to_chat(usr, "Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.")
// Set network ID
if(href_list["network_id"])
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index b92e3c78bb8..005e6720b8b 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -1,16 +1,24 @@
+#define REGIME_TELEPORT 0
+#define REGIME_GATE 1
+#define REGIME_GPS 2
+
/obj/machinery/computer/teleporter
name = "teleporter control console"
desc = "Used to control a linked teleportation Hub and Station."
icon_screen = "teleport"
icon_keyboard = "teleport_key"
circuit = /obj/item/circuitboard/teleporter
- var/obj/item/gps/locked = null
- var/regime_set = "Teleporter"
+ var/obj/item/gps/locked = null /// A GPS with a locked destination
+ var/regime = REGIME_TELEPORT /// Switches mode between teleporter, gate and gps
var/id = null
- var/obj/machinery/teleport/station/power_station
- var/calibrating
- var/turf/target //Used for one-time-use teleport cards (such as clown planet coordinates.)
- //Setting this to 1 will set src.locked to null after a player enters the portal and will not allow hand-teles to open portals to that location.
+ var/obj/machinery/teleport/station/power_station /// The power station that's connected to the console
+ var/calibrating = FALSE /// Whether calibration is in progress or not. Calibration prevents changes.
+ var/turf/target ///The target turf of the teleporter
+ var/target_list ///lists of suitable teleport targets, dependent on regime. Used in the UI
+
+ /* var/area_bypass is for one-time-use teleport cards (such as clown planet coordinates.)
+ Setting this to TRUE will set var/obj/item/gps/locked to null after a player enters the portal and will not allow hand-teles to open portals to that location.
+ */
var/area_bypass = FALSE
var/cc_beacon = FALSE
@@ -24,6 +32,7 @@
..()
link_power_station()
update_icon()
+ target_list = targets_teleport()
/obj/machinery/computer/teleporter/Destroy()
if(power_station)
@@ -55,202 +64,238 @@
/obj/machinery/computer/teleporter/emag_act(mob/user)
if(!emagged)
- emagged = 1
+ emagged = TRUE
to_chat(user, "The teleporter can now lock on to Syndicate beacons!")
else
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/computer/teleporter/attack_ai(mob/user)
- src.attack_hand(user)
+ attack_hand(user)
/obj/machinery/computer/teleporter/attack_hand(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+
+/obj/machinery/computer/teleporter/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(stat & (NOPOWER|BROKEN))
return
-
- // Set up the Nano UI
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.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 = new(user, src, ui_key, "Teleporter", "Teleporter Console", 380, 260)
ui.open()
-/obj/machinery/computer/teleporter/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/machinery/computer/teleporter/tgui_data(mob/user)
+ var/list/data = list()
data["powerstation"] = power_station
- if(power_station)
+ if(power_station?.teleporter_hub)
data["teleporterhub"] = power_station.teleporter_hub
data["calibrated"] = power_station.teleporter_hub.calibrated
- data["accurate"] = power_station.teleporter_hub.accurate
else
data["teleporterhub"] = null
data["calibrated"] = null
- data["accurate"] = null
- data["regime"] = regime_set
+ data["regime"] = regime
var/area/targetarea = get_area(target)
data["target"] = (!target || !targetarea) ? "None" : sanitize(targetarea.name)
data["calibrating"] = calibrating
- data["locked"] = locked
+ data["locked"] = locked ? TRUE : FALSE
+ data["targetsTeleport"] = target_list
return data
-/obj/machinery/computer/teleporter/Topic(href, href_list)
+/obj/machinery/computer/teleporter/tgui_act(action, params)
if(..())
- return 1
-
- if(href_list["eject"])
- eject()
- SSnanoui.update_uis(src)
return
if(!check_hub_connection())
- to_chat(usr, "Error: Unable to detect hub.")
- SSnanoui.update_uis(src)
+ atom_say("Error: Unable to detect hub.")
return
if(calibrating)
- to_chat(usr, "Error: Calibration in progress. Stand by.")
- SSnanoui.update_uis(src)
+ atom_say("Error: Calibration in progress. Stand by.")
return
- if(href_list["regimeset"])
- power_station.engaged = 0
- power_station.teleporter_hub.update_icon()
- power_station.teleporter_hub.calibrated = 0
- reset_regime()
- 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)
- 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)
- SSnanoui.update_uis(src)
- if(href_list["calibrate"])
- if(!target)
- to_chat(usr, "Error: No target set to calibrate to.")
- SSnanoui.update_uis(src)
- return
- if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3)
- to_chat(usr, "Hub is already calibrated.")
- SSnanoui.update_uis(src)
- return
- src.visible_message("Processing hub calibration to target...")
+ . = TRUE
- calibrating = 1
- SSnanoui.update_uis(src)
- spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration
- calibrating = 0
- if(check_hub_connection())
- power_station.teleporter_hub.calibrated = 1
- src.visible_message("Calibration complete.")
- else
- src.visible_message("Error: Unable to detect hub.")
- SSnanoui.update_uis(src)
+ switch(action)
+ if("eject") //eject gps device
+ eject()
+ if("load") //load gps coordinates
+ target = locate(locked.locked_location.x,locked.locked_location.y,locked.locked_location.z)
+ if("setregime")
+ regime = text2num(params["regime"])
+ if(regime == REGIME_TELEPORT)
+ target_list = targets_teleport()
+ if(regime == REGIME_GATE)
+ target_list = targets_gate()
+ if(regime == REGIME_GPS)
+ target_list = null //clears existing entries, target is added by load action
+ resetPowerstation()
+ target = null
+ if("settarget")
+ resetPowerstation()
+ var/turf/tmpTarget = locate(text2num(params["x"]),text2num(params["y"]),text2num(params["z"]))
+ if(!istype(tmpTarget, /turf))
+ atom_say("No valid targets available.")
+ return
+ target = tmpTarget
+ if(regime == REGIME_TELEPORT)
+ teleport_helper()
+ if(regime == REGIME_GATE)
+ gate_helper()
+ if("calibrate")
+ if(!target)
+ atom_say("Error: No target set to calibrate to.")
+ return
+ if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3)
+ atom_say("Hub is already calibrated.")
+ return
- SSnanoui.update_uis(src)
+ atom_say("Processing hub calibration to target...")
+ calibrating = TRUE
+ addtimer(CALLBACK(src, .proc/calibrateCallback), 50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration
+
+/**
+* Resets the connected powerstation to initial values. Helper function of tgui_act
+*/
+/obj/machinery/computer/teleporter/proc/resetPowerstation()
+ power_station.engaged = FALSE
+ power_station.teleporter_hub.calibrated = FALSE
+ power_station.teleporter_hub.update_icon()
+
+/**
+* Calibrates the hub. Helper function of tgui_act
+*/
+/obj/machinery/computer/teleporter/proc/calibrateCallback()
+ calibrating = FALSE
+ if(check_hub_connection())
+ power_station.teleporter_hub.calibrated = TRUE
+ atom_say("Calibration complete.")
+ else
+ atom_say("Error: Unable to detect hub.")
/obj/machinery/computer/teleporter/proc/check_hub_connection()
if(!power_station)
return
if(!power_station.teleporter_hub)
return
- return 1
-
-/obj/machinery/computer/teleporter/proc/reset_regime()
- target = null
- if(regime_set == "Teleporter")
- regime_set = "Gate"
- else
- regime_set = "Teleporter"
+ return TRUE
+/**
+* Helper function of tgui_act
+*
+* Triggered when ejecting a gps device. Sets the gps to the ground and resets the console
+*/
/obj/machinery/computer/teleporter/proc/eject()
if(locked)
locked.loc = loc
locked = null
+ regime = REGIME_TELEPORT
+ target_list = targets_teleport()
-/obj/machinery/computer/teleporter/proc/set_target(mob/user)
- area_bypass = FALSE
- if(regime_set == "Teleporter")
- var/list/L = list()
- var/list/areaindex = list()
+/**
+* Creates a list of viable targets for the teleport. Helper function of tgui_data
+*/
+/obj/machinery/computer/teleporter/proc/targets_teleport()
+ var/list/L = list()
+ var/list/areaindex = list()
- for(var/obj/item/radio/beacon/R in GLOB.beacons)
- var/turf/T = get_turf(R)
- if(!T)
- continue
- if(!is_teleport_allowed(T.z) && !R.cc_beacon)
- continue
- if(R.syndicate == 1 && emagged == 0)
- continue
- var/tmpname = T.loc.name
+ for(var/obj/item/radio/beacon/R in GLOB.beacons)
+ var/turf/T = get_turf(R)
+ if(!T)
+ continue
+ if(!is_teleport_allowed(T.z) && !R.cc_beacon)
+ continue
+ if(R.syndicate && !emagged)
+ continue
+ var/tmpname = T.loc.name
+ if(areaindex[tmpname])
+ tmpname = "[tmpname] ([++areaindex[tmpname]])"
+ else
+ areaindex[tmpname] = 1
+ L[tmpname] = list(
+ "name" = tmpname,
+ "x" = T.x,
+ "y" = T.y,
+ "z" = T.z)
+
+ for(var/obj/item/implant/tracking/I in GLOB.tracked_implants)
+ if(!I.implanted || !ismob(I.loc))
+ continue
+ else
+ var/mob/M = I.loc
+ if(M.stat == DEAD)
+ if(M.timeofdeath + 6000 < world.time)
+ continue
+ var/turf/T = get_turf(M)
+ if(!T) continue
+ if(!is_teleport_allowed(T.z)) continue
+ var/tmpname = M.real_name
if(areaindex[tmpname])
tmpname = "[tmpname] ([++areaindex[tmpname]])"
else
areaindex[tmpname] = 1
- L[tmpname] = R
+ L[tmpname] = list(
+ "name" = tmpname,
+ "x" = T.x,
+ "y" = T.y,
+ "z" = T.z)
+ return L
- for(var/obj/item/implant/tracking/I in GLOB.tracked_implants)
- if(!I.implanted || !ismob(I.loc))
- continue
- else
- var/mob/M = I.loc
- if(M.stat == DEAD)
- if(M.timeofdeath + 6000 < world.time)
- continue
- var/turf/T = get_turf(M)
- if(!T) continue
- if(!is_teleport_allowed(T.z)) continue
- var/tmpname = M.real_name
- if(areaindex[tmpname])
- tmpname = "[tmpname] ([++areaindex[tmpname]])"
- else
- areaindex[tmpname] = 1
- L[tmpname] = I
+/**
+* Creates a list of viable targets for the gate. Helper function of tgui_data
+*/
+/obj/machinery/computer/teleporter/proc/targets_gate(mob/users)
+ var/list/L = list()
+ var/list/areaindex = list()
+ var/list/S = power_station.linked_stations
+ if(!S.len)
+ return L
+ for(var/obj/machinery/teleport/station/R in S)
+ var/turf/T = get_turf(R)
+ if(!T || !R.teleporter_hub || !R.teleporter_console)
+ continue
+ if(!is_teleport_allowed(T.z))
+ continue
+ var/tmpname = T.loc.name
+ if(areaindex[tmpname])
+ tmpname = "[tmpname] ([++areaindex[tmpname]])"
+ else
+ areaindex[tmpname] = 1
+ L[tmpname] = list(
+ "name" = tmpname,
+ "x" = T.x,
+ "y" = T.y,
+ "z" = T.z)
+ return L
- var/desc = input("Please select a location to lock in.", "Locking Computer") in L
- target = L[desc]
- if(istype(target, /obj/item/radio/beacon))
- var/obj/item/radio/beacon/B = target
+/**
+* Helper function of tgui_act.
+*
+* Called after selecting a target for the gate in the UI. Sets area_bypass and cc_beacon.
+*/
+/obj/machinery/computer/teleporter/proc/teleport_helper()
+ area_bypass = FALSE
+ for(var/item in target.contents)
+ if(istype(item, /obj/item/radio/beacon))
+ var/obj/item/radio/beacon/B = item
if(B.area_bypass)
area_bypass = TRUE
cc_beacon = B.cc_beacon
- else
- var/list/L = list()
- var/list/areaindex = list()
- var/list/S = power_station.linked_stations
- if(!S.len)
- to_chat(user, "No connected stations located.")
- return
- for(var/obj/machinery/teleport/station/R in S)
- var/turf/T = get_turf(R)
- if(!T || !R.teleporter_hub || !R.teleporter_console)
- continue
- if(!is_teleport_allowed(T.z))
- continue
- var/tmpname = T.loc.name
- if(areaindex[tmpname])
- tmpname = "[tmpname] ([++areaindex[tmpname]])"
- else
- areaindex[tmpname] = 1
- L[tmpname] = R
- var/desc = input("Please select a station to lock in.", "Locking Computer") in L
- target = L[desc]
- if(target)
- var/obj/machinery/teleport/station/trg = target
- trg.linked_stations |= power_station
- trg.stat &= ~NOPOWER
- if(trg.teleporter_hub)
- trg.teleporter_hub.stat &= ~NOPOWER
- trg.teleporter_hub.update_icon()
- if(trg.teleporter_console)
- trg.teleporter_console.stat &= ~NOPOWER
- trg.teleporter_console.update_icon()
- return
+
+/**
+* Helper function of tgui_act.
+*
+* Called after selecting a target for the teleporter in the UI.
+*/
+/obj/machinery/computer/teleporter/proc/gate_helper()
+ area_bypass = FALSE
+ var/obj/machinery/teleport/station/trg = target
+ trg.linked_stations |= power_station
+ trg.stat &= ~NOPOWER
+ if(trg.teleporter_hub)
+ trg.teleporter_hub.stat &= ~NOPOWER
+ trg.teleporter_hub.update_icon()
+ if(trg.teleporter_console)
+ trg.teleporter_console.stat &= ~NOPOWER
+ trg.teleporter_console.update_icon()
/proc/find_loc(obj/R as obj)
if(!R) return null
@@ -263,20 +308,20 @@
/obj/machinery/teleport
name = "teleport"
icon = 'icons/obj/stationobjs.dmi'
- density = 1
- anchored = 1.0
+ density = TRUE
+ anchored = TRUE
/obj/machinery/teleport/hub
name = "teleporter hub"
desc = "It's the hub of a teleporting machine."
icon_state = "tele0"
- var/accurate = 0
+ var/accurate = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 2000
var/obj/machinery/teleport/station/power_station
var/calibrated //Calibration prevents mutation
- var/admin_usage = 0 // if 1, works on z2. If 0, doesn't. Used for admin room teleport.
+ var/admin_usage = FALSE // if 1, works on z2. If 0, doesn't. Used for admin room teleport.
/obj/machinery/teleport/hub/New()
..()
@@ -317,6 +362,7 @@
for(dir in list(NORTH,EAST,SOUTH,WEST))
power_station = locate(/obj/machinery/teleport/station, get_step(src, dir))
if(power_station)
+ power_station.link_console_and_hub()
break
return power_station
@@ -324,27 +370,12 @@
if(!is_teleport_allowed(z) && !admin_usage)
to_chat(M, "You can't use this here.")
return
- if(power_station && power_station.engaged && !panel_open)
- //--FalseIncarnate
- //Prevents AI cores from using the teleporter, prints out failure messages for clarity
- if(istype(M, /mob/living/silicon/ai) || istype(M, /obj/structure/AIcore))
- visible_message("The teleporter rejects the AI unit.")
- if(istype(M, /mob/living/silicon/ai))
- var/mob/living/silicon/ai/T = M
- var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!",
- "You cannot interface with this technology and get rejected!",
- "External firewalls prevent you from utilizing this machine!",
- "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!")
- to_chat(T, "[pick(TPError)]")
- return
- else
- if(!teleport(M) && isliving(M)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub
- visible_message("[src] emits a loud buzz, as its teleport portal flickers and fails!")
- playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
- power_station.toggle() // turn off the portal.
-
- use_power(5000)
- //--FalseIncarnate
+ if(power_station && power_station.engaged && !panel_open && !blockAI(M) && !istype(M, /obj/spacepod))
+ if(!teleport(M) && isliving(M)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub
+ visible_message("[src] emits a loud buzz, as its teleport portal flickers and fails!")
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ power_station.toggle() // turn off the portal.
+ use_power(5000)
return
/obj/machinery/teleport/hub/attackby(obj/item/I, mob/user, params)
@@ -375,7 +406,7 @@
. = do_teleport(M, locate(rand((2*TRANSITIONEDGE), world.maxx - (2*TRANSITIONEDGE)), rand((2*TRANSITIONEDGE), world.maxy - (2*TRANSITIONEDGE)), 3), 2, bypass_area_flag = com.area_bypass)
else
. = do_teleport(M, com.target, bypass_area_flag = com.area_bypass)
- calibrated = 0
+ calibrated = FALSE
/obj/machinery/teleport/hub/update_icon()
if(panel_open)
@@ -389,7 +420,7 @@
name = "permanent teleporter"
desc = "A teleporter with the target pre-set on the circuit board."
icon_state = "tele0"
- var/recalibrating = 0
+ var/recalibrating = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 2000
@@ -406,37 +437,42 @@
tele_delay = max(A, 0)
update_icon()
-/obj/machinery/teleport/perma/Bumped(M as mob|obj)
+/**
+ Internal helper function
+
+ Prevents AI from using the teleporter, prints out failure messages for clarity
+*/
+/obj/machinery/teleport/proc/blockAI(atom/A)
+ if(istype(A, /mob/living/silicon/ai) || istype(A, /obj/structure/AIcore))
+ visible_message("The teleporter rejects the AI unit.")
+ if(istype(A, /mob/living/silicon/ai))
+ var/mob/living/silicon/ai/T = A
+ var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!",
+ "You cannot interface with this technology and get rejected!",
+ "External firewalls prevent you from utilizing this machine!",
+ "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!")
+ to_chat(T, "[pick(TPError)]")
+ return TRUE
+ return FALSE
+
+/obj/machinery/teleport/perma/Bumped(atom/A)
if(stat & (BROKEN|NOPOWER))
return
if(!is_teleport_allowed(z))
- to_chat(M, "You can't use this here.")
+ to_chat(A, "You can't use this here.")
return
- if(target && !recalibrating && !panel_open)
- //--FalseIncarnate
- //Prevents AI cores from using the teleporter, prints out failure messages for clarity
- if(istype(M, /mob/living/silicon/ai) || istype(M, /obj/structure/AIcore))
- visible_message("The teleporter rejects the AI unit.")
- if(istype(M, /mob/living/silicon/ai))
- var/mob/living/silicon/ai/T = M
- var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!",
- "You cannot interface with this technology and get rejected!",
- "External firewalls prevent you from utilizing this machine!",
- "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!")
- to_chat(T, "[pick(TPError)]")
- return
- else
- do_teleport(M, target)
- use_power(5000)
- if(tele_delay)
- recalibrating = 1
- update_icon()
- spawn(tele_delay)
- recalibrating = 0
- update_icon()
- //--FalseIncarnate
- return
+ if(target && !recalibrating && !panel_open && !blockAI(A))
+ do_teleport(A, target)
+ use_power(5000)
+ if(tele_delay)
+ recalibrating = TRUE
+ update_icon()
+ addtimer(CALLBACK(src, .proc/BumpedCallback), tele_delay)
+
+/obj/machinery/teleport/perma/proc/BumpedCallback()
+ recalibrating = FALSE
+ update_icon()
/obj/machinery/teleport/perma/power_change()
..()
@@ -467,7 +503,7 @@
name = "station"
desc = "The power control station for a bluespace teleporter."
icon_state = "controller"
- var/engaged = 0
+ var/engaged = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 2000
@@ -568,7 +604,7 @@
/obj/machinery/teleport/station/attack_ai()
- src.attack_hand()
+ attack_hand()
/obj/machinery/teleport/station/attack_hand(mob/user)
if(!panel_open)
@@ -583,12 +619,12 @@
to_chat(user, "Close the hub's maintenance panel first.")
return
if(teleporter_console.target)
- src.engaged = !src.engaged
+ engaged = !engaged
use_power(5000)
visible_message("Teleporter [engaged ? "" : "dis"]engaged!")
else
visible_message("No target detected.")
- src.engaged = 0
+ engaged = FALSE
teleporter_hub.update_icon()
if(istype(user))
add_fingerprint(user)
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index cc9fc3e4666..18abeba7400 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -6,17 +6,45 @@
layer = MOB_LAYER+1 // Overhead
anchored = 1
density = 1
- var/transform_dead = 0
- var/transform_standing = 0
- var/cooldown_duration = 600 // 1 minute
- var/cooldown = 0
- var/robot_cell_charge = 5000
+ /// TRUE if the factory can transform dead mobs.
+ var/transform_dead = TRUE
+ /// TRUE if the mob can be standing and still be transformed.
+ var/transform_standing = TRUE
+ /// Cooldown between each transformation, in deciseconds.
+ var/cooldown_duration = 1 MINUTES
+ /// If the factory is currently on cooldown from its last transformation.
+ var/is_on_cooldown = FALSE
+ /// The type of cell that newly created borgs get.
+ var/robot_cell_type = /obj/item/stock_parts/cell/high/plus
+ /// The direction that mobs must moving in to get transformed.
var/acceptdir = EAST
+ /// The AI who placed this factory.
+ var/mob/living/silicon/ai/masterAI
-/obj/machinery/transformer/New()
- // On us
- ..()
- new /obj/machinery/conveyor/auto(loc, WEST)
+/obj/machinery/transformer/Initialize(mapload, mob/living/silicon/ai/_ai = null)
+ . = ..()
+ if(_ai)
+ masterAI = _ai
+ initialize_belts()
+
+/// Used to create all of the belts the transformer will be using. All belts should be pushing `WEST`.
+/obj/machinery/transformer/proc/initialize_belts()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return
+
+ // Belt under the factory.
+ new /obj/machinery/conveyor/auto(T, WEST)
+
+ // Get the turf 1 tile to the EAST.
+ var/turf/east = locate(T.x + 1, T.y, T.z)
+ if(istype(east, /turf/simulated/floor))
+ new /obj/machinery/conveyor/auto(east, WEST)
+
+ // Get the turf 1 tile to the WEST.
+ var/turf/west = locate(T.x - 1, T.y, T.z)
+ if(istype(west, /turf/simulated/floor))
+ new /obj/machinery/conveyor/auto(west, WEST)
/obj/machinery/transformer/power_change()
..()
@@ -24,7 +52,7 @@
/obj/machinery/transformer/update_icon()
..()
- if(stat & (BROKEN|NOPOWER) || cooldown == 1)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
icon_state = "separator-AO0"
else
icon_state = initial(icon_state)
@@ -35,120 +63,76 @@
C.setDir(newdir)
acceptdir = turn(newdir, 180)
-/obj/machinery/transformer/Bumped(var/atom/movable/AM)
+/// Resets `is_on_cooldown` to `FALSE` and updates our icon. Used in a callback after the transformer does a transformation.
+/obj/machinery/transformer/proc/reset_cooldown()
+ is_on_cooldown = FALSE
+ update_icon()
- if(cooldown == 1)
+/obj/machinery/transformer/Bumped(atom/movable/AM)
+ // They have to be human to be transformed.
+ if(is_on_cooldown || !ishuman(AM))
return
- // Crossed didn't like people lying down.
- if(ishuman(AM))
- // Only humans can enter from the west side, while lying down.
- var/move_dir = get_dir(loc, AM.loc)
- var/mob/living/carbon/human/H = AM
- if((transform_standing || H.lying) && move_dir == acceptdir)// || move_dir == WEST)
- AM.loc = src.loc
- do_transform(AM)
+ var/mob/living/carbon/human/H = AM
+ var/move_dir = get_dir(loc, H.loc)
-/obj/machinery/transformer/proc/do_transform(var/mob/living/carbon/human/H)
- if(stat & (BROKEN|NOPOWER))
- return
- if(cooldown == 1)
+ if((transform_standing || H.lying) && move_dir == acceptdir)
+ H.forceMove(drop_location())
+ do_transform(H)
+
+/// Transforms a human mob into a cyborg, connects them to the malf AI which placed the factory.
+/obj/machinery/transformer/proc/do_transform(mob/living/carbon/human/H)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
return
if(!transform_dead && H.stat == DEAD)
- playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
return
- playsound(src.loc, 'sound/items/welder.ogg', 50, 1)
- H.emote("scream") // It is painful
- H.adjustBruteLoss(max(0, 80 - H.getBruteLoss())) // Hurt the human, don't try to kill them though.
-
- // Sleep for a couple of ticks to allow the human to see the pain
- sleep(5)
-
+ playsound(loc, 'sound/items/welder.ogg', 50, 1)
use_power(5000) // Use a lot of power.
- var/mob/living/silicon/robot/R = H.Robotize(1) // Delete the items or they'll all pile up in a single tile and lag
-
- R.cell.maxcharge = robot_cell_charge
- R.cell.charge = robot_cell_charge
-
- // So he can't jump out the gate right away.
- R.lockcharge = !R.lockcharge
- spawn(50)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
- sleep(30)
- if(R)
- R.lockcharge = !R.lockcharge
- R.notify_ai(1)
// Activate the cooldown
- cooldown = 1
+ is_on_cooldown = TRUE
update_icon()
- spawn(cooldown_duration)
- cooldown = 0
- update_icon()
-
-/obj/machinery/transformer/conveyor/New()
- ..()
- var/turf/T = loc
- if(T)
- // Spawn Conveyour Belts
-
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, WEST)
-
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, WEST)
+ addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration)
+ addtimer(CALLBACK(null, .proc/playsound, loc, 'sound/machines/ping.ogg', 50, 0), 3 SECONDS)
+ H.emote("scream")
+ if(!masterAI) // If the factory was placed via admin spawning or other means, it wont have an owner_AI.
+ H.Robotize(robot_cell_type)
+ return
+ var/mob/living/silicon/robot/R = H.Robotize(robot_cell_type, FALSE, masterAI)
+ if(R.mind && !R.client && !R.grab_ghost()) // Make sure this is an actual player first and not just a humanized monkey or something.
+ message_admins("[key_name_admin(R)] was just transformed by a borg factory, but they were SSD. Polling ghosts for a replacement.")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a malfunctioning cyborg?", ROLE_TRAITOR, poll_time = 15 SECONDS)
+ if(!length(candidates))
+ return
+ var/mob/dead/observer/O = pick(candidates)
+ R.key= O.key
/obj/machinery/transformer/mime
name = "Mimetech Greyscaler"
desc = "Turns anything placed inside black and white."
-
-/obj/machinery/transformer/mime/conveyor/New()
- ..()
- var/turf/T = loc
- if(T)
- // Spawn Conveyour Belts
-
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, WEST)
-
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, WEST)
-
-/obj/machinery/transformer/mime/Bumped(var/atom/movable/AM)
-
- if(cooldown == 1)
+/obj/machinery/transformer/mime/Bumped(atom/movable/AM)
+ if(is_on_cooldown)
return
// Crossed didn't like people lying down.
- if(isatom(AM))
- AM.loc = src.loc
+ if(istype(AM))
+ AM.forceMove(drop_location())
do_transform_mime(AM)
else
to_chat(AM, "Only items can be greyscaled.")
return
-/obj/machinery/transformer/proc/do_transform_mime(var/obj/item/I)
- if(stat & (BROKEN|NOPOWER))
- return
- if(cooldown == 1)
+/obj/machinery/transformer/proc/do_transform_mime(obj/item/I)
+ if(is_on_cooldown || stat & (BROKEN|NOPOWER))
return
- playsound(src.loc, 'sound/items/welder.ogg', 50, 1)
- // Sleep for a couple of ticks to allow the human to see the pain
- sleep(5)
+ playsound(loc, 'sound/items/welder.ogg', 50, 1)
use_power(5000) // Use a lot of power.
var/icon/newicon = new(I.icon, I.icon_state)
@@ -156,42 +140,27 @@
I.icon = newicon
// Activate the cooldown
- cooldown = 1
+ is_on_cooldown = TRUE
update_icon()
- spawn(cooldown_duration)
- cooldown = 0
- update_icon()
+ addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration)
/obj/machinery/transformer/xray
name = "Automatic X-Ray 5000"
desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'backpack go in, backpack come out', 'human go in, irradiated human come out'."
+ acceptdir = WEST
-/obj/machinery/transformer/xray/Initialize(mapload)
- . = ..()
- // On us
- new /obj/machinery/conveyor/auto(loc, EAST)
-
-/obj/machinery/transformer/xray/conveyor/New()
- ..()
- var/turf/T = loc
+/obj/machinery/transformer/xray/initialize_belts()
+ var/turf/T = get_turf(src)
if(T)
- // Spawn Conveyour Belts
+ // This handles the belt under the transformer and 1 tile to the left and right.
+ . = ..()
- //East
- var/turf/east = locate(T.x + 1, T.y, T.z)
- if(istype(east, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(east, EAST)
- //East2
+ // Get the turf 2 tiles to the EAST.
var/turf/east2 = locate(T.x + 2, T.y, T.z)
if(istype(east2, /turf/simulated/floor))
new /obj/machinery/conveyor/auto(east2, EAST)
- // West
- var/turf/west = locate(T.x - 1, T.y, T.z)
- if(istype(west, /turf/simulated/floor))
- new /obj/machinery/conveyor/auto(west, EAST)
-
- // West2
+ // Get the turf 2 tiles to the WEST.
var/turf/west2 = locate(T.x - 2, T.y, T.z)
if(istype(west2, /turf/simulated/floor))
new /obj/machinery/conveyor/auto(west2, EAST)
@@ -207,30 +176,30 @@
else
icon_state = initial(icon_state)
-/obj/machinery/transformer/xray/Bumped(var/atom/movable/AM)
-
- if(cooldown == 1)
+/obj/machinery/transformer/xray/Bumped(atom/movable/AM)
+ if(is_on_cooldown)
return
// Crossed didn't like people lying down.
if(ishuman(AM))
// Only humans can enter from the west side, while lying down.
- var/move_dir = get_dir(loc, AM.loc)
var/mob/living/carbon/human/H = AM
- if(H.lying && move_dir == WEST)// || move_dir == WEST)
- AM.loc = src.loc
- irradiate(AM)
+ var/move_dir = get_dir(loc, H.loc)
- else if(isatom(AM))
- AM.loc = src.loc
+ if(H.lying && move_dir == acceptdir)
+ H.forceMove(drop_location())
+ irradiate(H)
+
+ else if(istype(AM))
+ AM.forceMove(drop_location())
scan(AM)
-/obj/machinery/transformer/xray/proc/irradiate(var/mob/living/carbon/human/H)
+/obj/machinery/transformer/xray/proc/irradiate(mob/living/carbon/human/H)
if(stat & (BROKEN|NOPOWER))
return
flick("separator-AO0",src)
- playsound(src.loc, 'sound/effects/alert.ogg', 50, 0)
+ playsound(loc, 'sound/effects/alert.ogg', 50, 0)
sleep(5)
H.apply_effect((rand(150,200)),IRRADIATE,0)
if(prob(5))
@@ -242,15 +211,15 @@
domutcheck(H,null,1)
-/obj/machinery/transformer/xray/proc/scan(var/obj/item/I)
+/obj/machinery/transformer/xray/proc/scan(obj/item/I)
if(scan_rec(I))
- playsound(src.loc, 'sound/effects/alert.ogg', 50, 0)
+ playsound(loc, 'sound/effects/alert.ogg', 50, 0)
flick("separator-AO0",src)
else
- playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
+ playsound(loc, 'sound/machines/ping.ogg', 50, 0)
sleep(30)
-/obj/machinery/transformer/xray/proc/scan_rec(var/obj/item/I)
+/obj/machinery/transformer/xray/proc/scan_rec(obj/item/I)
if(istype(I, /obj/item/gun))
return TRUE
if(istype(I, /obj/item/transfer_valve))
diff --git a/code/game/machinery/turntable.dm b/code/game/machinery/turntable.dm
deleted file mode 100644
index 70d7adff2be..00000000000
--- a/code/game/machinery/turntable.dm
+++ /dev/null
@@ -1,280 +0,0 @@
-/sound/turntable/test
- file = 'sound/turntable/testloop1.ogg'
- falloff = 2
- repeat = 1
-
-/mob/var/music = 0
-
-/obj/machinery/party/turntable
- name = "turntable"
- desc = "A turntable used for parties and shit."
- icon = 'icons/effects/lasers2.dmi'
- icon_state = "turntable"
- var/playing = 0
- anchored = 1
-
-/obj/machinery/party/mixer
- name = "mixer"
- desc = "A mixing board for mixing music"
- icon = 'icons/effects/lasers2.dmi'
- icon_state = "mixer"
- anchored = 1
-
-
-/obj/machinery/party/turntable/New()
- ..()
- sleep(2)
- new /sound/turntable/test(src)
- return
-
-/obj/machinery/party/turntable/attack_hand(mob/user as mob)
-
- var/t = "Turntable Interface
"
- //t += "On "
- t += "Off
"
- t += "One "
- t += "TestLoop2 "
- t += "TestLoop3 "
-
- user << browse(t, "window=turntable;size=420x700")
-
-
-/obj/machinery/party/turntable/Topic(href, href_list)
- ..()
- if( href_list["on1"] )
- if(src.playing == 0)
-// to_chat(world, "Should be working...")
- var/sound/S = sound('sound/turntable/testloop1.ogg')
- S.repeat = 1
- S.channel = 10
- S.falloff = 2
- S.wait = 1
- S.environment = 0
- //for(var/mob/M in world)
- // if(M.loc.loc == src.loc.loc && M.music == 0)
-// to_chat(world, "Found the song...")
-// M << S
- // M.music = 1
- var/area/A = src.loc.loc
-
- for(var/obj/machinery/party/lasermachine/L in A)
- L.turnon()
- playing = 1
- while(playing == 1)
- for(var/mob/M in world)
- if((M.loc.loc in A) && M.music == 0)
-// to_chat(world, "Found the song...")
- M << S
- M.music = 1
- else if(!(M.loc.loc in A) && M.music == 1)
- var/sound/Soff = sound(null)
- Soff.channel = 10
- M << Soff
- M.music = 0
- sleep(10)
- return
- if( href_list["on2"] )
- if(src.playing == 0)
-// to_chat(world, "Should be working...")
- var/sound/S = sound('sound/turntable/testloop2.ogg')
- S.repeat = 1
- S.channel = 10
- S.falloff = 2
- S.wait = 1
- S.environment = 0
- //for(var/mob/M in world)
- // if(M.loc.loc == src.loc.loc && M.music == 0)
-// to_chat(world, "Found the song...")
-// M << S
- // M.music = 1
- var/area/A = src.loc.loc
- for(var/obj/machinery/party/lasermachine/L in A)
- L.turnon()
- playing = 1
- while(playing == 1)
- for(var/mob/M in world)
- if(M.loc.loc == src.loc.loc && M.music == 0)
-// to_chat(world, "Found the song...")
- M << S
- M.music = 1
- else if(M.loc.loc != src.loc.loc && M.music == 1)
- var/sound/Soff = sound(null)
- Soff.channel = 10
- M << Soff
- M.music = 0
- sleep(10)
- return
- if( href_list["on3"] )
- if(src.playing == 0)
-// to_chat(world, "Should be working...")
- var/sound/S = sound('sound/turntable/testloop3.ogg')
- S.repeat = 1
- S.channel = 10
- S.falloff = 2
- S.wait = 1
- S.environment = 0
- //for(var/mob/M in world)
- // if(M.loc.loc == src.loc.loc && M.music == 0)
-// to_chat(world, "Found the song...")
-// M << S
- // M.music = 1
- var/area/A = src.loc.loc
- for(var/obj/machinery/party/lasermachine/L in A)
- L.turnon()
- playing = 1
- while(playing == 1)
- for(var/mob/M in world)
- if(M.loc.loc == src.loc.loc && M.music == 0)
-// to_chat(world, "Found the song...")
- M << S
- M.music = 1
- else if(M.loc.loc != src.loc.loc && M.music == 1)
- var/sound/Soff = sound(null)
- Soff.channel = 10
- M << Soff
- M.music = 0
- sleep(10)
- return
-
-
- if( href_list["off"] )
- if(src.playing == 1)
- var/sound/S = sound(null)
- S.channel = 10
- S.wait = 1
- for(var/mob/M in world)
- M << S
- M.music = 0
- playing = 0
- var/area/A = src.loc.loc
- for(var/obj/machinery/party/lasermachine/L in A)
- L.turnoff()
-
-
-
-/obj/machinery/party/lasermachine
- name = "laser machine"
- desc = "A laser machine that shoots lasers."
- icon = 'icons/effects/lasers2.dmi'
- icon_state = "lasermachine"
- anchored = 1
- var/mirrored = 0
-
-/obj/effect/turntable_laser
- name = "laser"
- desc = "A laser..."
- icon = 'icons/effects/lasers2.dmi'
- icon_state = "laserred1"
- anchored = 1
- layer = 4
-
-/obj/item/lasermachine/New()
- ..()
-
-/obj/machinery/party/lasermachine/proc/turnon()
- var/wall = 0
- var/cycle = 1
- var/area/A = get_area(src)
- var/X = 1
- var/Y = 0
- if(mirrored == 0)
- while(wall == 0)
- if(cycle == 1)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y+Y
- F.z = src.z
- F.icon_state = "laserred1"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- X++
- if(cycle == 2)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y+Y
- F.z = src.z
- F.icon_state = "laserred2"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- Y++
- if(cycle == 3)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y+Y
- F.z = src.z
- F.icon_state = "laserred3"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- X++
- if(mirrored == 1)
- while(wall == 0)
- if(cycle == 1)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y-Y
- F.z = src.z
- F.icon_state = "laserred1m"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- Y++
- if(cycle == 2)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y-Y
- F.z = src.z
- F.icon_state = "laserred2m"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- X++
- if(cycle == 3)
- var/obj/effect/turntable_laser/F = new(src)
- F.x = src.x+X
- F.y = src.y-Y
- F.z = src.z
- F.icon_state = "laserred3m"
- var/area/AA = get_area(F)
- var/turf/T = get_turf(F)
- if(T.density == 1 || AA.name != A.name)
- qdel(F)
- return
- cycle++
- if(cycle > 3)
- cycle = 1
- X++
-
-
-
-/obj/machinery/party/lasermachine/proc/turnoff()
- var/area/A = src.loc.loc
- for(var/obj/effect/turntable_laser/F in A)
- qdel(F)
diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm
index 9dda3b193bb..71cb58c6478 100644
--- a/code/game/machinery/turret_control.dm
+++ b/code/game/machinery/turret_control.dm
@@ -11,51 +11,56 @@
desc = "Used to control a room's automated defenses."
icon = 'icons/obj/machines/turret_control.dmi'
icon_state = "control_standby"
- anchored = 1
- density = 0
+ anchored = TRUE
+ density = FALSE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- var/enabled = 0
- var/lethal = 0
- var/locked = 1
+ var/enabled = FALSE
+ var/lethal = FALSE
+ var/lethal_is_configurable = TRUE
+ var/locked = TRUE
var/area/control_area //can be area name, path or nothing.
- var/check_arrest = 1 //checks if the perp is set to arrest
- var/check_records = 1 //checks if a security record exists at all
- var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
- var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
- var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
- var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
- var/ailock = 0 //Silicons cannot use this
+ var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI
+ var/check_arrest = TRUE //checks if the perp is set to arrest
+ var/check_records = TRUE //checks if a security record exists at all
+ var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have
+ var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements
+ var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
+ var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
+ var/check_borgs = FALSE //if TRUE, target all cyborgs.
+ var/ailock = FALSE //Silicons cannot use this
- var/syndicate = 0
+ var/syndicate = FALSE
var/faction = "" // Turret controls can only access turrets that are in the same faction
req_access = list(ACCESS_AI_UPLOAD)
/obj/machinery/turretid/stun
- enabled = 1
+ enabled = TRUE
icon_state = "control_stun"
/obj/machinery/turretid/lethal
- enabled = 1
- lethal = 1
+ enabled = TRUE
+ lethal = TRUE
icon_state = "control_kill"
/obj/machinery/turretid/syndicate
- enabled = 1
- lethal = 1
+ enabled = TRUE
+ lethal = TRUE
+ lethal_is_configurable = FALSE
+ targetting_is_configurable = FALSE
icon_state = "control_kill"
- lethal = 1
- check_arrest = 0
- check_records = 0
- check_weapons = 0
- check_access = 0
- check_anomalies = 1
- check_synth = 1
- ailock = 1
+ check_arrest = FALSE
+ check_records = FALSE
+ check_weapons = FALSE
+ check_access = FALSE
+ check_anomalies = TRUE
+ check_synth = TRUE
+ check_borgs = FALSE
+ ailock = TRUE
- syndicate = 1
+ syndicate = TRUE
faction = "syndicate"
req_access = list(ACCESS_SYNDICATE_LEADER)
@@ -87,21 +92,23 @@
return
/obj/machinery/turretid/proc/isLocked(mob/user)
- if(ailock && (isrobot(user) || isAI(user)))
- to_chat(user, "There seems to be a firewall preventing you from accessing this device.")
- return 1
+ if(isrobot(user) || isAI(user))
+ if(ailock)
+ to_chat(user, "There seems to be a firewall preventing you from accessing this device.")
+ return TRUE
+ else
+ return FALSE
- if(locked && !(isrobot(user) || isAI(user) || isobserver(user)))
- to_chat(user, "Access denied.")
- return 1
+ if(isobserver(user))
+ if(user.can_admin_interact())
+ return FALSE
+ else
+ return TRUE
- return 0
+ if(locked)
+ return TRUE
-/obj/machinery/turretid/CanUseTopic(mob/user)
- if(isLocked(user))
- return STATUS_CLOSE
-
- return ..()
+ return FALSE
/obj/machinery/turretid/attackby(obj/item/W, mob/user)
if(stat & BROKEN)
@@ -120,89 +127,82 @@
/obj/machinery/turretid/emag_act(user as mob)
if(!emagged)
to_chat(user, "You short out the turret controls' access analysis module.")
- emagged = 1
- locked = 0
- ailock = 0
+ emagged = TRUE
+ locked = FALSE
+ ailock = FALSE
return
/obj/machinery/turretid/attack_ai(mob/user as mob)
- if(isLocked(user))
- return
-
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/turretid/attack_ghost(mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
/obj/machinery/turretid/attack_hand(mob/user as mob)
- if(isLocked(user))
- return
+ tgui_interact(user)
- ui_interact(user)
-
-/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/turretid/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 350)
+ ui = new(user, src, ui_key, "PortableTurret", name, 500, 400)
ui.open()
- ui.set_auto_update(1)
-
-/obj/machinery/turretid/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
- data["access"] = !isLocked(user)
- data["locked"] = locked
- data["enabled"] = enabled
- data["lethal_control"] = !syndicate ? 1 : 0
- data["lethal"] = lethal
-
- if(data["access"] && !syndicate)
- var/settings[0]
- settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
- settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
- settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
- settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
- settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
- settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
- data["settings"] = settings
+/obj/machinery/turretid/tgui_data(mob/user)
+ var/list/data = list(
+ "locked" = isLocked(user), // does the current user have access?
+ "on" = enabled,
+ "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up
+ "lethal" = lethal,
+ "lethal_is_configurable" = lethal_is_configurable,
+ "check_weapons" = check_weapons,
+ "neutralize_noaccess" = check_access,
+ "one_access" = FALSE,
+ "selectedAccess" = list(),
+ "access_is_configurable" = FALSE,
+ "neutralize_norecord" = check_records,
+ "neutralize_criminals" = check_arrest,
+ "neutralize_all" = check_synth,
+ "neutralize_unidentified" = check_anomalies,
+ "neutralize_cyborgs" = check_borgs
+ )
return data
-/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0)
- if(..())
- return 1
-
+/obj/machinery/turretid/tgui_act(action, params)
+ if (..())
+ return
if(isLocked(usr))
- return 1
-
- if(href_list["command"] && href_list["value"])
- var/value = text2num(href_list["value"])
- if(href_list["command"] == "enable")
- enabled = value
- else if(syndicate)
- return 1
- else if(href_list["command"] == "lethal")
- lethal = value
- else if(href_list["command"] == "check_synth")
- check_synth = value
- else if(href_list["command"] == "check_weapons")
- check_weapons = value
- else if(href_list["command"] == "check_records")
- check_records = value
- else if(href_list["command"] == "check_arrest")
- check_arrest = value
- else if(href_list["command"] == "check_access")
- check_access = value
- else if(href_list["command"] == "check_anomalies")
- check_anomalies = value
-
- updateTurrets()
- return 1
+ return
+ . = TRUE
+ switch(action)
+ if("power")
+ enabled = !enabled
+ if("lethal")
+ if(lethal_is_configurable)
+ lethal = !lethal
+ if(targetting_is_configurable)
+ switch(action)
+ if("authweapon")
+ check_weapons = !check_weapons
+ if("authaccess")
+ check_access = !check_access
+ if("authnorecord")
+ check_records = !check_records
+ if("autharrest")
+ check_arrest = !check_arrest
+ if("authxeno")
+ check_anomalies = !check_anomalies
+ if("authsynth")
+ check_synth = !check_synth
+ if("authborgs")
+ check_borgs = !check_borgs
+ updateTurrets()
/obj/machinery/turretid/proc/updateTurrets()
var/datum/turret_checks/TC = new
TC.enabled = enabled
TC.lethal = lethal
TC.check_synth = check_synth
+ TC.check_borgs = check_borgs
TC.check_access = check_access
TC.check_records = check_records
TC.check_arrest = check_arrest
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 1487256c712..48b022a65f3 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -11,9 +11,6 @@
var/max_amount = 0
var/price = 0 // Price to buy one
-/**
- * A vending machine
- */
/obj/machinery/vending
name = "\improper Vendomat"
desc = "A generic vending machine."
@@ -25,8 +22,10 @@
max_integrity = 300
integrity_failure = 100
armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70)
- var/icon_vend //Icon_state when vending
- var/icon_deny //Icon_state when denying access
+ /// Icon_state when vending
+ var/icon_vend
+ /// Icon_state when denying access
+ var/icon_deny
// Power
use_power = IDLE_POWER_USE
@@ -34,12 +33,14 @@
var/vend_power_usage = 150
// Vending-related
- var/active = 1 //No sales pitches if off!
- var/vend_ready = 1 //Are we ready to vend?? Is it time??
- var/vend_delay = 10 //How long does it take to vend?
- var/datum/data/vending_product/currently_vending = null // What we're requesting payment for right now
- var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI
- var/status_error = 0 // Set to 1 if status_message is an error
+ /// No sales pitches if off
+ var/active = 1
+ /// If off, vendor is busy and unusable until current action finishes
+ var/vend_ready = TRUE
+ /// How long vendor takes to vend one item.
+ var/vend_delay = 10
+ /// Item currently being bought
+ var/datum/data/vending_product/currently_vending = null
// To be filled out at compile time
var/list/products = list() // For each, use the following pattern:
@@ -51,16 +52,19 @@
var/list/product_records = list()
var/list/hidden_records = list()
var/list/coin_records = list()
+ var/list/imagelist = list()
-
- var/list/ads_list = list() //Small ad messages in the vending screen - random chance, TODO: implementation
+ /// Unimplemented list of ads that are meant to show up somewhere, but don't.
+ var/list/ads_list = list()
// Stuff relating vocalizations
- var/list/slogan_list = list() //List of slogans the vendor will say, optional
+ /// List of slogans the vendor will say, optional
+ var/list/slogan_list = list()
var/vend_reply //Thank you for shopping!
- var/shut_up = 0 //Stop spouting those godawful pitches!
+ /// If true, prevent saying sales pitches
+ var/shut_up = FALSE
///can we access the hidden inventory?
- var/extended_inventory = 0
+ var/extended_inventory = FALSE
var/last_reply = 0
var/last_slogan = 0 //When did we last pitch?
var/slogan_delay = 6000 //How long until we can pitch again?
@@ -69,17 +73,26 @@
var/obj/item/vending_refill/refill_canister = null
// Things that can go wrong
- emagged = 0 //Ignores if somebody doesn't have card access to that machine.
- var/seconds_electrified = 0 //Shock customers like an airlock.
- var/shoot_inventory = 0 //Fire items at customers! We're broken!
- var/shoot_speed = 3 //How hard are we firing the items?
- var/shoot_chance = 2 //How often are we firing the items?
+ /// Allows people to access a vendor that's normally access restricted.
+ emagged = 0
+ /// Shocks people like an airlock
+ var/seconds_electrified = 0
+ /// Fire items at customers! We're broken!
+ var/shoot_inventory = FALSE
+ /// How hard are we firing the items?
+ var/shoot_speed = 3
+ /// How often are we firing the items? (prob(...))
+ var/shoot_chance = 2
- var/scan_id = 1
+ /// If true, enforce access checks on customers. Disabled by messing with wires.
+ var/scan_id = TRUE
+ /// Holder for a coin inserted into the vendor
var/obj/item/coin/coin
var/datum/wires/vending/wires = null
+ /// boolean, whether this vending machine can accept people inserting items into it, used for coffee vendors
var/item_slot = FALSE
+ /// the actual item inserted
var/obj/item/inserted_item = null
/obj/machinery/vending/Initialize(mapload)
@@ -92,6 +105,10 @@
build_inventory(products, product_records)
build_inventory(contraband, hidden_records)
build_inventory(premium, coin_records)
+ for (var/datum/data/vending_product/R in (product_records + coin_records + hidden_records))
+ var/obj/item/I = R.product_path
+ var/pp = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-")
+ imagelist[pp] = "[icon2base64(icon(initial(I.icon), initial(I.icon_state)))]"
if(LAZYLEN(slogan_list))
// So not all machines speak at the exact same time.
// The first time this machine says something will be at slogantime + this random value,
@@ -101,6 +118,7 @@
power_change()
/obj/machinery/vending/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
QDEL_NULL(coin)
QDEL_NULL(inserted_item)
@@ -190,7 +208,6 @@
var/obj/item/vending_refill/R = locate() in component_parts
if(!R)
CRASH("Constructible vending machine did not have a refill canister")
- return
R.products = unbuild_inventory(product_records)
R.contraband = unbuild_inventory(hidden_records)
@@ -214,37 +231,16 @@
..()
/obj/machinery/vending/attackby(obj/item/I, mob/user, params)
- if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended)
- var/paid = 0
- var/handled = 0
- if(istype(I, /obj/item/card/id))
- var/obj/item/card/id/C = I
- paid = pay_with_card(C)
- handled = 1
- if(istype(I, /obj/item/pda))
- var/obj/item/pda/PDA = I
- if(PDA.id)
- paid = pay_with_card(PDA.id)
- handled = 1
- else if(istype(I, /obj/item/stack/spacecash))
- var/obj/item/stack/spacecash/C = I
- paid = pay_with_cash(C, user)
- handled = 1
-
- if(paid)
- vend(currently_vending, usr)
+ if(istype(I, /obj/item/coin))
+ if(!premium.len)
+ to_chat(user, "[src] does not accept coins.")
return
- else if(handled)
- SSnanoui.update_uis(src)
- return // don't smack that machine with your 2 thalers
-
- if(istype(I, /obj/item/coin) && premium.len)
if(!user.drop_item())
return
I.forceMove(src)
coin = I
to_chat(user, "You insert the [I] into the [src]")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
return
if(refill_canister && istype(I, refill_canister))
if(!panel_open)
@@ -295,7 +291,7 @@
else
SCREWDRIVER_CLOSE_PANEL_MESSAGE
overlays.Cut()
- SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI
+ SStgui.update_uis(src)
/obj/machinery/vending/wirecutter_act(mob/user, obj/item/I)
. = TRUE
@@ -359,12 +355,10 @@
if(!user.drop_item())
to_chat(user, "[I] is stuck to your hand, you can't seem to put it down!")
return
-
inserted_item = I
I.forceMove(src)
-
to_chat(user, "You insert [I] into [src].")
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/vending/proc/eject_item(mob/user)
if(!item_slot || !inserted_item)
@@ -377,23 +371,19 @@
var/turf/T = get_turf(src)
inserted_item.forceMove(T)
inserted_item = null
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
/obj/machinery/vending/emag_act(user as mob)
emagged = TRUE
to_chat(user, "You short out the product lock on [src]")
-/**
- * Receive payment with cashmoney.
- *
- * usr is the mob who gets the change.
- */
+
/obj/machinery/vending/proc/pay_with_cash(obj/item/stack/spacecash/cashmoney, mob/user)
if(currently_vending.price > cashmoney.amount)
// This is not a status display message, since it's something the character
// themselves is meant to see BEFORE putting the money in
to_chat(usr, "[bicon(cashmoney)] That is not enough money.")
- return 0
+ return FALSE
// Bills (banknotes) cannot really have worth different than face value,
// so we have to eat the bill and spit out change in a bundle
@@ -404,66 +394,38 @@
cashmoney.use(currently_vending.price)
// Vending machines have no idea who paid with cash
- credit_purchase("(cash)")
- return 1
+ GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", name, "(cash)")
+ return TRUE
-/**
- * Scan a card and attempt to transfer payment from associated account.
- *
- * Takes payment for whatever is the currently_vending item. Returns 1 if
- * successful, 0 if failed
- */
-/obj/machinery/vending/proc/pay_with_card(var/obj/item/card/id/I)
- visible_message("[usr] swipes a card through [src].")
- return pay_with_account(get_card_account(I))
-/obj/machinery/vending/proc/pay_with_account(var/datum/money_account/customer_account)
+/obj/machinery/vending/proc/pay_with_card(obj/item/card/id/I, mob/M)
+ visible_message("[M] swipes a card through [src].")
+ return pay_with_account(get_card_account(I), M)
+
+/obj/machinery/vending/proc/pay_with_account(datum/money_account/customer_account, mob/M)
if(!customer_account)
- src.status_message = "Error: Unable to access account. Please contact technical support if problem persists."
- src.status_error = 1
- return 0
-
+ to_chat(M, "Error: Unable to access account. Please contact technical support if problem persists.")
+ return FALSE
if(customer_account.suspended)
- src.status_message = "Unable to access account: account suspended."
- src.status_error = 1
- return 0
-
- // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is
- // empty at high security levels
- if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2)
+ to_chat(M, "Unable to access account: account suspended.")
+ return FALSE
+ // Have the customer punch in the PIN before checking if there's enough money.
+ // Prevents people from figuring out acct is empty at high security levels
+ if(customer_account.security_level != 0)
+ // If card requires pin authentication (ie seclevel 1 or 2)
var/attempt_pin = input("Enter pin code", "Vendor transaction") as num
-
if(!attempt_account_access(customer_account.account_number, attempt_pin, 2))
- src.status_message = "Unable to access account: incorrect credentials."
- src.status_error = 1
- return 0
-
+ to_chat(M, "Unable to access account: incorrect credentials.")
+ return FALSE
if(currently_vending.price > customer_account.money)
- src.status_message = "Insufficient funds in account."
- src.status_error = 1
- return 0
- else
- // Okay to move the money at this point
- var/paid = customer_account.charge(currently_vending.price, GLOB.vendor_account,
- "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name,
- "Sale of [currently_vending.name]", customer_account.owner_name)
+ to_chat(M, "Your bank account has insufficient money to purchase this.")
+ return FALSE
+ // Okay to move the money at this point
+ customer_account.charge(currently_vending.price, GLOB.vendor_account,
+ "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name,
+ "Sale of [currently_vending.name]", customer_account.owner_name)
+ return TRUE
- if(paid)
- // Give the vendor the money. We use the account owner name, which means
- // that purchases made with stolen/borrowed card will look like the card
- // owner made them
- credit_purchase(customer_account.owner_name)
- return paid
-
-/**
- * Add money for current purchase to the vendor account.
- *
- * Called after the money has already been taken from the customer.
- */
-/obj/machinery/vending/proc/credit_purchase(var/target as text)
- GLOB.vendor_account.money += currently_vending.price
- GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]",
- name, target)
/obj/machinery/vending/attack_ai(mob/user)
return attack_hand(user)
@@ -479,174 +441,238 @@
if(src.shock(user, 100))
return
- ui_interact(user)
+ tgui_interact(user)
wires.Interact(user)
-/**
- * Display the NanoUI window for the vending machine.
- *
- * See NanoUI documentation for details.
- */
-/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/vending/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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)
+ var/estimated_height = 100 + (length(product_records) * 34)
+ if(length(prices) > 0)
+ estimated_height += 100 // to account for the "current user" interface
+ ui = new(user, src, ui_key, "Vending", name, 470, estimated_height, master_ui, state)
ui.open()
-/obj/machinery/vending/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/vending/tgui_data(mob/user)
var/list/data = list()
- if(currently_vending)
- data["mode"] = 1
- data["product"] = sanitize(currently_vending.name)
- data["price"] = currently_vending.price
- data["message_err"] = 0
- data["message"] = src.status_message
- data["message_err"] = src.status_error
- else
- data["mode"] = 0
- var/list/listed_products = list()
-
- var/list/display_records = product_records + coin_records
- if(extended_inventory)
- display_records = product_records + coin_records + hidden_records
-
- for(var/key = 1 to display_records.len)
- var/datum/data/vending_product/I = display_records[key]
-
- if(coin_records.Find(I) && !coin)
- continue
-
- if(hidden_records.Find(I) && !extended_inventory)
- continue
-
- listed_products.Add(list(list(
- "key" = key,
- "name" = sanitize(I.name),
- "price" = I.price,
- "amount" = I.amount)))
-
- data["products"] = listed_products
-
- if(coin)
- data["coin"] = coin.name
-
- 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"] = shut_up ? 0 : 1
- else
- data["panel"] = 0
+ var/mob/living/carbon/human/H
+ var/obj/item/card/id/C
+ data["guestNotice"] = "No valid ID card detected. Wear your ID, or present cash.";
+ data["userMoney"] = 0
+ data["user"] = null
+ if(ishuman(user))
+ H = user
+ C = H.get_idcard(TRUE)
+ var/obj/item/stack/spacecash/S = H.get_active_hand()
+ if(istype(S))
+ data["userMoney"] = S.amount
+ data["guestNotice"] = "Accepting Cash. You have: [S.amount] credits."
+ else if(istype(C))
+ var/datum/money_account/A = get_card_account(C)
+ if(istype(A))
+ data["user"] = list()
+ data["user"]["name"] = A.owner_name
+ data["userMoney"] = A.money
+ data["user"]["job"] = (istype(C) && C.rank) ? C.rank : "No Job"
+ else
+ data["guestNotice"] = "Unlinked ID detected. Present cash to pay.";
+ data["stock"] = list()
+ for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
+ data["stock"][R.name] = R.amount
+ data["extended_inventory"] = extended_inventory
+ data["vend_ready"] = vend_ready
+ data["coin_name"] = coin ? coin.name : FALSE
+ data["panel_open"] = panel_open ? TRUE : FALSE
+ data["speaker"] = shut_up ? FALSE : TRUE
+ data["item_slot"] = item_slot // boolean
+ data["inserted_item_name"] = inserted_item ? inserted_item.name : FALSE
return data
-/obj/machinery/vending/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon))
- if(!coin)
- to_chat(usr, "There is no coin in this machine.")
- return
+/obj/machinery/vending/tgui_static_data(mob/user)
+ var/list/data = list()
+ data["chargesMoney"] = length(prices) > 0 ? TRUE : FALSE
+ data["product_records"] = list()
+ var/i = 1
+ for (var/datum/data/vending_product/R in product_records)
+ var/list/data_pr = list(
+ path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"),
+ name = R.name,
+ price = (R.product_path in prices) ? prices[R.product_path] : 0,
+ max_amount = R.max_amount,
+ req_coin = FALSE,
+ is_hidden = FALSE,
+ inum = i
+ )
+ data["product_records"] += list(data_pr)
+ i++
+ data["coin_records"] = list()
+ for (var/datum/data/vending_product/R in coin_records)
+ var/list/data_cr = list(
+ path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"),
+ name = R.name,
+ price = (R.product_path in prices) ? prices[R.product_path] : 0,
+ max_amount = R.max_amount,
+ req_coin = TRUE,
+ is_hidden = FALSE,
+ inum = i,
+ premium = TRUE
+ )
+ data["coin_records"] += list(data_cr)
+ i++
+ data["hidden_records"] = list()
+ for (var/datum/data/vending_product/R in hidden_records)
+ var/list/data_hr = list(
+ path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"),
+ name = R.name,
+ price = (R.product_path in prices) ? prices[R.product_path] : 0,
+ max_amount = R.max_amount,
+ req_coin = FALSE,
+ is_hidden = TRUE,
+ inum = i,
+ premium = TRUE
+ )
+ data["hidden_records"] += list(data_hr)
+ i++
+ data["imagelist"] = imagelist
+ return data
- usr.put_in_hands(coin)
- coin = null
- to_chat(usr, "You remove [coin] from [src].")
+/obj/machinery/vending/tgui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ if(issilicon(usr) && !isrobot(usr))
+ to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!")
+ return
+ switch(action)
+ if("toggle_voice")
+ if(panel_open)
+ shut_up = !shut_up
+ . = TRUE
+ if("eject_item")
+ eject_item(usr)
+ . = TRUE
+ if("remove_coin")
+ if(!coin)
+ to_chat(usr, "There is no coin in this machine.")
+ return
+ if(istype(usr, /mob/living/silicon))
+ to_chat(usr, "You lack hands.")
+ return
+ to_chat(usr, "You remove [coin] from [src].")
+ usr.put_in_hands(coin)
+ coin = null
+ . = TRUE
+ if("vend")
+ if(!vend_ready)
+ to_chat(usr, "The vending machine is busy!")
+ return
+ if(panel_open)
+ to_chat(usr, "The vending machine cannot dispense products while its service panel is open!")
+ return
+ var/key = text2num(params["inum"])
+ var/list/display_records = product_records + coin_records
+ if(extended_inventory)
+ display_records = product_records + coin_records + hidden_records
+ if(key < 1 || key > length(display_records))
+ to_chat(usr, "ERROR: invalid inum passed to vendor. Report this bug.")
+ return
+ var/datum/data/vending_product/R = display_records[key]
+ if(!istype(R))
+ to_chat(usr, "ERROR: unknown vending_product record. Report this bug.")
+ return
+ var/list/record_to_check = product_records + coin_records
+ if(extended_inventory)
+ record_to_check = product_records + coin_records + hidden_records
+ if(!R || !istype(R) || !R.product_path)
+ to_chat(usr, "ERROR: unknown product record. Report this bug.")
+ return
+ if(R in hidden_records)
+ if(!extended_inventory)
+ // Exploit prevention, stop the user purchasing hidden stuff if they haven't hacked the machine.
+ to_chat(usr, "ERROR: machine does not allow extended_inventory in current state. Report this bug.")
+ return
+ else if (!(R in record_to_check))
+ // Exploit prevention, stop the user
+ message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!")
+ return
+ if (R.amount <= 0)
+ to_chat(usr, "Sold out of [R.name].")
+ flick(icon_deny, src)
+ return
- if(href_list["remove_item"])
- eject_item(usr)
+ vend_ready = FALSE // From this point onwards, vendor is locked to performing this transaction only, until it is resolved.
- if(href_list["pay"])
- if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended)
- var/paid = 0
- var/handled = 0
- var/datum/money_account/A = usr.get_worn_id_account()
- if(A)
- paid = pay_with_account(A)
- handled = 1
- else if(istype(usr.get_active_hand(), /obj/item/card))
- paid = pay_with_card(usr.get_active_hand())
- handled = 1
- else if(usr.can_admin_interact())
- paid = 1
- handled = 1
+ if(!ishuman(usr) || R.price <= 0)
+ // Either the purchaser is not human, or the item is free.
+ // Skip all payment logic.
+ vend(R, usr)
+ add_fingerprint(usr)
+ vend_ready = TRUE
+ . = TRUE
+ return
+
+ // --- THE REST OF THIS PROC IS JUST PAYMENT LOGIC ---
+
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/card/id/C = H.get_idcard(TRUE)
+
+ if(!GLOB.vendor_account || GLOB.vendor_account.suspended)
+ to_chat(usr, "Vendor account offline. Unable to process transaction.")
+ flick(icon_deny, src)
+ vend_ready = TRUE
+ return
+
+ currently_vending = R
+ var/paid = FALSE
+
+ if(istype(usr.get_active_hand(), /obj/item/stack/spacecash))
+ var/obj/item/stack/spacecash/S = usr.get_active_hand()
+ paid = pay_with_cash(S)
+ else if(istype(C, /obj/item/card))
+ // Because this uses H.get_idcard(TRUE), it will attempt to use:
+ // active hand, inactive hand, pda.id, and then wear_id ID in that order
+ // this is important because it lets people buy stuff with someone else's ID by holding it while using the vendor
+ paid = pay_with_card(C, usr)
+ else if(usr.can_advanced_admin_interact())
+ to_chat(usr, "Vending object due to admin interaction.")
+ paid = TRUE
+ else
+ to_chat(usr, "Payment failure: you have no ID or other method of payment.")
+ vend_ready = TRUE
+ flick(icon_deny, src)
+ . = TRUE // we set this because they shouldn't even be able to get this far, and we want the UI to update.
+ return
if(paid)
vend(currently_vending, usr)
- return
- else if(handled)
- SSnanoui.update_uis(src)
- return // don't smack that machine with your 2 credits
-
- if((href_list["vend"]) && vend_ready && !currently_vending)
-
- if(issilicon(usr) && !isrobot(usr))
- to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!")
- return
-
- if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- to_chat(usr, "Access denied.") //Unless emagged of course
- flick(icon_deny,src)
- return
-
- var/key = text2num(href_list["vend"])
- var/list/display_records = product_records + coin_records
- if(extended_inventory)
- display_records = product_records + coin_records + hidden_records
- var/datum/data/vending_product/R = display_records[key]
-
- // This should not happen unless the request from NanoUI was bad
- if(coin_records.Find(R) && !coin)
- return
-
- if(hidden_records.Find(R) && !extended_inventory)
- return
-
- if(R.price <= 0)
- vend(R, usr)
- else
- currently_vending = R
- if(!GLOB.vendor_account || GLOB.vendor_account.suspended)
- status_message = "This machine is currently unable to process payments due to problems with the associated account."
- status_error = 1
+ . = TRUE
else
- status_message = "Please swipe a card or insert cash to pay for the item."
- status_error = 0
+ to_chat(usr, "Payment failure: unable to process payment.")
+ vend_ready = TRUE
+ if(.)
+ add_fingerprint(usr)
- else if(href_list["cancelpurchase"])
- currently_vending = null
- else if(href_list["togglevoice"] && panel_open)
- shut_up = !src.shut_up
- add_fingerprint(usr)
- 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
- to_chat(usr, "Access denied.")//Unless emagged of course
- flick(icon_deny,src)
+ if(!allowed(user) && !user.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
+ to_chat(user, "Access denied.")//Unless emagged of course
+ flick(icon_deny, src)
+ vend_ready = TRUE
return
if(!R.amount)
to_chat(user, "The vending machine has ran out of that product.")
+ vend_ready = TRUE
return
- vend_ready = 0 //One thing at a time!!
- status_message = "Vending..."
- status_error = 0
- SSnanoui.update_uis(src)
+ vend_ready = FALSE //One thing at a time!!
if(coin_records.Find(R))
if(!coin)
to_chat(user, "You need to insert a coin to get this item.")
+ vend_ready = TRUE
return
if(coin.string_attached)
if(prob(50))
@@ -666,15 +692,13 @@
use_power(vend_power_usage) //actuators and stuff
if(icon_vend) //Show the vending animation if needed
flick(icon_vend, src)
+ playsound(get_turf(src), 'sound/machines/machine_vend.ogg', 50, TRUE)
addtimer(CALLBACK(src, .proc/delayed_vend, R, user), vend_delay)
/obj/machinery/vending/proc/delayed_vend(datum/data/vending_product/R, mob/user)
do_vend(R, user)
- status_message = ""
- status_error = 0
- vend_ready = 1
+ vend_ready = TRUE
currently_vending = null
- 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, mob/user)
@@ -810,17 +834,6 @@
*/
-/*
-/obj/machinery/vending/atmospherics //Commenting this out until someone ponies up some actual working, broken, and unpowered sprites - Quarxink
- name = "\improper Tank Vendor"
- desc = "A vendor with a wide variety of masks and gas tanks."
- icon = 'icons/obj/objects.dmi'
- icon_state = "dispenser"
- product_paths = "/obj/item/tank/oxygen;/obj/item/tank/plasma;/obj/item/tank/emergency_oxygen;/obj/item/tank/emergency_oxygen/engi;/obj/item/clothing/mask/breath"
- product_amounts = "10;10;10;5;25"
- vend_delay = 0
-*/
-
/obj/machinery/vending/assist
products = list( /obj/item/assembly/prox_sensor = 5,/obj/item/assembly/igniter = 3,/obj/item/assembly/signaler = 4,
/obj/item/wirecutters = 1, /obj/item/cartridge/signal = 4)
@@ -1386,7 +1399,6 @@
/obj/item/clothing/glasses/gglasses = 1,
/obj/item/clothing/shoes/jackboots = 1,
/obj/item/clothing/under/schoolgirl = 1,
- /obj/item/clothing/head/kitty = 1,
/obj/item/clothing/under/blackskirt = 1,
/obj/item/clothing/suit/toggle/owlwings = 1,
/obj/item/clothing/under/owl = 1,
@@ -1481,6 +1493,7 @@
/obj/item/clothing/under/victsuit/redblk = 1,
/obj/item/clothing/under/victsuit/red = 1,
/obj/item/clothing/suit/tailcoat = 1,
+ /obj/item/clothing/under/tourist_suit = 1,
/obj/item/clothing/suit/draculacoat = 1,
/obj/item/clothing/head/zepelli = 1,
/obj/item/clothing/under/redhawaiianshirt = 1,
@@ -1567,7 +1580,6 @@
desc = "Tools for tools."
icon_state = "tool"
icon_deny = "tool-deny"
- //req_access_txt = "12" //Maintenance access
products = list(/obj/item/stack/cable_coil/random = 10,/obj/item/crowbar = 5,/obj/item/weldingtool = 3,/obj/item/wirecutters = 5,
/obj/item/wrench = 5,/obj/item/analyzer = 5,/obj/item/t_scanner = 5,/obj/item/screwdriver = 5)
contraband = list(/obj/item/weldingtool/hugetank = 2,/obj/item/clothing/gloves/color/fyellow = 2)
diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm
index 5916d0f96c7..2248c37f518 100644
--- a/code/game/mecha/equipment/tools/mining_tools.dm
+++ b/code/game/mecha/equipment/tools/mining_tools.dm
@@ -94,7 +94,7 @@
/obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user)
target.visible_message("[chassis] is drilling [target] with [src]!",
"[chassis] is drilling you with [src]!")
- add_attack_logs(user, target, "DRILLED with [src] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
+ add_attack_logs(user, target, "DRILLED with [src] ([uppertext(user.a_intent)]) ([uppertext(damtype)])")
if(target.stat == DEAD && target.getBruteLoss() >= 200)
add_attack_logs(user, target, "gibbed")
if(LAZYLEN(target.butcher_results))
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index 43937ab1111..61ba683ad18 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -206,6 +206,7 @@
/obj/item/mecha_parts/mecha_equipment/repair_droid/detach()
chassis.overlays -= droid_overlay
STOP_PROCESSING(SSobj, src)
+ return ..()
/obj/item/mecha_parts/mecha_equipment/repair_droid/get_equip_info()
if(!chassis) return
diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm
index 63eeb21e165..5962a1eccbb 100644
--- a/code/game/mecha/equipment/tools/work_tools.dm
+++ b/code/game/mecha/equipment/tools/work_tools.dm
@@ -60,7 +60,7 @@
target.visible_message("[chassis] squeezes [target].", \
"[chassis] squeezes [target].",\
"You hear something crack.")
- add_attack_logs(chassis.occupant, M, "Squeezed with [src] (INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])")
+ add_attack_logs(chassis.occupant, M, "Squeezed with [src] ([uppertext(chassis.occupant.a_intent)]) ([uppertext(damtype)])")
start_cooldown()
else
step_away(M,chassis)
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index df6d78d86df..d1e63deaf0c 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -166,10 +166,6 @@
var/mob/M = A
if(istype(firer, /mob))
add_attack_logs(firer, M, "Mecha-shot with [src]")
- if(!iscarbon(firer))
- M.LAssailant = null
- else
- M.LAssailant = firer
else
add_attack_logs(null, M, "Mecha-shot with [src]")
if(life <= 0)
@@ -389,7 +385,7 @@
return
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang
- name = "SGL-6 Grenade Launcher"
+ name = "SGL-6 Flashbang Launcher"
icon_state = "mecha_grenadelnchr"
origin_tech = "combat=4;engineering=4"
projectile = /obj/item/grenade/flashbang
@@ -415,7 +411,7 @@
return
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang//Because I am a heartless bastard -Sieve
- name = "SOB-3 Grenade Launcher"
+ name = "SOB-3 Clusterbang Launcher"
desc = "A weapon for combat exosuits. Launches primed clusterbangs. You monster."
origin_tech = "combat=4;materials=4"
projectiles = 3
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index 04322c63093..34e8648c59e 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -5,8 +5,8 @@
/turf/simulated/floor/mech_bay_recharge_floor/airless
icon_state = "recharge_floor_asteroid"
- oxygen = 0.01
- nitrogen = 0.01
+ oxygen = 0
+ nitrogen = 0
temperature = TCMB
/obj/machinery/mech_bay_recharge_port
@@ -158,36 +158,36 @@
/obj/machinery/computer/mech_bay_power_console/attack_hand(mob/user as mob)
if(..())
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/mech_bay_power_console/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "mech_bay_console.tmpl", "Mech Bay Control Console", 500, 325)
- // open the new ui window
+ ui = new(user, src, ui_key, "MechBayConsole", name, 400, 150, master_ui, state)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/machinery/computer/mech_bay_power_console/tgui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("reconnect")
+ reconnect()
+ . = TRUE
+ update_icon()
+
+/obj/machinery/computer/mech_bay_power_console/tgui_data(mob/user)
+ var/data = list()
if(!recharge_port)
reconnect()
if(recharge_port && !QDELETED(recharge_port))
data["recharge_port"] = list("mech" = null)
if(recharge_port.recharging_mecha && !QDELETED(recharge_port.recharging_mecha))
- data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = initial(recharge_port.recharging_mecha.max_integrity), "cell" = null)
+ data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = recharge_port.recharging_mecha.max_integrity, "cell" = null, "name" = recharge_port.recharging_mecha.name)
if(recharge_port.recharging_mecha.cell && !QDELETED(recharge_port.recharging_mecha.cell))
- data["has_mech"] = 1
- data["mecha_name"] = recharge_port.recharging_mecha || "None"
- data["mecha_charge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.charge
- data["mecha_maxcharge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.maxcharge
- data["mecha_charge_percentage"] = isnull(recharge_port.recharging_mecha) ? 0 : round(recharge_port.recharging_mecha.cell.percent())
- else
- data["has_mech"] = 0
-
+ data["recharge_port"]["mech"]["cell"] = list(
+ "charge" = recharge_port.recharging_mecha.cell.charge,
+ "maxcharge" = recharge_port.recharging_mecha.cell.maxcharge
+ )
return data
/obj/machinery/computer/mech_bay_power_console/Initialize()
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 90256fe5d2a..50fdfef3cb3 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -38,9 +38,7 @@
)
/obj/machinery/mecha_part_fabricator/New()
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
- list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE), 0,
- FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
+ var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE), 0, FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
materials.precise_insertion = TRUE
..()
component_parts = list()
@@ -65,7 +63,7 @@
RefreshParts()
/obj/machinery/mecha_part_fabricator/Destroy()
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.retrieve_all()
return ..()
@@ -75,7 +73,7 @@
//maximum stocking amount (default 300000, 600000 at T4)
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
T += M.rating
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.max_amount = (200000 + (T*50000))
//resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55)
@@ -118,7 +116,7 @@
/obj/machinery/mecha_part_fabricator/proc/output_available_resources()
var/output
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
for(var/mat_id in materials.materials)
var/datum/material/M = materials.materials[mat_id]
output += "[M.name]: [M.amount] cm³"
@@ -139,7 +137,7 @@
/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D)
if(D.reagents_list.len) // No reagents storage - no reagent designs.
return FALSE
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
if(materials.has_materials(get_resources_w_coeff(D)))
return TRUE
return FALSE
@@ -149,7 +147,7 @@
desc = "It's building \a [initial(D.name)]."
var/list/res_coef = get_resources_w_coeff(D)
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.use_amount(res_coef)
overlays += "fab-active"
use_power = ACTIVE_POWER_USE
@@ -392,7 +390,7 @@
var/index = afilter.getNum("index")
var/new_index = index + afilter.getNum("queue_move")
if(isnum(index) && isnum(new_index))
- if(IsInRange(new_index,1,queue.len))
+ if(ISINRANGE(new_index,1,queue.len))
queue.Swap(index,new_index)
return update_queue_on_page()
if(href_list["clear_queue"])
@@ -414,7 +412,7 @@
break
if(href_list["remove_mat"] && href_list["material"])
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"])
updateUsrDialog()
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 610436406d8..2cc543b9dc9 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -1,3 +1,5 @@
+#define OCCUPANT_LOGGING occupant ? occupant : "empty mech"
+
/obj/mecha
name = "Mecha"
desc = "Exosuit"
@@ -11,6 +13,7 @@
force = 5
max_integrity = 300 //max_integrity is base health
armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100)
+ bubble_icon = "machine"
var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5)
var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming.
var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items)
@@ -36,6 +39,7 @@
var/lights_power = 6
var/emagged = FALSE
var/frozen = FALSE
+ var/repairing = FALSE
//inner atmos
var/use_internal_tank = 0
@@ -155,7 +159,6 @@
radio.name = "[src] radio"
radio.icon = icon
radio.icon_state = icon_state
- radio.subspace_transmission = 1
/obj/mecha/examine(mob/user)
. = ..()
@@ -327,6 +330,8 @@
else
occupant.clear_alert("mechaport")
if(leg_overload_mode)
+ log_message("Leg Overload damage.")
+ take_damage(1, BRUTE, FALSE, FALSE)
if(obj_integrity < max_integrity - max_integrity / 3)
leg_overload_mode = FALSE
step_in = initial(step_in)
@@ -498,7 +503,7 @@
check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
else
check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT))
- if(. >= 5 || prob(33))
+ if((. >= 5 || prob(33)) && !(. == 1 && leg_overload_mode)) //If it takes 1 damage and leg_overload_mode is true, do not say TAKING DAMAGE! to the user several times a second.
occupant_message("Taking damage!")
log_message("Took [damage_amount] points of damage. Damage type: [damage_type]")
@@ -536,12 +541,13 @@
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1)
- user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.")
+ user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.")
log_message("Attack by hand/paw. Attacker - [user].")
/obj/mecha/attack_alien(mob/living/user)
log_message("Attack by alien. Attacker - [user].", TRUE)
+ add_attack_logs(user, OCCUPANT_LOGGING, "Alien attacked mech [src]")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE)
attack_generic(user, 15, BRUTE, "melee", 0)
@@ -559,8 +565,8 @@
if(user.obj_damage)
animal_damage = user.obj_damage
animal_damage = min(animal_damage, 20*user.environment_smash)
- user.create_attack_log("attacked [name]")
- add_attack_logs(user, src, "Attacked")
+ if(animal_damage)
+ add_attack_logs(user, OCCUPANT_LOGGING, "Animal attacked mech [src]")
attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect)
return TRUE
@@ -571,7 +577,7 @@
. = ..()
if(.)
log_message("Attack by hulk. Attacker - [user].", 1)
- add_attack_logs(user, src, "Punched with hulk powers")
+ add_attack_logs(user, OCCUPANT_LOGGING, "Hulk punched mech [src]")
/obj/mecha/blob_act(obj/structure/blob/B)
log_message("Attack by blob. Attacker - [B].")
@@ -582,10 +588,14 @@
/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper
log_message("Hit by [AM].")
+ if(isitem(AM))
+ var/obj/item/I = AM
+ add_attack_logs(I.thrownby, OCCUPANT_LOGGING, "threw [AM] at mech [src]")
. = ..()
/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper
log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).")
+ add_attack_logs(Proj.firer, OCCUPANT_LOGGING, "shot [Proj.name]([Proj.flag]) at mech [src]")
..()
/obj/mecha/ex_act(severity, target)
@@ -609,6 +619,8 @@
occupant = null
icon_state = initial(icon_state)+"-open"
setDir(dir_in)
+ if(A in trackers)
+ trackers -= A
/obj/mecha/Destroy()
if(occupant)
@@ -639,7 +651,7 @@
cabin_air = null
QDEL_NULL(spark_system)
QDEL_NULL(smoke_system)
-
+ QDEL_LIST(trackers)
GLOB.mechas_list -= src //global mech list
return ..()
@@ -770,6 +782,8 @@
to_chat(user, "You stop installing [M].")
else
+ if(W.force)
+ add_attack_logs(user, OCCUPANT_LOGGING, "attacked mech '[src]' using [W]")
return ..()
@@ -836,7 +850,11 @@
if((obj_integrity >= max_integrity) && !internal_damage)
to_chat(user, "[src] is at full integrity!")
return
+ if(repairing)
+ to_chat(user, "[src] is currently being repaired!")
+ return
WELDER_ATTEMPT_REPAIR_MESSAGE
+ repairing = TRUE
if(I.use_tool(src, user, 15, volume = I.tool_volume))
if(internal_damage & MECHA_INT_TANK_BREACH)
clearInternalDamage(MECHA_INT_TANK_BREACH)
@@ -846,13 +864,14 @@
obj_integrity += min(10, max_integrity - obj_integrity)
else
to_chat(user, "[src] is at full integrity!")
+ repairing = FALSE
/obj/mecha/mech_melee_attack(obj/mecha/M)
if(!has_charge(melee_energy_drain))
return 0
use_power(melee_energy_drain)
if(M.damtype == BRUTE || M.damtype == BURN)
- add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
+ add_attack_logs(M.occupant, src, "Mecha-attacked with [M] ([uppertext(M.occupant.a_intent)]) ([uppertext(M.damtype)])")
. = ..()
/obj/mecha/emag_act(mob/user)
@@ -1255,6 +1274,9 @@
L.client.RemoveViewMod("mecha")
zoom_mode = FALSE
+/obj/mecha/force_eject_occupant()
+ go_out()
+
/////////////////////////
////// Access stuff /////
/////////////////////////
@@ -1444,8 +1466,10 @@
diag_hud_set_mechtracking()
-/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
- flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30)
+/obj/mecha/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
+ var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER)
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+ INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30)
/obj/mecha/update_remote_sight(mob/living/user)
if(occupant_sight_flags)
@@ -1454,7 +1478,7 @@
..()
-/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
+/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!no_effect)
if(selected)
used_item = selected
@@ -1523,3 +1547,5 @@
if(L.incapacitated())
return FALSE
return TRUE
+
+#undef OCCUPANT_LOGGING
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index 9a3dffb0c3b..f72770a8ea3 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -28,7 +28,12 @@
data["screen"] = screen
if(screen == 0)
var/list/mechas[0]
- for(var/obj/item/mecha_parts/mecha_tracking/TR in world)
+ var/list/trackerlist = list()
+ for(var/stompy in GLOB.mechas_list)
+ var/obj/mecha/MC = stompy
+ trackerlist += MC.trackers
+ for(var/thing in trackerlist)
+ var/obj/item/mecha_parts/mecha_tracking/TR = thing
var/answer = TR.get_mecha_info()
if(answer)
mechas[++mechas.len] = answer
diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm
index 494fc70d36b..7569d8b5263 100644
--- a/code/game/mecha/working/ripley.dm
+++ b/code/game/mecha/working/ripley.dm
@@ -124,8 +124,7 @@
//Attach hydraulic clamp
var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new
HC.attach(src)
- for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)//Deletes the beacon so it can't be found easily
- qdel(B)
+ QDEL_LIST(trackers) //Deletes the beacon so it can't be found easily
var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new
scanner.attach(src)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index bb1070f2cf1..e3c14ff459c 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -11,7 +11,7 @@
/atom/movable/attack_hand(mob/living/user)
. = ..()
if(can_buckle && has_buckled_mobs())
- if(buckled_mobs.len > 1)
+ if(length(buckled_mobs) > 1)
var/unbuckled = input(user, "Who do you wish to unbuckle?", "Unbuckle Who?") as null|mob in buckled_mobs
if(user_unbuckle_mob(unbuckled,user))
return TRUE
@@ -26,15 +26,12 @@
return TRUE
/atom/movable/proc/has_buckled_mobs()
- if(!buckled_mobs)
- return FALSE
- if(buckled_mobs.len)
- return TRUE
+ return length(buckled_mobs)
/atom/movable/attack_robot(mob/living/user)
. = ..()
if(can_buckle && has_buckled_mobs() && Adjacent(user)) // attack_robot is called on all ranges, so the Adjacent check is needed
- if(buckled_mobs.len > 1)
+ if(length(buckled_mobs) > 1)
var/unbuckled = input(user, "Who do you wish to unbuckle?", "Unbuckle Who?") as null|mob in buckled_mobs
if(user_unbuckle_mob(unbuckled,user))
return TRUE
@@ -54,7 +51,7 @@
if(check_loc && M.loc != loc)
return FALSE
- if((!can_buckle && !force) || M.buckled || (buckled_mobs.len >= max_buckled_mobs) || (buckle_requires_restraints && !M.restrained()) || M == src)
+ if((!can_buckle && !force) || M.buckled || (length(buckled_mobs) >= max_buckled_mobs) || (buckle_requires_restraints && !M.restrained()) || M == src)
return FALSE
M.buckling = src
if(!M.can_buckle() && !force)
@@ -69,6 +66,9 @@
if(buckle_prevents_pull)
M.pulledby.stop_pulling()
+ for(var/obj/item/grab/G in M.grabbed_by)
+ qdel(G)
+
if(!check_loc && M.loc != loc)
M.forceMove(loc)
diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm
index 9a879a02da3..e750c15e761 100644
--- a/code/game/objects/effects/decals/remains.dm
+++ b/code/game/objects/effects/decals/remains.dm
@@ -28,6 +28,12 @@
icon_state = "remainsrobot"
anchored = TRUE
+/obj/effect/decal/remains/robot/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["glass"] += 2
+ C.stored_comms["metal"] += 3
+ qdel(src)
+ return TRUE
+
/obj/effect/decal/remains/slime
name = "You shouldn't see this"
desc = "Noooooooooooooooooooooo"
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index c78c511e1e0..3f8eb81c6b9 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -1134,7 +1134,7 @@ would spawn and follow the beaker, even if it is carried or thrown.
qdel(src)
/obj/structure/foamedmetal/attack_alien(mob/living/carbon/alien/humanoid/M)
- M.visible_message("[M] tears apart \the [src]!");
+ M.visible_message("[M] tears apart \the [src]!")
qdel(src)
/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target, height=1.5)
diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm
index 5ae6b0c6860..9ae89309eed 100644
--- a/code/game/objects/effects/effect_system/effect_system.dm
+++ b/code/game/objects/effects/effect_system/effect_system.dm
@@ -49,6 +49,8 @@ would spawn and follow the beaker, even if it is carried or thrown.
holder = atom
/datum/effect_system/proc/start()
+ if(QDELETED(src))
+ return
for(var/i in 1 to number)
if(total_effects > 20)
return
@@ -68,7 +70,8 @@ would spawn and follow the beaker, even if it is carried or thrown.
for(var/j in 1 to steps_amt)
sleep(5)
step(E,direction)
- addtimer(CALLBACK(src, .proc/decrement_total_effect), 20)
+ if(!QDELETED(src))
+ addtimer(CALLBACK(src, .proc/decrement_total_effect), 20)
/datum/effect_system/proc/decrement_total_effect()
total_effects--
diff --git a/code/game/objects/effects/manifest.dm b/code/game/objects/effects/manifest.dm
index f267494496e..6ada54e75f7 100644
--- a/code/game/objects/effects/manifest.dm
+++ b/code/game/objects/effects/manifest.dm
@@ -10,7 +10,8 @@
/obj/effect/manifest/proc/manifest()
var/dat = "Crew Manifest: "
- for(var/mob/living/carbon/human/M in GLOB.mob_list)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/M = thing
dat += text(" [] - [] ", M.name, M.get_assignment())
var/obj/item/paper/P = new /obj/item/paper( src.loc )
P.info = dat
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index f8abaa7a17e..4978a4ee314 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -7,6 +7,7 @@
var/list/loot //a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
/obj/effect/spawner/lootdrop/New()
+ ..()
if(loot && loot.len)
for(var/i = lootcount, i > 0, i--)
if(!loot.len) break
@@ -15,7 +16,7 @@
loot.Remove(lootspawn)
if(lootspawn)
- new lootspawn(get_turf(src))
+ new lootspawn(loc)
qdel(src)
/obj/effect/spawner/lootdrop/armory_contraband
diff --git a/code/game/objects/effects/spawners/vaultspawner.dm b/code/game/objects/effects/spawners/vaultspawner.dm
index 0d882cc9bd2..a78c6111eb7 100644
--- a/code/game/objects/effects/spawners/vaultspawner.dm
+++ b/code/game/objects/effects/spawners/vaultspawner.dm
@@ -21,6 +21,7 @@
if(i == lowBoundX || i == hiBoundX || j == lowBoundY || j == hiBoundY)
new /turf/simulated/wall/vault(locate(i,j,z),type)
else
- new /turf/simulated/floor/vault(locate(i,j,z),type)
+ var/turf/T = new /turf/simulated/floor/vault(locate(i, j, z))
+ T.icon_state = "[type]vault"
qdel(src)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index d97663759af..639687a45e9 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -58,7 +58,7 @@
icon_state = "eggs"
var/amount_grown = 0
var/player_spiders = 0
- var/list/faction = list()
+ var/list/faction = list("spiders")
/obj/structure/spider/eggcluster/New()
..()
@@ -90,7 +90,7 @@
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent
var/travelling_in_vent = 0
var/player_spiders = 0
- var/list/faction = list()
+ var/list/faction = list("spiders")
var/selecting_player = 0
/obj/structure/spider/spiderling/New()
@@ -98,6 +98,7 @@
pixel_x = rand(6,-6)
pixel_y = rand(6,-6)
START_PROCESSING(SSobj, src)
+ AddComponent(/datum/component/swarming)
/obj/structure/spider/spiderling/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -179,7 +180,7 @@
if(player_spiders && !selecting_player)
selecting_player = 1
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S)
if(candidates.len)
var/mob/C = pick(candidates)
@@ -189,6 +190,16 @@
to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.")
qdel(src)
+/obj/structure/spider/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(!istype(user, /mob/living/silicon/robot/drone))
+ user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \
+ "It's a bit of a struggle, but you manage to suck [user] into your decompiler. It makes a series of visceral crunching noises.")
+ C.stored_comms["wood"] += 2
+ C.stored_comms["glass"] += 2
+ qdel(src)
+ return TRUE
+ return ..()
+
/obj/effect/decal/cleanable/spiderling_remains
name = "spiderling remains"
desc = "Green squishy mess."
diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm
index 0a47e57112e..bde6cb538df 100644
--- a/code/game/objects/effects/step_triggers.dm
+++ b/code/game/objects/effects/step_triggers.dm
@@ -51,7 +51,7 @@
var/list/affecting = list()
/obj/effect/step_trigger/thrower/Trigger(atom/A)
- if(!A || !ismovableatom(A))
+ if(!A || !ismovable(A))
return
var/atom/movable/AM = A
var/curtiles = 0
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index 54ddb01b653..1b31891e537 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -88,7 +88,7 @@
var/turf/T = A
if(!T)
continue
- var/dist = hypotenuse(T.x, T.y, x0, y0)
+ var/dist = HYPOTENUSE(T.x, T.y, x0, y0)
if(config.reactionary_explosions)
var/turf/Trajectory = T
@@ -209,7 +209,7 @@
var/list/wipe_colours = list()
for(var/turf/T in spiral_range_turfs(max_range, epicenter))
wipe_colours += T
- var/dist = hypotenuse(T.x, T.y, x0, y0)
+ var/dist = HYPOTENUSE(T.x, T.y, x0, y0)
if(newmode == "Yes")
var/turf/TT = T
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index fba55ba0557..edcc6620531 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -2,6 +2,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
/obj/item
name = "item"
icon = 'icons/obj/items.dmi'
+
+ move_resist = null // Set in the Initialise depending on the item size. Unless it's overriden by a specific item
var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers
var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
var/blood_overlay_color = null
@@ -19,6 +21,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
can_be_hit = FALSE
suicidal_hands = TRUE
+ var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
var/hitsound = null
var/usesound = null
var/throwhitsound
@@ -98,15 +101,6 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc.
var/sprite_sheets_obj = null //Used to override hardcoded clothing inventory object dmis in human clothing proc.
- var/trip_verb = TV_TRIP
- var/trip_chance = 0
-
- var/trip_stun = 0
- var/trip_weaken = 0
- var/trip_any = FALSE
- var/trip_walksafe = TRUE
- var/trip_tiles = 0
-
//Tooltip vars
var/in_inventory = FALSE //is this item equipped into an inventory slot or hand of a mob?
var/tip_timer = 0
@@ -121,6 +115,23 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
hitsound = 'sound/items/welder.ogg'
if(damtype == "brute")
hitsound = "swing_hit"
+ if(!move_resist)
+ determine_move_resist()
+
+/obj/item/proc/determine_move_resist()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ move_resist = MOVE_FORCE_EXTREMELY_WEAK
+ if(WEIGHT_CLASS_SMALL)
+ move_resist = MOVE_FORCE_VERY_WEAK
+ if(WEIGHT_CLASS_NORMAL)
+ move_resist = MOVE_FORCE_WEAK
+ if(WEIGHT_CLASS_BULKY)
+ move_resist = MOVE_FORCE_NORMAL
+ if(WEIGHT_CLASS_HUGE)
+ move_resist = MOVE_FORCE_NORMAL
+ if(WEIGHT_CLASS_GIGANTIC)
+ move_resist = MOVE_FORCE_NORMAL
/obj/item/Destroy()
flags &= ~DROPDEL //prevent reqdels
@@ -133,10 +144,10 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
return ..()
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
- if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside)))
- return 0
+ if(((src in target) && !target_self) || (!isturf(target.loc) && !isturf(target) && not_inside))
+ return FALSE
else
- return 1
+ return TRUE
/obj/item/blob_act(obj/structure/blob/B)
if(B && B.loc == loc)
@@ -514,7 +525,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
"You stab yourself in the eyes with [src]!" \
)
- add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])")
+ add_attack_logs(user, M, "Eye-stabbed with [src] ([uppertext(user.a_intent)])")
if(istype(H))
var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes)
@@ -606,16 +617,6 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
/obj/item/proc/is_equivalent(obj/item/I)
return I == src
-/obj/item/Crossed(atom/movable/AM, oldloc)
- . = ..()
- if(prob(trip_chance) && ishuman(AM))
- var/mob/living/carbon/human/H = AM
- on_trip(H)
-
-/obj/item/proc/on_trip(mob/living/carbon/human/H)
- if(H.slip(src, trip_stun, trip_weaken, trip_tiles, trip_walksafe, trip_any, trip_verb))
- return TRUE
-
/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm
index 5f5c62abd21..97e46e3abb5 100644
--- a/code/game/objects/items/blueprints.dm
+++ b/code/game/objects/items/blueprints.dm
@@ -221,6 +221,12 @@
A.contents += thing
thing.change_area(old_area, A)
+ var/area/oldA = get_area(get_turf(usr))
+ var/list/firedoors = oldA.firedoors
+ for(var/door in firedoors)
+ var/obj/machinery/door/firedoor/FD = door
+ FD.CalculateAffectingAreas()
+
interact()
area_created = TRUE
return area_created
@@ -236,6 +242,10 @@
return
set_area_machinery_title(A,str,prevname)
A.name = str
+ if(A.firedoors)
+ for(var/D in A.firedoors)
+ var/obj/machinery/door/firedoor/FD = D
+ FD.CalculateAffectingAreas()
to_chat(usr, "You rename the '[prevname]' to '[str]'.")
interact()
return 1
diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm
index 77aef9bb71d..5246652aa0e 100644
--- a/code/game/objects/items/contraband.dm
+++ b/code/game/objects/items/contraband.dm
@@ -56,7 +56,7 @@
desc = "Huh."
allow_wrap = FALSE
-/obj/item/storage/pill_bottle/random_drug_bottle/New()
- ..()
+/obj/item/storage/pill_bottle/random_drug_bottle/Initialize(mapload)
+ . = ..()
for(var/i in 1 to 5)
new /obj/item/reagent_containers/food/pill/random_drugs(src)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index eeb484b9f50..ce52e260bca 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -11,7 +11,7 @@
slot_flags = SLOT_BELT | SLOT_EARS
attack_verb = list("attacked", "coloured")
toolspeed = 1
- var/colour = "#FF0000" //RGB
+ var/colour = COLOR_RED
var/drawtype = "rune"
var/list/graffiti = list("body","amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","up","down","left","right","heart","borgsrogue","voxpox","shitcurity","catbeast","hieroglyphs1","hieroglyphs2","hieroglyphs3","security","syndicate1","syndicate2","nanotrasen","lie","valid","arrowleft","arrowright","arrowup","arrowdown","chicken","hailcrab","brokenheart","peace","scribble","scribble2","scribble3","skrek","squish","tunnelsnake","yip","youaredead")
var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
@@ -123,54 +123,54 @@
/obj/item/toy/crayon/red
icon_state = "crayonred"
- colour = "#DA0000"
+ colour = COLOR_RED
colourName = "red"
/obj/item/toy/crayon/orange
icon_state = "crayonorange"
- colour = "#FF9300"
+ colour = COLOR_ORANGE
colourName = "orange"
/obj/item/toy/crayon/yellow
icon_state = "crayonyellow"
- colour = "#FFF200"
+ colour = COLOR_YELLOW
colourName = "yellow"
/obj/item/toy/crayon/green
icon_state = "crayongreen"
- colour = "#A8E61D"
+ colour = COLOR_GREEN
colourName = "green"
/obj/item/toy/crayon/blue
icon_state = "crayonblue"
- colour = "#00B7EF"
+ colour = COLOR_BLUE
colourName = "blue"
/obj/item/toy/crayon/purple
icon_state = "crayonpurple"
- colour = "#DA00FF"
+ colour = COLOR_PURPLE
colourName = "purple"
/obj/item/toy/crayon/random/New()
icon_state = pick(list("crayonred", "crayonorange", "crayonyellow", "crayongreen", "crayonblue", "crayonpurple"))
switch(icon_state)
if("crayonred")
- colour = "#DA0000"
+ colour = COLOR_RED
colourName = "red"
if("crayonorange")
- colour = "#FF9300"
+ colour = COLOR_ORANGE
colourName = "orange"
if("crayonyellow")
- colour = "#FFF200"
+ colour = COLOR_YELLOW
colourName = "yellow"
if("crayongreen")
- colour = "#A8E61D"
+ colour =COLOR_GREEN
colourName = "green"
if("crayonblue")
- colour = "#00B7EF"
+ colour = COLOR_BLUE
colourName = "blue"
if("crayonpurple")
- colour = "#DA00FF"
+ colour = COLOR_PURPLE
colourName = "purple"
..()
@@ -197,10 +197,10 @@
if(!Adjacent(usr) || usr.incapacitated())
return
if(href_list["color"])
- if(colour != "#FFFFFF")
- colour = "#FFFFFF"
+ if(colour != COLOR_WHITE)
+ colour = COLOR_WHITE
else
- colour = "#000000"
+ colour = COLOR_BLACK
update_window(usr)
else
..()
diff --git a/code/game/objects/items/devices/airlock_painter.dm b/code/game/objects/items/devices/airlock_painter.dm
new file mode 100644
index 00000000000..8f845f2bac6
--- /dev/null
+++ b/code/game/objects/items/devices/airlock_painter.dm
@@ -0,0 +1,81 @@
+// Airlock painter
+
+/obj/item/airlock_painter
+ name = "airlock painter"
+ desc = "An advanced autopainter preprogrammed with several paintjobs for airlocks. Use it on a completed airlock to change its paintjob."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "airlock_painter"
+ item_state = "airlock_painter"
+ flags = CONDUCT | NOBLUDGEON
+ usesound = 'sound/effects/spray2.ogg'
+ w_class = WEIGHT_CLASS_SMALL
+ slot_flags = SLOT_BELT
+ materials = list(MAT_METAL = 3000, MAT_GLASS = 1000)
+ var/paint_setting
+
+ // All the different paint jobs that an airlock painter can apply.
+ // If the airlock you're using it on is glass, the new paint job will also be glass
+ var/list/available_paint_jobs = list(
+ "Atmospherics" = /obj/machinery/door/airlock/atmos,
+ "Command" = /obj/machinery/door/airlock/command,
+ "Engineering" = /obj/machinery/door/airlock/engineering,
+ "External" = /obj/machinery/door/airlock/external,
+ "External Maintenance"= /obj/machinery/door/airlock/maintenance/external,
+ "Freezer" = /obj/machinery/door/airlock/freezer,
+ "Maintenance" = /obj/machinery/door/airlock/maintenance,
+ "Medical" = /obj/machinery/door/airlock/medical,
+ "Mining" = /obj/machinery/door/airlock/mining,
+ "Public" = /obj/machinery/door/airlock/public,
+ "Research" = /obj/machinery/door/airlock/research,
+ "Science" = /obj/machinery/door/airlock/science,
+ "Security" = /obj/machinery/door/airlock/security,
+ "Standard" = /obj/machinery/door/airlock,
+ )
+
+//Only call this if you are certain that the painter will be used right after this check!
+/obj/item/airlock_painter/proc/paint(mob/user)
+ playsound(loc, usesound, 30, TRUE)
+ return TRUE
+
+/obj/item/airlock_painter/attack_self(mob/user)
+ paint_setting = input(user, "Please select a paintjob for this airlock.") as null|anything in available_paint_jobs
+ if(!paint_setting)
+ return
+ to_chat(user, "The [paint_setting] paint setting has been selected.")
+
+/obj/item/airlock_painter/suicide_act(mob/user)
+
+ var/obj/item/organ/internal/lungs/L = user.get_organ_slot("lungs")
+ var/lungs_name = "\improper[L.name]"
+
+ if(L)
+ user.visible_message("[user] is inhaling toner from [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ // Once you've inhaled the toner, you throw up your lungs
+ // and then die.
+
+ // they managed to lose their lungs between then and now. Good job.
+ if(!L)
+ return FALSE
+
+ L.remove(user)
+
+ // make some colorful reagent, and apply it to the lungs
+ L.create_reagents(10)
+ L.reagents.add_reagent("colorful_reagent", 10)
+ L.reagents.reaction(L, REAGENT_TOUCH, 1)
+
+ user.emote("scream")
+ user.visible_message("[user] vomits out [user.p_their()] [lungs_name]!")
+ playsound(user.loc, 'sound/effects/splat.ogg', 50, TRUE)
+
+ // make some vomit under the player, and apply colorful reagent
+ var/obj/effect/decal/cleanable/vomit/V = new(get_turf(user))
+ V.create_reagents(10)
+ V.reagents.add_reagent("colorful_reagent", 10)
+ V.reagents.reaction(V, REAGENT_TOUCH, 1)
+
+ L.forceMove(get_turf(user))
+
+ return OXYLOSS
+ else
+ return SHAME
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 97c5817f05b..95b36194f56 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -241,6 +241,7 @@
user.base_icon = disguise
user.icon_state = disguise
user.cham_proj = src
+ user.bubble_icon = "robot"
active = TRUE
user.update_icons()
@@ -249,6 +250,7 @@
S = user
user.base_icon = initial(user.base_icon)
user.icon_state = initial(user.icon_state)
+ user.bubble_icon = "syndibot"
active = FALSE
user.update_icons()
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 58848f3f7e4..8bfbf22f05d 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -113,7 +113,6 @@
/obj/item/flash/attack(mob/living/M, mob/user)
if(!try_use_flash(user))
return 0
-
if(iscarbon(M))
flash_carbon(M, user, 5, 1)
if(overcharged)
@@ -121,22 +120,12 @@
M.IgniteMob()
burn_out()
return 1
-
else if(issilicon(M))
- if(isrobot(M))
- var/mob/living/silicon/robot/R = M
- if(R.module) // Perhaps they didn't choose a module yet
- for(var/obj/item/borg/combat/shield/S in R.module.modules)
- if(R.activated(S))
- add_attack_logs(user, M, "Flashed with [src]")
- user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!")
- return 1
add_attack_logs(user, M, "Flashed with [src]")
if(M.flash_eyes(affect_silicon = 1))
M.Weaken(rand(5,10))
user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!")
return 1
-
user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!")
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index d9c6d60cf54..e96fceb8b6c 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -167,7 +167,7 @@
// Negative numbers will subtract
/obj/item/lightreplacer/proc/AddUses(amount = 1)
- uses = Clamp(uses + amount, 0, max_uses)
+ uses = clamp(uses + amount, 0, max_uses)
/obj/item/lightreplacer/proc/AddShards(amount = 1, user)
bulb_shards += amount
@@ -191,7 +191,7 @@
if(CanUse(U))
if(!Use(U))
return
- to_chat(U, "You replace [target.fitting] with [src].")
+ to_chat(U, "You replace the light [target.fitting] with [src].")
if(target.status != LIGHT_EMPTY)
AddShards(1, U)
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 941e90a31ef..c6fa7b20c66 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -33,8 +33,6 @@
if(H && H.mind && H.mind.miming)
to_chat(user, "Your vow of silence prevents you from speaking.")
return
- if(H.mind)
- span = H.mind.speech_span
if((COMIC in H.mutations) || H.get_int_organ(/obj/item/organ/internal/cyberimp/brain/clown_voice))
span = "sans"
if(spamcheck)
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index b3695229790..42c134736d7 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -18,13 +18,6 @@
name = "syndicate personal AI device"
faction = list("syndicate")
-/obj/item/paicard/relaymove(var/mob/user, var/direction)
- if(user.stat || user.stunned)
- return
- var/obj/item/rig/rig = get_rig()
- if(istype(rig))
- rig.forced_move(direction, user)
-
/obj/item/paicard/New()
..()
overlays += "pai-off"
diff --git a/code/game/objects/items/devices/pizza_bomb.dm b/code/game/objects/items/devices/pizza_bomb.dm
index c12331b2be2..e5c237938d6 100644
--- a/code/game/objects/items/devices/pizza_bomb.dm
+++ b/code/game/objects/items/devices/pizza_bomb.dm
@@ -27,7 +27,7 @@
desc = "A box suited for pizzas."
icon_state = "pizzabox1"
return
- timer = Clamp(timer, 10, 100)
+ timer = clamp(timer, 10, 100)
icon_state = "pizzabox1"
to_chat(user, "You set the timer to [timer / 10] before activating the payload and closing \the [src].")
message_admins("[key_name_admin(usr)] has set a timer on a pizza bomb to [timer/10] seconds at (JMP).")
@@ -52,7 +52,7 @@
if(disarmed)
visible_message("[bicon(src)] Sparks briefly jump out of the [correct_wire] wire on \the [src], but it's disarmed!")
return
- src.audible_message("[bicon(src)] [src] beeps, \"Enjoy the pizza!\"")
+ atom_say("Enjoy the pizza!")
src.visible_message("\The [src] violently explodes!")
explosion(src.loc,1,2,4,flame_range = 2) //Identical to a minibomb
qdel(src)
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index 083720f0e22..c9d95dd8a99 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -10,8 +10,6 @@
materials = list(MAT_METAL=10000, MAT_GLASS=2500)
var/code = 2
- is_special = 1
-
/obj/item/radio/electropack/attack_hand(mob/user as mob)
if(src == user.back)
to_chat(user, "You need help taking this off!")
@@ -54,23 +52,6 @@
if(src.flags & NODROP)
A.flags |= NODROP
-/obj/item/radio/electropack/Topic(href, href_list)
- if(..())
- return 1
-
- if(href_list["freq"])
- var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"]))
- set_frequency(new_frequency)
-
- else if(href_list["code"])
- code += text2num(href_list["code"])
- code = round(code)
- code = Clamp(code, 1, 100)
-
- else if(href_list["power"])
- on = !on
-
- add_fingerprint(usr)
/obj/item/radio/electropack/receive_signal(datum/signal/signal)
if(!signal || signal.encryption != code)
@@ -96,18 +77,48 @@
return
-/obj/item/radio/electropack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/radio/electropack/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
+ ui = SStgui.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 = new(user, src, ui_key, "Electropack", name, 360, 150, master_ui, state)
ui.open()
- ui.set_auto_update(1)
-
-/obj/item/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/item/radio/electropack/tgui_data(mob/user)
+ var/list/data = list()
data["power"] = on
- data["freq"] = format_frequency(frequency)
+ data["frequency"] = frequency
data["code"] = code
-
+ data["minFrequency"] = PUBLIC_LOW_FREQ
+ data["maxFrequency"] = PUBLIC_HIGH_FREQ
return data
+
+/obj/item/radio/electropack/tgui_act(action, params)
+ if(..())
+ return
+ . = TRUE
+ switch(action)
+ if("power")
+ on = !on
+ if("freq")
+ var/value = params["freq"]
+ if(value)
+ frequency = sanitize_frequency(value)
+ set_frequency(frequency)
+ else
+ . = FALSE
+ if("code")
+ var/value = text2num(params["code"])
+ if(value)
+ value = round(value)
+ code = clamp(value, 1, 100)
+ else
+ . = FALSE
+ if("reset")
+ if(params["reset"] == "freq")
+ frequency = initial(frequency)
+ else if(params["reset"] == "code")
+ code = initial(code)
+ else
+ . = FALSE
+ if(.)
+ add_fingerprint(usr)
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 691c12e1347..d9af70df391 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -9,7 +9,6 @@
"Vox Armalis" = 'icons/mob/species/armalis/ears.dmi'
) //We read you loud and skree-er.
materials = list(MAT_METAL=75)
- subspace_transmission = TRUE
canhear_range = 0 // can't hear headsets from very far away
slot_flags = SLOT_EARS
@@ -91,6 +90,7 @@
ks1type = /obj/item/encryptionkey/syndicate/nukeops
requires_tcomms = FALSE
instant = TRUE // Work instantly if there are no comms
+ freqlock = TRUE
/obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection
name = "syndicate headset"
@@ -289,6 +289,7 @@
icon_state = "com_headset"
item_state = "headset"
ks2type = /obj/item/encryptionkey/ert
+ freqlock = TRUE
/obj/item/radio/headset/ert/alt
name = "\proper emergency response team's bowman headset"
@@ -367,7 +368,7 @@
else
to_chat(user, "This headset doesn't have any encryption keys! How useless...")
-/obj/item/radio/headset/proc/recalculateChannels(var/setDescription = FALSE)
+/obj/item/radio/headset/recalculateChannels(setDescription = FALSE)
channels = list()
translate_binary = FALSE
translate_hive = FALSE
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 13c97f26730..d9d3aa3c3c1 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -46,16 +46,15 @@
name = "station intercom (Security)"
frequency = SEC_I_FREQ
-/obj/item/radio/intercom/New(turf/loc, ndir, building = 3)
- ..()
+/obj/item/radio/intercom/New(turf/loc, direction, building = 3)
+ . = ..()
buildstage = building
if(buildstage)
START_PROCESSING(SSobj, src)
else
- if(ndir)
- pixel_x = (ndir & EAST|WEST) ? (ndir == EAST ? 28 : -28) : 0
- pixel_y = (ndir & NORTH|SOUTH) ? (ndir == NORTH ? 28 : -28) : 0
- dir=ndir
+ if(direction)
+ setDir(direction)
+ set_pixel_offsets_from_dir(28, -28, 28, -28)
b_stat=1
on = 0
GLOB.global_intercoms.Add(src)
@@ -76,7 +75,6 @@
name = "illicit intercom"
desc = "Talk through this. Evilly"
frequency = SYND_FREQ
- subspace_transmission = TRUE
syndiekey = new /obj/item/encryptionkey/syndicate/nukeops
/obj/item/radio/intercom/syndicate/New()
@@ -86,7 +84,6 @@
/obj/item/radio/intercom/pirate
name = "pirate radio intercom"
desc = "You wouldn't steal a space shuttle. Piracy. It's a crime!"
- subspace_transmission = 1
/obj/item/radio/intercom/pirate/New()
..()
@@ -110,13 +107,13 @@
GLOB.global_intercoms.Remove(src)
return ..()
-/obj/item/radio/intercom/attack_ai(mob/user as mob)
+/obj/item/radio/intercom/attack_ai(mob/user)
add_hiddenprint(user)
add_fingerprint(user)
spawn(0)
attack_self(user)
-/obj/item/radio/intercom/attack_hand(mob/user as mob)
+/obj/item/radio/intercom/attack_hand(mob/user)
add_fingerprint(user)
spawn(0)
attack_self(user)
@@ -187,10 +184,10 @@
update_icon()
START_PROCESSING(SSobj, src)
for(var/i, i<= 5, i++)
- wires.UpdateCut(i,1)
+ wires.on_cut(i, 1)
/obj/item/radio/intercom/wirecutter_act(mob/user, obj/item/I)
- if(!(buildstage == 3 && b_stat && wires.IsAllCut()))
+ if(!(buildstage == 3 && b_stat && wires.is_all_cut()))
return
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
@@ -204,7 +201,7 @@
STOP_PROCESSING(SSobj, src)
/obj/item/radio/intercom/welder_act(mob/user, obj/item/I)
- if(!buildstage)
+ if(buildstage != 0)
return
. = TRUE
if(!I.tool_use_check(user, 3))
@@ -271,4 +268,4 @@
/obj/item/radio/intercom/locked/prison/New()
..()
- wires.CutWireIndex(RADIO_WIRE_TRANSMIT)
+ wires.cut(WIRE_RADIO_TRANSMIT)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 4c2aaa82c1e..cdcc2361937 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -26,21 +26,37 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
suffix = "\[3\]"
icon_state = "walkietalkie"
item_state = "walkietalkie"
- var/on = 1 // 0 for off
+ /// boolean for radio enabled or not
+ var/on = TRUE
var/last_transmission
- var/frequency = PUB_FREQ //common chat
- var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies
- var/canhear_range = 3 // the range which mobs can hear this radio from
+ var/frequency = PUB_FREQ
+ /// tune to frequency to unlock traitor supplies
+ var/traitor_frequency = 0
+ /// the range which mobs can hear this radio from
+ var/canhear_range = 3
var/datum/wires/radio/wires = null
var/b_stat = 0
- var/broadcasting = 0
- var/listening = 1
- var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h
- var/subspace_transmission = 0
- var/obj/item/encryptionkey/syndicate/syndiekey = null //Holder for the syndicate encryption key if present
- var/disable_timer = 0 //How many times this is disabled by EMPs
- var/is_special = 0 //For electropacks mostly, skips Topic() checks
+ /// Whether the radio will broadcast stuff it hears, out over the radio
+ var/broadcasting = FALSE
+ /// Whether the radio is currently receiving
+ var/listening = TRUE
+ /// Whether the radio can be re-tuned to restricted channels it has no key for
+ var/freerange = FALSE
+ /// Whether the radio is able to have its primary frequency changed. Used for radios with weird primary frequencies, like DS, syndi, etc
+ var/freqlock = FALSE
+
+ /// Whether the radio broadcasts to everyone within a few tiles, or not
+ var/loudspeaker = FALSE
+ /// Whether loudspeaker can be toggled by the user
+ var/has_loudspeaker = FALSE
+
+ /// see communications.dm for full list. First channes is a "default" for :h
+ var/list/channels = list()
+ /// Holder for the syndicate encryption key if present
+ var/obj/item/encryptionkey/syndicate/syndiekey = null
+ /// How many times this is disabled by EMPs
+ var/disable_timer = 0
flags = CONDUCT
slot_flags = SLOT_BELT
@@ -76,6 +92,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
GLOB.global_radios |= src
/obj/item/radio/Destroy()
+ SStgui.close_uis(wires)
QDEL_NULL(wires)
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -99,47 +116,87 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
/obj/item/radio/attack_ghost(mob/user)
return interact(user)
-/obj/item/radio/attack_self(mob/user as mob)
- user.set_machine(src)
- interact(user)
+/obj/item/radio/attack_self(mob/user)
+ tgui_interact(user)
/obj/item/radio/interact(mob/user)
if(!user)
return 0
-
if(b_stat)
wires.Interact(user)
+ tgui_interact(user)
- return ui_interact(user)
-
-/obj/item/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/radio/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.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 = new(user, src, ui_key, "Radio", name, 360, 150 + (length(channels) * 20), master_ui, state)
ui.open()
- ui.set_auto_update(1)
-/obj/item/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
+/obj/item/radio/tgui_data(mob/user)
+ var/list/data = list()
- data["mic_status"] = broadcasting
- data["speaker"] = listening
- data["freq"] = format_frequency(frequency)
- data["rawfreq"] = num2text(frequency)
-
- data["mic_cut"] = (wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
- data["spk_cut"] = (wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
-
- var/list/chanlist = list_channels(user)
- if(islist(chanlist) && chanlist.len)
- data["chan_list"] = chanlist
- data["chan_list_len"] = chanlist.len
-
- if(syndiekey)
- data["useSyndMode"] = 1
+ data["broadcasting"] = broadcasting
+ data["listening"] = listening
+ data["frequency"] = frequency
+ data["minFrequency"] = freerange ? RADIO_LOW_FREQ : PUBLIC_LOW_FREQ
+ data["maxFrequency"] = freerange ? RADIO_HIGH_FREQ : PUBLIC_HIGH_FREQ
+ data["canReset"] = frequency == initial(frequency) ? FALSE : TRUE
+ data["freqlock"] = freqlock
+ data["channels"] = list()
+ for(var/channel in channels)
+ data["channels"][channel] = channels[channel] & FREQ_LISTENING
+ data["has_loudspeaker"] = has_loudspeaker
+ data["loudspeaker"] = loudspeaker
return data
+/obj/item/radio/tgui_act(action, params, datum/tgui/ui)
+ if(..())
+ return
+ . = TRUE
+ switch(action)
+ if("frequency")
+ if(freqlock)
+ return
+ var/tune = params["tune"]
+ var/adjust = text2num(params["adjust"])
+ if(tune == "reset")
+ tune = initial(frequency)
+ else if(adjust)
+ tune = frequency + adjust * 10
+ else if(text2num(tune) != null)
+ tune = tune * 10
+ else
+ . = FALSE
+ if(hidden_uplink)
+ if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency))
+ usr << browse(null, "window=radio")
+ if(.)
+ set_frequency(sanitize_frequency(tune, freerange))
+ if("listen")
+ listening = !listening
+ if("broadcast")
+ broadcasting = !broadcasting
+ if("channel")
+ var/channel = params["channel"]
+ if(!(channel in channels))
+ return
+ if(channels[channel] & FREQ_LISTENING)
+ channels[channel] &= ~FREQ_LISTENING
+ else
+ channels[channel] |= FREQ_LISTENING
+ if("loudspeaker")
+ // Toggle loudspeaker mode, AKA everyone around you hearing your radio.
+ if(has_loudspeaker)
+ loudspeaker = !loudspeaker
+ if(loudspeaker)
+ canhear_range = 3
+ else
+ canhear_range = 0
+ else
+ . = FALSE
+ if(.)
+ add_fingerprint(usr)
/obj/item/radio/proc/list_channels(var/mob/user)
return list_internal_channels(user)
@@ -184,57 +241,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
return can_admin_interact()
/obj/item/radio/proc/ToggleBroadcast()
- broadcasting = !broadcasting && !(wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
+ broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL))
/obj/item/radio/proc/ToggleReception()
- listening = !listening && !(wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL))
-
-/obj/item/radio/Topic(href, href_list)
- if(..())
- return 1
-
- if(is_special)
- return 0
-
- if(href_list["track"])
- var/mob/target = locate(href_list["track"])
- var/mob/living/silicon/ai/A = locate(href_list["track2"])
- if(A && target)
- A.ai_actual_track(target)
- . = 1
-
- else if(href_list["freq"])
- var/new_frequency = (frequency + text2num(href_list["freq"]))
- if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ))
- new_frequency = sanitize_frequency(new_frequency)
- set_frequency(new_frequency)
- if(hidden_uplink)
- if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency))
- usr << browse(null, "window=radio")
- . = 1
- else if(href_list["talk"])
- ToggleBroadcast()
- . = 1
- else if(href_list["listen"])
- var/chan_name = href_list["ch_name"]
- if(!chan_name)
- ToggleReception()
- else
- if(channels[chan_name] & FREQ_LISTENING)
- channels[chan_name] &= ~FREQ_LISTENING
- else
- channels[chan_name] |= FREQ_LISTENING
- . = 1
- else if(href_list["spec_freq"])
- var freq = href_list["spec_freq"]
- if(has_channel_access(usr, freq))
- set_frequency(text2num(freq))
- . = 1
-
- if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk
- return 1
-
- add_fingerprint(usr)
+ listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL))
/obj/item/radio/proc/autosay(message, from, channel, role = "Unknown") //BS12 EDIT
var/datum/radio_frequency/connection = null
@@ -272,7 +282,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
tcm.sender_job = "Automated Announcement"
tcm.vname = "synthesized voice"
tcm.data = SIGNALTYPE_AINOTRACK
- // Datum radios dont have a location (obviously
+ // Datum radios dont have a location (obviously)
if(loc && loc.z)
tcm.source_level = loc.z // For anyone that reads this: This used to pull from a LIST from the CONFIG DATUM. WHYYYYYYYYY!!!!!!!! -aa
else
@@ -295,7 +305,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
universal_speak = 1
/mob/living/automatedannouncer/New()
- lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE)
+ lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), 10 SECONDS, TIMER_STOPPABLE)
..()
/mob/living/automatedannouncer/Destroy()
@@ -325,7 +335,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
// If we were to send to a channel we don't have, drop it.
return RADIO_CONNECTION_FAIL
-/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says")
+/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, verbage = "says")
if(!on)
return 0 // the device has to be on
// Fix for permacell radios, but kinda eh about actually fixing them.
@@ -334,7 +344,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
// Uncommenting this. To the above comment:
// The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom
- if(wires.IsIndexCut(RADIO_WIRE_TRANSMIT)) // The device has to have all its wires and shit intact
+ if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact
return 0
if(!M.IsVocal())
@@ -411,11 +421,16 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
jobname = "Unknown"
voicemask = TRUE
+ // Copy the message pieces so we can safely edit comms line without affecting the actual line
+ var/list/message_pieces_copy = list()
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ message_pieces_copy += new /datum/multilingual_say_piece(S.speaking, S.message)
+
// Make us a message datum!
var/datum/tcomms_message/tcm = new
tcm.sender_name = displayname
tcm.sender_job = jobname
- tcm.message_pieces = message_pieces
+ tcm.message_pieces = message_pieces_copy
tcm.source_level = position.z
tcm.freq = connection.frequency
tcm.vmask = voicemask
@@ -423,6 +438,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
tcm.connection = connection
tcm.vname = M.voice_name
tcm.sender = M
+ tcm.verbage = verbage
// Now put that through the stuff
var/handled = FALSE
if(connection)
@@ -509,7 +525,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
var/is_listening = TRUE
if(!on)
is_listening = FALSE
- if(!wires || wires.IsIndexCut(RADIO_WIRE_RECEIVE))
+ if(!wires || wires.is_cut(WIRE_RADIO_RECEIVER))
is_listening = FALSE
if(!listening)
is_listening = FALSE
@@ -575,19 +591,24 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
if(!disable_timer)
on = 1
+/obj/item/radio/proc/recalculateChannels()
+ /// Exists so that borg radios and headsets can override it.
+ stack_trace("recalculateChannels() called on a radio which does not implement the proc.")
+
///////////////////////////////
//////////Borg Radios//////////
///////////////////////////////
//Giving borgs their own radio to have some more room to work with -Sieve
/obj/item/radio/borg
+ name = "Cyborg Radio"
var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks
var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key
- var/shut_up = 1
icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component.
icon_state = "radio"
+ has_loudspeaker = TRUE
+ loudspeaker = FALSE
canhear_range = 0
- subspace_transmission = 1
dog_fashion = null
/obj/item/radio/borg/syndicate
@@ -609,12 +630,14 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
..()
syndiekey = keyslot
set_frequency(SYND_FREQ)
+ freqlock = TRUE
/obj/item/radio/borg/deathsquad
/obj/item/radio/borg/deathsquad/New()
..()
set_frequency(DTH_FREQ)
+ freqlock = TRUE
/obj/item/radio/borg/ert
keyslot = new /obj/item/encryptionkey/ert
@@ -622,6 +645,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
/obj/item/radio/borg/ert/New()
..()
set_frequency(ERT_FREQ)
+ freqlock = TRUE
+
+/obj/item/radio/borg/ert/specops
+ keyslot = new /obj/item/encryptionkey/centcom
/obj/item/radio/borg/attackby(obj/item/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/encryptionkey/))
@@ -663,7 +690,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
else
to_chat(user, "This radio doesn't have any encryption keys!")
-/obj/item/radio/borg/proc/recalculateChannels()
+/obj/item/radio/borg/recalculateChannels()
channels = list()
syndiekey = null
@@ -696,72 +723,12 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
return
-/obj/item/radio/borg/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["mode"])
- var/enable_subspace_transmission = text2num(href_list["mode"])
- if(enable_subspace_transmission != subspace_transmission)
- subspace_transmission = !subspace_transmission
- if(subspace_transmission)
- to_chat(usr, "Subspace Transmission is enabled.")
- else
- to_chat(usr, "Subspace Transmission is disabled.")
- if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled
- channels = list()
- else
- recalculateChannels()
- . = 1
- if(href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio.
- var/do_shut_up = text2num(href_list["shutup"])
- if(do_shut_up != shut_up)
- shut_up = !shut_up
- if(shut_up)
- canhear_range = 0
- to_chat(usr, "Loudspeaker disabled.")
- else
- canhear_range = 3
- to_chat(usr, "Loudspeaker enabled.")
- . = 1
-
-
-/obj/item/radio/borg/interact(mob/user as mob)
+/obj/item/radio/borg/interact(mob/user)
if(!on)
return
-
. = ..()
-/obj/item/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- 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()
- ui.set_auto_update(1)
-
-/obj/item/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
-
- data["mic_status"] = broadcasting
- data["speaker"] = listening
- data["freq"] = format_frequency(frequency)
- data["rawfreq"] = num2text(frequency)
-
- var/list/chanlist = list_channels(user)
- if(islist(chanlist) && chanlist.len)
- data["chan_list"] = chanlist
- data["chan_list_len"] = chanlist.len
-
- if(syndiekey)
- data["useSyndMode"] = 1
-
- data["has_loudspeaker"] = 1
- data["loudspeaker"] = !shut_up
- data["has_subspace"] = 1
- data["subspace"] = subspace_transmission
-
- return data
-
/obj/item/radio/proc/config(op)
if(SSradio)
for(var/ch_name in channels)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 0d8873a6377..43697ac4776 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -72,7 +72,7 @@ REAGENT SCANNER
var/turf/U = O.loc
if(U && U.intact)
O.invisibility = 101
- O.alpha = 255
+ O.alpha = 255
for(var/mob/living/M in T.contents)
var/oldalpha = M.alpha
if(M.alpha < 255 && istype(M))
@@ -141,7 +141,7 @@ REAGENT SCANNER
// Used by the PDA medical scanner too
/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE)
- if(!ishuman(M) || M.isSynthetic())
+ if(!ishuman(M) || ismachineperson(M))
//these sensors are designed for organic life
to_chat(user, "Analyzing Results for ERROR:\n\t Overall Status: ERROR")
to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute")
diff --git a/code/game/objects/items/devices/sensor_device.dm b/code/game/objects/items/devices/sensor_device.dm
index 8c69402ed70..9133bf37b9c 100644
--- a/code/game/objects/items/devices/sensor_device.dm
+++ b/code/game/objects/items/devices/sensor_device.dm
@@ -6,7 +6,7 @@
w_class = WEIGHT_CLASS_SMALL
slot_flags = SLOT_BELT
origin_tech = "programming=3;materials=3;magnets=3"
- var/datum/nano_module/crew_monitor/crew_monitor
+ var/datum/tgui_module/crew_monitor/crew_monitor
/obj/item/sensor_device/New()
..()
@@ -17,7 +17,7 @@
return ..()
/obj/item/sensor_device/attack_self(mob/user as mob)
- ui_interact(user)
+ tgui_interact(user)
-/obj/item/sensor_device/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- crew_monitor.ui_interact(user, ui_key, ui, force_open)
+/obj/item/sensor_device/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ crew_monitor.tgui_interact(user, ui_key, ui, force_open)
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 1c082ce60aa..1eba4e54056 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -86,64 +86,64 @@
O.hear_message(M, msg)
/obj/item/transfer_valve/attack_self(mob/user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/item/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/transfer_valve/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "transfer_valve.tmpl", "Tank Transfer Valve", 460, 280)
- // open the new ui window
+ ui = new(user, src, ui_key, "TransferValve", name, 460, 320, master_ui, state)
ui.open()
- // auto update every Master Controller tick
- //ui.set_auto_update(1)
-
-/obj/item/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
-
- data["attachmentOne"] = tank_one ? tank_one.name : null
- data["attachmentTwo"] = tank_two ? tank_two.name : null
- data["valveAttachment"] = attached_device ? attached_device.name : null
- data["valveOpen"] = valve_open ? 1 : 0
+/obj/item/transfer_valve/tgui_data(mob/user)
+ var/list/data = list()
+ data["tank_one"] = tank_one ? tank_one.name : null
+ data["tank_two"] = tank_two ? tank_two.name : null
+ data["attached_device"] = attached_device ? attached_device.name : null
+ data["valve"] = valve_open
return data
-/obj/item/transfer_valve/Topic(href, href_list)
- ..()
- if(usr.incapacitated())
- return 0
- if(loc != usr)
- return 0
- if(tank_one && href_list["tankone"])
- split_gases()
- valve_open = 0
- tank_one.forceMove(get_turf(src))
- tank_one = null
+
+
+/obj/item/transfer_valve/tgui_act(action, params)
+ if(..())
+ return
+ . = TRUE
+ switch(action)
+ if("tankone")
+ if(tank_one)
+ split_gases()
+ valve_open = FALSE
+ tank_one.forceMove(get_turf(src))
+ tank_one = null
+ update_icon()
+ if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL))
+ w_class = WEIGHT_CLASS_NORMAL
+ if("tanktwo")
+ if(tank_two)
+ split_gases()
+ valve_open = FALSE
+ tank_two.forceMove(get_turf(src))
+ tank_two = null
+ update_icon()
+ if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL))
+ w_class = WEIGHT_CLASS_NORMAL
+ if("toggle")
+ toggle_valve(usr)
+ if("device")
+ if(attached_device)
+ attached_device.attack_self(usr)
+ if("remove_device")
+ if(attached_device)
+ attached_device.forceMove(get_turf(src))
+ attached_device.holder = null
+ attached_device = null
+ update_icon()
+ else
+ . = FALSE
+ if(.)
update_icon()
- if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL))
- w_class = WEIGHT_CLASS_NORMAL
- else if(tank_two && href_list["tanktwo"])
- split_gases()
- valve_open = 0
- tank_two.forceMove(get_turf(src))
- tank_two = null
- update_icon()
- if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL))
- w_class = WEIGHT_CLASS_NORMAL
- else if(href_list["open"])
- toggle_valve(usr)
- else if(attached_device)
- if(href_list["rem_device"])
- attached_device.forceMove(get_turf(src))
- attached_device.holder = null
- attached_device = null
- update_icon()
- if(href_list["device"])
- attached_device.attack_self(usr)
- add_fingerprint(usr)
- return 1 // Returning 1 sends an update to attached UIs
+ add_fingerprint(usr)
+
/obj/item/transfer_valve/proc/process_activation(obj/item/D)
if(toggle)
diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm
index c5c8f67e34d..77e9eacc6a1 100644
--- a/code/game/objects/items/devices/uplinks.dm
+++ b/code/game/objects/items/devices/uplinks.dm
@@ -131,7 +131,6 @@ GLOBAL_LIST_EMPTY(world_uplinks)
else
var/datum/uplink_item/UI = ItemsReference[href_list["buy_item"]]
return buy(UI, UI ? UI.reference : "")
- return 0
/obj/item/uplink/proc/buy(var/datum/uplink_item/UI, var/reference)
if(!UI)
diff --git a/code/game/objects/items/flag.dm b/code/game/objects/items/flag.dm
index a442a69843d..0dda06d3e5c 100644
--- a/code/game/objects/items/flag.dm
+++ b/code/game/objects/items/flag.dm
@@ -251,6 +251,7 @@
log_game("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).")
investigate_log("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB)
add_attack_logs(user, src, "has lit (booby trapped with [boobytrap]", ATKLOG_FEW)
+ burn()
else
return ..()
@@ -267,8 +268,16 @@
/obj/item/flag/chameleon/burn()
if(boobytrap)
- boobytrap.prime()
- ..()
+ fire_act()
+ addtimer(CALLBACK(src, .proc/prime_boobytrap), boobytrap.det_time)
+ else
+ ..()
+
+/obj/item/flag/chameleon/proc/prime_boobytrap()
+ boobytrap.forceMove(get_turf(loc))
+ boobytrap.prime()
+ boobytrap = null
+ burn()
/obj/item/flag/chameleon/updateFlagIcon()
icon_state = updated_icon_state
diff --git a/code/game/objects/items/mixing_bowl.dm b/code/game/objects/items/mixing_bowl.dm
index da851437fd5..1e1a39d49d0 100644
--- a/code/game/objects/items/mixing_bowl.dm
+++ b/code/game/objects/items/mixing_bowl.dm
@@ -118,7 +118,7 @@
/obj/item/mixing_bowl/Topic(href, href_list)
if(..())
return
- if("dispose")
+ if(href_list["dispose"])
dispose()
return
diff --git a/code/game/objects/items/mountable_frames/fire_alarm.dm b/code/game/objects/items/mountable_frames/fire_alarm.dm
index 83f435a277c..22c526664aa 100644
--- a/code/game/objects/items/mountable_frames/fire_alarm.dm
+++ b/code/game/objects/items/mountable_frames/fire_alarm.dm
@@ -6,5 +6,5 @@
mount_reqs = list("simfloor", "nospace")
/obj/item/mounted/frame/firealarm/do_build(turf/on_wall, mob/user)
- new /obj/machinery/firealarm(get_turf(src), get_dir(on_wall, user), 1)
+ new /obj/machinery/firealarm(get_turf(src), get_dir(user, on_wall), 1)
qdel(src)
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index eae59151a38..f1487a09986 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -29,7 +29,7 @@
"[user] has prodded you with [src]!")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
- add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])")
+ add_attack_logs(user, M, "Stunned with [src] ([uppertext(user.a_intent)])")
/obj/item/borg/overdrive
name = "Overdrive"
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 46bc15a2ffe..ca2fb17c004 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -253,7 +253,16 @@
to_chat(user, "This [W] does not seem to fit.")
return
- var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc), unfinished = 1)
+ var/datum/ai_laws/laws_to_give
+ if(M.syndiemmi)
+ aisync = FALSE
+ lawsync = FALSE
+ laws_to_give = new /datum/ai_laws/syndicate_override
+
+ if(!aisync)
+ lawsync = FALSE
+
+ var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc), unfinished = 1, ai_to_sync_to = forced_ai)
if(!O)
return
@@ -263,24 +272,15 @@
if(istype(task))
task.unit_completed()
- if(M.syndiemmi)
- aisync = 0
- lawsync = 0
- O.laws = new /datum/ai_laws/syndicate_override
-
O.invisibility = 0
//Transfer debug settings to new mob
O.custom_name = created_name
O.rename_character(O.real_name, O.get_default_name())
O.locked = panel_locked
- if(!aisync)
- lawsync = 0
- O.connected_ai = null
- else
- O.notify_ai(1)
- if(forced_ai)
- O.connected_ai = forced_ai
- if(!lawsync && !M.syndiemmi)
+
+ if(laws_to_give)
+ O.laws = laws_to_give
+ else if(!lawsync)
O.lawupdate = 0
O.make_laws()
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 43178a85ef8..2d2b110fa06 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -9,23 +9,23 @@
origin_tech = "programming=2"
var/locked = 0
var/installed = 0
- var/require_module = 0
+ var/require_module = FALSE
var/module_type = null
/obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R)
if(R.stat == DEAD)
to_chat(usr, "[src] will not function on a deceased cyborg.")
- return 1
+ return TRUE
if(module_type && !istype(R.module, module_type))
to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!")
to_chat(usr, "There's no mounting point for the module!")
- return 1
+ return TRUE
/obj/item/borg/upgrade/reset
name = "cyborg module reset board"
desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the cyborg."
icon_state = "cyborg_upgrade1"
- require_module = 1
+ require_module = TRUE
/obj/item/borg/upgrade/reset/action(mob/living/silicon/robot/R)
if(..())
@@ -48,14 +48,14 @@
if(..())
return
if(!R.allow_rename)
- to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.");
+ to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.")
return 0
R.notify_ai(3, R.name, heldname)
R.name = heldname
R.custom_name = heldname
R.real_name = heldname
- return 1
+ return TRUE
/obj/item/borg/upgrade/restart
name = "cyborg emergency reboot module"
@@ -77,14 +77,14 @@
GLOB.alive_mob_list += R
R.notify_ai(1)
- return 1
+ return TRUE
/obj/item/borg/upgrade/vtec
name = "robotic VTEC Module"
desc = "Used to kick in a robot's VTEC systems, increasing their speed."
icon_state = "cyborg_upgrade2"
- require_module = 1
+ require_module = TRUE
origin_tech = "engineering=4;materials=5;programming=4"
/obj/item/borg/upgrade/vtec/action(var/mob/living/silicon/robot/R)
@@ -97,14 +97,14 @@
R.speed = -1 // Gotta go fast.
- return 1
+ return TRUE
/obj/item/borg/upgrade/disablercooler
name = "cyborg rapid disabler cooling module"
desc = "Used to cool a mounted disabler, increasing the potential current in it and thus its recharge rate."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;powerstorage=4;combat=4"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/security
/obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R)
@@ -122,7 +122,7 @@
T.charge_delay = max(2 , T.charge_delay - 4)
- return 1
+ return TRUE
/obj/item/borg/upgrade/thrusters
name = "ion thruster upgrade"
@@ -139,14 +139,14 @@
return
R.ionpulse = 1
- return 1
+ return TRUE
/obj/item/borg/upgrade/ddrill
name = "mining cyborg diamond drill"
desc = "A diamond drill replacement for the mining module's standard drill."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;materials=5"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/miner
/obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R)
@@ -161,14 +161,14 @@
R.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(R.module)
R.module.rebuild()
- return 1
+ return TRUE
/obj/item/borg/upgrade/soh
name = "mining cyborg satchel of holding"
desc = "A satchel of holding replacement for mining cyborg's ore satchel module."
icon_state = "cyborg_upgrade3"
origin_tech = "engineering=4;materials=4;bluespace=4"
- require_module = 1
+ require_module = TRUE
module_type = /obj/item/robot_module/miner
/obj/item/borg/upgrade/soh/action(mob/living/silicon/robot/R)
@@ -181,35 +181,46 @@
R.module.modules += new /obj/item/storage/bag/ore/holding(R.module)
R.module.rebuild()
- return 1
+ return TRUE
/obj/item/borg/upgrade/syndicate
- name = "illegal equipment module"
+ name = "safety override module"
desc = "Unlocks the hidden, deadlier functions of a cyborg. Also prevents emag subversion."
icon_state = "cyborg_upgrade3"
origin_tech = "combat=4;syndicate=1"
- require_module = 1
+ require_module = TRUE
/obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/R)
if(..())
return
-
if(R.emagged)
return
-
if(R.weapons_unlock)
- to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.");
+ to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.")
return
-
R.emagged = 1
+ return TRUE
- return 1
+/obj/item/borg/upgrade/lavaproof
+ name = "mining cyborg lavaproof chassis"
+ desc = "An upgrade kit to apply specialized coolant systems and insulation layers to a mining cyborg's chassis, enabling them to withstand exposure to molten rock."
+ icon_state = "ash_plating"
+ resistance_flags = LAVA_PROOF | FIRE_PROOF
+ require_module = TRUE
+ module_type = /obj/item/robot_module/miner
+
+/obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R)
+ if(..())
+ return
+ if(istype(R))
+ R.weather_immunities += "lava"
+ return TRUE
/obj/item/borg/upgrade/selfrepair
name = "self-repair module"
desc = "This module will repair the cyborg over time."
icon_state = "cyborg_upgrade5"
- require_module = 1
+ require_module = TRUE
var/repair_amount = -1
var/repair_tick = 1
var/msg_cooldown = 0
@@ -230,7 +241,7 @@
icon_state = "selfrepair_off"
var/datum/action/A = new /datum/action/item_action/toggle(src)
A.Grant(R)
- return 1
+ return TRUE
/obj/item/borg/upgrade/selfrepair/Destroy()
cyborg = null
diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm
index 8b85d8821d9..9b24b1efce6 100644
--- a/code/game/objects/items/shooting_range.dm
+++ b/code/game/objects/items/shooting_range.dm
@@ -4,24 +4,23 @@
desc = "A shooting target."
icon = 'icons/obj/objects.dmi'
icon_state = "target_h"
- density = 0
+ density = FALSE
var/hp = 1800
- var/icon/virtualIcon
- var/list/bulletholes = list()
/obj/item/target/Destroy()
+ cut_overlays()
// if a target is deleted and associated with a stake, force stake to forget
- for(var/obj/structure/target_stake/T in view(3,src))
+ for(var/obj/structure/target_stake/T in view(3, src))
if(T.pinned_target == src)
T.pinned_target = null
- T.density = 1
+ T.density = TRUE
break
return ..() // delete target
/obj/item/target/Move()
..()
// After target moves, check for nearby stakes. If associated, move to target
- for(var/obj/structure/target_stake/M in view(3,src))
+ for(var/obj/structure/target_stake/M in view(3, src))
if(M.density == 0 && M.pinned_target == src)
M.loc = loc
@@ -33,12 +32,12 @@
/obj/item/target/welder_act(mob/user, obj/item/I)
. = TRUE
- if(!use_tool(src, user, 0,, volume = I.tool_volume))
+ if(!use_tool(src, user, 0, volume = I.tool_volume))
return
overlays.Cut()
- to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.")
+ to_chat(user, "You slice off [src]'s uneven chunks of aluminium and scorch marks.")
-/obj/item/target/attack_hand(mob/user as mob)
+/obj/item/target/attack_hand(mob/user)
// taking pinned targets off!
var/obj/structure/target_stake/stake
for(var/obj/structure/target_stake/T in view(3,src))
@@ -48,8 +47,8 @@
if(stake)
if(stake.pinned_target)
- stake.density = 1
- density = 0
+ stake.density = TRUE
+ density = FALSE
layer = OBJ_LAYER
loc = user.loc
@@ -77,101 +76,37 @@
desc = "A shooting target that looks like a xenomorphic alien."
hp = 2350 // alium onest too kinda
-/obj/item/target/bullet_act(var/obj/item/projectile/Proj)
- var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!"
- var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1)
- var/decaltype = 1 // 1 - scorch, 2 - bullet
+#define DECALTYPE_SCORCH 1
+#define DECALTYPE_BULLET 2
- if(istype(/obj/item/projectile/bullet, Proj))
- decaltype = 2
+/obj/item/target/bullet_act(obj/item/projectile/P)
+ var/p_x = P.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset P.p_x!"
+ var/p_y = P.p_y + pick(0,0,0,0,0,-1,1)
+ var/decaltype = DECALTYPE_SCORCH
+ if(istype(P, /obj/item/projectile/bullet))
+ decaltype = DECALTYPE_BULLET
-
- virtualIcon = new(icon, icon_state)
-
- if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null)
-
- hp -= Proj.damage
+ var/icon/C = icon(icon, icon_state)
+ if(LAZYLEN(overlays) <= 35 && C.GetPixel(p_x, p_y)) // if the located pixel isn't blank (null)
+ hp -= P.damage
if(hp <= 0)
- visible_message("[src] breaks into tiny pieces and collapses!")
+ visible_message("[src] breaks into tiny pieces and collapses!")
qdel(src)
-
- // Create a temporary object to represent the damage
- var/obj/bmark = new
- bmark.pixel_x = p_x
- bmark.pixel_y = p_y
- bmark.icon = 'icons/effects/effects.dmi'
- bmark.layer = 3.5
- bmark.icon_state = "scorch"
-
- if(decaltype == 1)
- // Energy weapons are hot. they scorch!
-
- // offset correction
- bmark.pixel_x--
- bmark.pixel_y--
-
- if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice))
- bmark.icon_state = "scorch"
- bmark.dir = pick(NORTH,SOUTH,EAST,WEST) // random scorch design
-
-
+ return
+ var/image/bullet_hole = image('icons/effects/effects.dmi', "scorch", OBJ_LAYER + 0.5)
+ bullet_hole.pixel_x = p_x - 1 //offset correction
+ bullet_hole.pixel_y = p_y - 1
+ if(decaltype == DECALTYPE_SCORCH)
+ if(P.damage >= 20 || istype(P, /obj/item/projectile/beam/practice))
+ bullet_hole.setDir(pick(NORTH,SOUTH,EAST,WEST))// random scorch design. light_scorch does not have different directions
else
- bmark.icon_state = "light_scorch"
+ bullet_hole.icon_state = "light_scorch"
else
-
- // Bullets are hard. They make dents!
- bmark.icon_state = "dent"
-
- if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes
- if(decaltype == 2) // bullet
- if(prob(Proj.damage+30)) // bullets make holes more commonly!
- new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole
- else // Lasers!
- if(prob(Proj.damage-10)) // lasers make holes less commonly
- new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole
-
- // draw bullet holes
- for(var/datum/bullethole/B in bulletholes)
-
- virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right
- virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom
-
- overlays += bmark // add the decal
-
- icon = virtualIcon // apply bulletholes over decals
-
+ bullet_hole.icon_state = "dent"
+ add_overlay(bullet_hole)
return
return -1 // the bullet/projectile goes through the target! Ie, you missed
-
-// Small memory holder entity for transparent bullet holes
-/datum/bullethole
- // First box
- var/b1x1 = 0
- var/b1x2 = 0
- var/b1y = 0
-
- // Second box
- var/b2x = 0
- var/b2y1 = 0
- var/b2y2 = 0
-
-/datum/bullethole/New(obj/item/target/Target, pixel_x = 0, pixel_y = 0)
- if(!Target) return
-
- // Randomize the first box
- b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4)
- b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4)
- b1y = pixel_y
- if(prob(35))
- b1y += rand(-4,4)
-
- // Randomize the second box
- b2x = pixel_x
- if(prob(35))
- b2x += rand(-4,4)
- b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4)
- b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4)
-
- Target.bulletholes.Add(src)
+#undef DECALTYPE_SCORCH
+#undef DECALTYPE_BULLET
diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm
index 708070e1478..ff469ba3c59 100644
--- a/code/game/objects/items/stacks/nanopaste.dm
+++ b/code/game/objects/items/stacks/nanopaste.dm
@@ -57,7 +57,7 @@
E.heal_damage(0, remheal, 0, 1) //Healing Burn
remheal = nremheal
user.visible_message("\The [user] applies some nanite paste at \the [M]'s [E.name] with \the [src].")
- if(H.bleed_rate && H.isSynthetic())
+ if(H.bleed_rate && ismachineperson(H))
H.bleed_rate = 0
else
to_chat(user, "Nothing to fix here.")
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 4d3e3181726..000ad2d225f 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -17,9 +17,9 @@
GLOBAL_LIST_INIT(glass_recipes, list ( \
new/datum/stack_recipe/window("directional window", /obj/structure/window/basic, time = 0, on_floor = TRUE, window_checks = TRUE), \
new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/basic, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \
- new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 0), \
- new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 0, on_floor = TRUE), \
- new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 0, on_floor = TRUE) \
+ new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 10), \
+ new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 20, on_floor = TRUE), \
+ new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 40, on_floor = TRUE) \
))
/obj/item/stack/sheet/glass
diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm
index 5fc457dae62..9f6844c8b9e 100644
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ b/code/game/objects/items/stacks/sheets/leather.dm
@@ -169,9 +169,9 @@ GLOBAL_LIST_INIT(sinew_recipes, list ( \
return
if(is_type_in_typecache(target, goliath_platable_armor_typecache))
var/obj/item/clothing/C = target
- var/list/current_armor = C.armor
- if(current_armor["melee"] < 60)
- current_armor["melee"] = min(current_armor["melee"] + 10, 60)
+ var/datum/armor/current_armor = C.armor
+ if(current_armor.getRating("melee") < 60)
+ C.armor = current_armor.setRating(melee_value = min(current_armor.getRating("melee") + 10, 60))
to_chat(user, "You strengthen [target], improving its resistance against melee attacks.")
use(1)
else
@@ -180,9 +180,9 @@ GLOBAL_LIST_INIT(sinew_recipes, list ( \
var/obj/mecha/working/ripley/D = target
if(D.hides < 3)
D.hides++
- D.armor["melee"] = min(D.armor["melee"] + 10, 70)
- D.armor["bullet"] = min(D.armor["bullet"] + 5, 50)
- D.armor["laser"] = min(D.armor["laser"] + 5, 50)
+ D.armor = D.armor.setRating(melee_value = min(D.armor.getRating("melee") + 10, 70))
+ D.armor = D.armor.setRating(bullet_value = min(D.armor.getRating("bullet") + 5, 50))
+ D.armor = D.armor.setRating(laser_value = min(D.armor.getRating("laser") + 5, 50))
to_chat(user, "You strengthen [target], improving its resistance against melee attacks.")
D.update_icon()
if(D.hides == 3)
@@ -242,7 +242,7 @@ GLOBAL_LIST_INIT(sinew_recipes, list ( \
HS.amount++
src.use(1)
wetness = initial(wetness)
- break
+ return
//If it gets to here it means it did not find a suitable stack on the tile.
var/obj/item/stack/sheet/leather/HS = new(src.loc)
HS.amount = 1
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 54d42176f52..9dabea08319 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -218,10 +218,22 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
/obj/item/stack/sheet/mineral/plasma/welder_act(mob/user, obj/item/I)
if(I.use_tool(src, user, volume = I.tool_volume))
- message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])")
- investigate_log("was ignited by [key_name(user)]","atmos")
- fire_act()
+ log_and_set_aflame(user, I)
+ return TRUE
+
+/obj/item/stack/sheet/mineral/plasma/attackby(obj/item/I, mob/living/user, params)
+ if(is_hot(I))
+ log_and_set_aflame(user, I)
+ else
+ return ..()
+
+/obj/item/stack/sheet/mineral/plasma/proc/log_and_set_aflame(mob/user, obj/item/I)
+ var/turf/T = get_turf(src)
+ message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user, "?")]) ([ADMIN_FLW(user, "FLW")]) in ([COORD(T)] - [ADMIN_JMP(T)]")
+ log_game("Plasma sheets ignited by [key_name(user)] in [COORD(T)]")
+ investigate_log("was ignited by [key_name(user)]", "atmos")
+ user.create_log(MISC_LOG, "Plasma sheets ignited using [I]", src)
+ fire_act()
/obj/item/stack/sheet/mineral/plasma/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE)
..()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index ac635eebef7..4fef7b9f6d5 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -15,6 +15,7 @@
*/
GLOBAL_LIST_INIT(metal_recipes, list(
new /datum/stack_recipe("stool", /obj/structure/chair/stool, one_per_turf = 1, on_floor = 1),
+ new /datum/stack_recipe("barstool", /obj/structure/chair/stool/bar, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("chair", /obj/structure/chair, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("shuttle seat", /obj/structure/chair/comfy/shuttle, 2, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("sofa (middle)", /obj/structure/chair/sofa, one_per_turf = 1, on_floor = 1),
@@ -471,7 +472,8 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("cane mould", /obj/item/kitchen/mould/cane, 1, on_floor = 1), \
new /datum/stack_recipe("cash mould", /obj/item/kitchen/mould/cash, 1, on_floor = 1), \
new /datum/stack_recipe("coin mould", /obj/item/kitchen/mould/coin, 1, on_floor = 1), \
- new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1)))
+ new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1), \
+ new /datum/stack_recipe("warning cone", /obj/item/clothing/head/cone, 5, on_floor = 1)))
/obj/item/stack/sheet/plastic
name = "plastic"
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 14ccc103ab1..ab592cc0402 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -204,7 +204,7 @@
if(amount < 1) // Just in case a stack's amount ends up fractional somehow
var/oldsrc = src
- src = null //dont kill proc after del()
+ src = null //dont kill proc after qdel()
usr.unEquip(oldsrc, 1)
qdel(oldsrc)
if(istype(O, /obj/item))
diff --git a/code/game/objects/items/tools/tool_behaviour.dm b/code/game/objects/items/tools/tool_behaviour.dm
index 986e3a981b2..136bea77e6a 100644
--- a/code/game/objects/items/tools/tool_behaviour.dm
+++ b/code/game/objects/items/tools/tool_behaviour.dm
@@ -4,7 +4,7 @@
// No delay means there is no start message, and no reason to call tool_start_check before use_tool.
// Run the start check here so we wouldn't have to call it manually.
target.add_fingerprint(user)
- if(!tool_start_check(user, amount) && !delay)
+ if(!tool_start_check(target, user, amount) && !delay)
return
delay *= toolspeed
@@ -39,7 +39,7 @@
// Called before use_tool if there is a delay, or by use_tool if there isn't.
// Only ever used by welding tools and stacks, so it's not added on any other use_tool checks.
-/obj/item/proc/tool_start_check(mob/living/user, amount=0)
+/obj/item/proc/tool_start_check(atom/target, mob/living/user, amount=0)
return tool_use_check(user, amount)
// A check called by tool_start_check once, and by use_tool on every tick of delay.
diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm
index 270a44ee559..4ac934462a0 100644
--- a/code/game/objects/items/tools/welder.dm
+++ b/code/game/objects/items/tools/welder.dm
@@ -35,10 +35,12 @@
..()
create_reagents(maximum_fuel)
reagents.add_reagent("fuel", maximum_fuel)
- if(refills_over_time)
- reagents.reagents_generated_per_cycle += list("fuel" = 1)
update_icon()
+/obj/item/weldingtool/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
/obj/item/weldingtool/examine(mob/user)
. = ..()
if(get_dist(user, src) <= 0)
@@ -49,11 +51,15 @@
return FIRELOSS
/obj/item/weldingtool/process()
- var/turf/T = get_turf(src)
- if(T) // Implants for instance won't find a turf
- T.hotspot_expose(2500, 5)
- if(prob(5))
- remove_fuel(1)
+ if(tool_enabled)
+ var/turf/T = get_turf(src)
+ if(T) // Implants for instance won't find a turf
+ T.hotspot_expose(2500, 5)
+ if(prob(5))
+ remove_fuel(1)
+ if(refills_over_time)
+ if(GET_FUEL < maximum_fuel)
+ reagents.add_reagent("fuel", 1)
..()
/obj/item/weldingtool/attack_self(mob/user)
@@ -77,7 +83,8 @@
playsound(loc, activation_sound, 50, 1)
set_light(light_intensity)
else
- STOP_PROCESSING(SSobj, src)
+ if(!refills_over_time)
+ STOP_PROCESSING(SSobj, src)
damtype = BRUTE
force = 3
hitsound = "swing_hit"
@@ -101,9 +108,9 @@
return FALSE
// When welding is about to start, run a normal tool_use_check, then flash a mob if it succeeds.
-/obj/item/weldingtool/tool_start_check(mob/living/user, amount=0)
+/obj/item/weldingtool/tool_start_check(atom/target, mob/living/user, amount=0)
. = tool_use_check(user, amount)
- if(. && user)
+ if(. && user && !ismob(target)) // Don't flash the user if they're repairing robo limbs or repairing a borg etc. Only flash them if the target is an object
user.flash_eyes(light_intensity)
/obj/item/weldingtool/use(amount)
@@ -160,7 +167,7 @@
/obj/item/weldingtool/update_icon()
if(low_fuel_changes_icon)
var/ratio = GET_FUEL / maximum_fuel
- ratio = Ceiling(ratio*4) * 25
+ ratio = CEILING(ratio*4, 1) * 25
if(ratio == 100)
icon_state = initial(icon_state)
else
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 8e51a6760b7..cd1b91388d7 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -35,7 +35,7 @@
desc = "A translucent balloon. There's nothing in it."
icon = 'icons/obj/toy.dmi'
icon_state = "waterballoon-e"
- item_state = "balloon-empty"
+ item_state = "waterballoon-e"
/obj/item/toy/balloon/New()
..()
@@ -99,10 +99,10 @@
/obj/item/toy/balloon/update_icon()
if(src.reagents.total_volume >= 1)
icon_state = "waterballoon"
- item_state = "balloon"
+ item_state = "waterballoon"
else
icon_state = "waterballoon-e"
- item_state = "balloon-empty"
+ item_state = "waterballoon-e"
/obj/item/toy/syndicateballoon
name = "syndicate balloon"
@@ -344,56 +344,56 @@
/obj/item/toy/prize/ripley
name = "toy ripley"
- desc = "Mini-Mecha action figure! Collect them all! 1/11."
+ desc = "Mini-Mecha action figure! Collect them all! 1/11. This one is a ripley, a mining and engineering mecha."
/obj/item/toy/prize/fireripley
name = "toy firefighting ripley"
- desc = "Mini-Mecha action figure! Collect them all! 2/11."
+ desc = "Mini-Mecha action figure! Collect them all! 2/11. This one is a firefighter ripley, a fireproof mining and engineering mecha."
icon_state = "fireripleytoy"
/obj/item/toy/prize/deathripley
name = "toy deathsquad ripley"
- desc = "Mini-Mecha action figure! Collect them all! 3/11."
+ desc = "Mini-Mecha action figure! Collect them all! 3/11. This one is the black ripley used by the hero of DeathSquad, that TV drama about loose-cannon ERT officers!"
icon_state = "deathripleytoy"
/obj/item/toy/prize/gygax
name = "toy gygax"
- desc = "Mini-Mecha action figure! Collect them all! 4/11."
+ desc = "Mini-Mecha action figure! Collect them all! 4/11. This one is the speedy gygax combat mecha. Zoom zoom, pew pew!"
icon_state = "gygaxtoy"
/obj/item/toy/prize/durand
name = "toy durand"
- desc = "Mini-Mecha action figure! Collect them all! 5/11."
+ desc = "Mini-Mecha action figure! Collect them all! 5/11. This one is the heavy durand combat mecha. Stomp stomp!"
icon_state = "durandprize"
/obj/item/toy/prize/honk
name = "toy H.O.N.K."
- desc = "Mini-Mecha action figure! Collect them all! 6/11."
+ desc = "Mini-Mecha action figure! Collect them all! 6/11. This one is the infamous H.O.N.K mech!"
icon_state = "honkprize"
/obj/item/toy/prize/marauder
name = "toy marauder"
- desc = "Mini-Mecha action figure! Collect them all! 7/11."
+ desc = "Mini-Mecha action figure! Collect them all! 7/11. This one is the powerful marauder combat mecha! Run for cover!"
icon_state = "marauderprize"
/obj/item/toy/prize/seraph
name = "toy seraph"
- desc = "Mini-Mecha action figure! Collect them all! 8/11."
+ desc = "Mini-Mecha action figure! Collect them all! 8/11. This one is the powerful seraph combat mecha! Someone's in trouble!"
icon_state = "seraphprize"
/obj/item/toy/prize/mauler
name = "toy mauler"
- desc = "Mini-Mecha action figure! Collect them all! 9/11."
+ desc = "Mini-Mecha action figure! Collect them all! 9/11. This one is the deadly mauler combat mecha! Look out!"
icon_state = "maulerprize"
/obj/item/toy/prize/odysseus
name = "toy odysseus"
- desc = "Mini-Mecha action figure! Collect them all! 10/11."
+ desc = "Mini-Mecha action figure! Collect them all! 10/11. This one is the spindly, syringe-firing odysseus medical mecha."
icon_state = "odysseusprize"
/obj/item/toy/prize/phazon
name = "toy phazon"
- desc = "Mini-Mecha action figure! Collect them all! 11/11."
+ desc = "Mini-Mecha action figure! Collect them all! 11/11. This one is the mysterious Phazon combat mecha! Nobody's safe!"
icon_state = "phazonprize"
@@ -1023,6 +1023,18 @@ obj/item/toy/cards/deck/syndicate/black
name = "orange fox plushie"
icon_state = "orangefox"
+/obj/item/toy/plushie/orange_fox/grump
+ name = "grumpy fox"
+ desc = "An ancient plushie that seems particularly grumpy."
+
+/obj/item/toy/plushie/orange_fox/grump/ComponentInitialize()
+ . = ..()
+ var/static/list/grumps = list("Ahh, yes, you're so clever, var editing that.", "Really?", "If you make a runtime with var edits, it's your own damn fault.",
+ "Don't you dare post issues on the git when you don't even know how this works.", "Was that necessary?", "Ohhh, setting admin edited var must be your favorite pastime!",
+ "Oh, so you have time to var edit, but you don't have time to ban that greytider?", "Oh boy, is this another one of those 'events'?", "Seriously, just stop.", "You do realize this is incurring proc call overhead.",
+ "Congrats, you just left a reference with your dirty client and now that thing you edited will never garbage collect properly.", "Is it that time of day, again, for unecessary adminbus?")
+ AddComponent(/datum/component/edit_complainer, grumps)
+
/obj/item/toy/plushie/coffee_fox
name = "coffee fox plushie"
icon_state = "coffeefox"
@@ -1087,6 +1099,21 @@ obj/item/toy/cards/deck/syndicate/black
return
..()
+/obj/item/toy/plushie/ipcplushie
+ name = "IPC plushie"
+ desc = "An adorable IPC plushie, straight from New Canaan. Arguably more durable than the real deal. Toaster functionality included."
+ icon_state = "plushie_ipc"
+ item_state = "plushie_ipc"
+
+/obj/item/toy/plushie/ipcplushie/attackby(obj/item/B, mob/user, params)
+ if(istype(B, /obj/item/reagent_containers/food/snacks/breadslice))
+ new /obj/item/reagent_containers/food/snacks/toast(get_turf(loc))
+ to_chat(user, " You insert bread into the toaster. ")
+ playsound(loc, 'sound/machines/ding.ogg', 50, 1)
+ qdel(B)
+ else
+ return ..()
+
//New generation TG plushies
/obj/item/toy/plushie/lizardplushie
@@ -1117,15 +1144,15 @@ obj/item/toy/cards/deck/syndicate/black
* Foam Armblade
*/
- /obj/item/toy/foamblade
- name = "foam armblade"
- desc = "it says \"Sternside Changs #1 fan\" on it. "
- icon = 'icons/obj/toy.dmi'
- icon_state = "foamblade"
- item_state = "arm_blade"
- attack_verb = list("pricked", "absorbed", "gored")
- w_class = WEIGHT_CLASS_SMALL
- resistance_flags = FLAMMABLE
+/obj/item/toy/foamblade
+ name = "foam armblade"
+ desc = "it says \"Sternside Changs #1 fan\" on it. "
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "foamblade"
+ item_state = "arm_blade"
+ attack_verb = list("pricked", "absorbed", "gored")
+ w_class = WEIGHT_CLASS_SMALL
+ resistance_flags = FLAMMABLE
/*
* Toy/fake flash
@@ -1220,7 +1247,6 @@ obj/item/toy/cards/deck/syndicate/black
spawn(20)
cooldown = FALSE
return
- ..()
/obj/item/toy/owl
name = "owl action figure"
@@ -1382,6 +1408,7 @@ obj/item/toy/cards/deck/syndicate/black
name = "xenomorph action figure"
desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use."
w_class = WEIGHT_CLASS_SMALL
+ bubble_icon = "alien"
var/cooldown = 0
/obj/item/toy/toy_xeno/attack_self(mob/user)
@@ -1390,7 +1417,7 @@ obj/item/toy/cards/deck/syndicate/black
user.visible_message("[user] pulls back the string on [src].")
icon_state = "[initial(icon_state)]_used"
sleep(5)
- audible_message("[bicon(src)] Hiss!")
+ atom_say("Hiss!")
var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg')
playsound(get_turf(src), pick(possible_sounds), 50, 1)
spawn(45)
@@ -1564,182 +1591,217 @@ obj/item/toy/cards/deck/syndicate/black
/obj/item/toy/figure/cmo
name = "Chief Medical Officer action figure"
+ desc = "The ever-suffering CMO, from Space Life's SS12 figurine collection."
icon_state = "cmo"
toysay = "Suit sensors!"
/obj/item/toy/figure/assistant
name = "Assistant action figure"
+ desc = "The faceless, hairless scourge of the station, from Space Life's SS12 figurine collection."
icon_state = "assistant"
toysay = "Grey tide station wide!"
/obj/item/toy/figure/atmos
name = "Atmospheric Technician action figure"
+ desc = "The faithful atmospheric technician, from Space Life's SS12 figurine collection."
icon_state = "atmos"
toysay = "Glory to Atmosia!"
/obj/item/toy/figure/bartender
name = "Bartender action figure"
+ desc = "The suave bartender, from Space Life's SS12 figurine collection."
icon_state = "bartender"
toysay = "Wheres my monkey?"
/obj/item/toy/figure/borg
name = "Cyborg action figure"
+ desc = "The iron-willed cyborg, from Space Life's SS12 figurine collection."
icon_state = "borg"
toysay = "I. LIVE. AGAIN."
/obj/item/toy/figure/botanist
name = "Botanist action figure"
+ desc = "The drug-addicted botanist, from Space Life's SS12 figurine collection."
icon_state = "botanist"
toysay = "Dude, I see colors..."
/obj/item/toy/figure/captain
name = "Captain action figure"
+ desc = "The inept captain, from Space Life's SS12 figurine collection."
icon_state = "captain"
toysay = "Crew, the Nuke Disk is safely up my ass."
/obj/item/toy/figure/cargotech
name = "Cargo Technician action figure"
+ desc = "The hard-working cargo tech, from Space Life's SS12 figurine collection."
icon_state = "cargotech"
toysay = "For Cargonia!"
/obj/item/toy/figure/ce
name = "Chief Engineer action figure"
+ desc = "The expert Chief Engineer, from Space Life's SS12 figurine collection."
icon_state = "ce"
toysay = "Wire the solars!"
/obj/item/toy/figure/chaplain
name = "Chaplain action figure"
+ desc = "The obsessed Chaplain, from Space Life's SS12 figurine collection."
icon_state = "chaplain"
toysay = "Gods make me a killing machine please!"
/obj/item/toy/figure/chef
name = "Chef action figure"
+ desc = "The cannibalistic chef, from Space Life's SS12 figurine collection."
icon_state = "chef"
toysay = "I swear it's not human meat."
/obj/item/toy/figure/chemist
name = "Chemist action figure"
+ desc = "The legally dubious Chemist, from Space Life's SS12 figurine collection."
icon_state = "chemist"
toysay = "Get your pills!"
/obj/item/toy/figure/clown
name = "Clown action figure"
+ desc = "The mischevious Clown, from Space Life's SS12 figurine collection."
icon_state = "clown"
toysay = "Honk!"
/obj/item/toy/figure/ian
name = "Ian action figure"
+ desc = "The adorable corgi, from Space Life's SS12 figurine collection."
icon_state = "ian"
toysay = "Arf!"
/obj/item/toy/figure/detective
name = "Detective action figure"
+ desc = "The clever detective, from Space Life's SS12 figurine collection."
icon_state = "detective"
toysay = "This airlock has grey jumpsuit and insulated glove fibers on it."
/obj/item/toy/figure/dsquad
name = "Death Squad Officer action figure"
+ desc = "It's a member of the DeathSquad, a TV drama where loose-cannon ERT officers face up against the threats of the galaxy! It's from Space Life's special edition SS12 figurine collection."
icon_state = "dsquad"
toysay = "Eliminate all threats!"
/obj/item/toy/figure/engineer
name = "Engineer action figure"
+ desc = "The frantic engineer, from Space Life's SS12 figurine collection."
icon_state = "engineer"
toysay = "Oh god, the singularity is loose!"
/obj/item/toy/figure/geneticist
name = "Geneticist action figure"
+ desc = "The balding geneticist, from Space Life's SS12 figurine collection."
icon_state = "geneticist"
toysay = "I'm not qualified for this job."
/obj/item/toy/figure/hop
name = "Head of Personnel action figure"
+ desc = "The officious Head of Personnel, from Space Life's SS12 figurine collection."
icon_state = "hop"
- toysay = "Giving out all access!"
+ toysay = "Papers, please!"
/obj/item/toy/figure/hos
name = "Head of Security action figure"
+ desc = "The bloodlust-filled Head of Security, from Space Life's SS12 figurine collection."
icon_state = "hos"
- toysay = "I'm here to win, anything else is secondary."
+ toysay = "Space law? What?"
/obj/item/toy/figure/qm
name = "Quartermaster action figure"
+ desc = "The nationalistic Quartermaster, from Space Life's SS12 figurine collection."
icon_state = "qm"
toysay = "Hail Cargonia!"
/obj/item/toy/figure/janitor
name = "Janitor action figure"
+ desc = "The water-using Janitor, from Space Life's SS12 figurine collection."
icon_state = "janitor"
toysay = "Look at the signs, you idiot."
/obj/item/toy/figure/lawyer
name = "Internal Affairs Agent action figure"
+ desc = "The unappreciated Internal Affairs Agent, from Space Life's SS12 figurine collection."
icon_state = "lawyer"
toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!"
/obj/item/toy/figure/librarian
name = "Librarian action figure"
+ desc = "The quiet Librarian, from Space Life's SS12 figurine collection."
icon_state = "librarian"
toysay = "One day while..."
/obj/item/toy/figure/md
name = "Medical Doctor action figure"
+ desc = "The stressed-out doctor, from Space Life's SS12 figurine collection."
icon_state = "md"
toysay = "The patient is already dead!"
/obj/item/toy/figure/mime
name = "Mime action figure"
- desc = "A \"Space Life\" brand Mime action figure."
+ desc = "... from Space Life's SS12 figurine collection."
icon_state = "mime"
toysay = "..."
/obj/item/toy/figure/miner
name = "Shaft Miner action figure"
+ desc = "The gun-toting Shaft Miner, from Space Life's SS12 figurine collection."
icon_state = "miner"
toysay = "Oh god it's eating my intestines!"
/obj/item/toy/figure/ninja
name = "Ninja action figure"
+ desc = "It's the mysterious ninja! It's from Space Life's special edition SS12 figurine collection."
icon_state = "ninja"
toysay = "Oh god! Stop shooting, I'm friendly!"
/obj/item/toy/figure/wizard
name = "Wizard action figure"
+ desc = "It's the deadly, spell-slinging wizard! It's from Space Life's special edition SS12 figurine collection."
icon_state = "wizard"
toysay = "Ei Nath!"
/obj/item/toy/figure/rd
name = "Research Director action figure"
+ desc = "The ambitious RD, from Space Life's SS12 figurine collection."
icon_state = "rd"
toysay = "Blowing all of the borgs!"
/obj/item/toy/figure/roboticist
name = "Roboticist action figure"
+ desc = "The skillful Roboticist, from Space Life's SS12 figurine collection."
icon_state = "roboticist"
toysay = "He asked to be borged!"
/obj/item/toy/figure/scientist
name = "Scientist action figure"
+ desc = "The mad Scientist, from Space Life's SS12 figurine collection."
icon_state = "scientist"
toysay = "Someone else must have made those bombs!"
/obj/item/toy/figure/syndie
name = "Nuclear Operative action figure"
+ desc = "It's the red-suited Nuclear Operative! It's from Space Life's special edition SS12 figurine collection."
icon_state = "syndie"
toysay = "Get that fucking disk!"
/obj/item/toy/figure/secofficer
name = "Security Officer action figure"
+ desc = "The power-tripping Security Officer, from Space Life's SS12 figurine collection."
icon_state = "secofficer"
toysay = "I am the law!"
/obj/item/toy/figure/virologist
name = "Virologist action figure"
+ desc = "The pandemic-starting Virologist, from Space Life's SS12 figurine collection."
icon_state = "virologist"
- toysay = "The cure is potassium!"
+ toysay = "It's not my virus!"
/obj/item/toy/figure/warden
name = "Warden action figure"
+ desc = "The amnesiac Warden, from Space Life's SS12 figurine collection."
icon_state = "warden"
toysay = "Execute him for breaking in!"
diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm
index 7cf48095e72..8508c741524 100644
--- a/code/game/objects/items/trash.dm
+++ b/code/game/objects/items/trash.dm
@@ -8,6 +8,13 @@
desc = "This is rubbish."
resistance_flags = FLAMMABLE
+/obj/item/trash/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["metal"] += 2
+ C.stored_comms["wood"] += 1
+ C.stored_comms["glass"] += 1
+ qdel(src)
+ return TRUE
+
/obj/item/trash/raisins
name = "4no raisins"
icon_state= "4no_raisins"
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index d6397025424..f8f9a48102a 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -477,9 +477,7 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
return R.cell.charge >= (amount * use_multiplier)
/obj/item/rcd/proc/detonate_pulse()
- audible_message("[src] begins to vibrate and \
- buzz loudly!","[src] begins \
- vibrating violently!")
+ audible_message("[src] begins to vibrate and buzz loudly!", "[src] begins vibrating violently!")
// 5 seconds to get rid of it
addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50)
diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm
index a8da7503c72..26cef27a0b4 100644
--- a/code/game/objects/items/weapons/RSF.dm
+++ b/code/game/objects/items/weapons/RSF.dm
@@ -58,7 +58,7 @@ RSF
if(!proximity) return
if(!(istype(A, /obj/structure/table) || istype(A, /turf/simulated/floor)))
return
- var spawn_location
+ var/spawn_location
var/turf/T = get_turf(A)
if(istype(T) && !T.density)
spawn_location = T
diff --git a/code/game/objects/items/weapons/batons.dm b/code/game/objects/items/weapons/batons.dm
new file mode 100644
index 00000000000..34d2bdd7ac8
--- /dev/null
+++ b/code/game/objects/items/weapons/batons.dm
@@ -0,0 +1,133 @@
+/// Delay in deci-seconds between two non-lethal attacks
+#define BATON_STUN_COOLDOWN 4 SECONDS
+/// Force of the telescopic baton when deployed
+#define BATON_TELESCOPIC_FORCE_DEPLOYED 10
+
+/**
+ * # Police Baton
+ *
+ * Knocks down the hit mob when not on harm intent and when [/obj/item/melee/classic_baton/on] is TRUE
+ *
+ * A non-lethal attack has a cooldown to avoid spamming
+ */
+/obj/item/melee/classic_baton
+ name = "police baton"
+ desc = "A wooden truncheon for beating criminal scum."
+ icon_state = "baton"
+ item_state = "classic_baton"
+ slot_flags = SLOT_BELT
+ force = 12 //9 hit crit
+ w_class = WEIGHT_CLASS_NORMAL
+ /// Whether the baton is on cooldown
+ var/on_cooldown = FALSE
+ /// Whether the baton is toggled on (to allow attacking)
+ var/on = TRUE
+
+/obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user)
+ if(!on)
+ return ..()
+
+ add_fingerprint(user)
+ if((CLUMSY in user.mutations) && prob(50))
+ user.visible_message("[user] accidentally clubs [user.p_them()]self with [src]!", \
+ "You accidentally club yourself with [src]!")
+ user.Weaken(force * 3)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(force * 2, BRUTE, "head")
+ else
+ user.take_organ_damage(force * 2)
+ return
+
+ if(user.a_intent == INTENT_HARM || isrobot(target)) // Lethal attack or it's a borg (can't knock them down!)
+ return ..()
+ else if(!on_cooldown) // Non-lethal attack - knock them down
+ // Check for shield/countering
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
+ return
+ if(check_martial_counter(H, user))
+ return
+ // Visuals and sound
+ user.do_attack_animation(target)
+ playsound(target, 'sound/effects/woodhit.ogg', 75, TRUE, -1)
+ add_attack_logs(user, target, "Stunned with [src]")
+ target.visible_message("[user] has knocked down [target] with \the [src]!", \
+ "[user] has knocked down [target] with \the [src]!")
+ // Hit 'em
+ target.LAssailant = iscarbon(user) ? user : null
+ target.Weaken(3)
+ on_cooldown = TRUE
+ addtimer(CALLBACK(src, .proc/cooldown_finished), BATON_STUN_COOLDOWN)
+
+/**
+ * Called some time after a non-lethal attack
+ */
+/obj/item/melee/classic_baton/proc/cooldown_finished()
+ on_cooldown = FALSE
+
+/**
+ * # Fancy Cane
+ */
+/obj/item/melee/classic_baton/ntcane
+ name = "fancy cane"
+ desc = "A cane with special engraving on it. It seems well suited for fending off assailants..."
+ icon_state = "cane_nt"
+ item_state = "cane_nt"
+ needs_permit = FALSE
+
+/obj/item/melee/classic_baton/ntcane/is_crutch()
+ return TRUE
+
+/**
+ * # Telescopic Baton
+ */
+/obj/item/melee/classic_baton/telescopic
+ name = "telescopic baton"
+ desc = "A compact yet robust personal defense weapon. Can be concealed when folded."
+ icon_state = "telebaton_0"
+ item_state = null
+ slot_flags = SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ needs_permit = FALSE
+ force = 0
+ on = FALSE
+ /// Attack verbs when concealed (created on Initialize)
+ var/static/list/attack_verb_off
+ /// Attack verbs when extended (created on Initialize)
+ var/static/list/attack_verb_on
+
+/obj/item/melee/classic_baton/telescopic/Initialize(mapload)
+ . = ..()
+ if(!attack_verb_off)
+ attack_verb_off = list("hit", "poked")
+ attack_verb_on = list("smacked", "struck", "cracked", "beaten")
+ attack_verb = on ? attack_verb_on : attack_verb_off
+
+/obj/item/melee/classic_baton/telescopic/attack_self(mob/user)
+ on = !on
+ icon_state = "telebaton_[on]"
+ if(on)
+ to_chat(user, "You extend the baton.")
+ item_state = "nullrod"
+ w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance
+ force = BATON_TELESCOPIC_FORCE_DEPLOYED //stunbaton damage
+ attack_verb = attack_verb_on
+ else
+ to_chat(user, "You collapse the baton.")
+ item_state = null //no sprite for concealment even when in hand
+ slot_flags = SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ force = 0 //not so robust now
+ attack_verb = attack_verb_off
+ // Update mob hand visuals
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.update_inv_l_hand()
+ H.update_inv_r_hand()
+ playsound(loc, 'sound/weapons/batonextend.ogg', 50, TRUE)
+ add_fingerprint(user)
+
+#undef BATON_STUN_COOLDOWN
+#undef BATON_TELESCOPIC_FORCE_DEPLOYED
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 405ce47a033..b780a837559 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -212,6 +212,11 @@
return M
owner_ckey = null
+/obj/item/card/id/proc/getPlayerCkey()
+ var/mob/living/carbon/human/H = getPlayer()
+ if(istype(H))
+ return H.ckey
+
/obj/item/card/id/proc/is_untrackable()
return untrackable
@@ -356,13 +361,13 @@
/obj/item/card/id/syndicate/attack_self(mob/user as mob)
if(!src.registered_name)
- var t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name))
+ var/t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name))
if(!t)
to_chat(user, "Invalid name.")
return
src.registered_name = t
- var u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN))
+ var/u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN))
if(!u)
to_chat(user, "Invalid assignment.")
src.registered_name = ""
diff --git a/code/game/objects/items/weapons/chrono_eraser.dm b/code/game/objects/items/weapons/chrono_eraser.dm
index bf46343b963..1dc0b22abdf 100644
--- a/code/game/objects/items/weapons/chrono_eraser.dm
+++ b/code/game/objects/items/weapons/chrono_eraser.dm
@@ -188,7 +188,7 @@
/obj/structure/chrono_field/update_icon()
var/ttk_frame = 1 - (tickstokill / initial(tickstokill))
- ttk_frame = Clamp(Ceiling(ttk_frame * CHRONO_FRAME_COUNT), 1, CHRONO_FRAME_COUNT)
+ ttk_frame = clamp(CEILING(ttk_frame * CHRONO_FRAME_COUNT, 1), 1, CHRONO_FRAME_COUNT)
if(ttk_frame != RPpos)
RPpos = ttk_frame
mob_underlay.icon_state = "frame[RPpos]"
diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm
index 98a7b769642..bd80e43140f 100644
--- a/code/game/objects/items/weapons/cigs.dm
+++ b/code/game/objects/items/weapons/cigs.dm
@@ -31,6 +31,7 @@ LIGHTERS ARE IN LIGHTERS.DM
var/smoketime = 150
var/chem_volume = 60
var/list/list_reagents = list("nicotine" = 40)
+ var/first_puff = TRUE // the first puff is a bit more reagents ingested
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/mask.dmi',
"Unathi" = 'icons/mob/species/unathi/mask.dmi',
@@ -65,6 +66,10 @@ LIGHTERS ARE IN LIGHTERS.DM
..()
light()
+/obj/item/clothing/mask/cigarette/catch_fire()
+ if(!lit)
+ light("The [name] is lit by the flames!")
+
/obj/item/clothing/mask/cigarette/welder_act(mob/user, obj/item/I)
. = TRUE
if(I.tool_use_check(user, 0)) //Don't need to flash eyes because you are a badass
@@ -194,8 +199,9 @@ LIGHTERS ARE IN LIGHTERS.DM
if(reagents && reagents.total_volume) // check if it has any reagents at all
if(is_being_smoked) // if it's being smoked, transfer reagents to the mob
var/mob/living/carbon/C = loc
- for (var/datum/reagent/R in reagents.reagent_list)
- reagents.trans_id_to(C, R.id, max(REAGENTS_METABOLISM / reagents.reagent_list.len, 0.1)) //transfer at least .1 of each chem
+ for(var/datum/reagent/R in reagents.reagent_list)
+ reagents.trans_id_to(C, R.id, first_puff ? 1 : max(REAGENTS_METABOLISM / reagents.reagent_list.len, 0.1)) //transfer at least .1 of each chem
+ first_puff = FALSE
if(!reagents.total_volume) // There were reagents, but now they're gone
to_chat(C, "Your [name] loses its flavor.")
else // else just remove some of the reagents
@@ -309,6 +315,11 @@ LIGHTERS ARE IN LIGHTERS.DM
pixel_y = rand(-10,10)
transform = turn(transform,rand(0,360))
+/obj/item/cigbutt/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["wood"] += 1
+ qdel(src)
+ return TRUE
+
/obj/item/cigbutt/cigarbutt
name = "cigar butt"
desc = "A manky old cigar butt."
@@ -377,6 +388,7 @@ LIGHTERS ARE IN LIGHTERS.DM
to_chat(user, "You refill the pipe with tobacco.")
reagents.add_reagent("nicotine", chem_volume)
smoketime = initial(smoketime)
+ first_puff = TRUE
/obj/item/clothing/mask/cigarette/pipe/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers))
diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm
index 8815ad6a20f..fcfe183dddf 100644
--- a/code/game/objects/items/weapons/cosmetics.dm
+++ b/code/game/objects/items/weapons/cosmetics.dm
@@ -5,16 +5,22 @@
icon_state = "lipstick"
w_class = WEIGHT_CLASS_TINY
var/colour = "red"
- var/open = 0
- var/list/lipstick_colors = list(
- "purple" = "purple",
- "jade" = "#216F43",
- "lime" = "lime",
- "black" = "black",
- "green" = "green",
- "blue" = "blue",
- "white" = "white")
+ var/open = FALSE
+ var/static/list/lipstick_colors
+/obj/item/lipstick/Initialize(mapload)
+ . = ..()
+ if(!lipstick_colors)
+ lipstick_colors = list(
+ "black" = "#000000",
+ "white" = "#FFFFFF",
+ "red" = "#FF0000",
+ "green" = "#00C000",
+ "blue" = "#0000FF",
+ "purple" = "#D55CD0",
+ "jade" = "#216F43",
+ "lime" = "#00FF00",
+ )
/obj/item/lipstick/purple
name = "purple lipstick"
@@ -22,7 +28,7 @@
/obj/item/lipstick/jade
name = "jade lipstick"
- colour = "#216F43"
+ colour = "jade"
/obj/item/lipstick/lime
name = "lime lipstick"
@@ -47,40 +53,37 @@
/obj/item/lipstick/random
name = "lipstick"
-/obj/item/lipstick/random/New()
- ..()
- var/lscolor = pick(lipstick_colors)//A random color is picked from the var defined initially in a new var.
- colour = lipstick_colors[lscolor]//The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB)
- name = "[lscolor] lipstick"//The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon
+/obj/item/lipstick/random/Initialize(mapload)
+ . = ..()
+ colour = pick(lipstick_colors)
+ name = "[colour] lipstick"
-
-/obj/item/lipstick/attack_self(mob/user as mob)
- overlays.Cut()
+/obj/item/lipstick/attack_self(mob/user)
+ cut_overlays()
to_chat(user, "You twist \the [src] [open ? "closed" : "open"].")
open = !open
if(open)
- var/image/colored = image("icon"='icons/obj/items.dmi', "icon_state"="lipstick_uncap_color")
- colored.color = colour
+ var/mutable_appearance/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color")
+ colored.color = lipstick_colors[colour]
icon_state = "lipstick_uncap"
- overlays += colored
+ add_overlay(colored)
else
icon_state = "lipstick"
-/obj/item/lipstick/attack(mob/M as mob, mob/user as mob)
- if(!open) return
-
- if(!istype(M, /mob)) return
+/obj/item/lipstick/attack(mob/M, mob/user)
+ if(!open || !istype(M))
+ return
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(H.lip_style) //if they already have lipstick on
+ if(H.lip_style) // If they already have lipstick on
to_chat(user, "You need to wipe off the old lipstick first!")
return
if(H == user)
user.visible_message("[user] does [user.p_their()] lips with [src].", \
"You take a moment to apply [src]. Perfect!")
H.lip_style = "lipstick"
- H.lip_color = colour
+ H.lip_color = lipstick_colors[colour]
H.update_body()
else
user.visible_message("[user] begins to do [H]'s lips with \the [src].", \
@@ -89,7 +92,7 @@
user.visible_message("[user] does [H]'s lips with \the [src].", \
"You apply \the [src].")
H.lip_style = "lipstick"
- H.lip_color = colour
+ H.lip_color = lipstick_colors[colour]
H.update_body()
else
to_chat(user, "Where are the lips on that?")
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index 581c4395650..ee98d309876 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -72,7 +72,7 @@
if(powered) //so it doesn't show charge if it's unpowered
if(cell)
var/ratio = cell.charge / cell.maxcharge
- ratio = Ceiling(ratio*4) * 25
+ ratio = CEILING(ratio*4, 1) * 25
overlays += "[icon_state]-charge[ratio]"
/obj/item/defibrillator/CheckParts(list/parts_list)
diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm
index d2be3ddadff..ae49512853a 100644
--- a/code/game/objects/items/weapons/dice.dm
+++ b/code/game/objects/items/weapons/dice.dm
@@ -138,7 +138,7 @@
/obj/item/dice/proc/diceroll(mob/user)
result = roll(sides)
if(rigged != DICE_NOT_RIGGED && result != rigged_value)
- if(rigged == DICE_BASICALLY_RIGGED && prob(Clamp(1/(sides - 1) * 100, 25, 80)))
+ if(rigged == DICE_BASICALLY_RIGGED && prob(clamp(1/(sides - 1) * 100, 25, 80)))
result = rigged_value
else if(rigged == DICE_TOTALLY_RIGGED)
result = rigged_value
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index 8f11f65fe71..45047e97156 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -147,10 +147,6 @@
to_chat(user, "You inject yourself with [src].")
add_attack_logs(user, M, attack_log, ATKLOG_ALL)
- if(!iscarbon(user))
- M.LAssailant = null
- else
- M.LAssailant = user
inject(M, user)
used = TRUE
diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm
index 5c69a59aba9..9584130c0c6 100644
--- a/code/game/objects/items/weapons/explosives.dm
+++ b/code/game/objects/items/weapons/explosives.dm
@@ -62,7 +62,7 @@
return
var/newtime = input(usr, "Please set the timer.", "Timer", det_time) as num
if(user.is_in_active_hand(src))
- newtime = Clamp(newtime, 10, 60000)
+ newtime = clamp(newtime, 10, 60000)
det_time = newtime
to_chat(user, "Timer set for [det_time] seconds.")
diff --git a/code/game/objects/items/weapons/garrote.dm b/code/game/objects/items/weapons/garrote.dm
index 4bddaf551ea..e3c145e790b 100644
--- a/code/game/objects/items/weapons/garrote.dm
+++ b/code/game/objects/items/weapons/garrote.dm
@@ -104,7 +104,7 @@
playsound(src.loc, 'sound/weapons/cablecuff.ogg', 15, 1, -1)
M.visible_message("[U] comes from behind and begins garroting [M] with the [src]!", \
- "[U]\ begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \
+ "[U] begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \
"You hear struggling and wire strain against flesh!")
return
diff --git a/code/game/objects/items/weapons/grenades/clowngrenade.dm b/code/game/objects/items/weapons/grenades/clowngrenade.dm
index cfb2e1cad3d..1dcf250dfe4 100644
--- a/code/game/objects/items/weapons/grenades/clowngrenade.dm
+++ b/code/game/objects/items/weapons/grenades/clowngrenade.dm
@@ -13,22 +13,12 @@
/obj/item/grenade/clown_grenade/prime()
..()
playsound(src.loc, 'sound/items/bikehorn.ogg', 25, -3)
- /*
- for(var/turf/simulated/floor/T in view(affected_area, src.loc))
- if(prob(75))
- banana(T)
- */
var/i = 0
var/number = 0
for(var/direction in GLOB.alldirs)
for(i = 0; i < 2; i++)
number++
var/obj/item/grown/bananapeel/traitorpeel/peel = new /obj/item/grown/bananapeel/traitorpeel(get_turf(src.loc))
- /* var/direction = pick(alldirs)
- var/spaces = pick(1;150, 2)
- var/a = 0
- for(a = 0; a < spaces; a++)
- step(peel,direction)*/
var/a = 1
if(number & 2)
for(a = 1; a <= 2; a++)
@@ -39,21 +29,17 @@
qdel(src)
return
-/obj/item/grown/bananapeel/traitorpeel
- trip_stun = 0
- trip_weaken = 7
- trip_tiles = 4
- trip_walksafe = FALSE
-
- trip_chance = 100
-
-
-/obj/item/grown/bananapeel/traitorpeel/on_trip(mob/living/carbon/human/H)
+/obj/item/grown/bananapeel/traitorpeel/New(newloc, obj/item/seeds/new_seed)
. = ..()
- if(.)
- to_chat(H, "Your feet feel like they're on fire!")
- H.take_overall_damage(0, rand(2,8))
- H.take_organ_damage(2) // Was 5 -- TLE
+ // The reason this AddComponent is here and not in ComponentInitialize() is because if it's put there, it will be ran before the parent New proc for /grown types.
+ // And then be overriden by the generic component placed onto it by the `/datum/plant_gene/trait/slip`.
+ AddComponent(/datum/component/slippery, src, 0, 7, 100, 4, FALSE)
+
+/obj/item/grown/bananapeel/traitorpeel/after_slip(mob/living/carbon/human/H)
+ to_chat(H, "Your feet feel like they're on fire!")
+ H.take_overall_damage(0, rand(2,8))
+ H.take_organ_damage(2)
+ return ..()
/obj/item/grown/bananapeel/traitorpeel/throw_impact(atom/hit_atom)
var/burned = rand(1,3)
diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm
index b3028f13c90..13f9dd8f49d 100644
--- a/code/game/objects/items/weapons/grenades/flashbang.dm
+++ b/code/game/objects/items/weapons/grenades/flashbang.dm
@@ -5,39 +5,51 @@
origin_tech = "materials=2;combat=3"
light_power = 10
light_color = LIGHT_COLOR_WHITE
- var/light_time = 2
- var/range = 7
+
+ var/light_time = 0.2 SECONDS // The duration the area is illuminated
+ var/range = 7 // The range in tiles of the flashbang
/obj/item/grenade/flashbang/prime()
update_mob()
- var/flashbang_turf = get_turf(src)
- if(!flashbang_turf)
- return
+ var/turf/T = get_turf(src)
+ if(T)
+ // VFX and SFX
+ do_sparks(rand(5, 9), FALSE, src)
+ playsound(T, 'sound/effects/bang.ogg', 100, TRUE)
+ new /obj/effect/dummy/lighting_obj(T, light_color, range + 2, light_power, light_time)
- set_light(7)
-
- do_sparks(rand(5, 9), FALSE, src)
- playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1)
- bang(flashbang_turf, src, range)
-
- for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here
- var/damage = round(30 / (get_dist(B, get_turf(src)) + 1))
- B.take_damage(damage, BURN, "melee", 0)
-
- spawn(light_time)
- qdel(src)
+ // Stunning & damaging mechanic
+ bang(T, src, range)
+ qdel(src)
+/**
+ * Creates a flashing effect that blinds and deafens mobs within range
+ *
+ * Also damages blobs
+ * Arguments:
+ * * T - The turf to flash
+ * * A - The flashing atom
+ * * range - The range in tiles of the flash
+ * * flash - Whether to flash (blind)
+ * * bang - Whether to bang (deafen)
+ */
/proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE)
+ // Blob damage
+ for(var/obj/structure/blob/B in hear(range + 1, T))
+ var/damage = round(30 / (get_dist(B, T) + 1))
+ B.take_damage(damage, BURN, "melee", FALSE)
+
+ // Flashing mechanic
+ var/source_turf = get_turf(A)
for(var/mob/living/M in hearers(range, T))
if(M.stat == DEAD)
continue
M.show_message("BANG", 2)
- //Checking for protections
- var/ear_safety = M.check_ear_prot()
- var/distance = max(1, get_dist(get_turf(A), get_turf(M)))
+ var/distance = max(1, get_dist(source_turf, get_turf(M)))
+ var/stun_amount = max(10 / distance, 3)
- //Flash
+ // Flash
if(flash)
if(M.weakeyes)
M.visible_message("[M] screams and collapses!")
@@ -49,21 +61,20 @@
var/mob/living/carbon/human/H = M
var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes)
if(E)
- E.receive_damage(8, 1)
-
+ E.receive_damage(8, TRUE)
if(M.flash_eyes(affect_silicon = TRUE))
- M.Stun(max(10 / distance, 3))
- M.Weaken(max(10 / distance, 3))
+ M.Stun(stun_amount)
+ M.Weaken(stun_amount)
-
- //Bang
+ // Bang
+ var/ear_safety = M.check_ear_prot()
if(bang)
- if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection
+ if(!distance || A.loc == M || A.loc == M.loc) // Holding on person or being exactly where lies is significantly more dangerous and voids protection
M.Stun(10)
M.Weaken(10)
if(!ear_safety)
- M.Stun(max(10 / distance, 3))
- M.Weaken(max(10 / distance, 3))
+ M.Stun(stun_amount)
+ M.Weaken(stun_amount)
M.AdjustEarDamage(rand(0, 5), 15)
if(iscarbon(M))
var/mob/living/carbon/C = M
@@ -74,6 +85,5 @@
if(prob(ears.ear_damage - 5))
to_chat(M, "You can't hear anything!")
M.BecomeDeaf()
- else
- if(ears.ear_damage >= 5)
- to_chat(M, "Your ears start to ring!")
+ else if(ears.ear_damage >= 5)
+ to_chat(M, "Your ears start to ring!")
diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm
index 6dd07485f5b..53061e5fa61 100644
--- a/code/game/objects/items/weapons/grenades/smokebomb.dm
+++ b/code/game/objects/items/weapons/grenades/smokebomb.dm
@@ -19,7 +19,7 @@
/obj/item/grenade/smokebomb/prime()
playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
- src.smoke.set_up(10, 0, usr.loc)
+ smoke.set_up(10, 0)
spawn(0)
src.smoke.start()
sleep(10)
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 1f73e5c7062..69b4abbe10d 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -126,13 +126,15 @@
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
..()
-/obj/item/restraints/handcuffs/cable/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- colorC = color_rainbow()
- color = colorC
- else
+/obj/item/restraints/handcuffs/cable/proc/cable_color(colorC)
+ if(!colorC)
color = COLOR_RED
+ else if(colorC == "rainbow")
+ color = color_rainbow()
+ else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
+ color = COLOR_ORANGE
+ else
+ color = colorC
/obj/item/restraints/handcuffs/cable/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 4efb6994e8d..974bd3b677f 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -255,7 +255,7 @@
possessed = TRUE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, FALSE, 10 SECONDS, source = src)
var/mob/dead/observer/theghost = null
if(candidates.len)
@@ -488,7 +488,7 @@
var/mob/living/carbon/human/holder = loc
if(src == holder.l_hand || src == holder.r_hand) // Holding this in your hand will
for(var/mob/living/carbon/human/H in range(5, loc))
- if(H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full))
+ if(H.mind && H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full))
H.mind.vampire.nullified = max(5, H.mind.vampire.nullified + 2)
if(prob(10))
to_chat(H, "Being in the presence of [holder]'s [src] is interfering with your powers!")
diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm
index 740079db8e1..1bfeeabae03 100644
--- a/code/game/objects/items/weapons/lighters.dm
+++ b/code/game/objects/items/weapons/lighters.dm
@@ -244,12 +244,18 @@
else
..()
+/obj/item/match/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(burnt)
+ C.stored_comms["wood"] += 1
+ qdel(src)
+ return TRUE
+ return ..()
+
/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
-
/obj/item/match/firebrand
name = "firebrand"
desc = "An unlit firebrand. It makes you wonder why it's not just called a stick."
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index d91bb29ff76..bab2ff02fbb 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -5,6 +5,7 @@
icon = 'icons/obj/library.dmi'
due_date = 0 // Game time in 1/10th seconds
unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
+ has_drm = TRUE // No reuploading. Piracy is a crime
/obj/item/book/manual/engineering_construction
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 006de63af17..f6fb213b353 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -6,7 +6,7 @@
var/list/nemesis_factions //Any mob with a faction that exists in this list will take bonus damage/effects
w_class = WEIGHT_CLASS_SMALL
var/w_class_on = WEIGHT_CLASS_BULKY
- var/icon_state_on = "axe1"
+ var/icon_state_on
var/list/attack_verb_on = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
hitsound = 'sound/weapons/blade1.ogg' // Probably more appropriate than the previous hitsound. -- Dave
usesound = 'sound/weapons/blade1.ogg'
@@ -48,9 +48,9 @@
throw_speed = 4
if(attack_verb_on.len)
attack_verb = attack_verb_on
- if(!item_color)
+ if(icon_state_on)
icon_state = icon_state_on
- set_light(brightness_on)
+ set_light(brightness_on, l_color = item_color ? colormap[item_color] : null)
else
icon_state = "sword[item_color]"
set_light(brightness_on, l_color=colormap[item_color])
@@ -80,6 +80,7 @@
name = "energy axe"
desc = "An energised battle axe."
icon_state = "axe0"
+ icon_state_on = "axe1"
force = 40
force_on = 150
throwforce = 25
@@ -301,9 +302,9 @@
throw_speed = 4
if(attack_verb_on.len)
attack_verb = attack_verb_on
- if(!item_color)
+ if(icon_state_on)
icon_state = icon_state_on
- set_light(brightness_on)
+ set_light(brightness_on, l_color = item_color ? colormap[item_color] : null)
else
icon_state = "sword[item_color]"
set_light(brightness_on, l_color=colormap[item_color])
diff --git a/code/game/objects/items/weapons/shards.dm b/code/game/objects/items/weapons/shards.dm
index 52c2aea5ddb..f987f047f11 100644
--- a/code/game/objects/items/weapons/shards.dm
+++ b/code/game/objects/items/weapons/shards.dm
@@ -83,11 +83,16 @@
/obj/item/shard/Crossed(mob/living/L, oldloc)
if(istype(L) && has_gravity(loc))
- if(L.incorporeal_move || L.flying)
+ if(L.incorporeal_move || L.flying || L.floating)
return
playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE)
return ..()
+/obj/item/shard/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["glass"] += 3
+ qdel(src)
+ return TRUE
+
/obj/item/shard/plasma
name = "plasma shard"
desc = "A shard of plasma glass. Considerably tougher then normal glass shards. Apparently not tough enough to be a window."
diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm
index 62f65b5a6cc..cb220ff9565 100644
--- a/code/game/objects/items/weapons/soap.dm
+++ b/code/game/objects/items/weapons/soap.dm
@@ -11,15 +11,11 @@
throw_speed = 4
throw_range = 20
discrete = 1
-
- trip_stun = 4
- trip_weaken = 2
- trip_chance = 100
- trip_walksafe = FALSE
- trip_verb = TV_SLIP
-
var/cleanspeed = 50 //slower than mop
+/obj/item/soap/ComponentInitialize()
+ AddComponent(/datum/component/slippery, src, 4, 2, 100, 0, FALSE)
+
/obj/item/soap/afterattack(atom/target, mob/user, proximity)
if(!proximity) return
//I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing.
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 26045731bd9..e7880fd0868 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -372,6 +372,15 @@
new /obj/item/ammo_box/magazine/m12g/buckshot(src)
new /obj/item/ammo_box/magazine/m12g/dragon(src)
+/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
+ desc = "A large duffelbag, containing three types of extended drum magazines."
+
+/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags/New()
+ ..()
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg(src)
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot(src)
+ new /obj/item/ammo_box/magazine/m12g/XtrLrg/dragon(src)
+
/obj/item/storage/backpack/duffel/mining_conscript/
name = "mining conscription kit"
desc = "A kit containing everything a crewmember needs to support a shaft miner in the field."
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index 66288713775..121782b2e8e 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -33,9 +33,11 @@
icon_state = "trashbag"
item_state = "trashbag"
- w_class = WEIGHT_CLASS_TINY
+ w_class = WEIGHT_CLASS_BULKY
max_w_class = WEIGHT_CLASS_SMALL
+ slot_flags = null
storage_slots = 30
+ max_combined_w_class = 30
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
@@ -45,18 +47,15 @@
return TOXLOSS
/obj/item/storage/bag/trash/update_icon()
- if(contents.len == 0)
- w_class = WEIGHT_CLASS_TINY
- icon_state = "[initial(icon_state)]"
- else if(contents.len < 12)
- w_class = WEIGHT_CLASS_BULKY
- icon_state = "[initial(icon_state)]1"
- else if(contents.len < 21)
- w_class = WEIGHT_CLASS_BULKY
- icon_state = "[initial(icon_state)]2"
- else
- w_class = WEIGHT_CLASS_BULKY
- icon_state = "[initial(icon_state)]3"
+ switch(contents.len)
+ if(20 to INFINITY)
+ icon_state = "[initial(icon_state)]3"
+ if(11 to 20)
+ icon_state = "[initial(icon_state)]2"
+ if(1 to 11)
+ icon_state = "[initial(icon_state)]1"
+ else
+ icon_state = "[initial(icon_state)]"
/obj/item/storage/bag/trash/cyborg
@@ -381,12 +380,13 @@
w_class = WEIGHT_CLASS_BULKY
flags = CONDUCT
materials = list(MAT_METAL=3000)
+ cant_hold = list(/obj/item/disk/nuclear) // Prevents some cheesing
-/obj/item/storage/bag/tray/attack(mob/living/M as mob, mob/living/user as mob)
+/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
- quick_empty()
+ drop_inventory(user)
// Make each item scatter a bit
for(var/obj/item/I in oldContents)
@@ -422,13 +422,13 @@
/obj/item/storage/bag/tray/cyborg/afterattack(atom/target, mob/user as mob)
if( isturf(target) || istype(target,/obj/structure/table) )
- var foundtable = istype(target,/obj/structure/table/)
+ var/foundtable = istype(target,/obj/structure/table/)
if( !foundtable ) //it must be a turf!
for(var/obj/structure/table/T in target)
foundtable = 1
break
- var turf/dropspot
+ var/turf/dropspot
if( !foundtable ) // don't unload things onto walls or other silly places.
dropspot = user.loc
else if( isturf(target) ) // they clicked on a turf with a table in it
@@ -438,7 +438,7 @@
overlays = null
- var droppedSomething = 0
+ var/droppedSomething = 0
for(var/obj/item/I in contents)
I.loc = dropspot
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index 403088e2b71..0be9178967c 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -204,8 +204,10 @@
/obj/item/flashlight/pen,
/obj/item/seeds,
/obj/item/wirecutters,
- /obj/item/wrench,
- )
+ /obj/item/wrench,
+ /obj/item/reagent_containers/spray/weedspray,
+ /obj/item/reagent_containers/spray/pestspray
+ )
/obj/item/storage/belt/security
name = "security belt"
@@ -308,6 +310,18 @@
item_state = "utility"
use_item_overlays = 1 // So it will still show tools in it in case sec get lazy and just glance at it.
+/obj/item/storage/belt/military/traitor/hacker
+
+/obj/item/storage/belt/military/traitor/hacker/New()
+ ..()
+ new /obj/item/screwdriver(src, "red")
+ new /obj/item/wrench(src)
+ new /obj/item/weldingtool/largetank(src)
+ new /obj/item/crowbar/red(src)
+ new /obj/item/wirecutters(src, "red")
+ new /obj/item/stack/cable_coil(src, 30, COLOR_RED)
+ update_icon()
+
/obj/item/storage/belt/grenade
name = "grenadier belt"
desc = "A belt for holding grenades."
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index da0094bb2ef..9ef94a861a0 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -846,7 +846,7 @@
desc = "A small box of Almost But Not Quite Plasma Premium Matches."
icon = 'icons/obj/cigarettes.dmi'
icon_state = "matchbox"
- item_state = "zippo"
+ item_state = "matchbox"
storage_slots = 10
w_class = WEIGHT_CLASS_TINY
max_w_class = WEIGHT_CLASS_TINY
diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm
index f0ab5bd5ac7..f86ebb2f69d 100644
--- a/code/game/objects/items/weapons/storage/fancy.dm
+++ b/code/game/objects/items/weapons/storage/fancy.dm
@@ -215,18 +215,25 @@
//if we get this far, handle the insertion checks as normal
.=..()
+/obj/item/storage/fancy/cigarettes/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(!length(contents))
+ C.stored_comms["wood"] += 1
+ qdel(src)
+ return TRUE
+ return ..()
+
/obj/item/storage/fancy/cigarettes/dromedaryco
name = "\improper DromedaryCo packet"
desc = "A packet of six imported DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\""
icon_state = "Dpacket"
- item_state = "cigpacket"
+ item_state = "Dpacket"
/obj/item/storage/fancy/cigarettes/syndicate
name = "\improper Syndicate Cigarettes"
desc = "A packet of six evil-looking cigarettes, A label on the packaging reads, \"Donk Co\""
icon_state = "robustpacket"
- item_state = "cigpacket"
+ item_state = "robustpacket"
/obj/item/storage/fancy/cigarettes/syndicate/New()
..()
@@ -237,14 +244,14 @@
name = "cigarette packet"
desc = "An obscure brand of cigarettes."
icon_state = "syndiepacket"
- item_state = "cigpacket"
+ item_state = "syndiepacket"
cigarette_type = /obj/item/clothing/mask/cigarette/syndicate
/obj/item/storage/fancy/cigarettes/cigpack_med
name = "Medical Marijuana Packet"
desc = "A prescription packet containing six marijuana cigarettes."
icon_state = "medpacket"
- item_state = "cigpacket"
+ item_state = "medpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/medical_marijuana
@@ -252,46 +259,46 @@
name = "\improper Uplift Smooth packet"
desc = "Your favorite brand, now menthol flavored."
icon_state = "upliftpacket"
- item_state = "cigpacket"
+ item_state = "upliftpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/menthol
/obj/item/storage/fancy/cigarettes/cigpack_robust
name = "\improper Robust packet"
desc = "Smoked by the robust."
icon_state = "robustpacket"
- item_state = "cigpacket"
+ item_state = "robustpacket"
/obj/item/storage/fancy/cigarettes/cigpack_robustgold
name = "\improper Robust Gold packet"
desc = "Smoked by the truly robust."
icon_state = "robustgpacket"
- item_state = "cigpacket"
+ item_state = "robustgpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/robustgold
/obj/item/storage/fancy/cigarettes/cigpack_carp
name = "\improper Carp Classic packet"
desc = "Since 2313."
icon_state = "carppacket"
- item_state = "cigpacket"
+ item_state = "carppacket"
/obj/item/storage/fancy/cigarettes/cigpack_midori
name = "\improper Midori Tabako packet"
desc = "You can't understand the runes, but the packet smells funny."
icon_state = "midoripacket"
- item_state = "cigpacket"
+ item_state = "midoripacket"
/obj/item/storage/fancy/cigarettes/cigpack_shadyjims
name ="\improper Shady Jim's Super Slims"
desc = "Is your weight slowing you down? Having trouble running away from gravitational singularities? Can't stop stuffing your mouth? Smoke Shady Jim's Super Slims and watch all that fat burn away. Guaranteed results!"
icon_state = "shadyjimpacket"
- item_state = "cigpacket"
+ item_state = "shadyjimpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/shadyjims
/obj/item/storage/fancy/cigarettes/cigpack_random
name ="\improper Embellished Enigma packet"
desc = "For the true connoisseur of exotic flavors."
icon_state = "shadyjimpacket"
- item_state = "cigpacket"
+ item_state = "shadyjimpacket"
cigarette_type = /obj/item/clothing/mask/cigarette/random
/obj/item/storage/fancy/rollingpapers
@@ -300,6 +307,7 @@
w_class = WEIGHT_CLASS_TINY
icon = 'icons/obj/cigarettes.dmi'
icon_state = "cig_paper_pack"
+ item_state = "cig_paper_pack"
storage_slots = 10
icon_type = "rolling paper"
can_hold = list(/obj/item/rollingpaper)
diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm
index 4ff769a7d3f..1e22015bc97 100644
--- a/code/game/objects/items/weapons/storage/firstaid.dm
+++ b/code/game/objects/items/weapons/storage/firstaid.dm
@@ -205,7 +205,7 @@
if(empty)
return
new /obj/item/reagent_containers/hypospray/combat(src)
- new /obj/item/reagent_containers/applicator/dual(src) // Because you ain't got no time to look at what damage dey taking yo
+ new /obj/item/reagent_containers/applicator/dual/syndi(src) // Because you ain't got no time to look at what damage dey taking yo
new /obj/item/defibrillator/compact/combat/loaded(src)
new /obj/item/clothing/glasses/hud/health/night(src)
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 55b25ad725a..49b6c6a11ea 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -438,8 +438,11 @@
if((!ishuman(usr) && (src.loc != usr)) || usr.stat || usr.restrained())
return
+ drop_inventory(usr)
+
+/obj/item/storage/proc/drop_inventory(user)
var/turf/T = get_turf(src)
- hide_from(usr)
+ hide_from(user)
for(var/obj/item/I in contents)
remove_from_storage(I, T)
CHECK_TICK
@@ -499,10 +502,9 @@
/obj/item/storage/attack_self(mob/user)
- //Clicking on itself will empty it, if it has the verb to do that.
- if(user.is_in_active_hand(src))
- if(verbs.Find(/obj/item/storage/verb/quick_empty))
- quick_empty()
+ //Clicking on itself will empty it, if allow_quick_empty is TRUE
+ if(allow_quick_empty && user.is_in_active_hand(src))
+ drop_inventory(user)
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
//Returns -1 if the atom was not found on container.
@@ -541,7 +543,7 @@
return depth
/obj/item/storage/serialize()
- var data = ..()
+ var/data = ..()
var/list/content_list = list()
data["content"] = content_list
data["slots"] = storage_slots
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index f965907c5ba..704538ee2af 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -1,7 +1,7 @@
/obj/item/storage/box/syndicate/New()
..()
switch(pickweight(list("bloodyspai" = 1, "thief" = 1, "bond" = 1, "sabotage" = 1, "payday" = 1, "implant" = 1, "hacker" = 1, "darklord" = 1, "professional" = 1)))
- if("bloodyspai") // 35TC + one 0TC
+ if("bloodyspai") // 37TC + one 0TC
new /obj/item/clothing/under/chameleon(src) // 2TC
new /obj/item/clothing/mask/chameleon(src) // 0TC
new /obj/item/card/id/syndicate(src) // 2TC
@@ -14,12 +14,12 @@
new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src) // 2TC
new /obj/item/flashlight/emp(src) // 2TC
new /obj/item/clothing/glasses/hud/security/chameleon(src) // 2TC
- new /obj/item/chameleon(src) // 8TC
+ new /obj/item/chameleon(src) // 7TC
return
- if("thief") // 40TC
+ if("thief") // 39TC
new /obj/item/gun/energy/kinetic_accelerator/crossbow(src) // 12TC
- new /obj/item/chameleon(src) // 8TC
+ new /obj/item/chameleon(src) // 7TC
new /obj/item/clothing/glasses/chameleon/thermal(src) // 6TC
new /obj/item/clothing/gloves/color/black/thief(src) // 6TC
new /obj/item/card/id/syndicate(src) // 2TC
@@ -43,7 +43,7 @@
new /obj/item/CQC_manual(src) // 13TC
return
- if("sabotage") // 47TC + three 0TC
+ if("sabotage") // 41TC + two 0TC
new /obj/item/grenade/plastic/c4(src) // 1TC
new /obj/item/grenade/plastic/c4(src) // 1TC
new /obj/item/camera_bug(src) // 1TC
@@ -53,23 +53,23 @@
new /obj/item/card/emag(src) // 6TC
new /obj/item/clothing/gloves/color/yellow(src) // 0TC
new /obj/item/grenade/syndieminibomb(src) // 6TC
- new /obj/item/grenade/clusterbuster/n2o(src) // 0TC
+ new /obj/item/grenade/clusterbuster/n2o(src) // 4TC
new /obj/item/storage/box/syndie_kit/space(src) // 4TC
new /obj/item/encryptionkey/syndicate(src) // 2TC
return
- if("payday") // 33TC + four 0TC
+ if("payday") // 35TC + four 0TC
new /obj/item/gun/projectile/revolver(src) // 13TC
new /obj/item/ammo_box/a357(src) // 3TC
new /obj/item/ammo_box/a357(src) // 3TC
new /obj/item/card/emag(src) // 6TC
- new /obj/item/grenade/plastic/c4(src) // 1TC
+ new /obj/item/jammer(src) // 5TC
new /obj/item/card/id/syndicate(src) // 2TC
new /obj/item/clothing/under/suit_jacket/really_black(src) //0TC
new /obj/item/clothing/suit/storage/lawyer/blackjacket/armored(src) //0TC
new /obj/item/clothing/gloves/color/latex/nitrile(src) //0 TC
new /obj/item/clothing/mask/gas/clown_hat(src) // 0TC
- new /obj/item/thermal_drill(src) // 3TC
+ new /obj/item/thermal_drill/diamond_drill(src) // 1TC
new /obj/item/encryptionkey/syndicate(src) // 2TC
return
@@ -83,17 +83,20 @@
new /obj/item/encryptionkey/syndicate(src) // 2TC
return
- if("hacker") // 30TC + one 0TC
+ if("hacker") // 37TC + two 0TC
new /obj/item/aiModule/syndicate(src) // 12TC
new /obj/item/card/emag(src) // 6TC
new /obj/item/encryptionkey/syndicate(src) // 2TC
new /obj/item/encryptionkey/binary(src) // 5TC
new /obj/item/aiModule/toyAI(src) // 0TC
+ new /obj/item/clothing/glasses/chameleon/thermal(src) // 6TC
+ new /obj/item/storage/belt/military/traitor/hacker(src) // 3TC
+ new /obj/item/clothing/gloves/combat(src) // 0TC
new /obj/item/multitool/ai_detect(src) // 1TC
- new /obj/item/storage/box/syndie_kit/c4 // 4TC
+ new /obj/item/flashlight/emp(src) // 2TC
return
- if("darklord") // 22TC + two 0TC
+ if("darklord") // 24TC + two 0TC
new /obj/item/melee/energy/sword/saber/red(src) // 8TC
new /obj/item/melee/energy/sword/saber/red(src) // 8TC
new /obj/item/dnainjector/telemut/darkbundle(src) // 0TC
@@ -104,7 +107,7 @@
new /obj/item/encryptionkey/syndicate(src) // 2TC
return
- if("professional") // 32 TC + two 0TC
+ if("professional") // 34TC + two 0TC
new /obj/item/gun/projectile/automatic/sniper_rifle/syndicate/penetrator(src) // 16TC
new /obj/item/ammo_box/magazine/sniper_rounds/penetrator(src) // 5TC
new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) // 3TC
@@ -121,19 +124,6 @@
desc = "A sleek, sturdy box"
icon_state = "box_of_doom"
-/obj/item/storage/box/syndie_kit/romerol
- name = "Romerol Kit"
- desc = "A box containing a deadly virus capable of reanimating dead as zombies."
- max_w_class = WEIGHT_CLASS_NORMAL
- can_hold = list(/obj/item/reagent_containers/glass/bottle/romerol,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/dropper)
-
-/obj/item/storage/box/syndie_kit/romerol/New()
- ..()
- new /obj/item/reagent_containers/glass/bottle/romerol(src)
- new /obj/item/reagent_containers/syringe(src)
- new /obj/item/reagent_containers/dropper(src)
- return
-
/obj/item/storage/box/syndie_kit/space
name = "Boxed Space Suit and Helmet"
can_hold = list(/obj/item/clothing/suit/space/syndicate/black/red, /obj/item/clothing/head/helmet/space/syndicate/black/red, /obj/item/tank/emergency_oxygen/syndi, /obj/item/clothing/mask/gas/syndicate)
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 6782057d928..a39050cba47 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -189,8 +189,8 @@
L.apply_effect(STUTTER, stunforce)
if(user)
- user.lastattacked = L
- L.lastattacker = user
+ L.lastattacker = user.real_name
+ L.lastattackerckey = user.ckey
L.visible_message("[user] has stunned [L] with [src]!", \
"[user] has stunned you with [src]!")
add_attack_logs(user, L, "stunned")
diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm
deleted file mode 100644
index e75935a3cc2..00000000000
--- a/code/game/objects/items/weapons/swords_axes_etc.dm
+++ /dev/null
@@ -1,119 +0,0 @@
-/* Weapons
- * Contains:
- * Banhammer
- * Classic Baton
- */
-
-/*
- * Banhammer
- */
-/obj/item/banhammer/attack(mob/M, mob/user)
- to_chat(M, " You have been banned FOR NO REISIN by [user]")
- to_chat(user, " You have BANNED [M]")
- playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
-
-/*
- * Classic Baton
- */
-
-/obj/item/melee/classic_baton
- name = "police baton"
- desc = "A wooden truncheon for beating criminal scum."
- icon_state = "baton"
- item_state = "classic_baton"
- slot_flags = SLOT_BELT
- force = 12 //9 hit crit
- w_class = WEIGHT_CLASS_NORMAL
- var/cooldown = 0
- var/on = 1
-
-/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob)
- if(on)
- add_fingerprint(user)
- if((CLUMSY in user.mutations) && prob(50))
- to_chat(user, "You club yourself over the head.")
- user.Weaken(3 * force)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.apply_damage(2*force, BRUTE, "head")
- else
- user.take_organ_damage(2*force)
- return
- if(isrobot(target))
- ..()
- return
- if(!isliving(target))
- return
- if(user.a_intent == INTENT_HARM)
- if(!..()) return
- if(!isrobot(target)) return
- else
- if(cooldown <= 0)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
- return
- if(check_martial_counter(H, user))
- return
- playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1)
- target.Weaken(3)
- add_attack_logs(user, target, "Stunned with [src]")
- add_fingerprint(user)
- target.visible_message("[user] has knocked down [target] with \the [src]!", \
- "[user] has knocked down [target] with \the [src]!")
- if(!iscarbon(user))
- target.LAssailant = null
- else
- target.LAssailant = user
- cooldown = 1
- spawn(40)
- cooldown = 0
- return
- else
- return ..()
-
-/obj/item/melee/classic_baton/ntcane
- name = "fancy cane"
- desc = "A cane with special engraving on it. It seems well suited for fending off assailants..."
- icon_state = "cane_nt"
- item_state = "cane_nt"
- needs_permit = 0
-
-/obj/item/melee/classic_baton/ntcane/is_crutch()
- return 1
-
-//Telescopic baton
-/obj/item/melee/classic_baton/telescopic
- name = "telescopic baton"
- desc = "A compact yet robust personal defense weapon. Can be concealed when folded."
- icon_state = "telebaton_0"
- item_state = null
- slot_flags = SLOT_BELT
- w_class = WEIGHT_CLASS_SMALL
- needs_permit = 0
- force = 0
- on = 0
-
-/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob)
- on = !on
- if(on)
- to_chat(user, "You extend the baton.")
- icon_state = "telebaton_1"
- item_state = "nullrod"
- w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance
- force = 10 //stunbaton damage
- attack_verb = list("smacked", "struck", "cracked", "beaten")
- else
- to_chat(user, "You collapse the baton.")
- icon_state = "telebaton_0"
- item_state = null //no sprite for concealment even when in hand
- slot_flags = SLOT_BELT
- w_class = WEIGHT_CLASS_SMALL
- force = 0 //not so robust now
- attack_verb = list("hit", "poked")
- if(istype(user,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = user
- H.update_inv_l_hand()
- H.update_inv_r_hand()
- playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
- add_fingerprint(user)
diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm
index bc783457e76..edf32e2f5a6 100644
--- a/code/game/objects/items/weapons/tanks/jetpack.dm
+++ b/code/game/objects/items/weapons/tanks/jetpack.dm
@@ -219,28 +219,3 @@
turn_off(cur_user)
return
..()
-
-/obj/item/tank/jetpack/rig
- name = "jetpack"
- var/obj/item/rig/holder
- actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization)
-
-/obj/item/tank/jetpack/rig/examine()
- . = list("It's a jetpack. If you can see this, report it on the bug tracker.")
-
-/obj/item/tank/jetpack/rig/allow_thrust(num, mob/living/user)
- if(!on)
- return 0
-
- if(!istype(holder) || !holder.air_supply)
- return 0
-
- var/datum/gas_mixture/removed = holder.air_supply.air_contents.remove(num)
- if(removed.total_moles() < 0.005)
- turn_off(user)
- return 0
-
- var/turf/T = get_turf(user)
- T.assume_air(removed)
-
- return 1
diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm
index 11355b34170..a82d6c0c223 100644
--- a/code/game/objects/items/weapons/tanks/tank_types.dm
+++ b/code/game/objects/items/weapons/tanks/tank_types.dm
@@ -53,13 +53,8 @@ obj/item/tank/oxygen/empty/New()
/obj/item/tank/anesthetic/New()
..()
-
- air_contents.oxygen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
-
- var/datum/gas/sleeping_agent/trace_gas = new()
- trace_gas.moles = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
-
- air_contents.trace_gases += trace_gas
+ air_contents.oxygen = (3 * ONE_ATMOSPHERE) * 70 / (R_IDEAL_GAS_EQUATION * T20C) * O2STANDARD
+ air_contents.sleeping_agent = (3 * ONE_ATMOSPHERE) * 70 / (R_IDEAL_GAS_EQUATION * T20C) * N2STANDARD
/*
* Air
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 9ded7947202..8e05185baae 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -43,35 +43,31 @@
/obj/item/tank/proc/toggle_internals(mob/user, silent = FALSE)
var/mob/living/carbon/C = user
if(!istype(C))
- return 0
+ return FALSE
if(C.internal == src)
to_chat(C, "You close \the [src] valve.")
C.internal = null
else
- var/can_open_valve = 0
- if(C.get_organ_slot("breathing_tube"))
- can_open_valve = 1
- else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT)
- can_open_valve = 1
- else if(ishuman(C))
- var/mob/living/carbon/human/H = C
- if(H.head && H.head.flags & AIRTIGHT)
- can_open_valve = 1
+ if(!C.get_organ_slot("breathing_tube")) // Breathing tubes can always use internals, if they have one, skip ahead and turn internals on/off
+ if(!C.wear_mask) // Do we have a mask equipped?
+ return FALSE
- if(can_open_valve)
+ var/obj/item/clothing/mask/M = C.wear_mask
+ // If the "mask" isn't actually a mask OR That mask isn't internals compatible AND Their headgear isn't internals compatible
+ if(!istype(M) || (!(initial(M.flags) & AIRTIGHT) && !(C.head.flags & AIRTIGHT)))
+ if(!silent)
+ to_chat(C, "You are not wearing a suitable mask or helmet.")
+ return FALSE
+ if(M.mask_adjusted) // If the mask is equipped but pushed down
+ M.adjustmask(C) // Adjust it back
+
+ if(!silent)
if(C.internal)
- if(!silent)
- to_chat(C, "You switch your internals to [src].")
+ to_chat(C, "You switch your internals to [src].")
else
- if(!silent)
- to_chat(C, "You open \the [src] valve.")
- C.internal = src
- else
- if(!silent)
- to_chat(C, "You are not wearing a suitable mask or helmet.")
- return 0
-
+ to_chat(C, "You open \the [src] valve.")
+ C.internal = src
C.update_action_buttons_icon()
@@ -142,70 +138,55 @@
if(!(air_contents))
return
- ui_interact(user)
+ tgui_interact(user)
-/obj/item/tank/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 = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/item/tank/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
+ ui = SStgui.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
- ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300)
- // open the new ui window
+ ui = new(user, src, ui_key, "Tank", name, 300, 150, master_ui, state)
ui.open()
- // auto update every Master Controller tick
- ui.set_auto_update(1)
-/obj/item/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/using_internal
- if(iscarbon(loc))
- var/mob/living/carbon/C = loc
- if(C.internal == src)
- using_internal = 1
-
- var/data[0]
+/obj/item/tank/tgui_data(mob/user)
+ var/list/data = list()
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0)
data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
+ data["minReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE)
- data["valveOpen"] = using_internal ? 1 : 0
-
- data["maskConnected"] = 0
-
- if(iscarbon(loc))
- var/mob/living/carbon/C = loc
- if(C.internal == src)
- data["maskConnected"] = 1
- else
- if(C.wear_mask && (C.wear_mask.flags & AIRTIGHT))
- data["maskConnected"] = 1
- else if(ishuman(C))
- var/mob/living/carbon/human/H = C
- if(H.head && (H.head.flags & AIRTIGHT))
- data["maskConnected"] = 1
-
+ var/mob/living/carbon/C = user
+ if(!istype(C))
+ C = loc.loc
+ if(!istype(C))
+ return data
+ data["has_mask"] = C.wear_mask ? TRUE : FALSE
+ data["connected"] = (C.internal && C.internal == src) ? TRUE : FALSE
return data
-/obj/item/tank/Topic(href, href_list)
+/obj/item/tank/tgui_act(action, params)
if(..())
- return 1
-
- if(href_list["dist_p"])
- if(href_list["dist_p"] == "reset")
- distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE
- else if(href_list["dist_p"] == "max")
- distribute_pressure = TANK_MAX_RELEASE_PRESSURE
+ return
+ . = TRUE
+ switch(action)
+ if("pressure")
+ var/pressure = params["pressure"]
+ if(pressure == "reset")
+ pressure = initial(distribute_pressure)
+ else if(pressure == "min")
+ pressure = TANK_DEFAULT_RELEASE_PRESSURE
+ else if(pressure == "max")
+ pressure = TANK_MAX_RELEASE_PRESSURE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ else
+ . = FALSE
+ if(.)
+ distribute_pressure = clamp(round(pressure), TANK_DEFAULT_RELEASE_PRESSURE, TANK_MAX_RELEASE_PRESSURE)
+ if("internals")
+ toggle_internals(usr)
else
- var/cp = text2num(href_list["dist_p"])
- distribute_pressure += cp
- distribute_pressure = min(max(round(distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE)
-
- if(href_list["stat"])
- toggle_internals(usr)
-
- add_fingerprint(usr)
- return 1
-
+ . = FALSE
+ if(.)
+ add_fingerprint(usr)
/obj/item/tank/remove_air(amount)
return air_contents.remove(amount)
diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm
index 4ac9651ea65..33aeddeaab0 100644
--- a/code/game/objects/items/weapons/teleportation.dm
+++ b/code/game/objects/items/weapons/teleportation.dm
@@ -115,7 +115,7 @@ Frequency:
to_chat(user, "\The [src] is malfunctioning.")
return
var/list/L = list( )
- for(var/obj/machinery/computer/teleporter/com in world)
+ for(var/obj/machinery/computer/teleporter/com in GLOB.machines)
if(com.target)
if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
L["[com.id] (Active)"] = com.target
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
deleted file mode 100644
index 012fcf79b10..00000000000
--- a/code/game/objects/items/weapons/tools.dm
+++ /dev/null
@@ -1,785 +0,0 @@
-#define HEALPERWELD 15
-
-/* Tools!
- * Note: Multitools are in devices
- *
- * Contains:
- * Wrench
- * Screwdriver
- * Wirecutters
- * Welding Tool
- * Crowbar
- * Revolver Conversion Kit
- */
-
-//Wrench
-/obj/item/wrench
- name = "wrench"
- desc = "A wrench with common uses. Can be found in your hand."
- icon = 'icons/obj/tools.dmi'
- icon_state = "wrench"
- flags = CONDUCT
- slot_flags = SLOT_BELT
- force = 5
- throwforce = 7
- usesound = 'sound/items/ratchet.ogg'
- w_class = WEIGHT_CLASS_SMALL
- materials = list(MAT_METAL=150)
- origin_tech = "materials=1;engineering=1"
- attack_verb = list("bashed", "battered", "bludgeoned", "whacked")
- toolspeed = 1
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
-
-/obj/item/wrench/suicide_act(mob/user)
- user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1)
- return BRUTELOSS
-
-/obj/item/wrench/cyborg
- name = "automatic wrench"
- desc = "An advanced robotic wrench. Can be found in construction cyborgs."
- toolspeed = 0.5
-
-/obj/item/wrench/brass
- name = "brass wrench"
- desc = "A brass wrench. It's faintly warm to the touch."
- icon_state = "wrench_brass"
- toolspeed = 0.5
- resistance_flags = FIRE_PROOF | ACID_PROOF
-
-/obj/item/wrench/abductor
- name = "alien wrench"
- desc = "A polarized wrench. It causes anything placed between the jaws to turn."
- icon = 'icons/obj/abductor.dmi'
- icon_state = "wrench"
- usesound = 'sound/effects/empulse.ogg'
- toolspeed = 0.1
- origin_tech = "materials=5;engineering=5;abductor=3"
-
-/obj/item/wrench/power
- name = "hand drill"
- desc = "A simple powered drill with a bolt bit."
- icon_state = "drill_bolt"
- item_state = "drill"
- usesound = 'sound/items/drill_use.ogg'
- materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
- origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get
- force = 8 //might or might not be too high, subject to change
- throwforce = 8
- attack_verb = list("drilled", "screwed", "jabbed")
- toolspeed = 0.25
-
-/obj/item/wrench/power/attack_self(mob/user)
- playsound(get_turf(user),'sound/items/change_drill.ogg', 50, 1)
- var/obj/item/wirecutters/power/s_drill = new /obj/item/screwdriver/power
- to_chat(user, "You attach the screwdriver bit to [src].")
- qdel(src)
- user.put_in_active_hand(s_drill)
-
-/obj/item/wrench/power/suicide_act(mob/user)
- user.visible_message("[user] is pressing [src] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS
-
-/obj/item/wrench/medical
- name = "medical wrench"
- desc = "A medical wrench with common (medical?) uses. Can be found in your hand."
- icon_state = "wrench_medical"
- force = 2 //MEDICAL
- throwforce = 4
- origin_tech = "materials=1;engineering=1;biotech=3"
- attack_verb = list("wrenched", "medicaled", "tapped", "jabbed", "whacked")
-
-/obj/item/wrench/medical/suicide_act(mob/user)
- user.visible_message("[user] is praying to the medical wrench to take [user.p_their()] soul. It looks like [user.p_theyre()] trying to commit suicide!")
- // TODO Make them glow with the power of the M E D I C A L W R E N C H
- // during their ascension
-
- // Stun stops them from wandering off
- user.Stun(5)
- playsound(loc, 'sound/effects/pray.ogg', 50, 1, -1)
-
- // Let the sound effect finish playing
- sleep(20)
-
- if(!user)
- return
-
- for(var/obj/item/W in user)
- user.unEquip(W)
-
- var/obj/item/wrench/medical/W = new /obj/item/wrench/medical(loc)
- W.add_fingerprint(user)
- W.desc += " For some reason, it reminds you of [user.name]."
-
- if(!user)
- return
-
- user.dust()
- return OBLITERATION
-
-//Screwdriver
-/obj/item/screwdriver
- name = "screwdriver"
- desc = "You can be totally screwy with this."
- icon = 'icons/obj/tools.dmi'
- icon_state = "screwdriver_map"
- flags = CONDUCT
- slot_flags = SLOT_BELT
- force = 5
- w_class = WEIGHT_CLASS_TINY
- throwforce = 5
- throw_speed = 3
- throw_range = 5
- materials = list(MAT_METAL=75)
- attack_verb = list("stabbed")
- hitsound = 'sound/weapons/bladeslice.ogg'
- usesound = 'sound/items/screwdriver.ogg'
- toolspeed = 1
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
- var/random_color = TRUE //if the screwdriver uses random coloring
-
-/obj/item/screwdriver/nuke
- name = "screwdriver"
- desc = "A screwdriver with an ultra thin tip."
- icon_state = "screwdriver_nuke"
- toolspeed = 0.5
-
-/obj/item/screwdriver/suicide_act(mob/user)
- user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS
-
-/obj/item/screwdriver/New(loc, var/param_color = null)
- ..()
- if(random_color)
- if(!param_color)
- param_color = pick("red","blue","pink","brown","green","cyan","yellow")
- icon_state = "screwdriver_[param_color]"
-
- if (prob(75))
- src.pixel_y = rand(0, 16)
-
-/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user)
- if(!istype(M) || user.a_intent == INTENT_HELP)
- return ..()
- if(user.zone_selected != "eyes" && user.zone_selected != "head")
- return ..()
- if(HAS_TRAIT(user, TRAIT_PACIFISM))
- to_chat(user, "You don't want to harm [M]!")
- return
- if((CLUMSY in user.mutations) && prob(50))
- M = user
- return eyestab(M,user)
-
-/obj/item/screwdriver/brass
- name = "brass screwdriver"
- desc = "A screwdriver made of brass. The handle feels freezing cold."
- icon_state = "screwdriver_brass"
- toolspeed = 0.5
- random_color = FALSE
- resistance_flags = FIRE_PROOF | ACID_PROOF
-
-/obj/item/screwdriver/abductor
- name = "alien screwdriver"
- desc = "An ultrasonic screwdriver."
- icon = 'icons/obj/abductor.dmi'
- icon_state = "screwdriver"
- usesound = 'sound/items/pshoom.ogg'
- toolspeed = 0.1
- random_color = FALSE
-
-/obj/item/screwdriver/power
- name = "hand drill"
- desc = "A simple hand drill with a screwdriver bit attached."
- icon_state = "drill_screw"
- item_state = "drill"
- materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
- origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get
- force = 8 //might or might not be too high, subject to change
- throwforce = 8
- throw_speed = 2
- throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far
- attack_verb = list("drilled", "screwed", "jabbed","whacked")
- hitsound = 'sound/items/drill_hit.ogg'
- usesound = 'sound/items/drill_use.ogg'
- toolspeed = 0.25
- random_color = FALSE
-
-/obj/item/screwdriver/power/suicide_act(mob/user)
- user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!")
- return BRUTELOSS
-
-/obj/item/screwdriver/power/attack_self(mob/user)
- playsound(get_turf(user), 'sound/items/change_drill.ogg', 50, 1)
- var/obj/item/wrench/power/b_drill = new /obj/item/wrench/power
- to_chat(user, "You attach the bolt driver bit to [src].")
- qdel(src)
- user.put_in_active_hand(b_drill)
-
-/obj/item/screwdriver/cyborg
- name = "powered screwdriver"
- desc = "An electrical screwdriver, designed to be both precise and quick."
- usesound = 'sound/items/drill_use.ogg'
- toolspeed = 0.5
-
-//Wirecutters
-/obj/item/wirecutters
- name = "wirecutters"
- desc = "This cuts wires."
- icon = 'icons/obj/tools.dmi'
- icon_state = "cutters"
- flags = CONDUCT
- slot_flags = SLOT_BELT
- force = 6
- throw_speed = 3
- throw_range = 7
- w_class = WEIGHT_CLASS_SMALL
- materials = list(MAT_METAL=80)
- origin_tech = "materials=1;engineering=1"
- attack_verb = list("pinched", "nipped")
- hitsound = 'sound/items/wirecutter.ogg'
- usesound = 'sound/items/wirecutter.ogg'
- sharp = 1
- toolspeed = 1
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
- var/random_color = TRUE
-
-/obj/item/wirecutters/New(loc, param_color = null)
- ..()
- if(random_color)
- if(!param_color)
- param_color = pick("yellow", "red")
- icon_state = "cutters_[param_color]"
-
-/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user)
- if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable))
- user.visible_message("[user] cuts [C]'s restraints with [src]!")
- QDEL_NULL(C.handcuffed)
- if(C.buckled && C.buckled.buckle_requires_restraints)
- C.buckled.unbuckle_mob(C)
- C.update_handcuffed()
- return
- else
- ..()
-
-/obj/item/wirecutters/suicide_act(mob/user)
- user.visible_message("[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(loc, usesound, 50, 1, -1)
- return BRUTELOSS
-
-/obj/item/wirecutters/brass
- name = "brass wirecutters"
- desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch."
- icon_state = "cutters_brass"
- toolspeed = 0.5
- random_color = FALSE
- resistance_flags = FIRE_PROOF | ACID_PROOF
-
-/obj/item/wirecutters/abductor
- name = "alien wirecutters"
- desc = "Extremely sharp wirecutters, made out of a silvery-green metal."
- icon = 'icons/obj/abductor.dmi'
- icon_state = "cutters"
- toolspeed = 0.1
- origin_tech = "materials=5;engineering=4;abductor=3"
- random_color = FALSE
-
-/obj/item/wirecutters/cyborg
- name = "wirecutters"
- desc = "This cuts wires."
- toolspeed = 0.5
-
-/obj/item/wirecutters/power
- name = "jaws of life"
- desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a cutting head."
- icon_state = "jaws_cutter"
- item_state = "jawsoflife"
- origin_tech = "materials=2;engineering=2"
- materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
- usesound = 'sound/items/jaws_cut.ogg'
- toolspeed = 0.25
- random_color = FALSE
-
-/obj/item/wirecutters/power/suicide_act(mob/user)
- user.visible_message("[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!")
- playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"]
- if(head)
- head.droplimb(0, DROPLIMB_BLUNT, FALSE, TRUE)
- playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
- return BRUTELOSS
-
-/obj/item/wirecutters/power/attack_self(mob/user)
- playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
- var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power
- to_chat(user, "You attach the pry jaws to [src].")
- qdel(src)
- user.put_in_active_hand(pryjaws)
-
-//Welding Tool
-/obj/item/weldingtool
- name = "welding tool"
- desc = "A standard edition welder provided by Nanotrasen."
- icon = 'icons/obj/tools.dmi'
- icon_state = "welder"
- item_state = "welder"
- flags = CONDUCT
- slot_flags = SLOT_BELT
- force = 3
- throwforce = 5
- throw_speed = 3
- throw_range = 5
- hitsound = "swing_hit"
- usesound = 'sound/items/welder.ogg'
- var/acti_sound = 'sound/items/welderactivate.ogg'
- var/deac_sound = 'sound/items/welderdeactivate.ogg'
- w_class = WEIGHT_CLASS_SMALL
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
- resistance_flags = FIRE_PROOF
- materials = list(MAT_METAL=70, MAT_GLASS=30)
- origin_tech = "engineering=1;plasmatech=1"
- toolspeed = 1
- var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2)
- var/status = 1 //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower)
- var/max_fuel = 20 //The max amount of fuel the welder can hold
- var/change_icons = 1
- var/can_off_process = 0
- var/light_intensity = 2 //how powerful the emitted light is when used.
- var/nextrefueltick = 0
-
-/obj/item/weldingtool/New()
- ..()
- create_reagents(max_fuel)
- reagents.add_reagent("fuel", max_fuel)
- update_icon()
-
-/obj/item/weldingtool/examine(mob/user)
- . = ..()
- if(get_dist(user, src) <= 0)
- . += "It contains [get_fuel()] unit\s of fuel out of [max_fuel]."
-
-/obj/item/weldingtool/suicide_act(mob/user)
- user.visible_message("[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!")
- return FIRELOSS
-
-/obj/item/weldingtool/proc/update_torch()
- overlays.Cut()
- if(welding)
- overlays += "[initial(icon_state)]-on"
- item_state = "[initial(item_state)]1"
- else
- item_state = "[initial(item_state)]"
-
-/obj/item/weldingtool/update_icon()
- if(change_icons)
- var/ratio = get_fuel() / max_fuel
- ratio = Ceiling(ratio*4) * 25
- if(ratio == 100)
- icon_state = initial(icon_state)
- else
- icon_state = "[initial(icon_state)][ratio]"
- update_torch()
- ..()
-
-/obj/item/weldingtool/process()
- switch(welding)
- if(0)
- force = 3
- damtype = "brute"
- update_icon()
- if(!can_off_process)
- STOP_PROCESSING(SSobj, src)
- return
- //Welders left on now use up fuel, but lets not have them run out quite that fast
- if(1)
- force = 15
- damtype = "fire"
- if(prob(5))
- remove_fuel(1)
- update_icon()
-
- //This is to start fires. process() is only called if the welder is on.
- var/turf/location = loc
- if(ismob(location))
- var/mob/M = location
- if(M.l_hand == src || M.r_hand == src)
- location = get_turf(M)
- if(isturf(location))
- location.hotspot_expose(700, 5)
-
-/obj/item/weldingtool/attackby(obj/item/I, mob/user, params)
- if(isscrewdriver(I))
- flamethrower_screwdriver(I, user)
- else if(istype(I, /obj/item/stack/rods))
- flamethrower_rods(I, user)
- else
- ..()
-
-/obj/item/weldingtool/attack(mob/M, mob/user)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected]
-
- if(!S)
- return
-
- if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2)
- return ..()
-
- if(!isOn()) //why wasn't this being checked already?
- to_chat(user, "Turn on [src] before attempting repairs!")
- return 1
-
- if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP)
- to_chat(user, "The damage is far too severe to patch over externally.")
- return
-
- if(!S.brute_dam)
- to_chat(user, "Nothing to fix!")
- return
-
- if(get_fuel() >= 1)
- if(H == user)
- if(!do_mob(user, H, 10))
- return 1
- if(!remove_fuel(1,null))
- to_chat(user, "Need more welding fuel!")
- var/rembrute = HEALPERWELD
- var/nrembrute = 0
- var/childlist
- if(!isnull(S.children))
- childlist = S.children.Copy()
- var/parenthealed = FALSE
- while(rembrute > 0)
- var/obj/item/organ/external/E
- if(S.brute_dam)
- E = S
- else if(LAZYLEN(childlist))
- E = pick_n_take(childlist)
- if(!E.brute_dam || !E.is_robotic())
- continue
- else if(S.parent && !parenthealed)
- E = S.parent
- parenthealed = TRUE
- if(!E.brute_dam || !E.is_robotic())
- break
- else
- break
- playsound(src.loc, usesound, 50, 1)
- nrembrute = max(rembrute - E.brute_dam, 0)
- E.heal_damage(rembrute,0,0,1)
- rembrute = nrembrute
- user.visible_message("\The [user] patches some dents on \the [M]'s [E.name] with \the [src].")
- if(H.bleed_rate && H.isSynthetic())
- H.bleed_rate = 0
- user.visible_message("\The [user] patches some leaks on [M] with \the [src].")
- return 1
- else
- return ..()
-
-/obj/item/weldingtool/afterattack(atom/O, mob/user, proximity)
- if(!proximity)
- return
- if(welding)
- remove_fuel(1)
- var/turf/location = get_turf(user)
- location.hotspot_expose(700, 50, 1)
- if(get_fuel() <= 0)
- set_light(0)
-
- if(isliving(O))
- var/mob/living/L = O
- if(L.IgniteMob())
- message_admins("[key_name_admin(user)] set [key_name_admin(L)] on fire")
- log_game("[key_name(user)] set [key_name(L)] on fire")
-
-/obj/item/weldingtool/attack_self(mob/user)
- switched_on(user)
- if(welding)
- set_light(light_intensity)
-
- update_icon()
-
-//Returns the amount of fuel in the welder
-/obj/item/weldingtool/proc/get_fuel()
- return reagents.get_reagent_amount("fuel")
-
-//Removes fuel from the welding tool. If a mob is passed, it will try to flash the mob's eyes. This should probably be renamed to use()
-/obj/item/weldingtool/proc/remove_fuel(amount = 1, mob/living/M = null)
- if(!welding || !check_fuel())
- return FALSE
- if(get_fuel() >= amount)
- reagents.remove_reagent("fuel", amount)
- check_fuel()
- if(M)
- M.flash_eyes(light_intensity)
- return TRUE
- else
- if(M)
- to_chat(M, "You need more welding fuel to complete this task.")
- return FALSE
-
-//Returns whether or not the welding tool is currently on.
-/obj/item/weldingtool/proc/isOn()
- return welding
-
-//Turns off the welder if there is no more fuel (does this really need to be its own proc?)
-/obj/item/weldingtool/proc/check_fuel(mob/user)
- if(get_fuel() <= 0 && welding)
- switched_on(user)
- update_icon()
- //mob icon update
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_r_hand(0)
- M.update_inv_l_hand(0)
- return 0
- return 1
-
-//Switches the welder on
-/obj/item/weldingtool/proc/switched_on(mob/user)
- if(!status)
- to_chat(user, "[src] can't be turned on while unsecured!")
- return
- welding = !welding
- if(welding)
- if(get_fuel() >= 1)
- to_chat(user, "You switch [src] on.")
- playsound(loc, acti_sound, 50, 1)
- force = 15
- damtype = "fire"
- hitsound = 'sound/items/welder.ogg'
- update_icon()
- START_PROCESSING(SSobj, src)
- else
- to_chat(user, "You need more fuel!")
- switched_off(user)
- else
- if(user)
- to_chat(user, "You switch [src] off.")
- playsound(loc, deac_sound, 50, 1)
- switched_off(user)
-
-//Switches the welder off
-/obj/item/weldingtool/proc/switched_off(mob/user)
- welding = 0
- set_light(0)
-
- force = 3
- damtype = "brute"
- hitsound = "swing_hit"
- update_icon()
-
-/obj/item/weldingtool/proc/flamethrower_screwdriver(obj/item/I, mob/user)
- if(welding)
- to_chat(user, "Turn it off first!")
- return
- status = !status
- if(status)
- to_chat(user, "You resecure [src].")
- else
- to_chat(user, "[src] can now be attached and modified.")
- add_fingerprint(user)
-
-/obj/item/weldingtool/proc/flamethrower_rods(obj/item/I, mob/user)
- if(!status)
- var/obj/item/stack/rods/R = I
- if(R.use(1))
- var/obj/item/flamethrower/F = new /obj/item/flamethrower(user.loc)
- if(!remove_item_from_storage(F))
- user.unEquip(src)
- loc = F
- F.weldtool = src
- add_fingerprint(user)
- to_chat(user, "You add a rod to a welder, starting to build a flamethrower.")
- user.put_in_hands(F)
- else
- to_chat(user, "You need one rod to start building a flamethrower!")
-
-/obj/item/weldingtool/largetank
- name = "Industrial Welding Tool"
- desc = "A slightly larger welder with a larger tank."
- icon_state = "indwelder"
- max_fuel = 40
- materials = list(MAT_METAL=70, MAT_GLASS=60)
- origin_tech = "engineering=2;plasmatech=2"
-
-/obj/item/weldingtool/largetank/cyborg
- name = "integrated welding tool"
- desc = "An advanced welder designed to be used in robotic systems."
- toolspeed = 0.5
-
-/obj/item/weldingtool/largetank/flamethrower_screwdriver()
- return
-
-/obj/item/weldingtool/mini
- name = "emergency welding tool"
- desc = "A miniature welder used during emergencies."
- icon_state = "miniwelder"
- max_fuel = 10
- w_class = WEIGHT_CLASS_TINY
- materials = list(MAT_METAL=30, MAT_GLASS=10)
- change_icons = 0
-
-/obj/item/weldingtool/mini/flamethrower_screwdriver()
- return
-
-/obj/item/weldingtool/abductor
- name = "alien welding tool"
- desc = "An alien welding tool. Whatever fuel it uses, it never runs out."
- icon = 'icons/obj/abductor.dmi'
- icon_state = "welder"
- toolspeed = 0.1
- light_intensity = 0
- change_icons = 0
- origin_tech = "plasmatech=5;engineering=5;abductor=3"
- can_off_process = 1
-
-/obj/item/weldingtool/abductor/process()
- if(get_fuel() <= max_fuel)
- reagents.add_reagent("fuel", 1)
- ..()
-
-/obj/item/weldingtool/hugetank
- name = "Upgraded Welding Tool"
- desc = "An upgraded welder based off the industrial welder."
- icon_state = "upindwelder"
- item_state = "upindwelder"
- max_fuel = 80
- materials = list(MAT_METAL=70, MAT_GLASS=120)
- origin_tech = "engineering=3;plasmatech=2"
-
-/obj/item/weldingtool/experimental
- name = "Experimental Welding Tool"
- desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes."
- icon_state = "exwelder"
- item_state = "exwelder"
- max_fuel = 40
- materials = list(MAT_METAL=70, MAT_GLASS=120)
- origin_tech = "materials=4;engineering=4;bluespace=3;plasmatech=4"
- change_icons = 0
- can_off_process = 1
- light_intensity = 1
- toolspeed = 0.5
- var/last_gen = 0
-
-/obj/item/weldingtool/experimental/brass
- name = "brass welding tool"
- desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch."
- icon_state = "brasswelder"
- item_state = "brasswelder"
- resistance_flags = FIRE_PROOF | ACID_PROOF
-
-obj/item/weldingtool/experimental/process()
- ..()
- if(get_fuel() < max_fuel && nextrefueltick < world.time)
- nextrefueltick = world.time + 10
- reagents.add_reagent("fuel", 1)
-
-//Crowbar
-/obj/item/crowbar
- name = "pocket crowbar"
- desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors."
- icon = 'icons/obj/tools.dmi'
- icon_state = "crowbar"
- item_state = "crowbar"
- usesound = 'sound/items/crowbar.ogg'
- flags = CONDUCT
- slot_flags = SLOT_BELT
- force = 5
- throwforce = 7
- item_state = "crowbar"
- w_class = WEIGHT_CLASS_SMALL
- materials = list(MAT_METAL=50)
- origin_tech = "engineering=1;combat=1"
- attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked")
- toolspeed = 1
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
-
-/obj/item/crowbar/red
- icon_state = "crowbar_red"
- item_state = "crowbar_red"
- force = 8
-
-/obj/item/crowbar/brass
- name = "brass crowbar"
- desc = "A brass crowbar. It feels faintly warm to the touch."
- icon_state = "crowbar_brass"
- item_state = "crowbar_brass"
- toolspeed = 0.5
- resistance_flags = FIRE_PROOF | ACID_PROOF
-
-/obj/item/crowbar/abductor
- name = "alien crowbar"
- desc = "A hard-light crowbar. It appears to pry by itself, without any effort required."
- icon = 'icons/obj/abductor.dmi'
- usesound = 'sound/weapons/sonic_jackhammer.ogg'
- icon_state = "crowbar"
- toolspeed = 0.1
- origin_tech = "combat=4;engineering=4;abductor=3"
-
-/obj/item/crowbar/large
- name = "crowbar"
- desc = "It's a big crowbar. It doesn't fit in your pockets, because its too big."
- force = 12
- w_class = WEIGHT_CLASS_NORMAL
- throw_speed = 3
- throw_range = 3
- materials = list(MAT_METAL=70)
- icon_state = "crowbar_large"
- item_state = "crowbar_large"
- toolspeed = 0.5
-
-/obj/item/crowbar/cyborg
- name = "hydraulic crowbar"
- desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs."
- usesound = 'sound/items/jaws_pry.ogg'
- force = 10
- toolspeed = 0.5
-
-/obj/item/crowbar/power
- name = "jaws of life"
- desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a prying head."
- icon_state = "jaws_pry"
- item_state = "jawsoflife"
- materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
- origin_tech = "materials=2;engineering=2"
- usesound = 'sound/items/jaws_pry.ogg'
- force = 15
- toolspeed = 0.25
- var/airlock_open_time = 100 // Time required to open powered airlocks
-
-/obj/item/crowbar/power/suicide_act(mob/user)
- user.visible_message("[user] is putting [user.p_their()] head in [src]. It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1)
- return BRUTELOSS
-
-/obj/item/crowbar/power/attack_self(mob/user)
- playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
- var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power
- to_chat(user, "You attach the cutting jaws to [src].")
- qdel(src)
- user.put_in_active_hand(cutjaws)
-
-// Conversion kit
-/obj/item/conversion_kit
- name = "\improper Revolver Conversion Kit"
- desc = "A professional conversion kit used to convert any knock off revolver into the real deal capable of shooting lethal .357 rounds without the possibility of catastrophic failure."
- icon_state = "kit"
- flags = CONDUCT
- w_class = WEIGHT_CLASS_SMALL
- origin_tech = "combat=2"
- var/open = 0
-
-/obj/item/conversion_kit/New()
- ..()
- update_icon()
-
-/obj/item/conversion_kit/update_icon()
- icon_state = "[initial(icon_state)]_[open]"
-
-/obj/item/conversion_kit/attack_self(mob/user)
- open = !open
- to_chat(user, "You [open ? "open" : "close"] the conversion kit.")
- update_icon()
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 32158a9a3ab..e5f903a1853 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -1,3 +1,6 @@
+/**
+ * # Banhammer
+ */
/obj/item/banhammer
desc = "A banhammer"
name = "banhammer"
@@ -13,11 +16,15 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
resistance_flags = FIRE_PROOF
-
/obj/item/banhammer/suicide_act(mob/user)
to_chat(viewers(user), "[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.")
return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS
+/obj/item/banhammer/attack(mob/M, mob/user)
+ to_chat(M, " You have been banned FOR NO REISIN by [user]")
+ to_chat(user, " You have BANNED [M]")
+ playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
+
/obj/item/sord
name = "\improper SORD"
desc = "This thing is so unspeakably shitty you are having a hard time even holding it."
diff --git a/code/game/objects/items/weapons/whetstone.dm b/code/game/objects/items/weapons/whetstone.dm
index f74f8d28f57..d2ab4b2733f 100644
--- a/code/game/objects/items/weapons/whetstone.dm
+++ b/code/game/objects/items/weapons/whetstone.dm
@@ -34,15 +34,15 @@
if(TH.force_wielded > initial(TH.force_wielded))
to_chat(user, "[TH] has already been refined before. It cannot be sharpened further!")
return
- TH.force_wielded = Clamp(TH.force_wielded + increment, 0, max)//wieldforce is increased since normal force wont stay
+ TH.force_wielded = clamp(TH.force_wielded + increment, 0, max)//wieldforce is increased since normal force wont stay
if(I.force > initial(I.force))
to_chat(user, "[I] has already been refined before. It cannot be sharpened further!")
return
user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.")
if(!requires_sharpness)
I.sharp = 1
- I.force = Clamp(I.force + increment, 0, max)
- I.throwforce = Clamp(I.throwforce + increment, 0, max)
+ I.force = clamp(I.force + increment, 0, max)
+ I.throwforce = clamp(I.throwforce + increment, 0, max)
I.name = "[prefix] [I.name]"
playsound(get_turf(src), usesound, 50, 1)
name = "worn out [name]"
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index e1464e47e48..bbd43db7c1e 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -30,9 +30,9 @@
return 0
var/armor_protection = 0
if(damage_flag)
- armor_protection = armor[damage_flag]
+ armor_protection = armor.getRating(damage_flag)
if(armor_protection) //Only apply weak-against-armor/hollowpoint effects if there actually IS armor.
- armor_protection = Clamp(armor_protection - armour_penetration, min(armor_protection, 0), 100)
+ armor_protection = clamp(armor_protection - armour_penetration, min(armor_protection, 0), 100)
return round(damage_amount * (100 - armor_protection)*0.01, DAMAGE_PRECISION)
///the sound played when the obj is damaged.
@@ -201,7 +201,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
return
..()
if(exposed_temperature && !(resistance_flags & FIRE_PROOF))
- take_damage(Clamp(0.02 * exposed_temperature, 0, 20), BURN, "fire", 0)
+ take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, "fire", 0)
if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF))
resistance_flags |= ON_FIRE
SSfires.processing[src] = src
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 951eb79f853..7a56c53f73a 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -1,15 +1,14 @@
/obj
//var/datum/module/mod //not used
var/origin_tech = null //Used by R&D to determine what research bonuses it grants.
- var/crit_fail = 0
+ var/crit_fail = FALSE
animate_movement = 2
- var/list/attack_verb = list() //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
var/list/species_exception = null // list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item
- var/sharp = 0 // whether this object cuts
- var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING!
+ var/sharp = FALSE // whether this object cuts
+ var/in_use = FALSE // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING!
var/damtype = "brute"
var/force = 0
- var/list/armor
+ var/datum/armor/armor
var/obj_integrity //defaults to max_integrity
var/max_integrity = 500
var/integrity_failure = 0 //0 if we have no special broken behavior
@@ -22,9 +21,9 @@
var/can_be_hit = TRUE //can this be bludgeoned by items?
- var/Mtoollink = 0 // variable to decide if an object should show the multitool menu linking menu, not all objects use it
+ var/Mtoollink = FALSE // variable to decide if an object should show the multitool menu linking menu, not all objects use it
- var/being_shocked = 0
+ var/being_shocked = FALSE
var/speed_process = FALSE
var/on_blueprints = FALSE //Are we visible on the station blueprints at roundstart?
@@ -33,8 +32,6 @@
/obj/New()
..()
- if(!armor)
- armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0)
if(obj_integrity == null)
obj_integrity = max_integrity
if(on_blueprints && isturf(loc))
@@ -44,25 +41,34 @@
else
T.add_blueprints_preround(src)
-/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = GLOB.default_state)
+/obj/Initialize(mapload)
+ . = ..()
+ if(islist(armor))
+ armor = getArmor(arglist(armor))
+ else if(!armor)
+ armor = getArmor()
+ else if(!istype(armor, /datum/armor))
+ stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()")
+
+/obj/Topic(href, href_list, nowindow = FALSE, datum/topic_state/state = GLOB.default_state)
// Calling Topic without a corresponding window open causes runtime errors
if(!nowindow && ..())
- return 1
+ return TRUE
// In the far future no checks are made in an overriding Topic() beyond if(..()) return
// Instead any such checks are made in CanUseTopic()
if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE)
CouldUseTopic(usr)
- return 0
+ return FALSE
CouldNotUseTopic(usr)
- return 1
+ return TRUE
-/obj/proc/CouldUseTopic(var/mob/user)
+/obj/proc/CouldUseTopic(mob/user)
var/atom/host = nano_host()
host.add_fingerprint(user)
-/obj/proc/CouldNotUseTopic(var/mob/user)
+/obj/proc/CouldNotUseTopic(mob/user)
// Nada
/obj/Destroy()
@@ -110,23 +116,23 @@
// null if object handles breathing logic for lifeform
// datum/air_group to tell lifeform to process using that breath return
//DEFAULT: Take air from turf to give to have mob process
- if(breath_request>0)
+ if(breath_request > 0)
return remove_air(breath_request)
else
return null
/obj/proc/updateUsrDialog()
if(in_use)
- var/is_in_use = 0
+ var/is_in_use = FALSE
var/list/nearby = viewers(1, src)
for(var/mob/M in nearby)
if((M.client && M.machine == src))
- is_in_use = 1
+ is_in_use = TRUE
src.attack_hand(M)
if(istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
if(!(usr in nearby))
if(usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
- is_in_use = 1
+ is_in_use = TRUE
src.attack_ai(usr)
// check for TK users
@@ -134,8 +140,8 @@
if(istype(usr, /mob/living/carbon/human))
if(istype(usr.l_hand, /obj/item/tk_grab) || istype(usr.r_hand, /obj/item/tk_grab/))
if(!(usr in nearby))
- if(usr.client && usr.machine==src)
- is_in_use = 1
+ if(usr.client && usr.machine == src)
+ is_in_use = TRUE
src.attack_hand(usr)
in_use = is_in_use
@@ -143,15 +149,15 @@
// Check that people are actually using the machine. If not, don't update anymore.
if(in_use)
var/list/nearby = viewers(1, src)
- var/is_in_use = 0
+ var/is_in_use = FALSE
for(var/mob/M in nearby)
if((M.client && M.machine == src))
- is_in_use = 1
+ is_in_use = TRUE
src.interact(M)
var/ai_in_use = AutoUpdateAI(src)
if(!ai_in_use && !is_in_use)
- in_use = 0
+ in_use = FALSE
/obj/proc/interact(mob/user)
return
@@ -168,12 +174,12 @@
/atom/movable/proc/on_unset_machine(mob/user)
return
-/mob/proc/set_machine(var/obj/O)
+/mob/proc/set_machine(obj/O)
if(src.machine)
unset_machine()
src.machine = O
if(istype(O))
- O.in_use = 1
+ O.in_use = TRUE
/obj/item/proc/updateSelfDialog()
var/mob/M = src.loc
@@ -183,48 +189,48 @@
/obj/proc/hide(h)
return
-
/obj/proc/hear_talk(mob/M, list/message_pieces)
return
-/obj/proc/hear_message(mob/M as mob, text)
+/obj/proc/hear_message(mob/M, text)
-/obj/proc/multitool_menu(var/mob/user,var/obj/item/multitool/P)
+/obj/proc/multitool_menu(mob/user, obj/item/multitool/P)
return "NO MULTITOOL_MENU!"
-/obj/proc/linkWith(var/mob/user, var/obj/buffer, var/context)
- return 0
+/obj/proc/linkWith(mob/user, obj/buffer, context)
+ return FALSE
-/obj/proc/unlinkFrom(var/mob/user, var/obj/buffer)
- return 0
+/obj/proc/unlinkFrom(mob/user, obj/buffer)
+ return FALSE
-/obj/proc/canLink(var/obj/O, var/context)
- return 0
+/obj/proc/canLink(obj/O, list/context)
+ return FALSE
-/obj/proc/isLinkedWith(var/obj/O)
- return 0
+/obj/proc/isLinkedWith(obj/O)
+ return FALSE
-/obj/proc/getLink(var/idx)
+/obj/proc/getLink(idx)
return null
-/obj/proc/linkMenu(var/obj/O)
- var/dat=""
+/obj/proc/linkMenu(obj/O)
+ var/dat = ""
if(canLink(O, list()))
dat += " \[Link\] "
return dat
-/obj/proc/format_tag(var/label,var/varname, var/act="set_tag")
+/obj/proc/format_tag(label, varname, act = "set_tag")
var/value = vars[varname]
- if(!value || value=="")
- value="-----"
+ if(!value || value == "")
+ value = "-----"
return "[label]: [value]"
-/obj/proc/update_multitool_menu(mob/user as mob)
+/obj/proc/update_multitool_menu(mob/user)
var/obj/item/multitool/P = get_multitool(user)
if(!istype(P))
- return 0
+ return FALSE
+
var/dat = {"
[name] Configuration
@@ -246,13 +252,13 @@ a {
[name]
"}
if(allowed(user))//no, assistants, you're not ruining all vents on the station with just a multitool
- dat += multitool_menu(user,P)
+ dat += multitool_menu(user, P)
if(Mtoollink)
if(P)
if(P.buffer)
var/id = null
if("id_tag" in P.buffer.vars)
- id=P.buffer:id_tag
+ id = P.buffer:id_tag
dat += "MULTITOOL BUFFER: [P.buffer] [id ? "([id])" : ""]"
dat += linkMenu(P.buffer)
@@ -309,12 +315,12 @@ a {
/obj/singularity_pull(S, current_size)
..()
if(!anchored || current_size >= STAGE_FIVE)
- step_towards(src,S)
+ step_towards(src, S)
-/obj/proc/container_resist(var/mob/living)
+/obj/proc/container_resist(mob/living)
return
-/obj/proc/CanAStarPass()
+/obj/proc/CanAStarPass(ID, dir, caller)
. = !density
/obj/proc/on_mob_move(dir, mob/user)
@@ -341,6 +347,13 @@ a {
.["Make speed process"] = "?_src_=vars;makespeedy=[UID()]"
else
.["Make normal process"] = "?_src_=vars;makenormalspeed=[UID()]"
+ .["Modify armor values"] = "?_src_=vars;modifyarmor=[UID()]"
/obj/proc/check_uplink_validity()
- return 1
+ return TRUE
+
+/obj/proc/force_eject_occupant()
+ // This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed.
+ // In the event that the object doesn't have an overriden version of this proc to do it, log a runtime so one can be added.
+ CRASH("Proc force_eject_occupant() is not overriden on a machine containing a mob.")
+
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 663f210df3e..b1ed99357ad 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -7,8 +7,6 @@
var/broken = FALSE
/obj/structure/New()
- if (!armor)
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
..()
if(smooth)
if(SSticker && SSticker.current_state == GAME_STATE_PLAYING)
@@ -20,6 +18,11 @@
if(SSticker)
GLOB.cameranet.updateVisibility(src)
+/obj/structure/Initialize(mapload)
+ if(!armor)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ return ..()
+
/obj/structure/Destroy()
if(SSticker)
GLOB.cameranet.updateVisibility(src)
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index 1de93f4d583..033787cce75 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -141,7 +141,6 @@
return ..()
/obj/structure/alien/weeds/proc/Life()
- set background = BACKGROUND_ENABLED
var/turf/U = get_turf(src)
if(istype(U, /turf/space))
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 82487dbd0ae..138c77f92eb 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -12,6 +12,8 @@
var/opened = FALSE
var/welded = FALSE
var/locked = FALSE
+ var/large = TRUE
+ var/can_be_emaged = FALSE
var/wall_mounted = 0 //never solid (You can always pass over it)
var/lastbang
var/sound = 'sound/machines/click.ogg'
@@ -23,9 +25,13 @@
..()
spawn(1)
if(!opened) // if closed, any item at the crate's loc is put in the contents
+ var/itemcount = 0
for(var/obj/item/I in loc)
if(I.density || I.anchored || I == src) continue
I.forceMove(src)
+ // Ensure the storage cap is respected
+ if(++itemcount >= storage_capacity)
+ break
// Fix for #383 - C4 deleting fridges with corpses
/obj/structure/closet/Destroy()
@@ -143,84 +149,21 @@
/obj/structure/closet/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/rcs) && !opened)
- if(user in contents) //to prevent self-teleporting.
- return
var/obj/item/rcs/E = W
- if(E.rcell && (E.rcell.charge >= E.chargecost))
- if(!is_level_reachable(z))
- to_chat(user, "The rapid-crate-sender can't locate any telepads!")
- return
- if(E.mode == 0)
- if(!E.teleporting)
- var/list/L = list()
- var/list/areaindex = list()
- for(var/obj/machinery/telepad_cargo/R in world)
- if(R.stage == 0)
- var/turf/T = get_turf(R)
- var/tmpname = T.loc.name
- if(areaindex[tmpname])
- tmpname = "[tmpname] ([++areaindex[tmpname]])"
- else
- areaindex[tmpname] = 1
- L[tmpname] = R
- var/desc = input("Please select a telepad.", "RCS") in L
- E.pad = L[desc]
- if(!Adjacent(user))
- to_chat(user, "Unable to teleport, too far from crate.")
- return
- playsound(E.loc, E.usesound, 50, 1)
- to_chat(user, "Teleporting [name]...")
- E.teleporting = 1
- if(!do_after(user, 50 * E.toolspeed, target = src))
- E.teleporting = 0
- return
- E.teleporting = 0
- if(user in contents)
- to_chat(user, "Error: User located in container--aborting for safety.")
- playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
- return
- if(!(E.rcell && E.rcell.use(E.chargecost)))
- to_chat(user, "Unable to teleport, insufficient charge.")
- return
- do_sparks(5, 1, src)
- do_teleport(src, E.pad, 0)
- to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.")
- return
- else
- E.rand_x = rand(50,200)
- E.rand_y = rand(50,200)
- var/L = locate(E.rand_x, E.rand_y, 6)
- if(!Adjacent(user))
- to_chat(user, "Unable to teleport, too far from crate.")
- return
- playsound(E.loc, E.usesound, 50, 1)
- to_chat(user, "Teleporting [name]...")
- E.teleporting = 1
- if(!do_after(user, 50, E.toolspeed, target = src))
- E.teleporting = 0
- return
- E.teleporting = 0
- if(user in contents)
- to_chat(user, "Error: User located in container--aborting for safety.")
- playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
- return
- if(!(E.rcell && E.rcell.use(E.chargecost)))
- to_chat(user, "Unable to teleport, insufficient charge.")
- return
- do_sparks(5, 1, src)
- do_teleport(src, L)
- to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.")
- return
- else
- to_chat(user, "Out of charges.")
- return
+ E.try_send_container(user, src)
return
if(opened)
if(istype(W, /obj/item/grab))
- MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet
- if(istype(W,/obj/item/tk_grab))
+ var/obj/item/grab/G = W
+ if(large)
+ MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet
+ else
+ to_chat(user, "[src] is too small to stuff [G.affecting] into!")
+ if(istype(W, /obj/item/tk_grab))
return FALSE
+ if(user.a_intent != INTENT_HELP) // Stops you from putting your baton in the closet on accident
+ return
if(isrobot(user))
return
if(!user.drop_item()) //couldn't drop the item
@@ -228,13 +171,20 @@
return
if(W)
W.forceMove(loc)
+ return TRUE // It's resolved. No afterattack needed. Stops you from emagging lockers when putting in an emag
+ else if(can_be_emaged && (istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade) && !broken))
+ emag_act(user)
else if(istype(W, /obj/item/stack/packageWrap))
return
else if(user.a_intent != INTENT_HARM)
- attack_hand(user)
+ closed_item_click(user)
else
return ..()
+// What happens when the closet is attacked by a random item not on harm mode
+/obj/structure/closet/proc/closed_item_click(mob/user)
+ attack_hand(user)
+
/obj/structure/closet/welder_act(mob/user, obj/item/I)
. = TRUE
if(!opened && user.loc == src)
@@ -284,7 +234,7 @@
add_fingerprint(user)
/obj/structure/closet/attack_ai(mob/user)
- if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI
+ if(isrobot(user) && Adjacent(user) && !istype(user.loc, /obj/machinery/atmospherics)) //Robots can open/close it, but not the AI
attack_hand(user)
/obj/structure/closet/relaymove(mob/user)
@@ -408,6 +358,11 @@
/obj/structure/closet/AllowDrop()
return TRUE
+/obj/structure/closet/force_eject_occupant()
+ // Its okay to silently teleport mobs out of lockers, since the only thing affected is their contents list.
+ return
+
+
/obj/structure/closet/bluespace
name = "bluespace closet"
desc = "A storage unit that moves and stores through the fourth dimension."
diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
index 448fd6feb34..0da39d2255d 100644
--- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
@@ -115,6 +115,8 @@
new /obj/item/clothing/shoes/black(src)
new /obj/item/clothing/glasses/sunglasses/big(src)
new /obj/item/clothing/glasses/sunglasses/big(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
//Paramedic
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
index df7e1384ed9..7eab5858772 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
@@ -55,7 +55,7 @@
new /obj/item/radio/headset( src )
/obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params)
- if(!istype(W, /obj/item/card/id))
+ if(opened || !istype(W, /obj/item/card/id))
return ..()
if(broken)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
index fd67afcf797..311bc4a38c7 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
@@ -45,8 +45,6 @@
new /obj/item/clothing/suit/storage/labcoat(src)
new /obj/item/radio/headset/headset_sci(src)
new /obj/item/radio/headset/headset_sci(src)
- new /obj/item/reagent_containers/food/drinks/oilcan(src)
- new /obj/item/reagent_containers/food/drinks/oilcan(src)
/obj/structure/closet/secure_closet/RD
name = "research director's locker"
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
index dbb3b8f283d..7a648aa74e4 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
@@ -7,10 +7,10 @@
opened = 0
locked = 1
broken = 0
+ can_be_emaged = TRUE
max_integrity = 250
armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
damage_deflection = 20
- var/large = 1
icon_closed = "secure"
var/icon_locked = "secure1"
icon_opened = "secureopen"
@@ -66,31 +66,13 @@
else
to_chat(user, "Access Denied")
-/obj/structure/closet/secure_closet/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/rcs))
- return ..()
+/obj/structure/closet/secure_closet/closed_item_click(mob/user)
+ togglelock(user)
- if(opened)
- if(istype(W, /obj/item/grab))
- if(large)
- MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet
- else
- to_chat(user, "The locker is too small to stuff [W:affecting] into!")
- if(isrobot(user))
- return
- if(!user.drop_item()) //couldn't drop the item
- to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!")
- return
- if(W)
- W.forceMove(loc)
- else if((istype(W, /obj/item/card/emag)||istype(W, /obj/item/melee/energy/blade)) && !broken)
- emag_act(user)
- else if(istype(W,/obj/item/stack/packageWrap) || istype(W,/obj/item/weldingtool))
- return ..(W, user)
- else if(user.a_intent != INTENT_HARM)
+/obj/structure/closet/secure_closet/AltClick(mob/user)
+ ..()
+ if(Adjacent(user))
togglelock(user)
- else
- return ..()
/obj/structure/closet/secure_closet/emag_act(mob/user)
if(!broken)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 6d4b47df6ab..be004a82912 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -426,7 +426,7 @@
icon_off = "wall-lockeroff"
//too small to put a man in
- large = 0
+ large = FALSE
/obj/structure/closet/secure_closet/wall/update_icon()
if(broken)
@@ -466,3 +466,4 @@
new /obj/item/gavelhammer(src)
new /obj/item/clothing/head/justice_wig(src)
new /obj/item/clothing/accessory/medal/legal(src)
+ new /obj/item/clothing/accessory/lawyers_badge(src)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 67f787854d0..b4feccdc9e2 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -79,111 +79,42 @@
return TRUE
/obj/structure/closet/crate/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/rcs) && !src.opened)
- var/obj/item/rcs/E = W
- if(E.rcell && (E.rcell.charge >= E.chargecost))
- if(!is_level_reachable(src.z)) // This is inconsistent with the closet sending code
- to_chat(user, "The rapid-crate-sender can't locate any telepads!")
- return
- if(E.mode == 0)
- if(!E.teleporting)
- var/list/L = list()
- var/list/areaindex = list()
- for(var/obj/machinery/telepad_cargo/R in world)
- if(R.stage == 0)
- var/turf/T = get_turf(R)
- var/tmpname = T.loc.name
- if(areaindex[tmpname])
- tmpname = "[tmpname] ([++areaindex[tmpname]])"
- else
- areaindex[tmpname] = 1
- L[tmpname] = R
- var/desc = input("Please select a telepad.", "RCS") in L
- E.pad = L[desc]
- if(!Adjacent(user))
- to_chat(user, "Unable to teleport, too far from crate.")
- return
- playsound(E.loc, E.usesound, 50, 1)
- to_chat(user, "Teleporting [src.name]...")
- E.teleporting = TRUE
- if(!do_after(user, 50 * E.toolspeed, target = src))
- E.teleporting = 0
- return
- E.teleporting = 0
- if(!(E.rcell && E.rcell.use(E.chargecost)))
- to_chat(user, "Unable to teleport, insufficient charge.")
- return
- do_sparks(5, 1, src)
- do_teleport(src, E.pad, 0)
- to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.")
- return
-
- else
- E.rand_x = rand(50,200)
- E.rand_y = rand(50,200)
- var/L = locate(E.rand_x, E.rand_y, 6)
- if(!Adjacent(user))
- to_chat(user, "Unable to teleport, too far from crate.")
- return
- playsound(E.loc, E.usesound, 50, 1)
- to_chat(user, "Teleporting [src.name]...")
- E.teleporting = TRUE
- if(!do_after(user, 50 * E.toolspeed, target = src))
- E.teleporting = FALSE
- return
- E.teleporting = 0
- if(!(E.rcell && E.rcell.use(E.chargecost)))
- to_chat(user, "Unable to teleport, insufficient charge.")
- return
- do_sparks(5, 1, src)
- do_teleport(src, L)
- to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.")
- return
- else
- to_chat(user, "Out of charges.")
- return
-
- if(opened)
- if(isrobot(user))
- return
- if(!user.drop_item()) //couldn't drop the item
- to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!")
- return
- if(W)
- W.forceMove(loc)
- else if(istype(W, /obj/item/stack/packageWrap))
+ if(!opened && try_rig(W, user))
return
- else if(istype(W, /obj/item/stack/cable_coil))
+ return ..()
+
+/obj/structure/closet/crate/proc/try_rig(obj/item/W, mob/user)
+ if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = W
if(rigged)
to_chat(user, "[src] is already rigged!")
- return
+ return TRUE
if(C.use(15))
to_chat(user, "You rig [src].")
rigged = TRUE
else
to_chat(user, "You need atleast 15 wires to rig [src]!")
- return
- else if(istype(W, /obj/item/radio/electropack))
+ return TRUE
+ if(istype(W, /obj/item/radio/electropack))
if(rigged)
+ if(!user.drop_item())
+ to_chat(user, "[W] seems to be stuck to your hand!")
+ return TRUE
to_chat(user, "You attach [W] to [src].")
- user.drop_item()
W.forceMove(src)
- return
- else if(istype(W, /obj/item/wirecutters))
- if(rigged)
- to_chat(user, "You cut away the wiring.")
- playsound(loc, W.usesound, 100, 1)
- rigged = FALSE
- return
- else if(user.a_intent != INTENT_HARM)
- attack_hand(user)
- else
- return ..()
+ return TRUE
-/obj/structure/closet/singularity_act()
- dump_contents()
- ..()
+/obj/structure/closet/crate/wirecutter_act(mob/living/user, obj/item/I)
+ if(opened)
+ return
+ if(!rigged)
+ return
+
+ if(I.use_tool(src, user))
+ to_chat(user, "You cut away the wiring.")
+ playsound(loc, I.usesound, 100, 1)
+ rigged = FALSE
+ return TRUE
/obj/structure/closet/crate/welder_act()
return
@@ -229,9 +160,10 @@
max_integrity = 500
armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
damage_deflection = 25
- var/tamperproof = 0
- broken = 0
- locked = 1
+ var/tamperproof = FALSE
+ broken = FALSE
+ locked = TRUE
+ can_be_emaged = TRUE
/obj/structure/closet/crate/secure/update_icon()
..()
@@ -307,17 +239,8 @@
else
src.toggle(user)
-
-/obj/structure/closet/crate/secure/attackby(obj/item/W, mob/user, params)
- if(is_type_in_list(W, list(/obj/item/stack/packageWrap, /obj/item/stack/cable_coil, /obj/item/radio/electropack, /obj/item/wirecutters,/obj/item/rcs)))
- return ..()
- if((istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade)))
- emag_act(user)
- return
- if(!opened)
- src.togglelock(user)
- return
- return ..()
+/obj/structure/closet/crate/secure/closed_item_click(mob/user)
+ togglelock(user)
/obj/structure/closet/crate/secure/emag_act(mob/user)
if(locked)
diff --git a/code/game/objects/structures/crates_lockers/crittercrate.dm b/code/game/objects/structures/crates_lockers/crittercrate.dm
index fd726aab6a7..7629fc62918 100644
--- a/code/game/objects/structures/crates_lockers/crittercrate.dm
+++ b/code/game/objects/structures/crates_lockers/crittercrate.dm
@@ -82,9 +82,8 @@
content_mob = /mob/living/simple_animal/pet/dog/fox
/obj/structure/closet/critter/butterfly
- name = "butterflies crate"
+ name = "butterfly crate"
content_mob = /mob/living/simple_animal/butterfly
- amount = 50
/obj/structure/closet/critter/deer
name = "deer crate"
diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm
index e2ab534923d..029b0a761ad 100644
--- a/code/game/objects/structures/curtains.dm
+++ b/code/game/objects/structures/curtains.dm
@@ -46,7 +46,7 @@
/obj/structure/curtain/screwdriver_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
if(anchored)
user.visible_message("[user] unscrews [src] from the floor.", "You start to unscrew [src] from the floor...", "You hear rustling noises.")
@@ -65,7 +65,7 @@
if(anchored)
return
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.tool_start_check(src, user, 0))
return
WIRECUTTER_ATTEMPT_DISMANTLE_MESSAGE
if(I.use_tool(src, user, 50, volume = I.tool_volume))
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index 878b03436ff..0c0a7f5e4dd 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -56,27 +56,20 @@
/obj/structure/dresser/crowbar_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_start_check(user, 0))
+ if(!I.use_tool(src, user, 0))
return
TOOL_ATTEMPT_DISMANTLE_MESSAGE
if(I.use_tool(src, user, 50, volume = I.tool_volume))
TOOL_DISMANTLE_SUCCESS_MESSAGE
-
+ deconstruct(disassembled = TRUE)
/obj/structure/dresser/wrench_act(mob/user, obj/item/I)
. = TRUE
- if(!I.use_tool(src, user, 0, volume = I.tool_volume))
- return
- if(anchored)
- WRENCH_UNANCHOR_MESSAGE
- anchored = FALSE
- else
- if(!isfloorturf(loc))
- user.visible_message("A floor must be present to secure [src]!")
- return
- WRENCH_ANCHOR_MESSAGE
- anchored = TRUE
+ default_unfasten_wrench(user, I, time = 20)
-/obj/structure/dresser/deconstruct(disassembled = TRUE)
- new /obj/item/stack/sheet/wood(drop_location(), 30)
- qdel(src)
+obj/structure/dresser/deconstruct(disassembled = FALSE)
+ var/mat_drop = 15
+ if(disassembled)
+ mat_drop = 30
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index 2dc0cf415cc..33298669157 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -17,11 +17,11 @@
var/opened = 0
var/material_drop = /obj/item/stack/sheet/metal
-/obj/structure/extinguisher_cabinet/New(turf/loc, ndir = null)
+/obj/structure/extinguisher_cabinet/New(turf/loc, direction = null)
..()
- if(ndir)
- pixel_x = (ndir & EAST|WEST) ? (ndir == EAST ? 28 : -28) : 0
- pixel_y = (ndir & NORTH|SOUTH)? (ndir == WEST ? 28 : -28) : 0
+ if(direction)
+ setDir(direction)
+ set_pixel_offsets_from_dir(28, -28, 30, -30)
switch(extinguishertype)
if(NO_EXTINGUISHER)
return
@@ -40,7 +40,7 @@
return
if(!in_range(src, user))
return
- if(!iscarbon(usr))
+ if(!iscarbon(usr) && !isrobot(usr))
return
playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
@@ -165,9 +165,8 @@
else
icon_state = "extinguisher_empty"
-/obj/structure/extinguisher_cabinet/empty/New(turf/loc, ndir = null)
+/obj/structure/extinguisher_cabinet/empty
extinguishertype = NO_EXTINGUISHER
- ..()
#undef NO_EXTINGUISHER
#undef NORMAL_EXTINGUISHER
diff --git a/code/game/objects/structures/foodcart.dm b/code/game/objects/structures/foodcart.dm
index 198dbf5f195..3a9a3414986 100644
--- a/code/game/objects/structures/foodcart.dm
+++ b/code/game/objects/structures/foodcart.dm
@@ -40,7 +40,7 @@
food_slots[s]=I
update_icon()
success = 1
- break;
+ break
if(!success)
to_chat(user, fail_msg)
else if(istype(I, /obj/item/reagent_containers/food/drinks))
@@ -51,7 +51,7 @@
drink_slots[s]=I
update_icon()
success = 1
- break;
+ break
if(!success)
to_chat(user, fail_msg)
else if(istype(I, /obj/item/wrench))
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index dcb74369d72..6d27b21e686 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -76,14 +76,14 @@
if(istype(W,/obj/item/stack/rods))
var/obj/item/stack/rods/S = W
if(state == GIRDER_DISPLACED)
- if(S.get_amount() < 2)
- to_chat(user, "You need at least two rods to create a false wall!")
+ if(S.get_amount() < 5)
+ to_chat(user, "You need at least five rods to create a false wall!")
return
to_chat(user, "You start building a reinforced false wall...")
if(do_after(user, 20, target = src))
- if(!loc || !S || S.get_amount() < 2)
+ if(!loc || !S || S.get_amount() < 5)
return
- S.use(2)
+ S.use(5)
to_chat(user, "You create a false wall. Push on it to open or close the passage.")
var/obj/structure/falsewall/iron/FW = new (loc)
transfer_fingerprints_to(FW)
@@ -374,7 +374,7 @@
/obj/structure/girder/CanAStarPass(ID, dir, caller)
. = !density
- if(ismovableatom(caller))
+ if(ismovable(caller))
var/atom/movable/mover = caller
. = . || mover.checkpass(PASSGRILLE)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 755d2a3de1b..72acf72f613 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -111,7 +111,7 @@
/obj/structure/grille/CanAStarPass(ID, dir, caller)
. = !density
- if(ismovableatom(caller))
+ if(ismovable(caller))
var/atom/movable/mover = caller
. = . || mover.checkpass(PASSGRILLE)
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 21af7f6e615..c3f36e42f1d 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -69,9 +69,9 @@
if(isliving(G.affecting))
if(!has_buckled_mobs())
if(do_mob(user, src, 120))
- if(spike(G.affecting))
- G.affecting.visible_message("[user] slams [G.affecting] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.")
- qdel(G)
+ var/mob/living/affected = G.affecting
+ if(spike(affected))
+ affected.visible_message("[user] slams [affected] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.")
return
return ..()
@@ -130,16 +130,18 @@
release_mob(M)
/obj/structure/kitchenspike/proc/release_mob(mob/living/M)
- var/matrix/m180 = matrix(M.transform)
- m180.Turn(180)
- animate(M, transform = m180, time = 3)
- M.pixel_y = M.get_standard_pixel_y_offset(180)
M.adjustBruteLoss(30)
src.visible_message(text("[M] falls free of [src]!"))
unbuckle_mob(M, force = TRUE)
M.emote("scream")
M.AdjustWeakened(10)
+/obj/structure/kitchenspike/post_unbuckle_mob(mob/living/M)
+ M.pixel_y = M.get_standard_pixel_y_offset(0)
+ var/matrix/m180 = matrix(M.transform)
+ m180.Turn(180)
+ animate(M, transform = m180, time = 3)
+
/obj/structure/kitchenspike/Destroy()
if(has_buckled_mobs())
for(var/mob/living/L in buckled_mobs)
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index f9717956bb0..13c0ce77c0f 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -15,7 +15,6 @@
anchored = TRUE
resistance_flags = FIRE_PROOF | LAVA_PROOF
- var/gps = null
var/obj/effect/light_emitter/tendril/emitted_light
/obj/structure/spawner/lavaland/goliath
@@ -29,7 +28,6 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/Initialize(mapload)
. = ..()
emitted_light = new(loc)
- gps = new /obj/item/gps/internal(src)
GLOB.tendrils += src
return INITIALIZE_HINT_LATELOAD
@@ -59,7 +57,6 @@ GLOBAL_LIST_INIT(tendrils, list())
SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1)
GLOB.tendrils -= src
QDEL_NULL(emitted_light)
- QDEL_NULL(gps)
return ..()
/obj/effect/light_emitter/tendril
diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm
index d0bcd07f92f..4742a2c3c0c 100644
--- a/code/game/objects/structures/loom.dm
+++ b/code/game/objects/structures/loom.dm
@@ -11,12 +11,30 @@
anchored = TRUE
/obj/structure/loom/attackby(obj/item/I, mob/user)
- if(default_unfasten_wrench(user, I, 5))
- return
if(weave(I, user))
return
return ..()
+/obj/structure/loom/crowbar_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.use_tool(src, user, 0))
+ return
+ TOOL_ATTEMPT_DISMANTLE_MESSAGE
+ if(I.use_tool(src, user, 50, volume = I.tool_volume))
+ TOOL_DISMANTLE_SUCCESS_MESSAGE
+ deconstruct(disassembled = TRUE)
+
+/obj/structure/loom/wrench_act(mob/user, obj/item/I)
+ . = TRUE
+ default_unfasten_wrench(user, I, time = 20)
+
+/obj/structure/loom/deconstruct(disassembled = FALSE)
+ var/mat_drop = 5
+ if(disassembled)
+ mat_drop = 10
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
+
///Handles the weaving.
/obj/structure/loom/proc/weave(obj/item/stack/sheet/cotton/W, mob/user)
if(!istype(W))
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 1ea1c277af8..5849d96a158 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -232,7 +232,7 @@
/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller)
. = !density
- if(ismovableatom(caller))
+ if(ismovable(caller))
var/atom/movable/mover = caller
. = . || mover.checkpass(PASSTABLE)
diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm
deleted file mode 100644
index c3d63c47862..00000000000
--- a/code/game/objects/structures/musician.dm
+++ /dev/null
@@ -1,341 +0,0 @@
-
-
-/datum/song
- var/name = "Untitled"
- var/list/lines = new()
- var/tempo = 5 // delay between notes
-
- var/playing = 0 // if we're playing
- var/help = 0 // if help is open
- var/repeat = 0 // number of times remaining to repeat
- var/max_repeat = 10 // maximum times we can repeat
-
- var/instrumentDir = "piano" // the folder with the sounds
- var/instrumentExt = "ogg" // the file extension
- var/obj/instrumentObj = null // the associated obj playing the sound
-
-/datum/song/New(dir, obj, ext = "ogg")
- tempo = sanitize_tempo(tempo)
- instrumentDir = dir
- instrumentObj = obj
- instrumentExt = ext
-
-/datum/song/Destroy()
- instrumentObj = null
- return ..()
-
-// note is a number from 1-7 for A-G
-// acc is either "b", "n", or "#"
-// oct is 1-8 (or 9 for C)
-/datum/song/proc/playnote(note, acc as text, oct)
- // handle accidental -> B<>C of E<>F
- if(acc == "b" && (note == 3 || note == 6)) // C or F
- if(note == 3)
- oct--
- note--
- acc = "n"
- else if(acc == "#" && (note == 2 || note == 5)) // B or E
- if(note == 2)
- oct++
- note++
- acc = "n"
- else if(acc == "#" && (note == 7)) //G#
- note = 1
- acc = "b"
- else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
- acc = "b"
- note++
-
- // check octave, C is allowed to go to 9
- if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
- return
-
- // now generate name
- var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
- soundfile = file(soundfile)
- // make sure the note exists
- if(!fexists(soundfile))
- return
- // and play
- var/turf/source = get_turf(instrumentObj)
- var/sound/music_played = sound(soundfile)
- for(var/A in hearers(15, source))
- var/mob/M = A
- if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS))
- continue
- M.playsound_local(source, null, 100, falloff = 5, S = music_played)
-
-/datum/song/proc/shouldStopPlaying(mob/user)
- if(instrumentObj)
- //if(!user.canUseTopic(instrumentObj))
- //return 1
- return !instrumentObj.anchored // add special cases to stop in subclasses
- else
- return 1
-
-/datum/song/proc/playsong(mob/user)
- while(repeat >= 0)
- var/cur_oct[7]
- var/cur_acc[7]
- for(var/i = 1 to 7)
- cur_oct[i] = 3
- cur_acc[i] = "n"
-
- for(var/line in lines)
- for(var/beat in splittext(lowertext(line), ","))
- var/list/notes = splittext(beat, "/")
- for(var/note in splittext(notes[1], "-"))
- if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case
- playing = 0
- return
- if(length(note) == 0)
- continue
- var/cur_note = text2ascii(note) - 96
- if(cur_note < 1 || cur_note > 7)
- continue
- for(var/i=2 to length(note))
- var/ni = copytext(note,i,i+1)
- if(!text2num(ni))
- if(ni == "#" || ni == "b" || ni == "n")
- cur_acc[cur_note] = ni
- else if(ni == "s")
- cur_acc[cur_note] = "#" // so shift is never required
- else
- cur_oct[cur_note] = text2num(ni)
- playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
- if(notes.len >= 2 && text2num(notes[2]))
- sleep(sanitize_tempo(tempo / text2num(notes[2])))
- else
- sleep(tempo)
- repeat--
- playing = 0
- repeat = 0
-
-/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(!instrumentObj)
- return
-
- ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500)
- ui.open()
- ui.set_auto_update(1)
-
-/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- var/data[0]
-
- data["lines"] = lines
- data["tempo"] = tempo
-
- data["playing"] = playing
- data["help"] = help
- data["repeat"] = repeat
- data["maxRepeat"] = max_repeat
- data["minTempo"] = world.tick_lag
- data["maxTempo"] = 600
-
- return data
-
-/datum/song/Topic(href, href_list)
- if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated())
- usr << browse(null, "window=instrument")
- usr.unset_machine()
- return 1
-
- instrumentObj.add_fingerprint(usr)
-
- if(href_list["newsong"])
- playing = 0
- lines = new()
- tempo = sanitize_tempo(5) // default 120 BPM
- name = ""
- SSnanoui.update_uis(src)
-
- else if(href_list["import"])
- playing = 0
- var/t = ""
- do
- t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
- if(!in_range(instrumentObj, usr))
- return
-
- if(length(t) >= 12000)
- var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
- if(cont == "no")
- break
- while(length(t) > 12000)
-
- //split into lines
- spawn()
- lines = splittext(t, "\n")
- if(lines.len == 0)
- return 1
- if(copytext(lines[1],1,6) == "BPM: ")
- tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
- lines.Cut(1,2)
- else
- tempo = sanitize_tempo(5) // default 120 BPM
- if(lines.len > 200)
- to_chat(usr, "Too many lines!")
- lines.Cut(201)
- var/linenum = 1
- for(var/l in lines)
- if(length(l) > 200)
- to_chat(usr, "Line [linenum] too long!")
- lines.Remove(l)
- else
- linenum++
- SSnanoui.update_uis(src)
-
- else if(href_list["help"])
- help = !help
- SSnanoui.update_uis(src)
-
- if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops.
- if(playing)
- return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
- repeat += round(text2num(href_list["repeat"]))
- if(repeat < 0)
- repeat = 0
- if(repeat > max_repeat)
- repeat = max_repeat
- SSnanoui.update_uis(src)
-
- else if(href_list["tempo"])
- tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag)
- SSnanoui.update_uis(src)
-
- else if(href_list["play"])
- if(playing)
- return
- playing = 1
- spawn()
- playsong(usr)
- SSnanoui.update_uis(src)
-
- else if(href_list["insertline"])
- var/num = round(text2num(href_list["insertline"]))
- if(num < 1 || num > lines.len + 1)
- return
-
- var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
- if(!newline || !in_range(instrumentObj, usr))
- return
- if(lines.len > 200)
- return
- if(length(newline) > 200)
- newline = copytext(newline, 1, 200)
-
- lines.Insert(num, newline)
- SSnanoui.update_uis(src)
-
- else if(href_list["deleteline"])
- var/num = round(text2num(href_list["deleteline"]))
- if(num > lines.len || num < 1)
- return
- lines.Cut(num, num + 1)
- SSnanoui.update_uis(src)
-
- else if(href_list["modifyline"])
- var/num = round(text2num(href_list["modifyline"]))
- var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
- if(!content || !in_range(instrumentObj, usr))
- return
- if(length(content) > 200)
- content = copytext(content, 1, 200)
- if(num > lines.len || num < 1)
- return
- lines[num] = content
- SSnanoui.update_uis(src)
-
- else if(href_list["stop"])
- playing = 0
- SSnanoui.update_uis(src)
-
-/datum/song/proc/sanitize_tempo(new_tempo)
- new_tempo = abs(new_tempo)
- return max(round(new_tempo, world.tick_lag), world.tick_lag)
-
-// subclass for handheld instruments, like violin
-/datum/song/handheld
-
-/datum/song/handheld/shouldStopPlaying()
- if(instrumentObj)
- return !isliving(instrumentObj.loc)
- else
- return 1
-
-
-//////////////////////////////////////////////////////////////////////////
-
-
-/obj/structure/piano
- name = "space minimoog"
- icon = 'icons/obj/musician.dmi'
- icon_state = "minimoog"
- anchored = 1
- density = 1
- var/datum/song/song
-
-
-/obj/structure/piano/New()
- ..()
- song = new("piano", src)
-
- if(prob(50))
- name = "space minimoog"
- desc = "This is a minimoog, like a space piano, but more spacey!"
- icon_state = "minimoog"
- else
- name = "space piano"
- desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
- icon_state = "piano"
-
-/obj/structure/piano/Destroy()
- QDEL_NULL(song)
- return ..()
-
-/obj/structure/piano/Initialize()
- if(song)
- song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
- ..()
-
-/obj/structure/piano/attack_hand(mob/user as mob)
- ui_interact(user)
-
-/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if(!isliving(user) || user.incapacitated() || !anchored)
- return
-
- song.ui_interact(user, ui_key, ui, force_open)
-
-/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
- return song.ui_data(user, ui_key, state)
-
-/obj/structure/piano/Topic(href, href_list)
- song.Topic(href, href_list)
-
-/obj/structure/piano/wrench_act(mob/user, obj/item/I)
- . = TRUE
- if(!I.tool_use_check(user, 0))
- return
- if(!anchored && !isinspace())
- WRENCH_ANCHOR_MESSAGE
- if(!I.use_tool(src, user, 20, volume = I.tool_volume))
- return
- user.visible_message( \
- "[user] tightens [src]'s casters.", \
- " You have tightened [src]'s casters. Now it can be played again.", \
- "You hear ratchet.")
- anchored = TRUE
- else if(anchored)
- to_chat(user, " You begin to loosen [src]'s casters...")
- if(!I.use_tool(src, user, 40, volume = I.tool_volume))
- return
- user.visible_message( \
- "[user] loosens [src]'s casters.", \
- " You have loosened [src]. Now it can be pulled somewhere else.", \
- "You hear ratchet.")
- anchored = FALSE
- else
- to_chat(user, "[src] needs to be bolted to the floor!")
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index 1102901c8bd..e659bbeedd9 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -246,7 +246,7 @@ GLOBAL_LIST_EMPTY(safes)
var/ticks = text2num(href_list["turnright"])
for(var/i = 1 to ticks)
- dial = Wrap(dial - 1, 0, 100)
+ dial = WRAP(dial - 1, 0, 100)
var/invalid_turn = current_tumbler_index % 2 == 0 || current_tumbler_index > number_of_tumblers
if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset
@@ -274,7 +274,7 @@ GLOBAL_LIST_EMPTY(safes)
var/ticks = text2num(href_list["turnleft"])
for(var/i = 1 to ticks)
- dial = Wrap(dial + 1, 0, 100)
+ dial = WRAP(dial + 1, 0, 100)
var/invalid_turn = current_tumbler_index % 2 != 0 || current_tumbler_index > number_of_tumblers
if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset
@@ -373,6 +373,7 @@ GLOBAL_LIST_EMPTY(safes)
info = " |