diff --git a/baystation12.dme b/baystation12.dme
index bbdcaa38ead..1c4a43d0743 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -174,6 +174,7 @@
#include "code\datums\diseases\advance\symptoms\weight.dm"
#include "code\datums\helper_datums\construction_datum.dm"
#include "code\datums\helper_datums\events.dm"
+#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\global_iterator.dm"
#include "code\datums\helper_datums\teleport.dm"
#include "code\datums\helper_datums\topic_input.dm"
diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm
index 331c00dd37e..2dfcfcdf812 100644
--- a/code/ZAS/Turf.dm
+++ b/code/ZAS/Turf.dm
@@ -45,6 +45,45 @@
air_master.connect(sim, src)
+/*
+ Simple heuristic for determining if removing the turf from it's zone may possibly partition the zone (A very bad thing).
+ Instead of analyzing the entire zone, we only check the nearest 3x3 turfs surrounding the src turf.
+ This implementation may produce false positives but it (hopefully) will not produce any false negatives.
+*/
+
+/turf/simulated/proc/can_safely_remove_from_zone()
+ #ifdef ZLEVELS
+ return 1 //not sure how to generalize this to multiz at the moment.
+ #else
+
+ if(!zone) return 0
+
+ var/check_dirs = get_zone_neighbours(src)
+ var/unconnected_dirs = check_dirs
+
+ for(var/dir in list(NORTHWEST, NORTHEAST, SOUTHEAST, SOUTHWEST))
+
+ //for each pair of "adjacent" cardinals (e.g. NORTH and WEST, but not NORTH and SOUTH)
+ if((dir & check_dirs) == dir)
+ //check that they are connected by the corner turf
+ var/connected_dirs = get_zone_neighbours(get_step(src, dir))
+ if(connected_dirs && (dir & turn(connected_dirs, 180)) == dir)
+ unconnected_dirs &= ~dir //they are, so unflag the cardinals in question
+
+ //it is safe to remove src from the zone if all cardinals are connected by corner turfs
+ return !unconnected_dirs
+
+ #endif
+
+//helper for can_safely_remove_from_zone()
+/turf/simulated/proc/get_zone_neighbours(turf/simulated/T)
+ . = 0
+ if(istype(T) && T.zone)
+ for(var/dir in cardinal)
+ var/turf/simulated/other = get_step(T, dir)
+ if(istype(other) && other.zone == T.zone && !(other.c_airblock(T) & AIR_BLOCKED) && get_dist(src, other) <= 1)
+ . |= dir
+
/turf/simulated/update_air_properties()
if(zone && zone.invalid)
@@ -60,7 +99,7 @@
if(zone)
var/zone/z = zone
- if(s_block & ZONE_BLOCKED) //Hacky, but prevents normal airlocks from rebuilding zones all the time
+ if(can_safely_remove_from_zone()) //Helps normal airlocks avoid rebuilding zones all the time
z.remove(src)
else
z.rebuild()
diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm
index 69d01e3d358..50f55703d34 100644
--- a/code/__HELPERS/logging.dm
+++ b/code/__HELPERS/logging.dm
@@ -81,3 +81,15 @@
/proc/log_misc(text)
diary << "\[[time_stamp()]]MISC: [text][log_end]"
+
+//pretty print a direction bitflag, can be useful for debugging.
+/proc/print_dir(var/dir)
+ var/list/comps = list()
+ if(dir & NORTH) comps += "NORTH"
+ if(dir & SOUTH) comps += "SOUTH"
+ if(dir & EAST) comps += "EAST"
+ if(dir & WEST) comps += "WEST"
+ if(dir & UP) comps += "UP"
+ if(dir & DOWN) comps += "DOWN"
+
+ return english_list(comps, nothing_text="0", and_text="|", comma_text="|")
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 478700d5f82..216a45ee1c3 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -70,19 +70,19 @@
/proc/list2text(list/ls, sep)
if (ls.len <= 1) // Early-out code for empty or singleton lists.
return ls.len ? ls[1] : ""
-
+
var/l = ls.len // Made local for sanic speed.
var/i = 0 // Incremented every time a list index is accessed.
-
+
if (sep <> null)
// Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc...
#define S1 sep, ls[++i]
#define S4 S1, S1, S1, S1
#define S16 S4, S4, S4, S4
#define S64 S16, S16, S16, S16
-
+
. = "[ls[++i]]" // Make sure the initial element is converted to text.
-
+
// Having the small concatenations come before the large ones boosted speed by an average of at least 5%.
if (l-1 & 0x01) // 'i' will always be 1 here.
. = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2.
@@ -111,7 +111,7 @@
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
-
+
#undef S64
#undef S16
#undef S4
@@ -122,9 +122,9 @@
#define S4 S1, S1, S1, S1
#define S16 S4, S4, S4, S4
#define S64 S16, S16, S16, S16
-
+
. = "[ls[++i]]" // Make sure the initial element is converted to text.
-
+
if (l-1 & 0x01) // 'i' will always be 1 here.
. += S1 // Append 1 element if the remaining elements are not a multiple of 2.
if (l-i & 0x02)
@@ -145,7 +145,7 @@
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
-
+
#undef S64
#undef S16
#undef S4
@@ -165,11 +165,11 @@ proc/tg_list2text(list/list, glue=",")
var/delim_len = length(delimiter)
if (delim_len < 1)
return list(text)
-
+
. = list()
var/last_found = 1
var/found
-
+
do
found = findtext(text, delimiter, last_found, 0)
. += copytext(text, last_found, found)
@@ -181,11 +181,11 @@ proc/tg_list2text(list/list, glue=",")
var/delim_len = length(delimiter)
if (delim_len < 1)
return list(text)
-
+
. = list()
var/last_found = 1
var/found
-
+
do
found = findtextEx(text, delimiter, last_found, 0)
. += copytext(text, last_found, found)
@@ -325,3 +325,47 @@ proc/tg_list2text(list/list, glue=",")
. = 0
else
. = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307))
+
+// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time.
+// returns "YYYY-MM-DD" by default
+/proc/unix2date(timestamp, seperator = "-")
+ if(timestamp < 0)
+ return 0 //Do not accept negative values
+
+ var/const/dayInSeconds = 86400 //60secs*60mins*24hours
+ var/const/daysInYear = 365 //Non Leap Year
+ var/const/daysInLYear = daysInYear + 1//Leap year
+ var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc
+ var/year = 1970 //Unix Epoc begins 1970-01-01
+ var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1
+ var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code***
+ var/month = 1 //This will be the returned MONTH NUMBER.
+ var/day //This will be the returned day number.
+
+ while(tmpDays > daysInYear) //Start adding years to 1970
+ year++
+ if(isLeap(year))
+ tmpDays -= daysInLYear
+ else
+ tmpDays -= daysInYear
+
+ if(isLeap(year)) //The year is a leap year
+ monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334)
+ else
+ monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334)
+
+ var/mDays = 0;
+ var/monthIndex = 0;
+
+ for(var/m in monthsInDays)
+ monthIndex++
+ if(tmpDays > m)
+ mDays = m
+ month = monthIndex
+
+ day = tmpDays - mDays //Setup the date
+
+ return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]"
+
+/proc/isLeap(y)
+ return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index adaab0540a3..c7a12321eb4 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -95,6 +95,7 @@ var/list/gamemode_cache = list()
var/banappeals
var/wikiurl
var/forumurl
+ var/githuburl
//Alert level description
var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced."
@@ -395,6 +396,9 @@ var/list/gamemode_cache = list()
if ("forumurl")
config.forumurl = value
+ if ("githuburl")
+ config.githuburl = value
+
if ("guest_jobban")
config.guest_jobban = 1
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
new file mode 100644
index 00000000000..da4192df257
--- /dev/null
+++ b/code/datums/helper_datums/getrev.dm
@@ -0,0 +1,39 @@
+var/global/datum/getrev/revdata = new()
+
+/datum/getrev
+ var/revision
+ var/date
+ var/showinfo
+
+/datum/getrev/New()
+ var/list/head_log = file2list(".git/logs/HEAD", "\n")
+ for(var/line=head_log.len, line>=1, line--)
+ if(head_log[line])
+ var/list/last_entry = text2list(head_log[line], " ")
+ if(last_entry.len < 2) continue
+ revision = last_entry[2]
+ // Get date/time
+ if(last_entry.len >= 5)
+ var/unix_time = text2num(last_entry[5])
+ if(unix_time)
+ date = unix2date(unix_time)
+ break
+ world.log << "Running revision:"
+ world.log << date
+ world.log << revision
+ return
+
+client/verb/showrevinfo()
+ set category = "OOC"
+ set name = "Show Server Revision"
+ set desc = "Check the current server code revision"
+
+ if(revdata.revision)
+ src << "Server revision: [revdata.date]"
+ if(config.githuburl)
+ src << "[revdata.revision]"
+ else
+ src << revdata.revision
+ else
+ src << "Revision unknown"
+ return
diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index 9bfa0e1c5dd..9016836274a 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -204,7 +204,7 @@
item_quants[O.name]++
else
item_quants[O.name] = 1
- user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
+ user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].")
nanomanager.update_uis(src)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 1d585c9c894..76df8968edc 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -91,7 +91,7 @@ REAGENT SCANNER
if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
usr << "\red You don't have the dexterity to do this!"
return
- user.visible_message(" [user] has analyzed [M]'s vitals."," You have analyzed [M]'s vitals.")
+ user.visible_message(" [user] has analyzed [M]'s vitals."," You have analyzed [M]'s vitals.")
if (!istype(M, /mob/living/carbon) || (ishuman(M) && (M:species.flags & IS_SYNTHETIC)))
//these sensors are designed for organic life
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index 7cbd14cb21c..81df901b38b 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -20,7 +20,7 @@
armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20)
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
- siemens_coefficient = 0.1
+ siemens_coefficient = 0.2
permeability_coefficient = 0.1
unacidable = 1
diff --git a/code/modules/clothing/spacesuits/rig/suits/ert.dm b/code/modules/clothing/spacesuits/rig/suits/ert.dm
index 70c21d51089..c46b019a4d2 100644
--- a/code/modules/clothing/spacesuits/rig/suits/ert.dm
+++ b/code/modules/clothing/spacesuits/rig/suits/ert.dm
@@ -29,8 +29,8 @@
desc = "A suit worn by the engineering division of a NanoTrasen Emergency Response Team. Has orange highlights. Armoured and space ready."
suit_type = "ERT engineer"
icon_state = "ert_engineer_rig"
-
- glove_type = /obj/item/clothing/gloves/rig/ert_engineer
+ armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100)
+ siemens_coefficient = 0
initial_modules = list(
/obj/item/rig_module/ai_container,
@@ -39,10 +39,6 @@
/obj/item/rig_module/device/rcd
)
-/obj/item/clothing/gloves/rig/ert_engineer
- name = "insulated gauntlets"
- siemens_coefficient = 0
-
/obj/item/weapon/rig/ert/medical
name = "ERT-M suit control module"
desc = "A suit worn by the medical division of a NanoTrasen Emergency Response Team. Has white highlights. Armoured and space ready."
diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm
index 639cea4d5e9..2eb6121f083 100644
--- a/code/modules/events/viral_infection.dm
+++ b/code/modules/events/viral_infection.dm
@@ -1,3 +1,5 @@
+/var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed
+
datum/event/viral_infection
var/list/viruses = list()
@@ -40,10 +42,30 @@ datum/event/viral_infection/start()
if(!candidates.len) return
candidates = shuffle(candidates)//Incorporating Donkie's list shuffle
+ var/list/used_viruses = list()
+ var/list/used_candidates = list()
severity = max(EVENT_LEVEL_MUNDANE, severity - 1)
var/actual_severity = severity * rand(1, 3)
while(actual_severity > 0 && candidates.len)
var/datum/disease2/disease/D = pick(viruses)
infect_mob(candidates[1], D.getcopy())
+ used_candidates += candidates[1]
candidates.Remove(candidates[1])
actual_severity--
+ used_viruses |= D
+
+ event_viruses |= used_viruses
+ var/list/used_viruses_links = list()
+ var/list/used_viruses_text = list()
+ for(var/datum/disease2/disease/D in used_viruses)
+ used_viruses_links += "[D.name()]"
+ used_viruses_text += D.name()
+
+ var/list/used_candidates_links = list()
+ var/list/used_candidates_text = list()
+ for(var/mob/M in used_candidates)
+ used_candidates_links += key_name_admin(M)
+ used_candidates_text += key_name(M)
+
+ log_admin("Virus event affecting [english_list(used_candidates_text)] started; Viruses: [english_list(used_viruses_text)]")
+ message_admins("Virus event affecting [english_list(used_candidates_links)] started; Viruses: [english_list(used_viruses_links)]")
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 79dc2cc657a..a38c6e6b9ac 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1365,11 +1365,11 @@
var/obj/item/clothing/glasses/welding/O = glasses
if(!O.up)
found_welder = 1
- else if(istype(head, /obj/item/clothing/head/welding))
+ if(!found_welder && istype(head, /obj/item/clothing/head/welding))
var/obj/item/clothing/head/welding/O = head
if(!O.up)
found_welder = 1
- else if(istype(back, /obj/item/weapon/rig))
+ if(!found_welder && istype(back, /obj/item/weapon/rig))
var/obj/item/weapon/rig/O = back
if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES))
if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1))
diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm
index 203159f3c68..147edb1afe3 100644
--- a/code/modules/mob/typing_indicator.dm
+++ b/code/modules/mob/typing_indicator.dm
@@ -5,14 +5,17 @@ mob/var/typing
mob/var/last_typed
mob/var/last_typed_time
-var/global/image/typing_indicator
+mob/var/obj/effect/decal/typing_indicator
/mob/proc/set_typing_indicator(var/state)
if(!typing_indicator)
- typing_indicator = image('icons/mob/talk.dmi',null,"typing")
+ typing_indicator = new
+ typing_indicator.icon = 'icons/mob/talk.dmi'
+ typing_indicator.icon_state = "typing"
- if(client)
+ if(client && !stat)
+ typing_indicator.invisibility = invisibility
if(client.prefs.toggles & SHOW_TYPING)
overlays -= typing_indicator
else
diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm
index 5215400a4e1..ea572e8fb43 100644
--- a/code/modules/shuttles/shuttle_emergency.dm
+++ b/code/modules/shuttles/shuttle_emergency.dm
@@ -62,7 +62,11 @@
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
if (emergency_shuttle.autopilot)
emergency_shuttle.autopilot = 0
- world << "\blue Alert: The shuttle autopilot has been overridden. Launch sequence initiated!"
+ world << "Alert: The shuttle autopilot has been overridden. Launch sequence initiated!"
+
+ if(usr)
+ log_admin("[key_name(usr)] has overridden the shuttle autopilot and activated launch sequence")
+ message_admins("[key_name_admin(usr)] has overridden the shuttle autopilot and activated launch sequence")
..(user)
@@ -72,7 +76,11 @@
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
if (emergency_shuttle.autopilot)
emergency_shuttle.autopilot = 0
- world << "\blue Alert: The shuttle autopilot has been overridden. Bluespace drive engaged!"
+ world << "Alert: The shuttle autopilot has been overridden. Bluespace drive engaged!"
+
+ if(usr)
+ log_admin("[key_name(usr)] has overridden the shuttle autopilot and forced immediate launch")
+ message_admins("[key_name_admin(usr)] has overridden the shuttle autopilot and forced immediate launch")
..(user)
@@ -82,7 +90,11 @@
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
if (emergency_shuttle.autopilot)
emergency_shuttle.autopilot = 0
- world << "\blue Alert: The shuttle autopilot has been overridden. Launch sequence aborted!"
+ world << "Alert: The shuttle autopilot has been overridden. Launch sequence aborted!"
+
+ if(usr)
+ log_admin("[key_name(usr)] has overridden the shuttle autopilot and cancelled launch sequence")
+ message_admins("[key_name_admin(usr)] has overridden the shuttle autopilot and cancelled launch sequence")
..(user)
@@ -102,8 +114,8 @@
authorized = initial(authorized)
//returns 1 if the ID was accepted and a new authorization was added, 0 otherwise
-/obj/machinery/computer/shuttle_control/emergency/proc/read_authorization(var/ident)
- if (!ident)
+/obj/machinery/computer/shuttle_control/emergency/proc/read_authorization(var/obj/item/ident)
+ if (!ident || !istype(ident))
return 0
if (authorized.len >= req_authorizations)
return 0 //don't need any more
@@ -112,33 +124,35 @@
var/auth_name
var/dna_hash
- if(istype(ident, /obj/item/weapon/card/id))
- var/obj/item/weapon/card/id/ID = ident
- access = ID.access
- auth_name = "[ID.registered_name] ([ID.assignment])"
- dna_hash = ID.dna_hash
+ var/obj/item/weapon/card/id/ID = ident.GetID()
- if(istype(ident, /obj/item/device/pda))
- var/obj/item/device/pda/PDA = ident
- access = PDA.id.access
- auth_name = "[PDA.id.registered_name] ([PDA.id.assignment])"
- dna_hash = PDA.id.dna_hash
+ if(!ID)
+ return
+
+ access = ID.access
+ auth_name = "[ID.registered_name] ([ID.assignment])"
+ dna_hash = ID.dna_hash
if (!access || !istype(access))
return 0 //not an ID
if (dna_hash in authorized)
- src.visible_message("[src] buzzes. That ID has already been scanned.")
+ src.visible_message("\The [src] buzzes. That ID has already been scanned.")
return 0
if (!(access_heads in access))
- src.visible_message("[src] buzzes, rejecting [ident].")
+ src.visible_message("\The [src] buzzes, rejecting [ident].")
return 0
- src.visible_message("[src] beeps as it scans [ident].")
+ src.visible_message("\The [src] beeps as it scans [ident].")
authorized[dna_hash] = auth_name
if (req_authorizations - authorized.len)
- world << "\blue Alert: [req_authorizations - authorized.len] authorization\s needed to override the shuttle autopilot."
+ world << "Alert: [req_authorizations - authorized.len] authorization\s needed to override the shuttle autopilot."
+
+ if(usr)
+ log_admin("[key_name(usr)] has inserted [ID] into the shuttle control computer - [req_authorizations - authorized.len] authorisation\s needed")
+ message_admins("[key_name_admin(usr)] has inserted [ID] into the shuttle control computer - [req_authorizations - authorized.len] authorisation\s needed")
+
return 1
@@ -146,7 +160,7 @@
/obj/machinery/computer/shuttle_control/emergency/attackby(obj/item/weapon/W as obj, mob/user as mob)
if (istype(W, /obj/item/weapon/card/emag) && !emagged)
- user << "\blue You short out the [src]'s authorization protocols."
+ user << "You short out \the [src]'s authorization protocols."
emagged = 1
return
diff --git a/config/example/config.txt b/config/example/config.txt
index cc5af6a013b..7648a51a889 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -180,6 +180,9 @@ GUEST_BAN
## Wiki address
# WIKIURL http://example.com
+## GitHub address
+# GITHUBURL https://github.com/example-user/example-repository
+
## Ban appeals URL - usually for a forum or wherever people should go to contact your admins.
# BANAPPEALS http://example.com
diff --git a/html/changelogs/HarpyEagle-ERTERigFix.yml b/html/changelogs/HarpyEagle-ERTERigFix.yml
new file mode 100644
index 00000000000..fa888cba462
--- /dev/null
+++ b/html/changelogs/HarpyEagle-ERTERigFix.yml
@@ -0,0 +1,18 @@
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+
+author: HarpyEagle
+delete-after: True
+
+changes:
+ - bugfix: "Fixes Engineer ERT gloves not being insulated."
diff --git a/html/changelogs/PsiOmegaDelta-ServerRevision.yml b/html/changelogs/PsiOmegaDelta-ServerRevision.yml
new file mode 100644
index 00000000000..56581748c1c
--- /dev/null
+++ b/html/changelogs/PsiOmegaDelta-ServerRevision.yml
@@ -0,0 +1,5 @@
+author: PsiOmegaDelta
+delete-after: True
+
+changes:
+ - rscadd: "You can now review the server revision date and hash by using the 'Show Server Revision' verb in the OOC category."