diff --git a/code/__DEFINES/_versions.dm b/code/__DEFINES/_versions.dm
new file mode 100644
index 00000000000..83a18de6f73
--- /dev/null
+++ b/code/__DEFINES/_versions.dm
@@ -0,0 +1,2 @@
+/// Version of RUST-G that this codebase wants
+#define RUST_G_VERSION "0.5.0-P"
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
index 029d15f4895..62920ed2a40 100644
--- a/code/__DEFINES/job.dm
+++ b/code/__DEFINES/job.dm
@@ -7,58 +7,59 @@
#define JOBCAT_ENGSEC (1<<0)
-#define JOB_CAPTAIN (1<<0)
-#define JOB_HOS (1<<1)
-#define JOB_WARDEN (1<<2)
+#define JOB_CAPTAIN (1<<0)
+#define JOB_HOS (1<<1)
+#define JOB_WARDEN (1<<2)
#define JOB_DETECTIVE (1<<3)
-#define JOB_OFFICER (1<<4)
+#define JOB_OFFICER (1<<4)
#define JOB_CHIEF (1<<5)
#define JOB_ENGINEER (1<<6)
#define JOB_ATMOSTECH (1<<7)
-#define JOB_AI (1<<8)
-#define JOB_CYBORG (1<<9)
-#define JOB_CENTCOM (1<<10)
+#define JOB_AI (1<<8)
+#define JOB_CYBORG (1<<9)
+#define JOB_CENTCOM (1<<10)
#define JOB_SYNDICATE (1<<11)
+#define JOB_JUDGE (1<<12)
#define JOBCAT_MEDSCI (1<<1)
-#define JOB_RD (1<<0)
+#define JOB_RD (1<<0)
#define JOB_SCIENTIST (1<<1)
-#define JOB_CHEMIST (1<<2)
-#define JOB_CMO (1<<3)
-#define JOB_DOCTOR (1<<4)
-#define JOB_GENETICIST (1<<5)
-#define JOB_VIROLOGIST (1<<6)
+#define JOB_CHEMIST (1<<2)
+#define JOB_CMO (1<<3)
+#define JOB_DOCTOR (1<<4)
+#define JOB_GENETICIST (1<<5)
+#define JOB_VIROLOGIST (1<<6)
#define JOB_PSYCHIATRIST (1<<7)
-#define JOB_ROBOTICIST (1<<8)
+#define JOB_ROBOTICIST (1<<8)
#define JOB_PARAMEDIC (1<<9)
-#define JOB_CORONER (1<<10)
+#define JOB_CORONER (1<<10)
#define JOBCAT_SUPPORT (1<<2)
-#define JOB_HOP (1<<0)
+#define JOB_HOP (1<<0)
#define JOB_BARTENDER (1<<1)
#define JOB_BOTANIST (1<<2)
#define JOB_CHEF (1<<3)
-#define JOB_JANITOR (1<<4)
+#define JOB_JANITOR (1<<4)
#define JOB_LIBRARIAN (1<<5)
#define JOB_QUARTERMASTER (1<<6)
#define JOB_CARGOTECH (1<<7)
#define JOB_MINER (1<<8)
-#define JOB_LAWYER (1<<9)
+#define JOB_LAWYER (1<<9)
#define JOB_CHAPLAIN (1<<10)
#define JOB_CLOWN (1<<11)
#define JOB_MIME (1<<12)
#define JOB_CIVILIAN (1<<13)
#define JOB_EXPLORER (1<<14)
-#define JOBCAT_KARMA (1<<3)
+#define JOBCAT_KARMA (1<<3)
#define JOB_NANO (1<<0)
-#define JOB_BLUESHIELD (1<<1)
-#define JOB_BARBER (1<<3)
+#define JOB_BLUESHIELD (1<<1)
+#define JOB_BARBER (1<<3)
// #define JOB_MECHANIC (1<<4) // AA07 2021-10-02 - Removed: Kept for history sake
-#define JOB_BRIGDOC (1<<5)
-#define JOB_JUDGE (1<<6)
-// #define JOB_PILOT (1<<7) // AA07 2021-10-02 - Removed: Kept for history sake
+#define JOB_BRIGDOC (1<<5)
+// #define JOB_JUDGE (1<<6) // AA07 2021-10-09 - Moved to ENGSEC (Non karma): Define kept for history sake
+// #define JOB_PILOT (1<<7) // AA07 2021-10-02 - Removed: Kept for history sake
diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm
index 6a331a6131f..4ee84eda941 100644
--- a/code/__DEFINES/rust_g.dm
+++ b/code/__DEFINES/rust_g.dm
@@ -1,80 +1,130 @@
+// rust_g.dm - DM API for rust_g extension library
+//
+// To configure, create a `rust_g.config.dm` and set what you care about from
+// the following options:
+//
+// #define RUST_G "path/to/rust_g"
+// Override the .dll/.so detection logic with a fixed path or with detection
+// logic of your own.
+//
+// #define RUSTG_OVERRIDE_BUILTINS
+// Enable replacement rust-g functions for certain builtins. Off by default.
+
#ifndef RUST_G
-/// Locator for the RUSTG DLL or SO depending on system type. Override if needed.
-#define RUST_G (world.system_type == UNIX ? "./librust_g.so" : "./rust_g.dll")
+// Default automatic RUST_G detection.
+// On Windows, looks in the standard places for `rust_g.dll`.
+// On Linux, looks in `.`, `$LD_LIBRARY_PATH`, and `~/.byond/bin` for either of
+// `librust_g.so` (preferred) or `rust_g` (old).
+
+/* This comment bypasses grep checks */ /var/__rust_g
+
+/proc/__detect_rust_g()
+ if (world.system_type == UNIX)
+ if (fexists("./librust_g.so"))
+ // No need for LD_LIBRARY_PATH badness.
+ return __rust_g = "./librust_g.so"
+ else if (fexists("./rust_g"))
+ // Old dumb filename.
+ return __rust_g = "./rust_g"
+ else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g"))
+ // Old dumb filename in `~/.byond/bin`.
+ return __rust_g = "rust_g"
+ else
+ // It's not in the current directory, so try others
+ return __rust_g = "librust_g.so"
+ else
+ return __rust_g = "rust_g.dll"
+
+#define RUST_G (__rust_g || __detect_rust_g())
#endif
-// Gets the version of RUSTG
+/// Gets the version of rust_g
/proc/rustg_get_version() return call(RUST_G, "get_version")()
-// Defines for internal job subsystem //
-#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
-#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
-#define RUSTG_JOB_ERROR "JOB PANICKED"
+// Cellular Noise Operations //
-// DMI related operations //
+/**
+ * This proc generates a cellular automata noise grid which can be used in procedural generation methods.
+ *
+ * Returns a single string that goes row by row, with values of 1 representing an alive cell, and a value of 0 representing a dead cell.
+ *
+ * Arguments:
+ * * percentage: The chance of a turf starting closed
+ * * smoothing_iterations: The amount of iterations the cellular automata simulates before returning the results
+ * * birth_limit: If the number of neighboring cells is higher than this amount, a cell is born
+ * * death_limit: If the number of neighboring cells is lower than this amount, a cell dies
+ * * width: The width of the grid.
+ * * height: The height of the grid.
+ */
+#define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \
+ call(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height)
+
+// DMI Operations //
#define rustg_dmi_strip_metadata(fname) call(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(path, width, height, data) call(RUST_G, "dmi_create_png")(path, width, height, data)
+#define rustg_dmi_resize_png(path, width, height, resizetype) call(RUST_G, "dmi_resize_png")(path, width, height, resizetype)
-// Noise related operations //
-#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
-
-// File related operations //
+// File Operations //
#define rustg_file_read(fname) call(RUST_G, "file_read")(fname)
+#define rustg_file_exists(fname) call(RUST_G, "file_exists")(fname)
#define rustg_file_write(text, fname) call(RUST_G, "file_write")(text, fname)
#define rustg_file_append(text, fname) call(RUST_G, "file_append")(text, fname)
#ifdef RUSTG_OVERRIDE_BUILTINS
-#define file2text(fname) rustg_file_read(fname)
-#define text2file(text, fname) rustg_file_append(text, fname)
+ #define file2text(fname) rustg_file_read("[fname]")
+ #define text2file(text, fname) rustg_file_append(text, "[fname]")
#endif
-// Git related operations //
+// Git Operations //
#define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev)
#define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev)
-// Hash related operations //
+// Hashing Operations //
#define rustg_hash_string(algorithm, text) call(RUST_G, "hash_string")(algorithm, text)
#define rustg_hash_file(algorithm, fname) call(RUST_G, "hash_file")(algorithm, fname)
+#define rustg_hash_generate_totp(seed) call(RUST_G, "generate_totp")(seed)
+#define rustg_hash_generate_totp_tolerance(seed, tolerance) call(RUST_G, "generate_totp_tolerance")(seed, tolerance)
#define RUSTG_HASH_MD5 "md5"
#define RUSTG_HASH_SHA1 "sha1"
#define RUSTG_HASH_SHA256 "sha256"
#define RUSTG_HASH_SHA512 "sha512"
+#define RUSTG_HASH_XXH64 "xxh64"
+#define RUSTG_HASH_BASE64 "base64"
#ifdef RUSTG_OVERRIDE_BUILTINS
-#define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing))
+ #define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing))
#endif
-// Logging stuff //
-#define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text)
-/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
-
-// URL encoding stuff
-#define rustg_url_encode(text) call(RUST_G, "url_encode")(text)
-#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)
-
-#ifdef RUSTG_OVERRIDE_BUILTINS
-#define url_encode(text) rustg_url_encode(text)
-#define url_decode(text) rustg_url_decode(text)
-#endif
-
-// HTTP library stuff //
+// HTTP Operations //
#define RUSTG_HTTP_METHOD_GET "get"
#define RUSTG_HTTP_METHOD_PUT "put"
#define RUSTG_HTTP_METHOD_DELETE "delete"
#define RUSTG_HTTP_METHOD_PATCH "patch"
#define RUSTG_HTTP_METHOD_HEAD "head"
#define RUSTG_HTTP_METHOD_POST "post"
-
-// Commented out because this thing locks up the entire DD process when you use it
-// DO NOT USE FOR THE LOVE OF GOD
-// #define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers)
-#define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers)
+#define rustg_http_request_blocking(method, url, body, headers, options) call(RUST_G, "http_request_blocking")(method, url, body, headers, options)
+#define rustg_http_request_async(method, url, body, headers, options) call(RUST_G, "http_request_async")(method, url, body, headers, options)
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)
/proc/rustg_create_async_http_client() return call(RUST_G, "start_http_client")()
/proc/rustg_close_async_http_client() return call(RUST_G, "shutdown_http_client")()
-// SQL stuff //
+// Jobs Subsystem Operations //
+#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
+#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
+#define RUSTG_JOB_ERROR "JOB PANICKED"
+
+// JSON Operations //
+#define rustg_json_is_valid(text) (call(RUST_G, "json_is_valid")(text) == "true")
+
+// Logging Operations //
+#define rustg_log_write(fname, text) call(RUST_G, "log_write")(fname, text)
+/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
+
+// Noise Operations //
+#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
+
+// SQL Opeartions //
#define rustg_sql_connect_pool(options) call(RUST_G, "sql_connect_pool")(options)
#define rustg_sql_query_async(handle, query, params) call(RUST_G, "sql_query_async")(handle, query, params)
#define rustg_sql_query_blocking(handle, query, params) call(RUST_G, "sql_query_blocking")(handle, query, params)
@@ -82,8 +132,19 @@
#define rustg_sql_disconnect_pool(handle) call(RUST_G, "sql_disconnect_pool")(handle)
#define rustg_sql_check_query(job_id) call(RUST_G, "sql_check_query")("[job_id]")
-// toml2json stuff //
-#define rustg_toml2json(tomlfile) call(RUST_G, "toml2json")(tomlfile)
+// TOML Operations //
+#define rustg_read_toml_file(path) json_decode(call(RUST_G, "toml_file_to_json")(path) || "null")
+
+// Unzip Operations //
+#define rustg_unzip_download_async(url, unzip_directory) call(RUST_G, "unzip_download_async")(url, unzip_directory)
+#define rustg_unzip_check(job_id) call(RUST_G, "unzip_check")("[job_id]")
+
+// URL Encoder/Decoder Operations //
+#define rustg_url_encode(text) call(RUST_G, "url_encode")("[text]")
+#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)
+
+#ifdef RUSTG_OVERRIDE_BUILTINS
+ #define url_encode(text) rustg_url_encode(text)
+ #define url_decode(text) rustg_url_decode(text)
+#endif
-// RUSTG Version //
-#define RUST_G_VERSION "0.4.5-P3"
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index c88dd0671b1..daf7bfe6b52 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -622,6 +622,13 @@ so as to remain in compliance with the most up-to-date laws."
stone = null
return ..()
+/obj/screen/alert/notify_mapvote
+ name = "Map Vote"
+ desc = "Vote on which map you would like to play on next!"
+ icon_state = "map_vote"
+
+/obj/screen/alert/notify_mapvote/Click()
+ SSvote.browse_to(usr.client)
//OBJECT-BASED
diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm
index 81c2c6787bf..0550280527b 100644
--- a/code/controllers/configuration/configuration_core.dm
+++ b/code/controllers/configuration/configuration_core.dm
@@ -97,7 +97,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
var/config_file = "config/config.toml"
if(!fexists(config_file))
config_file = "config/example/config.toml" // Fallback to example if user hasnt setup config properly
- var/raw_json = rustg_toml2json(config_file)
+ var/raw_json = rustg_read_toml_file(config_file)
raw_data = json_decode(raw_json)
// Now pass through all our stuff
@@ -141,7 +141,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
DIRECT_OUTPUT(world.log, "Overrides found for this instance. Loading them.")
var/start = start_watch() // Time tracking
- var/raw_json = rustg_toml2json(override_file)
+ var/raw_json = rustg_read_toml_file(override_file)
raw_data = json_decode(raw_json)
// Now safely load our overrides.
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 28f69ba1795..5bb1d0eb69f 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -408,12 +408,65 @@ SUBSYSTEM_DEF(ticker)
if(player.mind.assigned_role != player.mind.special_role)
SSjobs.AssignRank(player, player.mind.assigned_role, FALSE)
SSjobs.EquipRank(player, player.mind.assigned_role, FALSE)
- EquipCustomItems(player)
+ equip_cuis(player)
+
if(captainless)
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M))
to_chat(M, "Captainship not forced on anyone.")
+/datum/controller/subsystem/ticker/proc/equip_cuis(mob/living/carbon/human/H)
+ if(!H.client)
+ return // If they are spawning without a client (somehow), they *cant* have a CUI list
+ for(var/datum/custom_user_item/cui in H.client.cui_entries)
+ // Skip items with invalid character names
+ if((cui.characer_name != H.real_name) && !cui.all_characters_allowed)
+ continue
+
+ var/ok = FALSE
+
+ if(!cui.all_jobs_allowed)
+ var/alt_blocked = FALSE
+ if(H.mind.role_alt_title)
+ if(!(H.mind.role_alt_title in cui.allowed_jobs))
+ alt_blocked = TRUE
+ if(!(H.mind.assigned_role in cui.allowed_jobs) || alt_blocked)
+ continue
+
+ var/obj/item/I = new cui.object_typepath()
+ var/name_override = cui.item_name_override
+ var/desc_override = cui.item_desc_override
+
+ if(name_override)
+ I.name = name_override
+ if(desc_override)
+ I.desc = desc_override
+
+ if(istype(H.back, /obj/item/storage)) // Try to place it in something on the mob's back
+ var/obj/item/storage/S = H.back
+ if(length(S.contents) < S.storage_slots)
+ I.forceMove(H.back)
+ ok = TRUE
+ to_chat(H, "Your [I.name] has been added to your [H.back.name].")
+
+ if(!ok)
+ for(var/obj/item/storage/S in H.contents) // Try to place it in any item that can store stuff, on the mob.
+ if(length(S.contents) < S.storage_slots)
+ I.forceMove(S)
+ ok = TRUE
+ to_chat(H, "Your [I.name] has been added to your [S.name].")
+ break
+
+ if(!ok) // Finally, since everything else failed, place it on the ground
+ var/turf/T = get_turf(H)
+ if(T)
+ I.forceMove(T)
+ to_chat(H, "Your [I.name] is on the [T.name] below you.")
+ else
+ to_chat(H, "Your [I.name] couldnt spawn anywhere on you or even on the floor below you. Please file a bug report.")
+ qdel(I)
+
+
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
var/m
if(selected_tip)
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 0185aa5931b..a93d7e443c8 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -266,8 +266,12 @@ SUBSYSTEM_DEF(vote)
Click here or type vote to place your vote.
You have [GLOB.configuration.vote.vote_time / 10] seconds to vote."})
switch(vote_type)
- if("crew transfer", "gamemode", "custom", "map")
+ if("crew transfer", "gamemode", "custom")
SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
+ if("map")
+ SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
+ for(var/mob/M in GLOB.player_list)
+ M.throw_alert("Map Vote", /obj/screen/alert/notify_mapvote, timeout_override = GLOB.configuration.vote.vote_time)
if(mode == "gamemode" && SSticker.ticker_going)
SSticker.ticker_going = FALSE
to_chat(world, "Round start has been delayed.")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index be7f82494f9..4ba27fb037d 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -423,6 +423,13 @@
/datum/action/item_action/remove_badge
name = "Remove Holobadge"
+
+// Clown Acrobat Shoes
+/datum/action/item_action/slipping
+ name = "Tactical Slip"
+ desc = "Activates the clown shoes' ankle-stimulating module, allowing the user to do a short slip forward going under anyone."
+ button_icon_state = "clown"
+
// Jump boots
/datum/action/item_action/bhop
name = "Activate Jump Boots"
diff --git a/code/datums/custom_user_item.dm b/code/datums/custom_user_item.dm
new file mode 100644
index 00000000000..9680a990488
--- /dev/null
+++ b/code/datums/custom_user_item.dm
@@ -0,0 +1,66 @@
+/**
+ * # Custom User Item
+ *
+ * Holder for CUIs
+ *
+ * This datum is a holder that is essentially a "model" of the `customuseritems`
+ * database table, and is used for giving people their CUIs on spawn.
+ * It is instanced as part of the client data loading framework on the client.
+ *
+ */
+/datum/custom_user_item
+ /// Can this be used on all characters?
+ var/all_characters_allowed = FALSE
+ /// Name of the character that can have this item.
+ var/characer_name
+ /// Are all jobs allowed?
+ var/all_jobs_allowed = FALSE
+ /// List of allowed jobs
+ var/list/allowed_jobs = list()
+ /// Custom item typepath
+ var/object_typepath
+ /// Custom item name override
+ var/item_name_override
+ /// Custom item description override
+ var/item_desc_override
+ /// Raw job mask
+ var/raw_job_mask
+
+
+/**
+ * CUI Info Parser
+ *
+ * Parses all the raw info into usable stuff, and also does validity checks
+ * Returns TRUE if its a valid item, and FALSE if not
+ *
+ * Arguments:
+ * * owning_ckey - Player who owns this item. Used for logging purposes.
+ */
+/datum/custom_user_item/proc/parse_info(owning_ckey)
+ . = FALSE // Setting this here means it will return false even if it runtimes
+
+ // Sort path
+ if(!object_typepath || !ispath(object_typepath))
+ stack_trace("Incorrect database entry found in table 'customuseritems' path value is [object_typepath ? object_typepath : "(NULL)"], which doesnt exist. Ask the host to look at CUI entries for [owning_ckey]")
+ return
+
+ // Sort job mask
+ if(raw_job_mask == "*")
+ all_jobs_allowed = TRUE
+ else
+ var/list/local_allowed_jobs = splittext(raw_job_mask, ",")
+ for(var/i in 1 to length(local_allowed_jobs))
+ if(istext(local_allowed_jobs[i]))
+ local_allowed_jobs[i] = trim(local_allowed_jobs[i])
+
+ allowed_jobs = local_allowed_jobs
+
+ // Sort character name
+ if(characer_name == "*")
+ all_characters_allowed = TRUE
+
+ return TRUE
+
+
+/datum/custom_user_item/vv_edit_var(var_name, var_value)
+ return FALSE // fuck off
diff --git a/code/datums/http.dm b/code/datums/http.dm
index e7ba1620854..6679b859c65 100644
--- a/code/datums/http.dm
+++ b/code/datums/http.dm
@@ -19,6 +19,8 @@
var/headers
/// URL that the request is being sent to
var/url
+ /// If present, response body will be saved to this file.
+ var/output_file
/// The raw response, which will be decoeded into a [/datum/http_response]
var/_raw_response
/// Callback for executing after async requests. Will be called with an argument of [/datum/http_response] as first argument
@@ -42,7 +44,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
* * _body - The body of the request, if applicable
* * _headers - Associative list of HTTP headers to send, if applicab;e
*/
-/datum/http_request/proc/prepare(_method, _url, _body = "", list/_headers)
+/datum/http_request/proc/prepare(_method, _url, _body = "", list/_headers, _output_file)
if(!length(_headers))
headers = ""
else
@@ -51,6 +53,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
method = _method
url = _url
body = _body
+ output_file = _output_file
/**
* Blocking executor
@@ -60,7 +63,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
*/
/datum/http_request/proc/execute_blocking()
CRASH("Attempted to execute a blocking HTTP request")
- // _raw_response = rustg_http_request_blocking(method, url, body, headers)
+ // _raw_response = rustg_http_request_blocking(method, url, body, headers, build_options())
/**
* Async execution starter
@@ -73,7 +76,7 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
if(in_progress)
CRASH("Attempted to re-use a request object.")
- id = rustg_http_request_async(method, url, body, headers)
+ id = rustg_http_request_async(method, url, body, headers, build_options())
if(isnull(text2num(id)))
_raw_response = "Proc error: [id]"
@@ -81,6 +84,16 @@ THE METHODS IN THIS FILE ARE TO BE USED BY THE SUBSYSTEM AS A MANGEMENT HUB
else
in_progress = TRUE
+/**
+ * Options builder
+ *
+ * Builds options for if we want to download files with SShttp
+ */
+/datum/http_request/proc/build_options()
+ if(output_file)
+ return json_encode(list("output_filename" = output_file, "body_filename" = null))
+ return null
+
/**
* Async completion checker
*
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 5aeb241d26a..68a31f59238 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -1456,6 +1456,12 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containertype = /obj/structure/closet/crate/secure
containername = "shaft miner starter kit"
+/datum/supply_packs/misc/carpet
+ name = "Carpet Crate"
+ cost = 20
+ contains = list(/obj/item/stack/tile/carpet/twenty)
+ containername = "carpet crate"
+
///////////// Paper Work
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index 4e59d8ad6dd..12e2f1f71e1 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -167,18 +167,19 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
//Clown
/datum/uplink_item/jobspecific/clowngrenade
name = "Banana Grenade"
- desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on"
+ desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on."
reference = "BG"
item = /obj/item/grenade/clown_grenade
cost = 5
job = list("Clown")
-/datum/uplink_item/jobspecific/clownmagboots
- name = "Clown Magboots"
- desc = "A pair of modified clown shoes fitted with an advanced magnetic traction system. Look and sound exactly like regular clown shoes unless closely inspected."
- reference = "CM"
- item = /obj/item/clothing/shoes/magboots/clown
+/datum/uplink_item/jobspecific/clownslippers
+ name = "Clown Acrobatic Shoes"
+ desc = "A pair of modified clown shoes fitted with a built-in propulsion system that allows the user to perform a short slip below anyone. Turning on the waddle dampeners removes the slowdown on the shoes."
+ reference = "CAS"
+ item = /obj/item/clothing/shoes/clown_shoes/slippers
cost = 3
+ surplus = 75
job = list("Clown")
/datum/uplink_item/jobspecific/trick_revolver
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 4f0552cbefa..3b07125cb5a 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -786,3 +786,24 @@
/datum/AI_Module/large/cameracrack/upgrade(mob/living/silicon/ai/AI)
if(AI.builtInCamera)
QDEL_NULL(AI.builtInCamera)
+
+/datum/AI_Module/large/engi_upgrade
+ module_name = "Engineering Cyborg Emitter Upgrade"
+ mod_pick_name = "emitter"
+ description = "Downloads firmware that activates the built in emitter in all engineering cyborgs linked to you. Cyborgs built after this upgrade will have it pre-installed."
+ cost = 50 // IDK look into this
+ one_purchase = TRUE
+ upgrade = TRUE
+ unlock_text = "Firmware downloaded. Bugs removed. Built in emitters operating at 73% efficiency."
+ unlock_sound = 'sound/items/rped.ogg'
+
+/datum/AI_Module/large/engi_upgrade/upgrade(mob/living/silicon/ai/AI)
+ AI.purchased_modules += /obj/item/robot_module/engineering
+ log_game("[key_name(usr)] purchased emitters for all engineering cyborgs.")
+ message_admins("[key_name_admin(usr)] purchased emitters for all engineering cyborgs!")
+ for(var/mob/living/silicon/robot/R in AI.connected_robots)
+ if(!istype(R.module, /obj/item/robot_module/engineering))
+ continue
+ R.module.malfhacked = TRUE
+ R.module.rebuild_modules()
+ to_chat(R, "New firmware downloaded. Emitter is now online.")
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 47c41d841fe..9dff5725b81 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -205,6 +205,15 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
/datum/objective/protect/mindslave //subytpe for mindslave implants
+/datum/objective/protect/mindslave/on_target_cryo()
+ if(owner?.current)
+ to_chat(owner.current, "
You notice that your master has entered cryogenic storage, and revert to your normal self, until they return again. You are no longer a mindslave!")
+ SEND_SOUND(owner.current, sound('sound/ambience/alarm4.ogg'))
+ owner.remove_antag_datum(/datum/antagonist/mindslave)
+ SSticker.mode.implanted.Remove(owner)
+ log_admin("[key_name(owner.current)]'s mindslave master has cryo'd, and is no longer a mindslave.")
+ message_admins("[key_name_admin(owner.current)]'s mindslave master has cryo'd, and is no longer a mindslave.") //Since they were on antag hud earlier, this feels important to log
+ qdel(src)
/datum/objective/hijack
martyr_compatible = 0 //Technically you won't get both anyway.
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index 0eef296334d..927b9472ad4 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -343,6 +343,21 @@
playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1)
return TRUE
+/datum/spellbook_entry/summon/slience_ghosts
+ name = "Silence Ghosts"
+ desc = "Tired of people talking behind your back, and spooking you? Why not silence them, and make the dead deader."
+ cost = 2
+ log_name = "SLG"
+ is_ragin_restricted = TRUE //Salt needs to flow here, to be honest
+
+/datum/spellbook_entry/summon/slience_ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
+ new /datum/event/wizard/ghost_mute()
+ active = TRUE
+ to_chat(user, "You have silenced all ghosts!")
+ playsound(get_turf(user), 'sound/effects/ghost.ogg', 50, 1)
+ message_admins("[key_name_admin(usr)] silenced all ghosts as a wizard! (Deadchat is now DISABLED)")
+ return TRUE
+
/datum/spellbook_entry/summon/guns
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first."
diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm
index fa0fb892cae..46ed46f5ae2 100644
--- a/code/game/jobs/job/supervisor.dm
+++ b/code/game/jobs/job/supervisor.dm
@@ -194,16 +194,18 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca
/datum/job/judge
title = "Magistrate"
flag = JOB_JUDGE
- department_flag = JOBCAT_KARMA
+ department_flag = JOBCAT_ENGSEC
total_positions = 1
spawn_positions = 1
supervisors = "the Nanotrasen Supreme Court"
department_head = list("Captain")
selection_color = "#ddddff"
- req_admin_notify = 1
- is_legal = 1
+ req_admin_notify = TRUE
+ is_legal = TRUE
transfer_allowed = FALSE
minimal_player_age = 30
+ exp_requirements = 6000 // 100 hours baby
+ exp_type = EXP_TYPE_SECURITY
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS,
ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS,
ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE,
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 5fd46e11434..c15c59303b7 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -96,8 +96,7 @@ GLOBAL_LIST_INIT(whitelisted_positions, list(
"Blueshield",
"Nanotrasen Representative",
"Barber",
- "Brig Physician",
- "Magistrate"
+ "Brig Physician"
))
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index c4454baf3c7..0c6342592b3 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -322,6 +322,8 @@
if(shock(user, 100))
return
if(!is_operational())
+ if(user.a_intent != INTENT_HELP)
+ return ..()
if(panel_open)
to_chat(usr, "Close the maintenance panel first.")
else
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 720b164e7d6..e0c9f4ee18c 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -88,6 +88,70 @@
/obj/item/stack/tile/carpet/black/twenty
amount = 20
+/obj/item/stack/tile/carpet/blue
+ name = "blue carpet"
+ icon_state = "tile-carpet-blue"
+ turf_type = /turf/simulated/floor/carpet/blue
+
+/obj/item/stack/tile/carpet/blue/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/cyan
+ name = "cyan carpet"
+ icon_state = "tile-carpet-cyan"
+ turf_type = /turf/simulated/floor/carpet/cyan
+
+/obj/item/stack/tile/carpet/cyan/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/green
+ name = "green carpet"
+ icon_state = "tile-carpet-green"
+ turf_type = /turf/simulated/floor/carpet/green
+
+/obj/item/stack/tile/carpet/green/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/orange
+ name = "orange carpet"
+ icon_state = "tile-carpet-orange"
+ turf_type = /turf/simulated/floor/carpet/orange
+
+/obj/item/stack/tile/carpet/orange/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/purple
+ name = "purple carpet"
+ icon_state = "tile-carpet-purple"
+ turf_type = /turf/simulated/floor/carpet/purple
+
+/obj/item/stack/tile/carpet/purple/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/red
+ name = "red carpet"
+ icon_state = "tile-carpet-red"
+ turf_type = /turf/simulated/floor/carpet/red
+
+/obj/item/stack/tile/carpet/red/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/royalblack
+ name = "royal black carpet"
+ icon_state = "tile-carpet-royalblack"
+ turf_type = /turf/simulated/floor/carpet/royalblack
+
+/obj/item/stack/tile/carpet/royalblack/twenty
+ amount = 20
+
+/obj/item/stack/tile/carpet/royalblue
+ name = "royal blue carpet"
+ icon_state = "tile-carpet-royalblue"
+ turf_type = /turf/simulated/floor/carpet/royalblue
+
+/obj/item/stack/tile/carpet/royalblue/twenty
+ amount = 20
+
//Plasteel
/obj/item/stack/tile/plasteel
name = "floor tiles"
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index c89fd8aeb0d..0894c00cf2e 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -371,6 +371,7 @@
icon = 'icons/obj/food/containers.dmi'
icon_state = "tray"
desc = "A metal tray to lay food on."
+ storage_slots = 8
force = 5
throwforce = 10
throw_speed = 3
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index 0b8938fc204..86a1e119dad 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -68,6 +68,9 @@
playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1)
make_plating()
+
+//Carpets
+
/turf/simulated/floor/carpet
name = "carpet"
icon = 'icons/turf/floors/carpet.dmi'
@@ -112,6 +115,48 @@
floor_tile = /obj/item/stack/tile/carpet/black
canSmoothWith = list(/turf/simulated/floor/carpet/black)
+/turf/simulated/floor/carpet/blue
+ icon = 'icons/turf/floors/carpet_blue.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/blue
+ canSmoothWith = list(/turf/simulated/floor/carpet/blue)
+
+/turf/simulated/floor/carpet/cyan
+ icon = 'icons/turf/floors/carpet_cyan.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/cyan
+ canSmoothWith = list(/turf/simulated/floor/carpet/cyan)
+
+/turf/simulated/floor/carpet/green
+ icon = 'icons/turf/floors/carpet_green.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/green
+ canSmoothWith = list(/turf/simulated/floor/carpet/green)
+
+/turf/simulated/floor/carpet/orange
+ icon = 'icons/turf/floors/carpet_orange.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/orange
+ canSmoothWith = list(/turf/simulated/floor/carpet/orange)
+
+/turf/simulated/floor/carpet/purple
+ icon = 'icons/turf/floors/carpet_purple.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/purple
+ canSmoothWith = list(/turf/simulated/floor/carpet/purple)
+
+/turf/simulated/floor/carpet/red
+ icon = 'icons/turf/floors/carpet_red.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/red
+ canSmoothWith = list(/turf/simulated/floor/carpet/red)
+
+/turf/simulated/floor/carpet/royalblack
+ icon = 'icons/turf/floors/carpet_royalblack.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/royalblack
+ canSmoothWith = list(/turf/simulated/floor/carpet/royalblack)
+
+/turf/simulated/floor/carpet/royalblue
+ icon = 'icons/turf/floors/carpet_royalblue.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/royalblue
+ canSmoothWith = list(/turf/simulated/floor/carpet/royalblue)
+
+//End of carpets
+
/turf/simulated/floor/fakespace
icon = 'icons/turf/space.dmi'
icon_state = "0"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index a78f3219e47..c4b14311986 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -706,8 +706,9 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
if(istext(flags))
flags = text2num(flags)
+ var/client/check_client = GLOB.directory[ckey]
// Do a little check here
- if(GLOB.configuration.system.is_production && (flags & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
+ if(GLOB.configuration.system.is_production && (flags & R_ADMIN) && check_client.prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
to_chat(src,"You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.") // Very fucking obvious
qdel(admin_read)
return
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index f68e20b1903..7c011962c08 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -117,10 +117,16 @@
/// Client's pAI save
var/datum/pai_save/pai_save
+ /// List of the clients CUIs
+ var/list/datum/custom_user_item/cui_entries = list()
+
/client/vv_edit_var(var_name, var_value)
switch(var_name)
// I know we will never be in a world where admins are editing client vars to let people bypass TOS
// But guess what, if I have the ability to overengineer something, I am going to do it
if("tos_consent")
return FALSE
+ // Dont fuck with this
+ if("cui_entries")
+ return FALSE
return ..()
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index fa5ae0b4d37..6f8d489baf6 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -149,8 +149,6 @@
karma_purchase(karma,30,"job","Nanotrasen Representative")
if("4")
karma_purchase(karma,30,"job","Blueshield")
- if("5")
- karma_purchase(karma,45,"job","Magistrate")
return
if(href_list["KarmaBuy2"])
var/karma=verify_karma()
diff --git a/code/modules/client/login_processing/45-cuis.dm b/code/modules/client/login_processing/45-cuis.dm
new file mode 100644
index 00000000000..88bfca44638
--- /dev/null
+++ b/code/modules/client/login_processing/45-cuis.dm
@@ -0,0 +1,20 @@
+/datum/client_login_processor/cuis
+ priority = 45
+
+/datum/client_login_processor/cuis/get_query(client/C)
+ var/datum/db_query/query = SSdbcore.NewQuery("SELECT cuiRealName, cuiPath, cuiItemName, cuiDescription, cuiJobMask FROM customuseritems WHERE cuiCKey=:ckey", list(
+ "ckey" = C.ckey
+ ))
+ return query
+
+/datum/client_login_processor/cuis/process_result(datum/db_query/Q, client/C)
+ while(Q.NextRow())
+ var/datum/custom_user_item/cui = new()
+ cui.characer_name = Q.item[1]
+ cui.object_typepath = text2path(Q.item[2])
+ cui.item_name_override = Q.item[3]
+ cui.item_desc_override = Q.item[4]
+ cui.raw_job_mask = Q.item[5]
+
+ if(cui.parse_info(C.ckey))
+ C.cui_entries += cui
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 329bea01f9d..4c23bbdff38 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -96,6 +96,9 @@
if(user.get_active_hand() != src)
to_chat(user, "You must hold [src] in your hand to do this.")
return
+ toggle_waddle(user)
+
+/obj/item/clothing/shoes/clown_shoes/proc/toggle_waddle(mob/living/user)
if(!enabled_waddle)
to_chat(user, "You switch off the waddle dampeners!")
enabled_waddle = TRUE
@@ -111,6 +114,45 @@
desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow."
magical = TRUE
+/obj/item/clothing/shoes/clown_shoes/slippers
+ actions_types = list(/datum/action/item_action/slipping)
+ var/slide_distance = 6
+ var/recharging_rate = 8 SECONDS
+ var/recharging_time = 0
+
+/obj/item/clothing/shoes/clown_shoes/slippers/item_action_slot_check(slot, mob/user)
+ if(slot == slot_shoes)
+ return TRUE
+
+/obj/item/clothing/shoes/clown_shoes/slippers/ui_action_click(mob/user, action)
+ if(recharging_time > world.time)
+ to_chat(user, "The boot's internal propulsion needs to recharge still!")
+ return
+ var/prev_dir = user.dir
+ var/prev_pass_flags = user.pass_flags
+ user.pass_flags |= PASSMOB
+ user.Weaken(2)
+ user.dir = prev_dir
+ playsound(src, 'sound/effects/stealthoff.ogg', 50, TRUE, 1)
+ recharging_time = world.time + recharging_rate
+ user.visible_message("[user] slips forward!")
+ for(var/i in 1 to slide_distance)
+ step(user, user.dir)
+ sleep(1)
+ user.SetWeakened(0)
+ user.pass_flags = prev_pass_flags
+
+
+/obj/item/clothing/shoes/clown_shoes/slippers/toggle_waddle(mob/living/user)
+ if(!enabled_waddle)
+ to_chat(user, "You switch off the waddle dampeners!")
+ enabled_waddle = TRUE
+ slowdown = initial(slowdown)
+ else
+ to_chat(user, "You switch on the waddle dampeners, [src] no longer slow you down!")
+ enabled_waddle = FALSE
+ slowdown = SHOES_SLOWDOWN
+
/obj/item/clothing/shoes/jackboots
name = "jackboots"
desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time."
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index 25ac77e90f8..e63e785edbb 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -373,7 +373,71 @@
result = list(/obj/item/stack/tile/carpet/black)
time = 20
reqs = list(/obj/item/stack/tile/carpet = 1)
- pathtools = list(/obj/item/toy/crayon)
+ pathtools = list(/obj/item/toy/crayon/black)
+ category = CAT_MISC
+
+/datum/crafting_recipe/bluecarpet
+ name = "Black Carpet"
+ result = list(/obj/item/stack/tile/carpet/blue)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/blue)
+ category = CAT_MISC
+
+/datum/crafting_recipe/cyancarpet
+ name = "Cyan Carpet"
+ result = list(/obj/item/stack/tile/carpet/blue)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/blue, /obj/item/toy/crayon/green)
+ category = CAT_MISC
+
+/datum/crafting_recipe/greencarpet
+ name = "Green Carpet"
+ result = list(/obj/item/stack/tile/carpet/green)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/green)
+ category = CAT_MISC
+
+/datum/crafting_recipe/orangecarpet
+ name = "Orange Carpet"
+ result = list(/obj/item/stack/tile/carpet/orange)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/yellow, /obj/item/toy/crayon/red)
+ category = CAT_MISC
+
+/datum/crafting_recipe/purplecarpet
+ name = "Purple Carpet"
+ result = list(/obj/item/stack/tile/carpet/purple)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/red, /obj/item/toy/crayon/blue)
+ category = CAT_MISC
+
+/datum/crafting_recipe/redcarpet
+ name = "Red Carpet"
+ result = list(/obj/item/stack/tile/carpet/purple)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet = 1)
+ pathtools = list(/obj/item/toy/crayon/red)
+ category = CAT_MISC
+
+/datum/crafting_recipe/royalblackcarpet
+ name = "Royal Black Carpet"
+ result = list(/obj/item/stack/tile/carpet/royalblack = 10)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet/black = 10,
+ /obj/item/stack/sheet/mineral/gold = 2)
+ category = CAT_MISC
+
+/datum/crafting_recipe/royalbluecarpet
+ name = "Royal Blue Carpet"
+ result = list(/obj/item/stack/tile/carpet/royalblue = 10)
+ time = 20
+ reqs = list(/obj/item/stack/tile/carpet/blue = 10,
+ /obj/item/stack/sheet/mineral/gold = 2)
category = CAT_MISC
/datum/crafting_recipe/showercurtain
diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm
deleted file mode 100644
index 5f998861cf6..00000000000
--- a/code/modules/customitems/item_spawning.dm
+++ /dev/null
@@ -1,101 +0,0 @@
-/proc/EquipCustomItems(mob/living/carbon/human/M)
- if(!SSdbcore.IsConnected())
- return
-
- // Grab the info we want.
- var/datum/db_query/query = SSdbcore.NewQuery({"
- SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM customuseritems
- WHERE cuiCKey=:ckey AND (cuiRealName=:realname OR cuiRealName='*')"}, list(
- "ckey" = M.ckey,
- "realname" = M.real_name
- ))
- if(!query.warn_execute(async = FALSE)) // Dont make this async. Youll make roundstart slow. Trust me.
- qdel(query)
- return
-
- while(query.NextRow())
- var/path = text2path(query.item[1])
- var/propadjust = query.item[2]
- var/jobmask = query.item[3]
- var/ok = 0
- if(!path || !ispath(path))
- log_debug("Incorrect database entry found in table 'customuseritems' path value = [path], cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[M.real_name]' OR cuiRealName='*'")
- continue
- if(jobmask != "*")
- var/list/allowed_jobs = splittext(jobmask,",")
- for(var/i = 1, i <= allowed_jobs.len, i++)
- if(istext(allowed_jobs[i]))
- allowed_jobs[i] = trim(allowed_jobs[i])
- var/alt_blocked = 0
- if(M.mind.role_alt_title)
- if(!(M.mind.role_alt_title in allowed_jobs))
- alt_blocked = 1
- if(!(M.mind.assigned_role in allowed_jobs) || alt_blocked)
- continue
-
- var/obj/item/Item = new path()
- var/description = query.item[4]
- var/newname = query.item[5]
- if(istype(Item,/obj/item/card/id))
- var/obj/item/card/id/I = Item
- for(var/obj/item/card/id/C in M)
- //default settings
- I.name = "[M.real_name]'s ID Card ([M.mind.role_alt_title ? M.mind.role_alt_title : M.mind.assigned_role])"
- I.registered_name = M.real_name
- I.access = C.access
- I.assignment = C.assignment
- I.blood_type = C.blood_type
- I.dna_hash = C.dna_hash
- I.fingerprint_hash = C.fingerprint_hash
- qdel(C)
- ok = M.equip_or_collect(I, slot_wear_id, 0) //if 1, last argument deletes on fail
- break
- else if(istype(M.back, /obj/item/storage)) // Try to place it in something on the mob's back
- var/obj/item/storage/S = M.back
- if(S.contents.len < S.storage_slots)
- Item.loc = M.back
- ok = 1
- to_chat(M, "Your [Item.name] has been added to your [M.back.name].")
- if(ok == 0)
- for(var/obj/item/storage/S in M.contents) // Try to place it in any item that can store stuff, on the mob.
- if(S.contents.len < S.storage_slots)
- Item.loc = S
- ok = 1
- to_chat(M, "Your [Item.name] has been added to your [S.name].")
- break
- if(description)
- Item.desc = description
- if(newname)
- Item.name = newname
-
- if(ok == 0) // Finally, since everything else failed, place it on the ground
- Item.loc = get_turf(M.loc)
-
- HackProperties(Item,propadjust)
- M.regenerate_icons()
- qdel(query)
-
-// This is hacky, but since it's difficult as fuck to make a proper parser in BYOND without killing the server, here it is. - N3X
-/proc/HackProperties(mob/living/carbon/human/M, obj/item/I, script)
- var/list/statements = splittext(script,";")
- if(statements.len == 0)
- return
- for(var/statement in statements)
- var/list/assignmentChunks = splittext(statement,"=")
- var/varname = assignmentChunks[1]
- var/list/typeChunks=splittext(script,":")
- var/desiredType=typeChunks[1]
- switch(desiredType)
- if("string")
- var/output = typeChunks[2]
- output = replacetext(output,"{REALNAME}", M.real_name)
- output = replacetext(output,"{ROLE}", M.mind.assigned_role)
- output = replacetext(output,"{ROLE_ALT}", "[M.mind.role_alt_title ? M.mind.role_alt_title : M.mind.assigned_role]")
- I.vars[varname]=output
- if("number")
- I.vars[varname]=text2num(typeChunks[2])
- if("icon")
- if(typeChunks.len==2)
- I.vars[varname]=new /icon(typeChunks[2])
- if(typeChunks.len==3)
- I.vars[varname]=new /icon(typeChunks[2],typeChunks[3])
diff --git a/code/modules/events/wizard/ghost.dm b/code/modules/events/wizard/ghost.dm
index 11529e3080d..e703799ff02 100644
--- a/code/modules/events/wizard/ghost.dm
+++ b/code/modules/events/wizard/ghost.dm
@@ -3,3 +3,12 @@
/datum/event/wizard/ghost/start()
var/msg = "You suddenly feel extremely obvious..."
set_observer_default_invisibility(0, msg)
+
+/datum/event/wizard/ghost_mute //The spook is silent
+
+/datum/event/wizard/ghost_mute/start()
+ GLOB.dsay_enabled = FALSE
+ var/sound/S = sound('sound/hallucinations/wail.ogg')
+ for(var/mob/dead/observer/silenced in GLOB.player_list)
+ to_chat(silenced, "Magical forces wrap around your spectral form. You can no longer speak to other ghosts!")
+ SEND_SOUND(silenced, S)
diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm
index 87e17efd10b..599eef5b2f0 100644
--- a/code/modules/games/cards.dm
+++ b/code/modules/games/cards.dm
@@ -200,11 +200,12 @@
deckshuffle()
/obj/item/deck/verb/verb_shuffle()
- set category = "Object"
- set name = "Shuffle"
- set desc = "Shuffle the cards in the deck."
- set src in view(1)
- deckshuffle()
+ if(!isobserver(usr))
+ set category = "Object"
+ set name = "Shuffle"
+ set desc = "Shuffle the cards in the deck."
+ set src in view(1)
+ deckshuffle()
/obj/item/deck/proc/deckshuffle()
var/mob/living/user = usr
diff --git a/code/modules/karma/karma.dm b/code/modules/karma/karma.dm
index f684a8b261c..14878faf54a 100644
--- a/code/modules/karma/karma.dm
+++ b/code/modules/karma/karma.dm
@@ -275,10 +275,6 @@ GLOBAL_LIST_EMPTY(karma_spenders)
dat += "Unlock Blueshield -- 30KP
"
else
dat += "Blueshield - Unlocked
"
- if(!("Magistrate" in joblist))
- dat += "Unlock Magistrate -- 45KP
"
- else
- dat+= "Magistrate - Unlocked
"
if(1) // Species Unlocks
if(!("Machine" in specieslist))
@@ -349,6 +345,9 @@ GLOBAL_LIST_EMPTY(karma_spenders)
if("Security Pod Pilot" in purchased)
refundable += "Security Pod Pilot"
dat += "Refund Security Pod Pilot -- 30KP
"
+ if("Magistrate" in purchased)
+ refundable += "Magistrate"
+ dat += "Refund Magistrate -- 45KP
"
if(!refundable.len)
dat += "You do not have any refundable karma purchases.
"
@@ -515,6 +514,8 @@ GLOBAL_LIST_EMPTY(karma_spenders)
cost = 30
if("Nanotrasen Recruiter")
cost = 10
+ if("Magistrate")
+ cost = 45
else
to_chat(usr, "That job is not refundable.")
return
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 47c383e1a33..943f894b151 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -79,6 +79,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
var/obj/machinery/power/apc/malfhack = null
var/explosive = 0 //does the AI explode when it dies?
+ /// List of modules the AI has purchased malf upgrades for.
+ var/list/purchased_modules = list()
+
var/mob/living/silicon/ai/parent = null
var/camera_light_on = 0
var/list/obj/machinery/camera/lit_cameras = list()
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 677f67ebf13..768bbbc65f9 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -26,7 +26,8 @@
"Parrot" = "parrot",
"Box Bot" = "boxbot",
"Spider Bot" = "spiderbot",
- "Fairy" = "fairy"
+ "Fairy" = "fairy",
+ "Snake" = "snake"
)
var/global/list/possible_say_verbs = list(
@@ -35,7 +36,8 @@
"Beep" = list("beeps","beeps loudly","boops"),
"Chirp" = list("chirps","chirrups","cheeps"),
"Feline" = list("purrs","yowls","meows"),
- "Canine" = list("yaps","barks","growls")
+ "Canine" = list("yaps","barks","growls"),
+ "Hiss" = list("hisses","hisses","hisses")
)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 8affd317c6a..60d13a88ce5 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1264,6 +1264,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
connected_ai = AI
connected_ai.connected_robots |= src
notify_ai(1)
+ if(module)
+ module.rebuild_modules() //This way, if a borg gets linked to a malf AI that has upgrades, they get their upgrades.
sync()
/mob/living/silicon/robot/adjustOxyLoss(amount)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index ab7c4ff9562..3c202a35029 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -6,12 +6,17 @@
item_state = "electronic"
flags = CONDUCT
+ /// Has the AI hacked the borg module, allowing access to the malf AI exclusive item.
+ var/malfhacked = FALSE
+
/// A list of all currently usable and created modules the robot currently has access too.
var/list/modules = list()
/// A list of module-specific, non-emag modules the borg will gain when this module is chosen.
var/list/basic_modules = list()
/// A list of modules the robot gets when emagged.
var/list/emag_modules = list()
+ /// A list of modules that the robot gets when malf AI buys it.
+ var/list/malf_modules = list()
/// A list of modules that require special recharge handling. Examples include things like flashes, sprays and welding tools.
var/list/special_rechargables = list()
/// A list of all "energy stacks", i.e metal, glass, brute kits, splints, etc.
@@ -43,12 +48,17 @@
emag_modules += I
emag_modules -= i
+ for(var/i in malf_modules)
+ var/obj/item/I = new i(src)
+ malf_modules += I
+ malf_modules -= i
+
// Flashes need a special recharge, and since basically every module uses it, add it here.
// Even if the module doesn't use a flash, it wont cause any issues to have it in this list.
special_rechargables += /obj/item/flash/cyborg
// This is done so we can loop through this list later and call cyborg_recharge() on the items while the borg is recharging.
- var/all_modules = basic_modules | emag_modules
+ var/all_modules = basic_modules | emag_modules | malf_modules
for(var/path in special_rechargables)
var/obj/item/I = locate(path) in all_modules
if(I) // If it exists, add the object reference.
@@ -70,6 +80,7 @@
QDEL_LIST(modules)
QDEL_LIST(basic_modules)
QDEL_LIST(emag_modules)
+ QDEL_LIST(malf_modules)
QDEL_LIST(storages)
QDEL_LIST(special_rechargables)
return ..()
@@ -88,6 +99,7 @@
var/list/lists = list(
basic_modules,
emag_modules,
+ malf_modules,
storages,
special_rechargables
)
@@ -155,13 +167,17 @@
return I
/**
- * Builds the usable module list from the modules we have in `basic_modules` and `emag_modules`
+ * Builds the usable module list from the modules we have in `basic_modules`, `emag_modules` and `malf_modules`
*/
/obj/item/robot_module/proc/rebuild_modules()
var/mob/living/silicon/robot/R = loc
R.uneq_all()
modules = list()
+ if(!malfhacked && R.connected_ai)
+ if(type in R.connected_ai.purchased_modules)
+ malfhacked = TRUE
+
// By this point these lists should only contain items. It's safe to use typeless loops here.
for(var/item in basic_modules)
add_module(item, FALSE)
@@ -170,6 +186,10 @@
for(var/item in emag_modules)
add_module(item, FALSE)
+ if(malfhacked)
+ for(var/item in malf_modules)
+ add_module(item, FALSE)
+
if(R.hud_used)
R.hud_used.update_robot_modules_display()
@@ -354,8 +374,9 @@
/obj/item/stack/sheet/glass/cyborg,
/obj/item/stack/sheet/rglass/cyborg
)
- emag_modules = list(/obj/item/borg/stun)
- special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg)
+ emag_modules = list(/obj/item/borg/stun, /obj/item/restraints/handcuffs/cable/zipties/cyborg)
+ malf_modules = list(/obj/item/gun/energy/emitter/cyborg)
+ special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg, /obj/item/gun/energy/emitter/cyborg)
/obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed)
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
@@ -400,7 +421,7 @@
/obj/item/holosign_creator,
/obj/item/extinguisher/mini
)
- emag_modules = list(/obj/item/reagent_containers/spray/cyborg_lube)
+ emag_modules = list(/obj/item/reagent_containers/spray/cyborg_lube, /obj/item/restraints/handcuffs/cable/zipties/cyborg)
special_rechargables = list(
/obj/item/lightreplacer,
/obj/item/reagent_containers/spray/cyborg_lube,
@@ -471,7 +492,7 @@
/obj/item/storage/bag/tray/cyborg,
/obj/item/reagent_containers/food/drinks/shaker
)
- emag_modules = list(/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer)
+ emag_modules = list(/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer, /obj/item/restraints/handcuffs/cable/zipties/cyborg)
special_rechargables = list(
/obj/item/reagent_containers/food/condiment/enzyme,
/obj/item/reagent_containers/food/drinks/cans/beer/sleepy_beer
@@ -530,7 +551,7 @@
/obj/item/gun/energy/kinetic_accelerator/cyborg,
/obj/item/gps/cyborg
)
- emag_modules = list(/obj/item/borg/stun, /obj/item/pickaxe/drill/cyborg/diamond)
+ emag_modules = list(/obj/item/borg/stun, /obj/item/pickaxe/drill/cyborg/diamond, /obj/item/restraints/handcuffs/cable/zipties/cyborg)
special_rechargables = list(/obj/item/extinguisher/mini, /obj/item/weldingtool/mini)
// Replace their normal drill with a diamond drill.
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 04aca83e512..9370829c746 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -359,7 +359,7 @@
character.buckled.dir = character.dir
character = SSjobs.EquipRank(character, rank, 1) //equips the human
- EquipCustomItems(character)
+ SSticker.equip_cuis(character) // Gives them their CUIs
SSticker.mode.latespawn(character)
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 25c83556882..dd5f68eb739 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -140,6 +140,7 @@
var/on = 0
var/brightness_on = 2
light_color = LIGHT_COLOR_RED
+ armour_penetration = 20
/obj/item/pen/edagger/attack_self(mob/living/user)
if(on)
diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm
index 18ed273ff23..049bf13c174 100644
--- a/code/modules/projectiles/ammunition/energy.dm
+++ b/code/modules/projectiles/ammunition/energy.dm
@@ -271,6 +271,17 @@
delay = 50
select_name = "snipe"
+/obj/item/ammo_casing/energy/emitter
+ projectile_type = /obj/item/projectile/beam/emitter
+ muzzle_flash_color = LIGHT_COLOR_GREEN
+ fire_sound = 'sound/weapons/emitter.ogg'
+ e_cost = 100
+ delay = 2 SECONDS // Lasers fire twice every second for 40 dps, this fires every 2 seconds for 15 dps. Seems fair, since every cyborg will have this with more shots?
+ select_name = "emitter"
+
+/obj/item/ammo_casing/energy/emitter/cyborg
+ e_cost = 500 // about 28 shots on an engineering borg from a borging machine, assuming some power is used for lights / movement. May need to change.
+
/obj/item/ammo_casing/energy/bsg
projectile_type = /obj/item/projectile/energy/bsg
muzzle_flash_color = LIGHT_COLOR_DARKBLUE
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index b205dc44797..9d7b9bde281 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -141,6 +141,29 @@
name = "cyborg immolator cannon"
ammo_type = list(/obj/item/ammo_casing/energy/immolator/scatter/cyborg, /obj/item/ammo_casing/energy/immolator/strong/cyborg) // scatter is default, because it is more useful
+/obj/item/gun/energy/emitter
+ name = "mobile emitter"
+ desc = "An emitter removed from its base, and attached to a laser cannon frame."
+ icon_state = "emittercannon"
+ item_state = "laser"
+ w_class = WEIGHT_CLASS_BULKY
+ shaded_charge = TRUE
+ can_holster = FALSE
+ origin_tech = "combat=4;magnets=4;powerstorage=3"
+ ammo_type = list(/obj/item/ammo_casing/energy/emitter)
+ ammo_x_offset = 3
+
+/obj/item/gun/energy/emitter/cyborg
+ name = "mounted emitter"
+ desc = "An emitter built into to your cyborg frame, draining charge from your cell."
+ ammo_type = list(/obj/item/ammo_casing/energy/emitter/cyborg)
+
+/obj/item/gun/energy/emitter/cyborg/newshot()
+ ..()
+ robocharge()
+
+/obj/item/gun/energy/emitter/cyborg/emp_act()
+ return
////////Laser Tag////////////////////
diff --git a/icons/mob/pai.dmi b/icons/mob/pai.dmi
index 6d5a2003b8c..e76b13409d2 100644
Binary files a/icons/mob/pai.dmi and b/icons/mob/pai.dmi differ
diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi
index 9ef222c0bff..3354c42a5b7 100644
Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index b4f2c2204ab..f2a1917a06e 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ
diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi
index 027f074a790..d7712e6ccb9 100644
Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ
diff --git a/icons/turf/floors/carpet_blue.dmi b/icons/turf/floors/carpet_blue.dmi
new file mode 100644
index 00000000000..cd3e7f01aa8
Binary files /dev/null and b/icons/turf/floors/carpet_blue.dmi differ
diff --git a/icons/turf/floors/carpet_cyan.dmi b/icons/turf/floors/carpet_cyan.dmi
new file mode 100644
index 00000000000..a1fbb6f9487
Binary files /dev/null and b/icons/turf/floors/carpet_cyan.dmi differ
diff --git a/icons/turf/floors/carpet_green.dmi b/icons/turf/floors/carpet_green.dmi
new file mode 100644
index 00000000000..bd945c45e17
Binary files /dev/null and b/icons/turf/floors/carpet_green.dmi differ
diff --git a/icons/turf/floors/carpet_orange.dmi b/icons/turf/floors/carpet_orange.dmi
new file mode 100644
index 00000000000..316dd135e22
Binary files /dev/null and b/icons/turf/floors/carpet_orange.dmi differ
diff --git a/icons/turf/floors/carpet_purple.dmi b/icons/turf/floors/carpet_purple.dmi
new file mode 100644
index 00000000000..8cc886139cc
Binary files /dev/null and b/icons/turf/floors/carpet_purple.dmi differ
diff --git a/icons/turf/floors/carpet_red.dmi b/icons/turf/floors/carpet_red.dmi
new file mode 100644
index 00000000000..2327b49a017
Binary files /dev/null and b/icons/turf/floors/carpet_red.dmi differ
diff --git a/icons/turf/floors/carpet_royalblack.dmi b/icons/turf/floors/carpet_royalblack.dmi
new file mode 100644
index 00000000000..77418dc06f8
Binary files /dev/null and b/icons/turf/floors/carpet_royalblack.dmi differ
diff --git a/icons/turf/floors/carpet_royalblue.dmi b/icons/turf/floors/carpet_royalblue.dmi
new file mode 100644
index 00000000000..bc2014c9ac6
Binary files /dev/null and b/icons/turf/floors/carpet_royalblue.dmi differ
diff --git a/librust_g.so b/librust_g.so
index 1f059e0b2ce..63d3e053d9e 100644
Binary files a/librust_g.so and b/librust_g.so differ
diff --git a/paradise.dme b/paradise.dme
index 5488d42c65c..b7db391ce27 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -23,6 +23,7 @@
#include "code\__DEFINES\_spacemandmm.dm"
#include "code\__DEFINES\_tgs_defines.dm"
#include "code\__DEFINES\_tick.dm"
+#include "code\__DEFINES\_versions.dm"
#include "code\__DEFINES\access.dm"
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\antagonists.dm"
@@ -278,6 +279,7 @@
#include "code\datums\callback.dm"
#include "code\datums\chatmessage.dm"
#include "code\datums\click_intercept.dm"
+#include "code\datums\custom_user_item.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datum.dm"
#include "code\datums\datumvars.dm"
@@ -1390,6 +1392,7 @@
#include "code\modules\client\login_processing\38-alts_cid.dm"
#include "code\modules\client\login_processing\39-cid_count.dm"
#include "code\modules\client\login_processing\40-pai_save.dm"
+#include "code\modules\client\login_processing\45-cuis.dm"
#include "code\modules\client\login_processing\__client_login_processor.dm"
#include "code\modules\client\preference\character.dm"
#include "code\modules\client\preference\link_processing.dm"
@@ -1484,7 +1487,6 @@
#include "code\modules\crafting\recipes.dm"
#include "code\modules\crafting\tailoring.dm"
#include "code\modules\customitems\item_defines.dm"
-#include "code\modules\customitems\item_spawning.dm"
#include "code\modules\detective_work\detective_work.dm"
#include "code\modules\detective_work\evidence.dm"
#include "code\modules\detective_work\footprints_and_rag.dm"
diff --git a/rust_g.dll b/rust_g.dll
index ed4ac5bc38b..a4b6f9074bd 100644
Binary files a/rust_g.dll and b/rust_g.dll differ
diff --git a/tools/ci/validate_rustg_windows.py b/tools/ci/validate_rustg_windows.py
index 787bafb9a60..1c3c883ae5e 100644
--- a/tools/ci/validate_rustg_windows.py
+++ b/tools/ci/validate_rustg_windows.py
@@ -37,7 +37,7 @@ else:
fail("RUSTG DM header file does NOT exist")
# Parse the version from the DM file
-f = open("code/__DEFINES/rust_g.dm", "r")
+f = open("code/__DEFINES/_versions.dm", "r")
lines = f.readlines()
f.close()
dm_version = None
@@ -92,16 +92,16 @@ else:
# Make sure we can parse TOML
string_array = c_char_p * 1
sa = string_array(bytes(ci_toml_file_location, "ascii"))
-rustg_dll.toml2json.argtypes = [c_int, c_char_p * 1]
+rustg_dll.toml_file_to_json.argtypes = [c_int, c_char_p * 1]
# Set args for JSON retrieval
-rustg_dll.toml2json.restype = c_char_p
+rustg_dll.toml_file_to_json.restype = c_char_p
try:
# Run it
- output_json = rustg_dll.toml2json(1, sa).decode()
+ output_json = rustg_dll.toml_file_to_json(1, sa).decode()
json.loads(output_json)
- success("toml2json conversion successful")
+ success("toml_file_to_json successful")
except Exception:
- fail("Failed to convert toml2json")
+ fail("Failed to run toml_file_to_json")
exit(0) # Success