diff --git a/.travis.yml b/.travis.yml
index 2559dcab32..c9a8d196fc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ sudo: false
env:
global:
- BYOND_MAJOR="512"
- - BYOND_MINOR="1412"
+ - BYOND_MINOR="1453"
- MACRO_COUNT=4
matrix:
- TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0"
diff --git a/code/__defines/admin.dm b/code/__defines/admin.dm
index d2afb7846e..84e0656c97 100644
--- a/code/__defines/admin.dm
+++ b/code/__defines/admin.dm
@@ -45,3 +45,27 @@
#define SMITE_BLUESPACEARTILLERY "Bluespace Artillery"
#define SMITE_SPONTANEOUSCOMBUSTION "Spontaneous Combustion"
#define SMITE_LIGHTNINGBOLT "Lightning Bolt"
+
+#define ADMIN_QUE(user) "(?)"
+#define ADMIN_FLW(user) "(FLW)"
+#define ADMIN_PP(user) "(PP)"
+#define ADMIN_VV(atom) "(VV)"
+#define ADMIN_SM(user) "(SM)"
+#define ADMIN_TP(user) "(TP)"
+#define ADMIN_BSA(user) "(BSA)"
+#define ADMIN_KICK(user) "(KICK)"
+#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
+#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
+#define ADMIN_SC(user) "(SC)"
+#define ADMIN_SMITE(user) "(SMITE)"
+#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
+#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
+#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)]"
+#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
+#define ADMIN_JMP(src) "(JMP)"
+#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
+#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+
+#define AHELP_ACTIVE 1
+#define AHELP_CLOSED 2
+#define AHELP_RESOLVED 3
\ No newline at end of file
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index c44df79802..d8c4c69e0a 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -149,6 +149,7 @@
#define MAT_GLASS "glass"
#define MAT_SILVER "silver"
#define MAT_GOLD "gold"
+#define MAT_URANIUM "uranium" //Did it
#define MAT_TITANIUM "titanium"
#define MAT_PHORON "phoron"
#define MAT_DIAMOND "diamond"
diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm
index 82ec12a211..51c6db5325 100644
--- a/code/__defines/qdel.dm
+++ b/code/__defines/qdel.dm
@@ -20,4 +20,11 @@
#define QDELING(X) (X.gc_destroyed)
#define QDELETED(X) (!X || X.gc_destroyed)
-#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
\ No newline at end of file
+#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+
+#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
+#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
+#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
+
+/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
+ QDEL_LIST(L)
diff --git a/code/__defines/typeids.dm b/code/__defines/typeids.dm
new file mode 100644
index 0000000000..9af310012b
--- /dev/null
+++ b/code/__defines/typeids.dm
@@ -0,0 +1,6 @@
+//Byond type ids
+#define TYPEID_NULL "0"
+#define TYPEID_NORMAL_LIST "f"
+//helper macros
+#define GET_TYPEID(ref) ( ( (lentext(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, lentext(ref)-6) ) )
+#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
\ No newline at end of file
diff --git a/code/_global_vars/misc.dm b/code/_global_vars/misc.dm
index de2779c45b..5e7737f163 100644
--- a/code/_global_vars/misc.dm
+++ b/code/_global_vars/misc.dm
@@ -1 +1,5 @@
-GLOBAL_LIST_EMPTY(all_observable_events)
\ No newline at end of file
+GLOBAL_LIST_EMPTY(all_observable_events)
+
+GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks.
+
+GLOBAL_VAR_INIT(TAB, " ")
\ No newline at end of file
diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm
index 7fa2b5d162..fac5a14036 100644
--- a/code/_global_vars/mobs.dm
+++ b/code/_global_vars/mobs.dm
@@ -1,2 +1,5 @@
GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
-GLOBAL_PROTECT(admins)
\ No newline at end of file
+GLOBAL_PROTECT(admins)
+GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
+GLOBAL_LIST_EMPTY(stealthminID)
+GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
\ No newline at end of file
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index dc3fdc005c..4a249b050c 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -26,17 +26,17 @@
max_z = max(z, max_z)
return max_z
-/proc/get_area(O)
- var/turf/loc = get_turf(O)
- if(loc)
- var/area/res = loc.loc
- .= res
+/proc/get_area(atom/A)
+ if(isarea(A))
+ return A
+ var/turf/T = get_turf(A)
+ return T ? T.loc : null
-/proc/get_area_name(N) //get area by its name
- for(var/area/A in all_areas)
- if(A.name == N)
- return A
- return 0
+/proc/get_area_name(atom/X, format_text = FALSE)
+ var/area/A = isarea(X) ? X : get_area(X)
+ if(!A)
+ return null
+ return format_text ? format_text(A.name) : A.name
/proc/get_area_master(const/O)
var/area/A = get_area(O)
@@ -605,3 +605,8 @@ datum/projectile_data
/proc/SecondsToTicks(var/seconds)
return seconds * 10
+
+/proc/window_flash(var/client_or_usr)
+ if (!client_or_usr)
+ return
+ winset(client_or_usr, "mainwindow", "flash=5")
\ No newline at end of file
diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm
index 97a7feafd2..8c4d62a605 100644
--- a/code/_helpers/global_lists_vr.dm
+++ b/code/_helpers/global_lists_vr.dm
@@ -106,11 +106,12 @@ var/global/list/global_egg_types = list(
"Tajaran" = TAJARAN_EGG,
"Akula" = AKULA_EGG,
"Skrell" = SKRELL_EGG,
+ "Nevrean" = NEVREAN_EGG,
"Sergal" = SERGAL_EGG,
"Human" = HUMAN_EGG,
"Slime" = SLIME_EGG,
"Egg" = EGG_EGG,
- "Xenochimera" = XENOCHIMERA_EGG,
+ "Xenochimera" = XENOCHIMERA_EGG,
"Xenomorph" = XENOMORPH_EGG)
var/global/list/tf_egg_types = list(
@@ -119,10 +120,11 @@ var/global/list/tf_egg_types = list(
"Akula" = /obj/structure/closet/secure_closet/egg/shark,
"Skrell" = /obj/structure/closet/secure_closet/egg/skrell,
"Sergal" = /obj/structure/closet/secure_closet/egg/sergal,
+ "Nevrean" = /obj/structure/closet/secure_closet/egg/nevrean,
"Human" = /obj/structure/closet/secure_closet/egg/human,
"Slime" = /obj/structure/closet/secure_closet/egg/slime,
"Egg" = /obj/structure/closet/secure_closet/egg,
- "Xenochimera" = /obj/structure/closet/secure_closet/egg/scree,
+ "Xenochimera" = /obj/structure/closet/secure_closet/egg/scree,
"Xenomorph" = /obj/structure/closet/secure_closet/egg/xenomorph)
var/global/list/edible_trash = list(/obj/item/trash,
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index 17bc605ae7..b9e5dd8623 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -240,6 +240,17 @@ proc/listclearnulls(list/list)
for(var/i in L)
. |= i
+//same, but returns nothing and acts on list in place (also handles associated values properly)
+/proc/uniqueList_inplace(list/L)
+ var/temp = L.Copy()
+ L.len = 0
+ for(var/key in temp)
+ if (isnum(key))
+ L |= key
+ else
+ L[key] = temp[key]
+
+
//Mergesort: divides up the list into halves to begin the sort
/proc/sortKey(var/list/client/L, var/order = 1)
if(isnull(L) || L.len < 2)
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index f75802eb45..ce640731a6 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -64,6 +64,11 @@ var/next_station_date_change = 1 DAY
var/time_portion = time2text(world.timeofday, "hh:mm:ss")
return "[date_portion]T[time_portion]"
+/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
+ if(!wtime)
+ wtime = world.time
+ return time2text(wtime - GLOB.timezoneOffset, format)
+
/* Returns 1 if it is the selected month and day */
proc/isDay(var/month, var/day)
if(isnum(month) && isnum(day))
@@ -137,4 +142,101 @@ var/round_start_time = 0
i *= 2
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, GLOB.CURRENT_TICKLIMIT))
-#undef DELTA_CALC
\ No newline at end of file
+#undef DELTA_CALC
+
+
+//Takes a value of time in deciseconds.
+//Returns a text value of that number in hours, minutes, or seconds.
+/proc/DisplayTimeText(time_value, truncate = FALSE)
+ var/second = (time_value)*0.1
+ var/second_adjusted = null
+ var/second_rounded = FALSE
+ var/minute = null
+ var/hour = null
+ var/day = null
+
+ if(!second)
+ return "0 seconds"
+ if(second >= 60)
+ minute = FLOOR(second/60, 1)
+ second = round(second - (minute*60), 0.1)
+ second_rounded = TRUE
+ if(second) //check if we still have seconds remaining to format, or if everything went into minute.
+ second_adjusted = round(second) //used to prevent '1 seconds' being shown
+ if(day || hour || minute)
+ if(second_adjusted == 1 && second >= 1)
+ second = " and 1 second"
+ else if(second > 1)
+ second = " and [second_adjusted] seconds"
+ else //shows a fraction if seconds is < 1
+ if(second_rounded) //no sense rounding again if it's already done
+ second = " and [second] seconds"
+ else
+ second = " and [round(second, 0.1)] seconds"
+ else
+ if(second_adjusted == 1 && second >= 1)
+ second = "[truncate ? "second" : "1 second"]"
+ else if(second > 1)
+ second = "[second_adjusted] seconds"
+ else
+ if(second_rounded)
+ second = "[second] seconds"
+ else
+ second = "[round(second, 0.1)] seconds"
+ else
+ second = null
+
+ if(!minute)
+ return "[second]"
+ if(minute >= 60)
+ hour = FLOOR(minute/60, 1)
+ minute = (minute - (hour*60))
+ if(minute) //alot simpler from here since you don't have to worry about fractions
+ if(minute != 1)
+ if((day || hour) && second)
+ minute = ", [minute] minutes"
+ else if((day || hour) && !second)
+ minute = " and [minute] minutes"
+ else
+ minute = "[minute] minutes"
+ else
+ if((day || hour) && second)
+ minute = ", 1 minute"
+ else if((day || hour) && !second)
+ minute = " and 1 minute"
+ else
+ minute = "[truncate ? "minute" : "1 minute"]"
+ else
+ minute = null
+
+ if(!hour)
+ return "[minute][second]"
+ if(hour >= 24)
+ day = FLOOR(hour/24, 1)
+ hour = (hour - (day*24))
+ if(hour)
+ if(hour != 1)
+ if(day && (minute || second))
+ hour = ", [hour] hours"
+ else if(day && (!minute || !second))
+ hour = " and [hour] hours"
+ else
+ hour = "[hour] hours"
+ else
+ if(day && (minute || second))
+ hour = ", 1 hour"
+ else if(day && (!minute || !second))
+ hour = " and 1 hour"
+ else
+ hour = "[truncate ? "hour" : "1 hour"]"
+ else
+ hour = null
+
+ if(!day)
+ return "[hour][minute][second]"
+ if(day > 1)
+ day = "[day] days"
+ else
+ day = "[truncate ? "day" : "1 day"]"
+
+ return "[day][hour][minute][second]"
\ No newline at end of file
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 8dbecfbf35..7590c0947c 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -522,6 +522,14 @@ Turf and target are seperate in case you want to teleport some distance from a t
return "[round((powerused * 0.000001),0.001)] MW"
return "[round((powerused * 0.000000001),0.0001)] GW"
+/proc/get_mob_by_ckey(key)
+ if(!key)
+ return
+ var/list/mobs = sortmobs()
+ for(var/mob/M in mobs)
+ if(M.ckey == key)
+ return M
+
//Forces a variable to be posative
/proc/modulus(var/M)
if(M >= 0)
diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm
index 5f4ac77a3a..1c9196d332 100644
--- a/code/game/jobs/job/captain.dm
+++ b/code/game/jobs/job/captain.dm
@@ -60,13 +60,13 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer,
access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station,
- access_hop, access_RC_announce, access_keycard_auth, access_gateway)
+ access_hop, access_RC_announce, access_keycard_auth) //, access_gateway) //VOREStation Edit
minimal_access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers,
access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads,
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer,
access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station,
- access_hop, access_RC_announce, access_keycard_auth, access_gateway)
+ access_hop, access_RC_announce, access_keycard_auth) //, access_gateway) //VOREStation Edit
/datum/job/secretary
title = "Command Secretary"
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 278e516aae..8792a7ba35 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -434,6 +434,7 @@ var/global/datum/controller/occupations/job_master
H.job = rank
log_game("JOINED [key_name(H)] as \"[rank]\"")
+ log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add
// If they're head, give them the account info for their department
if(H.mind && job.head_position)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 4d061899e8..20f5a617cf 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -395,7 +395,7 @@
return 0
// Do after stuff here
- user << "You start to weld the [src].."
+ user << "You start to weld [src].."
playsound(src.loc, WT.usesound, 50, 1)
WT.eyecheck(user)
busy = 1
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index 3826eb3c94..14ba6dd005 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -20,6 +20,7 @@
/obj/machinery/computer/atmoscontrol/laptop
name = "Atmospherics Laptop"
desc = "A cheap laptop."
+ icon_screen = "atmoslaptop"
icon_state = "laptop"
icon_keyboard = "laptop_key"
density = 0
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index 31e68c1b50..0a90aef582 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -5,6 +5,7 @@
desc = "Used to view, edit and maintain medical records."
icon_keyboard = "med_key"
icon_screen = "medcomp"
+ density = 0 //Why does a laptop blocks peoples.
light_color = "#315ab4"
req_one_access = list(access_medical, access_forensics_lockers, access_robotics)
circuit = /obj/item/weapon/circuitboard/med_data
diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm
index fd6a1048c7..2f781129b6 100644
--- a/code/game/machinery/oxygen_pump.dm
+++ b/code/game/machinery/oxygen_pump.dm
@@ -33,7 +33,7 @@
if(breather.internals)
breather.internals.icon_state = "internal0"
breather.remove_from_mob(contained)
- visible_message("The mask rapidly retracts just before /the [src] is destroyed!")
+ visible_message("\The [contained] rapidly retracts just before /the [src] is destroyed!")
breather = null
QDEL_NULL(tank)
@@ -45,11 +45,11 @@
if(istype(target) && CanMouseDrop(target))
if(!can_apply_to_target(target, usr)) // There is no point in attempting to apply a mask if it's impossible.
return
- usr.visible_message("\The [usr] begins placing the mask onto [target]..")
+ usr.visible_message("\The [usr] begins placing \the [contained] onto [target].")
if(!do_mob(usr, target, 25) || !can_apply_to_target(target, usr))
return
// place mask and add fingerprints
- usr.visible_message("\The [usr] has placed \the mask on [target]'s mouth.")
+ usr.visible_message("\The [usr] has placed \the [contained] on [target]'s mouth.")
attach_mask(target)
src.add_fingerprint(usr)
@@ -69,7 +69,7 @@
tank.forceMove(src)
breather.remove_from_mob(contained)
contained.forceMove(src)
- src.visible_message("\The [user] makes \The [contained] rapidly retracts back into \the [src]!")
+ src.visible_message("\The [user] makes \the [contained] rapidly retract back into \the [src]!")
if(breather.internals)
breather.internals.icon_state = "internal0"
breather = null
@@ -112,7 +112,7 @@
to_chat(user, "There is no tank in \the [src].")
return
if(stat & MAINT)
- to_chat(user, "Please close \the maintenance hatch first.")
+ to_chat(user, "Please close the maintenance hatch first.")
return
if(!Adjacent(target))
to_chat(user, "Please stay close to \the [src].")
@@ -123,18 +123,15 @@
return
//Checking if breather is still valid
if(target == breather && target.wear_mask != contained)
- to_chat(user, "\The [target] is not using the supplied mask.")
+ to_chat(user, "\The [target] is not using the supplied [contained].")
return
return 1
/obj/machinery/oxygen_pump/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_screwdriver())
stat ^= MAINT
- user.visible_message("\The [user] [stat & MAINT ? "opens" : "closes"] \the [src].", "You [stat & MAINT ? "open" : "close"] \the [src].")
- if(stat & MAINT)
- icon_state = icon_state_open
- if(!stat)
- icon_state = icon_state_closed
+ user.visible_message("\The [user] [(stat & MAINT) ? "opens" : "closes"] \the [src].", "You [(stat & MAINT) ? "open" : "close"] \the [src].")
+ icon_state = (stat & MAINT) ? icon_state_open : icon_state_closed
//TO-DO: Open icon
if(istype(W, /obj/item/weapon/tank) && (stat & MAINT))
if(tank)
@@ -151,7 +148,7 @@
/obj/machinery/oxygen_pump/examine(var/mob/user)
. = ..()
if(tank)
- to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())]")
+ to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())] kPa.")
else
to_chat(user, "It is missing a tank!")
diff --git a/code/game/objects/items/weapons/material/ashtray.dm b/code/game/objects/items/weapons/material/ashtray.dm
index d5e7cd67bb..8becb48010 100644
--- a/code/game/objects/items/weapons/material/ashtray.dm
+++ b/code/game/objects/items/weapons/material/ashtray.dm
@@ -14,7 +14,7 @@ var/global/list/ashtray_cache = list()
if(!material)
qdel(src)
return
- max_butts = round(material.hardness/10) //This is arbitrary but whatever.
+ max_butts = round(material.hardness/5) //This is arbitrary but whatever.
src.pixel_y = rand(-5, 5)
src.pixel_x = rand(-6, 6)
update_icon()
diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm
index 3a09d6611a..7c2f2ea26f 100644
--- a/code/game/objects/items/weapons/tools/weldingtool.dm
+++ b/code/game/objects/items/weapons/tools/weldingtool.dm
@@ -3,7 +3,7 @@
* Welding Tool
*/
/obj/item/weapon/weldingtool
- name = "welding tool"
+ name = "\improper welding tool"
icon = 'icons/obj/tools.dmi'
icon_state = "welder"
item_state = "welder"
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 18f6c88aa3..4a392c0bfe 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -9,18 +9,26 @@
var/icon_closed = "closed"
var/icon_opened = "open"
var/opened = 0
- var/welded = 0
+ var/sealed = 0
+ var/seal_tool = /obj/item/weapon/weldingtool //Tool used to seal the closet, defaults to welder
var/wall_mounted = 0 //never solid (You can always pass over it)
var/health = 100
+
var/breakout = 0 //if someone is currently breaking out. mutex
+ var/breakout_time = 2 //2 minutes by default
+ var/breakout_sound = 'sound/effects/grillehit.ogg' //Sound that plays while breaking out
+
var/storage_capacity = 2 * MOB_MEDIUM //This is so that someone can't pack hundreds of items in a locker/crate
//then open it in a populated area to crash clients.
+ var/storage_cost = 40 //How much space this closet takes up if it's stuffed in another closet
+
var/open_sound = 'sound/machines/click.ogg'
var/close_sound = 'sound/machines/click.ogg'
- var/store_misc = 1
- var/store_items = 1
- var/store_mobs = 1
+ var/store_misc = 1 //Chameleon item check
+ var/store_items = 1 //Will the closet store items?
+ var/store_mobs = 1 //Will the closet store mobs?
+ var/max_closets = 0 //Number of other closets allowed on tile before it won't close.
var/list/starts_with
@@ -42,7 +50,7 @@
content_size += Ceiling(I.w_class/2)
if(content_size > storage_capacity-5)
storage_capacity = content_size + 5
-
+ update_icon()
/obj/structure/closet/examine(mob/user)
if(..(user, 1) && !opened)
@@ -51,29 +59,33 @@
if(!I.anchored)
content_size += Ceiling(I.w_class/2)
if(!content_size)
- user << "It is empty."
+ to_chat(user, "It is empty.")
else if(storage_capacity > content_size*4)
- user << "It is barely filled."
+ to_chat(user, "It is barely filled.")
else if(storage_capacity > content_size*2)
- user << "It is less than half full."
+ to_chat(user, "It is less than half full.")
else if(storage_capacity > content_size)
- user << "There is still some free space."
+ to_chat(user, "There is still some free space.")
else
- user << "It is full."
+ to_chat(user, "It is full.")
/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0 || wall_mounted)) return 1
return (!density)
/obj/structure/closet/proc/can_open()
- if(src.welded)
+ if(src.sealed)
return 0
return 1
/obj/structure/closet/proc/can_close()
+ var/closet_count = 0
for(var/obj/structure/closet/closet in get_turf(src))
if(closet != src)
- return 0
+ if(!closet.anchored)
+ closet_count ++
+ if(closet_count > max_closets)
+ return 0
return 1
/obj/structure/closet/proc/dump_contents()
@@ -102,7 +114,7 @@
src.icon_state = src.icon_opened
src.opened = 1
playsound(src.loc, open_sound, 15, 1, -3)
- density = 0
+ density = !density
return 1
/obj/structure/closet/proc/close()
@@ -119,12 +131,14 @@
stored_units += store_items(stored_units)
if(store_mobs)
stored_units += store_mobs(stored_units)
+ if(max_closets)
+ stored_units += store_closets(stored_units)
src.icon_state = src.icon_closed
src.opened = 0
playsound(src.loc, close_sound, 15, 1, -3)
- density = 1
+ density = !density
return 1
//Cham Projector Exception
@@ -162,9 +176,25 @@
added_units += M.mob_size
return added_units
+/obj/structure/closet/proc/store_closets(var/stored_units)
+ var/added_units = 0
+ for(var/obj/structure/closet/C in src.loc)
+ if(C == src) //Don't store ourself
+ continue
+ if(C.anchored) //Don't worry about anchored things on the same tile
+ continue
+ if(C.max_closets) //Prevents recursive storage
+ continue
+ if(stored_units + added_units + storage_cost > storage_capacity)
+ break
+ C.forceMove(src)
+ added_units += storage_cost
+ return added_units
+
+
/obj/structure/closet/proc/toggle(mob/user as mob)
if(!(src.opened ? src.close() : src.open()))
- user << "It won't budge!"
+ to_chat(user, "It won't budge!")
return
update_icon()
@@ -222,7 +252,7 @@
if(!WT.isOn())
return
else
- user << "You need more welding fuel to complete this task."
+ to_chat(user, "You need more welding fuel to complete this task.")
return
playsound(src, WT.usesound, 50)
new /obj/item/stack/material/steel(src.loc)
@@ -248,21 +278,25 @@
W.forceMove(src.loc)
else if(istype(W, /obj/item/weapon/packageWrap))
return
- else if(istype(W, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = W
- if(!WT.remove_fuel(0,user))
- if(!WT.isOn())
- return
- else
- user << "You need more welding fuel to complete this task."
- return
- playsound(src, WT.usesound, 50)
- src.welded = !src.welded
- src.update_icon()
- for(var/mob/M in viewers(src))
- M.show_message("[src] has been [welded?"welded shut":"unwelded"] by [user.name].", 3, "You hear welding.", 2)
+ else if(seal_tool)
+ if(istype(W, seal_tool))
+ var/obj/item/weapon/S = W
+ if(istype(S, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = S
+ if(!WT.remove_fuel(0,user))
+ if(!WT.isOn())
+ return
+ else
+ to_chat(user, "You need more welding fuel to complete this task.")
+ return
+ if(do_after(user, 20 * S.toolspeed))
+ playsound(src, S.usesound, 50)
+ src.sealed = !src.sealed
+ src.update_icon()
+ for(var/mob/M in viewers(src))
+ M.show_message("[src] has been [sealed?"sealed":"unsealed"] by [user.name].", 3)
else if(W.is_wrench())
- if(welded)
+ if(sealed)
if(anchored)
user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.")
else
@@ -270,7 +304,7 @@
playsound(src, W.usesound, 50)
if(do_after(user, 20 * W.toolspeed))
if(!src) return
- user << "You [anchored? "un" : ""]secured \the [src]!"
+ to_chat(user, "You [anchored? "un" : ""]secured \the [src]!")
anchored = !anchored
else
src.attack_hand(user)
@@ -306,7 +340,7 @@
return
if(!src.open())
- user << "It won't budge!"
+ to_chat(user, "It won't budge!")
/obj/structure/closet/attack_hand(mob/user as mob)
src.add_fingerprint(user)
@@ -316,7 +350,7 @@
/obj/structure/closet/attack_self_tk(mob/user as mob)
src.add_fingerprint(user)
if(!src.toggle())
- usr << "It won't budge!"
+ to_chat(usr, "It won't budge!")
/obj/structure/closet/attack_ghost(mob/ghost)
if(ghost.client && ghost.client.inquisitive_ghost)
@@ -336,14 +370,14 @@
src.add_fingerprint(usr)
src.toggle(usr)
else
- usr << "This mob type can't use this verb."
+ to_chat(usr, "This mob type can't use this verb.")
-/obj/structure/closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot)
+/obj/structure/closet/update_icon()//Putting the sealed stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot)
overlays.Cut()
if(!opened)
icon_state = icon_closed
- if(welded)
- overlays += "welded"
+ if(sealed)
+ overlays += "sealed"
else
icon_state = icon_opened
@@ -359,20 +393,19 @@
/obj/structure/closet/proc/req_breakout()
if(opened)
return 0 //Door's open... wait, why are you in it's contents then?
- if(!welded)
- return 0 //closed but not welded...
+ if(!sealed)
+ return 0 //closed but not sealed...
return 1
/obj/structure/closet/proc/mob_breakout(var/mob/living/escapee)
- var/breakout_time = 2 //2 minutes by default
if(breakout || !req_breakout())
return
escapee.setClickCooldown(100)
- //okay, so the closet is either welded or locked... resist!!!
- escapee << "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)"
+ //okay, so the closet is either sealed or locked... resist!!!
+ to_chat(escapee, "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)")
visible_message("\The [src] begins to shake violently!")
@@ -389,20 +422,20 @@
breakout = 0
return
- playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
+ playsound(src.loc, breakout_sound, 100, 1)
animate_shake()
add_fingerprint(escapee)
//Well then break it!
breakout = 0
- escapee << "You successfully break out!"
+ to_chat(escapee, "You successfully break out!")
visible_message("\The [escapee] successfully broke out of \the [src]!")
- playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
+ playsound(src.loc, breakout_sound, 100, 1)
break_open()
animate_shake()
/obj/structure/closet/proc/break_open()
- welded = 0
+ sealed = 0
update_icon()
//Do this to prevent contents from being opened into nullspace (read: bluespace)
if(istype(loc, /obj/structure/bigDelivery))
@@ -421,3 +454,9 @@
/obj/structure/closet/AllowDrop()
return TRUE
+
+/obj/structure/closet/return_air_for_internal_lifeform(var/mob/living/L)
+ if(src.loc)
+ if(istype(src.loc, /obj/structure/closet))
+ return (loc.return_air_for_internal_lifeform(L))
+ return return_air()
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm
index 67de5d38b9..6d5df80ed8 100644
--- a/code/game/objects/structures/crates_lockers/closets/coffin.dm
+++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm
@@ -4,9 +4,161 @@
icon_state = "coffin"
icon_closed = "coffin"
icon_opened = "coffin_open"
+ seal_tool = /obj/item/weapon/tool/screwdriver
+ breakout_sound = 'sound/weapons/tablehit1.ogg'
/obj/structure/closet/coffin/update_icon()
if(!opened)
icon_state = icon_closed
else
icon_state = icon_opened
+
+/* Graves */
+/obj/structure/closet/grave
+ name = "grave"
+ desc = "Dirt."
+ icon_state = "grave"
+ icon_closed = "grave"
+ icon_opened = "grave_open"
+ seal_tool = null
+ breakout_sound = 'sound/weapons/thudswoosh.ogg'
+ anchored = 1
+ max_closets = 1
+ opened = 1
+
+/obj/structure/closet/grave/attack_hand(mob/user as mob)
+ if(opened)
+ visible_message("[user] starts to climb into \the [src.name].", \
+ "You start to lower yourself into \the [src.name].")
+ if(do_after(user, 50))
+ user.forceMove(src.loc)
+ visible_message("[user] climbs into \the [src.name].", \
+ "You climb into \the [src.name].")
+ else
+ visible_message("[user] decides not to climb into \the [src.name].", \
+ "You stop climbing into \the [src.name].")
+ return
+
+/obj/structure/closet/grave/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(opened && ismob(mover))
+ var/mob/M = mover
+ add_fingerprint(M)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.m_intent == "walk")
+ to_chat(H, "You stop at the edge of \the [src.name].")
+ return FALSE
+ else
+ to_chat(H, "You fall into \the [src.name]!")
+ fall_in(H)
+ return TRUE
+ if(isrobot(M))
+ var/mob/living/silicon/robot/R = M
+ if(R.a_intent == I_HELP)
+ to_chat(R, "You stop at the edge of \the [src.name].")
+ return FALSE
+ else
+ to_chat(R, "You enter \the [src.name].")
+ return TRUE
+ return TRUE //Everything else can move over the graves
+
+/obj/structure/closet/grave/proc/fall_in(mob/living/L) //Only called on humans for now, but still
+ L.Weaken(5)
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ var/limb_damage = rand(5,25)
+ H.adjustBruteLoss(limb_damage)
+
+/obj/structure/closet/grave/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(src.opened)
+ if(istype(W, /obj/item/weapon/shovel))
+ user.visible_message("[user] piles dirt into \the [src.name].", \
+ "You start to pile dirt into \the [src.name].", \
+ "You hear dirt being moved.")
+ if(do_after(user, 40 * W.toolspeed))
+ user.visible_message("[user] pats down the dirt on top of \the [src.name].", \
+ "You finish filling in \the [src.name].")
+ close()
+ return
+ else
+ user.visible_message("[user] stops filling in \the [src.name].", \
+ "You change your mind and stop filling in \the [src.name].")
+ return
+ if(istype(W, /obj/item/weapon/grab))
+ var/obj/item/weapon/grab/G = W
+ src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet
+ return 0
+ if(istype(W,/obj/item/tk_grab))
+ return 0
+ if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len)
+ var/obj/item/weapon/storage/laundry_basket/LB = W
+ var/turf/T = get_turf(src)
+ for(var/obj/item/I in LB.contents)
+ LB.remove_from_storage(I, T)
+ user.visible_message("[user] empties \the [LB] into \the [src].", \
+ "You empty \the [LB] into \the [src].", \
+ "You hear rustling of clothes.")
+ return
+ if(isrobot(user))
+ return
+ if(W.loc != user) // This should stop mounted modules ending up outside the module.
+ return
+ usr.drop_item()
+ if(W)
+ W.forceMove(src.loc)
+ else
+ if(istype(W, /obj/item/weapon/shovel))
+ if(user.a_intent == I_HURT) // Hurt intent means you're trying to kill someone, or just get rid of the grave
+ user.visible_message("[user] begins to smoothe out the dirt of \the [src.name].", \
+ "You start to smoothe out the dirt of \the [src.name].", \
+ "You hear dirt being moved.")
+ if(do_after(user, 40 * W.toolspeed))
+ user.visible_message("[user] finishes smoothing out \the [src.name].", \
+ "You finish smoothing out \the [src.name].")
+ if(LAZYLEN(contents))
+ alpha = 40 // If we've got stuff inside, like maybe a person, just make it hard to see us
+ else
+ qdel(src) // Else, go away
+ return
+ else
+ user.visible_message("[user] stops concealing \the [src.name].", \
+ "You stop concealing \the [src.name].")
+ return
+ else
+ user.visible_message("[user] begins to unearth \the [src.name].", \
+ "You start to unearth \the [src.name].", \
+ "You hear dirt being moved.")
+ if(do_after(user, 40 * W.toolspeed))
+ user.visible_message("[user] reaches the bottom of \the [src.name].", \
+ "You finish digging out \the [src.name].")
+ break_open()
+ return
+ else
+ user.visible_message("[user] stops digging out \the [src.name].", \
+ "You stop digging out \the [src.name].")
+ return
+ return
+
+/obj/structure/closet/grave/close()
+ ..()
+ if(!opened)
+ sealed = TRUE
+
+/obj/structure/closet/grave/open()
+ .=..()
+ alpha = 255 // Needed because of grave hiding
+
+/obj/structure/closet/grave/bullet_act(var/obj/item/projectile/P)
+ return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets
+
+/obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L)
+ var/gasid = "carbon_dioxide"
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.species && H.species.exhale_type)
+ gasid = H.species.exhale_type
+ var/datum/gas_mixture/grave_breath = new()
+ var/datum/gas_mixture/above_air = return_air()
+ grave_breath.adjust_gas(gasid, BREATH_MOLES)
+ grave_breath.temperature = (above_air.temperature) - 30 //Underground
+ return grave_breath
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/closets/egg_vr.dm b/code/game/objects/structures/crates_lockers/closets/egg_vr.dm
index b47a49c4b5..1a1bd40e2f 100644
--- a/code/game/objects/structures/crates_lockers/closets/egg_vr.dm
+++ b/code/game/objects/structures/crates_lockers/closets/egg_vr.dm
@@ -10,7 +10,7 @@
open_sound = 'sound/vore/schlorp.ogg'
close_sound = 'sound/vore/schlorp.ogg'
opened = 0
- welded = 0 //Don't touch this.
+ sealed = 0 //Don't touch this.
health = 100
/obj/structure/closet/secure_closet/egg/attackby(obj/item/weapon/W, mob/user as mob) //This also prevents crew from welding the eggs and making them unable to be opened.
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm b/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm
index c606151550..62497919fc 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm
@@ -43,8 +43,8 @@
overlays += icon(src.icon, "door")
- if(welded)
- overlays += icon(src.icon,"welded")
+ if(sealed)
+ overlays += icon(src.icon,"sealed")
if(broken)
overlays += icon(src.icon,"broken")
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 994fe0eca1..af07ceb9e5 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
@@ -46,23 +46,23 @@
/obj/structure/closet/secure_closet/proc/togglelock(mob/user as mob)
if(src.opened)
- user << "Close the locker first."
+ to_chat(user, "Close the locker first.")
return
if(src.broken)
- user << "The locker appears to be broken."
+ to_chat(user, "The locker appears to be broken.")
return
if(user.loc == src)
- user << "You can't reach the lock from inside."
+ to_chat(user, "You can't reach the lock from inside.")
return
if(src.allowed(user))
src.locked = !src.locked
playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3)
for(var/mob/O in viewers(user, 3))
if((O.client && !( O.blinded )))
- O << "The locker has been [locked ? null : "un"]locked by [user]."
+ to_chat(O, "The locker has been [locked ? null : "un"]locked by [user].")
update_icon()
else
- user << "Access Denied"
+ to_chat(user, "Access Denied")
/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(src.opened)
@@ -73,7 +73,7 @@
if(src.large)
src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet
else
- user << "The locker is too small to stuff [G.affecting] into!"
+ to_chat(user, "The locker is too small to stuff [G.affecting] into!")
if(isrobot(user))
return
if(W.loc != user) // This should stop mounted modules ending up outside the module.
@@ -89,14 +89,14 @@
playsound(src.loc, 'sound/weapons/blade1.ogg', 50, 1)
playsound(src.loc, "sparks", 50, 1)
else if(W.is_wrench())
- if(welded)
+ if(sealed)
if(anchored)
user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.")
else
user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.")
if(do_after(user, 20 * W.toolspeed))
if(!src) return
- user << "You [anchored? "un" : ""]secured \the [src]!"
+ to_chat(user, "You [anchored? "un" : ""]secured \the [src]!")
anchored = !anchored
return
else if(istype(W,/obj/item/weapon/packageWrap) || istype(W,/obj/item/weapon/weldingtool))
@@ -143,9 +143,9 @@
src.add_fingerprint(usr)
src.togglelock(usr)
else
- usr << "This mob type can't use this verb."
+ to_chat(usr, "This mob type can't use this verb.")
-/obj/structure/closet/secure_closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot)
+/obj/structure/closet/secure_closet/update_icon()//Putting the sealed stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot)
overlays.Cut()
if(!opened)
@@ -155,8 +155,8 @@
icon_state = icon_locked
else
icon_state = icon_closed
- if(welded)
- overlays += "welded"
+ if(sealed)
+ overlays += "sealed"
else
icon_state = icon_opened
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 0b39583ce6..95dab6cc1e 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -593,6 +593,25 @@ var/list/admin_verbs_event_manager = list(
feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
+/client/proc/findStealthKey(txt)
+ if(txt)
+ for(var/P in GLOB.stealthminID)
+ if(GLOB.stealthminID[P] == txt)
+ return P
+ txt = GLOB.stealthminID[ckey]
+ return txt
+
+/client/proc/createStealthKey()
+ var/num = (rand(0,1000))
+ var/i = 0
+ while(i == 0)
+ i = 1
+ for(var/P in GLOB.stealthminID)
+ if(num == GLOB.stealthminID[P])
+ num++
+ i = 0
+ GLOB.stealthminID["[ckey]"] = "@[num2text(num)]"
+
/client/proc/stealth()
set category = "Admin"
set name = "Stealth Mode"
@@ -603,10 +622,12 @@ var/list/admin_verbs_event_manager = list(
mob.name = capitalize(ckey)
else
var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null)
- if(!new_key) return
+ if(!new_key)
+ return
if(length(new_key) >= 26)
new_key = copytext(new_key, 1, 26)
holder.fakekey = new_key
+ createStealthKey()
if(istype(mob, /mob/new_player))
mob.name = new_key
log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index f92bae98fb..3b8bb9c13a 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -10,6 +10,20 @@
check_antagonists()
return
+ if(href_list["ahelp"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/ahelp_ref = href_list["ahelp"]
+ var/datum/admin_help/AH = locate(ahelp_ref)
+ if(AH)
+ AH.Action(href_list["ahelp_action"])
+ else
+ to_chat(usr, "Ticket [ahelp_ref] has been deleted!")
+
+ else if(href_list["ahelp_tickets"])
+ GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"]))
+
if(href_list["dbsearchckey"] || href_list["dbsearchadmin"])
var/adminckey = href_list["dbsearchadmin"]
@@ -841,7 +855,9 @@
M << "No ban appeals URL has been set."
log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")
-
+ var/datum/admin_help/AH = M.client ? M.client.current_ticket : null
+ if(AH)
+ AH.Resolve()
qdel(M.client)
//qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends.
if("No")
@@ -867,7 +883,9 @@
message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.")
feedback_inc("ban_perma",1)
DB_ban_record(BANTYPE_PERMA, M, -1, reason)
-
+ var/datum/admin_help/AH = M.client ? M.client.current_ticket : null
+ if(AH)
+ AH.Resolve()
qdel(M.client)
//qdel(M)
if("Cancel")
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 733d0f4f23..c27272a421 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -1,32 +1,599 @@
+/client/var/datum/admin_help/current_ticket //the current ticket the (usually) not-admin client is dealing with
-//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
-var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as")
+//
+//TICKET MANAGER
+//
+
+GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
+
+/datum/admin_help_tickets
+ var/list/active_tickets = list()
+ var/list/closed_tickets = list()
+ var/list/resolved_tickets = list()
+
+ var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE)
+ var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED)
+ var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED)
+
+/datum/admin_help_tickets/Destroy()
+ QDEL_LIST(active_tickets)
+ QDEL_LIST(closed_tickets)
+ QDEL_LIST(resolved_tickets)
+ QDEL_NULL(astatclick)
+ QDEL_NULL(cstatclick)
+ QDEL_NULL(rstatclick)
+ return ..()
+
+//private
+/datum/admin_help_tickets/proc/ListInsert(datum/admin_help/new_ticket)
+ var/list/ticket_list
+ switch(new_ticket.state)
+ if(AHELP_ACTIVE)
+ ticket_list = active_tickets
+ if(AHELP_CLOSED)
+ ticket_list = closed_tickets
+ if(AHELP_RESOLVED)
+ ticket_list = resolved_tickets
+ else
+ CRASH("Invalid ticket state: [new_ticket.state]")
+ var/num_closed = ticket_list.len
+ if(num_closed)
+ for(var/I in 1 to num_closed)
+ var/datum/admin_help/AH = ticket_list[I]
+ if(AH.id > new_ticket.id)
+ ticket_list.Insert(I, new_ticket)
+ return
+ ticket_list += new_ticket
+
+//opens the ticket listings for one of the 3 states
+/datum/admin_help_tickets/proc/BrowseTickets(state)
+ var/list/l2b
+ var/title
+ switch(state)
+ if(AHELP_ACTIVE)
+ l2b = active_tickets
+ title = "Active Tickets"
+ if(AHELP_CLOSED)
+ l2b = closed_tickets
+ title = "Closed Tickets"
+ if(AHELP_RESOLVED)
+ l2b = resolved_tickets
+ title = "Resolved Tickets"
+ if(!l2b)
+ return
+ var/list/dat = list("
[title]")
+ dat += "Refresh
"
+ for(var/I in l2b)
+ var/datum/admin_help/AH = I
+ dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
"
+
+ usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480")
+
+//Tickets statpanel
+/datum/admin_help_tickets/proc/stat_entry()
+ var/num_disconnected = 0
+ stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
+ for(var/I in active_tickets)
+ var/datum/admin_help/AH = I
+ if(AH.initiator)
+ stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update())
+ else
+ ++num_disconnected
+ if(num_disconnected)
+ stat("Disconnected:", astatclick.update("[num_disconnected]"))
+ stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]"))
+ stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
+
+//Reassociate still open ticket if one exists
+/datum/admin_help_tickets/proc/ClientLogin(client/C)
+ C.current_ticket = CKey2ActiveTicket(C.ckey)
+ if(C.current_ticket)
+ C.current_ticket.AddInteraction("Client reconnected.")
+ C.current_ticket.initiator = C
+
+//Dissasociate ticket
+/datum/admin_help_tickets/proc/ClientLogout(client/C)
+ if(C.current_ticket)
+ C.current_ticket.AddInteraction("Client disconnected.")
+ C.current_ticket.initiator = null
+ C.current_ticket = null
+
+//Get a ticket given a ckey
+/datum/admin_help_tickets/proc/CKey2ActiveTicket(ckey)
+ for(var/I in active_tickets)
+ var/datum/admin_help/AH = I
+ if(AH.initiator_ckey == ckey)
+ return AH
+
+//
+//TICKET LIST STATCLICK
+//
+
+/obj/effect/statclick/ticket_list
+ var/current_state
+
+/obj/effect/statclick/ticket_list/New(loc, name, state)
+ current_state = state
+ ..()
+
+/obj/effect/statclick/ticket_list/Click()
+ GLOB.ahelp_tickets.BrowseTickets(current_state)
+
+//
+//TICKET DATUM
+//
+
+/datum/admin_help
+ var/id
+ var/name
+ var/state = AHELP_ACTIVE
+
+ var/opened_at
+ var/closed_at
+
+ var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked
+ var/initiator_ckey
+ var/initiator_key_name
+
+ var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log()
+
+ var/obj/effect/statclick/ahelp/statclick
+
+ var/static/ticket_counter = 0
+
+//call this on its own to create a ticket, don't manually assign current_ticket
+//msg is the title of the ticket: usually the ahelp text
+//is_bwoink is TRUE if this ticket was started by an admin PM
+/datum/admin_help/New(msg, client/C, is_bwoink)
+ //clean the input msg
+ msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
+ if(!msg || !C || !C.mob)
+ qdel(src)
+ return
+
+ id = ++ticket_counter
+ opened_at = world.time
+
+ name = msg
+
+ initiator = C
+ initiator_ckey = initiator.ckey
+ initiator_key_name = key_name(initiator, FALSE, TRUE)
+ if(initiator.current_ticket) //This is a bug
+ log_debug("Multiple ahelp current_tickets")
+ initiator.current_ticket.AddInteraction("Ticket erroneously left open by code")
+ initiator.current_ticket.Close()
+ initiator.current_ticket = src
+
+ var/parsed_message = keywords_lookup(msg)
+
+ statclick = new(null, src)
+ _interactions = list()
+
+ if(is_bwoink)
+ AddInteraction("[key_name_admin(usr)] PM'd [LinkedReplyName()]")
+ message_admins("Ticket [TicketHref("#[id]")] created")
+ else
+ MessageNoRecipient(parsed_message)
+
+ //show it to the person adminhelping too
+ to_chat(C, "PM to-Admins: [name]")
+
+ //send it to irc if nobody is on and tell us how many were on
+ var/admin_number_present = send2irc_adminless_only(initiator_ckey, name)
+ log_admin("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
+ if(admin_number_present <= 0)
+ to_chat(C, "No active admins are online, your adminhelp was sent to the admin irc.")
+ send2adminchat() //VOREStation Add
+ GLOB.ahelp_tickets.active_tickets += src
+
+/datum/admin_help/Destroy()
+ RemoveActive()
+ GLOB.ahelp_tickets.closed_tickets -= src
+ GLOB.ahelp_tickets.resolved_tickets -= src
+ return ..()
+
+/datum/admin_help/proc/AddInteraction(formatted_message)
+ _interactions += "[gameTimestamp()]: [formatted_message]"
+
+//private
+/datum/admin_help/proc/FullMonty(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ . = ADMIN_FULLMONTY_NONAME(initiator.mob)
+ if(state == AHELP_ACTIVE)
+ . += ClosureLinks(ref_src)
+
+//private
+/datum/admin_help/proc/ClosureLinks(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ . = " (REJT)"
+ . += " (IC)"
+ . += " (CLOSE)"
+ . += " (RSLVE)"
+ . += " (HANDLE)"
+
+//private
+/datum/admin_help/proc/LinkedReplyName(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ return "[initiator_key_name]"
+
+//private
+/datum/admin_help/proc/TicketHref(msg, ref_src, action = "ticket")
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ return "[msg]"
+
+//message from the initiator without a target, all admins will see this
+//won't bug irc
+/datum/admin_help/proc/MessageNoRecipient(msg)
+ var/ref_src = "\ref[src]"
+ var/chat_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [msg]"
+
+ AddInteraction("[LinkedReplyName(ref_src)]: [msg]")
+ //send this msg to all admins
+
+ for(var/client/X in admins)
+ if(X.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping))
+ X << 'sound/effects/adminhelp.ogg'
+ window_flash(X)
+ to_chat(X, chat_msg)
+
+//Reopen a closed ticket
+/datum/admin_help/proc/Reopen()
+ if(state == AHELP_ACTIVE)
+ to_chat(usr, "This ticket is already open.")
+ return
+
+ if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey))
+ to_chat(usr, "This user already has an active ticket, cannot reopen this one.")
+ return
+
+ statclick = new(null, src)
+ GLOB.ahelp_tickets.active_tickets += src
+ GLOB.ahelp_tickets.closed_tickets -= src
+ GLOB.ahelp_tickets.resolved_tickets -= src
+ switch(state)
+ if(AHELP_CLOSED)
+ feedback_dec("ahelp_close")
+ if(AHELP_RESOLVED)
+ feedback_dec("ahelp_resolve")
+ state = AHELP_ACTIVE
+ closed_at = null
+ if(initiator)
+ initiator.current_ticket = src
+
+ AddInteraction("Reopened by [key_name_admin(usr)]")
+ if(initiator)
+ to_chat(initiator, "Ticket [TicketHref("#[id]")] was reopened by [key_name(usr)].")
+ var/msg = "Ticket [TicketHref("#[id]")] reopened by [key_name_admin(usr)]."
+ message_admins(msg)
+ log_admin(msg)
+ feedback_inc("ahelp_reopen")
+ TicketPanel() //can only be done from here, so refresh it
+
+//private
+/datum/admin_help/proc/RemoveActive()
+ if(state != AHELP_ACTIVE)
+ return
+ closed_at = world.time
+ QDEL_NULL(statclick)
+ GLOB.ahelp_tickets.active_tickets -= src
+ if(initiator && initiator.current_ticket == src)
+ initiator.current_ticket = null
+
+//Mark open ticket as closed/meme
+/datum/admin_help/proc/Close(silent = FALSE)
+ if(state != AHELP_ACTIVE)
+ return
+ RemoveActive()
+ state = AHELP_CLOSED
+ GLOB.ahelp_tickets.ListInsert(src)
+ AddInteraction("Closed by [key_name_admin(usr)].")
+ if(initiator)
+ to_chat(initiator, "Ticket [TicketHref("#[id]")] was closed by [key_name(usr)].")
+ if(!silent)
+ feedback_inc("ahelp_close")
+ var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name_admin(usr)]."
+ message_admins(msg)
+ log_admin(msg)
+
+//Mark open ticket as resolved/legitimate, returns ahelp verb
+/datum/admin_help/proc/Resolve(silent = FALSE)
+ if(state != AHELP_ACTIVE)
+ return
+ RemoveActive()
+ state = AHELP_RESOLVED
+ GLOB.ahelp_tickets.ListInsert(src)
+
+ AddInteraction("Resolved by [key_name_admin(usr)].")
+ if(initiator)
+ to_chat(initiator, "Ticket [TicketHref("#[id]")] was marked resolved by [key_name(usr)].")
+ if(!silent)
+ feedback_inc("ahelp_resolve")
+ var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name_admin(usr)]"
+ message_admins(msg)
+ log_admin(msg)
+
+//Close and return ahelp verb, use if ticket is incoherent
+/datum/admin_help/proc/Reject(key_name = key_name_admin(usr))
+ if(state != AHELP_ACTIVE)
+ return
+
+ if(initiator)
+ initiator << 'sound/effects/adminhelp.ogg'
+
+ to_chat(initiator, "- AdminHelp Rejected! -")
+ to_chat(initiator, "Your admin help was rejected.")
+ to_chat(initiator, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting.")
+
+ feedback_inc("ahelp_reject")
+ var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name_admin(usr)]"
+ message_admins(msg)
+ log_admin(msg)
+ AddInteraction("Rejected by [key_name_admin(usr)].")
+ Close(silent = TRUE)
+
+//Resolve ticket with IC Issue message
+/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr))
+ if(state != AHELP_ACTIVE)
+ return
+
+ var/msg = "- AdminHelp marked as IC issue! -
"
+ msg += "Losing is part of the game!
"
+ msg += "Your AdminHelp may also be unabled to be answered due to ongoing events."
+
+ if(initiator)
+ to_chat(initiator, msg)
+
+ feedback_inc("ahelp_icissue")
+ msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name_admin(usr)]"
+ message_admins(msg)
+ log_admin(msg)
+ AddInteraction("Marked as IC issue by [key_name_admin(usr)]")
+ Resolve(silent = TRUE)
+
+//Resolve ticket with IC Issue message
+/datum/admin_help/proc/HandleIssue()
+ if(state != AHELP_ACTIVE)
+ return
+
+ var/msg = "Your AdminHelp is being handled by [key_name(usr)] please be patient."
+
+ if(initiator)
+ to_chat(initiator, msg)
+
+ feedback_inc("ahelp_icissue")
+ msg = "Ticket [TicketHref("#[id]")] being handled by [key_name(usr)]"
+ message_admins(msg)
+ log_admin(msg)
+ AddInteraction("[key_name_admin(usr)] is now handling this ticket.")
+
+//Show the ticket panel
+/datum/admin_help/proc/TicketPanel()
+ var/list/dat = list("Ticket #[id]")
+ var/ref_src = "\ref[src]"
+ dat += "Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]
"
+ dat += "State: "
+ switch(state)
+ if(AHELP_ACTIVE)
+ dat += "OPEN"
+ if(AHELP_RESOLVED)
+ dat += "RESOLVED"
+ if(AHELP_CLOSED)
+ dat += "CLOSED"
+ else
+ dat += "UNKNOWN"
+ dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]"
+ if(state != AHELP_ACTIVE)
+ dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]"
+ dat += "
Opened at: [gameTimestamp(wtime = opened_at)] (Approx [(world.time - opened_at) / 600] minutes ago)"
+ if(closed_at)
+ dat += "
Closed at: [gameTimestamp(wtime = closed_at)] (Approx [(world.time - closed_at) / 600] minutes ago)"
+ dat += "
"
+ if(initiator)
+ dat += "Actions: [FullMonty(ref_src)]
"
+ else
+ dat += "DISCONNECTED[GLOB.TAB][ClosureLinks(ref_src)]
"
+ dat += "
Log:
"
+ for(var/I in _interactions)
+ dat += "[I]
"
+
+ usr << browse(dat.Join(), "window=ahelp[id];size=620x480")
+
+/datum/admin_help/proc/Retitle()
+ var/new_title = input(usr, "Enter a title for the ticket", "Rename Ticket", name) as text|null
+ if(new_title)
+ name = new_title
+ //not saying the original name cause it could be a long ass message
+ var/msg = "Ticket [TicketHref("#[id]")] titled [name] by [key_name_admin(usr)]"
+ message_admins(msg)
+ log_admin(msg)
+ TicketPanel() //we have to be here to do this
+
+//Forwarded action from admin/Topic
+/datum/admin_help/proc/Action(action)
+ testing("Ahelp action: [action]")
+ switch(action)
+ if("ticket")
+ TicketPanel()
+ if("retitle")
+ Retitle()
+ if("reject")
+ Reject()
+ if("reply")
+ usr.client.cmd_ahelp_reply(initiator)
+ if("icissue")
+ ICIssue()
+ if("close")
+ Close()
+ if("resolve")
+ Resolve()
+ if("handleissue")
+ HandleIssue()
+ if("reopen")
+ Reopen()
+
+//
+// TICKET STATCLICK
+//
+
+/obj/effect/statclick/ahelp
+ var/datum/admin_help/ahelp_datum
+
+/obj/effect/statclick/ahelp/New(loc, datum/admin_help/AH)
+ ahelp_datum = AH
+ ..(loc)
+
+/obj/effect/statclick/ahelp/update()
+ return ..(ahelp_datum.name)
+
+/obj/effect/statclick/ahelp/Click()
+ ahelp_datum.TicketPanel()
+
+/obj/effect/statclick/ahelp/Destroy()
+ ahelp_datum = null
+ return ..()
+
+//
+// CLIENT PROCS
+//
/client/verb/adminhelp(msg as text)
set category = "Admin"
set name = "Adminhelp"
if(say_disabled) //This is here to try to identify lag problems
- usr << "Speech is currently admin-disabled."
+ to_chat(usr, "Speech is currently admin-disabled.")
return
//handle muting and automuting
if(prefs.muted & MUTE_ADMINHELP)
- src << "Error: Admin-PM: You cannot send adminhelps (Muted)."
+ to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).")
+ return
+ if(handle_spam_prevention(msg,MUTE_ADMINHELP))
return
- adminhelped = 1 //Determines if they get the message to reply by clicking the name.
-
- if(msg)
- handle_spam_prevention(MUTE_ADMINHELP)
-
- //clean the input msg
if(!msg)
return
- msg = sanitize(msg)
- if(!msg)
+
+ //remove out adminhelp verb temporarily to prevent spamming of admins.
+ src.verbs -= /client/verb/adminhelp
+ spawn(1200)
+ src.verbs += /client/verb/adminhelp // 2 minute cool-down for adminhelps
+
+ feedback_add_details("admin_verb","Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ if(current_ticket)
+ if(alert(usr, "You already have a ticket open. Is this for the same issue?",,"Yes","No") != "No")
+ if(current_ticket)
+ current_ticket.MessageNoRecipient(msg)
+ return
+ else
+ to_chat(usr, "Ticket not found, creating new one...")
+ else
+ current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.")
+ current_ticket.Close()
+
+ new /datum/admin_help(msg, src, FALSE)
+
+//admin proc
+/client/proc/cmd_admin_ticket_panel()
+ set name = "Show Ticket List"
+ set category = "Admin"
+
+ if(!check_rights(R_ADMIN, TRUE))
return
- var/original_msg = msg
+
+ var/browse_to
+
+ switch(input("Display which ticket list?") as null|anything in list("Active Tickets", "Closed Tickets", "Resolved Tickets"))
+ if("Active Tickets")
+ browse_to = AHELP_ACTIVE
+ if("Closed Tickets")
+ browse_to = AHELP_CLOSED
+ if("Resolved Tickets")
+ browse_to = AHELP_RESOLVED
+ else
+ return
+
+ GLOB.ahelp_tickets.BrowseTickets(browse_to)
+
+//
+// LOGGING
+//
+
+//Use this proc when an admin takes action that may be related to an open ticket on what
+//what can be a client, ckey, or mob
+/proc/admin_ticket_log(what, message)
+ var/client/C
+ var/mob/Mob = what
+ if(istype(Mob))
+ C = Mob.client
+ else
+ C = what
+ if(istype(C) && C.current_ticket)
+ C.current_ticket.AddInteraction(message)
+ return C.current_ticket
+ if(istext(what)) //ckey
+ var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what)
+ if(AH)
+ AH.AddInteraction(message)
+ return AH
+
+//
+// HELPER PROCS
+//
+
+/proc/get_admin_counts(requiredflags = R_BAN)
+ . = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
+ for(var/client/X in admins)
+ .["total"] += X
+ if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X)) //VOREStation Edit
+ .["noflags"] += X
+ else if(X.is_afk())
+ .["afk"] += X
+ else if(X.holder.fakekey)
+ .["stealth"] += X
+ else
+ .["present"] += X
+
+/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
+ var/list/adm = get_admin_counts()
+ var/list/activemins = adm["present"]
+ . = activemins.len
+ if(. <= 0)
+ var/final = ""
+ var/list/afkmins = adm["afk"]
+ var/list/stealthmins = adm["stealth"]
+ var/list/powerlessmins = adm["noflags"]
+ var/list/allmins = adm["total"]
+ if(!afkmins.len && !stealthmins.len && !powerlessmins.len)
+ final = "[msg] - No admins online"
+ else
+ final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
+ send2irc(source,final)
+
+/proc/ircadminwho()
+ var/list/message = list("Admins: ")
+ var/list/admin_keys = list()
+ for(var/adm in admins)
+ var/client/C = adm
+ admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]"
+
+ for(var/admin in admin_keys)
+ if(LAZYLEN(admin_keys) > 1)
+ message += ", [admin]"
+ else
+ message += "[admin]"
+
+ return jointext(message, "")
+
+/proc/keywords_lookup(msg,irc)
+
+ //This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
+ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")
//explode the input msg into a list
var/list/msglist = splittext(msg, " ")
@@ -35,9 +602,11 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
var/list/surnames = list()
var/list/forenames = list()
var/list/ckeys = list()
+ var/founds = ""
for(var/mob/M in mob_list)
var/list/indexing = list(M.real_name, M.name)
- if(M.mind) indexing += M.mind.name
+ if(M.mind)
+ indexing += M.mind.name
for(var/string in indexing)
var/list/L = splittext(string, " ")
@@ -77,55 +646,17 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
mobs_found += found
if(!ai_found && isAI(found))
ai_found = 1
- msg += "[original_word] (?) "
+ var/is_antag = 0
+ if(found.mind && found.mind.special_role)
+ is_antag = 1
+ founds += "Name: [found.name]([found.real_name]) Ckey: [found.ckey] [is_antag ? "(Antag)" : null] "
+ msg += "[original_word](?|F) "
continue
- msg += "[original_word] "
-
- if(!mob) //this doesn't happen
- return
-
- var/ai_cl
- if(ai_found)
- ai_cl = " (CL)"
-
- //Options bar: mob, details ( admin = 2, dev = 3, event manager = 4, character name (0 = just ckey, 1 = ckey and character name), link? (0 no don't make it a link, 1 do so),
- // highlight special roles (0 = everyone has same looking name, 1 = antags / special roles get a golden name)
-
- msg = "Request for Help: [get_options_bar(mob, 2, 1, 1)][ai_cl] [msg]"
-
- var/admin_number_afk = 0
-
- for(var/client/X in admins)
- if((R_ADMIN|R_MOD|R_EVENT|R_SERVER) & X.holder.rights)
- if(X.is_afk())
- admin_number_afk++
- X << msg
- if(X.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping))
- X << 'sound/effects/adminhelp.ogg'
-
- //show it to the person adminhelping too
- src << "PM to-Staff : [original_msg]"
-
- var/admin_number_present = admins.len - admin_number_afk
- log_admin("HELP: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.")
- if(admin_number_present <= 0)
- send2adminirc("Request for Help from [key_name(src)]: [html_decode(original_msg)] - !![admin_number_afk ? "All admins AFK ([admin_number_afk])" : "No admins online"]!!")
- else
- send2adminirc("Request for Help from [key_name(src)]: [html_decode(original_msg)]")
- feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- // VoreStation Edit Start
- if (config.chat_webhook_url)
- spawn(0)
- var/query_string = "type=adminhelp"
- query_string += "&key=[url_encode(config.chat_webhook_key)]"
- query_string += "&from=[url_encode(key_name(src))]"
- query_string += "&msg=[url_encode(html_decode(original_msg))]"
- query_string += "&admin_number=[admins.len]"
- query_string += "&admin_number_afk=[admin_number_afk]"
- world.Export("[config.chat_webhook_url]?[query_string]")
- // VoreStation Edit End
- //YW EDIT START
- SERVER_TOOLS_RELAY_BROADCAST("ADMINHELP: FROM: [key_name(src)] - MSG: **[strip_html_properly(original_msg)]** - Heard by [admins.len-admin_number_afk] NON-AFK staff members.")
- //YW EDIT END
- return
+ msg += "[original_word] "
+ if(irc)
+ if(founds == "")
+ return "Search Failed"
+ else
+ return founds
+ return msg
\ No newline at end of file
diff --git a/code/modules/admin/verbs/adminhelp_vr.dm b/code/modules/admin/verbs/adminhelp_vr.dm
new file mode 100644
index 0000000000..9f3aee4a83
--- /dev/null
+++ b/code/modules/admin/verbs/adminhelp_vr.dm
@@ -0,0 +1,16 @@
+/datum/admin_help/proc/send2adminchat()
+ if(!config.chat_webhook_url)
+ return
+
+ var/list/adm = get_admin_counts()
+ var/list/afkmins = adm["afk"]
+ var/list/allmins = adm["total"]
+
+ spawn(0) //Unreliable world.Exports()
+ var/query_string = "type=adminhelp"
+ query_string += "&key=[url_encode(config.chat_webhook_key)]"
+ query_string += "&from=[url_encode(key_name(initiator))]"
+ query_string += "&msg=[url_encode(html_decode(name))]"
+ query_string += "&admin_number=[allmins.len]"
+ query_string += "&admin_number_afk=[afkmins.len]"
+ world.Export("[config.chat_webhook_url]?[query_string]")
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index 7485aa2531..56af4e7e15 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -109,8 +109,10 @@
if(!check_rights(R_ADMIN|R_MOD|R_DEBUG))
return
if(config.allow_admin_jump)
- log_admin("[key_name(usr)] teleported [key_name(M)]")
- message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1)
+ log_admin("[key_name(usr)] jumped to [key_name(M)]")
+ var/msg = "[key_name_admin(usr)] jumped to [key_name_admin(M)]"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
M.on_mob_jump()
M.loc = 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!
@@ -137,7 +139,9 @@
if(!M)
return
log_admin("[key_name(usr)] teleported [key_name(M)]")
- message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1)
+ var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
if(M)
M.on_mob_jump()
M.loc = get_turf(usr)
@@ -157,7 +161,9 @@
M.loc = 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]")
- message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1)
+ log_admin("[key_name(usr)] teleported [key_name(M)]")
+ var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
else
- alert("Admin jumping disabled")
+ alert("Admin jumping disabled")
\ No newline at end of file
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index a18937130e..2bf3a1f5cc 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -1,148 +1,286 @@
+#define IRCREPLYCOUNT 2
+
+
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
-/client/proc/cmd_admin_pm_context(mob/M as mob in mob_list)
+/client/proc/cmd_admin_pm_context(mob/M in mob_list)
set category = null
set name = "Admin PM Mob"
if(!holder)
- src << "Error: Admin-PM-Context: Only administrators may use this command."
+ to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.")
+ 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!
+ feedback_add_details("admin_verb","Admin PM Mob") //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"
if(!holder)
- src << "Error: Admin-PM-Panel: Only administrators may use this command."
+ to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.")
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/observer/dead))
+ else if(isobserver(T.mob))
targets["[T.mob.name](Ghost) - [T]"] = T
else
targets["[T.mob.real_name](as [T.mob.name]) - [T]"] = T
else
targets["(No Mob) - [T]"] = T
- var/list/sorted = sortList(targets)
- var/target = input(src,"To whom shall we send a message?","Admin PM",null) in sorted|null
+ var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sortList(targets)
cmd_admin_pm(targets[target],null)
- feedback_add_details("admin_verb","APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ feedback_add_details("admin_verb","Admin PM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/client/proc/cmd_ahelp_reply(whom)
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).")
+ return
+ var/client/C
+ if(istext(whom))
+ if(cmptext(copytext(whom,1,2),"@"))
+ whom = findStealthKey(whom)
+ C = GLOB.directory[whom]
+ else if(istype(whom,/client))
+ C = whom
+ if(!C)
+ if(holder)
+ to_chat(src, "Error: Admin-PM: Client not found.")
+ return
+
+ var/datum/admin_help/AH = C.current_ticket
+
+ if(AH)
+ message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
+ var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as text|null
+ if (!msg)
+ message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
+ return
+ cmd_admin_pm(whom, msg, AH)
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
//Fetching a message if needed. src is the sender and C is the target client
-
-/client/proc/cmd_admin_pm(var/client/C, var/msg = null)
+/client/proc/cmd_admin_pm(whom, msg, datum/admin_help/AH)
if(prefs.muted & MUTE_ADMINHELP)
- src << "Error: Private-Message: You are unable to use PM-s (muted)."
+ to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).")
return
- if(!istype(C,/client))
- if(holder) src << "Error: Private-Message: Client not found."
- else src << "Error: Private-Message: Client not found. They may have lost connection, so try using an adminhelp!"
+ if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
+ to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.")
+ to_chat(src, "Message: [msg]")
return
- //get message text, limit it's length.and clean/escape html
- if(!msg)
- msg = input(src,"Message:", "Private message to [key_name(C, 0, holder ? 1 : 0)]") as text|null
+ var/client/recipient
+ var/irc = 0
+ if(istext(whom))
+ if(cmptext(copytext(whom,1,2),"@"))
+ whom = findStealthKey(whom)
+ if(whom == "IRCKEY")
+ irc = 1
+ else
+ recipient = GLOB.directory[whom]
+ else if(istype(whom,/client))
+ recipient = whom
- if(!msg) return
- if(!C)
- if(holder) src << "Error: Admin-PM: Client not found."
- else src << "Error: Private-Message: Client not found. They may have lost connection, so try using an adminhelp!"
+
+ if(irc)
+ if(!ircreplyamount) //to prevent people from spamming irc
return
+ if(!msg)
+ msg = input(src,"Message:", "Private message to Administrator") as text|null
+
+ if(!msg)
+ return
+ if(holder)
+ to_chat(src, "Error: Use the admin IRC channel, nerd.")
+ return
+
+
+ else
+ if(!recipient)
+ if(holder)
+ to_chat(src, "Error: Admin-PM: Client not found.")
+ to_chat(src, msg)
+ else
+ current_ticket.MessageNoRecipient(msg)
+ return
+
+ //get message text, limit it's length.and clean/escape html
+ if(!msg)
+ msg = input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]") as text|null
+
+ if(!msg)
+ return
+
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).")
+ return
+
+ if(!recipient)
+ if(holder)
+ to_chat(src, "Error: Admin-PM: Client not found.")
+ else
+ current_ticket.MessageNoRecipient(msg)
+ return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the message if it's not sent by a high-rank admin
- //todo: sanitize for all???
- if(!check_rights(R_SERVER|R_DEBUG,0))
- msg = sanitize(msg)
- if(!msg) return
+ if(!check_rights(R_SERVER|R_DEBUG,0)||irc)//no sending html to the poor bots
+ msg = trim(sanitize(copytext(msg,1,MAX_MESSAGE_LEN)))
+ if(!msg)
+ return
- var/recieve_pm_type = "Player"
- if(holder)
- //mod PMs are maroon
- //PMs sent from admins and mods display their rank
- if(holder)
- if(!C.holder && holder && holder.fakekey)
- recieve_pm_type = "Admin"
- else
- recieve_pm_type = holder.rank
+ var/rawmsg = msg
- else if(!C.holder)
- src << "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden."
- return
+ var/keywordparsedmsg = keywords_lookup(msg)
- var/recieve_message
+ if(irc)
+ to_chat(src, "PM to-Admins: [rawmsg]")
+ admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]")
+ ircreplyamount--
+ send2irc("Reply: [ckey]",rawmsg)
+ else
+ if(recipient.holder)
+ if(holder) //both are admins
+ to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]")
+ to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]")
- if(holder && !C.holder)
- recieve_message = "-- Click the [recieve_pm_type]'s name to reply --\n"
- if(C.adminhelped)
- C << recieve_message
- C.adminhelped = 0
+ //omg this is dumb, just fill in both their tickets
+ var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]"
+ admin_ticket_log(src, interaction_message)
+ if(recipient != src) //reeee
+ admin_ticket_log(recipient, interaction_message)
- //AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
- if(config.popup_admin_pm)
- spawn(0) //so we don't hold the caller proc up
- var/sender = src
- var/sendername = key
- var/reply = sanitize(input(C, msg,"[recieve_pm_type] PM from [sendername]", "") as text|null) //show message and await a reply
- if(C && reply)
- if(sender)
- C.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
- else
- adminhelp(reply) //sender has left, adminhelp instead
+ else //recipient is an admin but sender is not
+ var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]"
+ admin_ticket_log(src, replymsg)
+ to_chat(recipient, replymsg)
+ to_chat(src, "PM to-Admins: [msg]")
+
+ //play the recieving admin the adminhelp sound (if they have them enabled)
+ if(recipient.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping))
+ recipient << 'sound/effects/adminhelp.ogg'
+
+ else
+ if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
+ if(!recipient.current_ticket)
+ new /datum/admin_help(msg, recipient, TRUE)
+
+ to_chat(recipient, "-- Administrator private message --")
+ to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]")
+ to_chat(recipient, "Click on the administrator's name to reply.")
+ to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]")
+
+ admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]")
+
+ //always play non-admin recipients the adminhelp sound
+ recipient << 'sound/effects/adminhelp.ogg'
+
+ //AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
+ if(config.popup_admin_pm)
+ spawn() //so we don't hold the caller proc up
+ var/sender = src
+ var/sendername = key
+ var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as text|null //show message and await a reply
+ if(recipient && reply)
+ if(sender)
+ recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
+ else
+ adminhelp(reply) //sender has left, adminhelp instead
+ return
+
+ else //neither are admins
+ to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.")
return
- src << "" + create_text_tag("pm_out_alt", "PM", src) + " to [get_options_bar(C, holder ? 1 : 0, holder ? 1 : 0, 1)]: [msg]"
- C << "" + create_text_tag("pm_in", "", C) + " \[[recieve_pm_type] PM\] [get_options_bar(src, C.holder ? 1 : 0, C.holder ? 1 : 0, 1)]: [msg]"
- //play the recieving admin the adminhelp sound (if they have them enabled)
- //non-admins shouldn't be able to disable this
- if(C.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping))
- C << 'sound/effects/adminhelp.ogg'
+ if(irc)
+ log_admin("PM: [key_name(src)]->IRC: [rawmsg]")
+ for(var/client/X in admins)
+ to_chat(X, "PM: [key_name(src, X, 0)]->IRC: [keywordparsedmsg]")
+ else
+ log_admin("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
+ //we don't use message_admins here because the sender/receiver might get it too
+ for(var/client/X in admins)
+ if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
+ to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" )
- log_adminpm(msg,src,C)
- send2adminirc("Reply: [key_name(src)]->[key_name(C)]: [html_decode(msg)]")
+/proc/IrcPm(target,msg,sender)
+ var/client/C = GLOB.directory[target]
- //we don't use message_admins here because the sender/receiver might get it too
- for(var/client/X in admins)
- //check client/X is an admin and isn't the sender or recipient
- if(X == C || X == src)
- continue
- if(X.key != key && X.key != C.key && (X.holder.rights & R_ADMIN|R_MOD|R_EVENT))
- X << "" + create_text_tag("pm_other", "PM:", X) + " [key_name(src, X, 0)] to [key_name(C, X, 0)]: [msg]"
+ var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
+ var/compliant_msg = trim(lowertext(msg))
+ var/irc_tagged = "[sender](IRC)"
+ var/list/splits = splittext(compliant_msg, " ")
+ if(splits.len && splits[1] == "ticket")
+ if(splits.len < 2)
+ return "Usage: ticket "
+ switch(splits[2])
+ if("close")
+ if(ticket)
+ ticket.Close(irc_tagged)
+ return "Ticket #[ticket.id] successfully closed"
+ if("resolve")
+ if(ticket)
+ ticket.Resolve(irc_tagged)
+ return "Ticket #[ticket.id] successfully resolved"
+ if("icissue")
+ if(ticket)
+ ticket.ICIssue(irc_tagged)
+ return "Ticket #[ticket.id] successfully marked as IC issue"
+ if("reject")
+ if(ticket)
+ ticket.Reject(irc_tagged)
+ return "Ticket #[ticket.id] successfully rejected"
+ else
+ return "Usage: ticket "
+ return "Error: Ticket could not be found"
-/client/proc/cmd_admin_irc_pm(sender)
- if(prefs.muted & MUTE_ADMINHELP)
- src << "Error: Private-Message: You are unable to use PM-s (muted)."
- return
+ var/static/stealthkey
+ var/adminname = "Administrator"
- var/msg = input(src,"Message:", "Reply private message to [sender] on IRC / 400 character limit") as text|null
+ if(!C)
+ return "Error: No client"
+ if(!stealthkey)
+ stealthkey = GenIrcStealthKey()
+
+ msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg)
- return
+ return "Error: No message"
- sanitize(msg)
+ message_admins("IRC message from [sender] to [key_name_admin(C)] : [msg]")
+ log_admin("IRC PM: [sender] -> [key_name(C)] : [msg]")
- // Handled on Bot32's end, unsure about other bots
-// if(length(msg) > 400) // TODO: if message length is over 400, divide it up into seperate messages, the message length restriction is based on IRC limitations. Probably easier to do this on the bots ends.
-// src << "Your message was not sent because it was more then 400 characters find your message below for ease of copy/pasting"
-// src << "[msg]"
-// return
+ to_chat(C, "-- Administrator private message --")
+ to_chat(C, "Admin PM from-[adminname]: [msg]")
+ to_chat(C, "Click on the administrator's name to reply.")
- send2adminirc("PlayerPM to [sender] from [key_name(src)]: [html_decode(msg)]")
+ admin_ticket_log(C, "PM From [irc_tagged]: [msg]")
- src << "" + create_text_tag("pm_out_alt", "", src) + " to IRC-[sender]: [msg]"
+ window_flash(C, ignorepref = TRUE)
+ //always play non-admin recipients the adminhelp sound
+ C << 'sound/effects/adminhelp.ogg'
- log_admin("PM: [key_name(src)]->IRC-[sender]: [msg]")
- for(var/client/X in admins)
- if(X == src)
- continue
- if(X.holder.rights & R_ADMIN|R_MOD)
- X << "" + create_text_tag("pm_other", "PM:", X) + " [key_name(src, X, 0)] to IRC-[sender]: [msg]"
+ C.ircreplyamount = IRCREPLYCOUNT
+
+ return "Message Successful"
+
+/proc/GenIrcStealthKey()
+ var/num = (rand(0,1000))
+ var/i = 0
+ while(i == 0)
+ i = 1
+ for(var/P in GLOB.stealthminID)
+ if(num == GLOB.stealthminID[P])
+ num++
+ i = 0
+ var/stealth = "@[num2text(num)]"
+ GLOB.stealthminID["IRCKEY"] = stealth
+ return stealth
+
+#undef IRCREPLYCOUNT
\ No newline at end of file
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index e3f2c3db56..1e8528acb9 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -96,7 +96,9 @@
M << "You hear a voice in your head... [msg]"
log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]")
- message_admins("SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]", 1)
+ msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE
@@ -135,9 +137,11 @@
if( !msg )
return
- M << msg
+ to_chat(M, msg)
log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]")
- message_admins(" DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
", 1)
+ msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_godmode(mob/M as mob in mob_list)
@@ -150,7 +154,9 @@
usr << " Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]"
log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]")
- message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]", 1)
+ var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -550,7 +556,9 @@ Traitors and the like can also be revived with the previous role mostly intact.
M.revive()
log_admin("[key_name(usr)] healed / revived [key_name(M)]")
- message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!", 1)
+ var/msg = "Admin [key_name_admin(usr)] healed / revived [ADMIN_LOOKUPFLW(M)]!"
+ message_admins(msg)
+ admin_ticket_log(M, msg)
else
alert("Admin revive disabled")
feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index 4f92884dfb..558dc668b0 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -39,17 +39,17 @@
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway)
. = ..()
+ density = 1 //VOREStation Add
/obj/machinery/gateway/centerstation/update_icon()
if(active)
icon_state = "oncenter"
return
icon_state = "offcenter"
-
+/* VOREStation Removal - Doesn't do anything
/obj/machinery/gateway/centerstation/New()
density = 1
-
-
+*/ //VOREStation Removal End
obj/machinery/gateway/centerstation/process()
if(stat & (NOPOWER))
@@ -90,7 +90,7 @@ obj/machinery/gateway/centerstation/process()
if(world.time < wait)
user << "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes."
return
- if(!awaygate.calibrated && LAZYLEN(awaydestinations))
+ if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit
user << "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update."
return
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index e49bdb0c74..47521f09f6 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -12,6 +12,7 @@
var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming.
var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent.
+ var/ircreplyamount = 0
/////////
//OTHER//
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 2d2eb18cc2..62ba21e744 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -57,7 +57,7 @@
if(mute_irc)
usr << ""
return
- cmd_admin_irc_pm(href_list["irc_msg"])
+ send2adminirc(href_list["irc_msg"])
return
@@ -112,6 +112,8 @@
clients += src
directory[ckey] = src
+ GLOB.ahelp_tickets.ClientLogin(src)
+
//Admin Authorisation
holder = admin_datums[ckey]
if(holder)
@@ -179,6 +181,7 @@
if(holder)
holder.owner = null
admins -= src
+ GLOB.ahelp_tickets.ClientLogout(src)
directory -= ckey
clients -= src
return ..()
diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm
index 58ecb608bc..8b43aca950 100644
--- a/code/modules/client/preference_setup/loadout/loadout_head.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_head.dm
@@ -353,25 +353,17 @@
sols[initial(sol.name)] = sol
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(sols))*/ // Vorestation removal.
-/datum/gear/head/surgical/black
- display_name = "surgical cap, black"
- path = /obj/item/clothing/head/surgery/black
+/datum/gear/head/surgical
+ display_name = "surgical cap selection"
+ path = /obj/item/clothing/head/surgery
-/datum/gear/head/surgical/blue
- display_name = "surgical cap, blue"
- path = /obj/item/clothing/head/surgery/blue
-
-/datum/gear/head/surgical/green
- display_name = "surgical cap, green"
- path = /obj/item/clothing/head/surgery/green
-
-/datum/gear/head/surgical/navyblue
- display_name = "surgical cap, navy blue"
- path = /obj/item/clothing/head/surgery/navyblue
-
-/datum/gear/head/surgical/purple
- display_name = "surgical cap, purple"
- path = /obj/item/clothing/head/surgery/purple
+/datum/gear/head/surgical/New()
+ ..()
+ var/list/caps = list()
+ for(var/surgery in typesof(/obj/item/clothing/head/surgery))
+ var/obj/item/clothing/head/surgery/cap_type = caps
+ caps[initial(cap_type.name)] = cap_type
+ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(caps))
/datum/gear/head/circuitry
display_name = "headwear, circuitry (empty)"
diff --git a/code/modules/client/preference_setup/vore/03_egg.dm b/code/modules/client/preference_setup/vore/03_egg.dm
index 51d36960bd..2567ca5021 100644
--- a/code/modules/client/preference_setup/vore/03_egg.dm
+++ b/code/modules/client/preference_setup/vore/03_egg.dm
@@ -4,6 +4,7 @@ var/AKULA_EGG = "Akula"
var/SKRELL_EGG = "Skrell"
var/SERGAL_EGG = "Sergal"
var/HUMAN_EGG = "Human"
+var/NEVREAN_EGG = "nevrean"
var/SLIME_EGG = "Slime"
var/EGG_EGG = "Egg"
var/XENOCHIMERA_EGG = "Xenochimera"
diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm
index c57f485146..4276a41163 100644
--- a/code/modules/client/preference_setup/vore/07_traits.dm
+++ b/code/modules/client/preference_setup/vore/07_traits.dm
@@ -87,6 +87,10 @@
var/S = pref.custom_base ? pref.custom_base : "Human"
var/datum/species/custom/new_CS = CS.produceCopy(S, pref.pos_traits + pref.neu_traits + pref.neg_traits, character)
+ //Statistics for this would be nice
+ var/english_traits = english_list(new_CS.traits, and_text = ";", comma_text = ";")
+ log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
+
//Any additional non-trait settings can be applied here
new_CS.blood_color = pref.blood_color
diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm
index 7f95ac306c..e3da347908 100644
--- a/code/modules/hydroponics/beekeeping/beehive.dm
+++ b/code/modules/hydroponics/beekeeping/beehive.dm
@@ -179,7 +179,7 @@
qdel(H)
spawn(50)
new /obj/item/honey_frame(loc)
- new /obj/item/stack/wax(loc)
+ new /obj/item/stack/material/wax(loc)
honey += processing
processing = 0
icon_state = "centrifuge"
@@ -234,14 +234,14 @@
qdel(src)
return
-/obj/item/stack/wax
+/obj/item/stack/material/wax
name = "wax"
singular_name = "wax piece"
desc = "Soft substance produced by bees. Used to make candles."
icon = 'icons/obj/beekeeping.dmi'
icon_state = "wax"
-/obj/item/stack/wax/New()
+/obj/item/stack/material/wax/New()
..()
recipes = wax_recipes
diff --git a/code/modules/maps/tg/reader.dm b/code/modules/maps/tg/reader.dm
index ff66d4bdd2..30de8067dc 100644
--- a/code/modules/maps/tg/reader.dm
+++ b/code/modules/maps/tg/reader.dm
@@ -157,7 +157,7 @@ var/global/use_preloader = FALSE
++turfsSkipped
#endif
CHECK_TICK
- maxx = max(maxx, ++xcrd)
+ maxx = max(maxx, xcrd++)
key_list[++key_list.len] = line_keys
// Rotate the list according to orientation
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 6f73930654..5985e708d2 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -30,11 +30,6 @@
var/excavation_amount = 200
-/obj/item/weapon/pickaxe/hammer
- name = "sledgehammer"
- //icon_state = "sledgehammer" Waiting on sprite
- desc = "A mining hammer made of reinforced metal. You feel like smashing your boss in the face with this."
-
/obj/item/weapon/pickaxe/silver
name = "silver pickaxe"
icon_state = "spickaxe"
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 676eedbdd9..9ed303732b 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -729,7 +729,7 @@
src << "blink, blink_r, blush, bow-(none)/mob, burp, choke, chuckle, clap, collapse, cough, cry, custom, deathgasp, drool, eyebrow, fastsway/qwag, \
frown, gasp, giggle, glare-(none)/mob, grin, groan, grumble, handshake, hug-(none)/mob, laugh, look-(none)/mob, moan, mumble, nod, pale, point-atom, \
raise, salute, scream, sneeze, shake, shiver, shrug, sigh, signal-#1-10, slap-(none)/mob, smile, sneeze, sniff, snore, stare-(none)/mob, stopsway/swag, sway/wag, swish, tremble, twitch, \
- twitch_v, vomit, whimper, wink, yawn. Synthetics: beep, buzz, yes, no, rcough, rsneeze, ping"
+ twitch_v, vomit, whimper, wink, yawn. Synthetics: beep, buzz, yess, no, rcough, rsneeze, ping"
else
src << "Unusable emote '[act]'. Say *help for a list."
diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm
index 213089d4fb..1146e5d6fa 100644
--- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm
@@ -33,6 +33,7 @@
/mob/living/proc/flying_toggle,
/mob/living/proc/start_wings_hovering) //Xenochimera get all the special verbs since they can't select traits.
+ virus_immune = 1 // They practically ARE one.
min_age = 18
max_age = 80
@@ -51,6 +52,16 @@
flags = NO_SCAN | NO_INFECT //Dying as a chimera is, quite literally, a death sentence. Well, if it wasn't for their revive, that is.
appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR
+ has_organ = list( //Same organ list as tajarans.
+ O_HEART = /obj/item/organ/internal/heart,
+ O_LUNGS = /obj/item/organ/internal/lungs,
+ O_VOICE = /obj/item/organ/internal/voicebox,
+ O_LIVER = /obj/item/organ/internal/liver,
+ O_KIDNEYS = /obj/item/organ/internal/kidneys,
+ O_BRAIN = /obj/item/organ/internal/brain,
+ O_EYES = /obj/item/organ/internal/eyes
+ )
+
flesh_color = "#AFA59E"
base_color = "#333333"
blood_color = "#14AD8B"
diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
index 3d29aad827..180d6ba441 100644
--- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
+++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
@@ -122,8 +122,8 @@
/datum/trait/winged_flight
name = "Winged Flight"
- desc = "Allows you to fly by using your wings."
- cost = 2 //Some in game value.
+ desc = "Allows you to fly by using your wings. Don't forget to bring them!"
+ cost = 1
/datum/trait/winged_flight/apply(var/datum/species/S,var/mob/living/carbon/human/H)
..(S,H)
diff --git a/code/modules/mob/living/simple_animal/humanoids/kobold.dm b/code/modules/mob/living/simple_animal/humanoids/kobold.dm
deleted file mode 100644
index 7355fce470..0000000000
--- a/code/modules/mob/living/simple_animal/humanoids/kobold.dm
+++ /dev/null
@@ -1,43 +0,0 @@
-//kobold
-/mob/living/simple_animal/kobold
- name = "kobold"
- desc = "A small, rat-like creature."
- icon = 'icons/mob/mob.dmi'
- icon_state = "kobold_idle"
- icon_living = "kobold_idle"
- icon_dead = "kobold_dead"
- intelligence_level = SA_HUMANOID
-
- run_at_them = 0
- cooperative = 1
-
- turns_per_move = 5
- see_in_dark = 6
-
- response_help = "pets"
- response_disarm = "gently pushes aside"
- response_harm = "kicks"
-
- min_oxy = 16 //Require atleast 16kPA oxygen
- minbodytemp = 223 //Below -50 Degrees Celcius
- maxbodytemp = 323 //Above 50 Degrees Celcius
-
- speak_chance = 5
- speak = list("You no take candle!","Ooh, pretty shiny.","Me take?","Where gold here...","Me likey.")
- speak_emote = list("mutters","hisses","grumbles")
- emote_hear = list("mutters under it's breath.","grumbles.", "yips!")
- emote_see = list("looks around suspiciously.", "scratches it's arm.","putters around a bit.")
-
- meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/monkey
-
-/mob/living/simple_animal/kobold/Life()
- . = ..()
- if(!.) return
-
- if(prob(5))
- flick("kobold_act",src)
-
-/mob/living/simple_animal/kobold/Move(var/dir)
- ..()
- if(!stat)
- flick("kobold_walk",src)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 6c1fdd2206..71a523c7d0 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -393,7 +393,7 @@
//Resisting out of closets
if(istype(loc,/obj/structure/closet))
var/obj/structure/closet/C = loc
- if(C.welded)
+ if(C.sealed)
handle_resist()
else
C.open()
diff --git a/code/modules/mob/living/simple_animal/simple_animal_vr.dm b/code/modules/mob/living/simple_animal/simple_animal_vr.dm
index 718dfee55b..dea5206448 100644
--- a/code/modules/mob/living/simple_animal/simple_animal_vr.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal_vr.dm
@@ -12,6 +12,10 @@
var/vore_bump_chance = 0 // Chance of trying to eat anyone that bumps into them, regardless of hostility
var/vore_bump_emote = "grabs hold of" // Allow messages for bumpnom mobs to have a flavorful bumpnom
var/vore_pounce_chance = 5 // Chance of this mob knocking down an opponent
+ var/vore_pounce_cooldown = 0 // Cooldown timer - if it fails a pounce it won't pounce again for a while
+ var/vore_pounce_successrate = 100 // Chance of a pounce succeeding against a theoretical 0-health opponent
+ var/vore_pounce_falloff = 1 // Success rate falloff per %health of target mob.
+ var/vore_pounce_maxhealth = 80 // Mob will not attempt to pounce targets above this %health
var/vore_standing_too = 0 // Can also eat non-stunned mobs
var/vore_ignores_undigestable = 1 // Refuse to eat mobs who are undigestable by the prefs toggle.
var/swallowsound = null // What noise plays when you succeed in eating the mob.
@@ -92,21 +96,49 @@
/mob/living/simple_animal/PunchTarget()
ai_log("vr/PunchTarget() [target_mob]", 3)
- // For things we don't want to eat, call the sideways "parent" to do normal punching
- if(!vore_active || !will_eat(target_mob))
+ // If we're not hungry, call the sideways "parent" to do normal punching
+ if(!vore_active)
return ..()
- // If target is standing we might pounce and eat them
- if(target_mob.canmove && prob(vore_pounce_chance))
- target_mob.Weaken(5)
- target_mob.visible_message("\the [src] pounces on \the [target_mob]!!")
+ // If target is standing we might pounce and knock them down instead of attacking
+ var/pouncechance = CanPounceTarget()
+ if(pouncechance)
+ return PounceTarget(pouncechance)
- // If they're down or we can eat standing, do it
- if(!target_mob.canmove || vore_standing_too)
+ // We're not attempting a pounce, if they're down or we can eat standing, do it as long as they're edible. Otherwise, hit normally.
+ if(will_eat(target_mob) && (!target_mob.canmove || vore_standing_too))
return EatTarget()
else
return ..()
+/mob/living/simple_animal/proc/CanPounceTarget() //returns either FALSE or a %chance of success
+ if(!target_mob.canmove || issilicon(target_mob) || world.time < vore_pounce_cooldown) //eliminate situations where pouncing CANNOT happen
+ return FALSE
+ if(!prob(vore_pounce_chance)) //mob doesn't want to pounce
+ return FALSE
+ if(will_eat(target_mob) && vore_standing_too) //100% chance of hitting people we can eat on the spot
+ return 100
+ var/TargetHealthPercent = (target_mob.health/target_mob.getMaxHealth())*100 //now we start looking at the target itself
+ if (TargetHealthPercent > vore_pounce_maxhealth) //target is too healthy to pounce
+ return FALSE
+ else
+ return max(0,(vore_pounce_successrate - (vore_pounce_falloff * TargetHealthPercent)))
+
+
+/mob/living/simple_animal/proc/PounceTarget(var/successrate = 100)
+ vore_pounce_cooldown = world.time + 20 SECONDS // don't attempt another pounce for a while
+ if(prob(successrate)) // pounce success!
+ target_mob.Weaken(5)
+ target_mob.visible_message("\the [src] pounces on \the [target_mob]!!")
+ else // pounce misses!
+ target_mob.visible_message("\the [src] attempts to pounce \the [target_mob] but misses!!")
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+
+ if(will_eat(target_mob) && (!target_mob.canmove || vore_standing_too)) //if they're edible then eat them too
+ return EatTarget()
+ else
+ return //just leave them
+
// Attempt to eat target
// TODO - Review this. Could be some issues here
/mob/living/simple_animal/proc/EatTarget()
@@ -117,7 +149,7 @@
. = animal_nom(target_mob)
playsound(src, swallowsound, 50, 1)
update_icon()
-
+
if(.)
// If we succesfully ate them, lose the target
LoseTarget()
diff --git a/code/modules/mob/living/simple_animal/vore/catgirl.dm b/code/modules/mob/living/simple_animal/vore/catgirl.dm
index 315ab0bc1b..e66e74219a 100644
--- a/code/modules/mob/living/simple_animal/vore/catgirl.dm
+++ b/code/modules/mob/living/simple_animal/vore/catgirl.dm
@@ -29,6 +29,7 @@
var/random_skin = 1
var/list/skins = list(
+ "catgirl",
"catgirlnude",
"catgirlbikini",
"catgirlrednude",
diff --git a/code/modules/mob/living/simple_animal/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_animal/vore/shadekin/shadekin.dm
index a435aaaf8c..ca6f035f32 100644
--- a/code/modules/mob/living/simple_animal/vore/shadekin/shadekin.dm
+++ b/code/modules/mob/living/simple_animal/vore/shadekin/shadekin.dm
@@ -241,7 +241,7 @@
if(specific_targets && isliving(A)) //Healing!
var/mob/living/L = A
var/health_percent = (L.health/L.maxHealth)*100
- if(health_percent <= 50)
+ if(health_percent <= 50 && will_eat(A))
return A
. = ..()
diff --git a/code/modules/mob/living/simple_animal/vore/shadekin/types.dm b/code/modules/mob/living/simple_animal/vore/shadekin/types.dm
index 3b3d102752..6dcf7645c7 100644
--- a/code/modules/mob/living/simple_animal/vore/shadekin/types.dm
+++ b/code/modules/mob/living/simple_animal/vore/shadekin/types.dm
@@ -17,7 +17,7 @@
eye_desc = "red eyes"
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \
slippery gullet of the creature. It's warm, and the air is thick. You can hear \
its body squelch and shift around you as you settle into its stomach! Thick digestive \
enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \
@@ -64,11 +64,11 @@
eye_desc = "blue eyes"
shy_approach = TRUE
stalker = TRUE
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \
slippery gullet of the creature. It's warm, and the air is thick. You can hear its body \
squelch and shift around you as you settle into its stomach! It's oddly calm, and very dark. \
The doughy flesh rolls across your form in gentle waves. The aches and pains across your form slowly begin to \
- diminish, your body is healing much faster than normal! You’re also soon soaked in harmless slime."
+ diminish, your body is healing much faster than normal! You're also soon soaked in harmless slime."
player_msg = "You've chosen to generate your own energy rather than taking \
it from others. Most of the time, anyway. You don't have a need to steal energy from others, and gather it up \
@@ -106,10 +106,10 @@
eye_desc = "purple eyes"
shy_approach = TRUE
stalker = TRUE
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, slippery gullet of the creature. \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet of the creature. \
It's warm, and the air is thick. You can hear its body squelch and shift around you as you settle into its stomach! \
- It’s relatively calm inside the dark organ. Wet and almost molten for how gooey your surroundings feel. \
- You can feel the doughy walls cling to you posessively... It’s almost like you could sink into them. \
+ It's relatively calm inside the dark organ. Wet and almost molten for how gooey your surroundings feel. \
+ You can feel the doughy walls cling to you posessively... It's almost like you could sink into them. \
There is also an ominous gurgling from somewhere nearby..."
player_msg = "You're familiar with generating your own energy, but occasionally \
@@ -147,10 +147,10 @@
eye_desc = "yellow eyes"
stalker = FALSE
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, slippery gullet \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \
of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \
as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \
- you out!! There doesn’t appear to be any actual danger here, harmless slime clings to you, but it’s getting \
+ you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \
harder and harder to move as those walls press in on you insistently!"
player_msg = "Your kind rarely ventures into realspace. Being in any well-lit \
@@ -187,10 +187,10 @@
eye_desc = "green eyes"
stalker = TRUE
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, slippery gullet \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \
of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \
as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \
- you out!! There doesn’t appear to be any actual danger here, harmless slime clings to you, but it’s getting \
+ you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \
harder and harder to move as those walls press in on you insistently!"
player_msg = "Your kind rarely ventures into realspace. Being in any well-lit area is very taxing on you, but you \
@@ -224,7 +224,7 @@
eye_desc = "orange eyes"
- vore_stomach_flavor = "You slip passed pointy triangle teeth and down the slick, \
+ vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \
slippery gullet of the creature. It's warm, and the air is thick. You can hear \
its body squelch and shift around you as you settle into its stomach! Thick digestive \
enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \
@@ -248,8 +248,8 @@
desc = "She appears to be a fluffer of some sort. Deep blue eyes and curious attitude."
icon_state = "rivyr"
eye_desc = ""
- vore_stomach_flavor = "Blue flesh gleams in the fading light as you slip down the little mar’s gullet! \
- Gooey flesh and heat surrounds your form as you’re tucked away into the darkness of her stomach! Thick slimes cling \
+ vore_stomach_flavor = "Blue flesh gleams in the fading light as you slip down the little mar's gullet! \
+ Gooey flesh and heat surrounds your form as you're tucked away into the darkness of her stomach! Thick slimes cling \
to you, but they seem to be harmless. The organ gently churns around you, clinging to your shape and forcing \
you to curl up a bit. You can feel her rub at you some through the layers of flesh and fluff, while aches \
and pains begin to fade away across your body."
diff --git a/code/modules/mob/living/simple_animal/vore/zz_vore_overrides.dm b/code/modules/mob/living/simple_animal/vore/zz_vore_overrides.dm
index 3b4074e455..fb4d74d360 100644
--- a/code/modules/mob/living/simple_animal/vore/zz_vore_overrides.dm
+++ b/code/modules/mob/living/simple_animal/vore/zz_vore_overrides.dm
@@ -162,6 +162,7 @@
vore_pounce_chance = 100
vore_digest_chance = 0 // just use the toggle
vore_default_mode = DM_HOLD //can use the toggle if you wanna be catfood
+ vore_standing_too = TRUE //gonna get pounced
/mob/living/simple_animal/cat/fluff/EatTarget()
var/mob/living/TM = target_mob
@@ -201,6 +202,7 @@
vore_pounce_chance = 100
vore_digest_chance = 0 // just use the toggle
vore_default_mode = DM_HOLD //can use the toggle if you wanna be foxfood
+ vore_standing_too = TRUE // gonna get pounced
/mob/living/simple_animal/fox/fluff/EatTarget()
var/mob/living/TM = target_mob
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 2acc36e61e..98dd934cf1 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -703,7 +703,10 @@
stat(null)
for(var/datum/controller/subsystem/SS in Master.subsystems)
SS.stat_entry()
-
+
+ if(statpanel("Tickets"))
+ GLOB.ahelp_tickets.stat_entry()
+
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
listed_turf = null
diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm
index 16f6d9168b..262275fdea 100644
--- a/code/modules/mob/new_player/sprite_accessories_vr.dm
+++ b/code/modules/mob/new_player/sprite_accessories_vr.dm
@@ -988,7 +988,7 @@
color_blend_mode = ICON_MULTIPLY
body_parts = list(BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_L_HAND,BP_R_HAND,BP_TORSO)
- spirit_panels_head
+ spirit_lights_head
name = "Ward - Spirit FBP Lights (head)"
icon_state = "lights"
color_blend_mode = ICON_MULTIPLY
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 12671d584d..5f8a9dd0d7 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -148,6 +148,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
can_assist = FALSE
/obj/item/organ/internal/brain/slime
+ icon = 'icons/obj/surgery_vr.dmi' // Vorestation edit
name = "slime core"
desc = "A complex, organic knot of jelly and crystalline particles."
icon_state = "core"
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index d072797366..2e97d735ba 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -255,7 +255,7 @@
if(istype(W, /obj/item/stack/cable_coil) && !terminal && !building_terminal)
building_terminal = 1
var/obj/item/stack/cable_coil/CC = W
- if (CC.get_amount() <= 10)
+ if (CC.get_amount() < 10)
to_chat(user, "You need more cables.")
building_terminal = 0
return 0
diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm
index 247a517a20..4b69151b2b 100644
--- a/code/modules/reagents/Chemistry-Machinery.dm
+++ b/code/modules/reagents/Chemistry-Machinery.dm
@@ -31,8 +31,8 @@
/obj/machinery/chem_master/New()
..()
- var/datum/reagents/R = new/datum/reagents(120)
- reagents = R
+ var/datum/reagents/R = new/datum/reagents(900) //Just a huge random number so the buffer should (probably) never dump your reagents.
+ reagents = R //There should be a nano ui thingy to warn of this.
R.my_atom = src
/obj/machinery/chem_master/ex_act(severity)
@@ -55,7 +55,7 @@
src.beaker = B
user.drop_item()
B.loc = src
- to_chat(user, "You add \the [B] to the machine!")
+ to_chat(user, "You add \the [B] to the machine.")
icon_state = "mixer1"
else if(istype(B, /obj/item/weapon/storage/pill_bottle))
@@ -67,7 +67,7 @@
src.loaded_pill_bottle = B
user.drop_item()
B.loc = src
- to_chat(user, "You add \the [loaded_pill_bottle] into the dispenser slot!")
+ to_chat(user, "You add \the [loaded_pill_bottle] into the dispenser slot.")
else if(default_unfasten_wrench(user, B, 20))
return
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 64f5b923ab..59b690f31a 100755
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -284,7 +284,7 @@
if (src.amount > 3 && !O.opened)
var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
P.wrapped = O
- O.welded = 1
+ O.sealed = 1
O.loc = P
src.amount -= 3
user.visible_message("\The [user] wraps \a [target] with \a [src].",\
@@ -311,7 +311,7 @@
wrapped.forceMove(get_turf(src))
if(istype(wrapped, /obj/structure/closet))
var/obj/structure/closet/O = wrapped
- O.welded = 0
+ O.sealed = 0
wrapped = null
var/turf/T = get_turf(src)
for(var/atom/movable/AM in contents)
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
old mode 100644
new mode 100755
index 6872ffba6d..3e8732c423
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -121,7 +121,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
//Loading a disk into it.
if(istype(D, /obj/item/weapon/disk))
if(t_disk || d_disk)
- user << "A disk is already loaded into the machine."
+ to_chat(user, "A disk is already loaded into the machine.")
return
if(istype(D, /obj/item/weapon/disk/tech_disk))
@@ -129,11 +129,11 @@ won't update every console in existence) but it's more of a hassle to do. Also,
else if (istype(D, /obj/item/weapon/disk/design_disk))
d_disk = D
else
- user << "Machine cannot accept disks in that format."
+ to_chat(user, "Machine cannot accept disks in that format.")
return
user.drop_item()
D.loc = src
- user << "You add \the [D] to the machine."
+ to_chat(user, "You add \the [D] to the machine.")
else
//The construction/deconstruction of the console code.
..()
@@ -145,7 +145,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
- user << "You you disable the security protocols."
+ to_chat(user, "You you disable the security protocols.")
return 1
/obj/machinery/computer/rdconsole/Topic(href, href_list)
@@ -155,12 +155,16 @@ won't update every console in existence) but it's more of a hassle to do. Also,
add_fingerprint(usr)
usr.set_machine(src)
+ if((screen < 1 || (screen == 1.6 && href_list["menu"] != "1.0")) && (!allowed(usr) && !emagged)) //Stops people from HREF exploiting out of the lock screen, but allow it if they have the access.
+ to_chat(usr, "Unauthorized Access")
+ return
+
if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code.
var/temp_screen = text2num(href_list["menu"])
if(temp_screen <= 1.1 || (3 <= temp_screen && 4.9 >= temp_screen) || allowed(usr) || emagged) //Unless you are making something, you need access.
screen = temp_screen
else
- usr << "Unauthorized Access."
+ to_chat(usr, "Unauthorized Access.")
else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk.
screen = 0.0
@@ -211,7 +215,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer.
if(linked_destroy)
if(linked_destroy.busy)
- usr << "The destructive analyzer is busy at the moment."
+ to_chat(usr, "The destructive analyzer is busy at the moment.")
else if(linked_destroy.loaded_item)
linked_destroy.loaded_item.loc = linked_destroy.loc
@@ -222,7 +226,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder.
if(linked_destroy)
if(linked_destroy.busy)
- usr << "The destructive analyzer is busy at the moment."
+ to_chat(usr, "The destructive analyzer is busy at the moment.")
else
if(alert("Proceeding will destroy loaded item. Continue?", "Destructive analyzer confirmation", "Yes", "No") == "No" || !linked_destroy)
return
@@ -234,7 +238,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
if(linked_destroy)
linked_destroy.busy = 0
if(!linked_destroy.loaded_item)
- usr <<"The destructive analyzer appears to be empty."
+ to_chat(usr, "The destructive analyzer appears to be empty.")
screen = 1.0
return
@@ -270,12 +274,12 @@ won't update every console in existence) but it's more of a hassle to do. Also,
if(allowed(usr))
screen = text2num(href_list["lock"])
else
- usr << "Unauthorized Access."
+ to_chat(usr, "Unauthorized Access.")
else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected.
screen = 0.0
if(!sync)
- usr << "You must connect to the network first."
+ to_chat(usr, "You must connect to the network first.")
else
griefProtection() //Putting this here because I dont trust the sync process
spawn(30)
diff --git a/code/modules/vore/appearance/sprite_accessories_vr.dm b/code/modules/vore/appearance/sprite_accessories_vr.dm
index ed43a5ff29..e786751e5d 100644
--- a/code/modules/vore/appearance/sprite_accessories_vr.dm
+++ b/code/modules/vore/appearance/sprite_accessories_vr.dm
@@ -1229,3 +1229,259 @@
icon_state = "zenghu_taj"
//Taurs moved to a separate file due to extra code around them
+
+//Buggo Abdomens!
+
+/datum/sprite_accessory/tail/buggo
+ name = "Bug abdomen, colorable"
+ desc = ""
+ icon_state = "buggo_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggobee
+ name = "Bug abdomen, bee top, dual-colorable"
+ desc = ""
+ icon_state = "buggo_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobee_markings"
+
+/datum/sprite_accessory/tail/buggobeefull
+ name = "Bug abdomen, bee full, dual-colorable"
+ desc = ""
+ icon_state = "buggo_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobeefull_markings"
+
+/datum/sprite_accessory/tail/buggounder
+ name = "Bug abdomen, underside, dual-colorable"
+ desc = ""
+ icon_state = "buggo_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggounder_markings"
+
+/datum/sprite_accessory/tail/buggofirefly
+ name = "Bug abdomen, firefly, dual-colorable"
+ desc = ""
+ icon_state = "buggo_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofirefly_markings"
+
+/datum/sprite_accessory/tail/buggofat
+ name = "Fat bug abdomen, colorable"
+ desc = ""
+ icon_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggofatbee
+ name = "Fat bug abdomen, bee top, dual-colorable"
+ desc = ""
+ icon_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatbee_markings"
+
+/datum/sprite_accessory/tail/buggofatbeefull
+ name = "Fat bug abdomen, bee full, dual-colorable"
+ desc = ""
+ icon_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatbeefull_markings"
+
+/datum/sprite_accessory/tail/buggofatunder
+ name = "Fat bug abdomen, underside, dual-colorable"
+ desc = ""
+ icon_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatunder_markings"
+
+/datum/sprite_accessory/tail/buggofatfirefly
+ name = "Fat bug abdomen, firefly, dual-colorable"
+ desc = ""
+ icon_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatfirefly_markings"
+
+/datum/sprite_accessory/tail/buggowag
+ name = "Bug abdomen, colorable, vwag change"
+ desc = ""
+ icon_state = "buggo_s"
+ ani_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggobeewag
+ name = "Bug abdomen, bee top, dual color, vwag"
+ desc = ""
+ icon_state = "buggo_s"
+ ani_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobee_markings"
+ extra_overlay_w = "buggofatbee_markings"
+
+/datum/sprite_accessory/tail/buggobeefullwag
+ name = "Bug abdomen, bee full, dual color, vwag"
+ desc = ""
+ icon_state = "buggo_s"
+ ani_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobeefull_markings"
+ extra_overlay_w = "buggofatbeefull_markings"
+
+/datum/sprite_accessory/tail/buggounderwag
+ name = "Bug abdomen, underside, dual color, vwag"
+ desc = ""
+ icon_state = "buggo_s"
+ ani_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggounder_markings"
+ extra_overlay_w = "buggofatunder_markings"
+
+/datum/sprite_accessory/tail/buggofireflywag
+ name = "Bug abdomen, firefly, dual color, vwag"
+ desc = ""
+ icon_state = "buggo_s"
+ ani_state = "buggofat_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofirefly_markings"
+ extra_overlay_w = "buggofatfirefly_markings"
+
+//Vass buggo variants!
+
+/datum/sprite_accessory/tail/buggovass
+ name = "Bug abdomen, vass, colorable"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggovassbee
+ name = "Bug abdomen, bee top, dc, vass"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobee_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassbeefull
+ name = "Bug abdomen, bee full, dc, vass"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobeefull_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassunder
+ name = "Bug abdomen, underside, dc, vass"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggounder_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfirefly
+ name = "Bug abdomen, firefly, dc, vass"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofirefly_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfat
+ name = "Fat bug abdomen, vass, colorable"
+ desc = ""
+ icon_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggovassfatbee
+ name = "Fat bug abdomen, bee top, dc, vass"
+ desc = ""
+ icon_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatbee_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfatbeefull
+ name = "Fat bug abdomen, bee full, dc, vass"
+ desc = ""
+ icon_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatbeefull_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfatunder
+ name = "Fat bug abdomen, underside, dc, vass"
+ desc = ""
+ icon_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatunder_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfatfirefly
+ name = "Fat bug abdomen, firefly, dc, vass"
+ desc = ""
+ icon_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofatfirefly_vass_markings"
+
+/datum/sprite_accessory/tail/buggovasswag
+ name = "Bug abdomen, vass, colorable, vwag change"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ ani_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
+/datum/sprite_accessory/tail/buggovassbeewag
+ name = "Bug abdomen, bee top, dc, vass, vwag"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ ani_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobee_vass_markings"
+ extra_overlay_w = "buggofatbee_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassbeefullwag
+ name = "Bug abdomen, bee full, dc, vass, vwag"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ ani_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggobeefull_vass_markings"
+ extra_overlay_w = "buggofatbeefull_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassunderwag
+ name = "Bug abdomen, underside, dc, vass, vwag"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ ani_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggounder_vass_markings"
+ extra_overlay_w = "buggofatunder_vass_markings"
+
+/datum/sprite_accessory/tail/buggovassfireflywag
+ name = "Bug abdomen, firefly, dc, vass, vwag"
+ desc = ""
+ icon_state = "buggo_vass_s"
+ ani_state = "buggofat_vass_s"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+ extra_overlay = "buggofirefly_vass_markings"
+ extra_overlay_w = "buggofatfirefly_vass_markings"
diff --git a/code/modules/vore/fluffstuff/custom_clothes_vr.dm b/code/modules/vore/fluffstuff/custom_clothes_vr.dm
index 69f74c3e8a..8163c861f4 100644
--- a/code/modules/vore/fluffstuff/custom_clothes_vr.dm
+++ b/code/modules/vore/fluffstuff/custom_clothes_vr.dm
@@ -910,7 +910,7 @@
icon_state = "khi_uniform_cmd_i"
item_state = "khi_uniform_cmd"
worn_state = "khi_uniform_cmd"
- armor = list(melee = 5, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0)
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
/obj/item/clothing/under/rank/khi/sec //Security version
name = "KHI security suit"
@@ -926,7 +926,7 @@
icon_state = "khi_uniform_med_i"
item_state = "khi_uniform_med"
worn_state = "khi_uniform_med"
- armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 5)
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0)
/obj/item/clothing/under/rank/khi/eng //Engineering version
name = "KHI engineering suit"
@@ -942,7 +942,7 @@
icon_state = "khi_uniform_sci_i"
item_state = "khi_uniform_sci"
worn_state = "khi_uniform_sci"
- armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 5)
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0)
/obj/item/clothing/under/rank/khi/fluff/aronai //Aro fluff version
name = "KHI meditech suit"
@@ -950,7 +950,7 @@
icon_state = "khi_uniform_aro_i"
item_state = "khi_uniform_aro"
worn_state = "khi_uniform_aro"
- armor = list(melee = 5, bullet = 5, laser = 5, energy = 0, bomb = 0, bio = 0, rad = 0)
+ armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0)
//jacobdragon:Earthen Breath
/obj/item/clothing/under/fluff/earthenbreath
@@ -1192,14 +1192,14 @@ Departamental Swimsuits, for general use
desc = "The uniform worn by command officers in the mid 2260s."
icon_state = "trek_command"
item_state = "trek_command"
- armor = list(melee = 10, bullet = 10, laser = 10,energy = 0, bomb = 0, bio = 10, rad = 10, fire = 0, acid = 0) // Considering only staff heads get to pick it
+ armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) // Considering only staff heads get to pick it
/obj/item/clothing/under/rank/trek/engsec
name = "Operations Uniform"
desc = "The uniform worn by operations officers of the mid 2260s. You feel strangely vulnerable just seeing this..."
icon_state = "trek_engsec"
item_state = "trek_engsec"
- armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10, fire = 0, acid = 0) // since they're shared between jobs and kinda moot.
+ armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) // since they're shared between jobs and kinda moot.
/obj/item/clothing/under/rank/trek/medsci
name = "MedSci Uniform"
@@ -1207,7 +1207,7 @@ Departamental Swimsuits, for general use
icon_state = "trek_medsci"
item_state = "trek_medsci"
permeability_coefficient = 0.50
- armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0, fire = 0, acid = 0) // basically a copy of vanilla sci/med
+ armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) // basically a copy of vanilla sci/med
//TNG
/obj/item/clothing/under/rank/trek/command/next
diff --git a/code/world.dm b/code/world.dm
index debe99889d..03d7fe0127 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -63,6 +63,8 @@ var/global/datum/global_init/init = new ()
// if(config && config.log_runtime)
// log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log")
+ GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000
+
callHook("startup")
//Emergency Fix
load_mods()
@@ -171,7 +173,10 @@ var/world_topic_spam_protect_time = world.timeofday
s["players"] = players.len
s["playerlist"] = list2params(players)
- s["admins"] = admins.len
+ var/list/adm = get_admin_counts()
+ var/list/presentmins = adm["present"]
+ var/list/afkmins = adm["afk"]
+ s["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
s["adminlist"] = list2params(admins)
else
var/n = 0
diff --git a/html/changelogs/Anewbe - Graves.yml b/html/changelogs/Anewbe - Graves.yml
new file mode 100644
index 0000000000..fd7b71a680
--- /dev/null
+++ b/html/changelogs/Anewbe - Graves.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# 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
+#################################
+
+# Your name.
+author: Anewbe
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Graves are now a thing in the code, will need some testing and probably more work before they get more common."
\ No newline at end of file
diff --git a/icons/mob/vore/tails_vr.dmi b/icons/mob/vore/tails_vr.dmi
index f628277b97..bf2129b3dd 100644
Binary files a/icons/mob/vore/tails_vr.dmi and b/icons/mob/vore/tails_vr.dmi differ
diff --git a/icons/obj/closet.dmi b/icons/obj/closet.dmi
index 5fd95e85d7..80fb1712be 100644
Binary files a/icons/obj/closet.dmi and b/icons/obj/closet.dmi differ
diff --git a/icons/obj/surgery_vr.dmi b/icons/obj/surgery_vr.dmi
new file mode 100644
index 0000000000..9b0f9b8aed
Binary files /dev/null and b/icons/obj/surgery_vr.dmi differ
diff --git a/interface/interface.dm b/interface/interface.dm
index 9adcfc965a..6ecfde4e7c 100644
--- a/interface/interface.dm
+++ b/interface/interface.dm
@@ -6,7 +6,7 @@
if(config.wikiurl)
if(query)
if(config.wikisearchurl)
- var/output = replacetext(config.wikisearchurl, "%s", url_encode(query))
+ var/output = replacetext(config.wikisearchurl, "%s", url_encode(query))
src << link(output)
else
src << " The wiki search URL is not set in the server configuration."
diff --git a/maps/northern_star/polaris-5.dmm b/maps/northern_star/polaris-5.dmm
index f4f4ea7608..184ffbd6c1 100644
--- a/maps/northern_star/polaris-5.dmm
+++ b/maps/northern_star/polaris-5.dmm
@@ -1587,7 +1587,7 @@
"EA" = (/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 1},/obj/structure/cable/blue{d1 = 4; d2 = 8; icon_state = "4-8"},/turf/simulated/floor/tiled,/area/outpost/mining_main/eva)
"EB" = (/obj/machinery/power/apc{dir = 4; name = "east bump"; pixel_x = 24},/obj/structure/cable/blue{d2 = 8; icon_state = "0-8"},/obj/item/device/radio/intercom{name = "Station Intercom (General)"; pixel_y = -21},/turf/simulated/floor/tiled,/area/outpost/mining_main/eva)
"EC" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/obj/machinery/light,/turf/simulated/floor/tiled/dark,/area/outpost/mining_main/storage)
-"ED" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe/hammer,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/turf/simulated/floor/tiled/dark,/area/outpost/mining_main/storage)
+"ED" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/turf/simulated/floor/tiled/dark,/area/outpost/mining_main/storage)
"EE" = (/turf/simulated/wall,/area/outpost/mining_main/storage)
"EF" = (/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 4},/obj/machinery/camera/network/mining{c_tag = "OPM - Mining Hallway Aft"; dir = 4},/obj/item/device/radio/intercom{dir = 8; name = "Station Intercom (General)"; pixel_x = -21},/turf/simulated/floor/tiled,/area/outpost/mining_main/south_hall)
"EG" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 9},/turf/simulated/floor/tiled,/area/outpost/mining_main/south_hall)
@@ -1912,7 +1912,7 @@
"KN" = (/obj/structure/lattice,/obj/structure/grille{density = 0; icon_state = "brokengrille"},/turf/space,/area/space)
"KO" = (/obj/structure/lattice,/obj/structure/grille,/turf/space,/area/space)
"KP" = (/obj/machinery/power/tracker,/obj/structure/cable/yellow,/turf/simulated/floor/airless{icon_state = "asteroidplating2"},/area/outpost/engineering/solarsoutside/aft)
-
+
(1,1,1) = {"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/maps/southern_cross/southern_cross-3.dmm b/maps/southern_cross/southern_cross-3.dmm
index 16cd2ebfc5..eabbfeedf6 100644
--- a/maps/southern_cross/southern_cross-3.dmm
+++ b/maps/southern_cross/southern_cross-3.dmm
@@ -98,8 +98,8 @@
"bT" = (/obj/structure/ore_box,/obj/effect/floor_decal/corner/brown{dir = 10},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
"bU" = (/obj/structure/reagent_dispensers/watertank,/obj/effect/floor_decal/corner/brown{dir = 10},/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 1},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
"bV" = (/obj/structure/reagent_dispensers/fueltank,/obj/effect/floor_decal/corner/brown{dir = 10},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
-"bW" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe/hammer,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/obj/effect/floor_decal/corner/brown{dir = 10},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
-"bX" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe/hammer,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/obj/effect/floor_decal/corner/brown{dir = 10},/obj/machinery/firealarm{dir = 1; pixel_x = 0; pixel_y = -24},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
+"bW" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/obj/effect/floor_decal/corner/brown{dir = 10},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
+"bX" = (/obj/structure/table/rack,/obj/item/weapon/pickaxe,/obj/item/weapon/tool/wrench,/obj/item/weapon/tool/crowbar,/obj/item/weapon/shovel,/obj/effect/floor_decal/corner/brown{dir = 10},/obj/machinery/firealarm{dir = 1; pixel_x = 0; pixel_y = -24},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main/storage)
"bY" = (/obj/effect/floor_decal/corner/brown{dir = 8},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main)
"bZ" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/cable/blue{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/tiled,/area/surface/outpost/mining_main)
"ca" = (/obj/effect/floor_decal/industrial/warning/corner,/turf/simulated/floor/tiled,/area/surface/outpost/mining_main)
@@ -1818,4 +1818,3 @@ aaafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafDADADADADA
aaafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafagafafafafafafafafDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDVDMDWDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDJDJDKDKDJDJDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDQDVDMDWDQDQDQDQDQDQDQDQDQDQDQDQDQDQafafafafafafafafafafafafagafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafafaa
aaaaaaaaaaaaaaaaabababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababababababababababababababDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDQDYDYDYDQDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDQDZDZDZDZDZDZDQDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDQDYDYDYDQDXDXDXDXDXDXDXDXDXDXDXDXDXababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababab
"}
-
diff --git a/maps/southern_cross/southern_cross_jobs_vr.dm b/maps/southern_cross/southern_cross_jobs_vr.dm
index 070281b47a..600ace3885 100644
--- a/maps/southern_cross/southern_cross_jobs_vr.dm
+++ b/maps/southern_cross/southern_cross_jobs_vr.dm
@@ -19,6 +19,6 @@ var/const/PATHFINDER =(1<<13) //VOREStation Edit - Added Pathfinder
idtype = /obj/item/weapon/card/id/science/head/pathfinder
economic_modifier = 7
- access = list(access_eva, access_maint_tunnels, access_external_airlocks, access_pilot, access_explorer, access_research)
- minimal_access = list(access_eva, access_pilot, access_explorer, access_research)
+ access = list(access_eva, access_maint_tunnels, access_external_airlocks, access_pilot, access_explorer, access_research, access_gateway)
+ minimal_access = list(access_eva, access_pilot, access_explorer, access_research, access_gateway)
outfit_type = /decl/hierarchy/outfit/job/pathfinder
diff --git a/maps/tether/submaps/_tether_submaps.dm b/maps/tether/submaps/_tether_submaps.dm
index 18be57a76f..8e787a5896 100644
--- a/maps/tether/submaps/_tether_submaps.dm
+++ b/maps/tether/submaps/_tether_submaps.dm
@@ -36,6 +36,7 @@
/datum/map_z_level/tether_lateload/underdark
name = "Underdark"
flags = MAP_LEVEL_CONTACT|MAP_LEVEL_PLAYER
+ base_turf = /turf/simulated/mineral/floor/virgo3b
/datum/map_template/tether_lateload/tether_underdark/on_map_loaded(z)
. = ..()
diff --git a/maps/tether/submaps/admin_use/fun.dm b/maps/tether/submaps/admin_use/fun.dm
index d16b00899c..9fc3c5490a 100644
--- a/maps/tether/submaps/admin_use/fun.dm
+++ b/maps/tether/submaps/admin_use/fun.dm
@@ -14,12 +14,19 @@
name = "\improper Unknown Area AU" //Nobody will know what this means if they see it, anyway.
requires_power = 1
dynamic_lighting = 1
+ power_equip = FALSE
+ power_environ = FALSE
+ power_light = FALSE
/area/submap/admin_upload/pow
name = "\improper Unknown Area AU+P"
requires_power = 0
/area/submap/admin_upload/lit
name = "\improper Unknown Area AU+L"
+ requires_power = 1
dynamic_lighting = 0
+ power_equip = FALSE
+ power_environ = FALSE
+ power_light = FALSE
/area/submap/admin_upload/powlit
name = "\improper Unknown Area AU+PL"
requires_power = 0
diff --git a/maps/tether/submaps/beach/_beach.dm b/maps/tether/submaps/beach/_beach.dm
index e58e678cc5..552a40b23a 100644
--- a/maps/tether/submaps/beach/_beach.dm
+++ b/maps/tether/submaps/beach/_beach.dm
@@ -189,6 +189,7 @@
/area/tether_away/cave
flags = RAD_SHIELDED
ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg')
+ base_turf = /turf/simulated/mineral/floor/ignore_mapgen/cave
/area/tether_away/cave/explored/normal
name = "\improper Away Mission - Virgo 4 Cave (E)"
diff --git a/maps/tether/tether-01-surface1.dmm b/maps/tether/tether-01-surface1.dmm
index 496932f07b..ed391bc815 100644
--- a/maps/tether/tether-01-surface1.dmm
+++ b/maps/tether/tether-01-surface1.dmm
@@ -1802,7 +1802,7 @@
/area/tether/surfacebase/mining_main/storage)
"adU" = (
/obj/structure/table/rack,
-/obj/item/weapon/pickaxe/hammer,
+/obj/item/weapon/pickaxe,
/obj/item/weapon/tool/wrench,
/obj/item/weapon/tool/crowbar,
/obj/item/weapon/shovel,
diff --git a/maps/tether/tether-07-station3.dmm b/maps/tether/tether-07-station3.dmm
index d55b2ed004..7f1d76cda5 100644
--- a/maps/tether/tether-07-station3.dmm
+++ b/maps/tether/tether-07-station3.dmm
@@ -8455,13 +8455,11 @@
opacity = 0
},
/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/turf/simulated/floor,
-/area/security/security_processing)
-"oh" = (
-/obj/machinery/door/firedoor/glass,
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
+/obj/structure/window/reinforced/polarized{
+ dir = 10;
+ icon_state = "fwindow";
+ id = "sec_processing"
+ },
/turf/simulated/floor,
/area/security/security_processing)
"oi" = (
@@ -13671,9 +13669,6 @@
/turf/simulated/floor/plating,
/area/quartermaster/office)
"wb" = (
-/obj/machinery/computer/supplycomp{
- dir = 1
- },
/obj/structure/cable/green{
d1 = 1;
d2 = 2;
@@ -13685,6 +13680,9 @@
/obj/effect/floor_decal/corner/brown/border{
dir = 8
},
+/obj/machinery/computer/supplycomp/control{
+ dir = 1
+ },
/turf/simulated/floor/tiled,
/area/quartermaster/office)
"wc" = (
@@ -35028,7 +35026,7 @@ cS
jk
jk
jk
-oh
+qQ
oE
po
pO
@@ -35170,7 +35168,7 @@ cT
jy
jk
jk
-oh
+qQ
oF
pp
pP
@@ -35313,9 +35311,9 @@ mH
lD
lD
of
-oh
-oh
-oh
+qQ
+qQ
+qQ
qk
qQ
qQ
diff --git a/maps/tether/tether_areas2.dm b/maps/tether/tether_areas2.dm
index 659518148d..f102bc216b 100644
--- a/maps/tether/tether_areas2.dm
+++ b/maps/tether/tether_areas2.dm
@@ -122,8 +122,10 @@
// Mining Underdark
/area/mine/unexplored/underdark
name = "\improper Mining Underdark"
+ base_turf = /turf/simulated/mineral/floor/virgo3b
/area/mine/explored/underdark
name = "\improper Mining Underdark"
+ base_turf = /turf/simulated/mineral/floor/virgo3b
// Mining outpost areas
/area/outpost/mining_main/passage
diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl
index 1b8315154a..8799bb05e0 100644
--- a/nano/templates/chem_master.tmpl
+++ b/nano/templates/chem_master.tmpl
@@ -72,6 +72,11 @@
{{:helper.link('', 'pill pill' + data.pillSprite, {'tab_select' : 'pill'}, null, 'link32')}}
{{:helper.link('', 'pill bottle' + data.bottleSprite, {'tab_select' : 'bottle'}, null, 'link32')}}
+ {{else}}
+
+
+ {{:helper.link('Create bottle (50 units max)', null, {'createbottle' : 1})}}
+
{{/if}}
{{/if}}
diff --git a/tools/mapmerge2/hooks/README.md b/tools/hooks/README.md
similarity index 83%
rename from tools/mapmerge2/hooks/README.md
rename to tools/hooks/README.md
index 844f3a3952..b15fb493d8 100644
--- a/tools/mapmerge2/hooks/README.md
+++ b/tools/hooks/README.md
@@ -14,6 +14,10 @@ automatically by GUI tools like TortoiseGit and GitHub for Windows, but
## Current Hooks
* **Pre-commit**: Runs [mapmerge2] on changed maps, if any.
+* **DMI merger**: Attempts to [fix icon conflicts] when performing a git merge.
+ If it succeeds, the file is marked merged. If it fails, it logs what states
+ are still in conflict and adds them to the .dmi file, where the desired
+ resolution can be chosen.
## Adding New Hooks
@@ -34,3 +38,4 @@ version of Python is found.
[merge drivers]: https://git-scm.com/docs/gitattributes#_performing_a_three_way_merge
[Git for Windows]: https://gitforwindows.org/
[mapmerge2]: ../mapmerge2/README.md
+[fix icon conflicts]: ../mapmerge2/merge_driver_dmi.py
diff --git a/tools/hooks/dmi.merge b/tools/hooks/dmi.merge
new file mode 100644
index 0000000000..4e2717867e
--- /dev/null
+++ b/tools/hooks/dmi.merge
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec tools/hooks/python.sh -m merge_driver_dmi "$@"
diff --git a/tools/hooks/install.bat b/tools/hooks/install.bat
new file mode 100644
index 0000000000..7a11129a2a
--- /dev/null
+++ b/tools/hooks/install.bat
@@ -0,0 +1,16 @@
+@echo off
+cd %~dp0
+for %%f in (*.hook) do (
+ echo Installing hook: %%~nf
+ copy %%f ..\..\.git\hooks\%%~nf >nul
+)
+for %%f in (*.merge) do (
+ echo Installing merge driver: %%~nf
+ echo [merge "%%~nf"]^
+
+ driver = tools/hooks/%%f %%P %%O %%A %%B %%L >> ..\..\.git\config
+)
+echo Installing Python dependencies
+python -m pip install -r ..\mapmerge2\requirements.txt
+echo Done
+pause
diff --git a/tools/hooks/install.sh b/tools/hooks/install.sh
new file mode 100644
index 0000000000..ccc4cf5227
--- /dev/null
+++ b/tools/hooks/install.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -e
+shopt -s nullglob
+cd "$(dirname "$0")"
+for f in *.hook; do
+ echo Installing hook: ${f%.hook}
+ cp $f ../../.git/hooks/${f%.hook}
+done
+for f in *.merge; do
+ echo Installing merge driver: ${f%.merge}
+ git config --replace-all merge.${f%.merge}.driver "tools/hooks/$f %P %O %A %B %L"
+done
+echo Installing Python dependencies
+./python.sh -m pip install -r ../mapmerge2/requirements.txt
+echo "Done"
diff --git a/tools/hooks/pre-commit.hook b/tools/hooks/pre-commit.hook
new file mode 100644
index 0000000000..7eccda6f58
--- /dev/null
+++ b/tools/hooks/pre-commit.hook
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec tools/hooks/python.sh -m precommit
diff --git a/tools/mapmerge2/hooks/python.sh b/tools/hooks/python.sh
similarity index 100%
rename from tools/mapmerge2/hooks/python.sh
rename to tools/hooks/python.sh
diff --git a/tools/mapmerge/1prepare_map.bat b/tools/mapmerge/1prepare_map.bat
deleted file mode 100644
index 43f317e114..0000000000
--- a/tools/mapmerge/1prepare_map.bat
+++ /dev/null
@@ -1,7 +0,0 @@
-cd ../../maps/tether
-
-FOR %%f IN (*.dmm) DO (
- copy %%f %%f.backup
-)
-
-pause
diff --git a/tools/mapmerge/2clean_map.bat b/tools/mapmerge/2clean_map.bat
deleted file mode 100644
index f97ad14793..0000000000
--- a/tools/mapmerge/2clean_map.bat
+++ /dev/null
@@ -1,8 +0,0 @@
-SET z_levels=10
-cd
-
-FOR %%f IN (../../maps/tether/*.dmm) DO (
- java -jar MapPatcher.jar -clean ../../maps/tether/%%f.backup ../../maps/tether/%%f ../../maps/tether/%%f
-)
-
-pause
diff --git a/tools/mapmerge/MapPatcher.jar b/tools/mapmerge/MapPatcher.jar
deleted file mode 100644
index f4351b3603..0000000000
Binary files a/tools/mapmerge/MapPatcher.jar and /dev/null differ
diff --git a/tools/mapmerge/clean_map_git.sh b/tools/mapmerge/clean_map_git.sh
deleted file mode 100755
index 25cef6da93..0000000000
--- a/tools/mapmerge/clean_map_git.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-for MAPFILE in ../../maps/*.dmm
-do
- MAPNAME=$(basename $MAPFILE)
- git show HEAD:maps/$MAPNAME > tmp.dmm
- java -jar MapPatcher.jar -clean tmp.dmm $MAPFILE $MAPFILE
- rm tmp.dmm
-done
diff --git a/tools/mapmerge/git_merge_installer.bat b/tools/mapmerge/git_merge_installer.bat
deleted file mode 100644
index 59e3a803e9..0000000000
--- a/tools/mapmerge/git_merge_installer.bat
+++ /dev/null
@@ -1,6 +0,0 @@
-@echo off
-set tab=
-echo. >> ../../.git/config
-echo [merge "merge-dmm"] >> ../../.git/config
-echo %tab%name = mapmerge driver >> ../../.git/config
-echo %tab%driver = ./tools/mapmerge/mapmerge.sh %%O %%A %%B >> ../../.git/config
diff --git a/tools/mapmerge/git_merge_installer.sh b/tools/mapmerge/git_merge_installer.sh
deleted file mode 100755
index c6d70bc0c9..0000000000
--- a/tools/mapmerge/git_merge_installer.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-F="../../.git/config"
-
-echo '' >> $F
-echo '[merge "merge-dmm"]' >> $F
-echo ' name = mapmerge driver' >> $F
-echo ' driver = ./tools/mapmerge/mapmerge.sh %O %A %B' >> $F
diff --git a/tools/mapmerge/install.txt b/tools/mapmerge/install.txt
deleted file mode 100644
index 943b195b5e..0000000000
--- a/tools/mapmerge/install.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-1. Install java(http://www.java.com/en/download/index.jsp)
-2. Make sure java is in your PATH. To test this, open git bash, and type "java". If it says unknown command, you need to add JAVA/bin to your PATH variable (A guide for this can be found at https://www.java.com/en/download/help/path.xml ).
-
-Committing
-1. Before starting to edit the map, double-click "prepare_map.bat" in the tools/mapmerge/ directory.
-2. After finishing your edit, and before your commit, double-click "clean_map.bat" in the tools/mapmerge/ directory.
-
-This will make sure in the new version of your map, no paths are needlessly changed, thus instead of 8000 lines changed you'll get 50 lines changed. This not only reduces size of your commit, it also makes it possible to get an overview of your map changes on the "files changed" page in your pull request.
-
-Merging
-The easiest way to do merging is to install the merge driver. For this, open `Baystation12/.git/config` in a text editor, and paste the following lines to the end of it:
-
-[merge "merge-dmm"]
- name = mapmerge driver
- driver = ./tools/mapmerge/mapmerge.sh %O %A %B
-
-You may optionally instead run git_merge_installer.bat or git_merge_installer.sh which should automatically insert these lines for you at the appropriate location.
-
-After this, merging maps should happen automagically unless there are conflicts(a tile that both you and someone else changed). If there are conflicts, you will unfortunately still be stuck with opening both versions in a map editor, and manually resolving the issues.
diff --git a/tools/mapmerge/mapmerge.sh b/tools/mapmerge/mapmerge.sh
deleted file mode 100755
index 9e347ceaa2..0000000000
--- a/tools/mapmerge/mapmerge.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-java -jar tools/mapmerge/MapPatcher.jar -merge $1 $2 $3 $2
-if [ "$?" -gt 0 ]
-then
- echo "Unable to automatically resolve map conflicts, please merge manually."
- exit 1
-fi
-java -jar tools/mapmerge/MapPatcher.jar -clean $3 $2 $2
-
-exit 0
diff --git a/tools/mapmerge2/README.txt b/tools/mapmerge2/README.txt
deleted file mode 100644
index 0ff4d21ac2..0000000000
--- a/tools/mapmerge2/README.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-# Map Merge 2
-
-**Map Merge 2** is an improvement over previous map merging scripts, with
-better merge-conflict prevention, multi-Z support, and automatic handling of
-key overflow. For up-to-date tips and tricks, also visit the [Map Merger] wiki article.
-
-## What Map Merging Is
-
-The "map merge" operation describes the process of rewriting a map file written
-by the DreamMaker map editor to A) use a format more amenable to Git's conflict
-resolution and B) differ in the least amount textually from the previous
-version of the map while maintaining all the actual changes. It requires an old
-version of the map to use as a reference and a new version of the map which
-contains the desired changes.
-
-## Installation
-
-To install Python dependencies, run `requirements-install.bat`, or run
-`python -m pip install -r requirements.txt` directly. See the [Git hooks]
-documentation to install the Git pre-commit hook which runs the map merger
-automatically, or use `tools/mapmerge/Prepare Maps.bat` to save backups before
-running `mapmerge.bat`.
-
-For up-to-date installation and detailed troubleshooting instructions, visit
-the [Map Merger] wiki article.
-
-## Code Structure
-
-Frontend scripts are meant to be run directly. They obey the environment
-variables `TGM` to set whether files are saved in TGM (1) or DMM (0) format,
-and `MAPROOT` to determine where maps are kept. By default, TGM is used and
-the map root is autodetected. Each script may either prompt for the desired map
-or be run with command-line parameters indicating which maps to act on. The
-scripts include:
-
-* `convert.py` for converting maps to and from the TGM format. Used by
- `tgm2dmm.bat` and `dmm2tgm.bat`.
-* `mapmerge.py` for running the map merge on map backups saved by
- `Prepare Maps.bat`. Used by `mapmerge.bat`
-
-Implementation modules:
-
-* `dmm.py` includes the map reader and writer.
-* `mapmerge.py` includes the implementation of the map merge operation.
-* `frontend.py` includes the common code for the frontend scripts.
-
-`precommit.py` is run by the [Git hooks] if installed, and merges the new
-version of any map saved in the index (`git add`ed) with the old version stored
-in Git when run.
-
-[Map Merger]: https://tgstation13.org/wiki/Map_Merger
-[Git hooks]: ../hooks/README.md
diff --git a/tools/mapmerge2/dmi.py b/tools/mapmerge2/dmi.py
new file mode 100644
index 0000000000..f0a8182c9f
--- /dev/null
+++ b/tools/mapmerge2/dmi.py
@@ -0,0 +1,253 @@
+# Tools for working with modern DreamMaker icon files (PNGs + metadata)
+
+import math
+from PIL import Image
+from PIL.PngImagePlugin import PngInfo
+
+DEFAULT_SIZE = 32, 32
+LOOP_UNLIMITED = 0
+LOOP_ONCE = 1
+
+NORTH = 1
+SOUTH = 2
+EAST = 4
+WEST = 8
+SOUTHEAST = SOUTH|EAST
+SOUTHWEST = SOUTH|WEST
+NORTHEAST = NORTH|EAST
+NORTHWEST = NORTH|WEST
+
+CARDINALS = [NORTH, SOUTH, EAST, WEST]
+DIR_ORDER = [SOUTH, NORTH, EAST, WEST, SOUTHEAST, SOUTHWEST, NORTHEAST, NORTHWEST]
+DIR_NAMES = {
+ 'SOUTH': SOUTH,
+ 'NORTH': NORTH,
+ 'EAST': EAST,
+ 'WEST': WEST,
+ 'SOUTHEAST': SOUTHEAST,
+ 'SOUTHWEST': SOUTHWEST,
+ 'NORTHEAST': NORTHEAST,
+ 'NORTHWEST': NORTHWEST,
+ **{str(x): x for x in DIR_ORDER},
+ **{x: x for x in DIR_ORDER},
+ '0': SOUTH,
+ None: SOUTH,
+}
+
+class Dmi:
+ version = "4.0"
+
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+ self.states = []
+
+ @classmethod
+ def from_file(cls, fname):
+ image = Image.open(fname)
+
+ # no metadata = regular image file
+ if 'Description' not in image.info:
+ dmi = Dmi(*image.size)
+ state = dmi.state("")
+ state.frame(image)
+ return dmi
+
+ # read metadata
+ metadata = image.info['Description']
+ line_iter = iter(metadata.splitlines())
+ assert next(line_iter) == "# BEGIN DMI"
+ assert next(line_iter) == f"version = {cls.version}"
+
+ dmi = Dmi(*DEFAULT_SIZE)
+ state = None
+
+ for line in line_iter:
+ if line == "# END DMI":
+ break
+ key, value = line.lstrip().split(" = ")
+ if key == 'width':
+ dmi.width = int(value)
+ elif key == 'height':
+ dmi.height = int(value)
+ elif key == 'state':
+ state = dmi.state(unescape(value))
+ elif key == 'dirs':
+ state.dirs = int(value)
+ elif key == 'frames':
+ state._nframes = int(value)
+ elif key == 'delay':
+ state.delays = [parse_num(x) for x in value.split(',')]
+ elif key == 'loop':
+ state.loop = int(value)
+ elif key == 'rewind':
+ state.rewind = parse_bool(value)
+ elif key == 'hotspot':
+ x, y, frm = [int(x) for x in value.split(',')]
+ state.hotspot(frm - 1, x, y)
+ elif key == 'movement':
+ state.movement = parse_bool(value)
+ else:
+ raise NotImplementedError(key)
+
+ # cut image into frames
+ width, height = image.size
+ gridwidth = width // dmi.width
+ i = 0
+ for state in dmi.states:
+ for frame in range(state._nframes):
+ for dir in range(state.dirs):
+ px = dmi.width * (i % gridwidth)
+ py = dmi.height * (i // gridwidth)
+ im = image.crop((px, py, px + dmi.width, py + dmi.height))
+ assert im.size == (dmi.width, dmi.height)
+ state.frames.append(im)
+ i += 1
+ state._nframes = None
+
+ return dmi
+
+ def state(self, *args, **kwargs):
+ s = State(self, *args, **kwargs)
+ self.states.append(s)
+ return s
+
+ @property
+ def default_state(self):
+ return self.states[0]
+
+ def get_state(self, name):
+ for state in self.states:
+ if state.name == name:
+ return state
+ raise KeyError(name)
+ return self.default_state
+
+ def _assemble_comment(self):
+ comment = "# BEGIN DMI\n"
+ comment += f"version = {self.version}\n"
+ comment += f"\twidth = {self.width}\n"
+ comment += f"\theight = {self.height}\n"
+ for state in self.states:
+ comment += f"state = {escape(state.name)}\n"
+ comment += f"\tdirs = {state.dirs}\n"
+ comment += f"\tframes = {state.framecount}\n"
+ if state.framecount > 1 and len(state.delays): #any(x != 1 for x in state.delays):
+ comment += "\tdelay = " + ",".join(map(str, state.delays)) + "\n"
+ if state.loop != 0:
+ comment += f"\tloop = {state.loop}\n"
+ if state.rewind:
+ comment += "\trewind = 1\n"
+ if state.movement:
+ comment += "\tmovement = 1\n"
+ if state.hotspots and any(state.hotspots):
+ current = None
+ for i, value in enumerate(state.hotspots):
+ if value != current:
+ x, y = value
+ comment += f"\thotspot = {x},{y},{i + 1}\n"
+ current = value
+ comment += "# END DMI"
+ return comment
+
+ def to_file(self, filename, *, palette=False):
+ # assemble comment
+ comment = self._assemble_comment()
+
+ # assemble spritesheet
+ W, H = self.width, self.height
+ num_frames = sum(len(state.frames) for state in self.states)
+ sqrt = math.ceil(math.sqrt(num_frames))
+ output = Image.new('RGBA', (sqrt * W, math.ceil(num_frames / sqrt) * H))
+
+ i = 0
+ for state in self.states:
+ for frame in state.frames:
+ output.paste(frame, ((i % sqrt) * W, (i // sqrt) * H))
+ i += 1
+
+ # save
+ pnginfo = PngInfo()
+ pnginfo.add_text('Description', comment, zip=True)
+ if palette:
+ output = output.convert('P')
+ output.save(filename, 'png', optimize=True, pnginfo=pnginfo)
+
+class State:
+ def __init__(self, dmi, name, *, loop=LOOP_UNLIMITED, rewind=False, movement=False, dirs=1):
+ self.dmi = dmi
+ self.name = name
+ self.loop = loop
+ self.rewind = rewind
+ self.movement = movement
+ self.dirs = dirs
+
+ self._nframes = None # used during loading only
+ self.frames = []
+ self.delays = []
+ self.hotspots = None
+
+ @property
+ def framecount(self):
+ if self._nframes is not None:
+ return self._nframes
+ else:
+ return len(self.frames) // self.dirs
+
+ def frame(self, image, *, delay=1):
+ assert image.size == (self.dmi.width, self.dmi.height)
+ self.delays.append(delay)
+ self.frames.append(image)
+
+ def hotspot(self, first_frame, x, y):
+ if self.hotspots is None:
+ self.hotspots = [None] * self.framecount
+ for i in range(first_frame, self.framecount):
+ self.hotspots[i] = x, y
+
+ def _frame_index(self, frame=0, dir=None):
+ ofs = DIR_ORDER.index(DIR_NAMES[dir])
+ if ofs >= self.dirs:
+ ofs = 0
+ return frame * self.dirs + ofs
+
+ def get_frame(self, *args, **kwargs):
+ return self.frames[self._frame_index(*args, **kwargs)]
+
+def escape(text):
+ assert '\\' not in text and '"' not in text
+ return f'"{text}"'
+
+def unescape(text, quote='"'):
+ if text == 'null':
+ return None
+ if not (text.startswith(quote) and text.endswith(quote)):
+ raise ValueError(text)
+ text = text[1:-1]
+ assert '\\' not in text and quote not in text
+ return text
+
+def parse_num(value):
+ if '.' in value:
+ return float(value)
+ return int(value)
+
+def parse_bool(value):
+ if value not in ('0', '1'):
+ raise ValueError(value)
+ return value == '1'
+
+if __name__ == '__main__':
+ # test: can we load every DMI in the tree
+ import os
+
+ count = 0
+ for dirpath, dirnames, filenames in os.walk('.'):
+ if '.git' in dirnames:
+ dirnames.remove('.git')
+ for filename in filenames:
+ if filename.endswith('.dmi'):
+ Dmi.from_file(os.path.join(dirpath, filename))
+ count += 1
+
+ print(f"Successfully parsed {count} dmi files")
diff --git a/tools/mapmerge2/dmm.py b/tools/mapmerge2/dmm.py
index d76f07e32c..8bb1dca353 100644
--- a/tools/mapmerge2/dmm.py
+++ b/tools/mapmerge2/dmm.py
@@ -58,6 +58,23 @@ class DMM:
raise RuntimeError("ran out of keys, this shouldn't happen")
+ def overwrite_key(self, key, fixed, bad_keys):
+ try:
+ self.dictionary[key] = fixed
+ return None
+ except bidict.DuplicationError:
+ old_key = self.dictionary.inv[fixed]
+ bad_keys[key] = old_key
+ print(f"Merging '{num_to_key(key, self.key_length)}' into '{num_to_key(old_key, self.key_length)}'")
+ return old_key
+
+ def reassign_bad_keys(self, bad_keys):
+ if not bad_keys:
+ return
+ for k, v in self.grid.items():
+ # reassign the grid entries which used the old key
+ self.grid[k] = bad_keys.get(v, v)
+
def _presave_checks(self):
# last-second handling of bogus keys to help prevent and fix broken maps
self._ensure_free_keys(0)
@@ -70,9 +87,16 @@ class DMM:
new_key = bad_keys[k] = self.generate_new_key()
self.dictionary.forceput(new_key, self.dictionary[k])
print(f" {num_to_key(k, self.key_length, True)} -> {num_to_key(new_key, self.key_length)}")
- for k, v in self.grid.items():
- # reassign the grid entries which used the old key
- self.grid[k] = bad_keys.get(v, v)
+
+ # handle entries in the dictionary which have atoms in the wrong order
+ keys = list(self.dictionary.keys())
+ for key in keys:
+ value = self.dictionary[key]
+ if is_bad_atom_ordering(num_to_key(key, self.key_length, True), value):
+ fixed = tuple(fix_atom_ordering(value))
+ self.overwrite_key(key, fixed, bad_keys)
+
+ self.reassign_bad_keys(bad_keys)
def _ensure_free_keys(self, desired):
# ensure that free keys exist by increasing the key length if necessary
@@ -179,6 +203,45 @@ def parse_map_atom(atom):
return path, vars
+def is_bad_atom_ordering(key, atoms):
+ seen_turfs = 0
+ seen_areas = 0
+ can_fix = False
+ for each in atoms:
+ if each.startswith('/turf'):
+ if seen_turfs == 1:
+ print(f"Warning: key '{key}' has multiple turfs!")
+ if seen_areas:
+ print(f"Warning: key '{key}' has area before turf (autofixing...)")
+ can_fix = True
+ seen_turfs += 1
+ elif each.startswith('/area'):
+ if seen_areas == 1:
+ print(f"Warning: key '{key}' has multiple areas!!!")
+ seen_areas += 1
+ else:
+ if (seen_turfs or seen_areas) and not can_fix:
+ print(f"Warning: key '{key}' has movable after turf or area (autofixing...)")
+ can_fix = True
+ if not seen_areas or not seen_turfs:
+ print(f"Warning: key '{key}' is missing either a turf or area")
+ return can_fix
+
+def fix_atom_ordering(atoms):
+ movables = []
+ turfs = []
+ areas = []
+ for each in atoms:
+ if each.startswith('/turf'):
+ turfs.append(each)
+ elif each.startswith('/area'):
+ areas.append(each)
+ else:
+ movables.append(each)
+ movables.extend(turfs)
+ movables.extend(areas)
+ return movables
+
# ----------
# TGM writer
@@ -307,7 +370,7 @@ def _parse(map_raw_text):
continue
elif in_comment_line:
continue
- elif char == "\t":
+ elif char in "\r\t":
continue
if char == "/" and not in_quote_block:
@@ -415,6 +478,9 @@ def _parse(map_raw_text):
# grid block
for char in it:
+ if char == "\r":
+ continue
+
if in_coord_block:
if char == ",":
if reading_coord == "x":
diff --git a/tools/mapmerge2/dmm2tgm.bat b/tools/mapmerge2/dmm2tgm.bat
index bcf6150c2e..b17b03afa0 100644
--- a/tools/mapmerge2/dmm2tgm.bat
+++ b/tools/mapmerge2/dmm2tgm.bat
@@ -1,5 +1,5 @@
@echo off
-set MAPROOT=../../_maps/
+set MAPROOT=../../maps/
set TGM=1
python convert.py
pause
diff --git a/tools/mapmerge2/hooks/install.bat b/tools/mapmerge2/hooks/install.bat
deleted file mode 100644
index 50fa647a66..0000000000
--- a/tools/mapmerge2/hooks/install.bat
+++ /dev/null
@@ -1,15 +0,0 @@
-@echo off
-cd %~dp0
-for %%f in (*.hook) do (
- echo Installing hook: %%~nf
- mkdir ..\..\..\.git\hooks
- copy %%f ..\..\..\.git\hooks\%%~nf >nul
-)
-for %%f in (*.merge) do (
- echo Installing merge driver: %%~nf
- echo [merge "%%~nf"]^
-
- driver = tools/hooks/%%f %%P %%O %%A %%B %%L >> ..\..\..\.git\config
-)
-echo Done
-pause
diff --git a/tools/mapmerge2/hooks/install.sh b/tools/mapmerge2/hooks/install.sh
deleted file mode 100644
index be42d2ee96..0000000000
--- a/tools/mapmerge2/hooks/install.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-cd "$(dirname "$0")"
-for f in *.hook; do
- echo Installing hook: ${f%.hook}
- cp $f ../../../.git/hooks/${f%.hook}
-done
-for f in *.merge; do
- echo Installing merge driver: ${f%.merge}
- git config --replace-all merge.${f%.merge}.driver "tools/mapmerge2/hooks/$f %P %O %A %B %L"
-done
-echo "Done"
diff --git a/tools/mapmerge2/hooks/pre-commit.hook b/tools/mapmerge2/hooks/pre-commit.hook
deleted file mode 100644
index b78780ccbb..0000000000
--- a/tools/mapmerge2/hooks/pre-commit.hook
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-exec tools/mapmerge2/hooks/python.sh -m precommit
diff --git a/tools/mapmerge2/map_scripts/turfs2decals.txt b/tools/mapmerge2/map_scripts/turfs2decals.txt
new file mode 100644
index 0000000000..13ae02baf7
--- /dev/null
+++ b/tools/mapmerge2/map_scripts/turfs2decals.txt
@@ -0,0 +1,465 @@
+/turf/open/floor/circuit/killroom : /turf/open/floor/circuit/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/airless/asteroid : /obj/effect/turf_decal/sand , /turf/open/floor/plasteel/airless {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/airless/bar : /obj/effect/turf_decal/tile/bar , /obj/effect/turf_decal/tile/bar {dir=1} , /turf/open/floor/plasteel/airless {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=1} : /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/arrival {dir=@UNSET} : /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=2} : /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=4} : /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=5} : /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/cafeteria {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=6} : /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival {dir=8} : /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/arrival {dir=9} : /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/arrival {dir=10} : /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/cafeteria {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival/corner {dir=1} : /turf/open/floor/plasteel/white/corner {@OLD}
+/turf/open/floor/plasteel/arrival/corner {dir=@UNSET} : /turf/open/floor/plasteel/white/corner {@OLD}
+/turf/open/floor/plasteel/arrival/corner {dir=2} : /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival/corner {dir=4} : /obj/effect/turf_decal/tile/blue {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/arrival/corner {dir=8} : /obj/effect/turf_decal/tile/blue {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/asteroid : /obj/effect/turf_decal/sand , /turf/open/floor/plasteel
+/turf/open/floor/plasteel/bar : /obj/effect/turf_decal/tile/bar , /obj/effect/turf_decal/tile/bar {dir=1} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/barber : /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/cafeteria {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/side {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blue/corner : /obj/effect/turf_decal/tile/blue {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/blueyellow/side {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=1} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=@UNSET} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=2} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=4} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=5} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=6} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=8} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=9} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown {dir=10} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/corner : /obj/effect/turf_decal/tile/brown {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=1} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=2} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=4} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=5} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=6} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=8} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=9} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms {dir=10} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=1} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=@UNSET} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=2} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=4} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=5} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=6} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=8} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=9} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brown/telecomms/mainframe {dir=10} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=1} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=@UNSET} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=2} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=4} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=5} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=6} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=8} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=9} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold {dir=10} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/brownold/corner : /obj/effect/turf_decal/tile/brown {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/caution {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=2} : /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=4} : /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/checker {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=6} : /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/caution {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/caution {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/checker {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution/corner {dir=1} : /turf/open/floor/plasteel/dark/corner {@OLD}
+/turf/open/floor/plasteel/caution/corner {dir=@UNSET} : /turf/open/floor/plasteel/dark/corner {@OLD}
+/turf/open/floor/plasteel/caution/corner {dir=2} : /turf/open/floor/plasteel/dark/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution/corner {dir=4} : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/caution/corner {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/cmo : /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/dark/telecomms/mainframe : /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/dark/telecomms/server : /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/dark/telecomms/server/walkway : /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/corner : /obj/effect/turf_decal/tile/blue {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkblue/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=1} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=@UNSET} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=2} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=4} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=5} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=6} : /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=8} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=9} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown {dir=4} , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/side {dir=10} : /obj/effect/turf_decal/tile/brown {dir=1} , /obj/effect/turf_decal/tile/brown , /obj/effect/turf_decal/tile/brown {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkbrown/corner : /obj/effect/turf_decal/tile/brown {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=1} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=@UNSET} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=2} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=4} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=5} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=6} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=8} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=9} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side {dir=10} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/corner : /obj/effect/turf_decal/tile/green {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkgreen/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=1} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=@UNSET} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=2} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=4} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=5} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=6} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=8} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=9} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side {dir=10} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/corner : /obj/effect/turf_decal/tile/purple {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkpurple/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/corner : /obj/effect/turf_decal/tile/red {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkred/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/corner : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/darkyellow/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=1} : /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/escape {dir=@UNSET} : /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=2} : /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=4} : /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=5} : /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/cafeteria {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=6} : /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape {dir=8} : /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/escape {dir=9} : /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white/corner {@OLD;dir=1}
+/turf/open/floor/plasteel/escape {dir=10} : /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/cafeteria {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape/corner {dir=1} : /turf/open/floor/plasteel/white/corner {@OLD}
+/turf/open/floor/plasteel/escape/corner {dir=@UNSET} : /turf/open/floor/plasteel/white/corner {@OLD}
+/turf/open/floor/plasteel/escape/corner {dir=2} : /turf/open/floor/plasteel/white/corner {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape/corner {dir=4} : /obj/effect/turf_decal/tile/red {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/escape/corner {dir=8} : /obj/effect/turf_decal/tile/red {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=1} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=@UNSET} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=2} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=4} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=5} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=6} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=8} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=9} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side {dir=10} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/corner : /obj/effect/turf_decal/tile/green {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/green/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenblue/side {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/greenyellow/side {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/hydrofloor : /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=1} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=@UNSET} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=2} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=4} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=5} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=6} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=8} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=9} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side {dir=10} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/corner : /obj/effect/turf_decal/tile/neutral {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/neutral/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/side {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/orange/corner : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=1} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=@UNSET} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=2} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=4} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=5} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=6} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=8} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=9} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/side {dir=10} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/purple/corner : /obj/effect/turf_decal/tile/purple {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/red/corner : /obj/effect/turf_decal/tile/red {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/redside {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redblue/blueside {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redgreen/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/redyellow/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/airless : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark/airless {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/snowdin : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark/snowdin {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/telecomms : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/telecomms/mainframe : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/killroom : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=1} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=@UNSET} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=2} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=4} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=5} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=6} : /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=8} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=9} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral {dir=4} , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/side {dir=10} : /obj/effect/turf_decal/tile/neutral {dir=1} , /obj/effect/turf_decal/tile/neutral , /obj/effect/turf_decal/tile/neutral {dir=8} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/vault/corner : /obj/effect/turf_decal/tile/neutral {dir=@OLD} , /turf/open/floor/plasteel/dark {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/corner : /obj/effect/turf_decal/tile/blue {dir=@OLD} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue {dir=4} , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteblue/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/blue {dir=1} , /obj/effect/turf_decal/tile/blue , /obj/effect/turf_decal/tile/blue {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=1} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=@UNSET} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=2} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=4} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=5} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=6} : /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=8} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=9} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green {dir=4} , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/side {dir=10} : /obj/effect/turf_decal/tile/green {dir=1} , /obj/effect/turf_decal/tile/green , /obj/effect/turf_decal/tile/green {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitegreen/corner : /obj/effect/turf_decal/tile/green {dir=@OLD} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=1} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=@UNSET} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=2} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=4} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=5} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=6} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=8} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=9} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side {dir=10} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/corner : /obj/effect/turf_decal/tile/purple {dir=@OLD} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=1} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=@UNSET} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=2} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=4} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=5} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=6} : /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=8} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=9} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple {dir=4} , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitepurple/side/telecomms {dir=10} : /obj/effect/turf_decal/tile/purple {dir=1} , /obj/effect/turf_decal/tile/purple , /obj/effect/turf_decal/tile/purple {dir=8} , /turf/open/floor/plasteel/white/telecomms {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=1} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=@UNSET} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=2} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=4} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=5} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=6} : /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=8} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=9} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red {dir=4} , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/side {dir=10} : /obj/effect/turf_decal/tile/red {dir=1} , /obj/effect/turf_decal/tile/red , /obj/effect/turf_decal/tile/red {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whitered/corner : /obj/effect/turf_decal/tile/red {dir=@OLD} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/side {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/whiteyellow/corner : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel/white {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=1} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=@UNSET} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=2} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=4} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=5} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=6} : /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=8} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=9} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow {dir=4} , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/side {dir=10} : /obj/effect/turf_decal/tile/yellow {dir=1} , /obj/effect/turf_decal/tile/yellow , /obj/effect/turf_decal/tile/yellow {dir=8} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
+/turf/open/floor/plasteel/yellow/corner : /obj/effect/turf_decal/tile/yellow {dir=@OLD} , /turf/open/floor/plasteel {@OLD;dir=@SKIP}
\ No newline at end of file
diff --git a/tools/mapmerge2/merge_driver_dmi.py b/tools/mapmerge2/merge_driver_dmi.py
new file mode 100644
index 0000000000..37c0abd954
--- /dev/null
+++ b/tools/mapmerge2/merge_driver_dmi.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3
+import sys
+import dmi
+
+def images_equal(left, right):
+ if left.size != right.size:
+ return False
+ w, h = left.size
+ left_load, right_load = left.load(), right.load()
+ for y in range(0, h):
+ for x in range(0, w):
+ lpixel, rpixel = left_load[x, y], right_load[x, y]
+ # quietly ignore changes where both pixels are fully transparent
+ if lpixel != rpixel and (lpixel[3] != 0 or rpixel[3] != 0):
+ return False
+ return True
+
+def states_equal(left, right):
+ result = True
+
+ # basic properties
+ for attr in ('loop', 'rewind', 'movement', 'dirs', 'delays', 'hotspots', 'framecount'):
+ lval, rval = getattr(left, attr), getattr(right, attr)
+ if lval != rval:
+ result = False
+
+ # frames
+ for (left_frame, right_frame) in zip(left.frames, right.frames):
+ if not images_equal(left_frame, right_frame):
+ result = False
+
+ return result
+
+def key_of(state):
+ return (state.name, state.movement)
+
+def dictify(sheet):
+ result = {}
+ for state in sheet.states:
+ k = key_of(state)
+ if k in result:
+ print(f" duplicate {k!r}")
+ result[k] = state
+ return result
+
+def three_way_merge(base, left, right):
+ base_dims = base.width, base.height
+ if base_dims != (left.width, left.height) or base_dims != (right.width, right.height):
+ print("Dimensions have changed:")
+ print(f" Base: {base.width} x {base.height}")
+ print(f" Ours: {left.width} x {left.height}")
+ print(f" Theirs: {right.width} x {right.height}")
+ return True, None
+
+ base_states, left_states, right_states = dictify(base), dictify(left), dictify(right)
+
+ new_left = {k: v for k, v in left_states.items() if k not in base_states}
+ new_right = {k: v for k, v in right_states.items() if k not in base_states}
+ new_both = {}
+ conflicts = []
+ for key, state in list(new_left.items()):
+ in_right = new_right.get(key, None)
+ if in_right:
+ if states_equal(state, in_right):
+ # allow it
+ new_both[key] = state
+ else:
+ # generate conflict states
+ print(f" C: {state.name!r}: added differently in both!")
+ state.name = f"{state.name} !CONFLICT! left"
+ conflicts.append(state)
+ in_right.name = f"{state.name} !CONFLICT! right"
+ conflicts.append(in_right)
+ # don't add it a second time
+ del new_left[key]
+ del new_right[key]
+
+ final_states = []
+ # add states that are currently in the base
+ for state in base.states:
+ in_left = left_states.get(key_of(state), None)
+ in_right = right_states.get(key_of(state), None)
+ left_equals = in_left and states_equal(state, in_left)
+ right_equals = in_right and states_equal(state, in_right)
+
+ if not in_left and not in_right:
+ # deleted in both left and right, it's just deleted
+ print(f" {state.name!r}: deleted in both")
+ elif not in_left:
+ # left deletes
+ print(f" {state.name!r}: deleted in left")
+ if not right_equals:
+ print(f" ... but modified in right")
+ final_states.append(in_right)
+ elif not in_right:
+ # right deletes
+ print(f" {state.name!r}: deleted in right")
+ if not left_equals:
+ print(f" ... but modified in left")
+ final_states.append(in_left)
+ elif left_equals and right_equals:
+ # changed in neither
+ #print(f"Same in both: {state.name!r}")
+ final_states.append(state)
+ elif left_equals:
+ # changed only in right
+ print(f" {state.name!r}: changed in left")
+ final_states.append(in_right)
+ elif right_equals:
+ # changed only in left
+ print(f" {state.name!r}: changed in right")
+ final_states.append(in_left)
+ elif states_equal(in_left, in_right):
+ # changed in both, to the same thing
+ print(f" {state.name!r}: changed same in both")
+ final_states.append(in_left) # either or
+ else:
+ # changed in both
+ name = state.name
+ print(f" C: {name!r}: changed differently in both!")
+ state.name = f"{name} !CONFLICT! base"
+ conflicts.append(state)
+ in_left.name = f"{name} !CONFLICT! left"
+ conflicts.append(in_left)
+ in_right.name = f"{name} !CONFLICT! right"
+ conflicts.append(in_right)
+
+ # add states which both left and right added the same
+ for key, state in new_both.items():
+ print(f" {state.name!r}: added same in both")
+ final_states.append(state)
+
+ # add states that are brand-new in the left
+ for key, state in new_left.items():
+ print(f" {state.name!r}: added in left")
+ final_states.append(state)
+
+ # add states that are brand-new in the right
+ for key, state in new_right.items():
+ print(f" {state.name!r}: added in right")
+ final_states.append(state)
+
+ final_states.extend(conflicts)
+ merged = dmi.Dmi(base.width, base.height)
+ merged.states = final_states
+ return len(conflicts), merged
+
+def main(path, original, left, right):
+ print(f"Merging icon: {path}")
+
+ icon_orig = dmi.Dmi.from_file(original)
+ icon_left = dmi.Dmi.from_file(left)
+ icon_right = dmi.Dmi.from_file(right)
+
+ trouble, merged = three_way_merge(icon_orig, icon_left, icon_right)
+ if merged:
+ merged.to_file(left)
+ if trouble:
+ print("!!! Manual merge required!")
+ if merged:
+ print(" A best-effort merge was performed. You must edit the icon and remove all")
+ print(" icon states marked with !CONFLICT!, leaving only the desired icon.")
+ else:
+ print(" The icon was totally unable to be merged, you must start with one version")
+ print(" or the other and manually resolve the conflict.")
+ print(" Information about which states conflicted is listed above.")
+ return trouble
+
+if __name__ == '__main__':
+ if len(sys.argv) != 6:
+ print("DMI merge driver called with wrong number of arguments")
+ print(" usage: merge-driver-dmi %P %O %A %B %L")
+ exit(1)
+
+ # "left" is also the file that ought to be overwritten
+ _, path, original, left, right, conflict_size_marker = sys.argv
+ exit(main(path, original, left, right))
diff --git a/tools/mapmerge2/precommit.py b/tools/mapmerge2/precommit.py
index 381f1ea8a3..f5ea49a50e 100644
--- a/tools/mapmerge2/precommit.py
+++ b/tools/mapmerge2/precommit.py
@@ -9,6 +9,13 @@ def main(repo):
print("You need to resolve merge conflicts first.")
return 1
+ try:
+ repo.lookup_reference('MERGE_HEAD')
+ print("Not running mapmerge for merge commit.")
+ return 0
+ except KeyError:
+ pass
+
changed = 0
for path, status in repo.status().items():
if path.endswith(".dmm") and (status & (pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_INDEX_NEW)):
@@ -20,12 +27,12 @@ def main(repo):
head_blob = repo[repo[repo.head.target].tree[path].id]
except KeyError:
# New map, no entry in HEAD
- print(f"Converting new map: {path}")
+ print(f"Converting new map: {path}", flush=True)
assert (status & pygit2.GIT_STATUS_INDEX_NEW)
merged_map = index_map
else:
# Entry in HEAD, merge the index over it
- print(f"Merging map: {path}")
+ print(f"Merging map: {path}", flush=True)
assert not (status & pygit2.GIT_STATUS_INDEX_NEW)
head_map = dmm.DMM.from_bytes(head_blob.read_raw())
merged_map = merge_map(index_map, head_map)
diff --git a/tools/mapmerge2/readme.md b/tools/mapmerge2/readme.md
index 251eee19c3..0ff4d21ac2 100644
--- a/tools/mapmerge2/readme.md
+++ b/tools/mapmerge2/readme.md
@@ -2,7 +2,7 @@
**Map Merge 2** is an improvement over previous map merging scripts, with
better merge-conflict prevention, multi-Z support, and automatic handling of
-key overflow.
+key overflow. For up-to-date tips and tricks, also visit the [Map Merger] wiki article.
## What Map Merging Is
@@ -15,17 +15,14 @@ contains the desired changes.
## Installation
-* Install Python 3.6.X from the [Python website]
- * Make sure to check 'Add Python to PATH' on the first screen of the setup!
-* Run `requirements-install.bat` in the tools/mapmerge2 folder (or run `python -m pip install -r requirements.txt`).
-* Run `install.bat` in the tools/mapmerge2/hooks folder.
-
-After this point, any time you make commits in Git, the maps should automatically be converted to tgm format for you.
-
-If you find it necessary to convert them by hand, there are batch files in the tools/mapmerge2 folder to do so.
-
-If you re-clone, you will need to re-run `install.bat`.
+To install Python dependencies, run `requirements-install.bat`, or run
+`python -m pip install -r requirements.txt` directly. See the [Git hooks]
+documentation to install the Git pre-commit hook which runs the map merger
+automatically, or use `tools/mapmerge/Prepare Maps.bat` to save backups before
+running `mapmerge.bat`.
+For up-to-date installation and detailed troubleshooting instructions, visit
+the [Map Merger] wiki article.
## Code Structure
@@ -47,8 +44,9 @@ Implementation modules:
* `mapmerge.py` includes the implementation of the map merge operation.
* `frontend.py` includes the common code for the frontend scripts.
-`precommit.py` is run by the Git hooks if installed, and merges the new
+`precommit.py` is run by the [Git hooks] if installed, and merges the new
version of any map saved in the index (`git add`ed) with the old version stored
in Git when run.
-[Python website]: https://www.python.org/downloads/
+[Map Merger]: https://tgstation13.org/wiki/Map_Merger
+[Git hooks]: ../hooks/README.md
diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt
index d01a2c6ccf..8d77427c73 100644
--- a/tools/mapmerge2/requirements.txt
+++ b/tools/mapmerge2/requirements.txt
@@ -1,2 +1,3 @@
-pygit2==0.26.0
+pygit2
bidict==0.13.1
+Pillow==5.1.0
diff --git a/tools/mapmerge2/tgm2dmm.bat b/tools/mapmerge2/tgm2dmm.bat
index 2748533feb..5bf5677453 100644
--- a/tools/mapmerge2/tgm2dmm.bat
+++ b/tools/mapmerge2/tgm2dmm.bat
@@ -1,5 +1,5 @@
@echo off
-set MAPROOT=../../_maps/
+set MAPROOT=../../maps/
set TGM=0
python convert.py
pause
diff --git a/tools/mapmerge2/update_paths.py b/tools/mapmerge2/update_paths.py
new file mode 100644
index 0000000000..6359b6bb30
--- /dev/null
+++ b/tools/mapmerge2/update_paths.py
@@ -0,0 +1,163 @@
+# A script and syntax for applying path updates to maps.
+import re
+import os
+import argparse
+import frontend
+from dmm import *
+
+desc = """
+Update dmm files given update file/string.
+Replacement syntax example:
+ /turf/open/floor/plasteel/warningline : /obj/effect/turf_decal {dir = @OLD ;tag = @SKIP;icon_state = @SKIP}
+ /turf/open/floor/plasteel/warningline : /obj/effect/turf_decal {@OLD} , /obj/thing {icon_state = @OLD:name; name = "meme"}
+ /turf/open/floor/plasteel/warningline{dir=2} : /obj/thing
+New paths properties:
+ @OLD - if used as property name copies all modified properties from original path to this one
+ property = @SKIP - will not copy this property through when global @OLD is used.
+ property = @OLD - will copy this modified property from original object even if global @OLD is not used
+ property = @OLD:name - will copy [name] property from original object even if global @OLD is not used
+ Anything else is copied as written.
+Old paths properties:
+ Will be used as a filter.
+ property = @UNSET - will apply the rule only if the property is not mapedited
+"""
+
+default_map_directory = "../../_maps"
+replacement_re = re.compile('\s*([^{]*)\s*(\{(.*)\})?')
+
+#urgent todo: replace with actual parser, this is slow as janitor in crit
+split_re = re.compile('((?:[A-Za-z0-9_\-$]+)\s*=\s*(?:"(?:.+?)"|[^";]*)|@OLD)')
+
+
+def props_to_string(props):
+ return "{{{0}}}".format(";".join([k+" = "+props[k] for k in props]))
+
+
+def string_to_props(propstring, verbose = False):
+ props = dict()
+ for raw_prop in re.split(split_re, propstring):
+ if not raw_prop or raw_prop.strip() == ';':
+ continue
+ prop = raw_prop.split('=', maxsplit=1)
+ props[prop[0].strip()] = prop[1].strip() if len(prop) > 1 else None
+ if verbose:
+ print("{0} to {1}".format(propstring, props))
+ return props
+
+
+def parse_rep_string(replacement_string, verbose = False):
+ # translates /blah/blah {meme = "test",} into path,prop dictionary tuple
+ match = re.match(replacement_re, replacement_string)
+ path = match.group(1)
+ props = match.group(3)
+ if props:
+ prop_dict = string_to_props(props, verbose)
+ else:
+ prop_dict = dict()
+ return path.strip(), prop_dict
+
+
+def update_path(dmm_data, replacement_string, verbose=False):
+ old_path_part, new_path_part = replacement_string.split(':', maxsplit=1)
+ old_path, old_path_props = parse_rep_string(old_path_part, verbose)
+ new_paths = list()
+ for replacement_def in new_path_part.split(','):
+ new_path, new_path_props = parse_rep_string(replacement_def, verbose)
+ new_paths.append((new_path, new_path_props))
+
+ def replace_def(match):
+ if match.group(2):
+ old_props = string_to_props(match.group(2), verbose)
+ else:
+ old_props = dict()
+ for filter_prop in old_path_props:
+ if filter_prop not in old_props:
+ if old_path_props[filter_prop] == "@UNSET":
+ continue
+ else:
+ return [match.group(0)]
+ else:
+ if old_props[filter_prop] != old_path_props[filter_prop] or old_path_props[filter_prop] == "@UNSET":
+ return [match.group(0)] #does not match current filter, skip the change.
+ if verbose:
+ print("Found match : {0}".format(match.group(0)))
+ out_paths = []
+ for new_path, new_props in new_paths:
+ out = new_path
+ out_props = dict()
+ for prop_name, prop_value in new_props.items():
+ if prop_name == "@OLD":
+ out_props = dict(old_props)
+ continue
+ if prop_value == "@SKIP":
+ out_props.pop(prop_name, None)
+ continue
+ if prop_value.startswith("@OLD"):
+ params = prop_value.split(":")
+ if prop_name in old_props:
+ out_props[prop_name] = old_props[params[1]] if len(params) > 1 else old_props[prop_name]
+ continue
+ out_props[prop_name] = prop_value
+ if out_props:
+ out += props_to_string(out_props)
+ out_paths.append(out)
+ if verbose:
+ print("Replacing with: {0}".format(out_paths))
+ return out_paths
+
+ def get_result(element):
+ p = re.compile("{0}\s*({{(.*)}})?$".format(re.escape(old_path)))
+ match = p.match(element)
+ if match:
+ return replace_def(match) # = re.sub(p,replace_def,element)
+ else:
+ return [element]
+
+ bad_keys = {}
+ keys = list(dmm_data.dictionary.keys())
+ for definition_key in keys:
+ def_value = dmm_data.dictionary[definition_key]
+ new_value = tuple(y for x in def_value for y in get_result(x))
+ if new_value != def_value:
+ dmm_data.overwrite_key(definition_key, new_value, bad_keys)
+ dmm_data.reassign_bad_keys(bad_keys)
+
+
+def update_map(map_filepath, updates, verbose=False):
+ print("Updating: {0}".format(map_filepath))
+ dmm_data = DMM.from_file(map_filepath)
+ for update_string in updates:
+ update_path(dmm_data, update_string, verbose)
+ dmm_data.to_file(map_filepath, True)
+
+
+def update_all_maps(map_directory, updates, verbose=False):
+ for root, _, files in os.walk(map_directory):
+ for filepath in files:
+ if filepath.endswith(".dmm"):
+ path = os.path.join(root, filepath)
+ update_map(path, updates, verbose)
+
+
+def main(args):
+ if args.inline:
+ updates = [args.update_source]
+ else:
+ with open(args.update_source) as f:
+ updates = [line for line in f if line and not line.startswith("#") and not line.isspace()]
+
+ if args.map:
+ update_map(args.map, updates, verbose=args.verbose)
+ else:
+ map_directory = args.directory or frontend.read_settings().map_folder
+ update_all_maps(map_directory, updates, verbose=args.verbose)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument("update_source", help="update file path / line of update notation")
+ parser.add_argument("--map", "-m", help="path to update, defaults to all maps in maps directory")
+ parser.add_argument("--directory", "-d", help="path to maps directory, defaults to _maps/")
+ parser.add_argument("--inline", "-i", help="treat update source as update string instead of path", action="store_true")
+ parser.add_argument("--verbose", "-v", help="toggle detailed update information", action="store_true")
+ main(parser.parse_args())
diff --git a/vorestation.dme b/vorestation.dme
index d92d18520b..7b47f046b9 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -72,6 +72,7 @@
#include "code\__defines\supply.dm"
#include "code\__defines\targeting.dm"
#include "code\__defines\turfs.dm"
+#include "code\__defines\typeids.dm"
#include "code\__defines\unit_tests.dm"
#include "code\__defines\xenoarcheaology.dm"
#include "code\__defines\ZAS.dm"
@@ -1408,6 +1409,7 @@
#include "code\modules\admin\secrets\random_events\trigger_cordical_borer_infestation.dm"
#include "code\modules\admin\secrets\random_events\trigger_xenomorph_infestation.dm"
#include "code\modules\admin\verbs\adminhelp.dm"
+#include "code\modules\admin\verbs\adminhelp_vr.dm"
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"
#include "code\modules\admin\verbs\adminsay.dm"
@@ -2287,7 +2289,6 @@
#include "code\modules\mob\living\simple_animal\constructs\soulstone.dm"
#include "code\modules\mob\living\simple_animal\humanoids\clown.dm"
#include "code\modules\mob\living\simple_animal\humanoids\head.dm"
-#include "code\modules\mob\living\simple_animal\humanoids\kobold.dm"
#include "code\modules\mob\living\simple_animal\humanoids\mechamobs.dm"
#include "code\modules\mob\living\simple_animal\humanoids\pirate.dm"
#include "code\modules\mob\living\simple_animal\humanoids\russian.dm"