diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 97b2237a934..551be3fc4ea 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -33,8 +33,12 @@
#define AMBIENT_OCCLUSION 2097152
#define AZERTY 4194304
#define NUMPAD_TARGET 8388606
+#define CHAT_GHOSTPDA 16777212
#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC|NUMPAD_TARGET)
-#define TOGGLES_TOTAL 16777212 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
+#define TOGGLES_TOTAL 33554424 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined.
+
+
+#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC|AMBIENT_OCCLUSION|CHAT_GHOSTPDA)
// Admin attack logs filter system, see /proc/add_attack_logs and /proc/msg_admin_attack
#define ATKLOG_ALL 0
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 6b5807deaef..9e01753d492 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -353,12 +353,12 @@
var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list
-//Mergsorge: uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
+//Mergsorge: uses sortAssoc() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(var/list/L)
var/list/Q = new()
for(var/atom/x in L)
Q[x.name] = x
- return sortList(Q)
+ return sortAssoc(Q)
/proc/mergeLists(var/list/L, var/list/R)
var/Li=1
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index c583617ac23..91c8d6250b4 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -453,7 +453,6 @@
/obj/screen/healths/corgi
icon = 'icons/mob/screen_corgi.dmi'
-
/obj/screen/healths/guardian
name = "summoner health"
icon = 'icons/mob/guardian.dmi'
@@ -467,14 +466,18 @@
screen_loc = ui_healthdoll
var/list/cached_healthdoll_overlays = list() // List of icon states (strings) for overlays
+/obj/screen/healthdoll/Click()
+ if(ishuman(usr))
+ var/mob/living/carbon/H = usr
+ H.check_self_for_injuries()
+
/obj/screen/component_button
var/obj/screen/parent
-
/obj/screen/component_button/Initialize(mapload, obj/screen/new_parent)
. = ..()
parent = new_parent
/obj/screen/component_button/Click(params)
if(parent)
- parent.component_click(src, params)
\ No newline at end of file
+ parent.component_click(src, params)
diff --git a/code/datums/helper_datums/input.dm b/code/datums/helper_datums/input.dm
index 3bcf9509e37..19205a4ab62 100644
--- a/code/datums/helper_datums/input.dm
+++ b/code/datums/helper_datums/input.dm
@@ -1,71 +1,90 @@
/proc/input_async(mob/user=usr, prompt, list/choices)
- var/datum/async_input/A = new(choices, prompt)
- A.show(user)
+ var/datum/async_input/A = new(choices, prompt, , user)
+ A.show()
return A
/proc/input_ranked_async(mob/user=usr, prompt="Order by greatest to least preference", list/choices)
- var/datum/async_input/ranked/A = new(choices, prompt)
- A.show(user)
+ var/datum/async_input/ranked/A = new(choices, prompt, "ranked_input", user)
+ A.show()
+ return A
+
+/proc/input_autocomplete_async(mob/user=usr, prompt="Enter text: ", list/choices)
+ var/datum/async_input/autocomplete/A = new(choices, prompt, "ac_input", user)
+ A.show()
return A
/datum/async_input
var/datum/browser/popup
+ // If associative list, key will be used for display, but the final result will be the value
var/list/choices
+ var/datum/callback/onCloseCb
var/flash = TRUE
var/immediate_submit = FALSE
var/prompt
var/result = null
var/style = "text-align: center;"
+ var/mob/user
var/window_id
var/height = 200
var/width = 400
-/datum/async_input/New(list/new_choices, new_prompt="Pick an option:", new_window_id="async_input")
+/datum/async_input/New(list/new_choices, new_prompt="Pick an option:", new_window_id="async_input", mob/new_user=usr)
choices = new_choices
prompt = new_prompt
window_id = new_window_id
+ user = new_user
+ popup = new(user, window_id, , width, height, src)
/datum/async_input/proc/close()
if(popup)
popup.close()
+ if(result && choices[result])
+ result = choices[result]
+ if(onCloseCb)
+ onCloseCb.Invoke(result)
return result
-/datum/async_input/proc/show(mob/user)
- var/dat = create_ui(user)
- popup = new(user, window_id, , width, height, src)
- popup.set_content(dat)
+// Callback function should take the result as the last argument
+/datum/async_input/proc/on_close(var/datum/callback/cb)
+ onCloseCb = cb
+
+/datum/async_input/proc/show()
+ popup.set_content(create_ui())
if(flash && result == null)
window_flash(user.client)
popup.open()
-/datum/async_input/proc/create_ui(mob/user)
+/datum/async_input/proc/create_ui()
var/dat = "
"
- dat += render_prompt(user)
- dat += render_choices(user)
+ dat += render_prompt()
+ dat += render_choices()
dat += " "
dat += " "
- dat += button("Submit", "submit=1", , result == null && !immediate_submit)
+ dat += render_buttons()
dat += "
"
return dat
-/datum/async_input/proc/render_prompt(mob/user)
+/datum/async_input/proc/render_prompt()
return "[prompt] "
-/datum/async_input/proc/render_choices(mob/user)
+/datum/async_input/proc/render_choices()
var/dat = " "
for(var/choice in choices)
dat += button(choice, "choice=[choice]", choice == result)
dat += " "
return dat
-/datum/async_input/proc/button(label, topic, on=FALSE, disabled=FALSE)
+/datum/async_input/proc/render_buttons()
+ return button("Submit", "submit=1", , result == null && !immediate_submit)
+
+/datum/async_input/proc/button(label, topic, on=FALSE, disabled=FALSE, id="")
var/class = ""
if(on)
class = "linkOn"
if(disabled)
class = "linkOff"
topic = ""
- return "[label] "
+ return "[label] "
/datum/async_input/Topic(href, href_list)
if(href_list["submit"] || href_list["close"])
@@ -74,14 +93,14 @@
if(href_list["choice"])
result = href_list["choice"]
- show(usr)
+ show()
return
/datum/async_input/ranked
height = 400
immediate_submit = TRUE
-/datum/async_input/ranked/render_choices(mob/user)
+/datum/async_input/ranked/render_choices()
var/dat = ""
dat += "
"
for(var/i = 1, i <= choices.len, i++)
@@ -103,13 +122,47 @@
if(href_list["upvote"])
var/index = text2num(href_list["upvote"])
choices.Swap(index, index - 1)
- show(usr)
+ show()
return
if(href_list["downvote"])
var/index = text2num(href_list["downvote"])
choices.Swap(index, index + 1)
- show(usr)
+ show()
+ return
+
+ ..()
+
+/datum/async_input/autocomplete
+ immediate_submit = TRUE
+ height = 150
+
+/datum/async_input/autocomplete/New()
+ ..()
+ popup.add_script("autocomplete.js", 'html/browser/autocomplete.js')
+
+/datum/async_input/autocomplete/render_prompt()
+ return "[prompt] "
+
+/datum/async_input/autocomplete/render_choices()
+ var/dat = " "
+ dat += ""
+ for(var/choice in choices)
+ dat += ""
+ dat += " "
+ return dat
+
+/datum/async_input/autocomplete/render_buttons()
+ var/dat = button("Submit", "", , result == null && !immediate_submit, "submit-button")
+ dat += button("Cancel", "close=1")
+ return dat
+
+/datum/async_input/autocomplete/Topic(href, href_list)
+ if(href_list["submit"])
+ // Entering an invalid choice is the same as canceling
+ if(href_list["submit"] in choices)
+ result = href_list["submit"]
+ close()
return
..()
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index b6167338ad3..79c68a2a2d2 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -1627,7 +1627,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "CIB"
item = /obj/item/storage/box/cyber_implants/bundle
cost = 40
-
+ gamemodes = list(/datum/game_mode/nuclear)
+
/datum/uplink_item/bundles_TC/medical
name = "Medical Bundle"
desc = "The support specialist: Aid your fellow operatives with this medical bundle. Contains a tactical medkit, \
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 3b67153e0b8..86c245a191f 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -58,6 +58,10 @@
pulledby = null
return ..()
+//Returns an atom's power cell, if it has one. Overload for individual items.
+/atom/movable/proc/get_cell()
+ return
+
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
if(QDELETED(AM))
return FALSE
diff --git a/code/game/machinery/defib_mount.dm b/code/game/machinery/defib_mount.dm
index 49c5fbcbce6..77d68590efa 100644
--- a/code/game/machinery/defib_mount.dm
+++ b/code/game/machinery/defib_mount.dm
@@ -15,6 +15,10 @@
var/obj/item/defibrillator/defib //this mount's defibrillator
var/clamps_locked = FALSE //if true, and a defib is loaded, it can't be removed without unlocking the clamps
+/obj/machinery/defibrillator_mount/get_cell()
+ if(defib)
+ return defib.get_cell()
+
/obj/machinery/defibrillator_mount/New(location, direction, building = 0)
..()
@@ -28,7 +32,6 @@
pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0
-
/obj/machinery/defibrillator_mount/loaded/New() //loaded subtype for mapping use
..()
defib = new/obj/item/defibrillator/loaded(src)
diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm
index c11b3d7a725..68e22f2e8c7 100644
--- a/code/game/machinery/floodlight.dm
+++ b/code/game/machinery/floodlight.dm
@@ -12,6 +12,8 @@
var/open = FALSE
var/brightness_on = 14
+/obj/machinery/floodlight/get_cell()
+ return cell
/obj/machinery/floodlight/Initialize()
. = ..()
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 81006be3fdd..b8f2d153d9f 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -8,6 +8,7 @@
idle_power_usage = 4
active_power_usage = 250
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"
@@ -81,22 +82,21 @@
if(stat & (NOPOWER|BROKEN) || !anchored)
return
- var/using_power = 0
+ using_power = FALSE
if(charging)
if(istype(charging, /obj/item/gun/energy))
var/obj/item/gun/energy/E = charging
if(E.power_supply.charge < E.power_supply.maxcharge)
E.power_supply.give(E.power_supply.chargerate)
use_power(250)
- using_power = 1
-
+ using_power = TRUE
if(istype(charging, /obj/item/melee/baton))
var/obj/item/melee/baton/B = charging
if(B.bcell)
if(B.bcell.give(B.bcell.chargerate))
use_power(200)
- using_power = 1
+ using_power = TRUE
if(istype(charging, /obj/item/modular_computer))
var/obj/item/modular_computer/C = charging
@@ -107,21 +107,21 @@
if(B.battery.charge < B.battery.maxcharge)
B.battery.give(B.battery.chargerate)
use_power(200)
- using_power = 1
+ 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 = 1
+ using_power = TRUE
if(istype(charging, /obj/item/bodyanalyzer))
var/obj/item/bodyanalyzer/B = charging
if(B.power_supply)
if(B.power_supply.give(B.power_supply.chargerate))
use_power(200)
- using_power = 1
+ using_power = TRUE
update_icon(using_power)
@@ -141,7 +141,7 @@
B.bcell.charge = 0
..(severity)
-/obj/machinery/recharger/update_icon(using_power = 0) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
+/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.
if(stat & (NOPOWER|BROKEN) || !anchored)
icon_state = icon_state_off
return
@@ -153,6 +153,23 @@
return
icon_state = icon_state_idle
+/obj/machinery/recharger/examine(mob/user)
+ ..()
+ if(charging && (!in_range(user, src) && !issilicon(user) && !isobserver(user)))
+ to_chat(user, "You're too far away to examine [src]'s contents and display! ")
+ return
+
+ if(charging)
+ to_chat(user, "\The [src] contains: ")
+ to_chat(user, "- \A [charging]. ")
+ if(!(stat & (NOPOWER|BROKEN)))
+ var/obj/item/stock_parts/cell/C = charging.get_cell()
+ to_chat(user, "The status display reads:")
+ if(using_power)
+ to_chat(user, "- Recharging [(C.chargerate/C.maxcharge)*100]% cell charge per cycle.")
+ if(charging)
+ to_chat(user, "- \The [charging]'s cell is at [C.percent()]% .")
+
// Atlantis: No need for that copy-pasta code, just use var to store icon_states instead.
/obj/machinery/recharger/wallcharger
name = "wall recharger"
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 3777dfc866d..ccba902b058 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -12,6 +12,9 @@
var/set_temperature = 50 // in celcius, add T0C for kelvin
var/heating_power = 40000
+/obj/machinery/space_heater/get_cell()
+ return cell
+
/obj/machinery/space_heater/New()
..()
cell = new(src)
@@ -39,7 +42,6 @@
else
to_chat(user, "The charge meter reads [cell ? round(cell.percent(),1) : 0]%")
-
/obj/machinery/space_heater/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
..(severity)
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index d9d9d5dd261..2ff5cabaaaa 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -137,6 +137,10 @@
////////////////////////
////// Helpers /////////
////////////////////////
+
+/obj/mecha/get_cell()
+ return cell
+
/obj/mecha/proc/add_airtank()
internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
return internal_tank
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 9bb036c4d05..cad4ae25ac8 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -591,6 +591,9 @@ REAGENT SCANNER
var/scan_time = 10 SECONDS //how long does it take to scan
var/scan_cd = 60 SECONDS //how long before we can scan again
+/obj/item/bodyanalyzer/get_cell()
+ return power_supply
+
/obj/item/bodyanalyzer/advanced
cell_type = /obj/item/stock_parts/cell/upgraded/plus
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index 05f725fcf54..fbdc403d758 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -22,6 +22,9 @@
var/obj/item/stock_parts/cell/high/bcell = null
var/combat = 0 //can we revive through space suits?
+/obj/item/defibrillator/get_cell()
+ return bcell
+
/obj/item/defibrillator/New() //starts without a cell for rnd
..()
paddles = make_paddles()
diff --git a/code/game/objects/items/weapons/rpd.dm b/code/game/objects/items/weapons/rpd.dm
index 63a3ffef0c5..4381c4295c7 100644
--- a/code/game/objects/items/weapons/rpd.dm
+++ b/code/game/objects/items/weapons/rpd.dm
@@ -4,6 +4,9 @@
#define RPD_COOLDOWN_TIME 4 //How long should we have to wait between dispensing pipes?
#define RPD_WALLBUILD_TIME 40 //How long should drilling into a wall take?
+#define RPD_MENU_ROTATE "Rotate pipes" //Stuff for radial menu
+#define RPD_MENU_FLIP "Flip pipes" //Stuff for radial menu
+#define RPD_MENU_DELETE "Delete pipes" //Stuff for radial menu
/obj/item/rpd
name = "rapid pipe dispenser"
@@ -172,6 +175,9 @@ var/list/pipemenu = list(
ui.open()
ui.set_auto_update(1)
+/obj/item/rpd/AltClick(mob/user)
+ radial_menu(user)
+
/obj/item/rpd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["iconrotation"] = iconrotation
@@ -200,6 +206,44 @@ var/list/pipemenu = list(
return
SSnanoui.update_uis(src)
+//RPD radial menu
+
+/obj/item/rpd/proc/check_menu(mob/living/user)
+ if(!istype(user))
+ return
+ if(user.incapacitated())
+ return
+ if(loc != user)
+ return
+ return TRUE
+
+/obj/item/rpd/proc/radial_menu(mob/user)
+ if(!check_menu(user))
+ to_chat(user, "You can't do that right now! ")
+ return
+ var/list/choices = list(
+ RPD_MENU_ROTATE = image(icon = 'icons/obj/interface.dmi', icon_state = "rpd_rotate"),
+ RPD_MENU_FLIP = image(icon = 'icons/obj/interface.dmi', icon_state = "rpd_flip"),
+ RPD_MENU_DELETE = image(icon = 'icons/obj/interface.dmi', icon_state = "rpd_delete"),
+ "UI" = image(icon = 'icons/obj/interface.dmi', icon_state = "ui_interact")
+ )
+ var/selected_mode = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, .proc/check_menu, user))
+ if(!check_menu(user))
+ return
+ if(selected_mode == "UI")
+ ui_interact(user)
+ else
+ switch(selected_mode)
+ if(RPD_MENU_ROTATE)
+ mode = RPD_ROTATE_MODE
+ if(RPD_MENU_FLIP)
+ mode = RPD_FLIP_MODE
+ if(RPD_MENU_DELETE)
+ mode = RPD_DELETE_MODE
+ else
+ return //Either nothing was selected, or an invalid mode was selected
+ to_chat(user, "You set [src]'s mode. ")
+
/obj/item/rpd/afterattack(atom/target, mob/user, proximity)
..()
if(loc != user)
@@ -236,3 +280,6 @@ var/list/pipemenu = list(
#undef RPD_COOLDOWN_TIME
#undef RPD_WALLBUILD_TIME
+#undef RPD_MENU_ROTATE
+#undef RPD_MENU_FLIP
+#undef RPD_MENU_DELETE
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index f128b874eaa..526340daea5 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -20,6 +20,9 @@
user.visible_message("[user] is putting the live [name] in [user.p_their()] mouth! It looks like [user.p_theyre()] trying to commit suicide. ")
return FIRELOSS
+/obj/item/melee/baton/get_cell()
+ return bcell
+
/obj/item/melee/baton/New()
..()
update_icon()
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index caa051f8748..83531c9b0b9 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -279,6 +279,9 @@
/datum/pm_tracker/proc/add_message(client/title, client/sender, message, mob/user)
if(!pms[title.key])
pms[title.key] = new /datum/pm_convo(title)
+ else if(!pms[title.key].client)
+ // If they DCed earlier, we need to add the client reference back
+ pms[title.key].client = title
pms[title.key].add(sender, message)
if(!open)
@@ -308,16 +311,25 @@
dat += "[label] "
var/datum/pm_convo/convo = pms[current_title]
+ var/datum/browser/popup = new(user, window_id, "Messages", 1000, 600, src)
if(convo)
+ popup.add_head_content(@{""})
convo.read = TRUE
dat += "[check_rights(R_ADMIN, FALSE, user) ? fancy_title(current_title) : current_title] "
dat += ""
- dat += " "
+ dat += ""
+ dat += "
"
for(var/message in convo.messages)
dat += "[message] "
dat += "
"
+ dat += "
"
if(convo.typing)
dat += "[current_title] is typing "
dat += " "
@@ -327,17 +339,23 @@
if(check_rights(R_ADMIN, FALSE, user))
dat += "Ping "
- var/datum/browser/popup = new(user, window_id, "Messages", 1000, 600, src)
popup.set_content(dat)
popup.open()
open = TRUE
/datum/pm_tracker/proc/fancy_title(title)
- var/client/C = pms[title].client
+ var/client/C = pms[title].client || update_client(title)
if(!C)
return "[title] (Disconnected)"
return "[key_name(C, FALSE)] ([ADMIN_QUE(C.mob,"?")]) ([ADMIN_PP(C.mob,"PP")]) ([ADMIN_VV(C.mob,"VV")]) ([ADMIN_SM(C.mob,"SM")]) ([admin_jump_link(C.mob)]) (CA )"
+/datum/pm_tracker/proc/update_client(title)
+ var/client/C = GLOB.directory[ckey(title)]
+ if(C)
+ pms[title].client = C
+ return C
+ return null
+
/datum/pm_tracker/Topic(href, href_list)
if(href_list["archive"])
pms[href_list["archive"]].archived = !pms[href_list["archive"]].archived
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 9c310fc29ee..e09fc89736a 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -455,6 +455,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
dat += "Ghost Ears: [(toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"] "
dat += "Ghost Radio: [(toggles & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"] "
dat += "Ghost Sight: [(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"] "
+ dat += "Ghost PDA: [(toggles & CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"] "
if(check_rights(R_ADMIN,0))
dat += "OOC Color: Change "
if(config.allow_Metadata)
@@ -2029,6 +2030,9 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if("ghost_radio")
toggles ^= CHAT_GHOSTRADIO
+
+ if("ghost_pda")
+ toggles ^= CHAT_GHOSTPDA
if("ghost_anonsay")
ghost_anonsay = !ghost_anonsay
diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm
index 3d44cd82bfe..e164a2ab843 100644
--- a/code/modules/client/preference/preferences_toggles.dm
+++ b/code/modules/client/preference/preferences_toggles.dm
@@ -266,3 +266,11 @@
else
to_chat(usr, "You are now in QWERTY mode.")
return
+/client/verb/toggle_ghost_pda()
+ set name = "Show/Hide GhostPDA"
+ set category = "Preferences"
+ set desc = ".Toggle seeing PDA messages as an observer."
+ prefs.toggles ^= CHAT_GHOSTPDA
+ 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!
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 227374bd420..8e5e658e31a 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -64,7 +64,6 @@
item_state = "lgloves"
flags = NODROP
-
/obj/item/clothing/gloves/color/yellow/stun
name = "stun gloves"
desc = "Horrendous and awful. It smells like cancer. The fact it has wires attached to it is incidental."
@@ -72,6 +71,9 @@
var/stun_strength = 5
var/stun_cost = 2000
+/obj/item/clothing/gloves/color/yellow/stun/get_cell()
+ return cell
+
/obj/item/clothing/gloves/color/yellow/stun/New()
..()
update_icon()
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index 913e65af734..b463a513764 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -97,6 +97,9 @@
to_chat(usr, "The maintenance panel is [open ? "open" : "closed"].")
to_chat(usr, "Hardsuit systems are [offline ? "offline " : "online "].")
+/obj/item/rig/get_cell()
+ return cell
+
/obj/item/rig/New()
..()
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index 48b4453f6d4..3cd95f37923 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -164,7 +164,7 @@
// Virology Medical Smartfridge
// ----------------------------
/obj/machinery/smartfridge/secure/chemistry/virology
- name = "smart virus storage"
+ 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,
@@ -386,7 +386,8 @@
return data
/obj/machinery/smartfridge/Topic(href, href_list)
- if(..()) return 0
+ if(..())
+ return FALSE
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
@@ -396,7 +397,7 @@
if(href_list["close"])
user.unset_machine()
ui.close()
- return 0
+ return FALSE
if(href_list["vend"])
var/index = text2num(href_list["vend"])
@@ -409,17 +410,26 @@
item_quants[K] = max(count - amount, 0)
var/i = amount
- for(var/obj/O in contents)
- if(O.name == K)
- O.forceMove(loc)
- adjust_item_drop_location(O)
- update_icon()
- i--
- if(i <= 0)
- return 1
-
- return 1
- return 0
+ if(i == 1 && Adjacent(user) && !issilicon(user))
+ for(var/obj/O in contents)
+ if(O.name == K)
+ if(!user.put_in_hands(O))
+ O.forceMove(loc)
+ adjust_item_drop_location(O)
+ update_icon()
+ break
+ return TRUE
+ else
+ for(var/obj/O in contents)
+ if(O.name == K)
+ O.forceMove(loc)
+ adjust_item_drop_location(O)
+ update_icon()
+ i--
+ if(i <= 0)
+ return TRUE
+ return TRUE
+ return FALSE
/obj/machinery/smartfridge/proc/throw_item()
var/obj/throw_item = null
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 8ba8b13c09c..f10b78dc526 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -82,6 +82,7 @@
/obj/item/flashlight/lantern
name = "lantern"
icon_state = "lantern"
+ item_state = "lantern"
desc = "A mining lantern."
brightness_on = 6 // luminosity when on
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index c8c7d5c32e3..1b11a35e58b 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -402,9 +402,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(usr, "Not when you're not dead!")
return
- var/area/A = input("Area to jump to", "BOOYEA") as null|anything in ghostteleportlocs
- var/area/thearea = ghostteleportlocs[A]
+ var/datum/async_input/A = input_autocomplete_async(usr, "Area to jump to: ", ghostteleportlocs)
+ A.on_close(CALLBACK(src, .proc/teleport))
+/mob/dead/observer/proc/teleport(area/thearea)
if(!thearea)
return
@@ -425,9 +426,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set desc = "Follow and orbit a mob."
var/list/mobs = getpois(skip_mindless=1)
- var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs
- var/mob/target = mobs[input]
- ManualFollow(target)
+ var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
+ A.on_close(CALLBACK(src, .proc/ManualFollow))
// This is the ghost's follow verb with an argument
/mob/dead/observer/proc/ManualFollow(var/atom/movable/target)
@@ -499,25 +499,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "Jump to Mob"
set desc = "Teleport to a mob"
- if(isobserver(usr)) //Make sure they're an observer!
- var/list/dest = list() //List of possible destinations (mobs)
- var/target = null //Chosen target.
+ if(isobserver(usr)) //Make sure they're an observer!
+ var/list/dest = getpois(mobs_only=1) //Fill list, prompt user with list
+ var/datum/async_input/A = input_autocomplete_async(usr, "Enter a mob name: ", dest)
+ A.on_close(CALLBACK(src, .proc/jump_to_mob))
- dest += getpois(mobs_only=1) //Fill list, prompt user with list
- target = input("Please, select a mob!", "Jump to Mob", null, null) as null|anything in dest
-
- if(!target) //Make sure we actually have a target
- return
- else
- var/mob/M = dest[target] //Destination mob
- var/mob/A = src //Source mob
- var/turf/T = get_turf(M) //Turf of the destination mob
-
- if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
- A.forceMove(T)
- else
- to_chat(A, "This mob is not located in the game world.")
+/mob/dead/observer/proc/jump_to_mob(mob/M)
+ if(!M)
+ return
+ var/mob/A = src //Source mob
+ var/turf/T = get_turf(M) //Turf of the destination mob
+ if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
+ A.forceMove(T)
+ return
+ to_chat(A, "This mob is not located in the game world.")
/* Now a spell. See spells.dm
/mob/dead/observer/verb/boo()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7be816e5a7a..a8dbaa7ac2f 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -216,56 +216,7 @@
add_attack_logs(M, src, "Shaked", ATKLOG_ALL)
if(health >= HEALTH_THRESHOLD_CRIT)
if(src == M && ishuman(src))
- var/mob/living/carbon/human/H = src
- visible_message( \
- text("[src] examines []. ",gender==MALE?"himself":"herself"), \
- "You check yourself for injuries. " \
- )
-
- var/list/missing = list("head", "chest", "groin", "l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")
- for(var/X in H.bodyparts)
- var/obj/item/organ/external/LB = X
- missing -= LB.limb_name
- var/status = ""
- var/brutedamage = LB.brute_dam
- var/burndamage = LB.burn_dam
-
- if(brutedamage > 0)
- status = "bruised"
- if(brutedamage > 20)
- status = "battered"
- if(brutedamage > 40)
- status = "mangled"
- if(brutedamage > 0 && burndamage > 0)
- status += " and "
- if(burndamage > 40)
- status += "peeling away"
-
- else if(burndamage > 10)
- status += "blistered"
- else if(burndamage > 0)
- status += "numb"
- if(LB.status & ORGAN_MUTATED)
- status = "weirdly shapen."
- if(status == "")
- status = "OK"
- to_chat(src, "\t Your [LB.name] is [status]. ")
-
- for(var/obj/item/I in LB.embedded_objects)
- to_chat(src, "\t There is \a [I] embedded in your [LB.name]! ")
-
- for(var/t in missing)
- to_chat(src, "Your [parse_zone(t)] is missing! ")
-
- if(H.bleed_rate)
- to_chat(src, "You are bleeding! ")
- if(staminaloss)
- if(staminaloss > 30)
- to_chat(src, "You're completely exhausted. ")
- else
- to_chat(src, "You feel fatigued. ")
- if((SKELETON in H.mutations) && (!H.w_uniform) && (!H.wear_suit))
- H.play_xylophone()
+ check_self_for_injuries()
else
if(player_logged)
M.visible_message("[M] shakes [src], but [p_they()] [p_do()] not respond. Probably suffering from SSD.", \
@@ -308,6 +259,58 @@
else if(H.w_uniform)
H.w_uniform.add_fingerprint(M)
+/mob/living/carbon/proc/check_self_for_injuries()
+ var/mob/living/carbon/human/H = src
+ visible_message( \
+ text("[src] examines []. ",gender==MALE?"himself":"herself"), \
+ "You check yourself for injuries. " \
+ )
+
+ var/list/missing = list("head", "chest", "groin", "l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")
+ for(var/X in H.bodyparts)
+ var/obj/item/organ/external/LB = X
+ missing -= LB.limb_name
+ var/status = ""
+ var/brutedamage = LB.brute_dam
+ var/burndamage = LB.burn_dam
+
+ if(brutedamage > 0)
+ status = "bruised"
+ if(brutedamage > 20)
+ status = "battered"
+ if(brutedamage > 40)
+ status = "mangled"
+ if(brutedamage > 0 && burndamage > 0)
+ status += " and "
+ if(burndamage > 40)
+ status += "peeling away"
+
+ else if(burndamage > 10)
+ status += "blistered"
+ else if(burndamage > 0)
+ status += "numb"
+ if(LB.status & ORGAN_MUTATED)
+ status = "weirdly shapen."
+ if(status == "")
+ status = "OK"
+ to_chat(src, "\t Your [LB.name] is [status]. ")
+
+ for(var/obj/item/I in LB.embedded_objects)
+ to_chat(src, "\t There is \a [I] embedded in your [LB.name]! ")
+
+ for(var/t in missing)
+ to_chat(src, "Your [parse_zone(t)] is missing! ")
+
+ if(H.bleed_rate)
+ to_chat(src, "You are bleeding! ")
+ if(staminaloss)
+ if(staminaloss > 30)
+ to_chat(src, "You're completely exhausted. ")
+ else
+ to_chat(src, "You feel fatigued. ")
+ if((SKELETON in H.mutations) && (!H.w_uniform) && (!H.wear_suit))
+ H.play_xylophone()
+
/mob/living/carbon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
. = ..()
var/damage = intensity - check_eye_prot()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 100dd1ffbba..0c6ddf80fc6 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -103,6 +103,9 @@ var/list/robot_verbs_default = list(
var/datum/action/item_action/toggle_research_scanner/scanner = null
var/list/module_actions = list()
+/mob/living/silicon/robot/get_cell()
+ return cell
+
/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0)
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 49a3b2718db..1db149418c7 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -50,6 +50,9 @@
var/currentBloodColor = "#A10808"
var/currentDNA = null
+/mob/living/simple_animal/bot/mulebot/get_cell()
+ return cell
+
/mob/living/simple_animal/bot/mulebot/New()
..()
wires = new /datum/wires/mulebot(src)
diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm
index 0a7955cfe52..27c81d48190 100644
--- a/code/modules/modular_computers/computers/item/computer_power.dm
+++ b/code/modules/modular_computers/computers/item/computer_power.dm
@@ -26,6 +26,10 @@
return battery_module.battery.give(amount)
return 0
+/obj/item/modular_computer/get_cell()
+ var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL]
+ if(battery_module && battery_module.battery)
+ return battery_module.battery
// Used in following function to reduce copypaste
/obj/item/modular_computer/proc/power_failure()
diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm
index 65b4ef1f640..98f1ab5667e 100644
--- a/code/modules/modular_computers/hardware/battery_module.dm
+++ b/code/modules/modular_computers/hardware/battery_module.dm
@@ -8,6 +8,9 @@
var/obj/item/stock_parts/cell/battery = null
device_type = MC_CELL
+/obj/item/computer_hardware/battery/get_cell()
+ return battery
+
/obj/item/computer_hardware/battery/New(loc, battery_type = null)
if(battery_type)
battery = new battery_type(src)
diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm
index dfd430a5754..6e550e64893 100644
--- a/code/modules/ninja/suit/suit.dm
+++ b/code/modules/ninja/suit/suit.dm
@@ -27,6 +27,9 @@ Contents:
var/obj/item/clothing/mask/gas/space_ninja/suitMask
var/mob/living/carbon/human/suitOccupant
+/obj/item/clothing/suit/space/space_ninja/get_cell()
+ return suitCell
+
/obj/item/clothing/suit/space/space_ninja/proc/toggle_suit_lock(mob/living/carbon/human/user)
if(!suitActive)
if(!istype(user.wear_suit, /obj/item/clothing/suit/space/space_ninja))
diff --git a/code/modules/ninja/suit/suit_attackby.dm b/code/modules/ninja/suit/suit_attackby.dm
index ab6250a2b0f..2a2683a2826 100644
--- a/code/modules/ninja/suit/suit_attackby.dm
+++ b/code/modules/ninja/suit/suit_attackby.dm
@@ -1,5 +1,3 @@
-
-
/obj/item/clothing/suit/space/space_ninja/attackby(obj/item/I, mob/U, params)
if(U==suitOccupant)//Safety, in case you try doing this without wearing the suit/being the person with the suit.
if(istype(I, /obj/item/stock_parts/cell))
@@ -21,4 +19,4 @@
else
to_chat(U, "Procedure interrupted. Protocol terminated. ")
return
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 451687cd38c..23a37e06409 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -75,16 +75,19 @@
assets.send(user)
var/data
- if((!user.say_understands(null, GLOB.all_languages["Galactic Common"]) && !forceshow) || forcestars) //assuming all paper is written in common is better than hardcoded type checks
- data = "[name] [stars(info)][stamps]"
- if(view)
- usr << browse(data, "window=[name];size=[paper_width]x[paper_height]")
- onclose(usr, "[name]")
+ var/stars = (!user.say_understands(null, GLOB.all_languages["Galactic Common"]) && !forceshow) || forcestars
+ if(stars) //assuming all paper is written in common is better than hardcoded type checks
+ data = "[stars(info)][stamps]"
else
- data = "[name] [infolinks ? info_links : info][stamps]"
- if(view)
- usr << browse(data, "window=[name];size=[paper_width]x[paper_height]")
- onclose(usr, "[name]")
+ data = "[infolinks ? info_links : info]
[stamps]"
+ if(view)
+ var/datum/browser/popup = new(user, name, , paper_width, paper_height)
+ popup.stylesheets = list()
+ popup.set_content(data)
+ if(!stars)
+ popup.add_script("marked.js", 'html/browser/marked.js')
+ popup.add_head_content("[name] ")
+ popup.open()
return data
/obj/item/paper/verb/rename()
diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm
index 67ccf48df8a..ab97c956d85 100644
--- a/code/modules/pda/messenger.dm
+++ b/code/modules/pda/messenger.dm
@@ -169,10 +169,18 @@
if(useTC != 2) // Does our recipient have a broadcaster on their level?
to_chat(U, "ERROR: Cannot reach recipient.")
return
+
useMS.send_pda_message("[P.owner]","[pda.owner]","[t]")
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]")))
PM.tnote.Add(list(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "\ref[pda]")))
pda.investigate_log("PDA Message - [U.key] - [pda.owner] -> [P.owner] : [t] ", "pda")
+
+ // Show it to ghosts
+ for(var/mob/M in GLOB.dead_mob_list)
+ if(isobserver(M) && M.client && (M.client.prefs.toggles & CHAT_GHOSTPDA))
+ var/ghost_message = "[pda.owner] ([ghost_follow_link(pda, ghost=M)]) PDA Message --> [P.owner] ([ghost_follow_link(P, ghost=M)]): [t] "
+ to_chat(M, "[ghost_message]")
+
if(!conversations.Find("\ref[P]"))
conversations.Add("\ref[P]")
if(!PM.conversations.Find("\ref[pda]"))
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index a69ff2a793b..7b3162fdefb 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -135,6 +135,9 @@
usesound = 'sound/items/deconstruct.ogg'
toolspeed = 1
+/obj/machinery/power/apc/get_cell()
+ return cell
+
/obj/machinery/power/apc/connect_to_network()
//Override because the APC does not directly connect to the network; it goes through a terminal.
//The terminal is what the power computer looks for anyway.
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 4719ff75261..33b423c6360 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -19,6 +19,9 @@
var/ratingdesc = TRUE
var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it.
+/obj/item/stock_parts/cell/get_cell()
+ return src
+
/obj/item/stock_parts/cell/New()
..()
START_PROCESSING(SSobj, src)
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index b25420b5491..cbc230c1e98 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -23,6 +23,9 @@
power_supply.use(round(power_supply.charge / severity))
update_icon()
+/obj/item/gun/energy/get_cell()
+ return power_supply
+
/obj/item/gun/energy/New()
..()
if(cell_type)
diff --git a/code/modules/projectiles/guns/throw/crossbow.dm b/code/modules/projectiles/guns/throw/crossbow.dm
index bf101cd49c3..9c87f7949a9 100644
--- a/code/modules/projectiles/guns/throw/crossbow.dm
+++ b/code/modules/projectiles/guns/throw/crossbow.dm
@@ -22,6 +22,9 @@
var/obj/item/stock_parts/cell/cell = null // Used for firing superheated rods.
var/list/possible_tensions = list(XBOW_TENSION_20, XBOW_TENSION_40, XBOW_TENSION_60, XBOW_TENSION_80, XBOW_TENSION_FULL)
+/obj/item/gun/throw/crossbow/get_cell()
+ return cell
+
/obj/item/gun/throw/crossbow/emp_act(severity)
if(cell && severity)
emp_act(severity)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 20ecaff13d4..7c53761a70f 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -24,6 +24,9 @@
var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode."
var/unhack_message = "You re-enable the safety safeguards, enabling the \"NT Standard\" mode."
+/obj/machinery/chem_dispenser/get_cell()
+ return cell
+
/obj/machinery/chem_dispenser/New()
..()
component_parts = list()
diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm
index a67f681973d..6f0c53730b4 100644
--- a/code/modules/reagents/chemistry/reagents/food.dm
+++ b/code/modules/reagents/chemistry/reagents/food.dm
@@ -815,7 +815,7 @@
var/update_flags = STATUS_UPDATE_NONE
if(prob(5))
if(prob(10))
- update_flags |= M.adjustToxLoss(rand(2.4), FALSE)
+ update_flags |= M.adjustToxLoss(rand(2,4), FALSE)
if(prob(7))
to_chat(M, "A horrible migraine overpowers you. ")
update_flags |= M.Stun(rand(2,5), FALSE)
diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm
index 273ea75e832..16f8e8f067f 100644
--- a/code/modules/spacepods/spacepod.dm
+++ b/code/modules/spacepods/spacepod.dm
@@ -92,6 +92,8 @@
has_paint = 1
update_icons()
+/obj/spacepod/get_cell()
+ return battery
/obj/spacepod/New()
. = ..()
diff --git a/code/modules/telesci/telepad.dm b/code/modules/telesci/telepad.dm
index 2f3667a645f..893e9719fb4 100644
--- a/code/modules/telesci/telepad.dm
+++ b/code/modules/telesci/telepad.dm
@@ -128,11 +128,13 @@
var/teleporting = 0
var/chargecost = 1000
+/obj/item/rcs/get_cell()
+ return rcell
+
/obj/item/rcs/New()
..()
rcell = new(src)
-
/obj/item/rcs/examine(mob/user)
..(user)
to_chat(user, "There are [round(rcell.charge/chargecost)] charge\s left.")
diff --git a/html/browser/autocomplete.js b/html/browser/autocomplete.js
new file mode 100644
index 00000000000..28d3be539c4
--- /dev/null
+++ b/html/browser/autocomplete.js
@@ -0,0 +1,61 @@
+var $ = document.querySelector.bind(document);
+var input;
+var submitButton;
+var optionsMap = {};
+
+function updateTopic() {
+ if (!input || !submitButton) {
+ return;
+ }
+
+ var hrefList = submitButton.getAttribute('href').split(';');
+ // Topic must come last in the submit button for this to work
+ hrefList = hrefList.slice(0, hrefList.length - 1);
+ hrefList.push(optionsMap[input.value] ? 'submit=' + optionsMap[input.value] : '');
+ submitButton.setAttribute('href', hrefList.join(';'));
+}
+
+function clean(name) {
+ // \improper shows up as 2 characters outside of ascii range
+ if (name.charCodeAt(0) > 127) {
+ return name.slice(2);
+ }
+ return name;
+}
+
+function setElements() {
+ input = $('#input');
+ submitButton = $('#submit-button');
+ var choices = $('#choices');
+
+ if (!input || !submitButton || !choices) {
+ return;
+ }
+
+
+ for (var i = 0; i < choices.options.length; i++) {
+ var name = choices.options[i].value;
+ var cleaned = clean(name);
+ optionsMap[cleaned] = name;
+ choices.options[i].value = cleaned;
+ }
+
+ input.addEventListener('keyup', function(event) {
+ if (event.key !== 'Enter') {
+ return;
+ }
+
+ if (Object.keys(optionsMap).indexOf(input.value) === -1) {
+ // Byond doesn't let you to use enter to select
+ // so we need to prevent unintended submissions
+ return
+ }
+
+ submitButton.click();
+ event.preventDefault();
+ });
+
+ input.focus();
+}
+
+window.onload = setElements;
diff --git a/html/browser/marked.js b/html/browser/marked.js
new file mode 100644
index 00000000000..6fe0e1e4e41
--- /dev/null
+++ b/html/browser/marked.js
@@ -0,0 +1,26 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/markedjs/marked
+ */
+!function(e){"use strict";var _={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:f,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,nptable:f,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,table:f,lheading:/^([^\n]+)\n {0,3}(=|-){2,} *(?:\n+|$)/,paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,text:/^[^\n]+/};function a(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=_.normal,this.options.pedantic?this.rules=_.pedantic:this.options.gfm&&(this.options.tables?this.rules=_.tables:this.rules=_.gfm)}_._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,_.def=i(_.def).replace("label",_._label).replace("title",_._title).getRegex(),_.bullet=/(?:[*+-]|\d{1,9}\.)/,_.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,_.item=i(_.item,"gm").replace(/bull/g,_.bullet).getRegex(),_.list=i(_.list).replace(/bull/g,_.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+_.def.source+")").getRegex(),_._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",_._comment=//,_.html=i(_.html,"i").replace("comment",_._comment).replace("tag",_._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),_.paragraph=i(_.paragraph).replace("hr",_.hr).replace("heading",_.heading).replace("lheading",_.lheading).replace("tag",_._tag).getRegex(),_.blockquote=i(_.blockquote).replace("paragraph",_.paragraph).getRegex(),_.normal=d({},_),_.gfm=d({},_.normal,{fences:/^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),_.gfm.paragraph=i(_.paragraph).replace("(?!","(?!"+_.gfm.fences.source.replace("\\1","\\2")+"|"+_.list.source.replace("\\1","\\3")+"|").getRegex(),_.tables=d({},_.gfm,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),_.pedantic=d({},_.normal,{html:i("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)| \\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",_._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/}),a.rules=_,a.lex=function(e,t){return new a(t).lex(e)},a.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},a.prototype.token=function(e,t){var n,r,s,i,l,o,a,h,p,u,c,g,f,d,k,m,b,x;for(e=e.replace(/^ +$/gm,"");e;)if((s=this.rules.newline.exec(e))&&(e=e.substring(s[0].length),1 ?/gm,""),x=1;b.match(/^ {0,3}>/);)x++,this.tokens.push({type:"blockquote_start"}),b=b.replace(/^ *> ?/gm,"");for(this.token(b,t),c=0;c?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:f,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:f,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=i(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=i(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=i(n.tag).replace("comment",_._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/,n._href=/\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=i(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=i(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:i(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:i(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:i(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(i[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(i[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(i[0])&&(this.inRawBlock=!1),e=e.substring(i[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):u(i[0]):i[0];else if(i=this.rules.link.exec(e)){var a=k(i[2],"()");if(-1$/,"$1"),o+=this.outputLink(i,{href:p.escapes(r),title:p.escapes(s)}),this.inLink=!1}else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),o+=this.renderer.strong(this.output(i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),o+=this.renderer.em(this.output(i[6]||i[5]||i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),o+=this.renderer.codespan(u(i[2].trim(),!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),o+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),o+=this.renderer.del(this.output(i[1]));else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),r="@"===i[2]?"mailto:"+(n=u(this.mangle(i[1]))):n=u(i[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.text.exec(e))e=e.substring(i[0].length),this.inRawBlock?o+=this.renderer.text(i[0]):o+=this.renderer.text(u(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===i[2])r="mailto:"+(n=u(i[0]));else{for(;l=i[0],i[0]=this.rules._backpedal.exec(i[0])[0],l!==i[0];);n=u(i[0]),r="www."===i[1]?"http://"+n:n}e=e.substring(i[0].length),o+=this.renderer.link(r,null,n)}return o},p.escapes=function(e){return e?e.replace(p.rules._escapes,"$1"):e},p.prototype.outputLink=function(e,t){var n=t.href,r=t.title?u(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,u(e[1]))},p.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},p.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,s=0;s'+(n?e:u(e,!0))+"\n":""+(n?e:u(e,!0))+" "},r.prototype.blockquote=function(e){return"\n"+e+" \n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+" \n"},r.prototype.hr=function(){return this.options.xhtml?" \n":" \n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},r.prototype.listitem=function(e){return""+e+" \n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return""+e+"
\n"},r.prototype.table=function(e,t){return t&&(t=""+t+" "),"\n"},r.prototype.tablerow=function(e){return"\n"+e+" \n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},r.prototype.strong=function(e){return""+e+" "},r.prototype.em=function(e){return""+e+" "},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?" ":" "},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=l(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+" "},r.prototype.image=function(e,t,n){if(null===(e=l(this.options.sanitize,this.options.baseUrl,e)))return n;var r=' ":">"},r.prototype.text=function(e){return e},s.prototype.strong=s.prototype.em=s.prototype.codespan=s.prototype.del=s.prototype.text=function(e){return e},s.prototype.link=s.prototype.image=function(e,t,n){return""+n},s.prototype.br=function(){return""},h.parse=function(e,t){return new h(t).parse(e)},h.prototype.parse=function(e){this.inline=new p(e.links,this.options),this.inlineText=new p(e.links,d({},this.options,{renderer:new s})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},h.prototype.next=function(){return this.token=this.tokens.pop(),this.token},h.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},h.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},h.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,g(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,s="",i="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},u.escapeTest=/[&<>"']/,u.escapeReplace=/[&<>"']/g,u.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},u.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,u.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var o={},c=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function f(){}function d(e){for(var t,n,r=1;rt)n.splice(t);else for(;n.lengthAn error occurred:"+u(e.message+"",!0)+" ";throw e}}f.exec=f,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tables:!0,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=h,m.parser=h.parse,m.Renderer=r,m.TextRenderer=s,m.Lexer=a,m.lexer=a.lex,m.InlineLexer=p,m.inlineLexer=p.output,m.Slugger=t,m.parse=m,"undefined"!=typeof module&&"object"==typeof exports?module.exports=m:"function"==typeof define&&define.amd?define(function(){return m}):e.marked=m}(this||("undefined"!=typeof window?window:global));
+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'), { breaks: true });
+}
+
+function main() {
+ if ($('#markdown')) {
+ parse($('#markdown'));
+ }
+}
+
+window.onload = main;
diff --git a/html/changelog.html b/html/changelog.html
index d98359137e3..b639984a5a6 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -56,6 +56,63 @@
-->
+
08 July 2019
+
Arkatos updated:
+
+ Clicking on a health doll icon will now check you for injuries
+ Rechargers now show their contents and charge status on close examine
+ Smartfridge will now try to put an item in your hands after vending, if able
+
+
AzuleUtama updated:
+
+ The cybernetic implant bundle can no longer be bought by traitors.
+
+
Citinited updated:
+
+ Alt-clicking the RPD now brings up a radial menu with rotate, flip, and delete modes accessible. The main interface remains unchanged.
+ Writing in blood can't be done while dead or incapacitated any more
+ You can't write stuff in blood at a distance any more
+
+
Couls updated:
+
+ lantern sprites
+ typo in msg
+
+
Improvedname updated:
+
+ Explorer suits now properly hide tails
+
+
JKnutson101 updated:
+
+ Fixed Construction Permits deleting themselves prematurely.
+
+
Kyep updated:
+
+ fixed a bug with biogenerator.
+
+
Markolie updated:
+
+ Ghosts can now see all PDA messages when enabled through a preference setting.
+ The Mask of Nar'Sie transformation buttons have been removed, as the mask was removed a long time ago.
+
+
Shadeykins updated:
+
+ Changed Quarantine, NT Default, Aggressive, and Corporate AI lawsets. balance: Reduced the ability for NT Default AI's to murder people just because.
+ Removed several redundancies in Aggressive lawset.
+ Fixed a few subject-verb disagreements (is -> are).
+
+
Tayyyyyyy updated:
+
+ paper supports markdown as well as pencode
+ Autocomplete for ghost buttons
+ PMs window scrollable
+ Fix a bug where a player who reconnects is still shown as disconnected
+
+
datlo updated:
+
+ Fix an antag rolling exploit.
+
+
30 June 2019
Crazylemon64 updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index b9801566492..49e86db023d 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -10415,3 +10415,42 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
2019-06-30:
Crazylemon64:
- rscdel: SDQL2 no longer allows a macro argument
+2019-07-08:
+ Arkatos:
+ - tweak: Clicking on a health doll icon will now check you for injuries
+ - rscadd: Rechargers now show their contents and charge status on close examine
+ - tweak: Smartfridge will now try to put an item in your hands after vending, if
+ able
+ AzuleUtama:
+ - bugfix: The cybernetic implant bundle can no longer be bought by traitors.
+ Citinited:
+ - rscadd: Alt-clicking the RPD now brings up a radial menu with rotate, flip, and
+ delete modes accessible. The main interface remains unchanged.
+ - bugfix: Writing in blood can't be done while dead or incapacitated any more
+ - bugfix: You can't write stuff in blood at a distance any more
+ Couls:
+ - imageadd: lantern sprites
+ - bugfix: typo in msg
+ Improvedname:
+ - bugfix: Explorer suits now properly hide tails
+ JKnutson101:
+ - bugfix: Fixed Construction Permits deleting themselves prematurely.
+ Kyep:
+ - bugfix: fixed a bug with biogenerator.
+ Markolie:
+ - rscadd: Ghosts can now see all PDA messages when enabled through a preference
+ setting.
+ - rscdel: The Mask of Nar'Sie transformation buttons have been removed, as the mask
+ was removed a long time ago.
+ Shadeykins:
+ - tweak: 'Changed Quarantine, NT Default, Aggressive, and Corporate AI lawsets.
+ balance: Reduced the ability for NT Default AI''s to murder people just because.'
+ - bugfix: Removed several redundancies in Aggressive lawset.
+ - spellcheck: Fixed a few subject-verb disagreements (is -> are).
+ Tayyyyyyy:
+ - tweak: paper supports markdown as well as pencode
+ - rscadd: Autocomplete for ghost buttons
+ - tweak: PMs window scrollable
+ - bugfix: Fix a bug where a player who reconnects is still shown as disconnected
+ datlo:
+ - bugfix: Fix an antag rolling exploit.
diff --git a/html/changelogs/AutoChangeLog-pr-11449.yml b/html/changelogs/AutoChangeLog-pr-11449.yml
deleted file mode 100644
index 3789ca10f13..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11449.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "Shadeykins"
-delete-after: True
-changes:
- - tweak: "Changed Quarantine, NT Default, Aggressive, and Corporate AI lawsets.
-balance: Reduced the ability for NT Default AI's to murder people just because."
- - bugfix: "Removed several redundancies in Aggressive lawset."
- - spellcheck: "Fixed a few subject-verb disagreements (is -> are)."
diff --git a/html/changelogs/AutoChangeLog-pr-11639.yml b/html/changelogs/AutoChangeLog-pr-11639.yml
deleted file mode 100644
index f47431f6876..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11639.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "datlo"
-delete-after: True
-changes:
- - bugfix: "Fix an antag rolling exploit."
diff --git a/html/changelogs/AutoChangeLog-pr-11663.yml b/html/changelogs/AutoChangeLog-pr-11663.yml
deleted file mode 100644
index c1129811f68..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11663.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Markolie"
-delete-after: True
-changes:
- - rscdel: "The Mask of Nar'Sie transformation buttons have been removed, as the mask was removed a long time ago."
diff --git a/html/changelogs/AutoChangeLog-pr-11706.yml b/html/changelogs/AutoChangeLog-pr-11706.yml
deleted file mode 100644
index 349da8c68a5..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11706.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Improvedname"
-delete-after: True
-changes:
- - bugfix: "Explorer suits now properly hide tails"
diff --git a/html/changelogs/AutoChangeLog-pr-11770.yml b/html/changelogs/AutoChangeLog-pr-11770.yml
deleted file mode 100644
index 94c44bb97b7..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11770.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Citinited"
-delete-after: True
-changes:
- - bugfix: "Writing in blood can't be done while dead or incapacitated any more"
- - bugfix: "You can't write stuff in blood at a distance any more"
diff --git a/html/changelogs/AutoChangeLog-pr-11771.yml b/html/changelogs/AutoChangeLog-pr-11771.yml
deleted file mode 100644
index edc2a64f5ab..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11771.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JKnutson101"
-delete-after: True
-changes:
- - bugfix: "Fixed Construction Permits deleting themselves prematurely."
diff --git a/html/changelogs/AutoChangeLog-pr-11786.yml b/html/changelogs/AutoChangeLog-pr-11786.yml
deleted file mode 100644
index 53546aede12..00000000000
--- a/html/changelogs/AutoChangeLog-pr-11786.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Kyep"
-delete-after: True
-changes:
- - bugfix: "fixed a bug with biogenerator."
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index ae285a5453a..47cb86a2c89 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 8b949a8e732..e4630c2764e 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/obj/interface.dmi b/icons/obj/interface.dmi
index 3b64561d5e0..631bba214c8 100644
Binary files a/icons/obj/interface.dmi and b/icons/obj/interface.dmi differ