From ba53d8cb9b2aef5b03146f8cd453243f3739ff5b Mon Sep 17 00:00:00 2001 From: Changelogs Date: Mon, 4 Sep 2023 01:07:27 +0000 Subject: [PATCH 01/12] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-4308.yml | 4 ---- html/changelogs/AutoChangeLog-pr-4316.yml | 5 ----- html/changelogs/archive/2023-09.yml | 6 ++++++ 3 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-4308.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-4316.yml diff --git a/html/changelogs/AutoChangeLog-pr-4308.yml b/html/changelogs/AutoChangeLog-pr-4308.yml deleted file mode 100644 index 10d65b66f0be..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4308.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Steelpoint" -delete-after: True -changes: - - spellcheck: "Box of donuts order from req is now spelt correctly" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-4316.yml b/html/changelogs/AutoChangeLog-pr-4316.yml deleted file mode 100644 index 61d078edfd78..000000000000 --- a/html/changelogs/AutoChangeLog-pr-4316.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Morrow" -delete-after: True -changes: - - bugfix: "Peri now works for brain damage" - - bugfix: "Brain damage movement will no longer occur while buckled and unconscious" \ No newline at end of file diff --git a/html/changelogs/archive/2023-09.yml b/html/changelogs/archive/2023-09.yml index f69a9a6dbc4c..79f138bc79dc 100644 --- a/html/changelogs/archive/2023-09.yml +++ b/html/changelogs/archive/2023-09.yml @@ -42,3 +42,9 @@ - imageadd: Moved some of the used sprites out of the unused category in pred_gear.dmi. - imagedel: Deleted old relay beacon sprite, deleted duplicate plasma pistol and thwei sprites. +2023-09-04: + Morrow: + - bugfix: Peri now works for brain damage + - bugfix: Brain damage movement will no longer occur while buckled and unconscious + Steelpoint: + - spellcheck: Box of donuts order from req is now spelt correctly From 5f9afd0d99328c0e9213008f15da8094d20e93bf Mon Sep 17 00:00:00 2001 From: Zonespace <41448081+Zonespace27@users.noreply.github.com> Date: Sun, 3 Sep 2023 23:33:30 -0700 Subject: [PATCH 02/12] Working Joes no longer get a cryo emergency kit (#4319) # About the pull request Title. Also gives the emergency kit the right amount of slots (2->3) # Explain why it's good for the game Joes are incapable of using guns and, in general, fighting back at all (some exceptions apply, but even then fists are better). # Testing Photographs and Procedure
Screenshots & Videos works
# Changelog :cl: fix: Working Joes no longer get cryo self defense kits fix: Cryo self defense kits now have the correct amount of slots /:cl: --- code/game/jobs/job/civilians/support/working_joe.dm | 1 + code/game/jobs/job/job.dm | 2 ++ code/modules/cm_marines/equipment/kit_boxes.dm | 2 +- code/modules/mob/new_player/new_player.dm | 9 +++++---- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/code/game/jobs/job/civilians/support/working_joe.dm b/code/game/jobs/job/civilians/support/working_joe.dm index d4b575f324f2..ce1c8010c216 100644 --- a/code/game/jobs/job/civilians/support/working_joe.dm +++ b/code/game/jobs/job/civilians/support/working_joe.dm @@ -12,6 +12,7 @@ flags_startup_parameters = ROLE_ADD_TO_DEFAULT|ROLE_WHITELISTED|ROLE_CUSTOM_SPAWN flags_whitelist = WHITELIST_JOE gear_preset = /datum/equipment_preset/synth/working_joe + gets_emergency_kit = FALSE job_options = list(STANDARD_VARIANT = "JOE", HAZMAT_VARIANT = "HAZ") var/standard = TRUE diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 234902e11d22..0785fc5c337f 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -37,6 +37,8 @@ /// When set you will be able to choose between the different job options when selecting your role. /// Associated list. Main list elements - actual options, associated values - shorthands for job preferences menu (keep those short). var/job_options + /// If TRUE, this job will spawn w/ a cryo emergency kit during evac/red alert + var/gets_emergency_kit = TRUE /datum/job/New() . = ..() diff --git a/code/modules/cm_marines/equipment/kit_boxes.dm b/code/modules/cm_marines/equipment/kit_boxes.dm index be60dce0e783..43cf733adb75 100644 --- a/code/modules/cm_marines/equipment/kit_boxes.dm +++ b/code/modules/cm_marines/equipment/kit_boxes.dm @@ -483,7 +483,7 @@ name = "\improper Cryo Self Defense Kit" desc = "A basic self-defense kit reserved for emergencies. As you might expect, not much care was put into keeping the stock fresh, who would be insane enough to attack a USCM ship directly?" icon_state = "cryo_defense_kit" - storage_slots = 2 + storage_slots = 3 /obj/item/storage/box/kit/cryo_self_defense/update_icon() if(LAZYLEN(contents)) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 9f88f0f9d611..5da499dabc52 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -228,6 +228,7 @@ new_player_panel() /mob/new_player/proc/AttemptLateSpawn(rank) + var/datum/job/player_rank = RoleAuthority.roles_for_mode[rank] if (src != usr) return if(SSticker.current_state != GAME_STATE_PLAYING) @@ -236,7 +237,7 @@ if(!enter_allowed) to_chat(usr, SPAN_WARNING("There is an administrative lock on entering the game! (The dropship likely crashed into the Almayer. This should take at most 20 minutes.)")) return - if(!RoleAuthority.assign_role(src, RoleAuthority.roles_for_mode[rank], 1)) + if(!RoleAuthority.assign_role(src, player_rank, 1)) to_chat(src, alert("[rank] is not available. Please try another.")) return @@ -244,16 +245,16 @@ close_spawn_windows() var/mob/living/carbon/human/character = create_character(TRUE) //creates the human and transfers vars and mind - RoleAuthority.equip_role(character, RoleAuthority.roles_for_mode[rank], late_join = TRUE) + RoleAuthority.equip_role(character, player_rank, late_join = TRUE) EquipCustomItems(character) - if(security_level > SEC_LEVEL_BLUE || EvacuationAuthority.evac_status) + if((security_level > SEC_LEVEL_BLUE || EvacuationAuthority.evac_status) && player_rank.gets_emergency_kit) to_chat(character, SPAN_HIGHDANGER("As you stagger out of hypersleep, the sleep bay blares: '[EvacuationAuthority.evac_status ? "VESSEL UNDERGOING EVACUATION PROCEDURES, SELF DEFENSE KIT PROVIDED" : "VESSEL IN HEIGHTENED ALERT STATUS, SELF DEFENSE KIT PROVIDED"]'.")) character.put_in_hands(new /obj/item/storage/box/kit/cryo_self_defense(character.loc)) GLOB.data_core.manifest_inject(character) SSticker.minds += character.mind//Cyborgs and AIs handle this in the transform proc. //TODO!!!!! ~Carn - SSticker.mode.latejoin_tally += RoleAuthority.calculate_role_weight(RoleAuthority.roles_for_mode[rank]) + SSticker.mode.latejoin_tally += RoleAuthority.calculate_role_weight(player_rank) for(var/datum/squad/sq in RoleAuthority.squads) if(sq) From 66a2329472318b890c0e0ba7844597e96825df6f Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 4 Sep 2023 07:42:15 +0100 Subject: [PATCH 03/12] Automatic changelog for PR #4319 [ci skip] --- html/changelogs/AutoChangeLog-pr-4319.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4319.yml diff --git a/html/changelogs/AutoChangeLog-pr-4319.yml b/html/changelogs/AutoChangeLog-pr-4319.yml new file mode 100644 index 000000000000..2eedcca45a4b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4319.yml @@ -0,0 +1,5 @@ +author: "Zonespace27" +delete-after: True +changes: + - bugfix: "Working Joes no longer get cryo self defense kits" + - bugfix: "Cryo self defense kits now have the correct amount of slots" \ No newline at end of file From 010c1819cbf344f1626f6fdd792f8e4bcfdae270 Mon Sep 17 00:00:00 2001 From: morrowwolf Date: Mon, 4 Sep 2023 02:43:46 -0400 Subject: [PATCH 04/12] Fixes perma helmet HUDs (#4322) # About the pull request Fixes https://github.com/cmss13-devs/cmss13/issues/4317 In the refactor to have all functionality in helmet_visors base it now requires the active_visor to *not* be the current visor to turn off so we gotta null active_visor before we turn it off. # Explain why it's good for the game Bug bad # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Morrow fix: Fixed perma helmet HUDs /:cl: --- code/modules/clothing/head/helmet.dm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 9da634eaba3e..91b29a51a5a4 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -494,19 +494,20 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list( return if(HAS_TRAIT(attacking_item, TRAIT_TOOL_SCREWDRIVER) && length(inserted_visors)) - for(var/obj/item/device/visor as anything in inserted_visors) + for(var/obj/item/device/helmet_visor/visor as anything in inserted_visors) visor.forceMove(get_turf(src)) inserted_visors = list() to_chat(user, SPAN_NOTICE("You remove the inserted visors.")) - turn_off_visor(user, active_visor, TRUE) + var/obj/item/device/helmet_visor/temp_visor_holder = active_visor + active_visor = null + turn_off_visor(user, temp_visor_holder, TRUE) var/datum/action/item_action/cycle_helmet_huds/cycle_action = locate() in actions cycle_action.set_default_overlay() if(!length(built_in_visors)) cycle_action.remove_from(user) - active_visor = null recalculate_visors(user) return From 2af77e56d94d48891747e49b8fde216b5cbdc753 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 4 Sep 2023 07:54:56 +0100 Subject: [PATCH 05/12] Automatic changelog for PR #4322 [ci skip] --- html/changelogs/AutoChangeLog-pr-4322.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4322.yml diff --git a/html/changelogs/AutoChangeLog-pr-4322.yml b/html/changelogs/AutoChangeLog-pr-4322.yml new file mode 100644 index 000000000000..7aea05453b2f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4322.yml @@ -0,0 +1,4 @@ +author: "Morrow" +delete-after: True +changes: + - bugfix: "Fixed perma helmet HUDs" \ No newline at end of file From 3d8b3800011ca6f906f8c8f02eb8fa89fdd67e20 Mon Sep 17 00:00:00 2001 From: harryob Date: Mon, 4 Sep 2023 07:46:01 +0100 Subject: [PATCH 06/12] overlay limbs for improved client performance (#4290) from: ![image](https://github.com/cmss13-devs/cmss13/assets/55142896/869653d5-5d30-4053-b40f-973ce7664f9d) to: ![image](https://github.com/cmss13-devs/cmss13/assets/55142896/2f154f46-a8bd-49ff-9f8d-544603732ca0) lower number is obviously good, especially on clients because this can make being on the front with a bunch of humans incredibly laggy for some reason we use vis_contents for limbs, which simplifies their icon updating. however it does also make clients lag the fuck out for whatever reason so we shouldn't do that also the fact that we copy these to ghosts doesn't help and can really start multiplying things can also remove iterating through visobjs in gFI, so ended up porting over the readability changes from mothblocks on tg here: https://github.com/tgstation/tgstation/pull/60285 god willing no player facing changes --- code/__DEFINES/human.dm | 15 +- code/__HELPERS/icons.dm | 276 ++++++++---------- .../mob/living/carbon/human/human_defines.dm | 6 + .../mob/living/carbon/human/update_icons.dm | 58 +++- code/modules/organs/limbs.dm | 178 ++++++----- icons/mob/humans/dam_human.dmi | Bin 14394 -> 19005 bytes 6 files changed, 291 insertions(+), 242 deletions(-) diff --git a/code/__DEFINES/human.dm b/code/__DEFINES/human.dm index 846119d6b55d..ebf08f495752 100644 --- a/code/__DEFINES/human.dm +++ b/code/__DEFINES/human.dm @@ -116,14 +116,15 @@ #define ORDER_FOCUS_MAX_LEVEL 50 //Human Overlays Indexes used in update_icons///////// -#define UNDERWEAR_LAYER 41 -#define UNDERSHIRT_LAYER 40 -#define MUTANTRACE_LAYER 39 +#define BODYPARTS_LAYER 42 +#define DAMAGE_LAYER 41 -/// For use by Hunter Flay -#define FLAY_LAYER 38 +#define UNDERWEAR_LAYER 40 +#define UNDERSHIRT_LAYER 39 +#define MUTANTRACE_LAYER 38 -#define DAMAGE_LAYER 37 +/// For use by Hunter Flay +#define FLAY_LAYER 37 #define UNIFORM_LAYER 36 /// bs12 specific. this hack is probably gonna come back to haunt me @@ -176,7 +177,7 @@ /// If you're hit by an acid DoT #define EFFECTS_LAYER 1 -#define TOTAL_LAYERS 41 +#define TOTAL_LAYERS 42 ////////////////////////////////// //Synthetic Defines diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 39d91b2ada26..b2ac00098c69 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -323,215 +323,185 @@ world . = list(r, g, b) if(usealpha) . += alpha +/// Create a single [/icon] from a given [/atom] or [/image]. +/// +/// Very low-performance. Should usually only be used for HTML, where BYOND's +/// appearance system (overlays/underlays, etc.) is not available. +/// +/// Only the first argument is required. +/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + // Loop through the underlays, then overlays, sorting them into the layers list + #define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \ + for (var/i in 1 to process.len) { \ + var/image/current = process[i]; \ + if (!current) { \ + continue; \ + } \ + if (current.plane != FLOAT_PLANE && current.plane != appearance.plane) { \ + continue; \ + } \ + var/current_layer = current.layer; \ + if (current_layer < 0) { \ + if (current_layer <= -1000) { \ + return flat; \ + } \ + current_layer = base_layer + appearance.layer + current_layer / 1000; \ + } \ + for (var/index_to_compare_to in 1 to layers.len) { \ + var/compare_to = layers[index_to_compare_to]; \ + if (current_layer < layers[compare_to]) { \ + layers.Insert(index_to_compare_to, current); \ + break; \ + } \ + } \ + layers[current] = current_layer; \ + } - -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? 1 : null); \ - if(A.alpha < 255) { \ - SELF_ICON.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color, ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE + if(!appearance || appearance.alpha <= 0) + return icon(flat_template) + if(start) if(!defdir) - defdir = A.dir + defdir = appearance.dir if(!deficon) - deficon = A.icon + deficon = appearance.icon if(!defstate) - defstate = A.icon_state + defstate = appearance.icon_state if(!defblend) - defblend = A.blend_mode + defblend = appearance.blend_mode - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate + var/curicon = appearance.icon || deficon + var/curstate = appearance.icon_state || defstate + var/curdir = (!appearance.dir || appearance.dir == SOUTH) ? defdir : appearance.dir - if(!(noIcon = (!curicon))) + var/render_icon = curicon + + if (render_icon) var/curstates = icon_states(curicon) if(!(curstate in curstates)) - if("" in curstates) + if ("" in curstates) curstate = "" else - noIcon = TRUE // Do not render this object. + render_icon = FALSE - var/curdir var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - //These should use the parent's direction (most likely) - if(!A.dir || A.dir == SOUTH) - curdir = defdir - else - curdir = A.dir - //Try to remove/optimize this section ASAP, CPU hog. //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) + if(render_icon && curdir != SOUTH) + if ( + !length(icon_states(icon(curicon, curstate, NORTH))) \ + && !length(icon_states(icon(curicon, curstate, EAST))) \ + && !length(icon_states(icon(curicon, curstate, WEST))) \ + ) base_icon_dir = SOUTH if(!base_icon_dir) base_icon_dir = curdir - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. - - var/curblend = A.blend_mode || defblend + var/curblend = appearance.blend_mode || defblend - if(length(A.overlays) || length(A.underlays)) - var/icon/flat = BLANK + if(appearance.overlays.len || appearance.underlays.len) + var/icon/flat = icon(flat_template) // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed var/list/layers = list() var/image/copy // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon = curicon, icon_state = curstate, layer = A.layer, dir = base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha + if(render_icon) + copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=base_icon_dir) + copy.color = appearance.color + copy.alpha = appearance.alpha copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 2) - var/list/process = process_set ? A.overlays : A.underlays - switch(process_set) - if(0) - process = A.underlays - if(1) - process = A.vis_contents - if(2) - process = A.overlays - for(var/i in 1 to length(process)) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - if(process_set == 1 && !istype(current)) - current = image(icon = current.icon, icon_state = current.icon_state, layer = current.layer, dir = current.dir) - var/current_layer = current.layer - if(current_layer < 0) - if(current_layer <= -1000) - return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to length(layers)) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer + layers[copy] = appearance.layer + + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.underlays, 0) + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.overlays, 1) var/icon/add // Icon of overlay being added - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] + var/flatX1 = 1 + var/flatX2 = flat.Width() + var/flatY1 = 1 + var/flatY2 = flat.Height() + + var/addX1 = 0 + var/addX2 = 0 + var/addY1 = 0 + var/addY2 = 0 - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) + for(var/image/layer_image as anything in layers) + if(layer_image.alpha == 0) continue - if(I == copy) // 'I' is an /image based on the object being flattened. + if(layer_image == copy) // 'layer_image' is an /image based on the object being flattened. curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) + add = icon(layer_image.icon, layer_image.icon_state, base_icon_dir) else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim) if(!add) continue + // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) + addX1 = min(flatX1, layer_image.pixel_x + 1) + addX2 = max(flatX2, layer_image.pixel_x + add.Width()) + addY1 = min(flatY1, layer_image.pixel_y + 1) + addY2 = max(flatY2, layer_image.pixel_y + add.Height()) + + if ( + addX1 != flatX1 \ + && addX2 != flatX2 \ + && addY1 != flatY1 \ + && addY2 != flatY2 \ ) - - if(flat_size ~! add_size) // Resize the flattened icon so the new icon fits flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 ) - flat_size = add_size.Copy() + + flatX1 = addX1 + flatX2 = addY1 + flatY1 = addX2 + flatY2 = addY2 // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + flat.Blend(add, blendMode2iconMode(curblend), layer_image.pixel_x + 2 - flatX1, layer_image.pixel_y + 2 - flatY1) - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) + if(appearance.color) + if(islist(appearance.color)) + flat.MapColors(arglist(appearance.color)) else - flat.Blend(A.color, ICON_MULTIPLY) + flat.Blend(appearance.color, ICON_MULTIPLY) - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + if(appearance.alpha < 255) + flat.Blend(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY) if(no_anim) //Clean up repeated frames var/icon/cleaned = new /icon() cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned + return cleaned else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF + return icon(flat, "", SOUTH) + else if (render_icon) // There's no overlays. + var/icon/final_icon = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? TRUE : null) + + if (appearance.alpha < 255) + final_icon.Blend(rgb(255,255,255, appearance.alpha), ICON_MULTIPLY) + + if (appearance.color) + if (islist(appearance.color)) + final_icon.MapColors(arglist(appearance.color)) + else + final_icon.Blend(appearance.color, ICON_MULTIPLY) + + return final_icon + + #undef PROCESS_OVERLAYS_OR_UNDERLAYS /proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 7c7ad7a0166b..4416ba74fda3 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -162,6 +162,12 @@ ///list of weakrefs of recently dropped objects var/list/remembered_dropped_objects = list() + /// associated list of body part zone -> currently active limb key + var/list/icon_render_keys = list() + + /// static associated list of limb key -> image to avoid unnecessary overlay generation + var/static/list/icon_render_image_cache = list() + /client/var/cached_human_playtime /client/proc/get_total_human_playtime(skip_cache = FALSE) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 88887126b1c1..5fa8b577e8c2 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -73,14 +73,14 @@ There are several things that need to be remembered: */ +/mob/living/carbon/human/apply_overlay(cache_index) + var/image/images = overlays_standing[cache_index] + if(!images) + return -/mob/living/carbon/human/apply_overlay(cache_index) - var/image/I = overlays_standing[cache_index] - if(I) - I.appearance_flags |= RESET_COLOR - SEND_SIGNAL(src, COMSIG_HUMAN_OVERLAY_APPLIED, cache_index, I) - overlays += I + SEND_SIGNAL(src, COMSIG_HUMAN_OVERLAY_APPLIED, cache_index, images) + overlays += images /mob/living/carbon/human/remove_overlay(cache_index) if(overlays_standing[cache_index]) @@ -132,10 +132,32 @@ There are several things that need to be remembered: //BASE MOB SPRITE /mob/living/carbon/human/proc/update_body() appearance_flags |= KEEP_TOGETHER // sanity - vis_contents.Cut() - for(var/obj/limb/part in limbs) - vis_contents += part - part.update_icon(TRUE) + + update_damage_overlays() + + var/list/needs_update = list() + for(var/obj/limb/part as anything in limbs) + part.update_limb() + + var/old_key = icon_render_keys?[part.icon_name] + icon_render_keys[part.icon_name] = part.get_limb_icon_key() + if(icon_render_keys[part.icon_name] == old_key) + continue + + needs_update += part + + var/list/new_limbs = list() + for(var/obj/limb/part as anything in limbs) + if(part in needs_update) + var/bodypart_icon = part.get_limb_icon() + new_limbs += bodypart_icon + icon_render_image_cache[icon_render_keys[part.icon_name]] = bodypart_icon + else + new_limbs += icon_render_image_cache[icon_render_keys[part.icon_name]] + + remove_overlay(BODYPARTS_LAYER) + overlays_standing[BODYPARTS_LAYER] = new_limbs + apply_overlay(BODYPARTS_LAYER) if(species.flags & HAS_UNDERWEAR) //Underwear @@ -154,6 +176,21 @@ There are several things that need to be remembered: overlays_standing[UNDERSHIRT_LAYER] = undershirt_icon apply_overlay(UNDERSHIRT_LAYER) +/// Recalculates and reapplies damage overlays to every limb +/mob/living/carbon/human/proc/update_damage_overlays() + remove_overlay(DAMAGE_LAYER) + + var/list/damage_overlays = list() + for(var/obj/limb/part as anything in limbs) + if(part.status & LIMB_DESTROYED) + continue + + damage_overlays += part.get_damage_overlays() + + overlays_standing[DAMAGE_LAYER] = damage_overlays + + apply_overlay(DAMAGE_LAYER) + /mob/living/carbon/human/proc/remove_underwear() // :flushed: - geeves remove_overlay(UNDERSHIRT_LAYER) remove_overlay(UNDERWEAR_LAYER) @@ -739,7 +776,6 @@ Applied by gun suicide and high impact bullet executions, removed by rejuvenate, //Human Overlays Indexes///////// #undef MUTANTRACE_LAYER -#undef DAMAGE_LAYER #undef UNIFORM_LAYER #undef TAIL_LAYER #undef ID_LAYER diff --git a/code/modules/organs/limbs.dm b/code/modules/organs/limbs.dm index 7d0261d971b9..58d0a4780681 100644 --- a/code/modules/organs/limbs.dm +++ b/code/modules/organs/limbs.dm @@ -71,6 +71,19 @@ var/status = LIMB_ORGANIC var/processing = FALSE + /// ethnicity of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()] + var/ethnicity = "western" + + /// body type of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()] + var/body_type = "mesomorphic" + + /// species of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()] + var/datum/species/species + + /// defines which sprite the limb should use if dimorphic, set in [/obj/limb/proc/update_limb()] + var/limb_gender = MALE + + /obj/limb/Initialize(mapload, obj/limb/P, mob/mob_owner) . = ..() if(P) @@ -81,12 +94,10 @@ if(mob_owner) owner = mob_owner - wound_overlay = image('icons/mob/humans/dam_human.dmi', "grayscale_0") - wound_overlay.blend_mode = BLEND_INSET_OVERLAY + wound_overlay = image('icons/mob/humans/dam_human.dmi', "grayscale_0", -DAMAGE_LAYER) wound_overlay.color = owner?.species.blood_color - burn_overlay = image('icons/mob/humans/dam_human.dmi', "burn_0") - burn_overlay.blend_mode = BLEND_INSET_OVERLAY + burn_overlay = image('icons/mob/humans/dam_human.dmi', "burn_0", -DAMAGE_LAYER) if(owner) forceMove(owner) @@ -361,7 +372,7 @@ SEND_SIGNAL(src, COMSIG_LIMB_TAKEN_DAMAGE, is_ff, previous_brute, previous_burn) owner.updatehealth() - update_icon() + owner.update_damage_overlays() start_processing() ///Special delimbs for different limbs @@ -651,7 +662,7 @@ This function completely restores a damaged organ to perfect condition. // sync the organ's damage with its wounds update_damages() - update_icon() + owner.update_damage_overlays() if(wound_disappeared) owner.update_med_icon() remove_wound_bleeding() @@ -670,74 +681,66 @@ This function completely restores a damaged organ to perfect condition. number_wounds += W.amount -/obj/limb/update_icon(forced = FALSE) - if(parent && parent.status & LIMB_DESTROYED) - overlays.Cut() - icon_state = "" - return +/// updates the various internal variables of the limb from the owner +/obj/limb/proc/update_limb() + SHOULD_CALL_PARENT(TRUE) - if(status & LIMB_DESTROYED) - if(forced) - overlays.Cut() - if(has_stump_icon && !(status & LIMB_AMPUTATED)) - icon = 'icons/mob/humans/dam_human.dmi' - icon_state = "stump_[icon_name]_bone" - var/image/blood_overlay = new('icons/mob/humans/dam_human.dmi', "stump_[icon_name]_blood") - blood_overlay.color = owner.species.blood_color - overlays += blood_overlay - else - icon_state = "" - return + var/datum/ethnicity/owner_ethnicity = GLOB.ethnicities_list[owner?.ethnicity] - var/race_icon = owner.species.icobase + if(owner_ethnicity) + ethnicity = owner_ethnicity.icon_name + else + ethnicity = "western" - if ((status & LIMB_ROBOT) && !(owner.species && owner.species.flags & IS_SYNTHETIC)) - overlays.Cut() - icon = 'icons/mob/robotic.dmi' - icon_state = "[icon_name]" - return + var/datum/body_type/owner_body_type = GLOB.body_types_list[owner?.body_type] - var/datum/ethnicity/E = GLOB.ethnicities_list[owner.ethnicity] - var/datum/body_type/B = GLOB.body_types_list[owner.body_type] + if(owner_body_type) + body_type = owner_body_type.icon_name + else + body_type = "mesomorphic" - var/e_icon - var/b_icon + if(isspeciesyautja(owner)) + ethnicity = owner.ethnicity + body_type = owner.body_type - if (!E) - e_icon = "western" - else - e_icon = E.icon_name + species = owner?.species ? owner.species : GLOB.all_species[SPECIES_HUMAN] + limb_gender = owner?.gender ? owner.gender : FEMALE - if (!B) - b_icon = "mesomorphic" - else - b_icon = B.icon_name +/// generates a list of overlays that should be applied to the owner +/obj/limb/proc/get_limb_icon() + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) - if(isspeciesyautja(owner)) - e_icon = owner.ethnicity - b_icon = owner.body_type + . = list() - icon = race_icon - icon_state = "[get_limb_icon_name(owner.species, b_icon, owner.gender, icon_name, e_icon)]" - wound_overlay.color = owner.species.blood_color + if(parent?.status & LIMB_DESTROYED) + return - var/n_is = damage_state_text() - if (forced || n_is != damage_state) - overlays.Cut() - damage_state = n_is - update_overlays() + if(status & LIMB_DESTROYED) + if(has_stump_icon && !(status & LIMB_AMPUTATED)) + . += image('icons/mob/humans/dam_human.dmi', "stump_[icon_name]_blood", -DAMAGE_LAYER) + return + var/image/limb = image(layer = -BODYPARTS_LAYER) -/obj/limb/proc/update_overlays() - var/brutestate = copytext(damage_state, 1, 2) - var/burnstate = copytext(damage_state, 2) - if(brutestate != "0") - wound_overlay.icon_state = "grayscale_[brutestate]" - overlays += wound_overlay + if ((status & LIMB_ROBOT) && !(owner.species && owner.species.flags & IS_SYNTHETIC)) + limb.icon = 'icons/mob/robotic.dmi' + limb.icon_state = "[icon_name]" + . += limb + return - if(burnstate != "0") - burn_overlay.icon_state = "burn_[burnstate]" - overlays += burn_overlay + limb.icon = species.icobase + limb.icon_state = "[get_limb_icon_name(species, body_type, limb_gender, icon_name, ethnicity)]" + + . += limb + + return + +/// generates a key for the purpose of caching the icon to avoid duplicate generations +/obj/limb/proc/get_limb_icon_key() + SHOULD_CALL_PARENT(TRUE) + + return "[species.name]-[body_type]-[limb_gender]-[icon_name]-[ethnicity]-[status]" // new damage icon system // returns just the brute/burn damage code @@ -774,7 +777,7 @@ This function completely restores a damaged organ to perfect condition. //Recursive setting of self and all child organs to amputated /obj/limb/proc/setAmputatedTree() status |= LIMB_AMPUTATED - update_icon(TRUE) + owner.update_body() for(var/obj/limb/O as anything in children) O.setAmputatedTree() @@ -1140,7 +1143,7 @@ treat_grafted var tells it to apply to grafted but unsalved wounds, for burn kit for(var/obj/limb/T as anything in children) T.robotize(uncalibrated = uncalibrated, synth_skin = synth_skin) - update_icon(TRUE) + owner.update_body(TRUE) /obj/limb/proc/calibrate_prosthesis() status &= ~LIMB_UNCALIBRATED_PROSTHETIC @@ -1243,6 +1246,20 @@ treat_grafted var tells it to apply to grafted but unsalved wounds, for burn kit owner.incision_depths[name] = SURGERY_DEPTH_SURFACE owner.active_surgeries[name] = null +/obj/limb/proc/get_damage_overlays() + . = list() + + damage_state = damage_state_text() + var/brutestate = copytext(damage_state, 1, 2) + if(brutestate != "0") + wound_overlay.icon_state = "grayscale_[icon_name]_[brutestate]" + . += wound_overlay + + var/burnstate = copytext(damage_state, 2) + if(burnstate != "0") + burn_overlay.icon_state = "burn_[icon_name]_[burnstate]" + . += wound_overlay + /* LIMB TYPES */ @@ -1389,17 +1406,36 @@ treat_grafted var tells it to apply to grafted but unsalved wounds, for burn kit bandage_icon_amount = 4 var/disfigured = 0 //whether the head is disfigured. -///Specifically, damage overlays. Severed limb gore effects are applied elsewhere. -/obj/limb/head/update_overlays() - ..() + var/eyes_r + var/eyes_g + var/eyes_b + + var/lip_style + +/obj/limb/head/update_limb() + . = ..() + + eyes_r = owner.r_eyes + eyes_g = owner.g_eyes + eyes_b = owner.b_eyes - var/image/eyes = new/image('icons/mob/humans/onmob/human_face.dmi', owner.species.eyes) + lip_style = owner.lip_style + +/obj/limb/head/get_limb_icon() + . = ..() + + var/image/eyes = image('icons/mob/humans/onmob/human_face.dmi', species.eyes, layer = -BODYPARTS_LAYER) eyes.color = list(null, null, null, null, rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) - overlays += eyes + . += eyes + + if(lip_style && (species && species.flags & HAS_LIPS)) + var/image/lips = image('icons/mob/humans/onmob/human_face.dmi', "paint_[lip_style]", layer = -BODYPARTS_LAYER) + . += lips + +/obj/limb/head/get_limb_icon_key() + . = ..() - if(owner.lip_style && (owner.species && owner.species.flags & HAS_LIPS)) - var/icon/lips = new /icon('icons/mob/humans/onmob/human_face.dmi', "paint_[owner.lip_style]") - overlays += lips + return "[.]-[eyes_r]-[eyes_g]-[eyes_b]-[lip_style]" /obj/limb/head/take_damage(brute, burn, sharp, edge, used_weapon = null,\ list/forbidden_limbs = list(), no_limb_loss,\ diff --git a/icons/mob/humans/dam_human.dmi b/icons/mob/humans/dam_human.dmi index 88cbb883db18d280918c9b9098a41eca6ae5e41d..ba8a2556569343931a020ae32f110330b3960980 100644 GIT binary patch literal 19005 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 literal 14394 zcmcJ$cT^Nl*Dl(C1Vxfaj)LSYNiq^eqLL*`Qpq4W3rYr&oRg9zO3old6eNQrVE`pF zNEp&^YJTtgowLq5Yu$U_yT0?s%yd_Gb?w^W*}JQriO|weCb&g?3xXg5mB$LtAP6G} z{lUWlEuZ@8I=~5|kB-3`1#34;*H_MOUO72JkXL5XWJ_cZKQZ=?pXZVHHd7v|Y}>ft z@qRQThEILF^M^#qHTm+2oR_?Y$nHr`8?DKZ7yf?HU*~tI2gSH99vpJGeGOT!rL#E5 z#UJkIS`Mh!a}zb0-}_@OC?Ne8*Jr#X=5;juHXW_5o8|4qypWRyd#?UH?75!T9+_f# z2k_x)IeObzvOkVji?d?pml}1~pEzSX)}}|n2Y2m3Sj)-J7eYQq98?x8ilz2dv=#!Z z!mh-Gj|gk%p1u7XI@6FC=?r@ozeCK-t<-uVbTiB=-Adxq4b_{f>=(`{asw97(3C!awMwlH>xfOd6bdt z`m**lMl%s~uWJo6cajr?N4}e~;M~ouYa=9aCTlYGP3Snv019+=vi(^;ciNHHvK$JC-`pZ#)3Tb_N%NJbaZsWv!*2O z1kjN~OzA>7qik|{AINXx2+QuKRYjBC#;Ma{|8(XjZ4{|xXB5TG!K>R$6g_YH%(%3! zzV80r1neO8M}cE9dP~q8$pm}z3IR??dMS>UNMmQwyZ)`l>V6!9JzPsuy?6mX>6{PP z?C~>}7>rMT5JaUkG8I0xGa4#>uixve_9j}3J@LTMxz!5G@9581c#a{JChF?XSOI?l zZZ1L^dh*TeD6TgtJFPx5c#dlRd;Vy5maMym#!uXcwQhQd<=cqh)0WYE^XSEbr+a8aSS|NAUb!orNwt*V)S zz+lWagFa4qQ%5alRFt#b{<3_eprDqn)49he)$A^+iHgFCjmc+2(<0z$BVPXkYlyt8 z(7nTu^>4Li%v0XYyG)JtLA;`C<{SJ23YKXCJg!VunbY;;h$)HbpC@StXMU^iQnHCC za=hxZq@eTn!daSQXfg9$dVPb3hVd;m`Yj|&`6SDtDau?*ii`0?8(|!&oK*Ol z>~|xZh_jMn0u>b%8&{6KB%e!a^TVgc=ID7vSb9I-b|hF60H$`pgYCr_v$j| z-U7^;6d*HLd|l%k|v1S)=!2S>jh@OtErRTn-l-G)5ds zoyQZZ@=FH4M_e#2WzK>N5$-2tKAbPhz3eZ)6m2($2OwLrY`bBzH5fW$q~S}r9+C;O zl!EbZfe(?Wr&o_v!=q>SkRC8$LEICl|1M|Ww=5ylp3+#1#t{`lCys#&x8;8GE;l4} zFJ*hHteIatK98QwBsU{6Uq3h6-Lr}e*0L=+rYpe4C7x{(7aE;3WaW$(9ykd2Pp4wLTH#AF{*Vpp(w3Hse-fox-Y%;NR5Hu@Q?Zn*tvCI z6ou&QLI-j0DQy3-SDvU=S{HrsrFJD~wedC?wdr5ryDUcf(p6VxCE~NBsGLGpShLHb#lm!U-!8qqSKe#( zrd73KSZc4^4CkbT6I{)K0oSLP%=0WG0)qZF#qXRP{lS$9CKNv6JDh{PY$6~U3z-@!3S0LyW>DI zCi)?pW39(v(oZ5)>$)$Qz#JB^p})V^F`(8>j_yIG$)D3q=p)M7_VyKCOAU_!hf>{> zE-k7kr6Hqs<0waaX|MH$!B~|ym7gq)YK#bEgR~ZHz_^==o$MteP>47g_W5Y4{f4oZdD^5_~|LZ zYaouH_zi84qrT^)Q0yV1zm>)g4v-fO>ak1U>r!pI?rFFAR{{W z*dJ3qN54(^ycbO@>==)TfmjaT+`pb z$K?2!0O0)w(@O;*js#8!#u08El?+ijCa$+Z>V4Ea3|J}eiVA9_jESvy^+pGr9P9){ zj;74oEP0>>mZBG>fBgF?nFaQ#2lA*Xd2f42X1-n6{)Jk6(Yl`?JgqPrA2%n!_J*L- zYo%e+!17yagT3>5tDhLdG7x8&nlz=7WnbpQDIJ7ob~{;&Dk^xc_wh)lz)0J&P7@Uv z{uFA&@61NEcU?Q!3{#3-K7(-@eO=>)KdS>5n7zjimK(^;OKhTh31 zGQ9l}2MN%|3ap{SNh{$Dnl*U(^jL zZ}hay0V3B8{` zHR{*_XaItP1kg3IMb}~#!sj+PG7_5_Z)HRB(07CO5_Nb`gP*}Es=rsh6pU(r^nIT% zEE2y9_$I{oMLf#(J@ZTtv2iHODN@ihwSI7{K>40iz&!8@S1CqI zM;q$OE~?6SVYdpDSEaBip`LKE2YJ>2dACB_1Zue;0PPg_@OyGJ>S33GoUzy7;62T` zf#piItLCIay~*dH-H;)w2B;rr(#!LUy}1(gMU1ep@T^Xg*s7<}d%IGL?AKy9?*E+w z12TKkJ0-sBn?+KMjH|24iF}1s)*$=kUJ4r`Aq626tkUNB$K=4dHHSXp9AIWr!w26Wu!3J=pLMp z@4eEsM)?A%aJ+-QHPgi}R~Qd;xFh28`m?>Cg!p`Qt&g(ThPtwC3pZvGY$acT^1VCf zI$dMu!Q?1#gCApagoeb?(X|O`9u)k3Uml5^@UAsUfS+!I(o}=u91`uhp?V!y_fJvp3Af$l)}~YeVJ8kb6YG^PHNBn7RJjw*qId zdkBj>4$iFF9Yfk_U+NZ_}YNzA(DQW_}Dh)7Y81u>Fq)MyfJOT zmGQ5X#RBiQUslq*ho+wlfdl21eyCB5|*_7A4LdIvZ`f)NNbrC;*z;4=2Jx~J1dnJG{|91$Z!qCWYFB1Dk) zl9d5}^Y2-rFskz)t$y@3>7mO981`zp>8-BW&OwLDl9H!C4^BWf1b+YFf>F;=&*hLn z4_%~DZsTwzS*}?2)@+l+c$i=bz};83Fcg+wve2winEEUoT73BTD+trwTQOVisZwi# z%@fhMcU4IwQf0xO6+}NxJh*d9fC}$y0H+~ z>*e&;Wu(PVtL2vMXr{cL1`c!_mnZbGy4LjpcN11Bd-5NpzS#FOH^Cd~O!;gAF9d)> z#wpWo58?GymXZJUO`NiR77ol{*>5}9@%Xh*rmYBXV6F%U1BY+D2uO*nA_3~bN5=;N zqeKpl(q7^Dj?p@4CvRUxc9`LX`S|D!oT};BAA_}lWbl32HFrEqO{@Dwb$T+Sl$7)r zqTC`4eWi-CFu^-+amII;&ZY6DkZR_q;f`L1$$a`gGLId#cZ-!~D&FS$%zrkxV5Cn7 zU_IzJo6*zJxg10aPT65lS;no^h;S-%?ZzuYL7PK-YN64uUS6KEYjy{X-tbV~sUGp# zl}34-+}iPyNhfm~Ckn-=qJ!Rc{mSY*pgIFckA8Onj@rHTnb?Y!Y9bXsXqS^-_2#dN zv%iNRSKxx83rD9M<1QLULH5FuDD&*seVnw2O}lt7D{#RoUT8v4O`43U#W78_zn>GJ zwn% z)de6!I!BXM%120&(6P72Jm2+fy??QD3gTRse_cMiM_K(YTDV5WiZEFw8i&%+E31)` zI#*hhZ3kb}@vlKTf)! z_EUsy=QW3-oq76Z2ZQM2lRgQv6)zv=52rH?#mq*|54Y?R#)?!54H;zp1KiJGz~Ao&4aF# zb~2aewleiQYJX;AE~=}1TLu=T_x5q1R#T)ViP`!nN7FZVh9DQI53yPy5C2+-CI=uy zJ||uODC3LDEc>1RL%%}t_oqm@i>{mV>U+9CL+^_I1g?yCatv<;Qphizr6#B2w_SNk zUTDhQT*mYGGnMjg3$}c-x}apZR|Rxojtcq)lkrAqm@}mld*|t&K=L8 z98{FG#2WZf^*R|n%X>6=WmCz1lw_CH7uPpD#%Z~Wt6RMb1}QSoma+Bu{r;M==`$X; zS^e$n(;Y+bt7bU7cd;kv-=X1y_c+^u{Cu!3FnR_2Pr1s-YCO~LkM zoa8mJ(9fH;*Axi35g$5wDi5ErtELxGLwf zXHe%NQGk9zjMltUpo7onkCldC=6eALEz}UVar@miTNychlf`1e7nUP7Y5f6OkB&V^ zk42^E=uc&qkJP?(4D9m}WXc6jOq6PC@68d=d^>-Kk2s&JHRy1@m%Ui^Xu#GnX~yo{ z&a@t)l|a(COq5pIxrX2C$c4g8{SDEz^7rhl$l!@05-D;MzioXre+?T)?Jw-0vRm0q zKHToyoJf_DAoCtdu|s$0KRc+Ton>=;e(b(G+qInPW6s*lP%ad|TQ835itBwMb7Ub1 z{aOuEr#!Es(!aZS2jL68-{uizxFC91q0S#+-?e?%Una~%wSE8II0o-p+OV;&$+d`G zmksRLtn{A4bOtK~5aX)Ggy+wtZj^~`w;&g8TN_(<57D8Wa`7X{({^vs_mZapqWNLs zxBSV zv6^mJ*!!x0F{akMZG(=NB!ol+91(V^M8E9tmCe=~FS_<$WBD+kgUA6?Y06o<6u?u& zN;X);*xZjOJ%Mrzn$80pHYuDYaE_$=E%Jue9Setcak3;(F=N0NR`qd^Xc{2{2HRk4*$-!Z*IE&HT6c#d)PAW+I z_h`?<Dx}&cdgzX7eI?dEbAz>>q5J``t>jJDq*oqXC$ZFc{_Sg&$gTR?haGkwqLHW$D7{a$d!Go z`l#PJ4Gpnt6nELPLiq6C4q6xYy-+69m~~7)U;CjsVaqTRUHdjVrsL9dX)`IH&X6-}DtvKiMEyrvszJ#;xqia($UtBFkYWrGBKfFtz5wMB;o$* zlMp%`C(-zCwO4BtGlIeMqpb`G|6dip!b#dBqkgk7ohGMBt!Mde&$?n$HHN{ul(sQ8 z$5Hnd0CI&vI+yQ#<71TVO=O39jIm)_~4v11BIp4TZmTKV(u*)&a9CBLl_ zr+6?LiXTL4&2>&bnYO4>$6V_O&o738uXp_6QGA>OJtMPL zykPnA)8ENv_ZR+4MYWwL9_}1Gn4p6OLSNsQ1cf@*lz#gBM(IpB$PfXqNV!jN!g}xi>DY=S4I;wbevWZZ^Ys*M?vn zYK*z+qy44oty|rJsI5pYCZ8L6rKYt?+(X0PVw##76VoH3pvamUE#q8A0odwW%)8ds zD@;MEB_)jlq7*|91fS9s*vgK;Fh2xkMo}d7?X>Sp_XYHqkMJDDIN@Dio}~&};!L-n zu|np%8BQ-oqNxr>bK-u8wp(~#O0=0K-o4BCd$so<=i^(v|&qloh<{o6KtG|2bBByeBCiuckgl7A<-t5d+F!qc|n4s-QXjJD2Um(?I zeKUtmL$Bj>;ryemvg*G>W;px{$Q&^@9Q=wF!V7Wn@>{CpFEGL<(x?Qx19c>Pudiac z#5r9LrOrNy@bD0P$s?|4x$?00Ie293d9nMF;2l3`ZD-5*`sVh})3t=>S4W%uJA1JY zQE7{d#W~W8hsbdU4i0diR(5=_UmPxUa7M=EJb!+i9NVMcj?DJ}hseuFGAIaKT&JP} zcRCwAiPW_ikZZ-G4f}ybgx6t#EDAV(gA6zkw5I6HHfVbeDwpnpj*Bcy-e#$4pE|FX z*8wbNq4Zv|#^Qm}mselE3Vps<=I9Ij-s%lr2wuHv^g>Az9-pl4bGH*;{1vwQHavSGMh+@6l;;Ng;2wPjnPri38wG*P0q9KW~2TotnL{wZ*Hcnb{nfc_=8~v)R8;+v-gnyEHX!JMX12m& zAA{uzsiwhs`^$ho_p1FihQk}d#a#G;n1TD{fOz34M0j+Xaqd$>Bz1SjP_gxzk9-b! z!DrG9X5s=$%U8Y`83=tt!`bOL?ELK0}OsG8z3FscippNue}_o1kTO&+rowu*f1aM_JNQOmZoH0R=YEXDgIuc=A1nQ zkq@A`0C$ov^s0Y5nJ#+IGvMp)8aqi<2wH@*3i8ZC}81+UQh z6@}`4rkKI<_*;2@f3N*Xj#esE(9!|8Qn+=FyK6yT)Bhom3{0UhW*fJ z+{Lv|`!Nqs{59W|qcgFs5-jU;kGwp(R$ltyUT*dk4;0W$sH@>lC_waKs`JiEn;a9P zt{u2p;Ng86TU&VH*6ZEgU<}Fm@wd$tyyeJYTdm4%14Pz#=D;@mH-FN1gIg}pDodtJx;Mr>F1)iDb?ul^GPhNq^T@ z*XINWxI$OOW*lF2!fUP+3>!Zc;^E7z+qz-qM3D=(;UfFmk{*gB6bQul4KQ?j1t~eDd^oes~@s{&U%B0Ffgm6gtDn)fbT%FrKRk&OG@hAxsq{viO1mgH)91^9orFo-6h)b%EwOy>jnL_>iZ>| zL7B@C>zE`&9iQT{^Mr(i%hwJDjZ~C&s#z%5;Ncm2-vTzR67QzGes`v5^LgW8Aw`fG zTbJRM6?fpJ3y;r}<=@%fN<%w)Fm*98kk??~ly!C4&0~K$S;i41_%v{FIj#<$Ja_cH zV0(l?i+S*7Uq@pD#g~b~ytQ$bauxO7nJ20k(R&zvU%uY8d#3%=b@R^iB6ay{6$*t3 zn_=0X?lTq}KZvEL-4_7#=i61jLm(+ke0dxPzW4Su$EL~4KW^C*|77@8AzTu*{$5Vv z`!;qVN9$+XpI=u}nFrfUkI3$t@4m&}a-~#eBnI1tVD(w^k2_eL16!k*!9gl%VsV@M z>EI=KKQsXvIv&Z2ni=kX;e_`JrQU%4uyAEG8ITJL}}2r|xz53Y}smP6?3 zr`-vZWcXABpB`4reC?!M)kH<}K#(V{>7F2I*M<#f>id$GhU^UH3c?9_#KXmgb>$y& zvrZ9wKL?AGU{Nm7VS?75?V$5y_vS8A;riN(_v8eiRYj*Fw)TE>fR0%ll~Mh}LxQt{ zb02LpC-V!B-RZAF+!O9-x3~6fdzg_HApub9d?2CtdX2O-;PC$5W7VFyVKpn?w#Jg? z=SGFwjlw+N6Hhg#JE3Hg)^HFLbq>r!nwf$7ZQ=0104v_h`ffS&%MMSA7l|P=MJVYq zemr{QKp8J$f~zY7pB2pWTeqC;Y*;r&QiH`br9Km&*4UEUL~#NSNgYPCHD9^e4tIiy z{^4Tp9LJ~l@nad?_s0=DCtQIhu-2@vq>)@J6GtQa+craBbeU%MH#J3p-;&3P^B$VWBm?qI)3l(gob%8v93>? zNGf%6(jrv-{gKft$edI$(#6e9194X(xua`=C`nX{QgXWpVUImKdr)cD3;{NFqtJs% z(eK;6zxuE6^k@TXlF>n*TtlOwO*+Qus4&{YJb!u|?hcB_X7 zAFiKd;}54{I$jCuZ8+JVdBzZ6F#PP`e(}r7Mc+!x$e=-EAMIyaZ@vp7M=sC$=h}=e zz%P6A2K5o=^Ei-ovOhSydxxAxU9DK;!HRtq%L~evlwUqyi-;|JGD>KDj7bwU6`qHorBQ-HeroZ;|^%Yn`1U59x)F46W zu|L}1X~t!Yr*Xawzz9sI6cm5V9D7)g{nto>x3*82K(C2^8WIBI^J z`diF#cx|6<5EN;`VIS|bA~rIRQv{;sP-mrMBgXaA)H|9K9{HZl3HBT*8k@ejlTlES zc!q=+d`yolm?&K7PSA{mWz*jP@MKW`^A@O%?gz*?P`7_b!PPXSPqwnfQjRqrYV}V3 zF0TpXKqzSt?$L?iT$jJy-$UYjGb|AD<)Idh+~R zM+TM5&jRK+MoG!TD;j1qEN^derK`ymc%KUri3p!1Xlp~tkoV4$*X>)Y!BKH2Px`>^ zVri5W-9*k!H3fx%d#@_MQhIz7-~j#?%jLi6a4-?`2OxNWlxpuQ!lQ07fLy8(|82Fv zwx(8iHwf(ep!1oAkM>?^=>ryuT2tH7+7~i_C>V~WH1K!y@1)cxCgp@#EwUk=OpoRy zd1svcER?)c19n9xCnp*I1z8D;AO>oco@Hf?iB-QD0ja*H8W%;m#;pt5=B3ld+!1Td~Na9uhMf&-_ ze>j=Q1)+Bx(@Z@%(HlF>75ID1HBV|<8_Gu}c1C}K%TVria|v$O&P>LWHzpK-NS59Q zAG`H8>f=Vpla0B0_p(`CUF$g)&HB+C-Lrb2IiOT8N%}8Yi^5|^Jb-EHky;RtNFD&^L8T7^A}IxL~31_j5N%GX-z#vrGT|mwWY4Pm#mf}!}XJ) z05BZBY#J22sw|qCo=!h`p`ef`S!hfIcw4OR|BK-I-|XD~;)DHXk@E3JMJZ>s!?vD< zC%r=A!pD4Qr?m7*DQslRkK)2__GH#FJxL(^<(6tBX4UNPT8Vcm)#7H?gGu(65$qO# zxcg;M3`8oy$9!B_1%(Yjjb(D>07P7-lo$W$VF{H7n`~n7bba!{;XLrG%z+p~OiLvZ3~VZ%i7F#BPIv6HTXjW8C06h$@!x(xN4JSLHH9J_O${Exi-v-_B8c zUm_)>@KfP-Gu{o%m*Ugmz#M=Eb&rOn77o1eI|xoRi4ANgmWL;BH_8;^pqJ4$z`iY@ zm?iN%zk^yTeF$mQzr2YiY@NVjFDzf11LO67p2hnAKgIu4Gf#X#Faj>0;(g!SQ#P}J z9sLvyT?#5LC17TsW846bO_Dy26>dnSN(T7A?60V3u7P{1S^`Rz?2P+UKw(i&$_A`U z5|%F@4!?qM1b75=p*lKvae-3@PQXt2(^NAnYj*3eKYu=NabskPGy+6UJe-ih_f|T6 zwRiqO9T&hs{D+erZp>Ly7))K+D)EXsna*3x-HiC@;p0Z!sINR<0)Y4|#3u1#d@@0ZA{dYs0sr_f2+A5oRV{68 zthheQi?qsrD9pK=2F&LdIKpYab)r-hv^B?#u|5zO13m(fE?t{iGOn)xJLD0F(~k$I z+5)U3>QX8bz&uQDEb8b$8}9vQK^2f{6?p$I9;y@o3jx*Qwfqh59cUtnu^zk|bRL46 zZmo0C^^(7=q1g+`=T_1`D0AtP?wqa>_k8U>e0>G^X^hDs)HGWpcXH?uAymkI& zAABMLa7ec>ye72KzBLUvFl9R##iOFRpqcDSM1@tLI0J|tBUPr@VAVq zHT|UOm>dj~nWR1ZrZ$jk2S=^S4GwKfPv2$j_keh!Ba>M2&jZ;MmV~C4!4d3Nzeq58 z_S*pB0UB>-EG!VH6-_z?WJ1=!LQbN8CHBdD%Rzr8x0-asUp}|^rjqde8#VV%Dve9t zWO(Z8M#LD^r`{2)!0d$O?659}aMCRQneChn~vn8xGu5KY3XZrkJ-^uYue33qe@nL)3J-!VEe@)m$e3va^CgPVP5LxFmd z1raUcIG2B>Uoi=FqA|7xZ=i7ug51?u3@TfE2B>Zc*f5)DY{XK@Fnt2!ysfv! zZ+eM1WRAqe4|4z53>CjTfJHgz6NUDF;nb77|F%vu^~BrF7vu5Pt6UF}R{NufN&IQ) zJl%4yaEc7|kCWJ)j*=H_HQ4}y0G@ZpwC`ZFy7$qoev#NS7kG@W1>#j*gn!&P*vaYl z0ZT_KqiG20&fjJ}5jh<AVT$#b*cX7RkQYIxF>v~|mt(3QhUhydxb(t{^vnO}xCm;9L@XT< zKm!*J@_lXRO~CjF{A1?nIKL^D*Xl+_FRAGAFxY%VgZVQ7H({*?I59B^%jYoE0WSI5 ke}r3m_WzL1kFPN#$Oe5<+#U;q{{%uRiW&+Pa^`RU2i64PqyPW_ From 8cbf5440a26cd72a450ded750260bbe402d29d3f Mon Sep 17 00:00:00 2001 From: Vile Beggar Date: Mon, 4 Sep 2023 09:10:03 +0200 Subject: [PATCH 07/12] Removes debuffs from the folded M39 stock (#4305) # About the pull request removes the debuffs from the folded M39 stock, making it equal to a stockless M39. # Explain why it's good for the game currently there's no reason to fold your stock, as it inflicts a heavy wielded scatter penalty (from 9 scatter when extended to 15 when folded) with the only benefit being a removal of the wield delay and slightly better onehanded performance. a marine shouldn't be penalized for interacting with the folding stock. hopefully this make the attachment a bit more useful in its secondary form.
stat changes for anyone curious how it looks like in-game ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/bfd6b1cf-48fa-4fec-b925-8b71b1857112) (old stock, folded) ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/c7b2aee8-cddd-4e55-b6f1-a005a9ee75ff)
# Changelog :cl: balance: The M39 foldable stock no longer inflicts debuffs when folded. /:cl: --- code/modules/projectiles/gun_attachables.dm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm index 4ca52166997d..19da628c7922 100644 --- a/code/modules/projectiles/gun_attachables.dm +++ b/code/modules/projectiles/gun_attachables.dm @@ -1797,7 +1797,7 @@ Defined in conflicts.dm of the #defines folder. /obj/item/attachable/stock/smg/collapsible name = "submachinegun folding stock" - desc = "A Kirchner brand K2 M39 folding stock, standard issue in the USCM. The stock, when extended, reduces recoil and improves accuracy, but at a reduction to handling and agility. Seemingly a bit more effective in a brawl. This stock can collapse in, removing almost all positive and negative effects, however it slightly increases spread due to weapon being off-balanced by the collapsed stock." + desc = "A Kirchner brand K2 M39 folding stock, standard issue in the USCM. The stock, when extended, reduces recoil and improves accuracy, but at a reduction to handling and agility. Seemingly a bit more effective in a brawl. This stock can collapse in, removing all positive and negative effects." slot = "stock" melee_mod = 10 size_mod = 1 @@ -1830,6 +1830,9 @@ Defined in conflicts.dm of the #defines folder. /obj/item/attachable/stock/smg/collapsible/apply_on_weapon(obj/item/weapon/gun/gun) if(stock_activated) + accuracy_mod = HIT_ACCURACY_MULT_TIER_3 + recoil_mod = -RECOIL_AMOUNT_TIER_4 + scatter_mod = -SCATTER_AMOUNT_TIER_8 scatter_unwielded_mod = SCATTER_AMOUNT_TIER_10 size_mod = 1 aim_speed_mod = CONFIG_GET(number/slowdown_low) @@ -1842,22 +1845,20 @@ Defined in conflicts.dm of the #defines folder. attach_icon = "smgstockc_a" else + accuracy_mod = 0 + recoil_mod = 0 + scatter_mod = 0 scatter_unwielded_mod = 0 size_mod = 0 aim_speed_mod = 0 wield_delay_mod = 0 movement_onehanded_acc_penalty_mod = 0 - accuracy_unwielded_mod = -HIT_ACCURACY_MULT_TIER_1 - recoil_unwielded_mod = RECOIL_AMOUNT_TIER_5 + accuracy_unwielded_mod = 0 + recoil_unwielded_mod = 0 hud_offset_mod = 3 icon_state = "smgstockcc" attach_icon = "smgstockcc_a" - //don't *= -1 on debuffs, you'd actually be making than without stock when it's collapsed. - accuracy_mod *= -1 - recoil_mod *= -1 - scatter_mod *= -1 - gun.recalculate_attachment_bonuses() gun.update_overlays(src, "stock") From 2b8ef06a1150b25763914f5c2570966c14367b2d Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 4 Sep 2023 08:22:44 +0100 Subject: [PATCH 08/12] Automatic changelog for PR #4305 [ci skip] --- html/changelogs/AutoChangeLog-pr-4305.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4305.yml diff --git a/html/changelogs/AutoChangeLog-pr-4305.yml b/html/changelogs/AutoChangeLog-pr-4305.yml new file mode 100644 index 000000000000..fb44c789fbb0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4305.yml @@ -0,0 +1,4 @@ +author: "VileBeggar" +delete-after: True +changes: + - balance: "The M39 foldable stock no longer inflicts debuffs when folded." \ No newline at end of file From 07da79411ecd1cef24faa67e4b1d30fb9efe6d90 Mon Sep 17 00:00:00 2001 From: Steelpoint <6595389+Steelpoint@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:16:15 +0800 Subject: [PATCH 09/12] Adds Helmets + Visor To Synth Vendor | Grey M10 Helmet Addition (#4306) # About the pull request Adds several helmet options to the synth vendor (M10, Corpsman and Technician Helmets), also adds a new 'grey' helmet that can be vended that is essentially a standard M10 helm but defaulted to the grey/urban camo. Also adds a riot visor to the vendor. # Explain why it's good for the game The helmets are already available to synths across the ship, this just speeds up the synthetics dressing up time. The grey helmet allows a synth to match their default synth armour with a helmet no matter the map. # Testing Photographs and Procedure
Screenshots & Videos Grey helmet and visor ![Screenshot 2023-09-02 01 35 54](https://github.com/cmss13-devs/cmss13/assets/6595389/e8b57648-6860-440e-8303-d442fae3fdd8) Menu ![Screenshot 2023-09-02 01 37 42](https://github.com/cmss13-devs/cmss13/assets/6595389/cca27911-3295-4083-9887-399723f25abf)
# Changelog :cl: add: Synthetic vendors now have the option to vend several helmets and a riot visor. Including a standard M10 helmet defaulted to non-camo grey. /:cl: Co-authored-by: Steelpoint --- code/game/machinery/vending/vendor_types/crew/synthetic.dm | 6 ++++++ code/modules/clothing/head/helmet.dm | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm index 460f43c3f234..8ddd8df4c3a2 100644 --- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm +++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm @@ -232,6 +232,12 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth_snowflake, list( list("MP Cap", 12, /obj/item/clothing/head/beret/marine/mp/mpcap, null, VENDOR_ITEM_REGULAR), list("RO Cap", 12, /obj/item/clothing/head/cmcap/req, null, VENDOR_ITEM_REGULAR), list("Officer Cap", 12, /obj/item/clothing/head/cmcap/ro, null, VENDOR_ITEM_REGULAR), + list("Marine Helmet", 12, /obj/item/clothing/head/helmet/marine, null, VENDOR_ITEM_REGULAR), + list("Grey Marine Helmet", 12, /obj/item/clothing/head/helmet/marine/grey, null, VENDOR_ITEM_REGULAR), + list("Technician Helmet", 12, /obj/item/clothing/head/helmet/marine/tech, null, VENDOR_ITEM_REGULAR), + list("Corpsman Helmet", 12, /obj/item/clothing/head/helmet/marine/medic, null, VENDOR_ITEM_REGULAR), + list("Attachable Helmet Shield", 12, /obj/item/prop/helmetgarb/riot_shield, null, VENDOR_ITEM_REGULAR), + list("SUIT", 0, null, null, null), list("Bomber Jacket, Brown", 12, /obj/item/clothing/suit/storage/bomber, null, VENDOR_ITEM_REGULAR), diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 91b29a51a5a4..aa1d21c4902e 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -718,6 +718,12 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list( specialty = "M10 technician" built_in_visors = list(new /obj/item/device/helmet_visor, new /obj/item/device/helmet_visor/welding_visor) +/obj/item/clothing/head/helmet/marine/grey + desc = "A standard M10 Pattern Helmet. This one has not had a camouflage pattern applied to it yet. There is a built-in camera on the right side." + icon_state = "c_helmet" + item_state = "c_helmet" + flags_atom = NO_SNOW_TYPE + /obj/item/clothing/head/helmet/marine/tech/tanker name = "\improper M50 tanker helmet" desc = "The lightweight M50 tanker helmet is designed for use by armored crewmen in the USCM. It offers low weight protection, and allows agile movement inside the confines of an armored vehicle. Features a toggleable welding screen for eye protection." From 0742198310db6d62a69d4bec523312401395e803 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 4 Sep 2023 08:37:45 +0100 Subject: [PATCH 10/12] Automatic changelog for PR #4306 [ci skip] --- html/changelogs/AutoChangeLog-pr-4306.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4306.yml diff --git a/html/changelogs/AutoChangeLog-pr-4306.yml b/html/changelogs/AutoChangeLog-pr-4306.yml new file mode 100644 index 000000000000..9bb2d328a49c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4306.yml @@ -0,0 +1,4 @@ +author: "Steelpoint" +delete-after: True +changes: + - rscadd: "Synthetic vendors now have the option to vend several helmets and a riot visor. Including a standard M10 helmet defaulted to non-camo grey." \ No newline at end of file From 7c37cff0e32a193894ad8ad4726a19aa18d0c787 Mon Sep 17 00:00:00 2001 From: Ben <91219575+Ben10083@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:21:08 -0400 Subject: [PATCH 11/12] Working Joe restriction bypass toggle (#4310) # About the pull request Adds a new flag, allowing bypasses for Working Joe spawns such as limit, entry_allowed, and if they were a Working Joe before. # Explain why it's good for the game This was requested by event staff, where they want to allow for Joes to respawn easier when they want Working Joes to interact in a manner where death is highly likely. This flag will allow them to loosen restrictions to join as a Joe (still need a WL however, no commoners allowed) for the sake of "roleplay" Considering how this is implemented, may expand to add similar flags for other roles, if needed. # Changelog :cl: admin: New toggleable flag added to bypass restrictions for Working Joe spawns as observer (excluding whitelist requirement) /:cl: --------- Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> --- code/__DEFINES/mode.dm | 1 + code/_globalvars/bitfields.dm | 1 + code/game/gamemodes/cm_initialize.dm | 6 +++--- code/modules/admin/admin_verbs.dm | 3 ++- code/modules/admin/tabs/admin_tab.dm | 13 +++++++++++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/code/__DEFINES/mode.dm b/code/__DEFINES/mode.dm index bb31f4d84b1e..6ca6a142beb2 100644 --- a/code/__DEFINES/mode.dm +++ b/code/__DEFINES/mode.dm @@ -71,6 +71,7 @@ #define MODE_SHIPSIDE_SD (1<<8) /// Toggles whether Predators can big SD when not on the groundmap #define MODE_HARDCORE_PERMA (1<<9) /// Toggles Hardcore for all marines, meaning they instantly perma upon death #define MODE_DISPOSABLE_MOBS (1<<10) // Toggles if mobs fit in disposals or not. Off by default. +#define MODE_BYPASS_JOE (1<<11) // Toggles if ghosts can bypass Working Joe spawn limitations, does NOT bypass WL requirement. Off by default. #define ROUNDSTATUS_FOG_DOWN 1 #define ROUNDSTATUS_PODDOORS_OPEN 2 diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index d71125c318f6..a497f4d01dfb 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -417,6 +417,7 @@ DEFINE_BITFIELD(toggleable_flags, list( "MODE_LZ_PROTECTION" = MODE_LZ_PROTECTION, "MODE_SHIPSIDE_SD" = MODE_SHIPSIDE_SD, "MODE_DISPOSABLE_MOBS" = MODE_DISPOSABLE_MOBS, + "MODE_BYPASS_JOE" = MODE_BYPASS_JOE, )) DEFINE_BITFIELD(state, list( diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm index bc6adc026b6e..36271054bb21 100644 --- a/code/game/gamemodes/cm_initialize.dm +++ b/code/game/gamemodes/cm_initialize.dm @@ -957,7 +957,7 @@ Additional game mode variables. to_chat(joe_candidate, SPAN_WARNING("You are not whitelisted! You may apply on the forums to be whitelisted as a synth.")) return - if(joe_candidate.ckey in joes) + if((joe_candidate.ckey in joes) && !MODE_HAS_TOGGLEABLE_FLAG(MODE_BYPASS_JOE)) if(show_warning) to_chat(joe_candidate, SPAN_WARNING("You already were a Working Joe this round!")) return @@ -965,12 +965,12 @@ Additional game mode variables. // council doesn't count towards this conditional. if(joe_job.get_whitelist_status(RoleAuthority.roles_whitelist, joe_candidate.client) == WHITELIST_NORMAL) var/joe_max = joe_job.total_positions - if(joe_job.current_positions >= joe_max) + if((joe_job.current_positions >= joe_max) && !MODE_HAS_TOGGLEABLE_FLAG(MODE_BYPASS_JOE)) if(show_warning) to_chat(joe_candidate, SPAN_WARNING("Only [joe_max] Working Joes may spawn per round.")) return - if(!enter_allowed) + if(!enter_allowed && !MODE_HAS_TOGGLEABLE_FLAG(MODE_BYPASS_JOE)) if(show_warning) to_chat(joe_candidate, SPAN_WARNING("There is an administrative lock from entering the game.")) return diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 5e527e6a5442..7d9127313094 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -135,7 +135,8 @@ var/list/admin_verbs_minor_event = list( /client/proc/adminpanelweapons, /client/proc/admin_general_quarters, /client/proc/admin_biohazard_alert, - /client/proc/toggle_hardcore_perma + /client/proc/toggle_hardcore_perma, + /client/proc/toggle_bypass_joe_restriction, ) var/list/admin_verbs_major_event = list( diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm index 1298d6150036..4f59ad7d2d3d 100644 --- a/code/modules/admin/tabs/admin_tab.dm +++ b/code/modules/admin/tabs/admin_tab.dm @@ -839,3 +839,16 @@ SSticker.mode.toggleable_flags ^= MODE_HARDCORE_PERMA message_admins("[src] has toggled Hardcore [MODE_HAS_TOGGLEABLE_FLAG(MODE_HARDCORE_PERMA) ? "on, causing all humans to instantly go perma on death" : "off, causing all humans to die like normal"].") +/client/proc/toggle_bypass_joe_restriction() + set name = "Toggle Working Joe Restrictions" + set category = "Admin.Flags" + + if(!admin_holder || !check_rights(R_EVENT, FALSE)) + return + + if(!SSticker.mode) + to_chat(usr, SPAN_WARNING("A mode hasn't been selected yet!")) + return + + SSticker.mode.toggleable_flags ^= MODE_BYPASS_JOE + message_admins("[src] has [MODE_HAS_TOGGLEABLE_FLAG(MODE_BYPASS_JOE) ? "allowed players to bypass (except whitelist)" : "prevented players from bypassing"] Working Joe spawn conditions.") From 1faa8bb9ff0c4a2efc55872741a3e19b491f5e0f Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:29:33 +0100 Subject: [PATCH 12/12] Automatic changelog for PR #4310 [ci skip] --- html/changelogs/AutoChangeLog-pr-4310.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-4310.yml diff --git a/html/changelogs/AutoChangeLog-pr-4310.yml b/html/changelogs/AutoChangeLog-pr-4310.yml new file mode 100644 index 000000000000..58ad6502a35f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-4310.yml @@ -0,0 +1,4 @@ +author: "Ben10083" +delete-after: True +changes: + - admin: "New toggleable flag added to bypass restrictions for Working Joe spawns as observer (excluding whitelist requirement)" \ No newline at end of file