diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 9f6fa4bedc57c..56f978ef8bdb9 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -49,7 +49,7 @@ jobs: run: | ~/dmdoc - name: Deploy - uses: JamesIves/github-pages-deploy-action@a1ea191d508feb8485aceba848389d49f80ca2dc + uses: JamesIves/github-pages-deploy-action@65b5dfd4f5bcd3a7403bbc2959c144256167464e with: token: ${{ secrets.GITHUB_TOKEN }} branch: gh-pages diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b6424ef67731..ba7ccd097e1fd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,8 @@ env: BYOND_MAJOR: "514" BYOND_MINOR: "1589" SPACEMAN_DMM_VERSION: suite-1.7.3 + RUST_G_REPO: "ss220-space/rust-g-tg" + RUST_G_VERSION: "3.0.0-ss220" jobs: PreFlight: @@ -94,6 +96,15 @@ jobs: with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} + - name: Install rust_g dependencies + run: ./scripts/install-rust_g-dependencies.sh + - name: Setup rust_g cache + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: ~/.byond/bin/librust_g.so + key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" + - name: Install rust_g + run: ./scripts/install-rust_g.sh - name: Run Tests env: TEST: MAP @@ -123,6 +134,15 @@ jobs: with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} + - name: Install rust_g dependencies + run: ./scripts/install-rust_g-dependencies.sh + - name: Setup rust_g cache + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: ~/.byond/bin/librust_g.so + key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" + - name: Install rust_g + run: ./scripts/install-rust_g.sh - name: Run Tests env: TEST: MAP @@ -152,6 +172,15 @@ jobs: with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} + - name: Install rust_g dependencies + run: ./scripts/install-rust_g-dependencies.sh + - name: Setup rust_g cache + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: ~/.byond/bin/librust_g.so + key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" + - name: Install rust_g + run: ./scripts/install-rust_g.sh - name: Run Tests env: TEST: MAP diff --git a/baystation12.dme b/baystation12.dme index 32356fef5188f..310cd15421ca8 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -88,6 +88,7 @@ #include "code\__defines\xenoarcheaology.dm" #include "code\__defines\ZAS.dm" #include "code\__defines\zmimic.dm" +#include "code\__defines\~mods\rust_g.dm" #include "code\__defines\~mods\expanded_culture_descriptor.dm" #include "code\__defines\~mods\~master_defines.dm" #include "code\_global_vars\edible.dm" @@ -361,6 +362,7 @@ #include "code\datums\observation\destroyed.dm" #include "code\datums\observation\dir_set.dm" #include "code\datums\observation\dismembered.dm" +#include "code\datums\observation\empd.dm" #include "code\datums\observation\entered.dm" #include "code\datums\observation\equipped.dm" #include "code\datums\observation\exited.dm" @@ -3322,10 +3324,12 @@ #include "maps\_maps.dm" #include "mods\_modpack.dm" #include "mods\global_modpacks.dm" +#include "mods\_master_files\code\game\world.dm" #include "mods\_master_files\code\game\gamemodes\ert.dm" #include "mods\_master_files\code\game\objects\effects\decals\contraband.dm" #include "mods\_master_files\code\game\objects\structures\crates_lockers\closets\_closet_appearance_definitions.dm" #include "mods\_master_files\code\modules\client\asset_cache.dm" +#include "mods\_master_files\code\modules\client\preferences_persist.dm" #include "mods\_master_files\code\modules\clothing\spacesuits\spacesuits.dm" #include "mods\_master_files\code\modules\culture_descriptor\_culture.dm" #include "mods\_master_files\code\modules\culture_descriptor\culture\cultures_adherent.dm" diff --git a/code/__defines/~mods/rust_g.dm b/code/__defines/~mods/rust_g.dm new file mode 100644 index 0000000000000..fdbe3ab4cfe18 --- /dev/null +++ b/code/__defines/~mods/rust_g.dm @@ -0,0 +1,196 @@ +// 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. + +// Enable replacement rust-g functions for certain builtins. Off by default. +#define RUSTG_OVERRIDE_BUILTINS + +#ifndef RUST_G +// 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" + +#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 + + +/** + * 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. + * + * 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) \ + RUSTG_CALL(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height) + +#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) +/** + * input: must be a path, not an /icon; you have to do your own handling if it is one, as icon objects can't be directly passed to rustg. + * + * output: json_encode'd list. json_decode to get a flat list with icon states in the order they're in inside the .dmi + */ +#define rustg_dmi_icon_states(fname) RUSTG_CALL(RUST_G, "dmi_icon_states")(fname) + +#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 fexists(fname) (rustg_file_exists("[fname]") == "true") +#endif + +#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_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" +#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) (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_log_write_formatted(log, text) rustg_log_write(log, text, "true") +#define rustg_log_write_no_format(log, text) rustg_log_write(log, text, "false") + +#define rustg_noise_get_at_coordinates(seed, x, y) RUSTG_CALL(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_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) + +/// Returns the timestamp as a string +/proc/rustg_unix_timestamp() + return RUSTG_CALL(RUST_G, "unix_timestamp")() + +#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_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) +#endif diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 3e7d1fd10b2de..946a56ed365b3 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -30,7 +30,10 @@ var/global/log_end= world.system_type == UNIX ? ascii2text(13) : "" to_world_log("## TESTING: [msg][log_end]") /proc/game_log(category, text) - to_file(global.diary, "\[[time_stamp()]] [game_id] [category]: [text][log_end]") + // [SIERRA-EDIT] - RUST_G + // to_file(global.diary, "\[[time_stamp()]] [game_id] [category]: [text][log_end]") // SIERRA-EDIT - ORIGINAL + rustg_log_write_formatted("[GLOB.log_directory]/game.log", "[category]: [text]") + // [/SIERRA-EDIT] /proc/log_admin(text) GLOB.admin_log.Add(text) diff --git a/code/_macros.dm b/code/_macros.dm index b4a22b3d029e7..6a1ce3fc97dfc 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -123,7 +123,10 @@ /// Common use #define legacy_chat(target, message) to_target(target, message) #define to_world(message) to_chat(world, message) -#define to_world_log(message) to_target(world.log, message) +// [SIERRA-EDIT] - RUST_G +// #define to_world_log(message) to_target(world.log, message) // SIERRA-EDIT - ORIGINAL +#define to_world_log(message) if (istext(world.log)) { rustg_log_write_formatted(world.log, message) } else { to_target(world.log, message) } +// [/SIERRA-EDIT] #define sound_to(target, sound) to_target(target, sound) #define image_to(target, image) to_target(target, image) #define show_browser(target, content, title) to_target(target, browse(content, title)) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 095f3e0c0c7dc..023d5398c2139 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -66,8 +66,12 @@ var/global/datum/controller/master/Master = new /datum/controller/master/New() Uptime() //Uptime as close to boot as possible to set its statics + // [SIERRA-REMOVE] - RUST_G + /* if (!global.diary) global.diary = file("data/logs/[time2text(world.timeofday, "YYYY/MM/DD", -world.timezone)].log") + */ + // [/SIERRA-REMOVE] if (!config) config = new total_run_times = list() diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 9cae056c008fa..818f48c7e81d2 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -65,8 +65,11 @@ SUBSYSTEM_DEF(garbage) qdel_log += "\tSleeps: [details.slept_destroy]" if (details.no_hint) qdel_log += "\tNo hint: [details.no_hint] times" - var/log_file = file("[GLOB.log_directory]/qdel.log") - to_file(log_file, jointext(qdel_log, "\n")) + // [SIERRA-EDIT] - RUST_G + // var/log_file = file("[GLOB.log_directory]/qdel.log") // SIERRA-EDIT - ORIGINAL + // to_file(log_file, jointext(qdel_log, "\n")) // SIERRA-EDIT - ORIGINAL + rustg_log_write_formatted("[GLOB.log_directory]/qdel.log", jointext(qdel_log, "\n")) + // [/SIERRA-EDIT] /datum/controller/subsystem/garbage/Initialize(start_uptime) diff --git a/code/datums/extensions/chameleon.dm b/code/datums/extensions/chameleon.dm index c361e72e052d6..ef447a8175822 100644 --- a/code/datums/extensions/chameleon.dm +++ b/code/datums/extensions/chameleon.dm @@ -2,10 +2,14 @@ base_type = /datum/extension/chameleon expected_type = /obj/item flags = EXTENSION_FLAG_IMMEDIATE - var/list/chameleon_choices + var/emp_amount = 0 var/static/list/chameleon_choices_by_type - var/atom/atom_holder - var/chameleon_verb + var/chameleon_choices + var/obj/item/item_holder + var/static/chameleon_verbs = list( + /obj/item/proc/ChameleonFlexibleAppearance, + /obj/item/proc/ChameleonOutfitAppearanceSingle, + /obj/item/proc/ChameleonOutfitAppearanceAll) /datum/extension/chameleon/New(datum/holder, base_type) ..() @@ -13,72 +17,55 @@ if (!chameleon_choices) var/chameleon_type = base_type || holder.parent_type chameleon_choices = LAZYACCESS(chameleon_choices_by_type, chameleon_type) - if(!chameleon_choices) - chameleon_choices = generate_chameleon_choices(chameleon_type) - LAZYSET(chameleon_choices_by_type, chameleon_type, chameleon_choices) - else - var/list/choices = list() - for(var/path in chameleon_choices) - add_chameleon_choice(choices, path) - chameleon_choices = sortAssoc(choices) + if (!chameleon_choices) + chameleon_choices = GenerateChameleonChoices(chameleon_type) - atom_holder = holder - chameleon_verb = /atom/proc/chameleon_appearance - atom_holder.verbs += chameleon_verb + item_holder = holder + item_holder.verbs += chameleon_verbs + GLOB.empd_event.register(item_holder, src, /datum/extension/chameleon/proc/OnEMP) /datum/extension/chameleon/Destroy() - . = ..() - atom_holder.verbs -= chameleon_verb - atom_holder = null - -/datum/extension/chameleon/proc/disguise(newtype, mob/user, newname, newdesc) - var/obj/item/copy = new newtype(null) //initial() does not handle lists well - var/obj/item/C = atom_holder - if (newname) - C.name = newname - else - C.name = copy.name - if (newdesc) - C.desc = newdesc - else - C.desc = copy.desc - C.icon = copy.icon - C.color = copy.color - C.icon_state = copy.icon_state - C.flags_inv = copy.flags_inv - C.item_state = copy.item_state - C.body_parts_covered = copy.body_parts_covered - - C.item_icons = copy.item_icons - C.item_state_slots = copy.item_state_slots - C.sprite_sheets = copy.sprite_sheets - - OnDisguise(copy) + if (emp_amount) + STOP_PROCESSING(SSobj, src) + GLOB.empd_event.unregister(item_holder) + item_holder.verbs -= chameleon_verbs + item_holder = null + return ..() + +/datum/extension/chameleon/proc/Disguise(newtype, newname, newdesc) + SHOULD_NOT_OVERRIDE(TRUE) // Subtypes should override OnDisguise + + var/obj/item/copy = new newtype(null) // initial() does not handle lists well + item_holder.name = newname || copy.name + item_holder.desc = newdesc || copy.desc + item_holder.icon = copy.icon + item_holder.color = copy.color + item_holder.icon_state = copy.icon_state + item_holder.flags_inv = copy.flags_inv + item_holder.item_state = copy.item_state + item_holder.body_parts_covered = copy.body_parts_covered + + item_holder.item_icons = copy.item_icons + item_holder.item_state_slots = copy.item_state_slots + item_holder.sprite_sheets = copy.sprite_sheets + + OnDisguise(item_holder, copy) qdel(copy) -/datum/extension/chameleon/proc/OnDisguise(obj/item/copy) +/datum/extension/chameleon/proc/OnDisguise(obj/item/holder, obj/item/copy) + return -/datum/extension/chameleon/clothing - expected_type = /obj/item/clothing +/datum/extension/chameleon/proc/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + return null -/datum/extension/chameleon/clothing/accessory - expected_type = /obj/item/clothing/accessory +/datum/extension/chameleon/proc/GenerateChameleonChoices(basetype) + var/choices = list() + var/types = islist(basetype) ? basetype : typesof(basetype) + for (var/path in types) + AddChameleonChoice(choices, path) + return sortAssoc(choices) -/datum/extension/chameleon/clothing/accessory/OnDisguise(obj/item/clothing/accessory/copy) - ..() - var/obj/item/clothing/accessory/A = holder - - A.slot = copy.slot - A.parent = copy.parent - A.inv_overlay = copy.inv_overlay - A.mob_overlay = copy.mob_overlay - A.overlay_state = copy.overlay_state - A.accessory_icons = copy.accessory_icons - A.on_rolled_down = copy.on_rolled_down - A.on_rolled_sleeves = copy.on_rolled_sleeves - A.accessory_flags = copy.accessory_flags - -/datum/extension/chameleon/proc/add_chameleon_choice(list/target, path) +/datum/extension/chameleon/proc/AddChameleonChoice(list/target, path) var/obj/item/I = path if (initial(I.icon) && initial(I.icon_state) && !(initial(I.item_flags) & ITEM_FLAG_INVALID_FOR_CHAMELEON)) var/name = initial(I.name) @@ -92,78 +79,221 @@ else target[name] = path -/datum/extension/chameleon/proc/generate_chameleon_choices(basetype) - var/choices = list() - var/types = islist(basetype) ? basetype : typesof(basetype) - for (var/path in types) - add_chameleon_choice(choices, path) - return sortAssoc(choices) +/datum/extension/chameleon/proc/OnEMP(holder, severity) + if(!prob(50/severity)) + return + + if (emp_amount == 0) + START_PROCESSING(SSobj, src) + + emp_amount += rand((30 SECONDS)/severity, (1 MINUTE)/severity) + emp_amount = min(2 MINUTES, emp_amount) // Cap EMP duration to 2 minutes + Malfunction() + +/datum/extension/chameleon/Process(wait) + var/trigger = FALSE + // For each second, check if a malfunction is triggered + for (var/i = 1 to ceil(wait / (1 SECOND))) + // There's a (EMP seconds left / 2) probability of another malfunction triggering + if (!trigger && prob(emp_amount / 2 / 1 SECOND)) + trigger = TRUE + emp_amount -= 10 SECONDS // If a malfunction did trigger, we're kind and reduce the remaining time by 10 seconds + else // Otherwise we only reduce it by 1 second + emp_amount -= 1 SECOND + + if (trigger) + Malfunction() + + if (emp_amount <= 0) + emp_amount = 0 + STOP_PROCESSING(SSobj, src) + +/datum/extension/chameleon/proc/Malfunction() + playsound(item_holder.loc, "sparks", 75, 1, -1) + Disguise(chameleon_choices[pick(chameleon_choices)]) /** - * Verb to handle changing the appearance of atoms that have the chameleon extension. + * Verbs to handle changing the appearance of atoms that have the chameleon extension. */ -/atom/proc/chameleon_appearance() - set name = "Change Appearance" +/obj/item/proc/ChameleonFlexibleAppearance() + set name = "Change Appearance - Flexible" set desc = "Activate the holographic appearance changing module." set category = "Object" - if (!CanPhysicallyInteract(usr)) + if (!CanPhysicallyInteractWith(usr, src)) return - if (has_extension(src,/datum/extension/chameleon)) - var/datum/extension/chameleon/C = get_extension(src, /datum/extension/chameleon) - C.change(usr) + + var/datum/extension/chameleon/C = get_extension(src, /datum/extension/chameleon) + if (C) + C.ChangeGeneral(usr) else - src.verbs -= /atom/proc/chameleon_appearance + src.verbs -= C.chameleon_verbs -/datum/extension/chameleon/proc/change(mob/user) +/datum/extension/chameleon/proc/ChangeGeneral(mob/user) var/choice = input(user, "Select a new appearance", "Select appearance") as null|anything in chameleon_choices - if (choice) - var/newname = input(user, "Choose a new name, or leave blank to use the default", "Choose item name") as null|text - var/newdesc = input(user, "Choose a new description, or leave blank to use the default", "Choose item description") as null|text - if (QDELETED(user) || QDELETED(holder)) - return - if(user.incapacitated() || !(holder in user)) - to_chat(user, SPAN_WARNING("You can't reach \the [holder].")) - return - disguise(chameleon_choices[choice], user, newname, newdesc) - OnChange(user,holder) - -/datum/extension/chameleon/proc/OnChange(mob/user, obj/item/clothing/C) //contains icon updates - if (istype(C)) - C.update_clothing_icon() + if (!choice) + return + + var/newname = input(user, "Choose a new name, or leave blank to use the default", "Choose item name") as null|text + var/newdesc = input(user, "Choose a new description, or leave blank to use the default", "Choose item description") as null|text + if(!CanPhysicallyInteractWith(user, holder)) + to_chat(user, SPAN_WARNING("You can't reach \the [holder].")) + return + Disguise(chameleon_choices[choice], newname, newdesc) + +/obj/item/proc/ChameleonOutfitAppearanceSingle() + set name = "Change Appearance - Outfit (Selected Only)" + set desc = "Activate the holographic appearance changing module." + set category = "Object" + + if (!CanPhysicallyInteractWith(usr, src)) + return + + var/datum/extension/chameleon/C = get_extension(src, /datum/extension/chameleon) + if (C) + C.ChangeOutfitSingle(usr) + else + src.verbs -= C.chameleon_verbs + +/datum/extension/chameleon/proc/ChangeOutfitSingle(mob/user) + var/choice = input(user, "Select a new appearance for the selected chameleon item", "Select appearance") as null|anything in outfits() + if (!choice) + return + if(!CanPhysicallyInteractWith(user, holder)) + to_chat(user, SPAN_WARNING("You can't reach \the [holder].")) + return + SetOutfitAppearance(user, list(src), choice) + +/obj/item/proc/ChameleonOutfitAppearanceAll() + set name = "Change Appearance - Outfit (All Equipped)" + set desc = "Activate the holographic appearance changing module." + set category = "Object" + + if (!CanPhysicallyInteractWith(usr, src)) + return + + var/datum/extension/chameleon/C = get_extension(src, /datum/extension/chameleon) + if (C) + C.ChangeOutfitAll(usr) + else + src.verbs -= C.chameleon_verbs +/datum/extension/chameleon/proc/ChangeOutfitAll(mob/user) + var/choice = input(usr, "Select a new appearance for the selected chameleon item", "Select appearance") as null|anything in outfits() + if (!choice) + return + if(!CanPhysicallyInteractWith(user, holder)) + to_chat(usr, SPAN_WARNING("You can't reach \the [holder].")) + return + + var/list/extensions = list() + for (var/obj/item/I as anything in user.get_equipped_items(TRUE)) + var/extension = get_extension(I, /datum/extension/chameleon) + if (extension) + extensions += extension + extensions |= src + SetOutfitAppearance(user, extensions, choice) + +/datum/extension/chameleon/proc/SetOutfitAppearance(mob/user, list/chameleon_extensions, singleton/hierarchy/outfit/outfit) + for (var/datum/extension/chameleon/chameleon_extension as anything in chameleon_extensions) + var/outfit_type = chameleon_extension.GetItemDisguiseType(outfit) + if (outfit_type) + to_chat(user, SPAN_NOTICE("The outfit '[outfit]' appearance was applied to \the [chameleon_extension.holder].")); + chameleon_extension.Disguise(outfit_type) + else + to_chat(user, SPAN_WARNING("The outfit '[outfit]' had no suitable appearance for \the [chameleon_extension.holder].")); + +/******************** +* Subtype overrides * +********************/ /datum/extension/chameleon/backpack expected_type = /obj/item/storage/backpack -/datum/extension/chameleon/backpack/OnChange(mob/user, obj/item/storage/backpack/C) - if (ismob(C.loc)) - var/mob/M = C.loc +/datum/extension/chameleon/backpack/OnDisguise(obj/item/storage/backpack/holder, obj/item/copy) + if (ismob(holder.loc)) + var/mob/M = holder.loc M.update_inv_back() -/datum/extension/chameleon/headset - expected_type = /obj/item/device/radio/headset +/datum/extension/chameleon/backpack/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.back, expected_type)) + return outfit.back + for (var/potential_backpack_type in list_values(outfit.backpack_overrides)) + if (ispath(potential_backpack_type, expected_type)) + return potential_backpack_type -/datum/extension/chameleon/headset/OnChange(mob/user, obj/item/device/radio/headset/C) - if (ismob(C.loc)) - var/mob/M = C.loc - M.update_inv_ears() +/datum/extension/chameleon/clothing + expected_type = /obj/item/clothing -/datum/extension/chameleon/gun - expected_type = /obj/item/gun +/datum/extension/chameleon/clothing/OnDisguise(obj/item/clothing/holder, obj/item/copy) + SHOULD_CALL_PARENT(TRUE) + ..() + if (istype(holder)) + holder.update_clothing_icon() -/datum/extension/chameleon/gun/OnChange(mob/user, obj/item/gun/C) - if (ismob(C.loc)) - var/mob/M = C.loc - M.update_inv_r_hand() - M.update_inv_l_hand() +/datum/extension/chameleon/clothing/accessory + expected_type = /obj/item/clothing/accessory + +/datum/extension/chameleon/clothing/accessory/OnDisguise(obj/item/clothing/accessory/holder, obj/item/clothing/accessory/copy) + holder.slot = copy.slot + holder.parent = copy.parent + holder.inv_overlay = copy.inv_overlay + holder.mob_overlay = copy.mob_overlay + holder.overlay_state = copy.overlay_state + holder.accessory_icons = copy.accessory_icons + holder.on_rolled_down = copy.on_rolled_down + holder.on_rolled_sleeves = copy.on_rolled_sleeves + holder.accessory_flags = copy.accessory_flags + ..() + +/datum/extension/chameleon/clothing/glasses + expected_type = /obj/item/clothing/glasses + +/datum/extension/chameleon/clothing/glasses/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.glasses, expected_type)) + return outfit.glasses + +/datum/extension/chameleon/clothing/gloves + expected_type = /obj/item/clothing/gloves + +/datum/extension/chameleon/clothing/gloves/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.gloves, expected_type)) + return outfit.gloves -/datum/extension/chameleon/gun/OnDisguise(obj/item/gun/copy) - var/obj/item/gun/G = atom_holder +/datum/extension/chameleon/clothing/head + expected_type = /obj/item/clothing/head - G.flags_inv = copy.flags_inv - G.fire_sound = copy.fire_sound - G.fire_sound_text = copy.fire_sound_text - G.icon = copy.icon +/datum/extension/chameleon/clothing/head/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.head, expected_type)) + return outfit.head + +/datum/extension/chameleon/clothing/mask + expected_type = /obj/item/clothing/mask + +/datum/extension/chameleon/clothing/mask/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.mask, expected_type)) + return outfit.mask + +/datum/extension/chameleon/clothing/shoes + expected_type = /obj/item/clothing/shoes + +/datum/extension/chameleon/clothing/shoes/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + ..() + if (ispath(outfit.shoes, expected_type)) + return outfit.shoes + +/datum/extension/chameleon/clothing/suit + expected_type = /obj/item/clothing/suit + +/datum/extension/chameleon/clothing/suit/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.suit, expected_type)) + return outfit.suit + +/datum/extension/chameleon/clothing/under + expected_type = /obj/item/clothing/under + +/datum/extension/chameleon/clothing/under/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.uniform, expected_type)) + return outfit.uniform /datum/extension/chameleon/emag expected_type = /obj/item/card @@ -175,3 +305,36 @@ /obj/item/card/data/disk, /obj/item/card/id ) + +/datum/extension/chameleon/emag/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (length(outfit.id_types) > 0) + var/id_path = outfit.id_types[0] + if (ispath(id_path, expected_type)) + return id_path + +/datum/extension/chameleon/gun + expected_type = /obj/item/gun + +/datum/extension/chameleon/gun/OnDisguise(obj/item/gun/holder, obj/item/gun/copy) + holder.flags_inv = copy.flags_inv + holder.fire_sound = copy.fire_sound + holder.fire_sound_text = copy.fire_sound_text + + if (ismob(holder.loc)) + var/mob/M = holder.loc + M.update_inv_r_hand() + M.update_inv_l_hand() + +/datum/extension/chameleon/headset + expected_type = /obj/item/device/radio/headset + +/datum/extension/chameleon/headset/OnDisguise(obj/item/holder, obj/item/copy) + if (ismob(holder.loc)) + var/mob/M = holder.loc + M.update_inv_ears() + +/datum/extension/chameleon/headset/GetItemDisguiseType(singleton/hierarchy/outfit/outfit) + if (ispath(outfit.l_ear, expected_type)) + return outfit.l_ear + if (ispath(outfit.r_ear, expected_type)) + return outfit.r_ear diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 49c11cc498065..104462f52e65e 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -11,6 +11,8 @@ var/global/datum/getrev/revdata = new() if(length(head_branch)) branch = copytext(head_branch[1], 17) + // [SIERRA-REMOVE] - RUST_G + /* var/list/head_log = file2list(".git/logs/HEAD", "\n") for(var/line=length(head_log), line>=1, line--) if(head_log[line]) @@ -23,6 +25,12 @@ var/global/datum/getrev/revdata = new() if(unix_time) date = unix2date(unix_time) break + */ + // [/SIERRA-REMOVE] + // [SIERRA-ADD] - RUST_G + revision = rustg_git_revparse("HEAD") + date = rustg_git_commit_date("HEAD") + // [/SIERRA-ADD] to_world_log("Running revision:") to_world_log(branch) diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index 11a8627311d65..e464ade578a8a 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -140,9 +140,13 @@ /datum/movement_handler/mob/delay/DoMove(direction, mover, is_external) if(is_external) return - next_move = world.time + max(1, mob.movement_delay()) - // [SIERRA-ADD] - GLIDING + // [SIERRA-EDIT] - SSINPUT + // next_move = world.time + max(1, mob.movement_delay()) // SIERRA-EDIT - ORIGINAL delay = max(1, mob.movement_delay()) + if((direction & (direction - 1))) // moved diagonally + delay *= sqrt(2) + + next_move = world.time + delay UpdateGlideSize() // [/SIERRA-ADD] diff --git a/code/datums/observation/empd.dm b/code/datums/observation/empd.dm new file mode 100644 index 0000000000000..0ca15228c033f --- /dev/null +++ b/code/datums/observation/empd.dm @@ -0,0 +1,13 @@ +// Observer Pattern Implementation: EMPd +// Registration type: /atom +// +// Raised when: A /atom instance is EMPd. +// +// Arguments that the called proc should expect: +// /atom/empd_instance: The instance that was EMPd. +// severity: The EMP severity + +GLOBAL_DATUM_INIT(empd_event, /singleton/observ/empd, new) + +/singleton/observ/empd + name = "EMPd" diff --git a/code/datums/supplypacks/nonessent.dm b/code/datums/supplypacks/nonessent.dm index 0e4b6b9be712b..a018d37bbe093 100644 --- a/code/datums/supplypacks/nonessent.dm +++ b/code/datums/supplypacks/nonessent.dm @@ -187,7 +187,7 @@ /obj/item/clothing/head/pirate, /obj/item/clothing/head/hasturhood, /obj/item/clothing/head/powdered_wig, - /obj/item/clothing/head/hairflower, + /obj/item/clothing/head/hairflower/red, /obj/item/clothing/head/hairflower/yellow, /obj/item/clothing/head/hairflower/blue, /obj/item/clothing/head/hairflower/pink, diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d01709522c080..cb218a01fd947 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -246,7 +246,6 @@ /atom/proc/HasProximity(atom/movable/AM as mob|obj) return - /** * Called when the atom is affected by an EMP. * @@ -254,10 +253,12 @@ * - `severity` Integer. The strength of the EMP, ranging from 1 to 3. NOTE: Lower numbers are stronger. */ /atom/proc/emp_act(severity) + SHOULD_CALL_PARENT(TRUE) if (get_max_health()) // No hitsound here - Doesn't make sense for EMPs. // Generalized - 75-125 damage at max, 38-63 at medium, 25-42 at minimum severities. damage_health(rand(75, 125) / severity, DAMAGE_EMP, severity = severity) + GLOB.empd_event.raise_event(src, severity) /** diff --git a/code/game/machinery/barrier.dm b/code/game/machinery/barrier.dm index a087e77090226..2edd80c1f1a90 100644 --- a/code/game/machinery/barrier.dm +++ b/code/game/machinery/barrier.dm @@ -86,6 +86,7 @@ return 1 /obj/machinery/barrier/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if (severity > EMP_ACT_LIGHT) return locked = FALSE @@ -94,6 +95,7 @@ if (severity > EMP_ACT_HEAVY) return sparks(3, 1, src) + GLOB.empd_event.raise_event(src, severity) emag_act() /obj/machinery/barrier/on_death() diff --git a/code/game/machinery/stasis_cage.dm b/code/game/machinery/stasis_cage.dm index e9534440f2520..3c6aff8c7fb68 100644 --- a/code/game/machinery/stasis_cage.dm +++ b/code/game/machinery/stasis_cage.dm @@ -260,6 +260,7 @@ var/global/const/STASISCAGE_WIRE_LOCK = 4 /obj/machinery/stasis_cage/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if (health_dead()) return if (inoperable()) @@ -280,7 +281,7 @@ var/global/const/STASISCAGE_WIRE_LOCK = 4 cell.emp_act(severity) update_icon() - + GLOB.empd_event.raise_event(src, severity) /obj/machinery/stasis_cage/on_update_icon() ClearOverlays() diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index 6f67316dbbe23..b04b12b5af725 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -180,6 +180,7 @@ var/global/list/all_gps_units = list() update_icon() /obj/item/device/gps/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if(emped) // Without a fancy callback system, this will have to do. return if(tracking) @@ -190,6 +191,7 @@ var/global/list/all_gps_units = list() emped = TRUE update_icon() addtimer(new Callback(src, .proc/reset_emp), duration) + GLOB.empd_event.raise_event(src, severity) /obj/item/device/gps/proc/reset_emp() emped = FALSE diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 102d299f09fb1..1fff8a8ac8bfa 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -323,8 +323,10 @@ M.show_message(SPAN_NOTICE("\The [src] flashes a message across its screen, \"Additional personalities available for download.\""), 3, SPAN_NOTICE("\The [src] bleeps electronically."), 2) /obj/item/device/paicard/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) for(var/mob/M in src) M.emp_act(severity) + GLOB.empd_event.raise_event(src, severity) /obj/item/device/paicard/ex_act(severity) if(pai) diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 28bb91637c0a0..2862b1f50a63c 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -179,7 +179,7 @@ var/global/const/NO_EMAG_ACT = -50 /obj/item/card/emag/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/emag) + set_extension(src, /datum/extension/chameleon/emag) /obj/item/card/emag/get_antag_info() . = ..() diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 578b4e8971ff8..23f25c25a27c5 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -105,6 +105,7 @@ /obj/item/melee/energy/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if (!active) return if (damaged) @@ -124,6 +125,7 @@ deactivate() update_icon() damaged = TRUE + GLOB.empd_event.raise_event(src, severity) /obj/item/melee/energy/get_storage_cost() diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 0597350de9c9f..c971366249516 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -270,6 +270,7 @@ /obj/item/shield/energy/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if (!active) return if (damaged) @@ -289,6 +290,7 @@ else deactivate() update_icon() + GLOB.empd_event.raise_event(src, severity) /obj/item/shield/energy/proc/UpdateSoundLoop() diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 10e25e3cbeba9..75f8af90675df 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -17,7 +17,7 @@ return list( /obj/item/clothing/head/that = 2, /obj/item/device/radio/headset/headset_service = 2, - /obj/item/clothing/head/hairflower, + /obj/item/clothing/head/hairflower/red, /obj/item/clothing/head/hairflower/pink, /obj/item/clothing/head/hairflower/yellow, /obj/item/clothing/head/hairflower/blue, diff --git a/code/game/world.dm b/code/game/world.dm index 97abf5ef9109b..2bf66098b3f7b 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -81,15 +81,24 @@ GLOBAL_VAR(href_logfile) SetupLogs() var/date_string = time2text(world.realtime, "YYYY/MM/DD") - to_file(global.diary, "[log_end]\n[log_end]\nStarting up. (ID: [game_id]) [time2text(world.timeofday, "hh:mm.ss")][log_end]\n---------------------[log_end]") + // [SIERRA-EDIT] - RUST_G + // to_file(global.diary, "[log_end]\n[log_end]\nStarting up. (ID: [game_id]) [time2text(world.timeofday, "hh:mm.ss")][log_end]\n---------------------[log_end]") // SIERRA-EDIT - ORIGINAL + rustg_log_write_formatted("[GLOB.log_directory]/game.log", "Starting up. (ID: [game_id])") + rustg_log_write_formatted("[GLOB.log_directory]/game.log", "---------------------------") + // [/SIERRA-EDIT] + if (config) if (config.server_name) name = "[config.server_name]" if (config.log_runtime) - var/runtime_log = file("data/logs/runtime/[date_string]_[time2text(world.timeofday, "hh:mm")]_[game_id].log") - to_file(runtime_log, "Game [game_id] starting up at [time2text(world.timeofday, "hh:mm.ss")]") - log = runtime_log + // [SIERRA-EDIT] - RUST_G + // var/runtime_log = file("data/logs/runtime/[date_string]_[time2text(world.timeofday, "hh:mm")]_[game_id].log") // SIERRA-EDIT - ORIGINAL + // to_file(runtime_log, "Game [game_id] starting up at [time2text(world.timeofday, "hh:mm.ss")]") // SIERRA-EDIT - ORIGINAL + // log = runtime_log // SIERRA-EDIT - ORIGINAL + log = "data/logs/runtime/[date_string]_[time2text(world.timeofday, "hh:mm")]_[game_id].log" + to_world_log("Game [game_id] starting up at [time2text(world.timeofday, "hh:mm.ss")]") + // [/SIERRA-EDIT] if (config.log_hrefs) GLOB.href_logfile = file("data/logs/[date_string] hrefs.htm") @@ -119,7 +128,12 @@ GLOBAL_VAR_INIT(world_topic_last, world.timeofday) /world/Topic(T, addr, master, key) - to_file(global.diary, "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]") + // [SIERRA-EDIT] - RUST_G + // to_file(global.diary, "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]") // SIERRA-EDIT - ORIGINAL + + // Currently we have no need in topic log + // game_log("TOPIC","url:\"[T]\", from:[addr], master:[master], key:[key][log_end]" ) + // [/SIERRA-EDIT] if (GLOB.world_topic_last > world.timeofday) GLOB.world_topic_throttle = list() //probably passed midnight @@ -515,6 +529,9 @@ GLOBAL_VAR_INIT(world_topic_last, world.timeofday) for(var/client/C in GLOB.clients) send_link(C, "byond://[config.server]") + // [SIERRA-ADD] - RUST_G - Past this point, no logging procs can be used, at risk of data loss. + rustg_log_close_all() + //[/SIERRA-ADD] if(config.wait_for_sigusr1_reboot && reason != 3) text2file("foo", "reboot_called") to_world(SPAN_DANGER("World reboot waiting for external scripts. Please be patient.")) diff --git a/code/modules/client/preference_setup/loadout/lists/headwear.dm b/code/modules/client/preference_setup/loadout/lists/headwear.dm index 033a0c8be7e46..3712c39393456 100644 --- a/code/modules/client/preference_setup/loadout/lists/headwear.dm +++ b/code/modules/client/preference_setup/loadout/lists/headwear.dm @@ -66,15 +66,7 @@ /datum/gear/head/hairflower display_name = "hair flower pin" path = /obj/item/clothing/head/hairflower - -/datum/gear/head/hairflower/New() - ..() - var/pins = list() - pins["blue pin"] = /obj/item/clothing/head/hairflower/blue - pins["pink pin"] = /obj/item/clothing/head/hairflower/pink - pins["red pin"] = /obj/item/clothing/head/hairflower - pins["yellow pin"] = /obj/item/clothing/head/hairflower/yellow - gear_tweaks += new/datum/gear_tweak/path(pins) + flags = GEAR_HAS_COLOR_SELECTION /datum/gear/head/hardhat display_name = "hardhat selection" diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 484c7af5bafe5..01fad96a891e3 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -9,7 +9,7 @@ /obj/item/clothing/under/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src,/datum/extension/chameleon/clothing/under) /obj/item/clothing/head/chameleon name = "cap" @@ -21,7 +21,7 @@ /obj/item/clothing/head/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src, /datum/extension/chameleon/clothing/head) /obj/item/clothing/suit/chameleon name = "armor" @@ -33,7 +33,7 @@ /obj/item/clothing/suit/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src, /datum/extension/chameleon/clothing/suit) /obj/item/clothing/shoes/chameleon name = "shoes" @@ -45,7 +45,7 @@ /obj/item/clothing/shoes/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src, /datum/extension/chameleon/clothing/shoes) /obj/item/storage/backpack/chameleon name = "backpack" @@ -69,7 +69,7 @@ /obj/item/clothing/gloves/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src, /datum/extension/chameleon/clothing/gloves) /obj/item/clothing/mask/chameleon name = "mask" @@ -81,7 +81,7 @@ /obj/item/clothing/mask/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing,/obj/item/clothing/mask) + set_extension(src, /datum/extension/chameleon/clothing/mask) /obj/item/clothing/glasses/chameleon name = "goggles" @@ -93,7 +93,7 @@ /obj/item/clothing/glasses/chameleon/Initialize() . = ..() - set_extension(src,/datum/extension/chameleon/clothing) + set_extension(src, /datum/extension/chameleon/clothing/glasses) /obj/item/device/radio/headset/chameleon name = "radio headset" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index fec542d75d76d..c96da0458d9ba 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -14,18 +14,21 @@ /obj/item/clothing/head/hairflower name = "hair flower pin" icon_state = "hairflower" - desc = "Smells nice." + desc = "A floral pin with a clip on the back to attach to hair." slot_flags = SLOT_HEAD | SLOT_EARS body_parts_covered = 0 +/obj/item/clothing/head/hairflower/red + color = COLOR_RED + /obj/item/clothing/head/hairflower/blue - icon_state = "hairflower_blue" + color = COLOR_BLUE /obj/item/clothing/head/hairflower/pink - icon_state = "hairflower_pink" + color = COLOR_PINK /obj/item/clothing/head/hairflower/yellow - icon_state = "hairflower_yellow" + color = COLOR_YELLOW /obj/item/clothing/head/hairflower/bow icon_state = "bow" diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index f042d43a9f501..3b76726b5d501 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -169,14 +169,32 @@ /obj/item/reagent_containers/food/snacks/grown/attackby(obj/item/W, mob/user) if(seed) - if(seed.get_trait(TRAIT_PRODUCES_POWER) && isCoil(W)) + if(isCoil(W)) var/obj/item/stack/cable_coil/C = W - if(C.use(5)) - //TODO: generalize this. + if(seed.get_trait(TRAIT_PRODUCT_ICON) in list("flower2","flower3","flower4","flower5","flower6")) + if(!C.can_use(1)) + USE_FEEDBACK_STACK_NOT_ENOUGH(C, 1, "to make a pin out of \the [src.name].") + return + C.use(1) + to_chat(user, SPAN_NOTICE("You add some wire to the [src.name] and make a pin.")) + var/obj/item/clothing/head/hairflower/pin = new /obj/item/clothing/head/hairflower(get_turf(src)) + pin.name = "[src.name] pin" + pin.icon = 'icons/obj/flora/hydroponics_products.dmi' + pin.icon_state = "[seed.get_trait(TRAIT_PRODUCT_ICON)]-product" + if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in icon_states('icons/obj/flora/hydroponics_products.dmi')) + var/image/fruit_leaves = image('icons/obj/flora/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf") + fruit_leaves.color = seed.get_trait(TRAIT_PLANT_COLOUR) + pin.AddOverlays(fruit_leaves) + pin.item_state = "hairflower" + pin.color = src.color + qdel(src) + return + else if(seed.get_trait(TRAIT_PRODUCES_POWER)) + if(!C.can_use(5)) + USE_FEEDBACK_STACK_NOT_ENOUGH(C, 5, "to wire \the [src.name].") + return to_chat(user, SPAN_NOTICE("You add some cable to the [src.name] and slide it inside the battery casing.")) - var/obj/item/cell/potato/pocell = new /obj/item/cell/potato(get_turf(user)) - if(src.loc == user && user.HasFreeHand() && istype(user,/mob/living/carbon/human)) - user.put_in_hands(pocell) + var/obj/item/cell/potato/pocell = new /obj/item/cell/potato(get_turf(src)) pocell.maxcharge = src.potency * 10 pocell.charge = pocell.maxcharge qdel(src) diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index b3e5b9012018b..e3a1b148c8e7d 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -1601,7 +1601,7 @@ /datum/seed/ximikoa name = "ximikoa" - seed_name = "ximi'koa stalks" + seed_name = "ximi'koa" display_name = "ximi'koa patch" chems = list(/datum/reagent/nutriment = list(1,2), /datum/reagent/sugar = list(4,5)) fruit_size = ITEM_SIZE_TINY diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index a7dbda672fb43..c35a425614445 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -41,8 +41,10 @@ activators = list("on toggle" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/input/toggle_button/emp_act() - return // This is a mainly physical thing, not affected by electricity +// This is a mainly physical thing, not affected by electricity +/obj/item/integrated_circuit/input/toggle_button/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) + GLOB.empd_event.raise_event(src, severity) /obj/item/integrated_circuit/input/toggle_button/get_topic_data(mob/user) return list("Toggle [get_pin_data(IC_OUTPUT, 1) ? "Off" : "On"]" = "toggle=1") diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 426c79677d583..c0f183595f3c2 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -51,6 +51,9 @@ /// Holder for skill value of current/recent operator for programs that tick. var/operator_skill = SKILL_MIN + /// How much processing size the program should take up. + var/processing_size = 1 + /datum/computer_file/program/Destroy() if(computer && computer.active_program == src) computer.kill_program(src) diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm index 50b7abf49b11d..abebf5762a4cf 100644 --- a/code/modules/modular_computers/file_system/programs/generic/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/generic/configurator.dm @@ -12,6 +12,7 @@ unsendable = 1 undeletable = 1 size = 4 + processing_size = 0.5 available_on_ntnet = FALSE requires_ntnet = FALSE nanomodule_path = /datum/nano_module/program/computer_configurator diff --git a/code/modules/modular_computers/file_system/programs/generic/email_client.dm b/code/modules/modular_computers/file_system/programs/generic/email_client.dm index badfcd97f100b..d6904ca8a7fa5 100644 --- a/code/modules/modular_computers/file_system/programs/generic/email_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/email_client.dm @@ -5,7 +5,8 @@ program_icon_state = "generic" program_key_state = "generic_key" program_menu_icon = "mail-closed" - size = 7 + size = 1 + processing_size = 0 //It's the only way to ensure people actually have it on. requires_ntnet = TRUE available_on_ntnet = TRUE var/stored_login = "" diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index aac088537fdca..eb3395399f5a4 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -6,6 +6,7 @@ program_key_state = "generic_key" program_menu_icon = "folder-collapsed" size = 8 + processing_size = 0.5 requires_ntnet = FALSE available_on_ntnet = FALSE undeletable = TRUE diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index 4928723295b11..79de2b0d5316c 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -8,6 +8,7 @@ unsendable = TRUE undeletable = TRUE size = 4 + processing_size = 0.5 requires_ntnet = TRUE requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD available_on_ntnet = FALSE diff --git a/code/modules/modular_computers/ntos/ntos.dm b/code/modules/modular_computers/ntos/ntos.dm index 5b7b94531d082..119b2ba403a01 100644 --- a/code/modules/modular_computers/ntos/ntos.dm +++ b/code/modules/modular_computers/ntos/ntos.dm @@ -7,8 +7,10 @@ var/on = FALSE /// A currently active program running on the computer. var/datum/computer_file/program/active_program = null - /// All programms currently running, background or active. + /// All programs currently running, background or active, including small programs. var/list/running_programs = list() + /// The processing size being used by all programs. + var/processing_size = 0 /// dmi where the screen overlays are kept, defaults to holder's icon if unset var/screen_icon_file @@ -140,22 +142,26 @@ /datum/extension/interactive/ntos/proc/run_program_remote(filename, mob/user = null, loud = 0) var/datum/computer_file/program/P = get_file(filename) - if(!istype(P)) + if (!istype(P)) loud && show_error(user, "I/O ERROR - Unable to run [filename]") return - if(!P.is_supported_by_hardware(get_hardware_flag())) + if (!P.is_supported_by_hardware(get_hardware_flag())) loud && show_error(user, "Hardware Error - Incompatible software") return - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) + if (P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) loud && show_error(user, "Unable to establish a working network connection. Please try again later. If problem persists, please contact your system administrator.") return - if(P in running_programs) + if (P in running_programs) return P - if(length(running_programs) >= get_program_capacity()) + + var/processing_total = 0 + for (var/datum/computer_file/program/program in running_programs) + processing_total += program.processing_size + if ((processing_total + P.processing_size) > get_program_capacity() && P.processing_size) loud && show_error(user, "Kernel Error - Insufficient CPU resources available to allocate.") return - if(!P.can_run(user, loud)) + if (!P.can_run(user, loud)) return P.on_startup(user, src) diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 759eecfb7a89e..493dadc7ac513 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -154,11 +154,16 @@ return var/burn_damage = 0 + var/rand_modifier = rand(1,3) switch (severity) if (EMP_ACT_HEAVY) - burn_damage = 30 + burn_damage = 10 * rand_modifier if (EMP_ACT_LIGHT) - burn_damage = 15 + burn_damage = 4 * rand_modifier + + /// Ions can't be aimed like conventional weaponry. This way damage is more even between center mass and limbs based on their total health relative to each other. + if(!(src.body_part & FULL_TORSO)) + burn_damage *= 0.5 var/mult = 1 + !!(BP_IS_ASSISTED(src)) // This macro returns (large) bitflags. burn_damage *= mult/species.get_burn_mod(owner) //ignore burn mod for EMP damage @@ -175,9 +180,6 @@ if(owner && limb_flags & ORGAN_FLAG_CAN_GRASP) owner.grasp_damage_disarm(src) - if(owner && limb_flags & ORGAN_FLAG_CAN_STAND) - owner.stance_damage_prone(src) - ..() /obj/item/organ/external/attack_self(mob/user) diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index a45788cbd9861..748cd4326d8bb 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -191,9 +191,10 @@ /obj/item/organ/internal/emp_act(severity) if(!BP_IS_ROBOTIC(src)) return + var/rand_modifier = rand(1, 3) switch (severity) if (EMP_ACT_HEAVY) - take_internal_damage(16) + take_internal_damage(5 * rand_modifier) if (EMP_ACT_LIGHT) - take_internal_damage(9) + take_internal_damage(2 * rand_modifier) ..() diff --git a/code/modules/organs/internal/species/fbp.dm b/code/modules/organs/internal/species/fbp.dm index acc7e73a8907a..e6e71c6e87714 100644 --- a/code/modules/organs/internal/species/fbp.dm +++ b/code/modules/organs/internal/species/fbp.dm @@ -56,6 +56,7 @@ if(!checked_use(cost) && owner.isSynthetic()) if(!owner.lying && !owner.buckled) to_chat(owner, SPAN_WARNING("You don't have enough energy to function!")) + owner.Weaken(3) owner.Paralyse(3) if(percent() < 10 && prob(1)) to_chat(owner, SPAN_WARNING("Your internal battery beeps an alert code, it is low on charge!")) diff --git a/code/modules/organs/internal/species/vox.dm b/code/modules/organs/internal/species/vox.dm index 18c255571bce0..c288d6850c1a2 100644 --- a/code/modules/organs/internal/species/vox.dm +++ b/code/modules/organs/internal/species/vox.dm @@ -193,6 +193,7 @@ to_chat(user, SPAN_NOTICE("The integrity light on [src] is off. It is empty and lifeless.")) /obj/item/organ/internal/voxstack/emp_act() + SHOULD_CALL_PARENT(FALSE) return /obj/item/organ/internal/voxstack/getToxLoss() diff --git a/code/modules/persistence/persistence_datum.dm b/code/modules/persistence/persistence_datum.dm index 71a70a9f9b810..5c6f33445e10b 100644 --- a/code/modules/persistence/persistence_datum.dm +++ b/code/modules/persistence/persistence_datum.dm @@ -106,9 +106,14 @@ for(var/thing in SSpersistence.tracking_values[type]) if(IsValidEntry(thing)) entries += list(CompileEntry(thing)) - if(fexists(filename)) - fdel(filename) - to_file(file(filename), json_encode(entries)) + // [SIERRA-EDIT] - RUST_G + // if(fexists(filename)) // SIERRA-EDIT - ORIGINAL + // fdel(filename) // SIERRA-EDIT - ORIGINAL + // to_file(file(filename), json_encode(entries)) // SIERRA-EDIT - ORIGINAL + var/error = rustg_file_write(json_encode(entries), filename) + if (error) + crash_with(error) + // [/SIERRA-EDIT] /datum/persistent/proc/RemoveValue(atom/value) qdel(value) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index e327da37f9d70..d3371de608b05 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -83,6 +83,7 @@ else to_chat(usr, SPAN_NOTICE("The generator is off.")) /obj/machinery/power/port_gen/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if(!active) return var/duration @@ -95,6 +96,7 @@ if(prob(25)) set_broken(TRUE) if(prob(10)) explode() else duration = 30 SECONDS + GLOB.empd_event.raise_event(src, severity) if(duration) set_stat(MACHINE_STAT_EMPED, TRUE) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index ee574f3feedb3..36c0e589ad867 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -138,6 +138,7 @@ efficiency *= 1 + (rand() - 1) * skill_modifier //subtract off between 0.8 and 0, depending on skill and luck. /obj/machinery/power/emitter/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) return /obj/machinery/power/emitter/Process() diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 239807e6e510d..511a3af123ce6 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -158,6 +158,7 @@ field_generator power level display /obj/machinery/field_generator/emp_act() + SHOULD_CALL_PARENT(FALSE) return /obj/machinery/field_generator/bullet_act(obj/item/projectile/Proj) diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 55f7c7ef0ba2e..3fd5a1946fea8 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -30,6 +30,7 @@ one_hand_penalty = 0 charge_cost = 40 max_shots = 3 + fire_delay = 30 projectile_type = /obj/item/projectile/ion/small /obj/item/gun/energy/ionrifle/mounted diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 03014073d406d..752bbd43336f3 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -124,4 +124,5 @@ return /obj/machinery/computer/shuttle_control/emp_act() + SHOULD_CALL_PARENT(FALSE) return diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 1638f4635be40..cb420de843231 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -671,16 +671,16 @@ // BROKEN PROSTHETIC SURGERY // ////////////////////////////////////////////////////////////////// -/singleton/surgery_step/robone +/singleton/surgery_step/robotics/robone surgery_candidate_flags = SURGERY_NO_FLESH | SURGERY_NO_CRYSTAL | SURGERY_NEEDS_ENCASEMENT var/required_stage = 0 -/singleton/surgery_step/robone/assess_bodypart(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/assess_bodypart(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = ..() if(affected && (affected.status & ORGAN_BROKEN) && affected.stage == required_stage) return affected -/singleton/surgery_step/robone/get_skill_reqs(mob/living/user, mob/living/carbon/human/target, obj/item/tool) +/singleton/surgery_step/robotics/robone/get_skill_reqs(mob/living/user, mob/living/carbon/human/target, obj/item/tool) if(target.isSynthetic()) return SURGERY_SKILLS_ROBOTIC else @@ -689,7 +689,7 @@ ////////////////////////////////////////////////////////////////// // welding surgery step ////////////////////////////////////////////////////////////////// -/singleton/surgery_step/robone/weld +/singleton/surgery_step/robotics/robone/weld name = "Begin structural support repair" allowed_tools = list( /obj/item/weldingtool = 50, @@ -699,7 +699,7 @@ min_duration = 50 max_duration = 60 -/singleton/surgery_step/robone/weld/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/weld/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s [affected.encased]" : "structural support in \the [target]'s [affected.name]" if (affected.stage == 0) @@ -710,7 +710,7 @@ playsound(target.loc, 'sound/items/Welder.ogg', 15, 1) ..() -/singleton/surgery_step/robone/weld/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/weld/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s [affected.encased]" : "structural support in \the [target]'s [affected.name]" user.visible_message( @@ -721,7 +721,7 @@ affected.stage = 1 affected.status &= ~ORGAN_BRITTLE -/singleton/surgery_step/robone/weld/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/weld/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) user.visible_message( SPAN_WARNING("\The [user]'s hand slips, causing damage with \the [tool] in the open panel on [target]'s [affected.name]!"), @@ -732,7 +732,7 @@ ////////////////////////////////////////////////////////////////// // prosthetic realignment surgery step ////////////////////////////////////////////////////////////////// -/singleton/surgery_step/robone/realign_support +/singleton/surgery_step/robotics/robone/realign_support name = "Realign support" allowed_tools = list( /obj/item/swapper/power_drill = 100, @@ -746,7 +746,7 @@ surgery_candidate_flags = SURGERY_NO_FLESH | SURGERY_NEEDS_ENCASEMENT required_stage = 1 -/singleton/surgery_step/robone/realign_support/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/realign_support/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s [affected.encased]" : "structural support in \the [target]'s [affected.name]" if(affected.encased == "skull") @@ -762,7 +762,7 @@ playsound(target.loc, 'sound/items/bonesetter.ogg', 50, TRUE) ..() -/singleton/surgery_step/robone/realign_support/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/realign_support/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s [affected.encased]" : "structural support in \the [target]'s [affected.name]" if (affected.status & ORGAN_BROKEN) @@ -784,7 +784,7 @@ ) affected.fracture() -/singleton/surgery_step/robone/realign_support/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/realign_support/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) user.visible_message( SPAN_WARNING("\The [user]'s hand slips, damaging the [affected.encased ? affected.encased : "structural support"] in \the [target]'s [affected.name] with \the [tool]!"), @@ -796,7 +796,7 @@ ////////////////////////////////////////////////////////////////// // post realignment surgery step ////////////////////////////////////////////////////////////////// -/singleton/surgery_step/robone/finish +/singleton/surgery_step/robotics/robone/finish name = "Finish structural support repair" allowed_tools = list( /obj/item/weldingtool = 50, @@ -807,7 +807,7 @@ max_duration = 60 required_stage = 2 -/singleton/surgery_step/robone/finish/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/finish/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s damaged [affected.encased]" : "structural support in \the [target]'s [affected.name]" user.visible_message( @@ -817,7 +817,7 @@ playsound(target.loc, 'sound/items/Welder.ogg', 15, 1) ..() -/singleton/surgery_step/robone/finish/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/finish/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) var/prosthetic = affected.encased ? "\the [target]'s damaged [affected.encased]" : "structural support in [target]'s [affected.name]" user.visible_message( @@ -828,7 +828,7 @@ affected.stage = 0 affected.update_wounds() -/singleton/surgery_step/robone/finish/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/singleton/surgery_step/robotics/robone/finish/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) user.visible_message( SPAN_WARNING("\The [user]'s hand slips, causing damage with \the [tool] in the open panel in [target]'s [affected.name]!"), diff --git a/code/modules/xenoarcheaology/anomaly_container.dm b/code/modules/xenoarcheaology/anomaly_container.dm index 80e801f938753..03cd776efc178 100644 --- a/code/modules/xenoarcheaology/anomaly_container.dm +++ b/code/modules/xenoarcheaology/anomaly_container.dm @@ -145,12 +145,14 @@ ..() /obj/machinery/anomaly_container/emp_act(severity) + SHOULD_CALL_PARENT(FALSE) if(health_dead) return if(contained) visible_message(SPAN_DANGER("\The [src]'s latches break loose, freeing the contents!")) playsound(loc, 'sound/mecha/hydraulic.ogg', 40) release() + GLOB.empd_event.raise_event(src, severity) /obj/machinery/anomaly_container/attackby(obj/item/P, mob/user) diff --git a/html/changelog.html b/html/changelog.html index a8100e0350ecf..c50a82914978a 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -28,7 +28,72 @@