diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
index 0111c8484dc..e47b5fcff02 100644
--- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm
@@ -11703,7 +11703,7 @@
})
"auD" = (
/obj/machinery/light_construct/small,
-/obj/item/toolbox_tiles_sensor,
+/obj/item/toolbox_tiles/sensor,
/turf/simulated/floor/plating,
/area/maintenance/fpmaint2{
name = "Port Maintenance"
diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm
index e3dcc7bcf12..d4b3cdc8c55 100644
--- a/_maps/map_files/cyberiad/cyberiad.dmm
+++ b/_maps/map_files/cyberiad/cyberiad.dmm
@@ -9787,6 +9787,7 @@
pixel_y = 2
},
/obj/item/stamp/law,
+/obj/item/pen/multi,
/turf/simulated/floor/plasteel{
tag = "icon-cult";
icon_state = "cult";
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index b138d19b513..8171efa7825 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -28,6 +28,46 @@ Thus, the two variables affect pump operation are set in New():
var/id = null
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/binary/pump/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/binary/pump/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+
/obj/machinery/atmospherics/binary/pump/Destroy()
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -203,7 +243,15 @@ Thus, the two variables affect pump operation are set in New():
update_icon()
/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params)
- if(!istype(W, /obj/item/wrench))
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else if(!istype(W, /obj/item/wrench))
return ..()
if(!(stat & NOPOWER) && on)
to_chat(user, "You cannot unwrench this [src], turn it off first.")
diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
index 11874172340..6633c1ac10d 100644
--- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
@@ -28,6 +28,46 @@ Thus, the two variables affect pump operation are set in New():
var/id = null
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/binary/volume_pump/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/set_max()
+ if(powered())
+ transfer_rate = MAX_TRANSFER_RATE
+ update_icon()
+
/obj/machinery/atmospherics/binary/volume_pump/Destroy()
if(SSradio)
SSradio.remove_object(src, frequency)
@@ -199,7 +239,15 @@ Thus, the two variables affect pump operation are set in New():
update_icon()
/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params)
- if(!istype(W, /obj/item/wrench))
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else if(!istype(W, /obj/item/wrench))
return ..()
if(!(stat & NOPOWER) && on)
to_chat(user, "You cannot unwrench this [src], turn it off first.")
diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
index c6720e45f6e..a778b01ea86 100755
--- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
@@ -22,6 +22,45 @@ Filter types:
var/frequency = 0
var/datum/radio_frequency/radio_connection
+/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/trinary/filter/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/filter/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
/obj/machinery/atmospherics/trinary/filter/Destroy()
if(SSradio)
@@ -211,4 +250,16 @@ Filter types:
. = TRUE
update_icon()
- SSnanoui.update_uis(src)
\ No newline at end of file
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else
+ return ..()
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
index 0c64804b6da..e7c91172e3b 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
@@ -12,10 +12,50 @@
//node 3 is the outlet, nodes 1 & 2 are intakes
+/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/mixer/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/trinary/mixer/AIAltClick()
+ set_max()
+ return ..()
+
/obj/machinery/atmospherics/trinary/mixer/flipped
icon_state = "mmap"
flipped = 1
+/obj/machinery/atmospherics/trinary/mixer/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/mixer/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+
/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0)
if(flipped)
icon_state = "m"
@@ -176,4 +216,16 @@
. = TRUE
update_icon()
- SSnanoui.update_uis(src)
\ No newline at end of file
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else
+ return ..()
\ No newline at end of file
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index fd5fd510e40..c567ba7adb2 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -491,7 +491,7 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list())
to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];")
to_chat(user, "Location = [location_description];")
to_chat(user, "[special_role_description]")
- to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")]) (CA)")
+ to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")]) (CA)")
// Gets the first mob contained in an atom, and warns the user if there's not exactly one
/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr)
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 5b639cc80f8..b90f7502e65 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -113,9 +113,14 @@
/proc/typing_input(mob/user, message = "", title = "", default = "")
if(user.client.checkTyping()) // Prevent double windows
return null
- user.client.typing = TRUE
+ var/client/C = user.client // Save it in a var in case the client disconnects from the mob
+ C.typing = TRUE
var/msg = input(user, message, title, default) as text|null
- user.client.typing = FALSE
+ if(!C)
+ return null
+ C.typing = FALSE
+ if(!user || C != user.client) // User got out of the mob for some reason or the mob is gone
+ return null
return msg
//Filters out undesirable characters from names
diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm
index f5234135749..3d7259eef55 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -3,7 +3,7 @@ var/datum/configuration/config = null
var/host = null
var/join_motd = null
GLOBAL_VAR(join_tos)
-var/game_version = "Custom ParaCode"
+var/game_version = "ParaCode"
var/changelog_hash = md5('html/changelog.html') //used to check if the CL changed
var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544)
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 18943100051..5ff132a7ee6 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -19,7 +19,6 @@ GLOBAL_LIST_INIT(navigation_computers, list())
GLOBAL_LIST_INIT(all_areas, list())
GLOBAL_LIST_INIT(machines, list())
-GLOBAL_LIST_INIT(processing_power_items, list()) //items that ask to be called every cycle
GLOBAL_LIST_INIT(rcd_list, list()) //list of Rapid Construction Devices.
GLOBAL_LIST_INIT(apcs, list())
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index 0b8c8d58a95..77443fb4ab2 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -1,6 +1,9 @@
var/global/obj/effect/overlay/plmaster = null
var/global/obj/effect/overlay/slmaster = null
+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.
var/global/obj/item/radio/intercom/global_announcer = create_global_announcer()
var/global/obj/item/radio/intercom/command/command_announcer = create_command_announcer()
diff --git a/code/_globalvars/station.dm b/code/_globalvars/station.dm
index 2d635996b30..e997a672e1f 100644
--- a/code/_globalvars/station.dm
+++ b/code/_globalvars/station.dm
@@ -1,6 +1,3 @@
var/global/datum/datacore/data_core = null
-var/CELLRATE = 0.002 // multiplier for watts per tick <> cell storage (eg: .002 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
-var/CHARGELEVEL = 0.001 // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
-
var/map_name = "Unknown" //The name of the map that is loaded. Assigned in world/New()
\ No newline at end of file
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index c84a64722c5..22921f5228d 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -49,7 +49,7 @@
if(!can_see(A))
if(isturf(A)) //On unmodified clients clicking the static overlay clicks the turf underneath
return // So there's no point messaging admins
- message_admins("[key_name_admin(src)] might be running a modified client! (failed can_see on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))")
+ add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed can_see on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL)
var/message = "[key_name(src)] might be running a modified client! (failed can_see on AI click of [A]([COORD(pixel_turf)]))"
log_admin(message)
send2irc_adminless_only("NOCHEAT", "[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))")
@@ -64,7 +64,7 @@
else
if(pixel_turf.obscured)
log_admin("[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)])")
- message_admins("[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))")
+ add_attack_logs(src, src, "[key_name_admin(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))", ATKLOG_ALL)
send2irc_adminless_only("NOCHEAT", "[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))")
return
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 24e58b502ad..286f8ce7095 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -122,6 +122,10 @@
throw_item(A)
return
+ if(isLivingSSD(A))
+ if(client && client.send_ssd_warning(A))
+ return
+
var/obj/item/W = get_active_hand()
if(W == A)
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index 32a9d7cf46d..5780e86a298 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -17,6 +17,7 @@
//Hide/Show Action Buttons ... Button
/obj/screen/movable/action_button/hide_toggle
name = "Hide Buttons"
+ desc = "Shift-click any button to reset its position. Alt-click to reset all buttons to their default positions."
icon = 'icons/mob/actions/actions.dmi'
icon_state = "bg_default"
var/hidden = 0
@@ -24,8 +25,18 @@
/obj/screen/movable/action_button/hide_toggle/Click(location,control,params)
var/list/modifiers = params2list(params)
if(modifiers["shift"])
- moved = 0
- return 1
+ moved = FALSE
+ usr.update_action_buttons(TRUE)
+ return TRUE
+ if(modifiers["alt"])
+ for(var/V in usr.actions)
+ var/datum/action/A = V
+ var/obj/screen/movable/action_button/B = A.button
+ B.moved = FALSE
+ moved = FALSE
+ usr.update_action_buttons(TRUE)
+ to_chat(usr, "Action button positions have been reset.")
+ return TRUE
usr.hud_used.action_buttons_hidden = !usr.hud_used.action_buttons_hidden
hidden = usr.hud_used.action_buttons_hidden
@@ -36,6 +47,15 @@
UpdateIcon()
usr.update_action_buttons()
+/obj/screen/movable/action_button/hide_toggle/AltClick(mob/user)
+ for(var/V in user.actions)
+ var/datum/action/A = V
+ var/obj/screen/movable/action_button/B = A.button
+ B.moved = FALSE
+ if(moved)
+ moved = FALSE
+ user.update_action_buttons(TRUE)
+ to_chat(user, "Action button positions have been reset.")
/obj/screen/movable/action_button/hide_toggle/proc/InitialiseIcon(mob/living/user)
if(isalien(user))
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index d8f0a39588c..149e98b3bf2 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -1,5 +1,7 @@
/datum/configuration
var/server_name = null // server name (for world name / status)
+ var/server_tag_line = null // server tagline (for showing on hub entry)
+ var/server_extra_features = null // server-specific extra features (for hub entry)
var/server_suffix = 0 // generate numeric suffix based on server port
var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit
@@ -53,7 +55,6 @@
var/humans_need_surnames = 0
var/allow_random_events = 0 // enables random events mid-round when set to 1
var/allow_ai = 1 // allow ai job
- var/hostedby = null
var/respawn = 0
var/guest_jobban = 1
var/usewhitelist = 0
@@ -71,6 +72,9 @@
var/assistantlimit = 0 //enables assistant limiting
var/assistantratio = 2 //how many assistants to security members
+ var/auto_cryo_ssd_mins = 0
+ var/ssd_warning = 0
+
var/prob_free_golems = 75 //chance for free golems spawners to appear roundstart
var/unrestricted_free_golems = FALSE //if true, free golems can appear on all roundtypes
@@ -215,7 +219,7 @@
// Automatic localhost admin disable
var/disable_localhost_admin = 0
-
+
//Start now warning
var/start_now_confirmation = 0
@@ -298,6 +302,12 @@
if("shadowling_max_age")
config.shadowling_max_age = text2num(value)
+ if("auto_cryo_ssd_mins")
+ config.auto_cryo_ssd_mins = text2num(value)
+
+ if("ssd_warning")
+ config.ssd_warning = 1
+
if("log_ooc")
config.log_ooc = 1
@@ -363,10 +373,10 @@
if("no_dead_vote")
config.vote_no_dead = 1
-
+
if("vote_autotransfer_initial")
config.vote_autotransfer_initial = text2num(value)
-
+
if("vote_autotransfer_interval")
config.vote_autotransfer_interval = text2num(value)
@@ -391,6 +401,12 @@
if("servername")
config.server_name = value
+ if("server_tag_line")
+ config.server_tag_line = value
+
+ if("server_extra_features")
+ config.server_extra_features = value
+
if("serversuffix")
config.server_suffix = 1
@@ -400,9 +416,6 @@
if("nudge_script_path")
config.nudge_script_path = value
- if("hostedby")
- config.hostedby = value
-
if("server")
config.server = value
@@ -650,7 +663,7 @@
if("disable_karma")
config.disable_karma = 1
-
+
if("start_now_confirmation")
config.start_now_confirmation = 1
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index ccd83640aa6..02f2681b22d 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -8,6 +8,8 @@ SUBSYSTEM_DEF(jobs)
var/list/name_occupations = list() //Dict of all jobs, keys are titles
var/list/type_occupations = list() //Dict of all jobs, keys are types
var/list/prioritized_jobs = list() // List of jobs set to priority by HoP/Captain
+ var/list/id_change_records = list() // List of all job transfer records
+ var/list/id_change_counter = 1
//Players who need jobs
var/list/unassigned = list()
//Debug info
@@ -601,3 +603,84 @@ SUBSYSTEM_DEF(jobs)
spawn(0)
to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]")
+
+/datum/controller/subsystem/jobs/proc/format_jobs_for_id_computer(obj/item/card/id/tgtcard)
+ var/list/jobs_to_formats = list()
+ if(tgtcard)
+ var/mob/M = tgtcard.getPlayer()
+ for(var/datum/job/job in occupations)
+ if(tgtcard.assignment && tgtcard.assignment == job.title)
+ jobs_to_formats[job.title] = "disabled" // the job they already have is pre-selected
+ else if(!job.would_accept_job_transfer_from_player(M))
+ jobs_to_formats[job.title] = "linkDiscourage" // karma jobs they don't have available are discouraged
+ else if(job.total_positions && !job.current_positions && job.title != "Civilian")
+ jobs_to_formats[job.title] = "linkEncourage" // jobs with nobody doing them at all are encouraged
+ else if(job.total_positions >= 0 && job.current_positions >= job.total_positions)
+ jobs_to_formats[job.title] = "linkDiscourage" // jobs that are full (no free positions) are discouraged
+ return jobs_to_formats
+
+
+/datum/controller/subsystem/jobs/proc/log_job_transfer(transferee, oldvalue, newvalue, whodidit)
+ id_change_records["[id_change_counter]"] = list("transferee" = transferee, "oldvalue" = oldvalue, "newvalue" = newvalue, "whodidit" = whodidit, "timestamp" = station_time_timestamp())
+ id_change_counter++
+
+/datum/controller/subsystem/jobs/proc/slot_job_transfer(oldtitle, newtitle)
+ var/datum/job/oldjobdatum = SSjobs.GetJob(oldtitle)
+ var/datum/job/newjobdatum = SSjobs.GetJob(newtitle)
+ if(istype(oldjobdatum) && oldjobdatum.current_positions > 0 && istype(newjobdatum))
+ if(!(oldjobdatum.title in command_positions) && !(newjobdatum.title in command_positions))
+ oldjobdatum.current_positions--
+ newjobdatum.current_positions++
+
+
+/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom)
+ var/record_html = "
"
+
+ var/table_headers = list("Crewman", "Old Rank", "New Rank", "Authorized By", "Time")
+ var/hidden_fields = list("deletedby")
+ if(centcom)
+ table_headers += "Deleted By"
+ record_html += ""
+ for(var/thisheader in table_headers)
+ record_html += "| [thisheader] | "
+ record_html += "
"
+
+ var/visible_record_count = 0
+ for(var/thisid in id_change_records)
+ var/thisrecord = id_change_records[thisid]
+
+ if(thisrecord["deletedby"] && !centcom)
+ continue
+
+ record_html += ""
+ for(var/lkey in thisrecord)
+ if(lkey in hidden_fields)
+ if(centcom)
+ record_html += "| [thisrecord[lkey]] | "
+ else
+ continue
+ else
+ record_html += "[thisrecord[lkey]] | "
+ record_html += "
"
+ visible_record_count++
+
+ record_html += "
"
+
+ if(!visible_record_count)
+ return "No records on file yet."
+ return record_html
+
+/datum/controller/subsystem/jobs/proc/delete_log_records(sourceuser, delete_all)
+ . = 0
+ if(!sourceuser)
+ return
+ var/list/new_id_change_records = list()
+ for(var/thisid in id_change_records)
+ var/thisrecord = id_change_records[thisid]
+ if(!thisrecord["deletedby"])
+ if(delete_all || thisrecord["whodidit"] == sourceuser)
+ thisrecord["deletedby"] = sourceuser
+ .++
+ new_id_change_records["[id_change_counter]"] = thisrecord
+ id_change_counter++
+ id_change_records = new_id_change_records
\ No newline at end of file
diff --git a/code/controllers/subsystem/machinery.dm b/code/controllers/subsystem/machinery.dm
index 7038ad4db4f..9a73c1dbabb 100644
--- a/code/controllers/subsystem/machinery.dm
+++ b/code/controllers/subsystem/machinery.dm
@@ -64,23 +64,6 @@ SUBSYSTEM_DEF(machines)
if(MC_TICK_CHECK)
return
-/datum/controller/subsystem/machines/proc/process_premachines(resumed = 0)
- /* Literally exists as snowflake for fucking powersinks goddamnit */
- if(!resumed)
- src.currentrun = GLOB.processing_power_items.Copy()
- //cache for sanid speed (lists are references anyways)
- var/list/currentrun = src.currentrun
- while(currentrun.len)
- var/obj/item/I = currentrun[currentrun.len]
- currentrun.len--
- if(!QDELETED(I))
- if(!I.pwr_drain())
- GLOB.processing_power_items.Remove(I)
- else
- GLOB.processing_power_items.Remove(I)
- if(MC_TICK_CHECK)
- return
-
/datum/controller/subsystem/machines/proc/process_machines(resumed = 0)
var/seconds = wait * 0.1
if(!resumed)
@@ -113,13 +96,6 @@ SUBSYSTEM_DEF(machines)
if(state != SS_RUNNING)
return
resumed = 0
- currentpart = SSMACHINES_PREMACHINERY
-
- if(currentpart == SSMACHINES_PREMACHINERY || !resumed)
- process_premachines(resumed)
- if(state != SS_RUNNING)
- return
- resumed = 0
currentpart = SSMACHINES_MACHINERY
if(currentpart == SSMACHINES_MACHINERY || !resumed)
diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm
index 710a1ff64c1..37d77687d8c 100644
--- a/code/datums/diseases/critical.dm
+++ b/code/datums/diseases/critical.dm
@@ -19,7 +19,7 @@
spread_text = "The patient is in shock"
max_stages = 3
spread_flags = SPECIAL
- cure_text = "Saline Solution"
+ cure_text = "Saline-Glucose Solution"
cures = list("salglu_solution")
cure_chance = 10
viable_mobtypes = list(/mob/living/carbon/human)
@@ -80,7 +80,7 @@
spread_text = "The patient is having a cardiac emergency"
max_stages = 3
spread_flags = SPECIAL
- cure_text = "Cardiac Stimulants"
+ cure_text = "Atropine or Epinephrine"
cures = list("atropine", "epinephrine")
cure_chance = 10
needs_all_cures = FALSE
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 6f589101f2f..65b3940cde2 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -29,6 +29,7 @@
var/memory
var/assigned_role //assigned role is what job you're assigned to when you join the station.
+ var/playtime_role //if set, overrides your assigned_role for the purpose of playtime awards. Set by IDcomputer when your ID is changed.
var/special_role //special roles are typically reserved for antags or roles like ERT. If you want to avoid a character being automatically announced by the AI, on arrival (becuase they're an off station character or something); ensure that special_role and assigned_role are equal.
var/offstation_role = FALSE //set to true for ERT, deathsquad, abductors, etc, that can go from and to z2 at will and shouldn't be antag targets
var/list/restricted_roles = list()
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 067223c0285..dcf5223e29c 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -63,6 +63,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/nad_allowed = FALSE //is the station NAD allowed on this area?
+ var/fast_despawn = FALSE
+
/*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*/
var/list/teleportlocs = list()
@@ -855,10 +857,12 @@ var/list/ghostteleportlocs = list()
/area/prison/solitary
name = "\improper Solitary Confinement"
icon_state = "brig"
+ fast_despawn = TRUE
/area/prison/cell_block
name = "\improper Prison Cell Block"
icon_state = "brig"
+ fast_despawn = TRUE
/area/prison/cell_block/A
name = "\improper Prison Cell Block A"
@@ -1676,6 +1680,7 @@ var/list/ghostteleportlocs = list()
/area/security/permabrig
name = "\improper Prison Wing"
icon_state = "sec_prison_perma"
+ fast_despawn = TRUE
/area/security/prison
name = "\improper Prison Wing"
diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm
index fde2c9bf8a3..fd6aa8c9f4a 100644
--- a/code/game/gamemodes/blob/blobs/factory.dm
+++ b/code/game/gamemodes/blob/blobs/factory.dm
@@ -23,13 +23,12 @@
/obj/structure/blob/factory/run_action()
if(spores.len >= max_spores)
- return 0
+ return
if(spore_delay > world.time)
- return 0
+ return
+ flick("blob_factory_glow", src)
spore_delay = world.time + 100 // 10 seconds
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
BS.color = overmind.blob_reagent_datum.complementary_color
BS.overmind = overmind
overmind.blob_mobs.Add(BS)
- return 0
-
diff --git a/code/game/gamemodes/blob/blobs/resource.dm b/code/game/gamemodes/blob/blobs/resource.dm
index 7198d3ef081..14c240f681b 100644
--- a/code/game/gamemodes/blob/blobs/resource.dm
+++ b/code/game/gamemodes/blob/blobs/resource.dm
@@ -13,13 +13,9 @@
qdel(src)
/obj/structure/blob/resource/run_action()
-
if(resource_delay > world.time)
- return 0
-
+ return
+ flick("blob_resource_glow", src)
resource_delay = world.time + 40 // 4 seconds
-
if(overmind)
overmind.add_points(1)
- return 0
-
diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm
index 748823829b5..61196c9862f 100644
--- a/code/game/gamemodes/blob/powers.dm
+++ b/code/game/gamemodes/blob/powers.dm
@@ -331,17 +331,13 @@
/mob/camera/blob/verb/rally_spores_power()
set category = "Blob"
- set name = "Rally Spores (5)"
+ set name = "Rally Spores"
set desc = "Rally the spores to move to your location."
var/turf/T = get_turf(src)
rally_spores(T)
/mob/camera/blob/proc/rally_spores(var/turf/T)
-
- if(!can_buy(5))
- return
-
to_chat(src, "You rally your spores.")
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
@@ -373,12 +369,12 @@
if(!N)
to_chat(src, "A node is required to birth your offspring...")
return
-
+
if(!can_buy(100))
return
split_used = TRUE
-
+
new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, "offspring")
qdel(N)
@@ -399,7 +395,8 @@
else
to_chat(usr, "You broadcast with your minions, [speak_text]")
for(var/mob/living/simple_animal/hostile/blob_minion in blob_mobs)
- blob_minion.say(speak_text)
+ if(blob_minion.stat == CONSCIOUS)
+ blob_minion.say(speak_text)
return
/mob/camera/blob/verb/create_storage()
@@ -478,4 +475,4 @@
to_chat(src, "Node Blobs are blobs which grow, like the core. Like the core it can activate resource and factory blobs.")
to_chat(src, "In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.")
to_chat(src, "Shortcuts: Click = Expand Blob | CTRL Click = Create Shield Blob | Middle Mouse Click = Rally Spores | Alt Click = Remove Blob")
- to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
\ No newline at end of file
+ to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 6ae995a1e77..972075ceece 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -508,6 +508,12 @@ var/round_start_time = 0
//Ask the event manager to print round end information
SSevents.RoundEnd()
+ // Add AntagHUD to everyone, see who was really evil the whole time!
+ for(var/datum/atom_hud/antag/H in huds)
+ for(var/m in GLOB.player_list)
+ var/mob/M = m
+ H.add_hud_to(M)
+
return 1
/datum/controller/gameticker/proc/HasRoundStarted()
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index bde3f7ee8e9..54b01ff38ce 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -2,7 +2,9 @@
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invsible
//Don't hear deadchat and are NOT normal ghosts
//Admin-spawn or random event
+
#define INVISIBILITY_REVENANT 50
+#define REVENANT_NAME_FILE "revenant_names.json"
/mob/living/simple_animal/revenant
name = "revenant"
@@ -124,9 +126,17 @@
ghost_darkness_images |= ghostimage
updateallghostimages()
remove_from_all_data_huds()
+ random_revenant_name()
addtimer(CALLBACK(src, .proc/firstSetupAttempt), 15 SECONDS) // Give admin 15 seconds to put in a ghost (Or wait 15 seconds before giving it objectives)
+/mob/living/simple_animal/revenant/proc/random_revenant_name()
+ var/built_name = ""
+ built_name += pick(strings(REVENANT_NAME_FILE, "spirit_type"))
+ built_name += " of "
+ built_name += pick(strings(REVENANT_NAME_FILE, "adjective"))
+ built_name += pick(strings(REVENANT_NAME_FILE, "theme"))
+ name = built_name
/mob/living/simple_animal/revenant/proc/firstSetupAttempt()
if(mind)
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index 9e06d662053..eebe4f2e9e4 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -256,6 +256,8 @@
if(H.mind && H.mind.initial_account)
C.associated_account_number = H.mind.initial_account.account_number
+ C.owner_uid = H.UID()
+ C.owner_ckey = H.ckey
/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H)
var/obj/item/pda/PDA = H.wear_pda
@@ -265,3 +267,10 @@
PDA.ownjob = C.assignment
PDA.ownrank = C.rank
PDA.name = "PDA-[H.real_name] ([PDA.ownjob])"
+
+/datum/job/proc/would_accept_job_transfer_from_player(mob/player)
+ if(!guest_jobbans(title)) // actually checks if job is a whitelisted position
+ return TRUE
+ if(!istype(player))
+ return FALSE
+ return is_job_whitelisted(player, title)
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
index 6f5be69280a..7c228ba554f 100644
--- a/code/game/jobs/job_exp.dm
+++ b/code/game/jobs/job_exp.dm
@@ -257,13 +257,14 @@ var/global/list/role_playtime_requirements = list(
play_records[rtype] = text2num(read_records[rtype])
else
play_records[rtype] = 0
- if(mob.stat == CONSCIOUS && mob.mind.assigned_role)
+ var/myrole = mob.mind.playtime_role ? mob.mind.playtime_role : mob.mind.assigned_role
+ if(mob.stat == CONSCIOUS && myrole)
play_records[EXP_TYPE_LIVING] += minutes
if(announce_changes)
to_chat(mob,"You got: [minutes] Living EXP!")
for(var/category in exp_jobsmap)
if(exp_jobsmap[category]["titles"])
- if(mob.mind.assigned_role in exp_jobsmap[category]["titles"])
+ if(myrole in exp_jobsmap[category]["titles"])
play_records[category] += minutes
if(announce_changes)
to_chat(mob,"You got: [minutes] [category] EXP!")
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index 95e609ee524..e392b468557 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -371,7 +371,15 @@ update_flag
SSnanoui.update_uis(src) // Update all NanoUIs attached to src
-
+/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve)
+ . = ..()
+ if(.)
+ if(close_valve)
+ valve_open = FALSE
+ update_icon()
+ investigate_log("Valve was closed by [key_name(user)].
", "atmos")
+ 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)
diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm
index dfecc3a4409..71e1310f96b 100644
--- a/code/game/machinery/atmoalter/portable_atmospherics.dm
+++ b/code/game/machinery/atmoalter/portable_atmospherics.dm
@@ -82,16 +82,49 @@
/obj/machinery/portable_atmospherics/portableConnectorReturnAir()
return air_contents
-/obj/machinery/portable_atmospherics/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
- if((istype(W, /obj/item/tank) && !( src.destroyed )))
- if(src.holding)
- return
- var/obj/item/tank/T = W
- user.drop_item()
- T.loc = src
- src.holding = T
- update_icon()
+/obj/machinery/portable_atmospherics/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
return
+ if(!in_range(src, user))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ if(holding)
+ to_chat(user, "You remove [holding] from [src].")
+ replace_tank(user, TRUE)
+
+/obj/machinery/portable_atmospherics/examine(mob/user)
+ ..()
+ if(holding)
+ to_chat(user, "\The [src] contains [holding]. Alt-click [src] to remove it.")
+
+/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank)
+ if(holding)
+ holding.forceMove(drop_location())
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(holding)
+ if(new_tank)
+ holding = new_tank
+ else
+ holding = null
+ update_icon()
+ return TRUE
+
+/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/tank))
+ if(!(stat & BROKEN))
+ if(!user.drop_item())
+ return
+ var/obj/item/tank/T = W
+ user.drop_item()
+ if(src.holding)
+ to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].")
+ replace_tank(user, FALSE)
+ T.loc = src
+ src.holding = T
+ update_icon()
+ return
else if(istype(W, /obj/item/wrench))
if(connected_port)
diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm
index a7680e815ff..5bd441f6748 100644
--- a/code/game/machinery/atmoalter/pump.dm
+++ b/code/game/machinery/atmoalter/pump.dm
@@ -95,6 +95,16 @@
/obj/machinery/portable_atmospherics/pump/return_air()
return air_contents
+/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve)
+ . = ..()
+ if(.)
+ if(close_valve)
+ if(on)
+ on = FALSE
+ update_icon()
+ else if(on && holding && direction_out)
+ investigate_log("[key_name(user)] started a transfer into [holding].
", "atmos")
+
/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob)
src.add_hiddenprint(user)
return src.attack_hand(user)
@@ -139,10 +149,14 @@
if(href_list["power"])
on = !on
+ if(on && direction_out)
+ investigate_log("[key_name(usr)] started a transfer into [holding].
", "atmos")
update_icon()
if(href_list["direction"])
direction_out = !direction_out
+ if(on && holding)
+ investigate_log("[key_name(usr)] started a transfer into [holding].
", "atmos")
if(href_list["remove_tank"])
if(holding)
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index 591cf1878d0..d404db8ae0a 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -56,6 +56,17 @@
/obj/item/circuitboard/machine
board_type = "machine"
+/obj/item/circuitboard/examine(mob/user)
+ ..()
+ if(LAZYLEN(req_components))
+ var/list/nice_list = list()
+ for(var/B in req_components)
+ var/atom/A = B
+ if(!ispath(A))
+ continue
+ nice_list += list("[req_components[A]] [initial(A.name)]")
+ to_chat(user,"Required components: [english_list(nice_list)].")
+
/obj/item/circuitboard/message_monitor
name = "Circuit board (Message Monitor)"
build_path = /obj/machinery/computer/message_monitor
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 5cb19358358..f4d21cbd125 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -66,14 +66,15 @@ var/time_last_changed_position = 0
/obj/machinery/computer/card/proc/get_target_rank()
return modify && modify.assignment ? modify.assignment : "Unassigned"
-/obj/machinery/computer/card/proc/format_jobs(list/jobs)
+/obj/machinery/computer/card/proc/format_jobs(list/jobs, targetrank, list/jobformats)
var/list/formatted = list()
for(var/job in jobs)
if(job_in_department(SSjobs.GetJob(job)))
formatted.Add(list(list(
"display_name" = replacetext(job, " ", " "),
- "target_rank" = get_target_rank(),
- "job" = job)))
+ "target_rank" = targetrank,
+ "job" = job,
+ "jlinkformat" = jobformats[job] ? jobformats[job] : null)))
return formatted
@@ -117,12 +118,14 @@ var/time_last_changed_position = 0
if(!usr.get_active_hand() && Adjacent(usr))
usr.put_in_hands(scan)
scan = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else if(modify)
to_chat(usr, "You remove \the [modify] from \the [src].")
modify.forceMove(get_turf(src))
if(!usr.get_active_hand() && Adjacent(usr))
usr.put_in_hands(modify)
modify = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else
to_chat(usr, "There is nothing to remove from the console.")
@@ -134,10 +137,12 @@ var/time_last_changed_position = 0
user.drop_item()
id_card.loc = src
scan = id_card
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else if(!modify)
user.drop_item()
id_card.loc = src
modify = id_card
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
SSnanoui.update_uis(src)
attack_hand(user)
@@ -242,6 +247,7 @@ var/time_last_changed_position = 0
data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----"
data["target_rank"] = get_target_rank()
data["scan_name"] = scan ? scan.name : "-----"
+ data["scan_owner"] = scan && scan.registered_name ? scan.registered_name : null
data["authenticated"] = is_authenticated(user)
data["has_modify"] = !!modify
data["account_number"] = modify ? modify.associated_account_number : null
@@ -249,15 +255,19 @@ var/time_last_changed_position = 0
data["all_centcom_access"] = null
data["regions"] = null
data["target_dept"] = target_dept
+ data["card_is_owned"] = modify && modify.owner_ckey
- data["engineering_jobs"] = format_jobs(engineering_positions)
- data["medical_jobs"] = format_jobs(medical_positions)
- data["science_jobs"] = format_jobs(science_positions)
- data["security_jobs"] = format_jobs(security_positions)
- data["support_jobs"] = format_jobs(support_positions)
- data["civilian_jobs"] = format_jobs(civilian_positions)
- data["special_jobs"] = format_jobs(whitelisted_positions)
- data["centcom_jobs"] = format_jobs(get_all_centcom_jobs())
+ var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify)
+
+ data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats)
+ data["engineering_jobs"] = format_jobs(engineering_positions, data["target_rank"], job_formats)
+ data["medical_jobs"] = format_jobs(medical_positions, data["target_rank"], job_formats)
+ data["science_jobs"] = format_jobs(science_positions, data["target_rank"], job_formats)
+ data["security_jobs"] = format_jobs(security_positions, data["target_rank"], job_formats)
+ data["support_jobs"] = format_jobs(support_positions, data["target_rank"], job_formats)
+ data["civilian_jobs"] = format_jobs(civilian_positions, data["target_rank"], job_formats)
+ data["special_jobs"] = format_jobs(whitelisted_positions, data["target_rank"], job_formats)
+ data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats)
data["card_skins"] = format_card_skins(get_station_card_skins())
data["job_slots"] = format_job_slots()
@@ -268,6 +278,9 @@ var/time_last_changed_position = 0
data["cooldown_mins"] = mins
data["cooldown_secs"] = (seconds < 10) ? "0[seconds]" : seconds
+ if(mode == 3 && is_authenticated(user))
+ data["id_change_html"] = SSjobs.fetch_transfer_record_html(is_centcom())
+
if(modify)
data["current_skin"] = modify.icon_state
@@ -315,15 +328,18 @@ var/time_last_changed_position = 0
if(!usr.get_active_hand() && Adjacent(usr))
usr.put_in_hands(modify)
modify = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else
modify.forceMove(get_turf(src))
modify = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else if(Adjacent(usr))
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
modify = I
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
if("scan")
if(scan)
@@ -332,15 +348,18 @@ var/time_last_changed_position = 0
if(!usr.get_active_hand() && Adjacent(usr))
usr.put_in_hands(scan)
scan = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else
scan.forceMove(get_turf(src))
scan = null
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
else if(Adjacent(usr))
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
if("access")
if(href_list["allowed"] && !target_dept)
@@ -361,7 +380,7 @@ var/time_last_changed_position = 0
if("assign")
if(is_authenticated(usr) && modify)
var/t1 = href_list["assign_target"]
- if(target_dept && modify.assignment == "Unassigned")
+ if(target_dept && modify.assignment == "Demoted")
visible_message("[src]: Demoted individuals must see the HoP for a new job.")
return 0
if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE))
@@ -373,11 +392,12 @@ var/time_last_changed_position = 0
var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN))
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t && modify)
+ SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name)
modify.assignment = temp_t
log_game("[key_name(usr)] has given \"[modify.registered_name]\" the custom job title \"[temp_t]\".")
else
var/list/access = list()
- if(is_centcom())
+ if(is_centcom() && islist(get_centcom_access(t1)))
access = get_centcom_access(t1)
else
var/datum/job/jobdatum
@@ -397,6 +417,16 @@ var/time_last_changed_position = 0
if(t1 == "Civilian")
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)
+
+ var/mob/living/carbon/human/H = modify.getPlayer()
+ if(istype(H))
+ if(jobban_isbanned(H, t1))
+ message_admins("[ADMIN_FULLMONTY(H)] has been assigned the job [t1], in possible violation of their job ban.")
+ if(H.mind)
+ H.mind.playtime_role = t1
+
modify.access = access
modify.rank = t1
modify.assignment = t1
@@ -426,6 +456,20 @@ var/time_last_changed_position = 0
if("mode")
mode = text2num(href_list["mode_target"])
+ if("wipe_my_logs")
+ if(is_authenticated(usr) && is_centcom())
+ var/delcount = SSjobs.delete_log_records(scan.registered_name, FALSE)
+ if(delcount)
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+ SSnanoui.update_uis(src)
+
+ if("wipe_all_logs")
+ if(is_authenticated(usr) && !target_dept)
+ var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE)
+ if(delcount)
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+ SSnanoui.update_uis(src)
+
if("print")
if(!printing && !target_dept)
printing = 1
@@ -464,14 +508,15 @@ var/time_last_changed_position = 0
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]\".")
+ SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name)
modify.assignment = "Terminated"
modify.access = list()
callHook("terminate_employee", list(modify))
if("demote")
if(is_authenticated(usr))
- if(modify.assignment == "Unassigned")
- visible_message("[src]: Unassigned crew cannot be demoted any further. If further action is warranted, ask the Captain about Termination.")
+ if(modify.assignment == "Demoted")
+ visible_message("[src]: Demoted crew cannot be demoted any further. If further action is warranted, ask the Captain about Termination.")
return 0
if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE))
visible_message("[src]: Heads may only demote members of their own department.")
@@ -482,12 +527,13 @@ var/time_last_changed_position = 0
access = jobdatum.get_access()
var/jobnamedata = modify.getRankAndAssignment()
- log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Unassigned)\".")
- message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Unassigned)\".")
+ 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)\".")
+ SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name)
modify.access = access
modify.rank = "Civilian"
- modify.assignment = "Unassigned"
+ modify.assignment = "Demoted"
modify.icon_state = "id"
if("make_job_available")
diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm
index 256b2212c11..416c73d2570 100644
--- a/code/game/machinery/computer/power.dm
+++ b/code/game/machinery/computer/power.dm
@@ -45,7 +45,7 @@
if(isturf(T))
attached = locate() in T
if(attached)
- return attached.get_powernet()
+ return attached.powernet
/obj/machinery/computer/monitor/attack_ai(mob/user)
attack_hand(user)
diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm
index 1b3dfe19001..bb5847fc6fb 100644
--- a/code/game/machinery/pipe/pipe_dispenser.dm
+++ b/code/game/machinery/pipe/pipe_dispenser.dm
@@ -2,8 +2,8 @@
name = "Pipe Dispenser"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "pipe_d"
- density = 1
- anchored = 1
+ density = TRUE
+ anchored = TRUE
var/unwrenched = 0
var/wait = 0
@@ -75,29 +75,25 @@
/obj/machinery/pipedispenser/Topic(href, href_list)
if(..() || unwrenched)
- return 1
+ return
usr.set_machine(src)
add_fingerprint(usr)
+ if(world.time < wait + 4)
+ return
+ wait = world.time
if(href_list["make"])
- if(!wait)
- var/p_type = text2num(href_list["make"])
- var/p_dir = text2num(href_list["dir"])
- var/obj/item/pipe/P = new (loc, pipe_type=p_type, dir=p_dir)
- P.update()
- P.add_fingerprint(usr)
- wait = world.time + 10
+ var/p_type = text2num(href_list["make"])
+ var/p_dir = text2num(href_list["dir"])
+ var/obj/item/pipe/P = new (loc, pipe_type=p_type, dir=p_dir)
+ P.update()
+ P.add_fingerprint(usr)
if(href_list["makemeter"])
- if(wait < world.time)
- new /obj/item/pipe_meter(loc)
- wait = world.time + 15
+ new /obj/item/pipe_meter(loc)
if(href_list["makegsensor"])
- if(!wait)
- new /obj/item/pipe_gsensor(loc)
- wait = 1
- spawn(15)
- wait = 0
+ new /obj/item/pipe_gsensor(loc)
+ return TRUE
/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
add_fingerprint(usr)
@@ -140,8 +136,6 @@
name = "Disposal Pipe Dispenser"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "pipe_d"
- density = 1
- anchored = 1.0
//Allow you to drag-drop disposal pipes into it
/obj/machinery/pipedispenser/disposal/MouseDrop_T(var/obj/structure/disposalconstruct/pipe, mob/usr)
@@ -182,16 +176,10 @@
popup.open()
/obj/machinery/pipedispenser/disposal/Topic(href, href_list)
- if(..() || unwrenched)
- return 1
-
- usr.set_machine(src)
- add_fingerprint(usr)
-
- if(wait < world.time)
+ if(!..())
+ return
+ if(href_list["dmake"])
var/p_type = text2num(href_list["dmake"])
var/obj/structure/disposalconstruct/C = new(loc, p_type)
if(p_type in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE))
C.density = TRUE
- C.add_fingerprint(usr)
- wait = world.time + 15
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index 304dc7dc639..6422e94dc74 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -188,11 +188,11 @@
/obj/machinery/power/singularity_beacon/process()
if(!active)
return PROCESS_KILL
+
+ if(surplus() >= 1500)
+ add_load(1500)
else
- if(surplus() > 1500)
- draw_power(1500)
- else
- Deactivate()
+ Deactivate()
/obj/machinery/power/singularity_beacon/syndicate
icontype = "beaconsynd"
diff --git a/code/game/machinery/telecomms/ntsl2.dm b/code/game/machinery/telecomms/ntsl2.dm
index f01f173ebb3..604fcdfbe7e 100644
--- a/code/game/machinery/telecomms/ntsl2.dm
+++ b/code/game/machinery/telecomms/ntsl2.dm
@@ -10,109 +10,109 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
*/
/datum/nttc_configuration
// ALL OF THE JOB CRAP
- // Dict of all jobs and their colors
+ // Dict of all jobs and their department color classes
var/all_jobs = list(
// AI
- "AI" = "#FF00FF",
- "Android" = "#FF00FF",
- "Cyborg" = "#FF00FF",
- "Personal AI" = "#FF00FF",
- "Robot" = "#FF00FF",
+ "AI" = "airadio",
+ "Android" = "airadio",
+ "Cyborg" = "airadio",
+ "Personal AI" = "airadio",
+ "Robot" = "airadio",
// Civilian + Varients
- "Assistant" = "#408010",
- "Businessman" = "#408010",
- "Civilian" = "#408010",
- "Tourist" = "#408010",
- "Trader" = "#408010",
+ "Assistant" = "radio",
+ "Businessman" = "radio",
+ "Civilian" = "radio",
+ "Tourist" = "radio",
+ "Trader" = "radio",
// Command (Solo command, not department heads)
- "Blueshield" = "#204090",
- "Captain" = "#204090",
- "Head of Personnel" = "#204090",
- "Nanotrasen Representative" = "#204090",
+ "Blueshield" = "comradio",
+ "Captain" = "comradio",
+ "Head of Personnel" = "comradio",
+ "Nanotrasen Representative" = "comradio",
// Engineeering
- "Atmospheric Technician" = "#A66300",
- "Chief Engineer" = "#A66300",
- "Electrician" = "#A66300",
- "Engine Technician" = "#A66300",
- "Life Support Specialist" = "#A66300",
- "Maintenance Technician" = "#A66300",
- "Mechanic" = "#A66300",
- "Station Engineer" = "#A66300",
+ "Atmospheric Technician" = "engradio",
+ "Chief Engineer" = "engradio",
+ "Electrician" = "engradio",
+ "Engine Technician" = "engradio",
+ "Life Support Specialist" = "engradio",
+ "Maintenance Technician" = "engradio",
+ "Mechanic" = "engradio",
+ "Station Engineer" = "engradio",
// ERT
- "Emergency Response Team Engineer" = "#5C5C7C",
- "Emergency Response Team Leader" = "#5C5C7C",
- "Emergency Response Team Medic" = "#5C5C7C",
- "Emergency Response Team Member" = "#5C5C7C",
- "Emergency Response Team Officer" = "#5C5C7C",
+ "Emergency Response Team Engineer" = "dsquadradio", // I know this says deathsquad but the class for responseteam is neon green. No.
+ "Emergency Response Team Leader" = "dsquadradio",
+ "Emergency Response Team Medic" = "dsquadradio",
+ "Emergency Response Team Member" = "dsquadradio",
+ "Emergency Response Team Officer" = "dsquadradio",
// Medical
- "Chemist" = "#009190",
- "Chief Medical Officer" = "#009190",
- "Coroner" = "#009190",
- "Medical Doctor" = "#009190",
- "Microbiologist" = "#009190",
- "Nurse" = "#009190",
- "Paramedic" = "#009190",
- "Pharmacologist" = "#009190",
- "Pharmacist" = "#009190",
- "Psychiatrist" = "#009190",
- "Psychologist" = "#009190",
- "Surgeon" = "#009190",
- "Therapist" = "#009190",
- "Virologist" = "#009190",
+ "Chemist" = "medradio",
+ "Chief Medical Officer" = "medradio",
+ "Coroner" = "medradio",
+ "Medical Doctor" = "medradio",
+ "Microbiologist" = "medradio",
+ "Nurse" = "medradio",
+ "Paramedic" = "medradio",
+ "Pharmacologist" = "medradio",
+ "Pharmacist" = "medradio",
+ "Psychiatrist" = "medradio",
+ "Psychologist" = "medradio",
+ "Surgeon" = "medradio",
+ "Therapist" = "medradio",
+ "Virologist" = "medradio",
// Science
- "Anomalist" = "#993399",
- "Biomechanical Engineer" = "#993399",
- "Chemical Researcher" = "#993399",
- "Geneticist" = "#993399",
- "Mechatronic Engineer" = "#993399",
- "Plasma Researcher" = "#993399",
- "Research Director" = "#993399",
- "Roboticist" = "#993399",
- "Scientist" = "#993399",
- "Xenoarcheologist" = "#993399",
- "Xenobiologist" = "#993399",
+ "Anomalist" = "sciradio",
+ "Biomechanical Engineer" = "sciradio",
+ "Chemical Researcher" = "sciradio",
+ "Geneticist" = "sciradio",
+ "Mechatronic Engineer" = "sciradio",
+ "Plasma Researcher" = "sciradio",
+ "Research Director" = "sciradio",
+ "Roboticist" = "sciradio",
+ "Scientist" = "sciradio",
+ "Xenoarcheologist" = "sciradio",
+ "Xenobiologist" = "sciradio",
// Security
- "Brig Physician" = "#A30000",
- "Detective" = "#A30000",
- "Forensic Technician" = "#A30000",
- "Head of Security" = "#A30000",
- "Human Resources Agent" = "#A30000",
- "Internal Affairs Agent" = "#A30000",
- "Magistrate" = "#A30000",
- "Security Officer" = "#A30000",
- "Security Pod Pilot" = "#A30000",
- "Warden" = "#A30000",
+ "Brig Physician" = "secradio",
+ "Detective" = "secradio",
+ "Forensic Technician" = "secradio",
+ "Head of Security" = "secradio",
+ "Human Resources Agent" = "secradio",
+ "Internal Affairs Agent" = "secradio",
+ "Magistrate" = "secradio",
+ "Security Officer" = "secradio",
+ "Security Pod Pilot" = "secradio",
+ "Warden" = "secradio",
// Supply
- "Quartermaster" = "#7F6539",
- "Cargo Technician" = "#7F6539",
- "Shaft Miner" = "#7F6539",
- "Spelunker" = "#7F6539",
+ "Quartermaster" = "supradio",
+ "Cargo Technician" = "supradio",
+ "Shaft Miner" = "supradio",
+ "Spelunker" = "supradio",
// Service
- "Barber" = "#80A000",
- "Bartender" = "#80A000",
- "Beautician" = "#80A000",
- "Botanical Researcher" = "#80A000",
- "Botanist" = "#80A000",
- "Butcher" = "#80A000",
- "Chaplain" = "#80A000",
- "Chef" = "#80A000",
- "Clown" = "#80A000",
- "Cook" = "#80A000",
- "Culinary Artist" = "#80A000",
- "Custodial Technician" = "#80A000",
- "Hair Stylist" = "#80A000",
- "Hydroponicist" = "#80A000",
- "Janitor" = "#80A000",
- "Journalist" = "#80A000",
- "Librarian" = "#80A000",
- "Mime" = "#80A000",
+ "Barber" = "srvradio",
+ "Bartender" = "srvradio",
+ "Beautician" = "srvradio",
+ "Botanical Researcher" = "srvradio",
+ "Botanist" = "srvradio",
+ "Butcher" = "srvradio",
+ "Chaplain" = "srvradio",
+ "Chef" = "srvradio",
+ "Clown" = "srvradio",
+ "Cook" = "srvradio",
+ "Culinary Artist" = "srvradio",
+ "Custodial Technician" = "srvradio",
+ "Hair Stylist" = "srvradio",
+ "Hydroponicist" = "srvradio",
+ "Janitor" = "srvradio",
+ "Journalist" = "srvradio",
+ "Librarian" = "srvradio",
+ "Mime" = "srvradio",
)
// Just command members
var/heads = list("Captain", "Head of Personnel", "Nanotrasen Representative", "Blueshield", "Chief Engineer", "Chief Medical Officer", "Research Director", "Head of Security")
// Just ERT
var/ert_jobs = list("Emergency Response Team Officer", "Emergency Response Team Engineer", "Emergency Response Team Medic", "Emergency Response Team Leader", "Emergency Response Team Member")
// Defined so code compiles and incase someone has a non-standard job
- var/job_color = "#000000"
+ var/job_class = "radio"
// NOW FOR ACTUAL TOGGLES
/* Simple Toggles */
var/toggle_activated = TRUE
@@ -274,10 +274,10 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
// All job and coloring shit
if(toggle_job_color || toggle_name_color)
var/job = signal.data["job"]
- job_color = all_jobs[job]
+ job_class = all_jobs[job]
if(toggle_name_color)
- var/new_name = "" + signal.data["name"] + ""
+ var/new_name = "" + signal.data["name"] + ""
signal.data["name"] = new_name
signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
@@ -289,13 +289,13 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
if(toggle_job_color)
switch(job_indicator_type)
if(JOB_STYLE_1)
- new_name = signal.data["name"] + " ([job]) "
+ new_name = signal.data["name"] + " ([job]) "
if(JOB_STYLE_2)
- new_name = signal.data["name"] + " - [job] "
+ new_name = signal.data["name"] + " - [job] "
if(JOB_STYLE_3)
- new_name = "\[[job]\] " + signal.data["name"] + " "
+ new_name = "\[[job]\] " + signal.data["name"] + " "
if(JOB_STYLE_4)
- new_name = "([job]) " + signal.data["name"] + " "
+ new_name = "([job]) " + signal.data["name"] + " "
else
switch(job_indicator_type)
if(JOB_STYLE_1)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 54e2d5bcdc5..97d79c25b6c 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -100,6 +100,10 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
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
+
/obj/item/New()
..()
for(var/path in actions_types)
@@ -340,11 +344,13 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
A.Remove(user)
if(flags & DROPDEL)
qdel(src)
+ in_inventory = FALSE
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
+ in_inventory = TRUE
return TRUE
// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
@@ -374,6 +380,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
var/datum/action/A = X
if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
+ in_inventory = TRUE
/obj/item/proc/item_action_slot_check(slot, mob/user)
return 1
@@ -532,6 +539,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
if(callback) //call the original callback
. = callback.Invoke()
throw_speed = initial(throw_speed) //explosions change this.
+ in_inventory = FALSE
/obj/item/proc/pwr_drain()
return 0 // Process Kill
@@ -585,3 +593,16 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
return
var/mob/owner = loc
owner.regenerate_icons()
+
+/obj/item/proc/openTip(location, control, params, user)
+ openToolTip(user, src, params, title = name, content = "[desc]", theme = "")
+
+/obj/item/MouseEntered(location, control, params)
+ if(in_inventory)
+ var/timedelay = 5
+ var/user = usr
+ tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE)
+
+/obj/item/MouseExited()
+ deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
+ closeToolTip(usr)
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 866ded7aa99..e8bfe429e20 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -307,6 +307,9 @@
brightness_on = 7
icon_state = "torch"
item_state = "torch"
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ light_color = LIGHT_COLOR_ORANGE
on_damage = 10
/obj/item/flashlight/slime
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index 463223f4268..562800c2ce0 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -1,3 +1,7 @@
+#define DISCONNECTED 0
+#define CLAMPED_OFF 1
+#define OPERATING 2
+
// Powersink - used to drain station power
/obj/item/powersink
@@ -13,28 +17,55 @@
throw_range = 2
materials = list(MAT_METAL=750)
origin_tech = "powerstorage=5;syndicate=5"
- var/drain_rate = 1600000 // amount of power to drain per tick
- var/apc_drain_rate = 50 // Max. amount drained from single APC. In Watts.
- var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts.
- var/power_drained = 0 // Amount of power drained.
- var/max_power = 1e10 // Detonation point.
- var/mode = 0 // 0 = off, 1=clamped (off), 2=operating
- var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs.
- var/admins_warned = 0 // stop spam, only warn the admins once that we are about to go boom
+ var/drain_rate = 2000000 // amount of power to drain per tick
+ var/power_drained = 0 // has drained this much power
+ var/max_power = 6e8 // maximum power that can be drained before exploding
+ var/mode = 0 // 0 = off, 1=clamped (off), 2=operating
+ var/admins_warned = FALSE // stop spam, only warn the admins once that we are about to boom
- var/datum/powernet/PN // Our powernet
var/obj/structure/cable/attached // the attached cable
/obj/item/powersink/Destroy()
STOP_PROCESSING(SSobj, src)
- GLOB.processing_power_items.Remove(src)
- PN = null
attached = null
return ..()
-/obj/item/powersink/attackby(var/obj/item/I, var/mob/user)
- if(istype(I, /obj/item/screwdriver))
- if(mode == 0)
+/obj/item/powersink/update_icon()
+ icon_state = "powersink[mode == OPERATING]"
+
+/obj/item/powersink/proc/set_mode(value)
+ if(value == mode)
+ return
+ switch(value)
+ if(DISCONNECTED)
+ attached = null
+ if(mode == OPERATING)
+ STOP_PROCESSING(SSobj, src)
+ anchored = FALSE
+ density = FALSE
+
+ if(CLAMPED_OFF)
+ if(!attached)
+ return
+ if(mode == OPERATING)
+ STOP_PROCESSING(SSobj, src)
+ anchored = TRUE
+ density = TRUE
+
+ if(OPERATING)
+ if(!attached)
+ return
+ START_PROCESSING(SSobj, src)
+ anchored = TRUE
+ density = TRUE
+
+ mode = value
+ update_icon()
+ set_light(0)
+
+/obj/item/powersink/attackby(obj/item/I, mob/user)
+ if(isscrewdriver(I))
+ if(mode == DISCONNECTED)
var/turf/T = loc
if(isturf(T) && !T.intact)
attached = locate() in T
@@ -42,98 +73,81 @@
to_chat(user, "No exposed cable here to attach to.")
return
else
- anchored = 1
- mode = 1
- src.visible_message("[user] attaches [src] to the cable!")
+ set_mode(CLAMPED_OFF)
+ visible_message("[user] attaches [src] to the cable!")
message_admins("Power sink activated by [key_name_admin(user)] at ([x],[y],[z] - JMP)")
log_game("Power sink activated by [key_name(user)] at ([x],[y],[z])")
- return
else
to_chat(user, "Device must be placed over an exposed cable to attach to it.")
- return
else
- if(mode == 2)
- STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite
- GLOB.processing_power_items.Remove(src)
- anchored = 0
- mode = 0
+ set_mode(DISCONNECTED)
src.visible_message("[user] detaches [src] from the cable!")
- set_light(0)
- icon_state = "powersink0"
-
- return
else
- ..()
+ return ..()
/obj/item/powersink/attack_ai()
return
/obj/item/powersink/attack_hand(var/mob/user)
switch(mode)
- if(0)
+ if(DISCONNECTED)
..()
- if(1)
- src.visible_message("[user] activates [src]!")
- mode = 2
- icon_state = "powersink1"
- START_PROCESSING(SSobj, src)
- GLOB.processing_power_items.Add(src)
- if(2) //This switch option wasn't originally included. It exists now. --NeoFite
- src.visible_message("[user] deactivates [src]!")
- mode = 1
- set_light(0)
- icon_state = "powersink0"
- STOP_PROCESSING(SSobj, src)
- GLOB.processing_power_items.Remove(src)
-
-/obj/item/powersink/pwr_drain()
- if(!attached)
- return 0
-
- if(drained_this_tick)
- return 1
- drained_this_tick = 1
-
- var/drained = 0
-
- if(!PN)
- return 1
-
- set_light(12)
- PN.trigger_warning()
- // found a powernet, so drain up to max power from it
- drained = PN.draw_power(drain_rate)
- // if tried to drain more than available on powernet
- // now look for APCs and drain their cells
- if(drained < drain_rate)
- for(var/obj/machinery/power/terminal/T in PN.nodes)
- // Enough power drained this tick, no need to torture more APCs
- if(drained >= drain_rate)
- break
- if(istype(T.master, /obj/machinery/power/apc))
- var/obj/machinery/power/apc/A = T.master
- if(A.operating && A.cell)
- A.cell.charge = max(0, A.cell.charge - apc_drain_rate)
- drained += apc_drain_rate
- if(A.charging == 2) // If the cell was full
- A.charging = 1 // It's no longer full
- power_drained += drained
- return 1
+ if(CLAMPED_OFF)
+ user.visible_message( \
+ "[user] activates \the [src]!", \
+ "You activate \the [src].",
+ "You hear a click.")
+ message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(src)]")
+ log_game("Power sink activated by [key_name(user)] at [AREACOORD(src)]")
+ set_mode(OPERATING)
+ if(OPERATING)
+ user.visible_message( \
+ "[user] deactivates \the [src]!", \
+ "You deactivate \the [src].",
+ "You hear a click.")
+ set_mode(CLAMPED_OFF)
/obj/item/powersink/process()
- drained_this_tick = 0
- power_drained -= min(dissipation_rate, power_drained)
+ if(!attached)
+ set_mode(DISCONNECTED)
+ return
+
+ var/datum/powernet/PN = attached.powernet
+ if(PN)
+ set_light(5)
+
+ // found a powernet, so drain up to max power from it
+
+ var/drained = min (drain_rate, attached.newavail())
+ attached.add_delayedload(drained)
+ power_drained += drained
+
+ // if tried to drain more than available on powernet
+ // now look for APCs and drain their cells
+ if(drained < drain_rate)
+ for(var/obj/machinery/power/terminal/T in PN.nodes)
+ if(istype(T.master, /obj/machinery/power/apc))
+ var/obj/machinery/power/apc/A = T.master
+ if(A.operating && A.cell)
+ A.cell.charge = max(0, A.cell.charge - 50)
+ power_drained += 50
+ if(A.charging == 2) // If the cell was full
+ A.charging = 1 // It's no longer full
+ if(drained >= drain_rate)
+ break
+
if(power_drained > max_power * 0.98)
- if(!admins_warned)
- admins_warned = 1
+ if (!admins_warned)
+ admins_warned = TRUE
message_admins("Power sink at ([x],[y],[z] - JMP) is 95% full. Explosion imminent.")
playsound(src, 'sound/effects/screech.ogg', 100, 1, 1)
+
if(power_drained >= max_power)
+ STOP_PROCESSING(SSobj, src)
explosion(src.loc, 4,8,16,32)
qdel(src)
- return
- if(attached && attached.powernet)
- PN = attached.powernet
- else
- PN = null
+
+#undef DISCONNECTED
+#undef CLAMPED_OFF
+#undef OPERATING
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 5cab26e062b..b0a1286d3b2 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -54,7 +54,7 @@
to_chat(user, "There are [amount] [singular_name]\s in the stack.")
else
to_chat(user, "There are [amount] [name]\s in the stack.")
- to_chat(user,"Ctrl-Shift-click to take a custom amount.")
+ to_chat(user,"Alt-click to take a custom amount.")
/obj/item/stack/proc/add(newamount)
amount += newamount
@@ -263,7 +263,7 @@
else
..()
-/obj/item/stack/CtrlShiftClick(mob/living/user)
+/obj/item/stack/AltClick(mob/living/user)
if(!istype(user) || user.incapacitated())
to_chat(user, "You can't do that right now!")
return
@@ -276,7 +276,7 @@
//get amount from user
var/min = 0
var/max = get_amount()
- var/stackmaterial = round(input(user, "How many sheets do you wish to take out of this stack? (Maximum: [max])") as num)
+ var/stackmaterial = round(input(user, "How many sheets do you wish to take out of this stack? (Maximum: [max])") as null|num)
if(stackmaterial == null || stackmaterial <= min || stackmaterial > get_amount())
return
change_stack(user,stackmaterial)
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 7ac7e3799fa..917bf7f032c 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -98,6 +98,8 @@
//alt titles are handled a bit weirdly in order to unobtrusively integrate into existing ID system
var/assignment = null //can be alt title or the actual job
var/rank = null //actual job
+ var/owner_uid
+ var/owner_ckey
var/dorm = 0 // determines if this ID has claimed a dorm already
var/sex
@@ -195,6 +197,19 @@
jobnamedata += " (" + assignment + ")"
return jobnamedata
+/obj/item/card/id/proc/getPlayer()
+ if(owner_uid)
+ var/mob/living/carbon/human/H = locateUID(owner_uid)
+ if(istype(H) && H.ckey == owner_ckey)
+ return H
+ owner_uid = null
+ if(owner_ckey)
+ for(var/mob/M in GLOB.player_list)
+ if(M.ckey && M.ckey == owner_ckey)
+ owner_uid = M.UID()
+ return M
+ owner_ckey = null
+
/obj/item/card/id/proc/is_untrackable()
return untrackable
diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm
index 666c14801d8..40fc1bf7d56 100644
--- a/code/game/objects/items/weapons/dice.dm
+++ b/code/game/objects/items/weapons/dice.dm
@@ -112,10 +112,9 @@
/obj/item/dice/attack_self(mob/user as mob)
diceroll(user)
-/obj/item/dice/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
- if(!..())
- return
- diceroll(thrower)
+/obj/item/dice/throw_impact(atom/target)
+ diceroll(thrownby)
+ . = ..()
/obj/item/dice/proc/diceroll(mob/user)
result = rand(1, sides)
diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm
index 297966525f0..66e0ba6b110 100644
--- a/code/game/objects/items/weapons/legcuffs.dm
+++ b/code/game/objects/items/weapons/legcuffs.dm
@@ -152,8 +152,14 @@
breakouttime = 35//easy to apply, easy to break out of
gender = NEUTER
origin_tech = "engineering=3;combat=1"
+ hitsound = 'sound/effects/snap.ogg'
var/weaken = 0
+/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+ playsound(loc,'sound/weapons/bolathrow.ogg', 50, TRUE)
+ if(!..())
+ return
+
/obj/item/restraints/legcuffs/bola/throw_impact(atom/hit_atom)
if(..() || !iscarbon(hit_atom))//if it gets caught or the target can't be cuffed,
return//abort
@@ -166,6 +172,7 @@
feedback_add_details("handcuffs","B")
to_chat(C, "[src] ensnares you!")
C.Weaken(weaken)
+ playsound(loc, hitsound, 50, TRUE)
/obj/item/restraints/legcuffs/bola/tactical //traitor variant
name = "reinforced bola"
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index 568fb0ededc..303862ff66f 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -28,6 +28,22 @@
else
has_extinguisher = new/obj/item/extinguisher
+/obj/structure/extinguisher_cabinet/examine(mob/user)
+ ..()
+ to_chat(user, "Alt-click to [opened ? "close":"open"] it.")
+
+/obj/structure/extinguisher_cabinet/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user))
+ return
+ if(!iscarbon(usr))
+ return
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
+ opened = !opened
+ update_icon()
+
/obj/structure/extinguisher_cabinet/Destroy()
QDEL_NULL(has_extinguisher)
return ..()
@@ -37,11 +53,16 @@
return
if(istype(O, /obj/item/extinguisher))
if(!has_extinguisher && opened)
+ if(!user.drop_item())
+ return
user.drop_item(O)
contents += O
has_extinguisher = O
+ update_icon()
to_chat(user, "You place [O] in [src].")
+ return TRUE
else
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
else if(istype(O, /obj/item/weldingtool))
if(has_extinguisher)
@@ -65,6 +86,7 @@
new material_drop(T)
qdel(src)
else
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
update_icon()
@@ -81,21 +103,27 @@
to_chat(user, "You try to move your [temp.name], but cannot!")
return
if(has_extinguisher)
+ if(icon_state == "extinguisher_closed")
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
user.put_in_hands(has_extinguisher)
to_chat(user, "You take [has_extinguisher] from [src].")
has_extinguisher = null
opened = 1
else
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
update_icon()
/obj/structure/extinguisher_cabinet/attack_tk(mob/user)
if(has_extinguisher)
+ if(icon_state == "extinguisher_closed")
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
has_extinguisher.loc = loc
to_chat(user, "You telekinetically remove [has_extinguisher] from [src].")
has_extinguisher = null
opened = 1
else
+ playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
update_icon()
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 154d4438530..1e3cb331cd1 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -273,8 +273,8 @@
var/obj/structure/cable/C = T.get_cable_node()
if(C)
playsound(loc, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5)
- tesla_zap(src, 3, C.powernet.avail * 0.01) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
- C.powernet.load += C.powernet.avail * 0.0375 // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock.
+ tesla_zap(src, 3, C.newavail() * 0.01) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
+ C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock.
return ..()
/obj/structure/grille/broken // Pre-broken grilles for map placement
diff --git a/code/game/world.dm b/code/game/world.dm
index 4681f46498a..86ca6d883bd 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -374,26 +374,28 @@ var/world_topic_spam_protect_time = world.timeofday
// apply some settings from config..
/world/proc/update_status()
+ status = get_status_text()
+
+/proc/get_world_status_text()
+ return world.get_status_text()
+
+/world/proc/get_status_text()
var/s = ""
if(config && config.server_name)
s += "[config.server_name] — "
+ s += "[station_name()] "
+ if(config && config.githuburl)
+ s+= "([game_version])"
- s += "[station_name()]";
- s += " ("
- s += "" //Change this to wherever you want the hub to link to.
- s += "[game_version]"
- s += ""
- s += ")"
- s += "
The Perfect Mix of RP & Action
"
-
-
-
+ if(config && config.server_tag_line)
+ s += "
[config.server_tag_line]"
+ s += "
"
var/list/features = list()
if(ticker)
- if(master_mode)
+ if(master_mode && master_mode != "secret")
features += master_mode
else
features += "STARTING"
@@ -401,39 +403,22 @@ var/world_topic_spam_protect_time = world.timeofday
if(!enter_allowed)
features += "closed"
- features += abandon_allowed ? "respawn" : "no respawn"
+ if(config && config.server_extra_features)
+ features += config.server_extra_features
if(config && config.allow_vote_mode)
features += "vote"
- if(config && config.allow_ai)
- features += "AI allowed"
+ if(config && config.wikiurl)
+ features += "Wiki"
- var/n = 0
- for(var/mob/M in GLOB.player_list)
- if(M.client)
- n++
-
- if(n > 1)
- features += "~[n] players"
- else if(n > 0)
- features += "~[n] player"
-
- /*
- is there a reason for this? the byond site shows 'hosted by X' when there is a proper host already.
- if(host)
- features += "hosted by [host]"
- */
-
-// if(!host && config && config.hostedby)
-// features += "hosted by [config.hostedby]"
+ if(abandon_allowed)
+ features += "respawn"
if(features)
- s += ": [jointext(features, ", ")]"
+ s += "[jointext(features, ", ")]"
- /* does this help? I do not know */
- if(src.status != s)
- src.status = s
+ return s
#define FAILED_DB_CONNECTION_CUTOFF 5
var/failed_db_connections = 0
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index c15600f7bb6..4fcf95dbfd2 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -78,7 +78,7 @@ var/global/nologevent = 0
body += "VV - "
body += "[ADMIN_TP(M,"TP")] - "
if(M.client)
- body += "PM - "
+ body += "PM - "
body += "[ADMIN_SM(M,"SM")] - "
if(ishuman(M) && M.mind)
body += "HM -"
@@ -950,9 +950,6 @@ var/gamma_ship_location = 1 // 0 = station , 1 = space
for(var/obj/machinery/mech_bay_recharge_port/P in toArea)
P.update_recharge_turf()
- for(var/obj/machinery/power/apc/A in toArea)
- A.init()
-
if(gamma_ship_location)
gamma_ship_location = 0
else
@@ -1096,7 +1093,7 @@ var/gamma_ship_location = 1 // 0 = station , 1 = space
dat += "| " + id + " | "
dat += "" + ckey + " | "
dat += "Unlink |
"
-
+
dat += "