diff --git a/.gitignore b/.gitignore index c30813913fb..6213071d42a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ #Ignore byond config folder. /cfg/**/* +config/starmap/starmap.json # Ignore compiled linux libs in the root folder, e.g. librust_g.so /*.so diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm index e82dee47717..c6534a8a88d 100644 --- a/code/__DEFINES/rust_g.dm +++ b/code/__DEFINES/rust_g.dm @@ -38,6 +38,59 @@ #define RUST_G (__rust_g || __detect_rust_g()) #endif +// Handle 515 call() -> call_ext() changes +#if DM_VERSION >= 515 +#define RUSTG_CALL call_ext +#else +#define RUSTG_CALL call +#endif + +/// Gets the version of rust_g +/proc/rustg_get_version() return RUSTG_CALL(RUST_G, "get_version")() + + +/** + * Sets up the Aho-Corasick automaton with its default options. + * + * The search patterns list and the replacements must be of the same length when replace is run, but an empty replacements list is allowed if replacements are supplied with the replace call + * Arguments: + * * key - The key for the automaton, to be used with subsequent rustg_acreplace/rustg_acreplace_with_replacements calls + * * patterns - A non-associative list of strings to search for + * * replacements - Default replacements for this automaton, used with rustg_acreplace + */ +#define rustg_setup_acreplace(key, patterns, replacements) RUSTG_CALL(RUST_G, "setup_acreplace")(key, json_encode(patterns), json_encode(replacements)) + +/** + * Sets up the Aho-Corasick automaton using supplied options. + * + * The search patterns list and the replacements must be of the same length when replace is run, but an empty replacements list is allowed if replacements are supplied with the replace call + * Arguments: + * * key - The key for the automaton, to be used with subsequent rustg_acreplace/rustg_acreplace_with_replacements calls + * * options - An associative list like list("anchored" = 0, "ascii_case_insensitive" = 0, "match_kind" = "Standard"). The values shown on the example are the defaults, and default values may be omitted. See the identically named methods at https://docs.rs/aho-corasick/latest/aho_corasick/struct.AhoCorasickBuilder.html to see what the options do. + * * patterns - A non-associative list of strings to search for + * * replacements - Default replacements for this automaton, used with rustg_acreplace + */ +#define rustg_setup_acreplace_with_options(key, options, patterns, replacements) RUSTG_CALL(RUST_G, "setup_acreplace")(key, json_encode(options), json_encode(patterns), json_encode(replacements)) + +/** + * Run the specified replacement engine with the provided haystack text to replace, returning replaced text. + * + * Arguments: + * * key - The key for the automaton + * * text - Text to run replacements on + */ +#define rustg_acreplace(key, text) RUSTG_CALL(RUST_G, "acreplace")(key, text) + +/** + * Run the specified replacement engine with the provided haystack text to replace, returning replaced text. + * + * Arguments: + * * key - The key for the automaton + * * text - Text to run replacements on + * * replacements - Replacements for this call. Must be the same length as the set-up patterns + */ +#define rustg_acreplace_with_replacements(key, text, replacements) RUSTG_CALL(RUST_G, "acreplace_with_replacements")(key, text, json_encode(replacements)) + /** * This proc generates a cellular automata noise grid which can be used in procedural generation methods. * @@ -52,35 +105,58 @@ * * height: The height of the grid. */ #define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \ - LIBCALL(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height) + RUSTG_CALL(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height) + +/** + * This proc generates a grid of perlin-like noise + * + * Returns a single string that goes row by row, with values of 1 representing an turned on cell, and a value of 0 representing a turned off cell. + * + * Arguments: + * * seed: seed for the function + * * accuracy: how close this is to the original perlin noise, as accuracy approaches infinity, the noise becomes more and more perlin-like + * * stamp_size: Size of a singular stamp used by the algorithm, think of this as the same stuff as frequency in perlin noise + * * world_size: size of the returned grid. + * * lower_range: lower bound of values selected for. (inclusive) + * * upper_range: upper bound of values selected for. (exclusive) + */ +#define rustg_dbp_generate(seed, accuracy, stamp_size, world_size, lower_range, upper_range) \ + RUSTG_CALL(RUST_G, "dbp_generate")(seed, accuracy, stamp_size, world_size, lower_range, upper_range) -#define rustg_dmi_strip_metadata(fname) LIBCALL(RUST_G, "dmi_strip_metadata")("[fname]") -#define rustg_dmi_create_png(path, width, height, data) LIBCALL(RUST_G, "dmi_create_png")(path, width, height, data) -#define rustg_dmi_resize_png(path, width, height, resizetype) LIBCALL(RUST_G, "dmi_resize_png")(path, width, height, resizetype) -#define rustg_file_read(fname) LIBCALL(RUST_G, "file_read")("[fname]") -#define rustg_file_exists(fname) LIBCALL(RUST_G, "file_exists")("[fname]") -#define rustg_file_write(text, fname) LIBCALL(RUST_G, "file_write")(text, "[fname]") -#define rustg_file_append(text, fname) LIBCALL(RUST_G, "file_append")(text, "[fname]") +#define rustg_dmi_strip_metadata(fname) RUSTG_CALL(RUST_G, "dmi_strip_metadata")("[fname]") +#define rustg_dmi_create_png(path, width, height, data) RUSTG_CALL(RUST_G, "dmi_create_png")(path, width, height, data) +#define rustg_dmi_resize_png(path, width, height, resizetype) RUSTG_CALL(RUST_G, "dmi_resize_png")(path, width, height, resizetype) + +#define rustg_file_read(fname) RUSTG_CALL(RUST_G, "file_read")("[fname]") +#define rustg_file_exists(fname) RUSTG_CALL(RUST_G, "file_exists")("[fname]") +#define rustg_file_write(text, fname) RUSTG_CALL(RUST_G, "file_write")(text, "[fname]") +#define rustg_file_append(text, fname) RUSTG_CALL(RUST_G, "file_append")(text, "[fname]") +#define rustg_file_get_line_count(fname) text2num(RUSTG_CALL(RUST_G, "file_get_line_count")("[fname]")) +#define rustg_file_seek_line(fname, line) RUSTG_CALL(RUST_G, "file_seek_line")("[fname]", "[line]") #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 -#define rustg_git_revparse(rev) LIBCALL(RUST_G, "rg_git_revparse")(rev) -#define rustg_git_commit_date(rev) LIBCALL(RUST_G, "rg_git_commit_date")(rev) +#define rustg_git_revparse(rev) RUSTG_CALL(RUST_G, "rg_git_revparse")(rev) +#define rustg_git_commit_date(rev) RUSTG_CALL(RUST_G, "rg_git_commit_date")(rev) -#define rustg_hash_string(algorithm, text) LIBCALL(RUST_G, "hash_string")(algorithm, text) -#define rustg_hash_file(algorithm, fname) LIBCALL(RUST_G, "hash_file")(algorithm, "[fname]") +#define rustg_hash_string(algorithm, text) RUSTG_CALL(RUST_G, "hash_string")(algorithm, text) +#define rustg_hash_file(algorithm, fname) RUSTG_CALL(RUST_G, "hash_file")(algorithm, "[fname]") +#define rustg_hash_generate_totp(seed) RUSTG_CALL(RUST_G, "generate_totp")(seed) +#define rustg_hash_generate_totp_tolerance(seed, tolerance) RUSTG_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 #define RUSTG_HTTP_METHOD_GET "get" @@ -89,35 +165,117 @@ #define RUSTG_HTTP_METHOD_PATCH "patch" #define RUSTG_HTTP_METHOD_HEAD "head" #define RUSTG_HTTP_METHOD_POST "post" -#define rustg_http_request_blocking(method, url, body, headers) LIBCALL(RUST_G, "http_request_blocking")(method, url, body, headers) -#define rustg_http_request_async(method, url, body, headers) LIBCALL(RUST_G, "http_request_async")(method, url, body, headers) -#define rustg_http_check_request(req_id) LIBCALL(RUST_G, "http_check_request")(req_id) +#define rustg_http_request_blocking(method, url, body, headers, options) RUSTG_CALL(RUST_G, "http_request_blocking")(method, url, body, headers, options) +#define rustg_http_request_async(method, url, body, headers, options) RUSTG_CALL(RUST_G, "http_request_async")(method, url, body, headers, options) +#define rustg_http_check_request(req_id) RUSTG_CALL(RUST_G, "http_check_request")(req_id) #define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET" #define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB" #define RUSTG_JOB_ERROR "JOB PANICKED" -#define rustg_json_is_valid(text) (LIBCALL(RUST_G, "json_is_valid")(text) == "true") +#define rustg_json_is_valid(text) (RUSTG_CALL(RUST_G, "json_is_valid")(text) == "true") + +#define rustg_log_write(fname, text, format) RUSTG_CALL(RUST_G, "log_write")("[fname]", text, format) +/proc/rustg_log_close_all() return RUSTG_CALL(RUST_G, "log_close_all")() + +#define rustg_noise_get_at_coordinates(seed, x, y) RUSTG_CALL(RUST_G, "noise_get_at_coordinates")(seed, x, y) + +/** + * Register a list of nodes into a rust library. This list of nodes must have been serialized in a json. + * Node {// Index of this node in the list of nodes + * unique_id: usize, + * // Position of the node in byond + * x: usize, + * y: usize, + * z: usize, + * // Indexes of nodes connected to this one + * connected_nodes_id: Vec} + * It is important that the node with the unique_id 0 is the first in the json, unique_id 1 right after that, etc. + * It is also important that all unique ids follow. {0, 1, 2, 4} is not a correct list and the registering will fail + * Nodes should not link across z levels. + * A node cannot link twice to the same node and shouldn't link itself either + */ +#define rustg_register_nodes_astar(json) RUSTG_CALL(RUST_G, "register_nodes_astar")(json) + +/** + * Add a new node to the static list of nodes. Same rule as registering_nodes applies. + * This node unique_id must be equal to the current length of the static list of nodes + */ +#define rustg_add_node_astar(json) RUSTG_CALL(RUST_G, "add_node_astar")(json) + +/**² + * Remove every link to the node with unique_id. Replace that node by null + */ +#define rustg_remove_node_astart(unique_id) RUSTG_CALL(RUST_G, "remove_node_astar")(unique_id) + +/** + * Compute the shortest path between start_node and goal_node using A*. Heuristic used is simple geometric distance + */ +#define rustg_generate_path_astar(start_node_id, goal_node_id) RUSTG_CALL(RUST_G, "generate_path_astar")(start_node_id, goal_node_id) + +#define RUSTG_REDIS_ERROR_CHANNEL "RUSTG_REDIS_ERROR_CHANNEL" -#define rustg_log_write(fname, text, format) LIBCALL(RUST_G, "log_write")("[fname]", text, format) -/proc/rustg_log_close_all() return LIBCALL(RUST_G, "log_close_all")() +#define rustg_redis_connect(addr) RUSTG_CALL(RUST_G, "redis_connect")(addr) +/proc/rustg_redis_disconnect() return RUSTG_CALL(RUST_G, "redis_disconnect")() +#define rustg_redis_subscribe(channel) RUSTG_CALL(RUST_G, "redis_subscribe")(channel) +/proc/rustg_redis_get_messages() return RUSTG_CALL(RUST_G, "redis_get_messages")() +#define rustg_redis_publish(channel, message) RUSTG_CALL(RUST_G, "redis_publish")(channel, message) -#define rustg_noise_get_at_coordinates(seed, x, y) LIBCALL(RUST_G, "noise_get_at_coordinates")(seed, x, y) +#define rustg_sql_connect_pool(options) RUSTG_CALL(RUST_G, "sql_connect_pool")(options) +#define rustg_sql_query_async(handle, query, params) RUSTG_CALL(RUST_G, "sql_query_async")(handle, query, params) +#define rustg_sql_query_blocking(handle, query, params) RUSTG_CALL(RUST_G, "sql_query_blocking")(handle, query, params) +#define rustg_sql_connected(handle) RUSTG_CALL(RUST_G, "sql_connected")(handle) +#define rustg_sql_disconnect_pool(handle) RUSTG_CALL(RUST_G, "sql_disconnect_pool")(handle) +#define rustg_sql_check_query(job_id) RUSTG_CALL(RUST_G, "sql_check_query")("[job_id]") -#define rustg_sql_connect_pool(options) LIBCALL(RUST_G, "sql_connect_pool")(options) -#define rustg_sql_query_async(handle, query, params) LIBCALL(RUST_G, "sql_query_async")(handle, query, params) -#define rustg_sql_query_blocking(handle, query, params) LIBCALL(RUST_G, "sql_query_blocking")(handle, query, params) -#define rustg_sql_connected(handle) LIBCALL(RUST_G, "sql_connected")(handle) -#define rustg_sql_disconnect_pool(handle) LIBCALL(RUST_G, "sql_disconnect_pool")(handle) -#define rustg_sql_check_query(job_id) LIBCALL(RUST_G, "sql_check_query")("[job_id]") +#define rustg_time_microseconds(id) text2num(RUSTG_CALL(RUST_G, "time_microseconds")(id)) +#define rustg_time_milliseconds(id) text2num(RUSTG_CALL(RUST_G, "time_milliseconds")(id)) +#define rustg_time_reset(id) RUSTG_CALL(RUST_G, "time_reset")(id) -#define rustg_unzip_download_async(url, unzip_directory) LIBCALL(RUST_G, "unzip_download_async")(url, unzip_directory) -#define rustg_unzip_check(job_id) LIBCALL(RUST_G, "unzip_check")("[job_id]") +/proc/rustg_unix_timestamp() + return text2num(RUSTG_CALL(RUST_G, "unix_timestamp")()) -#define rustg_url_encode(text) LIBCALL(RUST_G, "url_encode")(text) -#define rustg_url_decode(text) LIBCALL(RUST_G, "url_decode")(text) +#define rustg_raw_read_toml_file(path) json_decode(RUSTG_CALL(RUST_G, "toml_file_to_json")(path) || "null") + +/proc/rustg_read_toml_file(path) + var/list/output = rustg_raw_read_toml_file(path) + if (output["success"]) + return json_decode(output["content"]) + else + CRASH(output["content"]) + +#define rustg_raw_toml_encode(value) json_decode(RUSTG_CALL(RUST_G, "toml_encode")(json_encode(value))) + +/proc/rustg_toml_encode(value) + var/list/output = rustg_raw_toml_encode(value) + if (output["success"]) + return output["content"] + else + CRASH(output["content"]) + +#define rustg_unzip_download_async(url, unzip_directory) RUSTG_CALL(RUST_G, "unzip_download_async")(url, unzip_directory) +#define rustg_unzip_check(job_id) RUSTG_CALL(RUST_G, "unzip_check")("[job_id]") + +#define rustg_url_encode(text) RUSTG_CALL(RUST_G, "url_encode")("[text]") +#define rustg_url_decode(text) RUSTG_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) + #define url_encode(text) rustg_url_encode(text) + #define url_decode(text) rustg_url_decode(text) #endif + +/** + * This proc generates a noise grid using worley noise algorithm + * + * 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: + * * region_size: The size of regions + * * threshold: the value that determines wether a cell is dead or alive + * * node_per_region_chance: chance of a node existiing in a region + * * size: size of the returned grid + * * node_min: minimum amount of nodes in a region (after the node_per_region_chance is applied) + * * node_max: maximum amount of nodes in a region + */ +#define rustg_worley_generate(region_size, threshold, node_per_region_chance, size, node_min, node_max) \ + RUSTG_CALL(RUST_G, "worley_generate")(region_size, threshold, node_per_region_chance, size, node_min, node_max) diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index fdfec5e8ca0..e2c89df90e9 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "7.0.2" +#define TGS_DMAPI_VERSION "7.1.2" // All functions and datums outside this document are subject to change with any version and should not be relied on. @@ -50,6 +50,13 @@ #endif +#ifndef TGS_FILE2TEXT_NATIVE +#ifdef file2text +#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You can fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses +#endif +#define TGS_FILE2TEXT_NATIVE file2text +#endif + // EVENT CODES /// Before a reboot mode change, extras parameters are the current and new reboot mode enums. @@ -305,6 +312,7 @@ var/datum/tgs_chat_embed/structure/embed /datum/tgs_message_content/New(text) + ..() if(!istext(text)) TGS_ERROR_LOG("[/datum/tgs_message_content] created with no text!") text = null @@ -347,6 +355,7 @@ var/proxy_url /datum/tgs_chat_embed/media/New(url) + ..() if(!istext(url)) CRASH("[/datum/tgs_chat_embed/media] created with no url!") @@ -360,6 +369,7 @@ var/proxy_icon_url /datum/tgs_chat_embed/footer/New(text) + ..() if(!istext(text)) CRASH("[/datum/tgs_chat_embed/footer] created with no text!") @@ -376,6 +386,7 @@ var/proxy_icon_url /datum/tgs_chat_embed/provider/author/New(name) + ..() if(!istext(name)) CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!") @@ -388,6 +399,7 @@ var/is_inline /datum/tgs_chat_embed/field/New(name, value) + ..() if(!istext(name)) CRASH("[/datum/tgs_chat_embed/field] created with no name!") @@ -490,10 +502,20 @@ /world/proc/TgsChatChannelInfo() return +/** + * Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep! + * + * event_name - The name of the event to trigger + * parameters - Optional list of string parameters to pass as arguments to the event script. The first parameter passed to a script will always be the running game's directory followed by these parameters. + * wait_for_completion - If set, this function will not return until the event has run to completion. + */ +/world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE) + return + /* The MIT License -Copyright (c) 2017-2023 Jordan Brown +Copyright (c) 2017-2024 Jordan Brown Permission is hereby granted, free of charge, to any person obtaining a copy of this software and diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 4b79bdb8e68..feece392c90 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -20,14 +20,6 @@ /savefile/byond_version = MIN_COMPILER_VERSION #endif -// Temporary 515 block until it is completely compatible. -// AnturK says there are issues with savefiles that would make it dangerous to test merge, -// and so this check is in place to stop serious damage. -// That being said, if you really are ready, you can give YES_I_WANT_515 to TGS. -#if !defined(YES_I_WANT_515) && DM_VERSION >= 515 -#error We do not yet completely support BYOND 515. -#endif - // 515 split call for external libraries into call_ext #if DM_VERSION < 515 #define LIBCALL call diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 885a8ed47d4..9c22eb59a6e 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -72,10 +72,10 @@ //End NSV //Update this whenever the byond version is stable so people stop updating to hilariously broken versions -#define MAX_COMPILER_VERSION 514 -#define MAX_COMPILER_BUILD 1589 +#define MAX_COMPILER_VERSION 515 +#define MAX_COMPILER_BUILD 1700 #if DM_VERSION > MAX_COMPILER_VERSION || DM_BUILD > MAX_COMPILER_BUILD -#warn WARNING: Your BYOND version is over the recommended version (514.1589)! Stability is not guaranteed. +#warn WARNING: Your BYOND version is over the recommended version (515.1700)! Stability is not guaranteed. #endif //Log the full sendmaps profile on 514.1556+, any earlier and we get bugs or it not existing #if DM_VERSION >= 514 && DM_BUILD >= 1556 diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm index a4a78852480..b8a4782be97 100644 --- a/code/controllers/configuration/config_entry.dm +++ b/code/controllers/configuration/config_entry.dm @@ -38,7 +38,7 @@ /datum/config_entry/can_vv_get(var_name) . = ..() - if(var_name == NAMEOF_STATIC(src, config_entry_value) || var_name == NAMEOF_STATIC(src, default)) + if(var_name == NAMEOF(src, config_entry_value) || var_name == NAMEOF(src, default)) . &= !(protection & CONFIG_ENTRY_HIDDEN) /datum/config_entry/vv_edit_var(var_name, var_value) diff --git a/code/datums/components/holomap.dm b/code/datums/components/holomap.dm index 129544497a9..376f583d176 100644 --- a/code/datums/components/holomap.dm +++ b/code/datums/components/holomap.dm @@ -41,7 +41,7 @@ /datum/component/holomap/proc/get_user() RETURN_TYPE(/mob/living) var/atom/movable/holder = parent - return (isliving(holder) || !isatom(holder)) ? holder : holder.loc + return (isliving(holder) || !isatom(holder)) ? holder : holder.loc //FIXME - This proc is terrible (and can runtime). Just save the user and track if they get del'd like a sane person. Why is this like this?????? /datum/component/holomap/Initialize() . = ..() diff --git a/code/datums/http.dm b/code/datums/http.dm index 14bb8d21976..5dab601ffb5 100644 --- a/code/datums/http.dm +++ b/code/datums/http.dm @@ -6,10 +6,12 @@ var/body var/headers var/url + /// If present response body will be saved to this file. + var/output_file var/_raw_response -/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 @@ -19,15 +21,16 @@ src.url = url src.body = body src.headers = headers + src.output_file = output_file /datum/http_request/proc/execute_blocking() - _raw_response = rustg_http_request_blocking(method, url, body, headers) + _raw_response = rustg_http_request_blocking(method, url, body, headers, build_options()) /datum/http_request/proc/begin_async() 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))) stack_trace("Proc error: [id]") @@ -35,6 +38,11 @@ else in_progress = TRUE +/datum/http_request/proc/build_options() + if(output_file) + return json_encode(list("output_filename"=output_file,"body_filename"=null)) + return null + /datum/http_request/proc/is_complete() if (isnull(id)) return TRUE diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 24acbf6c7ab..5a5810cced0 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -64,10 +64,23 @@ if(!target_name) return + var/freshlist = FALSE //NSV13 - tracking bugs if(!track.initialized) trackable_mobs() + freshlist = TRUE //NSV13 - tracking bugs + //NSV13 - safer checks. var/datum/weakref/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + if((isnull(target) || !istype(target))) //Uh oh! + if(freshlist) //Already accurate new list + return + + trackable_mobs() //Target may be new. + target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + if((isnull(target) || !istype(target))) + return + //NSV13 end. + ai_start_tracking(target.resolve()) /mob/living/silicon/ai/proc/ai_start_tracking(mob/living/target) //starts ai tracking diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index e0ff8f815ae..dcfbfc98696 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -196,9 +196,9 @@ /obj/item/extinguisher/proc/manage_chair_speed(datum/move_loop/move/source) SIGNAL_HANDLER switch(source.lifetime) - if(5 to 4) + if(4 to 5) source.delay = 2 - if(3 to 1) + if(1 to 3) source.delay = 3 /obj/item/extinguisher/AltClick(mob/user) diff --git a/code/modules/tgs/LICENSE b/code/modules/tgs/LICENSE index 2bedf9a63aa..324c48e993e 100644 --- a/code/modules/tgs/LICENSE +++ b/code/modules/tgs/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2017-2023 Jordan Brown +Copyright (c) 2017-2024 Jordan Brown Permission is hereby granted, free of charge, to any person obtaining a copy of this software and diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index 8be96f27404..15622228e91 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -166,3 +166,11 @@ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) if(api) return api.Visibility() + +/world/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE) + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + if(!istype(parameters, /list)) + parameters = list() + + return api.TriggerEvent(event_name, parameters, wait_for_completion) diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm index 07ce3b68458..f734fd0527f 100644 --- a/code/modules/tgs/core/datum.dm +++ b/code/modules/tgs/core/datum.dm @@ -7,7 +7,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) var/list/warned_deprecated_command_runs /datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version) - . = ..() + ..() src.event_handler = event_handler src.version = version @@ -17,7 +17,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866 del(world) world.sleep_offline = FALSE // just in case, this is BYOND after all... - sleep(1) + sleep(world.tick_lag) TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]") /datum/tgs_api/latest @@ -69,3 +69,6 @@ TGS_PROTECT_DATUM(/datum/tgs_api) /datum/tgs_api/proc/Visibility() return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/TriggerEvent(event_name, list/parameters, wait_for_completion) + return FALSE diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm index a5dae1241a3..bc561e67487 100644 --- a/code/modules/tgs/core/tgs_version.dm +++ b/code/modules/tgs/core/tgs_version.dm @@ -1,4 +1,5 @@ /datum/tgs_version/New(raw_parameter) + ..() src.raw_parameter = raw_parameter deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "") var/list/version_bits = splittext(deprefixed_parameter, ".") diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm index 945e2e41176..7c87922750b 100644 --- a/code/modules/tgs/v4/api.dm +++ b/code/modules/tgs/v4/api.dm @@ -181,7 +181,7 @@ var/json = json_encode(data) while(requesting_new_port && !override_requesting_new_port) - sleep(1) + sleep(world.tick_lag) //we need some port open at this point to facilitate return communication if(!world.port) @@ -209,7 +209,7 @@ requesting_new_port = FALSE while(export_lock) - sleep(1) + sleep(world.tick_lag) export_lock = TRUE last_interop_response = null @@ -217,7 +217,7 @@ text2file(json, server_commands_json_path) for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I) - sleep(1) + sleep(world.tick_lag) if(!last_interop_response) TGS_ERROR_LOG("Failed to get export result for: [json]") diff --git a/code/modules/tgs/v5/__interop_version.dm b/code/modules/tgs/v5/__interop_version.dm index 616263098fd..f4806f7adb9 100644 --- a/code/modules/tgs/v5/__interop_version.dm +++ b/code/modules/tgs/v5/__interop_version.dm @@ -1 +1 @@ -"5.8.0" +"5.9.0" diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm index 1c7d67d20cd..92c7a8388a7 100644 --- a/code/modules/tgs/v5/_defines.dm +++ b/code/modules/tgs/v5/_defines.dm @@ -14,6 +14,7 @@ #define DMAPI5_BRIDGE_COMMAND_KILL 4 #define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5 #define DMAPI5_BRIDGE_COMMAND_CHUNK 6 +#define DMAPI5_BRIDGE_COMMAND_EVENT 7 #define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier" #define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands" @@ -34,6 +35,7 @@ #define DMAPI5_BRIDGE_PARAMETER_VERSION "version" #define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage" #define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel" +#define DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION "eventInvocation" #define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort" #define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation" @@ -81,6 +83,7 @@ #define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9 #define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10 #define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11 +#define DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT 12 #define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType" #define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand" @@ -116,3 +119,9 @@ #define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name" #define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText" #define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly" + +#define DMAPI5_EVENT_ID "eventId" + +#define DMAPI5_EVENT_INVOCATION_NAME "eventName" +#define DMAPI5_EVENT_INVOCATION_PARAMETERS "parameters" +#define DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION "notifyCompletion" diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm index a5c064a8eaf..95b8edd3ee5 100644 --- a/code/modules/tgs/v5/api.dm +++ b/code/modules/tgs/v5/api.dm @@ -27,6 +27,8 @@ var/chunked_requests = 0 var/list/chunked_topics = list() + var/list/pending_events = list() + var/detached = FALSE /datum/tgs_api/v5/New() @@ -46,6 +48,10 @@ var/datum/tgs_version/api_version = ApiVersion() version = null // we want this to be the TGS version, not the interop version + + // sleep once to prevent an issue where world.Export on the first tick can hang indefinitely + sleep(world.tick_lag) + var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") @@ -125,7 +131,7 @@ TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep") logged = TRUE - sleep(1) + sleep(world.tick_lag) TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed") @@ -249,6 +255,40 @@ WaitForReattach(TRUE) return chat_channels.Copy() +/datum/tgs_api/v5/TriggerEvent(event_name, list/parameters, wait_for_completion) + RequireInitialBridgeResponse() + WaitForReattach(TRUE) + + if(interop_version.minor < 9) + TGS_WARNING_LOG("Interop version too low for custom events!") + return FALSE + + var/str_parameters = list() + for(var/i in parameters) + str_parameters += "[i]" + + var/list/response = Bridge(DMAPI5_BRIDGE_COMMAND_EVENT, list(DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION = list(DMAPI5_EVENT_INVOCATION_NAME = event_name, DMAPI5_EVENT_INVOCATION_PARAMETERS = str_parameters, DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION = wait_for_completion))) + if(!response) + return FALSE + + var/event_id = response[DMAPI5_EVENT_ID] + if(!event_id) + return FALSE + + TGS_DEBUG_LOG("Created event ID: [event_id]") + if(!wait_for_completion) + return TRUE + + TGS_DEBUG_LOG("Waiting for completion of event ID: [event_id]") + + while(!pending_events[event_id]) + sleep(world.tick_lag) + + TGS_DEBUG_LOG("Completed wait on event ID: [event_id]") + pending_events -= event_id + + return TRUE + /datum/tgs_api/v5/proc/DecodeChannels(chat_update_json) TGS_DEBUG_LOG("DecodeChannels()") var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS] diff --git a/code/modules/tgs/v5/bridge.dm b/code/modules/tgs/v5/bridge.dm index a0ab3598767..0c5e701a32b 100644 --- a/code/modules/tgs/v5/bridge.dm +++ b/code/modules/tgs/v5/bridge.dm @@ -65,7 +65,7 @@ if(detached) // Wait up to one minute for(var/i in 1 to 600) - sleep(1) + sleep(world.tick_lag) if(!detached && (!require_channels || length(chat_channels))) break @@ -77,8 +77,11 @@ /datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request) WaitForReattach(FALSE) + TGS_DEBUG_LOG("Bridge request start") // This is an infinite sleep until we get a response var/export_response = world.Export(bridge_request) + TGS_DEBUG_LOG("Bridge request complete") + if(!export_response) TGS_ERROR_LOG("Failed bridge request: [bridge_request]") return @@ -88,7 +91,7 @@ TGS_ERROR_LOG("Failed bridge request, missing content!") return - var/response_json = file2text(content) + var/response_json = TGS_FILE2TEXT_NATIVE(content) if(!response_json) TGS_ERROR_LOG("Failed bridge request, failed to load content!") return diff --git a/code/modules/tgs/v5/topic.dm b/code/modules/tgs/v5/topic.dm index 05e6c4e1b21..e1f2cb63857 100644 --- a/code/modules/tgs/v5/topic.dm +++ b/code/modules/tgs/v5/topic.dm @@ -176,6 +176,10 @@ var/list/reattach_response = TopicResponse(error_message) reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands() reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort() + + for(var/eventId in pending_events) + pending_events[eventId] = TRUE + return reattach_response if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK) @@ -276,6 +280,15 @@ TGS_WORLD_ANNOUNCE(message) return TopicResponse() + if(DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT) + var/event_id = topic_parameters[DMAPI5_EVENT_ID] + if (!istext(event_id)) + return TopicResponse("Invalid or missing [DMAPI5_EVENT_ID]") + + TGS_DEBUG_LOG("Completing event ID [event_id]...") + pending_events[event_id] = TRUE + return TopicResponse() + return TopicResponse("Unknown command: [command]") /datum/tgs_api/v5/proc/WorldBroadcast(message) diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm index d531d4b7b9d..237207fdfd0 100644 --- a/code/modules/tgs/v5/undefs.dm +++ b/code/modules/tgs/v5/undefs.dm @@ -14,6 +14,7 @@ #undef DMAPI5_BRIDGE_COMMAND_KILL #undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND #undef DMAPI5_BRIDGE_COMMAND_CHUNK +#undef DMAPI5_BRIDGE_COMMAND_EVENT #undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER #undef DMAPI5_PARAMETER_CUSTOM_COMMANDS @@ -34,6 +35,7 @@ #undef DMAPI5_BRIDGE_PARAMETER_VERSION #undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE #undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL +#undef DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION #undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT #undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION @@ -81,6 +83,7 @@ #undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK #undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK #undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST +#undef DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT #undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE #undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND @@ -116,3 +119,9 @@ #undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME #undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT #undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY + +#undef DMAPI5_EVENT_ID + +#undef DMAPI5_EVENT_INVOCATION_NAME +#undef DMAPI5_EVENT_INVOCATION_PARAMETERS +#undef DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION diff --git a/dependencies.sh b/dependencies.sh index a5383d3b74f..caa899f6669 100755 --- a/dependencies.sh +++ b/dependencies.sh @@ -4,21 +4,21 @@ #Final authority on what's required to fully build the project # byond version -export BYOND_MAJOR=514 -export BYOND_MINOR=1589 +export BYOND_MAJOR=515 +export BYOND_MINOR=1608 #rust version -export RUST_VERSION=1.54.0 +export RUST_VERSION=1.67.1 #rust_g git tag -export RUST_G_VERSION=0.4.7.1 +export RUST_G_VERSION=1.2.0 #node version export NODE_VERSION=18 export NODE_VERSION_PRECISE=18.14.2 # SpacemanDMM git tag -export SPACEMAN_DMM_VERSION=suite-1.7.1 +export SPACEMAN_DMM_VERSION=suite-1.8 #auxmos version export AUXMOS_VERSION=2.2.2 diff --git a/html/changelog.html b/html/changelog.html index 1ae400eca5b..2ac7d0c8b06 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,24 @@ -->
+

