")
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..83347c20d3b 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)
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 a022851e45f..fc0f7520e0e 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -329,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
@@ -2015,3 +2016,11 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
/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/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 931a0e394e8..21af95cab48 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())
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index f5f71e65c57..50e70bc62a4 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -34,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 d67875db2de..e43062a18b7 100644
--- a/code/_globalvars/mapping.dm
+++ b/code/_globalvars/mapping.dm
@@ -55,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 74591eb8070..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
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/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/alert.dm b/code/_onclick/hud/alert.dm
index a93e65c762b..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
@@ -59,14 +59,16 @@
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.
@@ -94,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)
@@ -102,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
@@ -507,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
@@ -515,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"
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/item_attack.dm b/code/_onclick/item_attack.dm
index f88244c0cd8..4fd5b956df5 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -81,7 +81,7 @@
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)
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 d348747fb80..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
@@ -808,6 +809,11 @@
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)
for(var/t in Lines)
if(!t) continue
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/subsystem.dm b/code/controllers/subsystem.dm
index 03e54df61c5..c9934b128cf 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -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()
diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm
index 951a7fb1786..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
diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index 1a6e073c808..390c6bc1a62 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -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 619508544a6..e50c372584a 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -4,7 +4,7 @@ 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(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
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/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/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 19b57f59a70..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()
diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/processing/dcs.dm
similarity index 90%
rename from code/controllers/subsystem/dcs.dm
rename to code/controllers/subsystem/processing/dcs.dm
index 09dea24071f..a223f4676f6 100644
--- a/code/controllers/subsystem/dcs.dm
+++ b/code/controllers/subsystem/processing/dcs.dm
@@ -3,6 +3,8 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
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
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/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..08fa7709320 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
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 475bdaccb3c..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()]")
@@ -279,10 +286,22 @@ SUBSYSTEM_DEF(ticker)
for(var/mob/new_player/N in GLOB.mob_list)
if(N.client)
N.new_player_panel_proc()
+
+ // 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 1
+ return TRUE
+
/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null)
if(cinematic)
@@ -407,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/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/verbs.dm b/code/controllers/verbs.dm
index 3eaed85c303..abb0b604069 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")
@@ -26,7 +26,8 @@
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)
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 19935bda130..3b5c9d2f5fb 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -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/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/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/slippery.dm b/code/datums/components/slippery.dm
index 88e51209afb..a7b81856468 100644
--- a/code/datums/components/slippery.dm
+++ b/code/datums/components/slippery.dm
@@ -51,6 +51,6 @@
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) && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb))
+ 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/squeak.dm b/code/datums/components/squeak.dm
index 4c55ad2ad82..f79439f2b3b 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -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/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 e8d0b881ccb..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
@@ -1232,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 34c41c581f2..318dd176e45 100644
--- a/code/datums/diseases/advance/advance.dm
+++ b/code/datums/diseases/advance/advance.dm
@@ -395,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/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/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/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/mind.dm b/code/datums/mind.dm
index dadc8bc6e31..0c992bbb32c 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -47,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
@@ -65,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
@@ -89,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)
@@ -100,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
@@ -544,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")
@@ -1410,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()
@@ -1581,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/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/spell.dm b/code/datums/spell.dm
index 3855dbcfac3..cb10700e5c6 100644
--- a/code/datums/spell.dm
+++ b/code/datums/spell.dm
@@ -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 88f5eec9474..e4c652ae1d3 100644
--- a/code/datums/spells/knock.dm
+++ b/code/datums/spells/knock.dm
@@ -13,11 +13,6 @@
action_icon_state = "knock"
sound = 'sound/magic/knock.ogg'
-// Knock doesn't need to generate an attack log for every turf, set `make_attack_logs` to FALSE and just create a custom one.
-/obj/effect/proc_holder/spell/aoe_turf/knock/perform(list/targets, recharge, mob/user)
- add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL)
- return ..(targets, recharge, user, make_attack_logs = FALSE)
-
/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr)
for(var/turf/T in targets)
for(var/obj/machinery/door/door in T.contents)
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..8a172bb85b1 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
@@ -1726,8 +1725,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/robot.dm b/code/datums/wires/robot.dm
index 789a0472869..04a9509cfe2 100644
--- a/code/datums/wires/robot.dm
+++ b/code/datums/wires/robot.dm
@@ -15,16 +15,16 @@
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"
@@ -52,7 +52,7 @@
if(BORG_WIRE_AI_CONTROL) //Cut the AI wire to reset AI control
if(!mended)
if(R.connected_ai)
- R.connected_ai = null
+ R.disconnect_from_ai()
if(BORG_WIRE_CAMERA)
if(!isnull(R.camera) && !R.scrambledcodes)
@@ -74,8 +74,7 @@
switch(index)
if(BORG_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(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes)
diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm
index b490d150efe..430b5ce88aa 100644
--- a/code/datums/wires/syndicatebomb.dm
+++ b/code/datums/wires/syndicatebomb.dm
@@ -96,7 +96,6 @@
if(BOMB_WIRE_ACTIVATE)
if(!mended && 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/wires.dm b/code/datums/wires/wires.dm
index 09c874ce8a8..40aeda21b58 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -194,7 +194,7 @@ GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "b
return 1
/datum/wires/CanUseTopic(mob/user, datum/topic_state/state)
- if(!CanUse(user))
+ if(!holder || !CanUse(user))
return STATUS_CLOSE
return ..()
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/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 9070d1409f6..c33e7609685 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
-
/*-----------------------------------------------------------------------------*/
@@ -555,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
@@ -678,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"
@@ -1230,6 +1198,7 @@ GLOBAL_LIST_EMPTY(ghostteleportlocs)
name = "\improper Abandoned Teleporter"
icon_state = "teleporter"
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"
@@ -1740,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"
@@ -1816,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"
diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm
index 48760540c78..4a6606d836e 100644
--- a/code/game/area/areas/depot-areas.dm
+++ b/code/game/area/areas/depot-areas.dm
@@ -292,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()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index aed4a4c5c2d..d463c85eaf6 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -50,6 +50,7 @@
loc.handle_atom_del(src)
for(var/atom/movable/AM in contents)
qdel(AM)
+ LAZYCLEARLIST(client_mobs_in_contents)
loc = null
if(pulledby)
pulledby.stop_pulling()
@@ -60,7 +61,7 @@
/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)))
@@ -86,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
@@ -119,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
@@ -505,6 +512,7 @@
return //don't do an animation if attacking self
var/pixel_x_diff = 0
var/pixel_y_diff = 0
+
var/direction = get_dir(src, A)
if(direction & NORTH)
pixel_y_diff = 8
@@ -524,7 +532,8 @@
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
@@ -566,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/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index eae6edd3bd4..d887153c6d5 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -118,17 +118,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) && \
@@ -271,19 +269,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"
+/obj/machinery/dna_scannernew/force_eject_occupant()
+ go_out(null, TRUE)
+
/obj/machinery/dna_scannernew/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
@@ -489,7 +490,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
@@ -751,7 +752,7 @@
return TRUE
if(href_list["ejectOccupant"])
- connected.eject_occupant()
+ connected.eject_occupant(usr)
return TRUE
// Transfer Buffer Management
diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm
index 8b8a86009b9..a887959db48 100644
--- a/code/game/dna/genes/disabilities.dm
+++ b/code/game/dna/genes/disabilities.dm
@@ -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/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 9c7fb99fe81..8f6ba965222 100644
--- a/code/game/gamemodes/blob/blob_report.dm
+++ b/code/game/gamemodes/blob/blob_report.dm
@@ -18,7 +18,7 @@
intercepttext += " Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12. "
intercepttext += "Message ends."
if(2)
- var/nukecode = rand(10000, 99999)
+ var/nukecode = "[rand(10000, 99999)]"
for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines)
if(bomb && bomb.r_code)
if(is_station_level(bomb.z))
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index 46b1972e565..34c717f11a8 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -7,6 +7,7 @@
/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)
@@ -49,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()
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index be794b9092b..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)
@@ -104,10 +104,11 @@
var/mob/C = null
var/list/candidates = list()
if(!new_overmind)
+ // sendit
if(is_offspring)
- candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = src)
else
- candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = src)
if(length(candidates))
C = pick(candidates)
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/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 d6062a02c5d..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()
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/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/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index bf5fbb70731..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))
@@ -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
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 388b16d71af..a68e4c1e9ca 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -140,6 +140,7 @@
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
@@ -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"
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 0e96dd92062..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()
diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm
index 60d2a7ff73b..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
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 2d71df8c979..1e2dd3d554b 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
@@ -421,9 +424,9 @@ proc/display_roundstart_logout_report()
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.")
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index a4b8c594b89..526a2c01083 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.)
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/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index f647ae5a6d1..fdf17f6ddda 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -15,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
@@ -246,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)
@@ -312,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)
diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm
index 75f5fddba53..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)
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_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm
index 5e5f0f87a79..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()
diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
index d7e53bdae28..e58be201521 100644
--- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm
+++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm
@@ -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)
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 5fed8ae00f4..8316364781b 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
@@ -113,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)
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 c6f6218e969..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
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index b09bb7a851f..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 GLOB.mob_living_list)
+ 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 GLOB.mob_living_list)
+ 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"))
diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm
index 182bcc78bd2..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)
@@ -93,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)
@@ -121,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
@@ -177,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]
"}
@@ -218,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 b46bc3c6c51..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!")
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index 7e0f00f8721..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")
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 09b228bdd0c..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)
@@ -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/job/silicon.dm b/code/game/jobs/job/silicon.dm
index c510518716a..fef9c2302b3 100644
--- a/code/game/jobs/job/silicon.dm
+++ b/code/game/jobs/job/silicon.dm
@@ -32,7 +32,7 @@
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_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/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/Sleeper.dm b/code/game/machinery/Sleeper.dm
index e4d0befa61e..7c5a03e29e6 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -411,6 +411,9 @@
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
+/obj/machinery/sleeper/force_eject_occupant()
+ go_out()
+
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "The sleeper does not offer that chemical!")
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index 1ee30b6e9d0..3fa78dd02e6 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -172,6 +172,9 @@
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
+/obj/machinery/bodyscanner/force_eject_occupant()
+ go_out()
+
/obj/machinery/bodyscanner/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm
index 7af7751ff2c..d7a49e40d4b 100644
--- a/code/game/machinery/atmoalter/scrubber.dm
+++ b/code/game/machinery/atmoalter/scrubber.dm
@@ -187,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/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/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/card.dm b/code/game/machinery/computer/card.dm
index 978ba25b8d3..98f56feeb6a 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -52,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)
@@ -390,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)
@@ -419,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))
@@ -436,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
@@ -465,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)
@@ -504,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()
@@ -518,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..5198a97a88c 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -164,7 +164,7 @@
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
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 9a1fd08d289..366946254ea 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -298,10 +298,6 @@
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
@@ -395,8 +391,6 @@
data["shuttle"] = shuttle
- data["atcSquelched"] = GLOB.atc.squelched
-
return data
@@ -424,8 +418,6 @@
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.")
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 131956ca3c1..d6a1ccdd33c 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -27,7 +27,7 @@
return
tgui_interact(user)
-/obj/machinery/computer/crew/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)
+/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)
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/cryo.dm b/code/game/machinery/cryo.dm
index ea0104e75bf..8be09ef30b7 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -237,7 +237,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"
@@ -443,6 +443,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
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 36196ea4eca..fecd006886e 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)
@@ -768,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))
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 1450278e736..42ff08ff00f 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -80,6 +80,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
@@ -192,10 +193,10 @@ About the new airlock wires panel:
return wires.IsIndexCut(wireIndex)
/obj/machinery/door/airlock/proc/canAIControl()
- return ((aiControlDisabled!=1) && (!isAllPowerLoss()));
+ return ((aiControlDisabled!=1) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/canAIHack()
- return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss()));
+ return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
if(stat & (NOPOWER|BROKEN))
@@ -502,6 +503,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)
@@ -882,6 +913,8 @@ About the new airlock wires panel:
user.visible_message("[user] pins [C] to [src].", "You pin [C] to [src].")
note = C
update_icon()
+ else if(istype(C, /obj/item/airlock_painter))
+ change_paintjob(C, user)
else
return ..()
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index fccb77c5e2a..dd9581a5af3 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"
@@ -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,6 +428,7 @@
normal_integrity = 500
security_level = 1
damage_deflection = 30
+ paintable = FALSE
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
@@ -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
@@ -475,6 +490,7 @@
aiControlDisabled = 1
normal_integrity = 700
security_level = 1
+ paintable = FALSE
//////////////////////////////////
/*
@@ -489,6 +505,7 @@
damage_deflection = 10
hackProof = TRUE
aiControlDisabled = TRUE
+ 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 9970974e4ff..36eba1a6ee5 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -86,8 +86,8 @@
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)
+ if(prisoner_trank != "unknown" && prisoner_trank != "Civilian")
+ SSjobs.notify_dept_head(prisoner_trank, announcetext)
if(R)
prisoner = R
@@ -104,31 +104,6 @@
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/Initialize()
..()
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/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/recycler.dm b/code/game/machinery/recycler.dm
index e4e37ae1bcb..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
@@ -92,7 +92,6 @@
if(AM)
Bumped(AM)
-
/obj/machinery/recycler/Bumped(atom/movable/AM)
if(stat & (BROKEN|NOPOWER))
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 81379df97c4..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
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index d0cf38a272d..5952108b6a9 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -20,6 +20,12 @@
account = null
ui_interact(user)
+/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)
+
/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)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index da4df4f8382..cd6b33d0a95 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -740,10 +740,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 +752,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"
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index 2d15472f521..181d5e494da 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)
diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm
index 7e468a548bf..c3531c7f674 100644
--- a/code/game/machinery/tcomms/_base.dm
+++ b/code/game/machinery/tcomms/_base.dm
@@ -52,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
@@ -60,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 ..()
/**
@@ -91,23 +106,37 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
/**
- * Start of Ion Anomalie Event
+ * Start of Ion Anomaly Event
*
- * Proc to easily start an Ion Anomalie's effects, and update the icon
+ * Proc to easily start an Ion Anomaly's effects, and update the icon
*/
/obj/machinery/tcomms/proc/start_ion()
ion = TRUE
update_icon()
/**
- * End of Ion Anomalie Event
+ * End of Ion Anomaly Event
*
- * Proc to easily stop an Ion Anomalie's effects, and update the icon
+ * Proc to easily stop an Ion Anomaly's effects, and update the icon
*/
/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
*
@@ -306,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 ---
@@ -462,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 f1599edd8b8..622ce2e2b9f 100644
--- a/code/game/machinery/tcomms/core.dm
+++ b/code/game/machinery/tcomms/core.dm
@@ -14,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
@@ -34,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.
@@ -57,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
@@ -113,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 //
//////////////
@@ -194,8 +237,11 @@
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
diff --git a/code/game/machinery/tcomms/relay.dm b/code/game/machinery/tcomms/relay.dm
index 9309a6109da..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.
*
@@ -76,6 +118,16 @@
linked_core = null
linked = FALSE
+/**
+ * Power Change Handler
+ *
+ * Proc which ensures the host core has its zlevels updated (icons are updated by parent call)
+ */
+/obj/machinery/tcomms/relay/power_change()
+ ..()
+ if(linked_core)
+ linked_core.refresh_zlevels()
+
//////////////
// UI STUFF //
//////////////
@@ -116,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..59bae3174c4 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -79,7 +79,7 @@
/obj/machinery/computer/teleporter/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
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
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/vending.dm b/code/game/machinery/vending.dm
index 7aa5661febe..afed001f63e 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -403,7 +403,7 @@
cashmoney.use(currently_vending.price)
// Vending machines have no idea who paid with cash
- credit_purchase("(cash)")
+ GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", name, "(cash)")
return 1
/**
@@ -443,26 +443,12 @@
return 0
else
// Okay to move the money at this point
- var/paid = customer_account.charge(currently_vending.price, GLOB.vendor_account,
+ 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)
- 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
+ return TRUE
-/**
- * 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)
@@ -1480,6 +1466,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,
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/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 9acdcba48de..e05fef18e5a 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -385,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
@@ -411,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/mecha.dm b/code/game/mecha/mecha.dm
index 90d466baf3e..9a24edb4da3 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"
@@ -37,6 +39,7 @@
var/lights_power = 6
var/emagged = FALSE
var/frozen = FALSE
+ var/repairing = FALSE
//inner atmos
var/use_internal_tank = 0
@@ -328,6 +331,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)
@@ -499,7 +504,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]")
@@ -537,12 +542,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)
@@ -560,8 +566,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
@@ -572,7 +578,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].")
@@ -583,10 +589,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)
@@ -773,6 +783,8 @@
to_chat(user, "You stop installing [M].")
else
+ if(W.force)
+ add_attack_logs(user, OCCUPANT_LOGGING, "attacked mech '[src]' using [W]")
return ..()
@@ -839,7 +851,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)
@@ -849,13 +865,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)
@@ -1258,6 +1275,9 @@
L.client.RemoveViewMod("mecha")
zoom_mode = FALSE
+/obj/mecha/force_eject_occupant()
+ go_out()
+
/////////////////////////
////// Access stuff /////
/////////////////////////
@@ -1528,3 +1548,5 @@
if(L.incapacitated())
return FALSE
return TRUE
+
+#undef OCCUPANT_LOGGING
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 9907e903970..a55c2a731eb 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/spiders.dm b/code/game/objects/effects/spiders.dm
index 7af5d7a998f..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()
@@ -180,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)
@@ -190,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/items.dm b/code/game/objects/items.dm
index 76361776b0a..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
@@ -113,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
@@ -506,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)
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/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 7a6b41966a8..e96fceb8b6c 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -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/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 009ec01ef43..f18c862f878 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -226,7 +226,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
channels[chan_name] |= FREQ_LISTENING
. = 1
else if(href_list["spec_freq"])
- var freq = href_list["spec_freq"]
+ var/freq = href_list["spec_freq"]
if(has_channel_access(usr, freq))
set_frequency(text2num(freq))
. = 1
@@ -272,7 +272,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
@@ -325,7 +325,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.
@@ -411,11 +411,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 +428,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)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index f20a2cc5875..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))
diff --git a/code/game/objects/items/devices/sensor_device.dm b/code/game/objects/items/devices/sensor_device.dm
index b83128cf4a5..9133bf37b9c 100644
--- a/code/game/objects/items/devices/sensor_device.dm
+++ b/code/game/objects/items/devices/sensor_device.dm
@@ -19,5 +19,5 @@
/obj/item/sensor_device/attack_self(mob/user as mob)
tgui_interact(user)
-/obj/item/sensor_device/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)
+/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/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 8b82341a06c..2d2b110fa06 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -48,7 +48,7 @@
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
@@ -196,7 +196,7 @@
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
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 6bdfdd49234..9f6844c8b9e 100644
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ b/code/game/objects/items/stacks/sheets/leather.dm
@@ -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/toys.dm b/code/game/objects/items/toys.dm
index 66f35a169f9..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"
@@ -1591,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/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/cigs.dm b/code/game/objects/items/weapons/cigs.dm
index 98a7b769642..30ad91beffa 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',
@@ -194,8 +195,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 +311,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 +384,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/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/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/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/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/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index 4a449069d4c..6d5a192c641 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -421,13 +421,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
@@ -437,7 +437,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..e31e98789a3 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"
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..16bd6f4caa8 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -541,7 +541,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..6cf011bc2c3 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -121,19 +121,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/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/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/objs.dm b/code/game/objects/objs.dm
index d40c628ed90..7a56c53f73a 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -351,3 +351,9 @@ a {
/obj/proc/check_uplink_validity()
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/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 553cfda4d59..138c77f92eb 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -25,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()
@@ -230,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)
@@ -354,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/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
index 763717dc270..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
@@ -69,6 +69,11 @@
/obj/structure/closet/secure_closet/closed_item_click(mob/user)
togglelock(user)
+/obj/structure/closet/secure_closet/AltClick(mob/user)
+ ..()
+ if(Adjacent(user))
+ togglelock(user)
+
/obj/structure/closet/secure_closet/emag_act(mob/user)
if(!broken)
broken = TRUE
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/dresser.dm b/code/game/objects/structures/dresser.dm
index 4531613610e..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(src, 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 cd7b3525478..33298669157 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -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
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 5fea799ecb0..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)
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 1f0d4b8b473..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 ..()
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/signs.dm b/code/game/objects/structures/signs.dm
index ba5258004a0..5a8a122be21 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -292,12 +292,12 @@
/obj/structure/sign/directions/engineering
name = "\improper Engineering Department"
- desc = "A direction sign, pointing out which way the Engineering department is."
+ desc = "A direction sign, pointing out which way the Engineering Department is."
icon_state = "direction_eng"
/obj/structure/sign/directions/security
name = "\improper Security Department"
- desc = "A direction sign, pointing out which way the Security department is."
+ desc = "A direction sign, pointing out which way the Security Department is."
icon_state = "direction_sec"
/obj/structure/sign/directions/medical
@@ -307,12 +307,12 @@
/obj/structure/sign/directions/evac
name = "\improper Escape Arm"
- desc = "A direction sign, pointing out which way escape shuttle dock is."
+ desc = "A direction sign, pointing out which way Escape Shuttle Dock is."
icon_state = "direction_evac"
/obj/structure/sign/directions/cargo
name = "\improper Cargo Department"
- desc = "A direction sign, pointing out which way the Cargo department is."
+ desc = "A direction sign, pointing out which way the Cargo Department is."
icon_state = "direction_supply"
/obj/structure/sign/explosives
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 80fd69597c4..22449bb547b 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -288,6 +288,28 @@
return
setDir(turn(dir, 90))
+/obj/structure/statue/kidanstatue
+ name = "Obsidian Kidan warrior statue"
+ desc = "A beautifully carved and menacing statue of a Kidan warrior made out of obsidian. It looks very heavy."
+ icon_state = "kidan"
+ anchored = TRUE
+ oreAmount = 0
+
+/obj/structure/statue/chickenstatue
+ name = "Bronze Chickenman Statue"
+ desc = "An antique and oriental-looking statue of a Chickenman made of bronze."
+ icon_state = "chicken"
+ anchored = TRUE
+ oreAmount = 0
+
+/obj/structure/statue/russian_mulebot
+ desc = "Like a MULEbot, but more Russian and less functional.";
+ icon = 'icons/obj/aibots.dmi';
+ icon_state = "mulebot0";
+ name = "OXENbot"
+ anchored = TRUE
+ oreAmount = 10
+
////////////////////////////////
/obj/structure/snowman
@@ -320,19 +342,3 @@
..()
qdel(src)
-
-/obj/structure/kidanstatue
- name = "Obsidian Kidan warrior statue"
- desc = "A beautifully carved and menacing statue of a Kidan warrior made out of obsidian. It looks very heavy."
- icon = 'icons/obj/decorations.dmi'
- icon_state = "kidanstatue"
- anchored = 1
- density = 1
-
-/obj/structure/chickenstatue
- name = "Bronze Chickenman Statue"
- desc = "An antique and oriental-looking statue of a Chickenman made of bronze."
- icon = 'icons/obj/decorations.dmi'
- icon_state = "chickenstatue"
- anchored = 1
- density = 1
diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm
index 6bc1c4ba142..def0142a481 100644
--- a/code/game/turfs/simulated.dm
+++ b/code/game/turfs/simulated.dm
@@ -53,6 +53,7 @@
wet_overlay = image('icons/effects/water.dmi', src, "ice_floor")
else
wet_overlay = image('icons/effects/water.dmi', src, "wet_static")
+ wet_overlay.plane = FLOOR_OVERLAY_PLANE
overlays += wet_overlay
if(time == INFINITY)
return
diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm
index d8ed888bc27..26921ba9ffd 100644
--- a/code/game/turfs/simulated/floor/chasm.dm
+++ b/code/game/turfs/simulated/floor/chasm.dm
@@ -97,8 +97,8 @@
return FALSE
//Flies right over the chasm
if(isliving(AM))
- var/mob/M = AM
- if(M.flying)
+ var/mob/living/M = AM
+ if(M.flying || M.floating)
return FALSE
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
diff --git a/code/game/turfs/simulated/floor/indestructible.dm b/code/game/turfs/simulated/floor/indestructible.dm
index f231a4fd0b3..4f551fb485a 100644
--- a/code/game/turfs/simulated/floor/indestructible.dm
+++ b/code/game/turfs/simulated/floor/indestructible.dm
@@ -45,6 +45,7 @@
oxygen = 14
nitrogen = 23
temperature = 300
+ planetary_atmos = TRUE
/turf/simulated/floor/indestructible/necropolis/Initialize(mapload)
. = ..()
@@ -52,8 +53,8 @@
icon_state = "necro[rand(2,3)]"
/turf/simulated/floor/indestructible/necropolis/air
- oxygen = 0
- nitrogen = 0
+ oxygen = MOLES_O2STANDARD
+ nitrogen = MOLES_N2STANDARD
temperature = T20C
/turf/simulated/floor/indestructible/boss //you put stone tiles on this and use it as a base
@@ -64,6 +65,7 @@
oxygen = 14
nitrogen = 23
temperature = 300
+ planetary_atmos = TRUE
/turf/simulated/floor/indestructible/boss/air
oxygen = MOLES_O2STANDARD
@@ -77,6 +79,7 @@
oxygen = 14
nitrogen = 23
temperature = 300
+ planetary_atmos = TRUE
smooth = SMOOTH_TRUE
/turf/simulated/floor/indestructible/hierophant/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm
index 83835cd947f..0808fef7e95 100644
--- a/code/game/turfs/simulated/floor/misc_floor.dm
+++ b/code/game/turfs/simulated/floor/misc_floor.dm
@@ -111,6 +111,7 @@
oxygen = 14
nitrogen = 23
temperature = 300
+ planetary_atmos = TRUE
/turf/simulated/floor/lubed
name = "slippery floor"
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index ebad3554f18..8bf3e820d3d 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -439,7 +439,7 @@
var/area/A = get_area(bombturf)
var/notify_admins = 0
- if(z != 5)
+ if(!is_mining_level(z))
notify_admins = 1
if(!triggered_by_explosion)
message_admins("[key_name_admin(user)] has triggered a gibtonite deposit reaction at [A.name] (JMP).")
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index a96cbae1269..dda49fecdbd 100644
--- a/code/game/turfs/space/transit.dm
+++ b/code/game/turfs/space/transit.dm
@@ -94,8 +94,6 @@
var/max = world.maxx-TRANSITIONEDGE
var/min = 1+TRANSITIONEDGE
- var/_z = pick(levels_by_trait(REACHABLE)) //select a random space zlevel
-
//now select coordinates for a border turf
var/_x
var/_y
@@ -113,7 +111,8 @@
_x = rand(min,max)
_y = min
- var/turf/T = locate(_x, _y, _z)
+ var/list/levels_available = get_all_linked_levels_zpos()
+ var/turf/T = locate(_x, _y, pick(levels_available))
AM.forceMove(T)
AM.newtonian_move(dir)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index e174634123d..8d1f3920c0c 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -25,7 +25,7 @@
var/blocks_air = 0
- var/PathNode/PNode = null //associated PathNode in the A* algorithm
+ var/datum/pathnode/PNode = null //associated PathNode in the A* algorithm
flags = 0
@@ -67,19 +67,6 @@
return INITIALIZE_HINT_NORMAL
-/hook/startup/proc/smooth_world()
- var/watch = start_watch()
- log_startup_progress("Smoothing atoms...")
- for(var/turf/T in world)
- if(T.smooth)
- queue_smooth(T)
- for(var/A in T)
- var/atom/AA = A
- if(AA.smooth)
- queue_smooth(AA)
- log_startup_progress(" Smoothed atoms in [stop_watch(watch)]s.")
- return TRUE
-
/turf/Destroy(force)
. = QDEL_HINT_IWILLGC
if(!changing_turf)
@@ -178,7 +165,6 @@
var/mob/O = M
if(!O.lastarea)
O.lastarea = get_area(O.loc)
-// O.update_gravity(O.mob_has_gravity(src))
var/loopsanity = 100
for(var/atom/A in range(1))
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 824159487eb..04ee5901054 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -1,6 +1,6 @@
-GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
+GLOBAL_VAR_INIT(normal_ooc_colour, "#275FC5")
GLOBAL_VAR_INIT(member_ooc_colour, "#035417")
-GLOBAL_VAR_INIT(mentor_ooc_colour, "#0099cc")
+GLOBAL_VAR_INIT(mentor_ooc_colour, "#00B0EB")
GLOBAL_VAR_INIT(moderator_ooc_colour, "#184880")
GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
@@ -53,6 +53,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
return
log_ooc(msg, src)
+ mob.create_log(OOC_LOG, msg)
var/display_colour = GLOB.normal_ooc_colour
if(holder && !holder.fakekey)
@@ -205,7 +206,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
return
log_looc(msg, src)
-
+ mob.create_log(LOOC_LOG, msg)
var/mob/source = mob.get_looc_source()
var/list/heard = get_mobs_in_view(7, source)
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index 2683e236269..95b11353b86 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -27,7 +27,7 @@
entry += " - Observing"
else
entry += " - DEAD"
- else if(istype(C.mob, /mob/new_player))
+ else if(isnewplayer(C.mob))
entry += " - New Player"
else
entry += " - DEAD"
@@ -90,7 +90,7 @@
if(isobserver(C.mob))
msg += " - Observing"
- else if(istype(C.mob,/mob/new_player))
+ else if(isnewplayer(C.mob))
msg += " - Lobby"
else
msg += " - Playing"
@@ -106,7 +106,7 @@
if(isobserver(C.mob))
modmsg += " - Observing"
- else if(istype(C.mob,/mob/new_player))
+ else if(isnewplayer(C.mob))
modmsg += " - Lobby"
else
modmsg += " - Playing"
diff --git a/code/game/world.dm b/code/game/world.dm
index daeab8fbe4c..c279fa24b54 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -9,10 +9,14 @@ GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG)
enable_debugger() // Enable the extools debugger
log_world("World loaded at [time_stamp()]")
log_world("[GLOB.vars.len - GLOB.gvars_datum_in_built_vars.len] global variables")
+ connectDB() // This NEEDS TO HAPPEN EARLY. I CANNOT STRESS THIS ENOUGH!!!!!!! -aa
+ load_admins() // Same here
+
#ifdef UNIT_TESTS
log_world("Unit Tests Are Enabled!")
#endif
+
if(byond_version < MIN_COMPILER_VERSION || byond_build < MIN_COMPILER_BUILD)
log_world("Your server's byond version does not meet the recommended requirements for this code. Please update BYOND")
@@ -22,8 +26,7 @@ GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG)
GLOB.timezoneOffset = text2num(time2text(0, "hh")) * 36000
- makeDatumRefLists()
- callHook("startup")
+ startup_procs() // Call procs that need to occur on startup (Generate lists, load MOTD, etc)
src.update_status()
@@ -31,9 +34,6 @@ GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG)
. = ..()
- // Create robolimbs for chargen.
- populate_robolimb_list()
-
Master.Initialize(10, FALSE)
#ifdef UNIT_TESTS
@@ -42,6 +42,16 @@ GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG)
return
+// This is basically a replacement for hook/startup. Please dont shove random bullshit here
+// If it doesnt need to happen IMMEDIATELY on world load, make a subsystem for it
+/world/proc/startup_procs()
+ LoadBans() // Load up who is banned and who isnt. DONT PUT THIS IN A SUBSYSTEM IT WILL TAKE TOO LONG TO BE CALLED
+ jobban_loadbanfile() // Load up jobbans. Again, DO NOT PUT THIS IN A SUBSYSTEM IT WILL TAKE TOO LONG TO BE CALLED
+ load_motd() // Loads up the MOTD (Welcome message players see when joining the server)
+ load_mode() // Loads up the gamemode
+ investigate_reset() // This is part of the admin investigate system. PLEASE DONT SS THIS EITHER
+ makeDatumRefLists() // Setups up lists of datums and their subtypes
+
//world/Topic(href, href_list[])
// to_chat(world, "Received a Topic() call!")
// to_chat(world, "[href]")
@@ -232,7 +242,7 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
if(!C)
return "No client with that name on server"
- del(C)
+ qdel(C)
return "Kick Successful"
@@ -256,6 +266,13 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
update_status()
return "Set listed status to invisible."
+
+ else if("hostannounce" in input)
+ if(!key_valid)
+ return keySpamProtect(addr)
+
+ to_chat(world, " Server Announcement: [input["message"]] ")
+
/proc/keySpamProtect(var/addr)
if(GLOB.world_topic_spam_protect_ip == addr && abs(GLOB.world_topic_spam_protect_time - world.time) < 50)
spawn(50)
@@ -323,6 +340,8 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
#endif
for(var/client/C in GLOB.clients)
+ var/secs_before_auto_reconnect = 10 // TODO: make it higher if server is due for an update @AffectedArc07
+ C << output(list2params(list(secs_before_auto_reconnect)), "browseroutput:reboot")
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")
@@ -335,11 +354,6 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
else
..(0)
-
-/hook/startup/proc/loadMode()
- world.load_mode()
- return 1
-
/world/proc/load_mode()
var/list/Lines = file2list("data/mode.txt")
if(Lines.len)
@@ -352,10 +366,6 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
fdel(F)
F << the_mode
-/hook/startup/proc/loadMOTD()
- world.load_motd()
- return 1
-
/world/proc/load_motd()
GLOB.join_motd = file2text("config/motd.txt")
GLOB.join_tos = file2text("config/tos.txt")
@@ -444,7 +454,7 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0)
fdel(GLOB.config_error_log)
-/hook/startup/proc/connectDB()
+/world/proc/connectDB()
if(!setup_database_connection())
log_world("Your server failed to establish a connection with the feedback database.")
else
diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm
index 21b59586bfd..1f55fa11f70 100644
--- a/code/modules/admin/DB ban/functions.dm
+++ b/code/modules/admin/DB ban/functions.dm
@@ -146,7 +146,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
if(kickbannedckey)
if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey)
- del(banned_mob.client)
+ qdel(banned_mob.client)
if(isjobban)
jobban_client_fullban(ckey, job)
@@ -211,7 +211,7 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "")
query.Execute()
while(query.NextRow())
ban_id = query.item[1]
- ban_number++;
+ ban_number++
if(ban_number == 0)
to_chat(usr, "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.")
@@ -299,7 +299,8 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
datum/admins/proc/DB_ban_unban_by_id(var/id)
- if(!check_rights(R_BAN)) return
+ if(!check_rights(R_BAN))
+ return
var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]"
@@ -314,7 +315,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
query.Execute()
while(query.NextRow())
pckey = query.item[1]
- ban_number++;
+ ban_number++
if(ban_number == 0)
to_chat(usr, "Database update failed due to a ban id not being present in the database.")
@@ -343,9 +344,9 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
/client/proc/DB_ban_panel()
set category = "Admin"
set name = "Banning Panel"
- set desc = "Edit admin permissions"
+ set desc = "DB Ban Panel"
- if(!holder)
+ if(!check_rights(R_BAN))
return
holder.DB_ban_panel()
@@ -356,7 +357,8 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
if(!usr.client)
return
- if(!check_rights(R_BAN)) return
+ if(!check_rights(R_BAN))
+ return
establish_db_connection()
if(!GLOB.dbcon.IsConnected())
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index c7f2baac4ef..d9ceba62830 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -36,17 +36,6 @@ world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE)
// message_admins("Failed Login: [key] - Guests not allowed")
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a BYOND account.")
- //check if the IP address is a known Tor node
- if(config.ToRban && ToRban_isbanned(address))
- log_adminwarn("Failed Login: [key] [computer_id] [address] - Banned: Tor")
- message_admins("Failed Login: [key] - Banned: Tor")
- //ban their computer_id and ckey for posterity
- AddBan(ckey(key), computer_id, "Use of Tor", "Automated Ban", 0, 0)
- var/mistakemessage = ""
- if(config.banappeals)
- mistakemessage = "\nIf you believe this is a mistake, please request help at [config.banappeals]."
- return list("reason"="using Tor", "desc"="\nReason: The network you are using to connect has been banned.[mistakemessage]")
-
//check if the IP address is a known proxy/vpn, and the user is not whitelisted
if(check_ipintel && config.ipintel_email && config.ipintel_whitelist && ipintel_is_banned(key, address))
log_adminwarn("Failed Login: [key] [computer_id] [address] - Proxy/VPN")
diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm
index 7b9a9330704..293504919d4 100644
--- a/code/modules/admin/NewBan.dm
+++ b/code/modules/admin/NewBan.dm
@@ -58,9 +58,6 @@ GLOBAL_PROTECT(banlist_savefile) // Obvious reasons
GLOB.CMinutes = (world.realtime / 10) / 60
return 1
-/hook/startup/proc/loadBans()
- return LoadBans()
-
/proc/LoadBans()
GLOB.banlist_savefile = new("data/banlist.bdb")
@@ -106,7 +103,8 @@ GLOBAL_PROTECT(banlist_savefile) // Obvious reasons
GLOB.banlist_savefile.cd = "/base"
if( GLOB.banlist_savefile.dir.Find("[ckey][computerid]") )
- to_chat(usr, "Ban already exists.")
+ if(usr)
+ to_chat(usr, "Ban already exists.")
return 0
else
GLOB.banlist_savefile.dir.Add("[ckey][computerid]")
diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm
deleted file mode 100644
index b486f168503..00000000000
--- a/code/modules/admin/ToRban.dm
+++ /dev/null
@@ -1,89 +0,0 @@
-//By Carnwennan
-//fetches an external list and processes it into a list of ip addresses.
-//It then stores the processed list into a savefile for later use
-#define TORFILE "data/ToR_ban.bdb"
-#define TOR_UPDATE_INTERVAL 216000 //~6 hours
-
-/proc/ToRban_isbanned(var/ip_address)
- var/savefile/F = new(TORFILE)
- if(F)
- if( ip_address in F.dir )
- return 1
- return 0
-
-/proc/ToRban_autoupdate()
- var/savefile/F = new(TORFILE)
- if(F)
- var/last_update
- F["last_update"] >> last_update
- if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while
- ToRban_update()
- return
-
-/proc/ToRban_update()
- spawn(0)
- log_world("Downloading updated ToR data...")
- var/http[] = world.Export("http://exitlist.torproject.org/exit-addresses")
-
- var/list/rawlist = file2list(http["CONTENT"])
- if(rawlist.len)
- fdel(TORFILE)
- var/savefile/F = new(TORFILE)
- for( var/line in rawlist )
- if(!line) continue
- if( copytext(line,1,12) == "ExitAddress" )
- var/cleaned = copytext(line,13,length(line)-19)
- if(!cleaned) continue
- F[cleaned] << 1
- to_chat(F["last_update"], world.realtime)
- log_world("ToR data updated!")
- if(usr)
- to_chat(usr, "ToRban updated.")
- return 1
- log_world("ToR data update aborted: no data.")
- return 0
-
-/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find"))
- set name = "ToRban"
- set category = "Server"
- if(!holder) return
- switch(task)
- if("update")
- ToRban_update()
- if("toggle")
- if(config)
- if(config.ToRban)
- config.ToRban = 0
- message_admins("ToR banning disabled.")
- else
- config.ToRban = 1
- message_admins("ToR banning enabled.")
- if("show")
- var/savefile/F = new(TORFILE)
- var/dat
- if( length(F.dir) )
- for( var/i=1, i<=length(F.dir), i++ )
- dat += "| #[i] | [F.dir[i]] | "
- dat = ""
- else
- dat = "No addresses in list."
- src << browse(dat,"window=ToRban_show")
- if("remove")
- var/savefile/F = new(TORFILE)
- var/choice = input(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban",null) as null|anything in F.dir
- if(choice)
- F.dir.Remove(choice)
- to_chat(src, "Address removed")
- if("remove all")
- to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.")
- if("find")
- var/input = input(src,"Please input an IP address to search for:","Find ToR ban",null) as null|text
- if(input)
- if(ToRban_isbanned(input))
- to_chat(src, "Address is a known ToR address")
- else
- to_chat(src, "Address is not a known ToR address")
- return
-
-#undef TORFILE
-#undef TOR_UPDATE_INTERVAL
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index df156c5b0df..07dd4975ddb 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -69,15 +69,19 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += "Options panel for [M]"
if(M.client)
body += " played by [M.client] "
- body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
+ if(check_rights(R_PERMISSIONS, 0))
+ body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
+ else
+ body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]"
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
body += " Hasn't Entered Game "
else
body += " \[Heal\] "
body += "
\[ "
+ body += "LOGS - "
body += "VV - "
body += "[ADMIN_TP(M,"TP")] - "
if(M.client)
@@ -109,17 +113,18 @@ GLOBAL_VAR_INIT(nologevent, 0)
else
body += "Add to Watchlist "
- if(M.client)
body += "| Prison | "
body += "\ Send back to Lobby | "
+ body += "\ Erase Flavor Text | "
+ body += "\ Use Random Name | "
var/muted = M.client.prefs.muted
body += {" Mute:
- \[IC |
- OOC |
- PRAY |
- ADMINHELP |
- DEADCHAT\]
- (toggle all)
+ \[IC |
+ OOC |
+ PRAY |
+ ADMINHELP |
+ DEADCHAT\]
+ (toggle all)
"}
var/jumptoeye = ""
@@ -147,7 +152,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += {" | Cryo "}
if(M.client)
- if(!istype(M, /mob/new_player))
+ if(!isnewplayer(M))
body += "
"
body += "Transformation:"
body += " "
@@ -280,7 +285,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
/datum/admins/proc/vpn_whitelist()
set category = "Admin"
set name = "VPN Ckey Whitelist"
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_BAN))
return
var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32)
if(key)
@@ -699,7 +704,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
var/msg = ""
if(SSticker.current_state == GAME_STATE_STARTUP)
msg = " (The server is still setting up, but the round will be started as soon as possible.)"
- message_admins("[usr.key] has started the game.[msg]")
+ message_admins("[usr.key] has started the game.[msg]")
feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return 1
else
@@ -760,19 +765,6 @@ GLOBAL_VAR_INIT(nologevent, 0)
world.update_status()
feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/admins/proc/toggle_aliens()
- set category = "Event"
- set desc="Toggle alien mobs"
- set name="Toggle Aliens"
-
- if(!check_rights(R_EVENT))
- return
-
- GLOB.aliens_allowed = !GLOB.aliens_allowed
- log_admin("[key_name(usr)] toggled aliens to [GLOB.aliens_allowed].")
- message_admins("[key_name_admin(usr)] toggled aliens [GLOB.aliens_allowed ? "on" : "off"].")
- feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/datum/admins/proc/delay()
set category = "Server"
set desc="Delay the game start/end"
@@ -1004,13 +996,13 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
/proc/kick_clients_in_lobby(message, kick_only_afk = 0)
var/list/kicked_client_names = list()
for(var/client/C in GLOB.clients)
- if(istype(C.mob, /mob/new_player))
+ if(isnewplayer(C.mob))
if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk
continue
if(message)
to_chat(C, message)
kicked_client_names.Add("[C.ckey]")
- del(C)
+ qdel(C)
return kicked_client_names
//returns 1 to let the dragdrop code know we are trapping this event
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index 13b786c5174..527540e9490 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -11,10 +11,6 @@
/proc/investigate_subject2file(var/subject)
return file("[INVESTIGATE_DIR][subject].html")
-/hook/startup/proc/resetInvestigate()
- investigate_reset()
- return 1
-
/proc/investigate_reset()
if(fdel(INVESTIGATE_DIR)) return 1
return 0
@@ -37,7 +33,8 @@
/client/proc/investigate_show( subject in GLOB.investigate_log_subjects )
set name = "Investigate"
set category = "Admin"
- if(!holder) return
+ if(!check_rights(R_ADMIN))
+ return
switch(subject)
if("notes")
show_note()
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index 5956819d3d4..342ba454526 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -56,11 +56,12 @@ GLOBAL_PROTECT(admin_ranks) // this shit is being protected for obvious reasons
testing(msg)
#endif
-/hook/startup/proc/loadAdmins()
- load_admins()
- return 1
-
/proc/load_admins()
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Admin reload blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to reload admins via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to reload admins via advanced proc-call")
+ return
//clear the datums references
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 4e3d2d65f30..b3957a86b44 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -10,11 +10,8 @@ GLOBAL_LIST_INIT(admin_verbs_default, list(
GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/check_antagonists, /*shows all antags*/
/datum/admins/proc/show_player_panel,
- /client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/
/client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
- /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
- /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/datum/admins/proc/announce, /*priority announce something to all clients.*/
/client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/
/client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/
@@ -23,7 +20,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/
- /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
/client/proc/cmd_admin_open_logging_view,
@@ -37,6 +33,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcomm*/
+ /client/proc/admin_deny_shuttle, /*toggles availability of shuttle calling*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
/client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */
/client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/
@@ -54,36 +51,30 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/datum/admins/proc/PlayerNotes,
/client/proc/cmd_mentor_say,
/datum/admins/proc/show_player_notes,
- /datum/admins/proc/vpn_whitelist,
/client/proc/free_slot, /*frees slot for chosen job*/
/client/proc/toggleattacklogs,
/client/proc/toggleadminlogs,
/client/proc/toggledebuglogs,
/client/proc/update_mob_sprite,
- /client/proc/toggledrones,
/client/proc/man_up,
/client/proc/global_man_up,
/client/proc/delbook,
/client/proc/view_flagged_books,
+ /client/proc/view_asays,
/client/proc/empty_ai_core_toggle_latejoin,
/client/proc/aooc,
/client/proc/freeze,
- /client/proc/alt_check,
/client/proc/secrets,
- /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */
- /client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */
/client/proc/debug_variables,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
/client/proc/toggle_mentor_chat,
/client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/
- /client/proc/list_ssds_afks,
- /client/proc/cmd_admin_headset_message,
- /client/proc/spawn_floor_cluwne
+ /client/proc/list_ssds_afks
))
GLOBAL_LIST_INIT(admin_verbs_ban, list(
- /client/proc/unban_panel,
- /client/proc/jobbans,
- /client/proc/stickybanpanel
+ /client/proc/ban_panel,
+ /client/proc/stickybanpanel,
+ /datum/admins/proc/vpn_whitelist
))
GLOBAL_LIST_INIT(admin_verbs_sounds, list(
/client/proc/play_local_sound,
@@ -99,7 +90,6 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/drop_bomb,
/client/proc/cinematic,
/client/proc/one_click_antag,
- /datum/admins/proc/toggle_aliens,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_add_random_ai_law,
/client/proc/make_sound,
@@ -109,6 +99,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/show_tip,
/client/proc/cmd_admin_change_custom_event,
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
+ /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
/client/proc/response_team, // Response Teams admin verb
@@ -116,7 +107,10 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/fax_panel,
/client/proc/event_manager_panel,
/client/proc/modify_goals,
- /client/proc/outfit_manager
+ /client/proc/outfit_manager,
+ /client/proc/cmd_admin_headset_message,
+ /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */
+ /client/proc/change_human_appearance_self /* Allows the human-based mob itself to change its basic appearance */
))
GLOBAL_LIST_INIT(admin_verbs_spawn, list(
@@ -125,25 +119,27 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list(
/client/proc/admin_deserialize
))
GLOBAL_LIST_INIT(admin_verbs_server, list(
- /client/proc/ToRban,
+ /client/proc/reload_admins,
/client/proc/Set_Holiday,
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
+ /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
+ /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/client/proc/toggle_log_hrefs,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
- /client/proc/cmd_debug_del_all,
/client/proc/cmd_debug_del_sing,
- /datum/admins/proc/toggle_aliens,
/client/proc/delbook,
/client/proc/view_flagged_books,
+ /client/proc/view_asays,
/client/proc/toggle_antagHUD_use,
/client/proc/toggle_antagHUD_restrictions,
/client/proc/set_ooc,
- /client/proc/reset_ooc
+ /client/proc/reset_ooc,
+ /client/proc/toggledrones
))
GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/cmd_admin_list_open_jobs,
@@ -152,9 +148,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/debug_controller,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_admin_delete,
- /client/proc/cmd_debug_del_all,
/client/proc/cmd_debug_del_sing,
- /client/proc/reload_admins,
/client/proc/restart_controller,
/client/proc/enable_debug_verbs,
/client/proc/toggledebuglogs,
@@ -197,7 +191,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list(
/client/proc/player_panel_new,
/client/proc/dsay,
/datum/admins/proc/show_player_panel,
- /client/proc/jobbans,
+ /client/proc/ban_panel,
/client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
))
GLOBAL_LIST_INIT(admin_verbs_mentor, list(
@@ -337,7 +331,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
ghost.reenter_corpse()
log_admin("[key_name(usr)] re-entered their body")
feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- else if(istype(mob,/mob/new_player))
+ else if(isnewplayer(mob))
to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.")
else
//ghostize
@@ -367,19 +361,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
to_chat(mob, "Invisimin on. You are now as invisible as a ghost.")
mob.remove_from_all_data_huds()
-/client/proc/player_panel()
- set name = "Player Panel"
- set category = "Admin"
-
- if(!check_rights(R_ADMIN))
- return
-
- holder.player_panel_old()
- feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return
-
/client/proc/player_panel_new()
- set name = "Player Panel New"
+ set name = "Player Panel"
set category = "Admin"
if(!check_rights(R_ADMIN|R_MOD))
@@ -401,22 +384,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
-/client/proc/jobbans()
- set name = "Display Job bans"
- set category = "Admin"
-
- if(!check_rights(R_ADMIN|R_MOD))
- return
-
- if(config.ban_legacy_system)
- holder.Jobbans()
- else
- holder.DB_ban_panel()
- feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return
-
-/client/proc/unban_panel()
- set name = "Unban Panel"
+/client/proc/ban_panel()
+ set name = "Ban Panel"
set category = "Admin"
if(!check_rights(R_BAN))
@@ -542,7 +511,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban")
log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban")
to_chat(C, "You have been autobanned due to a warning by [ckey]. This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.")
- del(C)
+ qdel(C)
else
message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban")
@@ -809,7 +778,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
set desc = "Allows you to change the mob appearance"
set category = null
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -826,7 +795,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
return
if(holder)
- admin_log_and_message_admins("is altering the appearance of [H].")
+ log_and_message_admins("is altering the appearance of [H].")
H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0)
feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -835,7 +804,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
set desc = "Allows the mob to change its appearance"
set category = null
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -857,10 +826,10 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel"))
if("Yes")
- admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.")
+ log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.")
H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0)
if("No")
- admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.")
+ log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.")
H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1)
feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -980,8 +949,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
else
to_chat(usr, "You now won't get debug log messages")
-/client/proc/man_up(mob/T as mob in GLOB.mob_list)
- set category = "Admin"
+/client/proc/man_up(mob/T as mob in GLOB.player_list)
+ set category = null
set name = "Man Up"
set desc = "Tells mob to man up and deal with it."
diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm
index 9efc4c9ce88..699eca075bb 100644
--- a/code/modules/admin/banjob.dm
+++ b/code/modules/admin/banjob.dm
@@ -42,24 +42,6 @@ GLOBAL_DATUM_INIT(jobban_regex, /regex, regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ##
else
return 0
-/*
-DEBUG
-/mob/verb/list_all_jobbans()
- set name = "list all jobbans"
-
- for(var/s in jobban_keylist)
- to_chat(world, s)
-
-/mob/verb/reload_jobbans()
- set name = "reload jobbans"
-
- jobban_loadbanfile()
-*/
-
-/hook/startup/proc/loadJobBans()
- jobban_loadbanfile()
- return 1
-
/proc/jobban_loadbanfile()
if(config.ban_legacy_system)
var/savefile/S=new("data/job_full.ban")
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 740d182f3f2..470293bfe76 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -16,6 +16,11 @@ GLOBAL_PROTECT(admin_datums) // This is protected because we dont want people ma
var/admincaster_signature //What you'll sign the newsfeeds as
/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Admin rank creation blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to create a new admin rank via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback a new admin rank via advanced proc-call")
+ return
if(!ckey)
error("Admin datum created without a ckey argument. Datum has been deleted")
qdel(src)
@@ -26,10 +31,20 @@ GLOBAL_PROTECT(admin_datums) // This is protected because we dont want people ma
GLOB.admin_datums[ckey] = src
/datum/admins/Destroy()
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Admin rank deletion blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to delete an admin rank via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to delete an admin rank via advanced proc-call")
+ return
..()
return QDEL_HINT_HARDDEL_NOW
/datum/admins/proc/associate(client/C)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Rank association blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to associate an admin rank to a new client via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to associate an admin rank to a new client via advanced proc-call")
+ return
if(istype(C))
owner = C
owner.holder = src
@@ -39,6 +54,11 @@ GLOBAL_PROTECT(admin_datums) // This is protected because we dont want people ma
GLOB.admins |= C
/datum/admins/proc/disassociate()
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Rank disassociation blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to disassociate an admin rank from a client via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to disassociate an admin rank from a client via advanced proc-call")
+ return
if(owner)
GLOB.admins -= owner
owner.remove_admin_verbs()
@@ -88,6 +108,11 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself.
return 0
/client/proc/deadmin()
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Deadmin blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to de-admin a client via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to de-admin a client via advanced proc-call")
+ return
GLOB.admin_datums -= ckey
if(holder)
holder.disassociate()
diff --git a/code/modules/admin/machine_upgrade.dm b/code/modules/admin/machine_upgrade.dm
index 1e569b51d2d..4f38f70613d 100644
--- a/code/modules/admin/machine_upgrade.dm
+++ b/code/modules/admin/machine_upgrade.dm
@@ -1,20 +1,20 @@
/proc/machine_upgrade(obj/machinery/M in world)
set name = "Tweak Component Ratings"
- set category = "Debug"
+ set category = null
- if(!check_rights(R_DEBUG))
+ if(!check_rights(R_DEBUG))
return
-
+
if(!istype(M))
to_chat(usr, "This can only be used on subtypes of /obj/machinery.")
return
-
+
var/new_rating = input("Enter new rating:","Num") as num
if(!isnull(new_rating) && M.component_parts)
for(var/obj/item/stock_parts/P in M.component_parts)
P.rating = new_rating
M.RefreshParts()
-
+
message_admins("[key_name_admin(usr)] has set the component rating of [M] to [new_rating]")
log_admin("[key_name(usr)] has set the component rating of [M] to [new_rating]")
diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm
index 795538c7c33..952630c798a 100644
--- a/code/modules/admin/permissionverbs/permissionedit.dm
+++ b/code/modules/admin/permissionverbs/permissionedit.dm
@@ -102,6 +102,11 @@
to_chat(usr, "Admin rank changed.")
/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Admin edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit admin ranks via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit admin ranks via advanced proc-call")
+ return
if(config.admin_legacy_system)
return
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index efb5ac32f97..c2248261baa 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -268,7 +268,7 @@
else
M_job = "Living"
- else if(istype(M,/mob/new_player))
+ else if(isnewplayer(M))
M_job = "New player"
else if(isobserver(M))
@@ -332,65 +332,6 @@
usr << browse(dat, "window=players;size=600x480")
-//The old one
-/datum/admins/proc/player_panel_old()
- if(!usr.client.holder)
- return
- var/dat = "Player Menu"
- dat += "| Name | Real Name | Assigned Job | Key | Options | PM | Traitor? | "
- //add IP: | to this if wanting to add back in IP checking
- //add (IP: [M.lastKnownIP]) | if you want to know their ip to the lists below
- var/list/mobs = sortmobs()
-
- for(var/mob/M in mobs)
- if(!M.ckey) continue
-
- dat += "| [M.name] | "
- if(isAI(M))
- dat += "AI | "
- else if(isrobot(M))
- dat += "Cyborg | "
- else if(issmall(M))
- dat += "Monkey | "
- else if(ishuman(M))
- dat += "[M.real_name] | "
- else if(istype(M, /mob/living/silicon/pai))
- dat += "pAI | "
- else if(istype(M, /mob/new_player))
- dat += "New Player | "
- else if(isobserver(M))
- dat += "Ghost | "
- else if(isalien(M))
- dat += "Alien | "
- else
- dat += "Unknown | "
-
-
- if(istype(M,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = M
- if(H.mind && H.mind.assigned_role)
- dat += "[H.mind.assigned_role] | "
- else
- dat += "NA | "
-
-
- dat += {"[(M.client ? "[M.client]" : "No client")] |
- X |
- PM |
- "}
- switch(is_special_character(M))
- if(0)
- dat += {"Traitor? | "}
- if(1)
- dat += {"Traitor? | "}
- if(2)
- dat += {"Traitor? | "}
-
- dat += " "
-
- usr << browse(dat, "window=players;size=640x480")
-
-
/datum/admins/proc/check_antagonists_line(mob/M, caption = "", close = 1)
var/logout_status
@@ -404,7 +345,8 @@
PM [ADMIN_FLW(M, "FLW")] | [close ? "" : ""]"}
/datum/admins/proc/check_antagonists()
- if(!check_rights(R_ADMIN)) return
+ if(!check_rights(R_ADMIN))
+ return
if(SSticker && SSticker.current_state >= GAME_STATE_PLAYING)
var/dat = "Round StatusRound Status"
dat += "Current Game Mode: [SSticker.mode.name] "
@@ -570,6 +512,15 @@
if(SSticker.mode.ert.len)
dat += check_role_table("ERT", SSticker.mode.ert)
+ //list active security force count, so admins know how bad things are
+ var/list/sec_list = check_active_security_force()
+ dat += "
| Security | | "
+ dat += "| Total: | [sec_list[1]] | "
+ dat += " | Active: | [sec_list[2]] | "
+ dat += " | Dead: | [sec_list[3]] | "
+ dat += " | Antag: | [sec_list[4]] | "
+ dat += " "
+
dat += ""
usr << browse(dat, "window=roundstatus;size=400x500")
else
diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm
index a438a43eaec..04e28737479 100644
--- a/code/modules/admin/sql_notes.dm
+++ b/code/modules/admin/sql_notes.dm
@@ -2,7 +2,8 @@
if(checkrights && !check_rights(R_ADMIN|R_MOD))
return
if(!GLOB.dbcon.IsConnected())
- to_chat(usr, "Failed to establish database connection.")
+ if(usr)
+ to_chat(usr, "Failed to establish database connection.")
return
if(!target_ckey)
@@ -19,7 +20,8 @@
log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n")
return
if(!query_find_ckey.NextRow())
- to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.")
+ if(usr)
+ to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.")
return
var/exp_data = query_find_ckey.item[2]
@@ -52,8 +54,8 @@
log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n")
return
if(logged)
- log_admin("[key_name(usr)] has added a note to [target_ckey]: [notetext]")
- message_admins("[key_name_admin(usr)] has added a note to [target_ckey]: [notetext]")
+ log_admin("[usr ? key_name(usr) : adminckey] has added a note to [target_ckey]: [notetext]")
+ message_admins("[usr ? key_name_admin(usr) : adminckey] has added a note to [target_ckey]: [notetext]")
show_note(target_ckey)
/proc/remove_note(note_id)
@@ -63,7 +65,8 @@
var/notetext
var/adminckey
if(!GLOB.dbcon.IsConnected())
- to_chat(usr, "Failed to establish database connection.")
+ if(usr)
+ to_chat(usr, "Failed to establish database connection.")
return
if(!note_id)
return
@@ -82,15 +85,16 @@
var/err = query_del_note.ErrorMsg()
log_game("SQL ERROR removing note from table. Error : \[[err]\]\n")
return
- log_admin("[key_name(usr)] has removed a note made by [adminckey] from [ckey]: [notetext]")
- message_admins("[key_name_admin(usr)] has removed a note made by [adminckey] from [ckey]: [notetext]")
+ log_admin("[usr ? key_name(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]: [notetext]")
+ message_admins("[usr ? key_name_admin(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]: [notetext]")
show_note(ckey)
/proc/edit_note(note_id)
if(!check_rights(R_ADMIN|R_MOD))
return
if(!GLOB.dbcon.IsConnected())
- to_chat(usr, "Failed to establish database connection.")
+ if(usr)
+ to_chat(usr, "Failed to establish database connection.")
return
if(!note_id)
return
@@ -117,8 +121,8 @@
var/err = query_update_note.ErrorMsg()
log_game("SQL ERROR editing note. Error : \[[err]\]\n")
return
- log_admin("[key_name(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"")
- message_admins("[key_name_admin(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"")
+ log_admin("[usr ? key_name(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"")
+ message_admins("[usr ? key_name_admin(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"")
show_note(target_ckey)
/proc/show_note(target_ckey, index, linkless = 0)
diff --git a/code/modules/admin/tickets/adminticketsverbs.dm b/code/modules/admin/tickets/adminticketsverbs.dm
index 406019745f8..4e8e79f2a26 100644
--- a/code/modules/admin/tickets/adminticketsverbs.dm
+++ b/code/modules/admin/tickets/adminticketsverbs.dm
@@ -5,7 +5,7 @@
set name = "Open Admin Ticket Interface"
set category = "Admin"
- if(!holder || !check_rights(R_ADMIN))
+ if(!check_rights(R_ADMIN))
return
SStickets.showUI(usr)
@@ -14,7 +14,7 @@
set name = "Resolve All Open Admin Tickets"
set category = null
- if(!holder || !check_rights(R_ADMIN))
+ if(!check_rights(R_ADMIN))
return
if(alert("Are you sure you want to resolve ALL open admin tickets?","Resolve all open admin tickets?","Yes","No") != "Yes")
diff --git a/code/modules/admin/tickets/mentorticketsverbs.dm b/code/modules/admin/tickets/mentorticketsverbs.dm
index d65f4a30220..c7bec3e043d 100644
--- a/code/modules/admin/tickets/mentorticketsverbs.dm
+++ b/code/modules/admin/tickets/mentorticketsverbs.dm
@@ -5,7 +5,7 @@
set name = "Open Mentor Ticket Interface"
set category = "Admin"
- if(!holder || !check_rights(R_MENTOR|R_ADMIN))
+ if(!check_rights(R_MENTOR|R_ADMIN))
return
SSmentor_tickets.showUI(usr)
@@ -14,7 +14,7 @@
set name = "Resolve All Open Mentor Tickets"
set category = null
- if(!holder || !check_rights(R_ADMIN))
+ if(!check_rights(R_ADMIN))
return
if(alert("Are you sure you want to resolve ALL open mentor tickets?","Resolve all open mentor tickets?","Yes","No") != "Yes")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 1a040d46e90..2390394b280 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -14,7 +14,7 @@
if(!check_rights(R_ADMIN|R_MOD))
return
var/client/C = locateUID(href_list["rejectadminhelp"])
- if(!C)
+ if(!isclient(C))
return
C << 'sound/effects/adminhelp.ogg'
@@ -114,39 +114,39 @@
switch(bantype)
if(BANTYPE_PERMA)
if(!banckey || !banreason)
- to_chat(usr, "Not enough parameters (Requires ckey and reason)")
+ to_chat(usr, "Not enough parameters (Requires ckey and reason)")
return
banduration = null
banjob = null
if(BANTYPE_TEMP)
if(!banckey || !banreason || !banduration)
- to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)")
+ to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)")
return
banjob = null
if(BANTYPE_JOB_PERMA)
if(!banckey || !banreason || !banjob)
- to_chat(usr, "Not enough parameters (Requires ckey, reason and job)")
+ to_chat(usr, "Not enough parameters (Requires ckey, reason and job)")
return
banduration = null
if(BANTYPE_JOB_TEMP)
if(!banckey || !banreason || !banjob || !banduration)
- to_chat(usr, "Not enough parameters (Requires ckey, reason and job)")
+ to_chat(usr, "Not enough parameters (Requires ckey, reason and job)")
return
if(BANTYPE_APPEARANCE)
if(!banckey || !banreason)
- to_chat(usr, "Not enough parameters (Requires ckey and reason)")
+ to_chat(usr, "Not enough parameters (Requires ckey and reason)")
return
banduration = null
banjob = null
if(BANTYPE_ADMIN_PERMA)
if(!banckey || !banreason)
- to_chat(usr, "Not enough parameters (Requires ckey and reason)")
+ to_chat(usr, "Not enough parameters (Requires ckey and reason)")
return
banduration = null
banjob = null
if(BANTYPE_ADMIN_TEMP)
if(!banckey || !banreason || !banduration)
- to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)")
+ to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)")
return
banjob = null
@@ -324,8 +324,8 @@
if(!check_rights(R_SPAWN)) return
var/mob/M = locateUID(href_list["mob"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
var/delmob = 0
@@ -436,18 +436,18 @@
if(!check_rights(R_BAN))
return
var/mob/M = locateUID(href_list["appearanceban"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(!M.ckey) //sanity
- to_chat(usr, "This mob has no ckey")
+ to_chat(usr, "This mob has no ckey")
return
var/ban_ckey_param = href_list["dbbanaddckey"]
var/banreason = appearance_isbanned(M)
if(banreason)
/* if(!config.ban_legacy_system)
- to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel")
+ to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel")
DB_ban_panel(M.ckey)
return */
switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No"))
@@ -458,7 +458,7 @@
DB_ban_unban(M.ckey, BANTYPE_APPEARANCE)
appearance_unban(M)
message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1)
- to_chat(M, "[usr.client.ckey] has removed your appearance ban.")
+ to_chat(M, "[usr.client.ckey] has removed your appearance ban.")
else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel"))
if("Yes")
@@ -473,7 +473,7 @@
appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]")
add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0)
message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1)
- to_chat(M, "You have been appearance banned by [usr.client.ckey].")
+ to_chat(M, "You have been appearance banned by [usr.client.ckey].")
to_chat(M, "The reason is: [reason]")
to_chat(M, "Appearance ban can be lifted only upon request.")
if(config.banappeals)
@@ -487,15 +487,15 @@
// if(!check_rights(R_BAN)) return
var/mob/M = locateUID(href_list["jobban2"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(!M.ckey) //sanity
- to_chat(usr, "This mob has no ckey")
+ to_chat(usr, "This mob has no ckey")
return
if(!SSjobs)
- to_chat(usr, "SSjobs has not been setup!")
+ to_chat(usr, "SSjobs has not been setup!")
return
var/dat = ""
@@ -735,8 +735,8 @@
if(!check_rights(R_BAN)) return
var/mob/M = locateUID(href_list["jobban4"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(M != usr) //we can jobban ourselves
@@ -747,7 +747,7 @@
var/ban_ckey_param = href_list["dbbanaddckey"]
if(!SSjobs)
- to_chat(usr, "SSjobs has not been setup!")
+ to_chat(usr, "SSjobs has not been setup!")
return
//get jobs for department if specified, otherwise just returnt he one job in a list.
@@ -840,7 +840,7 @@
msg += ", [job]"
add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0)
message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1)
- to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].")
+ to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].")
to_chat(M, "The reason is: [reason]")
to_chat(M, "This jobban will be lifted in [mins] minutes.")
href_list["jobban2"] = 1 // lets it fall through and refresh
@@ -861,7 +861,7 @@
else msg += ", [job]"
add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0)
message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1)
- to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].")
+ to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].")
to_chat(M, "The reason is: [reason]")
to_chat(M, "Jobban can be lifted only upon request.")
href_list["jobban2"] = 1 // lets it fall through and refresh
@@ -873,7 +873,7 @@
//all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned)
if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban.
if(!config.ban_legacy_system)
- to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel")
+ to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel")
DB_ban_panel(M.ckey)
return
var/msg
@@ -894,22 +894,33 @@
continue
if(msg)
message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1)
- to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].")
+ to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].")
href_list["jobban2"] = 1 // lets it fall through and refresh
return 1
return 0 //we didn't do anything!
else if(href_list["boot2"])
var/mob/M = locateUID(href_list["boot2"])
- if(ismob(M))
- if(M.client && M.client.holder && (M.client.holder.rights & R_BAN))
- to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.")
+ if(!ismob(M))
+ return
+ var/client/C = M.client
+ if(C == null)
+ to_chat(usr, "Mob has no client to kick.")
+ return
+ if(alert("Kick [C.ckey]?",,"Yes","No") == "Yes")
+ if(C && C.holder && (C.holder.rights & R_BAN))
+ to_chat(usr, "[key_name_admin(C)] cannot be kicked from the server.")
return
- to_chat(M, "You have been kicked from the server")
- log_admin("[key_name(usr)] booted [key_name(M)].")
- message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1)
- //M.client = null
- del(M.client)
+ to_chat(C, "You have been kicked from the server")
+ log_admin("[key_name(usr)] booted [key_name(C)].")
+ message_admins("[key_name_admin(usr)] booted [key_name_admin(C)].", 1)
+ //C = null
+ qdel(C)
+
+ else if(href_list["open_logging_view"])
+ var/mob/M = locateUID(href_list["open_logging_view"])
+ if(ismob(M))
+ usr.client.open_logging_view(list(M), TRUE)
//Player Notes
else if(href_list["addnote"])
@@ -981,7 +992,7 @@
if(!check_rights(R_BAN)) return
var/mob/M = locateUID(href_list["newban"])
- if(!ismob(M))
+ if(!istype(M, /mob))
return
var/ban_ckey_param = href_list["dbbanaddckey"]
@@ -997,7 +1008,7 @@
M = admin_ban_mobsearch(M, ban_ckey_param, usr)
AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins)
ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.")
- to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
+ to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.")
feedback_inc("ban_tmp",1)
DB_ban_record(BANTYPE_TEMP, M, mins, reason)
@@ -1011,14 +1022,13 @@
log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
- del(M.client)
- //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends.
+ qdel(M.client)
if("No")
var/reason = input(usr,"Please state the reason","Reason") as message|null
if(!reason)
return
AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP)
- to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
+ to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].")
to_chat(M, "This ban does not expire automatically and must be appealed.")
if(M.client)
M.client.link_forum_account(TRUE)
@@ -1032,8 +1042,7 @@
feedback_inc("ban_perma",1)
DB_ban_record(BANTYPE_PERMA, M, -1, reason)
- del(M.client)
- //qdel(M)
+ qdel(M.client)
if("Cancel")
return
@@ -1087,7 +1096,7 @@
return
var/mob/M = locateUID(href_list["mute"])
- if(!ismob(M)) return
+ if(!istype(M, /mob)) return
if(!M.client) return
var/mute_type = href_list["mute_type"]
@@ -1101,7 +1110,7 @@
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- var/dat = {"What mode do you wish to play? "}
+ var/dat = {"What mode do you wish to play? "}
for(var/mode in config.modes)
dat += {"[config.mode_names[mode]] "}
dat += {"Secret "}
@@ -1116,7 +1125,7 @@
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "secret")
return alert(usr, "The game mode has to be secret!", null, null, null, null)
- var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret. "}
+ var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret. "}
for(var/mode in config.modes)
dat += {"[config.mode_names[mode]] "}
dat += {"Random (default) "}
@@ -1154,7 +1163,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes")
return
@@ -1169,7 +1178,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["corgione"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes")
@@ -1184,7 +1193,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makePAI"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes")
return
@@ -1207,8 +1216,9 @@
if(!check_rights(R_SERVER|R_EVENT)) return
var/mob/M = locateUID(href_list["forcespeech"])
- if(!ismob(M))
- to_chat(usr, "this can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins.
if(!speech) return
@@ -1224,11 +1234,11 @@
return
var/mob/M = locateUID(href_list["sendtoprison"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
var/turf/prison_cell = pick(GLOB.prisonwarp)
@@ -1287,6 +1297,71 @@
NP.ckey = M.ckey
qdel(M)
+ else if(href_list["eraseflavortext"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/mob/M = locateUID(href_list["eraseflavortext"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
+ if(!M.client)
+ to_chat(usr, "[M] doesn't seem to have an active client.")
+ return
+
+ if(M.flavor_text == "" && M.client.prefs.flavor_text == "")
+ to_chat(usr, "[M] has no flavor text set.")
+ return
+
+ if(alert(usr, "Erase [key_name(M)]'s flavor text?", "Message", "Yes", "No") != "Yes")
+ return
+
+ log_admin("[key_name(usr)] has erased [key_name(M)]'s flavor text.")
+ message_admins("[key_name_admin(usr)] has erased [key_name_admin(M)]'s flavor text.")
+
+ // Clears the mob's flavor text
+ M.flavor_text = ""
+
+ // Clear and save the DB character's flavor text
+ M.client.prefs.flavor_text = ""
+ M.client.prefs.save_character(M.client)
+
+ else if(href_list["userandomname"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/mob/M = locateUID(href_list["userandomname"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
+ if(!M.client)
+ to_chat(usr, "[M] doesn't seem to have an active client.")
+ return
+
+ if(alert(usr, "Force [key_name(M)] to use a random name?", "Message", "Yes", "No") != "Yes")
+ return
+
+ log_admin("[key_name(usr)] has forced [key_name(M)] to use a random name.")
+ message_admins("[key_name_admin(usr)] has forced [key_name_admin(M)] to use a random name.")
+
+ // Update the mob's name with a random one straight away
+ var/random_name = random_name(M.client.prefs.gender, M.client.prefs.species)
+ M.rename_character(M.real_name, random_name)
+
+ // Save that random name for next rounds
+ M.client.prefs.real_name = random_name
+ M.client.prefs.save_character(M.client)
+
+ else if(href_list["asays"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ usr.client.view_asays()
+
else if(href_list["tdome1"])
if(!check_rights(R_SERVER|R_EVENT)) return
@@ -1294,11 +1369,11 @@
return
var/mob/M = locateUID(href_list["tdome1"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
for(var/obj/item/I in M)
@@ -1324,11 +1399,11 @@
return
var/mob/M = locateUID(href_list["tdome2"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
for(var/obj/item/I in M)
@@ -1354,11 +1429,11 @@
return
var/mob/M = locateUID(href_list["tdomeadmin"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
M.Paralyse(5)
@@ -1376,11 +1451,11 @@
return
var/mob/M = locateUID(href_list["tdomeobserve"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
for(var/obj/item/I in M)
@@ -1410,11 +1485,11 @@
return
var/mob/M = locateUID(href_list["aroomwarp"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
if(istype(M, /mob/living/silicon/ai))
- to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
+ to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai")
return
M.Paralyse(5)
@@ -1431,7 +1506,7 @@
var/mob/living/L = locateUID(href_list["revive"])
if(!istype(L))
- to_chat(usr, "This can only be used on instances of type /mob/living")
+ to_chat(usr, "This can only be used on instances of type /mob/living")
return
L.revive()
@@ -1443,7 +1518,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makeai"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes")
@@ -1459,7 +1534,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makealien"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes")
return
@@ -1471,7 +1546,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makeslime"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes")
return
@@ -1483,7 +1558,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makesuper"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes")
@@ -1496,7 +1571,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["makerobot"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes")
return
@@ -1507,8 +1582,8 @@
if(!check_rights(R_SPAWN)) return
var/mob/M = locateUID(href_list["makeanimal"])
- if(istype(M, /mob/new_player))
- to_chat(usr, "This cannot be used on instances of type /mob/new_player")
+ if(isnewplayer(M))
+ to_chat(usr, "This cannot be used on instances of type /mob/new_player")
return
if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes")
return
@@ -1521,10 +1596,14 @@
var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"])
if(!istype(G))
- to_chat(usr, "This will only work on /mob/dead/observer")
+ to_chat(usr, "This will only work on /mob/dead/observer")
+ return
var/posttransformoutfit = usr.client.robust_dress_shop()
+ if(!posttransformoutfit)
+ return
+
var/mob/living/carbon/human/H = G.incarnate_ghost()
if(posttransformoutfit && istype(H))
@@ -1538,7 +1617,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["togmutate"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
var/block=text2num(href_list["block"])
//testing("togmutate([href_list["block"]] -> [block])")
@@ -1548,6 +1627,11 @@
else if(href_list["adminplayeropts"])
var/mob/M = locateUID(href_list["adminplayeropts"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
show_player_panel(M)
else if(href_list["adminplayerobservefollow"])
@@ -1557,6 +1641,11 @@
return
C.admin_ghost()
var/mob/M = locateUID(href_list["adminplayerobservefollow"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
var/mob/dead/observer/A = C.mob
sleep(2)
A.ManualFollow(M)
@@ -1585,6 +1674,12 @@
return
SStickets.autoRespond(index)
+ if(href_list["convert_ticket"])
+ var/indexNum = text2num(href_list["convert_ticket"])
+ if(href_list["is_mhelp"])
+ SSmentor_tickets.convert_to_other_ticket(indexNum)
+ else
+ SStickets.convert_to_other_ticket(indexNum)
else if(href_list["cult_nextobj"])
if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes")
return
@@ -1605,10 +1700,10 @@
for(var/datum/mind/H in SSticker.mode.cult)
if (H.current)
- to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]")
+ to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]")
for(var/mob/dead/observer/O in GLOB.player_list)
- to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]")
+ to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]")
message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].")
log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]")
@@ -1630,6 +1725,11 @@
else if(href_list["adminmoreinfo"])
var/mob/M = locateUID(href_list["adminmoreinfo"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
admin_mob_info(M)
else if(href_list["adminspawncookie"])
@@ -1637,7 +1737,7 @@
var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"])
if(!ishuman(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand )
@@ -1661,7 +1761,7 @@
var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"])
if(!isliving(M))
- to_chat(usr, "This can only be used on instances of type /mob/living")
+ to_chat(usr, "This can only be used on instances of type /mob/living")
return
if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes")
@@ -1699,6 +1799,11 @@
return
var/mob/M = locateUID(href_list["CentcommReply"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
usr.client.admin_headset_message(M, "Centcomm")
else if(href_list["SyndicateReply"])
@@ -1706,6 +1811,11 @@
return
var/mob/M = locateUID(href_list["SyndicateReply"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
usr.client.admin_headset_message(M, "Syndicate")
else if(href_list["HeadsetMessage"])
@@ -1713,6 +1823,11 @@
return
var/mob/M = locateUID(href_list["HeadsetMessage"])
+
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
usr.client.admin_headset_message(M)
else if(href_list["EvilFax"])
@@ -1720,7 +1835,7 @@
return
var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine")
var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes
@@ -1754,12 +1869,12 @@
P.ico = new
P.ico += "paper_stamp-[stampvalue]"
P.overlays += stampoverlay
- P.stamps += "
"
+ P.stamps += "
"
P.update_icon()
P.faxmachineid = fax.UID()
P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required.
if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset)))
- to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.")
+ to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.")
to_chat(src.owner, "You sent a [eviltype] fax to [H]")
log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax")
message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax")
@@ -1768,7 +1883,7 @@
return
var/mob/living/M = locateUID(href_list["Bless"])
if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob/living")
+ to_chat(usr, "This can only be used on instances of type /mob/living")
return
var/btypes = list("To Arrivals", "Moderate Heal")
var/mob/living/carbon/human/H
@@ -1835,7 +1950,7 @@
var/petchoice = input("Select pet type", "Pets") as null|anything in pets
if(isnull(petchoice))
return
- var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Play as the special event pet [H]?", poll_time = 20 SECONDS, min_hours = 10, source = petchoice)
var/mob/dead/observer/theghost = null
if(candidates.len)
var/mob/living/simple_animal/pet/P = new petchoice(H.loc)
@@ -1888,7 +2003,7 @@
var/mob/living/M = locateUID(href_list["Smite"])
var/mob/living/carbon/human/H
if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob/living")
+ to_chat(usr, "This can only be used on instances of type /mob/living")
return
var/ptypes = list("Lightning bolt", "Fire Death", "Gib")
if(ishuman(M))
@@ -1999,7 +2114,7 @@
var/datum/antagonist/traitor/T = new()
T.give_objectives = FALSE
to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...")
- to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]")
+ to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]")
newtraitormind.add_antag_datum(T)
else
to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].")
@@ -2028,10 +2143,10 @@
return
var/mob/living/carbon/human/H = locateUID(href_list["cryossd"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(!href_list["cryoafk"] && !isLivingSSD(H))
- to_chat(usr, "This can only be used on living, SSD players.")
+ to_chat(usr, "This can only be used on living, SSD players.")
return
if(istype(H.loc, /obj/machinery/cryopod))
var/obj/machinery/cryopod/P = H.loc
@@ -2051,28 +2166,28 @@
return
var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
var/obj/item/paper/P = new /obj/item/paper(null)
var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"])
P.name = "Central Command - paper"
var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions")
var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes
- var/tmsg = "
Nanotrasen Science Station [GLOB.using_map.station_short]
NAS Trurl Communications Department Report
"
+ var/tmsg = "
Nanotrasen Science Station [GLOB.using_map.station_short]
NAS Trurl Communications Department Report
"
if(stype == "Handle it yourselves!")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.
This is an automatic message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.
This is an automatic message."
else if(stype == "Illegible fax")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.
Please consult your nearest dictionary and/or thesaurus and try again.
This is an automatic message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.
Please consult your nearest dictionary and/or thesaurus and try again.
This is an automatic message."
else if(stype == "Fax not signed")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Your fax has not been correctly signed and, as such, we cannot verify your identity.
Please sign your faxes before sending them so that we may verify your identity.
This is an automatic message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Your fax has not been correctly signed and, as such, we cannot verify your identity.
Please sign your faxes before sending them so that we may verify your identity.
This is an automatic message."
else if(stype == "Not Right Now")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.
This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.
Please try again later.
This is an automatic message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.
This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.
Please try again later.
This is an automatic message."
else if(stype == "You are wasting our time")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.
Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.
This is an automatic message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.
Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.
This is an automatic message."
else if(stype == "Keep up the good work")
tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.
We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.
This is absolutely not an automated message."
else if(stype == "ERT Instructions")
- tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Please utilize the Card Swipers if you wish to call for an ERT.
This is an automated message."
+ tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.
Please utilize the Card Swipers if you wish to call for an ERT.
This is an automated message."
else
return
tmsg += ""
@@ -2093,11 +2208,11 @@
P.ico = new
P.ico += "paper_stamp-[stampvalue]"
P.overlays += stampoverlay
- P.stamps += "
"
+ P.stamps += "
"
P.update_icon()
fax.receivefax(P)
if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset)))
- to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.")
+ to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.")
to_chat(src.owner, "You sent a standard '[stype]' fax to [H]")
log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax")
message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax")
@@ -2105,10 +2220,10 @@
else if(href_list["HONKReply"])
var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset))
- to_chat(usr, "The person you are trying to contact is not wearing a headset")
+ to_chat(usr, "The person you are trying to contact is not wearing a headset")
return
var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "")
@@ -2125,13 +2240,13 @@
if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny")
var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"])
if(!istype(H))
- to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
+ to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
if(H.stat != 0)
- to_chat(usr, "The person you are trying to contact is not conscious.")
+ to_chat(usr, "The person you are trying to contact is not conscious.")
return
if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset))
- to_chat(usr, "The person you are trying to contact is not wearing a headset")
+ to_chat(usr, "The person you are trying to contact is not wearing a headset")
return
var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "")
@@ -2139,7 +2254,7 @@
GLOB.ert_request_answered = TRUE
to_chat(src.owner, "You sent [input] to [H] via a secure channel.")
log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].")
- to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].")
+ to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].")
else
src.owner.response_team()
@@ -2294,14 +2409,14 @@
P.stamped = new
P.stamped += /obj/item/stamp/centcom
P.overlays += stampoverlay
- P.stamps += "
"
+ P.stamps += "
"
else if(stamptype == "text")
if(!P.stamped)
P.stamped = new
P.stamped += /obj/item/stamp
P.overlays += stampoverlay
- P.stamps += " [stampvalue]"
+ P.stamps += " [stampvalue]"
if(destination != "All Departments")
if(!fax.receivefax(P))
@@ -2331,7 +2446,7 @@
if(notify == "Yes")
var/mob/living/carbon/human/H = sender
if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset)))
- to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.")
+ to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.")
if(sender)
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]")
message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1)
@@ -2350,8 +2465,8 @@
if(!check_rights(R_ADMIN))
return
var/mob/M = locateUID(href_list["getplaytimewindow"])
- if(!M)
- to_chat(usr, "ERROR: Mob not found.")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
cmd_mentor_show_exp_panel(M.client)
@@ -2359,6 +2474,9 @@
if(!check_rights(R_ADMIN)) return
var/mob/M = locateUID(href_list["jumpto"])
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
usr.client.jumptomob(M)
else if(href_list["getmob"])
@@ -2366,24 +2484,37 @@
if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return
var/mob/M = locateUID(href_list["getmob"])
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
usr.client.Getmob(M)
else if(href_list["sendmob"])
if(!check_rights(R_ADMIN)) return
var/mob/M = locateUID(href_list["sendmob"])
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
usr.client.sendmob(M)
else if(href_list["narrateto"])
if(!check_rights(R_ADMIN)) return
var/mob/M = locateUID(href_list["narrateto"])
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
usr.client.cmd_admin_direct_narrate(M)
else if(href_list["subtlemessage"])
- if(!check_rights(R_ADMIN)) return
+ if(!check_rights(R_EVENT))
+ return
var/mob/M = locateUID(href_list["subtlemessage"])
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
usr.client.cmd_admin_subtle_message(M)
else if(href_list["traitor"])
@@ -2394,8 +2525,8 @@
return
var/mob/M = locateUID(href_list["traitor"])
- if(!ismob(M))
- to_chat(usr, "This can only be used on instances of type /mob.")
+ if(!istype(M, /mob))
+ to_chat(usr, "This can only be used on instances of type /mob")
return
show_traitor_panel(M)
@@ -2464,7 +2595,7 @@
switch(where)
if("inhand")
if(!iscarbon(usr) && !isrobot(usr))
- to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.")
+ to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.")
where = "onfloor"
target = usr
@@ -2476,10 +2607,10 @@
target = locate(loc.x + X,loc.y + Y,loc.z + Z)
if("inmarked")
if(!marked_datum)
- to_chat(usr, "You don't have any object marked. Abandoning spawn.")
+ to_chat(usr, "You don't have any object marked. Abandoning spawn.")
return
else if(!istype(marked_datum,/atom))
- to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.")
+ to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.")
return
else
target = marked_datum
@@ -2544,7 +2675,7 @@
message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]")
log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]")
else
- to_chat(usr, "You may only use this when the game is running.")
+ to_chat(usr, "You may only use this when the game is running.")
else if(href_list["memoeditlist"])
if(!check_rights(R_SERVER)) return
@@ -2585,14 +2716,16 @@
if("monkey")
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","M")
- 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
spawn(0)
H.monkeyize()
ok = 1
if("corgi")
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","M")
- 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
spawn(0)
H.corgize()
ok = 1
@@ -2622,7 +2755,7 @@
feedback_add_details("admin_secrets_fun_used","TriAI")
if("gravity")
if(!(SSticker && SSticker.mode))
- to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.")
+ to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.")
return
GLOB.gravity_is_on = !GLOB.gravity_is_on
for(var/area/A in world)
@@ -2663,7 +2796,8 @@
feedback_inc("admin_secrets_fun_used",1)
feedback_add_details("admin_secrets_fun_used","PW")
message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1)
- 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
var/turf/loc = find_loc(H)
var/security = 0
if(!is_station_level(loc.z) || GLOB.prisonwarped.Find(H))
@@ -2958,7 +3092,7 @@
if(usr)
log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]")
if(ok)
- to_chat(world, text("A secret has been activated by []!", usr.key))
+ to_chat(world, text("A secret has been activated by []!", usr.key))
else if(href_list["secretsadmin"])
if(!check_rights(R_ADMIN)) return
@@ -2966,17 +3100,17 @@
var/ok = 0
switch(href_list["secretsadmin"])
if("list_signalers")
- var/dat = "Showing last [length(GLOB.lastsignalers)] signalers. "
+ var/dat = "Showing last [length(GLOB.lastsignalers)] signalers. "
for(var/sig in GLOB.lastsignalers)
dat += "[sig] "
usr << browse(dat, "window=lastsignalers;size=800x500")
if("list_lawchanges")
- var/dat = "Showing last [length(GLOB.lawchanges)] law changes. "
+ var/dat = "Showing last [length(GLOB.lawchanges)] law changes. "
for(var/sig in GLOB.lawchanges)
dat += "[sig] "
usr << browse(dat, "window=lawchanges;size=800x500")
if("list_job_debug")
- var/dat = "Job Debug info. "
+ var/dat = "Job Debug info. "
if(SSjobs)
for(var/line in SSjobs.job_debug)
dat += "[line] "
@@ -2994,9 +3128,10 @@
alert("The game mode is [SSticker.mode.name]")
else alert("For some reason there's a ticker, but not a game mode")
if("manifest")
- var/dat = "Showing Crew Manifest. "
+ var/dat = "Showing Crew Manifest. "
dat += "| Name | Position | "
- 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(H.ckey)
dat += text("| [] | [] | ", H.name, H.get_assignment())
dat += " "
@@ -3004,17 +3139,19 @@
if("check_antagonist")
check_antagonists()
if("DNA")
- var/dat = "Showing DNA from blood. "
+ var/dat = "Showing DNA from blood. "
dat += "| Name | DNA | Blood Type | "
- 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(H.dna && H.ckey)
dat += "| [H] | [H.dna.unique_enzymes] | [H.dna.blood_type] | "
dat += " "
usr << browse(dat, "window=DNA;size=440x410")
if("fingerprints")
- var/dat = "Showing Fingerprints. "
+ var/dat = "Showing Fingerprints. "
dat += "| Name | Fingerprints | "
- 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(H.ckey)
if(H.dna && H.dna.uni_identity)
dat += "| [H] | [md5(H.dna.uni_identity)] | "
@@ -3046,14 +3183,14 @@
if(usr)
log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]")
if(ok)
- to_chat(world, text("A secret has been activated by []!", usr.key))
+ to_chat(world, text("A secret has been activated by []!", usr.key))
else if(href_list["secretscoder"])
if(!check_rights(R_DEBUG)) return
switch(href_list["secretscoder"])
if("spawn_objects")
- var/dat = "Admin Log "
+ var/dat = "Admin Log "
for(var/l in GLOB.admin_log)
dat += "[l]"
if(!GLOB.admin_log.len)
@@ -3223,27 +3360,27 @@
else if(href_list["ac_censor_channel_author"])
var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"])
- if(FC.author != "\[REDACTED\]")
+ if(FC.author != "\[REDACTED\]")
FC.backup_author = FC.author
- FC.author = "\[REDACTED\]"
+ FC.author = "\[REDACTED\]"
else
FC.author = FC.backup_author
src.access_news_network()
else if(href_list["ac_censor_channel_story_author"])
var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"])
- if(MSG.author != "\[REDACTED\]")
+ if(MSG.author != "\[REDACTED\]")
MSG.backup_author = MSG.author
- MSG.author = "\[REDACTED\]"
+ MSG.author = "\[REDACTED\]"
else
MSG.author = MSG.backup_author
src.access_news_network()
else if(href_list["ac_censor_channel_story_body"])
var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"])
- if(MSG.body != "\[REDACTED\]")
+ if(MSG.body != "\[REDACTED\]")
MSG.backup_body = MSG.body
- MSG.body = "\[REDACTED\]"
+ MSG.body = "\[REDACTED\]"
else
MSG.body = MSG.backup_body
src.access_news_network()
@@ -3439,14 +3576,15 @@
return
var/datum/outfit/O = hunter_outfits[dresscode]
message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...")
- var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1)
+ var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_traitor")
+ var/list/candidates = SSghost_spawns.poll_candidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, TRUE, source = source)
if(!candidates.len)
- to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.")
+ to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.")
return
var/mob/C = pick(candidates)
var/key_of_hunter = C.key
if(!key_of_hunter)
- to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.")
+ to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.")
return
var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter)
hunter_mind.active = 1
@@ -3477,9 +3615,9 @@
hunter_mind.objectives += protect_objective
SSticker.mode.traitors |= hunter_mob.mind
to_chat(hunter_mob, "ATTENTION: You are now on a mission!")
- to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. ");
+ to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)].");
if(killthem)
- to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived.");
+ to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived.");
hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR
var/datum/atom_hud/antag/tatorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
tatorhud.join_hud(hunter_mob)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 018b45422c5..c4eabef3eae 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -444,9 +444,9 @@
if(object == world) // Global proc.
procname = "/proc/[procname]"
- return call(procname)(arglist(new_args))
+ return (WrapAdminProcCall(GLOBAL_PROC, procname, new_args))
- return call(object, procname)(arglist(new_args))
+ return (WrapAdminProcCall(object, procname, new_args))
/proc/SDQL2_tokenize(query_text)
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 8ac9c4fd2b0..0b88d6030bb 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -80,7 +80,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo
mobs_found += found
if(!ai_found && isAI(found))
ai_found = 1
- msg += "[original_word] "
+ msg += "[original_word] "
continue
msg += "[original_word] "
@@ -125,22 +125,28 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo
ticketNum = T.ticketNum // ticketNum is the number of their ticket.
T.addResponse(src, msg)
- msg = "[span][selected_type]: [key_name(src, TRUE, selected_type)] ([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) ([admin_jump_link(mob)]) (TICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]"
+ var/finalised_msg = "[span][selected_type]: [key_name(src, TRUE, selected_type)] "
+ finalised_msg += "([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) "
+ finalised_msg += "([admin_jump_link(mob)]) (TICKET) "
+ finalised_msg += "[ai_found ? "(CL)" : ""] (TAKE) "
+ finalised_msg += "(RESOLVE) [isMhelp ? "" : "(AUTO)"] "
+ finalised_msg += "(CONVERT) : [span][msg]"
+
if(isMhelp)
//Open a new adminticket and inform the user.
- SSmentor_tickets.newTicket(src, prunedmsg, msg)
+ SSmentor_tickets.newTicket(src, prunedmsg, finalised_msg)
for(var/client/X in mentorholders + modholders + adminholders)
if(X.prefs.sound & SOUND_MENTORHELP)
- X << 'sound/effects/adminhelp.ogg'
- to_chat(X, msg)
+ SEND_SOUND(X, 'sound/effects/adminhelp.ogg')
+ to_chat(X, finalised_msg)
else //Ahelp
//Open a new adminticket and inform the user.
- SStickets.newTicket(src, prunedmsg, msg)
+ SStickets.newTicket(src, prunedmsg, finalised_msg)
for(var/client/X in modholders + adminholders)
if(X.prefs.sound & SOUND_ADMINHELP)
- X << 'sound/effects/adminhelp.ogg'
+ SEND_SOUND(X, 'sound/effects/adminhelp.ogg')
window_flash(X)
- to_chat(X, msg)
+ to_chat(X, finalised_msg)
@@ -165,7 +171,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo
var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins)
var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both
for(var/client/X in GLOB.admins)
- admin_number_total++;
+ admin_number_total++
var/invalid = 0
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
admin_number_ignored++
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index 0c1a8c5f14f..e5e5dae03b5 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -22,6 +22,10 @@
to_chat(src, "Nowhere to jump to!")
return
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
+
admin_forcemove(usr, T)
log_admin("[key_name(usr)] jumped to [A]")
if(!isobserver(usr))
@@ -35,6 +39,9 @@
if(!check_rights(R_ADMIN))
return
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1)
@@ -52,6 +59,9 @@
log_admin("[key_name(usr)] jumped to [key_name(M)]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1)
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
if(src.mob)
var/mob/A = src.mob
var/turf/T = get_turf(M)
@@ -70,6 +80,9 @@
var/turf/T = locate(tx, ty, tz)
if(T)
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
admin_forcemove(usr, T)
if(isobserver(usr))
var/mob/dead/observer/O = usr
@@ -96,13 +109,15 @@
log_admin("[key_name(usr)] jumped to [key_name(M)]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1)
-
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
admin_forcemove(usr, M.loc)
feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getmob(var/mob/M in GLOB.mob_list)
- set category = "Admin"
+ set category = null
set name = "Get Mob"
set desc = "Mob to teleport"
@@ -111,11 +126,15 @@
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1)
+
+ if(isobj(M.loc))
+ var/obj/O = M.loc
+ O.force_eject_occupant()
admin_forcemove(M, get_turf(usr))
feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getkey()
- set category = "Admin"
+ set category = null
set name = "Get Key"
set desc = "Key to teleport"
@@ -135,6 +154,9 @@
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1)
if(M)
+ if(isobj(M.loc))
+ var/obj/O = M.loc
+ O.force_eject_occupant()
admin_forcemove(M, get_turf(usr))
admin_forcemove(usr, M.loc)
feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -148,6 +170,9 @@
var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas()
if(A)
+ if(isobj(M.loc))
+ var/obj/O = M.loc
+ O.force_eject_occupant()
admin_forcemove(M, pick(get_area_turfs(A)))
feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] teleported [key_name(M)] to [A]")
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 164d499e04a..ce5f06053eb 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -2,24 +2,24 @@
/client/proc/cmd_admin_pm_context(mob/M as mob in GLOB.mob_list)
set category = null
set name = "Admin PM Mob"
- if(!holder)
- to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.")
+ if(!check_rights(R_ADMIN|R_MENTOR))
+ return
+ if(!ismob(M) || !M.client)
return
- if( !ismob(M) || !M.client ) return
cmd_admin_pm(M.client,null)
feedback_add_details("admin_verb","APMM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
/client/proc/cmd_admin_pm_panel()
set category = "Admin"
set name = "Admin PM Name"
- if(!holder)
- to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.")
+ if(!check_rights(R_ADMIN|R_MENTOR))
return
var/list/client/targets[0]
for(var/client/T)
if(T.mob)
- if(istype(T.mob, /mob/new_player))
+ if(isnewplayer(T.mob))
targets["(New Player) - [T]"] = T
else if(istype(T.mob, /mob/dead/observer))
targets["[T.mob.name](Ghost) - [T]"] = T
@@ -36,13 +36,12 @@
/client/proc/cmd_admin_pm_by_key_panel()
set category = "Admin"
set name = "Admin PM Key"
- if(!holder)
- to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.")
+ if(!check_rights(R_ADMIN|R_MENTOR))
return
var/list/client/targets[0]
for(var/client/T)
if(T.mob)
- if(istype(T.mob, /mob/new_player))
+ if(isnewplayer(T.mob))
targets["[T] - (New Player)"] = T
else if(istype(T.mob, /mob/dead/observer))
targets["[T] - [T.mob.name](Ghost)"] = T
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index a0e8b4b9088..431633309a9 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -7,6 +7,8 @@
msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))
if(!msg) return
+ var/datum/asays/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday)
+ GLOB.asays += asay
log_adminsay(msg, src)
if(check_rights(R_ADMIN,0))
@@ -78,5 +80,5 @@
C.verbs -= msay
to_chat(C, "Mentor chat has been disabled.")
- admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].")
+ log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].")
feedback_add_details("admin_verb", "TMC")
diff --git a/code/modules/admin/verbs/alt_check.dm b/code/modules/admin/verbs/alt_check.dm
deleted file mode 100644
index c8060f22036..00000000000
--- a/code/modules/admin/verbs/alt_check.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-/client/proc/alt_check()
- set category = "Admin"
- set name = "Alt Account Checker"
-
- var/dat = {"Just to be sure you should try to also look up computer IDs/IPs on the server logs for a second opinion.
- Additionally make an attempt to introduce new players to the server
- "}
-
- if(GLOB.dbcon.IsConnected())
- for(var/client/C in GLOB.clients)
- dat += "[C.ckey] (Player Age: [C.player_age]) - [C.computer_id] / [C.address] "
- if(C.related_accounts_cid.len)
- dat += "--Accounts associated with CID: "
- dat += "[jointext(C.related_accounts_cid, " - ")] "
- if(C.related_accounts_ip.len)
- dat += "--Accounts associated with IP: "
- dat += "[jointext(C.related_accounts_ip, " - ")] "
- usr << browse(dat, "window=alt_panel;size=640x480")
- return
-
diff --git a/code/modules/admin/verbs/asays.dm b/code/modules/admin/verbs/asays.dm
new file mode 100644
index 00000000000..e713d2b1bcc
--- /dev/null
+++ b/code/modules/admin/verbs/asays.dm
@@ -0,0 +1,70 @@
+GLOBAL_LIST_EMPTY(asays)
+
+/datum/asays
+ var/ckey
+ var/rank
+ var/message
+ var/time
+
+/datum/asays/New(ckey = "", rank = "", message = "", time = 0)
+ src.ckey = ckey
+ src.rank = rank
+ src.message = message
+ src.time = time
+
+/client/proc/view_asays()
+ set name = "Asays"
+ set desc = "View Asays from the current round."
+ set category = "Admin"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/list/output = list({"
+
+ Refresh
+
+ "})
+
+ // Header & body start
+ output += {"
+
+
+ | Time |
+ Ckey |
+ Message |
+
+
+
+ "}
+
+ for(var/datum/asays/A in GLOB.asays)
+ var/timestr = time2text(A.time, "hh:mm:ss")
+ output += {"
+
+ | [timestr] |
+ [A.ckey] ([A.rank]) |
+ [A.message] |
+
+ "}
+
+ output += {"
+
+ "}
+
+ var/datum/browser/popup = new(src, "asays", "Current Round Asays ", 1200, 825)
+ popup.set_content(output.Join())
+ popup.open(0)
diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm
index b3d1543aedc..1284f9e0faf 100644
--- a/code/modules/admin/verbs/custom_event.dm
+++ b/code/modules/admin/verbs/custom_event.dm
@@ -3,8 +3,7 @@
set category = "Event"
set name = "Change Custom Event"
- if(!holder)
- to_chat(src, "Only administrators may use this command.")
+ if(!check_rights(R_EVENT))
return
var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 329efb8e067..d2c05ff4f6c 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -85,18 +85,80 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
return
message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
- returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc
+ returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
else
//this currently has no hascall protection. wasn't able to get it working.
message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]")
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]")
- returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc
+ returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]")
feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+// All these vars are related to proc call protection
+// If you add more of these, for the love of fuck, protect them
+
+/// Who is currently calling procs
GLOBAL_VAR(AdminProcCaller)
GLOBAL_PROTECT(AdminProcCaller)
+/// How many procs have been called
+GLOBAL_VAR_INIT(AdminProcCallCount, 0)
+GLOBAL_PROTECT(AdminProcCallCount)
+/// UID of the admin who last called
+GLOBAL_VAR(LastAdminCalledTargetUID)
+GLOBAL_PROTECT(LastAdminCalledTargetUID)
+/// Last target to have a proc called on it
+GLOBAL_VAR(LastAdminCalledTarget)
+GLOBAL_PROTECT(LastAdminCalledTarget)
+/// Last proc called
+GLOBAL_VAR(LastAdminCalledProc)
+GLOBAL_PROTECT(LastAdminCalledProc)
+/// List to handle proc call spam prevention
+GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
+GLOBAL_PROTECT(AdminProcCallSpamPrevention)
+
+
+// Wrapper for proccalls where the datum is flagged as vareditted
+/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
+ if(target && procname == "Del")
+ to_chat(usr, "Calling Del() is not allowed")
+ return
+
+ if(target != GLOBAL_PROC && !target.CanProcCall(procname))
+ to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
+ return
+ var/current_caller = GLOB.AdminProcCaller
+ var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
+ if(!ckey)
+ CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
+ if(current_caller && current_caller != ckey)
+ if(!GLOB.AdminProcCallSpamPrevention[ckey])
+ to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.")
+ GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
+ UNTIL(!GLOB.AdminProcCaller)
+ to_chat(usr, "Running your proc")
+ GLOB.AdminProcCallSpamPrevention -= ckey
+ else
+ UNTIL(!GLOB.AdminProcCaller)
+ GLOB.LastAdminCalledProc = procname
+ if(target != GLOBAL_PROC)
+ GLOB.LastAdminCalledTargetUID = target.UID()
+ GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
+ ++GLOB.AdminProcCallCount
+ . = world.WrapAdminProcCall(target, procname, arguments)
+ if(--GLOB.AdminProcCallCount == 0)
+ GLOB.AdminProcCaller = null
+
+//adv proc call this, ya nerds
+/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
+ if(target == GLOBAL_PROC)
+ return call(procname)(arglist(arguments))
+ else if(target != world)
+ return call(target, procname)(arglist(arguments))
+ else
+ to_chat(usr, "Call to world/proc/[procname] blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
+ log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]l")
/proc/IsAdminAdvancedProcCall()
#ifdef TESTING
@@ -106,7 +168,7 @@ GLOBAL_PROTECT(AdminProcCaller)
#endif
/client/proc/callproc_datum(var/A as null|area|mob|obj|turf)
- set category = "Debug"
+ set category = null
set name = "Atom ProcCall"
if(!check_rights(R_PROCCALL))
@@ -131,7 +193,7 @@ GLOBAL_PROTECT(AdminProcCaller)
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]")
spawn()
- var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc
+ var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]")
feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -251,7 +313,7 @@ GLOBAL_PROTECT(AdminProcCaller)
alert("That mob doesn't seem to exist, close the panel and try again.")
return
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
alert("The mob must not be a new_player.")
return
@@ -356,25 +418,6 @@ GLOBAL_PROTECT(AdminProcCaller)
else
alert("Invalid mob")
-//TODO: merge the vievars version into this or something maybe mayhaps
-/client/proc/cmd_debug_del_all()
- set category = "Debug"
- set name = "Del-All"
-
- if(!check_rights(R_DEBUG))
- return
-
- // to prevent REALLY stupid deletions
- var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai)
- var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked
- if(hsbitem)
- for(var/atom/O in world)
- if(istype(O, hsbitem))
- qdel(O)
- log_admin("[key_name(src)] has deleted all instances of [hsbitem].")
- message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0)
- feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/client/proc/cmd_debug_del_sing()
set category = "Debug"
set name = "Del Singulo / Tesla"
@@ -382,7 +425,7 @@ GLOBAL_PROTECT(AdminProcCaller)
if(!check_rights(R_DEBUG))
return
- //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all
+ //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with qdel-all
var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No")
if(confirm != "Yes")
return
@@ -428,7 +471,7 @@ GLOBAL_PROTECT(AdminProcCaller)
id.icon_state = "gold"
id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access()
else
- var/obj/item/card/id/id = new/obj/item/card/id(M);
+ var/obj/item/card/id/id = new/obj/item/card/id(M)
id.icon_state = "gold"
id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access()
id.registered_name = H.real_name
@@ -581,7 +624,7 @@ GLOBAL_PROTECT(AdminProcCaller)
for(var/areatype in areas_without_camera)
to_chat(world, "* [areatype]")
-/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list)
+/client/proc/cmd_admin_dress(mob/living/carbon/human/M in GLOB.human_list)
set category = "Event"
set name = "Select equipment"
@@ -816,21 +859,6 @@ GLOBAL_PROTECT(AdminProcCaller)
else
alert("Invalid mob")
-/client/proc/reload_nanoui_resources()
- set category = "Debug"
- set name = "Reload NanoUI Resources"
- set desc = "Force the client to redownload NanoUI Resources"
-
- // Close open NanoUIs.
- SSnanoui.close_user_uis(usr)
-
- // Re-load the assets.
- var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
- assets.register()
-
- // Clear the user's cache so they get resent.
- usr.client.cache = list()
-
/client/proc/view_runtimes()
set category = "Debug"
set name = "View Runtimes"
@@ -870,6 +898,9 @@ GLOBAL_PROTECT(AdminProcCaller)
if(istype(landmark))
var/datum/map_template/ruin/template = landmark.ruin_template
+ if(isobj(usr.loc))
+ var/obj/O = usr.loc
+ O.force_eject_occupant()
admin_forcemove(usr, get_turf(landmark))
to_chat(usr, "[template.name]")
diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm
index 78f2647e429..2ffb4659486 100644
--- a/code/modules/admin/verbs/diagnostics.dm
+++ b/code/modules/admin/verbs/diagnostics.dm
@@ -108,7 +108,7 @@
/client/proc/reload_admins()
set name = "Reload Admins"
- set category = "Debug"
+ set category = "Server"
if(!check_rights(R_SERVER))
return
diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm
index 37afe35ef6a..3403a4417cf 100644
--- a/code/modules/admin/verbs/gimmick_team.dm
+++ b/code/modules/admin/verbs/gimmick_team.dm
@@ -39,6 +39,7 @@
if(alert("Do you want these characters automatically classified as antagonists?",,"Yes","No")=="Yes")
is_syndicate = 1
+ var/datum/outfit/O = outfit_list[dresscode]
var/list/players_to_spawn = list()
if(pick_manually)
var/list/possible_ghosts = list()
@@ -52,14 +53,12 @@
players_to_spawn += candidate
else
to_chat(src, "Polling candidates...")
- players_to_spawn = pollCandidates("Do you want to play as an event character?")
+ players_to_spawn = SSghost_spawns.poll_candidates("Do you want to play as \a [O.name]?")
if(!players_to_spawn.len)
to_chat(src, "Nobody volunteered.")
return 0
- var/datum/outfit/O = outfit_list[dresscode]
-
var/players_spawned = 0
for(var/mob/thisplayer in players_to_spawn)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(T)
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 54ddf85045e..d4ea1a0dd2e 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -55,7 +55,8 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
infiltrators += candidate
else
to_chat(src, "Polling candidates...")
- infiltrators = pollCandidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, 1)
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate")
+ infiltrators = SSghost_spawns.poll_candidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, TRUE, source = ma)
if(!infiltrators.len)
to_chat(src, "Nobody volunteered.")
diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm
index 903926f4923..cafaab8f1d7 100644
--- a/code/modules/admin/verbs/logging_view.dm
+++ b/code/modules/admin/verbs/logging_view.dm
@@ -2,10 +2,20 @@ GLOBAL_LIST_INIT(open_logging_views, list())
/client/proc/cmd_admin_open_logging_view()
set category = "Admin"
- set name = "Open Logging View"
+ set name = "Logging View"
set desc = "Opens the detailed logging viewer"
+ open_logging_view()
- if(!GLOB.open_logging_views[usr.client.ckey])
- GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer()
- var/datum/log_viewer/LV = GLOB.open_logging_views[usr.client.ckey]
- LV.show_ui(usr)
+/client/proc/open_logging_view(list/mob/mobs_to_add = null, clear_view = FALSE)
+ var/datum/log_viewer/cur_view = GLOB.open_logging_views[usr.client.ckey]
+ if(!cur_view)
+ cur_view = new /datum/log_viewer()
+ GLOB.open_logging_views[usr.client.ckey] = cur_view
+ else if(clear_view)
+ cur_view.clear_all()
+
+ if(mobs_to_add?.len)
+ cur_view.add_mobs(mobs_to_add)
+
+ cur_view.show_ui(usr)
+
diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm
index edbca5a842f..0dc3dfc1791 100644
--- a/code/modules/admin/verbs/map_template_loadverb.dm
+++ b/code/modules/admin/verbs/map_template_loadverb.dm
@@ -2,8 +2,9 @@
set category = "Debug"
set name = "Map template - Place"
- if(!holder)
+ if(!check_rights(R_DEBUG))
return
+
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates
@@ -36,6 +37,9 @@
set category = "Debug"
set name = "Map Template - Upload"
+ if(!check_rights(R_DEBUG))
+ return
+
var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
if(!map)
return
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 3e89a6ac18d..5d9d218c90e 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -137,7 +137,8 @@ client/proc/one_click_antag()
var/confirm = alert("Are you sure?", "Confirm creation", "Yes", "No")
if(confirm != "Yes")
return 0
- var/list/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard")
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "wizard")
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", source = ma)
log_admin("[key_name(owner)] tried making a Wizard with One-Click-Antag")
message_admins("[key_name_admin(owner)] tried making a Wizard with One-Click-Antag")
diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm
index e51f6da991a..9f2579ad162 100644
--- a/code/modules/admin/verbs/onlyone.dm
+++ b/code/modules/admin/verbs/onlyone.dm
@@ -84,7 +84,7 @@
var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand)
H.unEquip(slot_item_hand)
- var /obj/item/multisword/pure_evil/multi = new(H)
+ var/obj/item/multisword/pure_evil/multi = new(H)
H.equip_to_slot_or_del(multi, slot_r_hand)
var/obj/item/card/id/W = new(H)
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index 6ec103cccc9..ed01bc822a7 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -53,7 +53,7 @@ GLOBAL_LIST_EMPTY(sounds_cache)
set name = "Play Server Sound"
if(!check_rights(R_SOUNDS)) return
- var/list/sounds = file2list("sound/serversound_list.txt");
+ var/list/sounds = file2list("sound/serversound_list.txt")
sounds += GLOB.sounds_cache
var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds
@@ -71,7 +71,7 @@ GLOBAL_LIST_EMPTY(sounds_cache)
var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope")
if(A != "Yep") return
- var/list/sounds = file2list("sound/serversound_list.txt");
+ var/list/sounds = file2list("sound/serversound_list.txt")
sounds += GLOB.sounds_cache
var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds
diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm
index 5cf4c34ffe2..3c1d0820dd7 100644
--- a/code/modules/admin/verbs/possess.dm
+++ b/code/modules/admin/verbs/possess.dm
@@ -2,6 +2,9 @@
set name = "Possess Obj"
set category = null
+ if(!check_rights(R_POSSESS))
+ return
+
if(istype(O,/obj/singularity))
if(config.forbid_singulo_possession)
to_chat(usr, "It is forbidden to possess singularities.")
@@ -35,6 +38,9 @@
set category = null
//usr.loc = get_turf(usr)
+ if(!check_rights(R_POSSESS))
+ return
+
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
usr.real_name = usr.name_archive
usr.name = usr.real_name
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index a7763f31fb1..3864f573597 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -51,7 +51,7 @@
if(!ismob(M))
return
- if(!check_rights(R_SERVER|R_EVENT))
+ if(!check_rights(R_EVENT))
return
var/msg = clean_input("Message:", text("Subtle PM to [M.key]"))
@@ -123,7 +123,7 @@
feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE
- set category = "Event"
+ set category = null
set name = "Direct Narrate"
if(!check_rights(R_SERVER|R_EVENT))
@@ -158,7 +158,7 @@
/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null)
var/mob/living/carbon/human/H = M
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -572,7 +572,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list)
- set category = "Event"
+ set category = null
set name = "Rejuvenate"
if(!check_rights(R_REJUVINATE))
@@ -627,7 +627,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
print_command_report(input, "[command_name()] Update")
if("No")
//same thing as the blob stuff - it's not public, so it's classified, dammit
- GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles.");
+ GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles.")
print_command_report(input, "Classified [command_name()] Update")
else
return
@@ -638,7 +638,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view())
- set category = "Admin"
+ set category = null
set name = "Delete"
if(!check_rights(R_ADMIN))
@@ -826,7 +826,10 @@ Traitors and the like can also be revived with the previous role mostly intact.
else
SSshuttle.emergency.canRecall = FALSE
- SSshuttle.emergency.request()
+ if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED)
+ SSshuttle.emergency.request(coefficient = 0.5, redAlert = TRUE)
+ else
+ SSshuttle.emergency.request()
feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] admin-called the emergency shuttle.")
@@ -874,6 +877,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(SSshuttle)
SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape
+ feedback_add_details("admin_verb", "DENYSHUT")
log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.")
message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.")
@@ -979,7 +983,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/role_string
var/obj_count = 0
var/obj_string = ""
- 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(!isLivingSSD(H))
continue
mins_ssd = round((world.time - H.last_logout) / 600)
@@ -1016,7 +1021,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
msg += "AFK Players:
"
msg += "| Key | Real Name | Job | Mins AFK | Special Role | Area | PPN | Cryo | "
var/mins_afk
- 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.client == null || H.stat == DEAD) // No clientless or dead
continue
mins_afk = round(H.client.inactivity / 600)
@@ -1072,12 +1078,12 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1)
/client/proc/show_tip()
- set category = "Admin"
+ set category = "Event"
set name = "Show Custom Tip"
set desc = "Sends a tip (that you specify) to all players. After all \
you're the experienced player here."
- if(!check_rights(R_ADMIN))
+ if(!check_rights(R_EVENT))
return
var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null
diff --git a/code/modules/admin/verbs/spawnfloorcluwne.dm b/code/modules/admin/verbs/spawnfloorcluwne.dm
deleted file mode 100644
index 37fd3057af4..00000000000
--- a/code/modules/admin/verbs/spawnfloorcluwne.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-/client/proc/spawn_floor_cluwne()
- set category = "Event"
- set name = "Unleash Floor Cluwne"
- set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!"
- var/mob/living/carbon/human/target
-
- if(!check_rights(R_EVENT))
- return
-
- var/confirm = alert("Are you sure you want to release a floor cluwne and kill a lot of people?", "Confirm Massacre", "Yes", "No")
- if(confirm == "Yes")
-
- var/turf/T = get_turf(usr)
- var/list/potential_targets = list()
- for(var/mob/M in GLOB.player_list)
- var/mob/living/carbon/human/H = M
- if(!istype(H))
- continue
- if(H.mind.assigned_role == "Cluwne")
- continue
- potential_targets += H
- if(!potential_targets.len) //You're probably the only player on this damn station, spawn it yourself
- to_chat(src, "No valid targets!")
- return
- target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in potential_targets
- var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T)
- if(target)
- FC.Acquire_Victim(target)
- log_admin("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].")
- message_admins("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].")
diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm
index 0b84c96e71a..40af6eefe49 100644
--- a/code/modules/admin/verbs/striketeam.dm
+++ b/code/modules/admin/verbs/striketeam.dm
@@ -37,7 +37,8 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
break
// Find ghosts willing to be DS
- var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
+ var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_deathsquad")
+ var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = source)
if(!commando_ghosts.len)
to_chat(usr, "Nobody volunteered to join the DeathSquad.")
return
diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm
index 12df7467cbc..405c1d3c3d2 100644
--- a/code/modules/admin/verbs/striketeam_syndicate.dm
+++ b/code/modules/admin/verbs/striketeam_syndicate.dm
@@ -45,7 +45,7 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0)
break
// Find ghosts willing to be SST
- var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
+ var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
if(!commando_ghosts.len)
to_chat(usr, "Nobody volunteered to join the SST.")
return
diff --git a/code/modules/admin/verbs/toggledebugverbs.dm b/code/modules/admin/verbs/toggledebugverbs.dm
index 216f5c3e4b0..38b49869298 100644
--- a/code/modules/admin/verbs/toggledebugverbs.dm
+++ b/code/modules/admin/verbs/toggledebugverbs.dm
@@ -17,8 +17,6 @@ GLOBAL_LIST_INIT(admin_verbs_show_debug_verbs, list(
/client/proc/print_jobban_old,
/client/proc/print_jobban_old_filter,
/client/proc/forceEvent,
- /client/proc/nanomapgen_DumpImage,
- /client/proc/reload_nanoui_resources,
/client/proc/admin_redo_space_transitions,
/client/proc/make_turf_space_map,
/client/proc/vv_by_ref
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 4a6fe7d6640..41333760f2d 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index beefc41735b..82f492d214b 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -46,7 +46,8 @@
checking = TRUE
to_chat(user, "You activate [src] and wait for confirmation.")
- var/list/nuke_candidates = pollCandidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 150)
+ var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate_space_sword")
+ var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = ma)
if(LAZYLEN(nuke_candidates))
checking = FALSE
if(QDELETED(src) || !check_usability(user))
@@ -180,7 +181,7 @@
var/type = "slaughter"
if(demon_type == /mob/living/simple_animal/slaughter/laughter)
type = "laughter"
- var/list/candidates = pollCandidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
@@ -194,7 +195,7 @@
to_chat(user, "The demons do not respond to your summon. Perhaps you should try again later.")
/obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user)
- var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
+ var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
S.vialspawned = TRUE
S.holder = holder
@@ -252,7 +253,7 @@
used = TRUE
to_chat(user, "You break the seal on the bottle, calling upon the dire sludge to awaken...")
- var/list/candidates = pollCandidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 10 SECONDS, source = morph_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index e13fb701317..6275c00ba9b 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -41,9 +41,8 @@
A.common_radio.channels.Remove("Syndicate") // De-traitored AIs can still state laws over the syndicate channel without this
A.laws.sorted_laws = A.laws.inherent_laws.Copy() // AI's 'notify laws' button will still state a law 0 because sorted_laws contains it
A.show_laws()
- A.malf_picker.remove_malf_verbs(A)
- A.verbs -= /mob/living/silicon/ai/proc/choose_modules
- qdel(A.malf_picker)
+ A.remove_malf_abilities()
+ QDEL_NULL(A.malf_picker)
if(owner.som)
var/datum/mindslaves/slaved = owner.som
@@ -251,9 +250,14 @@
/datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind)
- var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
- traitorhud.join_hud(owner.current, null)
- set_antag_hud(owner.current, "hudsyndicate")
+ if(locate(/datum/objective/hijack) in owner.objectives)
+ var/datum/atom_hud/antag/hijackhud = GLOB.huds[ANTAG_HUD_TRAITOR]
+ hijackhud.join_hud(owner.current, null)
+ set_antag_hud(owner.current, "hudhijack")
+ else
+ var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
+ traitorhud.join_hud(owner.current, null)
+ set_antag_hud(owner.current, "hudsyndicate")
/datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind)
@@ -400,7 +404,7 @@
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
- var message = " The code phrases were: [phrases] \
+ var/message = " The code phrases were: [phrases] \
The code responses were: [responses] "
return message
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 35e80e53d18..a17649bb008 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -63,6 +63,7 @@
else if(ismouse(target))
var/mob/living/simple_animal/mouse/M = target
visible_message("SPLAT!")
+ M.death()
M.splat()
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
layer = MOB_LAYER - 0.2
diff --git a/code/modules/awaymissions/maploader/dmm_suite.dm b/code/modules/awaymissions/maploader/dmm_suite.dm
index 1eee3d8fc34..5e958f28ee6 100644
--- a/code/modules/awaymissions/maploader/dmm_suite.dm
+++ b/code/modules/awaymissions/maploader/dmm_suite.dm
@@ -1,72 +1,82 @@
-GLOBAL_DATUM_INIT(maploader, /dmm_suite, new())
-dmm_suite{
- /*
+/*
- dmm_suite version 1.0
- Released January 30th, 2011.
+ dmm_suite version 1.0
+ Released January 30th, 2011.
- defines the object /dmm_suite
- - Provides the proc load_map()
- - Loads the specified map file onto the specified z-level.
- - provides the proc write_map()
- - Returns a text string of the map in dmm format
- ready for output to a file.
- - provides the proc save_map()
- - Returns a .dmm file if map is saved
- - Returns FALSE if map fails to save
+ defines the object /dmm_suite
+ - Provides the proc load_map()
+ - Loads the specified map file onto the specified z-level.
+ - provides the proc write_map()
+ - Returns a text string of the map in dmm format
+ ready for output to a file.
+ - provides the proc save_map()
+ - Returns a .dmm file if map is saved
+ - Returns FALSE if map fails to save
- The dmm_suite provides saving and loading of map files in BYOND's native DMM map
- format. It approximates the map saving and loading processes of the Dream Maker
- and Dream Seeker programs so as to allow editing, saving, and loading of maps at
- runtime.
+ The dmm_suite provides saving and loading of map files in BYOND's native DMM map
+ format. It approximates the map saving and loading processes of the Dream Maker
+ and Dream Seeker programs so as to allow editing, saving, and loading of maps at
+ runtime.
- ------------------------
+ ------------------------
- To save a map at runtime, create an instance of /dmm_suite, and then call
- write_map(), which accepts three arguments:
- - A turf representing one corner of a three dimensional grid (Required).
- - Another turf representing the other corner of the same grid (Required).
- - Any, or a combination, of several bit flags (Optional, see documentation).
+ To save a map at runtime, create an instance of /dmm_suite, and then call
+ write_map(), which accepts three arguments:
+ - A turf representing one corner of a three dimensional grid (Required).
+ - Another turf representing the other corner of the same grid (Required).
+ - Any, or a combination, of several bit flags (Optional, see documentation).
- The order in which the turfs are supplied does not matter, the /dmm_writer will
- determine the grid containing both, in much the same way as DM's block() function.
- write_map() will then return a string representing the saved map in dmm format;
- this string can then be saved to a file, or used for any other purose.
+ The order in which the turfs are supplied does not matter, the /dmm_writer will
+ determine the grid containing both, in much the same way as DM's block() function.
+ write_map() will then return a string representing the saved map in dmm format;
+ this string can then be saved to a file, or used for any other purose.
- ------------------------
+ ------------------------
- To load a map at runtime, create an instance of /dmm_suite, and then call load_map(),
- which accepts two arguments:
- - A .dmm file to load (Required).
- - A number representing the z-level on which to start loading the map (Optional).
+ To load a map at runtime, create an instance of /dmm_suite, and then call load_map(),
+ which accepts two arguments:
+ - A .dmm file to load (Required).
+ - A number representing the z-level on which to start loading the map (Optional).
- The /dmm_suite will load the map file starting on the specified z-level. If no
- z-level was specified, world.maxz will be increased so as to fit the map. Note
- that if you wish to load a map onto a z-level that already has objects on it,
- you will have to handle the removal of those objects. Otherwise the new map will
- simply load the new objects on top of the old ones.
+ The /dmm_suite will load the map file starting on the specified z-level. If no
+ z-level was specified, world.maxz will be increased so as to fit the map. Note
+ that if you wish to load a map onto a z-level that already has objects on it,
+ you will have to handle the removal of those objects. Otherwise the new map will
+ simply load the new objects on top of the old ones.
- Also note that all type paths specified in the .dmm file must exist in the world's
- code, and that the /dmm_reader trusts that files to be loaded are in fact valid
- .dmm files. Errors in the .dmm format will cause runtime errors.
+ Also note that all type paths specified in the .dmm file must exist in the world's
+ code, and that the /dmm_reader trusts that files to be loaded are in fact valid
+ .dmm files. Errors in the .dmm format will cause runtime errors.
- */
+*/
- verb/load_map(var/dmm_file as file, var/x_offset as num, var/y_offset as num, var/z_offset as num, do_sleep as num){
- // dmm_file: A .dmm file to load (Required).
- // z_offset: A number representing the z-level on which to start loading the map (Optional).
- }
- verb/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num){
- // t1: A turf representing one corner of a three dimensional grid (Required).
- // t2: Another turf representing the other corner of the same grid (Required).
- // flags: Any, or a combination, of several bit flags (Optional, see documentation).
- }
+GLOBAL_DATUM_INIT(maploader, /datum/dmm_suite, new())
- // save_map is included as a legacy proc. Use write_map instead.
- verb/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){
- // t1: A turf representing one corner of a three dimensional grid (Required).
- // t2: Another turf representing the other corner of the same grid (Required).
- // map_name: A valid name for the map to be saved, such as "castle" (Required).
- // flags: Any, or a combination, of several bit flags (Optional, see documentation).
- }
- }
+/datum/dmm_suite
+ var/static/quote = "\""
+
+ // These regexes are global - meaning that starting the maploader again mid-load will
+ // reset progress - which means we need to track our index per-map, or we'll
+ // eternally recurse
+ // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
+ var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g")
+ // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g
+ var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g")
+ // /^[\s\n]+|[\s\n]+$/
+ var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g")
+ var/static/list/modelCache = list()
+
+ var/static/list/letter_digits = 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",
+ "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"
+ )
diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm
index 9a7cf6b6405..f034c4f39d1 100644
--- a/code/modules/awaymissions/maploader/reader.dm
+++ b/code/modules/awaymissions/maploader/reader.dm
@@ -1,23 +1,11 @@
///////////////////////////////////////////////////////////////
-//SS13 Optimized Map loader
+// SS13 Optimized Map loader
//////////////////////////////////////////////////////////////
-//As of 3.6.2016
-//global datum that will preload variables on atoms instanciation
+// As of 3.6.2016
+// global datum that will preload variables on atoms instanciation
GLOBAL_VAR_INIT(use_preloader, FALSE)
-GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
-
-/dmm_suite
- // These regexes are global - meaning that starting the maploader again mid-load will
- // reset progress - which means we need to track our index per-map, or we'll
- // eternally recurse
- // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
- var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g")
- // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g
- var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g")
- // /^[\s\n]+|[\s\n]+$/
- var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g")
- var/static/list/modelCache = list()
+GLOBAL_DATUM_INIT(_preloader, /datum/dmm_suite/preloader, new())
/**
* Construct the model map and control the loading process
@@ -37,13 +25,18 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
* atmos will attempt to start before it's ready, causing runtimes galore if init is
* allowed to romp unchecked.
*/
-/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num)
- var/tfile = dmm_file//the map file we're creating
+/datum/dmm_suite/proc/load_map(dmm_file, x_offset = 0, y_offset = 0, z_offset = 0, shouldCropMap = FALSE, measureOnly = FALSE)
+ var/tfile = dmm_file// the map file we're creating
var/fname = "Lambda"
if(isfile(tfile))
fname = "[tfile]"
+ // Make sure we dont load a dir up
+ var/lastchar = copytext(fname, -1)
+ if(lastchar == "/" || lastchar == "\\")
+ log_debug("Attempted to load map template without filename (Attempted [tfile])")
+ return
tfile = file2text(tfile)
- if(length(tfile) == 0)
+ if(!length(tfile))
throw EXCEPTION("Map path '[fname]' does not exist!")
if(!x_offset)
@@ -57,7 +50,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
var/list/grid_models = list()
var/key_len = 0
- var/dmm_suite/loaded_map/LM = new
+ var/datum/dmm_suite/loaded_map/LM = new
// This try-catch is used as a budget "Finally" clause, as the dirt count
// needs to be reset
var/watch = start_watch()
@@ -76,7 +69,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(!key_len)
key_len = length(key)
else
- throw EXCEPTION("Inconsistant key length in DMM")
+ throw EXCEPTION("Inconsistent key length in DMM")
if(!measureOnly)
grid_models[key] = dmmRegex.group[2]
@@ -86,17 +79,17 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
throw EXCEPTION("Coords before model definition in DMM")
var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1
- //position of the currently processed square
+ // position of the currently processed square
var/xcrd
var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1
var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
if(!measureOnly)
if(zcrd > world.maxz)
- if(cropMap)
+ if(shouldCropMap)
continue
else
- GLOB.space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed
+ GLOB.space_manager.increase_max_zlevel_to(zcrd) // create a new z_level if needed
bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart)
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
@@ -118,7 +111,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
ycrd += gridLines.len - 1 // Start at the top and work down
- if(!cropMap && ycrd > world.maxy)
+ if(!shouldCropMap && ycrd > world.maxy)
if(!measureOnly)
world.maxy = ycrd // Expand Y here. X is expanded in the loop below
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
@@ -133,9 +126,9 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
for(var/line in gridLines)
if(ycrd <= world.maxy && ycrd >= 1)
xcrd = xcrdStart
- for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
+ for(var/tpos = 1 to (length(line) - key_len + 1) step key_len)
if(xcrd > world.maxx)
- if(cropMap)
+ if(shouldCropMap)
break
else
world.maxx = xcrd
@@ -152,7 +145,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
maxx = max(maxx, xcrd)
++xcrd
--ycrd
- bounds[MAP_MAXX] = max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx)
+ bounds[MAP_MAXX] = max(bounds[MAP_MAXX], shouldCropMap ? min(maxx, world.maxx) : maxx)
CHECK_TICK
catch(var/exception/e)
@@ -175,8 +168,8 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(!measureOnly)
for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
var/turf/T = t
- //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
- T.AfterChange(1, keep_cabling = TRUE)
+ // we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
+ T.AfterChange(TRUE, keep_cabling = TRUE)
return bounds
/**
@@ -196,14 +189,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
* 4) Instanciates the atom with its variables
*
*/
-/dmm_suite/proc/parse_grid(model as text,xcrd as num,ycrd as num,zcrd as num, dmm_suite/loaded_map/LM)
+/datum/dmm_suite/proc/parse_grid(model = "", xcrd = 0, ycrd = 0, zcrd = 0, datum/dmm_suite/loaded_map/LM)
/*Method parse_grid()
- Accepts a text string containing a comma separated list of type paths of the
same construction as those contained in a .dmm file, and instantiates them.
*/
- var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/dangerous/explored)
- var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
+ var/list/members // will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/dangerous/explored)
+ var/list/members_attributes // will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
var/list/cached = modelCache[model]
var/index
@@ -212,7 +205,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
members_attributes = cached[2]
else
/////////////////////////////////////////////////////////
- //Constructing members and corresponding variables lists
+ // Constructing members and corresponding variables lists
////////////////////////////////////////////////////////
members = list()
@@ -223,13 +216,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
var/dpos
do
- //finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/dangerous/explored)
- dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...}
+ // finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/dangerous/explored)
+ dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") // find next delimiter (comma here) that's not within {...}
- var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp}
+ var/full_def = trim_text(copytext(model, old_position, dpos)) // full definition, e.g : /obj/foo/bar{variables=derp}
var/variables_start = findtext(full_def, "{")
var/atom_text = trim_text(copytext(full_def, 1, variables_start))
- var/atom_def = text2path(atom_text) //path definition, e.g /obj/foo/bar
+ var/atom_def = text2path(atom_text) // path definition, e.g /obj/foo/bar
old_position = dpos + 1
if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
@@ -237,14 +230,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
continue
members.Add(atom_def)
- //transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
+ // transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
var/list/fields = list()
- if(variables_start)//if there's any variable
- full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}'
+ if(variables_start) // if there's any variable
+ full_def = copytext(full_def, variables_start + 1, length(full_def)) // removing the last '}'
fields = readlist(full_def, ";")
- //then fill the members_attributes list with the corresponding variables
+ // then fill the members_attributes list with the corresponding variables
members_attributes.len++
members_attributes[index++] = fields
@@ -255,24 +248,23 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
////////////////
- //Instanciation
+ // Instanciation
////////////////
- //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
+ // The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
- //first instance the /area and remove it from the members list
+ // first instance the /area and remove it from the members list
index = members.len
- var/turf/crds = locate(xcrd,ycrd,zcrd)
+ var/turf/crds = locate(xcrd, ycrd, zcrd)
if(members[index] != /area/template_noop)
// We assume `members[index]` is an area path, as above, yes? I will operate
// on that assumption.
if(!ispath(members[index], /area))
throw EXCEPTION("Oh no, I thought this was an area!")
- var/atom/instance
- GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
- instance = LM.area_path_to_real_area(members[index])
+ GLOB._preloader.setup(members_attributes[index]) // preloader for assigning set variables on atom creation
+ var/atom/instance = LM.area_path_to_real_area(members[index])
if(crds)
instance.contents.Add(crds)
@@ -280,45 +272,46 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(GLOB.use_preloader && instance)
GLOB._preloader.load(instance)
- //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
+ // then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
var/first_turf_index = 1
- while(!ispath(members[first_turf_index],/turf)) //find first /turf object in members
+ while(!ispath(members[first_turf_index], /turf)) // find first /turf object in members
first_turf_index++
- //instanciate the first /turf
+ // instanciate the first /turf
var/turf/T
if(members[first_turf_index] != /turf/template_noop)
- T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],xcrd,ycrd,zcrd)
+ T = instance_atom(members[first_turf_index], members_attributes[first_turf_index], xcrd, ycrd, zcrd)
if(T)
- //if others /turf are presents, simulates the underlays piling effect
+ // if others /turf are presents, simulates the underlays piling effect
index = first_turf_index + 1
- while(index <= members.len - 1) // Last item is an /area
+ var/mlen = members.len - 1
+ while(index <= mlen) // Last item is an /area
var/underlay
if(istype(T, /turf)) // I blame this on the stupid clown who coded the BYOND map editor
underlay = T.appearance
- T = instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)//instance new turf
- if(ispath(members[index],/turf))
+ T = instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd) // instance new turf
+ if(ispath(members[index], /turf))
T.underlays += underlay
index++
- //finally instance all remainings objects/mobs
- for(index in 1 to first_turf_index-1)
- instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)
+ // finally instance all remainings objects/mobs
+ for(index in 1 to first_turf_index - 1)
+ instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd)
CHECK_TICK
////////////////
-//Helpers procs
+// Helpers procs
////////////////
-//Instance an atom at (x,y,z) and gives it the variables in attributes
-/dmm_suite/proc/instance_atom(path,list/attributes, x, y, z)
+// Instance an atom at (x, y, z) and gives it the variables in attributes
+/datum/dmm_suite/proc/instance_atom(path, list/attributes, x, y, z)
var/atom/instance
GLOB._preloader.setup(attributes, path)
- var/turf/T = locate(x,y,z)
+ var/turf/T = locate(x, y, z)
if(T)
if(ispath(path, /turf))
T.ChangeTurf(path, defer_change = TRUE, keep_icon = FALSE)
@@ -326,115 +319,111 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
else if(ispath(path, /area))
else
- instance = new path (T)//first preloader pass
+ instance = new path(T) // first preloader pass
- if(GLOB.use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New()
+ if(GLOB.use_preloader && instance) // second preloader pass, for those atoms that don't ..() in New()
GLOB._preloader.load(instance)
return instance
-//text trimming (both directions) helper proc
-//optionally removes quotes before and after the text (for variable name)
-/dmm_suite/proc/trim_text(what as text,trim_quotes=0)
+// text trimming (both directions) helper proc
+// optionally removes quotes before and after the text (for variable name)
+/datum/dmm_suite/proc/trim_text(what, trim_quotes = FALSE)
if(trim_quotes)
return trimQuotesRegex.Replace(what, "")
else
return trimRegex.Replace(what, "")
-
-//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape
-//returns 0 if reached the last delimiter
-/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape=quote,closing_escape=quote)
+// find the position of the next delimiter, skipping whatever is comprised between opening_escape and closing_escape
+// returns 0 if reached the last delimiter
+/datum/dmm_suite/proc/find_next_delimiter_position(text, initial_position = 0, delimiter = ",", opening_escape = quote, closing_escape = quote)
var/position = initial_position
- var/next_delimiter = findtext(text,delimiter,position,0)
- var/next_opening = findtext(text,opening_escape,position,0)
+ var/next_delimiter = findtext(text, delimiter, position, 0)
+ var/next_opening = findtext(text, opening_escape, position, 0)
while((next_opening != 0) && (next_opening < next_delimiter))
- position = findtext(text,closing_escape,next_opening + 1,0)+1
- next_delimiter = findtext(text,delimiter,position,0)
- next_opening = findtext(text,opening_escape,position,0)
+ position = findtext(text, closing_escape, next_opening + 1, 0) + 1
+ next_delimiter = findtext(text, delimiter, position, 0)
+ next_opening = findtext(text, opening_escape, position, 0)
return next_delimiter
-
-//build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
-//return the filled list
-/dmm_suite/proc/readlist(text as text, delimiter=",")
-
+// build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
+// return the filled list
+/datum/dmm_suite/proc/readlist(text, delimiter = ",")
var/list/to_return = list()
var/position
var/old_position = 1
do
- //find next delimiter that is not within "..."
- position = find_next_delimiter_position(text,old_position,delimiter)
+ // find next delimiter that is not within "..."
+ position = find_next_delimiter_position(text, old_position, delimiter)
- //check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7))
- var/equal_position = findtext(text,"=",old_position, position)
+ // check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo", var2=7))
+ var/equal_position = findtext(text, "=", old_position, position)
- var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list
+ var/trim_left = trim_text(copytext(text, old_position, (equal_position ? equal_position : position)), 1) // the name of the variable, must trim quotes to build a BYOND compliant associatives list
old_position = position + 1
- if(equal_position)//associative var, so do the association
- var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable
+ if(equal_position) // associative var, so do the association
+ var/trim_right = trim_text(copytext(text, equal_position + 1, position)) // the content of the variable
- //Check for string
+ // Check for string
// Make it read to the next delimiter, instead of the quote
- if(findtext(trim_right,quote,1,2))
- var/endquote = findtext(trim_right,quote,-1)
+ if(findtext(trim_right, quote, 1, 2))
+ var/endquote = findtext(trim_right, quote, -1)
if(!endquote)
log_runtime(EXCEPTION("Terminating quote not found!"), src)
// Our map writer escapes quotes and curly brackets to avoid
// letting our simple parser choke on meanly-crafted names/etc
// - so we decode it here so it's back to good ol' legibility
- trim_right = dmm_decode(copytext(trim_right,2,endquote))
+ trim_right = dmm_decode(copytext(trim_right, 2, endquote))
- //Check for number
+ // Check for number
else if(isnum(text2num(trim_right)))
trim_right = text2num(trim_right)
- //Check for null
+ // Check for null
else if(trim_right == "null")
trim_right = null
- //Check for list
- else if(copytext(trim_right,1,5) == "list")
- trim_right = readlist(copytext(trim_right,6,length(trim_right)))
+ // Check for list
+ else if(copytext(trim_right, 1, 5) == "list")
+ trim_right = readlist(copytext(trim_right, 6, length(trim_right)))
- //Check for file
- else if(copytext(trim_right,1,2) == "'")
- trim_right = file(copytext(trim_right,2,length(trim_right)))
+ // Check for file
+ else if(copytext(trim_right, 1, 2) == "'")
+ trim_right = file(copytext(trim_right, 2, length(trim_right)))
- //Check for path
+ // Check for path
else if(ispath(text2path(trim_right)))
trim_right = text2path(trim_right)
to_return[trim_left] = trim_right
- else//simple var
+ else// simple var
to_return[trim_left] = null
while(position != 0)
return to_return
-/dmm_suite/Destroy()
+/datum/dmm_suite/Destroy()
..()
return QDEL_HINT_HARDDEL_NOW
//////////////////
-//Preloader datum
+// Preloader datum
//////////////////
// This ain't re-entrant, but we had this before the maploader update
-/dmm_suite/preloader
- parent_type = /datum
+/datum/dmm_suite/preloader
var/list/attributes
var/target_path
var/json_ready = 0
-/dmm_suite/preloader/proc/setup(list/the_attributes, path)
+/datum/dmm_suite/preloader/proc/setup(list/the_attributes, path)
if(the_attributes.len)
json_ready = 0
if("map_json_data" in the_attributes)
@@ -443,25 +432,24 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
attributes = the_attributes
target_path = path
-/dmm_suite/preloader/proc/load(atom/what)
+/datum/dmm_suite/preloader/proc/load(atom/A)
if(json_ready)
- var/json_data = attributes["map_json_data"]
+ var/json_data = dmm_decode(attributes["map_json_data"])
attributes -= "map_json_data"
- json_data = dmm_decode(json_data)
try
- what.deserialize(json_decode(json_data))
- catch(var/exception/e)
+ A.deserialize(json_decode(json_data))
+ catch(var/exception/E)
log_runtime(EXCEPTION("Bad json data: '[json_data]'"), src)
- throw e
+ throw E
for(var/attribute in attributes)
var/value = attributes[attribute]
if(islist(value))
value = deepCopyList(value)
- what.vars[attribute] = value
+ A.vars[attribute] = value
GLOB.use_preloader = FALSE
// If the map loader fails, make this safe
-/dmm_suite/preloader/proc/reset()
+/datum/dmm_suite/preloader/proc/reset()
GLOB.use_preloader = FALSE
attributes = list()
target_path = null
@@ -470,12 +458,11 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
// so that one can have separate "unpowered" areas for ruins or whatever,
// yet have a single area type for use of mapping, instead of creating
// a new area type for each new ruin
-/dmm_suite/loaded_map
- parent_type = /datum
+/datum/dmm_suite/loaded_map
var/list/area_list = list()
var/index = 1 // To store the state of the regex
-/dmm_suite/loaded_map/proc/area_path_to_real_area(area/A)
+/datum/dmm_suite/loaded_map/proc/area_path_to_real_area(area/A)
if(!ispath(A, /area))
throw EXCEPTION("Wrong argument to `area_path_to_real_area`")
diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm
index c6d1fd589cf..b609f26c69d 100644
--- a/code/modules/awaymissions/maploader/writer.dm
+++ b/code/modules/awaymissions/maploader/writer.dm
@@ -5,28 +5,12 @@
#define DMM_IGNORE_PLAYERS 16
#define DMM_IGNORE_MOBS 24
#define DMM_USE_JSON 32
-/dmm_suite
- var/quote = "\""
- var/list/letter_digits = 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",
- "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"
- )
-/dmm_suite/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num)
- //Check for illegal characters in file name... in a cheap way.
- if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name)))
+/datum/dmm_suite/proc/save_map(turf/t1, turf/t2, map_name = "", flags = 0)
+ // Check for illegal characters in file name... in a cheap way.
+ if(!((ckeyEx(map_name) == map_name) && ckeyEx(map_name)))
CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.")
- //Check for valid turfs.
+ // Check for valid turfs.
if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.")
@@ -35,17 +19,17 @@
if(fexists(map_path))
fdel(map_path)
var/saved_map = file(map_path)
- var/map_text = write_map(t1,t2,flags,saved_map)
+ var/map_text = write_map(t1, t2, flags, saved_map)
saved_map << map_text
return saved_map
-/dmm_suite/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num)
- //Check for valid turfs.
+/datum/dmm_suite/proc/write_map(turf/t1, turf/t2, flags = 0)
+ // Check for valid turfs.
if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.")
- var/turf/ne = locate(max(t1.x,t2.x),max(t1.y,t2.y),max(t1.z,t2.z)) // Outer corner
- var/turf/sw = locate(min(t1.x,t2.x),min(t1.y,t2.y),min(t1.z,t2.z)) // Inner corner
+ var/turf/ne = locate(max(t1.x, t2.x), max(t1.y, t2.y), max(t1.z, t2.z)) // Outer corner
+ var/turf/sw = locate(min(t1.x, t2.x), min(t1.y, t2.y), min(t1.z, t2.z)) // Inner corner
var/list/templates[0]
var/list/template_buffer = list()
var/template_buffer_text
@@ -54,37 +38,40 @@
var/total_timer = start_watch()
var/timer = start_watch()
log_debug("Reading turfs...")
+
// Read the contents of all the turfs we were given
for(var/pos_z in sw.z to ne.z)
for(var/pos_y in ne.y to sw.y step -1) // We're reversing this because the map format is silly
for(var/pos_x in sw.x to ne.x)
- var/turf/test_turf = locate(pos_x,pos_y,pos_z)
+ var/turf/test_turf = locate(pos_x, pos_y, pos_z)
var/test_template = make_template(test_turf, flags)
var/template_number = templates.Find(test_template)
if(!template_number)
templates.Add(test_template)
- template_number = templates.len
+ template_number = length(templates)
template_buffer += "[template_number],"
CHECK_TICK
-
template_buffer += ";"
-
template_buffer += "."
- template_buffer_text = jointext(template_buffer,"")
+
+ template_buffer_text = jointext(template_buffer, "")
log_debug("Reading turfs took [stop_watch(timer)]s.")
- if(templates.len == 0)
+ if(length(templates) == 0)
CRASH("No templates found!")
- var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1)
- var/list/keys[templates.len]
+
+ var/key_length = round(log(length(letter_digits), length(templates) - 1) + 1) // or floor
+ var/list/keys[length(templates)]
+
// Write the list of key/model pairs to the file
timer = start_watch()
log_debug("Writing out key/model pairs to file header...")
var/list/key_models = list()
- for(var/key_pos in 1 to templates.len)
- keys[key_pos] = get_model_key(key_pos,key_length)
+ for(var/key_pos in 1 to length(templates))
+ keys[key_pos] = get_model_key(key_pos, key_length)
key_models += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n"
CHECK_TICK
+
dmm_text += jointext(key_models,"")
log_debug("Writing key/model pairs complete, took [stop_watch(timer)]s.")
@@ -92,58 +79,69 @@
// Loop over all z in our zone
timer = start_watch()
log_debug("Writing out key map...")
+
var/list/key_map = list()
- for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer_text,".",z_pos)+1)
- if(z_pos>=length(template_buffer_text)) break
- if(z_level) key_map += "\n"
+ for(var/z_pos = 1; TRUE; z_pos = findtext(template_buffer_text, ".", z_pos) + 1)
+ if(z_pos >= length(template_buffer_text))
+ break
+
+ if(z_level)
+ key_map += "\n"
+
key_map += "\n(1,1,[++z_level]) = {\"\n"
- var/z_block = copytext(template_buffer_text,z_pos,findtext(template_buffer_text,".",z_pos))
- for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1)
- if(y_pos>=length(z_block)) break
- var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos))
+ var/z_block = copytext(template_buffer_text, z_pos, findtext(template_buffer_text, ".", z_pos))
+ for(var/y_pos = 1; TRUE; y_pos = findtext(z_block, ";", y_pos) + 1)
+ if(y_pos >= length(z_block))
+ break
+
+ var/y_block = copytext(z_block, y_pos, findtext(z_block, ";", y_pos))
// A row of keys
- for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1)
- if(x_pos>=length(y_block)) break
- var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos))
+ for(var/x_pos = 1; TRUE; x_pos = findtext(y_block, ",", x_pos) + 1)
+ if(x_pos >= length(y_block))
+ break
+
+ var/x_block = copytext(y_block, x_pos, findtext(y_block, ",", x_pos))
var/key_number = text2num(x_block)
var/temp_key = keys[key_number]
key_map += temp_key
CHECK_TICK
key_map += "\n"
key_map += "\"}"
- dmm_text += jointext(key_map,"")
+
+ dmm_text += jointext(key_map, "")
log_debug("Writing key map complete, took [stop_watch(timer)]s.")
log_debug("TOTAL TIME: [stop_watch(total_timer)]s.")
+
return dmm_text
-/dmm_suite/proc/make_template(var/turf/model as turf, var/flags as num)
- var/use_json = 0
- if(flags & DMM_USE_JSON)
- use_json = 1
+/datum/dmm_suite/proc/make_template(turf/model, flags = 0)
+ var/use_json = (flags & DMM_USE_JSON) ? TRUE : FALSE
+
var/template = ""
var/turf_template = ""
var/list/obj_template = list()
var/list/mob_template = list()
var/area_template = ""
-
-
// Turf
if(!(flags & DMM_IGNORE_TURFS))
turf_template = "[model.type][check_attributes(model,use_json=use_json)],"
- else turf_template = "[world.turf],"
+ else
+ turf_template = "[world.turf],"
// Objects loop
if(!(flags & DMM_IGNORE_OBJS))
for(var/obj/O in model.contents)
if(O.dont_save || QDELETED(O))
continue
+
obj_template += "[O.type][check_attributes(O,use_json=use_json)],"
// Mobs Loop
for(var/mob/M in model.contents)
if(M.dont_save || QDELETED(M))
continue
+
if(M.client)
if(!(flags & DMM_IGNORE_PLAYERS))
mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
@@ -155,65 +153,68 @@
if(!(flags & DMM_IGNORE_AREAS))
var/area/m_area = model.loc
area_template = "[m_area.type][check_attributes(m_area,use_json=use_json)]"
- else area_template = "[world.area]"
+ else
+ area_template = "[world.area]"
template = "[jointext(obj_template,"")][jointext(mob_template,"")][turf_template][area_template]"
return template
-/dmm_suite/proc/check_attributes(var/atom/A,use_json=0)
+/datum/dmm_suite/proc/check_attributes(atom/A, use_json = FALSE)
var/attributes_text = "{"
var/list/attributes = list()
if(!use_json)
for(var/V in A.vars)
CHECK_TICK
- if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))) continue
+ if((!issaved(A.vars[V])) || (A.vars[V] == initial(A.vars[V])))
+ continue
attributes += var_to_dmm(A.vars[V], V)
else
- var/list/yeah = A.serialize()
+ var/list/to_encode = A.serialize()
// We'll want to write out vars that are important to the editor
// So that the map is legible as before
- for(var/thing in A.map_important_vars())
+ for(var/T in A.map_important_vars())
// Save vars that are important for the map editor, so that
// json-encoded maps are legible for standard editors
- if(A.vars[thing] != initial(A.vars[thing]))
- yeah -= thing
- attributes += var_to_dmm(A.vars[thing],thing)
+ if(A.vars[T] != initial(A.vars[T]))
+ to_encode -= T
+ attributes += var_to_dmm(A.vars[T], T)
// Remove useless info
- yeah -= "type"
- if(yeah.len)
- var/json_stuff = json_encode(yeah)
+ to_encode -= "type"
+ if(length(to_encode))
+ var/json_stuff = json_encode(to_encode)
attributes += var_to_dmm(json_stuff, "map_json_data")
- if(attributes.len == 0)
+
+ if(length(attributes) == 0)
return
// Trim a trailing semicolon - `var_to_dmm` always appends a semicolon,
// so the last one will be trailing.
- if(copytext(attributes_text, length(attributes_text)-1, 0) == "; ")
- attributes_text = copytext(attributes_text, 1, length(attributes_text)-1)
+ if(copytext(attributes_text, length(attributes_text) - 1, 0) == "; ")
+ attributes_text = copytext(attributes_text, 1, length(attributes_text) - 1)
+
attributes_text = "{[jointext(attributes,"; ")]}"
return attributes_text
-
-/dmm_suite/proc/get_model_key(var/which as num, var/key_length as num)
+/datum/dmm_suite/proc/get_model_key(which, key_length)
var/list/key = list()
- var/working_digit = which-1
+ var/working_digit = which - 1
for(var/digit_pos in key_length to 1 step -1)
- var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1)))
- working_digit-=place_value*(letter_digits.len**(digit_pos-1))
- key += letter_digits[place_value+1]
+ var/place_value = round/*floor*/(working_digit / (length(letter_digits) ** (digit_pos - 1)))
+ working_digit -= place_value * (length(letter_digits) ** (digit_pos - 1))
+ key += letter_digits[place_value + 1]
+
return jointext(key,"")
-
-/dmm_suite/proc/var_to_dmm(attr, name)
+/datum/dmm_suite/proc/var_to_dmm(attr, name)
if(istext(attr))
// dmm_encode will strip out characters that would be capable of disrupting
// parsing - namely, quotes and curly braces
return "[name] = \"[dmm_encode(attr)]\""
- else if(isnum(attr)||ispath(attr))
+ else if(isnum(attr) || ispath(attr))
return "[name] = [attr]"
- else if(isicon(attr)||isfile(attr))
+ else if(isicon(attr) || isfile(attr))
if(length("[attr]") == 0)
// The DM map reader is unable to read files that have a '' file/icon entry
return
diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm
index 7cddddd8b08..65c64edd83f 100644
--- a/code/modules/awaymissions/mission_code/academy.dm
+++ b/code/modules/awaymissions/mission_code/academy.dm
@@ -193,7 +193,7 @@
servant_mind.objectives += O
servant_mind.transfer_to(H)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 300)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
index c5b56805213..75fbbf698e6 100644
--- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
+++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
@@ -4,10 +4,18 @@
name = "Mission Briefing"
info = "To the Magnificent Z.A.P. A small mining base has been created within our territory by wandless scum. Send them a message from the wizard federation they will not forget. I know your kind is rather fragile, but a group of lightly armed miners should not pose any threat to you at all. Just be warned they have a security cyborg for self defence, you might want to tune your spells to that threat. I look forward to hearing of your success. Grand Magus Abra the Wonderous"
+/obj/item/spellbook/oneuse/emp
+ spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech
+ spellname = "Disable Technology"
+ icon_state = "bookcharge" //it's a lightning bolt, seems appropriate enough
+ desc = "For the tech-hating wizard on the go."
+
+/obj/item/spellbook/oneuse/emp/used
+ used = TRUE //spawns used
/obj/effect/spawner/lootdrop/wizardcrash
loot = list(
- /obj/item/guardiancreator = 1, //jackpot.
+ /obj/item/guardiancreator = 1, //jackpot.
/obj/item/spellbook/oneuse/knock = 1, //tresspassing charges incoming
/obj/item/gun/magic/wand/resurrection = 1, //medbay's best friend
/obj/item/spellbook/oneuse/charge = 20, //and now for less useful stuff to dilute the good loot chances
diff --git a/code/modules/busy_space/air_traffic.dm b/code/modules/busy_space/air_traffic.dm
deleted file mode 100644
index e21414e8ed3..00000000000
--- a/code/modules/busy_space/air_traffic.dm
+++ /dev/null
@@ -1,128 +0,0 @@
-//Cactus, Speedbird, Dynasty, oh my
-GLOBAL_DATUM_INIT(atc, /datum/lore/atc_controller, new)
-
-/datum/lore/atc_controller
- var/delay_max = 10 MINUTES //Maximum amount of tiem between ATC messages. Default is 10 mins.
- var/delay_min = 5 MINUTES //Minimum amount of time between ATC messages. Default is 5 mins.
- var/backoff_delay = 5 MINUTES //How long to back off if we can't talk and want to. Default is 5 mins.
- var/next_message //When the next message should happen in world.time
- var/force_chatter_type //Force a specific type of messages
-
- var/squelched = FALSE //If ATC is squelched currently
-
-/datum/lore/atc_controller/New()
- spawn(30 SECONDS) //Lots of lag at the start of a shift.
- msg("New shift beginning, resuming traffic control.")
- next_message = world.time + rand(delay_min, delay_max)
- process()
-
-/datum/lore/atc_controller/process()
- if(world.time >= next_message)
- if(squelched)
- next_message = world.time + backoff_delay
- else
- next_message = world.time + rand(delay_min,delay_max)
- random_convo()
-
- spawn(1 MINUTES) //We don't really need high-accuracy here.
- process()
-
-/datum/lore/atc_controller/proc/msg(var/message,var/sender)
- ASSERT(message)
- GLOB.global_announcer.autosay("[message]", sender ? sender : "[GLOB.using_map.station_short] Space Control")
-
-/datum/lore/atc_controller/proc/reroute_traffic(var/yes = 1)
- if(yes)
- if(!squelched)
- msg("Rerouting traffic away from [GLOB.using_map.station_name].")
- squelched = TRUE
- else
- if(squelched)
- msg("Resuming normal traffic routing around [GLOB.using_map.station_name].")
- squelched = FALSE
-
-/datum/lore/atc_controller/proc/shift_ending(var/evac = 0)
- msg("Automated Shuttle departing [GLOB.using_map.station_name] for [GLOB.using_map.dock_name] on routine transfer route.", "NT Automated Shuttle")
- sleep(5 SECONDS)
- msg("Automated Shuttle, cleared to complete routine transfer from [GLOB.using_map.station_name] to [GLOB.using_map.dock_name].")
-
-/datum/lore/atc_controller/proc/random_convo()
- var/one = pick(GLOB.loremaster.organizations) //These will pick an index, not an instance
- var/two = pick(GLOB.loremaster.organizations)
-
- var/datum/lore/organization/source = GLOB.loremaster.organizations[one] //Resolve to the instances
- var/datum/lore/organization/dest = GLOB.loremaster.organizations[two]
-
- //Let's get some mission parameters
- var/owner = source.short_name //Use the short name
- var/prefix = pick(source.ship_prefixes) //Pick a random prefix
- var/mission = source.ship_prefixes[prefix] //The value of the prefix is the mission type that prefix does
- var/shipname = pick(source.ship_names) //Pick a random ship name to go with it
- var/destname = pick(dest.destination_names) //Pick a random holding from the destination
-
- var/combined_name = "[owner] [prefix] [shipname]"
- var/alt_atc_names = list("[GLOB.using_map.station_short] TraCon", "[GLOB.using_map.station_short] Control", "[GLOB.using_map.station_short] STC", "[GLOB.using_map.station_short] Airspace")
- var/wrong_atc_names = list("Sol Command", "Orion Control", "[GLOB.using_map.dock_name]")
- var/mission_noun = list("flight", "mission", "route")
- var/request_verb = list("requesting", "calling for", "asking for")
-
- //First response is 'yes', second is 'no'
- var/requests = list("[GLOB.using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"),
- "planetary flight rules" = list("cleared planetary flight rules", "unable to approve planetary flight rules due to traffic"),
- "special flight rules" = list("cleared special flight rules", "unable to approve special flight rules for your traffic class"),
- "current solar weather info" = list("sending you the relevant information via tightbeam", "cannot fulfill your request at the moment"),
- "nearby traffic info" = list("sending you current traffic info", "no known traffic for your flight plan route"),
- "remote telemetry data" = list("sending telemetry now", "no uplink from your ship, recheck your uplink and ask again"),
- "refueling information" = list("sending refueling information now", "no fuel for your ship class in this sector"),
- "a current system time sync" = list("sending time sync ping to you now", "your ship isn't compatible with our time sync, set time manually"),
- "current system starcharts" = list("transmitting current starcharts", "request on standby due to demand"),
- "permission to engage FTL" = list("cleared to FTL, good day", "hold position, traffic crossing"),
- "permission to transit system" = list("cleared to transit, good day", "hold position, traffic crossing"),
- "permission to depart system" = list("cleared to leave via flight plan route, good day", "hold position, traffic crossing"),
- "permission to enter system" = list("good day, cleared in as published", "hold position, traffic crossing"),
- )
-
- //Random chance things for variety
- var/chatter_type = "normal"
- if(force_chatter_type)
- chatter_type = force_chatter_type
- else
- chatter_type = pick(2;"emerg",5;"wrong_freq","normal") //Be nice to have wrong_lang...
-
- var/yes = prob(90) //Chance for them to say yes vs no
-
- var/request = pick(requests)
- var/callname = pick(alt_atc_names)
- var/response = requests[request][yes ? 1 : 2] //1 is yes, 2 is no
-
- var/full_request
- var/full_response
- var/full_closure
-
- switch(chatter_type)
- if("wrong_freq")
- callname = pick(wrong_atc_names)
- full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]."
- full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]."
- full_closure = "[GLOB.using_map.station_short] TraCon, copy, apologies."
- if("wrong_lang")
- //Can't implement this until autosay has language support
- if("emerg")
- var/problem = pick("hull breaches on multiple decks","unknown life forms on board","a drive about to go critical","asteroids impacting the hull","a total loss of engine power","people trying to board the ship")
- full_request = "Mayday, mayday, mayday, this is [combined_name] declaring an emergency! We have [problem]!"
- var/rand_freq = "[rand(700,999)].[rand(1,9)]"
- full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]."
- full_closure = "Roger, [GLOB.using_map.station_short] TraCon, contacting [rand_freq]."
- else
- full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]."
- full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon
- full_closure = "[GLOB.using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong
-
- //Ship sends request to ATC
- msg(full_request,"[prefix] [shipname]")
- sleep(5 SECONDS)
- //ATC sends response to ship
- msg(full_response)
- sleep(5 SECONDS)
- //Ship sends response to ATC
- msg(full_closure,"[prefix] [shipname]")
diff --git a/code/modules/busy_space/loremaster.dm b/code/modules/busy_space/loremaster.dm
deleted file mode 100644
index 77b8a8ed53d..00000000000
--- a/code/modules/busy_space/loremaster.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-//I AM THE LOREMASTER, ARE YOU THE GATEKEEPER?
-GLOBAL_DATUM_INIT(loremaster, /datum/lore/loremaster, new)
-
-/datum/lore/loremaster
- var/list/organizations = list()
-
-/datum/lore/loremaster/New()
-
- var/list/paths = typesof(/datum/lore/organization) - /datum/lore/organization
- for(var/path in paths)
- // Some intermediate paths are not real organizations (ex. /datum/lore/organization/mil). Only do ones with names
- var/datum/lore/organization/instance = path
- if(initial(instance.name))
- instance = new path()
- organizations[path] = instance
diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm
deleted file mode 100644
index c39e92ea49e..00000000000
--- a/code/modules/busy_space/organizations.dm
+++ /dev/null
@@ -1,549 +0,0 @@
-//Datums for different companies that can be used by busy_space
-/datum/lore/organization
- var/name = "" // Organization's name
- var/short_name = "" // Organization's shortname (Nanotrasen for "Nanotrasen Incorporated")
- var/acronym = "" // Organization's acronym, e.g. 'NT' for Nanotrasen'.
- var/desc = "" // One or two paragraph description of the organization, but only current stuff. Currently unused.
- var/history = "" // Historical description of the organization's origins Currently unused.
- var/work = "" // Short description of their work, eg "an arms manufacturer"
- var/headquarters = "" // Location of the organization's HQ. Currently unused.
- var/motto = "" // A motto/jingle/whatever, if they have one. Currently unused.
-
- var/list/ship_prefixes = list() //Some might have more than one! Like Nanotrasen. Value is the mission they perform, e.g. ("ABC" = "mission desc")
- var/list/ship_names = list( //Names of spaceships. This is a mostly generic list that all the other organizations inherit from if they don't have anything better.
- "Kestrel",
- "Beacon",
- "Signal",
- "Freedom",
- "Glory",
- "Axiom",
- "Eternal",
- "Icarus",
- "Harmony",
- "Light",
- "Discovery",
- "Endeavour",
- "Explorer",
- "Swift",
- "Dragonfly",
- "Ascendant",
- "Tenacious",
- "Pioneer",
- "Hawk",
- "Haste",
- "Radiant",
- "Luminous",
- "Gallant",
- "Dependable",
- "Indomitable",
- "Guardian",
- "Resolution",
- "Fearless",
- "Amazon",
- "Relentless",
- "Inspire",
- "Implacable",
- "Steadfast",
- "Leviathan",
- "Dauntless",
- "Adroit",
- "Mistral",
- "Typhoon",
- "Titan",
- "Kupua",
- "Alchemist",
- "Cuirass",
- "Citadel",
- "Rondelle",
- "Camail",
- "Ocrea",
- "Ram",
- "Crest",
- "Tanko",
- "Pommel",
- "Kissaki",
- "Cavalier",
- "Anelace",
- "Flint",
- "Xiphos",
- "Parrot",
- "Chamber",
- "Annellet",
- "Cestus",
- "Talwar")
- var/list/destination_names = list() //Names of static holdings that the organization's ships visit regularly.
- var/autogenerate_destination_names = TRUE
-
-/datum/lore/organization/New()
- ..()
- if(autogenerate_destination_names) // Lets pad out the destination names.
- var/i = rand(6, 10)
- var/list/star_names = list(
- "Sol", "Alpha Centauri", "Sirius", "Vega", "Regulus", "Vir", "Algol", "Aldebaran",
- "Delta Doradus", "Menkar", "Geminga", "Elnath", "Gienah", "Mu Leporis", "Nyx", "Tau Ceti",
- "Wazn", "Alphard", "Phact", "Altair", "Mauna", "Jargon", "Xarxis", "Hestia", "Dalstis", "Cygni", "Haverick", "Corvus", "Sancere", "Cydoni", "Kaliban", "Midway", "Dansik", "Branwyn")
- var/list/destination_types = list("dockyard", "station", "vessel", "waystation", "telecommunications satellite", "spaceport", "distress beacon", "anomaly", "colony", "outpost")
- while(i)
- destination_names.Add("a [pick(destination_types)] in [pick(star_names)]")
- i--
-
-//////////////////////////////////////////////////////////////////////////////////
-
-// TSCs
-/datum/lore/organization/tsc/nanotrasen
- name = "Nanotrasen Incorporated"
- short_name = "Nanotrasen"
- acronym = "NT"
- desc = "The largest shareholder in the galactic plasma markets, Nanotrasen is a research and mining corporation which specializes in\
- FTL technologies and weapon systems. Frowned upon by most governments due to their shady business tactics and poor ethics record,\
- Nanotrasen is often seen as a necessary evil for maintaining access to the often volatile plasma market. Nanotrasen was originally\
- incorporated on Earth with their headquarters situated on Mars, however they have recently moved most of their operations to the Epsilon Eridani sector."
- history = "" // To be written someday.
- work = "research giant"
- headquarters = "Mars"
- motto = ""
-
- ship_prefixes = list("NSV" = "exploration", "NTV" = "hauling", "NDV" = "patrol", "NRV" = "emergency response")
- // Note that the current station being used will be pruned from this list upon being instantiated
- destination_names = list(
- "NAS Trurl in Epsilon Eridani",
- "NAS Crescent in Tau Ceti",
- "NSS Exodus in Tau Ceti",
- "NSS Antiqua in Darsing",
- "NRS Orion in Sol",
- "NSS Vector in Omicron Ceti",
- "NBS Anansi in Omicron Ceti",
- "NSS Redemption in Sirius",
- "NDS Inferno in Tau Ceti",
- "NAB Smythside Central Headquarters on Earth",
- "NAB North Cimmeria Central Offices on Mars",
- )
-
-/datum/lore/organization/tsc/nanotrasen/New()
- ..()
- spawn(1) // BYOND shenanigans means using_map is not initialized yet. Wait a tick.
- // Get rid of the current map from the list, so ships flying in don't say they're coming to the current map.
- var/string_to_test = "[GLOB.using_map.station_name] in [GLOB.using_map.starsys_name]"
- if(string_to_test in destination_names)
- destination_names.Remove(string_to_test)
-
-
-/datum/lore/organization/tsc/donk
- name = "Donk Corporation"
- short_name = "Donk Co."
- acronym = "DC"
- desc = "The infamous rival of the well-known Waffle Corporation, Donk Co. is a company specializing in food delivery systems and brand-name food\
- products such as Donk Pockets. While generally seen as a neutral actor, Donk Corporation has been known to work both with Nanotrasen and\
- the Syndicate when it suits them - often acting as the primary logistical supplier for the Epsilon Eridani sector.\
- Donk Corporation is better known for recent high-profile litigation alleging that their food products are used for illicit drug distribution.\
- While the trial is ongoing, it has been repeatedly delayed due to incidents of methamphetamine poisoning."
- history = ""
- work = "food company that establishes and maintains delivery supply chains"
- headquarters = ""
- motto = "Now with 20% more donk!"
-
- ship_prefixes = list("D-Co." = "transportation")
- destination_names = list()
-
-/datum/lore/organization/tsc/hephaestus
- name = "Hephaestus Industries"
- short_name = "Hephaestus"
- acronym = "HI"
- desc = ""
- history = ""
- work = "arms manufacturer"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("HTV" = "freight", "HTV" = "munitions resupply")
- destination_names = list(
- "a SolGov dockyard on Luna"
- )
-
-/datum/lore/organization/tsc/waffle
- name = "Waffle Corporation"
- short_name = "Waffle Co."
- acronym = "WC"
- desc = "The once prominent competitor of Donk Corporation, Waffle Co. is well-known for its popular line of Waffle Co.\
- brand waffles and their use of violent tactics against competitors - often bribing, extorting, blackmailing or sabotaging businesses\
- that pose a direct or indirect threat to their market share. They have recently fallen on hard times primarily due to to\
- severe mismanagement which lead to much of their private arsenal being swindled by a pirate faction known as the Gorlex Marauders.\
- Waffle Co. commonly engages in smear campaigns against Donk Co., maintaining that the original recipe for Donk Pockets was stolen from them."
- history = ""
- work = "food logistics and marketing firm"
- headquarters = ""
- motto = "Now that's a Waffle Co. Waffle!"
-
- ship_prefixes = list("W-Co." = "transportation")
- destination_names = list()
-
-
-/datum/lore/organization/tsc/einstein
- name = "Einstein Engines Incorporated"
- short_name = "Einstein Inc."
- acronym = "EEI"
- desc = "An Engineering firm specializing in alternative fuel-technologies for FTL travel,\
- Einstein Engines is an up and coming player in the galactic FTL and energy markets.\
- As their research into alternative FTL fuel threatens both Nanotrasen's relative stranglehold on plasma as well as The Syndicate's vested\
- interest in the market, they are often the target of industrial sabotage by both Nanotrasen and The Syndicate.\
- Most of their contracts are based outside of the Epsilon Eridani sector, and they are frequently commissioned by smaller firms to retrofit new\
- and existing colonies, space stations, and outposts."
- history = ""
- work = "engineering firm specializing in engine technology"
- headquarters = "Jargon 4"
- motto = ""
-
- ship_prefixes = list("EE-T" = "transportation")
- destination_names = list()
-
-/datum/lore/organization/tsc/zeng_hu
- name = "Zeng-Hu pharmaceuticals"
- short_name = "Zeng-Hu"
- acronym = "ZH"
- desc = ""
- history = ""
- work = "pharmaceuticals company"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("ZTV" = "transportation", "ZMV" = "medical resupply")
- destination_names = list()
-
-/datum/lore/organization/tsc/biotech
- name = "Biotech Solutions"
- short_name = "Biotech"
- acronym = "BTS"
- desc = "A company specializing in the field of synthetic biology, BioTech solutions is at the forefront of providing cutting-edge prosthetics,\
- augmentations, and gene-therapy solutions. Their extensive list of patents and the highly secretive nature of their work often puts them at odds\
- with companies such as Nanotrasen, who commonly reverse-engineer their products. BioTech Solutions is often the victim of industrial sabotage by\
- Cybersun Industries and often relies on planetary governments for asset protection. BioTech Solutions also owns a number of prominent subsidiaries,\
- such as Bishop Cybernetics, Hesphiastos Industries, and Xion Manufacturing Group."
- history = ""
- work = "medical company specializing in prosthetics and pharmaceuticals"
- headquarters = "Xarxis 5"
- motto = ""
-
- ship_prefixes = list("CIND-T" = "transportation")
- destination_names = list()
-
-/datum/lore/organization/tsc/ward_takahashi
- name = "Ward-Takahashi General Manufacturing Conglomerate"
- short_name = "Ward-Takahashi"
- acronym = "WT"
- desc = ""
- history = ""
- work = "electronics manufacturer"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("WTV" = "freight")
- destination_names = list(
- ""
- )
-
-/datum/lore/organization/tsc/cybersun
- name = "Cybersun Industries"
- short_name = "Cybersun Ind."
- acronym = "CI"
- desc = "Cybersun Industries is a biotechnology company that primarily specializes on the research and development of human-enhancing augmentations.\
- They are better known for their aggressive corporate tactics and are known to often subsidize pirate bands to commit acts of industrial sabotage.\
- Cybersun Industries is usually the target of conspiracy theorists due to their development of the first mindslave implant, as well as their open financing of,\
- and involvement in, The Syndicate. They are one of Nanotrasen's largest detractors, and a direct competitor to BioTech Solutions."
- history = ""
- work = "RND company specializing in augmentations and implants."
- headquarters = "Luna"
- motto = ""
-
- ship_prefixes = list("CIND-T" = "transportation")
- destination_names = list()
-
-/datum/lore/organization/tsc/bishop
- name = "Bishop Cybernetics"
- short_name = "Bishop"
- acronym = "BC"
- desc = ""
- history = ""
- work = "cybernetics and augmentation manufacturer"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("BTV" = "transportation")
- destination_names = list()
-
-/datum/lore/organization/tsc/morpheus
- name = "Morpheus Cyberkinetics"
- short_name = "Morpheus"
- acronym = "MC"
- desc = "The only large corporation run by positronic intelligences, Morpheus caters almost exclusively to their sensibilities \
- and needs. A product of the synthetic colony of Shelf, Morpheus eschews traditional advertising to keep their prices low and \
- relied on word of mouth among positronics to reach their current economic dominance. Morpheus in exchange lobbies heavily for \
- positronic rights, sponsors positronics through their Jans-Fhriede test, and tends to other positronic concerns to earn them \
- the good-will of the positronics, and the ire of those who wish to exploit them."
- history = ""
- work = "cybernetics manufacturer"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("MTV" = "freight")
- // Culture names, because Anewbe told me so.
- ship_names = list(
- "Nervous Energy",
- "Prosthetic Conscience",
- "Revisionist",
- "Trade Surplus",
- "Flexible Demeanour",
- "Just Read The Instructions",
- "Limiting Factor",
- "Cargo Cult",
- "Gunboat Diplomat",
- "A Ship With A View",
- "Cantankerous",
- "Never Talk To Strangers",
- "Sacrificial Victim",
- "Unwitting Accomplice",
- "Bad For Business",
- "Just Testing",
- "Yawning Angel",
- "Liveware Problem",
- "Very Little Gravitas Indeed",
- "Zero Gravitas",
- "Gravitas Free Zone",
- "Absolutely No You-Know-What",
- "Existence Is Pain",
- "Screw Loose",
- "Limiting Factor",
- "So Much For Subtley",
- "Unfortunate Conflict Of Evidence",
- "Prime Mover",
- "Reasonable Excuse",
- "Honest Mistake",
- "Appeal To Reason",
- "My First Ship II",
- "Hidden Income",
- "Anything Legal Considered",
- "New Toy",
- "Me, I'm Always Counting",
- "Great White Snark",
- "No Shirt No Shoes",
- "Callsign"
- )
- destination_names = list(
- "a dockyard on New Canaan"
- )
-
-/datum/lore/organization/tsc/xion
- name = "Xion Manufacturing Group"
- short_name = "Xion"
- desc = ""
- history = ""
- work = "industrial equipment manufacturer"
- headquarters = ""
- motto = ""
-
- ship_prefixes = list("XTV" = "hauling")
- destination_names = list()
-
-/datum/lore/organization/tsc/shellguard
- name = "Shellguard Munitions"
- short_name = "Shellguard"
- acronym = "SM"
- desc = "The brainchild of a colonial war veteran, Shellguard Munitions is an arms manufacturer and private military contractor specializing\
- in anti-synthetic weapon systems and platforms. Initially a smaller private military force only serving frontier colonies,\
- Shellguard Munitions has become a household name due to its involvement in resolving the Haverick AI crisis in 2552.\
- Using its recently earned fame, the company has made a successful foray into the market of robotics and is highly regarded for the toughness \
- and reliability of their hardware. Despite being frequently contracted by the Trans-Solar Federation, Shellguard Munitions is known to\
- sell their services to the highest corporate bidder."
- history = ""
- work = "anti-synthetic arms manufacturer and PMC"
- headquarters = "Colony of Haverick"
- motto = ""
-
- ship_prefixes = list("BTS-T" = "transportation")
- destination_names = list()
-
-// Governments
-
-
-/datum/lore/organization/gov/solgov
- name = "Trans-Solar Federation"
- short_name = "SolGov"
- acronym = "TSF"
- desc = "Colloquially known as SolGov, the TSF is an authoritarian republic that manages the areas in and around the Sol system.\
- Despite being a highly militant organization headed by the government of Earth,\
- SolGov is usually conservative with its power and mostly serves as a mediator and peacekeeper in galactic affairs."
- history = "" // Todo
- work = "governing polity of humanity's Confederation"
- headquarters = "Earth"
- motto = ""
- autogenerate_destination_names = TRUE
-
- ship_prefixes = list("FTV" = "transporation", "FDV" = "diplomatic", "FSF" = "freight", "FIV" = "interception", "FDV" = "defense", "FCV-A" = "carrier", "FBB" = "battleship")
- destination_names = list(
- "Venus",
- "Earth",
- "Luna",
- "Mars",
- "Titan",
- "Ahdomai",
- "Kelune",
- "Dalstadt",
- "New Canaan",
- "Jargon 4",
- "Hoorlm",
- "Xarxis 5",
- "Aurum",
- "Moghes",
- "Haverick",
- "Darsing",
- "Norfolk",
- "Boron",
- "Iluk")
-
-/datum/lore/organization/gov/tajara
- name = "The Alchemist's Council"
- short_name = "The Council"
- acronym = "AC"
- desc = "The Alchemist's Council is a science-oriented organization of scholars, researchers, and entrepreneurs. \
- Though dedicated to industrializing the Tajaran world of Ahdomai, it is seen as one of the few remaining centralized powers of the Tajara peoples \
- due to the collapse of Ahdomai's provisional government."
- history = "" // Todo
- work = "science body that oversees Tajara economic and research policy"
- headquarters = "Ahdomai"
- motto = ""
- autogenerate_destination_names = TRUE
-
- ship_prefixes = list("ACS" = "transportation", "ADV" = "diplomatic", "ACF" = "freight")
- destination_names = list(
- "Ahdomai",
- "Iluk")
-
-
-/datum/lore/organization/gov/vulp
- name = "The Assembly"
- short_name = "Assembly"
- acronym = "ASB"
- desc = "A unifying body created to stave off extinction from a solar event,\
- The Assembly is the loose federal coalition of the Vulpkanin. It holds little centralized authority and mostly serves as a diplomatic body,\
- primarily concerned with facilitating trade between Vulpkanin colonies and Nanotrasen."
- history = "" // Todo
- work = "governing body of the Vulpakanin"
- headquarters = "Kelune and Dalstadt"
- motto = ""
- autogenerate_destination_names = TRUE
-
- ship_prefixes = list("ATV" = "transportation", "ADV" = "diplomatic", "ACF" = "freight")
- destination_names = list(
- "Kelune",
- "Dalstadt",
- "New Canaan")
-
-/datum/lore/organization/gov/synth
- name = "Synthetic Union"
- short_name = "Synthetica"
- acronym = "SYN"
- desc = "A defensive coalition of synthetics based out of New Canaan,\
- the Synthetic Union is an organization which aims to establish and consolidate synthetic rights across the galaxy.\
- Despite its synth oriented focus, the Synthetic Union has cordial relations with most governing bodies."
- history = "" // Todo
- work = "Union of Machines"
- headquarters = "New Canaan"
- motto = ""
- autogenerate_destination_names = TRUE
-
- ship_prefixes = list("01" = "transportation", "10" = "diplomatic", "112" = "freight")//copyed from solgov until new ones can be thought of
- destination_names = list(
- "Luna",
- "Dalstadt",
- "New Canaan",
- "Jargon 4",
- "Haverick",
- "Darsing",
- "Norfolk")
-
-/datum/lore/organization/gov/grey
- name = "The Technocracy"
- short_name = "Technocracy"
- acronym = "AYY"
- desc = "The Technocracy is a science council that operates based off the principles of a meritocracy.\
- The organization's leadership is highly competitive, and is headed by the most psionically gifted members of the Grey species.\
- The Technocracy, though enigmatic in its dealings, has cordial relations with almost all other galactic bodies."
- history = "" // Todo
- work = "Grey Council"
- headquarters = ""
- motto = ""
- autogenerate_destination_names = TRUE
-
- ship_prefixes = list("TC-T" = "transportation", "TC-D" = "diplomatic", "TC-F" = "freight")
- destination_names = list(
- "Venus",
- "Earth",
- "Luna",
- "Mars",
- "Titan",
- "Ahdomai",
- "Kelune",
- "Dalstadt",
- "New Canaan",
- "Jargon 4",
- "Hoorlm",
- "Xarxis 5",
- "Aurum",
- "Moghes",
- "Haverick",
- "Darsing",
- "Norfolk",
- "Boron",
- "Iluk")
-
-/datum/lore/organization/gov/vox
- name = "The Shoal"
- short_name = "Shoal"
- acronym = "SHA"
- desc = "The Shoal is the primary ark ship of the reclusive Vox species.\
- Little is known about The Shoal's political structure as Vox typically shy away from diplomatic engagements.\
- Subsequently, it is considered a politically neutral entity in galactic affairs by most governments."
- history = "" // Todo
- work = "Traders"
- headquarters = "Shoal"
- motto = ""
- autogenerate_destination_names = FALSE
-
- ship_prefixes = list("Legitimate Transport" = "transportation", "Legitimate Trader" = "freight", "Legitimate Diplomatic Vessel" = "raider")
- destination_names = list(
- "Ahdomai",
- "Kelune",
- "Dalstadt",
- "New Canaan",
- "Jargon 4",
- "Hoorlm",
- "Xarxis 5",
- "Aurum",
- "Moghes",
- "Haverick",
- "Darsing")
-
-/datum/lore/organization/tsc/skrell
- name = "Skrellian Central Authority"
- short_name = "Skrellian CA."
- acronym = "SCA"
- desc = "The primary governing body of the Skrellian homeworld of Jargon 4,\
- the SCA oversees all foreign and domestic policy for the Skrell and their colonies. The Skrellian Central Authority is better known for its\
- active role in the largest military alliance in the galaxy, the Human-Skrellian Alliance."
- history = ""
- work = "oversees Skrell worlds"
- headquarters = "Jargon 4"
- motto = ""
-
- ship_prefixes = list("SCA-V." = "transportation", "SCA-F" = "freight", "HSA-D" = "diplomatic")
- destination_names = list(
- "Venus",
- "Earth",
- "Luna",
- "Mars",
- "Titan",
- "Aurumn",
- "Jargon 4",
- "Xarxis 5",
- "Haverick",
- "Darsing",
- "Norfolk")
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 3cd6458afdf..72537f8ccc3 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -59,10 +59,6 @@
var/karma = 0
var/karma_spent = 0
var/karma_tab = 0
- /////////////////////////////////////////////
- // /vg/: MEDIAAAAAAAA
- // Set on login.
- var/datum/media_manager/media = null
var/topic_debugging = 0 //if set to true, allows client to see nanoUI errors -- yes i realize this is messy but it'll make live testing infinitely easier
@@ -85,10 +81,10 @@
// If set to true, this client can interact with atoms such as buttons and doors on top of regular machinery interaction
var/advanced_admin_interaction = FALSE
- // Has the client been varedited by an admin? [Inherits from datum now]
- // var/var_edited = FALSE
-
var/client_keysend_amount = 0
var/next_keysend_reset = 0
var/next_keysend_trip_reset = 0
var/keysend_tripped = FALSE
+
+ // Last world.time that the player tried to request their resources.
+ var/last_ui_resource_send = 0
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 561b0b48149..ae503be111b 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -432,7 +432,13 @@
//////////////
//DISCONNECT//
//////////////
+
/client/Del()
+ if(!gc_destroyed)
+ Destroy() //Clean up signals and timers.
+ return ..()
+
+/client/Destroy()
if(holder)
holder.owner = null
GLOB.admins -= src
@@ -442,7 +448,8 @@
movingmob.client_mobs_in_contents -= mob
UNSETEMPTY(movingmob.client_mobs_in_contents)
Master.UpdateTickRate()
- return ..()
+ ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
+ return QDEL_HINT_HARDDEL_NOW
/client/proc/donator_check()
@@ -556,7 +563,7 @@
if(GLOB.panic_bunker_enabled)
var/threshold = config.panic_bunker_threshold
src << "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later."
- del(src)
+ qdel(src)
return // Dont insert or they can just go in again
var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')")
@@ -906,3 +913,35 @@
return TRUE
#undef SSD_WARNING_TIMER
+
+/client/verb/resend_ui_resources()
+ set name = "Reload UI Resources"
+ set desc = "Reload your UI assets if they are not working"
+ set category = "Special Verbs"
+
+ if(last_ui_resource_send > world.time)
+ to_chat(usr, "You requested your UI resource files too quickly. Please try again in [(last_ui_resource_send - world.time)/10] seconds.")
+ return
+
+ var/choice = alert(usr, "This will reload your NanoUI and TGUI resources. If you have any open UIs this may break them. Are you sure?", "Resource Reloading", "Yes", "No")
+ if(choice == "Yes")
+ // 600 deciseconds = 1 minute
+ last_ui_resource_send = world.time + 60 SECONDS
+
+ // Close their open UIs
+ SSnanoui.close_user_uis(usr)
+ SStgui.close_user_uis(usr)
+
+ // Resend the resources
+ var/datum/asset/nano_assets = get_asset_datum(/datum/asset/nanoui)
+ nano_assets.register()
+
+ var/datum/asset/tgui_assets = get_asset_datum(/datum/asset/simple/tgui)
+ tgui_assets.register()
+
+ // Clear the user's cache so they get resent.
+ // This is not fully clearing their BYOND cache, just their assets sent from the server this round
+ cache = list()
+
+ to_chat(usr, "UI resource files resent successfully. If you are still having issues, please try manually clearing your BYOND cache. This can be achieved by opening your BYOND launcher, pressing the cog in the top right, selecting preferences, going to the Games tab, and pressing 'Clear Cache'.")
+
diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm
index 1091b38a11c..f1f0e2082de 100644
--- a/code/modules/client/preference/loadout/loadout.dm
+++ b/code/modules/client/preference/loadout/loadout.dm
@@ -9,39 +9,6 @@ GLOBAL_LIST_EMPTY(gear_datums)
category = cat
..()
-/hook/startup/proc/populate_gear_list()
- //create a list of gear datums to sort
- for(var/geartype in subtypesof(/datum/gear))
- var/datum/gear/G = geartype
-
- var/use_name = initial(G.display_name)
- var/use_category = initial(G.sort_category)
-
- if(G == initial(G.subtype_path))
- continue
-
- if(!use_name)
- error("Loadout - Missing display name: [G]")
- continue
- if(!initial(G.cost))
- error("Loadout - Missing cost: [G]")
- continue
- if(!initial(G.path))
- error("Loadout - Missing path definition: [G]")
- continue
-
- if(!GLOB.loadout_categories[use_category])
- GLOB.loadout_categories[use_category] = new /datum/loadout_category(use_category)
- var/datum/loadout_category/LC = GLOB.loadout_categories[use_category]
- GLOB.gear_datums[use_name] = new geartype
- LC.gear[use_name] = GLOB.gear_datums[use_name]
-
- GLOB.loadout_categories = sortAssoc(GLOB.loadout_categories)
- for(var/loadout_category in GLOB.loadout_categories)
- var/datum/loadout_category/LC = GLOB.loadout_categories[loadout_category]
- LC.gear = sortAssoc(LC.gear)
- return 1
-
/datum/gear
var/display_name //Name/index. Must be unique.
var/description //Description of this gear. If left blank will default to the description of the pathed item.
diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm
index 09cc082dc5d..b9318fd9e8c 100644
--- a/code/modules/client/preference/loadout/loadout_accessories.dm
+++ b/code/modules/client/preference/loadout/loadout_accessories.dm
@@ -163,41 +163,41 @@
display_name = "armband, blue-yellow"
path = /obj/item/clothing/accessory/armband/yb
-/datum/gear/accessory/armband_sec
+/datum/gear/accessory/armband_job
+ subtype_path = /datum/gear/accessory/armband_job
+ subtype_cost_overlap = FALSE
+
+/datum/gear/accessory/armband_job/sec
display_name = " armband, security"
path = /obj/item/clothing/accessory/armband/sec
allowed_roles = list("Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", "Security Pod Pilot")
-/datum/gear/accessory/armband_cargo
+/datum/gear/accessory/armband_job/cargo
display_name = "cargo armband"
path = /obj/item/clothing/accessory/armband/cargo
allowed_roles = list("Quartermaster","Cargo Technician", "Shaft Miner")
-/datum/gear/accessory/armband_medical
+/datum/gear/accessory/armband_job/medical
display_name = "armband, medical"
path = /obj/item/clothing/accessory/armband/med
allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Paramedic", "Brig Physician")
-/datum/gear/accessory/armband_emt
+/datum/gear/accessory/armband_job/emt
display_name = "armband, EMT"
path = /obj/item/clothing/accessory/armband/medgreen
allowed_roles = list("Paramedic", "Brig Physician")
-/datum/gear/accessory/armband_engineering
+/datum/gear/accessory/armband_job/engineering
display_name = "armband, engineering"
path = /obj/item/clothing/accessory/armband/engine
allowed_roles = list("Chief Engineer","Station Engineer", "Life Support Specialist")
-/datum/gear/accessory/armband_hydro
+/datum/gear/accessory/armband_job/hydro
display_name = "armband, hydroponics"
path = /obj/item/clothing/accessory/armband/hydro
allowed_roles = list("Botanist")
-/datum/gear/accessory/armband_sci
+/datum/gear/accessory/armband_job/sci
display_name = "armband, science"
path = /obj/item/clothing/accessory/armband/science
allowed_roles = list("Research Director","Scientist", "Roboticist")
-
-
-
-
diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm
index 7d6275e09e4..4993c8502ea 100644
--- a/code/modules/client/preference/loadout/loadout_hat.dm
+++ b/code/modules/client/preference/loadout/loadout_hat.dm
@@ -42,11 +42,6 @@
display_name = "fedora, brown"
path = /obj/item/clothing/head/fedora/brownfedora
-/datum/gear/hat/beretsec
- display_name = "security beret"
- path = /obj/item/clothing/head/beret/sec
- allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot")
-
/datum/gear/hat/capcsec
display_name = "security corporate cap"
path = /obj/item/clothing/head/soft/sec/corp
@@ -113,32 +108,51 @@
display_name = "cowboy hat, pink"
path = /obj/item/clothing/head/cowboyhat/pink
-/datum/gear/hat/pr_beret
+/datum/gear/hat/beret_purple
display_name = "beret, purple"
path = /obj/item/clothing/head/beret/purple_normal
-/datum/gear/hat/bl_beret
+/datum/gear/hat/beret_black
display_name = "beret, black"
path = /obj/item/clothing/head/beret/black
-/datum/gear/hat/blu_beret
+/datum/gear/hat/beret_blue
display_name = "beret, blue"
path = /obj/item/clothing/head/beret/blue
-/datum/gear/hat/red_beret
+/datum/gear/hat/beret_red
display_name = "beret, red"
path = /obj/item/clothing/head/beret
-/datum/gear/hat/sci_beret
+/datum/gear/hat/beret_job
+ subtype_path = /datum/gear/hat/beret_job
+ subtype_cost_overlap = FALSE
+
+/datum/gear/hat/beret_job/sec
+ display_name = "security beret"
+ path = /obj/item/clothing/head/beret/sec
+ allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot")
+
+/datum/gear/hat/beret_job/sci
display_name = "science beret"
path = /obj/item/clothing/head/beret/sci
allowed_roles = list("Research Director", "Scientist")
-/datum/gear/hat/med_beret
+/datum/gear/hat/beret_job/med
display_name = "medical beret"
path = /obj/item/clothing/head/beret/med
allowed_roles = list("Chief Medical Officer", "Medical Doctor" , "Virologist", "Brig Physician" , "Coroner")
+/datum/gear/hat/beret_job/eng
+ display_name = "engineering beret"
+ path = /obj/item/clothing/head/beret/eng
+ allowed_roles = list("Chief Engineer", "Station Engineer")
+
+/datum/gear/hat/beret_job/atmos
+ display_name = "atmospherics beret"
+ path = /obj/item/clothing/head/beret/atmos
+ allowed_roles = list("Chief Engineer", "Life Support Specialist")
+
/datum/gear/hat/surgicalcap_purple
display_name = "surgical cap, purple"
path = /obj/item/clothing/head/surgery/purple
diff --git a/code/modules/client/preference/loadout/loadout_suit.dm b/code/modules/client/preference/loadout/loadout_suit.dm
index 32a9bae99e7..57e7ea63414 100644
--- a/code/modules/client/preference/loadout/loadout_suit.dm
+++ b/code/modules/client/preference/loadout/loadout_suit.dm
@@ -175,32 +175,36 @@
display_name = "regal shawl"
path = /obj/item/clothing/suit/mantle/regal
-/datum/gear/suit/captain_cloak
+/datum/gear/suit/mantle/job
+ subtype_path = /datum/gear/suit/mantle/job
+ subtype_cost_overlap = FALSE
+
+/datum/gear/suit/mantle/job/captain
display_name = "mantle, captain"
path = /obj/item/clothing/suit/mantle/armor/captain
allowed_roles = list("Captain")
-/datum/gear/suit/ce_mantle
+/datum/gear/suit/mantle/job/ce
display_name = "mantle, chief engineer"
path = /obj/item/clothing/suit/mantle/chief_engineer
allowed_roles = list("Chief Engineer")
-/datum/gear/suit/cmo_mantle
+/datum/gear/suit/mantle/job/cmo
display_name = "mantle, chief medical officer"
path = /obj/item/clothing/suit/mantle/labcoat/chief_medical_officer
allowed_roles = list("Chief Medical Officer")
-/datum/gear/suit/armored_shawl
+/datum/gear/suit/mantle/job/hos
display_name = "mantle, head of security"
path = /obj/item/clothing/suit/mantle/armor
allowed_roles = list("Head of Security")
-/datum/gear/suit/hop_shawl
+/datum/gear/suit/mantle/job/hop
display_name = "mantle, head of personnel"
path = /obj/item/clothing/suit/mantle/armor/head_of_personnel
allowed_roles = list("Head of Personnel")
-/datum/gear/suit/rd_mantle
+/datum/gear/suit/mantle/job/rd
display_name = "mantle, research director"
path = /obj/item/clothing/suit/mantle/labcoat
allowed_roles = list("Research Director")
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 0b74b7132fd..12203b8155f 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -188,6 +188,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
// OOC Metadata:
var/metadata = ""
var/slot_name = ""
+ var/saved = FALSE // Indicates whether the character comes from the database or not
// Whether or not to use randomized character slots
var/randomslot = 0
@@ -263,10 +264,12 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "(Always Randomize) "
dat += ""
dat += ""
- dat += "Slot [slot_name] - "
+ dat += "Slot [default_slot][saved ? "" : " (empty)"] "
dat += "Load slot - "
dat += "Save slot - "
dat += "Reload slot"
+ if(saved)
+ dat += " - Clear slot"
dat += ""
dat += " | "
dat += ""
@@ -2078,6 +2081,11 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
load_preferences(user)
load_character(user)
+ if("clear")
+ if(!saved || real_name != input("This will clear the current slot permanently. Please enter the character's full name to confirm."))
+ return FALSE
+ clear_character_slot(user)
+
if("open_load_dialog")
if(!IsGuestKey(user.key))
open_load_dialog(user)
diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index 4219935c766..e889ec5a181 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -121,6 +121,7 @@
return 1
/datum/preferences/proc/load_character(client/C,slot)
+ saved = FALSE
if(!slot) slot = default_slot
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
@@ -262,6 +263,8 @@
loadout_gear = params2list(query.item[51])
autohiss_mode = text2num(query.item[52])
+ saved = TRUE
+
//Sanitize
var/datum/species/SP = GLOB.all_species[species]
metadata = sanitize_text(metadata, initial(metadata))
@@ -474,6 +477,8 @@
log_game("SQL ERROR during character slot saving. Error : \[[err]\]\n")
message_admins("SQL ERROR during character slot saving. Error : \[[err]\]\n")
return
+
+ saved = TRUE
return 1
/datum/preferences/proc/load_random_character_slot(client/C)
@@ -495,3 +500,20 @@
load_character(C,pick(saves))
return 1
+/datum/preferences/proc/clear_character_slot(client/C)
+ . = FALSE
+ // Is there a character in that slot?
+ var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' AND slot='[default_slot]'")
+ query.Execute()
+ if(!query.RowCount())
+ return
+
+ var/DBQuery/query2 = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' AND slot='[default_slot]'")
+ if(!query2.Execute())
+ var/err = query2.ErrorMsg()
+ log_game("SQL ERROR during character slot clearing. Error : \[[err]\]\n")
+ message_admins("SQL ERROR during character slot clearing. Error : \[[err]\]\n")
+ return
+
+ saved = FALSE
+ return TRUE
diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm
index 5534e18e253..0f4317e3ce2 100644
--- a/code/modules/client/preference/preferences_toggles.dm
+++ b/code/modules/client/preference/preferences_toggles.dm
@@ -30,7 +30,8 @@
set name = "Show/Hide RadioChatter"
set category = "Preferences"
set desc = "Toggle seeing radiochatter from radios and speakers"
- if(!holder) return
+ if(!check_rights(R_ADMIN))
+ return
prefs.toggles ^= CHAT_RADIO
prefs.save_preferences(src)
to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers")
@@ -49,7 +50,8 @@
set name = "Hear/Silence Admin Bwoinks"
set category = "Preferences"
set desc = "Toggle hearing a notification when admin PMs are recieved"
- if(!holder) return
+ if(!check_rights(R_ADMIN))
+ return
prefs.sound ^= SOUND_ADMINHELP
prefs.save_preferences(src)
to_chat(usr, "You will [(prefs.sound & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive.")
@@ -59,7 +61,7 @@
set name = "Hear/Silence Mentorhelp Bwoinks"
set category = "Preferences"
set desc = "Toggle hearing a notification when mentorhelps are recieved"
- if(!holder)
+ if(!check_rights(R_ADMIN|R_MENTOR))
return
prefs.sound ^= SOUND_MENTORHELP
prefs.save_preferences(src)
@@ -296,7 +298,7 @@
to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].")
prefs.save_preferences(src)
feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
+
/client/verb/silence_current_midi()
set name = "Silence Current Midi"
set category = "Preferences"
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index bfa43b92fa3..b1efd1f0b78 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -32,6 +32,7 @@
var/cooldown = 0
var/species_disguise = null
var/magical = FALSE
+ w_class = WEIGHT_CLASS_SMALL
/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc.
if(!can_use(user))
@@ -590,6 +591,7 @@ BLIND // can't see anything
name = "Space helmet"
icon_state = "space"
desc = "A special helmet designed for work in a hazardous, low-pressure environment."
+ w_class = WEIGHT_CLASS_NORMAL
flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
item_state = "s_helmet"
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 2a9abf461c9..05bd3d6f48d 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -2,6 +2,7 @@
name = "helmet"
desc = "Standard Security gear. Protects the head from impacts."
icon_state = "helmetmaterials"
+ w_class = WEIGHT_CLASS_NORMAL
flags = HEADBANGPROTECT
flags_cover = HEADCOVERSEYES
item_state = "helmetmaterials"
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 962cfed80c5..1f8cb7aaed1 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -6,6 +6,7 @@
/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses
name = "combat boots"
desc = "High speed, low drag combat boots."
+ w_class = WEIGHT_CLASS_NORMAL
can_cut_open = 1
icon_state = "jackboots"
item_state = "jackboots"
diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm
deleted file mode 100644
index 6b9d07a332d..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/combat.dm
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Contains
- * /obj/item/rig_module/grenade_launcher
- * /obj/item/rig_module/mounted
- * /obj/item/rig_module/mounted/taser
- * /obj/item/rig_module/shield
- * /obj/item/rig_module/fabricator
- * /obj/item/rig_module/device/flash
- */
-
-/obj/item/rig_module/device/flash
- name = "mounted flash"
- desc = "You are the law."
- icon_state = "flash"
- interface_name = "mounted flash"
- interface_desc = "Stuns your target by blinding them with a bright light."
- device_type = /obj/item/flash
-
-/obj/item/rig_module/grenade_launcher
-
- name = "mounted grenade launcher"
- desc = "A shoulder-mounted micro-explosive dispenser."
- selectable = 1
- icon_state = "grenade"
-
- interface_name = "integrated grenade launcher"
- interface_desc = "Discharges loaded grenades against the wearer's location."
-
- var/fire_force = 30
- var/fire_distance = 10
-
- charges = list(
- list("flashbang", "flashbang", /obj/item/grenade/flashbang, 3),
- list("smoke bomb", "smoke bomb", /obj/item/grenade/smokebomb, 3),
- list("EMP grenade", "EMP grenade", /obj/item/grenade/empgrenade, 3),
- )
-
-/obj/item/rig_module/grenade_launcher/accepts_item(var/obj/item/input_device, var/mob/living/user)
-
- if(!istype(input_device) || !istype(user))
- return 0
-
- var/datum/rig_charge/accepted_item
- for(var/charge in charges)
- var/datum/rig_charge/charge_datum = charges[charge]
- if(input_device.type == charge_datum.product_type)
- accepted_item = charge_datum
- break
-
- if(!accepted_item)
- return 0
-
- if(accepted_item.charges >= 5)
- to_chat(user, "Another grenade of that type will not fit into the module.")
- return 0
-
- to_chat(user, "You slot \the [input_device] into the suit module.")
- user.unEquip(input_device)
- qdel(input_device)
- accepted_item.charges++
- return 1
-
-/obj/item/rig_module/grenade_launcher/engage(atom/target)
-
- if(!..())
- return 0
-
- if(!target)
- return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- if(!charge_selected)
- to_chat(H, "You have not selected a grenade type.")
- return 0
-
- var/datum/rig_charge/charge = charges[charge_selected]
-
- if(!charge)
- return 0
-
- if(charge.charges <= 0)
- to_chat(H, "Insufficient grenades!")
- return 0
-
- charge.charges--
- var/obj/item/grenade/new_grenade = new charge.product_type(get_turf(H))
- H.visible_message("[H] launches \a [new_grenade]!")
- new_grenade.throw_at(target,fire_force,fire_distance)
- new_grenade.prime()
-
-/obj/item/rig_module/mounted
-
- name = "mounted laser cannon"
- desc = "A shoulder-mounted battery-powered laser cannon mount."
- selectable = 1
- usable = 1
- module_cooldown = 0
- icon_state = "lcannon"
-
- engage_string = "Configure"
-
- interface_name = "mounted laser cannon"
- interface_desc = "A shoulder-mounted cell-powered laser cannon."
-
- var/gun_type = /obj/item/gun/energy/lasercannon/mounted
- var/obj/item/gun/gun
-
-/obj/item/rig_module/mounted/New()
- ..()
- gun = new gun_type(src)
-
-/obj/item/rig_module/mounted/engage(atom/target)
-
- if(!..())
- return 0
-
- if(!target)
- gun.attack_self(holder.wearer)
- return 1
-
- gun.afterattack(target,holder.wearer)
- return 1
-
-/obj/item/rig_module/mounted/egun
-
- name = "mounted energy gun"
- desc = "A forearm-mounted energy projector."
- icon_state = "egun"
-
- interface_name = "mounted energy gun"
- interface_desc = "A forearm-mounted suit-powered energy gun."
-
- gun_type = /obj/item/gun/energy/gun/mounted
-
-/obj/item/rig_module/mounted/taser
-
- name = "mounted taser"
- desc = "A palm-mounted nonlethal energy projector."
- icon_state = "taser"
-
- usable = 0
-
- suit_overlay_active = "mounted-taser"
- suit_overlay_inactive = "mounted-taser"
-
- interface_name = "mounted energy gun"
- interface_desc = "A shoulder-mounted cell-powered energy gun."
-
- gun_type = /obj/item/gun/energy/taser/mounted
-
-/obj/item/rig_module/mounted/energy_blade
-
- name = "energy blade projector"
- desc = "A powerful cutting beam projector."
- icon_state = "eblade"
-
- activate_string = "Project Blade"
- deactivate_string = "Cancel Blade"
-
- interface_name = "spider fang blade"
- interface_desc = "A lethal energy projector that can shape a blade projected from the hand of the wearer or launch radioactive darts."
-
- usable = 0
- selectable = 1
- toggleable = 1
- use_power_cost = 50
- active_power_cost = 10
- passive_power_cost = 0
-
- gun_type = /obj/item/gun/energy/kinetic_accelerator/crossbow/ninja
-
-/obj/item/rig_module/mounted/energy_blade/process()
-
- if(holder && holder.wearer)
- if(!(locate(/obj/item/melee/energy/blade) in holder.wearer))
- deactivate()
- return 0
-
- return ..()
-
-/obj/item/rig_module/mounted/energy_blade/activate()
-
- ..()
-
- var/mob/living/M = holder.wearer
-
- if(M.l_hand && M.r_hand)
- to_chat(M, "Your hands are full.")
- deactivate()
- return
-
- var/obj/item/melee/energy/blade/blade = new(M)
- M.put_in_hands(blade)
-
-/obj/item/rig_module/mounted/energy_blade/deactivate()
-
- ..()
-
- var/mob/living/M = holder.wearer
-
- if(!M)
- return
-
- for(var/obj/item/melee/energy/blade/blade in M.contents)
- M.unEquip(blade)
- qdel(blade)
-
-/obj/item/rig_module/fabricator
-
- name = "matter fabricator"
- desc = "A self-contained microfactory system for hardsuit integration."
- selectable = 1
- usable = 1
- use_power_cost = 15
- icon_state = "enet"
-
- engage_string = "Fabricate Tile"
-
- interface_name = "death blossom launcher"
- interface_desc = "An integrated microfactory that produces floor tiles from thin air and electricity."
-
- var/fabrication_type = /obj/item/stack/tile/plasteel
- var/fire_force = 30
- var/fire_distance = 10
-
-/obj/item/rig_module/fabricator/engage(atom/target)
-
- if(!..())
- return 0
-
- var/mob/living/H = holder.wearer
-
- if(target)
- var/obj/item/firing = new fabrication_type()
- firing.forceMove(get_turf(src))
- H.visible_message("[H] launches \a [firing]!")
- firing.throw_at(target,fire_force,fire_distance)
- else
- if(H.l_hand && H.r_hand)
- to_chat(H, "Your hands are full.")
- else
- var/obj/item/new_weapon = new fabrication_type()
- new_weapon.forceMove(H)
- to_chat(H, "You quickly fabricate \a [new_weapon].")
- H.put_in_hands(new_weapon)
-
- return 1
diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm
deleted file mode 100644
index 749e12e8053..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/computer.dm
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Contains
- * /obj/item/rig_module/ai_container
- * /obj/item/rig_module/datajack
- * /obj/item/rig_module/power_sink
- * /obj/item/rig_module/electrowarfare_suite
- */
-
-/obj/item/ai_verbs
- name = "AI verb holder"
-
-/obj/item/ai_verbs/verb/hardsuit_interface()
- set category = "Hardsuit"
- set name = "Open Hardsuit Interface"
- set src in usr
-
- if(!usr.loc || !usr.loc.loc || !istype(usr.loc.loc, /obj/item/rig_module))
- to_chat(usr, "You are not loaded into a hardsuit.")
- return
-
- var/obj/item/rig_module/module = usr.loc.loc
- if(!module.holder)
- to_chat(usr, "Your module is not installed in a hardsuit.")
- return
-
- module.holder.ui_interact(usr, state = GLOB.contained_state)
-
-/obj/item/rig_module/ai_container
-
- name = "IIS module"
- desc = "An integrated intelligence system module suitable for most hardsuits."
- icon_state = "IIS"
- toggleable = 1
- usable = 1
- disruptive = 0
- activates_on_touch = 1
-
- engage_string = "Eject AI"
- activate_string = "Enable Dataspike"
- deactivate_string = "Disable Dataspike"
-
- interface_name = "integrated intelligence system"
- interface_desc = "A socket that supports a range of artificial intelligence systems."
-
- var/mob/integrated_ai // Direct reference to the actual mob held in the suit.
- var/obj/item/ai_card // Reference to the MMI, posibrain, intellicard or pAI card previously holding the AI.
- var/obj/item/ai_verbs/verb_holder
-
-/mob
- var/get_rig_stats = 0
-
-/obj/item/rig_module/ai_container/process()
- if(integrated_ai)
- var/obj/item/rig/rig = get_rig()
- if(rig && rig.ai_override_enabled)
- integrated_ai.get_rig_stats = 1
- else
- integrated_ai.get_rig_stats = 0
-
-/obj/item/rig_module/ai_container/proc/update_verb_holder()
- if(!verb_holder)
- verb_holder = new(src)
- if(integrated_ai)
- verb_holder.forceMove(integrated_ai)
- else
- verb_holder.forceMove(src)
-
-/obj/item/rig_module/ai_container/accepts_item(var/obj/item/input_device, var/mob/living/user)
-
- // Check if there's actually an AI to deal with.
- var/mob/living/silicon/ai/target_ai
- if(istype(input_device, /mob/living/silicon/ai))
- target_ai = input_device
- else
- target_ai = locate(/mob/living/silicon/ai) in input_device.contents
-
- var/obj/item/aicard/card = ai_card
-
- // Downloading from/loading to a terminal.
- if(istype(input_device,/obj/machinery/computer/aifixer) || istype(input_device,/mob/living/silicon/ai) || istype(input_device,/obj/structure/AIcore/deactivated))
-
- // If we're stealing an AI, make sure we have a card for it.
- if(!card)
- card = new /obj/item/aicard(src)
-
- // Terminal interaction only works with an intellicarded AI.
- if(!istype(card))
- return 0
-
- // Since we've explicitly checked for three types, this should be safe.
- card.afterattack(input_device, user, 1)
-
- // If the transfer failed we can delete the card.
- if(locate(/mob/living/silicon/ai) in card)
- ai_card = card
- integrated_ai = locate(/mob/living/silicon/ai) in card
- else
- eject_ai()
- update_verb_holder()
- return 1
-
- if(istype(input_device,/obj/item/aicard))
- // We are carding the AI in our suit.
- if(integrated_ai)
- var/obj/item/aicard/ext_card = input_device
- ext_card.afterattack(integrated_ai, user, 1)
- // If the transfer was successful, we can clear out our vars.
- if(integrated_ai.loc != src)
- integrated_ai = null
- eject_ai()
- else
- // You're using an empty card on an empty suit, idiot.
- if(!target_ai)
- return 0
- integrate_ai(input_device,user)
- return 1
-
- // Okay, it wasn't a terminal being touched, check for all the simple insertions.
- if(input_device.type in list(/obj/item/paicard, /obj/item/mmi, /obj/item/mmi/robotic_brain))
- if(integrated_ai)
- integrated_ai.attackby(input_device,user)
- // If the transfer was successful, we can clear out our vars.
- if(integrated_ai.loc != src)
- integrated_ai = null
- eject_ai()
- else
- integrate_ai(input_device,user)
- return 1
-
- return 0
-
-/obj/item/rig_module/ai_container/engage(atom/target)
-
- if(!..())
- return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- if(!target)
- if(ai_card)
- if(istype(ai_card,/obj/item/aicard))
- ai_card.ui_interact(H, state = GLOB.deep_inventory_state)
- else
- eject_ai(H)
- update_verb_holder()
- return 1
-
- if(accepts_item(target,H))
- return 1
-
- return 0
-
-/obj/item/rig_module/ai_container/removed()
- eject_ai()
- ..()
-
-/obj/item/rig_module/ai_container/proc/eject_ai(var/mob/user)
-
- if(ai_card)
- if(istype(ai_card, /obj/item/aicard))
- if(integrated_ai && !integrated_ai.stat)
- if(user)
- to_chat(user, "You cannot eject your currently stored AI. Purge it manually.")
- return 0
- to_chat(user, "You purge the remaining scraps of data from your previous AI, freeing it for use.")
- QDEL_NULL(integrated_ai)
- QDEL_NULL(ai_card)
- else if(user)
- user.put_in_hands(ai_card)
- else
- ai_card.forceMove(get_turf(src))
- ai_card = null
- integrated_ai = null
- update_verb_holder()
-
-
-
-/obj/item/rig_module/ai_container/proc/integrate_ai(var/obj/item/ai,var/mob/user)
- if(!ai) return
-
- // The ONLY THING all the different AI systems have in common is that they all store the mob inside an item.
- var/mob/living/ai_mob = locate(/mob/living) in ai.contents
- if(ai_mob)
- if(ai_mob.key && ai_mob.client)
- if(istype(ai, /obj/item/aicard))
- var/mob/living/silicon/ai/ROBUTT = ai_mob
- if(istype(ROBUTT))
- if(!ai_card)
- ai_card = new /obj/item/aicard(src)
-
- var/obj/item/aicard/source_card = ai
- var/obj/item/aicard/target_card = ai_card
- if(istype(source_card) && istype(target_card))
- ROBUTT.forceMove(target_card)
- ROBUTT.aiRestorePowerRoutine = 0//So the AI initially has power.
- ROBUTT.control_disabled = 1//Can't control things remotely if you're stuck in a card!
- ROBUTT.aiRadio.disabledAi = 1 //No talking on the built-in radio for you either!
- source_card.update_state()
- target_card.update_state()
- else
- return 0
-
- else
- user.unEquip(ai)
- ai.forceMove(src)
- ai_card = ai
- to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].")
- to_chat(user, "You load [ai_mob] into \the [holder]'s [src].")
-
- integrated_ai = ai_mob
-
- if(!(locate(integrated_ai) in ai_card))
- integrated_ai = null
- eject_ai()
- else
- to_chat(user, "There is no active AI within \the [ai].")
- else
- to_chat(user, "There is no active AI within \the [ai].")
- update_verb_holder()
-
-/obj/item/rig_module/datajack
-
- name = "datajack module"
- desc = "A simple induction datalink module."
- icon_state = "datajack"
- toggleable = 1
- activates_on_touch = 1
- usable = 0
-
- activate_string = "Enable Datajack"
- deactivate_string = "Disable Datajack"
-
- interface_name = "contact datajack"
- interface_desc = "An induction-powered high-throughput datalink suitable for hacking encrypted networks."
- var/list/stored_research
-
-/obj/item/rig_module/datajack/New()
- ..()
- stored_research = list()
-
-/obj/item/rig_module/datajack/engage(atom/target)
-
- if(!..())
- return 0
-
- if(target)
- var/mob/living/carbon/human/H = holder.wearer
- if(!accepts_item(target,H))
- return 0
- return 1
-
-/obj/item/rig_module/datajack/accepts_item(var/obj/item/input_device, var/mob/living/user)
-
- if(istype(input_device,/obj/item/disk/tech_disk))
- to_chat(user, "You slot the disk into [src].")
- var/obj/item/disk/tech_disk/disk = input_device
- if(disk.stored)
- if(load_data(disk.stored))
- to_chat(user, "Download successful; disk erased.")
- disk.stored = null
- else
- to_chat(user, "The disk is corrupt. It is useless to you.")
- else
- to_chat(user, "The disk is blank. It is useless to you.")
- return 1
-
- // I fucking hate R&D code. This typecheck spam would be totally unnecessary in a sane setup.
- else if(istype(input_device,/obj/machinery))
- var/datum/research/incoming_files
- if(istype(input_device,/obj/machinery/computer/rdconsole))
- var/obj/machinery/computer/rdconsole/input_machine = input_device
- incoming_files = input_machine.files
- else if(istype(input_device,/obj/machinery/r_n_d/server))
- var/obj/machinery/r_n_d/server/input_machine = input_device
- incoming_files = input_machine.files
- else if(istype(input_device,/obj/machinery/mecha_part_fabricator))
- var/obj/machinery/mecha_part_fabricator/input_machine = input_device
- incoming_files = input_machine.files
-
- if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len)
- to_chat(user, "Memory failure. There is nothing accessible stored on this terminal.")
- else
- // Maybe consider a way to drop all your data into a target repo in the future.
- if(load_data(incoming_files.known_tech))
- to_chat(user, "Download successful; local and remote repositories synchronized.")
- else
- to_chat(user, "Scan complete. There is nothing useful stored on this terminal.")
- return 1
- return 0
-
-/obj/item/rig_module/datajack/proc/load_data(var/incoming_data)
-
- if(islist(incoming_data))
- for(var/entry in incoming_data)
- load_data(entry)
- return 1
-
- if(istype(incoming_data, /datum/tech))
- var/data_found
- var/datum/tech/new_data = incoming_data
- for(var/datum/tech/current_data in stored_research)
- if(current_data.id == new_data.id)
- data_found = 1
- if(current_data.level < new_data.level)
- current_data.level = new_data.level
- break
- if(!data_found)
- stored_research += incoming_data
- return 1
- return 0
-
-/obj/item/rig_module/electrowarfare_suite
-
- name = "electrowarfare module"
- desc = "A bewilderingly complex bundle of fiber optics and chips."
- icon_state = "ewar"
- toggleable = 1
- usable = 0
-
- activate_string = "Enable Countermeasures"
- deactivate_string = "Disable Countermeasures"
-
- interface_name = "electrowarfare system"
- interface_desc = "An active counter-electronic warfare suite that disrupts AI tracking."
-
-/obj/item/rig_module/electrowarfare_suite/activate()
-
- if(!..())
- return
-
- // This is not the best way to handle this, but I don't want it to mess with ling camo
- var/mob/living/M = holder.wearer
- M.digitalcamo++
-
-/obj/item/rig_module/electrowarfare_suite/deactivate()
-
- if(!..())
- return
-
- var/mob/living/M = holder.wearer
- M.digitalcamo = max(0,(M.digitalcamo-1))
-
-/* //Not easily compatible with our current powernet, and is
-/obj/item/rig_module/power_sink // quite stupid anyways, iyam
-
- name = "hardsuit power sink"
- desc = "An heavy-duty power sink."
- icon_state = "powersink"
- toggleable = 1
- activates_on_touch = 1
- disruptive = 0
-
- activate_string = "Enable Power Sink"
- deactivate_string = "Disable Power Sink"
-
- interface_name = "niling d-sink"
- interface_desc = "Colloquially known as a power siphon, this module drains power through the suit hands into the suit battery."
-
- var/atom/interfaced_with // Currently draining power from this device.
- var/total_power_drained = 0
- var/drain_loc
-
-/obj/item/rig_module/power_sink/deactivate()
-
- if(interfaced_with)
- if(holder && holder.wearer)
- to_chat(holder.wearer, "Your power sink retracts as the module deactivates.")
- drain_complete()
- interfaced_with = null
- total_power_drained = 0
- return ..()
-
-/obj/item/rig_module/power_sink/activate()
- interfaced_with = null
- total_power_drained = 0
- return ..()
-
-/obj/item/rig_module/power_sink/engage(atom/target)
-
- if(!..())
- return 0
-
- //Target wasn't supplied or we're already draining.
- if(interfaced_with)
- return 0
-
- if(!target)
- return 1
-
- // Are we close enough?
- var/mob/living/carbon/human/H = holder.wearer
- if(!target.Adjacent(H))
- return 0
-
- // Is it a valid power source?
- if(target.drain_power(1) <= 0)
- return 0
-
- to_chat(H, "You begin draining power from [target]!")
- interfaced_with = target
- drain_loc = interfaced_with.loc
-
- holder.spark_system.start()
- playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1)
-
- return 1
-
-/obj/item/rig_module/power_sink/accepts_item(var/obj/item/input_device, var/mob/living/user)
- var/can_drain = input_device.drain_power(1)
- if(can_drain > 0)
- engage(input_device)
- return 1
- return 0
-
-/obj/item/rig_module/power_sink/process()
-
- if(!interfaced_with)
- return ..()
-
- var/mob/living/carbon/human/H
- if(holder && holder.wearer)
- H = holder.wearer
-
- if(!H || !istype(H))
- return 0
-
- holder.spark_system.start()
- playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1)
-
- if(!holder.cell)
- to_chat(H, "Your power sink flashes an error; there is no cell in your rig.")
- drain_complete(H)
- return
-
- if(!interfaced_with || !interfaced_with.Adjacent(H) || !(interfaced_with.loc == drain_loc))
- to_chat(H, "Your power sink retracts into its casing.")
- drain_complete(H)
- return
-
- if(holder.cell.fully_charged())
- to_chat(H, "Your power sink flashes an amber light; your rig cell is full.")
- drain_complete(H)
- return
-
- // Attempts to drain up to 40kW, determines this value from remaining cell capacity to ensure we don't drain too much..
- var/to_drain = min(40000, ((holder.cell.maxcharge - holder.cell.charge) / CELLRATE))
- var/target_drained = interfaced_with.drain_power(0,0,to_drain)
- if(target_drained <= 0)
- to_chat(H, "Your power sink flashes a red light; there is no power left in [interfaced_with].")
- drain_complete(H)
- return
-
- holder.cell.give(target_drained * CELLRATE)
- total_power_drained += target_drained
-
- return 1
-
-/obj/item/rig_module/power_sink/proc/drain_complete(var/mob/living/M)
-
- if(!interfaced_with)
- to_chat(if(M) M, "Total power drained: [round(total_power_drained/1000)]kJ.")
- else
- to_chat(if(M) M, "Total power drained from [interfaced_with]: [round(total_power_drained/1000)]kJ.")
- interfaced_with.drain_power(0,1,0) // Damage the victim.
-
- drain_loc = null
- interfaced_with = null
- total_power_drained = 0*/
-
-/*
-//Maybe make this use power when active or something
-/obj/item/rig_module/emp_shielding
- name = "\improper EMP dissipation module"
- desc = "A bewilderingly complex bundle of fiber optics and chips."
- toggleable = 1
- usable = 0
-
- activate_string = "Enable active EMP shielding"
- deactivate_string = "Disable active EMP shielding"
-
- interface_name = "active EMP shielding system"
- interface_desc = "A highly experimental system that augments the hardsuit's existing EM shielding."
- var/protection_amount = 20
-
-/obj/item/rig_module/emp_shielding/activate()
- if(!..())
- return
-
- holder.emp_protection += protection_amount
-
-/obj/item/rig_module/emp_shielding/deactivate()
- if(!..())
- return
-
- holder.emp_protection = max(0,(holder.emp_protection - protection_amount))
-*/
diff --git a/code/modules/clothing/spacesuits/rig/modules/handheld.dm b/code/modules/clothing/spacesuits/rig/modules/handheld.dm
deleted file mode 100644
index 31f9400afc2..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/handheld.dm
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/obj/item/rig_module/handheld
- name = "mounted device"
- desc = "Some kind of hardsuit extension."
- usable = 0
- selectable = 0
- toggleable = 1
- disruptive = 0
- activate_string = "Deploy"
- deactivate_string = "Retract"
-
- var/device_type
- var/obj/item
-
-/obj/item/rig_module/handheld/activate()
- if(!..())
- return
-
- if(!holder.wearer.put_in_hands(device))
- to_chat(holder.wearer, "You need a free hand to hold \the [device].")
- active = 0
- return
-
- to_chat(holder.wearer, "You deploy \the [device].")
-
-
-/obj/item/rig_module/handheld/deactivate()
- if(!..())
- return
- if(ismob(device.loc)) //Better check for the holder, instead of assuming the rigwearer has it.
- var/mob/M = device.loc //Helps in case the code fails to keep the module in one place, this should still return it.
- M.unEquip(device, 1)
-
- device.loc = src
- to_chat(holder.wearer, "You retract \the [device].")
-
-/obj/item/rig_module/handheld/New()
- ..()
- if(device_type)
- device = new device_type(src)
- device.flags |= NODROP //We don't want to drop it while it's active/inhand.
- activate_string += " [device]"
- deactivate_string += " [device]"
-
-/obj/item/rig_module/handheld/horn
- name = "mounted bikehorn"
- desc = "For tactical honking"
- interface_name = "mounted bikehorn"
- interface_desc = "Honks"
- device_type = /obj/item/bikehorn
diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm
deleted file mode 100644
index 53330341bfe..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/modules.dm
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Rigsuit upgrades/abilities.
- */
-
-/datum/rig_charge
- var/short_name = "undef"
- var/display_name = "undefined"
- var/product_type = "undefined"
- var/charges = 0
-
-/obj/item/rig_module
- name = "hardsuit upgrade"
- desc = "It looks pretty sciency."
- icon = 'icons/obj/rig_modules.dmi'
- icon_state = "module"
-
- toolspeed = 1
-
- var/damage = 0
- var/obj/item/rig/holder
-
- var/module_cooldown = 10
- var/next_use = 0
-
- var/toggleable // Set to 1 for the device to show up as an active effect.
- var/usable // Set to 1 for the device to have an on-use effect.
- var/selectable // Set to 1 to be able to assign the device as primary system.
- var/redundant // Set to 1 to ignore duplicate module checking when installing.
- var/permanent // If set, the module can't be removed.
- var/disruptive = 1 // Can disrupt by other effects.
- var/activates_on_touch // If set, unarmed attacks will call engage() on the target.
-
- var/active // Basic module status
- var/disruptable // Will deactivate if some other powers are used.
-
- var/use_power_cost = 0 // Power used when single-use ability called.
- var/active_power_cost = 0 // Power used when turned on.
- var/passive_power_cost = 0 // Power used when turned off.
-
- var/list/charges // Associative list of charge types and remaining numbers.
- var/charge_selected // Currently selected option used for charge dispensing.
-
- // Icons.
- var/suit_overlay
- var/suit_overlay_active // If set, drawn over icon and mob when effect is active.
- var/suit_overlay_inactive // As above, inactive.
- var/suit_overlay_used // As above, when engaged.
-
- //Display fluff
- var/interface_name = "hardsuit upgrade"
- var/interface_desc = "A generic hardsuit upgrade."
- var/engage_string = "Engage"
- var/activate_string = "Activate"
- var/deactivate_string = "Deactivate"
-
- var/list/stat_rig_module/stat_modules = new()
-
-/obj/item/rig_module/examine(mob/user)
- . = ..()
- switch(damage)
- if(0)
- . += "It is undamaged."
- if(1)
- . += "It is badly damaged."
- if(2)
- . += "It is almost completely destroyed."
-
-/obj/item/rig_module/attackby(obj/item/W as obj, mob/user as mob)
-
- if(istype(W,/obj/item/stack/nanopaste))
-
- if(damage == 0)
- to_chat(user, "There is no damage to mend.")
- return
-
- to_chat(user, "You start mending the damaged portions of \the [src]...")
-
- if(!do_after(user, 30 * W.toolspeed, target = src) || !W || !src)
- return
-
- var/obj/item/stack/nanopaste/paste = W
- damage = 0
- to_chat(user, "You mend the damage to [src] with [W].")
- paste.use(1)
- return
-
- else if(istype(W,/obj/item/stack/cable_coil))
-
- switch(damage)
- if(0)
- to_chat(user, "There is no damage to mend.")
- return
- if(2)
- to_chat(user, "There is no damage that you are capable of mending with such crude tools.")
- return
-
- var/obj/item/stack/cable_coil/cable = W
- if(!cable.amount >= 5)
- to_chat(user, "You need five units of cable to repair \the [src].")
- return
-
- to_chat(user, "You start mending the damaged portions of \the [src]...")
- if(!do_after(user, 30 * W.toolspeed, target = src) || !W || !src)
- return
-
- damage = 1
- to_chat(user, "You mend some of damage to [src] with [W], but you will need more advanced tools to fix it completely.")
- cable.use(5)
- return
- ..()
-
-/obj/item/rig_module/New()
- ..()
- if(suit_overlay_inactive)
- suit_overlay = suit_overlay_inactive
-
- if(charges && charges.len)
- var/list/processed_charges = list()
- for(var/list/charge in charges)
- var/datum/rig_charge/charge_dat = new
-
- charge_dat.short_name = charge[1]
- charge_dat.display_name = charge[2]
- charge_dat.product_type = charge[3]
- charge_dat.charges = charge[4]
-
- if(!charge_selected) charge_selected = charge_dat.short_name
- processed_charges[charge_dat.short_name] = charge_dat
-
- charges = processed_charges
-
- stat_modules += new/stat_rig_module/activate(src)
- stat_modules += new/stat_rig_module/deactivate(src)
- stat_modules += new/stat_rig_module/engage(src)
- stat_modules += new/stat_rig_module/select(src)
- stat_modules += new/stat_rig_module/charge(src)
-
-// Called when the module is installed into a suit.
-/obj/item/rig_module/proc/installed(var/obj/item/rig/new_holder)
- holder = new_holder
- return
-
-//Proc for one-use abilities like teleport.
-/obj/item/rig_module/proc/engage()
-
- if(damage >= 2)
- to_chat(usr, "The [interface_name] is damaged beyond use!")
- return 0
-
- if(world.time < next_use)
- to_chat(usr, "You cannot use the [interface_name] again so soon.")
- return 0
-
- if(!holder || (!(holder.flags & NODROP)))
- to_chat(usr, "The suit is not initialized.")
- return 0
-
- if(usr.lying || usr.stat || usr.stunned || usr.paralysis || usr.IsWeakened())
- to_chat(usr, "You cannot use the suit in this state.")
- return 0
-
- if(holder.wearer && holder.wearer.lying)
- to_chat(usr, "The suit cannot function while the wearer is prone.")
- return 0
-
- if(holder.security_check_enabled && !holder.check_suit_access(usr))
- to_chat(usr, "Access denied.")
- return 0
-
- if(!holder.check_power_cost(usr, use_power_cost, 0, src, (istype(usr,/mob/living/silicon ? 1 : 0) ) ) )
- return 0
-
- next_use = world.time + module_cooldown
-
- return 1
-
-// Proc for toggling on active abilities.
-/obj/item/rig_module/proc/activate()
-
- if(active || !engage())
- return 0
-
- active = 1
-
- spawn(1)
- if(suit_overlay_active)
- suit_overlay = suit_overlay_active
- else
- suit_overlay = null
- holder.update_icon()
-
- return 1
-
-// Proc for toggling off active abilities.
-/obj/item/rig_module/proc/deactivate()
-
- if(!active)
- return 0
-
- active = 0
-
- spawn(1)
- if(suit_overlay_inactive)
- suit_overlay = suit_overlay_inactive
- else
- suit_overlay = null
- if(holder)
- holder.update_icon()
-
- return 1
-
-// Called when the module is uninstalled from a suit.
-/obj/item/rig_module/proc/removed()
- deactivate()
- holder = null
- return
-
-// Called by the hardsuit each rig process tick.
-/obj/item/rig_module/process()
- if(active)
- return active_power_cost
- else
- return passive_power_cost
-
-// Called by holder rigsuit attackby()
-// Checks if an item is usable with this module and handles it if it is
-/obj/item/rig_module/proc/accepts_item(var/obj/item/input_device)
- return 0
-
-/mob/proc/SetupStat(var/obj/item/rig/R)
- if(R && (R.flags & NODROP) && R.installed_modules.len && statpanel("Hardsuit Modules"))
- var/cell_status = R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "ERROR"
- stat("Suit charge", cell_status)
- for(var/obj/item/rig_module/module in R.installed_modules)
- {
- for(var/stat_rig_module/SRM in module.stat_modules)
- if(SRM.CanUse())
- stat(SRM.module.interface_name,SRM)
- }
-
-/stat_rig_module
- parent_type = /atom/movable
- var/module_mode = ""
- var/obj/item/rig_module/module
-
-/stat_rig_module/New(var/obj/item/rig_module/module)
- ..()
- src.module = module
-
-/stat_rig_module/proc/AddHref(var/list/href_list)
- return
-
-/stat_rig_module/proc/CanUse()
- return 0
-
-/stat_rig_module/Click()
- if(CanUse())
- var/list/href_list = list(
- "interact_module" = module.holder.installed_modules.Find(module),
- "module_mode" = module_mode
- )
- AddHref(href_list)
- module.holder.Topic(usr, href_list)
-
-/stat_rig_module/DblClick()
- return Click()
-
-/stat_rig_module/activate/New(var/obj/item/rig_module/module)
- ..()
- name = module.activate_string
- if(module.active_power_cost)
- name += " ([module.active_power_cost*10]A)"
- module_mode = "activate"
-
-/stat_rig_module/activate/CanUse()
- return module.toggleable && !module.active
-
-/stat_rig_module/deactivate/New(var/obj/item/rig_module/module)
- ..()
- name = module.deactivate_string
- // Show cost despite being 0, if it means changing from an active cost.
- if(module.active_power_cost || module.passive_power_cost)
- name += " ([module.passive_power_cost*10]P)"
-
- module_mode = "deactivate"
-
-/stat_rig_module/deactivate/CanUse()
- return module.toggleable && module.active
-
-/stat_rig_module/engage/New(var/obj/item/rig_module/module)
- ..()
- name = module.engage_string
- if(module.use_power_cost)
- name += " ([module.use_power_cost*10]E)"
- module_mode = "engage"
-
-/stat_rig_module/engage/CanUse()
- return module.usable
-
-/stat_rig_module/select/New()
- ..()
- name = "Select"
- module_mode = "select"
-
-/stat_rig_module/select/CanUse()
- if(module.selectable)
- name = module.holder.selected_module == module ? "Selected" : "Select"
- return 1
- return 0
-
-/stat_rig_module/charge/New()
- ..()
- name = "Change Charge"
- module_mode = "select_charge_type"
-
-/stat_rig_module/charge/AddHref(var/list/href_list)
- var/charge_index = module.charges.Find(module.charge_selected)
- if(!charge_index)
- charge_index = 0
- else
- charge_index = charge_index == module.charges.len ? 1 : charge_index+1
-
- href_list["charge_type"] = module.charges[charge_index]
-
-/stat_rig_module/charge/CanUse()
- if(module.charges && module.charges.len)
- var/datum/rig_charge/charge = module.charges[module.charge_selected]
- name = "[charge.display_name] ([charge.charges]C) - Change"
- return 1
- return 0
diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
deleted file mode 100644
index 6e4b588ac24..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Contains
- * /obj/item/rig_module/stealth_field
- * /obj/item/rig_module/teleporter
- * /obj/item/rig_module/fabricator/energy_net
- * /obj/item/rig_module/self_destruct
- */
-
-/obj/item/rig_module/stealth_field
-
- name = "active camouflage module"
- desc = "A robust hardsuit-integrated stealth module."
- icon_state = "cloak"
-
- toggleable = 1
- disruptable = 1
- disruptive = 0
-
- use_power_cost = 50
- active_power_cost = 10
- passive_power_cost = 0
- module_cooldown = 30
-
- activate_string = "Enable Cloak"
- deactivate_string = "Disable Cloak"
-
- interface_name = "integrated stealth system"
- interface_desc = "An integrated active camouflage system."
-
- suit_overlay_active = "stealth_active"
- suit_overlay_inactive = "stealth_inactive"
-
-/obj/item/rig_module/stealth_field/activate()
-
- if(!..())
- return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- to_chat(H, "You are now invisible to normal detection.")
- H.invisibility = INVISIBILITY_LEVEL_TWO
-
- H.visible_message("[H.name] vanishes into thin air!",1)
-
-/obj/item/rig_module/stealth_field/deactivate()
-
- if(!..())
- return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- to_chat(H, "You are now visible.")
- H.invisibility = 0
-
- new /obj/effect/temp_visual/dir_setting/ninja(get_turf(H), H.dir)
-
- for(var/mob/O in oviewers(H))
- O.show_message("[H.name] appears from thin air!",1)
- playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 75, 1)
-
-
-/obj/item/rig_module/teleporter
-
- name = "teleportation module"
- desc = "A complex, sleek-looking, hardsuit-integrated teleportation module."
- icon_state = "teleporter"
- use_power_cost = 40
- redundant = 1
- usable = 1
- selectable = 1
-
- engage_string = "Emergency Leap"
-
- interface_name = "VOID-shift phase projector"
- interface_desc = "An advanced teleportation system. It is capable of pinpoint precision or random leaps forward."
-
-/obj/item/rig_module/teleporter/proc/phase_in(var/mob/M,var/turf/T)
-
- if(!M || !T)
- return
-
- holder.spark_system.start()
- playsound(T, 'sound/effects/phasein.ogg', 25, 1)
- playsound(T, 'sound/effects/sparks2.ogg', 50, 1)
- new /obj/effect/temp_visual/dir_setting/ninja/phase(T, M.dir)
-
-/obj/item/rig_module/teleporter/proc/phase_out(var/mob/M,var/turf/T)
-
- if(!M || !T)
- return
-
- playsound(T, "sparks", 50, 1)
- new /obj/effect/temp_visual/dir_setting/ninja/phase/out(T, M.dir)
-
-/obj/item/rig_module/teleporter/engage(var/atom/target, var/notify_ai)
-
- if(!..()) return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- if(!istype(H.loc, /turf))
- to_chat(H, "You cannot teleport out of your current location.")
- return 0
-
- var/turf/T
- if(target)
- T = get_turf(target)
- else
- T = get_teleport_loc(get_turf(H), H, rand(5, 9))
-
- /*if(!T || T.density)
- to_chat(H, "You cannot teleport into solid walls.")
- return 0*///Who the fuck cares? Ninjas in walls are cool.
-
- if(!is_teleport_allowed(T.z))
- to_chat(H, "You cannot use your teleporter on this Z-level.")
- return 0
-
- phase_out(H,get_turf(H))
- H.forceMove(T)
- phase_in(H,get_turf(H))
-
- for(var/obj/item/grab/G in H.contents)
- if(G.affecting)
- phase_out(G.affecting,get_turf(G.affecting))
- G.affecting.forceMove(locate(T.x+rand(-1,1),T.y+rand(-1,1),T.z))
- phase_in(G.affecting,get_turf(G.affecting))
-
- return 1
-
-/*
-/obj/item/rig_module/fabricator/energy_net
-
- name = "net projector"
- desc = "Some kind of complex energy projector with a hardsuit mount."
- icon_state = "enet"
-
- interface_name = "energy net launcher"
- interface_desc = "An advanced energy-patterning projector used to capture targets."
-
- engage_string = "Fabricate Net"
-
- fabrication_type = /obj/item/energy_net
- use_power_cost = 70
-
-/obj/item/rig_module/fabricator/energy_net/engage(atom/target)
-
- if(holder && holder.wearer)
- if(..(target) && target)
- holder.wearer.Beam(target,"n_beam",,10)
- return 1
- return 0*/
-
-/obj/item/rig_module/self_destruct
-
- name = "self-destruct module"
- desc = "Oh my God, Captain. A bomb."
- icon_state = "deadman"
- usable = 1
- active = 1
- permanent = 1
-
- engage_string = "Detonate"
-
- interface_name = "dead man's switch"
- interface_desc = "An integrated self-destruct module. When the wearer dies, so does the surrounding area. Do not press this button."
-
-/obj/item/rig_module/self_destruct/activate()
- return
-
-/obj/item/rig_module/self_destruct/deactivate()
- return
-
-/obj/item/rig_module/self_destruct/process()
-
- // Not being worn, leave it alone.
- if(!holder || !holder.wearer || !holder.wearer.wear_suit == holder)
- return 0
-
- //OH SHIT.
- if(holder.wearer.stat == 2)
- engage()
-
-/obj/item/rig_module/self_destruct/engage()
- explosion(get_turf(src), 1, 2, 4, 5)
- if(holder && holder.wearer)
- holder.wearer.unEquip(src)
- qdel(holder)
- qdel(src)
-
-/obj/item/rig_module/self_destruct/small/engage()
- explosion(get_turf(src), 0, 0, 3, 4)
- if(holder && holder.wearer)
- holder.wearer.unEquip(src)
- qdel(holder)
- qdel(src)
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
deleted file mode 100644
index 4b19d2a7753..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ /dev/null
@@ -1,476 +0,0 @@
-/* Contains:
- * /obj/item/rig_module/device
- * /obj/item/rig_module/device/plasmacutter
- * /obj/item/rig_module/device/healthscanner
- * /obj/item/rig_module/device/drill
- * /obj/item/rig_module/device/orescanner
- * /obj/item/rig_module/device/rcd
- * /obj/item/rig_module/device/anomaly_scanner
- * /obj/item/rig_module/maneuvering_jets
- * /obj/item/rig_module/foam_sprayer
- * /obj/item/rig_module/device/broadcaster
- * /obj/item/rig_module/chem_dispenser
- * /obj/item/rig_module/chem_dispenser/injector
- * /obj/item/rig_module/voice
- * /obj/item/rig_module/device/paperdispenser
- * /obj/item/rig_module/device/pen
- * /obj/item/rig_module/device/stamp
- */
-
-/obj/item/rig_module/device
- name = "mounted device"
- desc = "Some kind of hardsuit mount."
- usable = 0
- selectable = 1
- toggleable = 0
- disruptive = 0
-
- var/device_type
- var/obj/item/device
-
-/obj/item/rig_module/device/plasmacutter
- name = "hardsuit plasma cutter"
- desc = "A lethal-looking industrial cutter."
- icon_state = "plasmacutter"
- interface_name = "plasma cutter"
- interface_desc = "A self-sustaining plasma arc capable of cutting through walls."
- suit_overlay_active = "plasmacutter"
- suit_overlay_inactive = "plasmacutter"
-
- device_type = /obj/item/gun/energy/plasmacutter
-
-/obj/item/rig_module/device/healthscanner
- name = "health scanner module"
- desc = "A hardsuit-mounted health scanner."
- icon_state = "scanner"
- interface_name = "health scanner"
- interface_desc = "Shows an informative health readout when used on a subject."
-
- device_type = /obj/item/healthanalyzer
-
-/obj/item/rig_module/device/drill
- name = "hardsuit drill mount"
- desc = "A very heavy diamond-tipped drill."
- icon_state = "drill"
- interface_name = "mounted drill"
- interface_desc = "A diamond-tipped industrial drill."
- suit_overlay_active = "mounted-drill"
- suit_overlay_inactive = "mounted-drill"
- device_type = /obj/item/pickaxe/drill/diamonddrill
-
-/obj/item/rig_module/device/orescanner
- name = "ore scanner module"
- desc = "A clunky old ore scanner."
- icon_state = "scanner"
- interface_name = "ore detector"
- interface_desc = "A sonar system for detecting large masses of ore."
- engage_string = "Begin Scan"
- usable = 1
- selectable = 0
- device_type = /obj/item/mining_scanner
-/*
-/obj/item/rig_module/device/rcd
- name = "RCD mount"
- desc = "A cell-powered rapid construction device for a hardsuit."
- icon_state = "rcd"
- interface_name = "mounted RCD"
- interface_desc = "A device for building or removing walls. Cell-powered."
- usable = 1
- engage_string = "Configure RCD"
-
- device_type = /obj/item/rcd/mounted
-*/
-/obj/item/rig_module/device/New()
- ..()
- if(device_type)
- device = new device_type(src)
- device.flags |= ABSTRACT //Abstract in the sense that it's not an item that stands alone, but rather is just there to let the module act like it.
-
-/obj/item/rig_module/device/engage(atom/target)
- if(!..() || !device)
- return 0
-
- if(!target)
- device.attack_self(holder.wearer)
- return 1
-
- var/turf/T = get_turf(target)
- if(istype(T) && !T.Adjacent(get_turf(src)))
- return 0
-
- var/resolved = target.attackby(device,holder.wearer)
- if(!resolved && device && target)
- device.afterattack(target,holder.wearer,1)
- return 1
-
-
-
-/obj/item/rig_module/chem_dispenser
- name = "mounted chemical dispenser"
- desc = "A complex web of tubing and needles suitable for hardsuit use."
- icon_state = "injector"
- usable = 1
- selectable = 0
- toggleable = 0
- disruptive = 0
-
- engage_string = "Inject"
-
- interface_name = "integrated chemical dispenser"
- interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream."
-
- charges = list(
- list("saline-glucose", "salglu_solution", 0, 80),
- list("salicylic acid", "sal_acid", 0, 80),
- list("salbutamol", "salbutamol", 0, 80),
- list("antibiotics", "spaceacillin", 0, 80),
- list("charcoal", "charcoal", 0, 80),
- list("nutrients", "nutriment", 0, 80),
- list("potasssium iodide","potass_iodide", 0, 80),
- list("radium", "radium", 0, 80)
- )
-
- var/max_reagent_volume = 80 //Used when refilling.
-
-/obj/item/rig_module/chem_dispenser/ninja
- interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible."
-
- //just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face.
- charges = list(
- list("saline-glucose", "salglu_solution", 0, 20),
- list("salicylic acid", "sal_acid", 0, 20),
- list("salbutamol", "salbutamol", 0, 20),
- list("antibiotics", "spaceacillin", 0, 20),
- list("charcoal", "charcoal", 0, 20),
- list("nutrients", "nutriment", 0, 80),
- list("potasssium iodide","potass_iodide", 0, 20),
- list("radium", "radium", 0, 20)
- )
-
-
-/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user)
-
- if(!input_item.is_open_container())
- return 0
-
- if(!input_item.reagents || !input_item.reagents.total_volume)
- to_chat(user, "\The [input_item] is empty.")
- return 0
-
- // Magical chemical filtration system, do not question it.
- var/total_transferred = 0
- for(var/datum/reagent/R in input_item.reagents.reagent_list)
- for(var/chargetype in charges)
- var/datum/rig_charge/charge = charges[chargetype]
- if(charge.display_name == R.id)
-
- var/chems_to_transfer = R.volume
-
- if((charge.charges + chems_to_transfer) > max_reagent_volume)
- chems_to_transfer = max_reagent_volume - charge.charges
-
- charge.charges += chems_to_transfer
- input_item.reagents.remove_reagent(R.id, chems_to_transfer)
- total_transferred += chems_to_transfer
-
- break
-
- if(total_transferred)
- to_chat(user, "You transfer [total_transferred] units into the suit reservoir.")
- else
- to_chat(user, "None of the reagents seem suitable.")
- return 1
-
-/obj/item/rig_module/chem_dispenser/engage(atom/target)
-
- if(!..())
- return 0
-
- var/mob/living/carbon/human/H = holder.wearer
-
- if(!charge_selected)
- to_chat(H, "You have not selected a chemical type.")
- return 0
-
- var/datum/rig_charge/charge = charges[charge_selected]
-
- if(!charge)
- return 0
-
- var/chems_to_use = 10
- if(charge.charges <= 0)
- to_chat(H, "Insufficient chems!")
- return 0
- else if(charge.charges < chems_to_use)
- chems_to_use = charge.charges
-
- var/mob/living/carbon/target_mob
- if(target)
- if(istype(target,/mob/living/carbon))
- target_mob = target
- else
- return 0
- else
- target_mob = H
-
- if(target_mob != H)
- to_chat(H, "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name].")
- to_chat(target_mob, "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected.")
- target_mob.reagents.add_reagent(charge.display_name, chems_to_use)
-
- charge.charges -= chems_to_use
- if(charge.charges < 0) charge.charges = 0
-
- return 1
-
-/obj/item/rig_module/chem_dispenser/combat
-
- name = "combat chemical injector"
- desc = "A complex web of tubing and needles suitable for hardsuit use."
-
- charges = list(
- list("synaptizine", "synaptizine", 0, 30),
- list("hydrocodone", "hydrocodone", 0, 30),
- list("nutrients", "nutriment", 0, 80),
- )
-
- interface_name = "combat chem dispenser"
- interface_desc = "Dispenses loaded chemicals directly into the bloodstream."
-
-
-/obj/item/rig_module/chem_dispenser/injector
-
- name = "mounted chemical injector"
- desc = "A complex web of tubing and a large needle suitable for hardsuit use."
- usable = 0
- selectable = 1
- disruptive = 1
-
- interface_name = "mounted chem injector"
- interface_desc = "Dispenses loaded chemicals via an arm-mounted injector."
-
-/obj/item/rig_module/voice
-
- name = "hardsuit voice synthesiser"
- desc = "A speaker box and sound processor."
- icon_state = "megaphone"
- usable = 1
- selectable = 0
- toggleable = 0
- disruptive = 0
-
- engage_string = "Configure Synthesiser"
-
- interface_name = "voice synthesiser"
- interface_desc = "A flexible and powerful voice modulator system."
-
- var/obj/item/voice_changer/voice_holder
-
-/obj/item/rig_module/voice/New()
- ..()
- voice_holder = new(src)
- voice_holder.active = FALSE
-
-/obj/item/rig_module/voice/installed()
- ..()
- holder.speech = src
-
-/obj/item/rig_module/voice/engage()
- if(!..())
- return 0
-
- var/choice= input("Would you like to toggle the synthesiser or set the name?") as null|anything in list("Enable","Disable","Set Name")
-
- if(!choice)
- return 0
-
- switch(choice)
- if("Enable")
- active = TRUE
- voice_holder.active = TRUE
- to_chat(usr, "You enable the speech synthesiser.")
- if("Disable")
- active = FALSE
- voice_holder.active = FALSE
- to_chat(usr, "You disable the speech synthesiser.")
- if("Set Name")
- var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN)
- if(!raw_choice)
- return FALSE
- voice_holder.voice = raw_choice
- to_chat(usr, "You are now mimicking [voice_holder.voice].")
- return 1
-
-/obj/item/rig_module/maneuvering_jets
-
- name = "hardsuit maneuvering jets"
- desc = "A compact gas thruster system for a hardsuit."
- icon_state = "thrusters"
- usable = 1
- toggleable = 1
- selectable = 0
- disruptive = 0
-
- suit_overlay_active = "maneuvering_active"
- suit_overlay_inactive = null //"maneuvering_inactive"
-
- engage_string = "Toggle Stabilizers"
- activate_string = "Activate Thrusters"
- deactivate_string = "Deactivate Thrusters"
-
- interface_name = "maneuvering jets"
- interface_desc = "An inbuilt EVA maneuvering system that runs off the rig air supply."
-
- var/obj/item/tank/jetpack/rig/jets
-
-/obj/item/rig_module/maneuvering_jets/engage()
- if(!..())
- return 0
- jets.toggle_stabilization(usr)
- return 1
-
-/obj/item/rig_module/maneuvering_jets/activate()
-
- if(active)
- return 0
-
- active = 1
-
- spawn(1)
- if(suit_overlay_active)
- suit_overlay = suit_overlay_active
- else
- suit_overlay = null
- holder.update_icon()
-
- jets.turn_on()
- return 1
-
-/obj/item/rig_module/maneuvering_jets/deactivate()
- if(!..())
- return 0
- jets.turn_off()
- return 1
-
-/obj/item/rig_module/maneuvering_jets/New()
- ..()
- jets = new(src)
-
-/obj/item/rig_module/maneuvering_jets/installed()
- ..()
- jets.holder = holder
- jets.ion_trail.set_up(holder)
-
-/obj/item/rig_module/maneuvering_jets/removed()
- ..()
- jets.holder = null
- jets.ion_trail.set_up(jets)
-
-/obj/item/rig_module/foam_sprayer
-
-/obj/item/rig_module/device/paperdispenser
- name = "hardsuit paper dispenser"
- desc = "Crisp sheets."
- icon_state = "paper"
- interface_name = "paper dispenser"
- interface_desc = "Dispenses warm, clean, and crisp sheets of paper."
- engage_string = "Dispense"
- usable = 1
- selectable = 0
- device_type = /obj/item/paper_bin
-
-/obj/item/rig_module/device/paperdispenser/engage(atom/target)
-
- if(!..() || !device)
- return 0
-
- if(!target)
- device.attack_hand(holder.wearer)
- return 1
-
-/obj/item/rig_module/device/pen
- name = "mounted pen"
- desc = "For mecha John Hancocks."
- icon_state = "pen"
- interface_name = "mounted pen"
- interface_desc = "Signatures with style(tm)."
- engage_string = "Change color"
- usable = 1
- device_type = /obj/item/pen/multi
-
-/obj/item/rig_module/device/stamp
- name = "mounted internal affairs stamp"
- desc = "DENIED."
- icon_state = "stamp"
- interface_name = "mounted stamp"
- interface_desc = "Leave your mark."
- engage_string = "Toggle stamp type"
- usable = 1
- var/obj/iastamp //Theese were just vars, but any device would need to be an object
- var/obj/deniedstamp //Stops assigning non-objects to theese vars, which probably would break quite a bit.
-
-/obj/item/rig_module/device/stamp/New()
- ..()
- iastamp = new /obj/item/stamp/law(src)
- deniedstamp = new /obj/item/stamp/denied(src)
- iastamp.flags |= ABSTRACT
- deniedstamp.flags |= ABSTRACT
- device = iastamp
-
-/obj/item/rig_module/device/stamp/engage(atom/target)
- if(!..() || !device)
- return 0
-
- if(!target)
- if(device == iastamp)
- device = deniedstamp
- to_chat(holder.wearer, "Switched to denied stamp.")
- else if(device == deniedstamp)
- device = iastamp
- to_chat(holder.wearer, "Switched to internal affairs stamp.")
- return 1
-
-/obj/item/rig_module/welding_tank
- name = "welding fuel tank"
- desc = "A bluespace welding fuel storage tank for a rigsuit."
- icon_state = "welding_tank"
- interface_name = "mounted welding fuel tank"
- interface_desc = "A minitaure fuel tank used for storage of welding fuel, built into a hardsuit."
- engage_string = "Dispense fuel"
- usable = 1
-
- var/max_fuel = 300
-
-/obj/item/rig_module/welding_tank/New()
- ..()
-
- create_reagents(max_fuel)
- reagents.add_reagent("fuel", max_fuel)
-
-/obj/item/rig_module/welding_tank/engage(atom/target)
- if(!..() || !reagents)
- return 0
-
- if(!target)
- if(get_fuel() >= 0)
- var/obj/item/weldingtool/W = holder.wearer.get_active_hand()
- if(istype(W))
- fill_welder(W)
- else
- W = holder.wearer.get_inactive_hand()
- if(istype(W))
- fill_welder(W)
- else
- to_chat(holder.wearer, "Your welding tank is out of fuel!")
- else
- to_chat(holder.wearer, "You need to have a welding tool in one of your hands to dispense fuel.")
-
-/obj/item/rig_module/welding_tank/proc/fill_welder(obj/item/weldingtool/W)
- if(!istype(W))
- return
- W.refill(holder.wearer, src, W.maximum_fuel)
- if(!reagents.get_reagent_amount("fuel"))
- to_chat(holder.wearer, "You hear a faint dripping as your hardsuit welding tank completely empties.")
-
-/obj/item/rig_module/welding_tank/proc/get_fuel()
- return reagents.get_reagent_amount("fuel")
diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm
deleted file mode 100644
index 33ae007ceee..00000000000
--- a/code/modules/clothing/spacesuits/rig/modules/vision.dm
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Contains
- * /obj/item/rig_module/vision
- * /obj/item/rig_module/vision/multi
- * /obj/item/rig_module/vision/meson
- * /obj/item/rig_module/vision/thermal
- * /obj/item/rig_module/vision/nvg
- * /obj/item/rig_module/vision/medhud
- * /obj/item/rig_module/vision/sechud
- */
-
-/datum/rig_vision
- var/mode
- var/obj/item/clothing/glasses/glasses
-
-/datum/rig_vision/nvg
- mode = "night vision"
-/datum/rig_vision/nvg/New()
- glasses = new /obj/item/clothing/glasses/night
-
-/datum/rig_vision/thermal
- mode = "thermal scanner"
-/datum/rig_vision/thermal/New()
- glasses = new /obj/item/clothing/glasses/thermal
-
-/datum/rig_vision/meson
- mode = "meson scanner"
-/datum/rig_vision/meson/New()
- glasses = new /obj/item/clothing/glasses/meson
-
-/datum/rig_vision/sechud
- mode = "security HUD"
-/datum/rig_vision/sechud/New()
- glasses = new /obj/item/clothing/glasses/hud/security
-
-/datum/rig_vision/medhud
- mode = "medical HUD"
-/datum/rig_vision/medhud/New()
- glasses = new /obj/item/clothing/glasses/hud/health
-
-/obj/item/rig_module/vision
-
- name = "hardsuit visor"
- desc = "A layered, translucent visor system for a hardsuit."
- icon_state = "optics"
-
- interface_name = "optical scanners"
- interface_desc = "An integrated multi-mode vision system."
-
- usable = 1
- toggleable = 1
- disruptive = 0
-
- engage_string = "Cycle Visor Mode"
- activate_string = "Enable Visor"
- deactivate_string = "Disable Visor"
-
- var/datum/rig_vision/vision
- var/list/vision_modes = list(
- /datum/rig_vision/nvg,
- /datum/rig_vision/thermal,
- /datum/rig_vision/meson
- )
-
- var/vision_index
-
-/obj/item/rig_module/vision/multi
-
- name = "hardsuit optical package"
- desc = "A complete visor system of optical scanners and vision modes."
- icon_state = "fulloptics"
-
-
- interface_name = "multi optical visor"
- interface_desc = "An integrated multi-mode vision system."
-
- vision_modes = list(/datum/rig_vision/meson,
- /datum/rig_vision/nvg,
- /datum/rig_vision/thermal,
- /datum/rig_vision/sechud,
- /datum/rig_vision/medhud)
-
-/obj/item/rig_module/vision/meson
-
- name = "hardsuit meson scanner"
- desc = "A layered, translucent visor system for a hardsuit."
- icon_state = "meson"
-
- usable = 0
-
- interface_name = "meson scanner"
- interface_desc = "An integrated meson scanner."
-
- vision_modes = list(/datum/rig_vision/meson)
-
-/obj/item/rig_module/vision/thermal
-
- name = "hardsuit thermal scanner"
- desc = "A layered, translucent visor system for a hardsuit."
- icon_state = "thermal"
-
- usable = 0
-
- interface_name = "thermal scanner"
- interface_desc = "An integrated thermal scanner."
-
- vision_modes = list(/datum/rig_vision/thermal)
-
-/obj/item/rig_module/vision/nvg
-
- name = "hardsuit night vision interface"
- desc = "A multi input night vision system for a hardsuit."
- icon_state = "night"
-
- usable = 0
-
- interface_name = "night vision interface"
- interface_desc = "An integrated night vision system."
-
- vision_modes = list(/datum/rig_vision/nvg)
-
-/obj/item/rig_module/vision/sechud
-
- name = "hardsuit security hud"
- desc = "A simple tactical information system for a hardsuit."
- icon_state = "securityhud"
-
- usable = 0
-
- interface_name = "security HUD"
- interface_desc = "An integrated security heads up display."
-
- vision_modes = list(/datum/rig_vision/sechud)
-
-/obj/item/rig_module/vision/medhud
-
- name = "hardsuit medical hud"
- desc = "A simple medical status indicator for a hardsuit."
- icon_state = "healthhud"
-
- usable = 0
-
- interface_name = "medical HUD"
- interface_desc = "An integrated medical heads up display."
-
- vision_modes = list(/datum/rig_vision/medhud)
-
-
-// There should only ever be one vision module installed in a suit.
-/obj/item/rig_module/vision/installed()
- ..()
- holder.visor = src
-
-/obj/item/rig_module/vision/engage()
-
- var/starting_up = !active
-
- if(!..() || !vision_modes)
- return 0
-
- // Don't cycle if this engage() is being called by activate().
- if(starting_up)
- to_chat(holder.wearer, "You activate your visual sensors.")
- return 1
-
- if(vision_modes.len > 1)
- vision_index++
- if(vision_index > vision_modes.len)
- vision_index = 1
- vision = vision_modes[vision_index]
-
- to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.")
- else
- to_chat(holder.wearer, "Your sensors only have one mode.")
- return 1
-
-/obj/item/rig_module/vision/New()
- ..()
-
- if(!vision_modes)
- return
-
- vision_index = 1
- var/list/processed_vision = list()
-
- for(var/vision_mode in vision_modes)
- var/datum/rig_vision/vision_datum = new vision_mode
- if(!vision) vision = vision_datum
- processed_vision += vision_datum
-
- vision_modes = processed_vision
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
deleted file mode 100644
index 0d106a874a7..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ /dev/null
@@ -1,1059 +0,0 @@
-#define ONLY_DEPLOY 1
-#define ONLY_RETRACT 2
-#define SEAL_DELAY 30
-
-/*
- * Defines the behavior of hardsuits/rigs/power armour.
- */
-
-/obj/item/rig
-
- name = "hardsuit control module"
- icon = 'icons/obj/rig_modules.dmi'
- desc = "A back-mounted hardsuit deployment and control mechanism."
- slot_flags = SLOT_BACK
- req_one_access = list()
- req_access = list()
- w_class = WEIGHT_CLASS_BULKY
-
- // These values are passed on to all component pieces.
- armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75)
- min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
- max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
- siemens_coefficient = 0.2
- permeability_coefficient = 0.1
-
- var/interface_path = "hardsuit.tmpl"
- var/ai_interface_path = "hardsuit.tmpl"
- var/interface_title = "Hardsuit Controller"
- var/wearer_move_delay //Used for AI moving.
- var/ai_controlled_move_delay = 10
-
- // Keeps track of what this rig should spawn with.
- var/suit_type = "hardsuit"
- var/list/initial_modules
- var/chest_type = /obj/item/clothing/suit/space/new_rig
- var/helm_type = /obj/item/clothing/head/helmet/space/new_rig
- var/boot_type = /obj/item/clothing/shoes/magboots/rig
- var/glove_type = /obj/item/clothing/gloves/rig
- var/cell_type = /obj/item/stock_parts/cell/high
- var/air_type = /obj/item/tank/oxygen
-
- //Component/device holders.
- var/obj/item/tank/air_supply // Air tank, if any.
- var/obj/item/clothing/shoes/magboots/boots = null // Deployable boots, if any.
- var/obj/item/clothing/shoes/under_boots = null //Boots that are between the feet and the rig boots, if any.
- var/obj/item/clothing/suit/space/new_rig/chest // Deployable chestpiece, if any.
- var/obj/item/clothing/head/helmet/space/new_rig/helmet = null // Deployable helmet, if any.
- var/obj/item/clothing/gloves/rig/gloves = null // Deployable gauntlets, if any.
- var/obj/item/stock_parts/cell/cell // Power supply, if any.
- var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click)
- var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time.
- var/obj/item/rig_module/voice/speech // As above.
- var/mob/living/carbon/human/wearer // The person currently wearing the rig.
- var/image/mob_icon // Holder for on-mob icon.
- var/list/installed_modules = list() // Power consumption/use bookkeeping.
-
- // Rig status vars.
- var/open = 0 // Access panel status.
- var/locked = 1 // Lock status.
- var/subverted = 0
- var/interface_locked = 0
- var/control_overridden = 0
- var/ai_override_enabled = 0
- var/security_check_enabled = 1
- var/malfunctioning = 0
- var/malfunction_delay = 0
- var/electrified = 0
- var/locked_down = 0
-
- var/seal_delay = SEAL_DELAY
- var/sealing // Keeps track of seal status independantly of NODROP.
- var/offline = 1 // Should we be applying suit maluses?
- var/offline_slowdown = 3 // If the suit is deployed and unpowered, it sets slowdown to this.
- var/active_slowdown = 3 // How much the deployed suit slows down if powered.
- var/vision_restriction
- var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets.
- var/airtight = 1 //If set, will adjust AIRTIGHT and STOPSPRESSUREDMAGE flags on components. Otherwise it should leave them untouched.
-
- var/emp_protection = 0
- var/has_emergency_release = 1 //Allows suit to be removed from outside.
-
- // Wiring! How exciting.
- var/datum/wires/rig/wires
- var/datum/effect_system/spark_spread/spark_system
-
-/obj/item/rig/examine(mob/user)
- . = list("This is [src].")
- . += "[desc]"
- if(wearer)
- for(var/obj/item/piece in list(helmet,gloves,chest,boots))
- if(!piece || piece.loc != wearer)
- continue
- . += "[bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed."
-
- if(loc == usr)
- . += "The maintenance panel is [open ? "open" : "closed"]."
- . += "Hardsuit systems are [offline ? "offline" : "online"]."
-
-/obj/item/rig/get_cell()
- return cell
-
-/obj/item/rig/New()
- ..()
-
- item_state = icon_state
- wires = new(src)
-
- if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len))
- locked = 0
-
- spark_system = new()
- spark_system.set_up(5, 0, src)
- spark_system.attach(src)
-
- START_PROCESSING(SSobj, src)
-
- if(initial_modules && initial_modules.len)
- for(var/path in initial_modules)
- var/obj/item/rig_module/module = new path(src)
- installed_modules += module
- module.installed(src)
-
- // Create and initialize our various segments.
- if(cell_type)
- cell = new cell_type(src)
- if(air_type)
- air_supply = new air_type(src)
- if(glove_type)
- gloves = new glove_type(src)
- verbs |= /obj/item/rig/proc/toggle_gauntlets
- if(helm_type)
- helmet = new helm_type(src)
- verbs |= /obj/item/rig/proc/toggle_helmet
- helmet.item_color="[initial(icon_state)]_sealed" //For the lightswitching to know the correct string to manipulate
- if(boot_type)
- boots = new boot_type(src)
- verbs |= /obj/item/rig/proc/toggle_boots
- boots.magboot_state="[initial(icon_state)]_sealed" //For the magboot (de)activation to know the correct string to manipulate
- if(chest_type)
- chest = new chest_type(src)
- if(allowed)
- chest.allowed = allowed
- chest.slowdown = offline_slowdown
- chest.holder = src
- verbs |= /obj/item/rig/proc/toggle_chest
-
- for(var/obj/item/piece in list(gloves,helmet,boots,chest))
- if(!istype(piece))
- continue
- piece.name = "[suit_type] [initial(piece.name)]"
- piece.desc = "It seems to be part of a [src.name]."
- piece.icon_state = "[initial(icon_state)]"
- piece.min_cold_protection_temperature = min_cold_protection_temperature
- piece.max_heat_protection_temperature = max_heat_protection_temperature
- if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation.
- piece.siemens_coefficient = siemens_coefficient
- piece.permeability_coefficient = permeability_coefficient
- if(armor)
- piece.armor = armor
-
- update_icon(1)
-
-/obj/item/rig/Destroy()
- for(var/obj/item/piece in list(gloves,boots,helmet,chest))
- var/mob/living/M = piece.loc
- if(istype(M))
- M.unEquip(piece)
- qdel(piece)
- STOP_PROCESSING(SSobj, src)
- QDEL_NULL(wires)
- QDEL_NULL(spark_system)
- return ..()
-
-/obj/item/rig/proc/suit_is_deployed()
- if(!istype(wearer) || src.loc != wearer || wearer.back != src)
- return 0
- if(helm_type && !(helmet && wearer.head == helmet))
- return 0
- if(glove_type && !(gloves && wearer.gloves == gloves))
- return 0
- if(boot_type && !(boots && wearer.shoes == boots))
- return 0
- if(chest_type && !(chest && wearer.wear_suit == chest))
- return 0
- return 1
-
-/obj/item/rig/proc/reset()
- offline = 2
- flags &= ~NODROP
- if(helmet && helmet.on)
- helmet.toggle_light(wearer)
- if(boots && boots.magpulse)
- boots.attack_self(wearer)
- for(var/obj/item/piece in list(helmet,boots,gloves,chest))
- if(!piece) continue
- piece.icon_state = "[initial(icon_state)]"
- if(airtight)
- piece.flags &= ~(STOPSPRESSUREDMAGE | AIRTIGHT)
- update_icon(1)
-
-/obj/item/rig/proc/seal(mob/living/user)
- if(sealing)
- return 0
-
- if(!wearer || !user)
- return
-
- var/sealed = (flags & NODROP)
- if(sealed)
- to_chat(user, "\The [src] is already sealed!")
- return 0
-
- if(!check_power_cost(user, 1)) //need power to seal the suit
- return 0
-
- var/failed_to_seal = FALSE
-
- if(!suit_is_deployed())
- to_chat(user, "\The [src] cannot seal, as it is not fully deployed!")
- return 0
-
- flags |= NODROP
- sealing = TRUE
-
- to_chat(user, "\The [src] begins to tighten it's seals.")
- wearer.visible_message("\The [wearer]'s suit emits a quiet hum as it begins to tighten it's seals.",
- "With a quiet hum, your suit begins to seal.")
-
- if(seal_delay && !do_after(user, seal_delay, target = wearer))
- to_chat(user, "You must remain still to seal \the [src]!")
- failed_to_seal = TRUE
-
- if(!failed_to_seal)
- deploy(user)
-
- var/list/pieces_data = list(list(wearer.shoes, boots, "boots", boot_type),
- list(wearer.gloves, gloves, "gloves", glove_type),
- list(wearer.head, helmet, "helmet", helm_type),
- list(wearer.wear_suit, chest, "chest", chest_type))
-
- for(var/list/piece_data in pieces_data)
- var/obj/item/user_piece = piece_data[1]
- var/obj/item/correct_piece = piece_data[2]
- var/msg_type = piece_data[3]
- var/piece_type = piece_data[4]
-
- if(!user_piece || !piece_type)
- continue
-
- if(user_piece != correct_piece)
- to_chat(user, "\The [user_piece] is blocking \the [src] from deploying.")
- failed_to_seal = TRUE
-
- if(seal_delay && !do_after(user, seal_delay, needhand = 0, target = wearer))
- to_chat(user, "You must remain still to seal \the [src]!")
- failed_to_seal = TRUE
-
- if(failed_to_seal)
- break
-
- correct_piece.icon_state = "[initial(icon_state)]_sealed"
- switch(msg_type)
- if("boots")
- to_chat(wearer, "\The [correct_piece] seal around your feet.")
- correct_piece.icon_state = "[initial(icon_state)]_sealed0" //Solution to not need a sprite for off, on, and unused magboots.
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been sealed.")
- wearer.update_inv_shoes()
- if("gloves")
- to_chat(wearer, "\The [correct_piece] tighten around your fingers and wrists.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been sealed.")
- wearer.update_inv_gloves()
- if("chest")
- to_chat(wearer, "\The [correct_piece] cinches tight again your chest.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been sealed.")
- wearer.update_inv_wear_suit()
- if("helmet")
- to_chat(wearer, "\The [correct_piece] hisses closed.")
- correct_piece.icon_state = "[initial(icon_state)]_sealed0" //Solution to not need a sprite for off, on, and unused helmet light.
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been sealed.")
- wearer.update_inv_head()
- if(helmet)
- helmet.update_light(wearer)
-
- correct_piece.armor = correct_piece.armor.setRating(bio_value = 100)
-
- sealing = FALSE
-
- if(failed_to_seal)
- for(var/obj/item/piece in list(helmet, boots, gloves, chest))
- if(!piece)
- continue
- piece.icon_state = "[initial(icon_state)]"
- flags &= ~NODROP
- if(airtight)
- update_component_sealed()
- update_icon(1)
- return 0
-
- if(user != wearer)
- to_chat(user, "\The [src] has been loosened.")
- to_chat(wearer, "Your entire suit tightens around you as the components lock into place.")
- if(airtight)
- update_component_sealed()
- update_icon(1)
-
-/obj/item/rig/proc/unseal(mob/living/user)
- if(sealing)
- return 0
-
- if(!wearer || !user)
- return
-
- var/sealed = (flags & NODROP)
- if(!sealed)
- to_chat(user, "\The [src] is already unsealed!")
- return 0
-
- sealing = TRUE
-
- var/failed_to_seal = FALSE
-
- if(!suit_is_deployed())
- to_chat(user, "\The [src] cannot unseal, as it is not fully deployed!")
- failed_to_seal = TRUE
-
- if(!failed_to_seal)
- if(user != wearer)
- to_chat(user, "\The [src] begins to loosen it's seals.")
- wearer.visible_message("\The [wearer]'s suit emits a quiet hum as it begins to loosen it's seals.",
- "With a quiet hum, your suit begins to unseal.")
-
- if(seal_delay && !do_after(user, seal_delay, target = wearer))
- to_chat(user, "You must remain still to unseal \the [src]!")
- failed_to_seal = TRUE
-
- if(!failed_to_seal)
- var/list/pieces_data = list(list(wearer.shoes, boots, "boots", boot_type),
- list(wearer.gloves, gloves, "gloves", glove_type),
- list(wearer.head, helmet, "helmet", helm_type),
- list(wearer.wear_suit, chest, "chest", chest_type))
-
- for(var/list/piece_data in pieces_data)
- var/obj/item/user_piece = piece_data[1]
- var/obj/item/correct_piece = piece_data[2]
- var/msg_type = piece_data[3]
- var/piece_type = piece_data[4]
-
- if(!correct_piece || !piece_type)
- continue
-
- if(user_piece != correct_piece)
- to_chat(user, "\The [user_piece] is blocking \the [src] from deploying.")
- failed_to_seal = TRUE
-
- if(seal_delay && !do_after(user, seal_delay, needhand = 0, target = wearer))
- to_chat(user, "You must remain still to unseal \the [src]!")
- failed_to_seal = TRUE
-
- if(failed_to_seal)
- break
-
- correct_piece.icon_state = "[initial(icon_state)]"
- switch(msg_type)
- if("boots")
- to_chat(wearer, "\The [correct_piece] relax [correct_piece.p_their()] grip on your legs.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been unsealed.")
- wearer.update_inv_shoes()
- if("gloves")
- to_chat(wearer, "\The [correct_piece] become loose around your fingers.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been unsealed.")
- wearer.update_inv_gloves()
- if("chest")
- to_chat(wearer, "\The [correct_piece] releases your chest.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been unsealed.")
- wearer.update_inv_wear_suit()
- if("helmet")
- to_chat(wearer, "\The [correct_piece] hisses open.")
- if(user != wearer)
- to_chat(user, "\The [correct_piece] has been unsealed.")
- wearer.update_inv_head()
- if(helmet)
- helmet.update_light(wearer)
-
- correct_piece.armor = correct_piece.armor.setRating(bio_value = armor.getRating("bio"))
-
- sealing = FALSE
-
- if(failed_to_seal)
- for(var/obj/item/piece in list(gloves, chest))
- if(!piece)
- continue
- piece.icon_state = "[initial(icon_state)]_sealed"
- if(helmet)
- helmet.icon_state = "[initial(icon_state)]_sealed[helmet.on]"
- if(boots)
- boots.icon_state = "[initial(icon_state)]_sealed[boots.magpulse]"
- if(airtight)
- update_component_sealed()
- update_icon(1)
- return 0
-
- if(user != wearer)
- to_chat(user, "\The [src] has been unsealed.")
- to_chat(wearer, "Your entire suit loosens as the components relax.")
-
- flags &= ~NODROP
-
- for(var/obj/item/rig_module/module in installed_modules)
- module.deactivate()
-
- if(airtight)
- update_component_sealed()
- update_icon(1)
-
-/obj/item/rig/proc/update_component_sealed()
- if(istype(boots) && !(flags & NODROP) && boots.magpulse) //If we have (active) boots and unsealed the suit, we deactivate the magboots.
- boots.attack_self(wearer)
- if(istype(helmet) && !(flags & NODROP) && helmet.on) //If we have an (active) headlamp and unsealed the suit, we deactivate the headlamp.
- helmet.toggle_light(wearer)
- for(var/obj/item/piece in list(helmet,boots,gloves,chest))
- if(!(flags & NODROP))
- piece.flags &= ~(STOPSPRESSUREDMAGE | AIRTIGHT)
- else
- piece.flags |= STOPSPRESSUREDMAGE | AIRTIGHT
-
-/obj/item/rig/process()
- // If we've lost any parts, grab them back.
- var/mob/living/M
- for(var/obj/item/piece in list(gloves,boots,helmet,chest))
- if(piece.loc != src && !(wearer && piece.loc == wearer))
- if(istype(piece.loc, /mob/living))
- M = piece.loc
- M.unEquip(piece)
- piece.forceMove(src)
-
- if(cell && cell.charge > 0 && electrified > 0)
- electrified--
-
- if(malfunction_delay > 0)
- malfunction_delay--
- else if(malfunctioning)
- malfunctioning--
- malfunction()
-
- if(!istype(wearer) || loc != wearer || wearer.back != src || !(flags & NODROP) || !cell || cell.charge <= 0)
- if(!cell || cell.charge <= 0)
- if(electrified > 0)
- electrified = 0
- if(!offline)
- if(istype(wearer))
- if(flags & NODROP)
- if(offline_slowdown < 3)
- to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.")
- else
- to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.")
- if(offline_vision_restriction == 1)
- to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.")
- else if(offline_vision_restriction == 2)
- to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.")
- if(!offline)
- offline = 1
- if(istype(wearer) && wearer.wearing_rig)
- wearer.wearing_rig = null
- else
- if(offline)
- offline = 0
- if(istype(wearer) && !wearer.wearing_rig)
- wearer.wearing_rig = src
- chest.slowdown = active_slowdown
-
- if(offline)
- if(offline == 1)
- for(var/obj/item/rig_module/module in installed_modules)
- module.deactivate()
- offline = 2
- chest.slowdown = offline_slowdown
- return
-
-
- for(var/obj/item/rig_module/module in installed_modules)
- cell.use(module.process()*10)
-
-/obj/item/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai)
- if(!istype(user))
- return 0
-
- var/fail_msg
-
- if(!user_is_ai)
- var/mob/living/carbon/human/H = user
- if(istype(H) && H.back != src)
- fail_msg = "You must be wearing \the [src] to do this."
- else if(user.incorporeal_move)
- fail_msg = "You must be solid to do this."
- if(sealing)
- fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated."
- else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat)))
- fail_msg = "You are in no fit state to do that."
- else if(!cell)
- fail_msg = "There is no cell installed in the suit."
- else if(cost && cell.charge < cost * 10) //TODO: Cellrate?
- fail_msg = "Not enough stored power."
-
- if(fail_msg)
- to_chat(user, "[fail_msg]")
- return 0
-
- // This is largely for cancelling stealth and whatever.
- if(mod && mod.disruptive)
- for(var/obj/item/rig_module/module in (installed_modules - mod))
- if(module.active && module.disruptable)
- module.deactivate()
-
- cell.use(cost*10)
- return 1
-
-/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state)
- if(!user)
- return
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550, state = state)
- ui.open()
- ui.set_auto_update(1)
-
-/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state)
- var/data[0]
-
- data["primarysystem"] = null
- if(selected_module)
- data["primarysystem"] = "[selected_module.interface_name]"
-
- data["ai"] = 0
- if(src.loc != user)
- data["ai"] = 1
-
- var/is_sealed = (flags & NODROP) //1 if NODROP, 0 if no-nodrop
- data["seals"] = "[!is_sealed]" //1 if not NODROP (unsealed), 0 if NODROP (sealed)
- data["sealing"] = "[src.sealing]"
- data["helmet"] = (helmet ? "[helmet.name]" : "None.")
- data["gauntlets"] = (gloves ? "[gloves.name]" : "None.")
- data["boots"] = (boots ? "[boots.name]" : "None.")
- data["chest"] = (chest ? "[chest.name]" : "None.")
-
- data["charge"] = cell ? round(cell.charge,1) : 0
- data["maxcharge"] = cell ? cell.maxcharge : 0
- data["chargestatus"] = cell ? FLOOR((cell.charge/cell.maxcharge)*50, 1) : 0
-
- data["emagged"] = subverted
- data["coverlock"] = locked
- data["interfacelock"] = interface_locked
- data["aicontrol"] = control_overridden
- data["aioverride"] = ai_override_enabled
- data["securitycheck"] = security_check_enabled
- data["malf"] = malfunction_delay
-
-
- var/list/module_list = list()
- var/i = 1
- for(var/obj/item/rig_module/module in installed_modules)
- var/list/module_data = list(
- "index" = i,
- "name" = "[module.interface_name]",
- "desc" = "[module.interface_desc]",
- "can_use" = "[module.usable]",
- "can_select" = "[module.selectable]",
- "can_toggle" = "[module.toggleable]",
- "is_active" = "[module.active]",
- "engagecost" = module.use_power_cost*10,
- "activecost" = module.active_power_cost*10,
- "passivecost" = module.passive_power_cost*10,
- "engagestring" = module.engage_string,
- "activatestring" = module.activate_string,
- "deactivatestring" = module.deactivate_string,
- "damage" = module.damage
- )
-
- if(module.charges && module.charges.len)
-
- module_data["charges"] = list()
- var/datum/rig_charge/selected = module.charges[module.charge_selected]
- module_data["chargetype"] = selected ? "[selected.display_name]" : "none"
-
- for(var/chargetype in module.charges)
- var/datum/rig_charge/charge = module.charges[chargetype]
- module_data["charges"] += list(list("caption" = "[chargetype] ([charge.charges])", "index" = "[chargetype]"))
-
- module_list += list(module_data)
- i++
-
- if(module_list.len)
- data["modules"] = module_list
-
- return data
-
-/obj/item/rig/update_icon(var/update_mob_icon)
-
- //TODO: Maybe consider a cache for this (use mob_icon as blank canvas, use suit icon overlay).
- overlays.Cut()
- if(!mob_icon || update_mob_icon)
- var/species_icon = 'icons/mob/rig_back.dmi'
- // Since setting mob_icon will override the species checks in
- // update_inv_wear_suit(), handle species checks here.
- if(wearer && sprite_sheets && sprite_sheets[wearer.dna.species.name])
- species_icon = sprite_sheets[wearer.dna.species.name]
- mob_icon = image("icon" = species_icon, "icon_state" = "[icon_state]")
-
- if(installed_modules.len)
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.suit_overlay)
- chest.overlays += image("icon" = 'icons/mob/rig_modules.dmi', "icon_state" = "[module.suit_overlay]", "dir" = SOUTH)
-
- if(wearer)
- wearer.update_inv_shoes()
- wearer.update_inv_gloves()
- wearer.update_inv_head()
- wearer.update_inv_wear_suit()
- wearer.update_inv_back()
- return
-
-/obj/item/rig/proc/check_suit_access(var/mob/living/carbon/human/user)
-
- if(!security_check_enabled)
- return 1
-
- if(istype(user))
- if(malfunction_check(user))
- return 0
- if(user.back != src)
- return 0
- else if(!src.allowed(user))
- to_chat(user, "Unauthorized user. Access denied.")
- return 0
-
- else if(!ai_override_enabled)
- to_chat(user, "Synthetic access disabled. Please consult hardware provider.")
- return 0
-
- return 1
-
-/obj/item/rig/Topic(href,href_list)
- if(!check_suit_access(usr))
- return 0
-
- if(href_list["toggle_piece"])
- if(ishuman(usr) && (usr.stat || usr.stunned || usr.lying))
- return 0
- toggle_piece(href_list["toggle_piece"], usr)
- else if(href_list["toggle_seals"])
- if(flags & NODROP)
- unseal(usr)
- else
- seal(usr)
- else if(href_list["interact_module"])
- var/module_index = text2num(href_list["interact_module"])
-
- if(module_index > 0 && module_index <= installed_modules.len)
- var/obj/item/rig_module/module = installed_modules[module_index]
- switch(href_list["module_mode"])
- if("activate")
- module.activate()
- if("deactivate")
- module.deactivate()
- if("engage")
- module.engage()
- if("select")
- selected_module = module
- if("select_charge_type")
- module.charge_selected = href_list["charge_type"]
- else if(href_list["toggle_ai_control"])
- ai_override_enabled = !ai_override_enabled
- notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].")
- else if(href_list["toggle_suit_lock"])
- locked = !locked
-
- usr.set_machine(src)
- add_fingerprint(usr)
- return 0
-
-/obj/item/rig/proc/notify_ai(var/message)
- if(!message || !installed_modules || !installed_modules.len)
- return
- for(var/obj/item/rig_module/module in installed_modules)
- for(var/mob/living/silicon/ai/ai in module.contents)
- if(ai && ai.client && !ai.stat)
- to_chat(ai, "[message]")
-
-/obj/item/rig/equipped(mob/living/carbon/human/M, slot)
- ..()
- if(!istype(M) || slot != slot_back)
- return //we don't care about picking up/nonhumans
-
- spawn(1) //equipped() is called BEFORE the item is actually set as the slot
-
- if(seal_delay > 0 && istype(M) && M.back == src)
- M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...")
- if(!do_after(M, seal_delay, target = M))
- if(M && M.back == src)
- M.unEquip(src)
- M.put_in_hands(src)
- return
-
- if(istype(M) && M.back == src)
- M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].")
- wearer = M
- wearer.wearing_rig = src
- if(has_emergency_release)
- M.verbs |= /obj/item/rig/proc/emergency_release
- update_icon()
-
-/obj/item/rig/proc/toggle_piece(var/piece, var/mob/living/user, var/deploy_mode, var/force)
- if(!istype(wearer) || wearer.back != src)
- if(force) //can only force retracting sorry
- for(var/obj/item/uneq_piece in list(helmet, gloves, boots, chest))
- if(uneq_piece)
- if(isliving(uneq_piece.loc))
- var/mob/living/L = uneq_piece.loc
- L.unEquip(uneq_piece, 1)
- if(uneq_piece == boots)
- if(under_boots)
- if(L.equip_to_slot_if_possible(under_boots, slot_shoes))
- under_boots = null
- else
- to_chat(user, "Somehow, your [under_boots] got stuck to the [boots], and were retracted with them. ((This shouldn't happen, bug report this.))")
- uneq_piece.forceMove(src)
- return 0
-
- if(sealing || !cell || !cell.charge)
- return 0
-
- if(!(deploy_mode == ONLY_RETRACT && force)) //This should be the case while stripping, stripping does trigger the if statement below.
- if(user == wearer && user.incapacitated()) // If the user isn't wearing the suit it's probably an AI.
- return 0
-
- var/obj/item/check_slot
- var/equip_to
- var/obj/item/use_obj
-
- switch(piece)
- if("helmet")
- equip_to = slot_head
- use_obj = helmet
- check_slot = wearer.head
- if("gauntlets")
- equip_to = slot_gloves
- use_obj = gloves
- check_slot = wearer.gloves
- if("boots")
- equip_to = slot_shoes
- use_obj = boots
- check_slot = wearer.shoes
- if("chest")
- equip_to = slot_wear_suit
- use_obj = chest
- check_slot = wearer.wear_suit
-
- if(use_obj)
- if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) //user is wearing it, retract it if not forced to deploy
- if((flags & NODROP) && equip_to != slot_head && !force) //you can only retract the helmet if the suit isn't unsealed
- to_chat(user, "You can't retract \the [use_obj] while the suit is sealed!")
- return
-
- var/mob/living/to_strip
- if(wearer)
- to_strip = wearer
- else if(isliving(use_obj.loc))
- to_strip = use_obj.loc
-
- if(to_strip)
- to_strip.unEquip(use_obj, 1)
- if(use_obj == boots)
- if(under_boots)
- if(to_strip.equip_to_slot_if_possible(under_boots, slot_shoes))
- under_boots = null
- else
- to_chat(user, "Somehow, your [under_boots] got stuck to the [boots], and were retracted with them. ((This shouldn't happen, bug report this.))")
- use_obj.forceMove(src)
- if(wearer)
- to_chat(wearer, "Your [use_obj] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.")
-
- else if(deploy_mode != ONLY_RETRACT)
- if(check_slot)
- if(check_slot != use_obj) //If use_obj is already in check_slot, silently bail. Otherwise, tell the user why the part didn't deploy.
- if(use_obj == boots)
- under_boots = check_slot
- wearer.unEquip(under_boots)
- under_boots.forceMove(src)
- else
- to_chat(wearer, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.")
- return
- use_obj.forceMove(wearer)
- if(!wearer.equip_to_slot_if_possible(use_obj, equip_to, FALSE, TRUE))
- use_obj.forceMove(src)
- else
- if(wearer)
- to_chat(wearer, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.")
-
- if(piece == "helmet" && helmet)
- helmet.update_light(wearer)
-
-/obj/item/rig/proc/deploy(mob/user)
- if(!wearer || !user)
- return 0
-
- if(flags & NODROP) //We need to check if we have the part, the person is wearing something in the parts slot, and if yes, are they the same.
- if(helmet && wearer.head && wearer.head != helmet)
- to_chat(user, "\The [wearer.head] is blocking \the [src] from deploying!")
- return 0
- if(gloves && wearer.gloves && wearer.gloves != gloves)
- to_chat(user, "\The [wearer.gloves] is preventing \the [src] from deploying!")
- return 0
- /*if(boots && wearer.shoes && wearer.shoes != boots)
- to_chat(user, "\The [wearer.shoes] is preventing \the [src] from deploying!")
- return 0*/
- if(chest && wearer.wear_suit && wearer.wear_suit != chest)
- to_chat(user, "\The [wearer.wear_suit] is preventing \the [src] from deploying!")
- return 0
-
-
- for(var/piece in list("helmet", "gauntlets", "chest", "boots"))
- toggle_piece(piece, user, ONLY_DEPLOY)
-
-/obj/item/rig/dropped(var/mob/user)
- ..()
- user.verbs -= /obj/item/rig/proc/emergency_release
- for(var/piece in list("helmet","gauntlets","chest","boots"))
- toggle_piece(piece, user, ONLY_RETRACT, 1)
- if(wearer)
- wearer.wearing_rig = null
- wearer = null
-
-//Todo
-/obj/item/rig/proc/malfunction()
- return 0
-
-/obj/item/rig/emp_act(severity_class)
- //set malfunctioning
- if(emp_protection < 30) //for ninjas, really.
- malfunctioning += 10
- if(malfunction_delay <= 0)
- malfunction_delay = max(malfunction_delay, round(30/severity_class))
-
- //drain some charge
- if(cell) cell.emp_act(severity_class + 15)
-
- //possibly damage some modules
- take_hit((100/severity_class), "electrical pulse", 1)
-
-/obj/item/rig/proc/shock(mob/user)
- if(get_dist(src, user) <= 1) //Needs to be adjecant to the rig to get shocked.
- if(electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here.
- spark_system.start()
- if(user.stunned)
- return 1
- return 0
-
-/obj/item/rig/proc/take_hit(damage, source, is_emp=0)
-
- if(!installed_modules.len)
- return
-
- var/chance
- if(!is_emp)
- chance = 2*max(0, damage - (chest? chest.breach_threshold : 0))
- else
- //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average.
- //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules
- chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1)
-
- if(!prob(chance))
- return
-
- //deal addition damage to already damaged module first.
- //This way the chances of a module being disabled aren't so remote.
- var/list/valid_modules = list()
- var/list/damaged_modules = list()
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.damage < 2)
- valid_modules |= module
- if(module.damage > 0)
- damaged_modules |= module
-
- var/obj/item/rig_module/dam_module = null
- if(damaged_modules.len)
- dam_module = pick(damaged_modules)
- else if(valid_modules.len)
- dam_module = pick(valid_modules)
-
- if(!dam_module) return
-
- dam_module.damage++
-
- if(!source)
- source = "hit"
-
- if(wearer)
- if(dam_module.damage >= 2)
- to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!")
- else
- to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!")
- dam_module.deactivate()
-
-/obj/item/rig/proc/malfunction_check(var/mob/living/carbon/human/user)
- if(malfunction_delay)
- if(offline)
- to_chat(user, "The suit is completely unresponsive.")
- else
- to_chat(user, "ERROR: Hardware fault. Rebooting interface...")
- return 1
- return 0
-
-/obj/item/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0)
-
- if(check_for_ai)
- if(!(locate(/obj/item/rig_module/ai_container) in contents))
- return 0
- var/found_ai
- for(var/obj/item/rig_module/ai_container/module in contents)
- if(module.damage >= 2)
- continue
- if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat)
- found_ai = 1
- break
- if(!found_ai)
- return 0
-
- if(check_user_module)
- if(!user || !user.loc || !user.loc.loc)
- return 0
- var/obj/item/rig_module/ai_container/module = user.loc.loc
- if(!istype(module) || module.damage >= 2)
- to_chat(user, "Your host module is unable to interface with the suit.")
- return 0
-
- if(offline || !cell || !cell.charge || locked_down)
- if(user)
- to_chat(user, "Your host rig is unpowered and unresponsive.")
- return 0
- if(!wearer || wearer.back != src)
- if(user)
- to_chat(user, "Your host rig is not being worn.")
- return 0
- if(!wearer.stat && !control_overridden && !ai_override_enabled)
- if(user)
- to_chat(user, "You are locked out of the suit servo controller.")
- return 0
- return 1
-
-/obj/item/rig/proc/force_rest(var/mob/user)
- if(!ai_can_move_suit(user, check_user_module = 1))
- return
- wearer.lay_down()
- to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].")
-
-/obj/item/rig/proc/forced_move(var/direction, var/mob/user)
-
- // Why is all this shit in client/Move()? Who knows?
- if(world.time < wearer_move_delay)
- return
-
- if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1))
- return
-
- //This is sota the goto stop mobs from moving var
- if(wearer.notransform || !wearer.canmove)
- return
-
- if(!wearer.lastarea)
- wearer.lastarea = get_area(wearer.loc)
-
- if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0))
- if(!wearer.Process_Spacemove(0))
- return 0
-
- if(malfunctioning)
- direction = pick(GLOB.cardinal)
-
- // Inside an object, tell it we moved.
- if(isobj(wearer.loc) || ismob(wearer.loc))
- var/atom/O = wearer.loc
- return O.relaymove(wearer, direction)
-
- if(isturf(wearer.loc))
- if(wearer.restrained())//Why being pulled while cuffed prevents you from moving
- for(var/mob/M in range(wearer, 1))
- if(M.pulling == wearer)
- if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M))
- to_chat(user, "Your host is restrained! They can't move!")
- return 0
- else
- M.stop_pulling()
-
- // AIs are a bit slower than regular and ignore move intent.
- wearer_move_delay = world.time + ai_controlled_move_delay
-
- if(wearer.buckled) //if we're buckled to something, tell it we moved.
- return wearer.buckled.relaymove(wearer, direction)
-
- if(cell.use(200)) //Arbitrary, TODO
- wearer.Move(get_step(get_turf(wearer),direction),direction)
-
-// This returns the rig if you are contained inside one, but not if you are wearing it
-/atom/proc/get_rig()
- if(loc)
- return loc.get_rig()
- return null
-
-/obj/item/rig/get_rig()
- return src
-
-/mob/living/carbon/human/get_rig()
- if(istype(back,/obj/item/rig))
- return back
- else
- return null
-
-/obj/item/rig/proc/emergency_release()
- set name = "Suit Emergency Release"
- set desc = "Activate the suits emergency release system."
- set category = "Object"
- set src in oview(1)
- var/obj/item/rig/T = get_rig()
- return T.do_emergency_release(usr)
-
-/obj/item/rig/proc/do_emergency_release(var/mob/living/user)
- if(!can_touch(user, wearer) || !has_emergency_release)
- return can_touch(user,wearer)
- usr.visible_message("[user] starts activating \the [src] emergency seals release!")
- if(!do_after(user, 240, target = wearer))
- to_chat(user, "You need to focus on activating the emergency release.")
- return 0
- usr.visible_message("[user] activated \the [src] emergency seals release!")
- malfunctioning += 1
- malfunction_delay = 30
- unseal(user)
- return 1
-
-/obj/item/rig/proc/can_touch(var/mob/user, var/mob/wearer)
- if(!user)
- return 0
- if(!wearer.Adjacent(user))
- return 0
- if(user.restrained())
- to_chat(user, "You need your hands free for this.")
- return 0
- if(user.stat || user.paralysis || user.sleeping || user.lying || user.IsWeakened())
- return 0
- return 1
-#undef ONLY_DEPLOY
-#undef ONLY_RETRACT
-#undef SEAL_DELAY
diff --git a/code/modules/clothing/spacesuits/rig/rig_armormod.dm b/code/modules/clothing/spacesuits/rig/rig_armormod.dm
deleted file mode 100644
index c04270aa97b..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig_armormod.dm
+++ /dev/null
@@ -1,30 +0,0 @@
-/obj/item/clothing/suit/space/new_rig/calc_breach_damage()
- ..()
- holder.update_armor() //New dammage, new armormultiplikator.
- return damage
-
-/obj/item/rig/proc/update_armor()
- var/multi = 1 //Multiplicative modification to the armor, maybe add an additive later on
- if(chest)
- multi *= (100 - chest.damage) / 100 //If we have some breaches, lower the armor value.
-
- //TODO check for other armor mods, likely modules, which need to be coded.
- if(!armor) //Did we even give them some armor, if this is the case, the list should be initialized from New()
- return
-
- var/datum/armor/A = armor
- for(var/obj/item/piece in list(gloves, helmet, boots, chest))
- if(!istype(piece)) //Do we have the piece
- continue
-
- piece.armor = piece.armor.setRating(melee_value = A.getRating("melee") * multi,
- bullet_value = A.getRating("bullet") * multi,
- laser_value = A.getRating("laser") * multi,
- energy_value = A.getRating("energy") * multi,
- bomb_value = A.getRating("bomb") * multi,
- bio_value = A.getRating("bio") * multi,
- rad_value = A.getRating("rad") * multi,
- fire_value = A.getRating("fire") * multi,
- acid_value = A.getRating("acidd") * multi)
-
-//Perfect place to also add something like shield modules, or any other hit_reaction modules check.
diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm
deleted file mode 100644
index b48e17b2c41..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig_attackby.dm
+++ /dev/null
@@ -1,196 +0,0 @@
-/obj/item/rig/attackby(obj/item/W as obj, mob/user as mob)
-
- if(!istype(user,/mob/living)) return 0
-
- if(electrified != 0)
- if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here.
- return
-
- // Pass repair items on to the chestpiece.
- if(chest && (istype(W,/obj/item/stack) || istype(W, /obj/item/weldingtool)))
- return chest.attackby(W,user)
-
- // Lock or unlock the access panel.
- if(W.GetID())
- if(subverted)
- locked = 0
- to_chat(user, "It looks like the locking system has been shorted out.")
- return
-
- if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len))
- locked = 0
- to_chat(user, "\The [src] doesn't seem to have a locking mechanism.")
- return
-
- if(security_check_enabled && !src.allowed(user))
- to_chat(user, "Access denied.")
- return
-
- locked = !locked
- to_chat(user, "You [locked ? "lock" : "unlock"] \the [src] access panel.")
- return
-
- else if(istype(W,/obj/item/crowbar))
-
- if(!open && locked)
- to_chat(user, "The access panel is locked shut.")
- return
-
- open = !open
- to_chat(user, "You [open ? "open" : "close"] the access panel.")
- return
-
- if(open)
-
- // Hacking.
- if(istype(W,/obj/item/wirecutters) || istype(W,/obj/item/multitool))
- if(open)
- wires.Interact(user)
- else
- to_chat(user, "You can't reach the wiring.")
- return
- // Air tank.
- if(istype(W,/obj/item/tank)) //Todo, some kind of check for suits without integrated air supplies.
-
- if(air_supply)
- to_chat(user, "\The [src] already has a tank installed.")
- return
-
- user.unEquip(W)
- air_supply = W
- W.forceMove(src)
- to_chat(user, "You slot [W] into [src] and tighten the connecting valve.")
- return
-
- // Check if this is a hardsuit upgrade or a modification.
- else if(istype(W,/obj/item/rig_module))
-
- if(istype(src.loc,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = src.loc
- if(H.back == src)
- to_chat(user, "You can't install a hardsuit module while the suit is being worn.")
- return 1
-
- if(!installed_modules) installed_modules = list()
- if(installed_modules.len)
- for(var/obj/item/rig_module/installed_mod in installed_modules)
- if(!installed_mod.redundant && istype(installed_mod,W))
- to_chat(user, "The hardsuit already has a module of that class installed.")
- return 1
-
- var/obj/item/rig_module/mod = W
- to_chat(user, "You begin installing \the [mod] into \the [src].")
- if(!do_after(user, 40 * W.toolspeed, target = src))
- return
- if(!user || !W)
- return
- to_chat(user, "You install \the [mod] into \the [src].")
- user.unEquip(mod)
- installed_modules |= mod
- mod.forceMove(src)
- mod.installed(src)
- update_icon()
- return 1
-
- else if(!cell && istype(W,/obj/item/stock_parts/cell))
-
- to_chat(user, "You jack \the [W] into \the [src]'s battery mount.")
- user.unEquip(W)
- W.forceMove(src)
- src.cell = W
- return
-
- else if(istype(W,/obj/item/wrench))
-
- if(!air_supply)
- to_chat(user, "There is not tank to remove.")
- return
-
- if(user.r_hand && user.l_hand)
- air_supply.forceMove(get_turf(user))
- else
- user.put_in_hands(air_supply)
- to_chat(user, "You detach and remove \the [air_supply].")
- air_supply = null
- return
-
- else if(istype(W,/obj/item/screwdriver))
-
- var/list/current_mounts = list()
- if(cell) current_mounts += "cell"
- if(installed_modules && installed_modules.len) current_mounts += "system module"
-
- var/to_remove = input("Which would you like to modify?") as null|anything in current_mounts
- if(!to_remove)
- return
-
- if(istype(src.loc,/mob/living/carbon/human) && to_remove != "cell")
- var/mob/living/carbon/human/H = src.loc
- if(H.back == src)
- to_chat(user, "You can't remove an installed device while the hardsuit is being worn.")
- return
-
- switch(to_remove)
-
- if("cell")
-
- if(cell)
- to_chat(user, "You detatch \the [cell] from \the [src]'s battery mount.")
- for(var/obj/item/rig_module/module in installed_modules)
- module.deactivate()
- if(user.r_hand && user.l_hand)
- cell.forceMove(get_turf(user))
- else
- user.put_in_hands(cell)
- cell = null
- else
- to_chat(user, "There is nothing loaded in that mount.")
-
- if("system module")
-
- var/list/possible_removals = list()
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.permanent)
- continue
- possible_removals[module.name] = module
-
- if(!possible_removals.len)
- to_chat(user, "There are no installed modules to remove.")
- return
-
- var/removal_choice = input("Which module would you like to remove?") as null|anything in possible_removals
- if(!removal_choice)
- return
-
- var/obj/item/rig_module/removed = possible_removals[removal_choice]
- to_chat(user, "You detatch \the [removed] from \the [src].")
- removed.forceMove(get_turf(src))
- removed.removed()
- installed_modules -= removed
- update_icon()
-
- return
-
- // If we've gotten this far, all we have left to do before we pass off to root procs
- // is check if any of the loaded modules want to use the item we've been given.
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.accepts_item(W,user)) //Item is handled in this proc
- return
- ..()
-
-
-/obj/item/rig/attack_hand(var/mob/user)
-
- if(electrified != 0)
- if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here.
- return
- ..()
-
-/obj/item/rig/emag_act(var/remaining_charges, var/mob/user)
- if(!subverted)
- req_access.Cut()
- req_one_access.Cut()
- locked = 0
- subverted = 1
- to_chat(user, "You short out the access protocol for the suit.")
- return 1
diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm
deleted file mode 100644
index 246e3beb10f..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig_pieces.dm
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Defines the helmets, gloves and shoes for rigs.
- */
-
-/obj/item/clothing/head/helmet/space/new_rig
- name = "helmet"
- flags = BLOCKHAIR | THICKMATERIAL | NODROP
- flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEMASK
- body_parts_covered = HEAD
- heat_protection = HEAD
- cold_protection = HEAD
- var/brightness_on = 4
- var/on = 0
- sprite_sheets = list(
- "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi',
- "Skrell" = 'icons/mob/species/skrell/helmet.dmi',
- "Unathi" = 'icons/mob/species/unathi/helmet.dmi'
- )
- species_restricted = null
- actions_types = list(/datum/action/item_action/toggle_helmet_light)
-
- flash_protect = 2
-
-/obj/item/clothing/head/helmet/space/new_rig/attack_self(mob/user)
- if(!isturf(user.loc))
- to_chat(user, "You cannot turn the light on while in this [user.loc].")//To prevent some lighting anomalities.
-
- return
- toggle_light(user)
-
-/obj/item/clothing/head/helmet/space/new_rig/proc/toggle_light(mob/user)
- if(flags & AIRTIGHT) //Could also check for STOPSPRESSUREDMAGE, but one is enough, both get toggled when the seal gets toggled.
-
- on = !on
- icon_state = "[item_color][on]"
-
- if(on)
- set_light(brightness_on)
- else
- set_light(0)
- else
- to_chat(user, "You cannot turn the light on while the suit isn't sealed.")
-
- if(istype(user,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = user
- H.update_inv_head()
-
-/obj/item/clothing/gloves/rig
- name = "gauntlets"
- flags = THICKMATERIAL | NODROP
- body_parts_covered = HANDS
- heat_protection = HANDS
- cold_protection = HANDS
- species_restricted = null
- gender = PLURAL
-
-/obj/item/clothing/shoes/magboots/rig
- name = "boots"
- flags = NODROP
- body_parts_covered = FEET
- cold_protection = FEET
- heat_protection = FEET
- species_restricted = null
- gender = PLURAL
-
-/obj/item/clothing/shoes/magboots/rig/attack_self(mob/user)
- if(flags & AIRTIGHT) //Could also check for STOPSPRESSUREDMAGE, but one is enough, both get toggled when the seal gets toggled.
- ..(user)
- else
- to_chat(user, "You cannot activate mag-pulse traction system while the suit is not sealed.")
-
-/obj/item/clothing/suit/space/new_rig
- name = "chestpiece"
- allowed = list(/obj/item/flashlight,/obj/item/tank)
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT|HIDETAIL
- flags = STOPSPRESSUREDMAGE | THICKMATERIAL | AIRTIGHT | NODROP
- slowdown = 0
- breach_threshold = 20
- resilience = 0.2
- can_breach = 1
- var/obj/item/rig/holder
- sprite_sheets = list(
- "Tajaran" = 'icons/mob/species/tajaran/suit.dmi',
- "Unathi" = 'icons/mob/species/unathi/suit.dmi'
- )
-
-//TODO: move this to modules
-/obj/item/clothing/head/helmet/space/new_rig/proc/prevent_track()
- return 0
-
-/obj/item/clothing/gloves/rig/Touch(var/atom/A, var/proximity)
-
- if(!A || !proximity)
- return 0
-
- var/mob/living/carbon/human/H = loc
- if(!istype(H) || !H.back)
- return 0
-
- var/obj/item/rig/suit = H.back
- if(!suit || !istype(suit) || !suit.installed_modules.len)
- return 0
-
- for(var/obj/item/rig_module/module in suit.installed_modules)
- if(module.active && module.activates_on_touch)
- if(module.engage(A))
- return 1
-
- return 0
-
-//Rig pieces for non-spacesuit based rigs
-
-/obj/item/clothing/head/lightrig
- name = "mask"
- body_parts_covered = HEAD
- heat_protection = HEAD
- cold_protection = HEAD
- flags = THICKMATERIAL|AIRTIGHT
-
-/obj/item/clothing/suit/lightrig
- name = "suit"
- allowed = list(/obj/item/flashlight)
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
- flags = THICKMATERIAL
-
-/obj/item/clothing/shoes/lightrig
- name = "boots"
- body_parts_covered = FEET
- cold_protection = FEET
- heat_protection = FEET
- species_restricted = null
- gender = PLURAL
-
-/obj/item/clothing/gloves/lightrig
- name = "gloves"
- flags = THICKMATERIAL
- body_parts_covered = HANDS
- heat_protection = HANDS
- cold_protection = HANDS
- species_restricted = null
- gender = PLURAL
diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm
deleted file mode 100644
index 4a71f6c67bc..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm
+++ /dev/null
@@ -1,335 +0,0 @@
-// Interface for humans.
-/obj/item/rig/verb/hardsuit_interface()
- set name = "Open Hardsuit Interface"
- set desc = "Open the hardsuit system interface."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(wearer && wearer.back == src)
- ui_interact(usr)
-
-/obj/item/rig/verb/toggle_vision()
- set name = "Toggle Visor"
- set desc = "Turns your rig visor off or on."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_power_cost(usr))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!check_suit_access(usr))
- return
-
- if(!visor)
- to_chat(usr, "The hardsuit does not have a configurable visor.")
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- if(!visor.active)
- visor.activate()
- else
- visor.deactivate()
-
-/obj/item/rig/proc/toggle_helmet()
- set name = "Toggle Helmet"
- set desc = "Deploys or retracts your helmet."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_suit_access(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- toggle_piece("helmet", usr)
-
-/obj/item/rig/proc/toggle_chest()
- set name = "Toggle Chestpiece"
- set desc = "Deploys or retracts your chestpiece."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!check_suit_access(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- toggle_piece("chest", usr)
-
-/obj/item/rig/proc/toggle_gauntlets()
- set name = "Toggle Gauntlets"
- set desc = "Deploys or retracts your gauntlets."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_suit_access(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- toggle_piece("gauntlets", usr)
-
-/obj/item/rig/proc/toggle_boots()
- set name = "Toggle Boots"
- set desc = "Deploys or retracts your boots."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_suit_access(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- toggle_piece("boots", usr)
-
-/obj/item/rig/verb/deploy_suit()
- set name = "Deploy Hardsuit"
- set desc = "Deploys helmet, gloves and boots all at once."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_suit_access(usr))
- return
-
- if(!check_power_cost(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- deploy(wearer, usr)
-
-/obj/item/rig/verb/toggle_seals_verb()
- set name = "Toggle Hardsuit Seals"
- set desc = "Seals or unseals your rig."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_suit_access(usr))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- if(flags & NODROP)
- unseal(usr)
- else
- seal(usr)
-
-/obj/item/rig/verb/switch_vision_mode()
- set name = "Switch Vision Mode"
- set desc = "Switches between available vision modes."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(malfunction_check(usr))
- return
-
- if(!check_power_cost(usr, 0, 0, 0, 0))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!visor)
- to_chat(usr, "The hardsuit does not have a configurable visor.")
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- if(!visor.active)
- visor.activate()
-
- if(!visor.active)
- to_chat(usr, "The visor is suffering a hardware fault and cannot be configured.")
- return
-
- visor.engage()
-
-/obj/item/rig/verb/alter_voice()
- set name = "Configure Voice Synthesiser"
- set desc = "Toggles or configures your voice synthesizer."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(malfunction_check(usr))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!speech)
- to_chat(usr, "The hardsuit does not have a speech synthesiser.")
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- speech.engage()
-
-/obj/item/rig/verb/select_module()
- set name = "Select Module"
- set desc = "Selects a module as your primary system."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(malfunction_check(usr))
- return
-
- if(!check_power_cost(usr, 0, 0, 0, 0))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- var/list/selectable = list()
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.selectable)
- selectable |= module
-
- var/obj/item/rig_module/module = input("Which module do you wish to select?") as null|anything in selectable
-
- if(!istype(module))
- selected_module = null
- to_chat(usr, "Primary system is now: deselected.")
- return
-
- selected_module = module
- to_chat(usr, "Primary system is now: [selected_module.interface_name].")
-
-/obj/item/rig/verb/toggle_module()
- set name = "Toggle Module"
- set desc = "Toggle a system module."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(malfunction_check(usr))
- return
-
- if(!check_power_cost(usr, 0, 0, 0, 0))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- var/list/selectable = list()
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.toggleable)
- selectable |= module
-
- var/obj/item/rig_module/module = input("Which module do you wish to toggle?") as null|anything in selectable
-
- if(!istype(module))
- return
-
- if(module.active)
- to_chat(usr, "You attempt to deactivate \the [module.interface_name].")
- module.deactivate()
- else
- to_chat(usr, "You attempt to activate \the [module.interface_name].")
- module.activate()
-
-/obj/item/rig/verb/engage_module()
- set name = "Engage Module"
- set desc = "Engages a system module."
- set category = "Hardsuit"
- set src = usr.contents
-
- if(malfunction_check(usr))
- return
-
- if(!(flags & NODROP))
- to_chat(usr, "The suit is not active.")
- return
-
- if(!istype(wearer) || !wearer.back == src)
- to_chat(usr, "The hardsuit is not being worn.")
- return
-
- if(!check_power_cost(usr, 0, 0, 0, 0))
- return
-
- var/mob/M = usr
- if(M.incapacitated())
- return
-
- var/list/selectable = list()
- for(var/obj/item/rig_module/module in installed_modules)
- if(module.usable)
- selectable |= module
-
- var/obj/item/rig_module/module = input("Which module do you wish to engage?") as null|anything in selectable
-
- if(!istype(module))
- return
-
- to_chat(usr, "You attempt to engage the [module.interface_name].")
- module.engage()
diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/modules/clothing/spacesuits/rig/rig_wiring.dm
deleted file mode 100644
index 9d3108ac016..00000000000
--- a/code/modules/clothing/spacesuits/rig/rig_wiring.dm
+++ /dev/null
@@ -1,70 +0,0 @@
-/datum/wires/rig
- random = 1
- holder_type = /obj/item/rig
- wire_count = 5
-
-#define RIG_SECURITY 1
-#define RIG_AI_OVERRIDE 2
-#define RIG_SYSTEM_CONTROL 4
-#define RIG_INTERFACE_LOCK 8
-#define RIG_INTERFACE_SHOCK 16
-/*
- * Rig security can be snipped to disable ID access checks on rig.
- * Rig AI override can be pulsed to toggle whether or not the AI can take control of the suit.
- * System control can be pulsed to toggle some malfunctions.
- * Interface lock can be pulsed to toggle whether or not the interface can be accessed.
- */
-
-/datum/wires/rig/UpdateCut(var/index, var/mended)
-
- var/obj/item/rig/rig = holder
- switch(index)
- if(RIG_SECURITY)
- if(mended)
- rig.req_access = initial(rig.req_access)
- rig.req_one_access = initial(rig.req_one_access)
- if(RIG_INTERFACE_SHOCK)
- rig.electrified = mended ? 0 : -1
- rig.shock(usr,100)
-
-/datum/wires/rig/UpdatePulsed(var/index)
-
- var/obj/item/rig/rig = holder
- switch(index)
- if(RIG_SECURITY)
- rig.security_check_enabled = !rig.security_check_enabled
- rig.visible_message("\The [rig] twitches as several suit locks [rig.security_check_enabled?"close":"open"].")
- if(RIG_AI_OVERRIDE)
- rig.ai_override_enabled = !rig.ai_override_enabled
- rig.visible_message("A small red light on [rig] [rig.ai_override_enabled?"goes dead":"flickers on"].")
- if(RIG_SYSTEM_CONTROL)
- rig.malfunctioning += 10
- if(rig.malfunction_delay <= 0)
- rig.malfunction_delay = 20
- rig.shock(usr,100)
- if(RIG_INTERFACE_LOCK)
- rig.interface_locked = !rig.interface_locked
- rig.visible_message("\The [rig] clicks audibly as the software interface [rig.interface_locked?"darkens":"brightens"].")
- if(RIG_INTERFACE_SHOCK)
- if(rig.electrified != -1)
- rig.electrified = 30
- rig.shock(usr,100)
-
-/datum/wires/rig/GetWireName(index)
- switch(index)
- if(RIG_SECURITY)
- return "ID check"
- if(RIG_AI_OVERRIDE)
- return "AI control"
- if(RIG_SYSTEM_CONTROL)
- return "System control"
- if(RIG_INTERFACE_LOCK)
- return "Interface lock"
- if(RIG_INTERFACE_SHOCK)
- return "Electrification"
-
-/datum/wires/rig/CanUse(var/mob/living/L)
- var/obj/item/rig/rig = holder
- if(rig.open)
- return 1
- return 0
diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm
deleted file mode 100644
index 25c784ba7dd..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/alien.dm
+++ /dev/null
@@ -1,46 +0,0 @@
-/obj/item/rig/unathi
- name = "NT breacher chassis control module"
- desc = "A cheap NT knock-off of an Unathi battle-rig. Looks like a fish, moves like a fish, steers like a cow."
- suit_type = "NT breacher"
- icon_state = "breacher_rig_cheap"
- armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 45, bio = 100, rad = 50)
- emp_protection = -20
- active_slowdown = 6
- offline_slowdown = 10
- vision_restriction = 1
- offline_vision_restriction = 2
-
- chest_type = /obj/item/clothing/suit/space/new_rig/unathi
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/unathi
- glove_type = /obj/item/clothing/gloves/rig/unathi
- boot_type = /obj/item/clothing/shoes/magboots/rig/unathi
-
-/obj/item/rig/unathi/fancy
- name = "breacher chassis control module"
- desc = "An authentic Unathi breacher chassis. Huge, bulky and absurdly heavy. It must be like wearing a tank."
- suit_type = "breacher chassis"
- icon_state = "breacher_rig"
- armor = list(melee = 45, bullet = 45, laser = 45, energy = 45, bomb = 45, bio = 100, rad = 75) //Takes TEN TIMES as much damage to stop someone in a breacher. In exchange, it's slow. //Whoever made this was on meth
- vision_restriction = 0
-
-/obj/item/clothing/head/helmet/space/new_rig/unathi
- icon = 'icons/obj/clothing/species/unathi/hats.dmi'
- species_restricted = list("Unathi")
-
-/obj/item/clothing/suit/space/new_rig/unathi
- icon = 'icons/obj/clothing/species/unathi/suits.dmi'
- species_restricted = list("Unathi")
-
-/obj/item/clothing/gloves/rig/unathi
- icon = 'icons/obj/clothing/species/unathi/gloves.dmi'
- species_restricted = list("Unathi")
- sprite_sheets = list(
- "Unathi" = 'icons/mob/species/unathi/gloves.dmi'
- )
-
-/obj/item/clothing/shoes/magboots/rig/unathi
- icon = 'icons/obj/clothing/species/unathi/shoes.dmi'
- species_restricted = list("Unathi")
- sprite_sheets = list(
- "Unathi" = 'icons/mob/species/unathi/feet.dmi'
- )
diff --git a/code/modules/clothing/spacesuits/rig/suits/combat.dm b/code/modules/clothing/spacesuits/rig/suits/combat.dm
deleted file mode 100644
index cc05c8af2f7..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/combat.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-/obj/item/clothing/head/helmet/space/new_rig/combat
-
-/obj/item/rig/combat
- name = "combat hardsuit control module"
- desc = "A sleek and dangerous hardsuit for active combat."
- icon_state = "security_rig"
- suit_type = "combat hardsuit"
- armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100)
- active_slowdown = 1
- offline_slowdown = 3
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/combat
- allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton)
-
-
-/obj/item/rig/combat/equipped
-
-
- initial_modules = list(
- /obj/item/rig_module/mounted,
- /obj/item/rig_module/vision/thermal,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/ai_container,
- // /obj/item/rig_module/power_sink,
- /obj/item/rig_module/electrowarfare_suite,
- /obj/item/rig_module/chem_dispenser/combat
- )
diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm
deleted file mode 100644
index 5485e1fe882..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm
+++ /dev/null
@@ -1,81 +0,0 @@
-/obj/item/clothing/head/helmet/space/new_rig/ert
-
-/obj/item/rig/ert
- name = "ERT-C hardsuit control module"
- desc = "A suit worn by the commander of an Emergency Response Team. Has blue highlights. Armoured and space ready."
- suit_type = "ERT commander"
- icon_state = "ert_commander_rig"
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert
-
- req_access = list(ACCESS_CENT_SPECOPS)
-
- armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50)
- allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/crowbar, \
- /obj/item/screwdriver, /obj/item/weldingtool, /obj/item/wirecutters, /obj/item/wrench, /obj/item/multitool, \
- /obj/item/radio, /obj/item/analyzer,/obj/item/storage/briefcase/inflatable, /obj/item/melee/baton, /obj/item/gun, \
- /obj/item/storage/firstaid, /obj/item/reagent_containers/hypospray, /obj/item/roller)
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/datajack,
- )
-
-/obj/item/rig/ert/engineer
- name = "ERT-E suit control module"
- desc = "A suit worn by the engineering division of an Emergency Response Team. Has orange highlights. Armoured and space ready."
- suit_type = "ERT engineer"
- icon_state = "ert_engineer_rig"
- siemens_coefficient = 0
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/device/plasmacutter,
- // /obj/item/rig_module/device/rcd
- )
-
-/obj/item/rig/ert/medical
- name = "ERT-M suit control module"
- desc = "A suit worn by the medical division of an Emergency Response Team. Has white highlights. Armoured and space ready."
- suit_type = "ERT medic"
- icon_state = "ert_medical_rig"
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/device/healthscanner,
- /obj/item/rig_module/chem_dispenser/injector
- )
-
-/obj/item/rig/ert/security
- name = "ERT-S suit control module"
- desc = "A suit worn by the security division of an Emergency Response Team. Has red highlights. Armoured and space ready."
- suit_type = "ERT security"
- icon_state = "ert_security_rig"
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/mounted/egun,
- )
-
-/obj/item/rig/ert/assetprotection
- name = "Heavy Asset Protection suit control module"
- desc = "A heavy suit worn by the highest level of Asset Protection, don't mess with the person wearing this. Armoured and space ready."
- suit_type = "heavy asset protection"
- icon_state = "asset_protection_rig"
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/vision/multi,
- /obj/item/rig_module/mounted/egun,
- /obj/item/rig_module/chem_dispenser/injector,
- /obj/item/rig_module/device/plasmacutter,
- // /obj/item/rig_module/device/rcd,
- /obj/item/rig_module/datajack
- )
diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm
deleted file mode 100644
index d76691daf57..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/light.dm
+++ /dev/null
@@ -1,120 +0,0 @@
-// Light rigs are not space-capable, but don't suffer excessive slowdown or sight issues when depowered.
-/obj/item/rig/light
- name = "light suit control module"
- desc = "A lighter, less armoured rig suit."
- icon_state = "ninja_rig"
- suit_type = "light suit"
- allowed = list(/obj/item/gun,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/stock_parts/cell)
- emp_protection = 10
- active_slowdown = 0
- flags = STOPSPRESSUREDMAGE | THICKMATERIAL
- offline_slowdown = 0
- offline_vision_restriction = 0
-
- chest_type = /obj/item/clothing/suit/space/new_rig/light
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/light
- boot_type = /obj/item/clothing/shoes/magboots/rig/light
- glove_type = /obj/item/clothing/gloves/rig/light
-
-/obj/item/clothing/suit/space/new_rig/light
- name = "suit"
- breach_threshold = 18 //comparable to voidsuits
-
-/obj/item/clothing/gloves/rig/light
- name = "gloves"
-
-/obj/item/clothing/shoes/magboots/rig/light
- name = "shoes"
-
-/obj/item/clothing/head/helmet/space/new_rig/light
- name = "hood"
-
-/obj/item/rig/light/hacker
- name = "cybersuit control module"
- suit_type = "cyber"
- desc = "An advanced powered armour suit with many cyberwarfare enhancements. Comes with built-in insulated gloves for safely tampering with electronics."
- icon_state = "hacker_rig"
-
- req_access = list(ACCESS_SYNDICATE)
-
- airtight = 0
- seal_delay = 5 //not being vaccum-proof has an upside I guess
-
- helm_type = /obj/item/clothing/head/lightrig/hacker
- chest_type = /obj/item/clothing/suit/lightrig/hacker
- glove_type = /obj/item/clothing/gloves/lightrig/hacker
- boot_type = /obj/item/clothing/shoes/lightrig/hacker
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- // /obj/item/rig_module/power_sink,
- /obj/item/rig_module/datajack,
- /obj/item/rig_module/electrowarfare_suite,
- /obj/item/rig_module/voice,
- /obj/item/rig_module/vision,
- )
-
-//The cybersuit is not space-proof. It does however, have good siemens_coefficient values
-/obj/item/clothing/head/lightrig/hacker
- name = "HUD"
- siemens_coefficient = 0.4
- flags = 0
-
-/obj/item/clothing/suit/lightrig/hacker
- siemens_coefficient = 0.4
-
-/obj/item/clothing/shoes/lightrig/hacker
- siemens_coefficient = 0.4
- flags = NOSLIP //All the other rigs have magboots anyways, hopefully gives the hacker suit something more going for it.
-
-/obj/item/clothing/gloves/lightrig/hacker
- siemens_coefficient = 0
-
-/obj/item/rig/light/ninja
- name = "ominous suit control module"
- suit_type = "ominous"
- desc = "A unique, vaccum-proof suit of nano-enhanced armor designed specifically for Spider Clan assassins."
- icon_state = "ninja_rig"
- armor = list(melee = 50, bullet = 15, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 30)
- emp_protection = 40 //change this to 30 if too high.
- active_slowdown = 0
-
- chest_type = /obj/item/clothing/suit/space/new_rig/light/ninja
- glove_type = /obj/item/clothing/gloves/rig/light/ninja
-
- req_access = list(ACCESS_SYNDICATE)
-
- initial_modules = list(
- /obj/item/rig_module/teleporter,
- /obj/item/rig_module/stealth_field,
- /obj/item/rig_module/mounted/energy_blade,
- /obj/item/rig_module/vision,
- /obj/item/rig_module/voice,
- /obj/item/rig_module/chem_dispenser,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/fabricator,
- /obj/item/rig_module/ai_container,
- // /obj/item/rig_module/power_sink,
- /obj/item/rig_module/datajack,
- /obj/item/rig_module/self_destruct
- )
-
-/obj/item/clothing/gloves/rig/light/ninja
- name = "insulated gloves"
- siemens_coefficient = 0
-
-/obj/item/clothing/suit/space/new_rig/light/ninja
- breach_threshold = 38 //comparable to regular hardsuits
-
-/obj/item/rig/light/stealth
- name = "stealth suit control module"
- suit_type = "stealth"
- desc = "A highly advanced and expensive suit designed for covert operations."
- icon_state = "ninja_rig" //supposed to be "stealth_rig", but as it currently only has a semi-copied ninja rig sprite, we can just use them directly.
-
- req_access = list(ACCESS_SYNDICATE)
-
- initial_modules = list(
- /obj/item/rig_module/stealth_field,
- /obj/item/rig_module/vision
- )
diff --git a/code/modules/clothing/spacesuits/rig/suits/merc.dm b/code/modules/clothing/spacesuits/rig/suits/merc.dm
deleted file mode 100644
index 95abba52df9..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/merc.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-/obj/item/clothing/head/helmet/space/new_rig/merc
-
-/obj/item/rig/merc
- name = "crimson hardsuit control module"
- desc = "A blood-red hardsuit featuring some fairly illegal technology."
- icon_state = "merc_rig"
- suit_type = "crimson hardsuit"
- armor = list(melee = 40, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50)
- active_slowdown = 1
- offline_slowdown = 3
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/merc
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/gun,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/restraints/handcuffs)
-
- initial_modules = list(
- /obj/item/rig_module/mounted,
- /obj/item/rig_module/vision/thermal,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/ai_container,
- // /obj/item/rig_module/power_sink,
- /obj/item/rig_module/electrowarfare_suite,
- /obj/item/rig_module/chem_dispenser/combat,
- // /obj/item/rig_module/fabricator/energy_net
- )
-
-//Has most of the modules removed
-/obj/item/rig/merc/empty
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/electrowarfare_suite, //might as well
- )
diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm
deleted file mode 100644
index c4bbd059fae..00000000000
--- a/code/modules/clothing/spacesuits/rig/suits/station.dm
+++ /dev/null
@@ -1,223 +0,0 @@
-/obj/item/clothing/head/helmet/space/new_rig/industrial
-
-/obj/item/clothing/head/helmet/space/new_rig/ce
-
-/obj/item/clothing/head/helmet/space/new_rig/eva
-
-/obj/item/clothing/head/helmet/space/new_rig/hazmat
-
-/obj/item/clothing/head/helmet/space/new_rig/medical
-
-/obj/item/clothing/head/helmet/space/new_rig/hazard
-
-/obj/item/rig/internalaffairs
- name = "augmented tie"
- suit_type = "augmented suit"
- desc = "Prepare for paperwork."
- icon_state = "internalaffairs_rig"
- armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
- siemens_coefficient = 0.9
- active_slowdown = 0
- offline_slowdown = 0
- offline_vision_restriction = 0
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/briefcase,/obj/item/storage/secure/briefcase)
-
- req_access = list()
- req_one_access = list()
-
- glove_type = null
- helm_type = null
- boot_type = null
-
-/obj/item/rig/internalaffairs/equipped
-
- req_access = list(ACCESS_LAWYER)
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/device/flash,
- /obj/item/rig_module/device/paperdispenser,
- /obj/item/rig_module/device/pen,
- /obj/item/rig_module/device/stamp
- )
-
- glove_type = null
- helm_type = null
- boot_type = null
-
-/obj/item/rig/industrial
- name = "industrial suit control module"
- suit_type = "industrial hardsuit"
- desc = "A heavy, powerful rig used by construction crews and mining corporations."
- icon_state = "engineering_rig"
- armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75)
- active_slowdown = 3
- offline_slowdown = 10
- offline_vision_restriction = 2
- emp_protection = -20
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/industrial
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/bag/ore,/obj/item/t_scanner,/obj/item/pickaxe, /obj/item/rcd)
-
- req_access = list()
- req_one_access = list()
-
-
-/obj/item/rig/industrial/equipped
-
- initial_modules = list(
- /obj/item/rig_module/device/plasmacutter,
- /obj/item/rig_module/device/drill,
- /obj/item/rig_module/device/orescanner,
- // /obj/item/rig_module/device/rcd,
- /obj/item/rig_module/vision/meson
- )
-
-/obj/item/rig/eva
- name = "EVA suit control module"
- suit_type = "EVA hardsuit"
- desc = "A light rig for repairs and maintenance to the outside of habitats and vessels."
- icon_state = "eva_rig"
- active_slowdown = 0
- offline_slowdown = 1
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/eva
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/toolbox,/obj/item/storage/briefcase/inflatable,/obj/item/t_scanner,/obj/item/rcd)
-
- req_access = list()
- req_one_access = list()
-
-/obj/item/rig/eva/equipped
-
- initial_modules = list(
- /obj/item/rig_module/device/plasmacutter,
- /obj/item/rig_module/maneuvering_jets,
- // /obj/item/rig_module/device/rcd,
- /obj/item/rig_module/vision/meson
- )
-
-//Chief Engineer's rig. This is sort of a halfway point between the old hardsuits (voidsuits) and the rig class.
-/obj/item/rig/ce
-
- name = "advanced voidsuit control module"
- suit_type = "advanced voidsuit"
- desc = "An advanced voidsuit that protects against hazardous, low pressure environments. Shines with a high polish."
- icon_state = "ce_rig"
- armor = list(melee = 40, bullet = 5, laser = 10, energy = 5, bomb = 50, bio = 100, rad = 90)
- active_slowdown = 0
- offline_slowdown = 0
- offline_vision_restriction = 0
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/ce
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/bag/ore,/obj/item/t_scanner,/obj/item/pickaxe, /obj/item/rcd)
-
-
- req_access = list()
- req_one_access = list()
-
- boot_type = null
- glove_type = null
-
-/obj/item/rig/ce/equipped
-
- req_access = list(ACCESS_CE)
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/device/plasmacutter,
- // /obj/item/rig_module/device/rcd,
- /obj/item/rig_module/vision/meson
- )
-
- chest_type = /obj/item/clothing/suit/space/new_rig/ce
- boot_type = null
- glove_type = null
-
-/obj/item/clothing/suit/space/new_rig/ce
- heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
-
-/obj/item/rig/hazmat
-
- name = "AMI control module"
- suit_type = "hazmat hardsuit"
- desc = "An Anomalous Material Interaction hardsuit that protects against the strangest energies the universe can throw at it."
- icon_state = "science_rig"
- active_slowdown = 1
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazmat
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/pickaxe,/obj/item/healthanalyzer,/obj/item/gps,/obj/item/radio/beacon)
-
- req_access = list()
- req_one_access = list()
-
-/obj/item/rig/hazmat/equipped
-
- req_access = list(ACCESS_RD)
-
- initial_modules = list(
- /obj/item/rig_module/ai_container,
- /obj/item/rig_module/maneuvering_jets)
-
-/obj/item/rig/medical
-
- name = "rescue suit control module"
- suit_type = "rescue hardsuit"
- desc = "A durable suit designed for medical rescue in high risk areas."
- icon_state = "medical_rig"
- armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 50)
- active_slowdown = 1
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/medical
-
- allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/roller )
-
- req_access = list()
- req_one_access = list()
-
-/obj/item/rig/medical/equipped
-
- initial_modules = list(
- /obj/item/rig_module/chem_dispenser/injector,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/device/healthscanner,
- /obj/item/rig_module/vision/medhud
- )
-
-/obj/item/rig/hazard
- name = "hazard hardsuit control module"
- suit_type = "hazard hardsuit"
- desc = "A Security hardsuit designed for prolonged EVA in dangerous environments."
- icon_state = "hazard_rig"
- armor = list(melee = 30, bullet = 15, laser = 30, energy = 10, bomb = 10, bio = 100, rad = 50)
- active_slowdown = 1
- offline_slowdown = 3
- offline_vision_restriction = 1
-
- helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazard
-
- allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton)
-
- req_access = list()
- req_one_access = list()
-
-
-/obj/item/rig/hazard/equipped
-
- initial_modules = list(
- /obj/item/rig_module/vision/sechud,
- /obj/item/rig_module/maneuvering_jets,
- /obj/item/rig_module/grenade_launcher,
- /obj/item/rig_module/mounted/taser
- )
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 6c4ace8c993..bdcd27c24e3 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -11,8 +11,10 @@
resistance_flags = NONE
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
sprite_sheets = list(
- "Vox" = 'icons/mob/species/vox/suit.dmi'
+ "Vox" = 'icons/mob/species/vox/suit.dmi',
+ "Grey" = 'icons/mob/species/grey/suit.dmi'
)
+ w_class = WEIGHT_CLASS_NORMAL
/obj/item/clothing/suit/armor/vest
name = "armor"
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 7e9e698d185..63f5cf8dca3 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -12,6 +12,7 @@
/obj/item/clothing/suit/bluetag
name = "blue laser tag armour"
desc = "Blue Pride, Station Wide."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "bluetag"
item_state = "bluetag"
blood_overlay_type = "armor"
@@ -26,6 +27,7 @@
/obj/item/clothing/suit/redtag
name = "red laser tag armour"
desc = "Pew pew pew."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "redtag"
item_state = "redtag"
blood_overlay_type = "armor"
@@ -71,6 +73,7 @@
/obj/item/clothing/suit/cyborg_suit
name = "cyborg suit"
desc = "Suit for a cyborg costume."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "death"
item_state = "death"
flags = CONDUCT
@@ -371,6 +374,7 @@
/obj/item/clothing/suit/hooded/wintercoat/captain
name = "captain's winter coat"
icon_state = "wintercoat_captain"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatcaptain"
armor = list("melee" = 25, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50)
allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic)
@@ -382,6 +386,7 @@
/obj/item/clothing/suit/hooded/wintercoat/security
name = "security winter coat"
icon_state = "wintercoat_sec"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatsecurity"
armor = list("melee" = 15, "bullet" = 10, "laser" = 15, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic)
@@ -393,6 +398,7 @@
/obj/item/clothing/suit/hooded/wintercoat/medical
name = "medical winter coat"
icon_state = "wintercoat_med"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatmedical"
allowed = list(/obj/item/analyzer, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
@@ -404,6 +410,7 @@
/obj/item/clothing/suit/hooded/wintercoat/science
name = "science winter coat"
icon_state = "wintercoat_sci"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatscience"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic)
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 0, acid = 0)
@@ -415,6 +422,7 @@
/obj/item/clothing/suit/hooded/wintercoat/engineering
name = "engineering winter coat"
icon_state = "wintercoat_engi"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatengineer"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45)
allowed = list(/obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/t_scanner, /obj/item/rcd)
@@ -454,6 +462,7 @@
/obj/item/clothing/suit/hooded/wintercoat/miner
name = "mining winter coat"
icon_state = "wintercoat_miner"
+ w_class = WEIGHT_CLASS_NORMAL
item_state = "coatminer"
allowed = list(/obj/item/pickaxe, /obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter)
armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
@@ -767,6 +776,7 @@
/obj/item/clothing/suit/jacket/pilot
name = "security bomber jacket"
desc = "A stylish and worn-in armoured black bomber jacket emblazoned with the NT Security crest on the left breast. Looks rugged."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "bombersec"
item_state = "bombersec"
ignore_suitadjust = 0
@@ -825,6 +835,7 @@
/obj/item/clothing/suit/toggle/owlwings
name = "owl cloak"
desc = "A soft brown cloak made of synthetic feathers. Soft to the touch, stylish, and a 2 meter wing span that will drive the ladies mad."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "owl_wings"
item_state = "owl_wings"
body_parts_covered = ARMS
@@ -871,6 +882,7 @@
/obj/item/clothing/suit/advanced_protective_suit
name = "Advanced Protective Suit"
desc = "An incredibly advanced and complex suit; it has so many buttons and dials as to be incomprehensible."
+ w_class = WEIGHT_CLASS_BULKY
icon_state = "bomb"
item_state = "bomb"
actions_types = list(/datum/action/item_action/toggle)
@@ -924,6 +936,7 @@
//Syndicate Chaplain Robe (WOLOLO!)
/obj/item/clothing/suit/hooded/chaplain_hoodie/missionary_robe
description_antag = "This robe is made of reinforced fibers, granting it superior protection. The robes also wirelessly generate power for the neurotransmitter in the linked missionary staff while being worn."
+ w_class = WEIGHT_CLASS_NORMAL
armor = list(melee = 10, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 15, fire = 30, acid = 30)
var/obj/item/nullrod/missionary_staff/linked_staff = null
diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm
index 9a675742b5e..1da4138fe77 100644
--- a/code/modules/clothing/suits/storage.dm
+++ b/code/modules/clothing/suits/storage.dm
@@ -1,5 +1,6 @@
/obj/item/clothing/suit/storage
var/obj/item/storage/internal/pockets
+ w_class = WEIGHT_CLASS_NORMAL //we don't want these to be able to fit in their own pockets.
/obj/item/clothing/suit/storage/New()
..()
diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm
index 308380e4c55..e77bcb7478a 100644
--- a/code/modules/clothing/suits/utility.dm
+++ b/code/modules/clothing/suits/utility.dm
@@ -64,6 +64,7 @@
/obj/item/clothing/head/bomb_hood
name = "bomb hood"
desc = "Use in case of bomb."
+ w_class = WEIGHT_CLASS_NORMAL
icon_state = "bombsuit"
flags = BLOCKHAIR | THICKMATERIAL
armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50)
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index a9b4eab329e..9a2afbc8a60 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -838,3 +838,10 @@
icon_state = "cuban_suit"
item_state = "cuban_suit"
item_color = "cuban_suit"
+
+/obj/item/clothing/under/tourist_suit
+ name = "tourist outfit"
+ desc = "A light blue shirt with brown shorts. Feels oddly spooky."
+ icon_state = "tourist"
+ icon_state = "tourist"
+ item_color = "tourist"
diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm
index a0d6823fe80..aae3b84a3c5 100644
--- a/code/modules/crafting/craft.dm
+++ b/code/modules/crafting/craft.dm
@@ -357,7 +357,7 @@
if(!fail_msg)
to_chat(usr, "[TR.name] constructed.")
if(TR.alert_admins_on_craft)
- message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
+ message_admins("[key_name_admin(usr)] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
else
to_chat(usr, "Construction failed[fail_msg]")
busy = FALSE
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index 1a5da52ffce..530b9095f75 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -349,7 +349,7 @@
to_chat(user, "You modify the appearance of [target].")
var/obj/item/clothing/mask/gas/M = target
M.name = "Prescription Gas Mask"
- M.desc = "It looks heavily modified, but otherwise functions as a gas mask. The words “Property of Yon-Dale” can be seen on the inner band."
+ M.desc = "It looks heavily modified, but otherwise functions as a gas mask. The words \"Property of Yon-Dale\" can be seen on the inner band."
M.icon = 'icons/obj/custom_items.dmi'
M.icon_state = "gas_tariq"
M.sprite_sheets = list(
diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm
index 7fe60e8b77f..4ad8fb4eb90 100644
--- a/code/modules/events/abductor.dm
+++ b/code/modules/events/abductor.dm
@@ -9,7 +9,7 @@
makeAbductorTeam()
/datum/event/abductor/proc/makeAbductorTeam()
- var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, 1)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, TRUE)
if(candidates.len >= 2)
//Oh god why we can't have static functions
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index 8c01d5c7cc2..23e4e266819 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -23,7 +23,7 @@
vents += temp_vent
spawn()
- var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, TRUE, source = /mob/living/carbon/alien/larva)
while(spawncount > 0 && vents.len && candidates.len)
var/obj/vent = pick_n_take(vents)
diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm
index d2fe04dcc5d..c19b6cbe8b7 100644
--- a/code/modules/events/apc_short.dm
+++ b/code/modules/events/apc_short.dm
@@ -59,7 +59,17 @@
log_and_message_admins("APC Short event shorted out [affected_apc_count] APCs.")
/proc/power_restore(announce=TRUE)
- power_restore_quick(announce)
+ if(announce)
+ GLOB.event_announcement.Announce("Power has been restored to [station_name()]. We apologize for the inconvenience.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg')
+
+ // recharge the APCs
+ for(var/thing in GLOB.apcs)
+ var/obj/machinery/power/apc/A = thing
+ if(!is_station_level(A.z))
+ continue
+ var/obj/item/stock_parts/cell/C = A.get_cell()
+ if(C)
+ C.give(C.maxcharge)
/proc/power_restore_quick(announce=TRUE)
if(announce)
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index caec11de9f2..de2da9fbf64 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -12,14 +12,14 @@
if(!T)
return kill()
- var/list/candidates = pollCandidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, 1)
- if(!candidates.len)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected)
+ if(!length(candidates))
return kill()
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in GLOB.all_vent_pumps)
if(is_station_level(temp_vent.loc.z) && !temp_vent.welded)
- if(temp_vent.parent.other_atmosmch.len > 50)
+ if(length(temp_vent.parent.other_atmosmch) > 50)
vents += temp_vent
var/obj/vent = pick(vents)
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 563ef98697d..a11ef107a8f 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -16,7 +16,7 @@
density = 1
anchored = 1
var/strength = 2 //ex_act severity number
- var/life = 2 //how many things we hit before del(src)
+ var/life = 2 //how many things we hit before qdel(src)
var/atom/goal = null
/obj/effect/space_dust/weak
diff --git a/code/modules/events/event_procs.dm b/code/modules/events/event_procs.dm
index c5b7c893c26..cc00f05723f 100644
--- a/code/modules/events/event_procs.dm
+++ b/code/modules/events/event_procs.dm
@@ -1,9 +1,9 @@
/client/proc/forceEvent(var/type in SSevents.allEvents)
- set name = "Trigger Event (Debug Only)"
+ set name = "Trigger Event"
set category = "Debug"
- if(!holder)
+ if(!check_rights(R_EVENT))
return
if(ispath(type))
diff --git a/code/modules/events/holidays/AprilFools.dm b/code/modules/events/holidays/AprilFools.dm
deleted file mode 100644
index 562e6b98e0d..00000000000
--- a/code/modules/events/holidays/AprilFools.dm
+++ /dev/null
@@ -1 +0,0 @@
-//placeholder for holiday stuff
diff --git a/code/modules/events/holidays/Christmas.dm b/code/modules/events/holidays/Christmas.dm
deleted file mode 100644
index e04471482a5..00000000000
--- a/code/modules/events/holidays/Christmas.dm
+++ /dev/null
@@ -1,61 +0,0 @@
-/proc/Christmas_Game_Start()
- for(var/obj/structure/flora/tree/pine/xmas in world)
- if(!is_station_level(xmas.z)) continue
- for(var/turf/simulated/floor/T in orange(1,xmas))
- for(var/i=1,i<=rand(1,5),i++)
- new /obj/item/a_gift(T)
- for(var/mob/living/simple_animal/corgi/Ian/Ian in GLOB.mob_list)
- Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian))
-
-/proc/ChristmasEvent()
- for(var/obj/structure/flora/tree/pine/xmas in world)
- var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc)
- evil_tree.icon_state = xmas.icon_state
- evil_tree.icon_living = evil_tree.icon_state
- evil_tree.icon_dead = evil_tree.icon_state
- evil_tree.icon_gib = evil_tree.icon_state
- qdel(xmas)
-
-/obj/item/toy/xmas_cracker
- name = "xmas cracker"
- icon = 'icons/obj/christmas.dmi'
- icon_state = "cracker"
- desc = "Directions for use: Requires two people, one to pull each end."
- var/cracked = 0
-
-/obj/item/toy/xmas_cracker/New()
- ..()
-
-/obj/item/toy/xmas_cracker/attack(mob/target, mob/user)
- if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() )
- target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.")
- var/obj/item/paper/Joke = new /obj/item/paper(user.loc)
- Joke.name = "[pick("awful","terrible","unfunny")] joke"
- Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'",
- "Why couldn't the snowman get laid?\n\nHe was frigid!",
- "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.",
- "What happened to the man who stole advent calanders?\n\nHe got 25 days.",
- "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.",
- "Where do you find chili beans?\n\nThe north pole.",
- "What do you get from eating tree decorations?\n\nTinsilitis!",
- "What do snowmen wear on their heads?\n\nIce caps!",
- "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.",
- "Why doesn�t Santa have any children?\n\nBecause he only comes down the chimney.")
- new /obj/item/clothing/head/festive(target.loc)
- user.update_icons()
- cracked = 1
- icon_state = "cracker1"
- var/obj/item/toy/xmas_cracker/other_half = new /obj/item/toy/xmas_cracker(target)
- other_half.cracked = 1
- other_half.icon_state = "cracker2"
- target.put_in_active_hand(other_half)
- playsound(user, 'sound/effects/snap.ogg', 50, 1)
- return 1
- return ..()
-
-/obj/item/clothing/head/festive
- name = "festive paper hat"
- icon_state = "xmashat"
- desc = "A crappy paper hat that you are REQUIRED to wear."
- flags_inv = 0
- armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
diff --git a/code/modules/events/holidays/Easter.dm b/code/modules/events/holidays/Easter.dm
deleted file mode 100644
index 562e6b98e0d..00000000000
--- a/code/modules/events/holidays/Easter.dm
+++ /dev/null
@@ -1 +0,0 @@
-//placeholder for holiday stuff
diff --git a/code/modules/events/holidays/Holidays.dm b/code/modules/events/holidays/Holidays.dm
deleted file mode 100644
index 5bc49001e3a..00000000000
--- a/code/modules/events/holidays/Holidays.dm
+++ /dev/null
@@ -1,183 +0,0 @@
-//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays
-GLOBAL_VAR_INIT(Holiday) // I didnt update the rest of this code because this file hasnt been ticked in years, and holiday code got overhauled
- // If it really needs fixing, yell at me, -aa
-
-//Just thinking ahead! Here's the foundations to a more robust Holiday event system.
-//It's easy as hell to add stuff. Just set Holiday to something using the switch(or something else)
-//then use if(Holiday == "MyHoliday") to make stuff happen on that specific day only
-//Please, Don't spam stuff up with easter eggs, I'd rather somebody just delete this than people cause
-//the game to lag even more in the name of one-day content.
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////
-//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////
-// ~Carn
-
-/hook/startup/proc/updateHoliday()
- Get_Holiday()
- return 1
-
-//sets up the Holiday global variable. Shouldbe called on game configuration or something.
-/proc/Get_Holiday()
- if(!Holiday) return // Holiday stuff was not enabled in the config!
-
- Holiday = null // reset our switch now so we can recycle it as our Holiday name
-
- var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year
- var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
- var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
-
- //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever
- switch(MM)
- if(1) //Jan
- switch(DD)
- if(1) Holiday = "New Year's Day"
-
- if(2) //Feb
- switch(DD)
- if(2) Holiday = "Groundhog Day"
- if(14) Holiday = "Valentine's Day"
- if(17) Holiday = "Random Acts of Kindness Day"
-
- if(3) //Mar
- switch(DD)
- if(14) Holiday = "Pi Day"
- if(17) Holiday = "St. Patrick's Day"
- if(27)
- if(YY == 16)
- Holiday = "Easter"
- if(31)
- if(YY == 13)
- Holiday = "Easter"
-
- if(4) //Apr
- switch(DD)
- if(1)
- Holiday = "April Fool's Day"
- if(YY == 18 && prob(50)) Holiday = "Easter"
- if(5)
- if(YY == 15) Holiday = "Easter"
- if(16)
- if(YY == 17) Holiday = "Easter"
- if(20)
- Holiday = "Four-Twenty"
- if(YY == 14 && prob(50)) Holiday = "Easter"
- if(22) Holiday = "Earth Day"
-
- if(5) //May
- switch(DD)
- if(1) Holiday = "Labour Day"
- if(4) Holiday = "FireFighter's Day"
- if(12) Holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3
-
- if(6) //Jun
-
- if(7) //Jul
- switch(DD)
- if(1) Holiday = "Doctor's Day"
- if(2) Holiday = "UFO Day"
- if(8) Holiday = "Writer's Day"
- if(30) Holiday = "Friendship Day"
-
- if(8) //Aug
- switch(DD)
- if(5) Holiday = "Beer Day"
-
- if(9) //Sep
- switch(DD)
- if(19) Holiday = "Talk-Like-a-Pirate Day"
- if(28) Holiday = "Stupid-Questions Day"
-
- if(10) //Oct
- switch(DD)
- if(4) Holiday = "Animal's Day"
- if(7) Holiday = "Smiling Day"
- if(16) Holiday = "Boss' Day"
- if(31) Holiday = "Halloween"
-
- if(11) //Nov
- switch(DD)
- if(1) Holiday = "Vegan Day"
- if(13) Holiday = "Kindness Day"
- if(19) Holiday = "Flowers Day"
- if(21) Holiday = "Saying-'Hello' Day"
-
- if(12) //Dec
- switch(DD)
- if(10) Holiday = "Human-Rights Day"
- if(14) Holiday = "Monkey Day"
- if(21) if(YY==12) Holiday = "End of the World"
- if(22) Holiday = "Orgasming Day" //lol. These all actually exist
- if(24) Holiday = "Christmas Eve"
- if(25) Holiday = "Christmas"
- if(26) Holiday = "Boxing Day"
- if(31) Holiday = "New Year's Eve"
-
- if(!Holiday)
- //Friday the 13th
- if(DD == 13)
- if(time2text(world.timeofday, "DDD") == "Fri")
- Holiday = "Friday the 13th"
-
-//Allows GA and GM to set the Holiday variable
-/client/proc/Set_Holiday(T as text|null)
- set name = ".Set Holiday"
- set category = "Event"
- set desc = "Force-set the Holiday variable to make the game think it's a certain day."
- if(!check_rights(R_SERVER)) return
-
- Holiday = T
- //get a new station name
- station_name = null
- station_name()
- //update our hub status
- world.update_status()
- Holiday_Game_Start()
-
- message_admins("ADMIN: Event: [key_name_admin(src)] force-set Holiday to \"[Holiday]\"")
- log_admin("[key_name(src)] force-set Holiday to \"[Holiday]\"")
-
-
-//Run at the start of a round
-/proc/Holiday_Game_Start()
- if(Holiday)
- to_chat(world, "and...")
- to_chat(world, "Happy [Holiday] Everybody!")
- switch(Holiday) //special holidays
- if("Easter")
- //do easter stuff
- if("Christmas Eve","Christmas")
- Christmas_Game_Start()
-
- return
-
-//Nested in the random events loop. Will be triggered every 2 minutes
-/proc/Holiday_Random_Event()
- switch(Holiday) //special holidays
-
- if("",null) //no Holiday today! Back to work!
- return
-
- if("Easter") //I'll make this into some helper procs at some point
-/* var/list/turf/simulated/floor/Floorlist = list()
- for(var/turf/simulated/floor/T)
- if(T.contents)
- Floorlist += T
- var/turf/simulated/floor/F = Floorlist[rand(1,Floorlist.len)]
- Floorlist = null
- var/obj/structure/closet/C = locate(/obj/structure/closet) in F
- var/obj/item/reagent_containers/food/snacks/chocolateegg/wrapped/Egg
- if( C ) Egg = new(C)
- else Egg = new(F)
-*/
-/* var/list/obj/containers = list()
- for(var/obj/item/storage/S in world)
- if(!is_station_level(S.z)) continue
- containers += S
-
- message_admins("DEBUG: Event: Egg spawned at [Egg.loc] ([Egg.x],[Egg.y],[Egg.z])")*/
- if("End of the World")
- if(prob(eventchance)) GameOver()
-
- if("Christmas","Christmas Eve")
- if(prob(eventchance)) ChristmasEvent()
diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm
index 196642ce39c..b8f45023a27 100644
--- a/code/modules/events/mass_hallucination.dm
+++ b/code/modules/events/mass_hallucination.dm
@@ -2,7 +2,13 @@
announceWhen = rand(0, 20)
/datum/event/mass_hallucination/start()
- 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.stat == DEAD)
+ continue
+ var/turf/T = get_turf(H)
+ if(!is_station_level(T.z))
+ continue
var/armor = H.getarmor(type = "rad")
if((RADIMMUNE in H.dna.species.species_traits) || armor >= 75) // Leave radiation-immune species/rad armored players completely unaffected
continue
diff --git a/code/modules/events/money_hacker.dm b/code/modules/events/money_hacker.dm
index f26c12f5f7d..ba50cbbfd8f 100644
--- a/code/modules/events/money_hacker.dm
+++ b/code/modules/events/money_hacker.dm
@@ -38,7 +38,7 @@ GLOBAL_VAR_INIT(account_hack_attempted, 0)
if(!isnull(affected_account) && !affected_account.suspended)
message = "The hack attempt has succeeded."
- var/lost = affected_account.money * (MINIMUM_PERCENTAGE_LOSS + rand(0,VARIABLE_LOSS) / 10);
+ var/lost = affected_account.money * (MINIMUM_PERCENTAGE_LOSS + rand(0,VARIABLE_LOSS) / 10)
affected_account.phantom_charge(lost)
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index 918746f4595..7007a99f0ec 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -3,14 +3,13 @@
/datum/event/sentience/start()
processing = FALSE //so it won't fire again in next tick
- var/ghostmsg = "Do you want to awaken as a sentient being?"
- var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as a sentient being?", ROLE_SENTIENT, TRUE)
var/list/potential = list()
var/sentience_type = SENTIENCE_ORGANIC
for(var/mob/living/simple_animal/L in GLOB.alive_mob_list)
var/turf/T = get_turf(L)
- if (T.z != 1)
+ if (!is_station_level(T.z))
continue
if(!(L in GLOB.player_list) && !L.mind && (L.sentience_type == sentience_type))
potential += L
diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm
index ac265adb420..85f0f8aa555 100644
--- a/code/modules/events/slaughterevent.dm
+++ b/code/modules/events/slaughterevent.dm
@@ -3,7 +3,7 @@
/datum/event/spawn_slaughter/proc/get_slaughter(var/end_if_fail = 0)
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, source = /mob/living/simple_animal/slaughter)
if(!candidates.len)
key_of_slaughter = null
return kill()
@@ -33,7 +33,7 @@
spawn_locs += get_turf(player_mind.current)
if(!spawn_locs) //If we can't find THAT, then just retry
return kill()
- var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(pick(spawn_locs))
+ var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(pick(spawn_locs))
var/mob/living/simple_animal/slaughter/S = new /mob/living/simple_animal/slaughter/(holder)
S.holder = holder
player_mind.transfer_to(S)
diff --git a/code/modules/events/spider_terror.dm b/code/modules/events/spider_terror.dm
index 75b86d20cb0..79acc49fe73 100644
--- a/code/modules/events/spider_terror.dm
+++ b/code/modules/events/spider_terror.dm
@@ -1,3 +1,4 @@
+#define TS_HIGHPOP_TRIGGER 80
/datum/event/spider_terror
announceWhen = 240
@@ -22,23 +23,32 @@
if(temp_vent.parent.other_atmosmch.len > 50)
vents += temp_vent
var/spider_type
- var/infestation_type = pick(1, 2, 3, 4, 5)
+ var/infestation_type
+ if((length(GLOB.clients)) < TS_HIGHPOP_TRIGGER)
+ infestation_type = pick(1, 2, 3, 4)
+ else
+ infestation_type = pick(2, 3, 4, 5)
switch(infestation_type)
if(1)
+ // Weakest, only used during lowpop.
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/green
spawncount = 5
if(2)
- spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/white
- spawncount = 2
- if(3)
+ // Fairly weak. Dangerous in single combat but has little staying power. Always gets whittled down.
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/prince
spawncount = 1
+ if(3)
+ // Variable. Depends how many they infect.
+ spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/white
+ spawncount = 2
if(4)
- spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen
- spawncount = 1
- if(5)
+ // Pretty strong.
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/princess
spawncount = 2
+ if(5)
+ // Strongest, only used during highpop.
+ spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen
+ spawncount = 1
while(spawncount >= 1 && vents.len)
var/obj/machinery/atmospherics/unary/vent_pump/vent = pick(vents)
@@ -60,3 +70,6 @@
new spider_type(vent.loc)
spawncount--
+
+#undef TS_HIGHPOP_TRIGGER
+
diff --git a/code/modules/events/traders.dm b/code/modules/events/traders.dm
index 0beeb3691e2..d4ae1de3b9c 100644
--- a/code/modules/events/traders.dm
+++ b/code/modules/events/traders.dm
@@ -34,7 +34,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
trader_objectives = forge_trader_objectives()
spawn()
- var/list/candidates = pollCandidates("Do you want to play as a trader?", ROLE_TRADER, 1)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a Sol Trader?", ROLE_TRADER, TRUE)
var/index = 1
while(spawn_count > 0 && candidates.len > 0)
if(index > spawnlocs.len)
diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm
index d4c06fdc3a3..a24b73ae56c 100644
--- a/code/modules/flufftext/Dreaming.dm
+++ b/code/modules/flufftext/Dreaming.dm
@@ -19,7 +19,7 @@
var/list/newlist = dreamlist.Copy()
for(var/i in 1 to newlist.len)
newlist[i] = replacetext(newlist[i], "\[DREAMER\]", "[user.name]")
- return dreamlist
+ return newlist
//NIGHTMARES
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 51ac13d4e13..21b99acb910 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -149,10 +149,12 @@ Gunshots/explosions/opening doors/less rare audio (done)
/obj/effect/hallucination/fake_flood/process()
if(!target)
qdel(src)
+ return
if(next_expand <= world.time)
radius++
if(radius > FAKE_FLOOD_MAX_RADIUS)
qdel(src)
+ return
Expand()
if((get_turf(target) in flood_turfs) && !target.internal)
target.hallucinate("fake_alert", "too_much_tox")
@@ -460,8 +462,9 @@ Gunshots/explosions/opening doors/less rare audio (done)
target = T
var/image/A = null
var/kind = force_kind ? force_kind : pick("clown", "corgi", "carp", "skeleton", "demon","zombie")
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- if(H == target)
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
+ if(H.stat == DEAD || H == target)
continue
if(skip_nearby && (H in view(target)))
continue
@@ -539,7 +542,8 @@ Gunshots/explosions/opening doors/less rare audio (done)
var/mob/living/carbon/human/clone = null
var/clone_weapon = null
- 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.stat || H.lying)
continue
clone = H
@@ -751,8 +755,10 @@ GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/projectile, /obj/ite
target.client.images.Remove(speech_overlay)
else // Radio talk
var/list/humans = list()
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- humans += H
+ for(var/thing in GLOB.human_list)
+ var/mob/living/carbon/human/H = thing
+ if(H.stat != DEAD)
+ humans += H
person = pick(humans)
target.hear_radio(message_to_multilingual(pick(radio_messages), pick(person.languages)), speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ")
qdel(src)
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index d6395bf3d43..f77d4b60ab4 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -115,7 +115,7 @@
//The reagents in the bottle splash all over the target, thanks for the idea Nodrak
SplashReagents(target)
- //Finally, smash the bottle. This kills (del) the bottle.
+ //Finally, smash the bottle. This kills (qdel) the bottle.
smash(target, user)
/obj/item/reagent_containers/food/drinks/bottle/proc/SplashReagents(mob/M)
@@ -124,6 +124,13 @@
reagents.reaction(M, REAGENT_TOUCH)
reagents.clear_reagents()
+/obj/item/reagent_containers/food/drinks/bottle/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(!reagents.total_volume)
+ C.stored_comms["glass"] += 3
+ qdel(src)
+ return TRUE
+ return ..()
+
//Keeping this here for now, I'll ask if I should keep it here.
/obj/item/broken_bottle
name = "Broken Bottle"
@@ -141,6 +148,11 @@
var/icon/broken_outline = icon('icons/obj/drinks.dmi', "broken")
sharp = 1
+/obj/item/broken_bottle/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["glass"] += 3
+ qdel(src)
+ return TRUE
+
/obj/item/reagent_containers/food/drinks/bottle/gin
name = "Griffeater Gin"
desc = "A bottle of high quality gin, produced in the New London Space Station."
diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
index 5f13f5c7a06..8313b967722 100644
--- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
@@ -13,6 +13,10 @@
max_integrity = 20
resistance_flags = ACID_PROOF
+/obj/item/reagent_containers/food/drinks/set_APTFT()
+ set hidden = FALSE
+ ..()
+
/obj/item/reagent_containers/food/drinks/drinkingglass/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/food/snacks/egg)) //breaking eggs
var/obj/item/reagent_containers/food/snacks/egg/E = I
diff --git a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
index d5d114fb43f..7b3349e4c2d 100644
--- a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
@@ -7,6 +7,7 @@
materials = list(MAT_GLASS=100)
var/light_intensity = 2
light_color = LIGHT_COLOR_LIGHTBLUE
+ resistance_flags = FLAMMABLE
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change()
if(!isShotFlammable() && (resistance_flags & ON_FIRE))
@@ -30,6 +31,7 @@
overlays += filling
name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
if(resistance_flags & ON_FIRE)
+ cut_overlay(GLOB.fire_overlay, TRUE)
overlays += "shotglass_fire"
name = "flaming [name]"
else
diff --git a/code/modules/food_and_drinks/food.dm b/code/modules/food_and_drinks/food.dm
index 27f0919a3ce..15e28cc6c62 100644
--- a/code/modules/food_and_drinks/food.dm
+++ b/code/modules/food_and_drinks/food.dm
@@ -33,6 +33,10 @@
deltimer(ant_timer)
return ..()
+/obj/item/reagent_containers/food/set_APTFT()
+ set hidden = TRUE
+ ..()
+
/obj/item/reagent_containers/food/proc/check_for_ants()
if(!antable)
return
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index bdeadf2a72c..733ac4692a3 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -29,6 +29,10 @@
/obj/item/reagent_containers/food/condiment/attack_self(mob/user)
return
+/obj/item/reagent_containers/food/condiment/set_APTFT()
+ set hidden = FALSE
+ ..()
+
/obj/item/reagent_containers/food/condiment/attack(mob/M, mob/user, def_zone)
if(!reagents || !reagents.total_volume)
diff --git a/code/modules/food_and_drinks/food/foods/desserts.dm b/code/modules/food_and_drinks/food/foods/desserts.dm
index 9e8ded0bd1b..67034e164ce 100644
--- a/code/modules/food_and_drinks/food/foods/desserts.dm
+++ b/code/modules/food_and_drinks/food/foods/desserts.dm
@@ -17,10 +17,10 @@
update_icon()
/obj/item/reagent_containers/food/snacks/icecream/update_icon()
- overlays.Cut()
- var/image/filling = image('icons/obj/kitchen.dmi', src, "icecream_color")
- filling.icon += mix_color_from_reagents(reagents.reagent_list)
- overlays += filling
+ cut_overlays()
+ var/mutable_appearance/filling = mutable_appearance('icons/obj/kitchen.dmi', "icecream_color")
+ filling.color = mix_color_from_reagents(reagents.reagent_list)
+ add_overlay(filling)
/obj/item/reagent_containers/food/snacks/icecream/icecreamcone
name = "ice cream cone"
diff --git a/code/modules/food_and_drinks/food/foods/meat.dm b/code/modules/food_and_drinks/food/foods/meat.dm
index 382e2f39022..a9f0ab3d684 100644
--- a/code/modules/food_and_drinks/food/foods/meat.dm
+++ b/code/modules/food_and_drinks/food/foods/meat.dm
@@ -30,7 +30,7 @@
name = "-meat"
var/subjectname = ""
var/subjectjob = null
- tastes = list("tender meat" = 1)
+ tastes = list("salty meat" = 1)
/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct
name = "meat product"
diff --git a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm
index 69eda1e4269..0e9ecbaa7e4 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm
@@ -43,10 +43,9 @@
return
if(istype(I, /obj/item/reagent_containers/food/snacks/icecream))
if(!I.reagents.has_reagent("sprinkles"))
- if(I.reagents.total_volume > 29) I.reagents.remove_any(1)
- I.reagents.add_reagent("sprinkles",1)
- var/image/sprinkles = image('icons/obj/kitchen.dmi', src, "sprinkles")
- I.overlays += sprinkles
+ if(I.reagents.total_volume > 29)
+ I.reagents.remove_any(1)
+ I.reagents.add_reagent("sprinkles", 1)
I.name += " with sprinkles"
I.desc += ". This also has sprinkles."
else
diff --git a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat_2.dm b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat_2.dm
deleted file mode 100644
index c1602a14251..00000000000
--- a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat_2.dm
+++ /dev/null
@@ -1,256 +0,0 @@
-#define ICECREAM_VANILLA 1
-#define FLAVOUR_CHOCOLATE 2
-#define FLAVOUR_STRAWBERRY 3
-#define FLAVOUR_BLUE 4
-#define CONE_WAFFLE 5
-#define CONE_CHOC 6
-#define INGR_MILK 7
-#define INGR_FLOUR 8
-#define INGR_SUGAR 9
-#define INGR_ICE 10
-#define MUCK 11
-
-GLOBAL_LIST_INIT(ingredients_source, list(
-"berryjuice" = FLAVOUR_STRAWBERRY,\
-"cocoa" = FLAVOUR_CHOCOLATE,\
-"singulo" = FLAVOUR_BLUE,\
-"milk" = INGR_MILK,\
-"soymilk" = INGR_MILK,\
-"ice" = INGR_ICE,\
-"flour" = INGR_FLOUR,\
-"sugar" = INGR_SUGAR,\
-))
-
-/proc/get_icecream_flavour_string(var/flavour_type)
- switch(flavour_type)
- if(FLAVOUR_CHOCOLATE)
- return "chocolate"
- if(FLAVOUR_STRAWBERRY)
- return "strawberry"
- if(FLAVOUR_BLUE)
- return "blue"
- if(CONE_WAFFLE)
- return "waffle"
- if(CONE_CHOC)
- return "chocolate"
- if(INGR_MILK)
- return "milk"
- if(INGR_FLOUR)
- return "flour"
- if(INGR_SUGAR)
- return "sugar"
- if(INGR_ICE)
- return "ice"
- if(MUCK)
- return "muck"
- else
- return "vanilla"
-
-/obj/machinery/icecream_vat
- name = "icecream vat"
- desc = "Ding-aling ding dong. Get your Nanotrasen-approved ice cream!"
- icon = 'icons/obj/kitchen.dmi'
- icon_state = "icecream_vat"
- density = 1
- anchored = 0
- max_integrity = 300
- var/list/ingredients = list()
- var/dispense_flavour = 1
- var/obj/item/reagent_containers/glass/held_container
-
-/obj/machinery/icecream_vat/New()
- ..()
- create_reagents(50)
- while(ingredients.len < 11)
- ingredients.Add(5)
-
-/obj/machinery/icecream_vat/attack_hand(mob/user)
- user.set_machine(src)
- interact(user)
-
-/obj/machinery/icecream_vat/interact(mob/user)
- var/dat
- dat += "Dispense vanilla icecream There is [ingredients[ICECREAM_VANILLA]] scoops of vanilla icecream left (made from milk and ice). "
- dat += "Dispense strawberry icecream There is [ingredients[FLAVOUR_STRAWBERRY]] dollops of strawberry flavouring left (obtained from berry juice. "
- dat += "Dispense chocolate icecream There is [ingredients[FLAVOUR_CHOCOLATE]] dollops of chocolate flavouring left (obtained from cocoa powder). "
- dat += "Dispense blue icecream There is [ingredients[FLAVOUR_BLUE]] dollops of blue flavouring left (obtained from bluespace tomato singulo). "
- dat += " "
- dat += "Dispense waffle cones There are [ingredients[CONE_WAFFLE]] waffle cones left. "
- dat += "Dispense chocolate cones There are [ingredients[CONE_CHOC]] chocolate cones left. "
- dat += " "
- dat += "Make waffle cones There is [ingredients[INGR_FLOUR]]/[ingredients[INGR_SUGAR]] of flour and sugar left. "
- dat += "Make chocolate cones There is [ingredients[FLAVOUR_CHOCOLATE]]/[ingredients[CONE_WAFFLE]] of chocolate flavouring and waffle cones left. "
- dat += "Make vanilla icecream There is [ingredients[INGR_MILK]]/[ingredients[INGR_ICE]] of milk and ice left. "
- dat += " "
- if(held_container)
- dat += "Eject [held_container] "
- else
- dat += "No beaker inserted. "
- dat += "Refresh Close"
-
- var/datum/browser/popup = new(user, "icecreamvat", name, 600, 400)
- popup.set_content(dat)
- popup.open(0)
-
-/obj/machinery/icecream_vat/attackby(obj/item/O, mob/user, params)
- if(istype(O, /obj/item/reagent_containers))
- if(istype(O, /obj/item/reagent_containers/food/snacks/icecream))
- var/obj/item/reagent_containers/food/snacks/icecream/I = O
- if(!I.ice_creamed)
- if(ingredients[ICECREAM_VANILLA] > 0)
- var/flavour_name = get_icecream_flavour_string(dispense_flavour)
- if(dispense_flavour < 11 && ingredients[dispense_flavour] > 0)
- visible_message("[bicon(src)] [user] scoops delicious [flavour_name] flavoured icecream into [I].")
- ingredients[dispense_flavour] -= 1
- ingredients[ICECREAM_VANILLA] -= 1
-
- I.add_ice_cream(dispense_flavour)
- if(held_container)
- held_container.reagents.trans_to(I, 10)
- if(I.reagents.total_volume < 10)
- I.reagents.add_reagent("sugar", 10 - I.reagents.total_volume)
- else
- to_chat(user, "There is not enough [flavour_name] flavouring left! Insert more of the required ingredients.")
- else
- to_chat(user, "There is not enough icecream left! Insert more milk and ice.")
- else
- to_chat(user, "[O] already has icecream in it.")
- else if(istype(O, /obj/item/reagent_containers/glass))
- if(held_container)
- to_chat(user, "You must remove [held_container] from [src] first.")
- else
- if(!user.drop_item())
- to_chat(user, "\The [O] is stuck to your hand!")
- return
- O.forceMove(src)
- to_chat(user, "You insert [O] into [src].")
- held_container = O
- else
- var/obj/item/reagent_containers/R = O
- if(R.reagents)
- visible_message("[user] has emptied all of [R] into [src].")
- for(var/datum/reagent/current_reagent in R.reagents.reagent_list)
- if(GLOB.ingredients_source[current_reagent.id])
- add(GLOB.ingredients_source[current_reagent.id], current_reagent.volume / 2)
- else
- add(MUCK, current_reagent.volume / 5)
- R.reagents.clear_reagents()
- return 1
- else
- return ..()
-
-/obj/machinery/icecream_vat/proc/add(var/add_type, var/amount)
- if(add_type <= ingredients.len)
- ingredients[add_type] += amount
- updateDialog()
-
-/obj/machinery/icecream_vat/proc/make(var/mob/user, var/make_type)
- switch(make_type)
- if(CONE_WAFFLE)
- if(ingredients[INGR_FLOUR] > 0 && ingredients[INGR_SUGAR] > 0)
- var/amount = max( min(ingredients[INGR_FLOUR], ingredients[INGR_SUGAR]), 5)
- ingredients[INGR_FLOUR] -= amount
- ingredients[INGR_SUGAR] -= amount
- ingredients[CONE_WAFFLE] += amount
- visible_message("[user] cooks up some waffle cones.")
- else
- to_chat(user, "You require sugar and flour to make waffle cones.")
- if(CONE_CHOC)
- if(ingredients[FLAVOUR_CHOCOLATE] > 0 && ingredients[CONE_WAFFLE] > 0)
- var/amount = min(ingredients[CONE_WAFFLE], ingredients[FLAVOUR_CHOCOLATE])
- ingredients[CONE_WAFFLE] -= amount
- ingredients[FLAVOUR_CHOCOLATE] -= amount
- ingredients[CONE_CHOC] += amount
- visible_message("[user] cooks up some chocolate cones.")
- else
- to_chat(user, "You require waffle cones and chocolate flavouring to make chocolate cones.")
- if(ICECREAM_VANILLA)
- if(ingredients[INGR_ICE] > 0 && ingredients[INGR_MILK] > 0)
- var/amount = min(ingredients[INGR_ICE], ingredients[INGR_MILK])
- ingredients[INGR_ICE] -= amount
- ingredients[INGR_MILK] -= amount
- ingredients[ICECREAM_VANILLA] += amount
- visible_message("[user] whips up some vanilla icecream.")
- else
- to_chat(user, "You require milk and ice to make vanilla icecream.")
- updateDialog()
-
-/obj/machinery/icecream_vat/Topic(href, href_list)
- if(..())
- return
- if(href_list["dispense"])
- dispense_flavour = text2num(href_list["dispense"])
- visible_message("[usr] sets [src] to dispense [get_icecream_flavour_string(dispense_flavour)] flavoured icecream.")
-
- if(href_list["cone"])
- var/dispense_cone = text2num(href_list["cone"])
- if(ingredients[dispense_cone] <= ingredients.len)
- var/cone_name = get_icecream_flavour_string(dispense_cone)
- if(ingredients[dispense_cone] >= 1)
- ingredients[dispense_cone] -= 1
- var/obj/item/reagent_containers/food/snacks/icecream/I = new(loc)
- I.cone_type = cone_name
- I.icon_state = "icecream_cone_[cone_name]"
- I.desc = "Delicious [cone_name] cone, but no ice cream."
- visible_message("[usr] dispenses a crunchy [cone_name] cone from [src].")
- else
- to_chat(usr, "There are no [cone_name] cones left!")
- updateDialog()
-
- if(href_list["make"])
- make( usr, text2num(href_list["make"]) )
- updateDialog()
-
- if(href_list["eject"])
- if(held_container)
- held_container.forceMove(loc)
- held_container = null
- updateDialog()
-
- if(href_list["refresh"])
- updateDialog()
-
- if(href_list["close"])
- usr.unset_machine()
- usr << browse(null,"window=icecreamvat")
- return
-
-/obj/machinery/icecream_vat/deconstruct(disassembled = TRUE)
- if(!(flags & NODECONSTRUCT))
- new /obj/item/stack/sheet/metal(loc, 4)
- qdel(src)
-
-
-/obj/item/reagent_containers/food/snacks/icecream
- name = "ice cream cone"
- desc = "Delicious waffle cone, but no ice cream."
- icon = 'icons/obj/kitchen.dmi'
- icon_state = "icecream_cone"
- layer = 3.1
- var/ice_creamed = 0
- var/cone_type
- bitesize = 3
-
-/obj/item/reagent_containers/food/snacks/icecream/New()
- ..()
- create_reagents(20)
- reagents.add_reagent("nutriment", 5)
-
-/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(var/flavour)
- var/flavour_name = get_icecream_flavour_string(flavour)
- name = "[flavour_name] icecream"
- overlays += "icecream_[flavour_name]"
- desc = "Delicious [cone_type] cone with a dollop of [flavour_name] ice cream."
- ice_creamed = 1
-
-#undef ICECREAM_VANILLA
-#undef FLAVOUR_CHOCOLATE
-#undef FLAVOUR_STRAWBERRY
-#undef FLAVOUR_BLUE
-#undef CONE_WAFFLE
-#undef CONE_CHOC
-#undef INGR_MILK
-#undef INGR_FLOUR
-#undef INGR_SUGAR
-#undef INGR_ICE
-#undef MUCK
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index 346b78e5b26..f179de1459a 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -1,51 +1,73 @@
-/* SmartFridge. Much todo
-*/
+#define SMART_FRIDGE_LOCK_SHORTED -1
+
+/**
+ * # Smart Fridge
+ *
+ * Stores items of a specified type.
+ */
/obj/machinery/smartfridge
name = "\improper SmartFridge"
icon = 'icons/obj/vending.dmi'
icon_state = "smartfridge"
layer = 2.9
- density = 1
- anchored = 1
+ density = TRUE
+ anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 5
active_power_usage = 100
+ /// The maximum number of items the fridge can hold. Multiplicated by the matter bin component's rating.
var/max_n_of_items = 1500
- var/item_quants = list()
+ /// Associative list (/text => /number) tracking the amounts of a specific item held by the fridge.
+ var/list/item_quants
+ /// How long in ticks the fridge is electrified for. Decrements every process.
var/seconds_electrified = 0
+ /// Whether the fridge should randomly shoot held items at a nearby living target or not.
var/shoot_inventory = FALSE
+ /// Whether the fridge is locked. Used for the secure variant of the fridge.
var/locked = FALSE
+ /// Whether the fridge requires ID scanning. Used for the secure variant of the fridge.
var/scan_id = TRUE
+ /// Whether the fridge is considered secure. Used for wiring and display.
var/is_secure = FALSE
+ /// Whether the fridge can dry its' contents. Used for display.
var/can_dry = FALSE
+ /// Whether the fridge is currently drying. Used by [drying racks][/obj/machinery/smartfridge/drying_rack].
var/drying = FALSE
+ /// Whether the fridge's contents are visible on the world icon.
var/visible_contents = TRUE
- var/datum/wires/smartfridge/wires = null
+ /// The wires controlling the fridge.
+ var/datum/wires/smartfridge/wires
+ /// Typecache of accepted item types, init it in [/obj/machinery/smartfridge/Initialize].
+ var/list/accepted_items_typecache
-/obj/machinery/smartfridge/New()
- ..()
+/obj/machinery/smartfridge/Initialize(mapload)
+ . = ..()
+ item_quants = list()
+ // Reagents
create_reagents()
reagents.set_reacting(FALSE)
+ // Components
component_parts = list()
var/obj/item/circuitboard/smartfridge/board = new(null)
board.set_type(type)
component_parts += board
component_parts += new /obj/item/stock_parts/matter_bin(null)
RefreshParts()
-
-/obj/machinery/smartfridge/RefreshParts()
- for(var/obj/item/stock_parts/matter_bin/B in component_parts)
- max_n_of_items = 1500 * B.rating
-
-/obj/machinery/smartfridge/secure
- is_secure = 1
-
-/obj/machinery/smartfridge/New()
- ..()
+ // Wires
if(is_secure)
wires = new/datum/wires/smartfridge/secure(src)
else
wires = new/datum/wires/smartfridge(src)
+ // Accepted items
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/food/snacks/grown,
+ /obj/item/seeds,
+ /obj/item/grown,
+ ))
+
+/obj/machinery/smartfridge/RefreshParts()
+ for(var/obj/item/stock_parts/matter_bin/B in component_parts)
+ max_n_of_items = 1500 * B.rating
/obj/machinery/smartfridge/Destroy()
QDEL_NULL(wires)
@@ -53,154 +75,6 @@
A.forceMove(loc)
return ..()
-/obj/machinery/smartfridge/proc/accept_check(obj/item/O)
- if(istype(O,/obj/item/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/) || istype(O,/obj/item/grown/))
- return 1
- return 0
-
-/obj/machinery/smartfridge/seeds
- name = "\improper MegaSeed Servitor"
- desc = "When you need seeds fast!"
- icon = 'icons/obj/vending.dmi'
- icon_state = "seeds"
-
-/obj/machinery/smartfridge/seeds/accept_check(obj/item/O)
- if(istype(O,/obj/item/seeds/))
- return 1
- return 0
-
-/obj/machinery/smartfridge/medbay
- name = "\improper Refrigerated Medicine Storage"
- desc = "A refrigerated storage unit for storing medicine and chemicals."
- icon_state = "smartfridge" //To fix the icon in the map editor.
-
-/obj/machinery/smartfridge/medbay/accept_check(obj/item/O)
- if(istype(O,/obj/item/reagent_containers/glass))
- return 1
- if(istype(O,/obj/item/reagent_containers/iv_bag))
- return 1
- if(istype(O,/obj/item/storage/pill_bottle))
- return 1
- if(ispill(O))
- return 1
- return 0
-
-/obj/machinery/smartfridge/secure/extract
- name = "\improper Slime Extract Storage"
- desc = "A refrigerated storage unit for slime extracts"
- req_access_txt = "47"
-
-/obj/machinery/smartfridge/secure/extract/accept_check(obj/item/O)
- if(istype(O,/obj/item/slime_extract))
- return 1
- return 0
-
-/obj/machinery/smartfridge/secure/medbay
- name = "\improper Secure Refrigerated Medicine Storage"
- desc = "A refrigerated storage unit for storing medicine and chemicals."
- icon_state = "smartfridge" //To fix the icon in the map editor.
- req_one_access_txt = "5;33"
-
-/obj/machinery/smartfridge/secure/medbay/accept_check(obj/item/O)
- if(istype(O,/obj/item/reagent_containers/glass))
- return 1
- if(istype(O,/obj/item/reagent_containers/iv_bag))
- return 1
- if(istype(O,/obj/item/storage/pill_bottle))
- return 1
- if(ispill(O))
- return 1
- return 0
-
-/obj/machinery/smartfridge/secure/chemistry
- name = "\improper Smart Chemical Storage"
- desc = "A refrigerated storage unit for medicine and chemical storage."
- icon_state = "smartfridge" //To fix the icon in the map editor.
- req_access_txt = "33"
- var/list/spawn_meds = list()
-
-/obj/machinery/smartfridge/secure/chemistry/New()
- ..()
- for(var/typekey in spawn_meds)
- var/amount = spawn_meds[typekey]
- if(isnull(amount)) amount = 1
- while(amount)
- var/obj/item/I = new typekey(src)
- if(item_quants[I.name])
- item_quants[I.name]++
- else
- item_quants[I.name] = 1
- SSnanoui.update_uis(src)
- amount--
- update_icon()
-
-/obj/machinery/smartfridge/secure/chemistry/accept_check(obj/item/O)
- if(istype(O,/obj/item/storage/pill_bottle) || istype(O,/obj/item/reagent_containers))
- return 1
- return 0
-
-/obj/machinery/smartfridge/secure/chemistry/preloaded
- spawn_meds = list(
- /obj/item/reagent_containers/food/pill/epinephrine = 12,
- /obj/item/reagent_containers/food/pill/charcoal = 5,
- /obj/item/reagent_containers/glass/bottle/epinephrine = 1,
- /obj/item/reagent_containers/glass/bottle/charcoal = 1)
-
-/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate
- req_access_txt = null
- req_access = list(ACCESS_SYNDICATE)
-
-/obj/machinery/smartfridge/disks
- name = "disk compartmentalizer"
- desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
- icon_state = "disktoaster"
- pass_flags = PASSTABLE
- visible_contents = FALSE
-
-/obj/machinery/smartfridge/disks/accept_check(obj/item/O)
- return istype(O, /obj/item/disk)
-
-// ----------------------------
-// Virology Medical Smartfridge
-// ----------------------------
-/obj/machinery/smartfridge/secure/chemistry/virology
- name = "Smart Virus Storage"
- desc = "A refrigerated storage unit for volatile sample storage."
- req_access_txt = "39"
- spawn_meds = list(/obj/item/reagent_containers/syringe/antiviral = 4,
- /obj/item/reagent_containers/glass/bottle/cold = 1,
- /obj/item/reagent_containers/glass/bottle/flu_virion = 1,
- /obj/item/reagent_containers/glass/bottle/mutagen = 1,
- /obj/item/reagent_containers/glass/bottle/plasma = 1,
- /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1)
-
-/obj/machinery/smartfridge/secure/chemistry/virology/accept_check(obj/item/O)
- if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker))
- return 1
- return 0
-
-/obj/machinery/smartfridge/secure/chemistry/virology/preloaded
- spawn_meds = list(
- /obj/item/reagent_containers/syringe/antiviral = 4,
- /obj/item/reagent_containers/glass/bottle/cold = 1,
- /obj/item/reagent_containers/glass/bottle/flu_virion = 1,
- /obj/item/reagent_containers/glass/bottle/mutagen = 1,
- /obj/item/reagent_containers/glass/bottle/plasma = 1,
- /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1,
- /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1)
-
-/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate
- req_access_txt = null
- req_access = list(ACCESS_SYNDICATE)
-
-/obj/machinery/smartfridge/drinks
- name = "\improper Drink Showcase"
- desc = "A refrigerated storage unit for tasty tasty alcohol."
-
-/obj/machinery/smartfridge/drinks/accept_check(obj/item/O)
- if(istype(O,/obj/item/reagent_containers/glass) || istype(O,/obj/item/reagent_containers/food/drinks) || istype(O,/obj/item/reagent_containers/food/condiment))
- return 1
-
/obj/machinery/smartfridge/process()
if(stat & (BROKEN|NOPOWER))
return
@@ -216,52 +90,57 @@
update_icon()
/obj/machinery/smartfridge/update_icon()
+ var/prefix = initial(icon_state)
if(stat & (BROKEN|NOPOWER))
- icon_state = "[initial(icon_state)]-off"
+ icon_state = "[prefix]-off"
else if(visible_contents)
- switch(contents.len)
+ switch(length(contents))
if(0)
- icon_state = "[initial(icon_state)]"
+ icon_state = "[prefix]"
if(1 to 25)
- icon_state = "[initial(icon_state)]1"
+ icon_state = "[prefix]1"
if(26 to 75)
- icon_state = "[initial(icon_state)]2"
+ icon_state = "[prefix]2"
if(76 to INFINITY)
- icon_state = "[initial(icon_state)]3"
+ icon_state = "[prefix]3"
else
- icon_state = "[initial(icon_state)]"
+ icon_state = "[prefix]"
-/*******************
-* Item Adding
-********************/
-
-/obj/machinery/smartfridge/default_deconstruction_screwdriver(mob/user, obj/item/screwdriver/S)
- . = ..(user, icon_state, icon_state, S)
+// Interactions
+/obj/machinery/smartfridge/screwdriver_act(mob/living/user, obj/item/I)
+ . = default_deconstruction_screwdriver(user, icon_state, icon_state, I)
+ if(!.)
+ return
overlays.Cut()
if(panel_open)
overlays += image(icon, "[initial(icon_state)]-panel")
-/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user)
- if(default_deconstruction_screwdriver(user, O))
- return
-
- if(exchange_parts(user, O))
- return
-
- if(default_unfasten_wrench(user, O))
+/obj/machinery/smartfridge/wrench_act(mob/living/user, obj/item/I)
+ . = default_unfasten_wrench(user, I)
+ if(.)
power_change()
- return
- if(default_deconstruction_crowbar(user, O))
- return
+/obj/machinery/smartfridge/crowbar_act(mob/living/user, obj/item/I)
+ . = default_deconstruction_crowbar(user, I)
- if(istype(O, /obj/item/multitool)||istype(O, /obj/item/wirecutters))
- if(panel_open)
- attack_hand(user)
- return
+/obj/machinery/smartfridge/wirecutter_act(mob/living/user, obj/item/I)
+ if(panel_open)
+ attack_hand(user)
+ return TRUE
+ return ..()
- if(stat & NOPOWER)
+/obj/machinery/smartfridge/multitool_act(mob/living/user, obj/item/I)
+ if(panel_open)
+ attack_hand(user)
+ return TRUE
+ return ..()
+
+/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user)
+ if(exchange_parts(user, O))
+ SSnanoui.update_uis(src)
+ return
+ if(stat & (BROKEN|NOPOWER))
to_chat(user, "\The [src] is unpowered and useless.")
return
@@ -269,92 +148,63 @@
user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
SSnanoui.update_uis(src)
update_icon()
-
else if(istype(O, /obj/item/storage/bag))
var/obj/item/storage/bag/P = O
- var/plants_loaded = 0
+ var/items_loaded = 0
for(var/obj/G in P.contents)
if(load(G, user))
- plants_loaded++
- if(plants_loaded)
+ items_loaded++
+ if(items_loaded)
user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].")
- if(P.contents.len > 0)
- to_chat(user, "Some items are refused.")
-
- SSnanoui.update_uis(src)
- update_icon()
-
+ SSnanoui.update_uis(src)
+ update_icon()
+ var/failed = length(P.contents)
+ if(failed)
+ to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.")
else if(!istype(O, /obj/item/card/emag))
to_chat(user, "\The [src] smartly refuses [O].")
- return 1
-
-/obj/machinery/smartfridge/proc/load(obj/I, mob/user)
- if(accept_check(I))
- if(contents.len >= max_n_of_items)
- to_chat(user, "\The [src] is full.")
- return 0
- else
- if(istype(I.loc, /obj/item/storage))
- var/obj/item/storage/S = I.loc
- S.remove_from_storage(I, src)
- else if(istype(I.loc, /mob))
- var/mob/M = I.loc
- if(M.get_active_hand() == I)
- if(!M.drop_item())
- to_chat(user, "\The [I] is stuck to you!")
- return 0
- else
- M.unEquip(I)
- I.forceMove(src)
- else
- I.forceMove(src)
-
- if(item_quants[I.name])
- item_quants[I.name]++
- else
- item_quants[I.name] = 1
- return 1
- return 0
+ return TRUE
/obj/machinery/smartfridge/attack_ai(mob/user)
- return 0
+ return FALSE
/obj/machinery/smartfridge/attack_ghost(mob/user)
return attack_hand(user)
/obj/machinery/smartfridge/attack_hand(mob/user)
- if(stat & (NOPOWER|BROKEN))
+ if(stat & (BROKEN|NOPOWER))
return
wires.Interact(user)
ui_interact(user)
+ return ..()
//Drag pill bottle to fridge to empty it into the fridge
/obj/machinery/smartfridge/MouseDrop_T(obj/over_object, mob/user)
if(!istype(over_object, /obj/item/storage/pill_bottle)) //Only pill bottles, please
return
-
- if(stat & NOPOWER)
+ if(stat & (BROKEN|NOPOWER))
to_chat(user, "\The [src] is unpowered and useless.")
return
var/obj/item/storage/box/pillbottles/P = over_object
+ if(!length(P.contents))
+ to_chat(user, "\The [P] is empty.")
+ return
+
var/items_loaded = 0
for(var/obj/G in P.contents)
if(load(G, user))
items_loaded++
if(items_loaded)
- user.visible_message( \
- "[user] empties \the [P] into \the [src].", \
- "You empty \the [P] into \the [src].")
- if(P.contents.len > 0)
- to_chat(user, "Some items are refused.")
- SSnanoui.update_uis(src)
+ user.visible_message("[user] empties \the [P] into \the [src].", "You empty \the [P] into \the [src].")
+ SSnanoui.update_uis(src)
+ update_icon()
+ var/failed = length(P.contents)
+ if(failed)
+ to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.")
-/*******************
-* SmartFridge Menu
-********************/
-
-/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
+// UI
+/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
user.set_machine(src)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -374,13 +224,13 @@
data["drying"] = drying
var/list/items[0]
- for(var/i=1 to length(item_quants))
+ for(var/i in 1 to length(item_quants))
var/K = item_quants[i]
var/count = item_quants[K]
if(count > 0)
items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count)))
- if(items.len > 0)
+ if(length(items))
data["contents"] = items
return data
@@ -402,6 +252,8 @@
if(href_list["vend"])
var/index = text2num(href_list["vend"])
var/amount = text2num(href_list["amount"])
+ if(isnull(index) || !ISINDEXSAFE(item_quants, index) || isnull(amount))
+ return FALSE
var/K = item_quants[index]
var/count = item_quants[K]
@@ -429,36 +281,343 @@
if(i <= 0)
return TRUE
return TRUE
+
return FALSE
+/**
+ * Tries to load an item if it is accepted by [/obj/machinery/smartfridge/proc/accept_check].
+ *
+ * Arguments:
+ * * I - The item to load.
+ * * user - The user trying to load the item.
+ */
+/obj/machinery/smartfridge/proc/load(obj/I, mob/user)
+ if(accept_check(I))
+ if(length(contents) >= max_n_of_items)
+ to_chat(user, "\The [src] is full.")
+ return FALSE
+ else
+ if(istype(I.loc, /obj/item/storage))
+ var/obj/item/storage/S = I.loc
+ S.remove_from_storage(I, src)
+ else if(ismob(I.loc))
+ var/mob/M = I.loc
+ if(M.get_active_hand() == I)
+ if(!M.drop_item())
+ to_chat(user, "\The [I] is stuck to you!")
+ return FALSE
+ else
+ M.unEquip(I)
+ I.forceMove(src)
+ else
+ I.forceMove(src)
+
+ item_quants[I.name] += 1
+ return TRUE
+ return FALSE
+
+/**
+ * Tries to shoot a random at a nearby living mob.
+ */
/obj/machinery/smartfridge/proc/throw_item()
- var/obj/throw_item = null
- var/mob/living/target = locate() in view(7,src)
+ var/obj/item/throw_item = null
+ var/mob/living/target = locate() in view(7, src)
if(!target)
- return 0
+ return FALSE
for(var/O in item_quants)
if(item_quants[O] <= 0) //Try to use a record that actually has something to dump.
continue
-
item_quants[O]--
- for(var/obj/T in contents)
- if(T.name == O)
- T.forceMove(loc)
- throw_item = T
+ for(var/obj/I in contents)
+ if(I.name == O)
+ I.forceMove(loc)
+ throw_item = I
update_icon()
break
- break
if(!throw_item)
- return 0
- spawn(0)
- throw_item.throw_at(target,16,3,src)
- visible_message("[src] launches [throw_item.name] at [target.name]!")
- return 1
+ return FALSE
-// ----------------------------
-// Drying Rack 'smartfridge'
-// ----------------------------
+ INVOKE_ASYNC(throw_item, /atom/movable.proc/throw_at, target, 16, 3, src)
+ visible_message("[src] launches [throw_item.name] at [target.name]!")
+ return TRUE
+
+/**
+ * Returns whether the smart fridge can accept the given item.
+ *
+ * By default checks if the item is in [the typecache][/obj/machinery/smartfridge/var/accepted_items_typecache].
+ * Arguments:
+ * * O - The item to check.
+ */
+/obj/machinery/smartfridge/proc/accept_check(obj/item/O)
+ return is_type_in_typecache(O, accepted_items_typecache)
+
+/**
+ * # Secure Fridge
+ *
+ * Secure variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ * Can be emagged and EMP'd to short the lock.
+ */
+/obj/machinery/smartfridge/secure
+ is_secure = TRUE
+
+/obj/machinery/smartfridge/secure/emag_act(mob/user)
+ emagged = TRUE
+ locked = SMART_FRIDGE_LOCK_SHORTED
+ to_chat(user, "You short out the product lock on \the [src].")
+
+/obj/machinery/smartfridge/secure/emp_act(severity)
+ if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && prob(40 / severity))
+ playsound(loc, 'sound/effects/sparks4.ogg', 60, TRUE)
+ emagged = TRUE
+ locked = SMART_FRIDGE_LOCK_SHORTED
+
+/obj/machinery/smartfridge/secure/Topic(href, href_list)
+ if(stat & (BROKEN|NOPOWER))
+ return FALSE
+
+ if(href_list["vend"] && (usr.contents.Find(src) || Adjacent(usr)))
+ if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && scan_id && !allowed(usr))
+ to_chat(usr, "Access denied.")
+ SSnanoui.update_uis(src)
+ return FALSE
+
+ return ..()
+
+/**
+ * # Seed Storage
+ *
+ * Seeds variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ * Formerly known as MegaSeed Servitor, but renamed to avoid confusion with the [vending machine][/obj/machinery/vending/hydroseeds].
+ */
+/obj/machinery/smartfridge/seeds
+ name = "\improper Seed Storage"
+ desc = "When you need seeds fast!"
+ icon = 'icons/obj/vending.dmi'
+ icon_state = "seeds"
+
+/obj/machinery/smartfridge/seeds/Initialize(mapload)
+ . = ..()
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/seeds
+ ))
+
+/**
+ * # Refrigerated Medicine Storage
+ *
+ * Medical variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/medbay
+ name = "\improper Refrigerated Medicine Storage"
+ desc = "A refrigerated storage unit for storing medicine and chemicals."
+ icon_state = "smartfridge" //To fix the icon in the map editor.
+
+/obj/machinery/smartfridge/medbay/Initialize(mapload)
+ . = ..()
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/glass,
+ /obj/item/reagent_containers/iv_bag,
+ /obj/item/reagent_containers/applicator,
+ /obj/item/storage/pill_bottle,
+ /obj/item/reagent_containers/food/pill,
+ ))
+
+/**
+ * # Slime Extract Storage
+ *
+ * Secure, Xenobiology variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/secure/extract
+ name = "\improper Slime Extract Storage"
+ desc = "A refrigerated storage unit for slime extracts"
+
+/obj/machinery/smartfridge/secure/extract/Initialize(mapload)
+ . = ..()
+ req_access_txt = "[ACCESS_RESEARCH]"
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/slime_extract
+ ))
+
+/**
+ * # Secure Refrigerated Medicine Storage
+ *
+ * Secure, Medical variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/secure/medbay
+ name = "\improper Secure Refrigerated Medicine Storage"
+ desc = "A refrigerated storage unit for storing medicine and chemicals."
+ icon_state = "smartfridge" //To fix the icon in the map editor.
+ req_one_access_txt = "5;33"
+
+/obj/machinery/smartfridge/secure/medbay/Initialize(mapload)
+ . = ..()
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/glass,
+ /obj/item/reagent_containers/iv_bag,
+ /obj/item/reagent_containers/applicator,
+ /obj/item/storage/pill_bottle,
+ /obj/item/reagent_containers/food/pill,
+ ))
+
+/**
+ * # Smart Chemical Storage
+ *
+ * Secure, Chemistry variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/secure/chemistry
+ name = "\improper Smart Chemical Storage"
+ desc = "A refrigerated storage unit for medicine and chemical storage."
+ icon_state = "smartfridge" //To fix the icon in the map editor.
+ /// Associative list (/obj/item => /number) representing the items the fridge should initially contain.
+ var/list/spawn_meds
+
+/obj/machinery/smartfridge/secure/chemistry/Initialize(mapload)
+ . = ..()
+ req_access_txt = "[ACCESS_CHEMISTRY]"
+ // Spawn initial chemicals
+ if(mapload)
+ LAZYINITLIST(spawn_meds)
+ for(var/typekey in spawn_meds)
+ var/amount = spawn_meds[typekey] || 1
+ while(amount--)
+ var/obj/item/I = new typekey(src)
+ item_quants[I.name] += 1
+ update_icon()
+ // Accepted items
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/storage/pill_bottle,
+ /obj/item/reagent_containers,
+ ))
+
+/**
+ * # Smart Chemical Storage (Preloaded)
+ *
+ * A [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry] but with some items already in.
+ */
+/obj/machinery/smartfridge/secure/chemistry/preloaded
+ // I exist!
+
+/obj/machinery/smartfridge/secure/chemistry/preloaded/Initialize(mapload)
+ spawn_meds = list(
+ /obj/item/reagent_containers/food/pill/epinephrine = 12,
+ /obj/item/reagent_containers/food/pill/charcoal = 5,
+ /obj/item/reagent_containers/glass/bottle/epinephrine = 1,
+ /obj/item/reagent_containers/glass/bottle/charcoal = 1,
+ )
+ . = ..()
+
+/**
+ * # Smart Chemical Storage (Preloaded, Syndicate)
+ *
+ * A [Smart Chemical Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/preloaded] but with exclusive access to Syndicate.
+ */
+/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate
+ req_access_txt = null
+
+/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate/Initialize(mapload)
+ . = ..()
+ req_access = list(ACCESS_SYNDICATE)
+
+/**
+ * # Disk Compartmentalizer
+ *
+ * Disk variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/disks
+ name = "disk compartmentalizer"
+ desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
+ icon_state = "disktoaster"
+ pass_flags = PASSTABLE
+ visible_contents = FALSE
+
+/obj/machinery/smartfridge/disks/Initialize(mapload)
+ . = ..()
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/disk,
+ ))
+
+/**
+ * # Smart Virus Storage
+ *
+ * Secure, Virology variant of the [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry].
+ * Comes with some items.
+ */
+/obj/machinery/smartfridge/secure/chemistry/virology
+ name = "\improper Smart Virus Storage"
+ desc = "A refrigerated storage unit for volatile sample storage."
+
+/obj/machinery/smartfridge/secure/chemistry/virology/Initialize(mapload)
+ spawn_meds = list(
+ /obj/item/reagent_containers/syringe/antiviral = 4,
+ /obj/item/reagent_containers/glass/bottle/cold = 1,
+ /obj/item/reagent_containers/glass/bottle/flu_virion = 1,
+ /obj/item/reagent_containers/glass/bottle/mutagen = 1,
+ /obj/item/reagent_containers/glass/bottle/plasma = 1,
+ /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1
+ )
+ . = ..()
+ req_access_txt = "[ACCESS_VIROLOGY]"
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/syringe,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/glass/beaker,
+ ))
+
+/**
+ * # Smart Virus Storage (Preloaded)
+ *
+ * A [Smart Virus Storage][/obj/machinery/smartfridge/secure/chemistry/virology] but with some additional items.
+ */
+/obj/machinery/smartfridge/secure/chemistry/virology/preloaded
+ // I exist!
+
+/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/Initialize(mapload)
+ spawn_meds = list(
+ /obj/item/reagent_containers/syringe/antiviral = 4,
+ /obj/item/reagent_containers/glass/bottle/cold = 1,
+ /obj/item/reagent_containers/glass/bottle/flu_virion = 1,
+ /obj/item/reagent_containers/glass/bottle/mutagen = 1,
+ /obj/item/reagent_containers/glass/bottle/plasma = 1,
+ /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1,
+ /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1
+ )
+ . = ..()
+
+/**
+ * # Smart Virus Storage (Preloaded, Syndicate)
+ *
+ * A [Smart Virus Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/virology/preloaded] but with exclusive access to Syndicate.
+ */
+/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate
+ req_access_txt = null
+
+/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate/Initialize(mapload)
+ . = ..()
+ req_access = list(ACCESS_SYNDICATE)
+
+/**
+ * # Drink Showcase
+ *
+ * Drink variant of the [Smart Fridge][/obj/machinery/smartfridge].
+ */
+/obj/machinery/smartfridge/drinks
+ name = "\improper Drink Showcase"
+ desc = "A refrigerated storage unit for tasty tasty alcohol."
+
+/obj/machinery/smartfridge/drinks/Initialize(mapload)
+ . = ..()
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/glass,
+ /obj/item/reagent_containers/food/drinks,
+ /obj/item/reagent_containers/food/condiment,
+ ))
+
+/**
+ * # Drying Rack
+ *
+ * Variant of the [Smart Fridge][/obj/machinery/smartfridge] for drying stuff.
+ * Doesn't have components.
+ */
/obj/machinery/smartfridge/drying_rack
name = "drying rack"
desc = "A wooden contraption, used to dry plant products, food and leather."
@@ -470,11 +629,16 @@
can_dry = TRUE
visible_contents = FALSE
-/obj/machinery/smartfridge/drying_rack/New()
- ..()
- if(component_parts && component_parts.len)
- component_parts.Cut()
+/obj/machinery/smartfridge/drying_rack/Initialize(mapload)
+ . = ..()
+ // Remove components, this is wood duh
+ QDEL_LIST(component_parts)
component_parts = null
+ // Accepted items
+ accepted_items_typecache = typecacheof(list(
+ /obj/item/reagent_containers/food/snacks,
+ /obj/item/stack/sheet/wetleather,
+ ))
/obj/machinery/smartfridge/drying_rack/on_deconstruction()
new /obj/item/stack/sheet/wood(loc, 10)
@@ -483,34 +647,6 @@
/obj/machinery/smartfridge/drying_rack/RefreshParts()
return
-/obj/machinery/smartfridge/drying_rack/default_deconstruction_screwdriver()
- return
-
-/obj/machinery/smartfridge/drying_rack/exchange_parts()
- return
-
-/obj/machinery/smartfridge/drying_rack/spawn_frame()
- return
-
-/obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(user, obj/item/crowbar/C, ignore_panel = 1)
- ..()
-
-/obj/machinery/smartfridge/drying_rack/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["dryingOn"])
- drying = TRUE
- use_power = ACTIVE_POWER_USE
- update_icon()
- return 1
-
- if(href_list["dryingOff"])
- drying = FALSE
- use_power = IDLE_POWER_USE
- update_icon()
- return 1
- return 0
-
/obj/machinery/smartfridge/drying_rack/power_change()
if(powered() && anchored)
stat &= ~NOPOWER
@@ -519,35 +655,67 @@
toggle_drying(TRUE)
update_icon()
-/obj/machinery/smartfridge/drying_rack/load(obj/I, mob/user) //For updating the filled overlay
+/obj/machinery/smartfridge/drying_rack/screwdriver_act(mob/living/user, obj/item/I)
+ return
+
+/obj/machinery/smartfridge/drying_rack/exchange_parts()
+ return
+
+/obj/machinery/smartfridge/drying_rack/spawn_frame()
+ return
+
+/obj/machinery/smartfridge/drying_rack/crowbar_act(mob/living/user, obj/item/I)
+ . = default_deconstruction_crowbar(user, I, TRUE)
+
+/obj/machinery/smartfridge/drying_rack/emp_act(severity)
+ ..()
+ atmos_spawn_air(LINDA_SPAWN_HEAT)
+
+/obj/machinery/smartfridge/drying_rack/Topic(href, href_list)
if(..())
+ return TRUE
+
+ if(href_list["dryingOn"])
+ drying = TRUE
+ use_power = ACTIVE_POWER_USE
update_icon()
- return 1
+ return TRUE
+
+ if(href_list["dryingOff"])
+ drying = FALSE
+ use_power = IDLE_POWER_USE
+ update_icon()
+ return TRUE
+
+ return FALSE
/obj/machinery/smartfridge/drying_rack/update_icon()
..()
-
overlays.Cut()
if(drying)
overlays += "drying_rack_drying"
- if(contents.len)
+ if(length(contents))
overlays += "drying_rack_filled"
/obj/machinery/smartfridge/drying_rack/process()
..()
- if(drying)
- if(rack_dry())//no need to update unless something got dried
- update_icon()
+ if(drying && rack_dry())//no need to update unless something got dried
+ update_icon()
/obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O)
+ . = ..()
+ // If it's a food, reject non driable ones
if(istype(O, /obj/item/reagent_containers/food/snacks))
var/obj/item/reagent_containers/food/snacks/S = O
- if(S.dried_type)
- return TRUE
- if(istype(O, /obj/item/stack/sheet/wetleather))
- return TRUE
- return FALSE
+ if(!S.dried_type)
+ return FALSE
+/**
+ * Toggles the drying process.
+ *
+ * Arguments:
+ * * forceoff - Whether to force turn off the drying rack.
+ */
/obj/machinery/smartfridge/drying_rack/proc/toggle_drying(forceoff)
if(drying || forceoff)
drying = FALSE
@@ -557,6 +725,9 @@
use_power = ACTIVE_POWER_USE
update_icon()
+/**
+ * Called in [/obj/machinery/smartfridge/drying_rack/process] to dry the contents.
+ */
/obj/machinery/smartfridge/drying_rack/proc/rack_dry()
for(var/obj/item/reagent_containers/food/snacks/S in contents)
if(S.dried_type == S.type)//if the dried type is the same as the object's type, don't bother creating a whole new item...
@@ -580,30 +751,4 @@
return TRUE
return FALSE
-/obj/machinery/smartfridge/drying_rack/emp_act(severity)
- ..()
- atmos_spawn_air(LINDA_SPAWN_HEAT)
-
-/************************
-* Secure SmartFridges
-*************************/
-/obj/machinery/smartfridge/secure/emag_act(mob/user)
- emagged = 1
- locked = -1
- to_chat(user, "You short out the product lock on [src].")
-
-/obj/machinery/smartfridge/secure/emp_act(severity)
- if(prob(40/severity) && (!emagged) && (locked != -1))
- playsound(loc, 'sound/effects/sparks4.ogg', 60, 1)
- emagged = 1
- locked = -1
-
-/obj/machinery/smartfridge/secure/Topic(href, href_list)
- if(stat & (NOPOWER|BROKEN))
- return 0
- if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf)))
- if(!allowed(usr) && !emagged && locked != -1 && scan_id && href_list["vend"])
- to_chat(usr, "Access denied.")
- SSnanoui.update_uis(src)
- return 0
- return ..()
+#undef SMART_FRIDGE_LOCK_SHORTED
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index 612146e579a..5a73313601a 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -198,10 +198,17 @@
return
return ..()
+/obj/structure/beebox/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/beebox/wrench_act(mob/user, obj/item/I)
. = TRUE
- if(!I.tool_use_check(user, 0))
- return
default_unfasten_wrench(user, I, time = 20)
/obj/structure/beebox/attack_hand(mob/user)
@@ -261,15 +268,18 @@
visible_message("[user] removes the queen from the apiary.")
queen_bee = null
-/obj/structure/beebox/deconstruct(disassembled = TRUE)
- new /obj/item/stack/sheet/wood(loc, 20)
+/obj/structure/beebox/deconstruct(disassembled = FALSE)
+ var/mat_drop = 20
+ if(disassembled)
+ mat_drop = 40
+ new /obj/item/stack/sheet/wood(loc, mat_drop)
for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
if(B.loc == src)
B.forceMove(drop_location())
for(var/obj/item/honey_frame/HF in honey_frames)
HF.forceMove(drop_location())
honey_frames -= HF
- qdel(src)
+ ..()
/obj/structure/beebox/unwrenched
anchored = FALSE
diff --git a/code/modules/hydroponics/beekeeping/honeycomb.dm b/code/modules/hydroponics/beekeeping/honeycomb.dm
index 23bc55dfea2..1d086bae0f0 100644
--- a/code/modules/hydroponics/beekeeping/honeycomb.dm
+++ b/code/modules/hydroponics/beekeeping/honeycomb.dm
@@ -17,6 +17,8 @@
pixel_y = rand(8,-8)
update_icon()
+/obj/item/reagent_containers/honeycomb/set_APTFT()
+ set hidden = TRUE
/obj/item/reagent_containers/honeycomb/update_icon()
overlays.Cut()
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index f194c48e019..5031ac71373 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "barrel"
density = TRUE
- anchored = FALSE
+ anchored = TRUE
container_type = DRAINABLE | AMOUNT_VISIBLE
pressure_resistance = 2 * ONE_ATMOSPHERE
max_integrity = 300
@@ -65,6 +65,26 @@
to_chat(user, "You close [src], letting you draw from its tap.")
update_icon()
+/obj/structure/fermenting_barrel/crowbar_act(mob/living/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/fermenting_barrel/wrench_act(mob/living/user, obj/item/I)
+ . = TRUE
+ default_unfasten_wrench(user, I, time = 20)
+
+/obj/structure/fermenting_barrel/deconstruct(disassembled = FALSE)
+ var/mat_drop = 15
+ if(disassembled)
+ mat_drop = 30
+ new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
+ ..()
+
/obj/structure/fermenting_barrel/update_icon()
if(open)
icon_state = "barrel_open"
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index a3b522187e2..bad8813ea01 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -117,6 +117,7 @@
/obj/item/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom)
if(!..()) //was it caught by a mob?
if(seed)
+ log_action(thrownby, hit_atom, "Thrown [src] at")
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_throw_impact(src, hit_atom)
if(seed.get_gene(/datum/plant_gene/trait/squash))
@@ -147,11 +148,11 @@
qdel(src)
-/obj/item/reagent_containers/food/snacks/grown/On_Consume()
- if(iscarbon(usr))
+/obj/item/reagent_containers/food/snacks/grown/On_Consume(mob/M, mob/user)
+ if(iscarbon(M))
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_consume(src, usr)
+ T.on_consume(src, M)
..()
/obj/item/reagent_containers/food/snacks/grown/after_slip(mob/living/carbon/human/H)
@@ -168,6 +169,11 @@
return
return ..()
+/obj/item/reagent_containers/food/snacks/grown/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["wood"] += 4
+ qdel(src)
+ return TRUE
+
// For item-containing growns such as eggy or gatfruit
/obj/item/reagent_containers/food/snacks/grown/shell/attack_self(mob/user)
user.unEquip(src)
@@ -185,3 +191,16 @@
else
return ..()
+/obj/item/reagent_containers/food/snacks/grown/proc/log_action(mob/user, atom/target, what_done)
+ var/reagent_str = reagents.log_list()
+ var/genes_str = "No genes"
+ if(seed && length(seed.genes))
+ var/list/plant_gene_names = list()
+ for(var/thing in seed.genes)
+ var/datum/plant_gene/G = thing
+ if(G.dangerous)
+ plant_gene_names += G.name
+ genes_str = english_list(plant_gene_names)
+
+ add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])")
+
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 5bc63d4bace..1be8f11527d 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -978,7 +978,7 @@
/obj/machinery/hydroponics/proc/spawnplant() // why would you put strange reagent in a hydro tray you monster I bet you also feed them blood
var/list/livingplants = list(/mob/living/simple_animal/hostile/tree, /mob/living/simple_animal/hostile/killertomato)
var/chosen = pick(livingplants)
- var/mob/living/simple_animal/hostile/C = new chosen
+ var/mob/living/simple_animal/hostile/C = new chosen(get_turf(src))
C.faction = list("plants")
/obj/machinery/hydroponics/proc/become_self_sufficient() // Ambrosia Gaia effect
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index a4c6fe7280d..c2f4914351c 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -1,5 +1,7 @@
/datum/plant_gene
var/name
+ /// Used to determine if the trait should be logged when the holder is used
+ var/dangerous = FALSE
/datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name.
return name
@@ -194,6 +196,7 @@
name = "Liquid Contents"
examine_line = "It has a lot of liquid contents inside."
origin_tech = list("biotech" = 5)
+ dangerous = TRUE
/datum/plant_gene/trait/slip
// Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery.
@@ -201,6 +204,7 @@
name = "Slippery Skin"
rate = 0.1
examine_line = "It has a very slippery skin."
+ dangerous = TRUE
/datum/plant_gene/trait/slip/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc)
. = ..()
@@ -224,6 +228,7 @@
name = "Electrical Activity"
rate = 0.2
origin_tech = list("powerstorage" = 5)
+ dangerous = TRUE
/datum/plant_gene/trait/cell_charge/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C)
var/power = G.seed.potency*rate
@@ -298,6 +303,7 @@
name = "Bluespace Activity"
rate = 0.1
origin_tech = list("bluespace" = 5)
+ dangerous = TRUE
/datum/plant_gene/trait/teleport/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target)
if(isliving(target))
@@ -385,6 +391,7 @@
/datum/plant_gene/trait/stinging
name = "Hypodermic Prickles"
+ dangerous = TRUE
/datum/plant_gene/trait/stinging/on_throw_impact(obj/item/reagent_containers/food/snacks/grown/G, atom/target)
if(isliving(target) && G.reagents && G.reagents.total_volume)
@@ -398,6 +405,7 @@
/datum/plant_gene/trait/smoke
name = "gaseous decomposition"
+ dangerous = TRUE
/datum/plant_gene/trait/smoke/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target)
var/datum/effect_system/smoke_spread/chem/S = new
diff --git a/code/modules/library/admin.dm b/code/modules/library/admin.dm
index db907fc20ba..b56aa6994cf 100644
--- a/code/modules/library/admin.dm
+++ b/code/modules/library/admin.dm
@@ -3,8 +3,7 @@
set desc = "Permamently deletes a book from the database."
set category = "Admin"
- if(!holder)
- to_chat(src, "Only administrators may use this command.")
+ if(!check_rights(R_ADMIN))
return
var/isbn = input("ISBN number?", "Delete Book") as num | null
@@ -25,8 +24,7 @@
set desc = "View books flagged for content."
set category = "Admin"
- if(!holder)
- to_chat(src, "Only administrators may use this command.")
+ if(!check_rights(R_ADMIN))
return
holder.view_flagged_books()
diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm
index f9aae71b2a5..934234f645e 100644
--- a/code/modules/library/computers/checkout.dm
+++ b/code/modules/library/computers/checkout.dm
@@ -463,4 +463,5 @@
B.author = newbook.author
B.dat = newbook.content
B.icon_state = "book[rand(1,16)]"
+ B.has_drm = TRUE
visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?")
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index a7dd148b51a..e74625c8715 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -151,6 +151,8 @@
var/carved = 0 // Has the book been hollowed out for use as a secret storage item?
var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only)
var/obj/item/store // What's in the book?
+ /// Book DRM. If this var is TRUE, it cannot be scanned and re-uploaded
+ var/has_drm = FALSE
/obj/item/book/attack_self(var/mob/user as mob)
if(carved)
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 65fadaa9b45..0c23a0c5800 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -3,11 +3,6 @@
GLOBAL_DATUM_INIT(library_catalog, /datum/library_catalog, new())
GLOBAL_LIST_INIT(library_section_names, list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion"))
-
-/hook/startup/proc/load_manuals()
- GLOB.library_catalog.initialize()
- return 1
-
/*
* Borrowbook datum
*/
@@ -65,7 +60,7 @@ GLOBAL_LIST_INIT(library_section_names, list("Any", "Fiction", "Non-Fiction", "A
/datum/library_catalog
var/list/cached_books = list()
-/datum/library_catalog/proc/initialize()
+/datum/library_catalog/New()
var/newid=1
for(var/typepath in subtypesof(/obj/item/book/manual))
var/obj/item/book/B = new typepath(null)
@@ -155,6 +150,11 @@ GLOBAL_LIST_INIT(library_section_names, list("Any", "Fiction", "Non-Fiction", "A
power_change()
return
if(istype(I, /obj/item/book))
+ // NT with those pesky DRM schemes
+ var/obj/item/book/B = I
+ if(B.has_drm)
+ atom_say("Copyrighted material detected. Scanner is unable to copy book to memory.")
+ return FALSE
user.drop_item()
I.forceMove(src)
return 1
diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm
index 882da0a9387..8969463be1d 100644
--- a/code/modules/martial_arts/martial.dm
+++ b/code/modules/martial_arts/martial.dm
@@ -195,6 +195,9 @@
icon = 'icons/obj/library.dmi'
icon_state = "cqcmanual"
+/obj/item/CQC_manual/chef
+ desc = "A small, black manual. Written on the back it says: Bringing the home advantage with you."
+
/obj/item/CQC_manual/attack_self(mob/living/carbon/human/user)
if(!istype(user) || !user)
return
diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm
index 9b74c922a6b..044b78a04bc 100644
--- a/code/modules/mining/abandonedcrates.dm
+++ b/code/modules/mining/abandonedcrates.dm
@@ -82,7 +82,7 @@
new /obj/item/clothing/head/corgi(src)
if(67 to 68)
for(var/i in 1 to rand(4, 7))
- var /newitem = pick(subtypesof(/obj/item/stock_parts))
+ var/newitem = pick(subtypesof(/obj/item/stock_parts))
new newitem(src)
if(69 to 70)
new /obj/item/stack/ore/bluespace_crystal(src, 5)
diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm
index a0ea3eb7639..21bc247b0df 100644
--- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm
+++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm
@@ -120,18 +120,13 @@
return
var/mob/living/carbon/human/H = user
- var/random = rand(1,3)
+ var/random = rand(1,2)
switch(random)
if(1)
to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.")
H.set_species(/datum/species/skeleton)
if(2)
- to_chat(user, "Power courses through you! You can now shift your form at will.")
- if(user.mind)
- var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new
- user.mind.AddSpell(D)
- if(3)
to_chat(user, "You feel like you could walk straight through lava now.")
H.weather_immunities |= "lava"
diff --git a/code/modules/mining/lavaland/loot/bubblegum_loot.dm b/code/modules/mining/lavaland/loot/bubblegum_loot.dm
index fa7791edb9e..4ee2c76a419 100644
--- a/code/modules/mining/lavaland/loot/bubblegum_loot.dm
+++ b/code/modules/mining/lavaland/loot/bubblegum_loot.dm
@@ -82,7 +82,7 @@
B.mineEffect(L)
for(var/mob/living/carbon/human/H in GLOB.player_list)
- if(H == L)
+ if(H.stat == DEAD || H == L)
continue
to_chat(H, "You have an overwhelming desire to kill [L]. [L.p_they(TRUE)] [L.p_have()] been marked red! Go kill [L.p_them()]!")
H.put_in_hands(new /obj/item/kitchen/knife/butcher(H))
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index 0bde4d95464..e99e7132f03 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -349,7 +349,7 @@
harm_intent_damage = 1
friendly = "mends"
density = 0
- flying = 1
+ flying = TRUE
obj_damage = 0
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
ventcrawler = 2
diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm
index f54bc974f7e..0af08e40c74 100644
--- a/code/modules/mining/lavaland/loot/tendril_loot.dm
+++ b/code/modules/mining/lavaland/loot/tendril_loot.dm
@@ -383,7 +383,10 @@
var/mob/living/L = target
if(!L.anchored)
L.visible_message("[L] is snagged by [firer]'s hook!")
+ var/old_density = L.density
+ L.density = FALSE // Ensures the hook does not hit the target multiple times
L.forceMove(get_turf(firer))
+ L.density = old_density
/obj/item/projectile/hook/Destroy()
QDEL_NULL(chain)
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 8737fdf21b3..c01d04beba8 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -273,7 +273,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
icon_state = "Gibtonite active"
var/turf/bombturf = get_turf(src)
var/notify_admins = 0
- if(z != 5)//Only annoy the admins ingame if we're triggered off the mining zlevel
+ if(!is_mining_level(z))//Only annoy the admins ingame if we're triggered off the mining zlevel
notify_admins = 1
if(notify_admins)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 997920c8e23..31ff0098335 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -53,7 +53,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
if(ismob(body))
T = get_turf(body) //Where is the body located?
attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost
- logs = body.logs.Copy()
var/mutable_appearance/MA = copy_appearance(body)
if(body.mind && body.mind.name)
@@ -624,7 +623,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(href_list["jump"])
var/mob/target = locate(href_list["jump"])
- var/mob/A = usr;
+ var/mob/A = usr
to_chat(A, "Teleporting to [target]...")
//var/mob/living/silicon/ai/A = locate(href_list["track2"]) in GLOB.mob_list
if(target && target != usr)
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index 2f5627e8413..4d003753876 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -1,20 +1,10 @@
-/mob/dead/observer/say(var/message)
+/mob/dead/observer/say(message)
message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN))
if(!message)
return
- log_ghostsay(message, src)
-
- if(src.client)
- if(src.client.prefs.muted & MUTE_DEADCHAT)
- to_chat(src, "You cannot talk in deadchat (muted).")
- return
-
- if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
- return
-
- . = src.say_dead(message)
+ return say_dead(message)
/mob/dead/observer/emote(act, type, message, force)
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 5f18a27adc9..99e0af0ca6a 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -135,7 +135,7 @@
if(message)
for(var/mob/M in GLOB.player_list)
- if(istype(M, /mob/new_player))
+ if(isnewplayer(M))
continue
if(check_rights(R_ADMIN|R_MOD, 0, M) && M.get_preference(CHAT_DEAD)) // Show the emote to admins/mods
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 2b07e10291f..2514ce68b06 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -47,7 +47,7 @@
. = trim(. + trim(msg))
. += "\""
-/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
+/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(!client)
return 0
@@ -74,7 +74,7 @@
return 0
var/speaker_name = speaker.name
- if(ishuman(speaker))
+ if(use_voice && ishuman(speaker))
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
@@ -99,9 +99,9 @@
if(speaker == src)
to_chat(src, "You cannot hear yourself speak!")
else
- to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].")
+ to_chat(src, "[speaker.name] talks but you cannot hear [speaker.p_them()].")
else
- to_chat(src, "[speaker_name][speaker.GetAltName()] [track][message]")
+ to_chat(src, "[speaker_name][use_voice ? speaker.GetAltName() : ""] [track][message]")
if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
playsound_local(source, speech_sound, sound_vol, 1, sound_frequency)
@@ -171,7 +171,14 @@
to_chat(src, heard)
-/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
+/mob/proc/hear_holopad_talk(list/message_pieces, verb = "says", mob/speaker = null)
+ if(sleeping || stat == UNCONSCIOUS)
+ hear_sleep(multilingual_to_message(message_pieces))
+ return
+
+ if(!can_hear())
+ return
+
var/message = combine_message(message_pieces, verb, speaker)
var/name = speaker.name
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 92d936227cc..50d9b6d5fd5 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -186,7 +186,7 @@
if(prob(80))
new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]"
else
- new_name += ..(gender,1)
+ new_name += " [..(gender,1)]"
return new_name
/datum/language/vulpkanin
@@ -767,17 +767,6 @@
desc = "Bark bark bark."
key = "vu"
-/datum/language/zombie
- name = "Zombie"
- desc = "BRAAAAAAINS!"
- speech_verb = "moans"
- whisper_verb = "mutters"
- exclaim_verb = "wails"
- colour = "zombie"
- key = "zom"
- flags = RESTRICTED
- syllables = list("BRAAAAAAAAAAAAAAAAINS", "BRAAINS", "BRAINS")
-
/mob/proc/grant_all_babel_languages()
for(var/la in GLOB.all_languages)
var/datum/language/new_language = GLOB.all_languages[la]
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index aeee677b905..f281b8abec3 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -81,7 +81,7 @@
/mob/living/carbon/alien/larva/show_inv(mob/user as mob)
return
-/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return FALSE
/* Commented out because it's duplicated in life.dm
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 496a12cbf82..7acf07f4471 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -16,7 +16,7 @@
death()
return
- if(paralysis || sleeping || getOxyLoss() > 50 || (HEALTH_THRESHOLD_CRIT <= health && check_death_method()))
+ if(paralysis || sleeping || getOxyLoss() > 50 || (health <= HEALTH_THRESHOLD_CRIT && check_death_method()))
if(stat == CONSCIOUS)
KnockOut()
create_debug_log("fell unconscious, trigger reason: [reason]")
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 5d828e7c181..79ed90a61b8 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -70,7 +70,7 @@
return
polling = 1
spawn()
- var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0)
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, FALSE, source = /mob/living/carbon/alien/larva)
var/mob/C = null
// To stop clientless larva, we will check that our host has a client
diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm
index 16014e7c353..a17d7b8ebbd 100644
--- a/code/modules/mob/living/carbon/brain/MMI.dm
+++ b/code/modules/mob/living/carbon/brain/MMI.dm
@@ -226,13 +226,6 @@
brainmob.emp_damage += rand(0,10)
..()
-/obj/item/mmi/relaymove(var/mob/user, var/direction)
- if(user.stat || user.stunned)
- return
- var/obj/item/rig/rig = src.get_rig()
- if(rig)
- rig.forced_move(direction, user)
-
/obj/item/mmi/Destroy()
if(isrobot(loc))
var/mob/living/silicon/robot/borg = loc
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 98585262518..b6e93dc78ff 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -538,6 +538,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
take_organ_damage(10)
if(iscarbon(hit_atom) && hit_atom != src)
var/mob/living/carbon/victim = hit_atom
+ if(victim.flying)
+ return
if(hurt)
victim.take_organ_damage(10)
take_organ_damage(10)
diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm
index 043a704ab0c..1583bc62c70 100644
--- a/code/modules/mob/living/carbon/human/body_accessories.dm
+++ b/code/modules/mob/living/carbon/human/body_accessories.dm
@@ -1,17 +1,5 @@
GLOBAL_LIST_INIT(body_accessory_by_name, list("None" = null))
-
-/hook/startup/proc/initalize_body_accessories()
-
- __init_body_accessory(/datum/body_accessory/body)
- __init_body_accessory(/datum/body_accessory/tail)
-
- if(GLOB.body_accessory_by_name.len)
- if(initialize_body_accessory_by_species())
- return TRUE
-
- return FALSE //fail if no bodies are found
-
GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
/proc/initialize_body_accessory_by_species()
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index d213ee7206f..4a0b9ea55b1 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -109,9 +109,6 @@
// log_world("k")
sql_report_death(src)
- if(wearing_rig)
- wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.")
-
/mob/living/carbon/human/update_revive()
. = ..()
if(. && healthdoll)
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 10c2ac8f119..b69c13987b9 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -308,14 +308,14 @@
m_type = 1
if("bow", "bows")
- if(!buckled)
+ if(!restrained())
var/M = handle_emote_param(param)
message = "[src] bows[M ? " to [M]" : ""]."
m_type = 1
if("salute", "salutes")
- if(!buckled)
+ if(!restrained())
var/M = handle_emote_param(param)
message = "[src] salutes[M ? " to [M]" : ""]."
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index b2a66ed0b65..3739e337542 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -340,7 +340,7 @@
if(decaylevel == 3)
msg += "[p_they(TRUE)] [p_are()] rotting and blackened, the skin sloughing off. The smell is indescribably foul.\n"
if(decaylevel == 4)
- msg += "[p_they(TRUE)] [p_are()] mostly dessicated now, with only bones remaining of what used to be a person.\n"
+ msg += "[p_they(TRUE)] [p_are()] mostly desiccated now, with only bones remaining of what used to be a person.\n"
if(hasHUD(user,"security"))
var/perpname = get_visible_name(TRUE)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index fd33d963566..d495ca3d2f4 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -5,7 +5,6 @@
icon = 'icons/mob/human.dmi'
icon_state = "body_m_s"
deathgasp_on_death = TRUE
- var/obj/item/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call.
/mob/living/carbon/human/New(loc)
icon = null // This is now handled by overlays -- we just keep an icon for the sake of the map editor.
@@ -18,7 +17,7 @@
dna = new /datum/dna(null)
// Species name is handled by set_species()
- ..()
+ . = ..()
set_species(new_species, 1, delay_icon_update = 1, skip_same_check = TRUE)
@@ -68,55 +67,55 @@
status_flags = GODMODE|CANPUSH
/mob/living/carbon/human/skrell/Initialize(mapload)
- ..(mapload, /datum/species/skrell)
+ . = ..(mapload, /datum/species/skrell)
/mob/living/carbon/human/tajaran/Initialize(mapload)
- ..(mapload, /datum/species/tajaran)
+ . = ..(mapload, /datum/species/tajaran)
/mob/living/carbon/human/vulpkanin/Initialize(mapload)
- ..(mapload, /datum/species/vulpkanin)
+ . = ..(mapload, /datum/species/vulpkanin)
/mob/living/carbon/human/unathi/Initialize(mapload)
- ..(mapload, /datum/species/unathi)
+ . = ..(mapload, /datum/species/unathi)
/mob/living/carbon/human/vox/Initialize(mapload)
- ..(mapload, /datum/species/vox)
+ . = ..(mapload, /datum/species/vox)
/mob/living/carbon/human/voxarmalis/Initialize(mapload)
- ..(mapload, /datum/species/vox/armalis)
+ . = ..(mapload, /datum/species/vox/armalis)
/mob/living/carbon/human/skeleton/Initialize(mapload)
- ..(mapload, /datum/species/skeleton)
+ . = ..(mapload, /datum/species/skeleton)
/mob/living/carbon/human/kidan/Initialize(mapload)
- ..(mapload, /datum/species/kidan)
+ . = ..(mapload, /datum/species/kidan)
/mob/living/carbon/human/plasma/Initialize(mapload)
- ..(mapload, /datum/species/plasmaman)
+ . = ..(mapload, /datum/species/plasmaman)
/mob/living/carbon/human/slime/Initialize(mapload)
- ..(mapload, /datum/species/slime)
+ . = ..(mapload, /datum/species/slime)
/mob/living/carbon/human/grey/Initialize(mapload)
- ..(mapload, /datum/species/grey)
+ . = ..(mapload, /datum/species/grey)
/mob/living/carbon/human/abductor/Initialize(mapload)
- ..(mapload, /datum/species/abductor)
+ . = ..(mapload, /datum/species/abductor)
/mob/living/carbon/human/diona/Initialize(mapload)
- ..(mapload, /datum/species/diona)
+ . = ..(mapload, /datum/species/diona)
/mob/living/carbon/human/pod_diona/Initialize(mapload)
- ..(mapload, /datum/species/diona/pod)
+ . = ..(mapload, /datum/species/diona/pod)
/mob/living/carbon/human/machine/Initialize(mapload)
- ..(mapload, /datum/species/machine)
+ . = ..(mapload, /datum/species/machine)
/mob/living/carbon/human/machine/created
name = "Integrated Robotic Chassis"
/mob/living/carbon/human/machine/created/Initialize(mapload)
- ..()
+ . = ..()
rename_character(null, "Integrated Robotic Chassis ([rand(1, 9999)])")
update_dna()
for(var/obj/item/organ/external/E in bodyparts)
@@ -129,34 +128,34 @@
death()
/mob/living/carbon/human/shadow/Initialize(mapload)
- ..(mapload, /datum/species/shadow)
+ . = ..(mapload, /datum/species/shadow)
/mob/living/carbon/human/golem/Initialize(mapload)
- ..(mapload, /datum/species/golem)
+ . = ..(mapload, /datum/species/golem)
/mob/living/carbon/human/wryn/Initialize(mapload)
- ..(mapload, /datum/species/wryn)
+ . = ..(mapload, /datum/species/wryn)
/mob/living/carbon/human/nucleation/Initialize(mapload)
- ..(mapload, /datum/species/nucleation)
+ . = ..(mapload, /datum/species/nucleation)
/mob/living/carbon/human/drask/Initialize(mapload)
- ..(mapload, /datum/species/drask)
+ . = ..(mapload, /datum/species/drask)
/mob/living/carbon/human/monkey/Initialize(mapload)
- ..(mapload, /datum/species/monkey)
+ . = ..(mapload, /datum/species/monkey)
/mob/living/carbon/human/farwa/Initialize(mapload)
- ..(mapload, /datum/species/monkey/tajaran)
+ . = ..(mapload, /datum/species/monkey/tajaran)
/mob/living/carbon/human/wolpin/Initialize(mapload)
- ..(mapload, /datum/species/monkey/vulpkanin)
+ . = ..(mapload, /datum/species/monkey/vulpkanin)
/mob/living/carbon/human/neara/Initialize(mapload)
- ..(mapload, /datum/species/monkey/skrell)
+ . = ..(mapload, /datum/species/monkey/skrell)
/mob/living/carbon/human/stok/Initialize(mapload)
- ..(mapload, /datum/species/monkey/unathi)
+ . = ..(mapload, /datum/species/monkey/unathi)
/mob/living/carbon/human/Stat()
..()
@@ -182,13 +181,6 @@
stat("Tank Pressure", internal.air_contents.return_pressure())
stat("Distribution Pressure", internal.distribute_pressure)
- if(istype(back, /obj/item/rig))
- var/obj/item/rig/suit = back
- var/cell_status = "ERROR"
- if(suit.cell)
- cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]"
- stat(null, "Suit charge: [cell_status]")
-
// I REALLY need to split up status panel things into datums
var/mob/living/simple_animal/borer/B = has_brain_worms()
if(B && B.controlling)
@@ -963,16 +955,6 @@
var/obj/item/clothing/mask/MT = src.wear_mask
tinted += MT.tint
- //god help me
- if(istype(back, /obj/item/rig))
- var/obj/item/rig/O = back
- if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD))
- if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1))
- tinted = 2
- if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2))
- tinted = 3
- //im so sorry
-
return tinted
@@ -1465,12 +1447,13 @@
var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes)
var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes)
if(istype(dna.species) && dna.species.eyes)
- var/icon/eyes_icon = new /icon('icons/mob/human_face.dmi', dna.species.eyes)
+ var/icon/eyes_icon
if(eye_implant) //Eye implants override native DNA eye colo(u)r
eyes_icon = eye_implant.generate_icon()
else if(eyes)
eyes_icon = eyes.generate_icon()
else //Error 404: Eyes not found!
+ eyes_icon = new('icons/mob/human_face.dmi', dna.species.eyes)
eyes_icon.Blend("#800000", ICON_ADD)
return eyes_icon
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index f9058065fb0..621d8330761 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -129,13 +129,6 @@
O.heal_damage(0, -amount, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health)
return STATUS_UPDATE_HEALTH
-
-/mob/living/carbon/human/Paralyse(amount)
- // Notify our AI if they can now control the suit.
- if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second.
- wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.")
- return ..()
-
/mob/living/carbon/human/adjustCloneLoss(amount)
if(dna.species && amount > 0)
amount = amount * dna.species.clone_mod
@@ -342,7 +335,5 @@ This function restores all organs.
..(damage, damagetype, def_zone, blocked)
return 1
- //Handle BRUTE and BURN damage
- handle_suit_punctures(damagetype, damage)
//Handle species apply_damage procs
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 21a3e8bf274..6141ad02f9f 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -531,17 +531,6 @@ emp_act
w_uniform.add_mob_blood(source)
update_inv_w_uniform()
-/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage)
-
- if(!wear_suit) return
- if(!istype(wear_suit,/obj/item/clothing/suit/space)) return
- if(damtype != BURN && damtype != BRUTE) return
-
- var/obj/item/clothing/suit/space/SS = wear_suit
- var/penetrated_dam = max(0,(damage - max(0,(SS.breach_threshold - SS.damage))))
-
- if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam)
-
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 5e1b5163a24..7cb33bce249 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -16,12 +16,6 @@
else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit))
var/obj/item/clothing/suit/space/hardsuit/C = wear_suit
thrust = C.jetpack
- else if(istype(back,/obj/item/rig))
- var/obj/item/rig/rig = back
- for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules)
- thrust = module.jets
- break
-
if(thrust)
if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src))
return 1
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 90653a6750b..15dfe74880e 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -302,14 +302,6 @@
if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT)
null_internals = 1 //not wearing a mask or suitable helmet
- if(istype(back, /obj/item/rig)) //wearing a rigsuit
- var/obj/item/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason
- if(rig.offline && (rig.air_supply && internal == rig.air_supply)) //if rig IS offline AND (rig HAS air_supply AND internal IS air_supply)
- null_internals = 1 //offline suits do not breath
-
- else if(rig.air_supply && internal == rig.air_supply) //if rig HAS air_supply AND internal IS rig air_supply
- skip_contents_check = 1 //skip contents.Find() check, the oxygen is valid even being outside of the mob
-
if(!contents.Find(internal) && (!skip_contents_check)) //if internal NOT IN contents AND skip_contents_check IS false
null_internals = 1 //not a rigsuit and your oxygen is gone
diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm
index 41c108dadb9..9dcf83f2e9a 100644
--- a/code/modules/mob/living/carbon/human/npcs.dm
+++ b/code/modules/mob/living/carbon/human/npcs.dm
@@ -7,7 +7,7 @@
species_exception = list(/datum/species/monkey)
/mob/living/carbon/human/monkey/punpun/Initialize(mapload)
- ..()
+ . = ..()
name = "Pun Pun"
real_name = name
equip_to_slot(new /obj/item/clothing/under/punpun(src), slot_w_uniform)
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 65b4585ff6b..957b0828725 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -62,11 +62,6 @@
return ..()
/mob/living/carbon/human/proc/HasVoiceChanger()
- if(istype(back, /obj/item/rig))
- var/obj/item/rig/rig = back
- if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice)
- return rig.speech.voice_holder.voice
-
for(var/obj/item/gear in list(wear_mask, wear_suit, head))
if(!gear)
continue
@@ -134,8 +129,6 @@
S.message = "[S.message]"
verb = translator.speech_verb
return list("verb" = verb)
- if(mind)
- span = mind.speech_span
if((COMIC in mutations) \
|| (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \
|| HAS_TRAIT(src, TRAIT_JESTER))
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 2114299f939..a8e1eee5f52 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -53,7 +53,6 @@
var/stun_mod = 1 // If a species is more/less impacated by stuns/weakens/paralysis
var/speed_mod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
var/blood_damage_type = OXY //What type of damage does this species take if it's low on blood?
- var/obj/item/mutanthands
var/total_health = 100
var/punchdamagelow = 0 //lowest possible punch damage
var/punchdamagehigh = 9 //highest possible punch damage
@@ -346,15 +345,17 @@
switch(damagetype)
if(BRUTE)
- H.damageoverlaytemp = 20
damage = damage * brute_mod
+ if(damage)
+ H.damageoverlaytemp = 20
if(organ.receive_damage(damage, 0, sharp, used_weapon))
H.UpdateDamageIcon()
if(BURN)
- H.damageoverlaytemp = 20
damage = damage * burn_mod
+ if(damage)
+ H.damageoverlaytemp = 20
if(organ.receive_damage(0, damage, sharp, used_weapon))
H.UpdateDamageIcon()
@@ -518,9 +519,6 @@
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) //Handles any species-specific attackhand events.
if(!istype(M))
return
- if(H.frozen)
- to_chat(M, "Do not touch Admin-Frozen people.")
- return
if(istype(M))
var/obj/item/organ/external/temp = M.bodyparts_by_name["r_hand"]
@@ -816,20 +814,6 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(!isnull(hat.lighting_alpha))
H.lighting_alpha = min(hat.lighting_alpha, H.lighting_alpha)
- if(istype(H.back, /obj/item/rig)) ///aghhh so snowflakey
- var/obj/item/rig/rig = H.back
- if(rig.visor)
- if(!rig.helmet || (H.head && rig.helmet == H.head))
- if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses)
- var/obj/item/clothing/glasses/G = rig.visor.vision.glasses
- if(istype(G))
- H.sight |= G.vision_flags
- H.see_in_dark = max(G.see_in_dark, H.see_in_dark)
- H.see_invisible = min(G.invis_view, H.see_invisible)
-
- if(!isnull(G.lighting_alpha))
- H.lighting_alpha = min(G.lighting_alpha, H.lighting_alpha)
-
if(H.vision_type)
H.sight |= H.vision_type.sight_flags
H.see_in_dark = max(H.see_in_dark, H.vision_type.see_in_dark)
diff --git a/code/modules/mob/living/carbon/human/species/zombies.dm b/code/modules/mob/living/carbon/human/species/zombies.dm
deleted file mode 100644
index 1f1c4d979b7..00000000000
--- a/code/modules/mob/living/carbon/human/species/zombies.dm
+++ /dev/null
@@ -1,102 +0,0 @@
-#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin
-
-/datum/species/zombie
- // 1spooky
- name = "High-Functioning Zombie"
- name_plural = "High-Functioning Zombies"
- icobase = 'icons/mob/human_races/r_zombie.dmi'
- deform = 'icons/mob/human_races/r_def_zombie.dmi'
- dies_at_threshold = TRUE
- language = "Zombie"
- species_traits = list(NO_BLOOD, NOZOMBIE, NOTRANSSTING, NO_BREATHE, RADIMMUNE, NO_SCAN)
- var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
- warning_low_pressure = -1
- hazard_low_pressure = -1
- hazard_high_pressure = 999999999
- warning_high_pressure = 999999999
- cold_level_1 = -1
- cold_level_2 = -1
- cold_level_3 = -1
- tox_mod = 0
- flesh_color = "#00FF00" // for green examine text
- bodyflags = HAS_SKIN_COLOR
- dietflags = DIET_CARN
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes,
- "ears" = /obj/item/organ/internal/ears)
-
-
-/datum/species/zombie/infectious
- name = "Infectious Zombie"
- mutanthands = /obj/item/zombie_hand
- icobase = 'icons/mob/human_races/r_zombie.dmi'
- deform = 'icons/mob/human_races/r_def_zombie.dmi'
- brute_mod = 0.8 // 120 damage to KO a zombie, which kills it
- burn_mod = 0.8
- clone_mod = 0.8
- brain_mod = 0.8
- stamina_mod = 0.8
- speed_mod = 1.6
- default_language = "Zombie"
- var/heal_rate = 1
- var/regen_cooldown = 0
-
-/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H, amount)
- . = min(20, amount)
-
-/datum/species/zombie/infectious/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null)
- . = ..()
- if(damage)
- regen_cooldown = world.time + REGENERATION_DELAY
-
-/datum/species/zombie/infectious/handle_life(mob/living/carbon/human/H)
- . = ..()
- H.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW
-
- //Zombies never actually die, they just fall down until they regenerate enough to rise back up.
- //They must be restrained, beheaded or gibbed to stop being a threat.
- if(regen_cooldown < world.time)
- var/heal_amt = heal_rate
- if(H.InCritical())
- heal_amt *= 2
- H.heal_overall_damage(heal_amt,heal_amt)
- H.adjustToxLoss(-heal_amt)
- if(!H.InCritical() && prob(4))
- playsound(H, pick(spooks), 50, TRUE, 10)
-
-//Congrats you somehow died so hard you stopped being a zombie
-/datum/species/zombie/infectious/handle_death(gibbed, mob/living/carbon/C)
- . = ..()
- var/obj/item/organ/internal/zombie_infection/infection
- infection = C.get_organ_slot("zombie_infection")
- if(infection)
- qdel(infection)
-
-/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
- . = ..()
- // Deal with the source of this zombie corruption
- // Infection organ needs to be handled separately from mutant_organs
- // because it persists through species transitions
- if(mutanthands)
- H.drop_l_hand()
- H.drop_r_hand()
- H.put_in_hands(new mutanthands())
- H.put_in_hands(new mutanthands())
- var/obj/item/organ/internal/zombie_infection/infection
- infection = H.get_organ_slot("zombie_infection")
- if(!infection)
- infection = new()
- infection.insert(H)
-
-/datum/species/zombie/infectious/on_species_loss(mob/living/carbon/human/C, datum/species/old_species)
- QDEL_NULL(C.r_hand)
- QDEL_NULL(C.l_hand)
- return ..()
-
-#undef REGENERATION_DELAY
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 81907ba516f..e667833dfe3 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -273,11 +273,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER)
apply_overlay(UNDERWEAR_LAYER)
- if(lip_style && (LIPS in dna.species.species_traits))
- var/icon/lips = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "lips_[lip_style]_s")
- lips.Blend(lip_color, ICON_ADD)
- standing += mutable_appearance(lips, layer = -BODY_LAYER)
-
overlays_standing[BODY_LAYER] = standing
apply_overlay(BODY_LAYER)
//tail
@@ -964,10 +959,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/mutable_appearance/standing
if(back.icon_override)
standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER)
- else if(istype(back, /obj/item/rig))
- //If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc.
- var/obj/item/rig/rig = back
- standing = rig.mob_icon
else if(back.sprite_sheets && back.sprite_sheets[dna.species.name])
standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[back.icon_state]", layer = -BACK_LAYER)
else
@@ -1307,6 +1298,11 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
else
. += "#000000"
+ if(lip_color && (LIPS in dna.species.species_traits))
+ . += "[lip_color]"
+ else
+ . += "#000000"
+
for(var/organ_tag in dna.species.has_limbs)
var/obj/item/organ/external/part = bodyparts_by_name[organ_tag]
if(isnull(part))
diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm
index cb607bae294..c3d0ca679d4 100644
--- a/code/modules/mob/living/carbon/update_icons.dm
+++ b/code/modules/mob/living/carbon/update_icons.dm
@@ -29,7 +29,7 @@
if(changed)
animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT)
handle_transform_change()
- floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
+ floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
/mob/living/carbon/proc/handle_transform_change()
return
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 3062ef461f9..c0341f11b47 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -2,9 +2,8 @@
set waitfor = FALSE
set invisibility = 0
- if(flying) //TODO: Better floating
- animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING)
- animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING)
+ if(flying && !floating) //TODO: Better floating
+ float(TRUE)
if(client || registered_z) // This is a temporary error tracker to make sure we've caught everything
var/turf/T = get_turf(src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index f3d23ca3f51..9e9724d20ec 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -3,8 +3,29 @@
var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medhud.add_to_hud(src)
faction += "\ref[src]"
+ determine_move_and_pull_forces()
GLOB.mob_living_list += src
+// Used to determine the forces dependend on the mob size
+// Will only change the force if the force was not set in the mob type itself
+/mob/living/proc/determine_move_and_pull_forces()
+ var/value
+ switch(mob_size)
+ if(MOB_SIZE_TINY)
+ value = MOVE_FORCE_EXTREMELY_WEAK
+ if(MOB_SIZE_SMALL)
+ value = MOVE_FORCE_WEAK
+ if(MOB_SIZE_HUMAN)
+ value = MOVE_FORCE_NORMAL
+ if(MOB_SIZE_LARGE)
+ value = MOVE_FORCE_NORMAL // For now
+ if(!move_force)
+ move_force = value
+ if(!pull_force)
+ pull_force = value
+ if(!move_resist)
+ move_resist = value
+
/mob/living/prepare_huds()
..()
prepare_data_huds()
@@ -59,8 +80,9 @@
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
- if(now_pushing)
- return 1
+ // No pushing if we're already pushing past something, or if the mob we're pushing into is anchored.
+ if(now_pushing || M.anchored)
+ return TRUE
//Should stop you pushing a restrained person out of the way
if(isliving(M))
@@ -68,7 +90,7 @@
if(L.pulledby && L.pulledby != src && L.restrained())
if(!(world.time % 5))
to_chat(src, "[L] is restrained, you cannot push past.")
- return 1
+ return TRUE
if(L.pulling)
if(ismob(L.pulling))
@@ -76,28 +98,28 @@
if(P.restrained())
if(!(world.time % 5))
to_chat(src, "[L] is restrained, you cannot push past.")
- return 1
+ return TRUE
if(moving_diagonally) //no mob swap during diagonal moves.
- return 1
+ return TRUE
if(a_intent == INTENT_HELP) // Help intent doesn't mob swap a mob pulling a structure
if(isstructure(M.pulling) || isstructure(pulling))
- return 1
+ return TRUE
if(!M.buckled && !M.has_buckled_mobs())
var/mob_swap
//the puller can always swap with it's victim if on grab intent
if(M.pulledby == src && a_intent == INTENT_GRAB)
- mob_swap = 1
+ mob_swap = TRUE
//restrained people act if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP))
- mob_swap = 1
+ mob_swap = TRUE
if(mob_swap)
//switch our position with M
if(loc && !loc.Adjacent(M.loc))
- return 1
- now_pushing = 1
+ return TRUE
+ now_pushing = TRUE
var/oldloc = loc
var/oldMloc = M.loc
@@ -114,18 +136,18 @@
if(!M_passmob)
M.pass_flags &= ~PASSMOB
- now_pushing = 0
- return 1
+ now_pushing = FALSE
+ return TRUE
// okay, so we didn't switch. but should we push?
// not if he's not CANPUSH of course
if(!(M.status_flags & CANPUSH))
- return 1
+ return TRUE
//anti-riot equipment is also anti-push
if(M.r_hand && (prob(M.r_hand.block_chance * 2)) && !istype(M.r_hand, /obj/item/clothing))
- return 1
+ return TRUE
if(M.l_hand && (prob(M.l_hand.block_chance * 2)) && !istype(M.l_hand, /obj/item/clothing))
- return 1
+ return TRUE
//Called when we bump into an obj
/mob/living/proc/ObjBump(obj/O)
@@ -175,13 +197,6 @@
AM.setDir(current_dir)
now_pushing = FALSE
-/mob/living/Stat()
- . = ..()
- if(. && get_rig_stats)
- var/obj/item/rig/rig = get_rig()
- if(rig)
- SetupStat(rig)
-
/mob/living/proc/can_track(mob/living/user)
//basic fast checks go first. When overriding this proc, I recommend calling ..() at the end.
var/turf/T = get_turf(src)
@@ -209,7 +224,7 @@
set category = "Object"
if(istype(AM) && Adjacent(AM))
- start_pulling(AM)
+ start_pulling(AM, show_message = TRUE)
else
stop_pulling()
@@ -743,16 +758,17 @@
/mob/living/proc/float(on)
if(throwing)
return
- var/fixed = 0
+ var/fixed = FALSE
if(anchored || (buckled && buckled.anchored))
- fixed = 1
+ fixed = TRUE
if(on && !floating && !fixed)
animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
- floating = 1
+ sleep(10)
+ animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
+ floating = TRUE
else if(((!on || fixed) && floating))
- var/final_pixel_y = get_standard_pixel_y_offset(lying)
- animate(src, pixel_y = final_pixel_y, time = 10)
- floating = 0
+ animate(src, pixel_y = get_standard_pixel_y_offset(lying), time = 10)
+ floating = FALSE
/mob/living/proc/can_use_vents()
return "You can't fit into that vent."
@@ -827,15 +843,17 @@
if(!used_item)
used_item = get_active_hand()
..()
- floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
+ floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
/mob/living/proc/do_jitter_animation(jitteriness, loop_amount = 6)
- var/amplitude = min(4, (jitteriness/100) + 1)
+ var/amplitude = min(4, (jitteriness / 100) + 1)
var/pixel_x_diff = rand(-amplitude, amplitude)
- var/pixel_y_diff = rand(-amplitude/3, amplitude/3)
+ var/pixel_y_diff = rand(-amplitude / 3, amplitude / 3)
+ var/final_pixel_x = get_standard_pixel_x_offset(lying)
+ var/final_pixel_y = get_standard_pixel_y_offset(lying)
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 2, loop = loop_amount)
- animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y) , time = 2)
- floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
+ animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 2)
+ floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
/mob/living/proc/get_temperature(datum/gas_mixture/environment)
@@ -927,10 +945,10 @@
return 0
return 1
-/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
+/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(!AM || !src)
return FALSE
- if(!(AM.can_be_pulled(src, state, force)))
+ if(!(AM.can_be_pulled(src, state, force, show_message)))
return FALSE
if(incapacitated())
return
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index b38f1819524..4da8aa97e06 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -113,7 +113,7 @@
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration)
apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I)
if(I.thrownby)
- add_attack_logs(I.thrownby, src, "Hit with thrown [I]")
+ add_attack_logs(I.thrownby, src, "Hit with thrown [I]", !I.throwforce ? ATKLOG_ALMOSTALL : null) // Only message if the person gets damages
else
return 1
else
@@ -219,7 +219,7 @@
fire_stacks += L.fire_stacks
IgniteMob()
-/mob/living/can_be_pulled(user, grab_state, force)
+/mob/living/can_be_pulled(user, grab_state, force, show_message = FALSE)
return ..() && !(buckled && buckled.buckle_prevents_pull)
/mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index e230f7a2b99..f8a5f08b313 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -2,6 +2,11 @@
see_invisible = SEE_INVISIBLE_LIVING
pressure_resistance = 10
+ // Will be determined based on mob size if left null. Done in living/proc/determine_move_and_pull_forces()
+ move_resist = null
+ move_force = null
+ pull_force = null
+
//Health and life related vars
var/maxHealth = 100 //Maximum health that should be possible.
var/health = 100 //A mob's health
@@ -28,7 +33,7 @@
var/on_fire = 0 //The "Are we on fire?" var
var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20
- var/floating = 0
+ var/floating = FALSE
var/mob_size = MOB_SIZE_HUMAN
var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature..
var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes.
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 630b2223321..e944e692887 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -351,8 +351,6 @@ proc/get_radio_key_from_channel(var/channel)
return
if(stat)
- if(stat == DEAD)
- return say_dead(message_pieces)
return
if(is_muzzled())
@@ -451,14 +449,14 @@ proc/get_radio_key_from_channel(var/channel)
var/speech_bubble_test = say_test(message)
for(var/mob/M in listening)
- M.hear_say(message_pieces, verb, italics, src)
+ M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE)
if(M.client)
speech_bubble_recipients.Add(M.client)
if(eavesdropping.len)
stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less.
for(var/mob/M in eavesdropping)
- M.hear_say(message_pieces, verb, italics, src)
+ M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE)
if(M.client)
speech_bubble_recipients.Add(M.client)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 85c4ceac427..670088fd0bc 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -63,6 +63,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
//MALFUNCTION
var/datum/module_picker/malf_picker
+ var/datum/action/innate/ai/choose_modules/modules_action
var/list/datum/AI_Module/current_modules = list()
var/can_dominate_mechs = 0
var/shunted = 0 //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead
@@ -172,19 +173,19 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
add_language("Galactic Common", 1)
add_language("Sol Common", 1)
add_language("Tradeband", 1)
- add_language("Neo-Russkiya", 0)
- add_language("Gutter", 0)
- add_language("Sinta'unathi", 0)
- add_language("Siik'tajr", 0)
- add_language("Canilunzt", 0)
- add_language("Skrellian", 0)
- add_language("Vox-pidgin", 0)
- add_language("Orluum", 0)
- add_language("Rootspeak", 0)
+ add_language("Neo-Russkiya", 1)
+ add_language("Gutter", 1)
+ add_language("Sinta'unathi", 1)
+ add_language("Siik'tajr", 1)
+ add_language("Canilunzt", 1)
+ add_language("Skrellian", 1)
+ add_language("Vox-pidgin", 1)
+ add_language("Orluum", 1)
+ add_language("Rootspeak", 1)
add_language("Trinary", 1)
- add_language("Chittin", 0)
- add_language("Bubblish", 0)
- add_language("Clownish", 0)
+ add_language("Chittin", 1)
+ add_language("Bubblish", 1)
+ add_language("Clownish", 1)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if(!B)//If there is no player/brain inside.
@@ -243,7 +244,8 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/proc/show_borg_info()
stat(null, text("Connected cyborgs: [connected_robots.len]"))
- for(var/mob/living/silicon/robot/R in connected_robots)
+ for(var/thing in connected_robots)
+ var/mob/living/silicon/robot/R = thing
var/robot_status = "Nominal"
if(R.stat || !R.client)
robot_status = "OFFLINE"
@@ -251,8 +253,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
robot_status = "DEPOWERED"
// Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
var/area/A = get_area(R)
+ var/area_name = A ? sanitize(A.name) : "Unknown"
stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \
- Module: [R.designation] | Loc: [sanitize(A.name)] | Status: [robot_status]"))
+ Module: [R.designation] | Loc: [area_name] | Status: [robot_status]"))
/mob/living/silicon/ai/rename_character(oldname, newname)
if(!..(oldname, newname))
@@ -849,13 +852,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "Switched to [network] camera network.")
//End of code by Mord_Sith
-
-/mob/living/silicon/ai/proc/choose_modules()
- set category = "Malfunction"
- set name = "Choose Module"
-
- malf_picker.use(src)
-
/mob/living/silicon/ai/proc/ai_statuschange()
set category = "AI Commands"
set name = "AI Status"
@@ -1026,15 +1022,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
return
-/mob/living/silicon/ai/proc/corereturn()
- set category = "Malfunction"
- set name = "Return to Main Core"
-
- var/obj/machinery/power/apc/apc = loc
- if(!istype(apc))
- to_chat(src, "You are already in your Main Core.")
- return
- apc.malfvacate()
//Toggles the luminosity and applies it by re-entereing the camera.
/mob/living/silicon/ai/proc/toggle_camera_light()
@@ -1179,16 +1166,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/can_buckle()
return FALSE
-// Pass lying down or getting up to our pet human, if we're in a rig.
-/mob/living/silicon/ai/lay_down()
- set name = "Rest"
- set category = "IC"
-
- resting = 0
- var/obj/item/rig/rig = get_rig()
- if(rig)
- rig.force_rest(src)
-
/mob/living/silicon/ai/switch_to_camera(obj/machinery/camera/C)
if(!C.can_use() || !is_in_chassis())
return FALSE
@@ -1242,8 +1219,17 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.")
to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.")
view_core() //A BYOND bug requires you to be viewing your core before your verbs update
- verbs += /mob/living/silicon/ai/proc/choose_modules
malf_picker = new /datum/module_picker
+ modules_action = new(malf_picker)
+ modules_action.Grant(src)
+
+///Removes all malfunction-related /datum/action's from the target AI.
+/mob/living/silicon/ai/proc/remove_malf_abilities()
+ QDEL_NULL(modules_action)
+ for(var/datum/AI_Module/AM in current_modules)
+ for(var/datum/action/A in actions)
+ if(istype(A, initial(AM.power_type)))
+ qdel(A)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target)
if(!istype(target))
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index d23fedb09bc..faacf9ba359 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -142,7 +142,7 @@
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
-/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
+/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(relay_speech)
if(istype(ai))
ai.relay_speech(speaker, message_pieces, verb)
diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm
index 75e8854b45c..e1be5450811 100644
--- a/code/modules/mob/living/silicon/ai/latejoin.dm
+++ b/code/modules/mob/living/silicon/ai/latejoin.dm
@@ -1,15 +1,5 @@
GLOBAL_LIST_EMPTY(empty_playable_ai_cores)
-/hook/roundstart/proc/spawn_empty_ai()
- 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))
-
- return 1
-
/mob/living/silicon/ai/verb/wipe_core()
set name = "Wipe Core"
set category = "OOC"
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index 599063c2724..a4e90b50f8e 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -80,17 +80,26 @@ GLOBAL_VAR_INIT(announcing_vox, 0) // Stores the time of the last announcement
Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.\
WARNING: Misuse of the announcement system will get you job banned. "
- var/index = 0
- for(var/word in GLOB.vox_sounds)
- index++
- dat += "[capitalize(word)]"
- if(index != GLOB.vox_sounds.len)
- dat += " / "
+ // Show alert and voice sounds separately
+ var/vox_words = GLOB.vox_sounds - GLOB.vox_alerts
+ dat = help_format(GLOB.vox_alerts, dat)
+ dat = help_format(vox_words, dat)
var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400)
popup.set_content(dat)
popup.open()
+/mob/living/silicon/ai/proc/help_format(word_list, dat)
+ var/index = 0
+ for(var/word in word_list)
+ index++
+ dat += "[capitalize(word)]"
+ if(index != length(word_list))
+ dat += " / "
+ else
+ dat += " "
+ return dat
+
/mob/living/silicon/ai/proc/ai_announcement()
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 3d1293d2a34..0a4e4cee898 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -435,9 +435,6 @@
// Pass lying down or getting up to our pet human, if we're in a rig.
if(stat == CONSCIOUS && istype(loc,/obj/item/paicard))
resting = 0
- var/obj/item/rig/rig = get_rig()
- if(istype(rig))
- rig.force_rest(src)
else
resting = !resting
to_chat(src, "You are now [resting ? "resting" : "getting up"]")
@@ -520,7 +517,7 @@
/mob/living/silicon/pai/Bumped()
return
-/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return FALSE
/mob/living/silicon/pai/update_canmove(delay_action_updates = 0)
@@ -566,6 +563,10 @@
var/obj/item/holder/H = ..()
if(!istype(H))
return
+ if(stat == DEAD)
+ H.icon = 'icons/mob/pai.dmi'
+ H.icon_state = "[chassis]_dead"
+ return
if(resting)
icon_state = "[chassis]"
resting = 0
diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm
index f52da9942e4..920dff8a8c2 100644
--- a/code/modules/mob/living/silicon/pai/recruit.dm
+++ b/code/modules/mob/living/silicon/pai/recruit.dm
@@ -1,6 +1,6 @@
// Recruiting observers to play as pAIs
-GLOBAL_DATUM(paiController, /datum/paiController) // Global handler for pAI candidates
+GLOBAL_DATUM_INIT(paiController, /datum/paiController, new) // Global handler for pAI candidates
/datum/paiCandidate
var/name
@@ -10,12 +10,6 @@ GLOBAL_DATUM(paiController, /datum/paiController) // Global handler for pAI cand
var/comments
var/ready = 0
-
-/hook/startup/proc/paiControllerSetup()
- GLOB.paiController = new /datum/paiController()
- return 1
-
-
/datum/paiController
var/list/pai_candidates = list()
var/list/asked = list()
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index 3b51048452e..574dcda3230 100644
--- a/code/modules/mob/living/silicon/pai/software.dm
+++ b/code/modules/mob/living/silicon/pai/software.dm
@@ -13,19 +13,6 @@ GLOBAL_LIST_INIT(pai_emotions, list(
GLOBAL_LIST_EMPTY(pai_software_by_key)
GLOBAL_LIST_EMPTY(default_pai_software)
-/hook/startup/proc/populate_pai_software_list()
- var/r = 1 // I would use ., but it'd sacrifice runtime detection
- for(var/type in subtypesof(/datum/pai_software))
- var/datum/pai_software/P = new type()
- if(GLOB.pai_software_by_key[P.id])
- var/datum/pai_software/O = GLOB.pai_software_by_key[P.id]
- to_chat(world, "pAI software module [P.name] has the same key as [O.name]!")
- r = 0
- continue
- GLOB.pai_software_by_key[P.id] = P
- if(P.default)
- GLOB.default_pai_software[P.id] = P
- return r
/mob/living/silicon/pai/New()
..()
diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm
index a2fde7a43a7..52d58ad659d 100644
--- a/code/modules/mob/living/silicon/robot/component.dm
+++ b/code/modules/mob/living/silicon/robot/component.dm
@@ -227,7 +227,7 @@
throw_speed = 5
throw_range = 10
origin_tech = "magnets=1;biotech=1"
- var/mode = 1;
+ var/mode = 1
/obj/item/robotanalyzer/attack(mob/living/M as mob, mob/living/user as mob)
if(( (CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50))
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index f932f4270ba..5cc6e50fd06 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -14,10 +14,12 @@
braintype = "Robot"
lawupdate = 0
density = 0
+ has_camera = FALSE
req_one_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS)
ventcrawler = 2
magpulse = 1
mob_size = MOB_SIZE_SMALL
+ pull_force = MOVE_FORCE_VERY_WEAK // Can only drag small items
modules_break = FALSE
@@ -34,6 +36,14 @@
var/reboot_cooldown = 60 // one minute
var/last_reboot
var/emagged_time
+ var/list/pullable_drone_items = list(
+ /obj/item/pipe,
+ /obj/structure/disposalconstruct,
+ /obj/item/stack/cable_coil,
+ /obj/item/stack/rods,
+ /obj/item/stack/sheet,
+ /obj/item/stack/tile
+ )
holder_type = /obj/item/holder/drone
// var/sprite[0]
@@ -68,6 +78,10 @@
verbs -= /mob/living/silicon/robot/verb/Namepick
module = new /obj/item/robot_module/drone(src)
+ //Allows Drones to hear the Engineering channel.
+ module.channels = list("Engineering" = 1)
+ radio.recalculateChannels()
+
//Grab stacks.
stack_metal = locate(/obj/item/stack/sheet/metal/cyborg) in src.module
stack_wood = locate(/obj/item/stack/sheet/wood) in src.module
@@ -82,7 +96,7 @@
scanner.Grant(src)
update_icons()
-/mob/living/silicon/robot/drone/init()
+/mob/living/silicon/robot/drone/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/drone()
connected_ai = null
@@ -153,15 +167,17 @@
return
else
- user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.")
+ var/confirm = alert("Using your ID on a Maintenance Drone will shut it down, are you sure you want to do this?", "Disable Drone", "Yes", "No")
+ if(confirm == ("Yes") && (user in range(3, src)))
+ user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.")
- if(emagged)
- return
+ if(emagged)
+ return
- if(allowed(W))
- shut_down()
- else
- to_chat(user, "Access denied.")
+ if(allowed(W))
+ shut_down()
+ else
+ to_chat(user, "Access denied.")
return
@@ -320,20 +336,22 @@
/mob/living/silicon/robot/drone/Bumped(atom/movable/AM)
return
-/mob/living/silicon/robot/drone/start_pulling(var/atom/movable/AM)
+/mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
+
+ if(is_type_in_list(AM, pullable_drone_items))
+ ..(AM, force = INFINITY) // Drone power! Makes them able to drag pipes and such
- if(istype(AM,/obj/item/pipe) || istype(AM,/obj/structure/disposalconstruct))
- ..()
else if(istype(AM,/obj/item))
var/obj/item/O = AM
if(O.w_class > WEIGHT_CLASS_SMALL)
- to_chat(src, "You are too small to pull that.")
+ if(show_message)
+ to_chat(src, "You are too small to pull that.")
return
else
..()
else
- to_chat(src, "You are too small to pull that.")
- return
+ if(show_message)
+ to_chat(src, "You are too small to pull that.")
/mob/living/silicon/robot/drone/add_robot_verbs()
src.verbs |= silicon_subsystems
@@ -353,3 +371,20 @@
/mob/living/simple_animal/drone/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
if(affect_silicon)
return ..()
+
+/mob/living/silicon/robot/drone/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(!client && istype(user, /mob/living/silicon/robot/drone))
+ to_chat(user, "You begin decompiling the other drone.")
+ if(!do_after(user, 5 SECONDS, target = loc))
+ to_chat(user, "You need to remain still while decompiling such a large object.")
+ return
+ if(QDELETED(src) || QDELETED(user))
+ return ..()
+ to_chat(user, "You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.")
+ new/obj/effect/decal/cleanable/blood/oil(get_turf(src))
+ C.stored_comms["metal"] += 15
+ C.stored_comms["glass"] += 15
+ C.stored_comms["wood"] += 5
+ qdel(src)
+ return TRUE
+ return ..()
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index 8f2b9fee6ae..16e81f53276 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -8,7 +8,6 @@
//Has a list of items that it can hold.
var/list/can_hold = list(
- /obj/item/stock_parts/cell,
/obj/item/firealarm_electronics,
/obj/item/airalarm_electronics,
/obj/item/airlock_electronics,
@@ -24,6 +23,8 @@
/obj/item/mounted/frame/firealarm,
/obj/item/mounted/frame/newscaster_frame,
/obj/item/mounted/frame/intercom,
+ /obj/item/mounted/frame/extinguisher,
+ /obj/item/mounted/frame/light_switch,
/obj/item/rack_parts,
/obj/item/camera_assembly,
/obj/item/tank,
@@ -151,15 +152,13 @@
var/list/stored_comms = list(
"metal" = 0,
"glass" = 0,
- "wood" = 0,
- "plastic" = 0
+ "wood" = 0
)
/obj/item/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
return
-/obj/item/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params)
-
+/obj/item/matter_decompiler/afterattack(atom/target, mob/living/user, proximity, params)
if(!proximity) return //Not adjacent.
//We only want to deal with using this on turfs. Specific items aren't important.
@@ -168,101 +167,11 @@
return
//Used to give the right message.
- var/grabbed_something = 0
+ var/grabbed_something = FALSE
- for(var/mob/M in T)
- if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse))
- src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.")
- new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
- qdel(M)
- stored_comms["wood"]++
- stored_comms["wood"]++
- stored_comms["plastic"]++
- stored_comms["plastic"]++
- return
-
- else if(istype(M,/mob/living/silicon/robot/drone) && !M.client)
-
- var/mob/living/silicon/robot/drone/D = src.loc
-
- if(!istype(D))
- return
-
- to_chat(D, "You begin decompiling the other drone.")
-
- if(!do_after(D, 50, target = target))
- to_chat(D, "You need to remain still while decompiling such a large object.")
- return
-
- if(!M || !D) return
-
- to_chat(D, "You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.")
-
- qdel(M)
- new/obj/effect/decal/cleanable/blood/oil(get_turf(src))
-
- stored_comms["metal"] += 15
- stored_comms["glass"] += 15
- stored_comms["wood"] += 5
- stored_comms["plastic"] += 5
-
- return
- else
- continue
-
- for(var/obj/W in T)
- //Different classes of items give different commodities.
- if(istype(W,/obj/item/cigbutt))
- stored_comms["plastic"]++
- else if(istype(W,/obj/structure/spider/spiderling))
- stored_comms["wood"]++
- stored_comms["wood"]++
- stored_comms["plastic"]++
- stored_comms["plastic"]++
- else if(istype(W,/obj/item/light))
- var/obj/item/light/L = W
- if(L.status >= 2) //In before someone changes the inexplicably local defines. ~ Z
- stored_comms["metal"]++
- stored_comms["glass"]++
- else
- continue
- else if(istype(W,/obj/effect/decal/remains/robot))
- stored_comms["metal"]++
- stored_comms["metal"]++
- stored_comms["plastic"]++
- stored_comms["plastic"]++
- stored_comms["glass"]++
- else if(istype(W,/obj/item/trash))
- stored_comms["metal"]++
- stored_comms["plastic"]++
- stored_comms["plastic"]++
- else if(istype(W,/obj/effect/decal/cleanable/blood/gibs/robot))
- stored_comms["metal"]++
- stored_comms["metal"]++
- stored_comms["glass"]++
- stored_comms["glass"]++
- else if(istype(W,/obj/item/ammo_casing))
- stored_comms["metal"]++
- else if(istype(W,/obj/item/shard))
- stored_comms["glass"]++
- stored_comms["glass"]++
- stored_comms["glass"]++
- else if(istype(W,/obj/item/reagent_containers/food/snacks/grown))
- stored_comms["wood"]++
- stored_comms["wood"]++
- stored_comms["wood"]++
- stored_comms["wood"]++
- else if(istype(W,/obj/item/broken_bottle))
- stored_comms["glass"]++
- stored_comms["glass"]++
- stored_comms["glass"]++
- else if(istype(W,/obj/item/light/tube) || istype(W,/obj/item/light/bulb))
- stored_comms["glass"]++
- else
- continue
-
- qdel(W)
- grabbed_something = 1
+ for(var/atom/movable/A in T)
+ if(A.decompile_act(src, user)) // Each decompileable mob or obj needs to have this defined
+ grabbed_something = TRUE
if(grabbed_something)
to_chat(user, "You deploy your decompiler and clear out the contents of \the [T].")
@@ -353,11 +262,5 @@
stack_wood = new /obj/item/stack/sheet/wood(src.module)
stack_wood.amount = 1
stack = stack_wood
- if("plastic")
- if(!stack_plastic)
- stack_plastic = new /obj/item/stack/sheet/plastic(src.module)
- stack_plastic.amount = 1
- stack = stack_plastic
-
stack.amount++
decompiler.stored_comms[type]--
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index d7f42a9ceb5..7f1a18ac22d 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -85,6 +85,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/lockcharge //Used when locking down a borg to preserve cell charge
var/speed = 0 //Cause sec borgs gotta go fast //No they dont!
var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them.
+ var/has_camera = TRUE
var/pdahide = 0 //Used to hide the borg from the messenger list
var/tracking_entities = 0 //The number of known entities currently accessing the internal camera
var/braintype = "Cyborg"
@@ -111,7 +112,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/get_cell()
return cell
-/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0)
+/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
@@ -133,9 +134,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
radio = new /obj/item/radio/borg(src)
common_radio = radio
- init()
+ init(ai_to_sync_to = ai_to_sync_to)
- if(!camera && (!scrambledcodes || designation == "ERT"))
+ if(has_camera && !camera)
camera = new /obj/machinery/camera(src)
camera.c_tag = real_name
camera.network = list("SS13","Robots")
@@ -171,14 +172,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
scanner = new(src)
scanner.Grant(src)
-/mob/living/silicon/robot/proc/init(var/alien=0)
+/mob/living/silicon/robot/proc/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
make_laws()
additional_law_channels["Binary"] = ":b "
- var/new_ai = select_active_ai_with_fewest_borgs()
- if(new_ai)
+ var/found_ai = ai_to_sync_to
+ if(!found_ai)
+ found_ai = select_active_ai_with_fewest_borgs()
+ if(found_ai)
lawupdate = 1
- connect_to_ai(new_ai)
+ connect_to_ai(found_ai)
else
lawupdate = 0
@@ -238,7 +241,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
if(custom_name)
return 0
if(!allow_rename)
- to_chat(src, "Rename functionality is not enabled on this unit.");
+ to_chat(src, "Rename functionality is not enabled on this unit.")
return 0
rename_self(braintype, 1)
@@ -1147,7 +1150,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
if(module)
if(module.type == /obj/item/robot_module/janitor)
var/turf/tile = loc
- if(isturf(tile))
+ if(stat != DEAD && isturf(tile))
var/floor_only = TRUE
for(var/A in tile)
if(istype(A, /obj/effect))
@@ -1332,6 +1335,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
designation = "SpecOps"
lawupdate = 0
scrambledcodes = 1
+ has_camera = FALSE
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
magpulse = 1
@@ -1346,7 +1350,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
is_emaggable = FALSE
default_cell_type = /obj/item/stock_parts/cell/bluespace
-/mob/living/silicon/robot/deathsquad/init()
+/mob/living/silicon/robot/deathsquad/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/deathsquad(src)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
@@ -1376,7 +1380,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/eprefix = "Amber"
-/mob/living/silicon/robot/ert/init()
+/mob/living/silicon/robot/ert/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/ert_override
radio = new /obj/item/radio/borg/ert(src)
radio.recalculateChannels()
@@ -1420,6 +1424,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
designation = "Destroyer"
lawupdate = 0
scrambledcodes = 1
+ has_camera = FALSE
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
magpulse = 1
@@ -1431,8 +1436,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
xeno_disarm_chance = 10
default_cell_type = /obj/item/stock_parts/cell/bluespace
-/mob/living/silicon/robot/destroyer/init()
- ..()
+/mob/living/silicon/robot/destroyer/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
+ aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
+ additional_law_channels["Binary"] = ":b "
+ laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/destroyer(src)
module.add_languages(src)
module.add_subsystems_and_actions(src)
@@ -1441,6 +1448,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
qdel(radio)
radio = new /obj/item/radio/borg/ert/specops(src)
radio.recalculateChannels()
+ playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
/mob/living/silicon/robot/destroyer/borg_icons()
if(base_icon == "")
diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm
index 98afb8285d1..f5b58696059 100644
--- a/code/modules/mob/living/silicon/robot/syndicate.dm
+++ b/code/modules/mob/living/silicon/robot/syndicate.dm
@@ -3,6 +3,7 @@
icon_state = "syndie_bloodhound"
lawupdate = 0
scrambledcodes = 1
+ has_camera = FALSE
pdahide = 1
faction = list("syndicate")
bubble_icon = "syndibot"
@@ -20,7 +21,7 @@
..()
cell = new /obj/item/stock_parts/cell/hyper(src)
-/mob/living/silicon/robot/syndicate/init()
+/mob/living/silicon/robot/syndicate/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
laws = new /datum/ai_laws/syndicate_override
module = new /obj/item/robot_module/syndicate(src)
@@ -46,7 +47,7 @@
Your energy saw functions as a circular saw, but can be activated to deal more damage, and your operative pinpointer will find and locate fellow nuclear operatives. \
Help the operatives secure the disk at all costs!"
-/mob/living/silicon/robot/syndicate/medical/init()
+/mob/living/silicon/robot/syndicate/medical/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
..()
module = new /obj/item/robot_module/syndicate_medical(src)
@@ -66,7 +67,7 @@
Be aware that physical contact or taking damage will break your disguise. \
Help the operatives secure the disk at all costs!"
-/mob/living/silicon/robot/syndicate/saboteur/init()
+/mob/living/silicon/robot/syndicate/saboteur/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
..()
module = new /obj/item/robot_module/syndicate_saboteur(src)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index e720400abd5..cbb0287b76a 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -64,6 +64,6 @@
else
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
playsound(loc, 'sound/effects/bang.ogg', 10, 1)
- visible_message("[M] punches [src], but doesn't leave a dent.", \
- "[M] punches [src], but doesn't leave a dent.!")
+ visible_message("[M] punches [src], but doesn't leave a dent.", \
+ "[M] punches [src], but doesn't leave a dent.")
return FALSE
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index cb45fd98e8b..e51dc527832 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -756,7 +756,7 @@ Pass a positive integer as an argument to override a bot's default speed.
// send a radio signal with multiple data key/values
/mob/living/simple_animal/bot/proc/post_signal_multiple(var/freq, var/list/keyval)
- if(z != 1) //Bot control will only work on station.
+ if(!is_station_level(z)) //Bot control will only work on station.
return
var/datum/radio_frequency/frequency = SSradio.return_frequency(freq)
if(!frequency)
diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm
index 024e2b5f304..e18672ffa20 100644
--- a/code/modules/mob/living/simple_animal/bot/construction.dm
+++ b/code/modules/mob/living/simple_animal/bot/construction.dm
@@ -474,8 +474,7 @@
return
build_step++
to_chat(user, "You complete the Securitron! Beep boop.")
- var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot
- S.forceMove(get_turf(src))
+ var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot(get_turf(src))
S.name = created_name
S.robot_arm = robot_arm
qdel(I)
@@ -512,7 +511,7 @@
//General Griefsky
else if((istype(I, /obj/item/wrench)) && (build_step == 3))
- var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly
+ var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly(get_turf(src))
user.put_in_hands(A)
to_chat(user, "You adjust the arm slots for extra weapons!.")
user.unEquip(src, 1)
@@ -540,8 +539,7 @@
if(!user.unEquip(I))
return
to_chat(user, "You complete General Griefsky!.")
- var/mob/living/simple_animal/bot/secbot/griefsky/S = new /mob/living/simple_animal/bot/secbot/griefsky
- S.forceMove(get_turf(src))
+ new /mob/living/simple_animal/bot/secbot/griefsky(get_turf(src))
qdel(I)
qdel(src)
@@ -556,8 +554,7 @@
if(!user.unEquip(I))
return
to_chat(user, "You complete Genewul Giftskee!.")
- var/mob/living/simple_animal/bot/secbot/griefsky/toy/S = new /mob/living/simple_animal/bot/secbot/griefsky/toy
- S.forceMove(get_turf(src))
+ new /mob/living/simple_animal/bot/secbot/griefsky/toy(get_turf(src))
qdel(I)
qdel(src)
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index d9097a82d2e..62c133fffd7 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -33,7 +33,7 @@
/obj/machinery/bot_core/honkbot
req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME)
-/mob/living/simple_animal/bot/honkbot/Initialize()
+/mob/living/simple_animal/bot/honkbot/Initialize(mapload)
. = ..()
update_icon()
auto_patrol = TRUE
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index f150005116e..4c2c2332924 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -17,7 +17,7 @@
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
faction = list("cult")
- flying = 1
+ flying = TRUE
pressure_resistance = 100
universal_speak = 1
AIStatus = AI_OFF //normal constructs don't have AI
diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm
index 10460d11e65..8d0ad5d0e2b 100644
--- a/code/modules/mob/living/simple_animal/corpse.dm
+++ b/code/modules/mob/living/simple_animal/corpse.dm
@@ -103,9 +103,9 @@
name = "Space Wizard Corpse"
outfit = /datum/outfit/wizardcorpse
-/obj/effect/mob_spawn/human/corpse/clownoff/Initialize()
+/obj/effect/mob_spawn/human/corpse/clownoff/Initialize(mapload)
mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]"
- ..()
+ . = ..()
/datum/outfit/wizardcorpse
name = "Space Wizard Corpse"
diff --git a/code/modules/mob/living/simple_animal/friendly/butterfly.dm b/code/modules/mob/living/simple_animal/friendly/butterfly.dm
index 9aaffc6ef8f..ebf068ff130 100644
--- a/code/modules/mob/living/simple_animal/friendly/butterfly.dm
+++ b/code/modules/mob/living/simple_animal/friendly/butterfly.dm
@@ -15,6 +15,7 @@
harm_intent_damage = 1
friendly = "nudges"
density = 0
+ flying = TRUE
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
ventcrawler = 2
mob_size = MOB_SIZE_TINY
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 24818d07652..f44244b7eae 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -123,6 +123,7 @@
for(var/mob/living/simple_animal/mouse/M in view(1, src))
if(!M.stat && Adjacent(M))
custom_emote(1, "splats \the [M]!")
+ M.death()
M.splat()
movement_target = null
stop_automated_movement = 0
diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
index ea8e949f1c7..58e3ad54089 100644
--- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
+++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
@@ -126,6 +126,7 @@
gold_core_spawnable = FRIENDLY_SPAWN
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/udder/udder = null
+ gender = FEMALE
/mob/living/simple_animal/cow/Initialize()
udder = new()
diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm
index 96688963b73..566791786f6 100644
--- a/code/modules/mob/living/simple_animal/friendly/lizard.dm
+++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm
@@ -23,3 +23,14 @@
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1)
can_collar = 1
gold_core_spawnable = FRIENDLY_SPAWN
+
+/mob/living/simple_animal/lizard/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 [src] into your decompiler. It makes a series of visceral crunching noises.")
+ new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
+ C.stored_comms["wood"] += 2
+ C.stored_comms["glass"] += 2
+ qdel(src)
+ return TRUE
+ return ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index a48b36cb0c7..be3f985db2e 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -80,22 +80,16 @@
icon_resting = "mouse_[mouse_color]_sleep"
desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself."
-/mob/living/simple_animal/mouse/proc/splat()
- src.health = 0
- src.stat = DEAD
- src.icon_dead = "mouse_[mouse_color]_splat"
- src.icon_state = "mouse_[mouse_color]_splat"
- layer = MOB_LAYER
- if(client)
- client.time_died_as_mouse = world.time
-
/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob)
if(M.a_intent == INTENT_HELP)
get_scooped(M)
..()
-/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things
- to_chat(src, "You are too small to pull anything.")
+/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)//Prevents mouse from pulling things
+ if(istype(AM, /obj/item/reagent_containers/food/snacks/cheesewedge))
+ return ..() // Get dem
+ if(show_message)
+ to_chat(src, "You are too small to pull anything except cheese.")
return
/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc)
@@ -110,6 +104,10 @@
desc = "It's toast."
death()
+/mob/living/simple_animal/mouse/proc/splat()
+ icon_dead = "mouse_[mouse_color]_splat"
+ icon_state = "mouse_[mouse_color]_splat"
+
/mob/living/simple_animal/mouse/death(gibbed)
// Only execute the below if we successfully died
playsound(src, squeak_sound, 40, 1)
@@ -234,3 +232,14 @@
gold_core_spawnable = NO_SPAWN
can_collar = 0
butcher_results = list(/obj/item/stack/sheet/metal = 1)
+
+/mob/living/simple_animal/mouse/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 [src] into your decompiler. It makes a series of visceral crunching noises.")
+ new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
+ C.stored_comms["wood"] += 2
+ C.stored_comms["glass"] += 2
+ qdel(src)
+ return TRUE
+ return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm
index be04fc217e9..b47b0eb1f23 100644
--- a/code/modules/mob/living/simple_animal/hostile/bat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bat.dm
@@ -16,6 +16,7 @@
maxHealth = 20
health = 20
mob_size = MOB_SIZE_TINY
+ flying = TRUE
harm_intent_damage = 8
melee_damage_lower = 10
melee_damage_upper = 10
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
index 3276416fe7f..3419279ffe5 100644
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bees.dm
@@ -95,6 +95,10 @@
for(var/mob/A in searched_for)
. += A
+// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded.
+/mob/living/simple_animal/hostile/poison/bees/regenerate_icons()
+ return
+
/mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals()
overlays.Cut()
diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
index 08eb7128055..c093c8e3d36 100644
--- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm
+++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm
@@ -31,8 +31,7 @@
if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD))
Zombify(H)
break
- var/cycles = 4
- if(cycles >= 4)
+ if(times_fired % 4 == 0)
for(var/mob/living/simple_animal/K in oview(src, 1)) //Only for corpse right next to/on same tile
if(K.stat == DEAD || (!K.check_death_method() && K.health <= HEALTH_THRESHOLD_DEAD))
visible_message("[src] consumes [K] whole!")
@@ -40,8 +39,6 @@
health += 10
qdel(K)
break
- cycles = 0
- cycles++
/mob/living/simple_animal/hostile/headcrab/OpenFire(atom/A)
if(check_friendly_fire)
@@ -93,7 +90,6 @@
if(is_zombie)
qdel(src)
-
/mob/living/simple_animal/hostile/headcrab/handle_automated_speech() // This way they have different screams when attacking, sometimes. Might be seen as sphagetthi code though.
if(speak_chance)
if(rand(0,200) < speak_chance)
@@ -106,7 +102,6 @@
M.loc = get_turf(src)
return ..()
-
/mob/living/simple_animal/hostile/headcrab/update_icons()
. = ..()
if(is_zombie)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index b8c72d7cf84..2f97001d3ae 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -241,6 +241,14 @@ Difficulty: Very Hard
AT.pixel_y += random_y
return ..()
+/mob/living/simple_animal/hostile/megafauna/colossus/float(on) //we don't want this guy to float, messes up his animations
+ if(throwing)
+ return
+ if(on && !floating)
+ floating = TRUE
+ else if(!on && floating)
+ floating = FALSE
+
/obj/item/projectile/colossus
name ="death bolt"
icon_state= "chronobolt"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 6dca834169b..50531f976e9 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -534,7 +534,7 @@ obj/effect/temp_visual/fireball
duration = 9
pixel_z = 270
-/obj/effect/temp_visual/fireball/Initialize()
+/obj/effect/temp_visual/fireball/Initialize(mapload)
. = ..()
animate(src, pixel_z = 0, time = duration)
diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm
index 848a285abf4..fbf65ccfa9b 100644
--- a/code/modules/mob/living/simple_animal/hostile/mimic.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm
@@ -48,8 +48,8 @@
var/attempt_open = 0
// Pickup loot
-/mob/living/simple_animal/hostile/mimic/crate/Initialize()
- ..()
+/mob/living/simple_animal/hostile/mimic/crate/Initialize(mapload)
+ . = ..()
for(var/obj/item/I in loc)
I.loc = src
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index e8ce4027b4e..89164167005 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -31,7 +31,7 @@
deathmessage = "wails as its form turns into a pulpy mush."
death_sound = 'sound/voice/hiss6.ogg'
-/mob/living/simple_animal/hostile/netherworld/migo/Initialize()
+/mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload)
. = ..()
migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/goonstation/effects/screech.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/goonstation/misc/airraid_loop.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/chainsaw.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshots/gunshot_silenced.ogg', 'sound/weapons/gunshots/gunshot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg', 'sound/ambience/ambigen1.ogg', 'sound/ambience/ambigen3.ogg', 'sound/ambience/ambigen4.ogg', 'sound/ambience/ambigen5.ogg', 'sound/ambience/ambigen6.ogg', 'sound/ambience/ambigen10.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg') //hahahaha fuck you code divers
@@ -82,14 +82,14 @@
/obj/structure/spawner/nether/examine(mob/user)
. = ..()
- if(isskeleton(user) || iszombie(user))
+ if(isskeleton(user))
. += "A direct link to another dimension full of creatures very happy to see you. You can see your house from here!"
else
. += "A direct link to another dimension full of creatures not very happy to see you. Entering the link would be a very bad idea."
/obj/structure/spawner/nether/attack_hand(mob/user)
. = ..()
- if(isskeleton(user) || iszombie(user))
+ if(isskeleton(user))
to_chat(user, "You don't feel like going home yet...")
else
user.visible_message("[user] is violently pulled into the link!", \
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm
index f44e6c1f88a..ca6987d735c 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm
@@ -26,21 +26,34 @@
flying = 1
var/carp_color = "carp" //holder for icon set
- var/list/icon_sets = list("carp", "blue", "yellow", "grape", "rust", "teal", "purple")
+ var/static/list/carp_colors = list(\
+ "lightpurple" = "#c3b9f1", \
+ "lightpink" = "#da77a8", \
+ "green" = "#70ff25", \
+ "grape" = "#df0afb", \
+ "swamp" = "#e5e75a", \
+ "turquoise" = "#04e1ed", \
+ "brown" = "#ca805a", \
+ "teal" = "#20e28e", \
+ "lightblue" = "#4d88cc", \
+ "rusty" = "#dd5f34", \
+ "beige" = "#bbaeaf", \
+ "yellow" = "#f3ca4a", \
+ "blue" = "#09bae1", \
+ "palegreen" = "#7ef099", \
+ )
/mob/living/simple_animal/hostile/retaliate/carp/Initialize(mapload)
. = ..()
carp_randomify()
update_icons()
-/mob/living/simple_animal/hostile/retaliate/carp/proc/carp_randomify()
- if(prob(1))
- carp_color = pick("white", "black")
- else
- carp_color = pick(icon_sets)
- icon_state = "[carp_color]"
- icon_living = "[carp_color]"
- icon_dead = "[carp_color]_dead"
+/mob/living/simple_animal/hostile/retaliate/carp/proc/carp_randomify(rarechance)
+ // Simplified version of: /mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance)
+ var/our_color
+ our_color = pick(carp_colors)
+ add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY)
+ regenerate_icons()
/mob/living/simple_animal/hostile/retaliate/carp/koi
name = "space koi"
@@ -61,13 +74,15 @@
maxbodytemp = 1500
gold_core_spawnable = HOSTILE_SPAWN
+ var/randomize_icon = TRUE
/mob/living/simple_animal/hostile/retaliate/carp/koi/Initialize(mapload)
. = ..()
- var/koinum = rand(1, 4)
- icon_state = "koi[koinum]"
- icon_living = "koi[koinum]"
- icon_dead = "koi[koinum]-dead"
+ if(randomize_icon)
+ var/koinum = rand(1, 4)
+ icon_state = "koi[koinum]"
+ icon_living = "koi[koinum]"
+ icon_dead = "koi[koinum]-dead"
/mob/living/simple_animal/hostile/retaliate/carp/koi/Process_Spacemove(var/movement_dir)
return TRUE
@@ -76,3 +91,4 @@
icon_state = "koi5"
icon_living = "koi5"
icon_dead = "koi5-dead"
+ randomize_icon = FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm
index a3e32e9d35e..5d946662854 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm
@@ -46,6 +46,7 @@
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
+ flying = TRUE
pressure_resistance = 300
gold_core_spawnable = NO_SPAWN //too spooky for science
faction = list("undead") // did I mention ghost
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 7ebddfeec12..f992bb3cc10 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -260,7 +260,7 @@
alert_on_shield_breach = TRUE
/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload)
- ..()
+ . = ..()
if(prob(50))
// 50% chance of switching to extremely dangerous ranged variant
melee_damage_lower = 10
@@ -362,7 +362,7 @@
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
mob_size = MOB_SIZE_TINY
- flying = 1
+ flying = TRUE
bubble_icon = "syndibot"
gold_core_spawnable = HOSTILE_SPAWN
del_on_death = 1
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
index e7daced65ff..1946f1a32ee 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm
@@ -51,6 +51,10 @@
if(!(eggtype in eggtypes))
to_chat(src, "Unrecognized egg type.")
return 0
+ if(fed < feedings_to_lay)
+ // We have to check this again after the popup, to account for people spam-clicking the button, then doing all the popups at once.
+ to_chat(src, "You must wrap more humanoid prey before you can do this!")
+ return
visible_message("[src] lays a cluster of eggs.")
if(eggtype == TS_DESC_RED)
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 1)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
index 047219a151b..6f33c51b246 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm
@@ -15,7 +15,7 @@
ai_target_method = TS_DAMAGE_SIMPLE
icon_state = "terror_princess1"
icon_living = "terror_princess1"
- icon_dead = "terror_princess_dead1"
+ icon_dead = "terror_princess1_dead"
maxHealth = 150
health = 150
regen_points_per_hp = 1 // always regens very fast
@@ -51,7 +51,7 @@
if(fed == 0)
icon_state = "terror_princess1"
icon_living = "terror_princess1"
- icon_dead = "terror_princess_dead1"
+ icon_dead = "terror_princess1_dead"
desc = "An enormous spider. It looks strangely cute and fluffy, with soft pink fur covering most of its body."
else if(fed == 1)
icon_state = "terror_princess2"
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm
index bf3c98bb86a..4b18626a687 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm
@@ -287,6 +287,10 @@
if(eggtype == null || numlings == null)
to_chat(src, "Cancelled.")
return
+ if(canlay < numlings)
+ // We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once.
+ to_chat(src, "Too soon to do this again!")
+ return
canlay -= numlings
eggslaid += numlings
if(eggtype == TS_DESC_RED)
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 41786017ea2..8d6db8ade3c 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -86,6 +86,7 @@
//Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding.
var/obj/item/held_item = null
+ flying = TRUE
gold_core_spawnable = FRIENDLY_SPAWN
@@ -710,7 +711,7 @@
ears.talk_into(src, message_pieces, message_mode, verb)
used_radios += ears
-/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
+/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(speaker != src && prob(50))
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
diff --git a/code/modules/mob/living/simple_animal/posessed_object.dm b/code/modules/mob/living/simple_animal/posessed_object.dm
index c3cb61d5970..729790d098c 100644
--- a/code/modules/mob/living/simple_animal/posessed_object.dm
+++ b/code/modules/mob/living/simple_animal/posessed_object.dm
@@ -32,8 +32,9 @@
animate_ghostly_presence(src, -1, 20, 1) // Restart the floating animation after the attack animation, as it will be cancelled.
-/mob/living/simple_animal/possessed_object/start_pulling(var/atom/movable/AM) // Silly motherfuckers think they can pull things.
- to_chat(src, "You are unable to pull [AM]!")
+/mob/living/simple_animal/possessed_object/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) // Silly motherfuckers think they can pull things.
+ if(show_message)
+ to_chat(src, "You are unable to pull [AM]!")
/mob/living/simple_animal/possessed_object/ghost() // Ghosting will return the object to normal, and will not disqualify the ghoster from various mid-round antag positions.
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 663838d3a45..1e3c723b278 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -24,6 +24,7 @@
status_flags = 0
faction = list("cult")
status_flags = CANPUSH
+ flying = TRUE
loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm)
del_on_death = 1
deathmessage = "lets out a contented sigh as their form unwinds."
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 7d31b5d523b..d4f8e7c4c3d 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -615,3 +615,8 @@
if(pcollar && collar_type)
add_overlay("[collar_type]collar")
add_overlay("[collar_type]tag")
+
+/mob/living/simple_animal/Login()
+ ..()
+ walk(src, 0) // if mob is moving under ai control, then stop AI movement
+
diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm
index 74241dc676c..8679d64a699 100644
--- a/code/modules/mob/living/simple_animal/slime/say.dm
+++ b/code/modules/mob/living/simple_animal/slime/say.dm
@@ -9,7 +9,7 @@
return verb
-/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
+/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(speaker != src && !stat)
if(speaker in Friends)
speech_buffer = list()
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 15f483bef76..1b67b53119a 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -265,7 +265,7 @@
/mob/living/simple_animal/slime/unEquip(obj/item/I, force)
return
-/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return
/mob/living/simple_animal/slime/attack_ui(slot)
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 7cd2b4414f7..ce266ba5b8f 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -141,18 +141,6 @@
if(updating && val_change)
update_canmove()
-/mob/living/proc/StartFlying()
- var/val_change = !flying
- flying = TRUE
- if(val_change)
- update_animations()
-
-/mob/living/proc/StopFlying()
- var/val_change = !!flying
- flying = FALSE
- if(val_change)
- update_animations()
-
// SCALAR STATUS EFFECTS
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 5345bda5b94..c222f76eadb 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -28,6 +28,7 @@
/mob/Login()
GLOB.player_list |= src
+ last_known_ckey = ckey
update_Login_details()
world.update_status()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d6a86d3e3cd..848606be3ec 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -19,11 +19,10 @@
for(var/datum/alternate_appearance/AA in viewing_alternate_appearances)
AA.viewers -= src
viewing_alternate_appearances = null
- logs.Cut()
LAssailant = null
return ..()
-/mob/Initialize()
+/mob/Initialize(mapload)
GLOB.mob_list += src
if(stat == DEAD)
GLOB.dead_mob_list += src
@@ -31,7 +30,7 @@
GLOB.alive_mob_list += src
set_focus(src)
prepare_huds()
- ..()
+ . = ..()
/atom/proc/prepare_huds()
hud_list = list()
@@ -578,6 +577,8 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
changeNext_move(CLICK_CD_POINT)
var/obj/P = new /obj/effect/temp_visual/point(tile)
P.invisibility = invisibility
+ P.pixel_x = A.pixel_x
+ P.pixel_y = A.pixel_y
return 1
/mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag)
@@ -752,7 +753,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
if(client.holder && (client.holder.rights & R_ADMIN))
is_admin = 1
- else if(stat != DEAD || istype(src, /mob/new_player))
+ else if(stat != DEAD || isnewplayer(src))
to_chat(usr, "You must be observing to use this!")
return
@@ -880,6 +881,9 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
return
if(!Adjacent(usr))
return
+ if(IsFrozen(src) && !is_admin(usr))
+ to_chat(usr, "Interacting with admin-frozen players is not permitted.")
+ return
if(isLivingSSD(src) && M.client && M.client.send_ssd_warning(src))
return
show_inv(usr)
@@ -1235,10 +1239,13 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
create_log_in_list(debug_log, text, collapse, world.timeofday)
/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src))
- LAZYINITLIST(logs[log_type])
- var/list/log_list = logs[log_type]
+ if(!ckey)
+ return
+ var/real_ckey = ckey
+ if(ckey[1] == "@") // Admin aghosting will do this
+ real_ckey = copytext(ckey, 2)
var/datum/log_record/record = new(log_type, src, what, target, where, world.time)
- log_list.Add(record)
+ GLOB.logging.add_log(real_ckey, record)
/proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc
//this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar."
@@ -1297,8 +1304,6 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
.["Add Organ"] = "?_src_=vars;addorgan=[UID()]"
.["Remove Organ"] = "?_src_=vars;remorgan=[UID()]"
- .["Fix NanoUI"] = "?_src_=vars;fix_nano=[UID()]"
-
.["Add Verb"] = "?_src_=vars;addverb=[UID()]"
.["Remove Verb"] = "?_src_=vars;remverb=[UID()]"
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 1477be47d89..80404460b50 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -36,7 +36,7 @@
var/list/attack_log_old = list( )
var/list/debug_log = null
- var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm
+ var/last_known_ckey = null // Used in logging
var/last_log = 0
var/obj/machinery/machine = null
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 64444864804..37e116b4376 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -434,7 +434,7 @@
if(affecting)
if(!affecting.buckled)
affecting.pixel_x = 0
- affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing
+ affecting.pixel_y = 0 //used to be an animate, not quick enough for qdel'ing
affecting.layer = initial(affecting.layer)
affecting.grabbed_by -= src
affecting = null
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 63175a68024..ff01fbe5cc9 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -113,7 +113,7 @@
var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]"
if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes")
question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]"
- var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
@@ -459,7 +459,7 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
name = realname
for(var/mob/M in GLOB.player_list)
- if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD))
+ if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD))
var/follow
var/lname
if(subject)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 4797c685fe1..0325d13faf0 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -369,8 +369,9 @@
step(pulling, get_dir(pulling.loc, A))
return
-/mob/proc/update_gravity()
+/mob/proc/update_gravity(has_gravity)
return
+
/client/proc/check_has_body_select()
return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel)
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index c3ec68c9793..1e704ea3449 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -4,7 +4,7 @@
//Note that this proc does NOT do MMI related stuff!
/mob/proc/change_mob_type(var/new_type = null, var/turf/location = null, var/new_name = null as text, var/delete_old_mob = 0 as num, var/forcekey = 0)
- if(istype(src,/mob/new_player))
+ if(isnewplayer(src))
to_chat(usr, "cannot convert players who have not entered yet.")
return
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index af3d439df59..f53daba6626 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -12,8 +12,13 @@
stat = 2
canmove = 0
-/mob/new_player/New()
+/mob/new_player/Initialize(mapload)
+ SHOULD_CALL_PARENT(FALSE)
+ if(initialized)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ initialized = TRUE
GLOB.mob_list += src
+ return INITIALIZE_HINT_NORMAL
/mob/new_player/verb/new_player_panel()
set src = usr
@@ -205,6 +210,7 @@
if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed.
observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing!
observer.key = key
+ QDEL_NULL(mind)
GLOB.respawnable_list += observer
qdel(src)
return 1
diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm
index f67c53c9941..2e109d36d34 100644
--- a/code/modules/mob/new_player/poll.dm
+++ b/code/modules/mob/new_player/poll.dm
@@ -90,9 +90,9 @@
if(adminonly)
question = "(Admin only poll) " + question
- var output = ""
+ var/output = ""
if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION)
- select_query = GLOB.dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]");
+ select_query = GLOB.dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
select_query.Execute()
var/list/options = list()
var/total_votes = 1
@@ -177,7 +177,7 @@
output += " | "
output += " "
if(polltype == POLLTYPE_TEXT)
- select_query = GLOB.dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC");
+ select_query = GLOB.dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC")
select_query.Execute()
output += {"
diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
index 476861861a3..d4834eccc80 100644
--- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
@@ -48,7 +48,7 @@
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
// Restrict some styles to specific species
- var/list/species_allowed = list("Human", "Slime People", "Infectious Zombie", "High-Functioning Zombie")
+ var/list/species_allowed = list("Human", "Slime People")
var/list/sprite_sheets = list() //For accessories common across species but need to use 'fitted' sprites (like underwear). e.g. list("Vox" = 'icons/mob/species/vox/iconfile.dmi')
var/list/models_allowed = list() //Specifies which, if any, hairstyles or markings can be accessed by which prosthetics. Should equal the manufacturing company name in robolimbs.dm.
var/list/heads_allowed = null //Specifies which, if any, alt heads a head marking, hairstyle or facial hair style is compatible with.
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 0417e28f546..7a26de718b6 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -44,17 +44,27 @@
usr.emote(message)
-/mob/proc/say_dead(var/message)
- if(!(client && client.holder))
- if(!config.dsay_allowed)
- to_chat(src, "Deadchat is globally muted.")
+/mob/proc/say_dead(message)
+ if(client)
+ if(!client.holder)
+ if(!config.dsay_allowed)
+ to_chat(src, "Deadchat is globally muted.")
+ return
+
+ if(client.prefs.muted & MUTE_DEADCHAT)
+ to_chat(src, "You cannot talk in deadchat (muted).")
return
- if(client && !(client.prefs.toggles & CHAT_DEAD))
- to_chat(usr, "You have deadchat muted.")
- return
+ if(!(client.prefs.toggles & CHAT_DEAD))
+ to_chat(src, "You have deadchat muted.")
+ return
+
+ if(client.handle_spam_prevention(message, MUTE_DEADCHAT))
+ return
say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src)
+ create_log(DEADCHAT_LOG, message)
+ log_ghostsay(message, src)
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
if(stat == DEAD)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 2ee4a9d2a9c..8e7adea666b 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -82,14 +82,11 @@
O.notify_ai(1)
if(O.mind && O.mind.assigned_role == "Cyborg")
- if(O.mind.role_alt_title == "Android")
+ if(O.mind.role_alt_title == "Robot")
O.mmi = new /obj/item/mmi/robotic_brain(O)
- else if(O.mind.role_alt_title == "Robot")
- O.mmi = null //Robots do not have removable brains.
else
O.mmi = new /obj/item/mmi(O)
-
- if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client.
+ O.mmi.transfer_identity(src) //Does not transfer key/client.
O.update_pipe_vision()
diff --git a/code/modules/mob/update_status.dm b/code/modules/mob/update_status.dm
index 1708bcfb7a2..b603fc5974e 100644
--- a/code/modules/mob/update_status.dm
+++ b/code/modules/mob/update_status.dm
@@ -55,12 +55,6 @@
// Procs that update other things about the mob
-// Does various animations - Jitter, Flying, Spinning
-/mob/proc/update_animations()
- if(flying)
- animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING)
- animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING)
-
/mob/proc/update_stat()
return
diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm
index b85fb710e38..cae4b5ed2e2 100644
--- a/code/modules/modular_computers/file_system/programs/command/card.dm
+++ b/code/modules/modular_computers/file_system/programs/command/card.dm
@@ -222,7 +222,7 @@
if(is_authenticated(usr) && modify)
var/t1 = href_list["assign_target"]
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)
@@ -251,7 +251,8 @@
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))
@@ -266,7 +267,7 @@
if("PRG_reg")
if(is_authenticated(usr))
- 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
@@ -290,6 +291,8 @@
if(is_authenticated(usr))
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)
if("PRG_print")
@@ -331,9 +334,14 @@
if("PRG_terminate")
if(is_authenticated(usr))
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
+ log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for: \"[reason]\".")
+ message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for: \"[reason]\".")
+ usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" 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()
diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm
deleted file mode 100644
index be9c5bbe2b3..00000000000
--- a/code/modules/nano/nanomapgen.dm
+++ /dev/null
@@ -1,90 +0,0 @@
-// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm
-
-#define NANOMAP_ICON_SIZE 4
-#define NANOMAP_MAX_ICON_DIMENSION 1024
-
-#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE)
-
-#define NANOMAP_TERMINALERR 5
-#define NANOMAP_INPROGRESS 2
-#define NANOMAP_BADOUTPUT 2
-#define NANOMAP_SUCCESS 1
-#define NANOMAP_WATCHDOGSUCCESS 4
-#define NANOMAP_WATCHDOGTERMINATE 3
-
-
-//Call these procs to dump your world to a series of image files (!!)
-//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly
-
-/client/proc/nanomapgen_DumpImage()
- set name = "Generate NanoUI Map"
- set category = "Mapping"
-
- if(holder)
- nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate")))
-
-/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1)
-
- if(endX < 0 || endX > world.maxx)
- endX = world.maxx
-
- if(endY < 0 || endY > world.maxy)
- endY = world.maxy
-
- if(currentZ < 0 || currentZ > world.maxz)
- to_chat(usr, "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]")
-
- sleep(3)
- return NANOMAP_TERMINALERR
-
- if(startX > endX)
- to_chat(usr, "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])")
-
- sleep(3)
- return NANOMAP_TERMINALERR
-
- if(startY > endX)
- to_chat(usr, "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])")
- sleep(3)
- return NANOMAP_TERMINALERR
-
- var/icon/Tile = icon(file("nano/mapbase1024.png"))
- if(Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
- log_world("NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]")
- sleep(3)
- return NANOMAP_TERMINALERR
-
- log_world("NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])")
- to_chat(usr, "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])")
-
- var/count = 0;
- for(var/WorldX = startX, WorldX <= endX, WorldX++)
- for(var/WorldY = startY, WorldY <= endY, WorldY++)
-
- var/atom/Turf = locate(WorldX, WorldY, currentZ)
-
- var/icon/TurfIcon = new(Turf.icon, Turf.icon_state)
- TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
-
- Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE))
-
- count++
-
- if(count % 8000 == 0)
- log_world("NanoMapGen: [count] tiles done")
- sleep(1)
-
- var/mapFilename = "nanomap_z[currentZ]-new.png"
-
- log_world("NanoMapGen: sending [mapFilename] to client")
-
- usr << browse(Tile, "window=picture;file=[mapFilename];display=0")
-
- log_world("NanoMapGen: Done.")
-
- to_chat(usr, "NanoMapGen: Done. File [mapFilename] uploaded to your cache.")
-
- if(Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
- return NANOMAP_BADOUTPUT
-
- return NANOMAP_SUCCESS
diff --git a/code/modules/nano/subsystem.dm b/code/modules/nano/subsystem.dm
index 6ad4201b93d..f2f86c02a3c 100644
--- a/code/modules/nano/subsystem.dm
+++ b/code/modules/nano/subsystem.dm
@@ -163,7 +163,7 @@
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
open_uis[src_object_key] = list(ui.ui_key = list())
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();
+ open_uis[src_object_key][ui.ui_key] = list()
ui.user.open_uis.Add(ui)
var/list/uis = open_uis[src_object_key][ui.ui_key]
diff --git a/code/modules/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm
index 59303b4707c..b9200d0e771 100644
--- a/code/modules/ninja/suit/suit_initialisation.dm
+++ b/code/modules/ninja/suit/suit_initialisation.dm
@@ -14,30 +14,30 @@
suitBusy = 1
if(suitActive && (alert("Confirm suit systems shutdown? This cannot be halted once it has started.", "Confirm Shutdown", "Yes", "No") == "Yes"))
- to_chat(usr, "Now de-initializing...")
+ to_chat(usr, "Now de-initializing...")
sleep(15)
- to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.")
+ to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.")
sleep(10)
- to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.")
+ to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.")
sleep(5)
- to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.")
+ to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.")
//TODO: Shut down any active abilities
sleep(10)
- to_chat(usr, "Disconnecting neural-net interface... Success.")
+ to_chat(usr, "Disconnecting neural-net interface... Success.")
QDEL_NULL(usr.hud_used)
usr.create_mob_hud()
usr.regenerate_icons()
sleep(5)
- to_chat(usr, "Disengaging neural-net interface... Success.")
+ to_chat(usr, "Disengaging neural-net interface... Success.")
sleep(10)
- to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.")
+ to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.")
//TODO: Grant verbs
toggle_suit_lock(usr)
usr.regenerate_icons()
@@ -45,24 +45,24 @@
suitActive = 0
else if(!suitActive) // Activate the suit.
- to_chat(usr, "Now initializing...")
+ to_chat(usr, "Now initializing...")
sleep(15)
- to_chat(usr, "Now establishing neural-net interface...")
+ to_chat(usr, "Now establishing neural-net interface...")
if(usr.mind.special_role != "Ninja")
to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.")
return
sleep(10)
- to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.")
+ to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.")
sleep(10)
- to_chat(usr, "Securing external locking mechanism...")
+ to_chat(usr, "Securing external locking mechanism...")
if(!toggle_suit_lock(usr))
return
sleep(5)
- to_chat(usr, "Suit secured, extending neural-net interface...")
+ to_chat(usr, "Suit secured, extending neural-net interface...")
QDEL_NULL(usr.hud_used)
usr.hud_used = new /datum/hud/human(usr, 'icons/mob/screen_ninja.dmi', "#ffffff", 255)
@@ -71,22 +71,22 @@
usr.regenerate_icons()
sleep(10)
- to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE")
+ to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE")
sleep(5)
- to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.")
+ to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.")
if(suitCell)
- to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].")
+ to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].")
sleep(10)
- to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].")
+ to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].")
//TODO: Grant ninja verbs here.
suitBusy = 0
suitActive = 1
else
suitBusy = 0
- to_chat(usr, "NOTICE: Suit de-activation protocals aborted.")
+ to_chat(usr, "NOTICE: Suit de-activation protocals aborted.")
else
to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.")
return
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 0cb571d576c..45195bb1730 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -231,7 +231,7 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
var/atoms[] = list()
for(var/turf/the_turf in turfs)
// Add ourselves to the list of stuff to draw
- atoms.Add(the_turf);
+ atoms.Add(the_turf)
// As well as anything that isn't invisible.
for(var/atom/A in the_turf)
if(A.invisibility)
diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm
new file mode 100644
index 00000000000..0492d3da694
--- /dev/null
+++ b/code/modules/paperwork/ticketmachine.dm
@@ -0,0 +1,216 @@
+//Bureaucracy machine!
+//Simply set this up in the hopline and you can serve people based on ticket numbers
+
+/obj/machinery/ticket_machine
+ name = "ticket machine"
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "ticketmachine"
+ desc = "A marvel of bureaucratic engineering encased in an efficient plastic shell. It can be refilled with a hand labeler refill roll and linked to buttons with a multitool."
+ density = FALSE
+ anchored = TRUE
+ maptext_height = 26
+ maptext_width = 32
+ maptext_x = 7
+ maptext_y = 10
+ layer = HIGH_OBJ_LAYER
+ var/ticket_number = 0 //Increment the ticket number whenever the HOP presses his button
+ var/current_number = 0 //What ticket number are we currently serving?
+ var/max_number = 100 //At this point, you need to refill it.
+ var/cooldown = 50
+ var/ready = TRUE
+ var/list/ticket_holders = list()
+ var/list/tickets = list()
+ var/id = 1
+
+/obj/machinery/ticket_machine/Destroy()
+ for(var/obj/item/ticket_machine_ticket/ticket in tickets)
+ ticket.visible_message("\the [ticket] disperses!")
+ qdel(ticket)
+ tickets.Cut()
+ return ..()
+
+/obj/machinery/ticket_machine/emag_act(mob/user) //Emag the ticket machine to dispense burning tickets, as well as randomize its number to destroy the HoP's mind.
+ if(emagged)
+ return
+ to_chat(user, "You overload [src]'s bureaucratic logic circuitry to its MAXIMUM setting.")
+ ticket_number = rand(0, max_number)
+ current_number = ticket_number
+ emagged = TRUE
+ for(var/obj/item/ticket_machine_ticket/ticket in tickets)
+ ticket.visible_message("\the [ticket] disperses!")
+ qdel(ticket)
+ tickets.Cut()
+ update_icon()
+
+/obj/machinery/ticket_machine/Initialize(mapload)
+ . = ..()
+ update_icon()
+
+/obj/machinery/ticket_machine/proc/increment()
+ if(current_number > ticket_number)
+ return
+ if(current_number && !(emagged) && tickets[current_number])
+ var/obj/item/ticket_machine_ticket/ticket = tickets[current_number]
+ ticket.audible_message("\the [tickets[current_number]] disperses!")
+ qdel(ticket)
+ if(current_number < ticket_number)
+ current_number ++ //Increment the one we're serving.
+ playsound(src, 'sound/misc/announce_dig.ogg', 50, FALSE)
+ atom_say("Now serving ticket #[current_number]!")
+ if(!(emagged) && tickets[current_number])
+ var/obj/item/ticket_machine_ticket/ticket = tickets[current_number]
+ ticket.audible_message("\the [tickets[current_number]] vibrates!")
+ update_icon() //Update our icon here rather than when they take a ticket to show the current ticket number being served
+
+/obj/machinery/door_control/ticket_machine_button
+ name = "increment ticket counter"
+ desc = "Use this button after you've served someone to tell the next person to come forward."
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "doorctrl0"
+ req_access = list()
+ id = 1
+ var/cooldown = FALSE
+
+
+/obj/machinery/door_control/ticket_machine_button/attack_hand(mob/user)
+ if(allowed(usr) || user.can_advanced_admin_interact())
+ icon_state = "doorctrl1"
+ addtimer(CALLBACK(src, /obj/machinery/door_control/ticket_machine_button/.proc/update_icon), 15)
+ for(var/obj/machinery/ticket_machine/M in GLOB.machines)
+ if(M.id == id)
+ if(cooldown)
+ return
+ cooldown = TRUE
+ M.increment()
+ addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
+ else
+ to_chat(usr, "Access denied.")
+ flick("doorctrl-denied", src)
+
+/obj/machinery/door_control/ticket_machine_button/update_icon()
+ if(!(stat & NOPOWER))
+ icon_state = "doorctrl0"
+
+/obj/machinery/ticket_machine/update_icon()
+ switch(ticket_number) //Gives you an idea of how many tickets are left
+ if(0 to 49)
+ icon_state = "ticketmachine_100"
+ if(50 to 99)
+ icon_state = "ticketmachine_50"
+ if(100)
+ icon_state = "ticketmachine_0"
+ handle_maptext()
+
+/obj/machinery/ticket_machine/proc/handle_maptext()
+ switch(ticket_number) //This is here to handle maptext offsets so that the numbers align.
+ if(0 to 9)
+ maptext_x = 13
+ if(10 to 99)
+ maptext_x = 10
+ if(100)
+ maptext_x = 8
+ maptext = "[ticket_number]"
+
+/obj/machinery/ticket_machine/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/hand_labeler_refill))
+ if(!(ticket_number >= max_number))
+ to_chat(user, "[src] refuses [I]! There [max_number-ticket_number==1 ? "is" : "are"] still [max_number-ticket_number] ticket\s left!")
+ return
+ to_chat(user, "You start to refill [src]'s ticket holder (doing this will reset its ticket count!).")
+ if(do_after(user, 30, target = src))
+ to_chat(user, "You insert [I] into [src] as it whirs nondescriptly.")
+ user.drop_item()
+ qdel(I)
+ ticket_number = 0
+ current_number = 0
+ for(var/obj/item/ticket_machine_ticket/ticket in tickets)
+ ticket.audible_message("\the [ticket] disperses!")
+ qdel(ticket)
+ tickets.Cut()
+ max_number = initial(max_number)
+ update_icon()
+ return
+ else
+ return ..()
+
+/obj/machinery/ticket_machine/proc/reset_cooldown()
+ ready = TRUE
+
+/obj/machinery/ticket_machine/attack_hand(mob/living/carbon/user)
+ . = ..()
+ if(!ready)
+ to_chat(user,"You press the button, but nothing happens...")
+ return
+ if(ticket_number >= max_number)
+ to_chat(user,"Ticket supply depleted, please refill this unit with a hand labeller refill cartridge!")
+ return
+ if((user.UID() in ticket_holders) && !(emagged))
+ to_chat(user, "You already have a ticket!")
+ return
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 100, FALSE)
+ ticket_number ++
+ to_chat(user, "You take a ticket from [src], looks like you're ticket number #[ticket_number]...")
+ var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src))
+ theirticket.name = "Ticket #[ticket_number]"
+ theirticket.maptext = "[ticket_number]"
+ theirticket.saved_maptext = "[ticket_number]"
+ theirticket.ticket_number = ticket_number
+ theirticket.source = src
+ theirticket.owner = user.UID()
+ user.put_in_hands(theirticket)
+ ticket_holders += user.UID()
+ tickets += theirticket
+ if(emagged) //Emag the machine to destroy the HOP's life.
+ ready = FALSE
+ addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown)//Small cooldown to prevent piles of flaming tickets
+ theirticket.fire_act()
+ user.drop_item()
+ user.adjust_fire_stacks(1)
+ user.IgniteMob()
+
+// Stop AI penetrating the bureaucracy
+/obj/machinery/ticket_machine/attack_ai(mob/user)
+ return
+
+/obj/item/ticket_machine_ticket
+ name = "Ticket"
+ desc = "A ticket which shows your place in the Head of Personnel's line. Made from Nanotrasen patented NanoPaper. Though solid, its form seems to shimmer slightly. Feels (and burns) just like the real thing."
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "ticket"
+ maptext_x = 7
+ maptext_y = 10
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+ max_integrity = 50
+ var/saved_maptext = null
+ var/owner //soft ref of the ticket owner's UID()
+ var/obj/machinery/ticket_machine/source
+ var/ticket_number
+
+/obj/item/ticket_machine_ticket/attack_hand(mob/user)
+ . = ..()
+ maptext = saved_maptext //For some reason, storage code removes all maptext off objs, this stops its number from being wiped off when taken out of storage.
+
+/obj/item/ticket_machine_ticket/attackby(obj/item/P, mob/living/carbon/human/user, params) //Stolen from papercode
+ ..()
+ if(is_hot(P))
+ if((CLUMSY in user.mutations) && prob(10))
+ user.visible_message("[user] accidentally ignites [user.p_them()]self!", \
+ "You miss the paper and accidentally light yourself on fire!")
+ user.drop_item()
+ user.adjust_fire_stacks(1)
+ user.IgniteMob()
+ return
+ user.visible_message("[user] lights [src] ablaze with [P]!", "You light [src] on fire!")
+ fire_act()
+
+/obj/item/paper/extinguish()
+ ..()
+ update_icon()
+
+/obj/item/ticket_machine_ticket/Destroy()
+ if(owner && source)
+ source.ticket_holders -= owner
+ source.tickets[ticket_number] = null
+ source = null
+ return ..()
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index f613f34a8fd..409abe3d724 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -70,7 +70,6 @@
var/locked = 1
var/coverlocked = 1
var/aidisabled = 0
- var/tdir = null
var/obj/machinery/power/terminal/terminal = null
var/lastused_light = 0
var/lastused_equip = 0
@@ -155,15 +154,12 @@
GLOB.apcs = sortAtom(GLOB.apcs)
wires = new(src)
- // offset 24 pixels in direction of dir
- // this allows the APC to be embedded in a wall, yet still inside an area
- if(building)
- setDir(direction) // We set this to direction only for pixel location determination.
-
- set_pixel_offsets_from_dir(24, -24, 24, -24) // Set pixel offsets based on `dir`
- setDir(SOUTH) // APC's should always appear to *face* south.
if(building)
+ // Offset 24 pixels in direction of dir. This allows the APC to be embedded in a wall, yet still inside an area
+ setDir(direction) // This is only used for pixel offsets, and later terminal placement. APC dir doesn't affect its sprite since it only has one orientation.
+ set_pixel_offsets_from_dir(24, -24, 24, -24)
+
area = get_area(src)
area.apc |= src
opened = 1
@@ -193,8 +189,8 @@
/obj/machinery/power/apc/proc/make_terminal()
// create a terminal object at the same position as original turf loc
// wires will attach to this
- terminal = new/obj/machinery/power/terminal(src.loc)
- terminal.setDir(tdir)
+ terminal = new/obj/machinery/power/terminal(get_turf(src))
+ terminal.setDir(dir)
terminal.master = src
/obj/machinery/power/apc/Initialize(mapload)
@@ -1056,7 +1052,8 @@
occupier.eyeobj.name = "[occupier.name] (AI Eye)"
if(malf.parent)
qdel(malf)
- occupier.verbs += /mob/living/silicon/ai/proc/corereturn
+ var/datum/action/innate/ai/return_to_core/R = new
+ R.Grant(occupier)
occupier.cancel_camera()
if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking)
for(var/obj/item/pinpointer/point in GLOB.pinpointer_list)
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index b6c65d979cf..992020e7090 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -229,14 +229,15 @@ By design, d1 is the smallest direction and d2 is the highest
if(current_size >= STAGE_FIVE)
deconstruct()
-obj/structure/cable/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- color = color_rainbow()
- else
- color = colorC
+obj/structure/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 = "#DD0000"
+ color = colorC
/obj/structure/cable/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
@@ -845,13 +846,15 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
..()
-/obj/item/stack/cable_coil/proc/cable_color(var/colorC)
- if(colorC)
- if(colorC == "rainbow")
- colorC = color_rainbow()
- color = colorC
- else
+/obj/item/stack/cable_coil/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/stack/cable_coil/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 0a31d882488..df457111704 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -627,13 +627,19 @@
/obj/item/light/Crossed(mob/living/L)
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)
if(status == LIGHT_BURNED || status == LIGHT_OK)
shatter()
return ..()
+/obj/item/light/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ C.stored_comms["glass"] += 1
+ C.stored_comms["metal"] += 1
+ qdel(src)
+ return TRUE
+
/obj/item/light/tube
name = "light tube"
desc = "A replacement light tube."
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 80b10ab3a8b..bead9c935ef 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -127,6 +127,8 @@
/obj/singularity/narsie/proc/acquire(var/mob/food)
if(food == target)
return
+ if(!target)
+ return
to_chat(target, "[uppertext(SSticker.cultdat.entity_name)] HAS LOST INTEREST IN YOU")
target = food
if(ishuman(target))
diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm
index c8964c7ec34..621c5f02747 100644
--- a/code/modules/power/tracker.dm
+++ b/code/modules/power/tracker.dm
@@ -29,10 +29,10 @@
//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST
/obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC)
if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST))
- return 0
+ return FALSE
control = SC
SC.connected_tracker = src
- return 1
+ return TRUE
//set the control of the tracker to null and removes it from the previous control computer if needed
/obj/machinery/power/tracker/proc/unset_control()
@@ -44,7 +44,7 @@
if(!S)
S = new /obj/item/solar_assembly(src)
S.glass_type = /obj/item/stack/sheet/glass
- S.tracker = 1
+ S.tracker = TRUE
S.anchored = TRUE
S.forceMove(src)
update_icon()
@@ -63,7 +63,7 @@
. = TRUE
if(!I.tool_use_check(user, 0))
return
- playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
+ playsound(loc, 'sound/machines/click.ogg', 50, 1)
user.visible_message("[user] begins to take the glass off the solar tracker.")
if(I.use_tool(src, user, 50, volume = I.tool_volume))
user.visible_message("[user] takes the glass off the tracker.")
@@ -76,7 +76,7 @@
stat |= BROKEN
unset_control()
-/obj/machinery/power/solar/deconstruct(disassembled = TRUE)
+/obj/machinery/power/tracker/deconstruct(disassembled = TRUE)
if(!(flags & NODECONSTRUCT))
if(disassembled)
var/obj/item/solar_assembly/S = locate() in src
@@ -85,8 +85,8 @@
S.give_glass(stat & BROKEN)
else
playsound(src, "shatter", 70, TRUE)
- new /obj/item/shard(src.loc)
- new /obj/item/shard(src.loc)
+ new /obj/item/shard(loc)
+ new /obj/item/shard(loc)
qdel(src)
// Tracker Electronic
diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm
index 8cb48f8e36a..c59e5890331 100644
--- a/code/modules/projectiles/ammunition/magazines.dm
+++ b/code/modules/projectiles/ammunition/magazines.dm
@@ -133,6 +133,9 @@
/obj/item/ammo_box/magazine/internal/shot/riot/short
max_ammo = 3
+/obj/item/ammo_box/magazine/internal/shot/riot/buckshot
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
/obj/item/ammo_box/magazine/internal/grenadelauncher
name = "grenade launcher internal magazine"
ammo_type = /obj/item/ammo_casing/a40mm
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 411a460ac62..3e41bf81271 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -387,10 +387,6 @@
toggle_gunlight()
visible_message("[src]'s light fades and turns off.")
-/obj/item/gun/pickup(mob/user)
- . = ..()
- if(azoom)
- azoom.Grant(user)
/obj/item/gun/dropped(mob/user)
..()
@@ -522,3 +518,27 @@
if(zoomable)
azoom = new()
azoom.gun = src
+ RegisterSignal(src, COMSIG_ITEM_EQUIPPED, .proc/ZoomGrantCheck)
+
+/**
+ * Proc which will be called when the gun receives the `COMSIG_ITEM_EQUIPPED` signal.
+ *
+ * This happens if the mob picks up the gun, or equips it to any of their slots.
+ * If the slot is anything other than either of their hands (such as the back slot), un-zoom them, and `Remove` the zoom action button from the mob.
+ * Otherwise, `Grant` the mob the zoom action button.
+ *
+ * Arguments:
+ * * source - the gun that got equipped, which is `src`.
+ * * user - the mob equipping the gun.
+ * * slot - the slot the gun is getting equipped to.
+ */
+/obj/item/gun/proc/ZoomGrantCheck(datum/source, mob/user, slot)
+ // Checks if the gun got equipped into either of the user's hands.
+ if(slot != slot_r_hand && slot != slot_l_hand)
+ // If its not in their hands, un-zoom, and remove the zoom action button.
+ zoom(user, FALSE)
+ azoom.Remove(user)
+ return FALSE
+
+ // The gun is equipped in their hands, give them the zoom ability.
+ azoom.Grant(user)
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index ce0abc8cc69..695ab02c43b 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -15,7 +15,6 @@
ammo_x_offset = 2
var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail
var/selfcharge = 0
- var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically
var/charge_tick = 0
var/charge_delay = 4
@@ -68,11 +67,6 @@
charge_tick = 0
if(!cell)
return // check if we actually need to recharge
- var/obj/item/ammo_casing/energy/E = ammo_type[select]
- if(use_external_power)
- var/obj/item/stock_parts/cell/external = get_external_cell()
- if(!external || !external.use(E.e_cost)) //Take power from the borg...
- return //Note, uses /10 because of shitty mods to the cell system
cell.give(100) //... to recharge the shot
on_recharge()
update_icon()
@@ -207,14 +201,3 @@
var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot
if(R.cell.use(shot.e_cost)) //Take power from the borg...
cell.give(shot.e_cost) //... to recharge the shot
-
-/obj/item/gun/energy/proc/get_external_cell()
- if(istype(loc, /obj/item/rig_module))
- var/obj/item/rig_module/module = loc
- if(module.holder && module.holder.wearer)
- var/mob/living/carbon/human/H = module.holder.wearer
- if(istype(H) && H.back)
- var/obj/item/rig/suit = H.back
- if(istype(suit))
- return suit.cell
- return null
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 3e9c882458e..9106d80ef98 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -95,12 +95,6 @@
..()
damage = min(damage+7, 100)
-/obj/item/gun/energy/lasercannon/mounted
- name = "mounted laser cannon"
- selfcharge = 1
- use_external_power = 1
- charge_delay = 10
-
/obj/item/gun/energy/lasercannon/cyborg
/obj/item/gun/energy/lasercannon/cyborg/newshot()
diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm
index 2df2d877c53..3d9925da974 100644
--- a/code/modules/projectiles/guns/energy/nuclear.dm
+++ b/code/modules/projectiles/guns/energy/nuclear.dm
@@ -21,11 +21,6 @@
/obj/item/gun/energy/gun/cyborg/emp_act()
return
-/obj/item/gun/energy/gun/mounted
- name = "mounted energy gun"
- selfcharge = 1
- use_external_power = 1
-
/obj/item/gun/energy/gun/mini
name = "miniature energy gun"
desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill."
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index e64aced32f9..da72c971130 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -112,10 +112,6 @@
max_mod_capacity = 0
empty_state = null
-/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja
- name = "energy dart thrower"
- ammo_type = list(/obj/item/ammo_casing/energy/dart)
-
/obj/item/gun/energy/kinetic_accelerator/crossbow/large
name = "energy crossbow"
desc = "A reverse engineered weapon using syndicate technology."
@@ -312,6 +308,8 @@
icon_state = "esniper"
origin_tech = "combat=6;materials=5;powerstorage=4"
ammo_type = list(/obj/item/ammo_casing/energy/sniper)
+ item_state = null
+ weapon_weight = WEAPON_HEAVY
slot_flags = SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
zoomable = TRUE
@@ -422,7 +420,7 @@
update_icon()
if(istype(loc, /mob/living/carbon))
- var /mob/living/carbon/M = loc
+ var/mob/living/carbon/M = loc
if(src == M.machine)
update_dat()
M << browse("Temperature Gun Configuration [dat]", "window=tempgun;size=510x102")
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 70bb8be7e46..141366dd5d9 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -7,11 +7,6 @@
ammo_type = list(/obj/item/ammo_casing/energy/electrode)
ammo_x_offset = 3
-/obj/item/gun/energy/taser/mounted
- name = "mounted taser gun"
- selfcharge = 1
- use_external_power = 1
-
/obj/item/gun/energy/shock_revolver
name = "tesla revolver"
desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers."
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index 9196596c8b5..72d128178a6 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -58,8 +58,6 @@
ammo_type = /obj/item/ammo_casing/magic/slipping
icon_state = "staffofslipping"
item_state = "staffofslipping"
- max_charges = 10
- recharge_rate = 2
fire_sound = 'sound/items/bikehorn.ogg'
/obj/item/gun/magic/staff/slipping/honkmother
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index 25db1f10dcb..ca4499c8832 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -95,6 +95,7 @@
if(!user.unEquip(A))
return
to_chat(user, "You screw [S] onto [src].")
+ playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
suppressed = A
S.oldsound = fire_sound
S.initial_w_class = w_class
@@ -120,6 +121,7 @@
..()
return
to_chat(user, "You unscrew [suppressed] from [src].")
+ playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
user.put_in_hands(suppressed)
fire_sound = S.oldsound
w_class = S.initial_w_class
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index 17a55722d2c..6c11fda5fd7 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -197,6 +197,8 @@
..()
post_sawoff()
+/obj/item/gun/projectile/shotgun/riot/buckshot //comes pre-loaded with buckshot rather than rubber
+ mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/buckshot
///////////////////////
diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/syringe_gun.dm
index 04cf2d248d3..467cd62468e 100644
--- a/code/modules/projectiles/guns/syringe_gun.dm
+++ b/code/modules/projectiles/guns/syringe_gun.dm
@@ -18,62 +18,62 @@
..()
chambered = new /obj/item/ammo_casing/syringegun(src)
-/obj/item/gun/syringe/newshot()
- if(!syringes.len)
+/obj/item/gun/syringe/process_chamber()
+ if(!length(syringes) || chambered.BB)
return
var/obj/item/reagent_containers/syringe/S = syringes[1]
-
if(!S)
return
- chambered.BB = new S.projectile_type (src)
-
+ chambered.BB = new S.projectile_type(src)
S.reagents.trans_to(chambered.BB, S.reagents.total_volume)
chambered.BB.name = S.name
+
syringes.Remove(S)
-
qdel(S)
- return
-/obj/item/gun/syringe/process_chamber()
- return
-
-/obj/item/gun/syringe/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, params)
+/obj/item/gun/syringe/afterattack(atom/target, mob/living/user, flag, params)
if(target == loc)
return
- newshot()
..()
/obj/item/gun/syringe/examine(mob/user)
. = ..()
- . += "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining."
+ var/num_syringes = syringes.len + (chambered.BB ? 1 : 0)
+ . += "Can hold [max_syringes] syringe\s. Has [num_syringes] syringe\s remaining."
-/obj/item/gun/syringe/attack_self(mob/living/user as mob)
- if(!syringes.len)
+/obj/item/gun/syringe/attack_self(mob/living/user)
+ if(!length(syringes) && !chambered.BB)
to_chat(user, "[src] is empty.")
- return 0
+ return FALSE
- var/obj/item/reagent_containers/syringe/S = syringes[syringes.len]
-
- if(!S)
- return 0
- S.loc = user.loc
+ var/obj/item/reagent_containers/syringe/S
+ if(chambered.BB) // Remove the chambered syringe first
+ S = new()
+ chambered.BB.reagents.trans_to(S, chambered.BB.reagents.total_volume)
+ qdel(chambered.BB)
+ chambered.BB = null
+ else
+ S = syringes[length(syringes)]
+ user.put_in_hands(S)
syringes.Remove(S)
+ process_chamber()
to_chat(user, "You unload [S] from \the [src]!")
+ return TRUE
- return 1
-
-/obj/item/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = 1)
+/obj/item/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
if(istype(A, /obj/item/reagent_containers/syringe))
- if(syringes.len < max_syringes)
+ var/in_clip = length(syringes) + (chambered.BB ? 1 : 0)
+ if(in_clip < max_syringes)
if(!user.unEquip(A))
return
to_chat(user, "You load [A] into \the [src]!")
syringes.Add(A)
A.loc = src
- return 1
+ process_chamber() // Chamber the syringe if none is already
+ return TRUE
else
to_chat(user, "[src] cannot hold more syringes.")
else
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index c10d8dec600..c33c649ae77 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -247,8 +247,8 @@
M.Turn(Angle)
transform = M
- var/Pixel_x = round(sin(Angle) + 16 * sin(Angle) * 2)
- var/Pixel_y = round(cos(Angle) + 16 * cos(Angle) * 2)
+ var/Pixel_x = round(sin(Angle) + 16 * sin(Angle) * 2, 1)
+ var/Pixel_y = round(cos(Angle) + 16 * cos(Angle) * 2, 1)
var/pixel_x_offset = pixel_x + Pixel_x
var/pixel_y_offset = pixel_y + Pixel_y
var/new_x = x
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 4505d984f84..080114554bb 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -196,7 +196,7 @@
var/mob/living/silicon/robot/Robot = new_mob
Robot.mmi = new /obj/item/mmi(new_mob)
Robot.lawupdate = FALSE
- Robot.connected_ai = null
+ Robot.disconnect_from_ai()
Robot.clear_inherent_laws()
Robot.clear_zeroth_law()
if(ishuman(M))
@@ -270,7 +270,6 @@
M.mind.transfer_to(new_mob)
else
new_mob.attack_log_old = M.attack_log_old.Copy()
- new_mob.logs = M.logs.Copy()
new_mob.key = M.key
to_chat(new_mob, "Your form morphs into that of a [randomize].")
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index cf7ff936b6d..4d1f577969c 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -185,8 +185,8 @@
color = COLOR_PALE_BTL_GREEN
if("Orange wrapper")
color = COLOR_ORANGE
- loaded_pill_bottle.wrapper_color = color;
- loaded_pill_bottle.apply_wrap();
+ loaded_pill_bottle.wrapper_color = color
+ loaded_pill_bottle.apply_wrap()
else if(href_list["close"])
usr << browse(null, "window=chem_master")
onclose(usr, "chem_master")
diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm
index dec256ac455..b015b75be2a 100644
--- a/code/modules/reagents/chemistry/reagents/toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/toxins.dm
@@ -226,37 +226,6 @@
if(B && islist(B.data) && !data)
data = B.data.Copy()
-/datum/reagent/romerol
- name = "romerol"
- // the REAL zombie powder
- id = "romerol"
- description = "Romerol is a highly experimental bioterror agent \
- which causes dormant nodules to be etched into the grey matter of \
- the subject. These nodules only become active upon death of the \
- host, upon which, the secondary structures activate and take control \
- of the host body."
- color = "#123524" // RGB (18, 53, 36)
- metabolization_rate = INFINITY
- can_synth = FALSE
- taste_description = "CAAAARL"
-
-/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method = REAGENT_TOUCH, volume)
- if(!istype(H))
- return
- // Silently add the zombie infection organ to be activated upon death
- if(!H.get_organ_slot("zombie_infection"))
- var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new()
- ZI.insert(H)
- ..()
-
-/datum/reagent/romerol/on_mob_life(mob/living/M)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(!H.get_organ_slot("zombie_infection"))
- var/obj/item/organ/internal/zombie_infection/nodamage/ZI = new()
- ZI.insert(H)
- return ..()
-
/datum/reagent/uranium
name ="Uranium"
id = "uranium"
@@ -1268,6 +1237,7 @@
description = "An advanced corruptive toxin produced by something terrible."
reagent_state = LIQUID
color = "#5EFF3B" //RGB: 94, 255, 59
+ can_synth = FALSE
taste_description = "decay"
/datum/reagent/gluttonytoxin/reaction_mob(mob/living/L, method=REAGENT_TOUCH, reac_volume)
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index f2740676314..abdf0305934 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -56,7 +56,6 @@
/datum/chemical_reaction/nitroglycerin
name = "Nitroglycerin"
id = "nitroglycerin"
- result = "nitroglycerin"
required_reagents = list("glycerol" = 1, "facid" = 1, "sacid" = 1)
result_amount = 2
diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
index b1fb33edf85..1525ed42bd3 100644
--- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm
+++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
@@ -38,7 +38,7 @@
/datum/chemical_reaction/slimemonkey/on_reaction(datum/reagents/holder)
feedback_add_details("slime_cores_used","[type]")
for(var/i = 1, i <= 3, i++)
- var /obj/item/reagent_containers/food/snacks/monkeycube/M = new /obj/item/reagent_containers/food/snacks/monkeycube
+ var/obj/item/reagent_containers/food/snacks/monkeycube/M = new /obj/item/reagent_containers/food/snacks/monkeycube
M.forceMove(get_turf(holder.my_atom))
//Green
diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm
index 73c3c3759b1..87be22d3c8f 100644
--- a/code/modules/reagents/reagent_containers/applicator.dm
+++ b/code/modules/reagents/reagent_containers/applicator.dm
@@ -5,6 +5,7 @@
icon_state = "mender"
item_state = "mender"
volume = 200
+ possible_transfer_amounts = null
resistance_flags = ACID_PROOF
container_type = REFILLABLE | AMOUNT_VISIBLE
temperature_min = 270
@@ -21,6 +22,9 @@
ignore_flags = TRUE
to_chat(user, "You short out the safeties on [src].")
+/obj/item/reagent_containers/applicator/set_APTFT()
+ set hidden = TRUE
+
/obj/item/reagent_containers/applicator/on_reagent_change()
if(!emagged)
var/found_forbidden_reagent = FALSE
@@ -93,6 +97,20 @@
playsound(get_turf(src), pick('sound/goonstation/items/mender.ogg', 'sound/goonstation/items/mender2.ogg'), 50, 1)
+/obj/item/reagent_containers/applicator/verb/empty()
+ set name = "Empty Applicator"
+ set category = "Object"
+ set src in usr
+
+ if(usr.incapacitated())
+ return
+ if(alert(usr, "Are you sure you want to empty [src]?", "Empty Applicator:", "Yes", "No") != "Yes")
+ return
+ if(!usr.incapacitated() && isturf(usr.loc) && loc == usr)
+ to_chat(usr, "You empty [src] onto the floor.")
+ reagents.reaction(usr.loc)
+ reagents.clear_reagents()
+
/obj/item/reagent_containers/applicator/brute
name = "brute auto-mender"
list_reagents = list("styptic_powder" = 200)
@@ -104,3 +122,6 @@
/obj/item/reagent_containers/applicator/dual
name = "dual auto-mender"
list_reagents = list("synthflesh" = 200)
+
+/obj/item/reagent_containers/applicator/dual/syndi // It magically goes through hardsuits. Don't ask how.
+ ignore_flags = TRUE
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index 61552eae789..4274c3114db 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -12,11 +12,6 @@
container_type = OPENCONTAINER
volume = 30
-/obj/item/reagent_containers/glass/bottle/romerol
- name = "romerol bottle"
- desc = "A small bottle of Romerol. The REAL zombie powder."
- list_reagents = list("romerol" = 30)
-
/obj/item/reagent_containers/glass/bottle/on_reagent_change()
update_icon()
@@ -47,6 +42,13 @@
var/image/lid = image(icon, src, "lid_bottle")
overlays += lid
+/obj/item/reagent_containers/glass/bottle/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(!reagents.total_volume)
+ C.stored_comms["glass"] += 3
+ qdel(src)
+ return TRUE
+ return ..()
+
/obj/item/reagent_containers/glass/bottle/toxin
name = "toxin bottle"
desc = "A small bottle containing toxic compounds."
diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm
index 1e42899111b..175ae757df4 100644
--- a/code/modules/reagents/reagent_containers/glass_containers.dm
+++ b/code/modules/reagents/reagent_containers/glass_containers.dm
@@ -47,6 +47,9 @@
reagents.reaction(M, REAGENT_TOUCH)
reagents.clear_reagents()
else
+ if(!iscarbon(M)) // Non-carbons can't process reagents
+ to_chat(user, "You cannot find a way to feed [M].")
+ return
if(M != user)
M.visible_message("[user] attempts to feed something to [M].", \
"[user] attempts to feed something to you.")
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 8783d8a8c19..6e0f4277bde 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -24,6 +24,9 @@
mode = SYRINGE_INJECT
update_icon()
+/obj/item/reagent_containers/syringe/set_APTFT()
+ set hidden = TRUE
+
/obj/item/reagent_containers/syringe/on_reagent_change()
update_icon()
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index 13d88100042..e6dd04408c2 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -265,7 +265,7 @@
update()
return
-/obj/machinery/disposal/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)
+/obj/machinery/disposal/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, "DisposalBin", name, 300, 250, master_ui, state)
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index ecf6b9887fc..e678ec43398 100755
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -161,13 +161,13 @@
amount = 25
max_amount = 25
resistance_flags = FLAMMABLE
+ var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag, /obj/item/twohanded/required)
/obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity)
if(!proximity) return
if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete
return
- if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \
- || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag))
+ if(is_type_in_list(target, no_wrap))
return
if(target.anchored)
return
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 1a57f2e2aee..e813597c4d8 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -195,6 +195,14 @@
build_path = /obj/item/floor_painter
category = list("initial", "Miscellaneous")
+/datum/design/airlock_painter
+ name = "Airlock painter"
+ id = "airlock_painter"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 3000, MAT_GLASS = 1000)
+ build_path = /obj/item/airlock_painter
+ category = list("initial", "Miscellaneous")
+
/datum/design/metal
name = "Metal"
id = "metal"
@@ -535,6 +543,14 @@
build_path = /obj/item/assembly/health
category = list("initial", "Medical")
+/datum/design/stethoscope
+ name = "Stethoscope"
+ id = "stethoscope"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 500)
+ build_path = /obj/item/clothing/accessory/stethoscope
+ category = list("initial", "Medical")
+
/datum/design/timer
name = "Timer"
id = "timer"
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 76eabe46761..75e64a43215 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -751,7 +751,7 @@
category = list("Exosuit Equipment")
/datum/design/mech_mining_scanner
- name = "Exosuit Engineering Equipement (Mining Scanner)"
+ name = "Exosuit Engineering Equipment (Mining Scanner)"
id = "mech_mscanner"
build_type = MECHFAB
build_path = /obj/item/mecha_parts/mecha_equipment/mining_scanner
@@ -948,8 +948,8 @@
category = list("Exosuit Equipment")
/datum/design/mech_grenade_launcher
- name = "Exosuit Weapon (SGL-6 Grenade Launcher)"
- desc = "Allows for the construction of SGL-6 Grenade Launcher."
+ name = "Exosuit Weapon (SGL-6 Flashbang Launcher)"
+ desc = "Allows for the construction of SGL-6 Flashbang Launcher."
id = "mech_grenade_launcher"
build_type = MECHFAB
req_tech = list("combat" = 4, "engineering" = 4)
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index e3bbb2f4aec..3dabea7dc6b 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -371,7 +371,7 @@
name = "X-Ray implant"
desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile."
id = "ci-xray"
- req_tech = list("materials" = 7, "programming" = 5, "biotech" = 7, "magnets" = 5,"plasmatech" = 6)
+ req_tech = list("materials" = 7, "programming" = 5, "biotech" = 8, "magnets" = 5,"plasmatech" = 6)
build_type = PROTOLATHE | MECHFAB
construction_time = 60
materials = list(MAT_METAL = 600, MAT_GLASS = 600, MAT_SILVER = 600, MAT_GOLD = 600, MAT_PLASMA = 1000, MAT_URANIUM = 1000, MAT_DIAMOND = 1000, MAT_BLUESPACE = 1000)
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index caa273594de..43cc010ac1c 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -291,8 +291,7 @@
for(var/turf/T in oview(1, src))
if(!T.density)
if(prob(EFFECT_PROB_VERYHIGH))
- var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
- reagentdecal.reagents.add_reagent("radium", 7)
+ new /obj/effect/decal/cleanable/greenglow(T)
if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
var/savedName = "[exp_on]"
ejectItem(TRUE)
diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm
index 91306d910f1..3fe0ef54c0c 100644
--- a/code/modules/research/message_server.dm
+++ b/code/modules/research/message_server.dm
@@ -305,6 +305,11 @@ GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder)
//This proc is only to be called at round end.
/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql()
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Blackbox seal blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to seal the blackbox via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to seal the blackbox via advanced proc-call")
+ return
if(!feedback) return
round_end_data_gathering() //round_end time logging and some other data processing
@@ -331,6 +336,11 @@ GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder)
proc/feedback_set(var/variable,var/value)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Feedback edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ return
if(!GLOB.blackbox) return
variable = sanitizeSQL(variable)
@@ -342,6 +352,11 @@ proc/feedback_set(var/variable,var/value)
FV.set_value(value)
proc/feedback_inc(var/variable,var/value)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Feedback edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ return
if(!GLOB.blackbox) return
variable = sanitizeSQL(variable)
@@ -353,6 +368,11 @@ proc/feedback_inc(var/variable,var/value)
FV.inc(value)
proc/feedback_dec(var/variable,var/value)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Feedback edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ return
if(!GLOB.blackbox) return
variable = sanitizeSQL(variable)
@@ -364,6 +384,11 @@ proc/feedback_dec(var/variable,var/value)
FV.dec(value)
proc/feedback_set_details(var/variable,var/details)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Feedback edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ return
if(!GLOB.blackbox) return
variable = sanitizeSQL(variable)
@@ -376,6 +401,11 @@ proc/feedback_set_details(var/variable,var/details)
FV.set_details(details)
proc/feedback_add_details(var/variable,var/details)
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Feedback edit blocked: Advanced ProcCall detected.")
+ message_admins("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ log_admin("[key_name(usr)] attempted to edit feedback data via advanced proc-call")
+ return
if(!GLOB.blackbox) return
variable = sanitizeSQL(variable)
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 7c35730f4aa..ac7249a8b63 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -198,7 +198,7 @@
being_used = 1
var/ghostmsg = "Play as [SM.name], pet of [user.name]?"
- var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 0, 100)
+ var/list/candidates = SSghost_spawns.poll_candidates(ghostmsg, ROLE_SENTIENT, FALSE, 10 SECONDS, source = M)
if(!src)
return
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index a25be3814a7..f2d559e8b1f 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -63,7 +63,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
GLOB.active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots)
GLOB.send_emergency_team = TRUE
- var/list/ert_candidates = shuffle(pollCandidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 600, 1, GLOB.role_playtime_requirements[ROLE_ERT]))
+ var/list/ert_candidates = shuffle(SSghost_spawns.poll_candidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_ERT]))
if(!ert_candidates.len)
GLOB.active_team.cannot_send_team()
GLOB.send_emergency_team = FALSE
diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm
index 00c575e53b0..71fd1750902 100644
--- a/code/modules/security_levels/security levels.dm
+++ b/code/modules/security_levels/security levels.dm
@@ -123,10 +123,6 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
FA.overlays.Cut()
FA.overlays += image('icons/obj/monitors.dmi', "overlay_delta")
- if(level >= SEC_LEVEL_RED)
- GLOB.atc.reroute_traffic(yes = TRUE) // Tell them fuck off we're busy.
- else
- GLOB.atc.reroute_traffic(yes = FALSE)
SSnightshift.check_nightshift(TRUE)
else
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index f82598b1e14..aa0a21a165a 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -150,11 +150,6 @@
emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]")
- if(reason == "Automatic Crew Transfer" && signalOrigin == null) // Best way we have to check that it's actually a crew transfer and not just a player using the same message- any other calls to this proc should have a signalOrigin.
- GLOB.atc.shift_ending()
- else // Emergency shuttle call (probably)
- GLOB.atc.reroute_traffic(yes = TRUE)
-
/obj/docking_port/mobile/emergency/cancel(area/signalOrigin)
if(!canRecall)
@@ -178,8 +173,6 @@
continue
if(player.stat == DEAD) // Corpses
continue
- if(iszombie(player)) // Walking corpses
- continue
if(issilicon(player)) //Borgs are technically dead anyways
continue
if(isanimal(player)) //Poly does not own the shuttle
@@ -299,7 +292,8 @@
timer = 0
open_dock()
-/obj/docking_port/mobile/emergency/proc/open_dock();
+/obj/docking_port/mobile/emergency/proc/open_dock()
+ pass()
/*
for(var/obj/machinery/door/poddoor/shuttledock/D in airlocks)
var/turf/T = get_step(D, D.checkdir)
diff --git a/code/modules/shuttle/ert.dm b/code/modules/shuttle/ert.dm
index 356ca9687dc..5168ce89278 100644
--- a/code/modules/shuttle/ert.dm
+++ b/code/modules/shuttle/ert.dm
@@ -9,10 +9,10 @@
/obj/machinery/computer/shuttle/ert/Topic(href, href_list)
if(href_list["move"])
var/authorized_roles = list(SPECIAL_ROLE_ERT, SPECIAL_ROLE_DEATHSQUAD)
- if(!((usr.mind.assigned_role in authorized_roles) || is_admin(usr)))
+ if(!((usr.mind?.assigned_role in authorized_roles) || is_admin(usr)))
message_admins("Potential ERT shuttle hijack, ERT shuttle moved by unauthorized user: [key_name_admin(usr)]")
..()
-
+
/obj/machinery/computer/camera_advanced/shuttle_docker/ert
name = "specops navigation computer"
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index d6645ce6f2b..cfd3ab8e6f2 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -160,6 +160,12 @@
var/lock_shuttle_doors = 0
+// Preset for adding whiteship docks to ruins. Has widths preset which will auto-assign the shuttle
+/obj/docking_port/stationary/whiteship
+ dwidth = 10
+ height = 35
+ width = 21
+
/obj/docking_port/stationary/register()
if(!SSshuttle)
throw EXCEPTION("docking port [src] could not initialize.")
@@ -863,7 +869,16 @@
desc = "Used to control the White Ship."
circuit = /obj/item/circuitboard/white_ship
shuttleId = "whiteship"
- possible_destinations = "whiteship_away;whiteship_home"
+ possible_destinations = null // Set at runtime
+
+/obj/machinery/computer/shuttle/white_ship/Initialize(mapload)
+ if(mapload)
+ return INITIALIZE_HINT_LATELOAD
+ return ..()
+
+// Yes. This is disgusting, but the console needs to be loaded AFTER the docking ports load.
+/obj/machinery/computer/shuttle/white_ship/LateInitialize()
+ Initialize()
/obj/machinery/computer/shuttle/engineering
name = "Engineering Shuttle Console"
diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm
index 1d88f70454f..6aa3fa802a7 100644
--- a/code/modules/space_management/level_traits.dm
+++ b/code/modules/space_management/level_traits.dm
@@ -61,3 +61,16 @@ GLOBAL_LIST_INIT(default_map_traits, MAP_TRANSITION_CONFIG)
/proc/level_name_to_num(name)
var/datum/space_level/S = GLOB.space_manager.get_zlev_by_name(name)
return S.zpos
+
+/**
+ * Proc to get a list of all the linked-together Z-Levels
+ *
+ * Returns a list of zlevel numbers which can be accessed from travelling space naturally
+ */
+/proc/get_all_linked_levels_zpos()
+ var/list/znums = list()
+ for(var/i in GLOB.space_manager.z_list)
+ var/datum/space_level/SL = GLOB.space_manager.z_list[i]
+ if(SL.linkage == CROSSLINKED)
+ znums |= SL.zpos
+ return znums
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index db801fa5d0f..033cd9fdf94 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -7,7 +7,7 @@
#define VAULT_NOBREATH "Lung Enhancement"
#define VAULT_FIREPROOF "Thermal Regulation"
#define VAULT_STUNTIME "Neural Repathing"
-#define VAULT_ARMOUR "Bone Reinforcement"
+#define VAULT_ARMOUR "Hardened Skin"
#define VAULT_SPEED "Leg Muscle Stimulus"
#define VAULT_QUICK "Arm Muscle Stimulus"
diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm
index 53907e86d79..042e911fae9 100644
--- a/code/modules/surgery/organs/augments_eyes.dm
+++ b/code/modules/surgery/organs/augments_eyes.dm
@@ -62,7 +62,7 @@
name = "X-ray implant"
desc = "These cybernetic eye implants will give you X-ray vision. Blinking is futile."
implant_color = "#000000"
- origin_tech = "materials=4;programming=4;biotech=6;magnets=4"
+ origin_tech = "materials=4;programming=4;biotech=7;magnets=4"
vision_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm
index ca2698f2ad4..4102319a3fe 100644
--- a/code/modules/surgery/organs/helpers.dm
+++ b/code/modules/surgery/organs/helpers.dm
@@ -104,7 +104,7 @@
/mob/living/carbon/human/get_leg_ignore()
- if(flying == 1)
+ if(flying || floating)
return TRUE
var/obj/item/tank/jetpack/J
diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm
index 2a3b94ca249..b41ef824508 100644
--- a/code/modules/surgery/organs/organ_icon.dm
+++ b/code/modules/surgery/organs/organ_icon.dm
@@ -107,7 +107,10 @@ GLOBAL_LIST_EMPTY(limb_icon_cache)
add_overlay(eyes_icon)
if(owner.lip_style && (LIPS in dna.species.species_traits))
- add_overlay(mutable_appearance('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s")) //Hefty icon not necessary.
+ var/icon/lips_icon = new('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s")
+ lips_icon.Blend(owner.lip_color, ICON_MULTIPLY)
+ mob_icon.Blend(lips_icon, ICON_OVERLAY)
+ add_overlay(lips_icon)
var/head_marking = owner.m_styles["head"]
if(head_marking)
diff --git a/code/modules/surgery/organs/robolimbs.dm b/code/modules/surgery/organs/robolimbs.dm
index eb40fa40994..e69889a709b 100644
--- a/code/modules/surgery/organs/robolimbs.dm
+++ b/code/modules/surgery/organs/robolimbs.dm
@@ -3,18 +3,6 @@ GLOBAL_LIST_EMPTY(chargen_robolimbs)
GLOBAL_LIST_EMPTY(selectable_robolimbs)
GLOBAL_DATUM(basic_robolimb, /datum/robolimb)
-/proc/populate_robolimb_list()
- GLOB.basic_robolimb = new()
- for(var/limb_type in typesof(/datum/robolimb))
- var/datum/robolimb/R = new limb_type()
- GLOB.all_robolimbs[R.company] = R
- if(!R.unavailable_at_chargen)
- if(R != "head" && R != "chest" && R != "groin" ) //Part of the method that ensures only IPCs can access head, chest and groin prosthetics.
- if(R.has_subtypes) //Ensures solos get added to the list as well be incorporating has_subtypes == 1 and has_subtypes == 2.
- GLOB.chargen_robolimbs[R.company] = R //List only main brands and solo parts.
- if(R.selectable)
- GLOB.selectable_robolimbs[R.company] = R
-
/datum/robolimb
var/company = "Unbranded" // Shown when selecting the limb.
var/desc = "A generic unbranded robotic prosthesis." // Seen when examining a limb.
diff --git a/code/modules/surgery/rig_removal.dm b/code/modules/surgery/rig_removal.dm
deleted file mode 100644
index de15eacf8f3..00000000000
--- a/code/modules/surgery/rig_removal.dm
+++ /dev/null
@@ -1,59 +0,0 @@
-//Procedures in this file: Unsealing a Rig.
-
-/datum/surgery/rigsuit
- name = "Rig Unsealing"
- steps = list(/datum/surgery_step/rigsuit)
- possible_locs = list("chest")
-
-/datum/surgery/rigsuit/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/backitem = H.get_item_by_slot(slot_back)
- if(istype(backitem,/obj/item/rig)) //Check if we have a rig to operate on
- if(backitem.flags&NODROP) //Check if the rig is sealed, if not, we don't need to operate
- return 1
- return 0
-
-//Bay12 removal
-/datum/surgery_step/rigsuit
- name="Cut Seals"
- allowed_tools = list(
- /obj/item/weldingtool = 80,
- /obj/item/circular_saw = 60,
- /obj/item/gun/energy/plasmacutter = 100
- )
-
- can_infect = 0
- blood_level = 0
-
- time = 50
-
-/datum/surgery_step/hardsuit/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(!istype(target))
- return 0
- if(tool.tool_behaviour == TOOL_WELDER)
- if(!tool.tool_use_check(user, 0))
- return
- if(!tool.use(1))
- return
- return (target_zone == "chest") && istype(target.back, /obj/item/rig) && (target.back.flags&NODROP)
-
-/datum/surgery_step/rigsuit/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- user.visible_message("[user] starts cutting through the support systems of [target]'s [target.back] with \the [tool]." , \
- "You start cutting through the support systems of [target]'s [target.back] with \the [tool].")
- ..()
-
-/datum/surgery_step/rigsuit/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
-
- var/obj/item/rig/rig = target.back
- if(!istype(rig))
- return
- rig.reset()
- user.visible_message("[user] has cut through the support systems of [target]'s [rig] with \the [tool].", \
- "You have cut through the support systems of [target]'s [rig] with \the [tool].")
- return 1
-
-/datum/surgery_step/rigsuit/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- user.visible_message("[user]'s [tool] can't quite seem to get through the metal...", \
- "Your [tool] can't quite seem to get through the metal. It's weakening, though - try again.")
- return 0
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 0b13d18f9da..294e98d38d0 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -111,7 +111,7 @@
prob_chance *= get_location_modifier(target)
- if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics) && !ispath(surgery.steps[surgery.status], /datum/surgery_step/rigsuit))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig
+ if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig
if(ishuman(target))
var/mob/living/carbon/human/H = target //typecast to human
prob_chance *= get_pain_modifier(H)//operating on conscious people is hard.
@@ -196,7 +196,7 @@
if(AStar(E.loc, M.loc, /turf/proc/Distance, 2, simulated_only = 0))
germs++
- if(tool.blood_DNA && tool.blood_DNA.len) //germs from blood-stained tools
+ if(tool && tool.blood_DNA && tool.blood_DNA.len) //germs from blood-stained tools
germs += 30
if(E.internal_organs.len)
diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm
index 783498542e4..f1da4ed89ba 100644
--- a/code/modules/tgui/modules/crew_monitor.dm
+++ b/code/modules/tgui/modules/crew_monitor.dm
@@ -14,13 +14,13 @@
if("track")
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
- var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.mob_list
+ var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.human_list
if(hassensorlevel(H, SUIT_SENSOR_TRACKING))
AI.ai_actual_track(H)
return TRUE
-/datum/tgui_module/crew_monitor/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)
+/datum/tgui_module/crew_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)
// The 557 may seem random, but its the perfectsize for margins on the nanomap
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 5b1409af4e7..80467d19846 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -3,6 +3,7 @@
#ifdef UNIT_TESTS
#include "component_tests.dm"
+#include "map_templates.dm"
#include "reagent_id_typos.dm"
#include "spawn_humans.dm"
#include "sql.dm"
diff --git a/code/modules/unit_tests/map_templates.dm b/code/modules/unit_tests/map_templates.dm
new file mode 100644
index 00000000000..b590526a46b
--- /dev/null
+++ b/code/modules/unit_tests/map_templates.dm
@@ -0,0 +1,7 @@
+/datum/unit_test/map_templates/Run()
+ var/list/datum/map_template/templates = subtypesof(/datum/map_template)
+ for(var/I in templates)
+ var/datum/map_template/MT = new I // The new is important here to ensure stuff gets set properly
+ // Check if it even has a path and if so, does it exist
+ if(MT.mappath && !fexists(MT.mappath))
+ Fail("The map file for [MT.type] does not exist!")
diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm
deleted file mode 100644
index 0f0f85b088b..00000000000
--- a/code/modules/zombie/items.dm
+++ /dev/null
@@ -1,69 +0,0 @@
-/obj/item/zombie_hand
- name = "zombie claw"
- desc = "A zombie's claw is its primary tool, capable of infecting \
- unconscious or dead humans, butchering all other living things to \
- sustain the zombie, forcing open airlock doors and opening \
- child-safe caps on bottles."
- flags = NODROP|ABSTRACT|DROPDEL
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- icon = 'icons/effects/blood.dmi'
- icon_state = "bloodhand_left"
- var/icon_left = "bloodhand_left"
- var/icon_right = "bloodhand_right"
- hitsound = 'sound/hallucinations/growl1.ogg'
- force = 21 // Just enough to break airlocks with melee attacks
- damtype = "brute"
-
-/obj/item/zombie_hand/equipped(mob/user, slot)
- . = ..()
- switch(slot)
- // Yes, these intentionally don't match
- if(slot_l_hand)
- icon_state = icon_right
- if(slot_r_hand)
- icon_state = icon_left
-
-/obj/item/zombie_hand/afterattack(atom/target, mob/user, proximity_flag)
- . = ..()
- if(!proximity_flag)
- return
-
- else if(isliving(target))
- if(ishuman(target))
- try_to_zombie_infect(target)
- else
- check_feast(target, user)
-
-/proc/try_to_zombie_infect(mob/living/carbon/human/target)
- CHECK_DNA_AND_SPECIES(target)
-
- if(NOZOMBIE in target.dna.species.species_traits)
- // cannot infect any NOZOMBIE subspecies (such as high functioning
- // zombies)
- return
-
- var/obj/item/organ/internal/zombie_infection/infection
- infection = target.get_organ_slot("zombie_infection")
- if(!infection)
- infection = new()
- infection.insert(target)
-
-/obj/item/zombie_hand/proc/check_feast(mob/living/target, mob/living/user)
- if(target.stat == DEAD)
- var/hp_gained = target.maxHealth
- target.gib()
- user.adjustBruteLoss(-hp_gained, FALSE)
- user.adjustToxLoss(-hp_gained, FALSE)
- user.adjustFireLoss(-hp_gained, FALSE)
- user.adjustCloneLoss(-hp_gained, FALSE)
- user.adjustBrainLoss(-hp_gained, FALSE) // Zom Bee gibbers "BRAAAAISNSs!1!"
- user.updatehealth()
-
-/obj/item/zombie_hand/suicide_act(mob/living/carbon/human/user)
- user.visible_message("[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!")
- if(ishuman(user))
- var/mob/living/carbon/human/L = user
- var/obj/item/organ/external/O = L.get_organ("head")
- if(O)
- O.droplimb()
- return (BRUTELOSS)
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
deleted file mode 100644
index 6ea40c90e96..00000000000
--- a/code/modules/zombie/organs.dm
+++ /dev/null
@@ -1,99 +0,0 @@
-/obj/item/organ/internal/zombie_infection
- name = "festering ooze"
- desc = "A black web of pus and viscera."
- parent_organ = "head"
- slot = "zombie_infection"
- icon_state = "blacktumor"
- var/causes_damage = TRUE
- var/datum/species/old_species = /datum/species/human
- var/living_transformation_time = 30
- var/converts_living = FALSE
-
- var/revive_time_min = 450
- var/revive_time_max = 700
- var/timer_id
-
-/obj/item/organ/internal/zombie_infection/New(mob/living/carbon/holder)
- ..()
- GLOB.zombie_infection_list += src
-
-/obj/item/organ/internal/zombie_infection/Destroy()
- GLOB.zombie_infection_list -= src
- . = ..()
-
-/obj/item/organ/internal/zombie_infection/insert(mob/living/carbon/human/M, special = 0)
- ..()
- START_PROCESSING(SSobj, src)
-
-/obj/item/organ/internal/zombie_infection/remove(mob/living/carbon/human/M, special = 0)
- STOP_PROCESSING(SSobj, src)
- if(iszombie(M) && old_species)
- M.set_species(old_species, retain_damage = TRUE)
- if(timer_id)
- deltimer(timer_id)
- . = ..()
-
-/obj/item/organ/internal/zombie_infection/on_find(mob/living/finder)
- to_chat(finder, "Inside the head is a disgusting black \
- web of pus and viscera, bound tightly around the brain like some \
- biological harness.")
-
-/obj/item/organ/internal/zombie_infection/process()
- if(!owner)
- return
- if(!(src in owner.internal_organs))
- remove(owner)
- if(causes_damage && !iszombie(owner) && owner.stat != DEAD)
- owner.adjustToxLoss(1)
- if (prob(10))
- to_chat(owner, "You feel sick...")
- if(timer_id)
- return
- if(owner.suiciding)
- return
- if(owner.stat != DEAD && !converts_living)
- return
- if(!owner.get_int_organ(/obj/item/organ/internal/brain))
- return
- if(!iszombie(owner))
- to_chat(owner, "You can feel your heart stopping, but something isn't right... \
- life has not abandoned your broken form. You can only feel a deep and immutable hunger that \
- not even death can stop, you will rise again!")
- var/revive_time = rand(revive_time_min, revive_time_max)
- var/flags = TIMER_STOPPABLE
- timer_id = addtimer(CALLBACK(src, .proc/zombify), revive_time, flags)
-
-/obj/item/organ/internal/zombie_infection/proc/zombify()
- timer_id = null
-
- if(!converts_living && owner.stat != DEAD)
- return
-
- if(!iszombie(owner))
- old_species = owner.dna.species.type
- owner.set_species(/datum/species/zombie/infectious)
- for(var/datum/disease/critical/crit in owner.viruses) // cure any new crit viruses
- crit.cure(0)
-
- var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS)
- //Fully heal the zombie's damage the first time they rise
- owner.setToxLoss(0)
- owner.setOxyLoss(0)
- owner.setBrainLoss(0)
- owner.setCloneLoss(0)
- owner.SetLoseBreath(0)
- owner.heal_overall_damage(INFINITY, INFINITY, TRUE, TRUE, FALSE)
- owner.setStaminaLoss(0)
-
- if(!owner.update_revive())
- return
-
- owner.grab_ghost()
- owner.visible_message("[owner] suddenly convulses, as [owner.p_they()][stand_up ? " stagger to [owner.p_their()] feet and" : ""] gain a ravenous hunger in [owner.p_their()] eyes!", "You HUNGER!")
- playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, TRUE)
- owner.do_jitter_animation(living_transformation_time)
- owner.Stun(living_transformation_time * 0.05)
- to_chat(owner, "You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.")
-
-/obj/item/organ/internal/zombie_infection/nodamage
- causes_damage = FALSE
diff --git a/config/example/config.txt b/config/example/config.txt
index 4fbc5508db3..3a56570a03d 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -277,9 +277,6 @@ TICKLAG 0.5
## Whether the server will talk to other processes through socket_talk
SOCKET_TALK 0
-## Uncomment this to ban use of ToR
-#TOR_BAN
-
## Comment this out to disable automuting
#AUTOMUTE_ON
@@ -376,10 +373,10 @@ PLAYER_REROUTE_CAP 0
#DISABLE_SPACE_RUINS
## Minimum number of space ruins levels to generate
-EXTRA_SPACE_RUIN_LEVELS_MIN 2
+EXTRA_SPACE_RUIN_LEVELS_MIN 4
## Maximum number of space ruins levels to generate
-EXTRA_SPACE_RUIN_LEVELS_MAX 4
+EXTRA_SPACE_RUIN_LEVELS_MAX 8
## Uncomment to disable the OOC/LOOC channel by default.
#DISABLE_OOC
diff --git a/config/example/spaceRuinBlacklist.txt b/config/example/spaceRuinBlacklist.txt
index 148cdeef583..e83a528fb14 100644
--- a/config/example/spaceRuinBlacklist.txt
+++ b/config/example/spaceRuinBlacklist.txt
@@ -22,3 +22,20 @@
#_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm
#_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm
#_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm
+
+### The following ruins are based from past pre-spawned Zlevel content ###
+
+#_maps/map_files/RandomRuins/SpaceRuins/abandonedtele.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/blowntcommsat.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/clownmime.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/dj.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/druglab.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/ussp_tele.dmm
+#_maps/map_files/RandomRuins/SpaceRuins/ussp.dmm
+
+
+# The following is the white ship ruin. Its force-spawned and is required to stop SSshuttle runtiming on startup
+# Its also important incase a white-ship console is ever built midround
+# DO NOT DISABLE THIS UNLESS YOU HAVE A GOOD REASON
+#_maps/map_files/RandomRuins/SpaceRuins/whiteship.dmm
diff --git a/config/names/dreams.txt b/config/names/dreams.txt
index 89f559dae88..4bd2c2b984e 100644
--- a/config/names/dreams.txt
+++ b/config/names/dreams.txt
@@ -105,7 +105,7 @@ a grey
a kidan
a diona
a drask
-the ai core
+the AI core
the mining station
the research station
a beaker of strange liquid
diff --git a/config/names/nightmares.txt b/config/names/nightmares.txt
index 71df2e90cff..022a0494edd 100644
--- a/config/names/nightmares.txt
+++ b/config/names/nightmares.txt
@@ -10,7 +10,7 @@ a dead grey
a dead kidan
a dead diona
a dead drask
-the malf ai core
+the malf AI core
bLoOd
has been called
a horrible sense of dread comes over you
diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css
index e7b79557619..529827ad369 100644
--- a/goon/browserassets/css/browserOutput-dark.css
+++ b/goon/browserassets/css/browserOutput-dark.css
@@ -38,7 +38,7 @@ a.popt {text-decoration: none;}
* CUSTOM FONTS
*
******************************************/
-@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); }
+@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); }
/*****************************************
*
@@ -250,6 +250,7 @@ em {font-style: normal; font-weight: bold;}
{color: #638500; text-decoration: underline;}
.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover
{color: #638500;}
+.darkmblue {color: #6685f5;}
.prefix { font-weight: bold;}
.ooc { font-weight: bold;}
.looc {color: #6699CC;}
@@ -261,32 +262,32 @@ em {font-style: normal; font-weight: bold;}
.mentorhelp {color: #0077bb; font-weight: bold;}
.adminhelp {color: #aa0000; font-weight: bold;}
.playerreply {color: #8800bb; font-weight: bold;}
-.pmsend {color: #0000ff;}
+.pmsend {color: #6685f5;}
.name { font-weight: bold;}
.say {}
.yell { font-weight: bold;}
.siliconsay {font-family: 'Courier New', Courier, monospace;}
-.deadsay {color: #cc00c6;}
+.deadsay {color: #B800B1;}
.radio {color: #408010;}
.deptradio {color: #993399;}
-.comradio {color: #2040ff;}
+.comradio {color: #526aff;}
.syndradio {color: #993F40;}
.dsquadradio {color: #998599;}
.resteamradio {color: #18BC46;}
-.airadio {color: #FF00FF;}
-.centradio {color: #5C5C7C;}
+.airadio {color: #FF94FF;}
+.centradio {color: #78789B;}
.secradio {color: #CF0000;}
.engradio {color: #A66300;}
.medradio {color: #009190;}
.sciradio {color: #993399;}
.supradio {color: #9F8545;}
.srvradio {color: #80A000;}
-.admin_channel {color: #9A04D1; font-weight: bold;}
+.admin_channel {color: #fcba03; font-weight: bold;}
.mentor_channel {color: #775BFF; font-weight: bold;}
.mentor_channel_admin {color: #A35CFF; font-weight: bold;}
.djradio {color: #996600;}
.binaryradio {color: #1B00FB; font-family: 'Courier New', Courier, monospace;}
-.mommiradio {color: #1B00AB;}
+.mommiradio {color: #6685f5;}
.alert {color: #ff0000;}
h1.alert, h2.alert {color: #FFF;}
.ghostalert {color: #cc00c6; font-style: italic; font-weight: bold;}
@@ -304,10 +305,10 @@ h1.alert, h2.alert {color: #FFF;}
.userdanger {color: #ff0000; font-weight: bold; font-size: 120%;}
.biggerdanger {color: #ff0000; font-weight: bold; font-size: 150%;}
-.info {color: #0044DD;}
-.notice {color: #0044DD;}
-.bnotice {color: #0044DD; font-weight: bold;}
-.boldnotice {color: #0044DD; font-weight: bold;}
+.info {color: #6685f5;}
+.notice {color: #6685f5;}
+.bnotice {color: #6685f5; font-weight: bold;}
+.boldnotice {color: #6685f5; font-weight: bold;}
.suicide {color: #ff5050; font-style: italic;}
.green {color: #03bb39;}
.announce {color: #228b22; font-weight: bold;}
@@ -358,7 +359,7 @@ h1.alert, h2.alert {color: #FFF;}
.cultlarge {color: #A60000; font-weight: bold; font-size: 120%;}
.narsie {color: #A60000; font-weight: bold; font-size: 300%;}
.narsiesmall {color: #A60000; font-weight: bold; font-size: 200%;}
-.interface {color: #330033;}
+.interface {color: #9031C4;}
.big {font-size: 150%;}
.reallybig {font-size: 175%;}
.greentext {color: #00FF00; font-size: 150%;}
@@ -366,7 +367,7 @@ h1.alert, h2.alert {color: #FFF;}
.bold {font-weight: bold;}
.center {text-align: center;}
.red {color: #FF0000;}
-.purple {color: #5e2d79;}
+.purple {color: #9031C4;}
.skeleton {color: #C8C8C8; font-weight: bold; font-style: italic;}
.gutter {color: #7092BE; font-family: "Trebuchet MS", cursive, sans-serif;}
.orange {color: #ffa500;}
@@ -374,14 +375,14 @@ h1.alert, h2.alert {color: #FFF;}
.orangeb {color: #ffa500; font-weight: bold;}
.resonate {color: #298F85;}
-.revennotice {color: #1d29C3;}
-.revenboldnotice {color: #1d29C3; font-weight: bold;}
-.revenbignotice {color: #1d29C3; font-weight: bold; font-size: 120%;}
+.revennotice {color: #6685F5;}
+.revenboldnotice {color: #6685F5; font-weight: bold;}
+.revenbignotice {color: #6685F5; font-weight: bold; font-size: 120%;}
.revenminor {color: #823abb}
.revenwarning {color: #760fbb; font-style: italic;}
.revendanger {color: #760fbb; font-weight: bold; font-size: 120%;}
-.specialnotice {color: #36525e; font-weight: bold; font-size: 120%;}
+.specialnotice {color: #4A6F82; font-weight: bold; font-size: 120%;}
/* /vg/ */
.good {color: green;}
@@ -398,7 +399,11 @@ h1.alert, h2.alert {color: #FFF;}
.connectionClosed, .fatalError {background: red; color: white; padding: 5px;}
.connectionClosed.restored {background: green;}
-.internal.boldnshit {color: blue; font-weight: bold;}
+.internal.boldnshit {color: #6685f5; font-weight: bold;}
+
+.rebooting {background: #2979AF; color: white; padding: 5px;}
+.rebooting a {color: white !important; text-decoration-color: white !important;}
+#reconnectTimer {font-weight: bold;}
/* HELPER CLASSES */
.text-normal {font-weight: normal; font-style: normal;}
diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css
index 962ce7bb8e7..31da7aa96f5 100644
--- a/goon/browserassets/css/browserOutput.css
+++ b/goon/browserassets/css/browserOutput.css
@@ -37,7 +37,7 @@ a.popt {text-decoration: none;}
* CUSTOM FONTS
*
******************************************/
-@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); }
+@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); }
/*****************************************
*
@@ -247,6 +247,7 @@ em {font-style: normal; font-weight: bold;}
{color: #638500; text-decoration: underline;}
.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover
{color: #638500;}
+.darkmblue {color: #0000ff;}
.prefix { font-weight: bold;}
.ooc { font-weight: bold;}
.looc {color: #6699CC;}
@@ -399,6 +400,10 @@ h1.alert, h2.alert {color: #000000;}
.connectionClosed.restored {background: green;}
.internal.boldnshit {color: blue; font-weight: bold;}
+.rebooting {background: #2979AF; color: white; padding: 5px;}
+.rebooting a {color: white !important; text-decoration-color: white !important;}
+#reconnectTimer {font-weight: bold;}
+
/* HELPER CLASSES */
.text-normal {font-weight: normal; font-style: normal;}
.hidden {display: none; visibility: hidden;}
diff --git a/goon/browserassets/js/browserOutput.js b/goon/browserassets/js/browserOutput.js
index 40a69245188..414a5d644b6 100644
--- a/goon/browserassets/js/browserOutput.js
+++ b/goon/browserassets/js/browserOutput.js
@@ -69,10 +69,13 @@ var opts = {
'macros': {},
// Emoji toggle
- 'enableEmoji': true
+ 'enableEmoji': true,
+
+ // Reboot message stuff
+ 'rebootIntervalHandler': null
};
-var regexHasError = false; //variable to check if regex has excepted
+var regexHasError = false; //variable to check if regex has excepted
function outerHTML(el) {
var wrap = document.createElement('div');
@@ -97,10 +100,10 @@ if (typeof String.prototype.trim !== 'function') {
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
-
+
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
- }
+ }
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
@@ -124,7 +127,7 @@ function byondDecode(message) {
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
// Marvelous.
message = message.replace(/\+/g, "%20");
- try {
+ try {
// This is a workaround for the above not always working when BYOND's shitty url encoding breaks.
// Basically, sometimes BYOND's double encoding trick just arbitrarily produces something that makes decodeURIComponent
// throw an "Invalid Encoding URI" URIError... the simplest way to work around this is to just ignore it and use unescape instead
@@ -166,30 +169,79 @@ function emojiparse(el) {
}
}
-// Colorizes the highlight spans
-function setHighlightColor(match) {
- match.style.background = opts.highlightColor
+// Recolorizes the highlight spans
+function setHighlightColor() {
+ var highlightspans = document.getElementsByClassName("highlight")
+ for(var i in highlightspans){
+ highlightspans[i].setAttribute("style","background-color:"+opts.highlightColor)
+ }
+}
+
+function escapeRegexCharacters(input){ //escapes any characters that could be interpreted as regex patterns, potentially causing patterns to break if not escaped
+ return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//Highlights words based on user settings
function highlightTerms(el) {
- if(regexHasError) return; //just stop right there ig the regex is gonna except
+
+ if (regexHasError) return; //just stop right there ig the regex is gonna except
+
+ function highlightRecursor(element, term){ //recursor function to do the highlighting proper
+ var regex = new RegExp(term, "gi");
+
+ function replace(str) {
+ return str.replace(regex, '$&');
+ }
+
+ var s = '';
+ var work = element.innerHTML;
+ var ind = 0;
+
+ while(ind < work.length) {
+
+ var next_term = work.substring(ind).search(regex);
+ if(next_term != -1) next_term += ind;
+ var next_tag = work.indexOf('<', ind);
+ if(next_tag == -1) {
+ s+=replace(work.substring(ind));
+ break;
+ }
+ else if(next_term==-1) {
+ s += work.substring(ind);
+ break;
+ }
+ else if(next_tag < next_term) {
+ var temp = work.indexOf('>', next_tag);
+ s += work.substring(ind,temp+1);
+ ind = temp+1;
+ }
+ else {
+ s += replace(work.substring(ind, next_tag));
+ ind = next_tag;
+ }
+ }
+
+ element.innerHTML = s;
+ }
+
for (var i = 0; i < opts.highlightTerms.length; i++) { //Each highlight term
if(opts.highlightTerms[i]) {
if(!opts.highlightRegexEnable){
- if(el.innerText.toString().toLowerCase().includes(opts.highlightTerms[i].toLowerCase())) //match normally
- el.innerHTML = ''+el.innerHTML+'' //encloseincludes
- continue;
+ var innerTerms = opts.highlightTerms[i].split(" ")
+ for(var a in innerTerms){
+ highlightRecursor(el, escapeRegexCharacters(innerTerms[a]))
+ }
}
- var rexp;
- try{
- rexp = new RegExp(opts.highlightTerms[i],"gmi")
- } catch(e){
- el.innerHTML+=' Your highlight regex - '+opts.highlightTerms[i]+' - is malformed. Thrown exception: '+e+''
- regexHasError = true;
- return;
+ else {
+ try{
+ new RegExp(opts.highlightTerms[i], "gmi"); // check to make sure the pattern wont cause issues
+ } catch(e){
+ el.innerHTML += ' Your highlight regex pattern -- ' + opts.highlightTerms[i] + ' -- is malformed. Your highlights have been disabled until they are next edited Thrown exception: '+e+'';
+ regexHasError = true;
+ return;
+ }
+ highlightRecursor(el, opts.highlightTerms[i]);
}
- el.innerHTML = el.innerHTML.replace(rexp,"$0") //i cant figure out a proper, non snowflakey way to let people select the group that gets highlighted
}
}
}
@@ -524,6 +576,34 @@ function toggleWasd(state) {
opts.wasd = (state == 'on' ? true : false);
}
+function reboot(timeRaw) {
+ var timeLeftSecs = parseInt(timeRaw);
+ const intervalSecs = 1; // tick every 1 second
+
+ rebootFinished();
+ internalOutput('', 'internal');
+
+ opts.rebootIntervalHandler = setInterval(function() {
+ timeLeftSecs -= intervalSecs;
+ if (timeLeftSecs <= 0) {
+ $("#reconnectTimer").text('Reconnecting...');
+ window.location.href = 'byond://winset?command=.reconnect';
+ clearInterval(opts.rebootIntervalHandler)
+ opts.rebootIntervalHandler = null;
+ } else {
+ $("#reconnectTimer").text('Reconnect (' + timeLeftSecs + ')');
+ }
+ }, intervalSecs * 1000);
+}
+
+function rebootFinished() {
+ if (opts.rebootIntervalHandler != null) {
+ clearInterval(opts.rebootIntervalHandler)
+ }
+ $(" Reconnected automatically!").insertBefore("#reconnectTimer");
+ $("#reconnectTimer").remove();
+}
+
/*****************************************
*
* MAKE MACRO DICTIONARY
@@ -603,7 +683,7 @@ $(function() {
'shideSpam': getCookie('hidespam'),
'darkChat': getCookie('darkChat'),
};
-
+
if (savedConfig.sfontSize) {
$messages.css('font-size', savedConfig.sfontSize);
internalOutput('Loaded font size setting of: '+savedConfig.sfontSize+'', 'internal');
@@ -961,18 +1041,18 @@ $(function() {
} else {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
-
+
// synchronous requests are depricated in modern browsers
- xmlHttp.open('GET', 'browserOutput.css', true);
+ xmlHttp.open('GET', 'browserOutput.css', true);
xmlHttp.onload = function (e) {
if (xmlHttp.status === 200) { // request successful
-
+
// Generate Log
var saved = '';
saved += $messages.html();
saved = saved.replace(/&/g, '&');
saved = saved.replace(/Chat Log \
'+
@@ -982,7 +1062,7 @@ $(function() {
openWindow('Style Doc Retrieve Error: '+xmlHttp.statusText);
}
}
-
+
// timeout and request errors
xmlHttp.timeout = 300;
xmlHttp.ontimeout = function (e) {
@@ -1043,15 +1123,21 @@ $(function() {
count++;
}
- var color = $('#highlightColor').val();
opts.highlightRegexEnable = document.querySelector("#highlightRegexEnable").checked
- color = color.trim();
- if (color == '' || color.charAt(0) != '#') {
- opts.highlightColor = '#FFFF00';
- } else {
- opts.highlightColor = color;
+
+ var color = $('#highlightColor').val();
+ if(color != opts.highlightColor) { // did the color even change?
+ color = color.trim();
+ if (color == '' || color.charAt(0) != '#') {
+ opts.highlightColor = '#FFFF00';
+ } else {
+ opts.highlightColor = color;
+ }
+ setHighlightColor();
}
+
regexHasError = false; //they changed the regex so it might be valid now
+ internalOutput('Highlights have been updated.',"internal") // simplest way to test if pattern works, why reinvent the wheel?
var $popup = $('#highlightPopup').closest('.popup');
$popup.remove();
diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm
index 74c9d844728..ccefae869c3 100644
--- a/goon/code/datums/browserOutput.dm
+++ b/goon/code/datums/browserOutput.dm
@@ -109,6 +109,7 @@ var/list/chatResources = list(
loaded = TRUE
winset(owner, "browseroutput", "is-disabled=false")
+ owner << output(null, "browseroutput:rebootFinished")
if(owner.holder)
loadAdmin()
for(var/message in messageQueue)
diff --git a/html/browser/marked-paradise.js b/html/browser/marked-paradise.js
index 03519139409..a92cadb1cfe 100644
--- a/html/browser/marked-paradise.js
+++ b/html/browser/marked-paradise.js
@@ -4,24 +4,22 @@
var $ = document.querySelector.bind(document);
function parse(node) {
- for (var i = 0; i < node.childNodes.length; i++) {
- parse(node.childNodes[i]);
- }
-
- if (!node.innerHTML) {
- return;
- }
-
- node.innerHTML = marked(node.innerHTML.replace(/ /gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false })
- // marked.js wraps content into tags, which is looks atrocious when we call it recursively.
- // The following line unwraps it.
- if (node.children.length == 1) {
- node.innerHTML = node.children[0].innerHTML;
- }
+ for (var i = 0; i < node.childNodes.length; i++)
+ parse(node.childNodes[i]);
+
+ if (!node.innerHTML)
+ return;
+
+ if (node.children.length == 0) {
+ node.innerHTML = marked(node.innerHTML.replace(/ /gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false });
+ // marked.js wraps content into tags, which is looks atrocious when we call it recursively.
+ // The following line unwraps it.
+ if (node.children.length == 1 && node.children[0].tagName == "P")
+ node.innerHTML = node.children[0].innerHTML;
+ }
}
-
+
window.onload = function() {
- if ($('#markdown')) {
- parse($('#markdown'));
- }
+ if ($('#markdown'))
+ parse($('#markdown'));
}
diff --git a/icons/effects/lasers2.dmi b/icons/effects/lasers2.dmi
deleted file mode 100644
index f774fb9430b..00000000000
Binary files a/icons/effects/lasers2.dmi and /dev/null differ
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index 4ea648dea13..80a5dbe5b04 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/feet.dmi b/icons/mob/feet.dmi
index e6f132a73a5..3892442c6ea 100644
Binary files a/icons/mob/feet.dmi and b/icons/mob/feet.dmi differ
diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi
index 6d6165509c8..66d1474d61a 100644
Binary files a/icons/mob/hands.dmi and b/icons/mob/hands.dmi differ
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 88b28aff5a9..1bd516110e5 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index df6b8f4de1f..07593d09c0c 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi
index ff471760fe8..9b6d9468b9d 100644
Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ
diff --git a/icons/mob/human_races/r_def_zombie.dmi b/icons/mob/human_races/r_def_zombie.dmi
deleted file mode 100644
index 007eb3f31f3..00000000000
Binary files a/icons/mob/human_races/r_def_zombie.dmi and /dev/null differ
diff --git a/icons/mob/human_races/r_zombie.dmi b/icons/mob/human_races/r_zombie.dmi
deleted file mode 100644
index 007eb3f31f3..00000000000
Binary files a/icons/mob/human_races/r_zombie.dmi and /dev/null differ
diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi
index a0a6b7b3f39..940731f6e73 100644
Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi
index 545318bbed5..3ad61ad5794 100644
Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index fb8efeadfb1..b2f1bfa90c6 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index e11fd688338..a7772c3ebf4 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/rig_back.dmi b/icons/mob/rig_back.dmi
deleted file mode 100644
index 923939d323e..00000000000
Binary files a/icons/mob/rig_back.dmi and /dev/null differ
diff --git a/icons/mob/rig_modules.dmi b/icons/mob/rig_modules.dmi
deleted file mode 100644
index 3d18b5435ce..00000000000
Binary files a/icons/mob/rig_modules.dmi and /dev/null differ
diff --git a/icons/mob/screen_ghost.dmi b/icons/mob/screen_ghost.dmi
index 6519d00aa97..e0e85a6939c 100644
Binary files a/icons/mob/screen_ghost.dmi and b/icons/mob/screen_ghost.dmi differ
diff --git a/icons/mob/species/drask/head.dmi b/icons/mob/species/drask/head.dmi
index b9f0604c960..f0c888e9f68 100644
Binary files a/icons/mob/species/drask/head.dmi and b/icons/mob/species/drask/head.dmi differ
diff --git a/icons/mob/species/drask/suit.dmi b/icons/mob/species/drask/suit.dmi
index d75a28f2bee..cef6dbdf836 100644
Binary files a/icons/mob/species/drask/suit.dmi and b/icons/mob/species/drask/suit.dmi differ
diff --git a/icons/mob/species/grey/head.dmi b/icons/mob/species/grey/head.dmi
index 4ff783bbb74..c059a3bff9d 100644
Binary files a/icons/mob/species/grey/head.dmi and b/icons/mob/species/grey/head.dmi differ
diff --git a/icons/mob/species/grey/helmet.dmi b/icons/mob/species/grey/helmet.dmi
index cf244cec6b2..3e592369adc 100644
Binary files a/icons/mob/species/grey/helmet.dmi and b/icons/mob/species/grey/helmet.dmi differ
diff --git a/icons/mob/species/grey/mask.dmi b/icons/mob/species/grey/mask.dmi
index cbbd722ae69..348a39c0f03 100644
Binary files a/icons/mob/species/grey/mask.dmi and b/icons/mob/species/grey/mask.dmi differ
diff --git a/icons/mob/species/grey/suit.dmi b/icons/mob/species/grey/suit.dmi
index 3070f6627ea..c22c9daf3dd 100644
Binary files a/icons/mob/species/grey/suit.dmi and b/icons/mob/species/grey/suit.dmi differ
diff --git a/icons/mob/species/grey/uniform.dmi b/icons/mob/species/grey/uniform.dmi
index c956187ffa1..7a74f930598 100644
Binary files a/icons/mob/species/grey/uniform.dmi and b/icons/mob/species/grey/uniform.dmi differ
diff --git a/icons/mob/species/skrell/helmet.dmi b/icons/mob/species/skrell/helmet.dmi
index e17fa4174c5..e54cdb52d0f 100644
Binary files a/icons/mob/species/skrell/helmet.dmi and b/icons/mob/species/skrell/helmet.dmi differ
diff --git a/icons/mob/species/tajaran/helmet.dmi b/icons/mob/species/tajaran/helmet.dmi
index 298ff823ea4..00c3f1d2bdc 100644
Binary files a/icons/mob/species/tajaran/helmet.dmi and b/icons/mob/species/tajaran/helmet.dmi differ
diff --git a/icons/mob/species/tajaran/suit.dmi b/icons/mob/species/tajaran/suit.dmi
index 2f7d8f34907..61929dcceda 100644
Binary files a/icons/mob/species/tajaran/suit.dmi and b/icons/mob/species/tajaran/suit.dmi differ
diff --git a/icons/mob/species/unathi/helmet.dmi b/icons/mob/species/unathi/helmet.dmi
index 1fc21dd1611..68f6171bb19 100644
Binary files a/icons/mob/species/unathi/helmet.dmi and b/icons/mob/species/unathi/helmet.dmi differ
diff --git a/icons/mob/species/unathi/suit.dmi b/icons/mob/species/unathi/suit.dmi
index 14d1eaaa73a..eabd0f94672 100644
Binary files a/icons/mob/species/unathi/suit.dmi and b/icons/mob/species/unathi/suit.dmi differ
diff --git a/icons/mob/species/vox/head.dmi b/icons/mob/species/vox/head.dmi
index 6c4b2b43f6a..fb5d153d30d 100644
Binary files a/icons/mob/species/vox/head.dmi and b/icons/mob/species/vox/head.dmi differ
diff --git a/icons/mob/species/vox/suit.dmi b/icons/mob/species/vox/suit.dmi
index 730149860d5..4161227ac79 100644
Binary files a/icons/mob/species/vox/suit.dmi and b/icons/mob/species/vox/suit.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index d71fb9b1d21..12a0a371a94 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/obj/atmospherics/blue_pipe_tank.dmi b/icons/obj/atmospherics/blue_pipe_tank.dmi
deleted file mode 100644
index 3d2ee4c9d69..00000000000
Binary files a/icons/obj/atmospherics/blue_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/digital_valve.dmi b/icons/obj/atmospherics/digital_valve.dmi
deleted file mode 100644
index 136523b7223..00000000000
Binary files a/icons/obj/atmospherics/digital_valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/dp_vent_pump.dmi b/icons/obj/atmospherics/dp_vent_pump.dmi
deleted file mode 100644
index 86dae2cf435..00000000000
Binary files a/icons/obj/atmospherics/dp_vent_pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/filter.dmi b/icons/obj/atmospherics/filter.dmi
deleted file mode 100644
index 99afb55d22b..00000000000
Binary files a/icons/obj/atmospherics/filter.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/mainspipe.dmi b/icons/obj/atmospherics/mainspipe.dmi
deleted file mode 100644
index df6c2bc0d3a..00000000000
Binary files a/icons/obj/atmospherics/mainspipe.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/n2o_pipe_tank.dmi b/icons/obj/atmospherics/n2o_pipe_tank.dmi
deleted file mode 100644
index 40efbd9f15d..00000000000
Binary files a/icons/obj/atmospherics/n2o_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/omni_devices.dmi b/icons/obj/atmospherics/omni_devices.dmi
deleted file mode 100644
index 44497310026..00000000000
Binary files a/icons/obj/atmospherics/omni_devices.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/orange_pipe_tank.dmi b/icons/obj/atmospherics/orange_pipe_tank.dmi
deleted file mode 100644
index cc9442b8033..00000000000
Binary files a/icons/obj/atmospherics/orange_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/passive_gate.dmi b/icons/obj/atmospherics/passive_gate.dmi
deleted file mode 100644
index 42e8c9dc74f..00000000000
Binary files a/icons/obj/atmospherics/passive_gate.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_manifold.dmi b/icons/obj/atmospherics/pipe_manifold.dmi
deleted file mode 100644
index 1e6f5750d1b..00000000000
Binary files a/icons/obj/atmospherics/pipe_manifold.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_tank.dmi b/icons/obj/atmospherics/pipe_tank.dmi
deleted file mode 100644
index 72310a2657d..00000000000
Binary files a/icons/obj/atmospherics/pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pipe_vent.dmi b/icons/obj/atmospherics/pipe_vent.dmi
deleted file mode 100644
index e40f5946cf8..00000000000
Binary files a/icons/obj/atmospherics/pipe_vent.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/portables_connector.dmi b/icons/obj/atmospherics/portables_connector.dmi
deleted file mode 100644
index c651b959990..00000000000
Binary files a/icons/obj/atmospherics/portables_connector.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/pump.dmi b/icons/obj/atmospherics/pump.dmi
deleted file mode 100644
index e44a21991ba..00000000000
Binary files a/icons/obj/atmospherics/pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/red_orange_pipe_tank.dmi b/icons/obj/atmospherics/red_orange_pipe_tank.dmi
deleted file mode 100644
index 1770c46312a..00000000000
Binary files a/icons/obj/atmospherics/red_orange_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/red_pipe_tank.dmi b/icons/obj/atmospherics/red_pipe_tank.dmi
deleted file mode 100644
index 3fedc659e03..00000000000
Binary files a/icons/obj/atmospherics/red_pipe_tank.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/relief_valve.dmi b/icons/obj/atmospherics/relief_valve.dmi
deleted file mode 100644
index 485bf792b19..00000000000
Binary files a/icons/obj/atmospherics/relief_valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/valve.dmi b/icons/obj/atmospherics/valve.dmi
deleted file mode 100644
index 9ce45199bd7..00000000000
Binary files a/icons/obj/atmospherics/valve.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/vent_pump.dmi b/icons/obj/atmospherics/vent_pump.dmi
deleted file mode 100644
index 6b1f57baa44..00000000000
Binary files a/icons/obj/atmospherics/vent_pump.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/vent_scrubber.dmi b/icons/obj/atmospherics/vent_scrubber.dmi
deleted file mode 100644
index 7133072cd79..00000000000
Binary files a/icons/obj/atmospherics/vent_scrubber.dmi and /dev/null differ
diff --git a/icons/obj/atmospherics/volume_pump.dmi b/icons/obj/atmospherics/volume_pump.dmi
deleted file mode 100644
index e3ff3cf9854..00000000000
Binary files a/icons/obj/atmospherics/volume_pump.dmi and /dev/null differ
diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi
index 3d287a2365a..3f66fa90cd6 100644
Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ
diff --git a/icons/obj/cigarettes.dmi b/icons/obj/cigarettes.dmi
index a0133e5b383..97710ac4805 100644
Binary files a/icons/obj/cigarettes.dmi and b/icons/obj/cigarettes.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index f7f54357fdb..06bb96f43a5 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index f942f9fd8cd..30f34f01b4f 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi
index 30cdfae56ef..aae2d9df6fb 100644
Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index 7400d5d63ef..62fc0a6120c 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi
index bb7a1ce9188..39789326c49 100644
Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ
diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi
index 97a11d05cad..444863abd71 100644
Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ
diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi
index 7351c8c7483..35ce076db6c 100644
Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ
diff --git a/icons/obj/decorations.dmi b/icons/obj/decorations.dmi
index 8bbfa2e3592..79acc379c39 100644
Binary files a/icons/obj/decorations.dmi and b/icons/obj/decorations.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index b5c25d474d1..75838e00075 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/hydroponics/equipment.dmi b/icons/obj/hydroponics/equipment.dmi
index 9ad33b40e9d..3cb9e63cb38 100644
Binary files a/icons/obj/hydroponics/equipment.dmi and b/icons/obj/hydroponics/equipment.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index bc7e927aab2..e30f3e44392 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/obj/kitchen.dmi b/icons/obj/kitchen.dmi
index 0e9ee4649ac..40a8d2f7452 100644
Binary files a/icons/obj/kitchen.dmi and b/icons/obj/kitchen.dmi differ
diff --git a/icons/obj/paper.dmi b/icons/obj/paper.dmi
deleted file mode 100644
index 55781076833..00000000000
Binary files a/icons/obj/paper.dmi and /dev/null differ
diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi
index 60eb897b2b1..51e9b66ee30 100644
Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ
diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi
index 2a47e6817bb..c30eb425dda 100644
Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ
diff --git a/icons/obj/rig_modules.dmi b/icons/obj/rig_modules.dmi
deleted file mode 100644
index 90b1873d58e..00000000000
Binary files a/icons/obj/rig_modules.dmi and /dev/null differ
diff --git a/icons/obj/statue.dmi b/icons/obj/statue.dmi
index bcaddb9f34f..2d9ca959b1b 100644
Binary files a/icons/obj/statue.dmi and b/icons/obj/statue.dmi differ
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index 1ef7016307d..c51d2903b14 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/nano/images/Cyberiad_nanomap_z1.png b/nano/images/Cyberiad_nanomap_z1.png
index ff8c0bf01fe..126f9021dac 100644
Binary files a/nano/images/Cyberiad_nanomap_z1.png and b/nano/images/Cyberiad_nanomap_z1.png differ
diff --git a/nano/images/Delta_nanomap_z1.png b/nano/images/Delta_nanomap_z1.png
index 42499592f85..f0f4e31bca3 100644
Binary files a/nano/images/Delta_nanomap_z1.png and b/nano/images/Delta_nanomap_z1.png differ
diff --git a/nano/images/MetaStation_nanomap_z1.png b/nano/images/MetaStation_nanomap_z1.png
index 57bdbda01f9..280e28a2bc3 100644
Binary files a/nano/images/MetaStation_nanomap_z1.png and b/nano/images/MetaStation_nanomap_z1.png differ
diff --git a/nano/layouts/layout_default.tmpl b/nano/layouts/layout_default.tmpl
index 2c0953fc0a9..d3a86461a76 100644
--- a/nano/layouts/layout_default.tmpl
+++ b/nano/layouts/layout_default.tmpl
@@ -21,10 +21,10 @@
{{:helper.link('Hide Map', 'close', {'showMap' : 0})}}
Zoom Level:
- x1.0
- x1.5
- x2.0
- x2.5
+ x1
+ x2
+ x4
+ x8
@@ -44,4 +44,4 @@
\ No newline at end of file
+
diff --git a/nano/layouts/layout_program.tmpl b/nano/layouts/layout_program.tmpl
index eb0ac27c8bf..4d1dc615fc9 100644
--- a/nano/layouts/layout_program.tmpl
+++ b/nano/layouts/layout_program.tmpl
@@ -58,10 +58,10 @@
{{:helper.link('Hide Map', 'close', {'showMap' : 0})}}
Zoom Level:
- x1.0
- x1.5
- x2.0
- x2.5
+ x1
+ x2
+ x4
+ x8
@@ -82,4 +82,4 @@
\ No newline at end of file
+
diff --git a/nano/package-lock.json b/nano/package-lock.json
index b96939c5d9a..14642f304d6 100644
--- a/nano/package-lock.json
+++ b/nano/package-lock.json
@@ -1728,9 +1728,9 @@
"dev": true
},
"elliptic": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
- "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
+ "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
diff --git a/nano/templates/atmos_control_map_header.tmpl b/nano/templates/atmos_control_map_header.tmpl
index 9a741a249e5..c64fce951f7 100644
--- a/nano/templates/atmos_control_map_header.tmpl
+++ b/nano/templates/atmos_control_map_header.tmpl
@@ -1,8 +1,8 @@
{{:helper.link('Show Detail List', 'file-text', {'showMap' : 0})}}
Zoom Level:
- x1.0
- x1.5
- x2.0
- x2.5
-
\ No newline at end of file
+ x1
+ x2
+ x4
+ x8
+
diff --git a/nano/templates/comm_console.tmpl b/nano/templates/comm_console.tmpl
index e7224df2ecc..2c36a011ac6 100644
--- a/nano/templates/comm_console.tmpl
+++ b/nano/templates/comm_console.tmpl
@@ -71,13 +71,6 @@ Used In File(s): /code/game/machinery/computers/communications.dm
{{:helper.link('Restart Nano-Mob Hunter GO! Server','power-off',{'operation':'RestartNanoMob'})}}
-
- {{if data.atcSquelched}}
- {{:helper.link('Enable ATC Relay', 'signal', {'operation': 'ToggleATC'})}}
- {{else}}
- {{:helper.link('Disable ATC Relay', 'signal', {'operation': 'ToggleATC'})}}
- {{/if}}
-
{{else data.screen==2}}
diff --git a/nano/templates/sec_camera_map_header.tmpl b/nano/templates/sec_camera_map_header.tmpl
index 8052673efe1..ebe90da3015 100644
--- a/nano/templates/sec_camera_map_header.tmpl
+++ b/nano/templates/sec_camera_map_header.tmpl
@@ -16,8 +16,8 @@ Used In File(s): \code\game\machinery\computer\camera.dm
Zoom Level:
- x1.0
- x1.5
- x2.0
- x2.5
-
\ No newline at end of file
+ x1
+ x2
+ x4
+ x8
+
diff --git a/paradise.dme b/paradise.dme
index b687e6c9db2..5e88b673231 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -87,6 +87,7 @@
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
#include "code\__HELPERS\global_lists.dm"
+#include "code\__HELPERS\heap.dm"
#include "code\__HELPERS\icon_smoothing.dm"
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\lists.dm"
@@ -108,8 +109,6 @@
#include "code\__HELPERS\sorts\InsertSort.dm"
#include "code\__HELPERS\sorts\MergeSort.dm"
#include "code\__HELPERS\sorts\TimSort.dm"
-#include "code\_DATASTRUCTURES\heap.dm"
-#include "code\_DATASTRUCTURES\stacks.dm"
#include "code\_globalvars\configuration.dm"
#include "code\_globalvars\game_modes.dm"
#include "code\_globalvars\genetics.dm"
@@ -136,7 +135,6 @@
#include "code\_onclick\observer.dm"
#include "code\_onclick\other_mobs.dm"
#include "code\_onclick\overmind.dm"
-#include "code\_onclick\rig.dm"
#include "code\_onclick\telekinesis.dm"
#include "code\_onclick\hud\_defines.dm"
#include "code\_onclick\hud\action_button.dm"
@@ -207,8 +205,6 @@
#include "code\controllers\controller.dm"
#include "code\controllers\failsafe.dm"
#include "code\controllers\globals.dm"
-#include "code\controllers\hooks-defs.dm"
-#include "code\controllers\hooks.dm"
#include "code\controllers\master.dm"
#include "code\controllers\subsystem.dm"
#include "code\controllers\verbs.dm"
@@ -220,10 +216,10 @@
#include "code\controllers\subsystem\atoms.dm"
#include "code\controllers\subsystem\changelog.dm"
#include "code\controllers\subsystem\chat.dm"
-#include "code\controllers\subsystem\dcs.dm"
#include "code\controllers\subsystem\events.dm"
#include "code\controllers\subsystem\fires.dm"
#include "code\controllers\subsystem\garbage.dm"
+#include "code\controllers\subsystem\ghost_spawns.dm"
#include "code\controllers\subsystem\holiday.dm"
#include "code\controllers\subsystem\icon_smooth.dm"
#include "code\controllers\subsystem\idlenpcpool.dm"
@@ -250,8 +246,10 @@
#include "code\controllers\subsystem\throwing.dm"
#include "code\controllers\subsystem\ticker.dm"
#include "code\controllers\subsystem\timer.dm"
+#include "code\controllers\subsystem\titlescreen.dm"
#include "code\controllers\subsystem\vote.dm"
#include "code\controllers\subsystem\weather.dm"
+#include "code\controllers\subsystem\processing\dcs.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\processing.dm"
@@ -274,6 +272,7 @@
#include "code\datums\hud.dm"
#include "code\datums\log_record.dm"
#include "code\datums\log_viewer.dm"
+#include "code\datums\logging.dm"
#include "code\datums\mind.dm"
#include "code\datums\mixed.dm"
#include "code\datums\mutable_appearance.dm"
@@ -612,7 +611,6 @@
#include "code\game\jobs\access.dm"
#include "code\game\jobs\job_exp.dm"
#include "code\game\jobs\job_objective.dm"
-#include "code\game\jobs\job_scaling.dm"
#include "code\game\jobs\jobs.dm"
#include "code\game\jobs\whitelist.dm"
#include "code\game\jobs\job\central.dm"
@@ -686,7 +684,6 @@
#include "code\game\machinery\syndicatebomb.dm"
#include "code\game\machinery\teleporter.dm"
#include "code\game\machinery\transformer.dm"
-#include "code\game\machinery\turntable.dm"
#include "code\game\machinery\turret_control.dm"
#include "code\game\machinery\vending.dm"
#include "code\game\machinery\washing_machine.dm"
@@ -872,6 +869,7 @@
#include "code\game\objects\items\toys.dm"
#include "code\game\objects\items\trash.dm"
#include "code\game\objects\items\devices\aicard.dm"
+#include "code\game\objects\items\devices\airlock_painter.dm"
#include "code\game\objects\items\devices\autopsy.dm"
#include "code\game\objects\items\devices\camera_bug.dm"
#include "code\game\objects\items\devices\chameleonproj.dm"
@@ -941,6 +939,7 @@
#include "code\game\objects\items\tools\wrench.dm"
#include "code\game\objects\items\weapons\AI_modules.dm"
#include "code\game\objects\items\weapons\alien_specific.dm"
+#include "code\game\objects\items\weapons\batons.dm"
#include "code\game\objects\items\weapons\bee_briefcase.dm"
#include "code\game\objects\items\weapons\cards_ids.dm"
#include "code\game\objects\items\weapons\cash.dm"
@@ -989,7 +988,6 @@
#include "code\game\objects\items\weapons\staff.dm"
#include "code\game\objects\items\weapons\stock_parts.dm"
#include "code\game\objects\items\weapons\stunbaton.dm"
-#include "code\game\objects\items\weapons\swords_axes_etc.dm"
#include "code\game\objects\items\weapons\tape.dm"
#include "code\game\objects\items\weapons\teleportation.dm"
#include "code\game\objects\items\weapons\teleprod.dm"
@@ -1198,7 +1196,6 @@
#include "code\modules\admin\sql_notes.dm"
#include "code\modules\admin\stickyban.dm"
#include "code\modules\admin\topic.dm"
-#include "code\modules\admin\ToRban.dm"
#include "code\modules\admin\watchlist.dm"
#include "code\modules\admin\DB ban\functions.dm"
#include "code\modules\admin\permissionverbs\permissionedit.dm"
@@ -1208,8 +1205,8 @@
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"
#include "code\modules\admin\verbs\adminsay.dm"
-#include "code\modules\admin\verbs\alt_check.dm"
#include "code\modules\admin\verbs\antag-ooc.dm"
+#include "code\modules\admin\verbs\asays.dm"
#include "code\modules\admin\verbs\atmosdebug.dm"
#include "code\modules\admin\verbs\BrokenInhands.dm"
#include "code\modules\admin\verbs\cinematic.dm"
@@ -1237,7 +1234,6 @@
#include "code\modules\admin\verbs\randomverbs.dm"
#include "code\modules\admin\verbs\serialization.dm"
#include "code\modules\admin\verbs\space_transitions.dm"
-#include "code\modules\admin\verbs\spawnfloorcluwne.dm"
#include "code\modules\admin\verbs\striketeam.dm"
#include "code\modules\admin\verbs\striketeam_syndicate.dm"
#include "code\modules\admin\verbs\ticklag.dm"
@@ -1330,9 +1326,6 @@
#include "code\modules\buildmode\submodes\save.dm"
#include "code\modules\buildmode\submodes\throwing.dm"
#include "code\modules\buildmode\submodes\variable_edit.dm"
-#include "code\modules\busy_space\air_traffic.dm"
-#include "code\modules\busy_space\loremaster.dm"
-#include "code\modules\busy_space\organizations.dm"
#include "code\modules\client\asset_cache.dm"
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"
@@ -1382,7 +1375,6 @@
#include "code\modules\clothing\shoes\magboots.dm"
#include "code\modules\clothing\shoes\miscellaneous.dm"
#include "code\modules\clothing\spacesuits\alien.dm"
-#include "code\modules\clothing\spacesuits\breaches.dm"
#include "code\modules\clothing\spacesuits\chronosuit.dm"
#include "code\modules\clothing\spacesuits\ert.dm"
#include "code\modules\clothing\spacesuits\hardsuit.dm"
@@ -1390,24 +1382,6 @@
#include "code\modules\clothing\spacesuits\plasmamen.dm"
#include "code\modules\clothing\spacesuits\syndi.dm"
#include "code\modules\clothing\spacesuits\void.dm"
-#include "code\modules\clothing\spacesuits\rig\rig.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_armormod.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_attackby.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_pieces.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_verbs.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_wiring.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\combat.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\computer.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\modules.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\ninja.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\utility.dm"
-#include "code\modules\clothing\spacesuits\rig\modules\vision.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\alien.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\combat.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\ert_suits.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\light.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\merc.dm"
-#include "code\modules\clothing\spacesuits\rig\suits\station.dm"
#include "code\modules\clothing\suits\alien.dm"
#include "code\modules\clothing\suits\armor.dm"
#include "code\modules\clothing\suits\bio.dm"
@@ -1527,7 +1501,6 @@
#include "code\modules\examine\descriptions\weapons.dm"
#include "code\modules\ext_scripts\irc.dm"
#include "code\modules\ext_scripts\python.dm"
-#include "code\modules\fancytitle\fancytitle.dm"
#include "code\modules\fish\fish_eggs.dm"
#include "code\modules\fish\fish_items.dm"
#include "code\modules\fish\fish_types.dm"
@@ -1570,7 +1543,6 @@
#include "code\modules\food_and_drinks\kitchen_machinery\gibber.dm"
#include "code\modules\food_and_drinks\kitchen_machinery\grill_new.dm"
#include "code\modules\food_and_drinks\kitchen_machinery\icecream_vat.dm"
-#include "code\modules\food_and_drinks\kitchen_machinery\icecream_vat_2.dm"
#include "code\modules\food_and_drinks\kitchen_machinery\juicer.dm"
#include "code\modules\food_and_drinks\kitchen_machinery\kitchen_machine.dm"
#include "code\modules\food_and_drinks\kitchen_machinery\microwave.dm"
@@ -1850,7 +1822,6 @@
#include "code\modules\mob\living\carbon\human\species\vox.dm"
#include "code\modules\mob\living\carbon\human\species\vulpkanin.dm"
#include "code\modules\mob\living\carbon\human\species\wryn.dm"
-#include "code\modules\mob\living\carbon\human\species\zombies.dm"
#include "code\modules\mob\living\silicon\death.dm"
#include "code\modules\mob\living\silicon\emote.dm"
#include "code\modules\mob\living\silicon\laws.dm"
@@ -2121,7 +2092,6 @@
#include "code\modules\modular_computers\NTNet\NTNet_relay.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm"
#include "code\modules\nano\nanoexternal.dm"
-#include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoui.dm"
#include "code\modules\nano\subsystem.dm"
#include "code\modules\nano\interaction\admin.dm"
@@ -2168,6 +2138,7 @@
#include "code\modules\paperwork\photography.dm"
#include "code\modules\paperwork\silicon_photography.dm"
#include "code\modules\paperwork\stamps.dm"
+#include "code\modules\paperwork\ticketmachine.dm"
#include "code\modules\pda\ai.dm"
#include "code\modules\pda\app.dm"
#include "code\modules\pda\cart.dm"
@@ -2421,7 +2392,6 @@
#include "code\modules\surgery\other.dm"
#include "code\modules\surgery\plastic_surgery.dm"
#include "code\modules\surgery\remove_embedded_object.dm"
-#include "code\modules\surgery\rig_removal.dm"
#include "code\modules\surgery\robotics.dm"
#include "code\modules\surgery\surgery.dm"
#include "code\modules\surgery\tools.dm"
@@ -2503,8 +2473,6 @@
#include "code\modules\vehicle\speedbike.dm"
#include "code\modules\vehicle\sportscar.dm"
#include "code\modules\vehicle\vehicle.dm"
-#include "code\modules\zombie\items.dm"
-#include "code\modules\zombie\organs.dm"
#include "goon\code\datums\browserOutput.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
diff --git a/sound/misc/announce_dig.ogg b/sound/misc/announce_dig.ogg
new file mode 100644
index 00000000000..2342d2eb505
Binary files /dev/null and b/sound/misc/announce_dig.ogg differ
diff --git a/sound/turntable/testloop.ogg b/sound/turntable/testloop.ogg
deleted file mode 100644
index 453ad568fa2..00000000000
Binary files a/sound/turntable/testloop.ogg and /dev/null differ
diff --git a/sound/turntable/testloop1.ogg b/sound/turntable/testloop1.ogg
deleted file mode 100644
index 5e1210bfd06..00000000000
Binary files a/sound/turntable/testloop1.ogg and /dev/null differ
diff --git a/sound/turntable/testloop2.ogg b/sound/turntable/testloop2.ogg
deleted file mode 100644
index 5feaa762936..00000000000
Binary files a/sound/turntable/testloop2.ogg and /dev/null differ
diff --git a/sound/turntable/testloop3.ogg b/sound/turntable/testloop3.ogg
deleted file mode 100644
index 9ac0e02b773..00000000000
Binary files a/sound/turntable/testloop3.ogg and /dev/null differ
diff --git a/strings/tips.txt b/strings/tips.txt
index b2df1c17482..4218e01bbfe 100644
--- a/strings/tips.txt
+++ b/strings/tips.txt
@@ -1,4 +1,4 @@
-Where the space map levels connect is randomized every round, but are otherwise kept consistent within rounds.
+Space map levels' connections are randomized between rounds, but are otherwise kept consistent in the same round.
You can catch thrown items by toggling on your throw mode with an empty hand active.
To crack the safe in the vault, use a stethoscope or thermal drill.
You can climb onto a table by dragging yourself onto one. This takes some time. Clicking on a table that someone else is climbing onto will knock them down.
@@ -9,8 +9,8 @@ You can change the control scheme by pressing tab. One uses WASD for movement, w
Firesuits and winter coats offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all.
Glass shards can be welded to make glass, and metal rods can be welded to make metal. Ores can be welded too, but this takes a lot of fuel.
If you need to drag multiple people either to safety or to space, bring a locker over and stuff them all in before hauling them off.
-You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place them on a table by clicking on it, or throw them by toggling on throwing.
-Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking.
+You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on the grab itself. An aggressive grab will allow you to place them on a table by clicking on it, or throw them by toggling on throwing.
+Holding alt and left clicking a tile will allow you to see its contents in the top right window panel, which is much faster than right clicking.
The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting!
You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand.
You can recolor certain items like jumpsuits and gloves in washing machines by also throwing in a crayon.
@@ -21,7 +21,7 @@ When in doubt about technicial issues, clear your cache (byond launcher > cogwhe
Most things have special interactions with your middle mouse button, alt, shift, and control click. Experiment!
If you find yourself in a fistfight with another player, running away and calling for help is a perfectly viable option.
Different weapons have different strengths. Some weapons, such as spears, floor tiles, and throwing stars, deal more damage when thrown compared to when attacked normally.
-A thrown glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch.
+Clicking on a tile on harm intent with a glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch.
When dealing with security, you can often get your sentence negated entirely through cooperation and deception.
The P2P chat function found on tablet computers allows for a stealthy way to communicate with people.
We were all new once, be patient and guide new players in the right direction.
@@ -36,8 +36,8 @@ As the Captain, you have absolute access and control over the station, but this
As the Chief Medical Officer, your hypospray is like a refillable instant injection syringe that can hold 30 units and the unlike standard hypospray, yours is able to be filled with harmful reagents and injects without telling anyone what have you exactly injected the person with.
As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and geneticists during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting.
As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva.
-As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. Using disarm intent will intentionally fail the surgery step.
-As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable.
+As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone.
+As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable right now, but they might be clonable later.
As a Medical Doctor, treating plasmamen is not impossible! Salbutamol stops them from suffocating and showers stop them from burning alive. You can even perform surgery on them by doing the procedure on a roller bed under a shower.
As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment!
As a Chemist, some chemicals can only be synthesized by heating up the contents with a chemical heater or manually with lighters and similar tools.
@@ -50,7 +50,7 @@ As the Research Director, you can take AIs out of their cores by loading them in
As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled.
As the Research Director, you can spy on and even forge PDA communications with the message monitor console! The key is in your office.
As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract.
-As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device, or if researched, hit the anomaly with an anomaly analyzer. This will leave behind an anomaly core, which can be used to construct a Phazon mech or reactive armors!
+As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device. This will leave behind an anomaly core, which can be used to construct a Phazon mech!
As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions.
As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on an anomaly core, you can build a Phazon mech!
As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil.
@@ -59,12 +59,12 @@ As a Roboticist, you can augment people with cyborg limbs. Augmented limbs can e
As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them, they aren't wearing agent IDs, or aren't using digital camouflage as changelings.
As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt.
As the AI, you can take pictures with your camera and upload them to newscasters.
-As a Cyborg, choose your module carefully, as only cutting and mending your reset wire or using a cyborg reset module will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs.
+As a Cyborg, choose your module carefully, as only a cyborg reset module will let you repick it.
As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands.
As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world!
As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds.
-As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them.
-As a Medical Cyborg, you can fully perform surgery and even augment people.
+As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by using your magnetic gripper.
+As a Medical Cyborg, you can partially perform surgery, as you cannot replace organs, but you cannot fail any surgery steps.
As a Janitor Cyborg, you are the bane of all slaughter demons. Cleaning up blood stains will severely gimp them.
As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints.
As the Chief Engineer, your hardsuit is significantly better than everybody else's. It has the best features of both engineering and atmospherics hardsuits, boasting nigh-invulnerability to radiation and all atmospheric conditions.
@@ -73,10 +73,10 @@ As an Engineer, you can electrify grilles by placing wire "nodes" beneath them:
As an Engineer, return to Engineering once in a while to check on the engine and SMES cells. It's always a good idea to make sure containment isn't compromised.
As an Engineer, you can power the station solely with the solar arrays. They will provide just enough electricity to power the station, however their output is still much worse compared to the true engine.
As an Engineer, you can cool a supermatter shard by spraying it with a fire extinguisher. Only for the brave!
-As an Engineer, you can repair windows by using a welding tool on them while on any intent other than harm.
+As an Engineer, you can repair windows by using a welding tool on them while on help intent.
As an Engineer, you can lock APCs, fire alarms, emitters, and radiation collectors using your ID card to prevent others from disabling them.
As an Engineer, don't underestimate the humble P.A.C.M.A.N. generators. With upgraded parts, a couple units working in tandem are sufficient to take over for an exploded engine or shattered solars.
-As an Engineer, you can pry open secure storage blast doors by disabling the engine room APC's main breaker. This is obviously a bad idea if the engine is running.
+As an Engineer, you can pry open secure storage blast doors by turning off the secure storage APC's enviroment power channel.
As an Atmospheric Technician, look into replacing your gas pumps with volumetric gas pumps, as those move air in flat numerical amounts, rather than percentages which leave trace gases.
As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof hardsuit.
As an Atmospheric Technician, your backpack firefighter tank can launch cryofrost. This resin will extinguish fires and very quickly; it's ideal for plasma fires!
@@ -88,7 +88,7 @@ As the Warden, keep a close eye on the armory at all times, as it is a favored s
As the Warden, if a prisoner's crimes are heinous enough you can put them in permabrig or the gulag. Make sure to check on them once in a while!
As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals.
As a Security Officer, communicate and coordinate with your fellow officers using the security channel (:s) to avoid confusion.
-As a Security Officer, your sechuds or HUDsunglasses can not only see crewmates' job assignments and criminal status, but also if they are mindshield implanted. You can tell by the flashing blue outline around their job icon.
+As a Security Officer, your sechuds or HUDsunglasses let you see crewmates' job assignments, their criminal status, and whether they have a mindshield or not. A flashing green border around their job icon means they are mindshielded.
As a Security Officer, mindshield implants can only prevent someone from being turned into a cultist. It will not de-cult them if they have already been converted.
As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them.
As the Detective, keep in mind that people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department.
@@ -111,7 +111,7 @@ As the Chaplain, you are much more likely to get a response by praying to the go
As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo.
As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations.
As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist.
-As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen!
+As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine and plasma (blumpkin) + radium (glowshrooms) equals unstable mutagen!
As the Chef, you can create a very wide variety of food with the crafting menu. You can find it by looking for the hammer icon near your intents.
As the Chef, you can rename your custom made food with a pen.
As the Chef, if you are low on ingredients, consider making something that has several servings to last longer among the crew.
@@ -121,8 +121,8 @@ As a Janitor, mousetraps can be used to create bombs or booby-trap containers.
As the Librarian, be sure to keep the shelves stocked and the library clean for crew.
As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them!
As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it.
-As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more!
-As a Shaft Miner, the western side of the Asteroid has a lot more rare minerals than on the east, but is a lot more dangerous.
+As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, plasma sheets, rare seeds from hydroponics, technology disks from R&D, and more!
+As a Shaft Miner, the north side of Lavaland has a lot more rare minerals than on the south, but is a lot more dangerous.
As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die.
As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment!
As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you!
@@ -130,14 +130,14 @@ As a Traitor, the Captain and the Head of Security are two of the most difficult
As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool.
As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others.
As a Traitor, the syndicate encryption key is very useful for coordinating plans with your fellow traitors -- or, of course, betraying them.
-As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, welding tools, cigars and e-cigs will all explode when used.
+As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, cigars and e-cigs will all explode when used.
As a Traitor, if you can find another Traitor and pool your TC you can buy a mega surplus crate, which costs 40TC but contains a lot of random syndicate gear.
As a Nuclear Operative, communication is key! Use ; to speak to your fellow operatives and coordinate an attack plan.
As a Nuclear Operative, you should look into purchasing a syndicate cyborg, as they can provide heavy fire support, full access, are immune to conventional stuns, and can easily take down the AI.
As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire.
As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life.
As a Monkey, you can still wear a few human items, such as backpacks, gas masks, and hats, and still have two free hands.
-As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. This disables your doomsday device if it is active.
+As the Malfunctioning AI, you can shunt to an APC if the situation gets bad.
As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them.
As an Alien, your melee prowess is unmatched, but your ranged abilities are sorely lacking. Make use of corners to force a melee confrontation!
As an Alien, you take double damage from all burn attacks, such as lasers, welding tools, and fires. Furthermore, fire can destroy your resin and eggs. Expose areas to space to starve away any flamethrower fires before they can do damage!
@@ -155,11 +155,10 @@ As a Changeling, the Extract DNA sting counts for your genome absorb objective,
As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to rechoose your powers, the DNA of whoever you absorbed, the memory of the absorbed, and some samples of things the absorbed said.
As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win.
As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face.
-As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists of Nar'Sie nearby, but will create a fire where the rune stands on use.
+As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists nearby, but will create a fire where the rune stands on use.
As a Cultist, you can create an army of manifested goons using a combination of the Manifest rune, which creates homunculi from ghosts, and the Blood Drain rune, which drains life from anyone standing on any blood drain rune.
-As a Cultist, check the alert in the upper-right of your screen for all the details about your cult's current status and objective.
You can deconvert Cultists by feeding them large amounts of holy water.
-The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them.
+The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against cults and large amounts of it are a great contributor to success against them.
As a Wizard, you can turn people to stone, then animate the resulting statue with a staff of animation to create an extremely powerful minion, for all of 5 minutes at least.
As a Wizard, the fireball spell performs very poorly at close range, as it can easily catch you in the blast. It is best used as a form of artillery down long hallways.
As a Wizard, summoning guns will give everyone anything from a floral somatoray to a pulse rifle. Use at your own risk!
@@ -170,11 +169,10 @@ As an Abductor Agent, the combat mode vest has much higher resistance to every k
As an Abductor, the baton can cycle between four modes: stun, sleep, cuff and probe.
As a Revenant, the Chaplain is your worst enemy, as they can damage you massively with the null rod and make large swaths of the station impassable with holy water.
As a Revenant, your essence is also your health, so revealing yourself in front of humans to harvest the essence of the living is much safer if you've already stocked up on essences from poorly guarded corpses.
-As a Revenant, your Defile ability removes holy water from tiles in a small radius, along with salt, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water. It can also be used to open morgue trays!
+As a Revenant, your Defile ability removes holy water from tiles in a small radius, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water.
As a Revenant, your Overload Lights ability will only shock humans with lights if the lights are still on after a brief delay.
As a Revenant, your Malfunction ability in general damages machinery and mechanical objects, possibly even emagging some objects. Experiment!
-As a Revenant, the illness inflicted on humans by Blight can be easily cured by lying down or with holy water, making it best used on targets that have no time to lie down, such as humans in combat.
-As a Revenant, fastmos is your friend. Breaking windows to space can cause massive damage and mayhem.
+As a Revenant, space is your friend. Breaking windows to space can cause massive damage and mayhem.
As a Swarmer, you can deconstruct more things than you think. Try deconstructing light switches, buttons, air alarms and more. Experiment!
As a Swarmer, you can teleport fellow swarmers away if you think they are in danger.
As a Ghost, you can double click on just about anything to follow it. Or just warp around!
@@ -202,4 +200,4 @@ You can make lasertag turrets, for the ultimate lasertag tournament.
Blob structures take half damage from brute damage. Use lasers.
You can hide paper in vents, but you have to use a screwdriver to open it first.
While the Standard Operating Procedures aren't fully rules, they are there for safety and professional reasons.
-Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING.
\ No newline at end of file
+Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING.
diff --git a/tgui/docs/tutorial-and-examples.md b/tgui/docs/tutorial-and-examples.md
index 08bb6f92321..590ea0f0ee7 100644
--- a/tgui/docs/tutorial-and-examples.md
+++ b/tgui/docs/tutorial-and-examples.md
@@ -37,7 +37,7 @@ powerful interactions for embedded objects or remote access.
Let's start with a very basic hello world.
```dm
-/obj/machinery/my_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)
+/obj/machinery/my_machine/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, "my_machine", name, 300, 300, master_ui, state)
@@ -240,7 +240,7 @@ and builds a new array based on what was returned by that function.
```
If you need more examples of what you can do with React, see the
-[interface conversion guide](docs/converting-old-tgui-interfaces.md).
+[interface conversion guide](docs/converting-old-nano-interfaces.md).
#### Splitting UIs into smaller, modular components
@@ -294,7 +294,7 @@ here's what you need (note that you'll probably be forced to clean your shit up
upon code review):
```dm
-/obj/copypasta/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) // Remember to use the appropriate state.
+/obj/copypasta/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) // Remember to use the appropriate state.
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state)
diff --git a/tgui/yarn.lock b/tgui/yarn.lock
index 905d2871b69..84cab5eb253 100644
--- a/tgui/yarn.lock
+++ b/tgui/yarn.lock
@@ -1241,9 +1241,9 @@ bluebird@^3.5.5:
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.8"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
- integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+ version "4.11.9"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+ integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
body-parser@1.19.0:
version "1.19.0"
@@ -2198,9 +2198,9 @@ electron-to-chromium@^1.3.390:
integrity sha512-DbCBdwtARI0l3e3m6ZIxVaTNahb6dSsmGjuag/twiVcWuM4MSpL5IfsJsJSyqLqxosE/m0CXlZaBmxegQW/dAg==
elliptic@^6.0.0:
- version "6.5.2"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
- integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
+ integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
@@ -3730,9 +3730,9 @@ lodash.uniq@^4.5.0:
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
- version "4.17.15"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
- integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+ version "4.17.19"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
+ integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
loose-envify@^1.0.0, loose-envify@^1.4.0:
version "1.4.0"
diff --git a/tools/github-actions/README.MD b/tools/github-actions/README.MD
new file mode 100644
index 00000000000..74b31f5bdd6
--- /dev/null
+++ b/tools/github-actions/README.MD
@@ -0,0 +1,6 @@
+# GitHub Actions Scripts
+
+This folder contains all the script and tools required for GitHub actions. If you add something to this directory, **PLEASE** document it in here
+
+- `nanomap-renderer` - A linux application to render NanoMap images of the ingame maps automatically. Based off of SpacemanDMM (Modified source [here](https://github.com/AffectedArc07/ParaSpacemanDMM), original source [here](https://github.com/Spacemaniac/SpacemanDMM))
+- `nanomap-renderer-invoker.sh` - A script which invokes the render tool and clones the maps to the correct directory
diff --git a/tools/github-actions/nanomap-renderer b/tools/github-actions/nanomap-renderer
new file mode 100755
index 00000000000..69fa906c94e
Binary files /dev/null and b/tools/github-actions/nanomap-renderer differ
diff --git a/tools/github-actions/nanomap-renderer-invoker.sh b/tools/github-actions/nanomap-renderer-invoker.sh
new file mode 100755
index 00000000000..b6fadfac5f1
--- /dev/null
+++ b/tools/github-actions/nanomap-renderer-invoker.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# Generate maps
+tools/github-actions/nanomap-renderer minimap "./_maps/map_files/cyberiad/cyberiad.dmm"
+tools/github-actions/nanomap-renderer minimap "./_maps/map_files/Delta/delta.dmm"
+tools/github-actions/nanomap-renderer minimap "./_maps/map_files/MetaStation/MetaStation.v41A.II.dmm"
+# Move and rename files so the game understands them
+cd "data/nanomaps"
+mv "cyberiad_nanomap_z1.png" "Cyberiad_nanomap_z1.png"
+mv "delta_nanomap_z1.png" "Delta_nanomap_z1.png"
+mv "MetaStation.v41A.II_nanomap_z1.png" "MetaStation_nanomap_z1.png"
+cd "../../"
+cp "data/nanomaps/Cyberiad_nanomap_z1.png" "nano/images"
+cp "data/nanomaps/Delta_nanomap_z1.png" "nano/images"
+cp "data/nanomaps/MetaStation_nanomap_z1.png" "nano/images"
diff --git a/tools/send2server.py b/tools/send2server.py
new file mode 100644
index 00000000000..fd255a3cfb5
--- /dev/null
+++ b/tools/send2server.py
@@ -0,0 +1,14 @@
+# Script to send world/Topic announcements to a server via automated tooling
+# Author: AffectedArc07
+# Takes a message as command line argument
+# Example: python send2server.py "This is a message that will be sent to the server"
+import socket, struct, urllib.parse, sys
+
+message = sys.argv[1]
+commskey = "YOURKEYHERE"
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+cmd = "?hostannounce&key={}&message={}".format(urllib.parse.quote(commskey), urllib.parse.quote(message))
+query = b"\x00\x83" + struct.pack('>H', len(cmd) + 6) + b"\x00\x00\x00\x00\x00" + cmd.encode() + b"\x00"
+sock.connect(("localhost", 6666))
+sock.sendall(query)
|