diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index 2683922820..6acc667f2d 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -19,8 +19,8 @@
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
-#define START_PROCESSING(Processor, Datum) if (!Datum.is_processing) {Datum.is_processing = TRUE;Processor.processing += Datum}
-#define STOP_PROCESSING(Processor, Datum) Datum.is_processing = FALSE;Processor.processing -= Datum
+#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum}
+#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum
//! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm
index 8384c509d7..ae3c70db1d 100644
--- a/code/__defines/_tick.dm
+++ b/code/__defines/_tick.dm
@@ -10,3 +10,5 @@
#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
+
+#define UNTIL(X) while(!(X)) stoplag()
diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm
index 27b1afcc1c..b9417d2a86 100644
--- a/code/__defines/flags.dm
+++ b/code/__defines/flags.dm
@@ -1,6 +1,5 @@
-/*
- These defines are specific to the atom/flags_1 bitmask
-*/
+//MARK ALL FLAG CHANGES IN _globals/bitfields.dm!
+//All flags should go in here if possible.
#define ALL (~0) //For convenience.
#define NONE 0
@@ -17,3 +16,4 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
// datum_flags
#define DF_VAR_EDITED (1<<0)
#define DF_ISPROCESSING (1<<1)
+
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index d77b5cf297..95d4929acb 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -106,26 +106,15 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define ATMOS_DEFAULT_VOLUME_MIXER 200 // L.
#define ATMOS_DEFAULT_VOLUME_PIPE 70 // L.
+//wIP - PORT ALL OF THESE TO SUBSYSTEMS AND GET RID OF THE WHOLE LIST PROCESS THING
// Fancy-pants START/STOP_PROCESSING() macros that lets us custom define what the list is.
#define START_PROCESSING_IN_LIST(DATUM, LIST) \
-if (DATUM.is_processing) {\
- if(DATUM.is_processing != #LIST)\
- {\
- crash_with("Failed to start processing. [log_info_line(DATUM)] is already being processed by [DATUM.is_processing] but queue attempt occured on [#LIST]."); \
- }\
-} else {\
- DATUM.is_processing = #LIST;\
+if (!(DATUM.datum_flags & DF_ISPROCESSING)) {\
LIST += DATUM;\
+ DATUM.datum_flags |= DF_ISPROCESSING\
}
-#define STOP_PROCESSING_IN_LIST(DATUM, LIST) \
-if(DATUM.is_processing) {\
- if(LIST.Remove(DATUM)) {\
- DATUM.is_processing = null;\
- } else {\
- crash_with("Failed to stop processing. [log_info_line(DATUM)] is being processed by [is_processing] and not found in SSmachines.[#LIST]"); \
- }\
-}
+#define STOP_PROCESSING_IN_LIST(DATUM, LIST) LIST.Remove(DATUM);DATUM.datum_flags &= ~DF_ISPROCESSING
// Note - I would prefer these be defined machines.dm, but some are used prior in file order. ~Leshana
#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_machines)
diff --git a/code/__defines/vv.dm b/code/__defines/vv.dm
index 025b65d158..b7b54b0b57 100644
--- a/code/__defines/vv.dm
+++ b/code/__defines/vv.dm
@@ -20,6 +20,13 @@
#define VV_MARKED_DATUM "Marked Datum"
#define VV_BITFIELD "Bitfield"
+#define VV_MSG_MARKED "
Marked Object"
+#define VV_MSG_EDITED "
Var Edited"
+#define VV_MSG_DELETED "
Deleted"
+
+#define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50
+#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150
+
//Helpers for vv_get_dropdown()
#define VV_DROPDOWN_OPTION(href_key, name) . += ""
diff --git a/code/_global_vars/bitfields.dm b/code/_global_vars/bitfields.dm
new file mode 100644
index 0000000000..891c50ce88
--- /dev/null
+++ b/code/_global_vars/bitfields.dm
@@ -0,0 +1,7 @@
+GLOBAL_LIST_INIT(bitfields, list(
+ "datum_flags" = list(
+ "DF_VAR_EDITED" = DF_VAR_EDITED,
+ "DF_ISPROCESSING" = DF_ISPROCESSING
+ )
+
+))
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index 855aa9c138..b4d9f39a7f 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -753,3 +753,9 @@ proc/dd_sortedTextList(list/incoming)
for(var/i = 1 to l.len)
if(islist(.[i]))
.[i] = .(.[i])
+
+//Return a list with no duplicate entries
+/proc/uniqueList(list/L)
+ . = list()
+ for(var/i in L)
+ . |= i
diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm
index 0316e1d186..55dad2242f 100644
--- a/code/_helpers/type2type.dm
+++ b/code/_helpers/type2type.dm
@@ -242,3 +242,34 @@
/proc/isLeap(y)
return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
+
+//Takes a string and a datum
+//The string is well, obviously the string being checked
+//The datum is used as a source for var names, to check validity
+//Otherwise every single word could technically be a variable!
+/proc/string2listofvars(var/t_string, var/datum/var_source)
+ if(!t_string || !var_source)
+ return list()
+
+ . = list()
+
+ var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check
+ if(var_found)
+ //Find var names
+
+ // "A dog said hi [name]!"
+ // splittext() --> list("A dog said hi ","name]!"
+ // jointext() --> "A dog said hi name]!"
+ // splittext() --> list("A","dog","said","hi","name]!")
+
+ t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios
+ var/list/list_value = splittext(t_string,"\[")
+ var/intermediate_stage = jointext(list_value, null)
+
+ list_value = splittext(intermediate_stage," ")
+ for(var/value in list_value)
+ if(findtext(value,"]"))
+ value = splittext(value,"]") //"name]!" --> list("name","!")
+ for(var/A in value)
+ if(var_source.vars.Find(A))
+ . += A
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 2073f7970e..72a289a622 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1441,3 +1441,92 @@ var/mob/dview/dview_mob = new
return
#define NAMEOF(datum, X) (#X || ##datum.##X)
+
+/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
+ if (value == FALSE) //nothing should be calling us with a number, so this is safe
+ value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text
+ if (isnull(value))
+ return
+ value = trim(value)
+ if(!isnull(value) && value != "")
+ matches = filter_fancy_list(matches, value)
+
+ if(matches.len==0)
+ return
+
+ var/chosen
+ if(matches.len==1)
+ chosen = matches[1]
+ else
+ chosen = input("Select a type", "Pick Type", matches[1]) as null|anything in matches
+ if(!chosen)
+ return
+ chosen = matches[chosen]
+ return chosen
+
+/proc/get_fancy_list_of_atom_types()
+ var/static/list/pre_generated_list
+ if (!pre_generated_list) //init
+ pre_generated_list = make_types_fancy(typesof(/atom))
+ return pre_generated_list
+
+/proc/get_fancy_list_of_datum_types()
+ var/static/list/pre_generated_list
+ if (!pre_generated_list) //init
+ pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom)))
+ return pre_generated_list
+
+/proc/filter_fancy_list(list/L, filter as text)
+ var/list/matches = new
+ for(var/key in L)
+ var/value = L[key]
+ if(findtext("[key]", filter) || findtext("[value]", filter))
+ matches[key] = value
+ return matches
+
+/proc/make_types_fancy(var/list/types)
+ if (ispath(types))
+ types = list(types)
+ . = list()
+ for(var/type in types)
+ var/typename = "[type]"
+ var/static/list/TYPES_SHORTCUTS = list(
+ /obj/effect/decal/cleanable = "CLEANABLE",
+ /obj/item/device/radio/headset = "HEADSET",
+ /obj/item/clothing/head/helmet/space = "SPESSHELMET",
+ /obj/item/weapon/book/manual = "MANUAL",
+ /obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first
+ /obj/item/weapon/reagent_containers/food = "FOOD",
+ /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS",
+ /obj/machinery/atmospherics = "ATMOS_MECH",
+ /obj/machinery/portable_atmospherics = "PORT_ATMOS",
+ /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK",
+ /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP",
+ /obj/item/organ = "ORGAN",
+ /obj/item = "ITEM",
+ /obj/machinery = "MACHINERY",
+ /obj/effect = "EFFECT",
+ /obj = "O",
+ /datum = "D",
+ /turf/simulated/wall = "S-WALL",
+ /turf/simulated/floor = "S-FLOOR",
+ /turf/simulated = "SIMULATED",
+ /turf/unsimulated/wall = "US-WALL",
+ /turf/unsimulated/floor = "US-FLOOR",
+ /turf/unsimulated = "UNSIMULATED",
+ /turf = "T",
+ /mob/living/carbon = "CARBON",
+ /mob/living/simple_mob = "SIMPLE",
+ /mob/living = "LIVING",
+ /mob = "M"
+ )
+ for (var/tn in TYPES_SHORTCUTS)
+ if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
+ typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/"))
+ break
+ .[typename] = type
+
+/proc/IsValidSrc(datum/D)
+ if(istype(D))
+ return !QDELETED(D)
+ return FALSE
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
index fa7b917194..56a9244347 100644
--- a/code/controllers/globals.dm
+++ b/code/controllers/globals.dm
@@ -38,8 +38,10 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
stat("Globals:", statclick.update("Edit"))
-/datum/controller/global_vars/VV_hidden()
- return ..() + gvars_datum_protected_varlist
+/datum/controller/global_vars/vv_edit_var(var_name, var_value)
+ if(gvars_datum_protected_varlist[var_name])
+ return FALSE
+ return ..()
/datum/controller/global_vars/Initialize(var/exclude_these)
gvars_datum_init_order = list()
diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm
index 4cba70a981..4525175393 100644
--- a/code/controllers/subsystems/machines.dm
+++ b/code/controllers/subsystems/machines.dm
@@ -109,7 +109,7 @@ SUBSYSTEM_DEF(machines)
else
global.pipe_networks.Remove(PN)
if(!QDELETED(PN))
- PN.is_processing = null
+ DISABLE_BITFIELD(PN.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -127,7 +127,7 @@ SUBSYSTEM_DEF(machines)
else
global.processing_machines.Remove(M)
if(!QDELETED(M))
- M.is_processing = null
+ DISABLE_BITFIELD(M.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -144,7 +144,7 @@ SUBSYSTEM_DEF(machines)
else
global.powernets.Remove(PN)
if(!QDELETED(PN))
- PN.is_processing = null
+ DISABLE_BITFIELD(PN.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(machines)
current_run.len--
if(!I.pwr_drain(wait)) // 0 = Process Kill, remove from processing list.
global.processing_power_items.Remove(I)
- I.is_processing = null
+ DISABLE_BITFIELD(I.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
diff --git a/code/datums/browser.dm b/code/datums/browser.dm
index 577698532e..ea2f4feec2 100644
--- a/code/datums/browser.dm
+++ b/code/datums/browser.dm
@@ -105,6 +105,10 @@
"}
/datum/browser/proc/open(var/use_onclose = 1)
+ if(isnull(window_id)) //null check because this can potentially nuke goonchat
+ WARNING("Browser [title] tried to open with a null ID")
+ to_chat(user, "The [title] browser you tried to open failed a sanity check! Please report this on github!")
+ return
var/window_size = ""
if (width && height)
window_size = "size=[width]x[height];"
@@ -112,9 +116,6 @@
if (use_onclose)
onclose(user, window_id, ref)
-/datum/browser/proc/close()
- user << browse(null, "window=[window_id]")
-
// This will allow you to show an icon in the browse window
// This is added to mob so that it can be used without a reference to the browser object
// There is probably a better place for this...
@@ -157,12 +158,12 @@
//world << "OnClose [user]: [windowid] : ["on-close=\".windowclose [param]\""]"
-
// the on-close client verb
// called when a browser popup window is closed after registering with proc/onclose()
// if a valid atom reference is supplied, call the atom's Topic() with "close=1"
// otherwise, just reset the client mob's machine var.
//
+
/client/verb/windowclose(var/atomref as text)
set hidden = 1 // hide this verb from the user's panel
set name = ".windowclose" // no autocomplete on cmd line
@@ -182,3 +183,198 @@
//world << "[src] was [src.mob.machine], setting to null"
src.mob.unset_machine()
return
+
+/datum/browser/proc/close()
+ if(!isnull(window_id))//null check because this can potentially nuke goonchat
+ user << browse(null, "window=[window_id]")
+ else
+ WARNING("Browser [title] tried to close with a null ID")
+
+/datum/browser/modal/alert/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1,Timeout=6000)
+ if (!User)
+ return
+
+ var/output = {"