14 June 2024

+

DeltaFire15 updated:

+ +

benbot16 updated:

+ +

covertcorvid updated:

+ +

31 May 2024

DeltaFire15 updated:

GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 8a9f3b84930..d76c1833ee9 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -2041,3 +2041,16 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: Ammo racks may now be loaded by attacking them with ammunition - tweak: Jam SFX now plays on jam instead of on load attempt - bugfix: Fixes a few ammo rack-related edge cases +2024-06-14: + DeltaFire15: + - bugfix: The AI tracking window no longer breaks if anybody on the camera network + is naked. + - bugfix: As AI, tracking somebody with the crew monitor should be less likely to + brick. + benbot16: + - tweak: Air-to-air repair/refuel modules now have a maximum range. + - tweak: Adjusts repair/fuel transfer times for air-to-air modules + - bugfix: Air-to-air repair/refuel modules now function properly. + - bugfix: Fixes a virtual-z issue with dropships + covertcorvid: + - server: Upped the compiler version and max client version to 515.1607 diff --git a/html/changelogs/AutoChangeLog-pr-2654.yml b/html/changelogs/AutoChangeLog-pr-2654.yml new file mode 100644 index 00000000000..9ea53991d2c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2654.yml @@ -0,0 +1,5 @@ +author: someone543 +delete-after: true +changes: + - rscadd: Added some nsv clothes to the torp shop + - spellcheck: fixed a typo in peacekeer vest diff --git a/nsv13.dme b/nsv13.dme index f1c83cc7130..b3b278369d7 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -3878,6 +3878,8 @@ #include "nsv13\code\modules\client\loadout\loadout_colorizers.dm" #include "nsv13\code\modules\client\loadout\loadout_donator.dm" #include "nsv13\code\modules\client\loadout\loadout_general.dm" +#include "nsv13\code\modules\client\loadout\loadout_hat.dm" +#include "nsv13\code\modules\client\loadout\loadout_suit.dm" #include "nsv13\code\modules\client\loadout\loadout_uniform.dm" #include "nsv13\code\modules\clothing\custom_clothes.dm" #include "nsv13\code\modules\clothing\custom_outfits.dm" diff --git a/nsv13/code/__DEFINES/components.dm b/nsv13/code/__DEFINES/components.dm index 105ea218e06..df2ee8bd716 100644 --- a/nsv13/code/__DEFINES/components.dm +++ b/nsv13/code/__DEFINES/components.dm @@ -2,7 +2,11 @@ #define COMSIG_FTL_STATE_CHANGE "ftl_state_change" #define COMSIG_MOB_ITEM_EQUIPPED "mob_item_equipped" //Used for aiming component, tells you when a mob equips ANY item #define COMSIG_MOB_ITEM_DROPPED "mob_item_dropped" -#define COMSIG_LOCK_LOST "lock_lost" // When we lost lock on a ship + +// Ship targeting signals +#define COMSIG_TARGET_PAINTED "target_painted" //! from base of /obj/structure/overmap/proc/finish_lockon: (target, data_link_origin) +#define COMSIG_TARGET_LOCKED "target_locked" //! from base of /obj/structure/overmap/proc/select_target: (target) +#define COMSIG_LOCK_LOST "lock_lost" //! from base of /obj/structure/overmap/proc/dump_lock: (target) #define COMSIG_SHIP_RELEASE_BOARDING "release_boarding" #define COMSIG_SHIP_BLOCKS_RELEASE_BOARDING 1 diff --git a/nsv13/code/game/general_quarters/dropship.dm b/nsv13/code/game/general_quarters/dropship.dm index d075c2e01f7..54fa037683d 100644 --- a/nsv13/code/game/general_quarters/dropship.dm +++ b/nsv13/code/game/general_quarters/dropship.dm @@ -63,7 +63,7 @@ /area/dropship/get_virtual_z(turf/T) if(linked_dropship) - return linked_dropship.get_virtual_z_level() + return linked_dropship.get_interior_virtual_z() return ..() //If we ever want to let them build these things.. diff --git a/nsv13/code/game/general_quarters/dropship_types.dm b/nsv13/code/game/general_quarters/dropship_types.dm index 322745c3c05..679cdd261ad 100644 --- a/nsv13/code/game/general_quarters/dropship_types.dm +++ b/nsv13/code/game/general_quarters/dropship_types.dm @@ -73,7 +73,7 @@ Credit to TGMC for the interior sprites for all these! starmap.linked = src // Z override for transports. This returns our aircraft's unique Z. -/obj/structure/overmap/small_craft/transport/get_virtual_z_level() +/obj/structure/overmap/small_craft/transport/proc/get_interior_virtual_z() return linked_virtual_z != null ? linked_virtual_z : 0; /datum/map_template/dropship diff --git a/nsv13/code/modules/client/loadout/loadout_hat.dm b/nsv13/code/modules/client/loadout/loadout_hat.dm new file mode 100644 index 00000000000..6f3c1914575 --- /dev/null +++ b/nsv13/code/modules/client/loadout/loadout_hat.dm @@ -0,0 +1,4 @@ +/datum/gear/hat/sun + display_name = "headband of the rising sun" + path = /obj/item/clothing/head/ship/rising_sun + allowed_roles = list(JOB_NAME_MUNITIONSTECHNICIAN, JOB_NAME_MASTERATARMS, JOB_NAME_PILOT, JOB_NAME_AIRTRAFFICCONTROLLER) diff --git a/nsv13/code/modules/client/loadout/loadout_suit.dm b/nsv13/code/modules/client/loadout/loadout_suit.dm new file mode 100644 index 00000000000..81d5eb2e1af --- /dev/null +++ b/nsv13/code/modules/client/loadout/loadout_suit.dm @@ -0,0 +1,8 @@ +/datum/gear/suit/jacket/utility + display_name = "Utility jacket" + path = /obj/item/clothing/suit/ship/assistant_jacket + +/datum/gear/suit/peacevest + display_name = "Peacekeeper vest" + path = /obj/item/clothing/suit/ship/peacekeeper + allowed_roles = list(JOB_NAME_SECURITYOFFICER) diff --git a/nsv13/code/modules/client/loadout/loadout_uniform.dm b/nsv13/code/modules/client/loadout/loadout_uniform.dm index 7becfbc4a4f..8c7b7b2de94 100644 --- a/nsv13/code/modules/client/loadout/loadout_uniform.dm +++ b/nsv13/code/modules/client/loadout/loadout_uniform.dm @@ -2,3 +2,10 @@ display_name = "skirt, black" path = /obj/item/clothing/under/dress/skirt +/datum/gear/uniform/rank/assistant/utility + display_name = "Utilitarian coveralls" + path = /obj/item/clothing/under/ship/assistant + +/datum/gear/uniform/rank/security/spacecop + display_name = "Space cop uniform" + path = /obj/item/clothing/under/ship/peacekeeper diff --git a/nsv13/code/modules/clothing/custom_clothes.dm b/nsv13/code/modules/clothing/custom_clothes.dm index 13509b97ee5..41ea8025f15 100644 --- a/nsv13/code/modules/clothing/custom_clothes.dm +++ b/nsv13/code/modules/clothing/custom_clothes.dm @@ -72,7 +72,7 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 10, "fire" = 10, "acid" = 10) /obj/item/clothing/suit/ship/peacekeeper - name = "Peacekeer vest" + name = "Peacekeeper vest" icon_state = "peacekeeper_vest" item_state = "peacekeeper_vest" desc = "A nanoweave vest capable of impeding most small arms fire as well as improvised weapons. It bears the logo of the North Star peacekeeper force" diff --git a/nsv13/code/modules/mob/living/silicon/ai/track.dm b/nsv13/code/modules/mob/living/silicon/ai/track.dm index 673e7760b62..9ecd2f6c73a 100644 --- a/nsv13/code/modules/mob/living/silicon/ai/track.dm +++ b/nsv13/code/modules/mob/living/silicon/ai/track.dm @@ -62,7 +62,7 @@ GLOBAL_DATUM_INIT(tracking_menu, /datum/track_menu, new) var/mob/living/carbon/human/player = mob_poi var/nanite_sensors = HAS_TRAIT(player, TRAIT_NANITE_SENSORS) var/obj/item/clothing/under/uniform = player.w_uniform - if(nanite_sensors || uniform.sensor_mode >= SENSOR_VITALS) + if(nanite_sensors || (uniform && uniform.sensor_mode >= SENSOR_VITALS)) serialized["health"] = FLOOR((player.health / player.maxHealth * 100), 1) var/obj/item/card/id/identification_card = mob_poi.get_idcard() diff --git a/nsv13/code/modules/overmap/ai-skynet.dm b/nsv13/code/modules/overmap/ai-skynet.dm index 53106354a4e..3a41913bb39 100644 --- a/nsv13/code/modules/overmap/ai-skynet.dm +++ b/nsv13/code/modules/overmap/ai-skynet.dm @@ -1983,9 +1983,9 @@ Seek a ship thich we'll station ourselves around switch(angular_difference) if(-15 to 15) boost(NORTH) //ZOOOM - if(-45 to -180) + if(-180 to -45) boost(WEST) - if(-180 to -INFINITY) + if(-INFINITY to -180) boost(EAST) if(45 to 180) boost(EAST) diff --git a/nsv13/code/modules/overmap/fighters/fuel_tank.dm b/nsv13/code/modules/overmap/fighters/fuel_tank.dm index 66ea57a952c..5b8aff2ac01 100644 --- a/nsv13/code/modules/overmap/fighters/fuel_tank.dm +++ b/nsv13/code/modules/overmap/fighters/fuel_tank.dm @@ -20,6 +20,13 @@ var/allow_refuel = FALSE var/units_per_second = 50 +/obj/structure/reagent_dispensers/fueltank/cryogenic_fuel/Destroy() + QDEL_NULL(current_beam) + QDEL_NULL(nozzle) + QDEL_NULL(soundloop) + fuel_target = null + . = ..() + /obj/structure/reagent_dispensers/fueltank/cryogenic_fuel/ui_act(action, params, datum/tgui/ui) if(..()) return diff --git a/nsv13/code/modules/overmap/fighters/modules/refueling_kit.dm b/nsv13/code/modules/overmap/fighters/modules/refueling_kit.dm index 3a46f9c21f6..f24081ded6d 100644 --- a/nsv13/code/modules/overmap/fighters/modules/refueling_kit.dm +++ b/nsv13/code/modules/overmap/fighters/modules/refueling_kit.dm @@ -4,13 +4,28 @@ icon_state = "resupply_tier1" overmap_firing_sounds = list( 'nsv13/sound/effects/fighters/refuel.ogg') - fire_delay = 6 SECONDS + fire_delay = 4 SECONDS bypass_safety = TRUE var/datum/beam/current_beam var/next_fuel = 0 + var/is_fuelling = TRUE + var/is_charging = TRUE var/battery_recharge_amount = 500 - var/minimum_fuel_to_keep = 200 - var/fuel_transfer_rate = 100 + var/minimum_fuel_to_keep = 250 + var/fuel_transfer_rate = 50 + var/refuel_range = 10 + +/obj/item/fighter_component/primary/utility/refuel/on_install(obj/structure/overmap/target) + . = ..() + RegisterSignal(target, COMSIG_TARGET_LOCKED, PROC_REF(on_target_lock)) + +/obj/item/fighter_component/primary/utility/refuel/remove_from(obj/structure/overmap/target) + . = ..() + UnregisterSignal(target, COMSIG_TARGET_LOCKED) + +/obj/item/fighter_component/primary/utility/refuel/proc/on_target_lock(obj/structure/overmap/us, obj/structure/overmap/target) + is_fuelling = TRUE + is_charging = TRUE /obj/item/fighter_component/primary/utility/refuel/get_ammo() var/obj/structure/overmap/small_craft/F = loc @@ -27,13 +42,13 @@ /obj/item/fighter_component/primary/utility/refuel/tier2 name = "upgraded air to air resupply kit" icon_state = "resupply_tier2" - fire_delay = 5 SECONDS + fire_delay = 3 SECONDS tier = 2 /obj/item/fighter_component/primary/utility/refuel/tier3 name = "super air to air resupply kit" icon_state = "resupply_tier3" - fire_delay = 3 SECONDS + fire_delay = 2 SECONDS tier = 3 /obj/item/fighter_component/primary/utility/refuel/proc/cancel_action(obj/structure/overmap/us, obj/structure/overmap/them, message) @@ -41,7 +56,7 @@ QDEL_NULL(current_beam) // Remove targeting if(us && LAZYFIND(us.target_painted, them)) - us.start_lockon(them) + us.dump_lock(them) if(us && us.gunner && message) to_chat(us.gunner, message) @@ -53,54 +68,66 @@ // The component isn't installed, we're not on that mode, or we have no potential targets var/obj/structure/overmap/small_craft/us = loc if(!us || !istype(us) || (us.fire_mode != fire_mode) || !length(us.target_painted)) - cancel_action(us) return // The target isn't an overmap somehow, we're targeting ourselves, or they're an enemy var/obj/structure/overmap/small_craft/them = us.target_lock if(!them || !istype(them) || (them == us) || (them.faction != us.faction)) cancel_action(us, them) return + // We're out of range + if(overmap_dist(us, them) > refuel_range) + cancel_action(us, them, "Target out of range.") + return // Getting here means we should actually try refueling them next_fuel = world.time + fire_delay - if(!transfer_fuel(us, them) || jump_battery(us, them)) + if(is_fuelling) + var/fuel_message = transfer_fuel(us, them) + if(fuel_message) + to_chat(us.gunner, fuel_message) + is_fuelling = FALSE + + if(is_charging) + var/charge_message = jump_battery(us, them) + if(charge_message) + to_chat(us.gunner, charge_message) + is_charging = FALSE + + if(!is_fuelling && !is_charging) + cancel_action(us, them, "All operations complete. Disconnecting transfer equipment.") return + // See if we need to make a new beam. Comes after the refuel so we can not do this if we fail any checks. if(QDELETED(current_beam)) current_beam = new(us,them,beam_icon='nsv13/icons/effects/beam.dmi',time=INFINITY,maxdistance = INFINITY,beam_icon_state="hose",btype=/obj/effect/ebeam/fuel_hose) INVOKE_ASYNC(current_beam, TYPE_PROC_REF(/datum/beam, Start)) +// These procs handle transferring fuel/charge. If a string is returned, it means we're done. If FALSE is returned, we're not done. /obj/item/fighter_component/primary/utility/refuel/proc/transfer_fuel(obj/structure/overmap/small_craft/us, obj/structure/overmap/small_craft/them) var/transfer_amount = CLAMP((them.get_max_fuel() - them.get_fuel()), 0, fuel_transfer_rate) if(transfer_amount <= 0) - cancel_action(us, them, "Fuel tank is full.") - return - if(them.get_fuel() <= minimum_fuel_to_keep) // Don't give away ALL our fuel - cancel_action(us, them, "Fuel is below minimum safe transfer level.") - return + return "Target craft is fully fueled." + if(us.get_fuel() <= minimum_fuel_to_keep) // Don't give away ALL our fuel + return "Fuel levels below minimum safe transfer level." var/obj/item/fighter_component/fuel_tank/ourTank = us.loadout.get_slot(HARDPOINT_SLOT_FUEL) var/obj/item/fighter_component/fuel_tank/theirTank = them.loadout.get_slot(HARDPOINT_SLOT_FUEL) us.relay('nsv13/sound/effects/fighters/refuel.ogg') them.relay('nsv13/sound/effects/fighters/refuel.ogg') ourTank.reagents.trans_to(theirTank, transfer_amount) - return TRUE + return /obj/item/fighter_component/primary/utility/refuel/proc/jump_battery(obj/structure/overmap/small_craft/us, obj/structure/overmap/small_craft/them) var/obj/item/fighter_component/battery/ourBattery = us.loadout.get_slot(HARDPOINT_SLOT_BATTERY) if(!ourBattery || !istype(ourBattery)) - cancel_action(us, them, "This craft has no battery installed!") - return + return "This craft has no battery installed!" var/obj/item/fighter_component/battery/theirBattery = them.loadout.get_slot(HARDPOINT_SLOT_BATTERY) if(!theirBattery || !istype(theirBattery)) - cancel_action(us, them, "The target has no battery installed!") - return - if(!ourBattery.charge < (battery_recharge_amount * 2)) - cancel_action(us, them, "Battery charge is below minimum safe transfer level.") - return - if(!(theirBattery.charge < battery_recharge_amount)) - cancel_action(us, them, "Battery levels nominal.") - return + return "The target has no battery installed!" + if(ourBattery.charge < (battery_recharge_amount * 2)) + return "Battery charge is below minimum safe transfer level." + if(!(theirBattery.charge < theirBattery.maxcharge)) + return "Target craft is fully charged." theirBattery.give(battery_recharge_amount) //Jumpstart their battery ourBattery.use_power(battery_recharge_amount) - return TRUE + return diff --git a/nsv13/code/modules/overmap/fighters/modules/repair_kit.dm b/nsv13/code/modules/overmap/fighters/modules/repair_kit.dm index 2646696e82b..7bfb7d3f063 100644 --- a/nsv13/code/modules/overmap/fighters/modules/repair_kit.dm +++ b/nsv13/code/modules/overmap/fighters/modules/repair_kit.dm @@ -8,6 +8,8 @@ bypass_safety = TRUE var/datum/beam/current_beam = null var/next_repair = 0 + var/foam_consumption = 20 + var/repair_range = 10 /obj/item/fighter_component/primary/utility/repairer/get_ammo() return magazine?.reagents.total_volume @@ -20,12 +22,14 @@ icon_state = "repairer_tier2" tier = 2 fire_delay = 4 SECONDS + foam_consumption = 15 /obj/item/fighter_component/primary/utility/repairer/tier3 name = "super air to air repair kit" icon_state = "repairer_tier3" tier = 3 fire_delay = 3 SECONDS + foam_consumption = 10 /obj/item/fighter_component/primary/utility/repairer/load(obj/structure/overmap/target, atom/movable/AM) if(!istype(AM, accepted_ammo)) @@ -43,7 +47,7 @@ QDEL_NULL(current_beam) // Remove targeting if(us && LAZYFIND(us.target_painted, them)) - us.start_lockon(them) + us.dump_lock(them) if(us && us.gunner && message) to_chat(us.gunner, message) @@ -55,13 +59,16 @@ // The component isn't installed, we're not on that mode, or we have no potential targets var/obj/structure/overmap/small_craft/us = loc if(!us || !istype(us) || (us.fire_mode != fire_mode) || !length(us.target_painted)) - cancel_action(us) return // The target isn't an overmap somehow, we're targeting ourselves, or they're an enemy var/obj/structure/overmap/small_craft/them = us.target_lock if(!them || !istype(them) || (them == us) || (them.faction != us.faction)) cancel_action(us, them) return + // We're out of range + if(overmap_dist(us, them) > repair_range) + cancel_action(us, them, "Target out of range.") + return // We don't have a hull foam tank var/obj/structure/reagent_dispensers/foamtank/hull_repair_juice/tank = magazine if(!tank || !istype(tank)) @@ -69,7 +76,7 @@ return // We're out of juice if(tank.reagents.get_reagent_amount(/datum/reagent/hull_repair_juice) <= 0) - cancel_action(us, them, "Out of repair foam.") + cancel_action(us, them, "Repair foam reserves depleted.") return // They're fixed var/obj/item/fighter_component/armour_plating/theirArmour = them.loadout.get_slot(HARDPOINT_SLOT_ARMOUR) @@ -85,8 +92,9 @@ INVOKE_ASYNC(current_beam, TYPE_PROC_REF(/datum/beam, Start)) new /obj/effect/temp_visual/heal(get_turf(them), COLOR_CYAN) // Use some juice - tank.reagents.remove_reagent(/datum/reagent/hull_repair_juice, 5) - //You can repair the main ship too! However at a painfully slow rate. Higher tiers give you vastly better repairs, and bigger ships repair smaller ships way faster. - them.try_repair(0.5+tier-(them.mass-us.mass)) + tank.reagents.remove_reagent(/datum/reagent/hull_repair_juice, foam_consumption) + // You can repair the main ship too, but at a much slower rate than normal due to the increased mass. + // For reference: Fighters are repaired at a speed of 25 damage per second if done by hand. + them.try_repair(CEILING(10 * tier - (them.mass - us.mass), 5)) us.relay('sound/items/welder.ogg') them.relay('sound/items/welder2.ogg') diff --git a/nsv13/code/modules/overmap/overmap.dm b/nsv13/code/modules/overmap/overmap.dm index 86da1c84a30..e43a5e7c8f5 100644 --- a/nsv13/code/modules/overmap/overmap.dm +++ b/nsv13/code/modules/overmap/overmap.dm @@ -638,6 +638,7 @@ Proc to spool up a new Z-level for a player ship and assign it a treadmill. to_chat(gunner, "Target painted.") relay('nsv13/sound/effects/fighters/locked.ogg', message=null, loop=FALSE, channel=CHANNEL_IMPORTANT_SHIP_ALERT) RegisterSignal(target, list(COMSIG_PARENT_QDELETING, COMSIG_FTL_STATE_CHANGE), PROC_REF(dump_lock)) + SEND_SIGNAL(src, COMSIG_TARGET_PAINTED, target) if(autotarget) select_target(target) //autopaint our target @@ -652,13 +653,14 @@ Proc to spool up a new Z-level for a player ship and assign it a treadmill. update_gunner_cam() return target_lock = target + SEND_SIGNAL(src, COMSIG_TARGET_LOCKED, target) /obj/structure/overmap/proc/dump_lock(obj/structure/overmap/target) // Our locked target got destroyed/moved, dump the lock SIGNAL_HANDLER SEND_SIGNAL(src, COMSIG_LOCK_LOST, target) target_painted -= target target_last_tracked -= target - UnregisterSignal(target, COMSIG_PARENT_QDELETING) + UnregisterSignal(target, list(COMSIG_PARENT_QDELETING, COMSIG_FTL_STATE_CHANGE)) if(target_lock == target) update_gunner_cam() target_lock = null diff --git a/nsv13/code/modules/overmap/radar.dm b/nsv13/code/modules/overmap/radar.dm index b619f4bda09..23a7217a028 100644 --- a/nsv13/code/modules/overmap/radar.dm +++ b/nsv13/code/modules/overmap/radar.dm @@ -282,7 +282,7 @@ Called by add_sensor_profile_penalty if remove_in is used. if(!target) //Anomalies don't count. return if(dradis_targeting && (linked.gunner == usr || linked.pilot == usr)) - if(target.faction != linked.faction) + if(target.faction != linked.faction || (linked.can_friendly_fire() && !linked.target_lock)) linked.start_lockon(target) return linked.datalink_transmit(target) diff --git a/rust_g.dll b/rust_g.dll index 3c53cf871af..bccb89a274b 100644 Binary files a/rust_g.dll and b/rust_g.dll differ